@quereus/quereus 0.13.1 → 0.14.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/dist/src/core/database.d.ts +33 -1
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +204 -58
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/statement.d.ts +7 -0
- package/dist/src/core/statement.d.ts.map +1 -1
- package/dist/src/core/statement.js +55 -44
- package/dist/src/core/statement.js.map +1 -1
- package/dist/src/core/utils.d.ts +4 -0
- package/dist/src/core/utils.d.ts.map +1 -0
- package/dist/src/core/utils.js +9 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +17 -0
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.js +4 -3
- package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +5 -0
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/package.json +14 -14
|
@@ -28,7 +28,7 @@ export declare class Database {
|
|
|
28
28
|
private isAutocommit;
|
|
29
29
|
private inTransaction;
|
|
30
30
|
private activeConnections;
|
|
31
|
-
private
|
|
31
|
+
private inImplicit;
|
|
32
32
|
readonly optimizer: Optimizer;
|
|
33
33
|
readonly options: DatabaseOptionsManager;
|
|
34
34
|
private instructionTracer;
|
|
@@ -38,6 +38,12 @@ export declare class Database {
|
|
|
38
38
|
private changeLogLayers;
|
|
39
39
|
/** Deferred constraint evaluation queue */
|
|
40
40
|
private readonly deferredConstraints;
|
|
41
|
+
/**
|
|
42
|
+
* Mutex for serializing statement execution.
|
|
43
|
+
* This prevents concurrent statements from interfering with each other's
|
|
44
|
+
* transaction state, matching SQLite's behavior of serializing writes.
|
|
45
|
+
*/
|
|
46
|
+
private execMutex;
|
|
41
47
|
constructor();
|
|
42
48
|
/**
|
|
43
49
|
* Prepares an SQL statement for execution.
|
|
@@ -65,12 +71,34 @@ export declare class Database {
|
|
|
65
71
|
prepare(sql: string, paramsOrTypes?: SqlParameters | SqlValue[] | Map<string | number, ScalarType>): Statement;
|
|
66
72
|
/**
|
|
67
73
|
* Executes a query and returns the first result row as an object.
|
|
74
|
+
* The execution is serialized through the mutex and wrapped in an implicit
|
|
75
|
+
* transaction when in autocommit mode.
|
|
76
|
+
*
|
|
68
77
|
* @param sql The SQL query string to execute.
|
|
69
78
|
* @param params Optional parameters to bind.
|
|
70
79
|
* @returns A Promise resolving to the first result row as an object, or undefined if no rows.
|
|
71
80
|
* @throws QuereusError on failure.
|
|
72
81
|
*/
|
|
73
82
|
get(sql: string, params?: SqlParameters | SqlValue[]): Promise<Record<string, SqlValue> | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* Parses SQL into a statement batch.
|
|
85
|
+
*/
|
|
86
|
+
private _parseSql;
|
|
87
|
+
/**
|
|
88
|
+
* Determines if a statement batch contains explicit transaction control.
|
|
89
|
+
*/
|
|
90
|
+
private _hasExplicitTransactionControl;
|
|
91
|
+
/**
|
|
92
|
+
* Executes one or more SQL statements directly.
|
|
93
|
+
* Statements are serialized through the execution mutex and wrapped in an
|
|
94
|
+
* implicit transaction when in autocommit mode (unless they contain explicit
|
|
95
|
+
* transaction control statements).
|
|
96
|
+
*
|
|
97
|
+
* @param sql The SQL string(s) to execute.
|
|
98
|
+
* @param params Optional parameters to bind.
|
|
99
|
+
* @returns A Promise resolving when execution completes.
|
|
100
|
+
* @throws QuereusError on failure.
|
|
101
|
+
*/
|
|
74
102
|
exec(sql: string, params?: SqlParameters): Promise<void>;
|
|
75
103
|
/**
|
|
76
104
|
* Registers a virtual table module.
|
|
@@ -266,6 +294,10 @@ export declare class Database {
|
|
|
266
294
|
* The underlying statement is automatically finalized when iteration completes
|
|
267
295
|
* or if an error occurs.
|
|
268
296
|
*
|
|
297
|
+
* The execution is serialized through the mutex and wrapped in an implicit
|
|
298
|
+
* transaction when in autocommit mode. The mutex is held for the entire
|
|
299
|
+
* iteration to ensure transactional consistency.
|
|
300
|
+
*
|
|
269
301
|
* @param sql The SQL query string to execute.
|
|
270
302
|
* @param params Optional parameters to bind (array for positional, object for named).
|
|
271
303
|
* @yields Each result row as an object (`Record<string, SqlValue>`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../src/core/database.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAM5D,OAAO,EAAwF,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAErJ,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AAIxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAMnE,OAAO,EAAE,QAAQ,EAAgD,MAAM,+BAA+B,CAAC;AAMvG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAkB,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAG7E,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../src/core/database.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,6 +30,7 @@ import { DeclaredSchemaManager } from '../schema/declared-schema-manager.js';
|
|
|
30
30
|
import { analyzeRowSpecific } from '../planner/analysis/constraint-extractor.js';
|
|
31
31
|
import { DeferredConstraintQueue } from '../runtime/deferred-constraint-queue.js';
|
|
32
32
|
import { getParameterTypes } from './param.js';
|
|
33
|
+
import { rowToObject } from './utils.js';
|
|
33
34
|
const log = createLogger('core:database');
|
|
34
35
|
const warnLog = log.extend('warn');
|
|
35
36
|
const errorLog = log.extend('error');
|
|
@@ -46,7 +47,7 @@ export class Database {
|
|
|
46
47
|
isAutocommit = true; // Manages transaction state
|
|
47
48
|
inTransaction = false;
|
|
48
49
|
activeConnections = new Map();
|
|
49
|
-
|
|
50
|
+
inImplicit = false; // Track if we're in an implicit transaction
|
|
50
51
|
optimizer;
|
|
51
52
|
options;
|
|
52
53
|
instructionTracer;
|
|
@@ -56,6 +57,12 @@ export class Database {
|
|
|
56
57
|
changeLogLayers = [];
|
|
57
58
|
/** Deferred constraint evaluation queue */
|
|
58
59
|
deferredConstraints = new DeferredConstraintQueue(this);
|
|
60
|
+
/**
|
|
61
|
+
* Mutex for serializing statement execution.
|
|
62
|
+
* This prevents concurrent statements from interfering with each other's
|
|
63
|
+
* transaction state, matching SQLite's behavior of serializing writes.
|
|
64
|
+
*/
|
|
65
|
+
execMutex = Promise.resolve();
|
|
59
66
|
constructor() {
|
|
60
67
|
this.schemaManager = new SchemaManager(this);
|
|
61
68
|
this.declaredSchemaManager = new DeclaredSchemaManager();
|
|
@@ -205,14 +212,23 @@ export class Database {
|
|
|
205
212
|
}
|
|
206
213
|
/**
|
|
207
214
|
* Executes a query and returns the first result row as an object.
|
|
215
|
+
* The execution is serialized through the mutex and wrapped in an implicit
|
|
216
|
+
* transaction when in autocommit mode.
|
|
217
|
+
*
|
|
208
218
|
* @param sql The SQL query string to execute.
|
|
209
219
|
* @param params Optional parameters to bind.
|
|
210
220
|
* @returns A Promise resolving to the first result row as an object, or undefined if no rows.
|
|
211
221
|
* @throws QuereusError on failure.
|
|
212
222
|
*/
|
|
213
|
-
get(sql, params) {
|
|
223
|
+
async get(sql, params) {
|
|
224
|
+
this.checkOpen();
|
|
214
225
|
const stmt = this.prepare(sql, params);
|
|
215
|
-
|
|
226
|
+
try {
|
|
227
|
+
return await stmt.get(params);
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
await stmt.finalize();
|
|
231
|
+
}
|
|
216
232
|
}
|
|
217
233
|
/**
|
|
218
234
|
* Executes one or more SQL statements directly.
|
|
@@ -221,12 +237,78 @@ export class Database {
|
|
|
221
237
|
* @returns A Promise resolving when execution completes.
|
|
222
238
|
* @throws QuereusError on failure.
|
|
223
239
|
*/
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// Statement Execution - Core Infrastructure
|
|
242
|
+
// ============================================================================
|
|
243
|
+
/**
|
|
244
|
+
* Acquires the execution mutex and returns a release function.
|
|
245
|
+
* All statement execution is serialized through this mutex to prevent
|
|
246
|
+
* concurrent transactions from interfering with each other.
|
|
247
|
+
* This matches SQLite's behavior of serializing database access.
|
|
248
|
+
* @internal
|
|
249
|
+
*/
|
|
250
|
+
async _acquireExecMutex() {
|
|
251
|
+
const previousMutex = this.execMutex;
|
|
252
|
+
let releaseMutex;
|
|
253
|
+
this.execMutex = new Promise(resolve => {
|
|
254
|
+
releaseMutex = resolve;
|
|
255
|
+
});
|
|
256
|
+
await previousMutex;
|
|
257
|
+
return releaseMutex;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Unified execution wrapper that handles mutex acquisition and implicit transactions.
|
|
261
|
+
* All public execution methods should use this to ensure consistent behavior.
|
|
262
|
+
* @internal
|
|
263
|
+
*/
|
|
264
|
+
async _executeWithProtection(options, executor) {
|
|
265
|
+
let releaseMutex;
|
|
266
|
+
try {
|
|
267
|
+
// Acquire mutex if requested
|
|
268
|
+
if (options.acquireMutex) {
|
|
269
|
+
releaseMutex = await this._acquireExecMutex();
|
|
270
|
+
}
|
|
271
|
+
// Determine if we need an implicit transaction
|
|
272
|
+
const needsImplicit = options.manageTransaction && this.isAutocommit;
|
|
273
|
+
let executionError = null;
|
|
274
|
+
let result;
|
|
275
|
+
try {
|
|
276
|
+
if (needsImplicit) {
|
|
277
|
+
await this._beginImplicit();
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
result = await executor();
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
284
|
+
executionError = error instanceof QuereusError ? error : new QuereusError(error.message, StatusCode.ERROR, error);
|
|
285
|
+
throw executionError;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
if (needsImplicit) {
|
|
290
|
+
if (executionError) {
|
|
291
|
+
await this._rollbackImplicit();
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
await this._commitImplicit();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
if (releaseMutex) {
|
|
302
|
+
releaseMutex();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
224
306
|
/**
|
|
225
307
|
* @internal
|
|
226
|
-
* Executes a single AST statement
|
|
227
|
-
* Used
|
|
308
|
+
* Executes a single AST statement. Does not manage mutex or transactions.
|
|
309
|
+
* Used as the innermost execution primitive.
|
|
228
310
|
*/
|
|
229
|
-
async
|
|
311
|
+
async _executeSingleStatement(statementAst, params) {
|
|
230
312
|
const plan = this._buildPlan([statementAst], params);
|
|
231
313
|
if (plan.statements.length === 0)
|
|
232
314
|
return; // No-op for this AST
|
|
@@ -245,54 +327,81 @@ export class Database {
|
|
|
245
327
|
};
|
|
246
328
|
await scheduler.run(runtimeCtx);
|
|
247
329
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
330
|
+
/**
|
|
331
|
+
* @internal
|
|
332
|
+
* Executes a batch of AST statements sequentially.
|
|
333
|
+
* Does not manage mutex or transactions - caller must handle that.
|
|
334
|
+
*/
|
|
335
|
+
async _executeStatementBatch(batch, params) {
|
|
336
|
+
for (const statementAst of batch) {
|
|
337
|
+
await this._executeSingleStatement(statementAst, params);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Parses SQL into a statement batch.
|
|
342
|
+
*/
|
|
343
|
+
_parseSql(sql) {
|
|
251
344
|
const parser = new Parser();
|
|
252
|
-
let batch;
|
|
253
345
|
try {
|
|
254
|
-
|
|
346
|
+
return parser.parseAll(sql);
|
|
255
347
|
}
|
|
256
348
|
catch (e) {
|
|
257
349
|
if (e instanceof ParseError)
|
|
258
350
|
throw new QuereusError(`Parse error: ${e.message}`, StatusCode.ERROR, e);
|
|
259
351
|
throw e;
|
|
260
352
|
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Determines if a statement batch contains explicit transaction control.
|
|
356
|
+
*/
|
|
357
|
+
_hasExplicitTransactionControl(batch) {
|
|
358
|
+
return batch.some(ast => ast.type === 'begin' || ast.type === 'commit' || ast.type === 'rollback' || ast.type === 'savepoint' || ast.type === 'release');
|
|
359
|
+
}
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// Statement Execution - Public API
|
|
362
|
+
// ============================================================================
|
|
363
|
+
/**
|
|
364
|
+
* Executes one or more SQL statements directly.
|
|
365
|
+
* Statements are serialized through the execution mutex and wrapped in an
|
|
366
|
+
* implicit transaction when in autocommit mode (unless they contain explicit
|
|
367
|
+
* transaction control statements).
|
|
368
|
+
*
|
|
369
|
+
* @param sql The SQL string(s) to execute.
|
|
370
|
+
* @param params Optional parameters to bind.
|
|
371
|
+
* @returns A Promise resolving when execution completes.
|
|
372
|
+
* @throws QuereusError on failure.
|
|
373
|
+
*/
|
|
374
|
+
async exec(sql, params) {
|
|
375
|
+
this.checkOpen();
|
|
376
|
+
log('Executing SQL block: %s', sql);
|
|
377
|
+
const batch = this._parseSql(sql);
|
|
261
378
|
if (batch.length === 0)
|
|
262
379
|
return;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
else {
|
|
289
|
-
await this._commitImplicitTransaction();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
if (executionError) {
|
|
294
|
-
throw executionError;
|
|
295
|
-
}
|
|
380
|
+
// Don't wrap in implicit transaction if batch has explicit transaction control
|
|
381
|
+
const hasExplicitTxn = this._hasExplicitTransactionControl(batch);
|
|
382
|
+
await this._executeWithProtection({ acquireMutex: true, manageTransaction: !hasExplicitTxn }, () => this._executeStatementBatch(batch, params));
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Execute SQL without acquiring the mutex.
|
|
386
|
+
* Used by runtime code (e.g., APPLY SCHEMA) that needs to execute
|
|
387
|
+
* nested SQL statements while already holding the mutex.
|
|
388
|
+
* @internal
|
|
389
|
+
*/
|
|
390
|
+
async _execWithinTransaction(sql, params) {
|
|
391
|
+
log('Executing nested SQL: %s', sql);
|
|
392
|
+
const batch = this._parseSql(sql);
|
|
393
|
+
if (batch.length === 0)
|
|
394
|
+
return;
|
|
395
|
+
// No mutex, no implicit transaction - we're already inside one
|
|
396
|
+
await this._executeWithProtection({ acquireMutex: false, manageTransaction: false }, () => this._executeStatementBatch(batch, params));
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Execute a function with mutex and optional implicit transaction management.
|
|
400
|
+
* This is exposed for Statement to use, avoiding code duplication.
|
|
401
|
+
* @internal
|
|
402
|
+
*/
|
|
403
|
+
async _runWithProtection(acquireMutex, manageTransaction, executor) {
|
|
404
|
+
return this._executeWithProtection({ acquireMutex, manageTransaction }, executor);
|
|
296
405
|
}
|
|
297
406
|
/**
|
|
298
407
|
* Registers a virtual table module.
|
|
@@ -717,6 +826,10 @@ export class Database {
|
|
|
717
826
|
* The underlying statement is automatically finalized when iteration completes
|
|
718
827
|
* or if an error occurs.
|
|
719
828
|
*
|
|
829
|
+
* The execution is serialized through the mutex and wrapped in an implicit
|
|
830
|
+
* transaction when in autocommit mode. The mutex is held for the entire
|
|
831
|
+
* iteration to ensure transactional consistency.
|
|
832
|
+
*
|
|
720
833
|
* @param sql The SQL query string to execute.
|
|
721
834
|
* @param params Optional parameters to bind (array for positional, object for named).
|
|
722
835
|
* @yields Each result row as an object (`Record<string, SqlValue>`).
|
|
@@ -737,8 +850,17 @@ export class Database {
|
|
|
737
850
|
*/
|
|
738
851
|
async *eval(sql, params) {
|
|
739
852
|
this.checkOpen();
|
|
853
|
+
// Acquire mutex for the entire iteration
|
|
854
|
+
const releaseMutex = await this._acquireExecMutex();
|
|
855
|
+
// Determine if we need implicit transaction
|
|
856
|
+
const needsImplicit = this.isAutocommit;
|
|
857
|
+
let transactionStarted = false;
|
|
740
858
|
let stmt = null;
|
|
741
859
|
try {
|
|
860
|
+
if (needsImplicit) {
|
|
861
|
+
await this._beginImplicit();
|
|
862
|
+
transactionStarted = true;
|
|
863
|
+
}
|
|
742
864
|
stmt = this.prepare(sql);
|
|
743
865
|
if (stmt.astBatch.length === 0) {
|
|
744
866
|
// No statements, yield nothing.
|
|
@@ -747,17 +869,19 @@ export class Database {
|
|
|
747
869
|
if (stmt.astBatch.length > 1) {
|
|
748
870
|
// Multi-statement batch: execute all but the last statement,
|
|
749
871
|
// then yield results from the last statement
|
|
750
|
-
const
|
|
751
|
-
const batch = parser.parseAll(sql);
|
|
872
|
+
const batch = this._parseSql(sql);
|
|
752
873
|
// Execute all statements except the last one
|
|
753
874
|
for (let i = 0; i < batch.length - 1; i++) {
|
|
754
|
-
await this.
|
|
875
|
+
await this._executeSingleStatement(batch[i], params);
|
|
755
876
|
}
|
|
756
877
|
// Now prepare and execute the last statement to yield its results
|
|
757
878
|
const lastStmt = new Statement(this, [batch[batch.length - 1]]);
|
|
758
879
|
this.statements.add(lastStmt);
|
|
759
880
|
try {
|
|
760
|
-
|
|
881
|
+
const names = lastStmt.getColumnNames();
|
|
882
|
+
for await (const row of lastStmt.iterateRows(params)) {
|
|
883
|
+
yield rowToObject(row, names);
|
|
884
|
+
}
|
|
761
885
|
}
|
|
762
886
|
finally {
|
|
763
887
|
await lastStmt.finalize();
|
|
@@ -765,13 +889,34 @@ export class Database {
|
|
|
765
889
|
}
|
|
766
890
|
else {
|
|
767
891
|
// Single statement: execute and yield results
|
|
768
|
-
|
|
892
|
+
const names = stmt.getColumnNames();
|
|
893
|
+
for await (const row of stmt.iterateRows(params)) {
|
|
894
|
+
yield rowToObject(row, names);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
// Commit implicit transaction on successful completion
|
|
898
|
+
if (transactionStarted) {
|
|
899
|
+
await this._commitImplicit();
|
|
900
|
+
transactionStarted = false;
|
|
769
901
|
}
|
|
770
902
|
}
|
|
903
|
+
catch (err) {
|
|
904
|
+
// Rollback on error
|
|
905
|
+
if (transactionStarted) {
|
|
906
|
+
await this._rollbackImplicit();
|
|
907
|
+
transactionStarted = false;
|
|
908
|
+
}
|
|
909
|
+
throw err;
|
|
910
|
+
}
|
|
771
911
|
finally {
|
|
772
912
|
if (stmt) {
|
|
773
913
|
await stmt.finalize();
|
|
774
914
|
}
|
|
915
|
+
// If we somehow exit without committing/rolling back, rollback
|
|
916
|
+
if (transactionStarted) {
|
|
917
|
+
await this._rollbackImplicit();
|
|
918
|
+
}
|
|
919
|
+
releaseMutex();
|
|
775
920
|
}
|
|
776
921
|
}
|
|
777
922
|
getPlan(sqlOrAst) {
|
|
@@ -909,7 +1054,7 @@ export class Database {
|
|
|
909
1054
|
const connection = this.activeConnections.get(connectionId);
|
|
910
1055
|
if (connection) {
|
|
911
1056
|
// Don't disconnect during implicit transactions - let the transaction coordinate
|
|
912
|
-
if (this.
|
|
1057
|
+
if (this.inImplicit) {
|
|
913
1058
|
debugLog(`Deferring disconnect of connection ${connectionId} until implicit transaction completes`);
|
|
914
1059
|
return;
|
|
915
1060
|
}
|
|
@@ -972,9 +1117,9 @@ export class Database {
|
|
|
972
1117
|
* Begin an implicit transaction and coordinate with virtual table connections
|
|
973
1118
|
*/
|
|
974
1119
|
/** @internal Begin an implicit transaction */
|
|
975
|
-
async
|
|
1120
|
+
async _beginImplicit() {
|
|
976
1121
|
debugLog("Database: Starting implicit transaction for multi-statement block.");
|
|
977
|
-
this.
|
|
1122
|
+
this.inImplicit = true;
|
|
978
1123
|
// Begin transaction on all active connections first
|
|
979
1124
|
const connections = this.getAllConnections();
|
|
980
1125
|
for (const connection of connections) {
|
|
@@ -994,7 +1139,7 @@ export class Database {
|
|
|
994
1139
|
* Commit an implicit transaction and coordinate with virtual table connections
|
|
995
1140
|
* @internal
|
|
996
1141
|
*/
|
|
997
|
-
async
|
|
1142
|
+
async _commitImplicit() {
|
|
998
1143
|
debugLog("Database: Committing implicit transaction.");
|
|
999
1144
|
try {
|
|
1000
1145
|
const connectionsToCommit = this.getAllConnections();
|
|
@@ -1029,7 +1174,7 @@ export class Database {
|
|
|
1029
1174
|
finally {
|
|
1030
1175
|
this.inTransaction = false;
|
|
1031
1176
|
this.isAutocommit = true;
|
|
1032
|
-
this.
|
|
1177
|
+
this.inImplicit = false;
|
|
1033
1178
|
this._clearChangeLog();
|
|
1034
1179
|
}
|
|
1035
1180
|
}
|
|
@@ -1109,9 +1254,10 @@ export class Database {
|
|
|
1109
1254
|
}
|
|
1110
1255
|
}
|
|
1111
1256
|
async executeViolationOnce(assertionName, sql) {
|
|
1112
|
-
const stmt =
|
|
1257
|
+
const stmt = this.prepare(sql);
|
|
1113
1258
|
try {
|
|
1114
|
-
|
|
1259
|
+
// Use raw iterateRows() to avoid mutex acquisition - we're already inside the mutex
|
|
1260
|
+
for await (const _ of stmt.iterateRows()) {
|
|
1115
1261
|
throw new QuereusError(`Integrity assertion failed: ${assertionName}`, StatusCode.CONSTRAINT);
|
|
1116
1262
|
}
|
|
1117
1263
|
}
|
|
@@ -1251,7 +1397,7 @@ export class Database {
|
|
|
1251
1397
|
* Rollback an implicit transaction and coordinate with virtual table connections
|
|
1252
1398
|
*/
|
|
1253
1399
|
/** @internal Rollback an implicit transaction */
|
|
1254
|
-
async
|
|
1400
|
+
async _rollbackImplicit() {
|
|
1255
1401
|
debugLog("Database: Rolling back implicit transaction.");
|
|
1256
1402
|
// Rollback all active connections
|
|
1257
1403
|
const connections = this.getAllConnections();
|
|
@@ -1268,7 +1414,7 @@ export class Database {
|
|
|
1268
1414
|
// Reset database state
|
|
1269
1415
|
this.inTransaction = false;
|
|
1270
1416
|
this.isAutocommit = true;
|
|
1271
|
-
this.
|
|
1417
|
+
this.inImplicit = false;
|
|
1272
1418
|
}
|
|
1273
1419
|
createBlockWithNewStatements(block, statements) {
|
|
1274
1420
|
return new BlockNode(block.scope, statements, block.parameters);
|