@nextlyhq/adapter-sqlite 0.0.2-alpha.72 → 0.0.2-alpha.74

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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DrizzleAdapter } from "@nextlyhq/adapter-drizzle";
2
- import { AdapterLogger, BaseAdapterConfig, DatabaseCapabilities, DatabaseError, DatabaseErrorKind, DeleteOptions, InsertOptions, JoinSpec, OrderBySpec, PoolStats, SelectOptions, SqlParam, SqliteAdapterConfig, TransactionContext, TransactionOptions, UpdateOptions, UpsertOptions, WhereClause, WhereCondition, WhereOperator } from "@nextlyhq/adapter-drizzle/types";
2
+ import { AdapterLogger, BaseAdapterConfig, DatabaseCapabilities, DatabaseError, DatabaseErrorKind, DeleteOptions, InsertOptions, JoinSpec, OrderBySpec, PoolStats, SelectOptions, SessionOptions, SqlParam, SqliteAdapterConfig, StatementSession, TransactionContext, TransactionOptions, UpdateOptions, UpsertOptions, WhereClause, WhereCondition, WhereOperator } from "@nextlyhq/adapter-drizzle/types";
3
3
  import { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
4
4
  import { AnyRelations } from "drizzle-orm";
5
5
  /**
@@ -18,7 +18,8 @@ export declare const VERSION = "0.1.0";
18
18
  * - No connection pooling (single file/connection)
19
19
  * - RETURNING clause supported (SQLite 3.35+)
20
20
  * - Savepoints supported via nested transactions
21
- * - No native ILIKE (uses LOWER() LIKE workaround)
21
+ * - No native ILIKE (both sides case-folded, then LIKE; the fold is Nextly's own
22
+ * `nextly_fold_case`, registered on connect, since SQLite's `lower()` folds ASCII only)
22
23
  * - JSON support (not JSONB)
23
24
  * - No array types
24
25
  *
@@ -73,18 +74,6 @@ export declare class SqliteAdapter extends DrizzleAdapter {
73
74
  * surface area without changing the achievable concurrency.
74
75
  */
75
76
  private transactionQueue;
76
- /**
77
- * The transaction the current async chain is running inside, if any.
78
- *
79
- * A `transaction()` call made from INSIDE a transaction's work — a store
80
- * writing on behalf of the caller, a plugin's write inside its own
81
- * `ctx.db.transaction` — used to queue behind the very transaction that
82
- * was waiting for it, and neither ever settled: that hung every later
83
- * transaction on the instance too. Such a call now runs as a SAVEPOINT of
84
- * the active transaction instead, so it commits or rolls back with it and
85
- * its own failure undoes only its own part.
86
- */
87
- private readonly transactionScope;
88
77
  /**
89
78
  * Creates a new SQLite adapter instance.
90
79
  *
@@ -131,6 +120,8 @@ export declare class SqliteAdapter extends DrizzleAdapter {
131
120
  * @throws {DatabaseError} If query execution fails
132
121
  */
133
122
  executeQuery<T = unknown>(sql: string, params?: SqlParam[]): Promise<T[]>;
123
+ /** How long a transaction waits for the connection: `pool.connectionTimeoutMs`. */
124
+ private transactionWaitMs;
134
125
  /**
135
126
  * Execute work within a transaction.
136
127
  *
@@ -154,93 +145,36 @@ export declare class SqliteAdapter extends DrizzleAdapter {
154
145
  * Note: SQLite uses DEFERRED, IMMEDIATE, or EXCLUSIVE transaction modes
155
146
  * rather than isolation levels. This adapter uses IMMEDIATE.
156
147
  */
157
- /** How long a transaction waits for the connection: `pool.connectionTimeoutMs`. */
158
- private transactionWaitMs;
159
- transaction<T>(work: (tx: TransactionContext) => Promise<T>, _options?: TransactionOptions): Promise<T>;
148
+ transaction<T>(work: (tx: TransactionContext) => Promise<T>, options?: TransactionOptions): Promise<T>;
160
149
  /**
161
- * Hold `effect` until the transaction enclosing this async context commits,
162
- * or run it now when none does.
163
- *
164
- * The effect belongs to the scope this async context runs in, even one
165
- * that has already released or rolled back: a service whose own
166
- * transaction nested as a savepoint registers from its caller's scope once
167
- * that savepoint has released, and the change is undoable for as long as
168
- * any scope around it is. The outermost transaction runs the effect once
169
- * its COMMIT succeeds, unless a scope between the effect's scope and the
170
- * outermost transaction rolled back, and drops every effect on ROLLBACK or
171
- * a failed COMMIT. A context whose outermost transaction has already
172
- * finished is outside any transaction, so its effect runs now, unless what
173
- * it announces was undone and it has run no transaction since.
174
- */
175
- override afterCommit(effect: () => unknown, onDeferredFailure?: (error: unknown) => void): Promise<void>;
176
- /**
177
- * `effect`, bound to the async context it is registered from.
178
- *
179
- * A held effect runs after the outermost COMMIT, from the code that ran the
180
- * outermost transaction, so without this it sees that code's context rather
181
- * than its own: whatever its caller pinned around the write is gone. That
182
- * includes who is calling. A scope pinned for a scoped API key lost there
183
- * leaves a hook's nested call judged by the key's owner instead of the key,
184
- * and a person's frame lost there leaves the hook told of nobody.
150
+ * Runs `work` outside a transaction, each statement committing as it runs,
151
+ * once no other flow's transaction is open on the one connection.
185
152
  *
186
- * The adapter's own transaction scope is the one store it leaves: the
187
- * effect runs once that transaction has finished, outside it, as an effect
188
- * held to the outermost commit always has.
189
- */
190
- private inRegisteringContext;
191
- /**
192
- * Run the effects a committed transaction held, in order. A failure is
193
- * reported, never thrown: the change is durable, and reporting the
194
- * transaction as failed would invite a retry of something already done.
195
- */
196
- private runHeldEffects;
197
- /** Hand a held effect's failure to its reporter, or to the adapter's log. */
198
- private reportHeldEffectFailure;
199
- /**
200
- * Whether a `transaction()` call made here would join an open transaction
201
- * as a savepoint. On SQLite it would: the connection is the only one, so a
202
- * transaction open on it encloses whatever the async chain that opened it
203
- * starts.
204
- */
205
- override inTransaction(): boolean;
206
- /**
207
- * Whether the calling async flow is inside a transaction that has not
208
- * finished, judged from the transaction this flow was started inside,
209
- * never from the connection: another request's open transaction answers
210
- * false here, where its own `transaction()` queues behind that one.
211
- *
212
- * A flow started inside a transaction, even inside a savepoint that has
213
- * since released, stays inside it until its outermost transaction has
214
- * committed or rolled back: until then whatever it writes on the one
215
- * connection lands in that transaction.
153
+ * @remarks
154
+ * Waits its turn in `transactionQueue` as a transaction does: a statement
155
+ * run while another flow's transaction is open would join it, and be undone
156
+ * if it rolled back. Transactions begun meanwhile wait behind the session in
157
+ * turn, so `work` must not open one of its own: it would wait behind the
158
+ * session that waits for it, until it fails as busy.
216
159
  */
217
- override transactionOpenInFlow(): boolean;
160
+ override withSession<T>(work: (session: StatementSession) => Promise<T>, _options?: SessionOptions): Promise<T>;
218
161
  /**
219
- * The scope a `transaction()` call made here nests in, or none when it
220
- * opens a transaction of its own.
221
- *
222
- * A call made later from inside a savepoint that has since released runs
223
- * inside the innermost scope open on the connection. Queued on an enclosing
224
- * scope, or on the instance, it would wait behind the scope running there,
225
- * which may be the one awaiting it. Savepoints on one connection form a
226
- * stack, so whatever opens now is inside that innermost scope anyway; when
227
- * none is open the call queues on the instance.
228
- *
229
- * The innermost open scope need not be related to the call. When it is a
230
- * sibling savepoint that later rolls back, the call's write rolls back with
231
- * it, although the call itself already resolved. Waiting for an unrelated
232
- * scope instead could wait on the very scope that awaits the call.
162
+ * Runs `run` once every turn queued before it in `transactionQueue` is
163
+ * done, and holds the queue until `run` is done too.
233
164
  */
234
- private nestingScope;
165
+ private takeTurn;
166
+ /** A held effect's failure that no reporter took goes to the adapter's logger. */
167
+ protected override logHeldEffectFailure(failure: Error): void;
235
168
  /**
236
- * Run `work` as a savepoint of the active transaction: released when it
237
- * resolves, rolled back to when it throws.
169
+ * The statements of a savepoint `depth` deep on the one connection:
170
+ * released when its work resolves, rolled back to when it throws.
238
171
  */
239
- private runSavepoint;
172
+ private savepointSteps;
240
173
  /**
241
- * Inner transaction body. Kept private so callers always go through
242
- * the serialized `transaction()` queue above and the BEGIN/COMMIT
243
- * pair is never invoked outside of it.
174
+ * One attempt's transaction body, settled rather than rejected for the
175
+ * transaction's own failure (`runOutermost`). Kept private so callers always
176
+ * go through the serialized `transaction()` queue above and the
177
+ * BEGIN/COMMIT pair is never invoked outside of it.
244
178
  */
245
179
  private runTransaction;
246
180
  /**
package/dist/index.mjs CHANGED
@@ -1,9 +1,25 @@
1
- import { AsyncLocalStorage, AsyncResource } from "node:async_hooks";
2
1
  import { DrizzleAdapter } from "@nextlyhq/adapter-drizzle";
3
2
  import { createDatabaseError, isApplicationError, isDatabaseError, poolWaitTimeoutError } from "@nextlyhq/adapter-drizzle/types";
4
3
  import { checkDialectVersion } from "@nextlyhq/adapter-drizzle/version-check";
5
4
  import { drizzle } from "drizzle-orm/better-sqlite3";
6
5
  import { getTableConfig } from "drizzle-orm/sqlite-core";
6
+ import { registerSqliteFoldCase } from "@nextlyhq/adapter-drizzle/text-match";
7
+ // src/sql-functions.ts
8
+ /**
9
+ * The SQL functions a SQLite connection needs before Nextly's statements run on it.
10
+ *
11
+ * @module sql-functions
12
+ */
13
+ /**
14
+ * Register the SQL functions Nextly's statements call on SQLite, on `db`: the Unicode case fold
15
+ * (`nextly_fold_case`), since SQLite's own `lower()` folds ASCII letters only.
16
+ *
17
+ * Every connection the adapter opens is handed here as it opens, before anything runs on it, so
18
+ * no statement can reach a connection without the function.
19
+ */
20
+ function registerSqliteFunctions(db) {
21
+ registerSqliteFoldCase(db);
22
+ }
7
23
  // src/index.ts
8
24
  /**
9
25
  * @nextlyhq/adapter-sqlite
@@ -105,7 +121,8 @@ function sanitizeSqliteValue(v) {
105
121
  * - No connection pooling (single file/connection)
106
122
  * - RETURNING clause supported (SQLite 3.35+)
107
123
  * - Savepoints supported via nested transactions
108
- * - No native ILIKE (uses LOWER() LIKE workaround)
124
+ * - No native ILIKE (both sides case-folded, then LIKE; the fold is Nextly's own
125
+ * `nextly_fold_case`, registered on connect, since SQLite's `lower()` folds ASCII only)
109
126
  * - JSON support (not JSONB)
110
127
  * - No array types
111
128
  *
@@ -161,18 +178,6 @@ var SqliteAdapter = class extends DrizzleAdapter {
161
178
  */
162
179
  transactionQueue = Promise.resolve();
163
180
  /**
164
- * The transaction the current async chain is running inside, if any.
165
- *
166
- * A `transaction()` call made from INSIDE a transaction's work — a store
167
- * writing on behalf of the caller, a plugin's write inside its own
168
- * `ctx.db.transaction` — used to queue behind the very transaction that
169
- * was waiting for it, and neither ever settled: that hung every later
170
- * transaction on the instance too. Such a call now runs as a SAVEPOINT of
171
- * the active transaction instead, so it commits or rolls back with it and
172
- * its own failure undoes only its own part.
173
- */
174
- transactionScope = new AsyncLocalStorage();
175
- /**
176
181
  * Creates a new SQLite adapter instance.
177
182
  *
178
183
  * @param config - Adapter configuration
@@ -209,6 +214,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
209
214
  readonly: this.config.readonly ?? false,
210
215
  timeout: this.config.busyTimeout ?? DEFAULT_CONFIG.busyTimeout
211
216
  });
217
+ registerSqliteFunctions(this.db);
212
218
  if ((this.config.wal ?? DEFAULT_CONFIG.wal) && dbPath !== ":memory:" && !this.config.readonly) this.db.pragma("journal_mode = WAL");
213
219
  if (this.config.foreignKeys ?? DEFAULT_CONFIG.foreignKeys) this.db.pragma("foreign_keys = ON");
214
220
  await checkDialectVersion(this.db, "sqlite");
@@ -299,6 +305,10 @@ var SqliteAdapter = class extends DrizzleAdapter {
299
305
  throw this.classifyError(error, sql);
300
306
  }
301
307
  }
308
+ /** How long a transaction waits for the connection: `pool.connectionTimeoutMs`. */
309
+ transactionWaitMs() {
310
+ return this.config.pool?.connectionTimeoutMs ?? DEFAULT_CONFIG.connectionTimeoutMs;
311
+ }
302
312
  /**
303
313
  * Execute work within a transaction.
304
314
  *
@@ -322,206 +332,94 @@ var SqliteAdapter = class extends DrizzleAdapter {
322
332
  * Note: SQLite uses DEFERRED, IMMEDIATE, or EXCLUSIVE transaction modes
323
333
  * rather than isolation levels. This adapter uses IMMEDIATE.
324
334
  */
325
- /** How long a transaction waits for the connection: `pool.connectionTimeoutMs`. */
326
- transactionWaitMs() {
327
- return this.config.pool?.connectionTimeoutMs ?? DEFAULT_CONFIG.connectionTimeoutMs;
328
- }
329
- async transaction(work, _options) {
330
- const own = this.transactionScope.getStore();
331
- const parent = this.nestingScope();
332
- if (own && parent !== own) own.writesAfterClosing += 1;
333
- if (parent) return enqueueIn(parent, () => this.runSavepoint(work, parent));
334
- const run = async () => this.runTransaction(work);
335
- const previous = this.transactionQueue;
336
- const next = waitForTurn(previous, this.transactionWaitMs()).then(run);
337
- this.transactionQueue = previous.then(() => next).catch(() => void 0);
338
- const { result, effects } = await next;
339
- await this.runHeldEffects(effects);
340
- return result;
341
- }
342
- /**
343
- * Hold `effect` until the transaction enclosing this async context commits,
344
- * or run it now when none does.
345
- *
346
- * The effect belongs to the scope this async context runs in, even one
347
- * that has already released or rolled back: a service whose own
348
- * transaction nested as a savepoint registers from its caller's scope once
349
- * that savepoint has released, and the change is undoable for as long as
350
- * any scope around it is. The outermost transaction runs the effect once
351
- * its COMMIT succeeds, unless a scope between the effect's scope and the
352
- * outermost transaction rolled back, and drops every effect on ROLLBACK or
353
- * a failed COMMIT. A context whose outermost transaction has already
354
- * finished is outside any transaction, so its effect runs now, unless what
355
- * it announces was undone and it has run no transaction since.
356
- */
357
- async afterCommit(effect, onDeferredFailure) {
358
- const scope = this.transactionScope.getStore();
359
- if (!scope) {
360
- await effect();
361
- return;
362
- }
363
- const root = outermost(scope);
364
- if (!root.finished) {
365
- root.held.push({
366
- effect: this.inRegisteringContext(effect),
367
- onDeferredFailure,
368
- scope,
369
- writesBefore: scope.writesAfterClosing
370
- });
371
- return;
372
- }
373
- if (!undone(scope, scope.writesAfterClosing)) await effect();
335
+ transaction(work, options) {
336
+ return this.openOrJoin(work, options, {
337
+ run: () => this.takeTurn(() => this.runTransaction(work, options)),
338
+ retryable: () => false,
339
+ describe: () => ({
340
+ label: "a retryable error",
341
+ detail: {}
342
+ }),
343
+ logger: this.config.logger
344
+ });
374
345
  }
375
346
  /**
376
- * `effect`, bound to the async context it is registered from.
377
- *
378
- * A held effect runs after the outermost COMMIT, from the code that ran the
379
- * outermost transaction, so without this it sees that code's context rather
380
- * than its own: whatever its caller pinned around the write is gone. That
381
- * includes who is calling. A scope pinned for a scoped API key lost there
382
- * leaves a hook's nested call judged by the key's owner instead of the key,
383
- * and a person's frame lost there leaves the hook told of nobody.
347
+ * Runs `work` outside a transaction, each statement committing as it runs,
348
+ * once no other flow's transaction is open on the one connection.
384
349
  *
385
- * The adapter's own transaction scope is the one store it leaves: the
386
- * effect runs once that transaction has finished, outside it, as an effect
387
- * held to the outermost commit always has.
350
+ * @remarks
351
+ * Waits its turn in `transactionQueue` as a transaction does: a statement
352
+ * run while another flow's transaction is open would join it, and be undone
353
+ * if it rolled back. Transactions begun meanwhile wait behind the session in
354
+ * turn, so `work` must not open one of its own: it would wait behind the
355
+ * session that waits for it, until it fails as busy.
388
356
  */
389
- inRegisteringContext(effect) {
390
- return AsyncResource.bind(() => this.transactionScope.exit(effect));
357
+ async withSession(work, _options) {
358
+ this.refuseSessionInTransaction();
359
+ return this.takeTurn(() => work({ execute: (statement, params) => this.executeQuery(statement, params) }));
391
360
  }
392
361
  /**
393
- * Run the effects a committed transaction held, in order. A failure is
394
- * reported, never thrown: the change is durable, and reporting the
395
- * transaction as failed would invite a retry of something already done.
362
+ * Runs `run` once every turn queued before it in `transactionQueue` is
363
+ * done, and holds the queue until `run` is done too.
396
364
  */
397
- async runHeldEffects(effects) {
398
- for (const { effect, onDeferredFailure } of effects) try {
399
- await effect();
400
- } catch (error) {
401
- this.reportHeldEffectFailure(error, onDeferredFailure);
402
- }
365
+ takeTurn(run) {
366
+ const previous = this.transactionQueue;
367
+ const next = waitForTurn(previous, this.transactionWaitMs()).then(run);
368
+ this.transactionQueue = previous.then(() => next).catch(() => void 0);
369
+ return next;
403
370
  }
404
- /** Hand a held effect's failure to its reporter, or to the adapter's log. */
405
- reportHeldEffectFailure(error, onDeferredFailure) {
406
- try {
407
- if (onDeferredFailure) {
408
- onDeferredFailure(error);
409
- return;
410
- }
411
- } catch {}
412
- const failure = error instanceof Error ? error : new Error(String(error));
371
+ /** A held effect's failure that no reporter took goes to the adapter's logger. */
372
+ logHeldEffectFailure(failure) {
413
373
  if (this.config.logger?.error) this.config.logger.error(failure, { phase: "after-commit" });
414
- else console.error("An effect held until commit failed:", failure);
415
- }
416
- /**
417
- * Whether a `transaction()` call made here would join an open transaction
418
- * as a savepoint. On SQLite it would: the connection is the only one, so a
419
- * transaction open on it encloses whatever the async chain that opened it
420
- * starts.
421
- */
422
- inTransaction() {
423
- return this.nestingScope() !== void 0;
424
- }
425
- /**
426
- * Whether the calling async flow is inside a transaction that has not
427
- * finished, judged from the transaction this flow was started inside,
428
- * never from the connection: another request's open transaction answers
429
- * false here, where its own `transaction()` queues behind that one.
430
- *
431
- * A flow started inside a transaction, even inside a savepoint that has
432
- * since released, stays inside it until its outermost transaction has
433
- * committed or rolled back: until then whatever it writes on the one
434
- * connection lands in that transaction.
435
- */
436
- transactionOpenInFlow() {
437
- const own = this.transactionScope.getStore();
438
- return own !== void 0 && !outermost(own).finished;
439
- }
440
- /**
441
- * The scope a `transaction()` call made here nests in, or none when it
442
- * opens a transaction of its own.
443
- *
444
- * A call made later from inside a savepoint that has since released runs
445
- * inside the innermost scope open on the connection. Queued on an enclosing
446
- * scope, or on the instance, it would wait behind the scope running there,
447
- * which may be the one awaiting it. Savepoints on one connection form a
448
- * stack, so whatever opens now is inside that innermost scope anyway; when
449
- * none is open the call queues on the instance.
450
- *
451
- * The innermost open scope need not be related to the call. When it is a
452
- * sibling savepoint that later rolls back, the call's write rolls back with
453
- * it, although the call itself already resolved. Waiting for an unrelated
454
- * scope instead could wait on the very scope that awaits the call.
455
- */
456
- nestingScope() {
457
- const own = this.transactionScope.getStore();
458
- return own?.active ? own : own?.open.at(-1);
374
+ else super.logHeldEffectFailure(failure);
459
375
  }
460
376
  /**
461
- * Run `work` as a savepoint of the active transaction: released when it
462
- * resolves, rolled back to when it throws.
377
+ * The statements of a savepoint `depth` deep on the one connection:
378
+ * released when its work resolves, rolled back to when it throws.
463
379
  */
464
- async runSavepoint(work, parent) {
380
+ savepointSteps(depth) {
465
381
  const db = this.ensureDb();
466
- const name = `nextly_sp_${parent.depth + 1}`;
467
- try {
468
- const ctx = this.createTransactionContext(db);
469
- db.exec(`SAVEPOINT ${name}`);
470
- const scope = openScope(parent);
471
- try {
472
- const result = await this.transactionScope.run(scope, () => work(ctx));
473
- await settleNested(scope);
382
+ const name = `nextly_sp_${depth}`;
383
+ return {
384
+ context: this.createTransactionContext(db),
385
+ begin: () => {
386
+ db.exec(`SAVEPOINT ${name}`);
387
+ },
388
+ commit: () => {
389
+ db.exec(`RELEASE ${name}`);
390
+ },
391
+ rollback: () => {
392
+ db.exec(`ROLLBACK TO ${name}`);
474
393
  db.exec(`RELEASE ${name}`);
475
- return result;
476
- } catch (error) {
477
- await settleNested(scope);
478
- scope.rolledBack = true;
479
- try {
480
- db.exec(`ROLLBACK TO ${name}`);
481
- db.exec(`RELEASE ${name}`);
482
- } catch {}
483
- throw error;
484
394
  }
485
- } catch (error) {
486
- if (isApplicationError(error)) throw error;
487
- throw this.classifyError(error);
488
- }
395
+ };
489
396
  }
490
397
  /**
491
- * Inner transaction body. Kept private so callers always go through
492
- * the serialized `transaction()` queue above and the BEGIN/COMMIT
493
- * pair is never invoked outside of it.
398
+ * One attempt's transaction body, settled rather than rejected for the
399
+ * transaction's own failure (`runOutermost`). Kept private so callers always
400
+ * go through the serialized `transaction()` queue above and the
401
+ * BEGIN/COMMIT pair is never invoked outside of it.
494
402
  */
495
- async runTransaction(work) {
403
+ async runTransaction(work, options) {
496
404
  const db = this.ensureDb();
497
- const startTime = Date.now();
498
405
  try {
499
- const ctx = this.createTransactionContext(db);
500
- db.exec("BEGIN IMMEDIATE");
501
- const scope = openScope();
502
- try {
503
- const result = await this.transactionScope.run(scope, () => work(ctx));
504
- await settleNested(scope);
505
- db.exec("COMMIT");
506
- scope.finished = true;
507
- if (this.config.logger?.debug) {
508
- const durationMs = Date.now() - startTime;
509
- this.config.logger.debug("Transaction committed", { durationMs });
406
+ return await this.settleOutermost({
407
+ context: this.createTransactionContext(db),
408
+ join: { adapter: (depth) => this.savepointSteps(depth) },
409
+ begin: () => {
410
+ db.exec("BEGIN IMMEDIATE");
411
+ },
412
+ commit: () => {
413
+ db.exec("COMMIT");
414
+ },
415
+ rollback: () => {
416
+ try {
417
+ db.exec("ROLLBACK");
418
+ } catch (error) {
419
+ if (db.inTransaction) throw error;
420
+ }
510
421
  }
511
- return {
512
- result,
513
- effects: scope.held.filter((held) => !undone(held.scope, held.writesBefore))
514
- };
515
- } catch (error) {
516
- await settleNested(scope);
517
- scope.rolledBack = true;
518
- scope.finished = true;
519
- scope.held.length = 0;
520
- try {
521
- db.exec("ROLLBACK");
522
- } catch {}
523
- throw error;
524
- }
422
+ }, work, options);
525
423
  } catch (error) {
526
424
  if (isApplicationError(error)) throw error;
527
425
  throw this.classifyError(error);
@@ -565,6 +463,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
565
463
  if (data.length === 0) return [];
566
464
  const db = this.ensureDb();
567
465
  if (data.length === 1) return [await this.insert(table, data[0], options)];
466
+ const tableObj = this.resolvedTableObject(table);
568
467
  const columns = Object.keys(data[0]);
569
468
  const params = [];
570
469
  const valuesClauses = [];
@@ -583,9 +482,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
583
482
  sql += ` RETURNING ${returning}`;
584
483
  } else sql += " RETURNING *";
585
484
  try {
586
- const rows = db.prepare(sql).all(...params);
587
- const tableObj = this.getTableObject(table);
588
- return rows.map((r) => this.mapRowFromRawSql(tableObj, r));
485
+ return db.prepare(sql).all(...params).map((r) => this.mapRowFromRawSql(tableObj, r));
589
486
  } catch (error) {
590
487
  throw this.handleQueryError(error, "insertMany", table);
591
488
  }
@@ -672,7 +569,8 @@ var SqliteAdapter = class extends DrizzleAdapter {
672
569
  }
673
570
  },
674
571
  insert: async (table, data, options) => {
675
- const mapped = this.mapKeysToSqlColumns(this.getTableObject(table), data);
572
+ const tableObj = this.resolvedTableObject(table);
573
+ const mapped = this.mapKeysToSqlColumns(tableObj, data);
676
574
  const columns = Object.keys(mapped);
677
575
  const values = Object.values(mapped).map(sanitizeSqliteValue);
678
576
  const placeholders = values.map(() => "?").join(", ");
@@ -680,7 +578,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
680
578
  const ret = options?.returning;
681
579
  const returningEmpty = Array.isArray(ret) && ret.length === 0;
682
580
  if (!returningEmpty) {
683
- const returning = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(this.getTableObject(table), ret).map((col) => this.escapeIdentifier(col)).join(", ");
581
+ const returning = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(tableObj, ret).map((col) => this.escapeIdentifier(col)).join(", ");
684
582
  sql += ` RETURNING ${returning}`;
685
583
  }
686
584
  const stmt = db.prepare(sql);
@@ -689,11 +587,11 @@ var SqliteAdapter = class extends DrizzleAdapter {
689
587
  return;
690
588
  }
691
589
  const rows = stmt.all(...values);
692
- return this.mapRowFromRawSql(this.getTableObject(table), rows[0]);
590
+ return this.mapRowFromRawSql(tableObj, rows[0]);
693
591
  },
694
592
  insertMany: async (table, data, options) => {
695
593
  if (data.length === 0) return [];
696
- const tableObj = this.getTableObject(table);
594
+ const tableObj = this.resolvedTableObject(table);
697
595
  const mappedRecords = data.map((r) => this.mapKeysToSqlColumns(tableObj, r));
698
596
  const columns = Object.keys(mappedRecords[0]);
699
597
  const allValues = [];
@@ -710,7 +608,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
710
608
  const ret = options?.returning;
711
609
  const returningEmpty = Array.isArray(ret) && ret.length === 0;
712
610
  if (!returningEmpty) {
713
- const returning = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(this.getTableObject(table), ret).map((col) => this.escapeIdentifier(col)).join(", ");
611
+ const returning = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(tableObj, ret).map((col) => this.escapeIdentifier(col)).join(", ");
714
612
  sql += ` RETURNING ${returning}`;
715
613
  }
716
614
  const stmt = db.prepare(sql);
@@ -817,44 +715,6 @@ function isSqliteAdapter(value) {
817
715
  return value instanceof SqliteAdapter;
818
716
  }
819
717
  /**
820
- * Open the scope of a savepoint inside `parent`, or of an outermost
821
- * transaction when there is none, and record it among the connection's open
822
- * scopes.
823
- */
824
- function openScope(parent) {
825
- const scope = {
826
- active: true,
827
- depth: parent ? parent.depth + 1 : 0,
828
- parent,
829
- rolledBack: false,
830
- writesAfterClosing: 0,
831
- finished: false,
832
- held: [],
833
- queue: Promise.resolve(),
834
- open: parent ? parent.open : []
835
- };
836
- scope.open.push(scope);
837
- return scope;
838
- }
839
- /** The outermost transaction `scope` runs inside; itself for that one. */
840
- function outermost(scope) {
841
- let at = scope;
842
- while (at.parent) at = at.parent;
843
- return at;
844
- }
845
- /**
846
- * Whether an effect registered from `scope` describes a write that was
847
- * undone: `scope`, or a scope it runs inside, rolled back, and no
848
- * transaction had run from its context since it closed when the effect was
849
- * registered (`writesBefore`). An effect registered before such a write
850
- * still describes the undone one.
851
- */
852
- function undone(scope, writesBefore) {
853
- if (writesBefore > 0) return false;
854
- for (let at = scope; at; at = at.parent) if (at.rolledBack) return true;
855
- return false;
856
- }
857
- /**
858
718
  * Settles once `turn` does, or rejects with the pool-wait timeout error once
859
719
  * `boundMs` passes first. A bound of 0 waits without limit, as pg-pool reads a
860
720
  * `connectionTimeoutMillis` of 0.
@@ -870,23 +730,6 @@ function waitForTurn(turn, boundMs) {
870
730
  });
871
731
  });
872
732
  }
873
- /** Chain `run` onto a scope's queue, keeping the queue itself unrejected. */
874
- function enqueueIn(scope, run) {
875
- const next = scope.queue.then(run, run);
876
- scope.queue = next.catch(() => void 0);
877
- return next;
878
- }
879
- /**
880
- * Close a scope to new nested calls and wait for those already queued. A call
881
- * arriving after this joins the innermost scope still open on the connection,
882
- * or, when none is, goes to the instance queue behind the transaction.
883
- */
884
- async function settleNested(scope) {
885
- scope.active = false;
886
- const index = scope.open.indexOf(scope);
887
- if (index !== -1) scope.open.splice(index, 1);
888
- await scope.queue;
889
- }
890
733
  export { SqliteAdapter, VERSION, createSqliteAdapter, isSqliteAdapter };
891
734
 
892
735
  //# sourceMappingURL=index.mjs.map