serverless: Fix Connection.exec()

We need to use sequence requests to handle multiple SQL statements for exec():

```
  DatabaseError {
    message: 'SQL string contains more than one statement',
  }
```
This commit is contained in:
Pekka Enberg
2025-07-29 14:56:14 +03:00
parent 43fd44aa1a
commit 4841bfd78a
4 changed files with 68 additions and 15 deletions

View File

@@ -1,4 +1,4 @@
import { Connection, connect, type Config as TursoConfig } from './connection.js';
import { Session, type SessionConfig } from './session.js';
import { DatabaseError } from './error.js';
/**
@@ -127,17 +127,17 @@ export interface Client {
}
class LibSQLClient implements Client {
private connection: Connection;
private session: Session;
private _closed = false;
constructor(config: Config) {
this.validateConfig(config);
const tursoConfig: TursoConfig = {
const sessionConfig: SessionConfig = {
url: config.url,
authToken: config.authToken || ''
};
this.connection = connect(tursoConfig);
this.session = new Session(sessionConfig);
}
private validateConfig(config: Config): void {
@@ -246,8 +246,15 @@ class LibSQLClient implements Client {
normalizedStmt = this.normalizeStatement(stmtOrSql);
}
const result = await this.connection.exec(normalizedStmt.sql, normalizedStmt.args);
return this.convertResult(result);
await this.session.sequence(normalizedStmt.sql);
// Return empty result set for sequence execution
return this.convertResult({
columns: [],
columnTypes: [],
rows: [],
rowsAffected: 0,
lastInsertRowid: undefined
});
} catch (error: any) {
throw new LibsqlError(error.message, "EXECUTE_ERROR");
}
@@ -264,7 +271,7 @@ class LibSQLClient implements Client {
return normalized.sql; // For now, ignore args in batch
});
const result = await this.connection.batch(sqlStatements, mode);
const result = await this.session.batch(sqlStatements);
// Return array of result sets (simplified - actual implementation would be more complex)
return [this.convertResult(result)];
@@ -283,7 +290,15 @@ class LibSQLClient implements Client {
}
async executeMultiple(sql: string): Promise<void> {
throw new LibsqlError("Execute multiple not implemented", "NOT_IMPLEMENTED");
try {
if (this._closed) {
throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
}
await this.session.sequence(sql);
} catch (error: any) {
throw new LibsqlError(error.message, "EXECUTE_MULTIPLE_ERROR");
}
}
async sync(): Promise<any> {

View File

@@ -44,7 +44,6 @@ export class Connection {
* Execute a SQL statement and return all results.
*
* @param sql - The SQL statement to execute
* @param args - Optional array of parameter values
* @returns Promise resolving to the complete result set
*
* @example
@@ -53,8 +52,8 @@ export class Connection {
* console.log(result.rows);
* ```
*/
async exec(sql: string, args: any[] = []): Promise<any> {
return this.session.execute(sql, args);
async exec(sql: string): Promise<any> {
return this.session.sequence(sql);
}

View File

@@ -47,9 +47,14 @@ export interface BatchRequest {
};
}
export interface SequenceRequest {
type: 'sequence';
sql: string;
}
export interface PipelineRequest {
baton: string | null;
requests: (ExecuteRequest | BatchRequest)[];
requests: (ExecuteRequest | BatchRequest | SequenceRequest)[];
}
export interface PipelineResponse {
@@ -58,8 +63,8 @@ export interface PipelineResponse {
results: Array<{
type: 'ok' | 'error';
response?: {
type: 'execute' | 'batch';
result: ExecuteResult;
type: 'execute' | 'batch' | 'sequence';
result?: ExecuteResult;
};
error?: {
message: string;

View File

@@ -1,10 +1,13 @@
import {
executeCursor,
executePipeline,
encodeValue,
decodeValue,
type CursorRequest,
type CursorResponse,
type CursorEntry
type CursorEntry,
type PipelineRequest,
type SequenceRequest
} from './protocol.js';
import { DatabaseError } from './error.js';
@@ -214,4 +217,35 @@ export class Session {
lastInsertRowid
};
}
/**
* Execute a sequence of SQL statements separated by semicolons.
*
* @param sql - SQL string containing multiple statements separated by semicolons
* @returns Promise resolving when all statements are executed
*/
async sequence(sql: string): Promise<void> {
const request: PipelineRequest = {
baton: this.baton,
requests: [{
type: "sequence",
sql: sql
} as SequenceRequest]
};
const response = await executePipeline(this.baseUrl, this.config.authToken, request);
this.baton = response.baton;
if (response.base_url) {
this.baseUrl = response.base_url;
}
// Check for errors in the response
if (response.results && response.results[0]) {
const result = response.results[0];
if (result.type === "error") {
throw new DatabaseError(result.error?.message || 'Sequence execution failed');
}
}
}
}