From 508f2dd0322cc91366685a79366c704120024e66 Mon Sep 17 00:00:00 2001 From: Shireen Missi Date: Thu, 16 Jan 2025 13:20:56 +0000 Subject: [PATCH] Fix composit key upsert queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ --- .../nodes/Postgres/test/v2/operations.test.ts | 181 ++++++++++++++++++ .../v2/actions/database/upsert.operation.ts | 6 +- 2 files changed, 185 insertions(+), 2 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 5a7addf0d1453..a2c0055590f13 100644 --- a/packages/nodes-base/nodes/Postgres/test/v2/operations.test.ts +++ b/packages/nodes-base/nodes/Postgres/test/v2/operations.test.ts @@ -1151,3 +1151,184 @@ describe('Test PostgresV2, upsert operation', () => { ); }); }); + +describe('When matching on all columns', () => { + it('dataMode: define, should call runQueries with', async () => { + const nodeParameters: IDataObject = { + operation: 'upsert', + schema: { + __rl: true, + mode: 'list', + value: 'public', + }, + table: { + __rl: true, + value: 'my_table', + mode: 'list', + }, + columns: { + matchingColumns: ['id', 'json', 'foo'], + value: { id: '5', json: '{ "test": 5 }', foo: 'data 5' }, + mappingMode: 'defineBelow', + }, + options: { + outputColumns: ['json'], + nodeVersion: 2.5, + }, + }; + const columnsInfo: ColumnInfo[] = [ + { 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 = [ + { + json: { + id: 1, + json: { + test: 15, + }, + foo: 'data 1', + }, + }, + ]; + const nodeOptions = nodeParameters.options as IDataObject; + + await upsert.execute.call( + createMockExecuteFunction(nodeParameters), + runQueries, + inputItems, + nodeOptions, + createMockDb(columnsInfo), + ); + + expect(runQueries).toHaveBeenCalledWith( + [ + { + query: + 'INSERT INTO $1:name.$2:name($6:name) VALUES($6:csv) ON CONFLICT ($3:name,$4:name,$5:name) DO NOTHING RETURNING $7:name', + values: [ + 'public', + 'my_table', + 'id', + 'json', + 'foo', + { id: '5', json: '{ "test": 5 }', foo: 'data 5' }, + ['json'], + ], + }, + ], + inputItems, + nodeOptions, + ); + }); + + it('dataMode: autoMapInputData, should call runQueries with', async () => { + const nodeParameters: IDataObject = { + operation: 'upsert', + schema: { + __rl: true, + mode: 'list', + value: 'public', + }, + table: { + __rl: true, + value: 'my_table', + mode: 'list', + }, + columns: { + matchingColumns: ['id', 'json', 'foo'], + mappingMode: 'autoMapInputData', + }, + options: { nodeVersion: 2.5 }, + }; + const columnsInfo: ColumnInfo[] = [ + { 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 = [ + { + json: { + id: 1, + json: { + test: 15, + }, + foo: 'data 1', + }, + }, + { + json: { + id: 2, + json: { + test: 10, + }, + foo: 'data 2', + }, + }, + { + json: { + id: 3, + json: { + test: 5, + }, + foo: 'data 3', + }, + }, + ]; + + const nodeOptions = nodeParameters.options as IDataObject; + + await upsert.execute.call( + createMockExecuteFunction(nodeParameters), + runQueries, + inputItems, + nodeOptions, + createMockDb(columnsInfo), + ); + + expect(runQueries).toHaveBeenCalledWith( + [ + { + query: + 'INSERT INTO $1:name.$2:name($6:name) VALUES($6:csv) ON CONFLICT ($3:name,$4:name,$5:name) DO NOTHING RETURNING *', + values: [ + 'public', + 'my_table', + 'id', + 'json', + 'foo', + { id: 1, json: { test: 15 }, foo: 'data 1' }, + ], + }, + { + query: + 'INSERT INTO $1:name.$2:name($6:name) VALUES($6:csv) ON CONFLICT ($3:name,$4:name,$5:name) DO NOTHING RETURNING *', + values: [ + 'public', + 'my_table', + 'id', + 'json', + 'foo', + { id: 2, json: { test: 10 }, foo: 'data 2' }, + ], + }, + { + query: + 'INSERT INTO $1:name.$2:name($6:name) VALUES($6:csv) ON CONFLICT ($3:name,$4:name,$5:name) DO NOTHING RETURNING *', + values: [ + 'public', + 'my_table', + 'id', + 'json', + 'foo', + { id: 3, json: { test: 5 }, foo: 'data 3' }, + ], + }, + ], + inputItems, + nodeOptions, + ); + }); +}); diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts index 7a43605d56209..4a9567e5c22c1 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts @@ -285,7 +285,7 @@ export async function execute( valuesLength = valuesLength + 1; values.push(column); }); - const onConflict = ` ON CONFLICT (${conflictColumns.join(',')}) DO UPDATE `; + const onConflict = ` ON CONFLICT (${conflictColumns.join(',')})`; const insertQuery = `INSERT INTO $1:name.$2:name($${valuesLength}:name) VALUES($${valuesLength}:csv)${onConflict}`; valuesLength = valuesLength + 1; @@ -300,7 +300,9 @@ export async function execute( values.push(column, item[column] as string); } - let query = `${insertQuery} SET ${updates.join(', ')}`; + const updateQuery = + updates?.length > 0 ? ` DO UPDATE SET ${updates.join(', ')}` : ' DO NOTHING '; + let query = `${insertQuery}${updateQuery}`; const outputColumns = this.getNodeParameter('options.outputColumns', i, ['*']) as string[];