Browse Source

Fixes issue #2616 - this is a slightly complex fix and handles a few other issues with mysql (around returning on creation of a row and relationships) - a new mechanism is now used for pagination and limiting which makes sure the limits are applied to the outer table rather than the combination of the outer and the joined.

pull/2724/head
mike12345567 5 years ago
parent
commit
1952dc308e
  1. 2
      packages/server/scripts/integrations/postgres/docker-compose.yml
  2. 3
      packages/server/src/api/controllers/row/ExternalRequest.ts
  3. 5
      packages/server/src/definitions/datasource.ts
  4. 44
      packages/server/src/integrations/base/sql.ts
  5. 21
      packages/server/src/integrations/mysql.ts

2
packages/server/scripts/integrations/postgres/docker-compose.yml

@ -15,7 +15,7 @@ services:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
pgadmin:
container_name: pgadmin
container_name: pgadmin-pg
image: dpage/pgadmin4
restart: always
environment:

3
packages/server/src/api/controllers/row/ExternalRequest.ts

@ -544,6 +544,9 @@ module External {
extra: {
idFilter: buildFilters(id || generateIdForRow(row, table), {}, table),
},
meta: {
table,
}
}
// can't really use response right now
const response = await makeExternalQuery(appId, json)

5
packages/server/src/definitions/datasource.ts

@ -1,3 +1,5 @@
import {Table} from "./common";
export enum Operation {
CREATE = "CREATE",
READ = "READ",
@ -136,6 +138,9 @@ export interface QueryJson {
sort?: SortJson
paginate?: PaginationJson
body?: object
meta?: {
table?: Table,
}
extra?: {
idFilter?: SearchFilters
}

44
packages/server/src/integrations/base/sql.ts

@ -1,7 +1,5 @@
import { Knex, knex } from "knex"
const BASE_LIMIT = 5000
// if requesting a single row then need to up the limit for the sake of joins
const SINGLE_ROW_LIMIT = 100
import {
QueryJson,
SearchFilters,
@ -146,46 +144,48 @@ function buildCreate(
function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery {
let { endpoint, resource, filters, sort, paginate, relationships } = json
const tableName = endpoint.entityId
let query: KnexQuery = knex(tableName)
// select all if not specified
if (!resource) {
resource = { fields: [] }
}
let selectStatement: string|string[] = "*"
// handle select
if (resource.fields && resource.fields.length > 0) {
// select the resources as the format "table.columnName" - this is what is provided
// by the resource builder further up
query = query.select(resource.fields.map(field => `${field} as ${field}`))
} else {
query = query.select("*")
}
// handle where
query = addFilters(tableName, query, filters)
// handle join
query = addRelationships(query, tableName, relationships)
// handle sorting
if (sort) {
for (let [key, value] of Object.entries(sort)) {
const direction = value === SortDirection.ASCENDING ? "asc" : "desc"
query = query.orderBy(key, direction)
}
selectStatement = resource.fields.map(field => `${field} as ${field}`)
}
let foundLimit = limit || BASE_LIMIT
// handle pagination
let foundOffset: number | null = null
if (paginate && paginate.page && paginate.limit) {
// @ts-ignore
const page = paginate.page <= 1 ? 0 : paginate.page - 1
const offset = page * paginate.limit
foundLimit = paginate.limit
query = query.offset(offset)
foundOffset = offset
} else if (paginate && paginate.limit) {
foundLimit = paginate.limit
}
if (foundLimit === 1) {
foundLimit = SINGLE_ROW_LIMIT
// start building the query
let query: KnexQuery = knex(tableName).limit(foundLimit)
if (foundOffset) {
query = query.offset(foundOffset)
}
query = query.limit(foundLimit)
return query
if (sort) {
for (let [key, value] of Object.entries(sort)) {
const direction = value === SortDirection.ASCENDING ? "asc" : "desc"
query = query.orderBy(key, direction)
}
}
query = addFilters(tableName, query, filters)
// @ts-ignore
let preQuery: KnexQuery = knex({
// @ts-ignore
[tableName]: query,
}).select(selectStatement)
// handle joins
return addRelationships(preQuery, tableName, relationships)
}
function buildUpdate(

21
packages/server/src/integrations/mysql.ts

@ -104,7 +104,7 @@ module MySQLModule {
client: any,
query: SqlQuery,
connect: boolean = true
): Promise<any[]> {
): Promise<any[]|any> {
// Node MySQL is callback based, so we must wrap our call in a promise
return new Promise((resolve, reject) => {
if (connect) {
@ -238,6 +238,23 @@ module MySQLModule {
return internalQuery(this.client, input, false)
}
// when creating if an ID has been inserted need to make sure
// the id filter is enriched with it before trying to retrieve the row
checkLookupKeys(results: any, json: QueryJson) {
if (!results?.insertId || !json.meta?.table || !json.meta.table.primary) {
return json
}
const primaryKey = json.meta.table.primary?.[0]
json.extra = {
idFilter: {
equal: {
[primaryKey]: results.insertId
},
}
}
return json
}
async query(json: QueryJson) {
const operation = this._operation(json)
this.client.connect()
@ -250,7 +267,7 @@ module MySQLModule {
const results = await internalQuery(this.client, input, false)
// same as delete, manage returning
if (operation === Operation.CREATE || operation === Operation.UPDATE) {
row = this.getReturningRow(json)
row = this.getReturningRow(this.checkLookupKeys(results, json))
}
this.client.end()
if (operation !== Operation.READ) {

Loading…
Cancel
Save