mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 05:17:28 -08:00
fix(Postgres Node): Empty return data fix for Postgres and MySQL (#7016)
This commit is contained in:
parent
f02f6b659a
commit
176ccd62bc
|
@ -11,7 +11,7 @@ export class MySql extends VersionedNodeType {
|
||||||
name: 'mySql',
|
name: 'mySql',
|
||||||
icon: 'file:mysql.svg',
|
icon: 'file:mysql.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
defaultVersion: 2.1,
|
defaultVersion: 2.2,
|
||||||
description: 'Get, add and update data in MySQL',
|
description: 'Get, add and update data in MySQL',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ export class MySql extends VersionedNodeType {
|
||||||
1: new MySqlV1(baseDescription),
|
1: new MySqlV1(baseDescription),
|
||||||
2: new MySqlV2(baseDescription),
|
2: new MySqlV2(baseDescription),
|
||||||
2.1: new MySqlV2(baseDescription),
|
2.1: new MySqlV2(baseDescription),
|
||||||
|
2.2: new MySqlV2(baseDescription),
|
||||||
};
|
};
|
||||||
|
|
||||||
super(nodeVersions, baseDescription);
|
super(nodeVersions, baseDescription);
|
||||||
|
|
|
@ -305,7 +305,7 @@ describe('Test MySql V2, operations', () => {
|
||||||
|
|
||||||
const runQueries: QueryRunner = configureQueryRunner.call(
|
const runQueries: QueryRunner = configureQueryRunner.call(
|
||||||
fakeExecuteFunction,
|
fakeExecuteFunction,
|
||||||
nodeOptions,
|
{ ...nodeOptions, nodeVersion: 2 },
|
||||||
pool,
|
pool,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ describe('Test MySql V2, runQueries', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute in "Single" mode, should return success true', async () => {
|
it('should execute in "Single" mode, should return success true', async () => {
|
||||||
const nodeOptions: IDataObject = { queryBatching: BATCH_MODE.SINGLE };
|
const nodeOptions: IDataObject = { queryBatching: BATCH_MODE.SINGLE, nodeVersion: 2 };
|
||||||
|
|
||||||
const pool = createFakePool(fakeConnection);
|
const pool = createFakePool(fakeConnection);
|
||||||
const fakeExecuteFunction = createMockExecuteFunction({}, mySqlMockNode);
|
const fakeExecuteFunction = createMockExecuteFunction({}, mySqlMockNode);
|
||||||
|
@ -81,7 +81,7 @@ describe('Test MySql V2, runQueries', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute in "independently" mode, should return success true', async () => {
|
it('should execute in "independently" mode, should return success true', async () => {
|
||||||
const nodeOptions: IDataObject = { queryBatching: BATCH_MODE.INDEPENDENTLY };
|
const nodeOptions: IDataObject = { queryBatching: BATCH_MODE.INDEPENDENTLY, nodeVersion: 2 };
|
||||||
|
|
||||||
const pool = createFakePool(fakeConnection);
|
const pool = createFakePool(fakeConnection);
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ describe('Test MySql V2, runQueries', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute in "transaction" mode, should return success true', async () => {
|
it('should execute in "transaction" mode, should return success true', async () => {
|
||||||
const nodeOptions: IDataObject = { queryBatching: BATCH_MODE.TRANSACTION };
|
const nodeOptions: IDataObject = { queryBatching: BATCH_MODE.TRANSACTION, nodeVersion: 2 };
|
||||||
|
|
||||||
const pool = createFakePool(fakeConnection);
|
const pool = createFakePool(fakeConnection);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const versionDescription: INodeTypeDescription = {
|
||||||
name: 'mySql',
|
name: 'mySql',
|
||||||
icon: 'file:mysql.svg',
|
icon: 'file:mysql.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: [2, 2.1],
|
version: [2, 2.1, 2.2],
|
||||||
subtitle: '={{ $parameter["operation"] }}',
|
subtitle: '={{ $parameter["operation"] }}',
|
||||||
description: 'Get, add and update data in MySQL',
|
description: 'Get, add and update data in MySQL',
|
||||||
defaults: {
|
defaults: {
|
||||||
|
|
|
@ -167,7 +167,23 @@ export function prepareOutput(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!returnData.length) {
|
if (!returnData.length) {
|
||||||
returnData.push({ json: { success: true } });
|
if ((options?.nodeVersion as number) < 2.2) {
|
||||||
|
returnData.push({ json: { success: true } });
|
||||||
|
} else {
|
||||||
|
const isSelectQuery = statements
|
||||||
|
.filter((statement) => !statement.startsWith('--'))
|
||||||
|
.every((statement) =>
|
||||||
|
statement
|
||||||
|
.replace(/\/\*.*?\*\//g, '') // remove multiline comments
|
||||||
|
.replace(/\n/g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
.startsWith('select'),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isSelectQuery) {
|
||||||
|
returnData.push({ json: { success: true } });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnData;
|
return returnData;
|
||||||
|
|
|
@ -11,7 +11,7 @@ export class Postgres extends VersionedNodeType {
|
||||||
name: 'postgres',
|
name: 'postgres',
|
||||||
icon: 'file:postgres.svg',
|
icon: 'file:postgres.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
defaultVersion: 2.2,
|
defaultVersion: 2.3,
|
||||||
description: 'Get, add and update data in Postgres',
|
description: 'Get, add and update data in Postgres',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ export class Postgres extends VersionedNodeType {
|
||||||
2: new PostgresV2(baseDescription),
|
2: new PostgresV2(baseDescription),
|
||||||
2.1: new PostgresV2(baseDescription),
|
2.1: new PostgresV2(baseDescription),
|
||||||
2.2: new PostgresV2(baseDescription),
|
2.2: new PostgresV2(baseDescription),
|
||||||
|
2.3: new PostgresV2(baseDescription),
|
||||||
};
|
};
|
||||||
|
|
||||||
super(nodeVersions, baseDescription);
|
super(nodeVersions, baseDescription);
|
||||||
|
|
|
@ -44,7 +44,9 @@ describe('Test PostgresV2, runQueries', () => {
|
||||||
const thisArg = mock<IExecuteFunctions>();
|
const thisArg = mock<IExecuteFunctions>();
|
||||||
const runQueries = configureQueryRunner.call(thisArg, node, false, pgp, db);
|
const runQueries = configureQueryRunner.call(thisArg, node, false, pgp, db);
|
||||||
|
|
||||||
const result = await runQueries([{ query: 'SELECT * FROM table', values: [] }], [], {});
|
const result = await runQueries([{ query: 'SELECT * FROM table', values: [] }], [], {
|
||||||
|
nodeVersion: 2.2,
|
||||||
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
expect(result).toHaveLength(1);
|
expect(result).toHaveLength(1);
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const versionDescription: INodeTypeDescription = {
|
||||||
name: 'postgres',
|
name: 'postgres',
|
||||||
icon: 'file:postgres.svg',
|
icon: 'file:postgres.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: [2, 2.1, 2.2],
|
version: [2, 2.1, 2.2, 2.3],
|
||||||
subtitle: '={{ $parameter["operation"] }}',
|
subtitle: '={{ $parameter["operation"] }}',
|
||||||
description: 'Get, add and update data in Postgres',
|
description: 'Get, add and update data in Postgres',
|
||||||
defaults: {
|
defaults: {
|
||||||
|
|
|
@ -199,6 +199,15 @@ export function addReturning(
|
||||||
return [`${query} RETURNING $${replacementIndex}:name`, [...replacements, outputColumns]];
|
return [`${query} RETURNING $${replacementIndex}:name`, [...replacements, outputColumns]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isSelectQuery = (query: string) => {
|
||||||
|
return query
|
||||||
|
.replace(/\/\*.*?\*\//g, '') // remove multiline comments
|
||||||
|
.replace(/\n/g, '')
|
||||||
|
.split(';')
|
||||||
|
.filter((statement) => statement && !statement.startsWith('--')) // remove comments and empty statements
|
||||||
|
.every((statement) => statement.trim().toLowerCase().startsWith('select'));
|
||||||
|
};
|
||||||
|
|
||||||
export function configureQueryRunner(
|
export function configureQueryRunner(
|
||||||
this: IExecuteFunctions,
|
this: IExecuteFunctions,
|
||||||
node: INode,
|
node: INode,
|
||||||
|
@ -221,7 +230,16 @@ export function configureQueryRunner(
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.flat();
|
.flat();
|
||||||
returnData = returnData.length ? returnData : emptyReturnData;
|
|
||||||
|
if (!returnData.length) {
|
||||||
|
if ((options?.nodeVersion as number) < 2.3) {
|
||||||
|
returnData = emptyReturnData;
|
||||||
|
} else {
|
||||||
|
returnData = queries.every((query) => isSelectQuery(query.query))
|
||||||
|
? []
|
||||||
|
: [{ json: { success: true } }];
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = parsePostgresError(node, err, queries);
|
const error = parsePostgresError(node, err, queries);
|
||||||
if (!continueOnFail) throw error;
|
if (!continueOnFail) throw error;
|
||||||
|
@ -242,13 +260,26 @@ export function configureQueryRunner(
|
||||||
const result: INodeExecutionData[] = [];
|
const result: INodeExecutionData[] = [];
|
||||||
for (let i = 0; i < queries.length; i++) {
|
for (let i = 0; i < queries.length; i++) {
|
||||||
try {
|
try {
|
||||||
const transactionResult: IDataObject[] = await transaction.any(
|
const query = queries[i].query;
|
||||||
queries[i].query,
|
const values = queries[i].values;
|
||||||
queries[i].values,
|
|
||||||
);
|
let transactionResults;
|
||||||
|
if ((options?.nodeVersion as number) < 2.3) {
|
||||||
|
transactionResults = await transaction.any(query, values);
|
||||||
|
} else {
|
||||||
|
transactionResults = (await transaction.multi(query, values)).flat();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transactionResults.length) {
|
||||||
|
if ((options?.nodeVersion as number) < 2.3) {
|
||||||
|
transactionResults = emptyReturnData;
|
||||||
|
} else {
|
||||||
|
transactionResults = isSelectQuery(query) ? [] : [{ success: true }];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const executionData = this.helpers.constructExecutionMetaData(
|
const executionData = this.helpers.constructExecutionMetaData(
|
||||||
wrapData(transactionResult.length ? transactionResult : emptyReturnData),
|
wrapData(transactionResults),
|
||||||
{ itemData: { item: i } },
|
{ itemData: { item: i } },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -265,17 +296,30 @@ export function configureQueryRunner(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryBatching === 'independently') {
|
if (queryBatching === 'independently') {
|
||||||
returnData = await db.task(async (t) => {
|
returnData = await db.task(async (task) => {
|
||||||
const result: INodeExecutionData[] = [];
|
const result: INodeExecutionData[] = [];
|
||||||
for (let i = 0; i < queries.length; i++) {
|
for (let i = 0; i < queries.length; i++) {
|
||||||
try {
|
try {
|
||||||
const transactionResult: IDataObject[] = await t.any(
|
const query = queries[i].query;
|
||||||
queries[i].query,
|
const values = queries[i].values;
|
||||||
queries[i].values,
|
|
||||||
);
|
let transactionResults;
|
||||||
|
if ((options?.nodeVersion as number) < 2.3) {
|
||||||
|
transactionResults = await task.any(query, values);
|
||||||
|
} else {
|
||||||
|
transactionResults = (await task.multi(query, values)).flat();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transactionResults.length) {
|
||||||
|
if ((options?.nodeVersion as number) < 2.3) {
|
||||||
|
transactionResults = emptyReturnData;
|
||||||
|
} else {
|
||||||
|
transactionResults = isSelectQuery(query) ? [] : [{ success: true }];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const executionData = this.helpers.constructExecutionMetaData(
|
const executionData = this.helpers.constructExecutionMetaData(
|
||||||
wrapData(transactionResult.length ? transactionResult : emptyReturnData),
|
wrapData(transactionResults),
|
||||||
{ itemData: { item: i } },
|
{ itemData: { item: i } },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue