diff --git a/packages/pg-cursor/index.js b/packages/pg-cursor/index.js index 5d566882b..c093c096f 100644 --- a/packages/pg-cursor/index.js +++ b/packages/pg-cursor/index.js @@ -151,6 +151,17 @@ class Cursor extends EventEmitter { this._sendRows() this.state = 'done' this.emit('end', this._result) + this._finish(null, this._result) + } + + // Call the Submittable callback the client set on this cursor, at most once. The client wraps it + // to clear its `query_timeout` timer, which otherwise fires after the cursor is done. + _finish(err, result) { + const callback = this.callback + this.callback = null + if (typeof callback === 'function') { + callback(err, result) + } } handleEmptyQuery() { @@ -185,6 +196,7 @@ class Cursor extends EventEmitter { queuedCallback.call(this, msg) } this._queue.length = 0 + this._finish(msg) if (this.listenerCount('error') > 0) { // only dispatch error events if we have a listener diff --git a/packages/pg-cursor/test/query-timeout.js b/packages/pg-cursor/test/query-timeout.js new file mode 100644 index 000000000..0097053e1 --- /dev/null +++ b/packages/pg-cursor/test/query-timeout.js @@ -0,0 +1,37 @@ +'use strict' +const assert = require('assert') +const Cursor = require('../') +const pg = require('pg') + +const text = 'SELECT generate_series as num FROM generate_series(0, 4)' +const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) + +describe('cursor with query_timeout', function () { + beforeEach(async function () { + this.client = new pg.Client({ query_timeout: 100 }) + await this.client.connect() + }) + + afterEach(async function () { + await this.client.end() + }) + + it('does not report a read timeout after the cursor is done', async function () { + const calls = [] + const cursor = this.client.query(new Cursor(text), (err) => calls.push(err)) + const rows = await cursor.read(100) + assert.equal(rows.length, 5) + await cursor.close() + await wait(200) + assert.deepEqual(calls, [null]) + }) + + it('does not report a read timeout after the cursor errored', async function () { + const calls = [] + const cursor = this.client.query(new Cursor('SELECT 1 / 0'), (err) => calls.push(err)) + await assert.rejects(cursor.read(1), /division by zero/) + await wait(200) + assert.equal(calls.length, 1) + assert.match(calls[0].message, /division by zero/) + }) +})