fix(Postgres Node): Spread array queryReplacement across multiple bind values (#31704)

Co-authored-by: Bao <bao0541@users.noreply.github.com>
This commit is contained in:
Tongbo 2026-06-04 23:02:31 +08:00 committed by GitHub
parent 6eb583b2bd
commit ee7aa0b66a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 98 additions and 5 deletions

View File

@ -504,6 +504,88 @@ describe('Test PostgresV2, executeQuery operation', () => {
expect(utils.isJSON).toHaveBeenCalledTimes(1);
expect(utils.stringToArray).toHaveBeenCalledTimes(1);
});
const createMockExecuteForArrayQuery = (
nodeParameters: IDataObject,
returnArray: unknown[],
matchString: string,
) =>
({
getNodeParameter(
parameterName: string,
_itemIndex: number,
fallbackValue?: IDataObject,
options?: IGetNodeParameterOptions,
) {
const parameter = options?.extractValue ? `${parameterName}.value` : parameterName;
return get(nodeParameters, parameter, fallbackValue);
},
getNode() {
node.parameters = { ...node.parameters, ...(nodeParameters as INodeParameters) };
return node;
},
evaluateExpression(str: string, _: number) {
if (str.includes(matchString)) {
return returnArray;
}
return str.replace('{{', '').replace('}}', '');
},
}) as unknown as IExecuteFunctions;
it.each([
{
description: 'spread string array across individual bind values',
query: 'INSERT INTO my_table (col1, col2, col3) VALUES ($1, $2, $3)',
queryReplacement: "={{ ['a', 'b', 'c'] }}",
matchString: "['a', 'b', 'c']",
returnArray: ['a', 'b', 'c'],
expectedValues: ['a', 'b', 'c'],
},
{
description: 'JSON.stringify object elements in array bind values',
query: 'INSERT INTO my_table (col1, col2) VALUES ($1, $2)',
queryReplacement: '={{ [{id: 1}, {id: 2}] }}',
matchString: '[{id: 1}, {id: 2}]',
returnArray: [{ id: 1 }, { id: 2 }],
expectedValues: ['{"id":1}', '{"id":2}'],
},
{
description: 'handle null, number, and boolean elements in array bind values',
query: 'INSERT INTO my_table (col1, col2, col3) VALUES ($1, $2, $3)',
queryReplacement: '={{ [null, 42, true] }}',
matchString: '[null, 42, true]',
returnArray: [null, 42, true],
expectedValues: [null, 42, true],
},
])(
'should $description',
async ({ query, queryReplacement, matchString, returnArray, expectedValues }) => {
const nodeParameters: IDataObject = {
operation: 'executeQuery',
query,
options: {
queryReplacement,
nodeVersion: 2.6,
},
};
const nodeOptions = nodeParameters.options as IDataObject;
const mockExecute = createMockExecuteForArrayQuery(nodeParameters, returnArray, matchString);
await executeQuery.execute.call(mockExecute, runQueries, items, nodeOptions);
expect(runQueries).toHaveBeenCalledWith(
[
{
query,
values: expectedValues,
options: { partial: true },
},
],
nodeOptions,
);
},
);
});
describe('Test PostgresV2, insert operation', () => {

View File

@ -12,6 +12,7 @@ import type {
PgpDatabase,
PostgresNodeOptions,
QueriesRunner,
QueryValue,
QueryWithValues,
} from '../../helpers/interfaces';
import {
@ -68,7 +69,7 @@ export async function execute(
query = query.replace(resolvable, this.evaluateExpression(resolvable, index) as string);
}
let values: Array<IDataObject | string> = [];
let values: QueryValue[] = [];
let queryReplacement = this.getNodeParameter('options.queryReplacement', index, '');
@ -89,9 +90,19 @@ export async function execute(
const resolvables = getResolvables(rawValues);
if (resolvables.length) {
for (const resolvable of resolvables) {
const evaluatedExpression = evaluateExpression(
this.evaluateExpression(`${resolvable}`, index),
);
const rawEvaluated = this.evaluateExpression(`${resolvable}`, index);
if (Array.isArray(rawEvaluated)) {
for (const item of rawEvaluated) {
if (item === undefined) continue;
values.push(
typeof item === 'object' && item !== null ? JSON.stringify(item) : item,
);
}
continue;
}
const evaluatedExpression = evaluateExpression(rawEvaluated);
const evaluatedValues = isJSON(evaluatedExpression)
? [evaluatedExpression]
: stringToArray(evaluatedExpression);

View File

@ -5,7 +5,7 @@ import type pg from 'pg-promise/typescript/pg-subset';
export type QueryMode = 'single' | 'transaction' | 'independently';
export type QueryValue = string | number | IDataObject | string[];
export type QueryValue = string | number | boolean | null | IDataObject | string[];
export type QueryValues = QueryValue[];
export type QueryWithValues = { query: string; values?: QueryValues; options?: IFormattingOptions };