From eeb713f03dbd6eb9eefcc6ca98321fae20672a10 Mon Sep 17 00:00:00 2001 From: byGalax Date: Sun, 17 May 2026 00:59:39 +0200 Subject: [PATCH] fix(P6): convert positional bindings to named-params object for better-sqlite3 --- apps/desktop/electron/modules/sql.ts | 29 ++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/apps/desktop/electron/modules/sql.ts b/apps/desktop/electron/modules/sql.ts index 96e1b7f..40aa4a5 100644 --- a/apps/desktop/electron/modules/sql.ts +++ b/apps/desktop/electron/modules/sql.ts @@ -6,8 +6,10 @@ // (<1ms per op for the current workload). // // Binding param style: Tauri's plugin-sql used $1, $2... positionals with -// bindings as an array. SQLite natively accepts $N so existing queries -// keep working unmodified. +// bindings as an array. SQLite parses `$NAME` as a NAMED parameter +// (NAME = `1`, `2`, …), not as positional, so better-sqlite3 wants the +// bindings as `{ '1': v1, '2': v2 }` not `[v1, v2]`. We accept the old +// array-shape from callers and convert to the named-object on the way in. import { app, ipcMain } from 'electron'; import Database from 'better-sqlite3'; @@ -40,6 +42,20 @@ function requireHandle(h: string): Handle { return entry; } +// Convert a positional bindings array `[v1, v2]` to the named-params object +// `{ '1': v1, '2': v2 }` that better-sqlite3 needs when the SQL uses +// `$1`/`$2` named placeholders. Returns the original array (spread later) +// when it's empty. +function bindParams(bindings: unknown[] | undefined): Record | [] { + const arr = bindings ?? []; + if (arr.length === 0) return []; + const obj: Record = {}; + for (let i = 0; i < arr.length; i++) { + obj[String(i + 1)] = arr[i]; + } + return obj; +} + export function register(): void { ipcMain.handle(CHANNELS.SQL_LOAD, async (_evt, args: SqlLoadArgs): Promise => { const rawName = stripPrefix(args.name); @@ -59,7 +75,8 @@ export function register(): void { async (_evt, args: SqlExecuteArgs): Promise => { const entry = requireHandle(args.handle); const stmt = entry.db.prepare(args.query); - const info = stmt.run(...((args.bindings ?? []) as unknown[])); + const params = bindParams(args.bindings); + const info = Array.isArray(params) ? stmt.run() : stmt.run(params); return { rowsAffected: info.changes, lastInsertId: @@ -75,7 +92,11 @@ export function register(): void { async (_evt, args: SqlSelectArgs): Promise => { const entry = requireHandle(args.handle); const stmt = entry.db.prepare(args.query); - const rows = stmt.all(...((args.bindings ?? []) as unknown[])) as Record[]; + const params = bindParams(args.bindings); + const rows = (Array.isArray(params) ? stmt.all() : stmt.all(params)) as Record< + string, + unknown + >[]; return rows; }, );