Skip to content

Commit b563254

Browse files
michael-radencydana-gill
andauthoredMar 13, 2025··
fix(Postgres Node): RMC do not mark collumn as required if identity_generation is BY DEFAULT (#13752)
Co-authored-by: Dana <152518854+dana-gill@users.noreply.github.com>
1 parent ec8a719 commit b563254

File tree

7 files changed

+149
-9
lines changed

7 files changed

+149
-9
lines changed
 

‎packages/nodes-base/nodes/Postgres/Postgres.node.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export class Postgres extends VersionedNodeType {
1111
name: 'postgres',
1212
icon: 'file:postgres.svg',
1313
group: ['input'],
14-
defaultVersion: 2.5,
14+
defaultVersion: 2.6,
1515
description: 'Get, add and update data in Postgres',
1616
parameterPane: 'wide',
1717
};
@@ -24,6 +24,7 @@ export class Postgres extends VersionedNodeType {
2424
2.3: new PostgresV2(baseDescription),
2525
2.4: new PostgresV2(baseDescription),
2626
2.5: new PostgresV2(baseDescription),
27+
2.6: new PostgresV2(baseDescription),
2728
};
2829

2930
super(nodeVersions, baseDescription);

‎packages/nodes-base/nodes/Postgres/test/v2/operations.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,51 @@ describe('Test PostgresV2, insert operation', () => {
713713
expect(hasJsonDataTypeInSchemaSpy).toHaveBeenCalledWith(columnsInfo);
714714
});
715715
});
716+
717+
it('should insert default values if no values are provided', async () => {
718+
const nodeParameters: IDataObject = {
719+
schema: {
720+
__rl: true,
721+
mode: 'list',
722+
value: 'public',
723+
},
724+
table: {
725+
__rl: true,
726+
value: 'my_table',
727+
mode: 'list',
728+
},
729+
dataMode: 'defineBelow',
730+
valuesToSend: {
731+
values: [],
732+
},
733+
options: { nodeVersion: 2.6 },
734+
};
735+
const columnsInfo: ColumnInfo[] = [
736+
{ column_name: 'id', data_type: 'integer', is_nullable: 'NO', udt_name: '' },
737+
];
738+
739+
const nodeOptions = nodeParameters.options as IDataObject;
740+
741+
await insert.execute.call(
742+
createMockExecuteFunction(nodeParameters),
743+
runQueries,
744+
items,
745+
nodeOptions,
746+
createMockDb(columnsInfo),
747+
pgPromise(),
748+
);
749+
750+
expect(runQueries).toHaveBeenCalledWith(
751+
[
752+
{
753+
query: 'INSERT INTO $1:name.$2:name DEFAULT VALUES RETURNING *',
754+
values: ['public', 'my_table', {}],
755+
},
756+
],
757+
items,
758+
nodeOptions,
759+
);
760+
});
716761
});
717762

