From 035cda117761aaffe556f209de9331495fda3584 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 4 Apr 2022 15:39:38 +0100 Subject: [PATCH 1/2] Extension of fix for 4978 - fixing an issue where parentheses are added to the IN query, causing the query system to not recognise the need to switch out the binding. --- packages/server/src/threads/query.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/server/src/threads/query.js b/packages/server/src/threads/query.js index 270c2cc713..aee3ddb205 100644 --- a/packages/server/src/threads/query.js +++ b/packages/server/src/threads/query.js @@ -37,7 +37,7 @@ class QueryRunner { arrays = [] for (let binding of bindings) { // look for array/list operations in the SQL statement, which will need handled later - const listRegex = new RegExp(`(in|IN|In|iN)( )+${binding}`) + const listRegex = new RegExp(`(in|IN|In|iN)( )+[(]?${binding}[)]?`) const listRegexMatch = sql.match(listRegex) // check if the variable was used as part of a string concat e.g. 'Hello {{binding}}' const charConstRegex = new RegExp(`'[^']*${binding}[^']*'`) @@ -63,12 +63,14 @@ class QueryRunner { "," ) // build a string like ($1, $2, $3) - sql = sql.replace( - binding, - `(${Array.apply(null, Array(value.length)) - .map(() => integration.getBindingIdentifier()) - .join(",")})` - ) + let replacement = `${Array.apply(null, Array(value.length)) + .map(() => integration.getBindingIdentifier()) + .join(",")}` + // check if parentheses are needed + if (!listRegexMatch[0].includes(`(${binding})`)) { + replacement = `(${replacement})` + } + sql = sql.replace(binding, replacement) } else { sql = sql.replace(binding, integration.getBindingIdentifier()) } From 4d084383f3def3481a88152e019b2d0bfb8c31c9 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 4 Apr 2022 19:08:56 +0100 Subject: [PATCH 2/2] Fix for #5242 - string interpolation in SQL queries was too wide, it was combining parts of multiple strings, rather than searching for bindings within distinct strings. --- packages/server/src/threads/query.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/server/src/threads/query.js b/packages/server/src/threads/query.js index aee3ddb205..d6089567b8 100644 --- a/packages/server/src/threads/query.js +++ b/packages/server/src/threads/query.js @@ -9,6 +9,8 @@ const { const { doInAppContext, getAppDB } = require("@budibase/backend-core/context") const { isSQL } = require("../integrations/utils") +const CONST_CHAR_REGEX = new RegExp("'[^']*'", "g") + class QueryRunner { constructor(input, flags = { noRecursiveQuery: false }) { this.datasource = input.datasource @@ -37,17 +39,22 @@ class QueryRunner { arrays = [] for (let binding of bindings) { // look for array/list operations in the SQL statement, which will need handled later - const listRegex = new RegExp(`(in|IN|In|iN)( )+[(]?${binding}[)]?`) - const listRegexMatch = sql.match(listRegex) + const listRegexMatch = sql.match( + new RegExp(`(in|IN|In|iN)( )+[(]?${binding}[)]?`) + ) // 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) + // start by finding all the instances of const character strings + const charConstMatch = sql.match(CONST_CHAR_REGEX) || [] + // now look within them to see if a binding is used + const charConstBindingMatch = charConstMatch.find(string => + string.match(new RegExp(`'[^']*${binding}[^']*'`)) + ) + if (charConstBindingMatch) { + let [part1, part2] = charConstBindingMatch.split(binding) part1 = `'${part1.substring(1)}'` part2 = `'${part2.substring(0, part2.length - 1)}'` sql = sql.replace( - charConstMatch[0], + charConstBindingMatch, integration.getStringConcat([ part1, integration.getBindingIdentifier(),