Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/drift_sqlite_async/lib/src/executor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:async';

import 'package:drift/backends.dart';
import 'package:drift/drift.dart';
import 'package:sqlite_async/sqlite3_common.dart';
import 'package:sqlite3/common.dart';
import 'package:sqlite_async/sqlite_async.dart';

// Ends with " RETURNING *", or starts with insert/update/delete.
Expand Down
2 changes: 1 addition & 1 deletion packages/drift_sqlite_async/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ environment:

dependencies:
drift: ">=2.28.0 <3.0.0"
sqlite3: ^3.2.0
sqlite_async: ^0.14.0-wip

dev_dependencies:
build_runner: ^2.4.8
drift_dev: ">=2.28.0 <3.0.0"
glob: ^2.1.2
lints: ^6.0.0
sqlite3: ^3.2.0
test: ^1.25.2
test_api: ^0.7.0

Expand Down
33 changes: 2 additions & 31 deletions packages/drift_sqlite_async/test/utils/test_utils.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
import 'dart:io';

import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';
import 'package:sqlite_async/sqlite3_common.dart';
import 'package:sqlite_async/sqlite_async.dart';
import 'package:test_api/src/backend/invoker.dart';

class TestSqliteOpenFactory extends DefaultSqliteOpenFactory {
TestSqliteOpenFactory({
required super.path,
super.sqliteOptions,
});

@override
CommonDatabase open(SqliteOpenOptions options) {
final db = super.open(options);

return db;
}
}

DefaultSqliteOpenFactory testFactory({String? path}) {
return TestSqliteOpenFactory(path: path ?? dbPath());
}

Future<SqliteDatabase> setupDatabase({String? path}) async {
final db = SqliteDatabase.withFactory(testFactory(path: path));
final db =
SqliteDatabase.withFactory(SqliteOpenFactory(path: path ?? dbPath()));
await db.initialize();
return db;
}
Expand All @@ -48,15 +28,6 @@ Future<void> cleanDb({required String path}) async {
}
}

List<String> findSqliteLibraries() {
var glob = Glob('sqlite-*/.libs/libsqlite3.so');
List<String> sqlites = [
'libsqlite3.so.0',
for (var sqlite in glob.listSync()) sqlite.path
];
return sqlites;
}

