fix(Bun.SQL) fix IN, UPDATE support in helpers, and fix JSONB/JSON serialization (#22700)

### What does this PR do?
Fixes https://github.com/oven-sh/bun/issues/20669
Fixes https://github.com/oven-sh/bun/issues/18775
Fixes https://github.com/oven-sh/bun/issues/22156
Fixes https://github.com/oven-sh/bun/issues/22164
Fixes https://github.com/oven-sh/bun/issues/18254
Fixes https://github.com/oven-sh/bun/issues/21267
Fixes https://github.com/oven-sh/bun/issues/20669
Fixes https://github.com/oven-sh/bun/issues/1317
Fixes https://github.com/oven-sh/bun/pull/22700
Partially Fixes https://github.com/oven-sh/bun/issues/22757 (sqlite
pending, need a followup and probably @alii help here)
### How did you verify your code works?
Tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Ciro Spaciari
2025-09-30 13:26:15 -07:00
committed by GitHub
parent c5005a37d7
commit 5fe3e3774c
9 changed files with 579 additions and 198 deletions

View File

@@ -1,4 +1,4 @@
import { SQL } from "bun";
import { randomUUIDv7, SQL } from "bun";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, mock, test } from "bun:test";
import { tempDirWithFiles } from "harness";
import { existsSync } from "node:fs";
@@ -1311,6 +1311,130 @@ describe("SQL helpers", () => {
expect(results[0].value).toBe("test");
});
test("insert into with select helper using where IN", async () => {
const random_name = "test_" + randomUUIDv7("hex").replaceAll("-", "");
await sql`CREATE TEMPORARY TABLE ${sql(random_name)} (id int, name text, age int)`;
{
await sql`INSERT INTO ${sql(random_name)} ${sql({ id: 1, name: "John", age: 30 })}`;
const result = await sql`SELECT * FROM ${sql(random_name)}`;
expect(result[0].id).toBe(1);
expect(result[0].name).toBe("John");
expect(result[0].age).toBe(30);
}
await sql`CREATE TEMPORARY TABLE ${sql(random_name + "2")} (id int, name text, age int)`;
{
await sql`INSERT INTO ${sql(random_name + "2")} (id, name, age) SELECT id, name, age FROM ${sql(random_name)} WHERE id IN ${sql([1, 2])}`;
const result = await sql`SELECT * FROM ${sql(random_name + "2")}`;
expect(result[0].id).toBe(1);
expect(result[0].name).toBe("John");
expect(result[0].age).toBe(30);
}
});
test("update helper with undefined values", async () => {
const random_name = "test_" + randomUUIDv7("hex").replaceAll("-", "");
await sql`CREATE TEMPORARY TABLE ${sql(random_name)} (id int, name text, age int)`;
const users = [
{ id: 1, name: "John", age: 30 },
{ id: 2, name: "Jane", age: 25 },
];
await sql`INSERT INTO ${sql(random_name)} ${sql(users)}`;
await sql`UPDATE ${sql(random_name)} SET ${sql({ name: "Mary", age: undefined })} WHERE id IN ${sql([1, 2])}`;
const result = await sql`SELECT * FROM ${sql(random_name)}`;
expect(result[0].id).toBe(1);
expect(result[0].name).toBe("Mary");
expect(result[0].age).toBe(30);
expect(result[1].id).toBe(2);
expect(result[1].name).toBe("Mary");
expect(result[1].age).toBe(25);
});
test("update helper that starts with undefined values", async () => {
const random_name = "test_" + randomUUIDv7("hex").replaceAll("-", "");
await sql`CREATE TEMPORARY TABLE ${sql(random_name)} (id int, name text, age int)`;
const users = [
{ id: 1, name: "John", age: 30 },
{ id: 2, name: "Jane", age: 25 },
];
await sql`INSERT INTO ${sql(random_name)} ${sql(users)}`;
await sql`UPDATE ${sql(random_name)} SET ${sql({ name: undefined, age: 19 })} WHERE id IN ${sql([1, 2])}`;
const result = await sql`SELECT * FROM ${sql(random_name)}`;
expect(result[0].id).toBe(1);
expect(result[0].name).toBe("John");
expect(result[0].age).toBe(19);
expect(result[1].id).toBe(2);
expect(result[1].name).toBe("Jane");
expect(result[1].age).toBe(19);
});
test("update helper with undefined values and no columns", async () => {
const random_name = "test_" + randomUUIDv7("hex").replaceAll("-", "");
await sql`CREATE TEMPORARY TABLE ${sql(random_name)} (id int, name text, age int)`;
const users = [
{ id: 1, name: "John", age: 30 },
{ id: 2, name: "Jane", age: 25 },
];
await sql`INSERT INTO ${sql(random_name)} ${sql(users)}`;
try {
await sql`UPDATE ${sql(random_name)} SET ${sql({ name: undefined, age: undefined })} WHERE id IN ${sql([1, 2])}`;
expect.unreachable();
} catch (e) {
expect(e).toBeInstanceOf(SyntaxError);
expect(e.message).toBe("Update needs to have at least one column");
}
});
test("update helper with IN and column name", async () => {
const random_name = "test_" + randomUUIDv7("hex").replaceAll("-", "");
await sql`CREATE TEMPORARY TABLE ${sql(random_name)} (id int, name text, age int)`;
const users = [
{ id: 1, name: "John", age: 30 },
{ id: 2, name: "Jane", age: 25 },
];
await sql`INSERT INTO ${sql(random_name)} ${sql(users)}`;
await sql`UPDATE ${sql(random_name)} SET ${sql({ name: "Mary", age: 18 })} WHERE id IN ${sql(users, "id")}`;
const result = await sql`SELECT * FROM ${sql(random_name)}`;
expect(result[0].id).toBe(1);
expect(result[0].name).toBe("Mary");
expect(result[0].age).toBe(18);
expect(result[1].id).toBe(2);
expect(result[1].name).toBe("Mary");
expect(result[1].age).toBe(18);
});
test("select helper with IN using fragment", async () => {
const random_name = "test_" + randomUUIDv7("hex").replaceAll("-", "");
await sql`CREATE TEMPORARY TABLE ${sql(random_name)} (id int, name text, age int)`;
await sql`INSERT INTO ${sql(random_name)} ${sql({ id: 1, name: "John", age: 30 })}`;
const fragment = sql`id IN ${sql([1, 2])}`;
const result = await sql`SELECT * FROM ${sql(random_name)} WHERE ${fragment}`;
expect(result[0].id).toBe(1);
expect(result[0].name).toBe("John");
expect(result[0].age).toBe(30);
});
test("update helper with AND IN", async () => {
const random_name = "test_" + randomUUIDv7("hex").replaceAll("-", "");
await sql`CREATE TEMPORARY TABLE ${sql(random_name)} (id int, name text, age int)`;
const users = [
{ id: 1, name: "John", age: 30 },
{ id: 2, name: "Jane", age: 25 },
];
await sql`INSERT INTO ${sql(random_name)} ${sql(users)}`;
await sql`UPDATE ${sql(random_name)} SET ${sql({ name: "Mary", age: 18 })} WHERE 1=1 AND id IN ${sql([1, 2])}`;
const result = await sql`SELECT * FROM ${sql(random_name)}`;
expect(result[0].id).toBe(1);
expect(result[0].name).toBe("Mary");
expect(result[0].age).toBe(18);
expect(result[1].id).toBe(2);
expect(result[1].name).toBe("Mary");
expect(result[1].age).toBe(18);
});
test("file execution", async () => {
const dir = tempDirWithFiles("sql-files", {
"schema.sql": `