不要怂,就是干,撸起袖子干!

Commit 992ddf75 by supperchong Committed by Sushant

feat(logging): print bind parameters in logs with logQueryParameters option (#11267)

1 parent a0d56959
...@@ -324,14 +324,28 @@ class AbstractQuery { ...@@ -324,14 +324,28 @@ class AbstractQuery {
/** /**
* @param {string} sql * @param {string} sql
* @param {Function} debugContext * @param {Function} debugContext
* @param {Array|Object} parameters
* @protected * @protected
* @returns {Function} A function to call after the query was completed. * @returns {Function} A function to call after the query was completed.
*/ */
_logQuery(sql, debugContext) { _logQuery(sql, debugContext, parameters) {
const { connection, options } = this; const { connection, options } = this;
const benchmark = this.sequelize.options.benchmark || options.benchmark; const benchmark = this.sequelize.options.benchmark || options.benchmark;
const logQueryParameters = this.sequelize.options.logQueryParameters || options.logQueryParameters;
const startTime = Date.now(); const startTime = Date.now();
const fmt = `(${connection.uuid || 'default'}): ${sql}`; let logParameter = '';
if (logQueryParameters && parameters) {
const delimiter = sql.endsWith(';') ? '' : ';';
let paramStr;
if (Array.isArray(parameters)) {
paramStr = parameters.map(p=>JSON.stringify(p)).join(', ');
} else {
paramStr = JSON.stringify(parameters);
}
logParameter = `${delimiter} ${paramStr}`;
}
const fmt = `(${connection.uuid || 'default'}): ${sql}${logParameter}`;
const msg = `Executing ${fmt}`; const msg = `Executing ${fmt}`;
debugContext(msg); debugContext(msg);
if (!benchmark) { if (!benchmark) {
......
...@@ -39,7 +39,7 @@ class Query extends AbstractQuery { ...@@ -39,7 +39,7 @@ class Query extends AbstractQuery {
const showWarnings = this.sequelize.options.showWarnings const showWarnings = this.sequelize.options.showWarnings
|| options.showWarnings; || options.showWarnings;
const complete = this._logQuery(sql, debug); const complete = this._logQuery(sql, debug, parameters);
if (parameters) { if (parameters) {
debug('parameters(%j)', parameters); debug('parameters(%j)', parameters);
......
...@@ -40,7 +40,7 @@ class Query extends AbstractQuery { ...@@ -40,7 +40,7 @@ class Query extends AbstractQuery {
this.sql = sql; this.sql = sql;
const { options } = this; const { options } = this;
const complete = this._logQuery(sql, debug); const complete = this._logQuery(sql, debug, parameters);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const handleTransaction = err => { const handleTransaction = err => {
......
...@@ -34,7 +34,7 @@ class Query extends AbstractQuery { ...@@ -34,7 +34,7 @@ class Query extends AbstractQuery {
//do we need benchmark for this query execution //do we need benchmark for this query execution
const showWarnings = this.sequelize.options.showWarnings || options.showWarnings; const showWarnings = this.sequelize.options.showWarnings || options.showWarnings;
const complete = this._logQuery(sql, debug); const complete = this._logQuery(sql, debug, parameters);
return new Utils.Promise((resolve, reject) => { return new Utils.Promise((resolve, reject) => {
const handler = (err, results) => { const handler = (err, results) => {
......
...@@ -59,7 +59,7 @@ class Query extends AbstractQuery { ...@@ -59,7 +59,7 @@ class Query extends AbstractQuery {
? new Promise((resolve, reject) => connection.query(sql, parameters, (error, result) => error ? reject(error) : resolve(result))) ? new Promise((resolve, reject) => connection.query(sql, parameters, (error, result) => error ? reject(error) : resolve(result)))
: new Promise((resolve, reject) => connection.query(sql, (error, result) => error ? reject(error) : resolve(result))); : new Promise((resolve, reject) => connection.query(sql, (error, result) => error ? reject(error) : resolve(result)));
const complete = this._logQuery(sql, debug); const complete = this._logQuery(sql, debug, parameters);
return query.catch(err => { return query.catch(err => {
// set the client so that it will be reaped if the connection resets while executing // set the client so that it will be reaped if the connection resets while executing
......
...@@ -221,13 +221,16 @@ class Query extends AbstractQuery { ...@@ -221,13 +221,16 @@ class Query extends AbstractQuery {
const conn = this.connection; const conn = this.connection;
this.sql = sql; this.sql = sql;
const method = this.getDatabaseMethod(); const method = this.getDatabaseMethod();
let complete;
if (method === 'exec') { if (method === 'exec') {
// exec does not support bind parameter // exec does not support bind parameter
sql = AbstractQuery.formatBindParameters(sql, this.options.bind, this.options.dialect || 'sqlite', { skipUnescape: true })[0]; sql = AbstractQuery.formatBindParameters(sql, this.options.bind, this.options.dialect || 'sqlite', { skipUnescape: true })[0];
this.sql = sql; this.sql = sql;
complete = this._logQuery(sql, debug);
} else {
complete = this._logQuery(sql, debug, parameters);
} }
const complete = this._logQuery(sql, debug);
return new Promise(resolve => { return new Promise(resolve => {
const columnTypes = {}; const columnTypes = {};
......
...@@ -169,6 +169,7 @@ class Sequelize { ...@@ -169,6 +169,7 @@ class Sequelize {
* @param {Object} [options.operatorsAliases] String based operator alias. Pass object to limit set of aliased operators. * @param {Object} [options.operatorsAliases] String based operator alias. Pass object to limit set of aliased operators.
* @param {Object} [options.hooks] An object of global hook functions that are called before and after certain lifecycle events. Global hooks will run after any model-specific hooks defined for the same event (See `Sequelize.Model.init()` for a list). Additionally, `beforeConnect()`, `afterConnect()`, `beforeDisconnect()`, and `afterDisconnect()` hooks may be defined here. * @param {Object} [options.hooks] An object of global hook functions that are called before and after certain lifecycle events. Global hooks will run after any model-specific hooks defined for the same event (See `Sequelize.Model.init()` for a list). Additionally, `beforeConnect()`, `afterConnect()`, `beforeDisconnect()`, and `afterDisconnect()` hooks may be defined here.
* @param {boolean} [options.minifyAliases=false] A flag that defines if aliases should be minified (mostly useful to avoid Postgres alias character limit of 64) * @param {boolean} [options.minifyAliases=false] A flag that defines if aliases should be minified (mostly useful to avoid Postgres alias character limit of 64)
* @param {boolean} [options.logQueryParameters=false] A flag that defines if show bind patameters in log.
*/ */
constructor(database, username, password, options) { constructor(database, username, password, options) {
let config; let config;
...@@ -256,7 +257,8 @@ class Sequelize { ...@@ -256,7 +257,8 @@ class Sequelize {
databaseVersion: 0, databaseVersion: 0,
typeValidation: false, typeValidation: false,
benchmark: false, benchmark: false,
minifyAliases: false minifyAliases: false,
logQueryParameters: false
}, options || {}); }, options || {});
if (!this.options.dialect) { if (!this.options.dialect) {
......
...@@ -320,6 +320,62 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -320,6 +320,62 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
expect(typeof logger.args[0][1] === 'number').to.be.true; expect(typeof logger.args[0][1] === 'number').to.be.true;
}); });
}); });
describe('log sql when set logQueryParameters', () => {
beforeEach(function() {
this.sequelize = Support.createSequelizeInstance({
benchmark: true,
logQueryParameters: true
});
this.User = this.sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING
},
emailAddress: {
type: DataTypes.STRING
}
}, {
timestamps: false
});
return this.User.sync({ force: true });
});
it('add parameters in log sql', function() {
let createSql, updateSql;
return this.User.create({
username: 'john',
emailAddress: 'john@gmail.com'
}, {
logging: s =>{
createSql = s;
}
}).then(user=>{
user.username = 'li';
return user.save({
logging: s =>{
updateSql = s;
}
});
}).then(()=>{
expect(createSql).to.match(/; ("john", "john@gmail.com"|{"(\$1|0)":"john","(\$2|1)":"john@gmail.com"})/);
expect(updateSql).to.match(/; ("li", 1|{"(\$1|0)":"li","(\$2|1)":1})/);
});
});
it('add parameters in log sql when use bind value', function() {
let logSql;
const typeCast = dialect === 'postgres' ? '::text' : '';
return this.sequelize.query(`select $1${typeCast} as foo, $2${typeCast} as bar`, { bind: ['foo', 'bar'], logging: s=>logSql = s })
.then(()=>{
expect(logSql).to.match(/; ("foo", "bar"|{"(\$1|0)":"foo","(\$2|1)":"bar"})/);
});
});
});
}); });
it('executes select queries correctly', function() { it('executes select queries correctly', function() {
......
...@@ -360,6 +360,13 @@ export interface Options extends Logging { ...@@ -360,6 +360,13 @@ export interface Options extends Logging {
*/ */
minifyAliases?: boolean; minifyAliases?: boolean;
/**
* Set to `true` to show bind patameters in log.
*
* @default false
*/
logQueryParameters?: boolean;
retry?: RetryOptions; retry?: RetryOptions;
} }
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!