90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
import { findHBSBlocks } from "@budibase/string-templates"
|
|
import { DatasourcePlus } from "@budibase/types"
|
|
import sdk from "../../sdk"
|
|
|
|
const CONST_CHAR_REGEX = new RegExp("'[^']*'", "g")
|
|
|
|
export async function interpolateSQL(
|
|
fields: { sql: string; bindings: any[] },
|
|
parameters: { [key: string]: any },
|
|
integration: DatasourcePlus,
|
|
opts: { nullDefaultSupport: boolean }
|
|
) {
|
|
let sql = fields.sql
|
|
if (!sql || typeof sql !== "string") {
|
|
return fields
|
|
}
|
|
const bindings = findHBSBlocks(sql)
|
|
let variables = [],
|
|
arrays = []
|
|
for (let binding of bindings) {
|
|
// look for array/list operations in the SQL statement, which will need handled later
|
|
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}}'
|
|
// 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: any) =>
|
|
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(
|
|
charConstBindingMatch,
|
|
integration.getStringConcat([
|
|
part1,
|
|
integration.getBindingIdentifier(),
|
|
part2,
|
|
])
|
|
)
|
|
}
|
|
// generate SQL parameterised array
|
|
else if (listRegexMatch) {
|
|
arrays.push(binding)
|
|
// determine the length of the array
|
|
const value = (await sdk.queries.enrichContext([binding], parameters))[0]
|
|
.split(",")
|
|
.map((val: string) => val.trim())
|
|
// build a string like ($1, $2, $3)
|
|
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())
|
|
}
|
|
variables.push(binding)
|
|
}
|
|
// replicate the knex structure
|
|
fields.sql = sql
|
|
fields.bindings = await sdk.queries.enrichArrayContext(variables, parameters)
|
|
if (opts.nullDefaultSupport) {
|
|
for (let index in fields.bindings) {
|
|
if (fields.bindings[index] === "") {
|
|
fields.bindings[index] = null
|
|
}
|
|
}
|
|
}
|
|
// check for arrays in the data
|
|
let updated: string[] = []
|
|
for (let i = 0; i < variables.length; i++) {
|
|
if (arrays.includes(variables[i])) {
|
|
updated = updated.concat(
|
|
fields.bindings[i].split(",").map((val: string) => val.trim())
|
|
)
|
|
} else {
|
|
updated.push(fields.bindings[i])
|
|
}
|
|
}
|
|
fields.bindings = updated
|
|
return fields
|
|
}
|