Skip to content

Commit 85eb91e

Browse files
authored
Merge branch 'master' into master
2 parents de837a1 + 4c1f4a9 commit 85eb91e

File tree

9 files changed

+88
-19
lines changed

9 files changed

+88
-19
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.nyc_output/
2+
coverage/

.eslintrc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
"rules": {
66
"comma-dangle": [2, "never"],
77
"consistent-return": 2,
8-
"indent": ["error", 2, {"SwitchCase": 1}],
8+
"eqeqeq": [2, "allow-null"],
9+
"indent": [2, 2, { "VariableDeclarator": 2, "SwitchCase": 1 }],
910
"key-spacing": [2, { "align": { "beforeColon": true, "afterColon": true, "on": "colon" } }],
11+
"keyword-spacing": 2,
12+
"new-parens": 2,
1013
"no-cond-assign": 2,
1114
"no-constant-condition": 2,
1215
"no-control-regex": 2,
@@ -23,6 +26,7 @@
2326
"no-inner-declarations": 2,
2427
"no-invalid-regexp": 2,
2528
"no-irregular-whitespace": 2,
29+
"no-multiple-empty-lines": [2, { "max": 1 }],
2630
"no-negated-in-lhs": 2,
2731
"no-obj-calls": 2,
2832
"no-regex-spaces": 2,
@@ -31,7 +35,9 @@
3135
"no-unexpected-multiline": 2,
3236
"no-unreachable": 2,
3337
"no-unused-vars": 2,
38+
"quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals": true }],
3439
"semi": [2, "always"],
40+
"semi-spacing": 2,
3541
"space-infix-ops": 2,
3642
"use-isnan": 2,
3743
"valid-jsdoc": 2,

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
coverage
33
node_modules
44
npm-debug.log
5+
package-lock.json

.travis.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@ node_js:
99
- "3.3"
1010
- "4.8"
1111
- "5.12"
12-
- "6.10"
12+
- "6.11"
1313
- "7.10"
14+
- "8.4"
1415
sudo: false
16+
dist: precise
1517
cache:
1618
directories:
1719
- node_modules
1820
before_install:
21+
# Skip updating shrinkwrap / lock
22+
- "npm config set shrinkwrap false"
1923
# Setup Node.js version-specific dependencies
2024
- "test $TRAVIS_NODE_VERSION != '0.6' || npm rm --save-dev nyc"
2125
- "test $TRAVIS_NODE_VERSION != '0.8' || npm rm --save-dev nyc"

HISTORY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
unreleased
2+
==========
3+
4+
* Add `.toSqlString()` escape overriding
5+
16
2.2.0 / 2016-11-01
27
==================
38

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ Different value types are escaped differently, here is how:
7070
* Arrays are turned into list, e.g. `['a', 'b']` turns into `'a', 'b'`
7171
* Nested arrays are turned into grouped lists (for bulk inserts), e.g. `[['a',
7272
'b'], ['c', 'd']]` turns into `('a', 'b'), ('c', 'd')`
73+
* Objects that have a `toSqlString` method will have `.toSqlString()` called
74+
and the returned value is used as the raw SQL.
7375
* Objects are turned into `key = 'val'` pairs for each enumerable property on
7476
the object. If the property's value is a function, it is skipped; if the
7577
property's value is an object, toString() is called on it and the returned
@@ -79,20 +81,27 @@ Different value types are escaped differently, here is how:
7981
to insert them as values will trigger MySQL errors until they implement
8082
support.
8183

82-
If you paid attention, you may have noticed that this escaping allows you
83-
to do neat things like this:
84+
You may have noticed that this escaping allows you to do neat things like this:
8485

8586
```js
8687
var post = {id: 1, title: 'Hello MySQL'};
8788
var sql = SqlString.format('INSERT INTO posts SET ?', post);
8889
console.log(sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
8990
```
9091

92+
And the `toSqlString` method allows you to form complex queries with functions:
93+
94+
```js
95+
var CURRENT_TIMESTAMP = { toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } };
96+
var sql = SqlString.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]);
97+
console.log(sql); // UPDATE posts SET modified = CURRENT_TIMESTAMP() WHERE id = 42
98+
```
99+
91100
If you feel the need to escape queries by yourself, you can also use the escaping
92101
function directly:
93102

