Adding a separation for MariaDB and MySQL, mariaDB is the core of the problem, this solves for it by separating them and allowing us to use the special json_arrayagg for mariaDB, but use a correlated sub-query for MySQL.
This commit is contained in:
parent
6a7959e93c
commit
464f973f12
|
@ -150,6 +150,7 @@ class InternalBuilder {
|
||||||
return `"${str}"`
|
return `"${str}"`
|
||||||
case SqlClient.MS_SQL:
|
case SqlClient.MS_SQL:
|
||||||
return `[${str}]`
|
return `[${str}]`
|
||||||
|
case SqlClient.MARIADB:
|
||||||
case SqlClient.MY_SQL:
|
case SqlClient.MY_SQL:
|
||||||
return `\`${str}\``
|
return `\`${str}\``
|
||||||
}
|
}
|
||||||
|
@ -559,7 +560,10 @@ class InternalBuilder {
|
||||||
)}${wrap}, FALSE)`
|
)}${wrap}, FALSE)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else if (this.client === SqlClient.MY_SQL) {
|
} else if (
|
||||||
|
this.client === SqlClient.MY_SQL ||
|
||||||
|
this.client === SqlClient.MARIADB
|
||||||
|
) {
|
||||||
const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS"
|
const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS"
|
||||||
iterate(mode, (q, key, value) => {
|
iterate(mode, (q, key, value) => {
|
||||||
return q[rawFnc](
|
return q[rawFnc](
|
||||||
|
@ -1007,7 +1011,7 @@ class InternalBuilder {
|
||||||
`json_agg(json_build_object(${fieldList}))`
|
`json_agg(json_build_object(${fieldList}))`
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
case SqlClient.MY_SQL:
|
case SqlClient.MARIADB:
|
||||||
// can't use the standard wrap due to correlated sub-query limitations in MariaDB
|
// can't use the standard wrap due to correlated sub-query limitations in MariaDB
|
||||||
wrapperQuery = subQuery.select(
|
wrapperQuery = subQuery.select(
|
||||||
knex.raw(
|
knex.raw(
|
||||||
|
@ -1015,6 +1019,7 @@ class InternalBuilder {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
case SqlClient.MY_SQL:
|
||||||
case SqlClient.ORACLE:
|
case SqlClient.ORACLE:
|
||||||
wrapperQuery = standardWrap(
|
wrapperQuery = standardWrap(
|
||||||
`json_arrayagg(json_object(${fieldList}))`
|
`json_arrayagg(json_object(${fieldList}))`
|
||||||
|
@ -1181,7 +1186,8 @@ class InternalBuilder {
|
||||||
if (
|
if (
|
||||||
this.client === SqlClient.POSTGRES ||
|
this.client === SqlClient.POSTGRES ||
|
||||||
this.client === SqlClient.SQL_LITE ||
|
this.client === SqlClient.SQL_LITE ||
|
||||||
this.client === SqlClient.MY_SQL
|
this.client === SqlClient.MY_SQL ||
|
||||||
|
this.client === SqlClient.MARIADB
|
||||||
) {
|
) {
|
||||||
const primary = this.table.primary
|
const primary = this.table.primary
|
||||||
if (!primary) {
|
if (!primary) {
|
||||||
|
@ -1328,12 +1334,11 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
||||||
_query(json: QueryJson, opts: QueryOptions = {}): SqlQuery | SqlQuery[] {
|
_query(json: QueryJson, opts: QueryOptions = {}): SqlQuery | SqlQuery[] {
|
||||||
const sqlClient = this.getSqlClient()
|
const sqlClient = this.getSqlClient()
|
||||||
const config: Knex.Config = {
|
const config: Knex.Config = {
|
||||||
client: sqlClient,
|
client: this.getBaseSqlClient(),
|
||||||
}
|
}
|
||||||
if (sqlClient === SqlClient.SQL_LITE || sqlClient === SqlClient.ORACLE) {
|
if (sqlClient === SqlClient.SQL_LITE || sqlClient === SqlClient.ORACLE) {
|
||||||
config.useNullAsDefault = true
|
config.useNullAsDefault = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = knex(config)
|
const client = knex(config)
|
||||||
let query: Knex.QueryBuilder
|
let query: Knex.QueryBuilder
|
||||||
const builder = new InternalBuilder(sqlClient, client, json)
|
const builder = new InternalBuilder(sqlClient, client, json)
|
||||||
|
@ -1442,7 +1447,10 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
||||||
let id
|
let id
|
||||||
if (sqlClient === SqlClient.MS_SQL) {
|
if (sqlClient === SqlClient.MS_SQL) {
|
||||||
id = results?.[0].id
|
id = results?.[0].id
|
||||||
} else if (sqlClient === SqlClient.MY_SQL) {
|
} else if (
|
||||||
|
sqlClient === SqlClient.MY_SQL ||
|
||||||
|
sqlClient === SqlClient.MARIADB
|
||||||
|
) {
|
||||||
id = results?.insertId
|
id = results?.insertId
|
||||||
}
|
}
|
||||||
row = processFn(
|
row = processFn(
|
||||||
|
|
|
@ -210,16 +210,27 @@ function buildDeleteTable(knex: SchemaBuilder, table: Table): SchemaBuilder {
|
||||||
|
|
||||||
class SqlTableQueryBuilder {
|
class SqlTableQueryBuilder {
|
||||||
private readonly sqlClient: SqlClient
|
private readonly sqlClient: SqlClient
|
||||||
|
private extendedSqlClient: SqlClient | undefined
|
||||||
|
|
||||||
// pass through client to get flavour of SQL
|
// pass through client to get flavour of SQL
|
||||||
constructor(client: SqlClient) {
|
constructor(client: SqlClient) {
|
||||||
this.sqlClient = client
|
this.sqlClient = client
|
||||||
}
|
}
|
||||||
|
|
||||||
getSqlClient(): SqlClient {
|
getBaseSqlClient(): SqlClient {
|
||||||
return this.sqlClient
|
return this.sqlClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSqlClient(): SqlClient {
|
||||||
|
return this.extendedSqlClient || this.sqlClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// if working in a database like MySQL with many variants (MariaDB)
|
||||||
|
// we can set another client which overrides the base one
|
||||||
|
setExtendedSqlClient(client: SqlClient) {
|
||||||
|
this.extendedSqlClient = client
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param json the input JSON structure from which an SQL query will be built.
|
* @param json the input JSON structure from which an SQL query will be built.
|
||||||
* @return the operation that was found in the JSON.
|
* @return the operation that was found in the JSON.
|
||||||
|
|
|
@ -45,14 +45,14 @@ import { generateRowIdField } from "../../../integrations/utils"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
// ["in-memory", undefined],
|
["in-memory", undefined],
|
||||||
// ["lucene", undefined],
|
["lucene", undefined],
|
||||||
// ["sqs", undefined],
|
["sqs", undefined],
|
||||||
// [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
[DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
||||||
// [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
[DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
||||||
// [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
[DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
||||||
[DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)],
|
[DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)],
|
||||||
// [DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)],
|
[DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)],
|
||||||
])("search (%s)", (name, dsProvider) => {
|
])("search (%s)", (name, dsProvider) => {
|
||||||
const isSqs = name === "sqs"
|
const isSqs = name === "sqs"
|
||||||
const isLucene = name === "lucene"
|
const isLucene = name === "lucene"
|
||||||
|
|
|
@ -241,6 +241,16 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
this.client = await mysql.createConnection(this.config)
|
this.client = await mysql.createConnection(this.config)
|
||||||
|
const res = await this.internalQuery(
|
||||||
|
{
|
||||||
|
sql: "SELECT VERSION();",
|
||||||
|
},
|
||||||
|
{ connect: false }
|
||||||
|
)
|
||||||
|
const version = res?.[0]?.["VERSION()"]
|
||||||
|
if (version?.toLowerCase().includes("mariadb")) {
|
||||||
|
this.setExtendedSqlClient(SqlClient.MARIADB)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async disconnect() {
|
async disconnect() {
|
||||||
|
|
|
@ -195,6 +195,7 @@ export enum SqlClient {
|
||||||
MS_SQL = "mssql",
|
MS_SQL = "mssql",
|
||||||
POSTGRES = "pg",
|
POSTGRES = "pg",
|
||||||
MY_SQL = "mysql2",
|
MY_SQL = "mysql2",
|
||||||
|
MARIADB = "mariadb",
|
||||||
ORACLE = "oracledb",
|
ORACLE = "oracledb",
|
||||||
SQL_LITE = "sqlite3",
|
SQL_LITE = "sqlite3",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue