Skip to content

Get formatted query from Pool.query() method #991

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

Open
wants to merge 7 commits 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
6 changes: 4 additions & 2 deletions lib/Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,10 @@ Connection.prototype.query = function(sql, values, cb) {
if (!(typeof sql == 'object' && 'typeCast' in sql)) {
query.typeCast = this.config.typeCast;
}

query.sql = this.format(query.sql, query.values);
if (!query.isFormatted) {
query.sql = this.format(query.sql, query.values);
query.isFormatted = true;
}

return this._protocol._enqueue(query);
};
Expand Down
11 changes: 11 additions & 0 deletions lib/Pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ Pool.prototype.query = function (sql, values, cb) {
conn.query(query);
});

if (!query.isFormatted) {
query.isFormatted = true;
query.sql = this.format(query.sql, query.values);
}
return query;
};

Expand Down Expand Up @@ -255,6 +259,13 @@ Pool.prototype.escapeId = function escapeId(value) {
return mysql.escapeId(value, false);
};

Pool.prototype.format = function(sql, values) {
if (typeof this.config.connectionConfig.queryFormat == "function") {
return this.config.connectionConfig.queryFormat.call(this, sql, values, this.config.connectionConfig.timezone);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to think about if it's acceptable that the context of the queryFormat call is not going to be a Connection object in these cases and if this is going to be an issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did find a solution to keep createQuery as a static method of Connection and provide the right context for instance config, which keeps things backward compatible. I'll commit the changes later from another machine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it take care of this context issue, though?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise there is no need to add a new commit, really. I've already adjusted your commit to make it an instance method (still under your name). The problem right now is backwards compatibility with this function context.

}
return mysql.format(sql, values, this.config.connectionConfig.stringifyObjects, this.config.connectionConfig.timezone);
};

function spliceConnection(array, connection) {
var index;
if ((index = array.indexOf(connection)) !== -1) {
Expand Down
1 change: 1 addition & 0 deletions lib/protocol/sequences/Query.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Util.inherits(Query, Sequence);
function Query(options, callback) {
Sequence.call(this, options, callback);

this.isFormatted = false;
this.sql = options.sql;
this.values = options.values;
this.typeCast = (options.typeCast === undefined)
Expand Down
60 changes: 60 additions & 0 deletions test/unit/pool/test-query-return-formatted.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
var assert = require('assert');
var common = require('../../common');
var pool1 = common.createPool({port: common.fakeServerPort});
var pool2 = common.createPool({port: common.fakeServerPort, queryFormat: queryFormat});


function queryFormat(query, values, tz) {
if (!values) {
return query;
}

var escape = this.escape.bind(this);

return query.replace(/\:(\w+)/g, function (txt, key) {
if (values.hasOwnProperty(key)) {
return escape(values[key]);
}
return txt;
});
}

var server = common.createFakeServer();

server.listen(common.fakeServerPort, function (err) {
assert.ifError(err);



// When the fix is applied, this query demonstrates that connection.query(),
// which gets invoked internally by pool.query(), will not re-format the query
// a second time. Re-formatting the query a second time in this case would cause
// an error when the question marks inside the string 'John ? J ?' are interpreted
// as placeholders for values.

assert.equal(pool1.query('SELECT ?? FROM ?? WHERE id=? OR name=?', [['name', 'rdate'], 'dummytable', 1, "John ? J ?"]).sql, 'SELECT `name`, `rdate` FROM `dummytable` WHERE id=1 OR name=\'John ? J ?\'');



// This one will will fail before the fix is applied

assert.equal(pool2.query('SELECT :a1, :a2', { a1: 1, a2: 'two' }).sql, 'SELECT 1, \'two\'');



// These last 2 will work before and after the fix is applied as there is no transformation performed

assert.equal(pool2.query('SELECT :a1', []).sql, 'SELECT :a1');
assert.equal(pool2.query('SELECT :a1').sql, 'SELECT :a1');



pool1.end(function (err) {
assert.ifError(err);
pool2.end(function (err) {
assert.ifError(err);
server.destroy();
});
});

});