0

Basically when insert into table SET ? is used, the mysql npm package is scapping the quotes and that throws a parser error.

Code

var rows = [ { name: '100',
    value: '100',
    description: '100',
    type: 'P',
    scope: 'L' },
  { name: '101',
    value: '101',
    description: '101',
    type: 'P',
    scope: 'L' } ];

var sql = `INSERT into mytable SET ?;INSERT into mytable SET ?;`

connection.query(sql, rows, function(bulkInsertErr, bulkResult) {
  ....
});

Error Log

{ Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near
 'INSERT into variable SET `name` = '101', `value` = '101', `description` = '101',' at line 1
    at Query.Sequence._packetToError (/foo/bar/node_modules/mysql/lib/protocol/sequences/Sequence.js:47:14)
    at Query.ErrorPacket (/foo/bar/node_modules/mysql/lib/protocol/sequences/Query.js:79:18)
    at Protocol._parsePacket (/foo/bar/node_modules/mysql/lib/protocol/Protocol.js:291:23)
    at Parser._parsePacket (/foo/bar/node_modules/mysql/lib/protocol/Parser.js:433:10)
    at Parser.write (/foo/bar/node_modules/mysql/lib/protocol/Parser.js:43:10)
    at Protocol.write (/foo/bar/node_modules/mysql/lib/protocol/Protocol.js:38:16)
    at Socket.<anonymous> (/foo/bar/node_modules/mysql/lib/Connection.js:88:28)
    at Socket.<anonymous> (/foo/bar/node_modules/mysql/lib/Connection.js:526:10)
    at Socket.emit (events.js:198:13)
    at addChunk (_stream_readable.js:288:12)
    --------------------
    at Protocol._enqueue (/foo/bar/node_modules/mysql/lib/protocol/Protocol.js:144:48)
    at Connection.query (/foo/bar/node_modules/mysql/lib/Connection.js:198:25)
    at /foo/bar/database/repository/VariableRepository.js:124:18
    at DatabaseConnection.getConnection (/foo/bar/database/DatabaseConnection.js:65:9)
    at VariableRepository.bulkInsert (/foo/bar/database/repository/VariableRepository.js:123:24)
    at /foo/bar/routes/ApplicationVariableRouter.js:261:28
    at Query.<anonymous> (/foo/bar/database/repository/ApplicationVariableRepository.js:224:11)
    at Query.<anonymous> (/foo/bar/node_modules/mysql/lib/Connection.js:526:10)
    at Query._callback (/foo/bar/node_modules/mysql/lib/Connection.js:488:16)
    at Query.Sequence.end (/foo/bar/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
  code: 'ER_PARSE_ERROR',
  errno: 1064,
  sqlMessage:
   'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'INSERT into variable SET `name` = \'101\', `value` = \'101\', `description` = \'101\',\' at line 1',
  sqlState: '42000',
  index: 0,
  sql:
   'INSERT into variable SET `name` = \'100\', `value` = \'100\', `description` = \'100\', `type` = \'P\', `scope` = \'L\';INSERT into variable SET `name` = \'101\', `value` = \'101\', `description` = \'101\', `type` = \'P\', `scope` = \'L\';' }

As you can see, the generated sql string is:

INSERT into variable SET `name` = \'100\', `value` = \'100\', `description` = \'100\', `type` = \'P\', `scope` = \'L\';
INSERT into variable SET `name` = \'101\', `value` = \'101\', `description` = \'101\', `type` = \'P\', `scope` = \'L\';

If I paste this to my mysql ide, throws the same error obtained in nodejs:

parser-error

But if I fix replacing \' by ' in the ide, the rows are inserted.

I'm using ubuntu and this npm package "mysql": "2.18.1"

So, how can I fix that?

JRichardsz
  • 14,356
  • 6
  • 59
  • 94
  • What if you change single quotes to double quotes in your Code, e.g. `name: "100"`? – Serg Jul 30 '21 at 18:00
  • The array comes from another process. I will iterate it and try to replace it. Those are string, so I think the error is in SET auto binding for bulks. If I try a bulk with array of values instead array of objects, it works – JRichardsz Jul 30 '21 at 18:05
  • My point is that one should make some efforts to prepare input data in correct format for processing. So, if forming SQL query is your responsibility you need to take steps to transform data to valid format. Otherwise you'll have another problems when the source of data will change. – Serg Jul 30 '21 at 18:10
  • The generate sql string is not `INSERT into variable SET \`name\` = \'100\'` … but `INSERT into variable SET \`name\` = '100' …` the backslashes are due to logging. – t.niese Jul 30 '21 at 20:52
  • @t.niese thanks. If I try to exec that string on the IDE, I get the same error :(. That is why think that is the error – JRichardsz Aug 01 '21 at 01:02
  • @serg in the official docs, its using single quotes. Check [this](https://i.ibb.co/6NTLmdk/Screenshot-from-2021-07-31-20-05-29.png) and [this](https://www.npmjs.com/package/mysql#escaping-query-values) – JRichardsz Aug 01 '21 at 01:07
  • My point for using double quotes was just to distinguish SQL query strings from JavaScript strings to make more clear when the problem is. – Serg Aug 01 '21 at 11:44
  • Why are you attempting to execute the same query, to the same table, twice? Your `sql` query contains 2 placeholders (represented by `?` marks) but you have only provided one variable in the second argument of the query function. – dusthaines Aug 03 '21 at 05:02
  • @dusthaines, Because it is a bulk insert. I'm using the bulk insert with SET method. – JRichardsz Aug 03 '21 at 05:10
  • Only one query is required regardless of how many object entries your array contains. See the examples provided below in my answer. The first option is the approach you want. – dusthaines Aug 03 '21 at 05:17

3 Answers3

0

I'm not entirely sure that it is escaping your single quotes badly when executing the query, I think that it is just escaping them in the debug print.

I do not believe you can use the SET syntax in the mysql library for batch inserts by default. The reason being that you would need to include multiple statements within a single query which is not enabled by default, see here https://www.npmjs.com/package/mysql#multiple-statement-queries.

If you try something like this do you still get the same error?

var rows = [ { name: '100',
    value: '100',
    description: '100',
    type: 'P',
    scope: 'L' },
  { name: '101',
    value: '101',
    description: '101',
    type: 'P',
    scope: 'L' } ];

connection.query(
    'INSERT INTO variable (name, value, description, type, scope ) VALUES ?',
    [rows.map(row => [row.name, row.value, row.description, row.type, row.scope])],
    function(bulkInsertErr, bulkResult) {
  ....
});
Wodlo
  • 847
  • 5
  • 8
  • Thanks for your help. I think your code will not work because when the collection has objects you should use **insert ... SETC ?** . If collection just has values, **insert ... VALUES ?**. Check this https://stackoverflow.com/questions/861722/mysql-insert-into-table-values-vs-insert-into-table-set. I will try!! – JRichardsz Jul 30 '21 at 20:38
  • @JRichardsz I understand that both syntaxes are valid in mysql. However i'm not sure it is possible to do a batch insert using the `SET` syntax via the nodejs `mysql` library. The reason for this is because as you can see in your example in order to use the `SET` syntax you need to use multiple statements within a single query which is not supported. See here https://www.npmjs.com/package/mysql#multiple-statement-queries – Wodlo Jul 30 '21 at 20:59
  • It worked, thank you so much. I will mark as solved if I can't do it with **SET** – JRichardsz Aug 01 '21 at 01:13
0

It seems that the problem is not the escaping (due the fact it is only a view of your error between two '). Furthermore the error is near the second query (with values 101 and not 100, so the first query is correct)

You are using a multiple query statement that is disabled by default

You should:

  • or exec one query at time (with a loop, but be careful with async loops)

  • or use VALUES syntax of mysql that allows you to insert multiple values:

var rows = [
   ['100', '100', '100', 'P', 'L'], 
   ['101', '101', '101', 'P', 'L'] 
];

var sql = `INSERT into mytable (name, value, description, type, scope) VALUES 
${(new Array(rows.length)).fill("(?)").join(",")}
`
// is equivalent to => 'INSERT into mytable (name, value, description, type, scope) VALUES(?),(?)'
  • or enable multiple statements:
mysql.createConnection({multipleStatements: true});
Luca Rainone
  • 16,138
  • 2
  • 38
  • 52
  • Thanks for your help. I was able to insert with your method without multipleStatements param. But as you see, you need to create an array of simple values. My goal is, if I receive an array of objects, insert this array using **SET** instead **VALUES** – JRichardsz Aug 01 '21 at 00:57
  • then use the third option – Luca Rainone Aug 01 '21 at 06:22
0

It's unclear from your example why you want to run the same query twice at the same time. If you would like to do so, you need to enable the multiple queries option for the module. You also need to provide two variables to the second argument of the query function.

Below are two examples. One assumes you want to include all the entries from your array of objects with one query (this is the correct way to do this). The second assumes you want to do that twice as your code snippet seems to suggest - this will create duplicate records and is only provided as a point of reference.

const mysql = require('mysql');

const connection = mysql.createPool({
  connectionLimit: 10,
  host: process.env.DB_HOST || '127.0.0.1',
  user: process.env.DB_USER || 'local_user',
  password: process.env.DB_PASSWORD || 'local_password',
  database: process.env.DB_NAME || 'local_database',
  multipleStatements: true, // necessary to run chained queries
  charset: 'utf8mb4' // necessary if you might need support for emoji characters
});


let rows = [
{
  name: '100',
  value: '100',
  description: '100',
  type: 'P',
  scope: 'L'
},
{
  name: '101',
  value: '101',
  description: '101',
  type: 'P',
  scope: 'L'
}];

// Create two records from the entries in array rows
// with one query
let keys = Object.keys(rows[0]);
let values = rows.map( obj => keys.map( key => obj[key]));
let sql = 'INSERT INTO mytable (' + keys.join(',') + ') ?;'
connection.query(sql, values, function(error, results, fields) {
  if (error) throw error;
  console.log(results);
});

// Create four records from the two entries in array rows
// by running the same query twice
let keys = Object.keys(rows[0]);
let values = rows.map( obj => keys.map( key => obj[key]));
let sql = 'INSERT INTO mytable (' + keys.join(',') + ') ?; INSERT INTO mytable (' + keys.join(',') + ') ?;'
// Note: since we are using two placeholders in the query
// we must provide two variables that will be mapped to each
// in the second argument of the query function.
// Since more that one is provided, they must be provided as
// an array.
connection.query(sql, [values,values], function(error, results, fields) {
  if (error) throw error;
  console.log(results[0]); // the results of the first query
  console.log(results[1]); // the results of the second query
});
dusthaines
  • 1,320
  • 1
  • 11
  • 17
  • Thanks for your help. I remember trying the option **with one query**. I will try again. – JRichardsz Aug 03 '21 at 05:26
  • @JRichardsz, my appologies. Just realized I originally pasted in the wrong code snippet from those I use often. Please see the update I just made which is necessary since your array contains many objects. – dusthaines Aug 03 '21 at 05:41