From e303d14d8b075c7f9c424c9d3e1ae6bb74e856e1 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 26 Oct 2025 20:48:24 +0100 Subject: [PATCH] Document schema export changes --- docs/content/migrations/exports.md | 7 ++++++- docs/lib/src/generated_snippets.dart | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/content/migrations/exports.md b/docs/content/migrations/exports.md index 0a3a74806..3851a6dfe 100644 --- a/docs/content/migrations/exports.md +++ b/docs/content/migrations/exports.md @@ -81,13 +81,18 @@ If drift is unable to extract the version from your `schemaVersion` getter, prov $ dart run drift_dev schema dump lib/database/database.dart drift_schemas/drift_schema_v3.json ``` -!!! success " Dumping a database" +!!! tip "Dumping a database" If, instead of exporting the schema of a database class, you want to export the schema of an existing sqlite3 database file, you can do that as well! `drift_dev schema dump` recognizes a sqlite3 database file as its first argument and can extract the relevant schema from there. +The goal of these exported schemas is to match the actual `CREATE` statements making up your database. +For this reason, some columns like booleans and date time values get generated as the inner SQL type. +Using the actual source of truth as a snapshot instead of higher-level drift types makes exported schemas more +resilient to changes in your app or drift that could affect how the schema gets generated. + ## What next? Having exported your schema versions into files like this, drift tools are able diff --git a/docs/lib/src/generated_snippets.dart b/docs/lib/src/generated_snippets.dart index b7db13563..d76ed8d8b 100644 --- a/docs/lib/src/generated_snippets.dart +++ b/docs/lib/src/generated_snippets.dart @@ -5,7 +5,7 @@ const generatedSnippets = { 'lib/src/snippets/setup/custom_flutter_setup.dart.snippet.json': {'(full)': 'import \'package:drift/drift.dart\';\n\nimport \'dart:io\';\nimport \'package:drift/native.dart\';\nimport \'package:path_provider/path_provider.dart\';\nimport \'package:path/path.dart\' as p;\nimport \'package:sqlite3/sqlite3.dart\';\nimport \'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart\';\n\nLazyDatabase openConnection() {\n // the LazyDatabase util lets us find the right location for the file async.\n return LazyDatabase(() async {\n // put the database file, called db.sqlite here, into the documents folder\n // for your app.\n final dbFolder = await getApplicationDocumentsDirectory();\n final file = File(p.join(dbFolder.path, \'db.sqlite\'));\n\n // Also work around limitations on old Android versions\n if (Platform.isAndroid) {\n await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();\n }\n\n // Make sqlite3 pick a more suitable location for temporary files - the\n // one from the system may be inaccessible due to sandboxing.\n final cachebase = (await getTemporaryDirectory()).path;\n // We can\'t access /tmp on Android, which sqlite3 would try by default.\n // Explicitly tell it about the correct temporary directory.\n sqlite3.tempDirectory = cachebase;\n\n return NativeDatabase.createInBackground(file);\n });\n}\n\n','custom': 'import \'dart:io\';\nimport \'package:drift/native.dart\';\nimport \'package:path_provider/path_provider.dart\';\nimport \'package:path/path.dart\' as p;\nimport \'package:sqlite3/sqlite3.dart\';\nimport \'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart\';\n\nLazyDatabase openConnection() {\n // the LazyDatabase util lets us find the right location for the file async.\n return LazyDatabase(() async {\n // put the database file, called db.sqlite here, into the documents folder\n // for your app.\n final dbFolder = await getApplicationDocumentsDirectory();\n final file = File(p.join(dbFolder.path, \'db.sqlite\'));\n\n // Also work around limitations on old Android versions\n if (Platform.isAndroid) {\n await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();\n }\n\n // Make sqlite3 pick a more suitable location for temporary files - the\n // one from the system may be inaccessible due to sandboxing.\n final cachebase = (await getTemporaryDirectory()).path;\n // We can\'t access /tmp on Android, which sqlite3 would try by default.\n // Explicitly tell it about the correct temporary directory.\n sqlite3.tempDirectory = cachebase;\n\n return NativeDatabase.createInBackground(file);\n });\n}\n\n',}, 'lib/src/snippets/setup/database.dart.snippet.json': {'(full)': '// ignore_for_file: unused_element\nimport \'package:drift/drift.dart\';\n\nimport \'package:drift_flutter/drift_flutter.dart\';\nimport \'package:path_provider/path_provider.dart\';\nimport \'dart:io\';\nimport \'package:drift/native.dart\';\nimport \'package:drift_postgres/drift_postgres.dart\';\nimport \'package:postgres/postgres.dart\' as pg;\n\n\npart \'database.g.dart\';\n\nclass TodoItems extends Table {\n IntColumn get id => integer().autoIncrement()();\n TextColumn get title => text().withLength(min: 6, max: 32)();\n TextColumn get content => text().named(\'body\')();\n DateTimeColumn get createdAt => dateTime().nullable()();\n}\n\n@DriftDatabase(tables: [TodoItems])\nclass AppDatabase extends _\$AppDatabase {\n // After generating code, this class needs to define a `schemaVersion` getter\n // and a constructor telling drift where the database should be stored.\n // These are described in the getting started guide: https://drift.simonbinder.eu/setup/\n AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n\n static QueryExecutor _openConnection() {\n throw \'should not show as snippet\';\n }\n\n}\n\nclass OpenFlutter {\n static QueryExecutor _openConnection() {\n return driftDatabase(\n name: \'my_database\',\n native: const DriftNativeOptions(\n // By default, `driftDatabase` from `package:drift_flutter` stores the\n // database files in `getApplicationDocumentsDirectory()`.\n databaseDirectory: getApplicationSupportDirectory,\n ),\n // If you need web support, see https://drift.simonbinder.eu/platforms/web/\n );\n }\n}\n\nclass OpenPostgres {\n static QueryExecutor _openConnection() {\n return PgDatabase(\n endpoint: pg.Endpoint(\n host: \'localhost\',\n database: \'database\',\n username: \'dart\',\n password: \'mysecurepassword\',\n ),\n );\n }\n}\n\nclass OpenSqlite3 {\n static QueryExecutor _openConnection() {\n return NativeDatabase.createInBackground(File(\'path/to/your/database\'));\n }\n}\n\nclass WidgetsFlutterBinding {\n static void ensureInitialized() {}\n}\n\nvoid main() async {\n WidgetsFlutterBinding.ensureInitialized();\n\n final database = AppDatabase();\n\n await database\n .into(database.todoItems)\n .insert(\n TodoItemsCompanion.insert(\n title: \'todo: finish drift setup\',\n content: \'We can now write queries and define our own tables.\',\n ),\n );\n List<TodoItem> allItems = await database.select(database.todoItems).get();\n\n print(\'items in database: \$allItems\');\n}\n\n','flutter': 'import \'package:drift/drift.dart\';\nimport \'package:drift_flutter/drift_flutter.dart\';\nimport \'package:path_provider/path_provider.dart\';\n\npart \'database.g.dart\';\n\nclass TodoItems extends Table {\n IntColumn get id => integer().autoIncrement()();\n TextColumn get title => text().withLength(min: 6, max: 32)();\n TextColumn get content => text().named(\'body\')();\n DateTimeColumn get createdAt => dateTime().nullable()();\n}\n\n@DriftDatabase(tables: [TodoItems])\nclass AppDatabase extends _\$AppDatabase {\n // After generating code, this class needs to define a `schemaVersion` getter\n // and a constructor telling drift where the database should be stored.\n // These are described in the getting started guide: https://drift.simonbinder.eu/setup/\n AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n\n static QueryExecutor _openConnection() {\n return driftDatabase(\n name: \'my_database\',\n native: const DriftNativeOptions(\n // By default, `driftDatabase` from `package:drift_flutter` stores the\n // database files in `getApplicationDocumentsDirectory()`.\n databaseDirectory: getApplicationSupportDirectory,\n ),\n // If you need web support, see https://drift.simonbinder.eu/platforms/web/\n );\n }\n}\n','sqlite3': 'import \'package:drift/drift.dart\';\nimport \'dart:io\';\nimport \'package:drift/native.dart\';\n\npart \'database.g.dart\';\n\nclass TodoItems extends Table {\n IntColumn get id => integer().autoIncrement()();\n TextColumn get title => text().withLength(min: 6, max: 32)();\n TextColumn get content => text().named(\'body\')();\n DateTimeColumn get createdAt => dateTime().nullable()();\n}\n\n@DriftDatabase(tables: [TodoItems])\nclass AppDatabase extends _\$AppDatabase {\n // After generating code, this class needs to define a `schemaVersion` getter\n // and a constructor telling drift where the database should be stored.\n // These are described in the getting started guide: https://drift.simonbinder.eu/setup/\n AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n\n static QueryExecutor _openConnection() {\n return NativeDatabase.createInBackground(File(\'path/to/your/database\'));\n }\n}\n','postgres': 'import \'package:drift/drift.dart\';\nimport \'package:drift_postgres/drift_postgres.dart\';\nimport \'package:postgres/postgres.dart\' as pg;\n\npart \'database.g.dart\';\n\nclass TodoItems extends Table {\n IntColumn get id => integer().autoIncrement()();\n TextColumn get title => text().withLength(min: 6, max: 32)();\n TextColumn get content => text().named(\'body\')();\n DateTimeColumn get createdAt => dateTime().nullable()();\n}\n\n@DriftDatabase(tables: [TodoItems])\nclass AppDatabase extends _\$AppDatabase {\n // After generating code, this class needs to define a `schemaVersion` getter\n // and a constructor telling drift where the database should be stored.\n // These are described in the getting started guide: https://drift.simonbinder.eu/setup/\n AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n\n static QueryExecutor _openConnection() {\n return PgDatabase(\n endpoint: pg.Endpoint(\n host: \'localhost\',\n database: \'database\',\n username: \'dart\',\n password: \'mysecurepassword\',\n ),\n );\n }\n}\n','before_generation': 'import \'package:drift/drift.dart\';\n\npart \'database.g.dart\';\n\nclass TodoItems extends Table {\n IntColumn get id => integer().autoIncrement()();\n TextColumn get title => text().withLength(min: 6, max: 32)();\n TextColumn get content => text().named(\'body\')();\n DateTimeColumn get createdAt => dateTime().nullable()();\n}\n\n@DriftDatabase(tables: [TodoItems])\nclass AppDatabase extends _\$AppDatabase {\n}\n','table': 'class TodoItems extends Table {\n IntColumn get id => integer().autoIncrement()();\n TextColumn get title => text().withLength(min: 6, max: 32)();\n TextColumn get content => text().named(\'body\')();\n DateTimeColumn get createdAt => dateTime().nullable()();\n}\n\n','use': 'void main() async {\n WidgetsFlutterBinding.ensureInitialized();\n\n final database = AppDatabase();\n\n await database\n .into(database.todoItems)\n .insert(\n TodoItemsCompanion.insert(\n title: \'todo: finish drift setup\',\n content: \'We can now write queries and define our own tables.\',\n ),\n );\n List<TodoItem> allItems = await database.select(database.todoItems).get();\n\n print(\'items in database: \$allItems\');\n}\n\n',}, 'lib/src/snippets/setup/testing.dart.snippet.json': {'(full)': 'import \'package:drift/drift.dart\';\nimport \'package:drift/native.dart\';\nimport \'package:test/test.dart\';\n// the file defined above, you can test any drift database of course\nimport \'database.dart\';\n\nvoid main() {\n late AppDatabase database;\n\n setUp(() {\n database = AppDatabase(\n DatabaseConnection(\n NativeDatabase.memory(),\n // Recommended for widget tests to avoid test errors.\n closeStreamsSynchronously: true,\n ),\n );\n });\n tearDown(() async {\n await database.close();\n });\n}\n',}, -'lib/src/snippets/isolates.dart.snippet.json': {'(full)': 'import \'dart:io\';\nimport \'dart:isolate\';\n\nimport \'package:drift/drift.dart\';\nimport \'package:drift/isolate.dart\';\nimport \'package:drift/native.dart\';\nimport \'package:path/path.dart\' as p;\nimport \'package:path_provider/path_provider.dart\';\n\npart \'isolates.g.dart\';\n\nQueryExecutor _openConnection() {\n return NativeDatabase.memory();\n}\n\nclass SomeTable extends Table {\n IntColumn get id => integer().autoIncrement()();\n TextColumn get content => text()();\n}\n\n// Copying the definitions here because we can\'t import Flutter in documentation\n// snippets.\nclass RootIsolateToken {\n static RootIsolateToken? instance;\n}\n\nclass BackgroundIsolateBinaryMessenger {\n static void ensureInitialized(RootIsolateToken token) {}\n}\n\n\n@DriftDatabase(tables: [SomeTable] /* ... */)\nclass MyDatabase extends _\$MyDatabase {\n // A constructor like this can use the default connection as described in the\n // getting started guide, but also allows overriding the connection.\n MyDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n}\n\nFuture<DriftIsolate> createIsolateWithSpawn() async {\n final token = RootIsolateToken.instance!;\n return await DriftIsolate.spawn(() {\n // This function runs in a new isolate, so we must first initialize the\n // messenger to use platform channels.\n BackgroundIsolateBinaryMessenger.ensureInitialized(token);\n\n // The callback to DriftIsolate.spawn() must return the database connection\n // to use.\n return LazyDatabase(() async {\n // Note that this runs on a background isolate, which only started to\n // support platform channels in Flutter 3.7. For earlier Flutter versions,\n // a workaround is described later in this article.\n final dbFolder = await getApplicationDocumentsDirectory();\n final path = p.join(dbFolder.path, \'app.db\');\n\n return NativeDatabase(File(path));\n });\n });\n}\n\nFuture<DriftIsolate> createIsolateManually() async {\n final receiveIsolate = ReceivePort(\'receive drift isolate handle\');\n await Isolate.spawn<SendPort>((message) async {\n final server = DriftIsolate.inCurrent(() {\n // Again, this needs to return the LazyDatabase or the connection to use.\n throw \'stub\';\n });\n\n // Now, inform the original isolate about the created server:\n message.send(server);\n }, receiveIsolate.sendPort);\n\n final server = await receiveIsolate.first as DriftIsolate;\n receiveIsolate.close();\n return server;\n}\n\nFuture<DriftIsolate> createIsolate() => createIsolateWithSpawn();\n\nvoid main() async {\n final isolate = await createIsolate();\n\n // After creating the isolate, calling connect() will return a connection\n // which can be used to create a database.\n // As long as the isolate is used by only one database (it is here), we can\n // use `singleClientMode` to dispose the isolate after closing the connection.\n final database = MyDatabase(await isolate.connect(singleClientMode: true));\n\n // you can now use your database exactly like you regularly would, it\n // transparently uses a background isolate to execute queries.\n // Just using the db to avoid an analyzer error, this isn\'t part of the docs.\n database.customSelect(\'SELECT 1\');\n}\n\nvoid connectSynchronously() {\n MyDatabase(\n DatabaseConnection.delayed(\n Future.sync(() async {\n final isolate = await createIsolate();\n return isolate.connect(singleClientMode: true);\n }),\n ),\n );\n}\n\n\nFuture<DriftIsolate> _createDriftIsolate() async {\n // this method is called from the main isolate. Since we can\'t use\n // getApplicationDocumentsDirectory on a background isolate, we calculate\n // the database path in the foreground isolate and then inform the\n // background isolate about the path.\n final dir = await getApplicationDocumentsDirectory();\n final path = p.join(dir.path, \'db.sqlite\');\n final receivePort = ReceivePort();\n\n await Isolate.spawn(\n _startBackground,\n _IsolateStartRequest(receivePort.sendPort, path),\n );\n\n // _startBackground will send the DriftIsolate to this ReceivePort\n return await receivePort.first as DriftIsolate;\n}\n\nvoid _startBackground(_IsolateStartRequest request) {\n // this is the entry point from the background isolate! Let\'s create\n // the database from the path we received\n final executor = NativeDatabase(File(request.targetPath));\n // we\'re using DriftIsolate.inCurrent here as this method already runs on a\n // background isolate. If we used DriftIsolate.spawn, a third isolate would be\n // started which is not what we want!\n final driftIsolate = DriftIsolate.inCurrent(\n () => DatabaseConnection(executor),\n );\n // inform the starting isolate about this, so that it can call .connect()\n request.sendDriftIsolate.send(driftIsolate);\n}\n\n// used to bundle the SendPort and the target path, since isolate entry point\n// functions can only take one parameter.\nclass _IsolateStartRequest {\n final SendPort sendDriftIsolate;\n final String targetPath;\n\n _IsolateStartRequest(this.sendDriftIsolate, this.targetPath);\n}\n\nDatabaseConnection createDriftIsolateAndConnect() {\n return DatabaseConnection.delayed(\n Future.sync(() async {\n final isolate = await _createDriftIsolate();\n return await isolate.connect(singleClientMode: true);\n }),\n );\n}\n\nQueryExecutor createSimple() {\n return LazyDatabase(() async {\n final dir = await getApplicationDocumentsDirectory();\n final file = File(p.join(dir.path, \'db.sqlite\'));\n\n // Using createInBackground creates a drift isolate with the recommended\n // options behind the scenes.\n return NativeDatabase.createInBackground(file);\n });\n}\n\nFuture<void> invalidIsolateUsage() async {\n final database = MyDatabase(NativeDatabase.memory());\n\n // Unfortunately, this doesn\'t work: Drift databases contain references to\n // async primitives like streams and futures that can\'t be serialized across\n // isolates like this.\n await Isolate.run(() async {\n await database.batch((batch) {\n // ...\n });\n });\n}\n\nFuture<List<SomeTableData>> _complexAndExpensiveOperationToFetchRows() async {\n throw \'stub\';\n}\n\nFuture<void> insertBulkData(MyDatabase database) async {\n // computeWithDatabase is an extension provided by package:drift/isolate.dart\n await database.computeWithDatabase(\n computation: (database) async {\n // Expensive computation that runs on its own isolate but talks to the\n // main database.\n final rows = await _complexAndExpensiveOperationToFetchRows();\n await database.batch((batch) {\n batch.insertAll(database.someTable, rows);\n });\n },\n connect: (connection) {\n // This function is responsible for creating a second instance of your\n // database class with a short-lived [connection].\n // For this to work, your database class needs to have a constructor that\n // allows taking a connection as described above.\n return MyDatabase(connection);\n },\n );\n}\n\nFuture<void> customIsolateUsage(MyDatabase database) async {\n final connection = await database.serializableConnection();\n\n await Isolate.run(() async {\n // We can\'t share the [database] object across isolates, but the connection\n // is fine!\n final databaseForIsolate = MyDatabase(await connection.connect());\n\n try {\n await databaseForIsolate.batch((batch) {\n // (...)\n });\n } finally {\n databaseForIsolate.close();\n }\n }, debugName: \'My custom database task\');\n}\n\n','isolate': 'import \'package:drift/isolate.dart\';\n\n@DriftDatabase(tables: [SomeTable] /* ... */)\nclass MyDatabase extends _\$MyDatabase {\n // A constructor like this can use the default connection as described in the\n // getting started guide, but also allows overriding the connection.\n MyDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n}\nvoid main() async {\n final isolate = await createIsolate();\n\n // After creating the isolate, calling connect() will return a connection\n // which can be used to create a database.\n // As long as the isolate is used by only one database (it is here), we can\n // use `singleClientMode` to dispose the isolate after closing the connection.\n final database = MyDatabase(await isolate.connect(singleClientMode: true));\n\n // you can now use your database exactly like you regularly would, it\n // transparently uses a background isolate to execute queries.\n}\n','initialization': 'import \'package:path/path.dart\' as p;\nimport \'package:path_provider/path_provider.dart\';\n\nFuture<DriftIsolate> _createDriftIsolate() async {\n // this method is called from the main isolate. Since we can\'t use\n // getApplicationDocumentsDirectory on a background isolate, we calculate\n // the database path in the foreground isolate and then inform the\n // background isolate about the path.\n final dir = await getApplicationDocumentsDirectory();\n final path = p.join(dir.path, \'db.sqlite\');\n final receivePort = ReceivePort();\n\n await Isolate.spawn(\n _startBackground,\n _IsolateStartRequest(receivePort.sendPort, path),\n );\n\n // _startBackground will send the DriftIsolate to this ReceivePort\n return await receivePort.first as DriftIsolate;\n}\n\nvoid _startBackground(_IsolateStartRequest request) {\n // this is the entry point from the background isolate! Let\'s create\n // the database from the path we received\n final executor = NativeDatabase(File(request.targetPath));\n // we\'re using DriftIsolate.inCurrent here as this method already runs on a\n // background isolate. If we used DriftIsolate.spawn, a third isolate would be\n // started which is not what we want!\n final driftIsolate = DriftIsolate.inCurrent(\n () => DatabaseConnection(executor),\n );\n // inform the starting isolate about this, so that it can call .connect()\n request.sendDriftIsolate.send(driftIsolate);\n}\n\n// used to bundle the SendPort and the target path, since isolate entry point\n// functions can only take one parameter.\nclass _IsolateStartRequest {\n final SendPort sendDriftIsolate;\n final String targetPath;\n\n _IsolateStartRequest(this.sendDriftIsolate, this.targetPath);\n}\n','database-definition': '\n@DriftDatabase(tables: [SomeTable] /* ... */)\nclass MyDatabase extends _\$MyDatabase {\n // A constructor like this can use the default connection as described in the\n // getting started guide, but also allows overriding the connection.\n MyDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n}\n','driftisolate-spawn': 'Future<DriftIsolate> createIsolateWithSpawn() async {\n final token = RootIsolateToken.instance!;\n return await DriftIsolate.spawn(() {\n // This function runs in a new isolate, so we must first initialize the\n // messenger to use platform channels.\n BackgroundIsolateBinaryMessenger.ensureInitialized(token);\n\n // The callback to DriftIsolate.spawn() must return the database connection\n // to use.\n return LazyDatabase(() async {\n // Note that this runs on a background isolate, which only started to\n // support platform channels in Flutter 3.7. For earlier Flutter versions,\n // a workaround is described later in this article.\n final dbFolder = await getApplicationDocumentsDirectory();\n final path = p.join(dbFolder.path, \'app.db\');\n\n return NativeDatabase(File(path));\n });\n });\n}\n','custom-spawn': 'Future<DriftIsolate> createIsolateManually() async {\n final receiveIsolate = ReceivePort(\'receive drift isolate handle\');\n await Isolate.spawn<SendPort>((message) async {\n final server = DriftIsolate.inCurrent(() {\n // Again, this needs to return the LazyDatabase or the connection to use.\n });\n\n // Now, inform the original isolate about the created server:\n message.send(server);\n }, receiveIsolate.sendPort);\n\n final server = await receiveIsolate.first as DriftIsolate;\n receiveIsolate.close();\n return server;\n}\n','delayed': 'MyDatabase(\n DatabaseConnection.delayed(\n Future.sync(() async {\n final isolate = await createIsolate();\n return isolate.connect(singleClientMode: true);\n }),\n ),\n);\n','init_connect': 'DatabaseConnection createDriftIsolateAndConnect() {\n return DatabaseConnection.delayed(\n Future.sync(() async {\n final isolate = await _createDriftIsolate();\n return await isolate.connect(singleClientMode: true);\n }),\n );\n}\n','simple': 'QueryExecutor createSimple() {\n return LazyDatabase(() async {\n final dir = await getApplicationDocumentsDirectory();\n final file = File(p.join(dir.path, \'db.sqlite\'));\n\n // Using createInBackground creates a drift isolate with the recommended\n // options behind the scenes.\n return NativeDatabase.createInBackground(file);\n });\n}\n','invalid': 'Future<void> invalidIsolateUsage() async {\n final database = MyDatabase(NativeDatabase.memory());\n\n // Unfortunately, this doesn\'t work: Drift databases contain references to\n // async primitives like streams and futures that can\'t be serialized across\n // isolates like this.\n await Isolate.run(() async {\n await database.batch((batch) {\n // ...\n });\n });\n}\n','compute': 'Future<void> insertBulkData(MyDatabase database) async {\n // computeWithDatabase is an extension provided by package:drift/isolate.dart\n await database.computeWithDatabase(\n computation: (database) async {\n // Expensive computation that runs on its own isolate but talks to the\n // main database.\n final rows = await _complexAndExpensiveOperationToFetchRows();\n await database.batch((batch) {\n batch.insertAll(database.someTable, rows);\n });\n },\n connect: (connection) {\n // This function is responsible for creating a second instance of your\n // database class with a short-lived [connection].\n // For this to work, your database class needs to have a constructor that\n // allows taking a connection as described above.\n return MyDatabase(connection);\n },\n );\n}\n','custom-compute': 'Future<void> customIsolateUsage(MyDatabase database) async {\n final connection = await database.serializableConnection();\n\n await Isolate.run(() async {\n // We can\'t share the [database] object across isolates, but the connection\n // is fine!\n final databaseForIsolate = MyDatabase(await connection.connect());\n\n try {\n await databaseForIsolate.batch((batch) {\n // (...)\n });\n } finally {\n databaseForIsolate.close();\n }\n }, debugName: \'My custom database task\');\n}\n\n',}, +'lib/src/snippets/isolates.dart.snippet.json': {'(full)': 'import \'dart:io\';\nimport \'dart:isolate\';\n\nimport \'package:drift/drift.dart\';\nimport \'package:drift/isolate.dart\';\nimport \'package:drift/native.dart\';\nimport \'package:path/path.dart\' as p;\nimport \'package:path_provider/path_provider.dart\';\n\npart \'isolates.g.dart\';\n\nQueryExecutor _openConnection() {\n return NativeDatabase.memory();\n}\n\nclass SomeTable extends Table {\n IntColumn get id => integer().autoIncrement()();\n TextColumn get content => text()();\n}\n\n// Copying the definitions here because we can\'t import Flutter in documentation\n// snippets.\nclass RootIsolateToken {\n static RootIsolateToken? instance;\n}\n\nclass BackgroundIsolateBinaryMessenger {\n static void ensureInitialized(RootIsolateToken token) {}\n}\n\n\n@DriftDatabase(tables: [SomeTable] /* ... */)\nclass MyDatabase extends _\$MyDatabase {\n // A constructor like this can use the default connection as described in the\n // getting started guide, but also allows overriding the connection.\n MyDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n}\n\nFuture<DriftIsolate> createIsolateWithSpawn() async {\n final token = RootIsolateToken.instance!;\n return await DriftIsolate.spawn(() {\n // This function runs in a new isolate, so we must first initialize the\n // messenger to use platform channels.\n BackgroundIsolateBinaryMessenger.ensureInitialized(token);\n\n // The callback to DriftIsolate.spawn() must return the database connection\n // to use.\n return LazyDatabase(() async {\n // Note that this runs on a background isolate, which only started to\n // support platform channels in Flutter 3.7. For earlier Flutter versions,\n // a workaround is described later in this article.\n final dbFolder = await getApplicationDocumentsDirectory();\n final path = p.join(dbFolder.path, \'app.db\');\n\n return NativeDatabase(File(path));\n });\n });\n}\n\nFuture<DriftIsolate> createIsolateManually() async {\n final receiveIsolate = ReceivePort(\'receive drift isolate handle\');\n await Isolate.spawn<SendPort>((message) async {\n final server = DriftIsolate.inCurrent(() {\n // Again, this needs to return the LazyDatabase or the connection to use.\n throw \'stub\';\n });\n\n // Now, inform the original isolate about the created server:\n message.send(server);\n }, receiveIsolate.sendPort);\n\n final server = await receiveIsolate.first as DriftIsolate;\n receiveIsolate.close();\n return server;\n}\n\nFuture<DriftIsolate> createIsolate() => createIsolateWithSpawn();\n\nvoid main() async {\n final isolate = await createIsolate();\n\n // After creating the isolate, calling connect() will return a connection\n // which can be used to create a database.\n // As long as the isolate is used by only one database (it is here), we can\n // use `singleClientMode` to dispose the isolate after closing the connection.\n final database = MyDatabase(await isolate.connect(singleClientMode: true));\n\n // you can now use your database exactly like you regularly would, it\n // transparently uses a background isolate to execute queries.\n // Just using the db to avoid an analyzer error, this isn\'t part of the docs.\n database.customSelect(\'SELECT 1\');\n}\n\nvoid connectSynchronously() {\n MyDatabase(\n DatabaseConnection.delayed(\n Future.sync(() async {\n final isolate = await createIsolate();\n return isolate.connect(singleClientMode: true);\n }),\n ),\n );\n}\n\n\nFuture<DriftIsolate> _createDriftIsolate() async {\n // this method is called from the main isolate. Since we can\'t use\n // getApplicationDocumentsDirectory on a background isolate, we calculate\n // the database path in the foreground isolate and then inform the\n // background isolate about the path.\n final dir = await getApplicationDocumentsDirectory();\n final path = p.join(dir.path, \'db.sqlite\');\n final receivePort = ReceivePort();\n\n await Isolate.spawn(\n _startBackground,\n _IsolateStartRequest(receivePort.sendPort, path),\n );\n\n // _startBackground will send the DriftIsolate to this ReceivePort\n return await receivePort.first as DriftIsolate;\n}\n\nvoid _startBackground(_IsolateStartRequest request) {\n // this is the entry point from the background isolate! Let\'s create\n // the database from the path we received\n final executor = NativeDatabase(File(request.targetPath));\n // we\'re using DriftIsolate.inCurrent here as this method already runs on a\n // background isolate. If we used DriftIsolate.spawn, a third isolate would be\n // started which is not what we want!\n final driftIsolate = DriftIsolate.inCurrent(\n () => DatabaseConnection(executor),\n );\n // inform the starting isolate about this, so that it can call .connect()\n request.sendDriftIsolate.send(driftIsolate);\n}\n\n// used to bundle the SendPort and the target path, since isolate entry point\n// functions can only take one parameter.\nclass _IsolateStartRequest {\n final SendPort sendDriftIsolate;\n final String targetPath;\n\n _IsolateStartRequest(this.sendDriftIsolate, this.targetPath);\n}\n\nDatabaseConnection createDriftIsolateAndConnect() {\n return DatabaseConnection.delayed(\n Future.sync(() async {\n final isolate = await _createDriftIsolate();\n return await isolate.connect(singleClientMode: true);\n }),\n );\n}\n\nQueryExecutor createSimple() {\n return LazyDatabase(() async {\n final dir = await getApplicationDocumentsDirectory();\n final file = File(p.join(dir.path, \'db.sqlite\'));\n\n // Using createInBackground creates a drift isolate with the recommended\n // options behind the scenes.\n return NativeDatabase.createInBackground(file);\n });\n}\n\nFuture<void> invalidIsolateUsage() async {\n final database = MyDatabase(NativeDatabase.memory());\n\n // Unfortunately, this doesn\'t work: Drift databases contain references to\n // async primitives like streams and futures that can\'t be serialized across\n // isolates like this.\n await Isolate.run(() async {\n await database.batch((batch) {\n // ...\n });\n });\n}\n\nFuture<List<SomeTableData>> _complexAndExpensiveOperationToFetchRows() async {\n throw \'stub\';\n}\n\nFuture<void> insertBulkData(MyDatabase database) async {\n // computeWithDatabase is an extension provided by package:drift/isolate.dart\n await database.computeWithDatabase(\n computation: (database) async {\n // Expensive computation that runs on its own isolate but talks to the\n // main database.\n final rows = await _complexAndExpensiveOperationToFetchRows();\n await database.batch((batch) {\n batch.insertAll(database.someTable, rows);\n });\n },\n connect: (connection) {\n // This function is responsible for creating a second instance of your\n // database class with a short-lived [connection].\n // For this to work, your database class needs to have a constructor that\n // allows taking a connection as described above.\n return MyDatabase(connection);\n },\n );\n}\n\nFuture<void> customIsolateUsage(MyDatabase database) async {\n final connection = await database.serializableConnection();\n\n await Isolate.run(() async {\n // We can\'t share the [database] object across isolates, but the connection\n // is fine!\n final databaseForIsolate = MyDatabase(await connection.connect());\n\n try {\n await databaseForIsolate.batch((batch) {\n // (...)\n });\n } finally {\n databaseForIsolate.close();\n }\n }, debugName: \'My custom database task\');\n}\n\n','isolate': 'import \'package:drift/isolate.dart\';\n\n@DriftDatabase(tables: [SomeTable] /* ... */)\nclass MyDatabase extends _\$MyDatabase {\n // A constructor like this can use the default connection as described in the\n // getting started guide, but also allows overriding the connection.\n MyDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n}\nvoid main() async {\n final isolate = await createIsolate();\n\n // After creating the isolate, calling connect() will return a connection\n // which can be used to create a database.\n // As long as the isolate is used by only one database (it is here), we can\n // use `singleClientMode` to dispose the isolate after closing the connection.\n final database = MyDatabase(await isolate.connect(singleClientMode: true));\n\n // you can now use your database exactly like you regularly would, it\n // transparently uses a background isolate to execute queries.\n}\n','initialization': 'import \'package:path/path.dart\' as p;\nimport \'package:path_provider/path_provider.dart\';\n\nFuture<DriftIsolate> _createDriftIsolate() async {\n // this method is called from the main isolate. Since we can\'t use\n // getApplicationDocumentsDirectory on a background isolate, we calculate\n // the database path in the foreground isolate and then inform the\n // background isolate about the path.\n final dir = await getApplicationDocumentsDirectory();\n final path = p.join(dir.path, \'db.sqlite\');\n final receivePort = ReceivePort();\n\n await Isolate.spawn(\n _startBackground,\n _IsolateStartRequest(receivePort.sendPort, path),\n );\n\n // _startBackground will send the DriftIsolate to this ReceivePort\n return await receivePort.first as DriftIsolate;\n}\n\nvoid _startBackground(_IsolateStartRequest request) {\n // this is the entry point from the background isolate! Let\'s create\n // the database from the path we received\n final executor = NativeDatabase(File(request.targetPath));\n // we\'re using DriftIsolate.inCurrent here as this method already runs on a\n // background isolate. If we used DriftIsolate.spawn, a third isolate would be\n // started which is not what we want!\n final driftIsolate = DriftIsolate.inCurrent(\n () => DatabaseConnection(executor),\n );\n // inform the starting isolate about this, so that it can call .connect()\n request.sendDriftIsolate.send(driftIsolate);\n}\n\n// used to bundle the SendPort and the target path, since isolate entry point\n// functions can only take one parameter.\nclass _IsolateStartRequest {\n final SendPort sendDriftIsolate;\n final String targetPath;\n\n _IsolateStartRequest(this.sendDriftIsolate, this.targetPath);\n}\n','database-definition': '\n@DriftDatabase(tables: [SomeTable] /* ... */)\nclass MyDatabase extends _\$MyDatabase {\n // A constructor like this can use the default connection as described in the\n // getting started guide, but also allows overriding the connection.\n MyDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());\n\n @override\n int get schemaVersion => 1;\n}\n','driftisolate-spawn': 'Future<DriftIsolate> createIsolateWithSpawn() async {\n final token = RootIsolateToken.instance!;\n return await DriftIsolate.spawn(() {\n // This function runs in a new isolate, so we must first initialize the\n // messenger to use platform channels.\n BackgroundIsolateBinaryMessenger.ensureInitialized(token);\n\n // The callback to DriftIsolate.spawn() must return the database connection\n // to use.\n return LazyDatabase(() async {\n // Note that this runs on a background isolate, which only started to\n // support platform channels in Flutter 3.7. For earlier Flutter versions,\n // a workaround is described later in this article.\n final dbFolder = await getApplicationDocumentsDirectory();\n final path = p.join(dbFolder.path, \'app.db\');\n\n return NativeDatabase(File(path));\n });\n });\n}\n','custom-spawn': 'Future<DriftIsolate> createIsolateManually() async {\n final receiveIsolate = ReceivePort(\'receive drift isolate handle\');\n await Isolate.spawn<SendPort>((message) async {\n final server = DriftIsolate.inCurrent(() {\n // Again, this needs to return the LazyDatabase or the connection to use.\n });\n\n // Now, inform the original isolate about the created server:\n message.send(server);\n }, receiveIsolate.sendPort);\n\n final server = await receiveIsolate.first as DriftIsolate;\n receiveIsolate.close();\n return server;\n}\n','delayed': 'MyDatabase(\n DatabaseConnection.delayed(\n Future.sync(() async {\n final isolate = await createIsolate();\n return isolate.connect(singleClientMode: true);\n }),\n ),\n);\n','init_connect': 'DatabaseConnection createDriftIsolateAndConnect() {\n return DatabaseConnection.delayed(\n Future.sync(() async {\n final isolate = await _createDriftIsolate();\n return await isolate.connect(singleClientMode: true);\n }),\n );\n}\n','simple': 'QueryExecutor createSimple() {\n return LazyDatabase(() async {\n final dir = await getApplicationDocumentsDirectory();\n final file = File(p.join(dir.path, \'db.sqlite\'));\n\n // Using createInBackground creates a drift isolate with the recommended\n // options behind the scenes.\n return NativeDatabase.createInBackground(file);\n });\n}\n','invalid': 'Future<void> invalidIsolateUsage() async {\n final database = MyDatabase(NativeDatabase.memory());\n\n // Unfortunately, this doesn\'t work: Drift databases contain references to\n // async primitives like streams and futures that can\'t be serialized across\n // isolates like this.\n await Isolate.run(() async {\n await database.batch((batch) {\n // ...\n });\n });\n}\n','compute': 'Future<void> insertBulkData(MyDatabase database) async {\n // computeWithDatabase is an extension provided by package:drift/isolate.dart\n await database.computeWithDatabase(\n computation: (database) async {\n // Expensive computation that runs on its own isolate but talks to the\n // main database.\n final rows = await _complexAndExpensiveOperationToFetchRows();\n await database.batch((batch) {\n batch.insertAll(database.someTable, rows);\n });\n },\n connect: (connection) {\n // This function is responsible for creating a second instance of your\n // database class with a short-lived [connection].\n // For this to work, your database class needs to have a constructor that\n // allows taking a connection as described above.\n return MyDatabase(connection);\n },\n );\n}\n','custom-compute': 'Future<void> customIsolateUsage(MyDatabase database) async {\n final connection = await database.serializableConnection();\n\n await Isolate.run(() async {\n // We can\'t share the [database] object across isolates, but the connection\n // is fine!\n final databaseForIsolate = MyDatabase(await connection.connect());\n\n try {\n await databaseForIsolate.batch((batch) {\n // (...)\n });\n } finally {\n databaseForIsolate.close();\n }\n }, debugName: \'My custom database task\');\n}\n\n',}, 'lib/src/snippets/custom_row_classes/employee.dart.snippet.json': {'(full)': 'class EmployeeWithStaff {}\n',}, 'lib/src/snippets/custom_row_classes/employees_sql.drift.snippet.json': {'(full)': 'import \'employee.dart\';\n\nCREATE TABLE employees(\n id INTEGER NOT NULL PRIMARY KEY,\n name TEXT NOT NULL,\n supervisor INTEGER REFERENCES employees(id)\n);\n\nemployeeWithStaff WITH EmployeeWithStaff: SELECT\n self.**,\n supervisor.name,\n LIST(SELECT * FROM employees WHERE supervisor = self.id) AS staff\n FROM employees AS self\n INNER JOIN employees supervisor ON supervisor.id = self.supervisor\n WHERE id = ?;\n','example': 'CREATE TABLE employees(\n id INTEGER NOT NULL PRIMARY KEY,\n name TEXT NOT NULL,\n supervisor INTEGER REFERENCES employees(id)\n);\n\nemployeeWithStaff WITH EmployeeWithStaff: SELECT\n self.**,\n supervisor.name,\n LIST(SELECT * FROM employees WHERE supervisor = self.id) AS staff\n FROM employees AS self\n INNER JOIN employees supervisor ON supervisor.id = self.supervisor\n WHERE id = ?;\n',}, 'lib/src/snippets/custom_row_classes/named.dart.snippet.json': {'(full)': 'import \'package:drift/drift.dart\';\n\n@UseRowClass(User, constructor: \'fromDb\')\nclass Users extends Table {\n // ...\n IntColumn get id => integer().autoIncrement()();\n TextColumn get name => text()();\n DateTimeColumn get birthday => dateTime()();\n}\n\nclass User {\n final int id;\n final String name;\n final DateTime birthday;\n\n User.fromDb({required this.id, required this.name, required this.birthday});\n}\n\n','named': '@UseRowClass(User, constructor: \'fromDb\')\nclass Users extends Table {\n // ...\n}\n\nclass User {\n final int id;\n final String name;\n final DateTime birthday;\n\n User.fromDb({required this.id, required this.name, required this.birthday});\n}\n\n',},