@tursodatabase/serverless 1.0.0 → 1.1.0-pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compat.d.ts +2 -3
- package/dist/compat.js +118 -1
- package/dist/connection.d.ts +6 -5
- package/dist/connection.js +10 -10
- package/dist/error.d.ts +10 -0
- package/dist/error.js +14 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/protocol.d.ts +7 -2
- package/dist/protocol.js +60 -27
- package/dist/session.d.ts +9 -6
- package/dist/session.js +20 -13
- package/dist/statement.d.ts +16 -7
- package/dist/statement.js +86 -44
- package/package.json +1 -1
package/dist/compat.d.ts
CHANGED
|
@@ -87,11 +87,10 @@ export declare class LibsqlError extends Error {
|
|
|
87
87
|
constructor(message: string, code: string, extendedCode?: string, rawCode?: number, cause?: Error);
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* Interactive transaction interface
|
|
90
|
+
* Interactive transaction interface.
|
|
91
91
|
*
|
|
92
92
|
* @remarks
|
|
93
|
-
*
|
|
94
|
-
* Calling transaction() will throw a LibsqlError.
|
|
93
|
+
* A transaction keeps a dedicated session open until commit/rollback/close.
|
|
95
94
|
*/
|
|
96
95
|
export interface Transaction {
|
|
97
96
|
execute(stmt: InStatement): Promise<ResultSet>;
|
package/dist/compat.js
CHANGED
|
@@ -25,6 +25,7 @@ class LibSQLClient {
|
|
|
25
25
|
authToken: config.authToken || '',
|
|
26
26
|
remoteEncryptionKey: config.remoteEncryptionKey
|
|
27
27
|
};
|
|
28
|
+
this.sessionConfig = sessionConfig;
|
|
28
29
|
this.session = new Session(sessionConfig);
|
|
29
30
|
}
|
|
30
31
|
validateConfig(config) {
|
|
@@ -157,8 +158,124 @@ class LibSQLClient {
|
|
|
157
158
|
// For now, just call batch - in a real implementation this would disable foreign keys
|
|
158
159
|
return this.batch(stmts, "write");
|
|
159
160
|
}
|
|
161
|
+
modeToBeginSql(mode) {
|
|
162
|
+
switch (mode) {
|
|
163
|
+
case "write":
|
|
164
|
+
return "BEGIN IMMEDIATE";
|
|
165
|
+
case "deferred":
|
|
166
|
+
return "BEGIN DEFERRED";
|
|
167
|
+
case "read":
|
|
168
|
+
default:
|
|
169
|
+
return "BEGIN";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
160
172
|
async transaction(mode) {
|
|
161
|
-
|
|
173
|
+
await this.execLock.acquire();
|
|
174
|
+
if (this._closed) {
|
|
175
|
+
this.execLock.release();
|
|
176
|
+
throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
|
|
177
|
+
}
|
|
178
|
+
const txSession = new Session(this.sessionConfig);
|
|
179
|
+
let txClosed = false;
|
|
180
|
+
let cleanupStarted = false;
|
|
181
|
+
const ensureOpen = () => {
|
|
182
|
+
if (txClosed) {
|
|
183
|
+
throw new LibsqlError("Transaction is closed", "TRANSACTION_CLOSED");
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const closeTx = async () => {
|
|
187
|
+
if (cleanupStarted)
|
|
188
|
+
return;
|
|
189
|
+
cleanupStarted = true;
|
|
190
|
+
txClosed = true;
|
|
191
|
+
try {
|
|
192
|
+
await txSession.close();
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
this.execLock.release();
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const executeInTx = async (stmt) => {
|
|
199
|
+
ensureOpen();
|
|
200
|
+
const normalized = this.normalizeStatement(stmt);
|
|
201
|
+
try {
|
|
202
|
+
const result = await txSession.execute(normalized.sql, normalized.args, this._defaultSafeIntegers);
|
|
203
|
+
return this.convertResult(result);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
throw mapDatabaseError(error, "EXECUTE_ERROR");
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
await txSession.sequence(this.modeToBeginSql(mode));
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
await closeTx();
|
|
214
|
+
throw mapDatabaseError(error, "BEGIN_ERROR");
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
execute: async (stmtOrSql, args) => {
|
|
218
|
+
if (typeof stmtOrSql === "string") {
|
|
219
|
+
const normalizedArgs = args ? (Array.isArray(args) ? args : Object.values(args)) : [];
|
|
220
|
+
return executeInTx({ sql: stmtOrSql, args: normalizedArgs });
|
|
221
|
+
}
|
|
222
|
+
return executeInTx(stmtOrSql);
|
|
223
|
+
},
|
|
224
|
+
batch: async (stmts) => {
|
|
225
|
+
ensureOpen();
|
|
226
|
+
const results = [];
|
|
227
|
+
for (const stmt of stmts) {
|
|
228
|
+
results.push(await executeInTx(stmt));
|
|
229
|
+
}
|
|
230
|
+
return results;
|
|
231
|
+
},
|
|
232
|
+
executeMultiple: async (sql) => {
|
|
233
|
+
ensureOpen();
|
|
234
|
+
try {
|
|
235
|
+
await txSession.sequence(sql);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
throw mapDatabaseError(error, "EXECUTE_MULTIPLE_ERROR");
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
commit: async () => {
|
|
242
|
+
ensureOpen();
|
|
243
|
+
try {
|
|
244
|
+
await txSession.sequence("COMMIT");
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
throw mapDatabaseError(error, "COMMIT_ERROR");
|
|
248
|
+
}
|
|
249
|
+
finally {
|
|
250
|
+
await closeTx();
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
rollback: async () => {
|
|
254
|
+
ensureOpen();
|
|
255
|
+
try {
|
|
256
|
+
await txSession.sequence("ROLLBACK");
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
throw mapDatabaseError(error, "ROLLBACK_ERROR");
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
await closeTx();
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
close: () => {
|
|
266
|
+
if (txClosed)
|
|
267
|
+
return;
|
|
268
|
+
txClosed = true;
|
|
269
|
+
void txSession.sequence("ROLLBACK")
|
|
270
|
+
.catch(() => undefined)
|
|
271
|
+
.finally(() => {
|
|
272
|
+
void closeTx();
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
get closed() {
|
|
276
|
+
return txClosed;
|
|
277
|
+
},
|
|
278
|
+
};
|
|
162
279
|
}
|
|
163
280
|
async executeMultiple(sql) {
|
|
164
281
|
await this.execLock.acquire();
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type SessionConfig } from './session.js';
|
|
2
2
|
import { Statement } from './statement.js';
|
|
3
|
+
import { type QueryOptions } from './protocol.js';
|
|
3
4
|
/**
|
|
4
5
|
* Configuration options for connecting to a Turso database.
|
|
5
6
|
*/
|
|
@@ -64,7 +65,7 @@ export declare class Connection {
|
|
|
64
65
|
/**
|
|
65
66
|
* Prepare a SQL statement for execution.
|
|
66
67
|
*
|
|
67
|
-
*
|
|
68
|
+
* Prepared statements created from a Connection use the same underlying session so transaction boundaries are preserved.
|
|
68
69
|
* This method fetches column metadata using the describe functionality.
|
|
69
70
|
*
|
|
70
71
|
* @param sql - The SQL statement to prepare
|
|
@@ -91,7 +92,7 @@ export declare class Connection {
|
|
|
91
92
|
* console.log(result.rows);
|
|
92
93
|
* ```
|
|
93
94
|
*/
|
|
94
|
-
execute(sql: string, args?: any[]): Promise<any>;
|
|
95
|
+
execute(sql: string, args?: any[], queryOptions?: QueryOptions): Promise<any>;
|
|
95
96
|
/**
|
|
96
97
|
* Execute a SQL statement and return all results.
|
|
97
98
|
*
|
|
@@ -104,7 +105,7 @@ export declare class Connection {
|
|
|
104
105
|
* console.log(result.rows);
|
|
105
106
|
* ```
|
|
106
107
|
*/
|
|
107
|
-
exec(sql: string): Promise<any>;
|
|
108
|
+
exec(sql: string, queryOptions?: QueryOptions): Promise<any>;
|
|
108
109
|
/**
|
|
109
110
|
* Execute multiple SQL statements in a batch.
|
|
110
111
|
*
|
|
@@ -121,14 +122,14 @@ export declare class Connection {
|
|
|
121
122
|
* ]);
|
|
122
123
|
* ```
|
|
123
124
|
*/
|
|
124
|
-
batch(statements: string[], mode?: string): Promise<any>;
|
|
125
|
+
batch(statements: string[], mode?: string, queryOptions?: QueryOptions): Promise<any>;
|
|
125
126
|
/**
|
|
126
127
|
* Execute a pragma.
|
|
127
128
|
*
|
|
128
129
|
* @param pragma - The pragma to execute
|
|
129
130
|
* @returns Promise resolving to the result of the pragma
|
|
130
131
|
*/
|
|
131
|
-
pragma(pragma: string): Promise<any>;
|
|
132
|
+
pragma(pragma: string, queryOptions?: QueryOptions): Promise<any>;
|
|
132
133
|
/**
|
|
133
134
|
* Sets the default safe integers mode for all statements from this connection.
|
|
134
135
|
*
|
package/dist/connection.js
CHANGED
|
@@ -71,7 +71,7 @@ export class Connection {
|
|
|
71
71
|
/**
|
|
72
72
|
* Prepare a SQL statement for execution.
|
|
73
73
|
*
|
|
74
|
-
*
|
|
74
|
+
* Prepared statements created from a Connection use the same underlying session so transaction boundaries are preserved.
|
|
75
75
|
* This method fetches column metadata using the describe functionality.
|
|
76
76
|
*
|
|
77
77
|
* @param sql - The SQL statement to prepare
|
|
@@ -92,7 +92,7 @@ export class Connection {
|
|
|
92
92
|
const session = new Session(this.config);
|
|
93
93
|
const description = await session.describe(sql);
|
|
94
94
|
await session.close();
|
|
95
|
-
const stmt =
|
|
95
|
+
const stmt = Statement.fromSession(this.session, sql, description.cols, this.execLock);
|
|
96
96
|
if (this.defaultSafeIntegerMode) {
|
|
97
97
|
stmt.safeIntegers(true);
|
|
98
98
|
}
|
|
@@ -111,13 +111,13 @@ export class Connection {
|
|
|
111
111
|
* console.log(result.rows);
|
|
112
112
|
* ```
|
|
113
113
|
*/
|
|
114
|
-
async execute(sql, args) {
|
|
114
|
+
async execute(sql, args, queryOptions) {
|
|
115
115
|
if (!this.isOpen) {
|
|
116
116
|
throw new TypeError("The database connection is not open");
|
|
117
117
|
}
|
|
118
118
|
await this.execLock.acquire();
|
|
119
119
|
try {
|
|
120
|
-
return await this.session.execute(sql, args || [], this.defaultSafeIntegerMode);
|
|
120
|
+
return await this.session.execute(sql, args || [], this.defaultSafeIntegerMode, queryOptions);
|
|
121
121
|
}
|
|
122
122
|
finally {
|
|
123
123
|
this.execLock.release();
|
|
@@ -135,13 +135,13 @@ export class Connection {
|
|
|
135
135
|
* console.log(result.rows);
|
|
136
136
|
* ```
|
|
137
137
|
*/
|
|
138
|
-
async exec(sql) {
|
|
138
|
+
async exec(sql, queryOptions) {
|
|
139
139
|
if (!this.isOpen) {
|
|
140
140
|
throw new TypeError("The database connection is not open");
|
|
141
141
|
}
|
|
142
142
|
await this.execLock.acquire();
|
|
143
143
|
try {
|
|
144
|
-
return await this.session.sequence(sql);
|
|
144
|
+
return await this.session.sequence(sql, queryOptions);
|
|
145
145
|
}
|
|
146
146
|
finally {
|
|
147
147
|
this.execLock.release();
|
|
@@ -163,13 +163,13 @@ export class Connection {
|
|
|
163
163
|
* ]);
|
|
164
164
|
* ```
|
|
165
165
|
*/
|
|
166
|
-
async batch(statements, mode) {
|
|
166
|
+
async batch(statements, mode, queryOptions) {
|
|
167
167
|
if (!this.isOpen) {
|
|
168
168
|
throw new TypeError("The database connection is not open");
|
|
169
169
|
}
|
|
170
170
|
await this.execLock.acquire();
|
|
171
171
|
try {
|
|
172
|
-
return await this.session.batch(statements);
|
|
172
|
+
return await this.session.batch(statements, queryOptions);
|
|
173
173
|
}
|
|
174
174
|
finally {
|
|
175
175
|
this.execLock.release();
|
|
@@ -181,14 +181,14 @@ export class Connection {
|
|
|
181
181
|
* @param pragma - The pragma to execute
|
|
182
182
|
* @returns Promise resolving to the result of the pragma
|
|
183
183
|
*/
|
|
184
|
-
async pragma(pragma) {
|
|
184
|
+
async pragma(pragma, queryOptions) {
|
|
185
185
|
if (!this.isOpen) {
|
|
186
186
|
throw new TypeError("The database connection is not open");
|
|
187
187
|
}
|
|
188
188
|
await this.execLock.acquire();
|
|
189
189
|
try {
|
|
190
190
|
const sql = `PRAGMA ${pragma}`;
|
|
191
|
-
return await this.session.execute(sql);
|
|
191
|
+
return await this.session.execute(sql, [], false, queryOptions);
|
|
192
192
|
}
|
|
193
193
|
finally {
|
|
194
194
|
this.execLock.release();
|
package/dist/error.d.ts
CHANGED
|
@@ -7,3 +7,13 @@ export declare class DatabaseError extends Error {
|
|
|
7
7
|
cause?: Error;
|
|
8
8
|
constructor(message: string, code?: string, rawCode?: number, cause?: Error);
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Error thrown when a query exceeds the configured timeout.
|
|
12
|
+
*
|
|
13
|
+
* This is a subclass of `DatabaseError` with `code` set to `"TIMEOUT"`.
|
|
14
|
+
* Catch this type to distinguish timeouts from other database errors
|
|
15
|
+
* and decide whether to retry or fail gracefully.
|
|
16
|
+
*/
|
|
17
|
+
export declare class TimeoutError extends DatabaseError {
|
|
18
|
+
constructor(message?: string, cause?: Error);
|
|
19
|
+
}
|
package/dist/error.js
CHANGED
|
@@ -8,3 +8,17 @@ export class DatabaseError extends Error {
|
|
|
8
8
|
Object.setPrototypeOf(this, DatabaseError.prototype);
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when a query exceeds the configured timeout.
|
|
13
|
+
*
|
|
14
|
+
* This is a subclass of `DatabaseError` with `code` set to `"TIMEOUT"`.
|
|
15
|
+
* Catch this type to distinguish timeouts from other database errors
|
|
16
|
+
* and decide whether to retry or fail gracefully.
|
|
17
|
+
*/
|
|
18
|
+
export class TimeoutError extends DatabaseError {
|
|
19
|
+
constructor(message = 'Query timed out', cause) {
|
|
20
|
+
super(message, 'TIMEOUT', undefined, cause);
|
|
21
|
+
this.name = 'TimeoutError';
|
|
22
|
+
Object.setPrototypeOf(this, TimeoutError.prototype);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { Connection, connect, type Config } from './connection.js';
|
|
2
2
|
export { Statement } from './statement.js';
|
|
3
3
|
export { Session, type SessionConfig } from './session.js';
|
|
4
|
-
export { DatabaseError } from './error.js';
|
|
5
|
-
export { type Column, ENCRYPTION_KEY_HEADER } from './protocol.js';
|
|
4
|
+
export { DatabaseError, TimeoutError } from './error.js';
|
|
5
|
+
export { type Column, type QueryOptions, ENCRYPTION_KEY_HEADER } from './protocol.js';
|
package/dist/index.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
export { Connection, connect } from './connection.js';
|
|
3
3
|
export { Statement } from './statement.js';
|
|
4
4
|
export { Session } from './session.js';
|
|
5
|
-
export { DatabaseError } from './error.js';
|
|
5
|
+
export { DatabaseError, TimeoutError } from './error.js';
|
|
6
6
|
export { ENCRYPTION_KEY_HEADER } from './protocol.js';
|
package/dist/protocol.d.ts
CHANGED
|
@@ -108,8 +108,13 @@ export interface CursorEntry {
|
|
|
108
108
|
}
|
|
109
109
|
/** HTTP header key for the encryption key */
|
|
110
110
|
export declare const ENCRYPTION_KEY_HEADER = "x-turso-encryption-key";
|
|
111
|
-
|
|
111
|
+
/** Per-query timeout options. Overrides defaultQueryTimeout for this call. */
|
|
112
|
+
export interface QueryOptions {
|
|
113
|
+
/** Per-query timeout in milliseconds. Overrides defaultQueryTimeout for this call. */
|
|
114
|
+
queryTimeout?: number;
|
|
115
|
+
}
|
|
116
|
+
export declare function executeCursor(url: string, authToken: string | undefined, request: CursorRequest, remoteEncryptionKey?: string, signal?: AbortSignal): Promise<{
|
|
112
117
|
response: CursorResponse;
|
|
113
118
|
entries: AsyncGenerator<CursorEntry>;
|
|
114
119
|
}>;
|
|
115
|
-
export declare function executePipeline(url: string, authToken: string | undefined, request: PipelineRequest, remoteEncryptionKey?: string): Promise<PipelineResponse>;
|
|
120
|
+
export declare function executePipeline(url: string, authToken: string | undefined, request: PipelineRequest, remoteEncryptionKey?: string, signal?: AbortSignal): Promise<PipelineResponse>;
|
package/dist/protocol.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DatabaseError } from './error.js';
|
|
1
|
+
import { DatabaseError, TimeoutError } from './error.js';
|
|
2
2
|
export function encodeValue(value) {
|
|
3
3
|
if (value === null || value === undefined) {
|
|
4
4
|
return { type: 'null' };
|
|
@@ -53,7 +53,13 @@ export function decodeValue(value, safeIntegers = false) {
|
|
|
53
53
|
}
|
|
54
54
|
/** HTTP header key for the encryption key */
|
|
55
55
|
export const ENCRYPTION_KEY_HEADER = 'x-turso-encryption-key';
|
|
56
|
-
|
|
56
|
+
function wrapAbortError(error) {
|
|
57
|
+
if (error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')) {
|
|
58
|
+
throw new TimeoutError('Query timed out');
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
export async function executeCursor(url, authToken, request, remoteEncryptionKey, signal) {
|
|
57
63
|
const headers = {
|
|
58
64
|
'Content-Type': 'application/json',
|
|
59
65
|
};
|
|
@@ -63,11 +69,18 @@ export async function executeCursor(url, authToken, request, remoteEncryptionKey
|
|
|
63
69
|
if (remoteEncryptionKey) {
|
|
64
70
|
headers[ENCRYPTION_KEY_HEADER] = remoteEncryptionKey;
|
|
65
71
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
let response;
|
|
73
|
+
try {
|
|
74
|
+
response = await fetch(`${url}/v3/cursor`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers,
|
|
77
|
+
body: JSON.stringify(request),
|
|
78
|
+
signal,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
wrapAbortError(error);
|
|
83
|
+
}
|
|
71
84
|
if (!response.ok) {
|
|
72
85
|
let errorMessage = `HTTP error! status: ${response.status}`;
|
|
73
86
|
try {
|
|
@@ -90,22 +103,29 @@ export async function executeCursor(url, authToken, request, remoteEncryptionKey
|
|
|
90
103
|
let buffer = '';
|
|
91
104
|
let cursorResponse;
|
|
92
105
|
// First, read until we get the cursor response (first line)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
buffer += decoder.decode(value, { stream: true });
|
|
98
|
-
const newlineIndex = buffer.indexOf('\n');
|
|
99
|
-
if (newlineIndex !== -1) {
|
|
100
|
-
const line = buffer.slice(0, newlineIndex).trim();
|
|
101
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
102
|
-
if (line) {
|
|
103
|
-
cursorResponse = JSON.parse(line);
|
|
106
|
+
try {
|
|
107
|
+
while (!cursorResponse) {
|
|
108
|
+
const { done, value } = await reader.read();
|
|
109
|
+
if (done)
|
|
104
110
|
break;
|
|
111
|
+
buffer += decoder.decode(value, { stream: true });
|
|
112
|
+
const newlineIndex = buffer.indexOf('\n');
|
|
113
|
+
if (newlineIndex !== -1) {
|
|
114
|
+
const line = buffer.slice(0, newlineIndex).trim();
|
|
115
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
116
|
+
if (line) {
|
|
117
|
+
cursorResponse = JSON.parse(line);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
105
120
|
}
|
|
106
121
|
}
|
|
107
122
|
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
reader.releaseLock();
|
|
125
|
+
wrapAbortError(error);
|
|
126
|
+
}
|
|
108
127
|
if (!cursorResponse) {
|
|
128
|
+
reader.releaseLock();
|
|
109
129
|
throw new DatabaseError('No cursor response received');
|
|
110
130
|
}
|
|
111
131
|
async function* parseEntries() {
|
|
@@ -121,10 +141,16 @@ export async function executeCursor(url, authToken, request, remoteEncryptionKey
|
|
|
121
141
|
}
|
|
122
142
|
// Continue reading from the stream
|
|
123
143
|
while (true) {
|
|
124
|
-
|
|
125
|
-
|
|
144
|
+
let readResult;
|
|
145
|
+
try {
|
|
146
|
+
readResult = await reader.read();
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
wrapAbortError(error);
|
|
150
|
+
}
|
|
151
|
+
if (readResult.done)
|
|
126
152
|
break;
|
|
127
|
-
buffer += decoder.decode(value, { stream: true });
|
|
153
|
+
buffer += decoder.decode(readResult.value, { stream: true });
|
|
128
154
|
while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
|
|
129
155
|
const line = buffer.slice(0, newlineIndex).trim();
|
|
130
156
|
buffer = buffer.slice(newlineIndex + 1);
|
|
@@ -144,7 +170,7 @@ export async function executeCursor(url, authToken, request, remoteEncryptionKey
|
|
|
144
170
|
}
|
|
145
171
|
return { response: cursorResponse, entries: parseEntries() };
|
|
146
172
|
}
|
|
147
|
-
export async function executePipeline(url, authToken, request, remoteEncryptionKey) {
|
|
173
|
+
export async function executePipeline(url, authToken, request, remoteEncryptionKey, signal) {
|
|
148
174
|
const headers = {
|
|
149
175
|
'Content-Type': 'application/json',
|
|
150
176
|
};
|
|
@@ -154,11 +180,18 @@ export async function executePipeline(url, authToken, request, remoteEncryptionK
|
|
|
154
180
|
if (remoteEncryptionKey) {
|
|
155
181
|
headers[ENCRYPTION_KEY_HEADER] = remoteEncryptionKey;
|
|
156
182
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
183
|
+
let response;
|
|
184
|
+
try {
|
|
185
|
+
response = await fetch(`${url}/v3/pipeline`, {
|
|
186
|
+
method: 'POST',
|
|
187
|
+
headers,
|
|
188
|
+
body: JSON.stringify(request),
|
|
189
|
+
signal,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
wrapAbortError(error);
|
|
194
|
+
}
|
|
162
195
|
if (!response.ok) {
|
|
163
196
|
throw new DatabaseError(`HTTP error! status: ${response.status}`);
|
|
164
197
|
}
|
package/dist/session.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CursorResponse, type CursorEntry, type DescribeResult } from './protocol.js';
|
|
1
|
+
import { type CursorResponse, type CursorEntry, type DescribeResult, type QueryOptions } from './protocol.js';
|
|
2
2
|
/**
|
|
3
3
|
* Configuration options for a session.
|
|
4
4
|
*/
|
|
@@ -12,6 +12,8 @@ export interface SessionConfig {
|
|
|
12
12
|
* to enable access to encrypted Turso Cloud databases.
|
|
13
13
|
*/
|
|
14
14
|
remoteEncryptionKey?: string;
|
|
15
|
+
/** Default maximum query execution time in milliseconds before interruption. */
|
|
16
|
+
defaultQueryTimeout?: number;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* A database session that manages the connection state and baton.
|
|
@@ -24,13 +26,14 @@ export declare class Session {
|
|
|
24
26
|
private baton;
|
|
25
27
|
private baseUrl;
|
|
26
28
|
constructor(config: SessionConfig);
|
|
29
|
+
private createAbortSignal;
|
|
27
30
|
/**
|
|
28
31
|
* Describe a SQL statement to get its column metadata.
|
|
29
32
|
*
|
|
30
33
|
* @param sql - The SQL statement to describe
|
|
31
34
|
* @returns Promise resolving to the statement description
|
|
32
35
|
*/
|
|
33
|
-
describe(sql: string): Promise<DescribeResult>;
|
|
36
|
+
describe(sql: string, queryOptions?: QueryOptions): Promise<DescribeResult>;
|
|
34
37
|
/**
|
|
35
38
|
* Execute a SQL statement and return all results.
|
|
36
39
|
*
|
|
@@ -39,7 +42,7 @@ export declare class Session {
|
|
|
39
42
|
* @param safeIntegers - Whether to return integers as BigInt
|
|
40
43
|
* @returns Promise resolving to the complete result set
|
|
41
44
|
*/
|
|
42
|
-
execute(sql: string, args?: any[] | Record<string, any>, safeIntegers?: boolean): Promise<any>;
|
|
45
|
+
execute(sql: string, args?: any[] | Record<string, any>, safeIntegers?: boolean, queryOptions?: QueryOptions): Promise<any>;
|
|
43
46
|
/**
|
|
44
47
|
* Execute a SQL statement and return the raw response and entries.
|
|
45
48
|
*
|
|
@@ -47,7 +50,7 @@ export declare class Session {
|
|
|
47
50
|
* @param args - Optional array of parameter values or object with named parameters
|
|
48
51
|
* @returns Promise resolving to the raw response and cursor entries
|
|
49
52
|
*/
|
|
50
|
-
executeRaw(sql: string, args?: any[] | Record<string, any
|
|
53
|
+
executeRaw(sql: string, args?: any[] | Record<string, any>, queryOptions?: QueryOptions): Promise<{
|
|
51
54
|
response: CursorResponse;
|
|
52
55
|
entries: AsyncGenerator<CursorEntry>;
|
|
53
56
|
}>;
|
|
@@ -72,14 +75,14 @@ export declare class Session {
|
|
|
72
75
|
* @param statements - Array of SQL statements to execute
|
|
73
76
|
* @returns Promise resolving to batch execution results
|
|
74
77
|
*/
|
|
75
|
-
batch(statements: string[]): Promise<any>;
|
|
78
|
+
batch(statements: string[], queryOptions?: QueryOptions): Promise<any>;
|
|
76
79
|
/**
|
|
77
80
|
* Execute a sequence of SQL statements separated by semicolons.
|
|
78
81
|
*
|
|
79
82
|
* @param sql - SQL string containing multiple statements separated by semicolons
|
|
80
83
|
* @returns Promise resolving when all statements are executed
|
|
81
84
|
*/
|
|
82
|
-
sequence(sql: string): Promise<void>;
|
|
85
|
+
sequence(sql: string, queryOptions?: QueryOptions): Promise<void>;
|
|
83
86
|
/**
|
|
84
87
|
* Close the session.
|
|
85
88
|
*
|
package/dist/session.js
CHANGED
|
@@ -18,13 +18,20 @@ export class Session {
|
|
|
18
18
|
this.config = config;
|
|
19
19
|
this.baseUrl = normalizeUrl(config.url);
|
|
20
20
|
}
|
|
21
|
+
createAbortSignal(queryOptions) {
|
|
22
|
+
const timeout = queryOptions?.queryTimeout ?? this.config.defaultQueryTimeout;
|
|
23
|
+
if (timeout != null && timeout > 0) {
|
|
24
|
+
return AbortSignal.timeout(timeout);
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
21
28
|
/**
|
|
22
29
|
* Describe a SQL statement to get its column metadata.
|
|
23
30
|
*
|
|
24
31
|
* @param sql - The SQL statement to describe
|
|
25
32
|
* @returns Promise resolving to the statement description
|
|
26
33
|
*/
|
|
27
|
-
async describe(sql) {
|
|
34
|
+
async describe(sql, queryOptions) {
|
|
28
35
|
const request = {
|
|
29
36
|
baton: this.baton,
|
|
30
37
|
requests: [{
|
|
@@ -32,7 +39,7 @@ export class Session {
|
|
|
32
39
|
sql: sql
|
|
33
40
|
}]
|
|
34
41
|
};
|
|
35
|
-
const response = await executePipeline(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey);
|
|
42
|
+
const response = await executePipeline(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey, this.createAbortSignal(queryOptions));
|
|
36
43
|
this.baton = response.baton;
|
|
37
44
|
if (response.base_url) {
|
|
38
45
|
this.baseUrl = response.base_url;
|
|
@@ -57,8 +64,8 @@ export class Session {
|
|
|
57
64
|
* @param safeIntegers - Whether to return integers as BigInt
|
|
58
65
|
* @returns Promise resolving to the complete result set
|
|
59
66
|
*/
|
|
60
|
-
async execute(sql, args = [], safeIntegers = false) {
|
|
61
|
-
const { response, entries } = await this.executeRaw(sql, args);
|
|
67
|
+
async execute(sql, args = [], safeIntegers = false, queryOptions) {
|
|
68
|
+
const { response, entries } = await this.executeRaw(sql, args, queryOptions);
|
|
62
69
|
const result = await this.processCursorEntries(entries, safeIntegers);
|
|
63
70
|
return result;
|
|
64
71
|
}
|
|
@@ -69,7 +76,7 @@ export class Session {
|
|
|
69
76
|
* @param args - Optional array of parameter values or object with named parameters
|
|
70
77
|
* @returns Promise resolving to the raw response and cursor entries
|
|
71
78
|
*/
|
|
72
|
-
async executeRaw(sql, args = []) {
|
|
79
|
+
async executeRaw(sql, args = [], queryOptions) {
|
|
73
80
|
let positionalArgs = [];
|
|
74
81
|
let namedArgs = [];
|
|
75
82
|
if (Array.isArray(args)) {
|
|
@@ -118,7 +125,7 @@ export class Session {
|
|
|
118
125
|
}]
|
|
119
126
|
}
|
|
120
127
|
};
|
|
121
|
-
const { response, entries } = await executeCursor(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey);
|
|
128
|
+
const { response, entries } = await executeCursor(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey, this.createAbortSignal(queryOptions));
|
|
122
129
|
this.baton = response.baton;
|
|
123
130
|
if (response.base_url) {
|
|
124
131
|
this.baseUrl = response.base_url;
|
|
@@ -202,7 +209,7 @@ export class Session {
|
|
|
202
209
|
* @param statements - Array of SQL statements to execute
|
|
203
210
|
* @returns Promise resolving to batch execution results
|
|
204
211
|
*/
|
|
205
|
-
async batch(statements) {
|
|
212
|
+
async batch(statements, queryOptions) {
|
|
206
213
|
const request = {
|
|
207
214
|
baton: this.baton,
|
|
208
215
|
batch: {
|
|
@@ -216,7 +223,7 @@ export class Session {
|
|
|
216
223
|
}))
|
|
217
224
|
}
|
|
218
225
|
};
|
|
219
|
-
const { response, entries } = await executeCursor(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey);
|
|
226
|
+
const { response, entries } = await executeCursor(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey, this.createAbortSignal(queryOptions));
|
|
220
227
|
this.baton = response.baton;
|
|
221
228
|
if (response.base_url) {
|
|
222
229
|
this.baseUrl = response.base_url;
|
|
@@ -249,7 +256,7 @@ export class Session {
|
|
|
249
256
|
* @param sql - SQL string containing multiple statements separated by semicolons
|
|
250
257
|
* @returns Promise resolving when all statements are executed
|
|
251
258
|
*/
|
|
252
|
-
async sequence(sql) {
|
|
259
|
+
async sequence(sql, queryOptions) {
|
|
253
260
|
const request = {
|
|
254
261
|
baton: this.baton,
|
|
255
262
|
requests: [{
|
|
@@ -257,7 +264,7 @@ export class Session {
|
|
|
257
264
|
sql: sql
|
|
258
265
|
}]
|
|
259
266
|
};
|
|
260
|
-
const response = await executePipeline(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey);
|
|
267
|
+
const response = await executePipeline(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey, this.createAbortSignal(queryOptions));
|
|
261
268
|
this.baton = response.baton;
|
|
262
269
|
if (response.base_url) {
|
|
263
270
|
this.baseUrl = response.base_url;
|
|
@@ -288,9 +295,9 @@ export class Session {
|
|
|
288
295
|
};
|
|
289
296
|
await executePipeline(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey);
|
|
290
297
|
}
|
|
291
|
-
catch
|
|
292
|
-
// Ignore errors during close
|
|
293
|
-
|
|
298
|
+
catch {
|
|
299
|
+
// Ignore errors during close — the connection might already be closed
|
|
300
|
+
// or the baton may be stale after a timeout.
|
|
294
301
|
}
|
|
295
302
|
}
|
|
296
303
|
// Reset local state
|
package/dist/statement.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type Column } from './protocol.js';
|
|
2
|
-
import { type SessionConfig } from './session.js';
|
|
1
|
+
import { type Column, type QueryOptions } from './protocol.js';
|
|
2
|
+
import { Session, type SessionConfig } from './session.js';
|
|
3
|
+
import { type AsyncLock } from './async-lock.js';
|
|
3
4
|
/**
|
|
4
5
|
* A prepared SQL statement that can be executed in multiple ways.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
+
* Statements may either own a dedicated session or share a connection session to preserve transaction boundaries.
|
|
7
8
|
* Provides three execution modes:
|
|
8
9
|
* - `get(args?)`: Returns the first row or null
|
|
9
10
|
* - `all(args?)`: Returns all rows as an array
|
|
@@ -15,7 +16,14 @@ export declare class Statement {
|
|
|
15
16
|
private presentationMode;
|
|
16
17
|
private safeIntegerMode;
|
|
17
18
|
private columnMetadata;
|
|
19
|
+
private execLock?;
|
|
18
20
|
constructor(sessionConfig: SessionConfig, sql: string, columns?: Column[]);
|
|
21
|
+
/**
|
|
22
|
+
* Create a Statement that shares an existing session and serializes execution
|
|
23
|
+
* through the given lock. Used by Connection.prepare() so prepared statements
|
|
24
|
+
* participate in the connection's transaction scope.
|
|
25
|
+
*/
|
|
26
|
+
static fromSession(session: Session, sql: string, columns: Column[] | undefined, execLock: AsyncLock): Statement;
|
|
19
27
|
/**
|
|
20
28
|
* Whether the prepared statement returns data.
|
|
21
29
|
*
|
|
@@ -81,6 +89,7 @@ export declare class Statement {
|
|
|
81
89
|
* ```
|
|
82
90
|
*/
|
|
83
91
|
columns(): any[];
|
|
92
|
+
private withLock;
|
|
84
93
|
/**
|
|
85
94
|
* Executes the prepared statement.
|
|
86
95
|
*
|
|
@@ -94,7 +103,7 @@ export declare class Statement {
|
|
|
94
103
|
* console.log(`Inserted user with ID ${result.lastInsertRowid}`);
|
|
95
104
|
* ```
|
|
96
105
|
*/
|
|
97
|
-
run(args?: any): Promise<any>;
|
|
106
|
+
run(args?: any, queryOptions?: QueryOptions): Promise<any>;
|
|
98
107
|
/**
|
|
99
108
|
* Execute the statement and return the first row.
|
|
100
109
|
*
|
|
@@ -110,7 +119,7 @@ export declare class Statement {
|
|
|
110
119
|
* }
|
|
111
120
|
* ```
|
|
112
121
|
*/
|
|
113
|
-
get(args?: any): Promise<any>;
|
|
122
|
+
get(args?: any, queryOptions?: QueryOptions): Promise<any>;
|
|
114
123
|
/**
|
|
115
124
|
* Execute the statement and return all rows.
|
|
116
125
|
*
|
|
@@ -124,7 +133,7 @@ export declare class Statement {
|
|
|
124
133
|
* console.log(`Found ${activeUsers.length} active users`);
|
|
125
134
|
* ```
|
|
126
135
|
*/
|
|
127
|
-
all(args?: any): Promise<any[]>;
|
|
136
|
+
all(args?: any, queryOptions?: QueryOptions): Promise<any[]>;
|
|
128
137
|
/**
|
|
129
138
|
* Execute the statement and return an async iterator for streaming results.
|
|
130
139
|
*
|
|
@@ -143,7 +152,7 @@ export declare class Statement {
|
|
|
143
152
|
* }
|
|
144
153
|
* ```
|
|
145
154
|
*/
|
|
146
|
-
iterate(args?: any): AsyncGenerator<any>;
|
|
155
|
+
iterate(args?: any, queryOptions?: QueryOptions): AsyncGenerator<any>;
|
|
147
156
|
/**
|
|
148
157
|
* Normalize arguments to handle both single values and arrays.
|
|
149
158
|
* Matches the behavior of the native bindings.
|
package/dist/statement.js
CHANGED
|
@@ -4,7 +4,7 @@ import { DatabaseError } from './error.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* A prepared SQL statement that can be executed in multiple ways.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Statements may either own a dedicated session or share a connection session to preserve transaction boundaries.
|
|
8
8
|
* Provides three execution modes:
|
|
9
9
|
* - `get(args?)`: Returns the first row or null
|
|
10
10
|
* - `all(args?)`: Returns all rows as an array
|
|
@@ -18,6 +18,21 @@ export class Statement {
|
|
|
18
18
|
this.sql = sql;
|
|
19
19
|
this.columnMetadata = columns || [];
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a Statement that shares an existing session and serializes execution
|
|
23
|
+
* through the given lock. Used by Connection.prepare() so prepared statements
|
|
24
|
+
* participate in the connection's transaction scope.
|
|
25
|
+
*/
|
|
26
|
+
static fromSession(session, sql, columns, execLock) {
|
|
27
|
+
const stmt = Object.create(Statement.prototype);
|
|
28
|
+
stmt.session = session;
|
|
29
|
+
stmt.sql = sql;
|
|
30
|
+
stmt.columnMetadata = columns || [];
|
|
31
|
+
stmt.presentationMode = 'expanded';
|
|
32
|
+
stmt.safeIntegerMode = false;
|
|
33
|
+
stmt.execLock = execLock;
|
|
34
|
+
return stmt;
|
|
35
|
+
}
|
|
21
36
|
/**
|
|
22
37
|
* Whether the prepared statement returns data.
|
|
23
38
|
*
|
|
@@ -99,6 +114,18 @@ export class Statement {
|
|
|
99
114
|
type: col.decltype
|
|
100
115
|
}));
|
|
101
116
|
}
|
|
117
|
+
async withLock(fn) {
|
|
118
|
+
if (!this.execLock) {
|
|
119
|
+
return await fn();
|
|
120
|
+
}
|
|
121
|
+
await this.execLock.acquire();
|
|
122
|
+
try {
|
|
123
|
+
return await fn();
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
this.execLock.release();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
102
129
|
/**
|
|
103
130
|
* Executes the prepared statement.
|
|
104
131
|
*
|
|
@@ -112,10 +139,12 @@ export class Statement {
|
|
|
112
139
|
* console.log(`Inserted user with ID ${result.lastInsertRowid}`);
|
|
113
140
|
* ```
|
|
114
141
|
*/
|
|
115
|
-
async run(args) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
142
|
+
async run(args, queryOptions) {
|
|
143
|
+
return await this.withLock(async () => {
|
|
144
|
+
const normalizedArgs = this.normalizeArgs(args);
|
|
145
|
+
const result = await this.session.execute(this.sql, normalizedArgs, this.safeIntegerMode, queryOptions);
|
|
146
|
+
return { changes: result.rowsAffected, lastInsertRowid: result.lastInsertRowid };
|
|
147
|
+
});
|
|
119
148
|
}
|
|
120
149
|
/**
|
|
121
150
|
* Execute the statement and return the first row.
|
|
@@ -132,28 +161,30 @@ export class Statement {
|
|
|
132
161
|
* }
|
|
133
162
|
* ```
|
|
134
163
|
*/
|
|
135
|
-
async get(args) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
async get(args, queryOptions) {
|
|
165
|
+
return await this.withLock(async () => {
|
|
166
|
+
const normalizedArgs = this.normalizeArgs(args);
|
|
167
|
+
const result = await this.session.execute(this.sql, normalizedArgs, this.safeIntegerMode, queryOptions);
|
|
168
|
+
const row = result.rows[0];
|
|
169
|
+
if (!row) {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
if (this.presentationMode === 'pluck') {
|
|
173
|
+
// In pluck mode, return only the first column value
|
|
174
|
+
return row[0];
|
|
175
|
+
}
|
|
176
|
+
if (this.presentationMode === 'raw') {
|
|
177
|
+
// In raw mode, return the row as a plain array (it already is one)
|
|
178
|
+
// The row object is already an array with column properties added
|
|
179
|
+
return [...row];
|
|
180
|
+
}
|
|
181
|
+
// In expanded mode, convert to plain object with named properties
|
|
182
|
+
const obj = {};
|
|
183
|
+
result.columns.forEach((col, i) => {
|
|
184
|
+
obj[col] = row[i];
|
|
185
|
+
});
|
|
186
|
+
return obj;
|
|
155
187
|
});
|
|
156
|
-
return obj;
|
|
157
188
|
}
|
|
158
189
|
/**
|
|
159
190
|
* Execute the statement and return all rows.
|
|
@@ -168,23 +199,25 @@ export class Statement {
|
|
|
168
199
|
* console.log(`Found ${activeUsers.length} active users`);
|
|
169
200
|
* ```
|
|
170
201
|
*/
|
|
171
|
-
async all(args) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
202
|
+
async all(args, queryOptions) {
|
|
203
|
+
return await this.withLock(async () => {
|
|
204
|
+
const normalizedArgs = this.normalizeArgs(args);
|
|
205
|
+
const result = await this.session.execute(this.sql, normalizedArgs, this.safeIntegerMode, queryOptions);
|
|
206
|
+
if (this.presentationMode === 'pluck') {
|
|
207
|
+
// In pluck mode, return only the first column value from each row
|
|
208
|
+
return result.rows.map((row) => row[0]);
|
|
209
|
+
}
|
|
210
|
+
if (this.presentationMode === 'raw') {
|
|
211
|
+
return result.rows.map((row) => [...row]);
|
|
212
|
+
}
|
|
213
|
+
// In expanded mode, convert rows to plain objects with named properties
|
|
214
|
+
return result.rows.map((row) => {
|
|
215
|
+
const obj = {};
|
|
216
|
+
result.columns.forEach((col, i) => {
|
|
217
|
+
obj[col] = row[i];
|
|
218
|
+
});
|
|
219
|
+
return obj;
|
|
186
220
|
});
|
|
187
|
-
return obj;
|
|
188
221
|
});
|
|
189
222
|
}
|
|
190
223
|
/**
|
|
@@ -205,9 +238,18 @@ export class Statement {
|
|
|
205
238
|
* }
|
|
206
239
|
* ```
|
|
207
240
|
*/
|
|
208
|
-
async *iterate(args) {
|
|
241
|
+
async *iterate(args, queryOptions) {
|
|
242
|
+
// Shared-connection statements must not hold the connection lock across
|
|
243
|
+
// `yield` points, or nested queries in the loop body can deadlock.
|
|
244
|
+
if (this.execLock) {
|
|
245
|
+
const rows = await this.all(args, queryOptions);
|
|
246
|
+
for (const row of rows) {
|
|
247
|
+
yield row;
|
|
248
|
+
}
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
209
251
|
const normalizedArgs = this.normalizeArgs(args);
|
|
210
|
-
const {
|
|
252
|
+
const { entries } = await this.session.executeRaw(this.sql, normalizedArgs, queryOptions);
|
|
211
253
|
let columns = [];
|
|
212
254
|
for await (const entry of entries) {
|
|
213
255
|
switch (entry.type) {
|