Skip to content

Commit 8263b6b

Browse files
Merge pull request sailshq#8 from treelinehq/update-inputs
Update inputs
2 parents 04d95e0 + 55005a4 commit 8263b6b

File tree

4 files changed

+58
-41
lines changed

4 files changed

+58
-41
lines changed

machines/compile-statement.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ module.exports = {
4848
outputDescription: 'The `nativeQuery` property is the compiled native query for the database. The `meta` property is reserved for custom driver-specific extensions.',
4949
example: '==='
5050
// example: {
51-
// nativeQuery: '*',
51+
// nativeQuery: 'SELECT * FROM foo',
52+
// valuesToEscape: ['foo']
5253
// meta: '==='
5354
// }
5455
},
@@ -105,9 +106,16 @@ module.exports = {
105106
return exits.error(err);
106107
}
107108

109+
110+
// Attach a flag to the meta object to denote that the query was generated
111+
// with Knex and that it's valuesToEscape don't need to be processed any further.
112+
var meta = inputs.meta || {};
113+
meta.isUsingQuestionMarks = true;
114+
108115
return exits.success({
109-
nativeQuery: compiledNativeQuery,
110-
meta: inputs.meta
116+
nativeQuery: compiledNativeQuery.sql,
117+
valuesToEscape: compiledNativeQuery.bindings || [],
118+
meta: meta
111119
});
112120
}
113121

machines/send-native-query.js

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,23 @@ module.exports = {
2323
},
2424

2525
nativeQuery: {
26-
description: 'A SQL statement as a string (or to use built-in escaping, this should be provided as a dictionary).',
27-
extendedDescription: 'If provided as a dictionary, this should contain `sql` (the SQL statement string; e.g. \'SELECT * FROM dogs WHERE name = ?\') as well as an array of `bindings` (e.g. [\'David\']).',
28-
moreInfoUrl: 'https://github.com/felixge/node-mysql#performing-queries',
26+
description: 'A native query for the database.',
27+
extendedDescription: 'If `valuesToEscape` is provided, this supports template syntax like `$1`, `$2`, etc.',
2928
whereToGet: {
30-
description: 'This is oftentimes compiled from Waterline query syntax using "Compile statement", however it could also originate from userland code.',
29+
description: 'Write a native query for this database, or if this driver supports it, use `compileStatement()` to build a native query from Waterline syntax.',
30+
extendedDescription: 'This might be compiled from a Waterline statement (stage 4 query) using "Compile statement", however it could also originate directly from userland code.'
3131
},
32-
example: '===',
33-
// example: '*',
32+
example: 'SELECT * FROM pets WHERE species=$1 AND nickname=$2',
3433
required: true
3534
},
3635

36+
valuesToEscape: {
37+
description: 'An optional list of strings, numbers, or special literals (true, false, or null) to escape and include in the native query, in order.',
38+
extendedDescription: 'The first value in the list will be used to replace `$1`, the second value to replace `$2`, and so on. Note that numbers, `true`, `false`, and `null` are interpreted _differently_ than if they were strings wrapped in quotes. This array must never contain any arrays or dictionaries.',
39+
example: '===',
40+
defaultsTo: []
41+
},
42+
3743
meta: {
3844
friendlyName: 'Meta (custom)',
3945
description: 'Additional stuff to pass to the driver.',
@@ -95,49 +101,52 @@ module.exports = {
95101
}
96102

97103

98-
// Validate query
99-
// (supports raw SQL string or dictionary consisting of `sql` and `bindings` properties)
100-
var sql;
101-
var bindings = [];
102-
103-
if (_.isString(inputs.nativeQuery)) {
104-
sql = inputs.nativeQuery;
105-
} else if (_.isObject(inputs.nativeQuery) && _.isString(inputs.nativeQuery.sql)) {
106-
sql = inputs.nativeQuery.sql;
107-
if (_.isArray(inputs.nativeQuery.bindings)) {
108-
bindings = inputs.nativeQuery.bindings;
109-
}
110-
} else {
111-
return exits.error(new Error('Provided `nativeQuery` is invalid. Please specify either a string of raw SQL or a dictionary like `{sql: \'SELECT * FROM dogs WHERE name = $1\', bindings: [\'Rover\']}`.'));
112-
}
104+
// Validate provided native query.
105+
var sql = inputs.nativeQuery;
106+
var bindings = inputs.valuesToEscape || [];
107+
var queryInfo;
113108

114109

115110
debug('Running SQL Query:');
116111
debug('SQL: ' + sql);
117112
debug('Bindings: ' + bindings);
118113
debug('Connection Id: ' + inputs.connection.id);
119114

120-
// Process SQL template, escaping bindings.
121-
// This converts `$1`, `$2`, etc. into the escaped binding.
122-
sql = sql.replace(/\$[1-9][0-9]*/g, function (substr){
123-
124-
// e.g. `'$3'` => `'3'` => `3` => `2`
125-
var idx = +( substr.slice(1) ) - 1;
115+
// If the meta flag is defined and it has a flag titled `isUsingQuestionMarks`
116+
// then the query was generated by Knex in compileStatement and the query
117+
// string is using `?` in place of values rather than the Waterline standardized
118+
// $1, $2, etc.
119+
if (!inputs.meta || !inputs.meta.isUsingQuestionMarks) {
120+
// Process SQL template, escaping bindings.
121+
// This converts `$1`, `$2`, etc. into the escaped binding.
122+
sql = sql.replace(/\$[1-9][0-9]*/g, function (substr){
123+
124+
// e.g. `'$3'` => `'3'` => `3` => `2`
125+
var idx = +( substr.slice(1) ) - 1;
126+
127+
// If no such binding exists, then just leave the original
128+
// template string (e.g. "$3") alone.
129+
if (idx >= bindings.length) {
130+
return substr;
131+
}
126132

127-
// If no such binding exists, then just leave the original
128-
// template string (e.g. "$3") alone.
129-
if (idx >= bindings.length) {
130-
return substr;
131-
}
133+
// But otherwise, replace it with the escaped binding.
134+
return inputs.connection.escape(bindings[idx]);
135+
});
132136

133-
// But otherwise, replace it with the escaped binding.
134-
return inputs.connection.escape(bindings[idx]);
135-
});
137+
// In this case the query has the values inline.
138+
queryInfo = sql;
139+
} else {
140+
queryInfo = {
141+
sql: sql,
142+
values: bindings
143+
};
144+
}
136145

137146
debug('Compiled (final) SQL: ' + sql);
138147

139148
// Send native query to the database using node-mysql.
140-
inputs.connection.query(sql, function query() {
149+
inputs.connection.query(queryInfo, function query() {
141150
// The exact format of the arguments for this callback are not part of
142151
// the officially documented behavior of node-mysql (at least not as
143152
// of March 2016 when this comment is being written).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "machinepack-mysql",
3-
"version": "2.0.0-3",
3+
"version": "2.0.0-4",
44
"description": "Structured Node.js bindings for MySQL.",
55
"scripts": {
66
"test": "node ./node_modules/mocha/bin/mocha --recursive",

test/queryable/compile-statement.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('Queryable ::', function() {
1515
return done(err);
1616
}
1717

18-
assert.equal(report.nativeQuery.sql, 'select `title`, `author`, `year` from `books`');
18+
assert.equal(report.nativeQuery, 'select `title`, `author`, `year` from `books`');
1919
return done();
2020
});
2121
});

0 commit comments

Comments
 (0)