Start migrating schema files to new analyzer

This commit is contained in:
Simon Binder 2022-11-05 19:44:43 +01:00
parent c5a800d4c4
commit 3b12faaa46
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
1 changed files with 123 additions and 91 deletions

View File

@ -1,25 +1,25 @@
import 'package:drift_dev/moor_generator.dart'; import 'package:collection/collection.dart';
import 'package:drift_dev/src/analyzer/options.dart'; import 'package:drift/drift.dart' show DriftSqlType, UpdateKind;
import 'package:recase/recase.dart'; import 'package:recase/recase.dart';
import 'package:sqlparser/sqlparser.dart'; import 'package:sqlparser/sqlparser.dart' hide PrimaryKeyColumn;
import '../../analysis/results/results.dart';
import '../../analyzer/options.dart';
import '../../writer/utils/column_constraints.dart'; import '../../writer/utils/column_constraints.dart';
const _infoVersion = '1.0.0'; const _infoVersion = '1.0.0';
/// Utilities to transform moor schema entities to json. /// Utilities to transform moor schema entities to json.
class SchemaWriter { class SchemaWriter {
/// The parsed and resolved database for which the schema should be written.
final Database db;
final DriftOptions options; final DriftOptions options;
final List<DriftElement> elements;
final Map<DriftSchemaEntity, int> _entityIds = {}; final Map<DriftElement, int> _entityIds = {};
int _maxId = 0; int _maxId = 0;
SchemaWriter(this.db, {this.options = const DriftOptions.defaults()}); SchemaWriter(this.elements, {this.options = const DriftOptions.defaults()});
int _idOf(DriftSchemaEntity entity) { int _idOf(DriftElement entity) {
return _entityIds.putIfAbsent(entity, () => _maxId++); return _entityIds.putIfAbsent(entity, () => _maxId++);
} }
@ -32,7 +32,7 @@ class SchemaWriter {
}, },
'options': _serializeOptions(), 'options': _serializeOptions(),
'entities': [ 'entities': [
for (final entity in db.entities) _entityToJson(entity), for (final entity in elements) _entityToJson(entity),
], ],
}; };
} }
@ -45,32 +45,34 @@ class SchemaWriter {
return asJson; return asJson;
} }
Map _entityToJson(DriftSchemaEntity entity) { Map _entityToJson(DriftElement entity) {
String type; String type;
Map data; Map data;
if (entity is DriftTable) { if (entity is DriftTable) {
type = 'table'; type = 'table';
data = _tableData(entity); data = _tableData(entity);
} else if (entity is MoorTrigger) { } else if (entity is DriftTrigger) {
type = 'trigger'; type = 'trigger';
data = { data = {
'on': _idOf(entity.on!), 'on': _idOf(entity.on!),
'refences_in_body': [ 'refences_in_body': [
for (final ref in entity.bodyReferences) _idOf(ref), for (final ref in entity.references.whereType<DriftSchemaElement>())
_idOf(ref),
], ],
'name': entity.displayName, 'name': entity.schemaName,
'sql': entity.createSql(options), 'sql': entity.createStmt,
}; };
} else if (entity is MoorIndex) { } else if (entity is DriftIndex) {
type = 'index'; type = 'index';
data = { data = {
'on': _idOf(entity.table!), 'on': _idOf(entity.table!),
'name': entity.name, 'name': entity.schemaName,
'sql': entity.createStmt, 'sql': entity.createStmt,
}; };
} else if (entity is MoorView) { } else if (entity is DriftView) {
if (entity.declaration is! DriftViewDeclaration) { final source = entity.source;
if (source is! SqlViewSource) {
throw UnsupportedError( throw UnsupportedError(
'Exporting Dart-defined views into a schema is not ' 'Exporting Dart-defined views into a schema is not '
'currently supported'); 'currently supported');
@ -78,13 +80,13 @@ class SchemaWriter {
type = 'view'; type = 'view';
data = { data = {
'name': entity.name, 'name': entity.schemaName,
'sql': entity.createSql(const DriftOptions.defaults()), 'sql': source.createView,
'dart_data_name': entity.dartTypeName, 'dart_data_name': entity.nameOfRowClass,
'dart_info_name': entity.entityInfoName, 'dart_info_name': entity.entityInfoName,
'columns': [for (final column in entity.columns) _columnData(column)], 'columns': [for (final column in entity.columns) _columnData(column)],
}; };
} else if (entity is SpecialQuery) { } else if (entity is DefinedSqlQuery && entity.mode == QueryMode.atCreate) {
type = 'special-query'; type = 'special-query';
data = { data = {
'scenario': 'create', 'scenario': 'create',
@ -106,23 +108,30 @@ class SchemaWriter {
} }
Map _tableData(DriftTable table) { Map _tableData(DriftTable table) {
final primaryKeyFromTableConstraint =
table.tableConstraints.whereType<PrimaryKeyColumns>().firstOrNull;
final uniqueKeys = table.tableConstraints.whereType<UniqueColumns>();
return { return {
'name': table.sqlName, 'name': table.schemaName,
'was_declared_in_moor': table.isFromSql, 'was_declared_in_moor': table.declaration.isDriftDeclaration,
'columns': [for (final column in table.columns) _columnData(column)], 'columns': [for (final column in table.columns) _columnData(column)],
'is_virtual': table.isVirtualTable, 'is_virtual': table.isVirtual,
if (table.isVirtualTable) 'create_virtual_stmt': table.createVirtual, if (table.isVirtual)
if (table.overrideWithoutRowId != null) 'create_virtual_stmt': 'CREATE VIRTUAL TABLE "${table.schemaName}" '
'without_rowid': table.overrideWithoutRowId, 'USING ${table.virtualTableData!.module}'
'(${table.virtualTableData!.moduleArguments.join(', ')})',
'without_rowid': table.withoutRowId,
if (table.overrideTableConstraints != null) if (table.overrideTableConstraints != null)
'constraints': table.overrideTableConstraints, 'constraints': table.overrideTableConstraints,
if (table.primaryKey != null) if (primaryKeyFromTableConstraint != null)
'explicit_pk': [...table.primaryKey!.map((c) => c.name.name)], 'explicit_pk': [
if (table.uniqueKeys != null && table.uniqueKeys!.isNotEmpty) ...primaryKeyFromTableConstraint.primaryKey.map((c) => c.nameInSql)
],
if (uniqueKeys.isNotEmpty)
'unique_keys': [ 'unique_keys': [
for (final uniqueKey for (final uniqueKey in uniqueKeys)
in table.uniqueKeys ?? const <Set<DriftColumn>>[]) [for (final column in uniqueKey.uniqueSet) column.nameInSql],
[for (final column in uniqueKey) column.name.name],
] ]
}; };
} }
@ -131,16 +140,16 @@ class SchemaWriter {
final constraints = defaultConstraints(column); final constraints = defaultConstraints(column);
return { return {
'name': column.name.name, 'name': column.nameInSql,
'getter_name': column.dartGetterName, 'getter_name': column.nameInDart,
'moor_type': column.type.toSerializedString(), 'moor_type': column.sqlType.toSerializedString(),
'nullable': column.nullable, 'nullable': column.nullable,
'customConstraints': column.customConstraints, 'customConstraints': column.customConstraints,
if (constraints.isNotEmpty && column.customConstraints == null) if (constraints.isNotEmpty && column.customConstraints == null)
'defaultConstraints': defaultConstraints(column), 'defaultConstraints': defaultConstraints(column),
'default_dart': column.defaultArgument, 'default_dart': column.defaultArgument,
'default_client_dart': column.clientDefaultCode, 'default_client_dart': column.clientDefaultCode,
'dsl_features': [...column.features.map(_dslFeatureData)], 'dsl_features': [...column.constraints.map(_dslFeatureData)],
if (column.typeConverter != null) if (column.typeConverter != null)
'type_converter': { 'type_converter': {
'dart_expr': column.typeConverter!.expression, 'dart_expr': column.typeConverter!.expression,
@ -150,11 +159,9 @@ class SchemaWriter {
}; };
} }
dynamic _dslFeatureData(ColumnFeature feature) { dynamic _dslFeatureData(DriftColumnConstraint feature) {
if (feature is AutoIncrement) { if (feature is PrimaryKeyColumn) {
return 'auto-increment'; return feature.isAutoIncrement ? 'auto-increment' : 'primary-key';
} else if (feature is PrimaryKey) {
return 'primary-key';
} else if (feature is LimitingTextLength) { } else if (feature is LimitingTextLength) {
return { return {
'allowed-lengths': { 'allowed-lengths': {
@ -169,7 +176,9 @@ class SchemaWriter {
/// Reads files generated by [SchemaWriter]. /// Reads files generated by [SchemaWriter].
class SchemaReader { class SchemaReader {
final Map<int, DriftSchemaEntity> _entitiesById = {}; static final Uri _elementUri = Uri.parse('drift:hidden');
final Map<int, DriftElement> _entitiesById = {};
final Map<int, Map<String, dynamic>> _rawById = {}; final Map<int, Map<String, dynamic>> _rawById = {};
final Set<int> _currentlyProcessing = {}; final Set<int> _currentlyProcessing = {};
@ -183,7 +192,7 @@ class SchemaReader {
return SchemaReader._().._read(json); return SchemaReader._().._read(json);
} }
Iterable<DriftSchemaEntity> get entities => _entitiesById.values; Iterable<DriftElement> get entities => _entitiesById.values;
void _read(Map<String, dynamic> json) { void _read(Map<String, dynamic> json) {
// Read drift options if they are part of the schema file. // Read drift options if they are part of the schema file.
@ -205,10 +214,15 @@ class SchemaReader {
_rawById.keys.forEach(_processById); _rawById.keys.forEach(_processById);
} }
T _existingEntity<T extends DriftSchemaEntity>(dynamic id) { T _existingEntity<T extends DriftElement>(dynamic id) {
return _entitiesById[id as int] as T; return _entitiesById[id as int] as T;
} }
DriftElementId _id(String name) => DriftElementId(_elementUri, name);
DriftDeclaration get _declaration =>
DriftDeclaration(_elementUri, -1, '<unknown>');
void _processById(int id) { void _processById(int id) {
if (_entitiesById.containsKey(id)) return; if (_entitiesById.containsKey(id)) return;
if (_currentlyProcessing.contains(id)) { if (_currentlyProcessing.contains(id)) {
@ -227,7 +241,7 @@ class SchemaReader {
final content = rawData?['data'] as Map<String, dynamic>; final content = rawData?['data'] as Map<String, dynamic>;
final type = rawData?['type'] as String; final type = rawData?['type'] as String;
DriftSchemaEntity entity; DriftElement entity;
switch (type) { switch (type) {
case 'index': case 'index':
entity = _readIndex(content); entity = _readIndex(content);
@ -252,30 +266,37 @@ class SchemaReader {
_entitiesById[id] = entity; _entitiesById[id] = entity;
} }
MoorIndex _readIndex(Map<String, dynamic> content) { DriftIndex _readIndex(Map<String, dynamic> content) {
final on = _existingEntity<DriftTable>(content['on']); final on = _existingEntity<DriftTable>(content['on']);
final name = content['name'] as String; final name = content['name'] as String;
final sql = content['sql'] as String; final sql = content['sql'] as String;
return MoorIndex(name, const CustomIndexDeclaration(), sql, on); return DriftIndex(_id(name), _declaration, table: on, createStmt: sql);
} }
MoorTrigger _readTrigger(Map<String, dynamic> content) { DriftTrigger _readTrigger(Map<String, dynamic> content) {
final on = _existingEntity<DriftTable>(content['on']); final on = _existingEntity<DriftTable>(content['on']);
final name = content['name'] as String; final name = content['name'] as String;
final sql = content['sql'] as String; final sql = content['sql'] as String;
final trigger = MoorTrigger(name, CustomTriggerDeclaration(sql), on); return DriftTrigger(
for (final bodyRef in content['refences_in_body'] as List) { _id(name),
trigger.bodyReferences.add(_existingEntity(bodyRef)); _declaration,
} on: on,
return trigger; onWrite: UpdateKind.delete,
references: [
for (final bodyRef in content['references_in_body'] as List)
_existingEntity(bodyRef)
],
createStmt: sql,
writes: const [],
);
} }
DriftTable _readTable(Map<String, dynamic> content) { DriftTable _readTable(Map<String, dynamic> content) {
final sqlName = content['name'] as String; final sqlName = content['name'] as String;
final isVirtual = content['is_virtual'] as bool; final isVirtual = content['is_virtual'] as bool;
final withoutRowId = content['without_rowid'] as bool?; final withoutRowId = content['without_rowid'] as bool? ?? false;
final pascalCase = ReCase(sqlName).pascalCase; final pascalCase = ReCase(sqlName).pascalCase;
final columns = [ final columns = [
for (final rawColumn in content['columns'] as List) for (final rawColumn in content['columns'] as List)
@ -288,13 +309,15 @@ class SchemaReader {
_engine.parse(create).rootNode as CreateVirtualTableStatement; _engine.parse(create).rootNode as CreateVirtualTableStatement;
return DriftTable( return DriftTable(
sqlName: sqlName, _id(sqlName),
dartTypeName: '${pascalCase}Data', _declaration,
overriddenName: pascalCase,
declaration: CustomVirtualTableDeclaration(parsed),
overrideWithoutRowId: withoutRowId,
overrideDontWriteConstraints: true,
columns: columns, columns: columns,
baseDartName: pascalCase,
nameOfRowClass: '${pascalCase}Data',
writeDefaultConstraints: true,
withoutRowId: withoutRowId,
virtualTableData:
VirtualTableData(parsed.moduleName, parsed.argumentContent, null),
); );
} }
@ -307,7 +330,7 @@ class SchemaReader {
if (content.containsKey('explicit_pk')) { if (content.containsKey('explicit_pk')) {
explicitPk = { explicitPk = {
for (final columnName in content['explicit_pk'] as List<dynamic>) for (final columnName in content['explicit_pk'] as List<dynamic>)
columns.singleWhere((c) => c.name.name == columnName) columns.singleWhere((c) => c.nameInSql == columnName)
}; };
} }
@ -316,35 +339,44 @@ class SchemaReader {
for (final key in content['unique_keys']) { for (final key in content['unique_keys']) {
uniqueKeys.add({ uniqueKeys.add({
for (final columnName in key) for (final columnName in key)
columns.singleWhere((c) => c.name.name == columnName) columns.singleWhere((c) => c.nameInSql == columnName)
}); });
} }
} }
return DriftTable( return DriftTable(
sqlName: sqlName, _id(sqlName),
overriddenName: pascalCase, _declaration,
columns: columns, columns: columns,
dartTypeName: '${pascalCase}Data', baseDartName: pascalCase,
primaryKey: explicitPk, nameOfRowClass: '${pascalCase}Data',
uniqueKeys: uniqueKeys, writeDefaultConstraints: content['was_declared_in_moor'] != true,
withoutRowId: withoutRowId,
overrideTableConstraints: tableConstraints, overrideTableConstraints: tableConstraints,
overrideDontWriteConstraints: content['was_declared_in_moor'] as bool?, tableConstraints: [
overrideWithoutRowId: withoutRowId, if (explicitPk != null) PrimaryKeyColumns(explicitPk),
declaration: const CustomTableDeclaration(), for (final unique in uniqueKeys) UniqueColumns(unique)
],
); );
} }
MoorView _readView(Map<String, dynamic> content) { DriftView _readView(Map<String, dynamic> content) {
return MoorView( final name = content['name'] as String;
declaration: null,
name: content['name'] as String, return DriftView(
dartTypeName: content['dart_data_name'] as String, _id(name),
entityInfoName: content['dart_info_name'] as String, _declaration,
)..columns = [ columns: [
for (final column in content['columns']) for (final column in content['columns'])
_readColumn(column as Map<String, dynamic>) _readColumn(column as Map<String, dynamic>)
]; ],
source: SqlViewSource(content['sql'] as String),
customParentClass: null,
entityInfoName: content['dart_info_name'] as String,
existingRowClass: null,
nameOfRowClass: content['dart_data_name'] as String,
references: const [],
);
} }
DriftColumn _readColumn(Map<String, dynamic> data) { DriftColumn _readColumn(Map<String, dynamic> data) {
@ -354,30 +386,30 @@ class SchemaReader {
final nullable = data['nullable'] as bool; final nullable = data['nullable'] as bool;
final customConstraints = data['customConstraints'] as String?; final customConstraints = data['customConstraints'] as String?;
final defaultConstraints = data['defaultConstraints'] as String?; final defaultConstraints = data['defaultConstraints'] as String?;
final dslFeatures = [ final dslFeatures = <DriftColumnConstraint?>[
for (final feature in data['dsl_features'] as List<dynamic>) for (final feature in data['dsl_features'] as List<dynamic>)
_columnFeature(feature), _columnFeature(feature),
if (defaultConstraints != null) if (defaultConstraints != null)
DefaultConstraintsFromSchemaFile(defaultConstraints), DefaultConstraintsFromSchemaFile(defaultConstraints),
]; ].whereType<DriftColumnConstraint>().toList();
final getterName = data['getter_name'] as String?; final getterName = data['getter_name'] as String?;
// Note: Not including client default code because that usually depends on // Note: Not including client default code because that usually depends on
// imports from the database. // imports from the database.
return DriftColumn( return DriftColumn(
name: ColumnName.explicitly(name), sqlType: columnType,
dartGetterName: getterName ?? ReCase(name).camelCase,
type: columnType,
nullable: nullable, nullable: nullable,
defaultArgument: data['default_dart'] as String?, nameInSql: name,
nameInDart: getterName ?? ReCase(name).camelCase,
declaration: _declaration,
customConstraints: customConstraints, customConstraints: customConstraints,
features: dslFeatures.whereType<ColumnFeature>().toList(), constraints: dslFeatures,
); );
} }
ColumnFeature? _columnFeature(dynamic data) { DriftColumnConstraint? _columnFeature(dynamic data) {
if (data == 'auto-increment') return AutoIncrement(); if (data == 'auto-increment') return PrimaryKeyColumn(true);
if (data == 'primary-key') return const PrimaryKey(); if (data == 'primary-key') return PrimaryKeyColumn(false);
if (data is Map<String, dynamic>) { if (data is Map<String, dynamic>) {
final allowedLengths = data['allowed-lengths'] as Map<String, dynamic>; final allowedLengths = data['allowed-lengths'] as Map<String, dynamic>;