Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/js/internal/sql/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,11 @@ class SQLArrayParameter {
}

class SQLResultArray<T> extends PublicArray<T> {
public count!: number | null;
public command!: string | null;
public lastInsertRowid!: number | bigint | null;
public affectedRows!: number | bigint | null;
// Types only: real class fields would be defined (enumerable) after super() and redefined below.
declare count: number | null;
declare command: string | null;
declare lastInsertRowid: number | bigint | null;
declare affectedRows: number | bigint | null;

static [Symbol.toStringTag] = "SQLResults";

Expand All @@ -91,10 +92,10 @@ class SQLResultArray<T> extends PublicArray<T> {
// match postgres's result array, in this way for in will not list the
// properties and .map will not return undefined command and count
Object.defineProperties(this, {
count: { value: null, writable: true },
command: { value: null, writable: true },
lastInsertRowid: { value: null, writable: true },
affectedRows: { value: null, writable: true },
count: { value: null, writable: true, enumerable: false, configurable: true },
command: { value: null, writable: true, enumerable: false, configurable: true },
lastInsertRowid: { value: null, writable: true, enumerable: false, configurable: true },
affectedRows: { value: null, writable: true, enumerable: false, configurable: true },
});
}

Expand Down
25 changes: 25 additions & 0 deletions test/js/sql/sql-mysql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,31 @@ if (isDockerEnabled()) {
await sql`UPDATE ${sql(random_name)} SET name = "test2" WHERE id = ${lastInsertRowid}`;
expect(affectedRows).toBe(1);
});
test("result metadata properties are not enumerable", async () => {
await using db = new SQL({ ...getOptions(), max: 1, idleTimeout: 5 });
using sql = await db.reserve();
const t = "meta_" + randomUUIDv7("hex").replaceAll("-", "");
await sql`CREATE TEMPORARY TABLE ${sql(t)} (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name text)`;

const inserted = await sql`INSERT INTO ${sql(t)} (name) VALUES (${"test"})`;
expect(Object.keys(inserted)).toEqual([]);
expect(
["lastInsertRowid", "affectedRows"].map(key => Object.getOwnPropertyDescriptor(inserted, key)),
).toEqual([
{ value: 1, writable: true, enumerable: false, configurable: true },
{ value: 1, writable: true, enumerable: false, configurable: true },
]);

const selected = await sql`SELECT id, name FROM ${sql(t)}`;
expect(selected).toEqual([{ id: 1, name: "test" }]);
expect(Object.keys(selected)).toEqual(["0"]);
expect(Object.getOwnPropertyDescriptor(selected, "count")).toEqual({
value: 1,
writable: true,
enumerable: false,
configurable: true,
});
});
test("MEDIUMINT not in the last column reads following columns correctly", async () => {
// MySQL's binary protocol sends MYSQL_TYPE_INT24 as a fixed 4-byte
// field. Reading only 3 left the cursor 1 byte behind, silently
Expand Down
12 changes: 12 additions & 0 deletions test/js/sql/sql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,18 @@ if (isDockerEnabled()) {
expect((await sql`select 1`).command).toBe("SELECT");
});

test("Result metadata is not enumerable", async () => {
const result = await sql`select 1 as x`;
expect(Object.keys(result)).toEqual(["0"]);
const metadata = ["count", "command", "lastInsertRowid", "affectedRows"];
expect(metadata.map(key => Object.getOwnPropertyDescriptor(result, key))).toEqual([
{ value: 1, writable: true, enumerable: false, configurable: true },
{ value: "SELECT", writable: true, enumerable: false, configurable: true },
{ value: null, writable: true, enumerable: false, configurable: true },
{ value: null, writable: true, enumerable: false, configurable: true },
]);
});

test("Create table", async () => {
await sql`create table test(int int)`;
await sql`drop table test`;
Expand Down
32 changes: 32 additions & 0 deletions test/js/sql/sqlite-sql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,38 @@ describe("Query Execution", () => {
expect(result.command).toBe("DELETE");
});

test("result metadata properties are not enumerable", async () => {
await sql`CREATE TABLE result_metadata (id INTEGER PRIMARY KEY, name TEXT)`;
const inserted = await sql`INSERT INTO result_metadata (name) VALUES (${"a"}), (${"b"})`;
const selected = await sql`SELECT * FROM result_metadata ORDER BY id`;

// Like postgres.js, the metadata hangs off the array without showing up
// when the rows are enumerated.
expect(Object.keys(selected)).toEqual(["0", "1"]);
expect(Object.keys(inserted)).toEqual([]);
const forIn: string[] = [];
for (const key in selected) forIn.push(key);
expect(forIn).toEqual(["0", "1"]);
expect({ ...selected }).toEqual({
"0": { id: 1, name: "a" },
"1": { id: 2, name: "b" },
});

const metadata = ["count", "command", "lastInsertRowid", "affectedRows"];
expect(metadata.map(key => Object.getOwnPropertyDescriptor(selected, key))).toEqual([
{ value: 2, writable: true, enumerable: false, configurable: true },
{ value: "SELECT", writable: true, enumerable: false, configurable: true },
{ value: null, writable: true, enumerable: false, configurable: true },
{ value: null, writable: true, enumerable: false, configurable: true },
]);
expect(metadata.map(key => Object.getOwnPropertyDescriptor(inserted, key))).toEqual([
{ value: 2, writable: true, enumerable: false, configurable: true },
{ value: "INSERT", writable: true, enumerable: false, configurable: true },
{ value: 2, writable: true, enumerable: false, configurable: true },
{ value: null, writable: true, enumerable: false, configurable: true },
]);
});

test("SELECT with various clauses", async () => {
await sql`CREATE TABLE scores (id INTEGER, player TEXT, score INTEGER, team TEXT)`;
await sql`INSERT INTO scores VALUES
Expand Down
Loading