@tursodatabase/serverless 1.1.3 → 1.2.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/README.md +8 -0
- package/dist/compat/index.cjs +133 -49
- package/dist/compat/index.js +133 -49
- package/dist/index.cjs +218 -92
- package/dist/index.d.cts +78 -15
- package/dist/index.d.ts +78 -15
- package/dist/index.js +218 -92
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -237,6 +237,67 @@ async function executePipeline(url, authToken, request, remoteEncryptionKey, sig
|
|
|
237
237
|
return response.json();
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
// src/args.ts
|
|
241
|
+
function normalizeArgs(args) {
|
|
242
|
+
if (args === void 0) return [];
|
|
243
|
+
if (Array.isArray(args)) return args;
|
|
244
|
+
if (args !== null && typeof args === "object" && args.constructor === Object) {
|
|
245
|
+
return args;
|
|
246
|
+
}
|
|
247
|
+
return [args];
|
|
248
|
+
}
|
|
249
|
+
function isQueryOptions(value) {
|
|
250
|
+
return value != null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "queryTimeout");
|
|
251
|
+
}
|
|
252
|
+
function splitBindParameters(bindParameters) {
|
|
253
|
+
if (bindParameters.length === 0) {
|
|
254
|
+
return { params: void 0, queryOptions: void 0 };
|
|
255
|
+
}
|
|
256
|
+
if (isQueryOptions(bindParameters[bindParameters.length - 1])) {
|
|
257
|
+
if (bindParameters.length === 1) {
|
|
258
|
+
return { params: void 0, queryOptions: bindParameters[0] };
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
params: bindParameters.length === 2 ? bindParameters[0] : bindParameters.slice(0, -1),
|
|
262
|
+
queryOptions: bindParameters[bindParameters.length - 1]
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
params: bindParameters.length === 1 ? bindParameters[0] : bindParameters,
|
|
267
|
+
queryOptions: void 0
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function encodeSqlArgs(args = []) {
|
|
271
|
+
let positionalArgs = [];
|
|
272
|
+
let namedArgs = [];
|
|
273
|
+
if (Array.isArray(args)) {
|
|
274
|
+
positionalArgs = args.map(encodeValue);
|
|
275
|
+
} else {
|
|
276
|
+
const keys = Object.keys(args);
|
|
277
|
+
const isNumericKeys = keys.length > 0 && keys.every((key) => /^\d+$/.test(key));
|
|
278
|
+
if (isNumericKeys) {
|
|
279
|
+
const sortedKeys = keys.sort((a, b) => parseInt(a, 10) - parseInt(b, 10));
|
|
280
|
+
const maxIndex = parseInt(sortedKeys[sortedKeys.length - 1], 10);
|
|
281
|
+
positionalArgs = new Array(maxIndex);
|
|
282
|
+
for (const key of sortedKeys) {
|
|
283
|
+
const index = parseInt(key, 10) - 1;
|
|
284
|
+
positionalArgs[index] = encodeValue(args[key]);
|
|
285
|
+
}
|
|
286
|
+
for (let i = 0; i < positionalArgs.length; i++) {
|
|
287
|
+
if (positionalArgs[i] === void 0) {
|
|
288
|
+
positionalArgs[i] = { type: "null" };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
namedArgs = Object.entries(args).map(([name, value]) => ({
|
|
293
|
+
name,
|
|
294
|
+
value: encodeValue(value)
|
|
295
|
+
}));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return { args: positionalArgs, namedArgs };
|
|
299
|
+
}
|
|
300
|
+
|
|
240
301
|
// src/session.ts
|
|
241
302
|
function normalizeUrl(url) {
|
|
242
303
|
return url.replace(/^libsql:\/\//, "https://");
|
|
@@ -314,41 +375,15 @@ var Session = class {
|
|
|
314
375
|
* @returns Promise resolving to the raw response and cursor entries
|
|
315
376
|
*/
|
|
316
377
|
async executeRaw(sql, args = [], queryOptions) {
|
|
317
|
-
|
|
318
|
-
let namedArgs = [];
|
|
319
|
-
if (Array.isArray(args)) {
|
|
320
|
-
positionalArgs = args.map(encodeValue);
|
|
321
|
-
} else {
|
|
322
|
-
const keys = Object.keys(args);
|
|
323
|
-
const isNumericKeys = keys.length > 0 && keys.every((key) => /^\d+$/.test(key));
|
|
324
|
-
if (isNumericKeys) {
|
|
325
|
-
const sortedKeys = keys.sort((a, b) => parseInt(a) - parseInt(b));
|
|
326
|
-
const maxIndex = parseInt(sortedKeys[sortedKeys.length - 1]);
|
|
327
|
-
positionalArgs = new Array(maxIndex);
|
|
328
|
-
for (const key of sortedKeys) {
|
|
329
|
-
const index = parseInt(key) - 1;
|
|
330
|
-
positionalArgs[index] = encodeValue(args[key]);
|
|
331
|
-
}
|
|
332
|
-
for (let i = 0; i < positionalArgs.length; i++) {
|
|
333
|
-
if (positionalArgs[i] === void 0) {
|
|
334
|
-
positionalArgs[i] = { type: "null" };
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
} else {
|
|
338
|
-
namedArgs = Object.entries(args).map(([name, value]) => ({
|
|
339
|
-
name,
|
|
340
|
-
value: encodeValue(value)
|
|
341
|
-
}));
|
|
342
|
-
}
|
|
343
|
-
}
|
|
378
|
+
const encodedArgs = encodeSqlArgs(args);
|
|
344
379
|
const request = {
|
|
345
380
|
baton: this.baton,
|
|
346
381
|
batch: {
|
|
347
382
|
steps: [{
|
|
348
383
|
stmt: {
|
|
349
384
|
sql,
|
|
350
|
-
args:
|
|
351
|
-
named_args: namedArgs,
|
|
385
|
+
args: encodedArgs.args,
|
|
386
|
+
named_args: encodedArgs.namedArgs,
|
|
352
387
|
want_rows: true
|
|
353
388
|
}
|
|
354
389
|
}]
|
|
@@ -439,23 +474,76 @@ var Session = class {
|
|
|
439
474
|
}
|
|
440
475
|
/**
|
|
441
476
|
* Execute multiple SQL statements in a batch.
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
477
|
+
*
|
|
478
|
+
* When `mode` is set, the batch is sent as a single Hrana request that
|
|
479
|
+
* also carries `BEGIN <mode>` / `COMMIT` / `ROLLBACK` steps using the
|
|
480
|
+
* server-side condition chain, giving atomic execution in one round-trip.
|
|
481
|
+
* When `mode` is omitted, the user statements are sent as-is and run
|
|
482
|
+
* under autocommit (or whatever transaction is already active on this
|
|
483
|
+
* stream).
|
|
484
|
+
*
|
|
485
|
+
* @param statements - Array of SQL statements to execute.
|
|
486
|
+
* @param mode - Optional locking mode; when set, the batch executes
|
|
487
|
+
* atomically. Accepts the same values as `Database.transaction(...)`
|
|
488
|
+
* variants: `"deferred"`, `"immediate"`, `"exclusive"`, `"concurrent"`.
|
|
489
|
+
* @returns Promise resolving to batch execution results.
|
|
445
490
|
*/
|
|
446
|
-
async batch(statements, queryOptions) {
|
|
491
|
+
async batch(statements, mode, queryOptions) {
|
|
492
|
+
const userSteps = statements.map((statement) => {
|
|
493
|
+
if (typeof statement === "string") {
|
|
494
|
+
return {
|
|
495
|
+
stmt: { sql: statement, args: [], named_args: [], want_rows: false }
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
const encodedArgs = encodeSqlArgs(statement.args ?? []);
|
|
499
|
+
return {
|
|
500
|
+
stmt: {
|
|
501
|
+
sql: statement.sql,
|
|
502
|
+
args: encodedArgs.args,
|
|
503
|
+
named_args: encodedArgs.namedArgs,
|
|
504
|
+
want_rows: false
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
});
|
|
508
|
+
let steps;
|
|
509
|
+
let firstUserStepIdx = 0;
|
|
510
|
+
let lastUserStepIdx = userSteps.length - 1;
|
|
511
|
+
let beginIdx = -1;
|
|
512
|
+
let commitIdx = -1;
|
|
513
|
+
let rollbackIdx = -1;
|
|
514
|
+
if (mode === void 0) {
|
|
515
|
+
steps = userSteps;
|
|
516
|
+
} else {
|
|
517
|
+
beginIdx = 0;
|
|
518
|
+
firstUserStepIdx = 1;
|
|
519
|
+
lastUserStepIdx = userSteps.length;
|
|
520
|
+
commitIdx = lastUserStepIdx + 1;
|
|
521
|
+
rollbackIdx = commitIdx + 1;
|
|
522
|
+
steps = [
|
|
523
|
+
{ stmt: { sql: `BEGIN ${mode.toUpperCase()}`, args: [], named_args: [], want_rows: false } },
|
|
524
|
+
...userSteps.map((step, i) => ({
|
|
525
|
+
...step,
|
|
526
|
+
condition: { type: "ok", step: i === 0 ? beginIdx : firstUserStepIdx + i - 1 }
|
|
527
|
+
})),
|
|
528
|
+
{
|
|
529
|
+
stmt: { sql: "COMMIT", args: [], named_args: [], want_rows: false },
|
|
530
|
+
condition: { type: "ok", step: lastUserStepIdx }
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
stmt: { sql: "ROLLBACK", args: [], named_args: [], want_rows: false },
|
|
534
|
+
condition: {
|
|
535
|
+
type: "and",
|
|
536
|
+
conds: [
|
|
537
|
+
{ type: "ok", step: beginIdx },
|
|
538
|
+
{ type: "not", cond: { type: "ok", step: commitIdx } }
|
|
539
|
+
]
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
];
|
|
543
|
+
}
|
|
447
544
|
const request = {
|
|
448
545
|
baton: this.baton,
|
|
449
|
-
batch: {
|
|
450
|
-
steps: statements.map((sql) => ({
|
|
451
|
-
stmt: {
|
|
452
|
-
sql,
|
|
453
|
-
args: [],
|
|
454
|
-
named_args: [],
|
|
455
|
-
want_rows: false
|
|
456
|
-
}
|
|
457
|
-
}))
|
|
458
|
-
}
|
|
546
|
+
batch: { steps }
|
|
459
547
|
};
|
|
460
548
|
let batchResult;
|
|
461
549
|
try {
|
|
@@ -471,21 +559,46 @@ var Session = class {
|
|
|
471
559
|
}
|
|
472
560
|
let totalRowsAffected = 0;
|
|
473
561
|
let lastInsertRowid;
|
|
562
|
+
let deferredError = null;
|
|
563
|
+
let currentStep;
|
|
564
|
+
const isUserStep = (step) => {
|
|
565
|
+
if (mode === void 0) {
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
return step !== void 0 && step >= firstUserStepIdx && step <= lastUserStepIdx;
|
|
569
|
+
};
|
|
474
570
|
for await (const entry of entries) {
|
|
475
571
|
switch (entry.type) {
|
|
572
|
+
case "step_begin":
|
|
573
|
+
currentStep = entry.step;
|
|
574
|
+
break;
|
|
476
575
|
case "step_end":
|
|
477
|
-
if (
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
576
|
+
if (isUserStep(currentStep)) {
|
|
577
|
+
if (entry.affected_row_count !== void 0) {
|
|
578
|
+
totalRowsAffected += entry.affected_row_count;
|
|
579
|
+
}
|
|
580
|
+
if (entry.last_insert_rowid !== void 0 && entry.last_insert_rowid !== null) {
|
|
581
|
+
lastInsertRowid = typeof entry.last_insert_rowid === "number" ? entry.last_insert_rowid : parseInt(entry.last_insert_rowid, 10);
|
|
582
|
+
}
|
|
482
583
|
}
|
|
584
|
+
currentStep = void 0;
|
|
483
585
|
break;
|
|
484
586
|
case "step_error":
|
|
587
|
+
if (mode === void 0) {
|
|
588
|
+
throw new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
|
|
589
|
+
}
|
|
590
|
+
if (deferredError === null && entry.step !== rollbackIdx) {
|
|
591
|
+
deferredError = new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
|
|
592
|
+
}
|
|
593
|
+
currentStep = void 0;
|
|
594
|
+
break;
|
|
485
595
|
case "error":
|
|
486
596
|
throw new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
|
|
487
597
|
}
|
|
488
598
|
}
|
|
599
|
+
if (deferredError !== null) {
|
|
600
|
+
throw deferredError;
|
|
601
|
+
}
|
|
489
602
|
return {
|
|
490
603
|
rowsAffected: totalRowsAffected,
|
|
491
604
|
lastInsertRowid
|
|
@@ -547,37 +660,6 @@ var Session = class {
|
|
|
547
660
|
}
|
|
548
661
|
};
|
|
549
662
|
|
|
550
|
-
// src/args.ts
|
|
551
|
-
function normalizeArgs(args) {
|
|
552
|
-
if (args === void 0) return [];
|
|
553
|
-
if (Array.isArray(args)) return args;
|
|
554
|
-
if (args !== null && typeof args === "object" && args.constructor === Object) {
|
|
555
|
-
return args;
|
|
556
|
-
}
|
|
557
|
-
return [args];
|
|
558
|
-
}
|
|
559
|
-
function isQueryOptions(value) {
|
|
560
|
-
return value != null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "queryTimeout");
|
|
561
|
-
}
|
|
562
|
-
function splitBindParameters(bindParameters) {
|
|
563
|
-
if (bindParameters.length === 0) {
|
|
564
|
-
return { params: void 0, queryOptions: void 0 };
|
|
565
|
-
}
|
|
566
|
-
if (isQueryOptions(bindParameters[bindParameters.length - 1])) {
|
|
567
|
-
if (bindParameters.length === 1) {
|
|
568
|
-
return { params: void 0, queryOptions: bindParameters[0] };
|
|
569
|
-
}
|
|
570
|
-
return {
|
|
571
|
-
params: bindParameters.length === 2 ? bindParameters[0] : bindParameters.slice(0, -1),
|
|
572
|
-
queryOptions: bindParameters[bindParameters.length - 1]
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
return {
|
|
576
|
-
params: bindParameters.length === 1 ? bindParameters[0] : bindParameters,
|
|
577
|
-
queryOptions: void 0
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
|
|
581
663
|
// src/statement.ts
|
|
582
664
|
var Statement = class _Statement {
|
|
583
665
|
constructor(sessionConfig, sql, columns) {
|
|
@@ -1009,20 +1091,61 @@ var Connection = class {
|
|
|
1009
1091
|
}
|
|
1010
1092
|
}
|
|
1011
1093
|
/**
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
1015
|
-
*
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
1094
|
+
* Executes a batch of SQL statements over this connection.
|
|
1095
|
+
*
|
|
1096
|
+
* By default, batch() is not transactional: each statement runs in its
|
|
1097
|
+
* own autocommit step, so a failure mid-batch leaves earlier successful
|
|
1098
|
+
* statements committed. Pass a `mode` to make the batch atomic — the
|
|
1099
|
+
* statements are wrapped in `BEGIN <mode>` / `COMMIT` (with `ROLLBACK`
|
|
1100
|
+
* on failure) and dispatched as a single Hrana request, so the whole
|
|
1101
|
+
* batch completes in one round-trip. When called from inside a
|
|
1102
|
+
* `connection.transaction(...)` callback the `mode` argument is ignored
|
|
1103
|
+
* and the surrounding transaction is reused.
|
|
1104
|
+
*
|
|
1105
|
+
* When `mode` is set, `batch()` owns the surrounding
|
|
1106
|
+
* `BEGIN`/`COMMIT`/`ROLLBACK`, so the `statements` array must not
|
|
1107
|
+
* contain its own transaction-control SQL (`BEGIN`, `COMMIT`,
|
|
1108
|
+
* `ROLLBACK`, `SAVEPOINT`, `RELEASE`). The input is not validated
|
|
1109
|
+
* for that — a user-supplied `COMMIT` will close the wrapper
|
|
1110
|
+
* transaction mid-batch and leave earlier statements committed,
|
|
1111
|
+
* defeating the all-or-nothing contract.
|
|
1112
|
+
*
|
|
1113
|
+
* @param statements - An array of SQL strings or `{ sql, args }` objects.
|
|
1114
|
+
* @param mode - When set, makes the batch atomic. Accepts the same
|
|
1115
|
+
* values as `connection.transaction(...)` variants: `"deferred"`,
|
|
1116
|
+
* `"immediate"`, `"exclusive"`, `"concurrent"`. Ignored when already
|
|
1117
|
+
* inside a transaction.
|
|
1118
|
+
* @returns An object with `rowsAffected` (sum of affected rows) and
|
|
1119
|
+
* `lastInsertRowid` (rowid of the last successful insert).
|
|
1120
|
+
*
|
|
1018
1121
|
* @example
|
|
1019
|
-
*
|
|
1020
|
-
* await
|
|
1021
|
-
* "
|
|
1022
|
-
* "INSERT INTO users
|
|
1023
|
-
* "INSERT INTO users (name) VALUES ('Bob')"
|
|
1122
|
+
* // Plain SQL strings (non-atomic).
|
|
1123
|
+
* await db.batch([
|
|
1124
|
+
* "INSERT INTO users(name) VALUES ('Alice')",
|
|
1125
|
+
* "INSERT INTO users(name) VALUES ('Bob')",
|
|
1024
1126
|
* ]);
|
|
1025
|
-
*
|
|
1127
|
+
*
|
|
1128
|
+
* @example
|
|
1129
|
+
* // Positional and named bind parameters.
|
|
1130
|
+
* await db.batch([
|
|
1131
|
+
* { sql: "INSERT INTO users(name, email) VALUES (?, ?)", args: ["Carol", "carol@example.net"] },
|
|
1132
|
+
* { sql: "INSERT INTO users(name, email) VALUES (:name, :email)", args: { name: "Dave", email: "dave@example.net" } },
|
|
1133
|
+
* ]);
|
|
1134
|
+
*
|
|
1135
|
+
* @example
|
|
1136
|
+
* // Atomic via the mode parameter.
|
|
1137
|
+
* await db.batch([
|
|
1138
|
+
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] },
|
|
1139
|
+
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Frank"] },
|
|
1140
|
+
* ], "immediate");
|
|
1141
|
+
*
|
|
1142
|
+
* @example
|
|
1143
|
+
* // Atomic via the transaction() API for mixed workloads.
|
|
1144
|
+
* const txn = db.transaction(async () => {
|
|
1145
|
+
* await db.batch([{ sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] }]);
|
|
1146
|
+
* await db.execute("UPDATE counters SET n = n + 1");
|
|
1147
|
+
* });
|
|
1148
|
+
* await txn.immediate();
|
|
1026
1149
|
*/
|
|
1027
1150
|
async batch(statements, mode, queryOptions) {
|
|
1028
1151
|
if (!this.isOpen) {
|
|
@@ -1030,7 +1153,8 @@ var Connection = class {
|
|
|
1030
1153
|
}
|
|
1031
1154
|
await this.execLock.acquire();
|
|
1032
1155
|
try {
|
|
1033
|
-
|
|
1156
|
+
const effectiveMode = this._inTransaction ? void 0 : mode;
|
|
1157
|
+
return await this.session.batch(statements, effectiveMode, queryOptions);
|
|
1034
1158
|
} finally {
|
|
1035
1159
|
this.execLock.release();
|
|
1036
1160
|
}
|
|
@@ -1103,12 +1227,14 @@ var Connection = class {
|
|
|
1103
1227
|
const properties = {
|
|
1104
1228
|
default: { value: wrapTxn("") },
|
|
1105
1229
|
deferred: { value: wrapTxn("DEFERRED") },
|
|
1230
|
+
concurrent: { value: wrapTxn("CONCURRENT") },
|
|
1106
1231
|
immediate: { value: wrapTxn("IMMEDIATE") },
|
|
1107
1232
|
exclusive: { value: wrapTxn("EXCLUSIVE") },
|
|
1108
1233
|
database: { value: this, enumerable: true }
|
|
1109
1234
|
};
|
|
1110
1235
|
Object.defineProperties(properties.default.value, properties);
|
|
1111
1236
|
Object.defineProperties(properties.deferred.value, properties);
|
|
1237
|
+
Object.defineProperties(properties.concurrent.value, properties);
|
|
1112
1238
|
Object.defineProperties(properties.immediate.value, properties);
|
|
1113
1239
|
Object.defineProperties(properties.exclusive.value, properties);
|
|
1114
1240
|
return properties.default.value;
|
package/dist/index.d.cts
CHANGED
|
@@ -39,6 +39,11 @@ interface QueryOptions {
|
|
|
39
39
|
queryTimeout?: number;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Locking mode for atomic `batch()` execution. Accepts the same values
|
|
44
|
+
* as the variants of `Connection.transaction(...)`.
|
|
45
|
+
*/
|
|
46
|
+
type BatchMode = 'deferred' | 'immediate' | 'exclusive' | 'concurrent';
|
|
42
47
|
/**
|
|
43
48
|
* Configuration options for a session.
|
|
44
49
|
*/
|
|
@@ -112,10 +117,23 @@ declare class Session {
|
|
|
112
117
|
/**
|
|
113
118
|
* Execute multiple SQL statements in a batch.
|
|
114
119
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
120
|
+
* When `mode` is set, the batch is sent as a single Hrana request that
|
|
121
|
+
* also carries `BEGIN <mode>` / `COMMIT` / `ROLLBACK` steps using the
|
|
122
|
+
* server-side condition chain, giving atomic execution in one round-trip.
|
|
123
|
+
* When `mode` is omitted, the user statements are sent as-is and run
|
|
124
|
+
* under autocommit (or whatever transaction is already active on this
|
|
125
|
+
* stream).
|
|
126
|
+
*
|
|
127
|
+
* @param statements - Array of SQL statements to execute.
|
|
128
|
+
* @param mode - Optional locking mode; when set, the batch executes
|
|
129
|
+
* atomically. Accepts the same values as `Database.transaction(...)`
|
|
130
|
+
* variants: `"deferred"`, `"immediate"`, `"exclusive"`, `"concurrent"`.
|
|
131
|
+
* @returns Promise resolving to batch execution results.
|
|
117
132
|
*/
|
|
118
|
-
batch(statements: string
|
|
133
|
+
batch(statements: Array<string | {
|
|
134
|
+
sql: string;
|
|
135
|
+
args?: any[] | Record<string, any>;
|
|
136
|
+
}>, mode?: BatchMode, queryOptions?: QueryOptions): Promise<any>;
|
|
119
137
|
/**
|
|
120
138
|
* Execute a sequence of SQL statements separated by semicolons.
|
|
121
139
|
*
|
|
@@ -298,6 +316,10 @@ declare class Statement {
|
|
|
298
316
|
*/
|
|
299
317
|
interface Config extends SessionConfig {
|
|
300
318
|
}
|
|
319
|
+
type BatchStatement = string | {
|
|
320
|
+
sql: string;
|
|
321
|
+
args?: any[] | Record<string, any>;
|
|
322
|
+
};
|
|
301
323
|
/**
|
|
302
324
|
* A connection to a Turso database.
|
|
303
325
|
*
|
|
@@ -418,22 +440,63 @@ declare class Connection {
|
|
|
418
440
|
*/
|
|
419
441
|
exec(sql: string, queryOptions?: QueryOptions): Promise<any>;
|
|
420
442
|
/**
|
|
421
|
-
*
|
|
443
|
+
* Executes a batch of SQL statements over this connection.
|
|
444
|
+
*
|
|
445
|
+
* By default, batch() is not transactional: each statement runs in its
|
|
446
|
+
* own autocommit step, so a failure mid-batch leaves earlier successful
|
|
447
|
+
* statements committed. Pass a `mode` to make the batch atomic — the
|
|
448
|
+
* statements are wrapped in `BEGIN <mode>` / `COMMIT` (with `ROLLBACK`
|
|
449
|
+
* on failure) and dispatched as a single Hrana request, so the whole
|
|
450
|
+
* batch completes in one round-trip. When called from inside a
|
|
451
|
+
* `connection.transaction(...)` callback the `mode` argument is ignored
|
|
452
|
+
* and the surrounding transaction is reused.
|
|
453
|
+
*
|
|
454
|
+
* When `mode` is set, `batch()` owns the surrounding
|
|
455
|
+
* `BEGIN`/`COMMIT`/`ROLLBACK`, so the `statements` array must not
|
|
456
|
+
* contain its own transaction-control SQL (`BEGIN`, `COMMIT`,
|
|
457
|
+
* `ROLLBACK`, `SAVEPOINT`, `RELEASE`). The input is not validated
|
|
458
|
+
* for that — a user-supplied `COMMIT` will close the wrapper
|
|
459
|
+
* transaction mid-batch and leave earlier statements committed,
|
|
460
|
+
* defeating the all-or-nothing contract.
|
|
461
|
+
*
|
|
462
|
+
* @param statements - An array of SQL strings or `{ sql, args }` objects.
|
|
463
|
+
* @param mode - When set, makes the batch atomic. Accepts the same
|
|
464
|
+
* values as `connection.transaction(...)` variants: `"deferred"`,
|
|
465
|
+
* `"immediate"`, `"exclusive"`, `"concurrent"`. Ignored when already
|
|
466
|
+
* inside a transaction.
|
|
467
|
+
* @returns An object with `rowsAffected` (sum of affected rows) and
|
|
468
|
+
* `lastInsertRowid` (rowid of the last successful insert).
|
|
422
469
|
*
|
|
423
|
-
* @
|
|
424
|
-
*
|
|
425
|
-
*
|
|
470
|
+
* @example
|
|
471
|
+
* // Plain SQL strings (non-atomic).
|
|
472
|
+
* await db.batch([
|
|
473
|
+
* "INSERT INTO users(name) VALUES ('Alice')",
|
|
474
|
+
* "INSERT INTO users(name) VALUES ('Bob')",
|
|
475
|
+
* ]);
|
|
426
476
|
*
|
|
427
477
|
* @example
|
|
428
|
-
*
|
|
429
|
-
* await
|
|
430
|
-
* "
|
|
431
|
-
* "INSERT INTO users
|
|
432
|
-
* "INSERT INTO users (name) VALUES ('Bob')"
|
|
478
|
+
* // Positional and named bind parameters.
|
|
479
|
+
* await db.batch([
|
|
480
|
+
* { sql: "INSERT INTO users(name, email) VALUES (?, ?)", args: ["Carol", "carol@example.net"] },
|
|
481
|
+
* { sql: "INSERT INTO users(name, email) VALUES (:name, :email)", args: { name: "Dave", email: "dave@example.net" } },
|
|
433
482
|
* ]);
|
|
434
|
-
*
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* // Atomic via the mode parameter.
|
|
486
|
+
* await db.batch([
|
|
487
|
+
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] },
|
|
488
|
+
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Frank"] },
|
|
489
|
+
* ], "immediate");
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* // Atomic via the transaction() API for mixed workloads.
|
|
493
|
+
* const txn = db.transaction(async () => {
|
|
494
|
+
* await db.batch([{ sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] }]);
|
|
495
|
+
* await db.execute("UPDATE counters SET n = n + 1");
|
|
496
|
+
* });
|
|
497
|
+
* await txn.immediate();
|
|
435
498
|
*/
|
|
436
|
-
batch(statements:
|
|
499
|
+
batch(statements: BatchStatement[], mode?: BatchMode, queryOptions?: QueryOptions): Promise<any>;
|
|
437
500
|
/**
|
|
438
501
|
* Execute a pragma.
|
|
439
502
|
*
|
|
@@ -527,4 +590,4 @@ declare class TimeoutError extends DatabaseError {
|
|
|
527
590
|
constructor(message?: string, cause?: Error);
|
|
528
591
|
}
|
|
529
592
|
|
|
530
|
-
export { type Column, type Config, Connection, DatabaseError, ENCRYPTION_KEY_HEADER, type QueryOptions, Session, type SessionConfig, Statement, TimeoutError, connect };
|
|
593
|
+
export { type BatchStatement, type Column, type Config, Connection, DatabaseError, ENCRYPTION_KEY_HEADER, type QueryOptions, Session, type SessionConfig, Statement, TimeoutError, connect };
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,11 @@ interface QueryOptions {
|
|
|
39
39
|
queryTimeout?: number;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Locking mode for atomic `batch()` execution. Accepts the same values
|
|
44
|
+
* as the variants of `Connection.transaction(...)`.
|
|
45
|
+
*/
|
|
46
|
+
type BatchMode = 'deferred' | 'immediate' | 'exclusive' | 'concurrent';
|
|
42
47
|
/**
|
|
43
48
|
* Configuration options for a session.
|
|
44
49
|
*/
|
|
@@ -112,10 +117,23 @@ declare class Session {
|
|
|
112
117
|
/**
|
|
113
118
|
* Execute multiple SQL statements in a batch.
|
|
114
119
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
120
|
+
* When `mode` is set, the batch is sent as a single Hrana request that
|
|
121
|
+
* also carries `BEGIN <mode>` / `COMMIT` / `ROLLBACK` steps using the
|
|
122
|
+
* server-side condition chain, giving atomic execution in one round-trip.
|
|
123
|
+
* When `mode` is omitted, the user statements are sent as-is and run
|
|
124
|
+
* under autocommit (or whatever transaction is already active on this
|
|
125
|
+
* stream).
|
|
126
|
+
*
|
|
127
|
+
* @param statements - Array of SQL statements to execute.
|
|
128
|
+
* @param mode - Optional locking mode; when set, the batch executes
|
|
129
|
+
* atomically. Accepts the same values as `Database.transaction(...)`
|
|
130
|
+
* variants: `"deferred"`, `"immediate"`, `"exclusive"`, `"concurrent"`.
|
|
131
|
+
* @returns Promise resolving to batch execution results.
|
|
117
132
|
*/
|
|
118
|
-
batch(statements: string
|
|
133
|
+
batch(statements: Array<string | {
|
|
134
|
+
sql: string;
|
|
135
|
+
args?: any[] | Record<string, any>;
|
|
136
|
+
}>, mode?: BatchMode, queryOptions?: QueryOptions): Promise<any>;
|
|
119
137
|
/**
|
|
120
138
|
* Execute a sequence of SQL statements separated by semicolons.
|
|
121
139
|
*
|
|
@@ -298,6 +316,10 @@ declare class Statement {
|
|
|
298
316
|
*/
|
|
299
317
|
interface Config extends SessionConfig {
|
|
300
318
|
}
|
|
319
|
+
type BatchStatement = string | {
|
|
320
|
+
sql: string;
|
|
321
|
+
args?: any[] | Record<string, any>;
|
|
322
|
+
};
|
|
301
323
|
/**
|
|
302
324
|
* A connection to a Turso database.
|
|
303
325
|
*
|
|
@@ -418,22 +440,63 @@ declare class Connection {
|
|
|
418
440
|
*/
|
|
419
441
|
exec(sql: string, queryOptions?: QueryOptions): Promise<any>;
|
|
420
442
|
/**
|
|
421
|
-
*
|
|
443
|
+
* Executes a batch of SQL statements over this connection.
|
|
444
|
+
*
|
|
445
|
+
* By default, batch() is not transactional: each statement runs in its
|
|
446
|
+
* own autocommit step, so a failure mid-batch leaves earlier successful
|
|
447
|
+
* statements committed. Pass a `mode` to make the batch atomic — the
|
|
448
|
+
* statements are wrapped in `BEGIN <mode>` / `COMMIT` (with `ROLLBACK`
|
|
449
|
+
* on failure) and dispatched as a single Hrana request, so the whole
|
|
450
|
+
* batch completes in one round-trip. When called from inside a
|
|
451
|
+
* `connection.transaction(...)` callback the `mode` argument is ignored
|
|
452
|
+
* and the surrounding transaction is reused.
|
|
453
|
+
*
|
|
454
|
+
* When `mode` is set, `batch()` owns the surrounding
|
|
455
|
+
* `BEGIN`/`COMMIT`/`ROLLBACK`, so the `statements` array must not
|
|
456
|
+
* contain its own transaction-control SQL (`BEGIN`, `COMMIT`,
|
|
457
|
+
* `ROLLBACK`, `SAVEPOINT`, `RELEASE`). The input is not validated
|
|
458
|
+
* for that — a user-supplied `COMMIT` will close the wrapper
|
|
459
|
+
* transaction mid-batch and leave earlier statements committed,
|
|
460
|
+
* defeating the all-or-nothing contract.
|
|
461
|
+
*
|
|
462
|
+
* @param statements - An array of SQL strings or `{ sql, args }` objects.
|
|
463
|
+
* @param mode - When set, makes the batch atomic. Accepts the same
|
|
464
|
+
* values as `connection.transaction(...)` variants: `"deferred"`,
|
|
465
|
+
* `"immediate"`, `"exclusive"`, `"concurrent"`. Ignored when already
|
|
466
|
+
* inside a transaction.
|
|
467
|
+
* @returns An object with `rowsAffected` (sum of affected rows) and
|
|
468
|
+
* `lastInsertRowid` (rowid of the last successful insert).
|
|
422
469
|
*
|
|
423
|
-
* @
|
|
424
|
-
*
|
|
425
|
-
*
|
|
470
|
+
* @example
|
|
471
|
+
* // Plain SQL strings (non-atomic).
|
|
472
|
+
* await db.batch([
|
|
473
|
+
* "INSERT INTO users(name) VALUES ('Alice')",
|
|
474
|
+
* "INSERT INTO users(name) VALUES ('Bob')",
|
|
475
|
+
* ]);
|
|
426
476
|
*
|
|
427
477
|
* @example
|
|
428
|
-
*
|
|
429
|
-
* await
|
|
430
|
-
* "
|
|
431
|
-
* "INSERT INTO users
|
|
432
|
-
* "INSERT INTO users (name) VALUES ('Bob')"
|
|
478
|
+
* // Positional and named bind parameters.
|
|
479
|
+
* await db.batch([
|
|
480
|
+
* { sql: "INSERT INTO users(name, email) VALUES (?, ?)", args: ["Carol", "carol@example.net"] },
|
|
481
|
+
* { sql: "INSERT INTO users(name, email) VALUES (:name, :email)", args: { name: "Dave", email: "dave@example.net" } },
|
|
433
482
|
* ]);
|
|
434
|
-
*
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* // Atomic via the mode parameter.
|
|
486
|
+
* await db.batch([
|
|
487
|
+
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] },
|
|
488
|
+
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Frank"] },
|
|
489
|
+
* ], "immediate");
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* // Atomic via the transaction() API for mixed workloads.
|
|
493
|
+
* const txn = db.transaction(async () => {
|
|
494
|
+
* await db.batch([{ sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] }]);
|
|
495
|
+
* await db.execute("UPDATE counters SET n = n + 1");
|
|
496
|
+
* });
|
|
497
|
+
* await txn.immediate();
|
|
435
498
|
*/
|
|
436
|
-
batch(statements:
|
|
499
|
+
batch(statements: BatchStatement[], mode?: BatchMode, queryOptions?: QueryOptions): Promise<any>;
|
|
437
500
|
/**
|
|
438
501
|
* Execute a pragma.
|
|
439
502
|
*
|
|
@@ -527,4 +590,4 @@ declare class TimeoutError extends DatabaseError {
|
|
|
527
590
|
constructor(message?: string, cause?: Error);
|
|
528
591
|
}
|
|
529
592
|
|
|
530
|
-
export { type Column, type Config, Connection, DatabaseError, ENCRYPTION_KEY_HEADER, type QueryOptions, Session, type SessionConfig, Statement, TimeoutError, connect };
|
|
593
|
+
export { type BatchStatement, type Column, type Config, Connection, DatabaseError, ENCRYPTION_KEY_HEADER, type QueryOptions, Session, type SessionConfig, Statement, TimeoutError, connect };
|