1
0
Fork 0
mirror of synced 2024-09-25 13:51:40 +12:00

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:
mike12345567 2024-09-24 12:01:47 +01:00
parent 6a7959e93c
commit 464f973f12
5 changed files with 44 additions and 14 deletions

View file

@ -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(

View file

@ -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.

View file

@ -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"

View file

@ -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() {

View file

@ -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",
} }