executeStmt method
Implementation
Future<QueryResult> executeStmt(Statement stmt) async {
// Statements that span a transaction boundary always need exclusive
// access; otherwise SELECT-ish statements take the shared arm.
final wantsWrite = inTransaction ||
_isMutation(stmt) ||
stmt is BeginStmt ||
stmt is CommitStmt ||
stmt is RollbackStmt ||
stmt is SavepointStmt ||
stmt is ReleaseSavepointStmt ||
stmt is RollbackToSavepointStmt;
Future<QueryResult> body() async {
if (_readOnlySnapshot && _isMutation(stmt)) {
throw StateError(
'Cannot mutate inside a read-only snapshot transaction',
);
}
final cb = authorizer;
if (cb != null) {
final outcome = cb(stmt, _statementTable(stmt));
if (outcome == AuthorizerResult.deny) {
throw StateError('not authorized');
}
if (outcome == AuthorizerResult.ignore) {
return QueryResult.message('ignored by authorizer');
}
}
_executionStack.add(this);
final prevConnState = connStateLookup;
connStateLookup = (which) {
switch (which) {
case 'last_insert_rowid':
return _lastInsertRowid;
case 'changes':
return _changesCount;
case 'total_changes':
return _totalChangesCount;
default:
return 0;
}
};
QueryResult result;
try {
// Paged-table fast path: a `CREATE TABLE … USING paged` lives
// in [_pagedTables] rather than [_tables], and statements that
// target one of those names use the async PagedTable API. Both
// need to run before the synchronous [_dispatch] so they can
// await disk I/O.
final paged = await _maybeRunPagedStmt(stmt);
if (paged != null) {
result = paged;
} else {
// V48: async-only PRAGMAs (currently just `fts5_warm`) also
// pre-empt `_dispatch` — they need to await work the sync
// pragma handler can't.
final asyncPragma = await _maybeRunAsyncPragma(stmt);
if (asyncPragma != null) {
result = asyncPragma;
} else {
result = _dispatch(stmt);
}
}
} finally {
_executionStack.removeLast();
connStateLookup = prevConnState;
// Drain any paged commit/rollback queued by a sync _commit /
// _rollback. Rollbacks always run before commits — and run
// unconditionally, including on the exception path — so a
// deferred-FK-failed COMMIT (which calls _rollback internally
// and rethrows) still undoes the paged side.
if (_pendingPagedRollback.isNotEmpty) {
final pending = List<PagedTable>.from(_pendingPagedRollback);
_pendingPagedRollback.clear();
for (final pt in pending) {
try {
await pt.rollback();
} catch (_) {
/* best-effort */
}
}
}
if (_pendingPagedCommit.isNotEmpty) {
final pending = List<PagedTable>.from(_pendingPagedCommit);
_pendingPagedCommit.clear();
for (final pt in pending) {
await pt.commit();
}
}
}
if (_isMutation(stmt)) {
// Track changes()/total_changes() at the SQL function layer.
if (stmt is InsertStmt || stmt is UpdateStmt || stmt is DeleteStmt) {
_changesCount = result.affected;
_totalChangesCount += result.affected;
}
// Conservatively drop FTS5 caches for the statement's target
// table; the next ranking call will rebuild on demand.
final tname = _statementTable(stmt);
if (tname != null) {
_invalidateFts5(tname);
_invalidateRtree(tname);
_invalidateVectorIndexes(stmt, tname);
final t = _tables[tname];
if (t != null) _refreshPartialIndexes(t);
}
}
if (stmt is CommitStmt) {
await _persist();
return result;
}
if (!inTransaction && _isMutation(stmt)) {
await _persist();
}
return result;
}
return wantsWrite ? _lock.write(body) : _lock.read(body);
}