From 596a35b58e661b75cab713e90756bf89f979f47a Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 18 Nov 2022 16:33:19 +0100 Subject: [PATCH] Document basic analysis concept --- drift/example/main.g.dart | 5 +- drift/test/generated/custom_tables.g.dart | 162 ++++++++---------- drift/test/generated/todos.g.dart | 9 +- .../integration_tests/drift_files_test.dart | 2 +- drift_dev/lib/src/analysis/backend.dart | 18 ++ drift_dev/lib/src/analysis/driver/cache.dart | 4 + drift_dev/lib/src/analysis/driver/driver.dart | 48 ++++++ drift_dev/lib/src/analysis/driver/state.dart | 7 + .../lib/src/analysis/preprocess_drift.dart | 5 + .../src/analysis/resolver/dart/helper.dart | 4 + .../lib/src/analysis/resolver/discover.dart | 2 + .../src/analysis/resolver/drift/table.dart | 1 + .../src/analysis/resolver/file_analysis.dart | 1 + .../lib/src/analysis/resolver/resolver.dart | 20 +++ drift_dev/lib/src/analysis/serializer.dart | 73 ++++++-- .../lib/src/backends/build/drift_builder.dart | 43 ++++- drift_dev/lib/src/writer/writer.dart | 7 +- examples/app/lib/database/database.g.dart | 23 ++- examples/encryption/lib/database.g.dart | 4 - .../lib/src/database/database.g.dart | 6 +- .../migrations_example/lib/database.g.dart | 30 ++-- .../web_worker_example/lib/database.g.dart | 6 +- .../with_built_value/lib/database.drift.dart | 10 +- examples/with_built_value/lib/tables.drift | 2 +- .../benchmarks/lib/src/moor/database.g.dart | 4 - .../lib/database/database.g.dart | 18 +- ...ves_after_migration_regression_test.g.dart | 4 - 27 files changed, 318 insertions(+), 200 deletions(-) diff --git a/drift/example/main.g.dart b/drift/example/main.g.dart index 4cc5ab89..a58f20e2 100644 --- a/drift/example/main.g.dart +++ b/drift/example/main.g.dart @@ -2,10 +2,7 @@ part of 'main.dart'; -// DriftElementId(asset:drift/example/main.dart, todo_category_item_count) -// DriftElementId(asset:drift/example/main.dart, todo_items) -// DriftElementId(asset:drift/example/main.dart, todo_categories) -// DriftElementId(asset:drift/example/main.dart, customViewName) +// ignore_for_file: type=lint class TodoCategory extends DataClass implements Insertable { final int id; final String name; diff --git a/drift/test/generated/custom_tables.g.dart b/drift/test/generated/custom_tables.g.dart index d71ebc78..02eb01b1 100644 --- a/drift/test/generated/custom_tables.g.dart +++ b/drift/test/generated/custom_tables.g.dart @@ -2,6 +2,7 @@ part of 'custom_tables.dart'; +// ignore_for_file: type=lint class NoIdsCompanion extends UpdateCompanion { final Value payload; const NoIdsCompanion({ @@ -42,11 +43,11 @@ class NoIdsCompanion extends UpdateCompanion { } } -class $NoIdsTable extends Table with TableInfo<$NoIdsTable, NoIdRow> { +class NoIds extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $NoIdsTable(this.attachedDatabase, [this._alias]); + NoIds(this.attachedDatabase, [this._alias]); final VerificationMeta _payloadMeta = const VerificationMeta('payload'); late final GeneratedColumn payload = GeneratedColumn( 'payload', aliasedName, false, @@ -85,8 +86,8 @@ class $NoIdsTable extends Table with TableInfo<$NoIdsTable, NoIdRow> { } @override - $NoIdsTable createAlias(String alias) { - return $NoIdsTable(attachedDatabase, alias); + NoIds createAlias(String alias) { + return NoIds(attachedDatabase, alias); } @override @@ -216,12 +217,11 @@ class WithDefaultsCompanion extends UpdateCompanion { } } -class $WithDefaultsTable extends Table - with TableInfo<$WithDefaultsTable, WithDefault> { +class WithDefaults extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $WithDefaultsTable(this.attachedDatabase, [this._alias]); + WithDefaults(this.attachedDatabase, [this._alias]); final VerificationMeta _aMeta = const VerificationMeta('a'); late final GeneratedColumn a = GeneratedColumn( 'a', aliasedName, true, @@ -269,8 +269,8 @@ class $WithDefaultsTable extends Table } @override - $WithDefaultsTable createAlias(String alias) { - return $WithDefaultsTable(attachedDatabase, alias); + WithDefaults createAlias(String alias) { + return WithDefaults(attachedDatabase, alias); } @override @@ -420,12 +420,12 @@ class WithConstraintsCompanion extends UpdateCompanion { } } -class $WithConstraintsTable extends Table - with TableInfo<$WithConstraintsTable, WithConstraint> { +class WithConstraints extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $WithConstraintsTable(this.attachedDatabase, [this._alias]); + WithConstraints(this.attachedDatabase, [this._alias]); final VerificationMeta _aMeta = const VerificationMeta('a'); late final GeneratedColumn a = GeneratedColumn( 'a', aliasedName, true, @@ -485,8 +485,8 @@ class $WithConstraintsTable extends Table } @override - $WithConstraintsTable createAlias(String alias) { - return $WithConstraintsTable(attachedDatabase, alias); + WithConstraints createAlias(String alias) { + return WithConstraints(attachedDatabase, alias); } @override @@ -514,11 +514,11 @@ class Config extends DataClass implements Insertable { map['config_value'] = Variable(configValue); } if (!nullToAbsent || syncState != null) { - final converter = $ConfigTable.$convertersyncStaten; + final converter = ConfigTable.$convertersyncStaten; map['sync_state'] = Variable(converter.toSql(syncState)); } if (!nullToAbsent || syncStateImplicit != null) { - final converter = $ConfigTable.$convertersyncStateImplicitn; + final converter = ConfigTable.$convertersyncStateImplicitn; map['sync_state_implicit'] = Variable(converter.toSql(syncStateImplicit)); } @@ -657,11 +657,11 @@ class ConfigCompanion extends UpdateCompanion { map['config_value'] = Variable(configValue.value); } if (syncState.present) { - final converter = $ConfigTable.$convertersyncStaten; + final converter = ConfigTable.$convertersyncStaten; map['sync_state'] = Variable(converter.toSql(syncState.value)); } if (syncStateImplicit.present) { - final converter = $ConfigTable.$convertersyncStateImplicitn; + final converter = ConfigTable.$convertersyncStateImplicitn; map['sync_state_implicit'] = Variable(converter.toSql(syncStateImplicit.value)); } @@ -680,11 +680,11 @@ class ConfigCompanion extends UpdateCompanion { } } -class $ConfigTable extends Table with TableInfo<$ConfigTable, Config> { +class ConfigTable extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $ConfigTable(this.attachedDatabase, [this._alias]); + ConfigTable(this.attachedDatabase, [this._alias]); final VerificationMeta _configKeyMeta = const VerificationMeta('configKey'); late final GeneratedColumn configKey = GeneratedColumn( 'config_key', aliasedName, false, @@ -704,7 +704,7 @@ class $ConfigTable extends Table with TableInfo<$ConfigTable, Config> { type: DriftSqlType.int, requiredDuringInsert: false, $customConstraints: '') - .withConverter($ConfigTable.$convertersyncStaten); + .withConverter(ConfigTable.$convertersyncStaten); final VerificationMeta _syncStateImplicitMeta = const VerificationMeta('syncStateImplicit'); late final GeneratedColumnWithTypeConverter @@ -713,7 +713,7 @@ class $ConfigTable extends Table with TableInfo<$ConfigTable, Config> { type: DriftSqlType.int, requiredDuringInsert: false, $customConstraints: '') - .withConverter($ConfigTable.$convertersyncStateImplicitn); + .withConverter(ConfigTable.$convertersyncStateImplicitn); @override List get $columns => [configKey, configValue, syncState, syncStateImplicit]; @@ -753,18 +753,18 @@ class $ConfigTable extends Table with TableInfo<$ConfigTable, Config> { .read(DriftSqlType.string, data['${effectivePrefix}config_key'])!, configValue: attachedDatabase.options.types .read(DriftSqlType.string, data['${effectivePrefix}config_value']), - syncState: $ConfigTable.$convertersyncStaten.fromSql(attachedDatabase + syncState: ConfigTable.$convertersyncStaten.fromSql(attachedDatabase .options.types .read(DriftSqlType.int, data['${effectivePrefix}sync_state'])), - syncStateImplicit: $ConfigTable.$convertersyncStateImplicitn.fromSql( + syncStateImplicit: ConfigTable.$convertersyncStateImplicitn.fromSql( attachedDatabase.options.types.read( DriftSqlType.int, data['${effectivePrefix}sync_state_implicit'])), ); } @override - $ConfigTable createAlias(String alias) { - return $ConfigTable(attachedDatabase, alias); + ConfigTable createAlias(String alias) { + return ConfigTable(attachedDatabase, alias); } static TypeConverter $convertersyncState = @@ -955,11 +955,11 @@ class MytableCompanion extends UpdateCompanion { } } -class $MytableTable extends Table with TableInfo<$MytableTable, MytableData> { +class Mytable extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $MytableTable(this.attachedDatabase, [this._alias]); + Mytable(this.attachedDatabase, [this._alias]); final VerificationMeta _someidMeta = const VerificationMeta('someid'); late final GeneratedColumn someid = GeneratedColumn( 'someid', aliasedName, false, @@ -1037,8 +1037,8 @@ class $MytableTable extends Table with TableInfo<$MytableTable, MytableData> { } @override - $MytableTable createAlias(String alias) { - return $MytableTable(attachedDatabase, alias); + Mytable createAlias(String alias) { + return Mytable(attachedDatabase, alias); } @override @@ -1181,12 +1181,12 @@ class EmailCompanion extends UpdateCompanion { } } -class $EmailTable extends Table - with TableInfo<$EmailTable, EMail>, VirtualTableInfo<$EmailTable, EMail> { +class Email extends Table + with TableInfo, VirtualTableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $EmailTable(this.attachedDatabase, [this._alias]); + Email(this.attachedDatabase, [this._alias]); final VerificationMeta _senderMeta = const VerificationMeta('sender'); late final GeneratedColumn sender = GeneratedColumn( 'sender', aliasedName, false, @@ -1253,8 +1253,8 @@ class $EmailTable extends Table } @override - $EmailTable createAlias(String alias) { - return $EmailTable(attachedDatabase, alias); + Email createAlias(String alias) { + return Email(attachedDatabase, alias); } @override @@ -1381,12 +1381,11 @@ class WeirdTableCompanion extends UpdateCompanion { } } -class $WeirdTableTable extends Table - with TableInfo<$WeirdTableTable, WeirdData> { +class WeirdTable extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $WeirdTableTable(this.attachedDatabase, [this._alias]); + WeirdTable(this.attachedDatabase, [this._alias]); final VerificationMeta _sqlClassMeta = const VerificationMeta('sqlClass'); late final GeneratedColumn sqlClass = GeneratedColumn( 'class', aliasedName, false, @@ -1439,8 +1438,8 @@ class $WeirdTableTable extends Table } @override - $WeirdTableTable createAlias(String alias) { - return $WeirdTableTable(attachedDatabase, alias); + WeirdTable createAlias(String alias) { + return WeirdTable(attachedDatabase, alias); } @override @@ -1548,10 +1547,10 @@ class MyView extends ViewInfo implements HasResultSet { .read(DriftSqlType.string, data['${effectivePrefix}config_key'])!, configValue: attachedDatabase.options.types .read(DriftSqlType.string, data['${effectivePrefix}config_value']), - syncState: $ConfigTable.$convertersyncStaten.fromSql(attachedDatabase + syncState: ConfigTable.$convertersyncStaten.fromSql(attachedDatabase .options.types .read(DriftSqlType.int, data['${effectivePrefix}sync_state'])), - syncStateImplicit: $ConfigTable.$convertersyncStateImplicitn.fromSql( + syncStateImplicit: ConfigTable.$convertersyncStateImplicitn.fromSql( attachedDatabase.options.types.read( DriftSqlType.int, data['${effectivePrefix}sync_state_implicit'])), ); @@ -1566,12 +1565,12 @@ class MyView extends ViewInfo implements HasResultSet { late final GeneratedColumnWithTypeConverter syncState = GeneratedColumn('sync_state', aliasedName, true, type: DriftSqlType.int) - .withConverter($ConfigTable.$convertersyncStaten); + .withConverter(ConfigTable.$convertersyncStaten); late final GeneratedColumnWithTypeConverter syncStateImplicit = GeneratedColumn( 'sync_state_implicit', aliasedName, true, type: DriftSqlType.int) - .withConverter($ConfigTable.$convertersyncStateImplicitn); + .withConverter(ConfigTable.$convertersyncStateImplicitn); @override MyView createAlias(String alias) { return MyView(attachedDatabase, alias); @@ -1586,16 +1585,15 @@ class MyView extends ViewInfo implements HasResultSet { abstract class _$CustomTablesDb extends GeneratedDatabase { _$CustomTablesDb(QueryExecutor e) : super(e); _$CustomTablesDb.connect(DatabaseConnection c) : super.connect(c); - late final $NoIdsTable noIds = $NoIdsTable(this); - late final $WithDefaultsTable withDefaults = $WithDefaultsTable(this); - late final $WithConstraintsTable withConstraints = - $WithConstraintsTable(this); - late final $ConfigTable config = $ConfigTable(this); + late final NoIds noIds = NoIds(this); + late final WithDefaults withDefaults = WithDefaults(this); + late final WithConstraints withConstraints = WithConstraints(this); + late final ConfigTable config = ConfigTable(this); late final Index valueIdx = Index('value_idx', 'CREATE INDEX IF NOT EXISTS value_idx ON config (config_value)'); - late final $MytableTable mytable = $MytableTable(this); - late final $EmailTable email = $EmailTable(this); - late final $WeirdTableTable weirdTable = $WeirdTableTable(this); + late final Mytable mytable = Mytable(this); + late final Email email = Email(this); + late final WeirdTable weirdTable = WeirdTable(this); late final Trigger myTrigger = Trigger( 'CREATE TRIGGER my_trigger AFTER INSERT ON config BEGIN INSERT INTO with_defaults VALUES (new.config_key, LENGTH(new.config_value));END', 'my_trigger'); @@ -1674,11 +1672,11 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { 'SELECT config_key FROM config WHERE ${generatedpred.sql} AND(sync_state = ?1 OR sync_state_implicit IN ($expandedvar2))', variables: [ Variable(NullAwareTypeConverter.wrapToSql( - $ConfigTable.$convertersyncState, var1)), + ConfigTable.$convertersyncState, var1)), ...generatedpred.introducedVariables, for (var $ in var2) Variable(NullAwareTypeConverter.wrapToSql( - $ConfigTable.$convertersyncStateImplicit, $)) + ConfigTable.$convertersyncStateImplicit, $)) ], readsFrom: { config, @@ -1686,14 +1684,14 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { }).map((QueryRow row) => row.read('config_key')); } - Selectable tableValued() { + Selectable tableValued() { return customSelect( 'SELECT "key", value FROM config,json_each(config.config_value)WHERE json_valid(config_value)', variables: [], readsFrom: { config, }).map((QueryRow row) { - return TableValuedResult( + return JsonResult( row: row, key: row.read('key'), value: row.readNullable('value'), @@ -1701,12 +1699,12 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { }); } - Selectable another() { + Selectable another() { return customSelect( 'SELECT \'one\' AS "key", NULLIF(\'two\', \'another\') AS value', variables: [], readsFrom: {}).map((QueryRow row) { - return AnotherResult( + return JsonResult( row: row, key: row.read('key'), value: row.readNullable('value'), @@ -1772,10 +1770,10 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { configKey: row.read('config_key'), configValue: row.readNullable('config_value'), syncState: NullAwareTypeConverter.wrapFromSql( - $ConfigTable.$convertersyncState, + ConfigTable.$convertersyncState, row.readNullable('sync_state')), syncStateImplicit: NullAwareTypeConverter.wrapFromSql( - $ConfigTable.$convertersyncStateImplicit, + ConfigTable.$convertersyncStateImplicit, row.readNullable('sync_state_implicit')), ); }); @@ -1875,14 +1873,14 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { const DriftDatabaseOptions(storeDateTimeAsText: true); } -typedef ReadMultiple$clause = OrderBy Function($ConfigTable config); -typedef ReadDynamic$predicate = Expression Function($ConfigTable config); -typedef TypeConverterVar$pred = Expression Function($ConfigTable config); +typedef ReadMultiple$clause = OrderBy Function(ConfigTable config); +typedef ReadDynamic$predicate = Expression Function(ConfigTable config); +typedef TypeConverterVar$pred = Expression Function(ConfigTable config); -class TableValuedResult extends CustomResultSet { +class JsonResult extends CustomResultSet { final String key; final String? value; - TableValuedResult({ + JsonResult({ required QueryRow row, required this.key, this.value, @@ -1892,38 +1890,12 @@ class TableValuedResult extends CustomResultSet { @override bool operator ==(Object other) => identical(this, other) || - (other is TableValuedResult && + (other is JsonResult && other.key == this.key && other.value == this.value); @override String toString() { - return (StringBuffer('TableValuedResult(') - ..write('key: $key, ') - ..write('value: $value') - ..write(')')) - .toString(); - } -} - -class AnotherResult extends CustomResultSet { - final String key; - final String? value; - AnotherResult({ - required QueryRow row, - required this.key, - this.value, - }) : super(row); - @override - int get hashCode => Object.hash(key, value); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is AnotherResult && - other.key == this.key && - other.value == this.value); - @override - String toString() { - return (StringBuffer('AnotherResult(') + return (StringBuffer('JsonResult(') ..write('key: $key, ') ..write('value: $value') ..write(')')) @@ -1962,7 +1934,7 @@ class MultipleResult extends CustomResultSet { } typedef Multiple$predicate = Expression Function( - $WithDefaultsTable d, $WithConstraintsTable c); + WithDefaults d, WithConstraints c); class ReadRowIdResult extends CustomResultSet { final int rowid; @@ -2003,7 +1975,7 @@ class ReadRowIdResult extends CustomResultSet { } } -typedef ReadRowId$expr = Expression Function($ConfigTable config); +typedef ReadRowId$expr = Expression Function(ConfigTable config); class NestedResult extends CustomResultSet { final WithDefault defaults; diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 3243d14f..952b9973 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -2,10 +2,7 @@ part of 'todos.dart'; -// DriftElementId(asset:drift/test/generated/todos.dart, category_todo_count_view) -// DriftElementId(asset:drift/test/generated/todos.dart, todos) -// DriftElementId(asset:drift/test/generated/todos.dart, categories) -// DriftElementId(asset:drift/test/generated/todos.dart, todo_with_category_view) +// ignore_for_file: type=lint class Category extends DataClass implements Insertable { final int id; final String description; @@ -1731,10 +1728,6 @@ class AllTodosWithCategoryResult extends CustomResultSet { } } -// DriftElementId(asset:drift/test/generated/todos.dart, users) -// DriftElementId(asset:drift/test/generated/todos.dart, shared_todos) -// DriftElementId(asset:drift/test/generated/todos.dart, table_without_p_k) -// DriftElementId(asset:drift/test/generated/todos.dart, pure_defaults) mixin _$SomeDaoMixin on DatabaseAccessor { $UsersTable get users => attachedDatabase.users; $SharedTodosTable get sharedTodos => attachedDatabase.sharedTodos; diff --git a/drift/test/integration_tests/drift_files_test.dart b/drift/test/integration_tests/drift_files_test.dart index d3e8d214..fa829c6f 100644 --- a/drift/test/integration_tests/drift_files_test.dart +++ b/drift/test/integration_tests/drift_files_test.dart @@ -242,6 +242,6 @@ void main() { .getSingleOrNull(); verify(mock.runSelect('SELECT * FROM "config" WHERE "sync_state" = ?;', - [$ConfigTable.$convertersyncState.toSql(SyncType.synchronized)])); + [ConfigTable.$convertersyncState.toSql(SyncType.synchronized)])); }); } diff --git a/drift_dev/lib/src/analysis/backend.dart b/drift_dev/lib/src/analysis/backend.dart index 2c551ab2..9662407d 100644 --- a/drift_dev/lib/src/analysis/backend.dart +++ b/drift_dev/lib/src/analysis/backend.dart @@ -2,18 +2,36 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:logging/logging.dart'; +/// The backend used by drift's analysis implementation to read files and access +/// the Dart analyzer. abstract class DriftBackend { + /// A logger through which drift's analyzer will emit internal warnings and + /// debugging information. Logger get log; + /// Resolves a uri and normalizes it into a format used by this backend. Uri resolveUri(Uri base, String uriString); + /// Reads a file as string. Future readAsString(Uri uri); Future uriOfDart(Element element) async { return element.source!.uri; } + /// Resolves a Dart library by its uri. + /// + /// This should also be able to resolve SDK libraries. + /// If no Dart library can be found under that uri, throws a + /// [NotALibraryException]. Future readDart(Uri uri); + + /// Loads the resolved AST node defining the given [element]. + /// + /// Depending on how the analyzer is accessed, this may throw an exception if + /// the resolved AST is not available. + /// When the [element] does not have a syntactic representation in the AST, + /// null is returned. Future loadElementDeclaration(Element element); /// Resolves a Dart expression from a string. diff --git a/drift_dev/lib/src/analysis/driver/cache.dart b/drift_dev/lib/src/analysis/driver/cache.dart index 19ac6bfe..b38b72b1 100644 --- a/drift_dev/lib/src/analysis/driver/cache.dart +++ b/drift_dev/lib/src/analysis/driver/cache.dart @@ -1,7 +1,11 @@ import '../results/element.dart'; import 'state.dart'; +/// An in-memory cache of analysis results for drift elements. +/// +/// At the moment, the cache is not set up to handle changing files. class DriftAnalysisCache { + final Map> serializedElements = {}; final Map knownFiles = {}; final Map discoveredElements = {}; diff --git a/drift_dev/lib/src/analysis/driver/driver.dart b/drift_dev/lib/src/analysis/driver/driver.dart index 8957c06f..d183c5a3 100644 --- a/drift_dev/lib/src/analysis/driver/driver.dart +++ b/drift_dev/lib/src/analysis/driver/driver.dart @@ -16,6 +16,40 @@ import 'cache.dart'; import 'error.dart'; import 'state.dart'; +/// The main entrypoint for drift element analysis. +/// +/// The purpose of this analyzer is to extract tables, views, databases and +/// other elements of interest to drift from source files. Where possible, the +/// analysis steps should be modular, meaning that they don't require a central +/// entrypoint like a database class. Instead, every element can be analyzed in +/// isolation (except for its dependencies). +/// +/// Analysis currently happens in three stages: +/// +/// 1. __Discovery__: In this first step, the names and types of drift elements +/// is detected in each file. After this step, we might know that there's a +/// table named "users" in a file named "tables.drift", but we don't know its +/// columns yet. This enables the analysis stage to efficiently resolve +/// references. The step is mainly implemented in [DiscoverStep] and +/// [prepareFileForAnalysis]. +/// 2. __Element analysis__: In this step, discovered entries from the first +/// step are fully resolved. +/// Resolving elements happens in a depth-first approach, where dependencies +/// are analyzed before dependants. It is forbidden to have circular references +/// between elements (which is detected and handled gracefully). This step is +/// coordinated by a [DriftResolver], with the classes in `resolver/dart` and +/// `resolver/drift` being responsible for the individual analysis work for +/// different element types. +/// 3. __File analysis__: In this final step, some elements are analyzed again +/// to fully resolve them. This includes drift databases, drift accessors and +/// queries defined in `.drift` files. They require all other elements to be +/// fully analyzed. +/// The main motivation for this being a third step is that the results of +/// resolving queries are very difficult to serialize. At the moment, modular +/// analysis is implemented by serializing the results of the second step +/// (element analysis). By running file analysis later and only for entrypoints +/// where that is required, we obtain a reasonable degree of modularity without +/// having to serialize the complex model of serialized queries. class DriftAnalysisDriver { final DriftBackend backend; final DriftAnalysisCache cache = DriftAnalysisCache(); @@ -47,10 +81,15 @@ class DriftAnalysisDriver { ); } + /// Loads types important for Drift analysis. Future loadKnownTypes() async { return _knownTypes ??= await KnownDriftTypes.resolve(this); } + /// For a given file under [uri], attempts to restore serialized analysis + /// results that have been stored before. + /// + /// Returns non-null if analysis results were found and successfully restored. Future?> readStoredAnalysisResult(Uri uri) async { final cached = cache.serializedElements[uri]; if (cached != null) return cached; @@ -85,6 +124,7 @@ class DriftAnalysisDriver { return allRecovered; } + /// Runs the first step (element discovery) on a file with the given [uri]. Future prepareFileForAnalysis(Uri uri, {bool needsDiscovery = true}) async { var known = cache.knownFiles[uri] ?? cache.notifyFileChanged(uri); @@ -120,6 +160,11 @@ class DriftAnalysisDriver { return known; } + /// Runs the second analysis step (element analysis) on a file. + /// + /// The file, as well as all imports, should have undergone the first analysis + /// step (discovery) at this point, so that the resolver is able to + /// recognize dependencies between different elements. Future _analyzePrepared(FileState state) async { assert(state.discovery != null); @@ -136,6 +181,8 @@ class DriftAnalysisDriver { } } + /// Resolves elements in a file under the given [uri] by doing all the + /// necessary work up until that point. Future resolveElements(Uri uri) async { var known = cache.stateForUri(uri); await prepareFileForAnalysis(uri, needsDiscovery: false); @@ -159,6 +206,7 @@ class DriftAnalysisDriver { return known; } + /// Fully analyzes a file under the [uri] by running all analysis steps. Future fullyAnalyze(Uri uri) async { // First, make sure that elements in this file and all imports are fully // resolved. diff --git a/drift_dev/lib/src/analysis/driver/state.dart b/drift_dev/lib/src/analysis/driver/state.dart index 2a50a2ba..8b037f7f 100644 --- a/drift_dev/lib/src/analysis/driver/state.dart +++ b/drift_dev/lib/src/analysis/driver/state.dart @@ -3,6 +3,7 @@ import 'package:meta/meta.dart'; import 'package:path/path.dart' show url; import 'package:sqlparser/sqlparser.dart' hide AnalysisError; +import '../results/database.dart'; import '../results/element.dart'; import '../results/file_results.dart'; import 'error.dart'; @@ -20,6 +21,12 @@ class FileState { String get extension => url.extension(ownUri.path); + /// Whether this file contains a drift database or a drift accessor / DAO. + bool get containsDatabaseAccessor { + return analyzedElements.any((e) => e is BaseDriftAccessor); + } + + /// All analyzed [DriftElement]s found in this library. @visibleForTesting Iterable get analyzedElements { return analysis.values.map((e) => e.result).whereType(); diff --git a/drift_dev/lib/src/analysis/preprocess_drift.dart b/drift_dev/lib/src/analysis/preprocess_drift.dart index 4e189759..80878207 100644 --- a/drift_dev/lib/src/analysis/preprocess_drift.dart +++ b/drift_dev/lib/src/analysis/preprocess_drift.dart @@ -48,6 +48,11 @@ class DriftPreprocessorResult { Map toJson() => _$DriftPreprocessorResultToJson(this); } +/// Processes `.drift` files to extract all embedded Dart snippets. +/// +/// To analyze drift files in the build system, we extract these snippets into +/// a standalone Dart file so that we're able to analyze them with the resolvers +/// provided by the build system. class DriftPreprocessor { final DriftPreprocessorResult result; final String temporaryDartFile; diff --git a/drift_dev/lib/src/analysis/resolver/dart/helper.dart b/drift_dev/lib/src/analysis/resolver/dart/helper.dart index 817ac69a..7422a8ca 100644 --- a/drift_dev/lib/src/analysis/resolver/dart/helper.dart +++ b/drift_dev/lib/src/analysis/resolver/dart/helper.dart @@ -7,6 +7,10 @@ import 'package:collection/collection.dart'; import '../../driver/driver.dart'; import '../../results/results.dart'; +/// A collection of elements and Dart types important to Drift. +/// +/// These types are used to determine whether a given Dart class has drift- +/// specific annotations or whether it defines a table. class KnownDriftTypes { final LibraryElement helperLibrary; final ClassElement tableElement; diff --git a/drift_dev/lib/src/analysis/resolver/discover.dart b/drift_dev/lib/src/analysis/resolver/discover.dart index d22570d2..a43c3bb0 100644 --- a/drift_dev/lib/src/analysis/resolver/discover.dart +++ b/drift_dev/lib/src/analysis/resolver/discover.dart @@ -13,6 +13,8 @@ import '../results/element.dart'; import 'dart/helper.dart'; import 'intermediate_state.dart'; +/// Finds the name and kind (e.g. table, view, database, index, ...) of entries +/// defined in a given file. class DiscoverStep { final DriftAnalysisDriver _driver; final FileState _file; diff --git a/drift_dev/lib/src/analysis/resolver/drift/table.dart b/drift_dev/lib/src/analysis/resolver/drift/table.dart index 6b68c7ac..2a76c0c1 100644 --- a/drift_dev/lib/src/analysis/resolver/drift/table.dart +++ b/drift_dev/lib/src/analysis/resolver/drift/table.dart @@ -336,6 +336,7 @@ class DriftTableResolver extends LocalElementResolver { references: references.toList(), nameOfRowClass: dataClassName, baseDartName: dartTableName, + fixedEntityInfoName: dartTableName, existingRowClass: existingRowClass, withoutRowId: table.withoutRowId, strict: table.isStrict, diff --git a/drift_dev/lib/src/analysis/resolver/file_analysis.dart b/drift_dev/lib/src/analysis/resolver/file_analysis.dart index 376f7991..95291e87 100644 --- a/drift_dev/lib/src/analysis/resolver/file_analysis.dart +++ b/drift_dev/lib/src/analysis/resolver/file_analysis.dart @@ -9,6 +9,7 @@ import '../results/results.dart'; import 'queries/query_analyzer.dart'; import 'queries/required_variables.dart'; +/// Fully resolves databases and queries after elements have been resolved. class FileAnalyzer { final DriftAnalysisDriver driver; diff --git a/drift_dev/lib/src/analysis/resolver/resolver.dart b/drift_dev/lib/src/analysis/resolver/resolver.dart index e5f2dff2..0a8230d0 100644 --- a/drift_dev/lib/src/analysis/resolver/resolver.dart +++ b/drift_dev/lib/src/analysis/resolver/resolver.dart @@ -16,13 +16,18 @@ import 'drift/trigger.dart' as drift_trigger; import 'drift/view.dart' as drift_view; import 'intermediate_state.dart'; +/// Analyzes and resolves drift elements. class DriftResolver { final DriftAnalysisDriver driver; + /// The current depth-first path of drift elements being analyzed. + /// + /// This path is used to detect and prevent circular references. final List _currentDependencyPath = []; DriftResolver(this.driver); + /// Resolves a discovered element by analyzing it and its dependencies. Future resolveDiscovered(DiscoveredElement discovered) async { LocalElementResolver resolver; @@ -68,6 +73,13 @@ class DriftResolver { return resolved; } + /// Attempts to resolve a dependency for an element if that is allowed. + /// + /// It usually _is_ allowed, but there could be a forbidden circular reference + /// in which case the reference is reported to be unavailable. + /// Further, an internal bug in the analyzer could cause a crash analyzing + /// the element. To not cause the entire analysis run to fail, this reports + /// an error message and otherwise continues analysis of other elements. Future resolveReferencedElement( DriftElementId owner, DriftElementId reference) async { if (owner == reference) { @@ -117,6 +129,8 @@ class DriftResolver { 'Unknown pending element $reference, this is a bug in drift_dev'); } + /// Resolves a Dart element reference, if the referenced Dart [element] + /// defines an element understood by drift. Future resolveDartReference( DriftElementId owner, Element element) async { final uri = await driver.backend.uriOfDart(element.library!); @@ -136,6 +150,12 @@ class DriftResolver { } } + /// Resolves a reference in SQL. + /// + /// This works by looking at known imports of the file defining the [owner] + /// and using the results of the discovery step to find a known element with + /// the same name. If one exists, it is resolved and returned. Otherwise, an + /// error result is returned. Future resolveReference( DriftElementId owner, String reference) async { final candidates = []; diff --git a/drift_dev/lib/src/analysis/serializer.dart b/drift_dev/lib/src/analysis/serializer.dart index ecfe2a49..f181060c 100644 --- a/drift_dev/lib/src/analysis/serializer.dart +++ b/drift_dev/lib/src/analysis/serializer.dart @@ -10,6 +10,12 @@ import 'driver/driver.dart'; import 'driver/state.dart'; import 'results/results.dart'; +/// Serializes [DriftElement]s to JSON. +/// +/// By first analyzing elements and later generating code, drift's build setup +/// is more efficient and incremental (as not everything is analyzed again if +/// a single file changes). However, it means that we have to serialize analysis +/// results to read them back in in a later build step. class ElementSerializer { Map serializeElements(Iterable elements) { return { @@ -346,6 +352,7 @@ class _DartTypeSerializer extends TypeVisitor> { } } +/// Deserializes the element structure emitted by [ElementSerializer]. class ElementDeserializer { final Map _loadedLibraries = {}; final List _currentlyReading = []; @@ -404,7 +411,12 @@ class ElementDeserializer { 'Analysis data for ${id..libraryUri} not found'); } - assert(!_currentlyReading.contains(id)); + if (_currentlyReading.contains(id)) { + throw StateError( + 'Circular error when deserializing drift modules. This is a ' + 'bug in drift_dev!'); + } + try { _currentlyReading.add(id); @@ -445,7 +457,7 @@ class ElementDeserializer { case 'table': final columns = [ for (final rawColumn in json['columns'] as List) - await _readColumn(rawColumn as Map), + await _readColumn(rawColumn as Map, id), ]; final columnByName = { for (final column in columns) column.nameInSql: column, @@ -478,7 +490,7 @@ class ElementDeserializer { ); } - return DriftTable( + final table = DriftTable( id, declaration, references: references, @@ -504,6 +516,23 @@ class ElementDeserializer { ? (json['custom_constraints'] as List).cast() : null, ); + + for (final column in columns) { + for (var i = 0; i < column.constraints.length; i++) { + final constraint = column.constraints[i]; + + if (constraint is _PendingReferenceToOwnTable) { + column.constraints[i] = ForeignKeyReference( + columns.singleWhere( + (e) => e.nameInSql == constraint.referencedColumn), + constraint.onUpdate, + constraint.onDelete, + ); + } + } + } + + return table; case 'index': return DriftIndex( id, @@ -547,7 +576,7 @@ class ElementDeserializer { case 'view': final columns = [ for (final rawColumn in json['columns'] as List) - await _readColumn(rawColumn as Map), + await _readColumn(rawColumn as Map, id), ]; final serializedSource = json['source'] as Map; @@ -622,7 +651,7 @@ class ElementDeserializer { declaredViews: views, declaredIncludes: includes, declaredQueries: queries, - schemaVersion: json['schema_version'] as int, + schemaVersion: json['schema_version'] as int?, accessorTypes: [ for (final dao in json['daos']) AnnotatedDartCode.fromJson(dao as Map) @@ -646,7 +675,7 @@ class ElementDeserializer { } } - Future _readColumn(Map json) async { + Future _readColumn(Map json, DriftElementId ownTable) async { final rawConverter = json['typeConverter'] as Map?; return DriftColumn( @@ -668,7 +697,7 @@ class ElementDeserializer { documentationComment: json['documentationComment'] as String?, constraints: [ for (final rawConstraint in json['constraints'] as List) - await _readConstraint(rawConstraint as Map) + await _readConstraint(rawConstraint as Map, ownTable) ], customConstraints: json['customConstraints'] as String?, ); @@ -701,7 +730,8 @@ class ElementDeserializer { return value == null ? null : ReferenceAction.values.byName(value); } - Future _readConstraint(Map json) async { + Future _readConstraint( + Map json, DriftElementId ownTable) async { final type = json['type'] as String; switch (type) { @@ -710,11 +740,20 @@ class ElementDeserializer { case 'primary': return PrimaryKeyColumn.fromJson(json); case 'foreign_key': - return ForeignKeyReference( - await _readDriftColumnReference(json['column'] as Map), - _readAction(json['onUpdate'] as String?), - _readAction(json['onDelete'] as String?), - ); + final table = DriftElementId.fromJson(json['column']['table'] as Map); + if (table == ownTable) { + return _PendingReferenceToOwnTable( + json['column']['name'] as String, + _readAction(json['onUpdate'] as String?), + _readAction(json['onDelete'] as String?), + ); + } else { + return ForeignKeyReference( + await _readDriftColumnReference(json['column'] as Map), + _readAction(json['onUpdate'] as String?), + _readAction(json['onDelete'] as String?), + ); + } case 'generated_as': return ColumnGeneratedAs.fromJson(json); case 'check': @@ -767,3 +806,11 @@ class CouldNotDeserializeException implements Exception { @override String toString() => message; } + +class _PendingReferenceToOwnTable extends DriftColumnConstraint { + final String referencedColumn; + final ReferenceAction? onUpdate, onDelete; + + _PendingReferenceToOwnTable( + this.referencedColumn, this.onUpdate, this.onDelete); +} diff --git a/drift_dev/lib/src/backends/build/drift_builder.dart b/drift_dev/lib/src/backends/build/drift_builder.dart index 4e9f85b4..9576854d 100644 --- a/drift_dev/lib/src/backends/build/drift_builder.dart +++ b/drift_dev/lib/src/backends/build/drift_builder.dart @@ -6,6 +6,7 @@ import '../../analysis/driver/driver.dart'; import '../../analysis/driver/state.dart'; import '../../analysis/results/results.dart'; import '../../analysis/options.dart'; +import '../../utils/string_escaper.dart'; import '../../writer/database_writer.dart'; import '../../writer/drift_accessor_writer.dart'; import '../../writer/import_manager.dart'; @@ -32,6 +33,14 @@ enum DriftGenerationMode { monolithicPart; bool get isMonolithic => true; + + /// Whether the analysis happens in the generating build step. + /// + /// For most generation modes, we run analysis work in a previous build step. + /// For backwards compatibility and since the result of the analysis work + /// should not be user-visible, the non-shared part builder runs its analysis + /// work in the generation build step. + bool get embeddedAnalyzer => this == DriftGenerationMode.monolithicPart; } class DriftBuilder extends Builder { @@ -74,13 +83,17 @@ class DriftBuilder extends Builder { final driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options) ..cacheReader = BuildCacheReader(buildStep); - final fromCache = - await driver.readStoredAnalysisResult(buildStep.inputId.uri); + if (!generationMode.embeddedAnalyzer) { + // An analysis step should have already run for this asset. If we can't + // pick up results from that, there is no code for drift to generate. + final fromCache = + await driver.readStoredAnalysisResult(buildStep.inputId.uri); - if (fromCache == null) { - // Don't do anything! There are no analysis results for this file, so - // there's nothing for drift to generate code for. - return; + if (fromCache == null) { + // Don't do anything! There are no analysis results for this file, so + // there's nothing for drift to generate code for. + return; + } } Set analyzedUris = {}; @@ -98,6 +111,12 @@ class DriftBuilder extends Builder { final fileResult = await analyze(buildStep.inputId.uri); + // For the monolithic build modes, we only generate code for databases and + // crawl the tables from there. + if (generationMode.isMonolithic && !fileResult.containsDatabaseAccessor) { + return; + } + final generationOptions = GenerationOptions( imports: ImportManagerForPartFiles(), ); @@ -140,13 +159,19 @@ class DriftBuilder extends Builder { AccessorGenerationInput(result, resolved, importedQueries); AccessorWriter(input, writer.child()).write(); } - } else { - writer.leaf().writeln('// ${element.ownId}'); } } - var generated = writer.writeGenerated(); + final output = StringBuffer(); + output.writeln('// ignore_for_file: type=lint'); + if (generationMode == DriftGenerationMode.monolithicPart) { + final originalFile = buildStep.inputId.pathSegments.last; + output.writeln('part of ${asDartLiteral(originalFile)};'); + } + output.write(writer.writeGenerated()); + + var generated = output.toString(); try { generated = DartFormatter().format(generated); } on FormatterException { diff --git a/drift_dev/lib/src/writer/writer.dart b/drift_dev/lib/src/writer/writer.dart index 80b51204..d5944e91 100644 --- a/drift_dev/lib/src/writer/writer.dart +++ b/drift_dev/lib/src/writer/writer.dart @@ -68,8 +68,9 @@ abstract class _NodeOrWriter { /// Returns a Dart expression evaluating to the [converter]. AnnotatedDartCode readConverter(AppliedTypeConverter converter, {bool forNullable = false}) { - final fieldName = - forNullable ? converter.nullableFieldName : converter.fieldName; + final fieldName = forNullable && converter.canBeSkippedForNulls + ? converter.nullableFieldName + : converter.fieldName; final table = converter.owningColumn.owner; return AnnotatedDartCode([ @@ -95,7 +96,7 @@ abstract class _NodeOrWriter { ..questionMarkIfNullable(makeNullable) ..addText(',') ..addTopLevel(sqlDartType) - ..questionMarkIfNullable(makeNullable); + ..questionMarkIfNullable(makeNullable || converter.sqlTypeIsNullable); if (converter.alsoAppliesToJsonConversion) { b diff --git a/examples/app/lib/database/database.g.dart b/examples/app/lib/database/database.g.dart index 4dd6dc97..ffe4224c 100644 --- a/examples/app/lib/database/database.g.dart +++ b/examples/app/lib/database/database.g.dart @@ -2,10 +2,6 @@ part of 'database.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - // ignore_for_file: type=lint class Category extends DataClass implements Insertable { final int id; @@ -18,7 +14,7 @@ class Category extends DataClass implements Insertable { map['id'] = Variable(id); map['name'] = Variable(name); { - final converter = $CategoriesTable.$converter0; + final converter = $CategoriesTable.$convertercolor; map['color'] = Variable(converter.toSql(color)); } return map; @@ -123,7 +119,7 @@ class CategoriesCompanion extends UpdateCompanion { map['name'] = Variable(name.value); } if (color.present) { - final converter = $CategoriesTable.$converter0; + final converter = $CategoriesTable.$convertercolor; map['color'] = Variable(converter.toSql(color.value)); } return map; @@ -163,7 +159,7 @@ class $CategoriesTable extends Categories late final GeneratedColumnWithTypeConverter color = GeneratedColumn('color', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true) - .withConverter($CategoriesTable.$converter0); + .withConverter($CategoriesTable.$convertercolor); @override List get $columns => [id, name, color]; @override @@ -198,7 +194,8 @@ class $CategoriesTable extends Categories .read(DriftSqlType.int, data['${effectivePrefix}id'])!, name: attachedDatabase.options.types .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - color: $CategoriesTable.$converter0.fromSql(attachedDatabase.options.types + color: $CategoriesTable.$convertercolor.fromSql(attachedDatabase + .options.types .read(DriftSqlType.int, data['${effectivePrefix}color'])!), ); } @@ -208,7 +205,7 @@ class $CategoriesTable extends Categories return $CategoriesTable(attachedDatabase, alias); } - static TypeConverter $converter0 = const ColorConverter(); + static TypeConverter $convertercolor = const ColorConverter(); } class TodoEntry extends DataClass implements Insertable { @@ -402,7 +399,7 @@ class $TodoEntriesTable extends TodoEntries 'category', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, - defaultConstraints: 'REFERENCES "categories" ("id")'); + defaultConstraints: 'REFERENCES categories (id)'); final VerificationMeta _dueDateMeta = const VerificationMeta('dueDate'); @override late final GeneratedColumn dueDate = GeneratedColumn( @@ -592,7 +589,7 @@ class TextEntries extends Table } @override - Set get $primaryKey => {}; + Set get $primaryKey => const {}; @override TextEntrie map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; @@ -607,6 +604,8 @@ class TextEntries extends Table return TextEntries(attachedDatabase, alias); } + @override + List get customConstraints => const []; @override bool get dontWriteConstraints => true; @override @@ -641,7 +640,7 @@ abstract class _$AppDatabase extends GeneratedDatabase { id: row.readNullable('id'), name: row.readNullable('name'), color: NullAwareTypeConverter.wrapFromSql( - $CategoriesTable.$converter0, row.readNullable('color')), + $CategoriesTable.$convertercolor, row.readNullable('color')), amount: row.read('amount'), ); }); diff --git a/examples/encryption/lib/database.g.dart b/examples/encryption/lib/database.g.dart index 3c3ad0af..0f5427ad 100644 --- a/examples/encryption/lib/database.g.dart +++ b/examples/encryption/lib/database.g.dart @@ -2,10 +2,6 @@ part of 'database.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - // ignore_for_file: type=lint class Note extends DataClass implements Insertable { final int id; diff --git a/examples/flutter_web_worker_example/lib/src/database/database.g.dart b/examples/flutter_web_worker_example/lib/src/database/database.g.dart index eefe72ae..9b33d43c 100644 --- a/examples/flutter_web_worker_example/lib/src/database/database.g.dart +++ b/examples/flutter_web_worker_example/lib/src/database/database.g.dart @@ -2,10 +2,6 @@ part of 'database.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - // ignore_for_file: type=lint class Entrie extends DataClass implements Insertable { final int id; @@ -172,6 +168,8 @@ class Entries extends Table with TableInfo { return Entries(attachedDatabase, alias); } + @override + List get customConstraints => const []; @override bool get dontWriteConstraints => true; } diff --git a/examples/migrations_example/lib/database.g.dart b/examples/migrations_example/lib/database.g.dart index 44fa9264..12850681 100644 --- a/examples/migrations_example/lib/database.g.dart +++ b/examples/migrations_example/lib/database.g.dart @@ -2,10 +2,6 @@ part of 'database.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - // ignore_for_file: type=lint class User extends DataClass implements Insertable { final int id; @@ -200,7 +196,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> { 'next_user', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, - defaultConstraints: 'REFERENCES "users" ("id")'); + defaultConstraints: 'REFERENCES users (id)'); @override List get $columns => [id, name, birthday, nextUser]; @override @@ -443,13 +439,13 @@ class Groups extends Table with TableInfo { type: DriftSqlType.bool, requiredDuringInsert: false, $customConstraints: 'DEFAULT FALSE', - defaultValue: const CustomExpression('FALSE')); + defaultValue: const CustomExpression('FALSE')); final VerificationMeta _ownerMeta = const VerificationMeta('owner'); late final GeneratedColumn owner = GeneratedColumn( 'owner', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true, - $customConstraints: 'NOT NULL REFERENCES users (id)'); + $customConstraints: 'NOT NULL REFERENCES users(id)'); @override List get $columns => [id, title, deleted, owner]; @override @@ -506,7 +502,7 @@ class Groups extends Table with TableInfo { } @override - List get customConstraints => const ['PRIMARY KEY (id)']; + List get customConstraints => const ['PRIMARY KEY(id)']; @override bool get dontWriteConstraints => true; } @@ -704,7 +700,7 @@ class Notes extends Table } @override - Set get $primaryKey => {}; + Set get $primaryKey => const {}; @override Note map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; @@ -723,6 +719,8 @@ class Notes extends Table return Notes(attachedDatabase, alias); } + @override + List get customConstraints => const []; @override bool get dontWriteConstraints => true; @override @@ -749,8 +747,8 @@ class GroupCountData extends DataClass { id: serializer.fromJson(json['id']), name: serializer.fromJson(json['name']), birthday: serializer.fromJson(json['birthday']), - nextUser: serializer.fromJson(json['nextUser']), - groupCount: serializer.fromJson(json['groupCount']), + nextUser: serializer.fromJson(json['next_user']), + groupCount: serializer.fromJson(json['group_count']), ); } @override @@ -760,8 +758,8 @@ class GroupCountData extends DataClass { 'id': serializer.toJson(id), 'name': serializer.toJson(name), 'birthday': serializer.toJson(birthday), - 'nextUser': serializer.toJson(nextUser), - 'groupCount': serializer.toJson(groupCount), + 'next_user': serializer.toJson(nextUser), + 'group_count': serializer.toJson(groupCount), }; } @@ -818,7 +816,7 @@ class GroupCount extends ViewInfo String get entityName => 'group_count'; @override String get createViewStmt => - 'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users'; + 'CREATE VIEW group_count AS SELECT\n users.*,\n (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count\n FROM users;'; @override GroupCount get asDslTable => this; @override @@ -868,14 +866,14 @@ abstract class _$Database extends GeneratedDatabase { _$Database.connect(DatabaseConnection c) : super.connect(c); late final $UsersTable users = $UsersTable(this); late final Groups groups = Groups(this); - late final GroupCount groupCount = GroupCount(this); late final Notes notes = Notes(this); + late final GroupCount groupCount = GroupCount(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => - [users, groups, groupCount, notes]; + [users, groups, notes, groupCount]; @override DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); diff --git a/examples/web_worker_example/lib/database.g.dart b/examples/web_worker_example/lib/database.g.dart index 9a7521eb..64342e33 100644 --- a/examples/web_worker_example/lib/database.g.dart +++ b/examples/web_worker_example/lib/database.g.dart @@ -2,10 +2,6 @@ part of 'database.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - // ignore_for_file: type=lint class Entrie extends DataClass implements Insertable { final int id; @@ -172,6 +168,8 @@ class Entries extends Table with TableInfo { return Entries(attachedDatabase, alias); } + @override + List get customConstraints => const []; @override bool get dontWriteConstraints => true; } diff --git a/examples/with_built_value/lib/database.drift.dart b/examples/with_built_value/lib/database.drift.dart index ee0843a0..a7f87b4e 100644 --- a/examples/with_built_value/lib/database.drift.dart +++ b/examples/with_built_value/lib/database.drift.dart @@ -1,12 +1,6 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - +// ignore_for_file: type=lint part of 'database.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - -// ignore_for_file: type=lint class User extends DataClass implements Insertable { final int id; final String name; @@ -172,6 +166,8 @@ class Users extends Table with TableInfo { return Users(attachedDatabase, alias); } + @override + List get customConstraints => const []; @override bool get dontWriteConstraints => true; } diff --git a/examples/with_built_value/lib/tables.drift b/examples/with_built_value/lib/tables.drift index 22f00902..a7601bbb 100644 --- a/examples/with_built_value/lib/tables.drift +++ b/examples/with_built_value/lib/tables.drift @@ -1,4 +1,4 @@ CREATE TABLE users ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL + name TEXT NOT NULL ); diff --git a/extras/benchmarks/lib/src/moor/database.g.dart b/extras/benchmarks/lib/src/moor/database.g.dart index 2788c144..04e11e56 100644 --- a/extras/benchmarks/lib/src/moor/database.g.dart +++ b/extras/benchmarks/lib/src/moor/database.g.dart @@ -2,10 +2,6 @@ part of 'database.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - // ignore_for_file: type=lint class KeyValue extends DataClass implements Insertable { final String key; diff --git a/extras/integration_tests/drift_testcases/lib/database/database.g.dart b/extras/integration_tests/drift_testcases/lib/database/database.g.dart index 4cce27ff..7cde4c0d 100644 --- a/extras/integration_tests/drift_testcases/lib/database/database.g.dart +++ b/extras/integration_tests/drift_testcases/lib/database/database.g.dart @@ -2,10 +2,6 @@ part of 'database.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - // ignore_for_file: type=lint class User extends DataClass implements Insertable { /// The user id @@ -34,7 +30,7 @@ class User extends DataClass implements Insertable { map['profile_picture'] = Variable(profilePicture); } if (!nullToAbsent || preferences != null) { - final converter = $UsersTable.$converter0; + final converter = $UsersTable.$converterpreferences; map['preferences'] = Variable(converter.toSql(preferences)); } return map; @@ -190,7 +186,7 @@ class UsersCompanion extends UpdateCompanion { map['profile_picture'] = Variable(profilePicture.value); } if (preferences.present) { - final converter = $UsersTable.$converter0; + final converter = $UsersTable.$converterpreferences; map['preferences'] = Variable(converter.toSql(preferences.value)); } return map; @@ -243,7 +239,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> { late final GeneratedColumnWithTypeConverter preferences = GeneratedColumn('preferences', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter($UsersTable.$converter0); + .withConverter($UsersTable.$converterpreferences); @override List get $columns => [id, name, birthDate, profilePicture, preferences]; @@ -295,7 +291,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> { .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date'])!, profilePicture: attachedDatabase.options.types .read(DriftSqlType.blob, data['${effectivePrefix}profile_picture']), - preferences: $UsersTable.$converter0.fromSql(attachedDatabase + preferences: $UsersTable.$converterpreferences.fromSql(attachedDatabase .options.types .read(DriftSqlType.string, data['${effectivePrefix}preferences'])), ); @@ -306,7 +302,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> { return $UsersTable(attachedDatabase, alias); } - static TypeConverter $converter0 = + static TypeConverter $converterpreferences = const PreferenceConverter(); } @@ -474,7 +470,7 @@ class $FriendshipsTable extends Friendships 'really_good_friends', aliasedName, false, type: DriftSqlType.bool, requiredDuringInsert: false, - defaultConstraints: 'CHECK ("really_good_friends" IN (0, 1))', + defaultConstraints: 'CHECK (really_good_friends IN (0, 1))', defaultValue: const Constant(false)); @override List get $columns => @@ -592,7 +588,7 @@ abstract class _$Database extends GeneratedDatabase { ], readsFrom: { users, - }).map((QueryRow row) => $UsersTable.$converter0 + }).map((QueryRow row) => $UsersTable.$converterpreferences .fromSql(row.readNullable('preferences'))); } diff --git a/extras/integration_tests/web/test/saves_after_migration_regression_test.g.dart b/extras/integration_tests/web/test/saves_after_migration_regression_test.g.dart index eae7a020..cc6ba914 100644 --- a/extras/integration_tests/web/test/saves_after_migration_regression_test.g.dart +++ b/extras/integration_tests/web/test/saves_after_migration_regression_test.g.dart @@ -2,10 +2,6 @@ part of 'saves_after_migration_regression_test.dart'; -// ************************************************************************** -// DriftDatabaseGenerator -// ************************************************************************** - // ignore_for_file: type=lint class Foo extends DataClass implements Insertable { final int id;