@tursodatabase/serverless 1.2.0-pre.1 → 1.2.0
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/README.md +8 -0
- package/dist/async-lock.d.ts +6 -0
- package/dist/async-lock.js +22 -0
- package/dist/compat/index.d.ts +1 -147
- package/dist/compat/index.js +1 -882
- package/dist/{compat/index.d.cts → compat.d.ts} +11 -13
- package/dist/compat.js +395 -0
- package/dist/connection.d.ts +197 -0
- package/dist/connection.js +312 -0
- package/dist/error.d.ts +19 -0
- package/dist/error.js +24 -0
- package/dist/index.d.ts +5 -530
- package/dist/index.js +6 -1138
- package/dist/protocol.d.ts +120 -0
- package/dist/protocol.js +199 -0
- package/dist/session.d.ts +93 -0
- package/dist/session.js +307 -0
- package/dist/statement.d.ts +161 -0
- package/dist/statement.js +308 -0
- package/package.json +1 -1
- package/dist/compat/index.cjs +0 -885
- package/dist/index.cjs +0 -1146
- package/dist/index.d.cts +0 -530
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { decodeValue } from './protocol.js';
|
|
2
|
+
import { Session } from './session.js';
|
|
3
|
+
import { DatabaseError } from './error.js';
|
|
4
|
+
/**
|
|
5
|
+
* A prepared SQL statement that can be executed in multiple ways.
|
|
6
|
+
*
|
|
7
|
+
* Statements may either own a dedicated session or share a connection session to preserve transaction boundaries.
|
|
8
|
+
* Provides three execution modes:
|
|
9
|
+
* - `get(args?)`: Returns the first row or null
|
|
10
|
+
* - `all(args?)`: Returns all rows as an array
|
|
11
|
+
* - `iterate(args?)`: Returns an async iterator for streaming results
|
|
12
|
+
*/
|
|
13
|
+
export class Statement {
|
|
14
|
+
constructor(sessionConfig, sql, columns) {
|
|
15
|
+
this.presentationMode = 'expanded';
|
|
16
|
+
this.safeIntegerMode = false;
|
|
17
|
+
this.session = new Session(sessionConfig);
|
|
18
|
+
this.sql = sql;
|
|
19
|
+
this.columnMetadata = columns || [];
|
|
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
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether the prepared statement returns data.
|
|
38
|
+
*
|
|
39
|
+
* This is `true` for SELECT queries and statements with RETURNING clause,
|
|
40
|
+
* and `false` for INSERT, UPDATE, DELETE statements without RETURNING.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const stmt = await conn.prepare(sql);
|
|
45
|
+
* if (stmt.reader) {
|
|
46
|
+
* return stmt.all(args); // SELECT-like query
|
|
47
|
+
* } else {
|
|
48
|
+
* return stmt.run(args); // INSERT/UPDATE/DELETE
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
get reader() {
|
|
53
|
+
return this.columnMetadata.length > 0;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Enable raw mode to return arrays instead of objects.
|
|
57
|
+
*
|
|
58
|
+
* @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled.
|
|
59
|
+
* @returns This statement instance for chaining
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const stmt = client.prepare("SELECT * FROM users WHERE id = ?");
|
|
64
|
+
* const row = await stmt.raw().get([1]);
|
|
65
|
+
* console.log(row); // [1, "Alice", "alice@example.org"]
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
raw(raw) {
|
|
69
|
+
this.presentationMode = raw === false ? 'expanded' : 'raw';
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Enable pluck mode to return only the first column value from each row.
|
|
74
|
+
*
|
|
75
|
+
* @param pluck Enable or disable pluck mode. If you don't pass the parameter, pluck mode is enabled.
|
|
76
|
+
* @returns This statement instance for chaining
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const stmt = client.prepare("SELECT id FROM users");
|
|
81
|
+
* const ids = await stmt.pluck().all();
|
|
82
|
+
* console.log(ids); // [1, 2, 3, ...]
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
pluck(pluck) {
|
|
86
|
+
this.presentationMode = pluck === false ? 'expanded' : 'pluck';
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Sets safe integers mode for this statement.
|
|
91
|
+
*
|
|
92
|
+
* @param toggle Whether to use safe integers. If you don't pass the parameter, safe integers mode is enabled.
|
|
93
|
+
* @returns This statement instance for chaining
|
|
94
|
+
*/
|
|
95
|
+
safeIntegers(toggle) {
|
|
96
|
+
this.safeIntegerMode = toggle === false ? false : true;
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get column information for this statement.
|
|
101
|
+
*
|
|
102
|
+
* @returns Array of column metadata objects matching the native bindings format
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const stmt = await client.prepare("SELECT id, name, email FROM users");
|
|
107
|
+
* const columns = stmt.columns();
|
|
108
|
+
* console.log(columns); // [{ name: 'id', type: 'INTEGER', column: null, database: null, table: null }, ...]
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
columns() {
|
|
112
|
+
return this.columnMetadata.map(col => ({
|
|
113
|
+
name: col.name,
|
|
114
|
+
type: col.decltype
|
|
115
|
+
}));
|
|
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
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Executes the prepared statement.
|
|
131
|
+
*
|
|
132
|
+
* @param args - Optional array of parameter values or object with named parameters
|
|
133
|
+
* @returns Promise resolving to the result of the statement
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const stmt = client.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
|
|
138
|
+
* const result = await stmt.run(['John Doe', 'john.doe@example.com']);
|
|
139
|
+
* console.log(`Inserted user with ID ${result.lastInsertRowid}`);
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
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
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Execute the statement and return the first row.
|
|
151
|
+
*
|
|
152
|
+
* @param args - Optional array of parameter values or object with named parameters
|
|
153
|
+
* @returns Promise resolving to the first row or undefined if no results
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const stmt = client.prepare("SELECT * FROM users WHERE id = ?");
|
|
158
|
+
* const user = await stmt.get([123]);
|
|
159
|
+
* if (user) {
|
|
160
|
+
* console.log(user.name);
|
|
161
|
+
* }
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
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;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Execute the statement and return all rows.
|
|
191
|
+
*
|
|
192
|
+
* @param args - Optional array of parameter values or object with named parameters
|
|
193
|
+
* @returns Promise resolving to an array of all result rows
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* const stmt = client.prepare("SELECT * FROM users WHERE active = ?");
|
|
198
|
+
* const activeUsers = await stmt.all([true]);
|
|
199
|
+
* console.log(`Found ${activeUsers.length} active users`);
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
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;
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Execute the statement and return an async iterator for streaming results.
|
|
225
|
+
*
|
|
226
|
+
* This method provides memory-efficient processing of large result sets
|
|
227
|
+
* by streaming rows one at a time instead of loading everything into memory.
|
|
228
|
+
*
|
|
229
|
+
* @param args - Optional array of parameter values or object with named parameters
|
|
230
|
+
* @returns AsyncGenerator that yields individual rows
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* const stmt = client.prepare("SELECT * FROM large_table WHERE category = ?");
|
|
235
|
+
* for await (const row of stmt.iterate(['electronics'])) {
|
|
236
|
+
* // Process each row individually
|
|
237
|
+
* console.log(row.id, row.name);
|
|
238
|
+
* }
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
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
|
+
}
|
|
251
|
+
const normalizedArgs = this.normalizeArgs(args);
|
|
252
|
+
const { entries } = await this.session.executeRaw(this.sql, normalizedArgs, queryOptions);
|
|
253
|
+
let columns = [];
|
|
254
|
+
for await (const entry of entries) {
|
|
255
|
+
switch (entry.type) {
|
|
256
|
+
case 'step_begin':
|
|
257
|
+
if (entry.cols) {
|
|
258
|
+
columns = entry.cols.map(col => col.name);
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
case 'row':
|
|
262
|
+
if (entry.row) {
|
|
263
|
+
const decodedRow = entry.row.map(value => decodeValue(value, this.safeIntegerMode));
|
|
264
|
+
if (this.presentationMode === 'pluck') {
|
|
265
|
+
// In pluck mode, yield only the first column value
|
|
266
|
+
yield decodedRow[0];
|
|
267
|
+
}
|
|
268
|
+
else if (this.presentationMode === 'raw') {
|
|
269
|
+
// In raw mode, yield arrays of values
|
|
270
|
+
yield decodedRow;
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
// In expanded mode, yield plain objects with named properties (consistent with all())
|
|
274
|
+
const obj = {};
|
|
275
|
+
columns.forEach((col, i) => {
|
|
276
|
+
obj[col] = decodedRow[i];
|
|
277
|
+
});
|
|
278
|
+
yield obj;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
case 'step_error':
|
|
283
|
+
case 'error':
|
|
284
|
+
throw new DatabaseError(entry.error?.message || 'SQL execution failed');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Normalize arguments to handle both single values and arrays.
|
|
290
|
+
* Matches the behavior of the native bindings.
|
|
291
|
+
*/
|
|
292
|
+
normalizeArgs(args) {
|
|
293
|
+
// No arguments provided
|
|
294
|
+
if (args === undefined) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
// If it's an array, return as-is
|
|
298
|
+
if (Array.isArray(args)) {
|
|
299
|
+
return args;
|
|
300
|
+
}
|
|
301
|
+
// Check if it's a plain object (for named parameters)
|
|
302
|
+
if (args !== null && typeof args === 'object' && args.constructor === Object) {
|
|
303
|
+
return args;
|
|
304
|
+
}
|
|
305
|
+
// Single value - wrap in array
|
|
306
|
+
return [args];
|
|
307
|
+
}
|
|
308
|
+
}
|