Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Named Parameters #496 #531

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,72 @@ db.driver.execQuery(
)
```

#### Raw queries with Named Parameters

You can also use named parameters via db.driver.execNamedQuery.

You can begin with "::" for identifiers and other meta values, but beware that they are not escaped, only replaced as is.

Generally this should not be an issue, but make sure to understand what this means.

For value substitution begin with: ":".

Same named parameter can be used multiple times within the query.

```js
var query = 'SELECT ::table.* FROM ::table, ::joined WHERE id = :id AND (name = :name OR name0 = :name0 OR nick = :name) OFFSET ::offset LIMIT ::limit';
var callback = function(err, data){ ... }
var obj = {id:"245", name0:"Jane", name:"John"};
var meta = {table:'users', joined:'orders', limit:7, offset:3};
db.driver.execNamedQuery(query, callback, obj, meta);
```

One can test the resulting query and the parameters the function produces by passing in a true value for the fifth argument.

If the query contains manually written statements that contain a pattern a similar to '":parameter"' and the parameter field exists in the 'obj' or 'meta' variables defined above, then those statements will be replaced as well.

To fix this issue, custom regexes can be provided as the last and sixth argument.

```js
var query = 'SELECT !::table.* FROM !::table, !::joined WHERE id = !:id AND (name = !:name OR name0 = !:name0 OR nick = !:name) AND controlField = ":name" AND controlMeta = "::table" OFFSET !::offset LIMIT !::limit';
```

We define regexes that replace "!:" and "!::" instead of the default ":" and "::".
```js
var rgx = {values: /!:(\w+)/g, meta: /!::(\w+)/g};
var unit_test = true;
```

By passing in the unit_test "true" argument as the fifth argument, the query is not executed but the processed query and prepared parameters are returned for inspection.

If the regexes are passed in, but you want to execute the query, make sure to provide false value for the fifth argument.

```js
var data = db.driver.execNamedQuery(query, cb, obj, meta, unit_test, rgx);
console.log(data.sql);
```

```sql
SELECT users.* FROM users, orders
WHERE id = ? AND (name = ? OR name0 = ? OR nick = ?) AND
controlField = ":name" AND controlMeta = "::table"
OFFSET 3 LIMIT 7
```

```js
console.log(data.sql);
//["245", "John", "Jane", "John"]
```

If we did not use custom regexes, then we would have received 'controlField = "?" AND controlMeta = "users"', which is not what we want.

With the data returned from the test, you can also go ahead and execute it if you want.

This is very similar to the way the query is executed internally through db.driver.execNamedQuery
```js
db.driver.execQuery(data.sql, data.values, callback);
```

### Caching & Integrity

Model instances are cached. If multiple different queries will result in the same result, you will
Expand Down
30 changes: 29 additions & 1 deletion lib/Drivers/DML/_shared.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

var RGX_COLON_SINGLE = /:(\w+)/g;
var RGX_COLON_DOUBLE = /::(\w+)/g;
module.exports = {
execQuery: function () {
if (arguments.length == 2) {
Expand All @@ -10,6 +11,33 @@ module.exports = {
}
return this.execSimpleQuery(query, cb);
},
execNamedQuery: function (query, cb, obj, meta, unit_test, rgx) {
var values = [];
var rgx1 = (rgx && rgx.values) ? rgx.values : RGX_COLON_SINGLE;
var rgx2 = (rgx && rgx.meta) ? rgx.meta : RGX_COLON_DOUBLE;
if(meta){
query = query.replace(rgx2, function(txt, key){
if(meta.hasOwnProperty(key)){
return meta[key];
}
return txt;
});
}
if(obj){
query = query.replace(rgx1, function(txt, key){
if(obj.hasOwnProperty(key)){
values.push(obj[key]);
return '?';
}
return txt;
});
}
if(unit_test){
return {sql:query, values:values};
}
query = this.query.escape(query, values);
return this.execSimpleQuery(query, cb);
},
eagerQuery: function (association, opts, keys, cb) {
var desiredKey = Object.keys(association.field);
var assocKey = Object.keys(association.mergeAssocId);
Expand Down
29 changes: 29 additions & 0 deletions test/integration/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,5 +289,34 @@ describe("db.driver", function () {
});
});
});

describe("#execNamedQuery", function () {
var obj = {id:"245", name0:"Jane", name:"John"};
var meta = {table:'users', joined:'orders', limit:7, offset:3};
var unit_test = true;
var cb = function(){};
it("default regexes work as expected", function (done) {
var query = 'SELECT ::table.* FROM ::table, ::joined WHERE id = :id AND (name = :name OR name0 = :name0 OR nick = :name) OFFSET ::offset LIMIT ::limit';
var data = db.driver.execNamedQuery(query, cb, obj, meta, unit_test);
should(data.sql === 'SELECT users.* FROM users, orders WHERE id = ? AND (name = ? OR name0 = ? OR nick = ?) OFFSET 3 LIMIT 7');
should(data.values[0] === "245");
should(data.values[1] === "John");
should(data.values[2] === "Jane");
should(data.values[3] === "John");
done();
});

it("different symbols can be used with custom regexes to preserve text within quotes", function (done) {
var query = 'SELECT !::table.* FROM !::table, !::joined WHERE id = !:id AND (name = !:name OR name0 = !:name0 OR nick = !:name) AND control_field = ":name" AND control_meta = "::table" OFFSET !::offset LIMIT !::limit';
var rgx = {values: /!:(\w+)/g, meta: /!::(\w+)/g};
var data = db.driver.execNamedQuery(query, cb, obj, meta, unit_test, rgx);
should(data.sql === 'SELECT users.* FROM users, orders WHERE id = ? AND (name = ? OR name0 = ? OR nick = ?) AND control_field = ":name" AND control_meta = "::table" OFFSET 3 LIMIT 7');
should(data.values[0] === "245");
should(data.values[1] === "John");
should(data.values[2] === "Jane");
should(data.values[3] === "John");
done();
});
});
});
});