1
1
import 'dart:async' ;
2
2
3
3
import 'package:drift/backends.dart' ;
4
- import 'package:drift_sqlite_async /src/transaction_executor .dart' ;
4
+ import 'package:drift /src/runtime/query_builder/query_builder .dart' ;
5
5
import 'package:sqlite_async/sqlite3_common.dart' ;
6
6
import 'package:sqlite_async/sqlite_async.dart' ;
7
7
8
- class _SqliteAsyncDelegate extends DatabaseDelegate {
8
+ // Ends with " RETURNING *", or starts with insert/update/delete.
9
+ // Drift-generated queries will always have the RETURNING *.
10
+ // The INSERT/UPDATE/DELETE check is for custom queries, and is not exhaustive.
11
+ final _returningCheck = RegExp (r'( RETURNING \*;?$)|(^(INSERT|UPDATE|DELETE))' ,
12
+ caseSensitive: false );
13
+
14
+ class _SqliteAsyncDelegate extends _SqliteAsyncQueryDelegate
15
+ implements DatabaseDelegate {
9
16
final SqliteConnection db;
10
17
bool _closed = false ;
11
18
12
- _SqliteAsyncDelegate (this .db);
19
+ _SqliteAsyncDelegate (this .db) : super (db, db.writeLock);
20
+
21
+ bool isInTransaction = false ; // unused
13
22
14
23
@override
15
24
late final DbVersionDelegate versionDelegate =
@@ -18,18 +27,11 @@ class _SqliteAsyncDelegate extends DatabaseDelegate {
18
27
// Not used - we override beginTransaction() with SqliteAsyncTransactionExecutor for more control.
19
28
@override
20
29
late final TransactionDelegate transactionDelegate =
21
- const NoTransactionDelegate ( );
30
+ _SqliteAsyncTransactionDelegate (db );
22
31
23
32
@override
24
33
bool get isOpen => ! db.closed && ! _closed;
25
34
26
- // Ends with " RETURNING *", or starts with insert/update/delete.
27
- // Drift-generated queries will always have the RETURNING *.
28
- // The INSERT/UPDATE/DELETE check is for custom queries, and is not exhaustive.
29
- final _returningCheck = RegExp (
30
- r'( RETURNING \*;?$)|(^(INSERT|UPDATE|DELETE))' ,
31
- caseSensitive: false );
32
-
33
35
@override
34
36
Future <void > open (QueryExecutorUser user) async {
35
37
// Workaround - this ensures the db is open
@@ -42,9 +44,30 @@ class _SqliteAsyncDelegate extends DatabaseDelegate {
42
44
_closed = true ;
43
45
}
44
46
47
+ @override
48
+ void notifyDatabaseOpened (OpeningDetails details) {
49
+ // Unused
50
+ }
51
+ }
52
+
53
+ class _SqliteAsyncQueryDelegate extends QueryDelegate {
54
+ final SqliteWriteContext _context;
55
+ final Future <T > Function <T >(
56
+ Future <T > Function (SqliteWriteContext tx) callback)? _writeLock;
57
+
58
+ _SqliteAsyncQueryDelegate (this ._context, this ._writeLock);
59
+
60
+ Future <T > writeLock <T >(Future <T > Function (SqliteWriteContext tx) callback) {
61
+ if (_writeLock case var writeLock? ) {
62
+ return writeLock.call (callback);
63
+ } else {
64
+ return callback (_context);
65
+ }
66
+ }
67
+
45
68
@override
46
69
Future <void > runBatched (BatchedStatements statements) async {
47
- return db. writeLock ((tx) async {
70
+ return writeLock ((tx) async {
48
71
// sqlite_async's batch functionality doesn't have enough flexibility to support
49
72
// this with prepared statements yet.
50
73
for (final arg in statements.arguments) {
@@ -56,12 +79,12 @@ class _SqliteAsyncDelegate extends DatabaseDelegate {
56
79
57
80
@override
58
81
Future <void > runCustom (String statement, List <Object ?> args) {
59
- return db .execute (statement, args);
82
+ return _context .execute (statement, args);
60
83
}
61
84
62
85
@override
63
86
Future <int > runInsert (String statement, List <Object ?> args) async {
64
- return db. writeLock ((tx) async {
87
+ return writeLock ((tx) async {
65
88
await tx.execute (statement, args);
66
89
final row = await tx.get ('SELECT last_insert_rowid() as row_id' );
67
90
return row['row_id' ];
@@ -77,24 +100,38 @@ class _SqliteAsyncDelegate extends DatabaseDelegate {
77
100
// This takes write lock, so we want to avoid it for plain select statements.
78
101
// This is not an exhaustive check, but should cover all Drift-generated queries using
79
102
// `runSelect()`.
80
- result = await db .execute (statement, args);
103
+ result = await _context .execute (statement, args);
81
104
} else {
82
105
// Plain SELECT statement - use getAll() to avoid using a write lock.
83
- result = await db .getAll (statement, args);
106
+ result = await _context .getAll (statement, args);
84
107
}
85
108
return QueryResult (result.columnNames, result.rows);
86
109
}
87
110
88
111
@override
89
112
Future <int > runUpdate (String statement, List <Object ?> args) {
90
- return db. writeLock ((tx) async {
113
+ return writeLock ((tx) async {
91
114
await tx.execute (statement, args);
92
115
final row = await tx.get ('SELECT changes() as changes' );
93
116
return row['changes' ];
94
117
});
95
118
}
96
119
}
97
120
121
+ class _SqliteAsyncTransactionDelegate extends SupportedTransactionDelegate {
122
+ final SqliteConnection _db;
123
+
124
+ _SqliteAsyncTransactionDelegate (this ._db);
125
+
126
+ @override
127
+ Future <void > startTransaction (Future Function (QueryDelegate p1) run) async {
128
+ await _db.writeTransaction ((context) async {
129
+ final delegate = _SqliteAsyncQueryDelegate (context, null );
130
+ return run (delegate);
131
+ });
132
+ }
133
+ }
134
+
98
135
class _SqliteAsyncVersionDelegate extends DynamicVersionDelegate {
99
136
final SqliteConnection _db;
100
137
@@ -137,9 +174,4 @@ class SqliteAsyncQueryExecutor extends DelegatedDatabase {
137
174
138
175
@override
139
176
bool get isSequential => false ;
140
-
141
- @override
142
- TransactionExecutor beginTransaction () {
143
- return SqliteAsyncTransactionExecutor (db);
144
- }
145
177
}
0 commit comments