94103
```js
95-
var sql = 'SELECT * FROM posts WHERE title=' + SqlString.escape("Hello MySQL");
104+
var sql = 'SELECT * FROM posts WHERE title=' + SqlString.escape('Hello MySQL');
96105
console.log(sql); // SELECT * FROM posts WHERE title='Hello MySQL'
97106
```
98107

lib/SqlString.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ SqlString.escape = function escape(val, stringifyObjects, timeZone) {
5151
return SqlString.arrayToList(val, timeZone);
5252
} else if (Buffer.isBuffer(val)) {
5353
return SqlString.bufferToString(val);
54+
} else if (typeof val.toSqlString === 'function') {
55+
return String(val.toSqlString());
5456
} else if (stringifyObjects) {
5557
return escapeString(val.toString());
5658
} else {
@@ -97,8 +99,8 @@ SqlString.format = function format(sql, values, stringifyObjects, timeZone) {
9799

98100
while (valuesIndex < values.length && (match = placeholdersRegex.exec(sql))) {
99101
var value = match[0] === '??'
100-
? SqlString.escapeId(values[valuesIndex])
101-
: SqlString.escape(values[valuesIndex], stringifyObjects, timeZone);
102+
? SqlString.escapeId(values[valuesIndex])
103+
: SqlString.escape(values[valuesIndex], stringifyObjects, timeZone);
102104

103105
result += sql.slice(chunkIndex, match.index) + value;
104106
chunkIndex = placeholdersRegex.lastIndex;
@@ -165,7 +167,7 @@ SqlString.dateToString = function dateToString(date, timeZone) {
165167
};
166168

167169
SqlString.bufferToString = function bufferToString(buffer) {
168-
return "X" + escapeString(buffer.toString('hex'));
170+
return 'X' + escapeString(buffer.toString('hex'));
169171
};
170172

171173
SqlString.objectToValues = function objectToValues(object, timeZone) {
@@ -222,7 +224,7 @@ function convertTimezone(tz) {
222224

223225
var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/);
224226
if (m) {
225-
return (m[1] == '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60;
227+
return (m[1] === '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60;
226228
}
227229
return false;
228230
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"Adri Van Houdt <[email protected]>",
77
"Douglas Christopher Wilson <[email protected]>",
88
"fengmk2 <[email protected]> (http://fengmk2.github.com)",
9+
"Kevin Jose Martin <[email protected]>",
910
"Nathan Woltman <[email protected]>"
1011
],
1112
"license": "MIT",
@@ -17,7 +18,7 @@
1718
],
1819
"repository": "mysqljs/sqlstring",
1920
"devDependencies": {
20-
"eslint": "3.19.0",
21+
"eslint": "4.4.1",
2122
"eslint-plugin-markdown": "1.0.0-beta.6",
2223
"nyc": "10.3.2",
2324
"urun": "0.0.8",

test/unit/test-SqlString.js

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,47 @@ var test = require('utest');
44

55
test('SqlString.escapeId', {
66
'value is quoted': function() {
7-
assert.equal('`id`', SqlString.escapeId('id'));
7+
assert.equal(SqlString.escapeId('id'), '`id`');
88
},
99

1010
'value can be a number': function() {
11-
assert.equal('`42`', SqlString.escapeId(42));
11+
assert.equal(SqlString.escapeId(42), '`42`');
12+
},
13+
14+
'value can be an object': function() {
15+
assert.equal(SqlString.escapeId({}), '`[object Object]`');
16+
},
17+
18+
'value toString is called': function() {
19+
assert.equal(SqlString.escapeId({ toString: function() { return 'foo'; } }), '`foo`');
20+
},
21+
22+
'value toString is quoted': function() {
23+
assert.equal(SqlString.escapeId({ toString: function() { return 'f`oo'; } }), '`f``oo`');
1224
},
1325

1426
'value containing escapes is quoted': function() {
15-
assert.equal('`i``d`', SqlString.escapeId('i`d'));
27+
assert.equal(SqlString.escapeId('i`d'), '`i``d`');
1628
},
1729

1830
'value containing separator is quoted': function() {
19-
assert.equal('`id1`.`id2`', SqlString.escapeId('id1.id2'));
31+
assert.equal(SqlString.escapeId('id1.id2'), '`id1`.`id2`');
2032
},
2133

2234
'value containing separator and escapes is quoted': function() {
23-
assert.equal('`id``1`.`i``d2`', SqlString.escapeId('id`1.i`d2'));
35+
assert.equal(SqlString.escapeId('id`1.i`d2'), '`id``1`.`i``d2`');
2436
},
2537

2638
'value containing separator is fully escaped when forbidQualified': function() {
27-
assert.equal('`id1.id2`', SqlString.escapeId('id1.id2', true));
39+
assert.equal(SqlString.escapeId('id1.id2', true), '`id1.id2`');
2840
},
2941

3042
'arrays are turned into lists': function() {
31-
assert.equal(SqlString.escapeId(['a', 'b', 't.c']), "`a`, `b`, `t`.`c`");
43+
assert.equal(SqlString.escapeId(['a', 'b', 't.c']), '`a`, `b`, `t`.`c`');
3244
},
3345

3446
'nested arrays are flattened': function() {
35-
assert.equal(SqlString.escapeId(['a', ['b', ['t.c']]]), "`a`, `b`, `t`.`c`");
47+
assert.equal(SqlString.escapeId(['a', ['b', ['t.c']]]), '`a`, `b`, `t`.`c`');
3648
},
3749

3850
'instances of Escaped are not quoted': function() {
@@ -66,10 +78,30 @@ test('SqlString.escape', {
6678
assert.equal(SqlString.escape({a: 'b', c: function() {}}), "`a` = 'b'");
6779
},
6880

81+
'object values toSqlString is called': function() {
82+
assert.equal(SqlString.escape({id: { toSqlString: function() { return 'LAST_INSERT_ID()'; } }}), '`id` = LAST_INSERT_ID()');
83+
},
84+
85+
'objects toSqlString is called': function() {
86+
assert.equal(SqlString.escape({ toSqlString: function() { return '@foo_id'; } }), '@foo_id');
87+
},
88+
89+
'objects toSqlString is not quoted': function() {
90+
assert.equal(SqlString.escape({ toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } }), 'CURRENT_TIMESTAMP()');
91+
},
92+
6993
'nested objects are cast to strings': function() {
7094
assert.equal(SqlString.escape({a: {nested: true}}), "`a` = '[object Object]'");
7195
},
7296

97+
'nested objects use toString': function() {
98+
assert.equal(SqlString.escape({a: { toString: function() { return 'foo'; } }}), "`a` = 'foo'");
99+
},
100+
101+
'nested objects use toString is quoted': function() {
102+
assert.equal(SqlString.escape({a: { toString: function() { return "f'oo"; } }}), "`a` = 'f\\'oo'");
103+
},
104+
73105
'arrays are turned into lists': function() {
74106
assert.equal(SqlString.escape([1, 2, 'c']), "1, 2, 'c'");
75107
},
@@ -82,6 +114,10 @@ test('SqlString.escape', {
82114
assert.equal(SqlString.escape([1, {nested: true}, 2]), "1, '[object Object]', 2");
83115
},
84116

117+
'nested objects inside arrays use toString': function() {
118+
assert.equal(SqlString.escape([1, { toString: function() { return 'foo'; } }, 2]), "1, 'foo', 2");
119+
},
120+
85121
'strings are quoted': function() {
86122
assert.equal(SqlString.escape('Super'), "'Super'");
87123
},
@@ -256,6 +292,9 @@ test('SqlString.format', {
256292

257293
var sql = SqlString.format('?', { toString: function () { return 'hello'; } }, true);
258294
assert.equal(sql, "'hello'");
295+
296+
var sql = SqlString.format('?', { toSqlString: function () { return '@foo'; } }, true);
297+
assert.equal(sql, '@foo');
259298
},
260299

261300
'sql is untouched if no values are provided': function () {
@@ -267,4 +306,4 @@ test('SqlString.format', {
267306
var sql = SqlString.format('SELECT COUNT(*) FROM table', ['a', 'b']);
268307
assert.equal(sql, 'SELECT COUNT(*) FROM table');
269308
}
270-
});
309+
});

0 commit comments

Comments
 (0)