mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-31 13:54:27 +01:00
Merge 'JavaScript serverless driver fixes' from Pekka Enberg
...align it with the native bindings. Closes #2386
This commit is contained in:
@@ -75,11 +75,36 @@ export class Session {
|
||||
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)
|
||||
}));
|
||||
// Check if this is an object with numeric keys (for ?1, ?2 style parameters)
|
||||
const keys = Object.keys(args);
|
||||
const isNumericKeys = keys.length > 0 && keys.every(key => /^\d+$/.test(key));
|
||||
|
||||
if (isNumericKeys) {
|
||||
// Convert numeric-keyed object to positional args
|
||||
// Sort keys numerically to ensure correct order
|
||||
const sortedKeys = keys.sort((a, b) => parseInt(a) - parseInt(b));
|
||||
const maxIndex = parseInt(sortedKeys[sortedKeys.length - 1]);
|
||||
|
||||
// Create array with undefined for missing indices
|
||||
positionalArgs = new Array(maxIndex);
|
||||
for (const key of sortedKeys) {
|
||||
const index = parseInt(key) - 1; // Convert to 0-based index
|
||||
positionalArgs[index] = encodeValue(args[key]);
|
||||
}
|
||||
|
||||
// Fill any undefined values with null
|
||||
for (let i = 0; i < positionalArgs.length; i++) {
|
||||
if (positionalArgs[i] === undefined) {
|
||||
positionalArgs[i] = { type: 'null' };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Convert object with named parameters to NamedArg array
|
||||
namedArgs = Object.entries(args).map(([name, value]) => ({
|
||||
name,
|
||||
value: encodeValue(value)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const request: CursorRequest = {
|
||||
|
||||
@@ -17,12 +17,32 @@ import { DatabaseError } from './error.js';
|
||||
export class Statement {
|
||||
private session: Session;
|
||||
private sql: string;
|
||||
private presentationMode: 'expanded' | 'raw' | 'pluck' = 'expanded';
|
||||
|
||||
constructor(sessionConfig: SessionConfig, sql: string) {
|
||||
this.session = new Session(sessionConfig);
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable raw mode to return arrays instead of objects.
|
||||
*
|
||||
* @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled.
|
||||
* @returns This statement instance for chaining
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const stmt = client.prepare("SELECT * FROM users WHERE id = ?");
|
||||
* const row = await stmt.raw().get([1]);
|
||||
* console.log(row); // [1, "Alice", "alice@example.org"]
|
||||
* ```
|
||||
*/
|
||||
raw(raw?: boolean): Statement {
|
||||
this.presentationMode = raw === false ? 'expanded' : 'raw';
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the prepared statement.
|
||||
*
|
||||
@@ -36,8 +56,9 @@ export class Statement {
|
||||
* console.log(`Inserted user with ID ${result.lastInsertRowid}`);
|
||||
* ```
|
||||
*/
|
||||
async run(args: any[] | Record<string, any> = []): Promise<any> {
|
||||
const result = await this.session.execute(this.sql, args);
|
||||
async run(args?: any): Promise<any> {
|
||||
const normalizedArgs = this.normalizeArgs(args);
|
||||
const result = await this.session.execute(this.sql, normalizedArgs);
|
||||
return { changes: result.rowsAffected, lastInsertRowid: result.lastInsertRowid };
|
||||
}
|
||||
|
||||
@@ -45,7 +66,7 @@ export class Statement {
|
||||
* Execute the statement and return the first row.
|
||||
*
|
||||
* @param args - Optional array of parameter values or object with named parameters
|
||||
* @returns Promise resolving to the first row or null if no results
|
||||
* @returns Promise resolving to the first row or undefined if no results
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -56,9 +77,21 @@ export class Statement {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async get(args: any[] | Record<string, any> = []): Promise<any> {
|
||||
const result = await this.session.execute(this.sql, args);
|
||||
return result.rows[0] || null;
|
||||
async get(args?: any): Promise<any> {
|
||||
const normalizedArgs = this.normalizeArgs(args);
|
||||
const result = await this.session.execute(this.sql, normalizedArgs);
|
||||
const row = result.rows[0];
|
||||
if (!row) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.presentationMode === 'raw') {
|
||||
// In raw mode, return the row as a plain array (it already is one)
|
||||
// The row object is already an array with column properties added
|
||||
return [...row];
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,8 +107,16 @@ export class Statement {
|
||||
* console.log(`Found ${activeUsers.length} active users`);
|
||||
* ```
|
||||
*/
|
||||
async all(args: any[] | Record<string, any> = []): Promise<any[]> {
|
||||
const result = await this.session.execute(this.sql, args);
|
||||
async all(args?: any): Promise<any[]> {
|
||||
const normalizedArgs = this.normalizeArgs(args);
|
||||
const result = await this.session.execute(this.sql, normalizedArgs);
|
||||
|
||||
if (this.presentationMode === 'raw') {
|
||||
// In raw mode, return arrays of values
|
||||
// Each row is already an array with column properties added
|
||||
return result.rows.map((row: any) => [...row]);
|
||||
}
|
||||
|
||||
return result.rows;
|
||||
}
|
||||
|
||||
@@ -97,8 +138,9 @@ export class Statement {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async *iterate(args: any[] | Record<string, any> = []): AsyncGenerator<any> {
|
||||
const { response, entries } = await this.session.executeRaw(this.sql, args);
|
||||
async *iterate(args?: any): AsyncGenerator<any> {
|
||||
const normalizedArgs = this.normalizeArgs(args);
|
||||
const { response, entries } = await this.session.executeRaw(this.sql, normalizedArgs);
|
||||
|
||||
let columns: string[] = [];
|
||||
|
||||
@@ -112,8 +154,13 @@ export class Statement {
|
||||
case 'row':
|
||||
if (entry.row) {
|
||||
const decodedRow = entry.row.map(decodeValue);
|
||||
const rowObject = this.session.createRowObject(decodedRow, columns);
|
||||
yield rowObject;
|
||||
if (this.presentationMode === 'raw') {
|
||||
// In raw mode, yield arrays of values
|
||||
yield decodedRow;
|
||||
} else {
|
||||
const rowObject = this.session.createRowObject(decodedRow, columns);
|
||||
yield rowObject;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'step_error':
|
||||
@@ -123,4 +170,27 @@ export class Statement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize arguments to handle both single values and arrays.
|
||||
* Matches the behavior of the native bindings.
|
||||
*/
|
||||
private normalizeArgs(args: any): any[] | Record<string, any> {
|
||||
// No arguments provided
|
||||
if (args === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If it's an array, return as-is
|
||||
if (Array.isArray(args)) {
|
||||
return args;
|
||||
}
|
||||
|
||||
// Check if it's a plain object (for named parameters)
|
||||
if (args !== null && typeof args === 'object' && args.constructor === Object) {
|
||||
return args;
|
||||
}
|
||||
|
||||
// Single value - wrap in array
|
||||
return [args];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user