From ff6af0e2f732b1a5298dc07593ff879589288a0d Mon Sep 17 00:00:00 2001 From: Ciro Spaciari Date: Fri, 29 Aug 2025 01:03:43 -0700 Subject: [PATCH] fix(Bun.SQL) delay postgres promise resolve for prepared statements (#22090) ### What does this PR do? fixes https://github.com/oven-sh/bun/issues/21945 ### How did you verify your code works? Run the code bellow and will be way harder the encounter the same problem (I got it 1 times after 10 tries the same effect as Bun.sleep mentioned before) ```ts const sql = new Bun.SQL("postgres://localhost"); using conn1 = await sql.reserve(); using conn2 = await sql.reserve(); await sql`DROP TABLE IF EXISTS test1`; await sql`CREATE TABLE IF NOT EXISTS test1 ( id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, uuid UUID NOT NULL )`; await sql`INSERT INTO test1 (uuid) VALUES (gen_random_uuid())`; type Row = { id: number; uuid: string; }; for (let i = 0; i < 100_000; i++) { const [original]: Array = await conn1`SELECT id, uuid FROM test1 LIMIT 1`; const [updated]: Array = await conn1`UPDATE test1 SET uuid = gen_random_uuid() WHERE id = ${original.id} RETURNING id, uuid`; const [retrieved]: Array = await conn2`SELECT id, uuid FROM test1 WHERE id = ${original.id}`; if (retrieved.uuid !== updated.uuid) { console.log("Expected retrieved and updated to match", retrieved, updated, i); break; } } ``` --- src/js/internal/sql/postgres.ts | 75 +++++++++++----------- src/sql/postgres/PostgresSQLConnection.zig | 7 +- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/src/js/internal/sql/postgres.ts b/src/js/internal/sql/postgres.ts index 73f17dbb0e..e660430ae8 100644 --- a/src/js/internal/sql/postgres.ts +++ b/src/js/internal/sql/postgres.ts @@ -26,49 +26,46 @@ function wrapPostgresError(error: Error | PostgresErrorOptions) { initPostgres( function onResolvePostgresQuery(query, result, commandTag, count, queries, is_last) { - /// simple queries - if (query[_flags] & SQLQueryFlags.simple) { - // simple can have multiple results or a single result - if (is_last) { - if (queries) { - const queriesIndex = queries.indexOf(query); - if (queriesIndex !== -1) { - queries.splice(queriesIndex, 1); - } - } - try { - query.resolve(query[_results]); - } catch {} - return; - } - $assert(result instanceof SQLResultArray, "Invalid result array"); - // prepare for next query - query[_handle].setPendingValue(new SQLResultArray()); - - if (typeof commandTag === "string") { - if (commandTag.length > 0) { - result.command = commandTag; - } - } else { - result.command = cmds[commandTag]; - } - - result.count = count || 0; - const last_result = query[_results]; - - if (!last_result) { - query[_results] = result; - } else { - if (last_result instanceof SQLResultArray) { - // multiple results - query[_results] = [last_result, result]; - } else { - // 3 or more results - last_result.push(result); + if (is_last) { + if (queries) { + const queriesIndex = queries.indexOf(query); + if (queriesIndex !== -1) { + queries.splice(queriesIndex, 1); } } + try { + query.resolve(query[_results]); + } catch {} return; } + $assert(result instanceof SQLResultArray, "Invalid result array"); + // prepare for next query + query[_handle].setPendingValue(new SQLResultArray()); + + if (typeof commandTag === "string") { + if (commandTag.length > 0) { + result.command = commandTag; + } + } else { + result.command = cmds[commandTag]; + } + + result.count = count || 0; + const last_result = query[_results]; + + if (!last_result) { + query[_results] = result; + } else { + if (last_result instanceof SQLResultArray) { + // multiple results + query[_results] = [last_result, result]; + } else { + // 3 or more results + last_result.push(result); + } + } + return; + /// prepared statements $assert(result instanceof SQLResultArray, "Invalid result array"); if (typeof commandTag === "string") { diff --git a/src/sql/postgres/PostgresSQLConnection.zig b/src/sql/postgres/PostgresSQLConnection.zig index 0ddbd7c13e..f5bbeebdc0 100644 --- a/src/sql/postgres/PostgresSQLConnection.zig +++ b/src/sql/postgres/PostgresSQLConnection.zig @@ -1453,12 +1453,7 @@ pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.enum_litera debug("-> {s}", .{cmd.command_tag.slice()}); defer this.updateRef(); - if (request.flags.simple) { - // simple queries can have multiple commands - request.onResult(cmd.command_tag.slice(), this.globalObject, this.js_value, false); - } else { - request.onResult(cmd.command_tag.slice(), this.globalObject, this.js_value, true); - } + request.onResult(cmd.command_tag.slice(), this.globalObject, this.js_value, false); }, .BindComplete => { try reader.eatMessage(protocol.BindComplete);