Skip to content

Commit 5be660f

Browse files
committed
Refactor APIs to lock() and transaction().
1 parent 0d44716 commit 5be660f

File tree

6 files changed

+75
-18
lines changed

6 files changed

+75
-18
lines changed

lib/src/connection_pool.dart

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
132132
readOnly: false,
133133
openFactory: _factory);
134134
return _runZoned(() {
135+
// ignore: deprecated_member_use_from_same_package
135136
return _writeConnection!.writeLock(callback,
136137
lockTimeout: lockTimeout, debugContext: debugContext);
137138
}, debugContext: debugContext ?? 'execute()');

lib/src/sqlite_connection.dart

+34-4
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,26 @@ abstract class SqliteWriteContext extends SqliteReadContext {
7272

7373
/// Abstract class representing a connection to the SQLite database.
7474
abstract class SqliteConnection extends SqliteWriteContext {
75+
/// Open a transaction.
76+
///
77+
/// Opens a read-write transaction by default. Set [readOnly] to open a
78+
/// read-only transaction.
79+
///
80+
/// Only one read-write transaction can execute against the database at a time.
81+
///
82+
/// Statements within the transaction must be done on the provided
83+
/// [SqliteWriteContext] - attempting statements on the [SqliteConnection]
84+
/// instance will error.
85+
Future<T> transaction<T>(Future<T> Function(SqliteWriteContext tx) callback,
86+
{bool? readOnly});
87+
7588
/// Open a read-only transaction.
7689
///
7790
/// Statements within the transaction must be done on the provided
7891
/// [SqliteReadContext] - attempting statements on the [SqliteConnection]
7992
/// instance will error.
93+
@Deprecated(
94+
'Use [transaction(callback)] instead, and set lockTimeout on [SqliteOpenOptions].')
8095
Future<T> readTransaction<T>(
8196
Future<T> Function(SqliteReadContext tx) callback,
8297
{Duration? lockTimeout});
@@ -89,6 +104,8 @@ abstract class SqliteConnection extends SqliteWriteContext {
89104
/// Statements within the transaction must be done on the provided
90105
/// [SqliteWriteContext] - attempting statements on the [SqliteConnection]
91106
/// instance will error.
107+
@Deprecated(
108+
'Use [transaction(callback)] instead, and set lockTimeout on [SqliteOpenOptions].')
92109
Future<T> writeTransaction<T>(
93110
Future<T> Function(SqliteWriteContext tx) callback,
94111
{Duration? lockTimeout});
@@ -102,18 +119,31 @@ abstract class SqliteConnection extends SqliteWriteContext {
102119
{List<Object?> parameters = const [],
103120
Duration throttle = const Duration(milliseconds: 30)});
104121

105-
/// Takes a read lock, without starting a transaction.
122+
/// Takes a read lock on this connection, without starting a transaction.
106123
///
107-
/// In most cases, [readTransaction] should be used instead.
124+
/// This is a low-level API. In most cases, [transaction] should be used instead.
125+
@Deprecated('Use [lock] instead, and set lockTimeout on [SqliteOpenOptions].')
108126
Future<T> readLock<T>(Future<T> Function(SqliteReadContext tx) callback,
109127
{Duration? lockTimeout, String? debugContext});
110128

111-
/// Takes a global lock, without starting a transaction.
129+
/// Takes a global write lock, without starting a transaction.
112130
///
113-
/// In most cases, [writeTransaction] should be used instead.
131+
/// This is a low-level API. In most cases, [transaction] should be used instead.
132+
@Deprecated('Use [lock] instead, and set lockTimeout on [SqliteOpenOptions].')
114133
Future<T> writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
115134
{Duration? lockTimeout, String? debugContext});
116135

136+
/// Lock a connection for exclusive usage.
137+
///
138+
/// When using a connection pool, use [readOnly] to select a read connection.
139+
/// In other contexts, [readOnly] has no effect.
140+
///
141+
/// This is a low-level API. In most cases, [transaction] should be used instead.
142+
///
143+
/// Any direct query methods such as [getAll] or [execute] uses [lock] internally.
144+
Future<T> lock<T>(Future<T> Function(SqliteWriteContext tx) callback,
145+
{bool? readOnly, String? debugContext});
146+
117147
Future<void> close();
118148

119149
/// Returns true if the connection is closed

lib/src/sqlite_migrations.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class SqliteMigrations {
9090
Future<void> migrate(SqliteConnection db) async {
9191
_validateCreateDatabase();
9292

93-
await db.writeTransaction((tx) async {
93+
await db.transaction((tx) async {
9494
await tx.execute(
9595
'CREATE TABLE IF NOT EXISTS $migrationTable(id INTEGER PRIMARY KEY, down_migrations TEXT)');
9696

lib/src/sqlite_queries.dart

+35-9
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,40 @@ import 'update_notification.dart';
66

77
/// Mixin to provide default query functionality.
88
///
9-
/// Classes using this need to implement [SqliteConnection.readLock]
10-
/// and [SqliteConnection.writeLock].
9+
/// Classes using this need to implement [SqliteConnection.lock].
1110
mixin SqliteQueries implements SqliteWriteContext, SqliteConnection {
1211
/// Broadcast stream that is notified of any table updates
1312
Stream<UpdateNotification>? get updates;
1413

1514
@override
1615
Future<sqlite.ResultSet> execute(String sql,
1716
[List<Object?> parameters = const []]) async {
18-
return writeLock((ctx) async {
17+
return lock((ctx) async {
1918
return ctx.execute(sql, parameters);
2019
}, debugContext: 'execute()');
2120
}
2221

2322
@override
2423
Future<sqlite.ResultSet> getAll(String sql,
2524
[List<Object?> parameters = const []]) {
26-
return readLock((ctx) async {
25+
return lock((ctx) async {
2726
return ctx.getAll(sql, parameters);
28-
}, debugContext: 'getAll()');
27+
}, readOnly: true, debugContext: 'getAll()');
2928
}
3029

3130
@override
3231
Future<sqlite.Row> get(String sql, [List<Object?> parameters = const []]) {
33-
return readLock((ctx) async {
32+
return lock((ctx) async {
3433
return ctx.get(sql, parameters);
35-
}, debugContext: 'get()');
34+
}, readOnly: true, debugContext: 'get()');
3635
}
3736

3837
@override
3938
Future<sqlite.Row?> getOptional(String sql,
4039
[List<Object?> parameters = const []]) {
41-
return readLock((ctx) async {
40+
return lock((ctx) async {
4241
return ctx.getOptional(sql, parameters);
43-
}, debugContext: 'getOptional()');
42+
}, readOnly: true, debugContext: 'getOptional()');
4443
}
4544

4645
@override
@@ -102,6 +101,7 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection {
102101
Future<T> readTransaction<T>(
103102
Future<T> Function(SqliteReadContext tx) callback,
104103
{Duration? lockTimeout}) async {
104+
// ignore: deprecated_member_use_from_same_package
105105
return readLock((ctx) async {
106106
return await internalReadTransaction(ctx, callback);
107107
}, lockTimeout: lockTimeout, debugContext: 'readTransaction()');
@@ -111,6 +111,7 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection {
111111
Future<T> writeTransaction<T>(
112112
Future<T> Function(SqliteWriteContext tx) callback,
113113
{Duration? lockTimeout}) async {
114+
// ignore: deprecated_member_use_from_same_package
114115
return writeLock((ctx) async {
115116
return await internalWriteTransaction(ctx, callback);
116117
}, lockTimeout: lockTimeout, debugContext: 'writeTransaction()');
@@ -140,4 +141,29 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection {
140141
return tx.executeBatch(sql, parameterSets);
141142
});
142143
}
144+
145+
@override
146+
Future<T> lock<T>(Future<T> Function(SqliteWriteContext tx) callback,
147+
{bool? readOnly, String? debugContext}) {
148+
if (readOnly == true) {
149+
// ignore: deprecated_member_use_from_same_package
150+
return readLock((ctx) => callback(ctx as SqliteWriteContext),
151+
debugContext: debugContext);
152+
} else {
153+
// ignore: deprecated_member_use_from_same_package
154+
return writeLock(callback, debugContext: debugContext);
155+
}
156+
}
157+
158+
@override
159+
Future<T> transaction<T>(Future<T> Function(SqliteWriteContext tx) callback,
160+
{bool? readOnly}) {
161+
if (readOnly == true) {
162+
// ignore: deprecated_member_use_from_same_package
163+
return readTransaction((ctx) => callback(ctx as SqliteWriteContext));
164+
} else {
165+
// ignore: deprecated_member_use_from_same_package
166+
return writeTransaction(callback);
167+
}
168+
}
143169
}

scripts/benchmark.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ List<SqliteBenchmark> benchmarks = [
4646
}
4747
});
4848
}, maxBatchSize: 200000, enabled: false),
49-
SqliteBenchmark('writeLock in isolate',
49+
SqliteBenchmark('lock in isolate',
5050
(SqliteDatabase db, List<List<String>> parameters) async {
5151
var factory = db.isolateConnectionFactory();
5252
var len = parameters.length;
5353
await Isolate.run(() async {
5454
final db = factory.open();
5555
for (var i = 0; i < len; i++) {
56-
await db.writeLock((tx) async {});
56+
await db.lock((tx) async {});
5757
}
5858
await db.close();
5959
});

test/util.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import 'package:sqlite3/sqlite3.dart' as sqlite;
99
import 'package:sqlite_async/sqlite_async.dart';
1010
import 'package:test_api/src/backend/invoker.dart';
1111

12-
const defaultSqlitePath = 'libsqlite3.so.0';
13-
// const defaultSqlitePath = './sqlite-autoconf-3410100/.libs/libsqlite3.so.0';
12+
// const defaultSqlitePath = 'libsqlite3.so.0';
13+
const defaultSqlitePath = './sqlite-autoconf-3410100/.libs/libsqlite3.so.0';
1414

1515
class TestSqliteOpenFactory extends DefaultSqliteOpenFactory {
1616
String sqlitePath;

0 commit comments

Comments
 (0)