mirror of https://github.com/AMT-Cheif/drift.git
Start with generator for all schema versions
This commit is contained in:
parent
e6cccafd40
commit
8b7872fc67
|
@ -0,0 +1,73 @@
|
|||
import 'package:drift/drift.dart';
|
||||
|
||||
abstract base class VersionedSchema {
|
||||
final DatabaseConnectionUser database;
|
||||
|
||||
VersionedSchema(this.database);
|
||||
|
||||
DatabaseSchemaEntity lookup(String name, int version) {
|
||||
return allEntitiesAt(version)
|
||||
.singleWhere((element) => element.entityName == name);
|
||||
}
|
||||
|
||||
Iterable<DatabaseSchemaEntity> allEntitiesAt(int version);
|
||||
}
|
||||
|
||||
class VersionedTable extends Table with TableInfo<Table, QueryRow> {
|
||||
@override
|
||||
final String entityName;
|
||||
final String? _alias;
|
||||
@override
|
||||
final bool isStrict;
|
||||
|
||||
@override
|
||||
final bool withoutRowId;
|
||||
|
||||
@override
|
||||
final DatabaseConnectionUser attachedDatabase;
|
||||
|
||||
@override
|
||||
final List<GeneratedColumn> $columns;
|
||||
|
||||
@override
|
||||
final List<String> customConstraints;
|
||||
|
||||
VersionedTable({
|
||||
required this.entityName,
|
||||
required this.isStrict,
|
||||
required this.withoutRowId,
|
||||
required this.attachedDatabase,
|
||||
required List<GeneratedColumn Function(String)> columns,
|
||||
required List<String> tableConstraints,
|
||||
String? alias,
|
||||
}) : customConstraints = tableConstraints,
|
||||
$columns = [for (final column in columns) column(alias ?? entityName)],
|
||||
_alias = alias;
|
||||
|
||||
@override
|
||||
String get actualTableName => entityName;
|
||||
|
||||
@override
|
||||
String get aliasedName => _alias ?? entityName;
|
||||
|
||||
@override
|
||||
bool get dontWriteConstraints => true;
|
||||
|
||||
@override
|
||||
VersionedTable createAlias(String alias) {
|
||||
return VersionedTable(
|
||||
entityName: entityName,
|
||||
isStrict: isStrict,
|
||||
withoutRowId: withoutRowId,
|
||||
attachedDatabase: attachedDatabase,
|
||||
columns: $columns,
|
||||
tableConstraints: customConstraints,
|
||||
alias: alias,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
QueryRow map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
return QueryRow(data, attachedDatabase);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import 'commands/schema.dart';
|
|||
import 'logging.dart';
|
||||
|
||||
Future run(List<String> args) async {
|
||||
final cli = MoorCli();
|
||||
final cli = DriftDevCli();
|
||||
try {
|
||||
return await cli.run(args);
|
||||
} on UsageException catch (e) {
|
||||
|
@ -21,14 +21,14 @@ Future run(List<String> args) async {
|
|||
}
|
||||
}
|
||||
|
||||
class MoorCli {
|
||||
class DriftDevCli {
|
||||
Logger get logger => Logger.root;
|
||||
late final CommandRunner _runner;
|
||||
late final MoorProject project;
|
||||
|
||||
bool verbose = false;
|
||||
|
||||
MoorCli() {
|
||||
DriftDevCli() {
|
||||
_runner = CommandRunner(
|
||||
'dart run drift_dev',
|
||||
'CLI utilities for the drift package, currently in an experimental state.',
|
||||
|
@ -72,7 +72,7 @@ class MoorCli {
|
|||
}
|
||||
|
||||
abstract class MoorCommand extends Command {
|
||||
final MoorCli cli;
|
||||
final DriftDevCli cli;
|
||||
|
||||
MoorCommand(this.cli);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:io';
|
|||
import '../cli.dart';
|
||||
|
||||
class AnalyzeCommand extends MoorCommand {
|
||||
AnalyzeCommand(MoorCli cli) : super(cli);
|
||||
AnalyzeCommand(DriftDevCli cli) : super(cli);
|
||||
|
||||
@override
|
||||
String get description => 'Analyze and lint drift database code';
|
||||
|
|
|
@ -7,7 +7,7 @@ import '../../analysis/results/results.dart';
|
|||
import '../cli.dart';
|
||||
|
||||
class IdentifyDatabases extends MoorCommand {
|
||||
IdentifyDatabases(MoorCli cli) : super(cli);
|
||||
IdentifyDatabases(DriftDevCli cli) : super(cli);
|
||||
|
||||
@override
|
||||
String get description =>
|
||||
|
|
|
@ -23,7 +23,7 @@ class MigrateCommand extends MoorCommand {
|
|||
|
||||
late final AnalysisContext context;
|
||||
|
||||
MigrateCommand(MoorCli cli) : super(cli);
|
||||
MigrateCommand(DriftDevCli cli) : super(cli);
|
||||
|
||||
@override
|
||||
String get description => 'Migrate a project from moor to drift';
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import 'package:args/command_runner.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../analysis/results/results.dart';
|
||||
import '../../services/schema/schema_files.dart';
|
||||
import 'schema/dump.dart';
|
||||
import 'schema/generate_utils.dart';
|
||||
|
||||
import '../cli.dart';
|
||||
import 'schema/versions.dart';
|
||||
|
||||
class SchemaCommand extends Command {
|
||||
@override
|
||||
|
@ -12,8 +19,37 @@ class SchemaCommand extends Command {
|
|||
@override
|
||||
String get name => 'schema';
|
||||
|
||||
SchemaCommand(MoorCli cli) {
|
||||
SchemaCommand(DriftDevCli cli) {
|
||||
addSubcommand(DumpSchemaCommand(cli));
|
||||
addSubcommand(GenerateUtilsCommand(cli));
|
||||
addSubcommand(WriteVersions(cli));
|
||||
}
|
||||
}
|
||||
|
||||
class ExportedSchema {
|
||||
final List<DriftElement> schema;
|
||||
final Map<String, Object?> options;
|
||||
|
||||
ExportedSchema(this.schema, this.options);
|
||||
}
|
||||
|
||||
final _filenames = RegExp(r'(?:moor|drift)_schema_v(\d+)\.json');
|
||||
|
||||
Future<Map<int, ExportedSchema>> parseSchema(Directory directory) async {
|
||||
final results = <int, ExportedSchema>{};
|
||||
|
||||
await for (final entity in directory.list()) {
|
||||
final basename = p.basename(entity.path);
|
||||
final match = _filenames.firstMatch(basename);
|
||||
|
||||
if (match == null || entity is! File) continue;
|
||||
|
||||
final version = int.parse(match.group(1)!);
|
||||
final rawData = json.decode(await entity.readAsString());
|
||||
|
||||
final schema = SchemaReader.readJson(rawData as Map<String, dynamic>);
|
||||
results[version] = ExportedSchema(schema.entities.toList(), schema.options);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class DumpSchemaCommand extends Command {
|
|||
return '${runner!.executableName} schema dump [arguments] <input> <output>';
|
||||
}
|
||||
|
||||
final MoorCli cli;
|
||||
final DriftDevCli cli;
|
||||
|
||||
DumpSchemaCommand(this.cli) {
|
||||
argParser.addSeparator("It's recommended to run this commend from the "
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
|
@ -13,9 +12,10 @@ import '../../../writer/database_writer.dart';
|
|||
import '../../../writer/import_manager.dart';
|
||||
import '../../../writer/writer.dart';
|
||||
import '../../cli.dart';
|
||||
import '../schema.dart';
|
||||
|
||||
class GenerateUtilsCommand extends Command {
|
||||
final MoorCli cli;
|
||||
final DriftDevCli cli;
|
||||
|
||||
GenerateUtilsCommand(this.cli) {
|
||||
argParser.addFlag(
|
||||
|
@ -61,7 +61,7 @@ class GenerateUtilsCommand extends Command {
|
|||
await outputDir.create();
|
||||
}
|
||||
|
||||
final schema = await _parseSchema(inputDir);
|
||||
final schema = await parseSchema(inputDir);
|
||||
for (final versionAndEntities in schema.entries) {
|
||||
final version = versionAndEntities.key;
|
||||
final entities = versionAndEntities.value;
|
||||
|
@ -81,30 +81,10 @@ class GenerateUtilsCommand extends Command {
|
|||
'Wrote ${schema.length + 1} files into ${p.relative(outputDir.path)}');
|
||||
}
|
||||
|
||||
Future<Map<int, _ExportedSchema>> _parseSchema(Directory directory) async {
|
||||
final results = <int, _ExportedSchema>{};
|
||||
|
||||
await for (final entity in directory.list()) {
|
||||
final basename = p.basename(entity.path);
|
||||
final match = _filenames.firstMatch(basename);
|
||||
|
||||
if (match == null || entity is! File) continue;
|
||||
|
||||
final version = int.parse(match.group(1)!);
|
||||
final rawData = json.decode(await entity.readAsString());
|
||||
|
||||
final schema = SchemaReader.readJson(rawData as Map<String, dynamic>);
|
||||
results[version] =
|
||||
_ExportedSchema(schema.entities.toList(), schema.options);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
Future<void> _writeSchemaFile(
|
||||
Directory output,
|
||||
int version,
|
||||
_ExportedSchema schema,
|
||||
ExportedSchema schema,
|
||||
bool dataClasses,
|
||||
bool companions,
|
||||
) {
|
||||
|
@ -184,15 +164,7 @@ class GenerateUtilsCommand extends Command {
|
|||
|
||||
String _filenameForVersion(int version) => 'schema_v$version.dart';
|
||||
|
||||
static final _filenames = RegExp(r'(?:moor|drift)_schema_v(\d+)\.json');
|
||||
static final _dartfmt = DartFormatter();
|
||||
static const _prefix = '// GENERATED CODE, DO NOT EDIT BY HAND.\n'
|
||||
'// ignore_for_file: type=lint';
|
||||
}
|
||||
|
||||
class _ExportedSchema {
|
||||
final List<DriftElement> schema;
|
||||
final Map<String, Object?> options;
|
||||
|
||||
_ExportedSchema(this.schema, this.options);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
|
||||
import '../../../analysis/options.dart';
|
||||
import '../../../analysis/results/element.dart';
|
||||
import '../../../writer/import_manager.dart';
|
||||
import '../../../writer/schema_version_writer.dart';
|
||||
import '../../../writer/writer.dart';
|
||||
import '../../cli.dart';
|
||||
import '../schema.dart';
|
||||
|
||||
class WriteVersions extends Command {
|
||||
final DriftDevCli cli;
|
||||
|
||||
WriteVersions(this.cli);
|
||||
|
||||
@override
|
||||
String get name => 'versions';
|
||||
|
||||
@override
|
||||
String get description =>
|
||||
'Write a Dart file helping with incremental migrations between schema versions.';
|
||||
|
||||
@override
|
||||
String get invocation {
|
||||
return '${runner!.executableName} schema versions <schema directory> <output file>';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> run() async {
|
||||
final rest = argResults!.rest;
|
||||
if (rest.length != 2) {
|
||||
usageException('Expected input and output directories');
|
||||
}
|
||||
|
||||
final inputDirectory = Directory(rest[0]);
|
||||
final outputFile = File(rest[1]);
|
||||
final outputDirectory = outputFile.parent;
|
||||
|
||||
if (!await inputDirectory.exists()) {
|
||||
cli.exit('The provided input directory does not exist.');
|
||||
}
|
||||
|
||||
if (!await outputDirectory.exists()) {
|
||||
await outputDirectory.create();
|
||||
}
|
||||
|
||||
final imports = LibraryImportManager();
|
||||
final writer = Writer(
|
||||
const DriftOptions.defaults(),
|
||||
generationOptions: GenerationOptions(imports: imports),
|
||||
);
|
||||
imports.linkToWriter(writer);
|
||||
|
||||
final schema = await parseSchema(inputDirectory);
|
||||
final byVersion = [
|
||||
for (final MapEntry(key: version, value: schema) in schema.entries)
|
||||
SchemaVersion(
|
||||
version,
|
||||
schema.schema.whereType<DriftSchemaElement>().toList(),
|
||||
schema.options,
|
||||
),
|
||||
];
|
||||
byVersion.sortBy<num>((s) => s.version);
|
||||
|
||||
writer.leaf().write("import 'package:drift/drift.dart';");
|
||||
SchemaVersionWriter(byVersion, writer.child()).write();
|
||||
|
||||
var code = writer.writeGenerated();
|
||||
try {
|
||||
code = DartFormatter().format(code);
|
||||
} on FormatterException {
|
||||
// Ignore. Probably a bug in drift_dev, the user will notice.
|
||||
}
|
||||
|
||||
await outputFile.writeAsString(code);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
import 'package:collection/collection.dart';
|
||||
|
||||
import '../analysis/results/results.dart';
|
||||
import '../utils/string_escaper.dart';
|
||||
import 'tables/table_writer.dart';
|
||||
import 'writer.dart';
|
||||
|
||||
class SchemaVersion {
|
||||
final int version;
|
||||
final List<DriftSchemaElement> schema;
|
||||
final Map<String, Object?> options;
|
||||
|
||||
SchemaVersion(this.version, this.schema, this.options);
|
||||
}
|
||||
|
||||
class SchemaVersionWriter {
|
||||
static final Uri _schemaLibrary =
|
||||
Uri.parse('package:drift/internal/versioned_schema.dart');
|
||||
|
||||
/// All schema versions, sorted by [SchemaVersion.version].
|
||||
final List<SchemaVersion> versions;
|
||||
final Scope scope;
|
||||
|
||||
final Map<String, String> _columnCodeToFactory = {};
|
||||
|
||||
/// All schema entities across all versions are put in a single list, sorted
|
||||
/// by their schema version.
|
||||
///
|
||||
/// By keeping a start and end-index pair for each schema, we can efficiently
|
||||
/// find all schema entities for a given version at runtime.
|
||||
final rangesForVersion = <(int, int)>[];
|
||||
|
||||
SchemaVersionWriter(this.versions, this.scope);
|
||||
|
||||
String _referenceColumn(DriftColumn column) {
|
||||
final text = scope.leaf();
|
||||
final (type, code) = TableOrViewWriter.instantiateColumn(column, text);
|
||||
|
||||
return _columnCodeToFactory.putIfAbsent(code, () {
|
||||
final methodName = 'column_${_columnCodeToFactory.length}';
|
||||
text.write('$type $methodName(String aliasedName) => $code;');
|
||||
return methodName;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeTable(DriftTable table, TextEmitter writer) {
|
||||
writer
|
||||
..writeUriRef(_schemaLibrary, 'VersionedTable(')
|
||||
..write('entityName: ${asDartLiteral(table.schemaName)},')
|
||||
..write('withoutRowId: ${table.withoutRowId},')
|
||||
..write('isStrict: ${table.strict},')
|
||||
..write('attachedDatabase: database,')
|
||||
..write('columns: [');
|
||||
|
||||
for (final column in table.columns) {
|
||||
writer
|
||||
..write(_referenceColumn(column))
|
||||
..write(',');
|
||||
}
|
||||
|
||||
writer
|
||||
..write('],')
|
||||
..write('tableConstraints: [],')
|
||||
..write(')');
|
||||
}
|
||||
|
||||
void _writeEntity(DriftSchemaElement element, TextEmitter writer) {
|
||||
if (element is DriftTable) {
|
||||
_writeTable(element, writer);
|
||||
} else if (element is DriftIndex) {
|
||||
writer
|
||||
..writeDriftRef('Index(')
|
||||
..write(asDartLiteral(element.schemaName))
|
||||
..write(',')
|
||||
..write(asDartLiteral(element.createStmt))
|
||||
..write(')');
|
||||
} else if (element is DriftTrigger) {
|
||||
writer
|
||||
..writeDriftRef('Trigger(')
|
||||
..write(asDartLiteral(element.createStmt))
|
||||
..write(',')
|
||||
..write(asDartLiteral(element.schemaName))
|
||||
..write(')');
|
||||
} else {
|
||||
writer.writeln('null');
|
||||
}
|
||||
}
|
||||
|
||||
void _implementAllEntitiesAt(TextEmitter writer) {
|
||||
writer
|
||||
..writeln('@override')
|
||||
..write('Iterable<')
|
||||
..writeDriftRef('DatabaseSchemaEntity')
|
||||
..writeln('> allEntitiesAt(int version) {')
|
||||
..writeln('int start, count;')
|
||||
..writeln('switch (version) {');
|
||||
|
||||
versions.forEachIndexed((index, schema) {
|
||||
final (start, end) = rangesForVersion[index];
|
||||
|
||||
writer
|
||||
..writeln('case ${schema.version}:')
|
||||
..writeln('start = $start;')
|
||||
..writeln('count = ${end - start};');
|
||||
});
|
||||
|
||||
writer
|
||||
..writeln('default:')
|
||||
..writeln(r"throw ArgumentError('Unknown schema version $version');");
|
||||
|
||||
writer
|
||||
..writeln('}')
|
||||
..writeln('return entities.skip(start).take(count);')
|
||||
..writeln('}');
|
||||
}
|
||||
|
||||
void write() {
|
||||
final classWriter = scope.leaf();
|
||||
|
||||
classWriter
|
||||
..write('final class VersionedSchema extends ')
|
||||
..writeUriRef(_schemaLibrary, 'VersionedSchema')
|
||||
..writeln('{')
|
||||
..writeln('VersionedSchema(super.database);');
|
||||
|
||||
var currentIndex = 0;
|
||||
classWriter
|
||||
..write('late final ')
|
||||
..writeUriRef(AnnotatedDartCode.dartCore, 'List')
|
||||
..write('<')
|
||||
..writeDriftRef('DatabaseSchemaEntity')
|
||||
..write('> entities = [');
|
||||
|
||||
for (final version in versions) {
|
||||
classWriter.writeln(' // VERSION ${version.version}');
|
||||
final startIndex = currentIndex;
|
||||
|
||||
for (final entity in version.schema) {
|
||||
_writeEntity(entity, classWriter);
|
||||
classWriter.writeln(',');
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
final endIndex = currentIndex;
|
||||
rangesForVersion.add((startIndex, endIndex));
|
||||
}
|
||||
classWriter.write('];');
|
||||
|
||||
_implementAllEntitiesAt(classWriter);
|
||||
|
||||
classWriter.writeln('}');
|
||||
}
|
||||
}
|
|
@ -16,140 +16,24 @@ abstract class TableOrViewWriter {
|
|||
StringBuffer get buffer => emitter.buffer;
|
||||
|
||||
void writeColumnGetter(DriftColumn column, bool isOverride) {
|
||||
final isNullable = column.nullable;
|
||||
final additionalParams = <String, String>{};
|
||||
final expressionBuffer = StringBuffer();
|
||||
final constraints = defaultConstraints(column);
|
||||
|
||||
for (final constraint in column.constraints) {
|
||||
if (constraint is LimitingTextLength) {
|
||||
final buffer =
|
||||
StringBuffer(emitter.drift('GeneratedColumn.checkTextLength('));
|
||||
|
||||
if (constraint.minLength != null) {
|
||||
buffer.write('minTextLength: ${constraint.minLength},');
|
||||
}
|
||||
if (constraint.maxLength != null) {
|
||||
buffer.write('maxTextLength: ${constraint.maxLength}');
|
||||
}
|
||||
buffer.write(')');
|
||||
|
||||
additionalParams['additionalChecks'] = buffer.toString();
|
||||
}
|
||||
|
||||
if (constraint is DartCheckExpression) {
|
||||
final dartCheck = emitter.dartCode(constraint.dartExpression);
|
||||
additionalParams['check'] = '() => $dartCheck';
|
||||
}
|
||||
|
||||
if (constraint is ColumnGeneratedAs) {
|
||||
final dartCode = emitter.dartCode(constraint.dartExpression);
|
||||
|
||||
additionalParams['generatedAs'] =
|
||||
'${emitter.drift('GeneratedAs')}($dartCode, ${constraint.stored})';
|
||||
}
|
||||
|
||||
if (constraint is PrimaryKeyColumn && constraint.isAutoIncrement) {
|
||||
additionalParams['hasAutoIncrement'] = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
additionalParams['type'] = emitter.drift(column.sqlType.toString());
|
||||
bool? isRequiredForInsert;
|
||||
|
||||
if (tableOrView is DriftTable) {
|
||||
additionalParams['requiredDuringInsert'] = (tableOrView as DriftTable)
|
||||
.isColumnRequiredForInsert(column)
|
||||
.toString();
|
||||
isRequiredForInsert =
|
||||
(tableOrView as DriftTable).isColumnRequiredForInsert(column);
|
||||
}
|
||||
|
||||
if (column.customConstraints != null) {
|
||||
additionalParams['\$customConstraints'] =
|
||||
asDartLiteral(column.customConstraints!);
|
||||
} else if (constraints.values.any((constraint) => constraint.isNotEmpty)) {
|
||||
// Use the default constraints supported by drift
|
||||
|
||||
if (constraints.values.any(
|
||||
(value) => value != constraints.values.first,
|
||||
)) {
|
||||
// One or more constraints are different depending on dialect, generate
|
||||
// per-dialect constraints
|
||||
|
||||
final literalEntries = [
|
||||
for (final entry in constraints.entries)
|
||||
'${emitter.drift('SqlDialect.${entry.key.name}')}: ${asDartLiteral(entry.value)},',
|
||||
];
|
||||
|
||||
additionalParams['defaultConstraints'] =
|
||||
'${emitter.drift('GeneratedColumn.constraintsDependsOnDialect')}({${literalEntries.join('\n')}})';
|
||||
} else {
|
||||
// Constraints are the same regardless of dialect, only generate one set
|
||||
// of them
|
||||
|
||||
final constraint = asDartLiteral(constraints.values.first);
|
||||
|
||||
additionalParams['defaultConstraints'] =
|
||||
'${emitter.drift('GeneratedColumn.constraintIsAlways')}($constraint)';
|
||||
}
|
||||
}
|
||||
|
||||
if (column.defaultArgument != null) {
|
||||
additionalParams['defaultValue'] =
|
||||
emitter.dartCode(column.defaultArgument!);
|
||||
}
|
||||
|
||||
if (column.clientDefaultCode != null) {
|
||||
additionalParams['clientDefault'] =
|
||||
emitter.dartCode(column.clientDefaultCode!);
|
||||
}
|
||||
|
||||
final innerType = emitter.innerColumnType(column);
|
||||
var type =
|
||||
'${emitter.drift('GeneratedColumn')}<${emitter.dartCode(innerType)}>';
|
||||
expressionBuffer
|
||||
..write(type)
|
||||
..write(
|
||||
'(${asDartLiteral(column.nameInSql)}, aliasedName, $isNullable, ');
|
||||
|
||||
var first = true;
|
||||
additionalParams.forEach((name, value) {
|
||||
if (!first) {
|
||||
expressionBuffer.write(', ');
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
expressionBuffer
|
||||
..write(name)
|
||||
..write(': ')
|
||||
..write(value);
|
||||
});
|
||||
|
||||
expressionBuffer.write(')');
|
||||
|
||||
final converter = column.typeConverter;
|
||||
if (converter != null) {
|
||||
// Generate a GeneratedColumnWithTypeConverter instance, as it has
|
||||
// additional methods to check for equality against a mapped value.
|
||||
final mappedType = emitter.dartCode(emitter.writer.dartType(column));
|
||||
|
||||
final converterCode = emitter.dartCode(emitter.writer
|
||||
.readConverter(converter, forNullable: column.nullable));
|
||||
|
||||
type = '${emitter.drift('GeneratedColumnWithTypeConverter')}'
|
||||
'<$mappedType, ${emitter.dartCode(innerType)}>';
|
||||
expressionBuffer
|
||||
..write('.withConverter<')
|
||||
..write(mappedType)
|
||||
..write('>(')
|
||||
..write(converterCode)
|
||||
..write(')');
|
||||
}
|
||||
final (type, expression) = instantiateColumn(
|
||||
column,
|
||||
emitter,
|
||||
isRequiredForInsert: isRequiredForInsert,
|
||||
);
|
||||
|
||||
writeMemoizedGetter(
|
||||
buffer: buffer,
|
||||
getterName: column.nameInDart,
|
||||
returnType: type,
|
||||
code: expressionBuffer.toString(),
|
||||
code: expression,
|
||||
hasOverride: isOverride,
|
||||
);
|
||||
}
|
||||
|
@ -276,6 +160,143 @@ abstract class TableOrViewWriter {
|
|||
buffer.write(
|
||||
'@override\n${tableOrView.entityInfoName} get asDslTable => this;\n');
|
||||
}
|
||||
|
||||
/// Returns the Dart type and the Dart expression creating a `GeneratedColumn`
|
||||
/// instance in drift for the givne [column].
|
||||
static (String, String) instantiateColumn(
|
||||
DriftColumn column,
|
||||
TextEmitter emitter, {
|
||||
bool? isRequiredForInsert,
|
||||
}) {
|
||||
final isNullable = column.nullable;
|
||||
final additionalParams = <String, String>{};
|
||||
final expressionBuffer = StringBuffer();
|
||||
final constraints = defaultConstraints(column);
|
||||
|
||||
for (final constraint in column.constraints) {
|
||||
if (constraint is LimitingTextLength) {
|
||||
final buffer =
|
||||
StringBuffer(emitter.drift('GeneratedColumn.checkTextLength('));
|
||||
|
||||
if (constraint.minLength != null) {
|
||||
buffer.write('minTextLength: ${constraint.minLength},');
|
||||
}
|
||||
if (constraint.maxLength != null) {
|
||||
buffer.write('maxTextLength: ${constraint.maxLength}');
|
||||
}
|
||||
buffer.write(')');
|
||||
|
||||
additionalParams['additionalChecks'] = buffer.toString();
|
||||
}
|
||||
|
||||
if (constraint is DartCheckExpression) {
|
||||
final dartCheck = emitter.dartCode(constraint.dartExpression);
|
||||
additionalParams['check'] = '() => $dartCheck';
|
||||
}
|
||||
|
||||
if (constraint is ColumnGeneratedAs) {
|
||||
final dartCode = emitter.dartCode(constraint.dartExpression);
|
||||
|
||||
additionalParams['generatedAs'] =
|
||||
'${emitter.drift('GeneratedAs')}($dartCode, ${constraint.stored})';
|
||||
}
|
||||
|
||||
if (constraint is PrimaryKeyColumn && constraint.isAutoIncrement) {
|
||||
additionalParams['hasAutoIncrement'] = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
additionalParams['type'] = emitter.drift(column.sqlType.toString());
|
||||
|
||||
if (isRequiredForInsert != null) {
|
||||
additionalParams['requiredDuringInsert'] = isRequiredForInsert.toString();
|
||||
}
|
||||
|
||||
if (column.customConstraints != null) {
|
||||
additionalParams['\$customConstraints'] =
|
||||
asDartLiteral(column.customConstraints!);
|
||||
} else if (constraints.values.any((constraint) => constraint.isNotEmpty)) {
|
||||
// Use the default constraints supported by drift
|
||||
|
||||
if (constraints.values.any(
|
||||
(value) => value != constraints.values.first,
|
||||
)) {
|
||||
// One or more constraints are different depending on dialect, generate
|
||||
// per-dialect constraints
|
||||
|
||||
final literalEntries = [
|
||||
for (final entry in constraints.entries)
|
||||
'${emitter.drift('SqlDialect.${entry.key.name}')}: ${asDartLiteral(entry.value)},',
|
||||
];
|
||||
|
||||
additionalParams['defaultConstraints'] =
|
||||
'${emitter.drift('GeneratedColumn.constraintsDependsOnDialect')}({${literalEntries.join('\n')}})';
|
||||
} else {
|
||||
// Constraints are the same regardless of dialect, only generate one set
|
||||
// of them
|
||||
|
||||
final constraint = asDartLiteral(constraints.values.first);
|
||||
|
||||
additionalParams['defaultConstraints'] =
|
||||
'${emitter.drift('GeneratedColumn.constraintIsAlways')}($constraint)';
|
||||
}
|
||||
}
|
||||
|
||||
if (column.defaultArgument != null) {
|
||||
additionalParams['defaultValue'] =
|
||||
emitter.dartCode(column.defaultArgument!);
|
||||
}
|
||||
|
||||
if (column.clientDefaultCode != null) {
|
||||
additionalParams['clientDefault'] =
|
||||
emitter.dartCode(column.clientDefaultCode!);
|
||||
}
|
||||
|
||||
final innerType = emitter.innerColumnType(column);
|
||||
var type =
|
||||
'${emitter.drift('GeneratedColumn')}<${emitter.dartCode(innerType)}>';
|
||||
expressionBuffer
|
||||
..write(type)
|
||||
..write(
|
||||
'(${asDartLiteral(column.nameInSql)}, aliasedName, $isNullable, ');
|
||||
|
||||
var first = true;
|
||||
additionalParams.forEach((name, value) {
|
||||
if (!first) {
|
||||
expressionBuffer.write(', ');
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
expressionBuffer
|
||||
..write(name)
|
||||
..write(': ')
|
||||
..write(value);
|
||||
});
|
||||
|
||||
expressionBuffer.write(')');
|
||||
|
||||
final converter = column.typeConverter;
|
||||
if (converter != null) {
|
||||
// Generate a GeneratedColumnWithTypeConverter instance, as it has
|
||||
// additional methods to check for equality against a mapped value.
|
||||
final mappedType = emitter.dartCode(emitter.writer.dartType(column));
|
||||
|
||||
final converterCode = emitter.dartCode(emitter.writer
|
||||
.readConverter(converter, forNullable: column.nullable));
|
||||
|
||||
type = '${emitter.drift('GeneratedColumnWithTypeConverter')}'
|
||||
'<$mappedType, ${emitter.dartCode(innerType)}>';
|
||||
expressionBuffer
|
||||
..write('.withConverter<')
|
||||
..write(mappedType)
|
||||
..write('>(')
|
||||
..write(converterCode)
|
||||
..write(')');
|
||||
}
|
||||
|
||||
return (type, expressionBuffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
class TableWriter extends TableOrViewWriter {
|
||||
|
|
|
@ -11,7 +11,7 @@ topics:
|
|||
- database
|
||||
|
||||
environment:
|
||||
sdk: '>=2.17.0 <4.0.0'
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
charcode: ^1.2.0
|
||||
|
|
|
@ -13,7 +13,7 @@ class TestDriftProject {
|
|||
|
||||
Future<void> runDriftCli(Iterable<String> args) {
|
||||
return IOOverrides.runZoned(
|
||||
() => MoorCli().run(args),
|
||||
() => DriftDevCli().run(args),
|
||||
getCurrentDirectory: () => root,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
import 'package:drift/internal/versioned_schema.dart' as i0;
|
||||
import 'package:drift/drift.dart' as i1;
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
final class VersionedSchema extends i0.VersionedSchema {
|
||||
VersionedSchema(super.database);
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
// VERSION 1
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
// VERSION 2
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
column_1,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
// VERSION 3
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
column_1,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_2,
|
||||
column_3,
|
||||
column_4,
|
||||
column_5,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
// VERSION 4
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
column_6,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_2,
|
||||
column_3,
|
||||
column_4,
|
||||
column_5,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
// VERSION 5
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
column_6,
|
||||
column_7,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_2,
|
||||
column_3,
|
||||
column_4,
|
||||
column_5,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
null,
|
||||
// VERSION 6
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
column_6,
|
||||
column_8,
|
||||
column_7,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_2,
|
||||
column_3,
|
||||
column_4,
|
||||
column_5,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
null,
|
||||
// VERSION 7
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
column_6,
|
||||
column_8,
|
||||
column_7,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_2,
|
||||
column_3,
|
||||
column_4,
|
||||
column_5,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
null,
|
||||
i0.VersionedTable(
|
||||
entityName: 'notes',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_9,
|
||||
column_10,
|
||||
column_11,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
// VERSION 8
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
column_6,
|
||||
column_8,
|
||||
column_12,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_2,
|
||||
column_3,
|
||||
column_4,
|
||||
column_5,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
null,
|
||||
i0.VersionedTable(
|
||||
entityName: 'notes',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_9,
|
||||
column_10,
|
||||
column_11,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
// VERSION 9
|
||||
i0.VersionedTable(
|
||||
entityName: 'users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_0,
|
||||
column_6,
|
||||
column_8,
|
||||
column_7,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_2,
|
||||
column_3,
|
||||
column_13,
|
||||
column_14,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
i0.VersionedTable(
|
||||
entityName: 'notes',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
attachedDatabase: database,
|
||||
columns: [
|
||||
column_9,
|
||||
column_10,
|
||||
column_11,
|
||||
],
|
||||
tableConstraints: [],
|
||||
),
|
||||
null,
|
||||
];
|
||||
@override
|
||||
Iterable<i1.DatabaseSchemaEntity> allEntitiesAt(int version) {
|
||||
int start, count;
|
||||
switch (version) {
|
||||
case 1:
|
||||
start = 0;
|
||||
count = 1;
|
||||
case 2:
|
||||
start = 1;
|
||||
count = 1;
|
||||
case 3:
|
||||
start = 2;
|
||||
count = 2;
|
||||
case 4:
|
||||
start = 4;
|
||||
count = 2;
|
||||
case 5:
|
||||
start = 6;
|
||||
count = 3;
|
||||
case 6:
|
||||
start = 9;
|
||||
count = 3;
|
||||
case 7:
|
||||
start = 12;
|
||||
count = 4;
|
||||
case 8:
|
||||
start = 16;
|
||||
count = 4;
|
||||
case 9:
|
||||
start = 20;
|
||||
count = 4;
|
||||
default:
|
||||
throw ArgumentError('Unknown schema version $version');
|
||||
}
|
||||
return entities.skip(start).take(count);
|
||||
}
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> column_0(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||
hasAutoIncrement: true,
|
||||
type: i1.DriftSqlType.int,
|
||||
defaultConstraints:
|
||||
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||
i1.GeneratedColumn<String> column_1(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<int> column_2(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||
type: i1.DriftSqlType.int, $customConstraints: 'NOT NULL');
|
||||
i1.GeneratedColumn<String> column_3(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('title', aliasedName, false,
|
||||
type: i1.DriftSqlType.string, $customConstraints: 'NOT NULL');
|
||||
i1.GeneratedColumn<bool> column_4(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('deleted', aliasedName, true,
|
||||
type: i1.DriftSqlType.bool,
|
||||
$customConstraints: 'DEFAULT FALSE',
|
||||
defaultValue: const CustomExpression<bool>('FALSE'));
|
||||
i1.GeneratedColumn<int> column_5(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('owner', aliasedName, false,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NOT NULL REFERENCES users (id)');
|
||||
i1.GeneratedColumn<String> column_6(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||
type: i1.DriftSqlType.string, defaultValue: const Constant('name'));
|
||||
i1.GeneratedColumn<int> column_7(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('next_user', aliasedName, true,
|
||||
type: i1.DriftSqlType.int,
|
||||
defaultConstraints:
|
||||
i1.GeneratedColumn.constraintIsAlways('REFERENCES users (id)'));
|
||||
i1.GeneratedColumn<DateTime> column_8(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('birthday', aliasedName, true,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<String> column_9(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('title', aliasedName, false,
|
||||
type: i1.DriftSqlType.string, $customConstraints: '');
|
||||
i1.GeneratedColumn<String> column_10(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('content', aliasedName, false,
|
||||
type: i1.DriftSqlType.string, $customConstraints: '');
|
||||
i1.GeneratedColumn<String> column_11(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('search_terms', aliasedName, false,
|
||||
type: i1.DriftSqlType.string, $customConstraints: '');
|
||||
i1.GeneratedColumn<int> column_12(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('next_user', aliasedName, true,
|
||||
type: i1.DriftSqlType.int,
|
||||
defaultConstraints:
|
||||
i1.GeneratedColumn.constraintIsAlways('REFERENCES "users" ("id")'));
|
||||
i1.GeneratedColumn<bool> column_13(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('deleted', aliasedName, true,
|
||||
type: i1.DriftSqlType.bool,
|
||||
$customConstraints: 'DEFAULT FALSE',
|
||||
defaultValue: const CustomExpression('FALSE'));
|
||||
i1.GeneratedColumn<int> column_14(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('owner', aliasedName, false,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NOT NULL REFERENCES users(id)');
|
|
@ -3,7 +3,7 @@ publish_to: none
|
|||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
drift:
|
||||
|
|
Loading…
Reference in New Issue