From 670f1d67921104408d973b733bdb33f59b346d9f Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Wed, 24 Apr 2024 13:21:37 +0300 Subject: [PATCH 1/3] :zap: fix --- packages/nodes-base/nodes/MySql/MySql.node.ts | 3 +- .../MySql/v2/actions/versionDescription.ts | 2 +- .../nodes/MySql/v2/helpers/utils.ts | 36 +++++++++++++++---- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/packages/nodes-base/nodes/MySql/MySql.node.ts b/packages/nodes-base/nodes/MySql/MySql.node.ts index aad3df9bdce8a..30cbbaada0b12 100644 --- a/packages/nodes-base/nodes/MySql/MySql.node.ts +++ b/packages/nodes-base/nodes/MySql/MySql.node.ts @@ -11,7 +11,7 @@ export class MySql extends VersionedNodeType { name: 'mySql', icon: 'file:mysql.svg', group: ['input'], - defaultVersion: 2.3, + defaultVersion: 2.4, description: 'Get, add and update data in MySQL', parameterPane: 'wide', }; @@ -22,6 +22,7 @@ export class MySql extends VersionedNodeType { 2.1: new MySqlV2(baseDescription), 2.2: new MySqlV2(baseDescription), 2.3: new MySqlV2(baseDescription), + 2.4: new MySqlV2(baseDescription), }; super(nodeVersions, baseDescription); diff --git a/packages/nodes-base/nodes/MySql/v2/actions/versionDescription.ts b/packages/nodes-base/nodes/MySql/v2/actions/versionDescription.ts index c1802bd063663..2c1b7b3e7db70 100644 --- a/packages/nodes-base/nodes/MySql/v2/actions/versionDescription.ts +++ b/packages/nodes-base/nodes/MySql/v2/actions/versionDescription.ts @@ -8,7 +8,7 @@ export const versionDescription: INodeTypeDescription = { name: 'mySql', icon: 'file:mysql.svg', group: ['input'], - version: [2, 2.1, 2.2, 2.3], + version: [2, 2.1, 2.2, 2.3, 2.4], subtitle: '={{ $parameter["operation"] }}', description: 'Get, add and update data in MySQL', defaults: { diff --git a/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts b/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts index be48ed42e64fd..16261f8f639db 100644 --- a/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts +++ b/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts @@ -129,7 +129,7 @@ export function wrapData(data: IDataObject | IDataObject[]): INodeExecutionData[ })); } -export function prepareOutput( +function prepareOutput( response: IDataObject[], options: IDataObject, statements: string[], @@ -190,6 +190,11 @@ export function prepareOutput( return returnData; } +const END_OF_STATMENT = /;(?=(?:[^'\\]|'[^']*?'|\\[\s\S])*?$)/g; +const splitQueryToStatements = (query: string, filterOutEmpty = true) => { + const statements = query.replace(/\n/g, '').split(END_OF_STATMENT); + return filterOutEmpty ? statements.filter((statement) => statement !== '') : statements; +}; export function configureQueryRunner( this: IExecuteFunctions, @@ -225,10 +230,15 @@ export function configureQueryRunner( if (!response) return []; - const statements = singleQuery - .replace(/\n/g, '') - .split(';') - .filter((statement) => statement !== ''); + let statements; + if ((options?.nodeVersion as number) <= 2.3) { + statements = singleQuery + .replace(/\n/g, '') + .split(';') + .filter((statement) => statement !== ''); + } else { + statements = splitQueryToStatements(singleQuery); + } if (Array.isArray(response)) { if (statements.length === 1) response = [response]; @@ -261,7 +271,13 @@ export function configureQueryRunner( try { const { query, values } = queryWithValues; formatedQuery = connection.format(query, values); - const statements = formatedQuery.split(';').map((q) => q.trim()); + + let statements; + if ((options?.nodeVersion as number) <= 2.3) { + statements = formatedQuery.split(';').map((q) => q.trim()); + } else { + statements = splitQueryToStatements(formatedQuery, false); + } const responses: IDataObject[] = []; for (const statement of statements) { @@ -300,7 +316,13 @@ export function configureQueryRunner( try { const { query, values } = queryWithValues; formatedQuery = connection.format(query, values); - const statements = formatedQuery.split(';').map((q) => q.trim()); + + let statements; + if ((options?.nodeVersion as number) <= 2.3) { + statements = formatedQuery.split(';').map((q) => q.trim()); + } else { + statements = splitQueryToStatements(formatedQuery, false); + } const responses: IDataObject[] = []; for (const statement of statements) { From 77d908743651f3c947cd940b482239c6d8c7367d Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Wed, 24 Apr 2024 15:29:03 +0300 Subject: [PATCH 2/3] :zap: tests --- .../nodes/MySql/test/v2/utils.test.ts | 27 +++++++++++++++++++ .../nodes/MySql/v2/helpers/utils.ts | 9 ++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/packages/nodes-base/nodes/MySql/test/v2/utils.test.ts b/packages/nodes-base/nodes/MySql/test/v2/utils.test.ts index 1f98b9adf2884..92bfa58ed0862 100644 --- a/packages/nodes-base/nodes/MySql/test/v2/utils.test.ts +++ b/packages/nodes-base/nodes/MySql/test/v2/utils.test.ts @@ -8,6 +8,7 @@ import { addSortRules, replaceEmptyStringsByNulls, escapeSqlIdentifier, + splitQueryToStatements, } from '../../v2/helpers/utils'; const mySqlMockNode: INode = { @@ -175,3 +176,29 @@ describe('Test MySql V2, escapeSqlIdentifier', () => { expect(escapedIdentifier).toEqual('`db_name`.`some.dotted.tbl_name`'); }); }); + +describe('Test MySql V2, splitQueryToStatements', () => { + it('should split query into statements', () => { + const query = + "insert into models (`created_at`, custom_ship_time, id) values ('2023-09-07 10:26:20', 'some random; data with a semicolon', 1); insert into models (`created_at`, custom_ship_time, id) values ('2023-09-07 10:27:55', 'random data without semicolon\n', 2);"; + + const statements = splitQueryToStatements(query); + + expect(statements).toBeDefined(); + expect(statements).toEqual([ + "insert into models (`created_at`, custom_ship_time, id) values ('2023-09-07 10:26:20', 'some random; data with a semicolon', 1)", + "insert into models (`created_at`, custom_ship_time, id) values ('2023-09-07 10:27:55', 'random data without semicolon', 2)", + ]); + }); + it('should not split by ; inside string literal', () => { + const query = + "SELECT custom_ship_time FROM models WHERE models.custom_ship_time LIKE CONCAT('%', ';', '%') LIMIT 10"; + + const statements = splitQueryToStatements(query); + + expect(statements).toBeDefined(); + expect(statements).toEqual([ + "SELECT custom_ship_time FROM models WHERE models.custom_ship_time LIKE CONCAT('%', ';', '%') LIMIT 10", + ]); + }); +}); diff --git a/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts b/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts index 16261f8f639db..19de10d683fa6 100644 --- a/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts +++ b/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts @@ -129,7 +129,7 @@ export function wrapData(data: IDataObject | IDataObject[]): INodeExecutionData[ })); } -function prepareOutput( +export function prepareOutput( response: IDataObject[], options: IDataObject, statements: string[], @@ -191,8 +191,11 @@ function prepareOutput( return returnData; } const END_OF_STATMENT = /;(?=(?:[^'\\]|'[^']*?'|\\[\s\S])*?$)/g; -const splitQueryToStatements = (query: string, filterOutEmpty = true) => { - const statements = query.replace(/\n/g, '').split(END_OF_STATMENT); +export const splitQueryToStatements = (query: string, filterOutEmpty = true) => { + const statements = query + .replace(/\n/g, '') + .split(END_OF_STATMENT) + .map((statement) => statement.trim()); return filterOutEmpty ? statements.filter((statement) => statement !== '') : statements; }; From 2d15e712b02da9e01190ffcaad6080e873c59fa5 Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Thu, 25 Apr 2024 14:26:48 +0300 Subject: [PATCH 3/3] :zap: typo fix --- packages/nodes-base/nodes/MySql/v2/helpers/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts b/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts index 19de10d683fa6..40b0f30b9c9dc 100644 --- a/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts +++ b/packages/nodes-base/nodes/MySql/v2/helpers/utils.ts @@ -190,11 +190,11 @@ export function prepareOutput( return returnData; } -const END_OF_STATMENT = /;(?=(?:[^'\\]|'[^']*?'|\\[\s\S])*?$)/g; +const END_OF_STATEMENT = /;(?=(?:[^'\\]|'[^']*?'|\\[\s\S])*?$)/g; export const splitQueryToStatements = (query: string, filterOutEmpty = true) => { const statements = query .replace(/\n/g, '') - .split(END_OF_STATMENT) + .split(END_OF_STATEMENT) .map((statement) => statement.trim()); return filterOutEmpty ? statements.filter((statement) => statement !== '') : statements; };