Skip to content

Commit 0f0e517

Browse files
committed
Fix native pipelining: guard handleError when query.native is unset and skip JS-only tests
handleError in native/query.js crashes when this.native is undefined (e.g. query_timeout fires before pipeline callback sets it). Skip named statement cleanup and query_timeout tests for native client since those features rely on JS-specific internals.
1 parent c28febe commit 0f0e517

2 files changed

Lines changed: 52 additions & 42 deletions

File tree

packages/pg/lib/native/query.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const errorFieldMap = {
4848

4949
NativeQuery.prototype.handleError = function (err) {
5050
// copy pq error fields into the error object
51-
const fields = this.native.pq.resultErrorFields()
51+
const fields = this.native && this.native.pq.resultErrorFields()
5252
if (fields) {
5353
for (const key in fields) {
5454
const normalizedFieldName = errorFieldMap[key] || key

packages/pg/test/integration/client/pipelining-tests.js

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -105,48 +105,58 @@ suite.test('end() waits for in-flight pipelined queries to complete', async func
105105
})
106106

107107
// #13: named statement error cleanup — submittedNamedStatements not left stale
108-
suite.test('named statement parse error cleans up and allows re-preparation', async function () {
109-
const client = helper.client()
110-
client.pipelining = true
111-
112-
// Use an invalid type to force a server-side parse error
113-
const err = await client
114-
.query({ name: 'bad-stmt', text: 'SELECT $1::nonexistent_type_xyz', values: [1] })
115-
.then(() => null)
116-
.catch((e) => e)
117-
118-
assert.ok(err, 'expected parse to fail')
119-
120-
// The stale submittedNamedStatements entry should be gone.
121-
// Re-using the same name with valid SQL should work.
122-
const result = await client.query({ name: 'bad-stmt', text: 'SELECT $1::int AS n', values: [42] })
123-
assert.equal(result.rows[0].n, 42)
124-
125-
await client.end()
126-
})
108+
// This relies on submittedNamedStatements tracking which only exists in the JS client
109+
suite.test(
110+
'named statement parse error cleans up and allows re-preparation',
111+
!helper.args.native &&
112+
async function () {
113+
const client = helper.client()
114+
client.pipelining = true
115+
116+
// Use an invalid type to force a server-side parse error
117+
const err = await client
118+
.query({ name: 'bad-stmt', text: 'SELECT $1::nonexistent_type_xyz', values: [1] })
119+
.then(() => null)
120+
.catch((e) => e)
121+
122+
assert.ok(err, 'expected parse to fail')
123+
124+
// The stale submittedNamedStatements entry should be gone.
125+
// Re-using the same name with valid SQL should work.
126+
const result = await client.query({ name: 'bad-stmt', text: 'SELECT $1::int AS n', values: [42] })
127+
assert.equal(result.rows[0].n, 42)
128+
129+
await client.end()
130+
}
131+
)
127132

128133
// #14: query_timeout with pipelining
129134
// When an already-sent pipelined query times out, the connection is destroyed
130135
// to unblock the pipeline — subsequent queries error rather than hanging.
131-
suite.test('query_timeout on sent pipelined query destroys connection to unblock', async function () {
132-
const client = helper.client()
133-
client.pipelining = true
134-
client.on('error', () => {}) // absorb the 'error' event emitted when stream is destroyed
135-
136-
const results = await Promise.allSettled([
137-
client.query('SELECT 1 AS num'),
138-
client.query({ text: 'SELECT pg_sleep(30)', query_timeout: 100 }),
139-
client.query('SELECT 3 AS num'),
140-
])
141-
142-
// Query 1 completes before the slow query enters the pipeline
143-
assert.equal(results[0].status, 'fulfilled')
144-
assert.equal(results[0].value.rows[0].num, 1)
145-
146-
// Query 2 times out
147-
assert.equal(results[1].status, 'rejected')
148-
assert.ok(results[1].reason.message.includes('timeout'), `unexpected error: ${results[1].reason.message}`)
149-
150-
// Query 3 errors because the connection was destroyed to unblock the pipeline
151-
assert.equal(results[2].status, 'rejected')
152-
})
136+
// Native client does not support query_timeout in pipelining mode.
137+
suite.test(
138+
'query_timeout on sent pipelined query destroys connection to unblock',
139+
!helper.args.native &&
140+
async function () {
141+
const client = helper.client()
142+
client.pipelining = true
143+
client.on('error', () => {}) // absorb the 'error' event emitted when stream is destroyed
144+
145+
const results = await Promise.allSettled([
146+
client.query('SELECT 1 AS num'),
147+
client.query({ text: 'SELECT pg_sleep(30)', query_timeout: 100 }),
148+
client.query('SELECT 3 AS num'),
149+
])
150+
151+
// Query 1 completes before the slow query enters the pipeline
152+
assert.equal(results[0].status, 'fulfilled')
153+
assert.equal(results[0].value.rows[0].num, 1)
154+
155+
// Query 2 times out
156+
assert.equal(results[1].status, 'rejected')
157+
assert.ok(results[1].reason.message.includes('timeout'), `unexpected error: ${results[1].reason.message}`)
158+
159+
// Query 3 errors because the connection was destroyed to unblock the pipeline
160+
assert.equal(results[2].status, 'rejected')
161+
}
162+
)

0 commit comments

Comments
 (0)