From 76df13f99a2324ed58a4b77cec646259a180fd72 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 15 Jan 2019 11:37:36 -0500 Subject: [PATCH 1/2] compare prepared statement text with previous to prevent incorrect queries - fixes #1813 --- lib/client.js | 2 +- lib/native/query.js | 6 +++++- lib/query.js | 4 ++++ test/integration/client/prepared-statement-tests.js | 11 +++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/client.js b/lib/client.js index 8e7d307f7..194bc097a 100644 --- a/lib/client.js +++ b/lib/client.js @@ -276,7 +276,7 @@ Client.prototype._attachListeners = function (con) { // it again on the same client con.on('parseComplete', function (msg) { if (self.activeQuery.name) { - con.parsedStatements[self.activeQuery.name] = true + con.parsedStatements[self.activeQuery.name] = self.activeQuery.text } }) diff --git a/lib/native/query.js b/lib/native/query.js index 7602d161b..27607fa4e 100644 --- a/lib/native/query.js +++ b/lib/native/query.js @@ -139,12 +139,16 @@ NativeQuery.prototype.submit = function (client) { // check if the client has already executed this named query // if so...just execute it again - skip the planning phase if (client.namedQueries[this.name]) { + if (this.text && client.namedQueries[this.name] !== this.text) { + const err = new Error(`Prepared statements must be unique — '${this.name}' was used for different queries`) + return after(err) + } return client.native.execute(this.name, values, after) } // plan the named query the first time, then execute it return client.native.prepare(this.name, this.text, values.length, function (err) { if (err) return after(err) - client.namedQueries[self.name] = true + client.namedQueries[self.name] = self.text return self.native.execute(self.name, values, after) }) } else if (this.values) { diff --git a/lib/query.js b/lib/query.js index 94c2bc3c1..2ca462040 100644 --- a/lib/query.js +++ b/lib/query.js @@ -148,6 +148,10 @@ Query.prototype.submit = function (connection) { if (typeof this.text !== 'string' && typeof this.name !== 'string') { return new Error('A query must have either text or a name. Supplying neither is unsupported.') } + const previous = connection.parsedStatements[this.name] + if (this.text && previous && this.text !== previous) { + return new Error(`Prepared statements must be unique — '${this.name}' was used for different queries`) + } if (this.values && !Array.isArray(this.values)) { return new Error('Query values must be an array') } diff --git a/test/integration/client/prepared-statement-tests.js b/test/integration/client/prepared-statement-tests.js index 188bf8266..81048d16f 100644 --- a/test/integration/client/prepared-statement-tests.js +++ b/test/integration/client/prepared-statement-tests.js @@ -56,6 +56,17 @@ var suite = new helper.Suite() q.on('end', () => done()) }) + + suite.test('with same name, but with different text', function (done) { + client.query(new Query({ + text: 'select name from person where age >= $1 and name LIKE $2', + name: queryName, + values: [30, '%n%'] + }), assert.calls(err => { + assert.equal(err.message, `Prepared statements must be unique — '${queryName}' was used for different queries`) + done() + })) + }) })() ;(function () { From 248f7a036dbdb1c15179ccc52e119f3b4e0b5c29 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 15 Jan 2019 12:52:01 -0500 Subject: [PATCH 2/2] make suggested changes to error message --- lib/native/query.js | 2 +- lib/query.js | 2 +- test/integration/client/prepared-statement-tests.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/native/query.js b/lib/native/query.js index 27607fa4e..db4eb8cca 100644 --- a/lib/native/query.js +++ b/lib/native/query.js @@ -140,7 +140,7 @@ NativeQuery.prototype.submit = function (client) { // if so...just execute it again - skip the planning phase if (client.namedQueries[this.name]) { if (this.text && client.namedQueries[this.name] !== this.text) { - const err = new Error(`Prepared statements must be unique — '${this.name}' was used for different queries`) + const err = new Error(`Prepared statements must be unique - '${this.name}' was used for a different statement`) return after(err) } return client.native.execute(this.name, values, after) diff --git a/lib/query.js b/lib/query.js index 2ca462040..9e34b0d2b 100644 --- a/lib/query.js +++ b/lib/query.js @@ -150,7 +150,7 @@ Query.prototype.submit = function (connection) { } const previous = connection.parsedStatements[this.name] if (this.text && previous && this.text !== previous) { - return new Error(`Prepared statements must be unique — '${this.name}' was used for different queries`) + return new Error(`Prepared statements must be unique - '${this.name}' was used for a different statement`) } if (this.values && !Array.isArray(this.values)) { return new Error('Query values must be an array') diff --git a/test/integration/client/prepared-statement-tests.js b/test/integration/client/prepared-statement-tests.js index 81048d16f..76654eaa3 100644 --- a/test/integration/client/prepared-statement-tests.js +++ b/test/integration/client/prepared-statement-tests.js @@ -63,7 +63,7 @@ var suite = new helper.Suite() name: queryName, values: [30, '%n%'] }), assert.calls(err => { - assert.equal(err.message, `Prepared statements must be unique — '${queryName}' was used for different queries`) + assert.equal(err.message, `Prepared statements must be unique - '${queryName}' was used for a different statement`) done() })) })