From 0f7cdaf7acf6e85f76c35284c5ac424a130a365e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Mar 2022 19:59:02 -0500 Subject: [PATCH 1/3] Remove object key-value-pair escape behavior --- HISTORY.md | 5 +++++ README.md | 24 ++++++-------------- lib/SqlString.js | 26 ++++------------------ test/unit/test-SqlString.js | 44 +++++++++---------------------------- 4 files changed, 26 insertions(+), 73 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index aea1dfc..e170095 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Remove object key-value-pair escape behavior + 2.3.3 / 2022-03-06 ================== diff --git a/README.md b/README.md index a00c560..ec4312e 100644 --- a/README.md +++ b/README.md @@ -75,24 +75,14 @@ Different value types are escaped differently, here is how: 'b'], ['c', 'd']]` turns into `('a', 'b'), ('c', 'd')` * Objects that have a `toSqlString` method will have `.toSqlString()` called and the returned value is used as the raw SQL. -* Objects are turned into `key = 'val'` pairs for each enumerable property on - the object. If the property's value is a function, it is skipped; if the - property's value is an object, toString() is called on it and the returned - value is used. * `undefined` / `null` are converted to `NULL` * `NaN` / `Infinity` are left as-is. MySQL does not support these, and trying to insert them as values will trigger MySQL errors until they implement support. +* All other values types are converted to a string using the global `String()` + and the resulting value is escaped. -You may have noticed that this escaping allows you to do neat things like this: - -```js -var post = {id: 1, title: 'Hello MySQL'}; -var sql = SqlString.format('INSERT INTO posts SET ?', post); -console.log(sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL' -``` - -And the `toSqlString` method allows you to form complex queries with functions: +The `toSqlString` method allows you to form complex queries with functions: ```js var CURRENT_TIMESTAMP = { toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } }; @@ -176,8 +166,7 @@ console.log(sql); // SELECT * FROM `users` WHERE `id` = 1 Following this you then have a valid, escaped query that you can then send to the database safely. This is useful if you are looking to prepare the query before actually sending it to the database. -You also have the option (but are not required) to pass in `stringifyObject` and `timeZone`, -allowing you provide a custom means of turning objects into strings, as well as a +You also have the option (but are not required) to pass in `timeZone`, allowing you provide a location-specific/timezone-aware `Date`. This can be further combined with the `SqlString.raw()` helper to generate SQL @@ -185,8 +174,9 @@ that includes MySQL functions as dynamic vales: ```js var userId = 1; -var data = { email: 'foobar@example.com', modified: SqlString.raw('NOW()') }; -var sql = SqlString.format('UPDATE ?? SET ? WHERE `id` = ?', ['users', data, userId]); +var email = 'foobar@example.com'; +var sql = SqlString.format('UPDATE ?? SET `email` = ?, `modified` = ? WHERE `id` = ?', + ['users', email, SqlString.raw('NOW()'), userId]); console.log(sql); // UPDATE `users` SET `email` = 'foobar@example.com', `modified` = NOW() WHERE `id` = 1 ``` diff --git a/lib/SqlString.js b/lib/SqlString.js index 8206dad..cf610e8 100644 --- a/lib/SqlString.js +++ b/lib/SqlString.js @@ -31,14 +31,14 @@ SqlString.escapeId = function escapeId(val, forbidQualified) { } }; -SqlString.escape = function escape(val, stringifyObjects, timeZone) { +SqlString.escape = function escape(val, timeZone) { if (val === undefined || val === null) { return 'NULL'; } switch (typeof val) { case 'boolean': return (val) ? 'true' : 'false'; - case 'number': return val + ''; + case 'number': return String(val); case 'object': if (Object.prototype.toString.call(val) === '[object Date]') { return SqlString.dateToString(val, timeZone || 'local'); @@ -48,12 +48,10 @@ SqlString.escape = function escape(val, stringifyObjects, timeZone) { return SqlString.bufferToString(val); } else if (typeof val.toSqlString === 'function') { return String(val.toSqlString()); - } else if (stringifyObjects) { - return escapeString(val.toString()); } else { - return SqlString.objectToValues(val, timeZone); + return escapeString(String(val)); } - default: return escapeString(val); + default: return escapeString(String(val)); } }; @@ -167,22 +165,6 @@ SqlString.bufferToString = function bufferToString(buffer) { return 'X' + escapeString(buffer.toString('hex')); }; -SqlString.objectToValues = function objectToValues(object, timeZone) { - var sql = ''; - - for (var key in object) { - var val = object[key]; - - if (typeof val === 'function') { - continue; - } - - sql += (sql.length === 0 ? '' : ', ') + SqlString.escapeId(key) + ' = ' + SqlString.escape(val, true, timeZone); - } - - return sql; -}; - SqlString.raw = function raw(sql) { if (typeof sql !== 'string') { throw new TypeError('argument sql must be a string'); diff --git a/test/unit/test-SqlString.js b/test/unit/test-SqlString.js index 580aa4e..9dea225 100644 --- a/test/unit/test-SqlString.js +++ b/test/unit/test-SqlString.js @@ -71,16 +71,9 @@ test('SqlString.escape', { assert.equal(SqlString.escape(SqlString.raw('NOW()')), 'NOW()'); }, - 'objects are turned into key value pairs': function() { - assert.equal(SqlString.escape({a: 'b', c: 'd'}), "`a` = 'b', `c` = 'd'"); - }, - - 'objects function properties are ignored': function() { - assert.equal(SqlString.escape({a: 'b', c: function() {}}), "`a` = 'b'"); - }, - - 'object values toSqlString is called': function() { - assert.equal(SqlString.escape({id: { toSqlString: function() { return 'LAST_INSERT_ID()'; } }}), '`id` = LAST_INSERT_ID()'); + 'objects are turned into string value': function() { + assert.equal(SqlString.escape({ 'hello': 'world' }), "'[object Object]'"); + assert.equal(SqlString.escape({ toString: function () { return 'hello'; } }), "'hello'"); }, 'objects toSqlString is called': function() { @@ -91,18 +84,6 @@ test('SqlString.escape', { assert.equal(SqlString.escape({ toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } }), 'CURRENT_TIMESTAMP()'); }, - 'nested objects are cast to strings': function() { - assert.equal(SqlString.escape({a: {nested: true}}), "`a` = '[object Object]'"); - }, - - 'nested objects use toString': function() { - assert.equal(SqlString.escape({a: { toString: function() { return 'foo'; } }}), "`a` = 'foo'"); - }, - - 'nested objects use toString is quoted': function() { - assert.equal(SqlString.escape({a: { toString: function() { return "f'oo"; } }}), "`a` = 'f\\'oo'"); - }, - 'arrays are turned into lists': function() { assert.equal(SqlString.escape([1, 2, 'c']), "1, 2, 'c'"); }, @@ -179,7 +160,7 @@ test('SqlString.escape', { 'dates are converted to specified time zone "Z"': function() { var expected = '2012-05-07 11:42:03.002'; var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); - var string = SqlString.escape(date, false, 'Z'); + var string = SqlString.escape(date, 'Z'); assert.strictEqual(string, "'" + expected + "'"); }, @@ -187,7 +168,7 @@ test('SqlString.escape', { 'dates are converted to specified time zone "+01"': function() { var expected = '2012-05-07 12:42:03.002'; var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); - var string = SqlString.escape(date, false, '+01'); + var string = SqlString.escape(date, '+01'); assert.strictEqual(string, "'" + expected + "'"); }, @@ -195,7 +176,7 @@ test('SqlString.escape', { 'dates are converted to specified time zone "+0200"': function() { var expected = '2012-05-07 13:42:03.002'; var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); - var string = SqlString.escape(date, false, '+0200'); + var string = SqlString.escape(date, '+0200'); assert.strictEqual(string, "'" + expected + "'"); }, @@ -203,15 +184,15 @@ test('SqlString.escape', { 'dates are converted to specified time zone "-05:00"': function() { var expected = '2012-05-07 06:42:03.002'; var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); - var string = SqlString.escape(date, false, '-05:00'); + var string = SqlString.escape(date, '-05:00'); assert.strictEqual(string, "'" + expected + "'"); }, 'dates are converted to UTC for unknown time zone': function() { var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); - var expected = SqlString.escape(date, false, 'Z'); - var string = SqlString.escape(date, false, 'foo'); + var expected = SqlString.escape(date, 'Z'); + var string = SqlString.escape(date, 'foo'); assert.strictEqual(string, expected); }, @@ -291,13 +272,8 @@ test('SqlString.format', { assert.equal(sql, '?'); }, - 'objects is converted to values': function () { + 'objects is converted to string value': function () { var sql = SqlString.format('?', { 'hello': 'world' }, false); - assert.equal(sql, "`hello` = 'world'"); - }, - - 'objects is not converted to values': function () { - var sql = SqlString.format('?', { 'hello': 'world' }, true); assert.equal(sql, "'[object Object]'"); var sql = SqlString.format('?', { toString: function () { return 'hello'; } }, true); From 9d7cb825165ac20a4bce9386b59f8f8c949959e3 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Mar 2022 20:08:11 -0500 Subject: [PATCH 2/3] Remove array escape behavior --- HISTORY.md | 1 + README.md | 6 +----- lib/SqlString.js | 28 +--------------------------- test/unit/test-SqlString.js | 24 ++++-------------------- 4 files changed, 7 insertions(+), 52 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e170095..365947e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,7 @@ unreleased ========== + * Remove array escape behavior * Remove object key-value-pair escape behavior 2.3.3 / 2022-03-06 diff --git a/README.md b/README.md index ec4312e..035b8cf 100644 --- a/README.md +++ b/README.md @@ -70,9 +70,6 @@ Different value types are escaped differently, here is how: * Date objects are converted to `'YYYY-mm-dd HH:ii:ss'` strings * Buffers are converted to hex strings, e.g. `X'0fa5'` * Strings are safely escaped -* Arrays are turned into list, e.g. `['a', 'b']` turns into `'a', 'b'` -* Nested arrays are turned into grouped lists (for bulk inserts), e.g. `[['a', - 'b'], ['c', 'd']]` turns into `('a', 'b'), ('c', 'd')` * Objects that have a `toSqlString` method will have `.toSqlString()` called and the returned value is used as the raw SQL. * `undefined` / `null` are converted to `NULL` @@ -144,8 +141,7 @@ like to have escaped like this: ```js var userId = 1; -var columns = ['username', 'email']; -var sql = SqlString.format('SELECT ?? FROM ?? WHERE id = ?', [columns, 'users', userId]); +var sql = SqlString.format('SELECT ??, ?? FROM ?? WHERE id = ?', ['username', 'email', 'users', userId]); console.log(sql); // SELECT `username`, `email` FROM `users` WHERE id = 1 ``` **Please note that this last character sequence is experimental and syntax might change** diff --git a/lib/SqlString.js b/lib/SqlString.js index cf610e8..e945d43 100644 --- a/lib/SqlString.js +++ b/lib/SqlString.js @@ -16,15 +16,7 @@ var CHARS_ESCAPE_MAP = { }; SqlString.escapeId = function escapeId(val, forbidQualified) { - if (Array.isArray(val)) { - var sql = ''; - - for (var i = 0; i < val.length; i++) { - sql += (i === 0 ? '' : ', ') + SqlString.escapeId(val[i], forbidQualified); - } - - return sql; - } else if (forbidQualified) { + if (forbidQualified) { return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``') + '`'; } else { return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``').replace(QUAL_GLOBAL_REGEXP, '`.`') + '`'; @@ -42,8 +34,6 @@ SqlString.escape = function escape(val, timeZone) { case 'object': if (Object.prototype.toString.call(val) === '[object Date]') { return SqlString.dateToString(val, timeZone || 'local'); - } else if (Array.isArray(val)) { - return SqlString.arrayToList(val, timeZone); } else if (Buffer.isBuffer(val)) { return SqlString.bufferToString(val); } else if (typeof val.toSqlString === 'function') { @@ -55,22 +45,6 @@ SqlString.escape = function escape(val, timeZone) { } }; -SqlString.arrayToList = function arrayToList(array, timeZone) { - var sql = ''; - - for (var i = 0; i < array.length; i++) { - var val = array[i]; - - if (Array.isArray(val)) { - sql += (i === 0 ? '' : ', ') + '(' + SqlString.arrayToList(val, timeZone) + ')'; - } else { - sql += (i === 0 ? '' : ', ') + SqlString.escape(val, true, timeZone); - } - } - - return sql; -}; - SqlString.format = function format(sql, values, stringifyObjects, timeZone) { if (values == null) { return sql; diff --git a/test/unit/test-SqlString.js b/test/unit/test-SqlString.js index 9dea225..a8bcc0d 100644 --- a/test/unit/test-SqlString.js +++ b/test/unit/test-SqlString.js @@ -40,12 +40,8 @@ test('SqlString.escapeId', { assert.equal(SqlString.escapeId('id1.id2', true), '`id1.id2`'); }, - 'arrays are turned into lists': function() { - assert.equal(SqlString.escapeId(['a', 'b', 't.c']), '`a`, `b`, `t`.`c`'); - }, - - 'nested arrays are flattened': function() { - assert.equal(SqlString.escapeId(['a', ['b', ['t.c']]]), '`a`, `b`, `t`.`c`'); + 'arrays are stringified and then escaped': function() { + assert.equal(SqlString.escapeId(['a', 'b', 'c']), '`a,b,c`'); } }); @@ -84,20 +80,8 @@ test('SqlString.escape', { assert.equal(SqlString.escape({ toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } }), 'CURRENT_TIMESTAMP()'); }, - 'arrays are turned into lists': function() { - assert.equal(SqlString.escape([1, 2, 'c']), "1, 2, 'c'"); - }, - - 'nested arrays are turned into grouped lists': function() { - assert.equal(SqlString.escape([[1, 2, 3], [4, 5, 6], ['a', 'b', {nested: true}]]), "(1, 2, 3), (4, 5, 6), ('a', 'b', '[object Object]')"); - }, - - 'nested objects inside arrays are cast to strings': function() { - assert.equal(SqlString.escape([1, {nested: true}, 2]), "1, '[object Object]', 2"); - }, - - 'nested objects inside arrays use toString': function() { - assert.equal(SqlString.escape([1, { toString: function() { return 'foo'; } }, 2]), "1, 'foo', 2"); + 'arrays are stringified and escaped': function() { + assert.equal(SqlString.escape([1, 2, 'c']), "'1,2,c'"); }, 'strings are quoted': function() { From baa8c98a8fc87f8a4dfec369d54cfd8a2af8ecb6 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Mar 2022 20:11:12 -0500 Subject: [PATCH 3/3] Change identifier escaping to always be literal --- HISTORY.md | 1 + README.md | 17 ----------------- lib/SqlString.js | 9 ++------- test/unit/test-SqlString.js | 6 +----- 4 files changed, 4 insertions(+), 29 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 365947e..5662115 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,7 @@ unreleased ========== + * Change identifier escaping to always be literal * Remove array escape behavior * Remove object key-value-pair escape behavior diff --git a/README.md b/README.md index 035b8cf..8f645b1 100644 --- a/README.md +++ b/README.md @@ -119,23 +119,6 @@ var sql = 'SELECT * FROM posts ORDER BY ' + SqlString.escapeId(sorter); console.log(sql); // SELECT * FROM posts ORDER BY `date` ``` -It also supports adding qualified identifiers. It will escape both parts. - -```js -var sorter = 'date'; -var sql = 'SELECT * FROM posts ORDER BY ' + SqlString.escapeId('posts.' + sorter); -console.log(sql); // SELECT * FROM posts ORDER BY `posts`.`date` -``` - -If you do not want to treat `.` as qualified identifiers, you can set the second -argument to `true` in order to keep the string as a literal identifier: - -```js -var sorter = 'date.2'; -var sql = 'SELECT * FROM posts ORDER BY ' + SqlString.escapeId(sorter, true); -console.log(sql); // SELECT * FROM posts ORDER BY `date.2` -``` - Alternatively, you can use `??` characters as placeholders for identifiers you would like to have escaped like this: diff --git a/lib/SqlString.js b/lib/SqlString.js index e945d43..14babef 100644 --- a/lib/SqlString.js +++ b/lib/SqlString.js @@ -1,7 +1,6 @@ var SqlString = exports; var ID_GLOBAL_REGEXP = /`/g; -var QUAL_GLOBAL_REGEXP = /\./g; var CHARS_GLOBAL_REGEXP = /[\0\b\t\n\r\x1a\"\'\\]/g; // eslint-disable-line no-control-regex var CHARS_ESCAPE_MAP = { '\0' : '\\0', @@ -15,12 +14,8 @@ var CHARS_ESCAPE_MAP = { '\\' : '\\\\' }; -SqlString.escapeId = function escapeId(val, forbidQualified) { - if (forbidQualified) { - return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``') + '`'; - } else { - return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``').replace(QUAL_GLOBAL_REGEXP, '`.`') + '`'; - } +SqlString.escapeId = function escapeId(val) { + return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``') + '`'; }; SqlString.escape = function escape(val, timeZone) { diff --git a/test/unit/test-SqlString.js b/test/unit/test-SqlString.js index a8bcc0d..3f5b086 100644 --- a/test/unit/test-SqlString.js +++ b/test/unit/test-SqlString.js @@ -29,11 +29,7 @@ test('SqlString.escapeId', { }, 'value containing separator is quoted': function() { - assert.equal(SqlString.escapeId('id1.id2'), '`id1`.`id2`'); - }, - - 'value containing separator and escapes is quoted': function() { - assert.equal(SqlString.escapeId('id`1.i`d2'), '`id``1`.`i``d2`'); + assert.equal(SqlString.escapeId('id1.id2'), '`id1.id2`'); }, 'value containing separator is fully escaped when forbidQualified': function() { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy