From fff7bf52f3fe595bdbfbf1966be1e5dd976dbe1b Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Wed, 30 Jul 2025 14:30:54 +0300 Subject: [PATCH] serverless: Add support for named parameters --- packages/turso-serverless/src/protocol.ts | 8 +++++- packages/turso-serverless/src/session.ts | 29 +++++++++++++++++----- packages/turso-serverless/src/statement.ts | 16 ++++++------ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/packages/turso-serverless/src/protocol.ts b/packages/turso-serverless/src/protocol.ts index aac5c7117..152132fb5 100644 --- a/packages/turso-serverless/src/protocol.ts +++ b/packages/turso-serverless/src/protocol.ts @@ -18,12 +18,17 @@ export interface ExecuteResult { last_insert_rowid?: string; } +export interface NamedArg { + name: string; + value: Value; +} + export interface ExecuteRequest { type: 'execute'; stmt: { sql: string; args: Value[]; - named_args: Value[]; + named_args: NamedArg[]; want_rows: boolean; }; } @@ -32,6 +37,7 @@ export interface BatchStep { stmt: { sql: string; args: Value[]; + named_args?: NamedArg[]; want_rows: boolean; }; condition?: { diff --git a/packages/turso-serverless/src/session.ts b/packages/turso-serverless/src/session.ts index 3adf37a40..74d9e06f4 100644 --- a/packages/turso-serverless/src/session.ts +++ b/packages/turso-serverless/src/session.ts @@ -8,7 +8,9 @@ import { type CursorEntry, type PipelineRequest, type SequenceRequest, - type CloseRequest + type CloseRequest, + type NamedArg, + type Value } from './protocol.js'; import { DatabaseError } from './error.js'; @@ -50,10 +52,10 @@ export class Session { * Execute a SQL statement and return all results. * * @param sql - The SQL statement to execute - * @param args - Optional array of parameter values + * @param args - Optional array of parameter values or object with named parameters * @returns Promise resolving to the complete result set */ - async execute(sql: string, args: any[] = []): Promise { + async execute(sql: string, args: any[] | Record = []): Promise { const { response, entries } = await this.executeRaw(sql, args); const result = await this.processCursorEntries(entries); return result; @@ -63,17 +65,31 @@ export class Session { * Execute a SQL statement and return the raw response and entries. * * @param sql - The SQL statement to execute - * @param args - Optional array of parameter values + * @param args - Optional array of parameter values or object with named parameters * @returns Promise resolving to the raw response and cursor entries */ - async executeRaw(sql: string, args: any[] = []): Promise<{ response: CursorResponse; entries: AsyncGenerator }> { + async executeRaw(sql: string, args: any[] | Record = []): Promise<{ response: CursorResponse; entries: AsyncGenerator }> { + let positionalArgs: Value[] = []; + let namedArgs: NamedArg[] = []; + + if (Array.isArray(args)) { + positionalArgs = args.map(encodeValue); + } else { + // Convert object with named parameters to NamedArg array + namedArgs = Object.entries(args).map(([name, value]) => ({ + name, + value: encodeValue(value) + })); + } + const request: CursorRequest = { baton: this.baton, batch: { steps: [{ stmt: { sql, - args: args.map(encodeValue), + args: positionalArgs, + named_args: namedArgs, want_rows: true } }] @@ -181,6 +197,7 @@ export class Session { stmt: { sql, args: [], + named_args: [], want_rows: false } })) diff --git a/packages/turso-serverless/src/statement.ts b/packages/turso-serverless/src/statement.ts index 72907fb2d..77e7a39e9 100644 --- a/packages/turso-serverless/src/statement.ts +++ b/packages/turso-serverless/src/statement.ts @@ -26,7 +26,7 @@ export class Statement { /** * Executes the prepared statement. * - * @param args - Optional array of parameter values for the SQL statement + * @param args - Optional array of parameter values or object with named parameters * @returns Promise resolving to the result of the statement * * @example @@ -36,7 +36,7 @@ export class Statement { * console.log(`Inserted user with ID ${result.lastInsertRowid}`); * ``` */ - async run(args: any[] = []): Promise { + async run(args: any[] | Record = []): Promise { const result = await this.session.execute(this.sql, args); return { changes: result.rowsAffected, lastInsertRowid: result.lastInsertRowid }; } @@ -44,7 +44,7 @@ export class Statement { /** * Execute the statement and return the first row. * - * @param args - Optional array of parameter values for the SQL statement + * @param args - Optional array of parameter values or object with named parameters * @returns Promise resolving to the first row or null if no results * * @example @@ -56,7 +56,7 @@ export class Statement { * } * ``` */ - async get(args: any[] = []): Promise { + async get(args: any[] | Record = []): Promise { const result = await this.session.execute(this.sql, args); return result.rows[0] || null; } @@ -64,7 +64,7 @@ export class Statement { /** * Execute the statement and return all rows. * - * @param args - Optional array of parameter values for the SQL statement + * @param args - Optional array of parameter values or object with named parameters * @returns Promise resolving to an array of all result rows * * @example @@ -74,7 +74,7 @@ export class Statement { * console.log(`Found ${activeUsers.length} active users`); * ``` */ - async all(args: any[] = []): Promise { + async all(args: any[] | Record = []): Promise { const result = await this.session.execute(this.sql, args); return result.rows; } @@ -85,7 +85,7 @@ export class Statement { * This method provides memory-efficient processing of large result sets * by streaming rows one at a time instead of loading everything into memory. * - * @param args - Optional array of parameter values for the SQL statement + * @param args - Optional array of parameter values or object with named parameters * @returns AsyncGenerator that yields individual rows * * @example @@ -97,7 +97,7 @@ export class Statement { * } * ``` */ - async *iterate(args: any[] = []): AsyncGenerator { + async *iterate(args: any[] | Record = []): AsyncGenerator { const { response, entries } = await this.session.executeRaw(this.sql, args); let columns: string[] = [];