Skip to content

Commit

Permalink
fix(Postgres Node): Allow using composite key in upsert queries (n8n-…
Browse files Browse the repository at this point in the history
…io#12639)

Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <netroy@users.noreply.github.com>
  • Loading branch information
ShireenMissi and netroy authored Jan 17, 2025
1 parent 982131a commit 83ce3a9
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 2 deletions.
181 changes: 181 additions & 0 deletions packages/nodes-base/nodes/Postgres/test/v2/operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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[];

Expand Down

0 comments on commit 83ce3a9

Please sign in to comment.