Begin optional modular code generation

This commit is contained in:
Simon Binder 2022-11-20 17:28:27 +01:00
parent f41cff5fc6
commit f4e45584ec
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
25 changed files with 871 additions and 140 deletions

View File

@ -0,0 +1,66 @@
/// Internal library used by generated code when drift's modular code generation
/// is enabled.
///
/// This library is not part of drift's public API and should not be imported
/// manually.
library drift.internal.modules;
import 'package:drift/drift.dart';
final _databaseElementCache = Expando<_DatabaseElementCache>();
/// A database accessor implicitly created by a `.drift` file.
///
/// When modular code generation is enabled, drift will emit a file with a
/// [ModularAccessor] for each drift file instead of generating all the code for
/// a database into a single file.
class ModularAccessor extends DatabaseAccessor<GeneratedDatabase> {
/// Default constructor - create an accessor from the [attachedDatabase].
ModularAccessor(super.attachedDatabase);
/// Find a result set by its [name] in the database. The result is cached.
T resultSet<T extends ResultSetImplementation>(String name) {
return attachedDatabase.resultSet(name);
}
/// Find an accessor by its [name] in the database, or create it with
/// [create]. The result will be cached.
T accessor<T extends DatabaseAccessor>(
String name, T Function(GeneratedDatabase) create) {
return attachedDatabase.accessor<T>(name, create);
}
}
/// Look up cached elements or accessors from a database.
/// This extension is meant to be used by drift-generated code.
extension ReadDatabaseContainer on GeneratedDatabase {
_DatabaseElementCache get _cache {
return _databaseElementCache[attachedDatabase] ??=
_DatabaseElementCache(attachedDatabase);
}
/// Find a result set by its [name] in the database. The result is cached.
T resultSet<T extends ResultSetImplementation>(String name) {
return _cache.knownEntities[name]! as T;
}
/// Find an accessor by its [name] in the database, or create it with
/// [create]. The result will be cached.
T accessor<T extends DatabaseAccessor>(
String name, T Function(GeneratedDatabase) create) {
final cache = _cache.knownAccessors;
return cache.putIfAbsent(name, () => create(attachedDatabase)) as T;
}
}
class _DatabaseElementCache {
final Map<String, DatabaseSchemaEntity> knownEntities;
final Map<String, DatabaseAccessor> knownAccessors = {};
_DatabaseElementCache(GeneratedDatabase database)
: knownEntities = {
for (final entity in database.allSchemaEntities)
entity.entityName: entity
};
}

View File

@ -20,6 +20,7 @@ builders:
build_to: cache
applies_builders: ["drift_dev:cleanup"]
# Regular build flow, emitting a shared part file for source_gen to pick up
drift_dev:
import: "package:drift_dev/integrations/build.dart"
builder_factories: ["analyzer", "driftBuilder"]
@ -31,6 +32,8 @@ builders:
required_inputs: [".drift_prep.json"]
applies_builders: ["source_gen:combining_builder", ":preparing_builder"]
# Similar to the regular builder, but emitting a direct part file instead of a
# shared part file.
not_shared:
import: "package:drift_dev/integrations/build.dart"
builder_factories: ["driftBuilderNotShared"]
@ -39,6 +42,26 @@ builders:
auto_apply: none
required_inputs: [".drift_prep.json"]
# A work-in-progress builder that emits standalone Dart libraries for each
# .drift file.
modular:
import: "package:drift_dev/integrations/build.dart"
builder_factories: ["modular"]
build_extensions: {".dart": [".drift.dart"]}
build_to: source
auto_apply: none
required_inputs: [".drift.drift_module.json"]
applies_builders: [":analyzer"]
analyzer:
import: "package:drift_dev/integrations/build.dart"
builder_factories: ["analyzer"]
build_extensions:
".dart": [".dart.drift_module.json"]
".drift": [".drift.drift_module.json"]
build_to: cache
auto_apply: none
required_inputs: [".drift_prep.json"]
post_process_builders:
cleanup:
import: "package:drift_dev/integrations/build.dart"

View File

@ -13,6 +13,9 @@ Builder driftBuilder(BuilderOptions options) =>
Builder driftBuilderNotShared(BuilderOptions options) =>
DriftBuilder(DriftGenerationMode.monolithicPart, options);
Builder modular(BuilderOptions options) =>
DriftBuilder(DriftGenerationMode.modular, options);
PostProcessBuilder driftCleanup(BuilderOptions options) {
return const FileDeletingBuilder(['.temp.dart']);
}

View File

