diff --git a/packages/pg/lib/query.js b/packages/pg/lib/query.js index 3b93cb5ba..f00b74342 100644 --- a/packages/pg/lib/query.js +++ b/packages/pg/lib/query.js @@ -235,8 +235,10 @@ class Query extends EventEmitter { valueMapper: utils.prepareValue, }) } catch (err) { - // we should close parse to avoid leaking connections - connection.close({ type: 'S', name: this.name }) + // only close the unnamed statement. a named one stays prepared and is used again + if (!this.name) { + connection.close({ type: 'S', name: this.name }) + } connection.sync() this.handleError(err, connection) diff --git a/packages/pg/test/integration/client/prepared-statement-tests.js b/packages/pg/test/integration/client/prepared-statement-tests.js index 5c102eb13..7bea1c19a 100644 --- a/packages/pg/test/integration/client/prepared-statement-tests.js +++ b/packages/pg/test/integration/client/prepared-statement-tests.js @@ -228,3 +228,60 @@ const suite = new helper.Suite() suite.test('cleanup', () => client.end()) })() +;(function () { + const name = 'bind-throw' + const text = 'SELECT $1::text AS v' + const badValue = { + toPostgres() { + throw new Error('bad value') + }, + } + + suite.test( + 'a named statement still runs after its values fail to serialize', + !helper.args.native && + async function () { + const client = helper.client() + try { + await assert.rejects(client.query({ name, text, values: [badValue] }), /bad value/) + const result = await client.query({ name, text, values: ['ok'] }) + assert.equal(result.rows[0].v, 'ok') + } finally { + await client.end() + } + } + ) + + suite.test( + 'an already prepared statement still runs after its values fail to serialize', + !helper.args.native && + async function () { + const client = helper.client() + try { + await client.query({ name, text, values: ['first'] }) + await assert.rejects(client.query({ name, text, values: [badValue] }), /bad value/) + const result = await client.query({ name, text, values: ['ok'] }) + assert.equal(result.rows[0].v, 'ok') + } finally { + await client.end() + } + } + ) + + suite.test( + 'a pipelined query with the same name still runs after the first fails to serialize', + !helper.args.native && + async function () { + const client = helper.client(undefined, { pipeline: true }) + try { + const failed = client.query({ name, text, values: [badValue] }) + const next = client.query({ name, text, values: ['ok'] }) + await assert.rejects(failed, /bad value/) + const result = await next + assert.equal(result.rows[0].v, 'ok') + } finally { + await client.end() + } + } + ) +})()