Skip to content

Commit dc84452

Browse files
fix(MySQL Node): Query to statements splitting fix (#9207)
1 parent 093dcef commit dc84452

File tree

4 files changed

+61
-8
lines changed

4 files changed

+61
-8
lines changed

packages/nodes-base/nodes/MySql/MySql.node.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export class MySql extends VersionedNodeType {
1111
name: 'mySql',
1212
icon: 'file:mysql.svg',
1313
group: ['input'],
14-
defaultVersion: 2.3,
14+
defaultVersion: 2.4,
1515
description: 'Get, add and update data in MySQL',
1616
parameterPane: 'wide',
1717
};
@@ -22,6 +22,7 @@ export class MySql extends VersionedNodeType {
2222
2.1: new MySqlV2(baseDescription),
2323
2.2: new MySqlV2(baseDescription),
2424
2.3: new MySqlV2(baseDescription),
25+
2.4: new MySqlV2(baseDescription),
2526
};
2627

2728
super(nodeVersions, baseDescription);

packages/nodes-base/nodes/MySql/test/v2/utils.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
addSortRules,
99
replaceEmptyStringsByNulls,
1010
escapeSqlIdentifier,
11+
splitQueryToStatements,
1112
} from '../../v2/helpers/utils';
1213

1314
const mySqlMockNode: INode = {
@@ -175,3 +176,29 @@ describe('Test MySql V2, escapeSqlIdentifier', () => {
175176
expect(escapedIdentifier).toEqual('`db_name`.`some.dotted.tbl_name`');
176177
});
177178
});
179+
180+
describe('Test MySql V2, splitQueryToStatements', () => {
181+
it('should split query into statements', () => {
182+
const query =
183+
"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);";
184+
185+
const statements = splitQueryToStatements(query);
186+
187+
expect(statements).toBeDefined();
188+
expect(statements).toEqual([
189+
"insert into models (`created_at`, custom_ship_time, id) values ('2023-09-07 10:26:20', 'some random; data with a semicolon', 1)",
190+
"insert into models (`created_at`, custom_ship_time, id) values ('2023-09-07 10:27:55', 'random data without semicolon', 2)",
191+
]);
192+
});
193+
it('should not split by ; inside string literal', () => {
194+
const query =
195+
"SELECT custom_ship_time FROM models WHERE models.custom_ship_time LIKE CONCAT('%', ';', '%') LIMIT 10";
196+
197+
const statements = splitQueryToStatements(query);
198+
199+
expect(statements).toBeDefined();
200+
expect(statements).toEqual([
201+
"SELECT custom_ship_time FROM models WHERE models.custom_ship_time LIKE CONCAT('%', ';', '%') LIMIT 10",
202+
]);
203+
});
204+
});

packages/nodes-base/nodes/MySql/v2/actions/versionDescription.ts

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

packages/nodes-base/nodes/MySql/v2/helpers/utils.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,14 @@ export function prepareOutput(
190190

191191
return returnData;
192192
}
193+
const END_OF_STATEMENT = /;(?=(?:[^'\\]|'[^']*?'|\\[\s\S])*?$)/g;
194+
export const splitQueryToStatements = (query: string, filterOutEmpty = true) => {
195+
const statements = query
196+
.replace(/\n/g, '')
197+
.split(END_OF_STATEMENT)
198+
.map((statement) => statement.trim());
199+
return filterOutEmpty ? statements.filter((statement) => statement !== '') : statements;
200+
};
193201

194202
export function configureQueryRunner(
195203
this: IExecuteFunctions,
@@ -225,10 +233,15 @@ export function configureQueryRunner(
225233

226234
if (!response) return [];
227235

228-
const statements = singleQuery
229-
.replace(/\n/g, '')
230-
.split(';')
231-
.filter((statement) => statement !== '');
236+
let statements;
237+
if ((options?.nodeVersion as number) <= 2.3) {
238+
statements = singleQuery
239+
.replace(/\n/g, '')
240+
.split(';')
241+
.filter((statement) => statement !== '');
242+
} else {
243+
statements = splitQueryToStatements(singleQuery);
244+
}
232245

233246
if (Array.isArray(response)) {
234247
if (statements.length === 1) response = [response];
@@ -261,7 +274,13 @@ export function configureQueryRunner(
261274
try {
262275
const { query, values } = queryWithValues;
263276
formatedQuery = connection.format(query, values);
264-
const statements = formatedQuery.split(';').map((q) => q.trim());
277+
278+
let statements;
279+
if ((options?.nodeVersion as number) <= 2.3) {
280+
statements = formatedQuery.split(';').map((q) => q.trim());
281+
} else {
282+
statements = splitQueryToStatements(formatedQuery, false);
283+
}
265284

266285
const responses: IDataObject[] = [];
267286
for (const statement of statements) {
@@ -300,7 +319,13 @@ export function configureQueryRunner(
300319
try {
301320
const { query, values } = queryWithValues;
302321
formatedQuery = connection.format(query, values);
303-
const statements = formatedQuery.split(';').map((q) => q.trim());
322+
323+
let statements;
324+
if ((options?.nodeVersion as number) <= 2.3) {
325+
statements = formatedQuery.split(';').map((q) => q.trim());
326+
} else {
327+
statements = splitQueryToStatements(formatedQuery, false);
328+
}
304329

305330
const responses: IDataObject[] = [];
306331
for (const statement of statements) {

0 commit comments

Comments
 (0)