Skip to content

Commit 9f3f7dc

Browse files
author
DominicGBauer
committed
chore: folder structure changes
1 parent 9023a7b commit 9f3f7dc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1719
-1392
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ pubspec.lock
1212
test-db
1313
sqlite-autoconf-*
1414
doc
15+
16+
build

lib/sqlite3_common.dart

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Exports common Sqlite3 exports which are available in different environments.
2+
export 'package:sqlite3/common.dart';

lib/sqlite_async.dart

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
/// See [SqliteDatabase] as a starting point.
44
library;
55

6+
export 'src/common/abstract_open_factory.dart';
7+
export 'src/common/connection/sync_sqlite_connection.dart';
8+
export 'src/common/isolate_connection_factory.dart';
9+
export 'src/common/mutex.dart';
10+
export 'src/common/port_channel.dart';
11+
export 'src/common/sqlite_database.dart';
612
export 'src/isolate_connection_factory.dart';
713
export 'src/sqlite_connection.dart';
814
export 'src/sqlite_database.dart';
+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import 'dart:async';
2+
import 'package:meta/meta.dart';
3+
4+
import 'package:sqlite_async/sqlite3_common.dart' as sqlite;
5+
import 'package:sqlite_async/src/common/mutex.dart';
6+
import 'package:sqlite_async/src/sqlite_connection.dart';
7+
import 'package:sqlite_async/src/sqlite_options.dart';
8+
import 'package:sqlite_async/src/update_notification.dart';
9+
10+
/// Factory to create new SQLite database connections.
11+
///
12+
/// Since connections are opened in dedicated background isolates, this class
13+
/// must be safe to pass to different isolates.
14+
abstract class SqliteOpenFactory<Database extends sqlite.CommonDatabase> {
15+
String get path;
16+
17+
/// Opens a direct connection to the SQLite database
18+
FutureOr<Database> open(SqliteOpenOptions options);
19+
20+
/// Opens an asynchronous [SqliteConnection]
21+
FutureOr<SqliteConnection> openConnection(SqliteOpenOptions options);
22+
}
23+
24+
class SqliteOpenOptions {
25+
/// Whether this is the primary write connection for the database.
26+
final bool primaryConnection;
27+
28+
/// Whether this connection is read-only.
29+
final bool readOnly;
30+
31+
/// Mutex to use in [SqliteConnection]s
32+
final Mutex? mutex;
33+
34+
/// Name used in debug logs
35+
final String? debugName;
36+
37+
/// Stream of external update notifications
38+
final Stream<UpdateNotification>? updates;
39+
40+
const SqliteOpenOptions(
41+
{required this.primaryConnection,
42+
required this.readOnly,
43+
this.mutex,
44+
this.debugName,
45+
this.updates});
46+
47+
sqlite.OpenMode get openMode {
48+
if (primaryConnection) {
49+
return sqlite.OpenMode.readWriteCreate;
50+
} else if (readOnly) {
51+
return sqlite.OpenMode.readOnly;
52+
} else {
53+
return sqlite.OpenMode.readWrite;
54+
}
55+
}
56+
}
57+
58+
/// The default database factory.
59+
///
60+
/// This takes care of opening the database, and running PRAGMA statements
61+
/// to configure the connection.
62+
///
63+
/// Override the [open] method to customize the process.
64+
abstract class AbstractDefaultSqliteOpenFactory<
65+
Database extends sqlite.CommonDatabase>
66+
implements SqliteOpenFactory<Database> {
67+
@override
68+
final String path;
69+
final SqliteOptions sqliteOptions;
70+
71+
const AbstractDefaultSqliteOpenFactory(
72+
{required this.path,
73+
this.sqliteOptions = const SqliteOptions.defaults()});
74+
75+
List<String> pragmaStatements(SqliteOpenOptions options);
76+
77+
@protected
78+
79+
/// Opens a direct connection to a SQLite database connection
80+
FutureOr<Database> openDB(SqliteOpenOptions options);
81+
82+
@override
83+
84+
/// Opens a direct connection to a SQLite database connection
85+
/// and executes setup pragma statements to initialize the DB
86+
FutureOr<Database> open(SqliteOpenOptions options) async {
87+
var db = await openDB(options);
88+
89+
// Pragma statements don't have the same BUSY_TIMEOUT behavior as normal statements.
90+
// We add a manual retry loop for those.
91+
for (var statement in pragmaStatements(options)) {
92+
for (var tries = 0; tries < 30; tries++) {
93+
try {
94+
db.execute(statement);
95+
break;
96+
} on sqlite.SqliteException catch (e) {
97+
if (e.resultCode == sqlite.SqlError.SQLITE_BUSY && tries < 29) {
98+
continue;
99+
} else {
100+
rethrow;
101+
}
102+
}
103+
}
104+
}
105+
return db;
106+
}
107+
108+
@override
109+
110+
/// Opens an asynchronous [SqliteConnection] to a SQLite database
111+
/// and executes setup pragma statements to initialize the DB
112+
FutureOr<SqliteConnection> openConnection(SqliteOpenOptions options);
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import 'package:sqlite3/common.dart';
2+
import 'package:sqlite_async/src/common/mutex.dart';
3+
import 'package:sqlite_async/src/sqlite_connection.dart';
4+
import 'package:sqlite_async/src/sqlite_queries.dart';
5+
import 'package:sqlite_async/src/update_notification.dart';
6+
7+
/// A simple "synchronous" connection which provides the async SqliteConnection
8+
/// implementation using a synchronous SQLite connection
9+
class SyncSqliteConnection extends SqliteConnection with SqliteQueries {
10+
final CommonDatabase db;
11+
late Mutex mutex;
12+
@override
13+
late final Stream<UpdateNotification> updates;
14+
15+
bool _closed = false;
16+
17+
SyncSqliteConnection(this.db, Mutex m) {
18+
mutex = m.open();
19+
updates = db.updates.map(
20+
(event) {
21+
return UpdateNotification({event.tableName});
22+
},
23+
);
24+
}
25+
26+
@override
27+
Future<T> readLock<T>(Future<T> Function(SqliteReadContext tx) callback,
28+
{Duration? lockTimeout, String? debugContext}) {
29+
return mutex.lock(() => callback(SyncReadContext(db)),
30+
timeout: lockTimeout);
31+
}
32+
33+
@override
34+
Future<T> writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
35+
{Duration? lockTimeout, String? debugContext}) {
36+
return mutex.lock(() => callback(SyncWriteContext(db)),
37+
timeout: lockTimeout);
38+
}
39+
40+
@override
41+
Future<void> close() async {
42+
_closed = true;
43+
return db.dispose();
44+
}
45+
46+
@override
47+
bool get closed => _closed;
48+
49+
@override
50+
Future<bool> getAutoCommit() async {
51+
return db.autocommit;
52+
}
53+
}
54+
55+
class SyncReadContext implements SqliteReadContext {
56+
CommonDatabase db;
57+
58+
SyncReadContext(this.db);
59+
60+
@override
61+
Future<T> computeWithDatabase<T>(
62+
Future<T> Function(CommonDatabase db) compute) {
63+
return compute(db);
64+
}
65+
66+
@override
67+
Future<Row> get(String sql, [List<Object?> parameters = const []]) async {
68+
return db.select(sql, parameters).first;
69+
}
70+
71+
@override
72+
Future<ResultSet> getAll(String sql,
73+
[List<Object?> parameters = const []]) async {
74+
return db.select(sql, parameters);
75+
}
76+
77+
@override
78+
Future<Row?> getOptional(String sql,
79+
[List<Object?> parameters = const []]) async {
80+
final rows = await getAll(sql, parameters);
81+
return rows.isEmpty ? null : rows.first;
82+
}
83+
84+
@override
85+
bool get closed => false;
86+
87+
@override
88+
Future<bool> getAutoCommit() async {
89+
return db.autocommit;
90+
}
91+
}
92+
93+
class SyncWriteContext extends SyncReadContext implements SqliteWriteContext {
94+
SyncWriteContext(super.db);
95+
96+
@override
97+
Future<ResultSet> execute(String sql,
98+
[List<Object?> parameters = const []]) async {
99+
return db.select(sql, parameters);
100+
}
101+
102+
@override
103+
Future<void> executeBatch(
104+
String sql, List<List<Object?>> parameterSets) async {
105+
return computeWithDatabase((db) async {
106+
final statement = db.prepare(sql, checkNoTail: true);
107+
try {
108+
for (var parameters in parameterSets) {
109+
statement.execute(parameters);
110+
}
111+
} finally {
112+
statement.dispose();
113+
}
114+
});
115+
}
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'dart:async';
2+
import 'package:sqlite_async/sqlite3_common.dart' as sqlite;
3+
import 'package:sqlite_async/src/common/mutex.dart';
4+
import 'package:sqlite_async/src/common/abstract_open_factory.dart';
5+
import 'package:sqlite_async/src/impl/isolate_connection_factory_impl.dart';
6+
import 'package:sqlite_async/src/sqlite_connection.dart';
7+
import 'port_channel.dart';
8+
9+
mixin IsolateOpenFactoryMixin<Database extends sqlite.CommonDatabase> {
10+
AbstractDefaultSqliteOpenFactory<Database> get openFactory;
11+
12+
/// Opens a synchronous sqlite.Database directly in the current isolate.
13+
///
14+
/// This gives direct access to the database, but:
15+
/// 1. No app-level locking is performed automatically. Transactions may fail
16+
/// with SQLITE_BUSY if another isolate is using the database at the same time.
17+
/// 2. Other connections are not notified of any updates to tables made within
18+
/// this connection.
19+
FutureOr<Database> openRawDatabase({bool readOnly = false}) async {
20+
return openFactory
21+
.open(SqliteOpenOptions(primaryConnection: false, readOnly: readOnly));
22+
}
23+
}
24+
25+
/// A connection factory that can be passed to different isolates.
26+
abstract class IsolateConnectionFactory<Database extends sqlite.CommonDatabase>
27+
with IsolateOpenFactoryMixin {
28+
Mutex get mutex;
29+
30+
SerializedPortClient get upstreamPort;
31+
32+
factory IsolateConnectionFactory(
33+
{required openFactory,
34+
required mutex,
35+
required SerializedPortClient upstreamPort}) {
36+
return IsolateConnectionFactoryImpl(
37+
openFactory: openFactory,
38+
mutex: mutex,
39+
upstreamPort: upstreamPort) as IsolateConnectionFactory<Database>;
40+
}
41+
42+
/// Open a new SqliteConnection.
43+
///
44+
/// This opens a single connection in a background execution isolate.
45+
SqliteConnection open({String? debugName, bool readOnly = false});
46+
}

lib/src/common/mutex.dart

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'package:sqlite_async/src/impl/mutex_impl.dart';
2+
3+
abstract class Mutex {
4+
factory Mutex() {
5+
return MutexImpl();
6+
}
7+
8+
/// timeout is a timeout for acquiring the lock, not for the callback
9+
Future<T> lock<T>(Future<T> Function() callback, {Duration? timeout});
10+
11+
/// Use [open] to get a [AbstractMutex] instance.
12+
/// This is mainly used for shared mutexes
13+
Mutex open() {
14+
return this;
15+
}
16+
17+
/// Release resources used by the Mutex.
18+
///
19+
/// Subsequent calls to [lock] may fail, or may never call the callback.
20+
Future<void> close();
21+
}
22+
23+
class LockError extends Error {
24+
final String message;
25+
26+
LockError(this.message);
27+
28+
@override
29+
String toString() {
30+
return 'LockError: $message';
31+
}
32+
}
File renamed without changes.

0 commit comments

Comments
 (0)