diff --git a/packages/server/src/integrations/base/datasourcePlus.ts b/packages/server/src/integrations/base/datasourcePlus.ts index 32edbc06a2..f55dcf0f9a 100644 --- a/packages/server/src/integrations/base/datasourcePlus.ts +++ b/packages/server/src/integrations/base/datasourcePlus.ts @@ -8,5 +8,6 @@ export interface DatasourcePlus extends IntegrationBase { // if the datasource supports the use of bindings directly (to protect against SQL injection) // this returns the format of the identifier getBindingIdentifier(): string + getStringConcat(parts: string[]): string buildSchema(datasourceId: string, entities: Record): any } diff --git a/packages/server/src/integrations/googlesheets.ts b/packages/server/src/integrations/googlesheets.ts index 9f2f6bedf7..bb2de26f5d 100644 --- a/packages/server/src/integrations/googlesheets.ts +++ b/packages/server/src/integrations/googlesheets.ts @@ -115,6 +115,10 @@ module GoogleSheetsModule { return "" } + getStringConcat(parts: string[]) { + return "" + } + /** * Pull the spreadsheet ID out from a valid google sheets URL * @param spreadsheetId - the URL or standard spreadsheetId of the google sheet diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index aa06c47083..1b37b5df9a 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -126,7 +126,11 @@ module MSSQLModule { } getBindingIdentifier(): string { - return `(@p${this.index++})` + return `@p${this.index++}` + } + + getStringConcat(parts: string[]): string { + return `concat(${parts.join(", ")})` } async connect() { diff --git a/packages/server/src/integrations/mysql.ts b/packages/server/src/integrations/mysql.ts index 42b53bc603..8b2c9ac944 100644 --- a/packages/server/src/integrations/mysql.ts +++ b/packages/server/src/integrations/mysql.ts @@ -99,6 +99,10 @@ module MySQLModule { return "?" } + getStringConcat(parts: string[]): string { + return `concat(${parts.join(", ")})` + } + async connect() { this.client = await mysql.createConnection(this.config) } diff --git a/packages/server/src/integrations/oracle.ts b/packages/server/src/integrations/oracle.ts index bb4ef70403..7cb7ba88cf 100644 --- a/packages/server/src/integrations/oracle.ts +++ b/packages/server/src/integrations/oracle.ts @@ -179,6 +179,10 @@ module OracleModule { return `:${this.index++}` } + getStringConcat(parts: string[]): string { + return parts.join(" || ") + } + /** * Map the flat tabular columns and constraints data into a nested object */ diff --git a/packages/server/src/integrations/postgres.ts b/packages/server/src/integrations/postgres.ts index e86cd89c03..1dc6fd9d2d 100644 --- a/packages/server/src/integrations/postgres.ts +++ b/packages/server/src/integrations/postgres.ts @@ -148,6 +148,10 @@ module PostgresModule { return `$${this.index++}` } + getStringConcat(parts: string[]): string { + return parts.join(" || ") + } + async internalQuery(query: SqlQuery) { const client = this.client this.index = 1 diff --git a/packages/server/src/threads/query.js b/packages/server/src/threads/query.js index 36bc3d7f36..c547a10c74 100644 --- a/packages/server/src/threads/query.js +++ b/packages/server/src/threads/query.js @@ -37,7 +37,23 @@ class QueryRunner { for (let binding of bindings) { let variable = integration.getBindingIdentifier() variables.push(binding) - sql = sql.replace(binding, variable) + // check if the variable was used as part of a string concat e.g. 'Hello {{binding}}' + const charConstRegex = new RegExp(`'[^']*${binding}[^']*'`) + const charConstMatch = sql.match(charConstRegex) + if (charConstMatch) { + let [part1, part2] = charConstMatch[0].split(binding) + part1 = `'${part1.substring(1)}'` + part2 = `'${part2.substring(0, part2.length - 1)}'` + sql = sql.replace( + charConstMatch[0], + integration.getStringConcat([part1, variable, part2]) + ) + } else { + sql = sql.replace(binding, variable) + } + // const indexOfBinding = sql.indexOf(binding) + // const constantStr = `'${binding}'` + // sql = sql.replace(sql.indexOf(constantStr) === indexOfBinding - 1 ? constantStr : binding, variable) } // replicate the knex structure fields.sql = sql