@ -6,6 +6,7 @@ import 'package:sqlparser/sqlparser.dart' hide AnalysisError;
import '../results/database.dart';
import '../results/element.dart';
import '../results/file_results.dart';
import '../results/query.dart';
import 'error.dart';
class FileState {
@ -26,6 +27,15 @@ class FileState {
return analyzedElements.any((e) => e is BaseDriftAccessor);
}
/// Whether an accessor class making queries and imports available should be
/// written for this file if modular analysis is enabled.
bool get hasModularDriftAccessor {
final hasImports = discovery?.importDependencies.isNotEmpty == true;
final hasQuery = analyzedElements.any((e) => e is DefinedSqlQuery);
return hasImports || hasQuery;
}
/// All analyzed [DriftElement]s found in this library.
@visibleForTesting
Iterable<DriftElement> get analyzedElements {

View File

@ -24,6 +24,12 @@ class DriftElementId {
Map<String, Object?> toJson() => _$DriftElementIdToJson(this);
Uri get modularImportUri {
final path = url.withoutExtension(libraryUri.path);
return libraryUri.replace(path: '$path.drift.dart');
}
@override
int get hashCode => Object.hash(DriftElementId, libraryUri, name);

View File

@ -1,5 +1,6 @@
import 'package:build/build.dart';
import 'package:dart_style/dart_style.dart';
import 'package:drift_dev/src/writer/tables/table_writer.dart';
import 'package:pub_semver/pub_semver.dart';
import '../../analysis/custom_result_class.dart';
@ -11,6 +12,8 @@ import '../../utils/string_escaper.dart';
import '../../writer/database_writer.dart';
import '../../writer/drift_accessor_writer.dart';
import '../../writer/import_manager.dart';
import '../../writer/modules.dart';
import '../../writer/tables/view_writer.dart';
import '../../writer/writer.dart';
import 'backend.dart';
@ -26,16 +29,29 @@ enum DriftGenerationMode {
///
/// Drift will generate a single part file for the main database file and each
/// DAO-defining file.
monolithicSharedPart,
monolithicSharedPart(true, true),
/// Like [monolithicSharedPart], except that drift will generate a single
/// part file on its own instead of generating a part file for `source_gen`
/// to process later.
monolithicPart;
monolithicPart(true, true),
bool get isMonolithic => true;
/// Generates a separate Dart library (no `part of` directive) for each input
/// (.drift file or .dart file with databases / tables).
modular(false, false);
bool get isPartFile => true;
/// Whether this mode defines a "monolithic" build.
///
/// In a monolithic build, drift will generate all code into a single file,
/// even if tables and queries are defined across multiple `.drift` files.
/// In modular (non-monolithic) builds, files are generated for each input
/// defining drift elements instead.
final bool isMonolithic;
/// Whether this build mode generates a part file.
final bool isPartFile;
const DriftGenerationMode(this.isMonolithic, this.isPartFile);
/// Whether the analysis happens in the generating build step.
///
@ -47,8 +63,6 @@ enum DriftGenerationMode {
}
class DriftBuilder extends Builder {
static final Version _minimalDartLanguageVersion = Version(2, 12, 0);
final DriftOptions options;
final DriftGenerationMode generationMode;
@ -71,11 +85,93 @@ class DriftBuilder extends Builder {
return {
'.dart': ['.drift.dart']
};
case DriftGenerationMode.modular:
return {
'.dart': ['.drift.dart'],
'.drift': ['.drift.dart'],
};
}
}
@override
Future<void> build(BuildStep buildStep) async {
final run = _DriftBuildRun(options, generationMode, buildStep);
await run.run();
}
}
extension on Version {
String get majorMinor => '$major.$minor';
}
class _DriftBuildRun {
final DriftOptions options;
final DriftGenerationMode mode;
final BuildStep buildStep;
final DriftAnalysisDriver driver;
/// When emitting a direct part file, contains the `// @dart` language version
/// comment from the main library. We need to apply it to the part file as
/// well.
Version? overriddenLanguageVersion;
/// The Dart language version from the package. When it's too old and we're
/// generating libraries, we need to apply a `// @dart` version comment to get
/// a suitable version.
Version? packageLanguageVersion;
late Writer writer;
Set<Uri> analyzedUris = {};
_DriftBuildRun(this.options, this.mode, this.buildStep)
: driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options)
..cacheReader = BuildCacheReader(buildStep);
Future<void> run() async {
await _warnAboutDeprecatedOptions();
if (!await _checkForElementsToBuild()) return;
await _checkForLanguageVersions();
final fileResult =
await _analyze(buildStep.inputId.uri, isEntrypoint: true);
// For the monolithic build modes, we only generate code for databases and
// crawl the tables from there.
if (mode.isMonolithic && !fileResult.containsDatabaseAccessor) {
return;
}
_createWriter();
if (mode.isMonolithic) {
await _generateMonolithic(fileResult);
} else {
await _generateModular(fileResult);
}
await _emitCode();
}
Future<FileState> _analyze(Uri uri, {bool isEntrypoint = false}) async {
final result = await driver.fullyAnalyze(uri);
// If we're doing a monolithic build, we need to warn about errors in
// imports too.
final printErrors =
isEntrypoint || (mode.isMonolithic && analyzedUris.add(result.ownUri));
if (printErrors) {
for (final error in result.fileAnalysis?.analysisErrors ?? const []) {
log.warning(error);
}
}
return result;
}
/// Once per build, prints a warning about deprecated build options if they
/// are applied to this builder.
Future<void> _warnAboutDeprecatedOptions() async {
final flags = await buildStep.fetchResource(_flags);
if (!flags.didWarnAboutDeprecatedOptions &&
options.enabledDeprecatedOption) {
@ -84,11 +180,11 @@ class DriftBuilder extends Builder {
'Consider removing the option from your build.yaml.');
flags.didWarnAboutDeprecatedOptions = true;
}
}
final driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options)
..cacheReader = BuildCacheReader(buildStep);
if (!generationMode.embeddedAnalyzer) {
/// Checks if the input file contains elements drift should generate code for.
Future<bool> _checkForElementsToBuild() async {
if (!mode.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 =
@ -97,15 +193,17 @@ class DriftBuilder extends Builder {
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;
return false;
}
}
// Ok, we actually have something to generate. We're generating code
// needing version 2.12 (or later) of the Dart _language_. This property is
// inherited from the main file, so let's check that.
Version? overriddenLanguageVersion;
if (generationMode.isPartFile) {
return true;
}
/// Prints a warning if the used Dart version is incompatible with drift's
/// minimal version constraints.
Future<void> _checkForLanguageVersions() async {
if (mode.isPartFile) {
final library = await buildStep.inputLibrary;
overriddenLanguageVersion = library.languageVersion.override;
@ -123,43 +221,41 @@ class DriftBuilder extends Builder {
);
}
}
}
Set<Uri> analyzedUris = {};
Future<FileState> analyze(Uri uri) async {
final fileResult = await driver.fullyAnalyze(uri);
if (analyzedUris.add(fileResult.ownUri)) {
for (final error
in fileResult.fileAnalysis?.analysisErrors ?? const []) {
log.warning(error);
}
Future<void> _generateModular(FileState entrypointState) async {
for (final element in entrypointState.analysis.values) {
final result = element.result;
if (result is DriftTable) {
TableWriter(result, writer.child()).writeInto();
} else if (result is DriftView) {
ViewWriter(result, writer.child(), null).write();
} else if (result is DriftDatabase) {
final resolved =
entrypointState.fileAnalysis!.resolvedDatabases[result.id]!;
final input = DatabaseGenerationInput(result, resolved, const {});
DatabaseWriter(input, writer.child()).write();
}
return fileResult;
}
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;
if (entrypointState.hasModularDriftAccessor) {
ModularAccessorWriter(writer.child(), entrypointState).write();
}
}
final generationOptions = GenerationOptions(
imports: ImportManagerForPartFiles(),
);
final writer = Writer(options, generationOptions: generationOptions);
for (final element in fileResult.analysis.values) {
Future<void> _generateMonolithic(FileState entrypointState) async {
for (final element in entrypointState.analysis.values) {
final result = element.result;
if (result is BaseDriftAccessor) {
final resolved = fileResult.fileAnalysis!.resolvedDatabases[result.id]!;
final resolved =
entrypointState.fileAnalysis!.resolvedDatabases[result.id]!;
var importedQueries = <DefinedSqlQuery, SqlQuery>{};
for (final query
in resolved.availableElements.whereType<DefinedSqlQuery>()) {
final resolvedFile = await analyze(query.id.libraryUri);
final resolvedFile = await _analyze(query.id.libraryUri);
final resolvedQuery =
resolvedFile.fileAnalysis?.resolvedQueries[query.id];
@ -189,34 +285,51 @@ class DriftBuilder extends Builder {
}
}
}
}
void _createWriter() {
if (mode.isMonolithic) {
final generationOptions = GenerationOptions(
imports: ImportManagerForPartFiles(),
);
writer = Writer(options, generationOptions: generationOptions);
} else {
final imports = LibraryInputManager();
final generationOptions = GenerationOptions(
imports: imports,
isModular: true,
);
writer = Writer(options, generationOptions: generationOptions);
imports.linkToWriter(writer);
}
}
Future<void> _emitCode() {
final output = StringBuffer();
output.writeln('// ignore_for_file: type=lint');
if (generationMode == DriftGenerationMode.monolithicPart) {
if (mode == DriftGenerationMode.monolithicPart) {
final originalFile = buildStep.inputId.pathSegments.last;
if (overriddenLanguageVersion != null) {
// Part files need to have the same version as the main library.
output.writeln('// @dart=${overriddenLanguageVersion.majorMinor}');
output.writeln('// @dart=${overriddenLanguageVersion!.majorMinor}');
}
output.writeln('part of ${asDartLiteral(originalFile)};');
}
output.write(writer.writeGenerated());
var generated = output.toString();
var code = output.toString();
try {
generated = DartFormatter().format(generated);
code = DartFormatter().format(code);
} on FormatterException {
log.warning('Could not format generated source. The generated code is '
'probably invalid, and this is most likely a bug in drift_dev.');
}
await buildStep.writeAsString(buildStep.allowedOutputs.single, generated);
return buildStep.writeAsString(buildStep.allowedOutputs.single, code);
}
}
extension on Version {
String get majorMinor => '$major.$minor';
static final Version _minimalDartLanguageVersion = Version(2, 12, 0);
}

View File

@ -8,6 +8,7 @@ import '../analysis/results/file_results.dart';
import '../analysis/results/results.dart';
import '../services/find_stream_update_rules.dart';
import '../utils/string_escaper.dart';
import 'modules.dart';
import 'queries/query_writer.dart';
import 'tables/table_writer.dart';
import 'tables/view_writer.dart';
@ -28,18 +29,22 @@ class DatabaseWriter {
return 'DatabaseAtV${scope.generationOptions.forSchema}';
}
return '_\$${db.id.name}';
final prefix = scope.generationOptions.isModular ? '' : r'_';
return '$prefix\$${db.id.name}';
}
void write() {
final elements = input.resolvedAccessor.availableElements;
// Write data classes, companions and info classes
for (final reference in elements) {
if (reference is DriftTable) {
TableWriter(reference, scope.child()).writeInto();
} else if (reference is DriftView) {
ViewWriter(reference, scope.child(), this).write();
if (!scope.generationOptions.isModular) {
for (final reference in elements) {
if (reference is DriftTable) {
TableWriter(reference, scope.child()).writeInto();
} else if (reference is DriftView) {
ViewWriter(reference, scope.child(), this).write();
}
}
}
@ -74,7 +79,7 @@ class DatabaseWriter {
}
if (entity is DriftTable) {
final tableClassName = entity.entityInfoName;
final tableClassName = dbScope.dartCode(dbScope.entityInfoType(entity));
writeMemoizedGetter(
buffer: dbScope.leaf().buffer,
@ -126,6 +131,27 @@ class DatabaseWriter {
);
}
// Also write implicit DAOs for modular imports
if (scope.generationOptions.isModular) {
for (final import in input.resolvedAccessor.knownImports) {
if (import.hasModularDriftAccessor) {
final type = dbScope.modularAccessor(import.ownUri);
final getter = ReCase(type.toString()).camelCase;
dbScope.leaf()
..writeDart(type)
..write(' get $getter => ')
..writeUriRef(
ModularAccessorWriter.modularSupport, 'ReadDatabaseContainer')
..writeln('(this).accessor<')
..writeDart(type)
..write('>(${asDartLiteral(import.ownUri.toString())}, ')
..writeDart(type)
..writeln('.new);');
}
}
}
// Write implementation for query methods
for (final query in input.availableRegularQueries) {
QueryWriter(dbScope.child()).write(query);
@ -134,11 +160,14 @@ class DatabaseWriter {
// Write List of tables
final schemaScope = dbScope.leaf();
final tableInfoType =
'${dbScope.drift('TableInfo')}<${dbScope.drift('Table')}, Object?>';
final schemaEntity = dbScope.drift('DatabaseSchemaEntity');
schemaScope
..write(
'@override\nIterable<TableInfo<Table, dynamic>> get allTables => ')
..write('allSchemaEntities.whereType<TableInfo<Table, Object?>>();\n')
..write('@override\nList<DatabaseSchemaEntity> get allSchemaEntities ')
..write('@override\nIterable<$tableInfoType> get allTables => ')
..write('allSchemaEntities.whereType<$tableInfoType>();\n')
..write('@override\nList<$schemaEntity> get allSchemaEntities ')
..write('=> [');
schemaScope

View File

@ -1,3 +1,6 @@
import '../utils/string_escaper.dart';
import 'writer.dart';
abstract class ImportManager {
String? prefixFor(Uri definitionUri, String elementName);
}
@ -8,3 +11,31 @@ class ImportManagerForPartFiles extends ImportManager {
return null; // todo: Find import alias from existing imports?
}
}
class LibraryInputManager extends ImportManager {
static final _dartCore = Uri.parse('dart:core');
final Map<Uri, String> _importAliases = {};
TextEmitter? emitter;
LibraryInputManager();
void linkToWriter(Writer writer) {
emitter = writer.leaf();
}
@override
String? prefixFor(Uri definitionUri, String elementName) {
if (definitionUri == _dartCore) {
return null;
}
return _importAliases.putIfAbsent(definitionUri, () {
final alias = 'i${_importAliases.length}';
emitter?.writeln(
'import ${asDartLiteral(definitionUri.toString())} as $alias;');
return alias;
});
}
}

View File

@ -0,0 +1,71 @@
import '../analysis/driver/state.dart';
import '../analysis/results/results.dart';
import '../utils/string_escaper.dart';
import 'queries/query_writer.dart';
import 'writer.dart';
/// Write a modular database accessor for a drift file.
///
/// In drift's (opt-in) modular build set, a `.drift.dart` file is generated for
/// each `.drift` file. This file defines elements defined in that drift file
/// (like tables, data classes, companions, fields for indexes and triggers).
///
/// If queries are defined in that drift file, a "modular accessor" is generated
/// as well. This accessor contains generated methods for all queries. The main
/// database will make these accessor available as getters.
class ModularAccessorWriter {
final Scope scope;
final FileState file;
ModularAccessorWriter(this.scope, this.file);
void write() {
if (!file.hasModularDriftAccessor) return;
final className = scope.modularAccessor(file.ownUri);
final generatedDatabase = scope.drift('GeneratedDatabase');
scope.leaf()
..write('class $className extends ')
..write(_modular('ModularAccessor'))
..writeln('{ $className($generatedDatabase db): super(db);');
final referencedElements = <DriftElement>{};
final queries = file.fileAnalysis?.resolvedQueries ?? const {};
for (final query in queries.entries) {
final queryElement = file.analysis[query.key]?.result;
if (queryElement != null) {
referencedElements.addAll(queryElement.references);
}
QueryWriter(scope.child()).write(query.value);
}
final restOfClass = scope.leaf();
for (final reference in referencedElements) {
// This element is referenced in a query, and the query writer expects it
// to be available as a getter. So, let's generate that getter:
if (reference is DriftElementWithResultSet) {
final infoType = restOfClass.entityInfoType(reference);
restOfClass
..writeDart(infoType)
..write(' get ${reference.dbGetterName} => this.resultSet<')
..writeDart(infoType)
..write('>(${asDartLiteral(reference.schemaName)});');
}
}
restOfClass.writeln('}');
}
String _modular(String element) {
return scope.refUri(modularSupport, element);
}
static final Uri modularSupport =
Uri.parse('package:drift/internal/modular.dart');
}

View File

@ -710,10 +710,10 @@ String? _defaultForDartPlaceholder(
// the surrounding precedence in SQL.
final sql = SqlWriter(scope.options)
.writeNodeIntoStringLiteral(Parentheses(kind.defaultValue!));
return 'const CustomExpression($sql)';
return 'const ${scope.drift('CustomExpression')}($sql)';
} else if (kind is SimpleDartPlaceholderType &&
kind.kind == SimpleDartPlaceholderKind.orderBy) {
return 'const OrderBy.nothing()';
return 'const ${scope.drift('OrderBy')}.nothing()';
} else {
assert(!placeholder.hasDefaultOrImplicitFallback);
return null;

View File

@ -17,7 +17,7 @@ class DataClassWriter {
DataClassWriter(this.table, this.scope) : _emitter = scope.leaf();
String get serializerType => 'ValueSerializer?';
String get serializerType => _emitter.drift('ValueSerializer?');
String _columnType(DriftColumn column) {
return _emitter.dartCode(_emitter.dartType(column));
@ -44,14 +44,15 @@ class DataClassWriter {
}
void write() {
final parentClass = table.customParentClass ?? 'DataClass';
final parentClass = table.customParentClass ?? _emitter.drift('DataClass');
_buffer.write('class ${table.nameOfRowClass} extends $parentClass ');
if (isInsertable) {
// The data class is only an insertable if we can actually insert rows
// into the target entity.
final type = _emitter.dartCode(_emitter.writer.rowType(table));
_buffer.writeln('implements Insertable<$type> {');
_buffer.writeln('implements ${_emitter.drift('Insertable')}<$type> {');
} else {
_buffer.writeln('{');
}
@ -121,7 +122,8 @@ class DataClassWriter {
..write('factory $dataClassName.fromJson('
'Map<String, dynamic> json, {$serializerType serializer}'
') {\n')
..write('serializer ??= driftRuntimeOptions.defaultSerializer;\n')
..write('serializer ??= ${_emitter.drift('driftRuntimeOptions')}'
'.defaultSerializer;\n')
..write('return $dataClassName(');
for (final column in columns) {
@ -161,7 +163,8 @@ class DataClassWriter {
void _writeToJson() {
_buffer.write('@override Map<String, dynamic> toJson('
'{$serializerType serializer}) {\n'
'serializer ??= driftRuntimeOptions.defaultSerializer;\n'
'serializer ??= ${_emitter.drift('driftRuntimeOptions')}'
'.defaultSerializer;\n'
'return <String, dynamic>{\n');
for (final column in columns) {
@ -187,6 +190,7 @@ class DataClassWriter {
void _writeCopyWith() {
final dataClassName = _emitter.dartCode(_emitter.writer.rowClass(table));
final wrapNullableInValue = scope.options.generateValuesInCopyWith;
final valueType = _emitter.drift('Value');
_buffer.write('$dataClassName copyWith({');
for (var i = 0; i < columns.length; i++) {
@ -197,8 +201,8 @@ class DataClassWriter {
final typeName = _columnType(column);
if (wrapNullableInValue && isNullable) {
_buffer
..write('Value<$typeName> ${column.nameInDart} ')
..write('= const Value.absent()');
..write('$valueType<$typeName> ${column.nameInDart} ')
..write('= const $valueType.absent()');
} else if (!isNullable) {
// We always use nullable parameters in copyWith, since all parameters
// are optional. The !isNullable check is there to avoid a duplicate
@ -233,10 +237,13 @@ class DataClassWriter {
}
void _writeToColumnsOverride() {
final expression = _emitter.drift('Expression');
final variable = _emitter.drift('Variable');
_buffer
..write('@override\nMap<String, Expression> toColumns'
..write('@override\nMap<String, $expression> toColumns'
'(bool nullToAbsent) {\n')
..write('final map = <String, Expression> {};');
..write('final map = <String, $expression> {};');
for (final column in columns) {
// Generated column - cannot be used for inserts or updates
@ -255,7 +262,7 @@ class DataClassWriter {
final typeName = column.variableTypeCode(nullable: false);
final mapSetter = 'map[${asDartLiteral(column.nameInSql)}] = '
'Variable<$typeName>';
'$variable<$typeName>';
if (column.typeConverter != null) {
// apply type converter before writing the variable
@ -309,12 +316,14 @@ class DataClassWriter {
if (needsNullCheck) {
_buffer
..write(dartName)
..write(' == null && nullToAbsent ? const Value.absent() : ');
..write(' == null && nullToAbsent ? '
'const ${_emitter.drift('Value')}.absent() : ');
// We'll write the non-null case afterwards
}
_buffer
..write('Value (')
..write(_emitter.drift('Value'))
..write('(')
..write(dartName)
..write('),');
}
@ -369,7 +378,7 @@ class RowMappingWriter {
final columnName = column.nameInSql;
final rawData = "data['\${effectivePrefix}$columnName']";
final sqlType = column.sqlType.toString();
final sqlType = writer.drift(column.sqlType.toString());
var loadType = '$databaseGetter.typeMapping.read($sqlType, $rawData)';
if (!column.nullable) {

View File

@ -23,7 +23,8 @@ abstract class TableOrViewWriter {
for (final constraint in column.constraints) {
if (constraint is LimitingTextLength) {
final buffer = StringBuffer('GeneratedColumn.checkTextLength(');
final buffer =
StringBuffer(emitter.drift('GeneratedColumn.checkTextLength('));
if (constraint.minLength != null) {
buffer.write('minTextLength: ${constraint.minLength},');
@ -45,7 +46,7 @@ abstract class TableOrViewWriter {
final dartCode = emitter.dartCode(constraint.dartExpression);
additionalParams['generatedAs'] =
'GeneratedAs($dartCode, ${constraint.stored})';
'${emitter.drift('GeneratedAs')}($dartCode, ${constraint.stored})';
}
if (constraint is PrimaryKeyColumn && constraint.isAutoIncrement) {
@ -53,7 +54,7 @@ abstract class TableOrViewWriter {
}
}
additionalParams['type'] = column.sqlType.toString();
additionalParams['type'] = emitter.drift(column.sqlType.toString());
if (tableOrView is DriftTable) {
additionalParams['requiredDuringInsert'] = (tableOrView as DriftTable)
@ -102,7 +103,7 @@ abstract class TableOrViewWriter {
}
final innerType = column.innerColumnType();
var type = 'GeneratedColumn<$innerType>';
var type = '${emitter.drift('GeneratedColumn')}<$innerType>';
expressionBuffer
..write(type)
..write(
@ -133,7 +134,8 @@ abstract class TableOrViewWriter {
final converterCode = emitter.dartCode(emitter.writer
.readConverter(converter, forNullable: column.nullable));
type = 'GeneratedColumnWithTypeConverter<$mappedType, $innerType>';
type = '${emitter.drift('GeneratedColumnWithTypeConverter')}'
'<$mappedType, $innerType>';
expressionBuffer
..write('.withConverter<')
..write(mappedType)
@ -155,7 +157,7 @@ abstract class TableOrViewWriter {
if (!scope.generationOptions.writeDataClasses) {
buffer.writeln('''
@override
Never map(Map<String, dynamic> data, {$String? tablePrefix}) {
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
throw UnsupportedError('TableInfo.map in schema verification code');
}
''');
@ -229,7 +231,8 @@ abstract class TableOrViewWriter {
void writeGetColumnsOverride() {
final columnsWithGetters =
tableOrView.columns.map((c) => c.nameInDart).join(', ');
buffer.write('@override\nList<GeneratedColumn> get \$columns => '
buffer.write(
'@override\nList<${emitter.drift('GeneratedColumn')}> get \$columns => '
'[$columnsWithGetters];\n');
}
@ -276,30 +279,37 @@ class TableWriter extends TableOrViewWriter {
if (!scope.generationOptions.writeDataClasses) {
// Write a small table header without data class
buffer
..write('class ${table.entityInfoName} extends ')
..write(emitter.drift('Table'))
..write(' with ')
..write(emitter.drift('TableInfo'));
buffer.write('class ${table.entityInfoName} extends Table with '
'TableInfo');
if (table.isVirtual) {
buffer.write(', VirtualTableInfo');
buffer.write(', ${emitter.drift('VirtualTableInfo')}');
}
} else {
// Regular generation, write full table class
final dataClass = emitter.dartCode(emitter.writer.rowClass(table));
final tableDslName = table.definingDartClass ?? 'Table';
final tableDslName = table.definingDartClass ?? emitter.drift('Table');
// class UsersTable extends Users implements TableInfo<Users, User> {
final typeArgs = '<${table.entityInfoName}, $dataClass>';
buffer.write('class ${table.entityInfoName} extends $tableDslName with '
'TableInfo$typeArgs ');
'${emitter.drift('TableInfo')}$typeArgs ');
if (table.isVirtual) {
buffer.write(', VirtualTableInfo$typeArgs ');
buffer.write(', ${emitter.drift('VirtualTableInfo')}$typeArgs ');
}
}
buffer
..writeln('{')
// write a GeneratedDatabase reference that is set in the constructor
..writeln('@override final GeneratedDatabase attachedDatabase;')
..writeln(
'@override final ${emitter.drift('GeneratedDatabase')} attachedDatabase;')
..writeln('final String? _alias;')
..writeln(
'${table.entityInfoName}(this.attachedDatabase, [this._alias]);');
@ -366,9 +376,11 @@ class TableWriter extends TableOrViewWriter {
void _writeColumnVerificationMeta(DriftColumn column) {
if (!_skipVerification) {
final meta = emitter.drift('VerificationMeta');
buffer
..write('final VerificationMeta ${_fieldNameForColumnMeta(column)} = ')
..write("const VerificationMeta('${column.nameInDart}');\n");
..write('static const $meta ${_fieldNameForColumnMeta(column)} = ')
..writeln("const $meta('${column.nameInDart}');");
}
}
@ -376,11 +388,13 @@ class TableWriter extends TableOrViewWriter {
if (_skipVerification) return;
final innerType = emitter.dartCode(emitter.writer.rowType(table));
buffer
..write('@override\nVerificationContext validateIntegrity'
'(Insertable<$innerType> instance, '
'{bool isInserting = false}) {\n')
..write('final context = VerificationContext();\n')
emitter
..writeln('@override')
..writeDriftRef('VerificationContext')
..write(' validateIntegrity(')
..writeDriftRef('Insertable')
..writeln('<$innerType> instance, {bool isInserting = false}) {')
..write('final context = ${emitter.drift('VerificationContext')}();\n')
..write('final data = instance.toColumns(true);\n');
const locals = {'instance', 'isInserting', 'context', 'data'};
@ -392,8 +406,8 @@ class TableWriter extends TableOrViewWriter {
if (column.typeConverter != null) {
// dont't verify custom columns, we assume that the user knows what
// they're doing
buffer.write(
'context.handle($metaName, const VerificationResult.success());');
buffer.write('context.handle($metaName, '
'const ${emitter.drift('VerificationResult')}.success());');
continue;
}
@ -421,11 +435,12 @@ class TableWriter extends TableOrViewWriter {
}
void _writePrimaryKeyOverride() {
buffer.write('@override\nSet<GeneratedColumn> get \$primaryKey => ');
buffer.write(
'@override\nSet<${emitter.drift('GeneratedColumn')}> get \$primaryKey => ');
final primaryKey = table.fullPrimaryKey;
if (primaryKey.isEmpty) {
buffer.write('const <GeneratedColumn>{};');
buffer.write('const {};');
return;
}
@ -452,7 +467,8 @@ class TableWriter extends TableOrViewWriter {
return;
}
buffer.write('@override\nList<Set<GeneratedColumn>> get uniqueKeys => [');
buffer.write('@override\nList<Set<${emitter.drift('GeneratedColumn')}>> '
'get uniqueKeys => [');
for (final uniqueKey in uniqueKeys) {
buffer.write('{');

View File

@ -13,6 +13,8 @@ class UpdateCompanionWriter {
StringBuffer get _buffer => _emitter.buffer;
String get _value => _emitter.drift('Value');
late final List<DriftColumn> columns = [
for (final column in table.columns)
if (!column.isGenerated) column,
@ -20,15 +22,15 @@ class UpdateCompanionWriter {
UpdateCompanionWriter(this.table, this.scope) : _emitter = scope.leaf();
String get _companionClass =>
_emitter.dartCode(_emitter.companionType(table));
String get _companionClass => _emitter.companionType(table).toString();
String get _companionType => _emitter.dartCode(_emitter.companionType(table));
void write() {
final rowClass = _emitter.dartCode(_emitter.rowType(table));
_buffer.write('class $_companionClass '
'extends '
'UpdateCompanion<$rowClass> {\n');
'${_emitter.drift('UpdateCompanion')}<$rowClass> {\n');
_writeFields();
_writeConstructor();
@ -50,7 +52,7 @@ class UpdateCompanionWriter {
for (final column in columns) {
final modifier = scope.options.fieldModifier;
final type = _emitter.dartCode(scope.writer.dartType(column));
_buffer.write('$modifier Value<$type> ${column.nameInDart};\n');
_buffer.write('$modifier $_value<$type> ${column.nameInDart};\n');
}
}
@ -61,7 +63,7 @@ class UpdateCompanionWriter {
_buffer.write('$_companionClass({');
for (final column in columns) {
_buffer.write('this.${column.nameInDart} = const Value.absent(),');
_buffer.write('this.${column.nameInDart} = const $_value.absent(),');
}
_buffer.write('});\n');
@ -93,7 +95,7 @@ class UpdateCompanionWriter {
_buffer.write('required $typeName $param,');
} else {
_buffer.write('this.$param = const Value.absent(),');
_buffer.write('this.$param = const $_value.absent(),');
}
}
_buffer.write('})');
@ -108,7 +110,7 @@ class UpdateCompanionWriter {
}
final param = required.nameInDart;
_buffer.write('$param = Value($param)');
_buffer.write('$param = $_value($param)');
}
_buffer.write(';\n');
@ -124,17 +126,19 @@ class UpdateCompanionWriter {
final rowType = _emitter.dartCode(_emitter.rowType(table));
_buffer
..write('static Insertable<$rowType> $constructorName')
..write(
'static ${_emitter.drift('Insertable')}<$rowType> $constructorName')
..write('({');
final expression = _emitter.drift('Expression');
for (final column in columns) {
final typeName = column.innerColumnType();
_buffer.write('Expression<$typeName>? ${column.nameInDart}, \n');
_buffer.write('$expression<$typeName>? ${column.nameInDart}, \n');
}
_buffer
..write('}) {\n')
..write('return RawValuesInsertable({');
..write('return ${_emitter.drift('RawValuesInsertable')}({');
for (final column in columns) {
_buffer
@ -148,7 +152,7 @@ class UpdateCompanionWriter {
void _writeCopyWith() {
_buffer
..write(_companionClass)
..write(_companionType)
..write(' copyWith({');
var first = true;
for (final column in columns) {
@ -158,12 +162,12 @@ class UpdateCompanionWriter {
first = false;
final typeName = _emitter.dartCode(_emitter.dartType(column));
_buffer.write('Value<$typeName>? ${column.nameInDart}');
_buffer.write('$_value<$typeName>? ${column.nameInDart}');
}
_buffer
..writeln('}) {')
..write('return $_companionClass(');
..write('return $_companionType(');
for (final column in columns) {
final name = column.nameInDart;
_buffer.write('$name: $name ?? this.$name,');
@ -172,11 +176,11 @@ class UpdateCompanionWriter {
}
void _writeToColumnsOverride() {
// Map<String, Variable> entityToSql(covariant UpdateCompanion<D> instance)
final expression = _emitter.drift('Expression');
_buffer
..write('@override\nMap<String, Expression> toColumns'
..write('@override\nMap<String, $expression> toColumns'
'(bool nullToAbsent) {\n')
..write('final map = <String, Expression> {};');
..write('final map = <String, $expression> {};');
const locals = {'map', 'nullToAbsent', 'converter'};
@ -186,7 +190,7 @@ class UpdateCompanionWriter {
_buffer.write('if ($getterName.present) {');
final typeName = column.variableTypeCode(nullable: false);
final mapSetter = 'map[${asDartLiteral(column.nameInSql)}] = '
'Variable<$typeName>';
'${_emitter.drift('Variable')}<$typeName>';
final converter = column.typeConverter;
if (converter != null) {
@ -237,7 +241,7 @@ class UpdateCompanionWriter {
'$insertableClass(this._object);\n\n'
'@override\n'
'Map<String, Expression> toColumns(bool nullToAbsent) {\n'
'return $_companionClass(\n');
'return $_companionType(\n');
final columns = info.positionalColumns.followedBy(info.namedColumns.values);
for (final columnName in columns) {

View File

@ -8,7 +8,7 @@ import 'table_writer.dart';
class ViewWriter extends TableOrViewWriter {
final DriftView view;
final Scope scope;
final DatabaseWriter databaseWriter;
final DatabaseWriter? databaseWriter;
@override
late TextEmitter emitter;
@ -41,10 +41,10 @@ class ViewWriter extends TableOrViewWriter {
}
buffer.writeln(' implements HasResultSet {');
final dbClassName = databaseWriter?.dbClassName ?? 'GeneratedDatabase';
buffer
..writeln('final String? _alias;')
..writeln(
'@override final ${databaseWriter.dbClassName} attachedDatabase;')
..writeln('@override final $dbClassName attachedDatabase;')
..writeln('${view.entityInfoName}(this.attachedDatabase, '
'[this._alias]);');

View File

@ -1,4 +1,6 @@
import 'package:recase/recase.dart';
import 'package:sqlparser/sqlparser.dart' as sql;
import 'package:path/path.dart' show url;
import '../analysis/results/results.dart';
import '../analysis/options.dart';
@ -55,14 +57,53 @@ class Writer extends _NodeOrWriter {
abstract class _NodeOrWriter {
Writer get writer;
AnnotatedDartCode _generatedElement(DriftElement element, String dartName) {
if (writer.generationOptions.isModular) {
return AnnotatedDartCode([
DartTopLevelSymbol(dartName, element.id.modularImportUri),
]);
} else {
return AnnotatedDartCode([dartName]);
}
}
AnnotatedDartCode modularAccessor(Uri driftFile) {
final id = DriftElementId(driftFile, '(file)');
return AnnotatedDartCode([
DartTopLevelSymbol(
ReCase(url.basename(driftFile.path)).pascalCase, id.modularImportUri),
]);
}
AnnotatedDartCode companionType(DriftTable table) {
final baseName = writer.options.useDataClassNameForCompanions
? table.nameOfRowClass
: table.baseDartName;
return AnnotatedDartCode([
DartTopLevelSymbol('${baseName}Companion', table.id.libraryUri),
]);
return _generatedElement(table, '${baseName}Companion');
}
AnnotatedDartCode entityInfoType(DriftElementWithResultSet element) {
return _generatedElement(element, element.entityInfoName);
}
AnnotatedDartCode rowType(DriftElementWithResultSet element) {
final existing = element.existingRowClass;
if (existing != null) {
return existing.targetType;
} else {
return _generatedElement(element, element.nameOfRowClass);
}
}
AnnotatedDartCode rowClass(DriftElementWithResultSet element) {
final existing = element.existingRowClass;
if (existing != null) {
return existing.targetClass;
} else {
return _generatedElement(element, element.nameOfRowClass);
}
}
/// Returns a Dart expression evaluating to the [converter].
@ -139,24 +180,6 @@ abstract class _NodeOrWriter {
}
}
AnnotatedDartCode rowType(DriftElementWithResultSet element) {
final existing = element.existingRowClass;
if (existing != null) {
return existing.targetType;
} else {
return AnnotatedDartCode([element.nameOfRowClass]);
}
}
AnnotatedDartCode rowClass(DriftElementWithResultSet element) {
final existing = element.existingRowClass;
if (existing != null) {
return existing.targetClass;
} else {
return AnnotatedDartCode([element.nameOfRowClass]);
}
}
String refUri(Uri definition, String element) {
final prefix =
writer.generationOptions.imports.prefixFor(definition, element);
@ -168,6 +191,12 @@ abstract class _NodeOrWriter {
}
}
/// References a top-level symbol exposed by the core `package:drift/drift.dart`
/// library.
String drift(String element) {
return refUri(AnnotatedDartCode.drift, element);
}
String dartCode(AnnotatedDartCode code) {
final buffer = StringBuffer();
@ -291,6 +320,10 @@ class GenerationOptions {
/// Whether companions should be generated.
final bool writeCompanions;
/// Whether multiple files are generated, instead of just generating one file
/// for each database.
final bool isModular;
final ImportManager imports;
const GenerationOptions({
@ -298,6 +331,7 @@ class GenerationOptions {
this.forSchema,
this.writeDataClasses = true,
this.writeCompanions = true,
this.isModular = false,
});
/// Whether, instead of generating the full database code, we're only

View File

@ -6,5 +6,6 @@ This collection of examples demonstrates how to use some advanced drift features
- `encryption`: A very simple Flutter app running an encrypted drift database.
- `flutter_web_worker_example`: Asynchronously run a drift database through a web worker with Fluter.
- `migrations_example`: Example showing to how to generate test utilities to verify schema migration.
- `modular`: Example using drift's upcoming modular generation mode.
- `web_worker_-_example`: Asynchronously run a drift database through a web worker, without Flutter.
- `with_built_value`: Configure `build_runner` so that drift-generated classes can be used by `build_runner`.

3
examples/modular/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Files and directories created by pub.
.dart_tool/
.packages

View File

@ -0,0 +1 @@
include: package:lints/recommended.yaml

View File

@ -0,0 +1,8 @@
import 'package:drift/native.dart';
import 'package:modular/database.dart';
void main() async {
final database = Database(NativeDatabase.memory(logStatements: true));
database.usersDrift.findUsers().watch().listen(print);
}

View File

@ -0,0 +1,10 @@
targets:
$default:
builders:
drift_dev:
enabled: false
drift_dev:analyzer:
enabled: true
drift_dev:modular:
enabled: true

View File

@ -0,0 +1,11 @@
import 'package:drift/drift.dart';
import 'database.drift.dart';
@DriftDatabase(include: {'src/users.drift'})
class Database extends $Database {
Database(super.e);
@override
int get schemaVersion => 1;
}

View File

@ -0,0 +1,17 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:modular/src/users.drift.dart' as i1;
import 'package:drift/internal/modular.dart' as i2;
abstract class $Database extends i0.GeneratedDatabase {
$Database(i0.QueryExecutor e) : super(e);
late final i1.Users users = i1.Users(this);
i1.UsersDrift get usersDrift =>
i2.ReadDatabaseContainer(this).accessor<i1.UsersDrift>(
'package:modular/src/users.drift', i1.UsersDrift.new);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@override
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [users];
}

View File

@ -0,0 +1,7 @@
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
biography TEXT
);
findUsers($predicate = TRUE): SELECT * FROM users WHERE $predicate;

View File

@ -0,0 +1,243 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:modular/src/users.drift.dart' as i1;
import 'package:drift/internal/modular.dart' as i2;
class User extends i0.DataClass implements i0.Insertable<i1.User> {
final int id;
final String name;
final String? biography;
const User({required this.id, required this.name, this.biography});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<int>(id);
map['name'] = i0.Variable<String>(name);
if (!nullToAbsent || biography != null) {
map['biography'] = i0.Variable<String>(biography);
}
return map;
}
i1.UsersCompanion toCompanion(bool nullToAbsent) {
return i1.UsersCompanion(
id: i0.Value(id),
name: i0.Value(name),
biography: biography == null && nullToAbsent
? const i0.Value.absent()
: i0.Value(biography),
);
}
factory User.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return User(
id: serializer.fromJson<int>(json['id']),
name: serializer.fromJson<String>(json['name']),
biography: serializer.fromJson<String?>(json['biography']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'name': serializer.toJson<String>(name),
'biography': serializer.toJson<String?>(biography),
};
}
i1.User copyWith(
{int? id,
String? name,
i0.Value<String?> biography = const i0.Value.absent()}) =>
i1.User(
id: id ?? this.id,
name: name ?? this.name,
biography: biography.present ? biography.value : this.biography,
);
@override
String toString() {
return (StringBuffer('User(')
..write('id: $id, ')
..write('name: $name, ')
..write('biography: $biography')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, name, biography);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.User &&
other.id == this.id &&
other.name == this.name &&
other.biography == this.biography);
}
class UsersCompanion extends i0.UpdateCompanion<i1.User> {
final i0.Value<int> id;
final i0.Value<String> name;
final i0.Value<String?> biography;
const UsersCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
this.biography = const i0.Value.absent(),
});
UsersCompanion.insert({
this.id = const i0.Value.absent(),
required String name,
this.biography = const i0.Value.absent(),
}) : name = i0.Value(name);
static i0.Insertable<i1.User> custom({
i0.Expression<int>? id,
i0.Expression<String>? name,
i0.Expression<String>? biography,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
if (biography != null) 'biography': biography,
});
}
i1.UsersCompanion copyWith(
{i0.Value<int>? id,
i0.Value<String>? name,
i0.Value<String?>? biography}) {
return i1.UsersCompanion(
id: id ?? this.id,
name: name ?? this.name,
biography: biography ?? this.biography,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<int>(id.value);
}
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
if (biography.present) {
map['biography'] = i0.Variable<String>(biography.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('i1.UsersCompanion(')
..write('id: $id, ')
..write('name: $name, ')
..write('biography: $biography')
..write(')'))
.toString();
}
}
class Users extends i0.Table with i0.TableInfo<Users, i1.User> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
Users(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
'id', aliasedName, false,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL PRIMARY KEY');
static const i0.VerificationMeta _nameMeta =
const i0.VerificationMeta('name');
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name', aliasedName, false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL');
static const i0.VerificationMeta _biographyMeta =
const i0.VerificationMeta('biography');
late final i0.GeneratedColumn<String> biography = i0.GeneratedColumn<String>(
'biography', aliasedName, true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
$customConstraints: '');
@override
List<i0.GeneratedColumn> get $columns => [id, name, biography];
@override
String get aliasedName => _alias ?? 'users';
@override
String get actualTableName => 'users';
@override
i0.VerificationContext validateIntegrity(i0.Insertable<i1.User> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('name')) {
context.handle(
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
} else if (isInserting) {
context.missing(_nameMeta);
}
if (data.containsKey('biography')) {
context.handle(_biographyMeta,
biography.isAcceptableOrUnknown(data['biography']!, _biographyMeta));
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.User map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.User(
id: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!,
name: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
biography: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}biography']),
);
}
@override
Users createAlias(String alias) {
return Users(attachedDatabase, alias);
}
@override
List<String> get customConstraints => const [];
@override
bool get dontWriteConstraints => true;
}
class UsersDrift extends i2.ModularAccessor {
UsersDrift(i0.GeneratedDatabase db) : super(db);
i0.Selectable<i1.User> findUsers({FindUsers$predicate? predicate}) {
var $arrayStartIndex = 1;
final generatedpredicate = $write(
predicate?.call(this.users) ?? const i0.CustomExpression('(TRUE)'),
startIndex: $arrayStartIndex);
$arrayStartIndex += generatedpredicate.amountOfVariables;
return customSelect('SELECT * FROM users WHERE ${generatedpredicate.sql}',
variables: [
...generatedpredicate.introducedVariables
],
readsFrom: {
users,
...generatedpredicate.watchedTables,
}).asyncMap(users.mapFromRow);
}
i1.Users get users => this.resultSet<i1.Users>('users');
}
typedef FindUsers$predicate = i0.Expression<bool> Function(Users users);

View File

@ -0,0 +1,15 @@
name: modular
version: 1.0.0
publish_to: none
environment:
sdk: '>=2.18.0 <3.0.0'
dependencies:
drift:
dev_dependencies:
build_runner: ^2.3.2
drift_dev:
lints: ^2.0.0
test: ^1.16.0