Gone some way toward getting time-only fields to work. Still test failures though.
This commit is contained in:
parent
5cb294f33e
commit
ad414b982e
|
@ -42,176 +42,6 @@ const envLimit = environment.SQL_MAX_ROWS
|
||||||
: null
|
: null
|
||||||
const BASE_LIMIT = envLimit || 5000
|
const BASE_LIMIT = envLimit || 5000
|
||||||
|
|
||||||
// Takes a string like foo and returns a quoted string like [foo] for SQL Server
|
|
||||||
// and "foo" for Postgres.
|
|
||||||
function quote(client: SqlClient, str: string): string {
|
|
||||||
switch (client) {
|
|
||||||
case SqlClient.SQL_LITE:
|
|
||||||
case SqlClient.ORACLE:
|
|
||||||
case SqlClient.POSTGRES:
|
|
||||||
return `"${str}"`
|
|
||||||
case SqlClient.MS_SQL:
|
|
||||||
return `[${str}]`
|
|
||||||
case SqlClient.MY_SQL:
|
|
||||||
return `\`${str}\``
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes a string like a.b.c and returns a quoted identifier like [a].[b].[c]
|
|
||||||
// for SQL Server and `a`.`b`.`c` for MySQL.
|
|
||||||
function quotedIdentifier(client: SqlClient, key: string): string {
|
|
||||||
return key
|
|
||||||
.split(".")
|
|
||||||
.map(part => quote(client, part))
|
|
||||||
.join(".")
|
|
||||||
}
|
|
||||||
|
|
||||||
function parse(input: any) {
|
|
||||||
if (Array.isArray(input)) {
|
|
||||||
return JSON.stringify(input)
|
|
||||||
}
|
|
||||||
if (input == undefined) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (typeof input !== "string") {
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
if (isInvalidISODateString(input)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (isValidISODateString(input)) {
|
|
||||||
return new Date(input.trim())
|
|
||||||
}
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseBody(body: any) {
|
|
||||||
for (let [key, value] of Object.entries(body)) {
|
|
||||||
body[key] = parse(value)
|
|
||||||
}
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseFilters(filters: SearchFilters | undefined): SearchFilters {
|
|
||||||
if (!filters) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
for (let [key, value] of Object.entries(filters)) {
|
|
||||||
let parsed
|
|
||||||
if (typeof value === "object") {
|
|
||||||
parsed = parseFilters(value)
|
|
||||||
} else {
|
|
||||||
parsed = parse(value)
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
|
||||||
filters[key] = parsed
|
|
||||||
}
|
|
||||||
return filters
|
|
||||||
}
|
|
||||||
|
|
||||||
// OracleDB can't use character-large-objects (CLOBs) in WHERE clauses,
|
|
||||||
// so when we use them we need to wrap them in to_char(). This function
|
|
||||||
// converts a field name to the appropriate identifier.
|
|
||||||
function convertClobs(client: SqlClient, table: Table, field: string): string {
|
|
||||||
const parts = field.split(".")
|
|
||||||
const col = parts.pop()!
|
|
||||||
const schema = table.schema[col]
|
|
||||||
let identifier = quotedIdentifier(client, field)
|
|
||||||
if (
|
|
||||||
schema.type === FieldType.STRING ||
|
|
||||||
schema.type === FieldType.LONGFORM ||
|
|
||||||
schema.type === FieldType.BB_REFERENCE_SINGLE ||
|
|
||||||
schema.type === FieldType.OPTIONS ||
|
|
||||||
schema.type === FieldType.BARCODEQR
|
|
||||||
) {
|
|
||||||
identifier = `to_char(${identifier})`
|
|
||||||
}
|
|
||||||
return identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateSelectStatement(
|
|
||||||
json: QueryJson,
|
|
||||||
knex: Knex
|
|
||||||
): (string | Knex.Raw)[] | "*" {
|
|
||||||
const { resource, meta } = json
|
|
||||||
const client = knex.client.config.client as SqlClient
|
|
||||||
|
|
||||||
if (!resource || !resource.fields || resource.fields.length === 0) {
|
|
||||||
return "*"
|
|
||||||
}
|
|
||||||
|
|
||||||
const schema = meta.table.schema
|
|
||||||
return resource.fields.map(field => {
|
|
||||||
const parts = field.split(/\./g)
|
|
||||||
let table: string | undefined = undefined
|
|
||||||
let column: string | undefined = undefined
|
|
||||||
|
|
||||||
// Just a column name, e.g.: "column"
|
|
||||||
if (parts.length === 1) {
|
|
||||||
column = parts[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// A table name and a column name, e.g.: "table.column"
|
|
||||||
if (parts.length === 2) {
|
|
||||||
table = parts[0]
|
|
||||||
column = parts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// A link doc, e.g.: "table.doc1.fieldName"
|
|
||||||
if (parts.length > 2) {
|
|
||||||
table = parts[0]
|
|
||||||
column = parts.slice(1).join(".")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!column) {
|
|
||||||
throw new Error(`Invalid field name: ${field}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const columnSchema = schema[column]
|
|
||||||
|
|
||||||
if (
|
|
||||||
client === SqlClient.POSTGRES &&
|
|
||||||
columnSchema?.externalType?.includes("money")
|
|
||||||
) {
|
|
||||||
return knex.raw(
|
|
||||||
`${quotedIdentifier(
|
|
||||||
client,
|
|
||||||
[table, column].join(".")
|
|
||||||
)}::money::numeric as ${quote(client, field)}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
client === SqlClient.MS_SQL &&
|
|
||||||
columnSchema?.type === FieldType.DATETIME &&
|
|
||||||
columnSchema.timeOnly
|
|
||||||
) {
|
|
||||||
// Time gets returned as timestamp from mssql, not matching the expected
|
|
||||||
// HH:mm format
|
|
||||||
return knex.raw(`CONVERT(varchar, ${field}, 108) as "${field}"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's at least two edge cases being handled in the expression below.
|
|
||||||
// 1. The column name could start/end with a space, and in that case we
|
|
||||||
// want to preseve that space.
|
|
||||||
// 2. Almost all column names are specified in the form table.column, except
|
|
||||||
// in the case of relationships, where it's table.doc1.column. In that
|
|
||||||
// case, we want to split it into `table`.`doc1.column` for reasons that
|
|
||||||
// aren't actually clear to me, but `table`.`doc1` breaks things with the
|
|
||||||
// sample data tests.
|
|
||||||
if (table) {
|
|
||||||
return knex.raw(
|
|
||||||
`${quote(client, table)}.${quote(client, column)} as ${quote(
|
|
||||||
client,
|
|
||||||
field
|
|
||||||
)}`
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return knex.raw(`${quote(client, field)} as ${quote(client, field)}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTableName(table?: Table): string | undefined {
|
function getTableName(table?: Table): string | undefined {
|
||||||
// SQS uses the table ID rather than the table name
|
// SQS uses the table ID rather than the table name
|
||||||
if (
|
if (
|
||||||
|
@ -247,6 +77,181 @@ class InternalBuilder {
|
||||||
this.client = client
|
this.client = client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Takes a string like foo and returns a quoted string like [foo] for SQL Server
|
||||||
|
// and "foo" for Postgres.
|
||||||
|
private quote(str: string): string {
|
||||||
|
switch (this.client) {
|
||||||
|
case SqlClient.SQL_LITE:
|
||||||
|
case SqlClient.ORACLE:
|
||||||
|
case SqlClient.POSTGRES:
|
||||||
|
return `"${str}"`
|
||||||
|
case SqlClient.MS_SQL:
|
||||||
|
return `[${str}]`
|
||||||
|
case SqlClient.MY_SQL:
|
||||||
|
return `\`${str}\``
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes a string like a.b.c and returns a quoted identifier like [a].[b].[c]
|
||||||
|
// for SQL Server and `a`.`b`.`c` for MySQL.
|
||||||
|
private quotedIdentifier(key: string): string {
|
||||||
|
return key
|
||||||
|
.split(".")
|
||||||
|
.map(part => this.quote(part))
|
||||||
|
.join(".")
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateSelectStatement(
|
||||||
|
json: QueryJson,
|
||||||
|
knex: Knex
|
||||||
|
): (string | Knex.Raw)[] | "*" {
|
||||||
|
const { resource, meta } = json
|
||||||
|
const client = knex.client.config.client as SqlClient
|
||||||
|
|
||||||
|
if (!resource || !resource.fields || resource.fields.length === 0) {
|
||||||
|
return "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = meta.table.schema
|
||||||
|
return resource.fields.map(field => {
|
||||||
|
const parts = field.split(/\./g)
|
||||||
|
let table: string | undefined = undefined
|
||||||
|
let column: string | undefined = undefined
|
||||||
|
|
||||||
|
// Just a column name, e.g.: "column"
|
||||||
|
if (parts.length === 1) {
|
||||||
|
column = parts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// A table name and a column name, e.g.: "table.column"
|
||||||
|
if (parts.length === 2) {
|
||||||
|
table = parts[0]
|
||||||
|
column = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// A link doc, e.g.: "table.doc1.fieldName"
|
||||||
|
if (parts.length > 2) {
|
||||||
|
table = parts[0]
|
||||||
|
column = parts.slice(1).join(".")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!column) {
|
||||||
|
throw new Error(`Invalid field name: ${field}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnSchema = schema[column]
|
||||||
|
|
||||||
|
if (
|
||||||
|
client === SqlClient.POSTGRES &&
|
||||||
|
columnSchema?.externalType?.includes("money")
|
||||||
|
) {
|
||||||
|
return knex.raw(
|
||||||
|
`${this.quotedIdentifier(
|
||||||
|
[table, column].join(".")
|
||||||
|
)}::money::numeric as ${this.quote(field)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
client === SqlClient.MS_SQL &&
|
||||||
|
columnSchema?.type === FieldType.DATETIME &&
|
||||||
|
columnSchema.timeOnly
|
||||||
|
) {
|
||||||
|
// Time gets returned as timestamp from mssql, not matching the expected
|
||||||
|
// HH:mm format
|
||||||
|
return knex.raw(`CONVERT(varchar, ${field}, 108) as "${field}"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's at least two edge cases being handled in the expression below.
|
||||||
|
// 1. The column name could start/end with a space, and in that case we
|
||||||
|
// want to preseve that space.
|
||||||
|
// 2. Almost all column names are specified in the form table.column, except
|
||||||
|
// in the case of relationships, where it's table.doc1.column. In that
|
||||||
|
// case, we want to split it into `table`.`doc1.column` for reasons that
|
||||||
|
// aren't actually clear to me, but `table`.`doc1` breaks things with the
|
||||||
|
// sample data tests.
|
||||||
|
if (table) {
|
||||||
|
return knex.raw(
|
||||||
|
`${this.quote(table)}.${this.quote(column)} as ${this.quote(field)}`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return knex.raw(`${this.quote(field)} as ${this.quote(field)}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// OracleDB can't use character-large-objects (CLOBs) in WHERE clauses,
|
||||||
|
// so when we use them we need to wrap them in to_char(). This function
|
||||||
|
// converts a field name to the appropriate identifier.
|
||||||
|
private convertClobs(table: Table, field: string): string {
|
||||||
|
const parts = field.split(".")
|
||||||
|
const col = parts.pop()!
|
||||||
|
const schema = table.schema[col]
|
||||||
|
let identifier = this.quotedIdentifier(field)
|
||||||
|
if (
|
||||||
|
schema.type === FieldType.STRING ||
|
||||||
|
schema.type === FieldType.LONGFORM ||
|
||||||
|
schema.type === FieldType.BB_REFERENCE_SINGLE ||
|
||||||
|
schema.type === FieldType.OPTIONS ||
|
||||||
|
schema.type === FieldType.BARCODEQR
|
||||||
|
) {
|
||||||
|
identifier = `to_char(${identifier})`
|
||||||
|
}
|
||||||
|
return identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
private parse(input: any, schema: FieldSchema) {
|
||||||
|
if (schema.type === FieldType.DATETIME && schema.timeOnly) {
|
||||||
|
if (this.client === SqlClient.ORACLE) {
|
||||||
|
return new Date(`1970-01-01 ${input}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(input)) {
|
||||||
|
return JSON.stringify(input)
|
||||||
|
}
|
||||||
|
if (input == undefined) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (typeof input !== "string") {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
if (isInvalidISODateString(input)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (isValidISODateString(input)) {
|
||||||
|
return new Date(input.trim())
|
||||||
|
}
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseBody(body: any, table: Table) {
|
||||||
|
for (let [key, value] of Object.entries(body)) {
|
||||||
|
body[key] = this.parse(value, table.schema[key])
|
||||||
|
}
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseFilters(
|
||||||
|
filters: SearchFilters | undefined,
|
||||||
|
table: Table
|
||||||
|
): SearchFilters {
|
||||||
|
if (!filters) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
for (let [key, value] of Object.entries(filters)) {
|
||||||
|
let parsed
|
||||||
|
if (typeof value === "object") {
|
||||||
|
parsed = this.parseFilters(value, table)
|
||||||
|
} else {
|
||||||
|
parsed = this.parse(value, table.schema[key])
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
filters[key] = parsed
|
||||||
|
}
|
||||||
|
return filters
|
||||||
|
}
|
||||||
|
|
||||||
// right now we only do filters on the specific table being queried
|
// right now we only do filters on the specific table being queried
|
||||||
addFilters(
|
addFilters(
|
||||||
query: Knex.QueryBuilder,
|
query: Knex.QueryBuilder,
|
||||||
|
@ -261,7 +266,7 @@ class InternalBuilder {
|
||||||
if (!filters) {
|
if (!filters) {
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
filters = parseFilters(filters)
|
filters = this.parseFilters(filters, table)
|
||||||
// if all or specified in filters, then everything is an or
|
// if all or specified in filters, then everything is an or
|
||||||
const allOr = filters.allOr
|
const allOr = filters.allOr
|
||||||
const sqlStatements = new SqlStatements(this.client, table, {
|
const sqlStatements = new SqlStatements(this.client, table, {
|
||||||
|
@ -318,10 +323,9 @@ class InternalBuilder {
|
||||||
} else {
|
} else {
|
||||||
const rawFnc = `${fnc}Raw`
|
const rawFnc = `${fnc}Raw`
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
query = query[rawFnc](
|
query = query[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
|
||||||
`LOWER(${quotedIdentifier(this.client, key)}) LIKE ?`,
|
`%${value.toLowerCase()}%`,
|
||||||
[`%${value.toLowerCase()}%`]
|
])
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,10 +375,7 @@ class InternalBuilder {
|
||||||
}
|
}
|
||||||
statement +=
|
statement +=
|
||||||
(statement ? andOr : "") +
|
(statement ? andOr : "") +
|
||||||
`COALESCE(LOWER(${quotedIdentifier(
|
`COALESCE(LOWER(${this.quotedIdentifier(key)}), '') LIKE ?`
|
||||||
this.client,
|
|
||||||
key
|
|
||||||
)}), '') LIKE ?`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statement === "") {
|
if (statement === "") {
|
||||||
|
@ -393,7 +394,7 @@ class InternalBuilder {
|
||||||
filters.oneOf,
|
filters.oneOf,
|
||||||
(key: string, array) => {
|
(key: string, array) => {
|
||||||
if (this.client === SqlClient.ORACLE) {
|
if (this.client === SqlClient.ORACLE) {
|
||||||
key = convertClobs(this.client, table, key)
|
key = this.convertClobs(table, key)
|
||||||
array = Array.isArray(array) ? array : [array]
|
array = Array.isArray(array) ? array : [array]
|
||||||
const binding = new Array(array.length).fill("?").join(",")
|
const binding = new Array(array.length).fill("?").join(",")
|
||||||
query = query.whereRaw(`${key} IN (${binding})`, array)
|
query = query.whereRaw(`${key} IN (${binding})`, array)
|
||||||
|
@ -415,10 +416,9 @@ class InternalBuilder {
|
||||||
} else {
|
} else {
|
||||||
const rawFnc = `${fnc}Raw`
|
const rawFnc = `${fnc}Raw`
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
query = query[rawFnc](
|
query = query[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
|
||||||
`LOWER(${quotedIdentifier(this.client, key)}) LIKE ?`,
|
`${value.toLowerCase()}%`,
|
||||||
[`${value.toLowerCase()}%`]
|
])
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -456,21 +456,18 @@ class InternalBuilder {
|
||||||
const fnc = allOr ? "orWhereRaw" : "whereRaw"
|
const fnc = allOr ? "orWhereRaw" : "whereRaw"
|
||||||
if (this.client === SqlClient.MS_SQL) {
|
if (this.client === SqlClient.MS_SQL) {
|
||||||
query = query[fnc](
|
query = query[fnc](
|
||||||
`CASE WHEN ${quotedIdentifier(
|
`CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 1`,
|
||||||
this.client,
|
|
||||||
key
|
|
||||||
)} = ? THEN 1 ELSE 0 END = 1`,
|
|
||||||
[value]
|
[value]
|
||||||
)
|
)
|
||||||
} else if (this.client === SqlClient.ORACLE) {
|
} else if (this.client === SqlClient.ORACLE) {
|
||||||
const identifier = convertClobs(this.client, table, key)
|
const identifier = this.convertClobs(table, key)
|
||||||
query = query[fnc](
|
query = query[fnc](
|
||||||
`(${identifier} IS NOT NULL AND ${identifier} = ?)`,
|
`(${identifier} IS NOT NULL AND ${identifier} = ?)`,
|
||||||
[value]
|
[value]
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
query = query[fnc](
|
query = query[fnc](
|
||||||
`COALESCE(${quotedIdentifier(this.client, key)} = ?, FALSE)`,
|
`COALESCE(${this.quotedIdentifier(key)} = ?, FALSE)`,
|
||||||
[value]
|
[value]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -481,21 +478,18 @@ class InternalBuilder {
|
||||||
const fnc = allOr ? "orWhereRaw" : "whereRaw"
|
const fnc = allOr ? "orWhereRaw" : "whereRaw"
|
||||||
if (this.client === SqlClient.MS_SQL) {
|
if (this.client === SqlClient.MS_SQL) {
|
||||||
query = query[fnc](
|
query = query[fnc](
|
||||||
`CASE WHEN ${quotedIdentifier(
|
`CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 0`,
|
||||||
this.client,
|
|
||||||
key
|
|
||||||
)} = ? THEN 1 ELSE 0 END = 0`,
|
|
||||||
[value]
|
[value]
|
||||||
)
|
)
|
||||||
} else if (this.client === SqlClient.ORACLE) {
|
} else if (this.client === SqlClient.ORACLE) {
|
||||||
const identifier = convertClobs(this.client, table, key)
|
const identifier = this.convertClobs(table, key)
|
||||||
query = query[fnc](
|
query = query[fnc](
|
||||||
`(${identifier} IS NOT NULL AND ${identifier} != ?)`,
|
`(${identifier} IS NOT NULL AND ${identifier} != ?)`,
|
||||||
[value]
|
[value]
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
query = query[fnc](
|
query = query[fnc](
|
||||||
`COALESCE(${quotedIdentifier(this.client, key)} != ?, TRUE)`,
|
`COALESCE(${this.quotedIdentifier(key)} != ?, TRUE)`,
|
||||||
[value]
|
[value]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -692,7 +686,7 @@ class InternalBuilder {
|
||||||
create(knex: Knex, json: QueryJson, opts: QueryOptions): Knex.QueryBuilder {
|
create(knex: Knex, json: QueryJson, opts: QueryOptions): Knex.QueryBuilder {
|
||||||
const { endpoint, body } = json
|
const { endpoint, body } = json
|
||||||
let query = this.knexWithAlias(knex, endpoint)
|
let query = this.knexWithAlias(knex, endpoint)
|
||||||
const parsedBody = parseBody(body)
|
const parsedBody = this.parseBody(body, json.meta.table)
|
||||||
// make sure no null values in body for creation
|
// make sure no null values in body for creation
|
||||||
for (let [key, value] of Object.entries(parsedBody)) {
|
for (let [key, value] of Object.entries(parsedBody)) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
@ -714,7 +708,7 @@ class InternalBuilder {
|
||||||
if (!Array.isArray(body)) {
|
if (!Array.isArray(body)) {
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
const parsedBody = body.map(row => parseBody(row))
|
const parsedBody = body.map(row => this.parseBody(row, json.meta.table))
|
||||||
return query.insert(parsedBody)
|
return query.insert(parsedBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,7 +718,7 @@ class InternalBuilder {
|
||||||
if (!Array.isArray(body)) {
|
if (!Array.isArray(body)) {
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
const parsedBody = body.map(row => parseBody(row))
|
const parsedBody = body.map(row => this.parseBody(row, json.meta.table))
|
||||||
if (
|
if (
|
||||||
this.client === SqlClient.POSTGRES ||
|
this.client === SqlClient.POSTGRES ||
|
||||||
this.client === SqlClient.SQL_LITE ||
|
this.client === SqlClient.SQL_LITE ||
|
||||||
|
@ -806,7 +800,7 @@ class InternalBuilder {
|
||||||
})
|
})
|
||||||
// if counting, use distinct count, else select
|
// if counting, use distinct count, else select
|
||||||
preQuery = !counting
|
preQuery = !counting
|
||||||
? preQuery.select(generateSelectStatement(json, knex))
|
? preQuery.select(this.generateSelectStatement(json, knex))
|
||||||
: this.addDistinctCount(preQuery, json)
|
: this.addDistinctCount(preQuery, json)
|
||||||
// have to add after as well (this breaks MS-SQL)
|
// have to add after as well (this breaks MS-SQL)
|
||||||
if (this.client !== SqlClient.MS_SQL && !counting) {
|
if (this.client !== SqlClient.MS_SQL && !counting) {
|
||||||
|
@ -837,7 +831,7 @@ class InternalBuilder {
|
||||||
update(knex: Knex, json: QueryJson, opts: QueryOptions): Knex.QueryBuilder {
|
update(knex: Knex, json: QueryJson, opts: QueryOptions): Knex.QueryBuilder {
|
||||||
const { endpoint, body, filters, tableAliases } = json
|
const { endpoint, body, filters, tableAliases } = json
|
||||||
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
||||||
const parsedBody = parseBody(body)
|
const parsedBody = this.parseBody(body, json.meta.table)
|
||||||
query = this.addFilters(query, filters, json.meta.table, {
|
query = this.addFilters(query, filters, json.meta.table, {
|
||||||
columnPrefix: json.meta.columnPrefix,
|
columnPrefix: json.meta.columnPrefix,
|
||||||
aliases: tableAliases,
|
aliases: tableAliases,
|
||||||
|
@ -861,7 +855,7 @@ class InternalBuilder {
|
||||||
if (opts.disableReturning) {
|
if (opts.disableReturning) {
|
||||||
return query.delete()
|
return query.delete()
|
||||||
} else {
|
} else {
|
||||||
return query.delete().returning(generateSelectStatement(json, knex))
|
return query.delete().returning(this.generateSelectStatement(json, knex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1318,7 +1318,7 @@ describe.each([
|
||||||
})
|
})
|
||||||
|
|
||||||
!isInternal &&
|
!isInternal &&
|
||||||
describe("datetime - time only", () => {
|
describe.only("datetime - time only", () => {
|
||||||
const T_1000 = "10:00:00"
|
const T_1000 = "10:00:00"
|
||||||
const T_1045 = "10:45:00"
|
const T_1045 = "10:45:00"
|
||||||
const T_1200 = "12:00:00"
|
const T_1200 = "12:00:00"
|
||||||
|
|
|
@ -398,7 +398,6 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getConnection = async (): Promise<Connection> => {
|
private getConnection = async (): Promise<Connection> => {
|
||||||
//connectString : "(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))(CONNECT_DATA =(SID= ORCL)))"
|
|
||||||
const connectString = `${this.config.host}:${this.config.port || 1521}/${
|
const connectString = `${this.config.host}:${this.config.port || 1521}/${
|
||||||
this.config.database
|
this.config.database
|
||||||
}`
|
}`
|
||||||
|
|
|
@ -315,6 +315,15 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
column.subtype
|
column.subtype
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (column.type === FieldType.DATETIME && column.timeOnly) {
|
||||||
|
for (let row of enriched) {
|
||||||
|
if (row[property] instanceof Date) {
|
||||||
|
const hours = row[property].getHours().toString().padStart(2, "0")
|
||||||
|
const minutes = row[property].getMinutes().toString().padStart(2, "0")
|
||||||
|
const seconds = row[property].getSeconds().toString().padStart(2, "0")
|
||||||
|
row[property] = `${hours}:${minutes}:${seconds}`
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue