From cddc789f49fe7baf4e7375f9881f2c1bf1aa1326 Mon Sep 17 00:00:00 2001 From: uap-dev Date: Mon, 2 Feb 2026 13:02:35 -0800 Subject: [PATCH 1/4] fixes bug where submittable without callback throws error --- packages/pg/lib/client.js | 2 +- packages/pg/lib/native/client.js | 2 +- .../test/unit/client/query-timeout-tests.js | 43 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 packages/pg/test/unit/client/query-timeout-tests.js diff --git a/packages/pg/lib/client.js b/packages/pg/lib/client.js index df430f881..2534f9ca7 100644 --- a/packages/pg/lib/client.js +++ b/packages/pg/lib/client.js @@ -592,7 +592,7 @@ class Client extends EventEmitter { } if (readTimeout) { - queryCallback = query.callback + queryCallback = query.callback || (() => {}) readTimeoutTimer = setTimeout(() => { const error = new Error('Query read timeout') diff --git a/packages/pg/lib/native/client.js b/packages/pg/lib/native/client.js index 4bee7ce3c..667bf613d 100644 --- a/packages/pg/lib/native/client.js +++ b/packages/pg/lib/native/client.js @@ -184,7 +184,7 @@ Client.prototype.query = function (config, values, callback) { } if (readTimeout) { - queryCallback = query.callback + queryCallback = query.callback || (() => {}) readTimeoutTimer = setTimeout(() => { const error = new Error('Query read timeout') diff --git a/packages/pg/test/unit/client/query-timeout-tests.js b/packages/pg/test/unit/client/query-timeout-tests.js new file mode 100644 index 000000000..e6e0c36f4 --- /dev/null +++ b/packages/pg/test/unit/client/query-timeout-tests.js @@ -0,0 +1,43 @@ +'use strict' + +const helper = require('./test-helper') +const Query = require('../../../lib/query') +const assert = require('assert') +const suite = new helper.Suite() +const test = suite.test.bind(suite) + +// Regression tests for GitHub issue #1860 +// https://github.com/brianc/node-postgres/issues/1860 +// +// When a Submittable (Query, QueryStream, Cursor) is passed to client.query() +// with query_timeout configured but WITHOUT a callback, the client would crash +// with "TypeError: queryCallback is not a function" when the timeout fires. +// +// The fix: queryCallback = query.callback || (() => {}) + +test('query timeout with Submittable without callback delivers error via handleError', function (done) { + const client = helper.client() + client.connectionParameters = { query_timeout: 10 } + + const query = new Query({ text: 'SELECT 1' }) + query.handleError = (err) => { + assert.equal(err.message, 'Query read timeout') + done() + } + + client.connection.emit('readyForQuery') + client.query(query) +}) + +test('query timeout with Submittable with callback delivers error via callback', function (done) { + const client = helper.client() + client.connectionParameters = { query_timeout: 10 } + + const query = new Query({ text: 'SELECT 1' }) + client.connection.emit('readyForQuery') + + client.query(query, (err) => { + assert.equal(err.message, 'Query read timeout') + done() + }) +}) From e0d7cb4759730f1ec789119c41dcdb248cc505fc Mon Sep 17 00:00:00 2001 From: uap-dev Date: Mon, 2 Feb 2026 13:50:12 -0800 Subject: [PATCH 2/4] add querystream to test remove comments --- .../test/unit/client/query-timeout-tests.js | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/pg/test/unit/client/query-timeout-tests.js b/packages/pg/test/unit/client/query-timeout-tests.js index e6e0c36f4..c7d661fb7 100644 --- a/packages/pg/test/unit/client/query-timeout-tests.js +++ b/packages/pg/test/unit/client/query-timeout-tests.js @@ -2,19 +2,11 @@ const helper = require('./test-helper') const Query = require('../../../lib/query') +const QueryStream = require('pg-query-stream') const assert = require('assert') const suite = new helper.Suite() const test = suite.test.bind(suite) -// Regression tests for GitHub issue #1860 -// https://github.com/brianc/node-postgres/issues/1860 -// -// When a Submittable (Query, QueryStream, Cursor) is passed to client.query() -// with query_timeout configured but WITHOUT a callback, the client would crash -// with "TypeError: queryCallback is not a function" when the timeout fires. -// -// The fix: queryCallback = query.callback || (() => {}) - test('query timeout with Submittable without callback delivers error via handleError', function (done) { const client = helper.client() client.connectionParameters = { query_timeout: 10 } @@ -41,3 +33,17 @@ test('query timeout with Submittable with callback delivers error via callback', done() }) }) + +test('query timeout with QueryStream without callback does not crash', function (done) { + const client = helper.client() + client.connectionParameters = { query_timeout: 10 } + + const stream = new QueryStream('SELECT 1') + stream.handleError = (err) => { + assert.equal(err.message, 'Query read timeout') + done() + } + + client.connection.emit('readyForQuery') + client.query(stream) +}) From 737075df6e59e2d36ac7eef0385542fff2b5387c Mon Sep 17 00:00:00 2001 From: uap-dev Date: Mon, 2 Feb 2026 15:26:56 -0800 Subject: [PATCH 3/4] fix test --- packages/pg-query-stream/test/error.ts | 24 +++++++++++++++++-- .../test/unit/client/query-timeout-tests.js | 15 ------------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/pg-query-stream/test/error.ts b/packages/pg-query-stream/test/error.ts index 8ddb4da7d..0f64f71ae 100644 --- a/packages/pg-query-stream/test/error.ts +++ b/packages/pg-query-stream/test/error.ts @@ -1,7 +1,7 @@ import assert from 'assert' -import helper from './helper' +import { Client, Pool } from 'pg' import QueryStream from '../src' -import { Pool, Client } from 'pg' +import helper from './helper' helper('error', function (client) { it('receives error on stream', function (done) { @@ -170,4 +170,24 @@ describe('error recovery', () => { conn.release() await pool.end() }) + + it('does not crash when query_timeout fires without callback', async () => { + const client = new Client({ query_timeout: 50 }) + await client.connect() + + const stream = new QueryStream('SELECT pg_sleep(10)') + + // cursor.on('error') fires synchronously when handleError is called + // stream.on('error') fires asynchronously via destroy() + // Use cursor for immediate error detection + const errorPromise = new Promise((resolve) => { + stream.cursor.on('error', resolve) + }) + + client.query(stream) + + const error = await errorPromise + assert.equal(error.message, 'Query read timeout') + await client.end() + }) }) diff --git a/packages/pg/test/unit/client/query-timeout-tests.js b/packages/pg/test/unit/client/query-timeout-tests.js index c7d661fb7..dbf20cf1f 100644 --- a/packages/pg/test/unit/client/query-timeout-tests.js +++ b/packages/pg/test/unit/client/query-timeout-tests.js @@ -2,7 +2,6 @@ const helper = require('./test-helper') const Query = require('../../../lib/query') -const QueryStream = require('pg-query-stream') const assert = require('assert') const suite = new helper.Suite() const test = suite.test.bind(suite) @@ -33,17 +32,3 @@ test('query timeout with Submittable with callback delivers error via callback', done() }) }) - -test('query timeout with QueryStream without callback does not crash', function (done) { - const client = helper.client() - client.connectionParameters = { query_timeout: 10 } - - const stream = new QueryStream('SELECT 1') - stream.handleError = (err) => { - assert.equal(err.message, 'Query read timeout') - done() - } - - client.connection.emit('readyForQuery') - client.query(stream) -}) From 0988e17386d58b7a0c1ed21125196a97f852aa07 Mon Sep 17 00:00:00 2001 From: Charmander <~@charmander.me> Date: Mon, 2 Feb 2026 17:36:27 -0800 Subject: [PATCH 4/4] Revert unrelated reformatting --- packages/pg-query-stream/test/error.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pg-query-stream/test/error.ts b/packages/pg-query-stream/test/error.ts index 0f64f71ae..5f7a78565 100644 --- a/packages/pg-query-stream/test/error.ts +++ b/packages/pg-query-stream/test/error.ts @@ -1,7 +1,7 @@ import assert from 'assert' -import { Client, Pool } from 'pg' -import QueryStream from '../src' import helper from './helper' +import QueryStream from '../src' +import { Pool, Client } from 'pg' helper('error', function (client) { it('receives error on stream', function (done) {