mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(sql) fix state being set to prepared too soon (#17732)
This commit is contained in:
@@ -1295,7 +1295,6 @@ function doCreateQuery(strings, values, allowUnsafeTransaction, poolSize, bigint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return createQuery(sqlString, final_values, new SQLResultArray(), undefined, !!bigint, !!simple);
|
||||
}
|
||||
|
||||
|
||||
@@ -853,6 +853,7 @@ pub const PostgresSQLQuery = struct {
|
||||
connection.prepared_statement_id += 1;
|
||||
stmt.* = .{ .signature = signature, .ref_count = 2, .status = if (can_execute) .parsing else .pending };
|
||||
this.statement = stmt;
|
||||
|
||||
entry_value.* = stmt;
|
||||
} else {
|
||||
stmt.* = .{ .signature = signature, .ref_count = 1, .status = if (can_execute) .parsing else .pending };
|
||||
@@ -956,9 +957,21 @@ pub const PostgresRequest = struct {
|
||||
iter.to(0);
|
||||
var i: usize = 0;
|
||||
while (iter.next()) |value| : (i += 1) {
|
||||
const parameter_field = parameter_fields[i];
|
||||
const is_custom_type = std.math.maxInt(short) < parameter_field;
|
||||
const tag: types.Tag = if (is_custom_type) .text else @enumFromInt(@as(short, @intCast(parameter_field)));
|
||||
const tag: types.Tag = brk: {
|
||||
if (i >= len) {
|
||||
// parameter in array but not in parameter_fields
|
||||
// this is probably a bug a bug in bun lets return .text here so the server will send a error 08P01
|
||||
// with will describe better the error saying exactly how many parameters are missing and are expected
|
||||
// Example:
|
||||
// SQL error: PostgresError: bind message supplies 0 parameters, but prepared statement "PSELECT * FROM test_table WHERE id=$1 .in$0" requires 1
|
||||
// errno: "08P01",
|
||||
// code: "ERR_POSTGRES_SERVER_ERROR"
|
||||
break :brk .text;
|
||||
}
|
||||
const parameter_field = parameter_fields[i];
|
||||
const is_custom_type = std.math.maxInt(short) < parameter_field;
|
||||
break :brk if (is_custom_type) .text else @enumFromInt(@as(short, @intCast(parameter_field)));
|
||||
};
|
||||
if (value.isEmptyOrUndefinedOrNull()) {
|
||||
debug(" -> NULL", .{});
|
||||
// As a special case, -1 indicates a
|
||||
@@ -3598,7 +3611,8 @@ pub const PostgresSQLConnection = struct {
|
||||
try reader.eatMessage(protocol.ParseComplete);
|
||||
const request = this.current() orelse return error.ExpectedRequest;
|
||||
if (request.statement) |statement| {
|
||||
if (statement.status == .parsing) {
|
||||
// if we have params wait for parameter description
|
||||
if (statement.status == .parsing and statement.signature.fields.len == 0) {
|
||||
statement.status = .prepared;
|
||||
}
|
||||
}
|
||||
@@ -3609,6 +3623,9 @@ pub const PostgresSQLConnection = struct {
|
||||
const request = this.current() orelse return error.ExpectedRequest;
|
||||
var statement = request.statement orelse return error.ExpectedStatement;
|
||||
statement.parameters = description.parameters;
|
||||
if (statement.status == .parsing) {
|
||||
statement.status = .prepared;
|
||||
}
|
||||
},
|
||||
.RowDescription => {
|
||||
var description: protocol.RowDescription = undefined;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"dependencies": {
|
||||
"@azure/service-bus": "7.9.4",
|
||||
"@duckdb/node-api": "1.1.3-alpha.7",
|
||||
"@electric-sql/pglite": "0.2.16",
|
||||
"@electric-sql/pglite": "0.2.17",
|
||||
"@grpc/grpc-js": "1.12.0",
|
||||
"@grpc/proto-loader": "0.7.10",
|
||||
"@happy-dom/global-registrator": "17.0.3",
|
||||
@@ -52,6 +52,7 @@
|
||||
"nodemailer": "6.9.3",
|
||||
"pg": "8.11.1",
|
||||
"pg-connection-string": "2.6.1",
|
||||
"pg-gateway": "^0.3.0-beta.4",
|
||||
"pino": "9.4.0",
|
||||
"pino-pretty": "11.2.2",
|
||||
"postgres": "3.3.5",
|
||||
@@ -171,7 +172,7 @@
|
||||
|
||||
"@duckdb/node-bindings-win32-x64": ["@duckdb/node-bindings-win32-x64@1.1.3-alpha.7", "", { "os": "win32", "cpu": "x64" }, "sha512-5OqjpYRFdQATbniL0o8gF8Z92bBuINlXOve0o+qgM6W+nlIRp/cUHk6vWwYySZnF0AIHZ5JG7mngzJOLmA/kPQ=="],
|
||||
|
||||
"@electric-sql/pglite": ["@electric-sql/pglite@0.2.16", "", {}, "sha512-dCSHpoOKuTxecaYhWDRp2yFTN3XWcMPMrBVl5yOR8VZEUprz4+R3iuU7BipmlsqBnBDO/6l9H/C2ZwJdunkWyw=="],
|
||||
"@electric-sql/pglite": ["@electric-sql/pglite@0.2.17", "", {}, "sha512-qEpKRT2oUaWDH6tjRxLHjdzMqRUGYDnGZlKrnL4dJ77JVMcP2Hpo3NYnOSPKdZdeec57B6QPprCUFg0picx5Pw=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@0.44.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw=="],
|
||||
|
||||
@@ -1665,6 +1666,8 @@
|
||||
|
||||
"pg-connection-string": ["pg-connection-string@2.6.1", "", {}, "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg=="],
|
||||
|
||||
"pg-gateway": ["pg-gateway@0.3.0-beta.4", "", {}, "sha512-CTjsM7Z+0Nx2/dyZ6r8zRsc3f9FScoD5UAOlfUx1Fdv/JOIWvRbF7gou6l6vP+uypXQVoYPgw8xZDXgMGvBa4Q=="],
|
||||
|
||||
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
||||
|
||||
"pg-pool": ["pg-pool@3.6.2", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg=="],
|
||||
|
||||
7
test/js/third_party/pg-gateway/package.json
vendored
Normal file
7
test/js/third_party/pg-gateway/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "pg-gateway",
|
||||
"dependencies": {
|
||||
"@electric-sql/pglite": "0.2.17",
|
||||
"pg-gateway": "0.3.0-beta.4"
|
||||
}
|
||||
}
|
||||
90
test/js/third_party/pg-gateway/pglite.test.ts
vendored
Normal file
90
test/js/third_party/pg-gateway/pglite.test.ts
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
import { PGlite } from "@electric-sql/pglite";
|
||||
import { SQL, randomUUIDv7 } from "bun";
|
||||
import net, { AddressInfo } from "node:net";
|
||||
import { fromNodeSocket } from "pg-gateway/node";
|
||||
import { expect, test } from "bun:test";
|
||||
import { once } from "events";
|
||||
test("pglite should be able to query using pg-gateway and Bun.SQL", async () => {
|
||||
const name = "test_" + randomUUIDv7("hex").replaceAll("-", "");
|
||||
const dataDir = `memory://${name}`;
|
||||
const db = new PGlite(dataDir);
|
||||
|
||||
// Wait for the database to initialize
|
||||
await db.waitReady;
|
||||
|
||||
// Create a simple test table
|
||||
await db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS test_table (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO test_table (name) VALUES ('Test 1'), ('Test 2'), ('Test 3');
|
||||
`);
|
||||
|
||||
// Create a simple server using pg-gateway
|
||||
const server = net.createServer(async socket => {
|
||||
await fromNodeSocket(socket, {
|
||||
serverVersion: "16.3",
|
||||
auth: {
|
||||
method: "trust",
|
||||
},
|
||||
async onStartup() {
|
||||
// Wait for PGlite to be ready before further processing
|
||||
await db.waitReady;
|
||||
},
|
||||
async onMessage(data, { isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
// Only forward messages to PGlite after authentication
|
||||
if (!isAuthenticated) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await db.execProtocolRaw(data);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Start listening
|
||||
await once(server.listen(0), "listening");
|
||||
|
||||
const port = (server.address() as AddressInfo).port;
|
||||
|
||||
await using sql = new SQL({
|
||||
hostname: "localhost",
|
||||
port: port,
|
||||
database: name,
|
||||
max: 1,
|
||||
});
|
||||
|
||||
{
|
||||
// prepared statement without parameters
|
||||
const result = await sql`SELECT * FROM test_table WHERE id = 1`;
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0]).toEqual({ id: 1, name: "Test 1" });
|
||||
}
|
||||
|
||||
{
|
||||
// using prepared statement
|
||||
const result = await sql`SELECT * FROM test_table WHERE id = ${1}`;
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0]).toEqual({ id: 1, name: "Test 1" });
|
||||
}
|
||||
|
||||
{
|
||||
// using simple query
|
||||
const result = await sql`SELECT * FROM test_table WHERE id = 1`.simple();
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0]).toEqual({ id: 1, name: "Test 1" });
|
||||
}
|
||||
|
||||
{
|
||||
// using unsafe with parameters
|
||||
const result = await sql.unsafe("SELECT * FROM test_table WHERE id = $1", [1]);
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0]).toEqual({ id: 1, name: "Test 1" });
|
||||
}
|
||||
});
|
||||
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"@azure/service-bus": "7.9.4",
|
||||
"@duckdb/node-api": "1.1.3-alpha.7",
|
||||
"@electric-sql/pglite": "0.2.16",
|
||||
"@electric-sql/pglite": "0.2.17",
|
||||
"@grpc/grpc-js": "1.12.0",
|
||||
"@grpc/proto-loader": "0.7.10",
|
||||
"@happy-dom/global-registrator": "17.0.3",
|
||||
@@ -57,6 +57,7 @@
|
||||
"nodemailer": "6.9.3",
|
||||
"pg": "8.11.1",
|
||||
"pg-connection-string": "2.6.1",
|
||||
"pg-gateway": "0.3.0-beta.4",
|
||||
"pino": "9.4.0",
|
||||
"pino-pretty": "11.2.2",
|
||||
"postgres": "3.3.5",
|
||||
|
||||
Reference in New Issue
Block a user