String dbPath() {
final test = Invoker.current!.liveTest;
var testName = test.test.name;
Expand Down
10 changes: 10 additions & 0 deletions packages/sqlite_async/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
- __Breaking__: Rewrite the native connection pool implementation.
- Remove isolate connection factories. Simply open the same database on another isolate, it's safe to do so now.
- It is no longer possible to register user-defined functions in Dart. Extensions providing functions in native code can still be used.
- __Breaking__: Remove `AbstractDefaultSqliteOpenFactory` and `DefaultSqliteOpenFactory`. Use `SqliteOpenFactory` instead.
To provide a custom open factory, import `NativeSqliteOpenFactory` or `WebSqliteOpenFactory` with a platform-specific
import and extend those classes.
- __Breaking__: Remove the `maxReaders` parameter on `SqliteDatabase`. Set that parameter on `SqliteOptions` instead.
- __Breaking__: Remove libraries exporting the `sqlite3` package:
- Instead of `package:sqlite_async/sqlite3.dart`, import `package:sqlite3/sqlite3.dart`.
- Instead of `package:sqlite_async/sqlite3_common.dart`, import `package:sqlite3/common.dart`.
- Instead of `package:sqlite_async/sqlite3_wasm.dart`, import `package:sqlite3/wasm.dart`.
- Instead of `package:sqlite_async/sqlite3_web.dart`, import `package:sqlite3_web/sqlite3_web.dart`.


## 0.13.1

Expand Down
2 changes: 1 addition & 1 deletion packages/sqlite_async/example/web/worker.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:sqlite_async/sqlite3_web.dart';
import 'package:sqlite3_web/sqlite3_web.dart';
import 'package:sqlite_async/sqlite3_web_worker.dart';

void main() {
Expand Down
1 change: 1 addition & 0 deletions packages/sqlite_async/lib/native.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'src/native/native_sqlite_open_factory.dart';
5 changes: 0 additions & 5 deletions packages/sqlite_async/lib/sqlite3.dart

This file was deleted.

2 changes: 0 additions & 2 deletions packages/sqlite_async/lib/sqlite3_common.dart

This file was deleted.

5 changes: 0 additions & 5 deletions packages/sqlite_async/lib/sqlite3_wasm.dart

This file was deleted.

5 changes: 0 additions & 5 deletions packages/sqlite_async/lib/sqlite3_web.dart

This file was deleted.

6 changes: 2 additions & 4 deletions packages/sqlite_async/lib/sqlite_async.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
/// See [SqliteDatabase] as a starting point.
library;

export 'src/common/abstract_open_factory.dart';
export 'src/common/abstract_open_factory.dart' hide InternalOpenFactory;
export 'src/common/connection/sync_sqlite_connection.dart';
export 'src/common/mutex.dart';
export 'src/common/sqlite_database.dart';
export 'src/common/sqlite_database.dart' hide SqliteDatabaseImpl;
export 'src/sqlite_connection.dart';
export 'src/sqlite_database.dart';
export 'src/sqlite_migrations.dart';
export 'src/sqlite_open_factory.dart';
export 'src/sqlite_options.dart';
export 'src/sqlite_queries.dart';
export 'src/update_notification.dart';
Expand Down
139 changes: 57 additions & 82 deletions packages/sqlite_async/lib/src/common/abstract_open_factory.dart
Original file line number Diff line number Diff line change
@@ -1,48 +1,80 @@
import 'dart:async';
/// @docImport '../native/native_sqlite_open_factory.dart';
/// @docImport '../web/web_sqlite_open_factory.dart';
library;

import 'package:meta/meta.dart';
import 'package:sqlite3/common.dart' as sqlite;

import '../impl/platform.dart' as platform;

import 'package:sqlite_async/sqlite3_common.dart' as sqlite;
import 'package:sqlite_async/src/common/mutex.dart';
import 'package:sqlite_async/src/sqlite_connection.dart';
import 'package:sqlite_async/src/sqlite_options.dart';
import 'package:sqlite_async/src/update_notification.dart';
import '../sqlite_options.dart';

/// Factory to create new SQLite database connections.
///
/// Since connections are opened in dedicated background isolates, this class
/// must be safe to pass to different isolates.
abstract class SqliteOpenFactory<Database extends sqlite.CommonDatabase> {
String get path;
///
/// How databases are opened is platform specific. For this reason, this class
/// can't be extended directly. To customize how databases are opened across
/// platforms, prefer using [SqliteOptions]. If that class doesn't provide the
/// degree of customization you need, you can also subclass platform-specific
/// connection factory implementations:
///
/// - On native platforms, extend [NativeSqliteOpenFactory].
/// - When compiling for the web, extend [WebSqliteOpenFactory].
sealed class SqliteOpenFactory {
final String path;
final SqliteOptions sqliteOptions;

SqliteOpenFactory._({
required this.path,
this.sqliteOptions = const SqliteOptions(),
});

/// Creates a default open factory opening databases at the given path with
/// specified options.
///
/// This will return a [NativeSqliteOpenFactory] on native platforms and a
/// [WebSqliteOpenFactory] when compiling for the web.
factory SqliteOpenFactory({
required String path,
SqliteOptions options = const SqliteOptions(),
}) {
return platform.createDefaultOpenFactory(path, options);
}

/// Opens a direct connection to the SQLite database
Database open(SqliteOpenOptions options);
/// Pragma statements to run on newly opened connections to configure them.
///
/// On native platforms, these configure WAL mode for instance. This can also
/// be useed to configure an encryption key if SQLite3MultipleCiphers is used.
List<String> pragmaStatements(SqliteOpenOptions options);
}

/// Opens an asynchronous [SqliteConnection]
FutureOr<SqliteConnection> openConnection(SqliteOpenOptions options);
/// The superclass for all connection factories.
///
/// By keeping this class internal, we can safely assert that all connection
/// factories on native and web platforms extend [NativeSqliteOpenFactory] and
/// [WebSqliteOpenFactory], respectively.
@internal
abstract base class InternalOpenFactory extends SqliteOpenFactory {
InternalOpenFactory({required super.path, super.sqliteOptions}) : super._();
}

class SqliteOpenOptions {
final class SqliteOpenOptions {
/// Whether this is the primary write connection for the database.
final bool primaryConnection;

/// Whether this connection is read-only.
final bool readOnly;

/// Mutex to use in [SqliteConnection]s
final Mutex? mutex;

/// Name used in debug logs
final String? debugName;

/// Stream of external update notifications
final Stream<UpdateNotification>? updates;

const SqliteOpenOptions(
{required this.primaryConnection,
required this.readOnly,
this.mutex,
this.debugName,
this.updates});
const SqliteOpenOptions({
required this.primaryConnection,
required this.readOnly,
this.debugName,
});

sqlite.OpenMode get openMode {
if (primaryConnection) {
Expand All @@ -54,60 +86,3 @@ class SqliteOpenOptions {
}
}
}

/// The default database factory.
///
/// This takes care of opening the database, and running PRAGMA statements
/// to configure the connection.
///
/// Override the [open] method to customize the process.
abstract class AbstractDefaultSqliteOpenFactory<
Database extends sqlite.CommonDatabase>
implements SqliteOpenFactory<Database> {
@override
final String path;
final SqliteOptions sqliteOptions;

const AbstractDefaultSqliteOpenFactory(
{required this.path,
this.sqliteOptions = const SqliteOptions.defaults()});

List<String> pragmaStatements(SqliteOpenOptions options);

@protected

/// Opens a direct connection to a SQLite database connection
Database openDB(SqliteOpenOptions options);

@override

/// Opens a direct connection to a SQLite database connection
/// and executes setup pragma statements to initialize the DB
Database open(SqliteOpenOptions options) {
var db = openDB(options);

// Pragma statements don't have the same BUSY_TIMEOUT behavior as normal statements.
// We add a manual retry loop for those.
for (var statement in pragmaStatements(options)) {
for (var tries = 0; tries < 30; tries++) {
try {
db.execute(statement);
break;
} on sqlite.SqliteException catch (e) {
if (e.resultCode == sqlite.SqlError.SQLITE_BUSY && tries < 29) {
continue;
} else {
rethrow;
}
}
}
}
return db;
}

@override

/// Opens an asynchronous [SqliteConnection] to a SQLite database
/// and executes setup pragma statements to initialize the DB
FutureOr<SqliteConnection> openConnection(SqliteOpenOptions options);
}
33 changes: 17 additions & 16 deletions packages/sqlite_async/lib/src/common/sqlite_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import 'dart:async';
import 'package:meta/meta.dart';
import 'package:sqlite_async/src/common/abstract_open_factory.dart';
import 'package:sqlite_async/src/impl/single_connection_database.dart';
import 'package:sqlite_async/src/impl/sqlite_database_impl.dart';
import 'package:sqlite_async/src/sqlite_options.dart';
import 'package:sqlite_async/src/sqlite_queries.dart';
import 'package:sqlite_async/src/update_notification.dart';
import 'package:sqlite_async/src/sqlite_connection.dart';

import '../impl/platform.dart' as platform;

mixin SqliteDatabaseMixin implements SqliteConnection, SqliteQueries {
/// Maximum number of concurrent read transactions.
int get maxReaders;
Expand All @@ -18,7 +19,7 @@ mixin SqliteDatabaseMixin implements SqliteConnection, SqliteQueries {
/// This must be safe to pass to different isolates.
///
/// Use a custom class for this to customize the open process.
AbstractDefaultSqliteOpenFactory get openFactory;
SqliteOpenFactory get openFactory;

/// Use this stream to subscribe to notifications of updates to tables.
@override
Expand Down Expand Up @@ -47,11 +48,10 @@ mixin SqliteDatabaseMixin implements SqliteConnection, SqliteQueries {
///
/// Use one instance per database file. If multiple instances are used, update
/// notifications may not trigger, and calls may fail with "SQLITE_BUSY" errors.
abstract class SqliteDatabase
abstract base class SqliteDatabase
with SqliteQueries, SqliteDatabaseMixin
implements SqliteConnection {
/// The maximum number of concurrent read transactions if not explicitly specified.
static const int defaultMaxReaders = 5;
SqliteDatabase._();

/// Open a SqliteDatabase.
///
Expand All @@ -61,14 +61,11 @@ abstract class SqliteDatabase
/// transactions, and a single concurrent write transaction. Write transactions
/// do not block read transactions, and read transactions will see the state
/// from the last committed write transaction.
///
/// A maximum of [maxReaders] concurrent read transactions are allowed.
factory SqliteDatabase(
{required String path,
int maxReaders = SqliteDatabase.defaultMaxReaders,
SqliteOptions options = const SqliteOptions.defaults()}) {
return SqliteDatabaseImpl(
path: path, maxReaders: maxReaders, options: options);
{required String path, SqliteOptions options = const SqliteOptions()}) {
return SqliteDatabase.withFactory(
SqliteOpenFactory(path: path, options: options),
);
}

/// Advanced: Open a database with a specified factory.
Expand All @@ -80,10 +77,8 @@ abstract class SqliteDatabase
/// 2. Running additional per-connection PRAGMA statements on each connection.
/// 3. Creating custom SQLite functions.
/// 4. Creating temporary views or triggers.
factory SqliteDatabase.withFactory(
AbstractDefaultSqliteOpenFactory openFactory,
{int maxReaders = SqliteDatabase.defaultMaxReaders}) {
return SqliteDatabaseImpl.withFactory(openFactory, maxReaders: maxReaders);
factory SqliteDatabase.withFactory(SqliteOpenFactory openFactory) {
return platform.openDatabaseWithFactory(openFactory);
}

/// Opens a [SqliteDatabase] that only wraps an underlying connection.
Expand All @@ -106,3 +101,9 @@ abstract class SqliteDatabase
return SingleConnectionDatabase(connection);
}
}

/// Internal superclass for all [SqliteDatabase] implementations.
@internal
abstract base class SqliteDatabaseImpl extends SqliteDatabase {
SqliteDatabaseImpl() : super._();
}
5 changes: 0 additions & 5 deletions packages/sqlite_async/lib/src/impl/open_factory_impl.dart

This file was deleted.

3 changes: 3 additions & 0 deletions packages/sqlite_async/lib/src/impl/platform.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'platform_stub.dart'
if (dart.library.js_interop) 'platform_web.dart'
if (dart.library.io) 'platform_native.dart';
Loading
Loading