718763
describe('Test PostgresV2, select operation', () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import type { MockProxy } from 'jest-mock-extended';
2+
import { mock } from 'jest-mock-extended';
3+
import type { ILoadOptionsFunctions } from 'n8n-workflow';
4+
5+
import { getMappingColumns } from '../../v2/methods/resourceMapping';
6+
7+
jest.mock('../../transport', () => {
8+
const originalModule = jest.requireActual('../../transport');
9+
return {
10+
...originalModule,
11+
configurePostgres: jest.fn(async () => ({ db: {} })),
12+
};
13+
});
14+
15+
jest.mock('../../v2/helpers/utils', () => {
16+
const originalModule = jest.requireActual('../../v2/helpers/utils');
17+
return {
18+
...originalModule,
19+
getEnums: jest.fn(() => []),
20+
getEnumValues: jest.fn(),
21+
getTableSchema: jest.fn(() => [
22+
{
23+
column_name: 'id',
24+
data_type: 'bigint',
25+
is_nullable: 'NO',
26+
udt_name: 'int8',
27+
column_default: null,
28+
identity_generation: 'BY DEFAULT',
29+
is_generated: 'NEVER',
30+
},
31+
{
32+
column_name: 'name',
33+
data_type: 'text',
34+
is_nullable: 'YES',
35+
udt_name: 'text',
36+
column_default: null,
37+
identity_generation: null,
38+
is_generated: 'NEVER',
39+
},
40+
]),
41+
};
42+
});
43+
44+
describe('Postgres, resourceMapping', () => {
45+
let loadOptionsFunctions: MockProxy<ILoadOptionsFunctions>;
46+
47+
beforeEach(() => {
48+
loadOptionsFunctions = mock<ILoadOptionsFunctions>();
49+
});
50+
51+
afterEach(() => {
52+
jest.resetAllMocks();
53+
});
54+
55+
it('should mark id as not required if identity_generation is "BY_DEFAULT"', async () => {
56+
loadOptionsFunctions.getCredentials.mockResolvedValue({});
57+
loadOptionsFunctions.getNodeParameter.mockReturnValueOnce('public');
58+
loadOptionsFunctions.getNodeParameter.mockReturnValueOnce('test_table');
59+
loadOptionsFunctions.getNodeParameter.mockReturnValueOnce('insert');
60+
61+
const fields = await getMappingColumns.call(loadOptionsFunctions);
62+
63+
expect(fields).toEqual({
64+
fields: [
65+
{
66+
canBeUsedToMatch: true,
67+
defaultMatch: true,
68+
display: true,
69+
displayName: 'id',
70+
id: 'id',
71+
options: undefined,
72+
required: false,
73+
type: 'number',
74+
},
75+
{
76+
canBeUsedToMatch: true,
77+
defaultMatch: false,
78+
display: true,
79+
displayName: 'name',
80+
id: 'name',
81+
options: undefined,
82+
required: false,
83+
type: 'string',
84+
},
85+
],
86+
});
87+
});
88+
});

‎packages/nodes-base/nodes/Postgres/test/v2/utils.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,15 @@ describe('Test PostgresV2, parsePostgresError', () => {
146146

147147
it('should update message with syntax error', () => {
148148
// eslint-disable-next-line n8n-local-rules/no-unneeded-backticks
149-
const errorMessage = String.raw`syntax error at or near "seelect"`;
149+
const errorMessage = String.raw`syntax error at or near "select"`;
150150
const error = new Error();
151151
error.message = errorMessage;
152152

153153
const parsedError = parsePostgresError(node, error, [
154-
{ query: 'seelect * from my_table', values: [] },
154+
{ query: 'select * from my_table', values: [] },
155155
]);
156156
expect(parsedError).toBeDefined();
157-
expect(parsedError.message).toEqual('Syntax error at line 1 near "seelect"');
157+
expect(parsedError.message).toEqual('Syntax error at line 1 near "select"');
158158
expect(parsedError instanceof NodeOperationError).toEqual(true);
159159
});
160160
});
@@ -201,7 +201,7 @@ describe('Test PostgresV2, addWhereClauses', () => {
201201
expect(updatedValues).toEqual(['public', 'my_table', 'id', '1', 'foo', 'select 2']);
202202
});
203203

204-
it('should ignore incorect combine conition ad use AND', () => {
204+
it('should ignore incorrect combine condition ad use AND', () => {
205205
const query = 'SELECT * FROM $1:name.$2:name';
206206
const values = ['public', 'my_table'];
207207
const whereClauses = [
@@ -246,7 +246,7 @@ describe('Test PostgresV2, addSortRules', () => {
246246
expect(updatedQuery).toEqual('SELECT * FROM $1:name.$2:name ORDER BY $3:name DESC');
247247
expect(updatedValues).toEqual(['public', 'my_table', 'id']);
248248
});
249-
it('should ignore incorect direction', () => {
249+
it('should ignore incorrect direction', () => {
250250
const query = 'SELECT * FROM $1:name.$2:name';
251251
const values = ['public', 'my_table'];
252252
const sortRules = [{ column: 'id', direction: 'SELECT * FROM my_table' }];
@@ -340,7 +340,7 @@ describe('Test PostgresV2, replaceEmptyStringsByNulls', () => {
340340
});
341341

342342
describe('Test PostgresV2, prepareItem', () => {
343-
it('should convert fixedColections values to object', () => {
343+
it('should convert fixedCollection values to object', () => {
344344
const values = [
345345
{
346346
column: 'id',

‎packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts

+4
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,10 @@ export async function execute(
241241

242242
const outputColumns = this.getNodeParameter('options.outputColumns', i, ['*']) as string[];
243243

244+
if (nodeVersion >= 2.6 && Object.keys(item).length === 0) {
245+
query = 'INSERT INTO $1:name.$2:name DEFAULT VALUES';
246+
}
247+
244248
[query, values] = addReturning(query, outputColumns, values);
245249

246250
queries.push({ query, values });

‎packages/nodes-base/nodes/Postgres/v2/actions/versionDescription.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const versionDescription: INodeTypeDescription = {
88
name: 'postgres',
99
icon: 'file:postgres.svg',
1010
group: ['input'],
11-
version: [2, 2.1, 2.2, 2.3, 2.4, 2.5],
11+
version: [2, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6],
1212
subtitle: '={{ $parameter["operation"] }}',
1313
description: 'Get, add and update data in Postgres',
1414
defaults: {

‎packages/nodes-base/nodes/Postgres/v2/methods/resourceMapping.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ export async function getMappingColumns(
7474
const options =
7575
type === 'options' ? getEnumValues(enumInfo, col.udt_name as string) : undefined;
7676
const hasDefault = Boolean(col.column_default);
77-
const isGenerated = col.is_generated === 'ALWAYS' || col.identity_generation === 'ALWAYS';
77+
const isGenerated =
78+
col.is_generated === 'ALWAYS' ||
79+
['ALWAYS', 'BY DEFAULT'].includes(col.identity_generation ?? '');
7880
const nullable = col.is_nullable === 'YES';
7981
return {
8082
id: col.column_name,

0 commit comments

Comments
 (0)
Please sign in to comment.