From 00b404329ca21c508ac97fbeee92b10ded779520 Mon Sep 17 00:00:00 2001 From: Taesu Date: Sun, 31 May 2026 10:17:44 +0900 Subject: [PATCH] fix(kysely-adapter): report affected rows and insert id from bun/node sqlite dialects --- .changeset/kysely-sqlite-affected-rows.md | 5 ++ .../kysely-adapter/src/bun-sqlite-dialect.ts | 17 ++++- .../src/node-sqlite-dialect.test.ts | 69 +++++++++++++++++++ .../kysely-adapter/src/node-sqlite-dialect.ts | 17 ++++- .../src/sqlite-introspector.test.ts | 10 ++- 5 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 .changeset/kysely-sqlite-affected-rows.md create mode 100644 packages/kysely-adapter/src/node-sqlite-dialect.test.ts diff --git a/.changeset/kysely-sqlite-affected-rows.md b/.changeset/kysely-sqlite-affected-rows.md new file mode 100644 index 0000000000..ee494905f3 --- /dev/null +++ b/.changeset/kysely-sqlite-affected-rows.md @@ -0,0 +1,5 @@ +--- +"@better-auth/kysely-adapter": patch +--- + +`deleteMany` and `updateMany` now return the correct number of affected rows on the `bun:sqlite` and `node:sqlite` dialects, which previously always returned `0`. diff --git a/packages/kysely-adapter/src/bun-sqlite-dialect.ts b/packages/kysely-adapter/src/bun-sqlite-dialect.ts index c2c12c0904..2874acaf4b 100644 --- a/packages/kysely-adapter/src/bun-sqlite-dialect.ts +++ b/packages/kysely-adapter/src/bun-sqlite-dialect.ts @@ -2,7 +2,7 @@ * @see {@link https://github.com/dylanblokhuis/kysely-bun-sqlite} - Fork of the original kysely-bun-sqlite package by @dylanblokhuis */ -import type { Database } from "bun:sqlite"; +import type { Database, SQLQueryBindings } from "bun:sqlite"; import type { DatabaseConnection, DatabaseIntrospector, @@ -132,8 +132,21 @@ class BunSqliteConnection implements DatabaseConnection { const { sql, parameters } = compiledQuery; const stmt = this.#db.prepare(sql); + // SELECT and RETURNING statements expose columns and return rows. + // A plain mutation reports its row count and insert id only through run(). + if (stmt.columnNames.length > 0) { + return Promise.resolve({ + rows: stmt.all(...(parameters as SQLQueryBindings[])) as O[], + }); + } + + const { changes, lastInsertRowid } = stmt.run( + ...(parameters as SQLQueryBindings[]), + ); return Promise.resolve({ - rows: stmt.all(parameters as any) as O[], + rows: [], + numAffectedRows: changes != null ? BigInt(changes) : undefined, + insertId: lastInsertRowid != null ? BigInt(lastInsertRowid) : undefined, }); } diff --git a/packages/kysely-adapter/src/node-sqlite-dialect.test.ts b/packages/kysely-adapter/src/node-sqlite-dialect.test.ts new file mode 100644 index 0000000000..29fddaa96e --- /dev/null +++ b/packages/kysely-adapter/src/node-sqlite-dialect.test.ts @@ -0,0 +1,69 @@ +import { DatabaseSync } from "node:sqlite"; +import type { Generated } from "kysely"; +import { Kysely } from "kysely"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { NodeSqliteDialect } from "./node-sqlite-dialect"; + +interface TestDatabase { + item: { + id: Generated; + name: string; + }; +} + +describe("NodeSqliteDialect", () => { + let raw: DatabaseSync; + let db: Kysely; + + beforeEach(async () => { + raw = new DatabaseSync(":memory:"); + db = new Kysely({ + dialect: new NodeSqliteDialect({ database: raw }), + }); + await db.schema + .createTable("item") + .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement()) + .addColumn("name", "text") + .execute(); + await db + .insertInto("item") + .values([{ name: "a" }, { name: "b" }, { name: "c" }]) + .execute(); + }); + + afterEach(async () => { + await db.destroy(); + }); + + // A mutation without a RETURNING clause must still report how many rows it + // touched. updateMany/deleteMany in the adapter read numUpdatedRows / + // numDeletedRows, which Kysely derives from the connection's numAffectedRows + // (falling back to 0 when absent). A dialect that drops it silently reports 0. + it("reports the number of rows affected by a delete", async () => { + const result = await db + .deleteFrom("item") + .where("name", "in", ["a", "b"]) + .executeTakeFirst(); + + expect(Number(result.numDeletedRows)).toBe(2); + }); + + it("reports the number of rows affected by an update", async () => { + const result = await db + .updateTable("item") + .set({ name: "z" }) + .executeTakeFirst(); + + expect(Number(result.numUpdatedRows)).toBe(3); + }); + + it("reports the inserted row id when no RETURNING clause is used", async () => { + const result = await db + .insertInto("item") + .values({ name: "d" }) + .executeTakeFirst(); + + expect(result.insertId).toBeDefined(); + expect(Number(result.insertId)).toBeGreaterThan(0); + }); +}); diff --git a/packages/kysely-adapter/src/node-sqlite-dialect.ts b/packages/kysely-adapter/src/node-sqlite-dialect.ts index aa51c98d92..db3a8c6095 100644 --- a/packages/kysely-adapter/src/node-sqlite-dialect.ts +++ b/packages/kysely-adapter/src/node-sqlite-dialect.ts @@ -2,7 +2,7 @@ * @see {@link https://nodejs.org/api/sqlite.html} - Node.js SQLite API documentation */ -import type { DatabaseSync } from "node:sqlite"; +import type { DatabaseSync, SQLInputValue } from "node:sqlite"; import type { DatabaseConnection, DatabaseIntrospector, @@ -132,10 +132,21 @@ class NodeSqliteConnection implements DatabaseConnection { const { sql, parameters } = compiledQuery; const stmt = this.#db.prepare(sql); - const rows = stmt.all(...(parameters as any[])) as O[]; + // SELECT and RETURNING statements expose columns and return rows. + // A plain mutation reports its row count and insert id only through run(). + if (stmt.columns().length > 0) { + return Promise.resolve({ + rows: stmt.all(...(parameters as SQLInputValue[])) as O[], + }); + } + const { changes, lastInsertRowid } = stmt.run( + ...(parameters as SQLInputValue[]), + ); return Promise.resolve({ - rows, + rows: [], + numAffectedRows: changes != null ? BigInt(changes) : undefined, + insertId: lastInsertRowid != null ? BigInt(lastInsertRowid) : undefined, }); } diff --git a/packages/kysely-adapter/src/sqlite-introspector.test.ts b/packages/kysely-adapter/src/sqlite-introspector.test.ts index d3762cd37f..772b562cc7 100644 --- a/packages/kysely-adapter/src/sqlite-introspector.test.ts +++ b/packages/kysely-adapter/src/sqlite-introspector.test.ts @@ -20,8 +20,14 @@ function asBunLikeDatabase(db: DatabaseSync) { prepare(sql: string) { const stmt = db.prepare(sql); return { - all(params: SQLInputValue[]) { - return stmt.all(...(params ?? [])); + get columnNames() { + return stmt.columns().map((column) => column.name); + }, + all(...params: SQLInputValue[]) { + return stmt.all(...params); + }, + run(...params: SQLInputValue[]) { + return stmt.run(...params); }, }; },