From 4cae091cfb54701dfa51b5204799df0a8b4929cd Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Tue, 25 Jul 2023 15:34:45 +0300 Subject: [PATCH] fix(Postgres Node): Arrays in query replacement fix (#6718) --- .../nodes/Postgres/test/v2/operations.test.ts | 45 +++++++++++-------- .../database/executeQuery.operation.ts | 44 +++++++++++++----- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/packages/nodes-base/nodes/Postgres/test/v2/operations.test.ts b/packages/nodes-base/nodes/Postgres/test/v2/operations.test.ts index 4d0e30fa5..c2f5d7e57 100644 --- a/packages/nodes-base/nodes/Postgres/test/v2/operations.test.ts +++ b/packages/nodes-base/nodes/Postgres/test/v2/operations.test.ts @@ -1,4 +1,10 @@ -import type { IDataObject, IExecuteFunctions, IGetNodeParameterOptions, INode } from 'n8n-workflow'; +import type { + IDataObject, + IExecuteFunctions, + IGetNodeParameterOptions, + INode, + INodeParameters, +} from 'n8n-workflow'; import type { ColumnInfo, PgpDatabase, QueriesRunner } from '../../v2/helpers/interfaces'; @@ -38,6 +44,7 @@ const createMockExecuteFunction = (nodeParameters: IDataObject) => { return get(nodeParameters, parameter, fallbackValue); }, getNode() { + node.parameters = { ...node.parameters, ...(nodeParameters as INodeParameters) }; return node; }, } as unknown as IExecuteFunctions; @@ -252,9 +259,9 @@ describe('Test PostgresV2, insert operation', () => { options: {}, }; const columnsInfo: ColumnInfo[] = [ - { column_name: 'id', data_type: 'integer', is_nullable: 'NO' }, - { column_name: 'json', data_type: 'json', is_nullable: 'NO' }, - { column_name: 'foo', data_type: 'text', is_nullable: 'NO' }, + { column_name: 'id', data_type: 'integer', is_nullable: 'NO', udt_name: '' }, + { column_name: 'json', data_type: 'json', is_nullable: 'NO', udt_name: '' }, + { column_name: 'foo', data_type: 'text', is_nullable: 'NO', udt_name: '' }, ]; const nodeOptions = nodeParameters.options as IDataObject; @@ -295,9 +302,9 @@ describe('Test PostgresV2, insert operation', () => { options: {}, }; const columnsInfo: ColumnInfo[] = [ - { column_name: 'id', data_type: 'integer', is_nullable: 'NO' }, - { column_name: 'json', data_type: 'json', is_nullable: 'NO' }, - { column_name: 'foo', data_type: 'text', is_nullable: 'NO' }, + { column_name: 'id', data_type: 'integer', is_nullable: 'NO', udt_name: '' }, + { column_name: 'json', data_type: 'json', is_nullable: 'NO', udt_name: '' }, + { column_name: 'foo', data_type: 'text', is_nullable: 'NO', udt_name: '' }, ]; const inputItems = [ @@ -505,9 +512,9 @@ describe('Test PostgresV2, update operation', () => { }, }; const columnsInfo: ColumnInfo[] = [ - { column_name: 'id', data_type: 'integer', is_nullable: 'NO' }, - { column_name: 'json', data_type: 'json', is_nullable: 'NO' }, - { column_name: 'foo', data_type: 'text', is_nullable: 'NO' }, + { column_name: 'id', data_type: 'integer', is_nullable: 'NO', udt_name: '' }, + { column_name: 'json', data_type: 'json', is_nullable: 'NO', udt_name: '' }, + { column_name: 'foo', data_type: 'text', is_nullable: 'NO', udt_name: '' }, ]; const nodeOptions = nodeParameters.options as IDataObject; @@ -561,9 +568,9 @@ describe('Test PostgresV2, update operation', () => { options: {}, }; const columnsInfo: ColumnInfo[] = [ - { column_name: 'id', data_type: 'integer', is_nullable: 'NO' }, - { column_name: 'json', data_type: 'json', is_nullable: 'NO' }, - { column_name: 'foo', data_type: 'text', is_nullable: 'NO' }, + { column_name: 'id', data_type: 'integer', is_nullable: 'NO', udt_name: '' }, + { column_name: 'json', data_type: 'json', is_nullable: 'NO', udt_name: '' }, + { column_name: 'foo', data_type: 'text', is_nullable: 'NO', udt_name: '' }, ]; const inputItems = [ @@ -664,9 +671,9 @@ describe('Test PostgresV2, upsert operation', () => { }, }; const columnsInfo: ColumnInfo[] = [ - { column_name: 'id', data_type: 'integer', is_nullable: 'NO' }, - { column_name: 'json', data_type: 'json', is_nullable: 'NO' }, - { column_name: 'foo', data_type: 'text', is_nullable: 'NO' }, + { column_name: 'id', data_type: 'integer', is_nullable: 'NO', udt_name: '' }, + { column_name: 'json', data_type: 'json', is_nullable: 'NO', udt_name: '' }, + { column_name: 'foo', data_type: 'text', is_nullable: 'NO', udt_name: '' }, ]; const nodeOptions = nodeParameters.options as IDataObject; @@ -720,9 +727,9 @@ describe('Test PostgresV2, upsert operation', () => { options: {}, }; const columnsInfo: ColumnInfo[] = [ - { column_name: 'id', data_type: 'integer', is_nullable: 'NO' }, - { column_name: 'json', data_type: 'json', is_nullable: 'NO' }, - { column_name: 'foo', data_type: 'text', is_nullable: 'NO' }, + { column_name: 'id', data_type: 'integer', is_nullable: 'NO', udt_name: '' }, + { column_name: 'json', data_type: 'json', is_nullable: 'NO', udt_name: '' }, + { column_name: 'foo', data_type: 'text', is_nullable: 'NO', udt_name: '' }, ]; const inputItems = [ diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts index 4a53c6b16..89d115d0e 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts @@ -61,22 +61,44 @@ export async function execute( query = query.replace(resolvable, this.evaluateExpression(resolvable, i) as string); } - let values: IDataObject[] = []; + let values: Array = []; - let queryReplacement = this.getNodeParameter('options.queryReplacement', i, ''); + const queryReplacement = this.getNodeParameter('options.queryReplacement', i, ''); if (typeof queryReplacement === 'string') { - queryReplacement = queryReplacement.split(',').map((entry) => entry.trim()); - } + const node = this.getNode(); - if (Array.isArray(queryReplacement)) { - values = queryReplacement as IDataObject[]; + const rawReplacements = (node.parameters.options as IDataObject)?.queryReplacement as string; + + if (rawReplacements) { + const rawValues = rawReplacements + .replace(/^=+/, '') + .split(',') + .filter((entry) => entry) + .map((entry) => entry.trim()); + + for (const rawValue of rawValues) { + const resolvables = getResolvables(rawValue); + + if (resolvables.length) { + for (const resolvable of resolvables) { + values.push(this.evaluateExpression(`${resolvable}`, i) as IDataObject); + } + } else { + values.push(rawValue); + } + } + } } else { - throw new NodeOperationError( - this.getNode(), - 'Query Replacement must be a string of comma-separated values, or an array of values', - { itemIndex: i }, - ); + if (Array.isArray(queryReplacement)) { + values = queryReplacement as IDataObject[]; + } else { + throw new NodeOperationError( + this.getNode(), + 'Query Parameters must be a string of comma-separated values or an array of values', + { itemIndex: i }, + ); + } } queries.push({ query, values });