Repository navigation
Conversation
A query that errors after some DataRows (cancelled, or a runtime error such as division by zero) never reaches CommandComplete, so the row index was left as is and the next query on the connection started writing its rows at that offset, leaving empty slots at the start of its result. Fixes porsager#1253 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
We carry this line too. It is the line #1190 adds for #1181, and #1119 reports the same fault, so either pull request closes #1253, #1181 and #1119. Two more paths it covers, besides a query that fails on a live connection:
Scripts and outputAgainst PostgreSQL 17. The first prints import postgres from 'postgres'
// Server and credentials from PGHOST, PGPORT, PGUSER, PGPASSWORD and PGDATABASE.
const sql = postgres({ max: 1 })
const admin = postgres({ max: 1 })
const [{ pid }] = await sql`select pg_backend_pid() as pid`
const lost = sql`
select g, repeat('x', 20000), pg_sleep(case when g > 20 then 0.2 else 0 end) from generate_series(1, 100) g
`.catch(e => e.code)
while (!(await admin`select 1 from pg_stat_activity where pid = ${ pid } and wait_event = 'PgSleep'`).length)
await new Promise(r => setTimeout(r, 20))
await admin`select pg_terminate_backend(${ pid })`
console.log('query under the close:', await lost)
console.log('first query after it :', await sql`select 1 as x`.then(x => 'length ' + x.length, e => e.code))
console.log('second query after it:', await sql`select 1 as x`.then(x => 'length ' + x.length, e => e.code))
await sql.end()
await admin.end()The second prints import postgres from 'postgres'
// Server and credentials from PGHOST, PGPORT, PGUSER, PGPASSWORD and PGDATABASE.
const sql = postgres({ max: 1 })
await sql`select 1`
sql`select pg_sleep(0.5)`.execute()
const query = sql`
select g, repeat('x', 20000), pg_sleep(case when g > 5 then 0.3 else 0 end) from generate_series(1, 20) g
`
const cancelled = query.then(x => 'length ' + x.length, e => e.code)
const next = sql`select 'a' as x union all select 'b'`.then(x => JSON.stringify(x), e => e.code)
setTimeout(() => query.cancel(), 100)
const pending = new Promise(r => setTimeout(r, 5000, 'still pending after 5 s'))
console.log('cancelled while piped:', await Promise.race([cancelled, pending]))
console.log('query behind it :', await Promise.race([next, pending]))
process.exit(0) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1253
DataRowwritesresult[rows++] = row, androwsis reset only inCommandComplete. A query that errors after some rows have arrived never reachesCommandComplete. Examples: a cancelled query, or a runtime error like division by zero on a later row.ReadyForQuerythen gives the connection a freshResultbut keeps the oldrows, so the next query on that connection starts writing at that offset and its result begins with empty slots.This resets
rowsinReadyForQuery, next toresult = new Result(), and adds a test that uses the division-by-zero case so it doesn't depend on timing.🤖 Generated with Claude Code