Finally generate table classes from sql

This commit is contained in:
Simon Binder 2019-07-29 14:22:39 +02:00
parent 4798d0a7e5
commit e0a82b0e32
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
7 changed files with 391 additions and 5 deletions

View File

@ -42,15 +42,22 @@ class UseMoor {
/// Moor will generate two methods for you: `userById(int id)` and
/// `watchUserById(int id)`.
/// {@endtemplate}
@experimental
final Map<String, String> queries;
/// Defines the `.moor` files to include when building the table structure for
/// this database.
///
/// Please note that this feature is experimental at the moment.
@experimental
final Set<String> include;
/// Use this class as an annotation to inform moor_generator that a database
/// class should be generated using the specified [UseMoor.tables].
const UseMoor({
@required this.tables,
this.daos = const [],
@experimental this.queries = const {},
this.queries = const {},
@experimental this.include = const {},
});
}

View File

@ -0,0 +1,10 @@
CREATE TABLE state (
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
CREATE TABLE experiments (
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL,
state INT REFERENCES state(id) ON UPDATE CASCADE ON DELETE SET NULL
)

View File

@ -88,6 +88,7 @@ class CustomConverter extends TypeConverter<MyCustomObject, String> {
PureDefaults,
],
daos: [SomeDao],
include: {'test.moor'},
queries: {
'allTodosWithCategory': 'SELECT t.*, c.id as catId, c."desc" as catDesc '
'FROM todos t INNER JOIN categories c ON c.id = t.category',

View File

@ -1187,6 +1187,331 @@ class $PureDefaultsTable extends PureDefaults
}
}
class StateData extends DataClass implements Insertable<StateData> {
final int id;
final String name;
StateData({@required this.id, @required this.name});
factory StateData.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return StateData(
id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']),
name: stringType.mapFromDatabaseResponse(data['${effectivePrefix}name']),
);
}
factory StateData.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return StateData(
id: serializer.fromJson<int>(json['id']),
name: serializer.fromJson<String>(json['name']),
);
}
@override
Map<String, dynamic> toJson(
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return {
'id': serializer.toJson<int>(id),
'name': serializer.toJson<String>(name),
};
}
@override
T createCompanion<T extends UpdateCompanion<StateData>>(bool nullToAbsent) {
return StateCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
name: name == null && nullToAbsent ? const Value.absent() : Value(name),
) as T;
}
StateData copyWith({int id, String name}) => StateData(
id: id ?? this.id,
name: name ?? this.name,
);
@override
String toString() {
return (StringBuffer('StateData(')
..write('id: $id, ')
..write('name: $name')
..write(')'))
.toString();
}
@override
int get hashCode => $mrjf($mrjc(id.hashCode, name.hashCode));
@override
bool operator ==(other) =>
identical(this, other) ||
(other is StateData && other.id == id && other.name == name);
}
class StateCompanion extends UpdateCompanion<StateData> {
final Value<int> id;
final Value<String> name;
const StateCompanion({
this.id = const Value.absent(),
this.name = const Value.absent(),
});
}
class $StateTable extends Table with TableInfo<$StateTable, StateData> {
final GeneratedDatabase _db;
final String _alias;
$StateTable(this._db, [this._alias]);
final VerificationMeta _idMeta = const VerificationMeta('id');
GeneratedIntColumn _id;
@override
GeneratedIntColumn get id => _id ??= _constructId();
GeneratedIntColumn _constructId() {
return GeneratedIntColumn('id', $tableName, false,
hasAutoIncrement: true,
$customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT');
}
final VerificationMeta _nameMeta = const VerificationMeta('name');
GeneratedTextColumn _name;
@override
GeneratedTextColumn get name => _name ??= _constructName();
GeneratedTextColumn _constructName() {
return GeneratedTextColumn('name', $tableName, false,
$customConstraints: 'NOT NULL');
}
@override
List<GeneratedColumn> get $columns => [id, name];
@override
$StateTable get asDslTable => this;
@override
String get $tableName => _alias ?? 'state';
@override
final String actualTableName = 'state';
@override
VerificationContext validateIntegrity(StateCompanion d,
{bool isInserting = false}) {
final context = VerificationContext();
if (d.id.present) {
context.handle(_idMeta, id.isAcceptableValue(d.id.value, _idMeta));
} else if (id.isRequired && isInserting) {
context.missing(_idMeta);
}
if (d.name.present) {
context.handle(
_nameMeta, name.isAcceptableValue(d.name.value, _nameMeta));
} else if (name.isRequired && isInserting) {
context.missing(_nameMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
StateData map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
return StateData.fromData(data, _db, prefix: effectivePrefix);
}
@override
Map<String, Variable> entityToSql(StateCompanion d) {
final map = <String, Variable>{};
if (d.id.present) {
map['id'] = Variable<int, IntType>(d.id.value);
}
if (d.name.present) {
map['name'] = Variable<String, StringType>(d.name.value);
}
return map;
}
@override
$StateTable createAlias(String alias) {
return $StateTable(_db, alias);
}
}
class Experiment extends DataClass implements Insertable<Experiment> {
final int id;
final String description;
final int state;
Experiment({@required this.id, @required this.description, this.state});
factory Experiment.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return Experiment(
id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']),
description: stringType
.mapFromDatabaseResponse(data['${effectivePrefix}description']),
state: intType.mapFromDatabaseResponse(data['${effectivePrefix}state']),
);
}
factory Experiment.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return Experiment(
id: serializer.fromJson<int>(json['id']),
description: serializer.fromJson<String>(json['description']),
state: serializer.fromJson<int>(json['state']),
);
}
@override
Map<String, dynamic> toJson(
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return {
'id': serializer.toJson<int>(id),
'description': serializer.toJson<String>(description),
'state': serializer.toJson<int>(state),
};
}
@override
T createCompanion<T extends UpdateCompanion<Experiment>>(bool nullToAbsent) {
return ExperimentsCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
description: description == null && nullToAbsent
? const Value.absent()
: Value(description),
state:
state == null && nullToAbsent ? const Value.absent() : Value(state),
) as T;
}
Experiment copyWith({int id, String description, int state}) => Experiment(
id: id ?? this.id,
description: description ?? this.description,
state: state ?? this.state,
);
@override
String toString() {
return (StringBuffer('Experiment(')
..write('id: $id, ')
..write('description: $description, ')
..write('state: $state')
..write(')'))
.toString();
}
@override
int get hashCode =>
$mrjf($mrjc(id.hashCode, $mrjc(description.hashCode, state.hashCode)));
@override
bool operator ==(other) =>
identical(this, other) ||
(other is Experiment &&
other.id == id &&
other.description == description &&
other.state == state);
}
class ExperimentsCompanion extends UpdateCompanion<Experiment> {
final Value<int> id;
final Value<String> description;
final Value<int> state;
const ExperimentsCompanion({
this.id = const Value.absent(),
this.description = const Value.absent(),
this.state = const Value.absent(),
});
}
class $ExperimentsTable extends Table
with TableInfo<$ExperimentsTable, Experiment> {
final GeneratedDatabase _db;
final String _alias;
$ExperimentsTable(this._db, [this._alias]);
final VerificationMeta _idMeta = const VerificationMeta('id');
GeneratedIntColumn _id;
@override
GeneratedIntColumn get id => _id ??= _constructId();
GeneratedIntColumn _constructId() {
return GeneratedIntColumn('id', $tableName, false,
hasAutoIncrement: true,
$customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT');
}
final VerificationMeta _descriptionMeta =
const VerificationMeta('description');
GeneratedTextColumn _description;
@override
GeneratedTextColumn get description =>
_description ??= _constructDescription();
GeneratedTextColumn _constructDescription() {
return GeneratedTextColumn('description', $tableName, false,
$customConstraints: 'NOT NULL');
}
final VerificationMeta _stateMeta = const VerificationMeta('state');
GeneratedIntColumn _state;
@override
GeneratedIntColumn get state => _state ??= _constructState();
GeneratedIntColumn _constructState() {
return GeneratedIntColumn('state', $tableName, true,
$customConstraints:
'REFERENCES state(id) ON UPDATE CASCADE ON DELETE SET NULL');
}
@override
List<GeneratedColumn> get $columns => [id, description, state];
@override
$ExperimentsTable get asDslTable => this;
@override
String get $tableName => _alias ?? 'experiments';
@override
final String actualTableName = 'experiments';
@override
VerificationContext validateIntegrity(ExperimentsCompanion d,
{bool isInserting = false}) {
final context = VerificationContext();
if (d.id.present) {
context.handle(_idMeta, id.isAcceptableValue(d.id.value, _idMeta));
} else if (id.isRequired && isInserting) {
context.missing(_idMeta);
}
if (d.description.present) {
context.handle(_descriptionMeta,
description.isAcceptableValue(d.description.value, _descriptionMeta));
} else if (description.isRequired && isInserting) {
context.missing(_descriptionMeta);
}
if (d.state.present) {
context.handle(
_stateMeta, state.isAcceptableValue(d.state.value, _stateMeta));
} else if (state.isRequired && isInserting) {
context.missing(_stateMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
Experiment map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
return Experiment.fromData(data, _db, prefix: effectivePrefix);
}
@override
Map<String, Variable> entityToSql(ExperimentsCompanion d) {
final map = <String, Variable>{};
if (d.id.present) {
map['id'] = Variable<int, IntType>(d.id.value);
}
if (d.description.present) {
map['description'] = Variable<String, StringType>(d.description.value);
}
if (d.state.present) {
map['state'] = Variable<int, IntType>(d.state.value);
}
return map;
}
@override
$ExperimentsTable createAlias(String alias) {
return $ExperimentsTable(_db, alias);
}
}
class AllTodosWithCategoryResult {
final int id;
final String title;
@ -1229,6 +1554,10 @@ abstract class _$TodoDb extends GeneratedDatabase {
$PureDefaultsTable _pureDefaults;
$PureDefaultsTable get pureDefaults =>
_pureDefaults ??= $PureDefaultsTable(this);
$StateTable _state;
$StateTable get state => _state ??= $StateTable(this);
$ExperimentsTable _experiments;
$ExperimentsTable get experiments => _experiments ??= $ExperimentsTable(this);
SomeDao _someDao;
SomeDao get someDao => _someDao ??= SomeDao(this as TodoDb);
AllTodosWithCategoryResult _rowToAllTodosWithCategoryResult(QueryRow row) {
@ -1367,7 +1696,9 @@ abstract class _$TodoDb extends GeneratedDatabase {
users,
sharedTodos,
tableWithoutPK,
pureDefaults
pureDefaults,
state,
experiments
];
}

View File

@ -17,8 +17,16 @@ class UseMoorParser {
final tableTypes =
annotation.peek('tables').listValue.map((obj) => obj.toTypeValue());
final queryStrings = annotation.peek('queries')?.mapValue ?? {};
final includes = annotation
.read('include')
.objectValue
.toSetValue()
?.map((e) => e.toStringValue()) ??
{};
final parsedTables = await session.parseTables(tableTypes, element);
parsedTables.addAll(await session.resolveIncludes(includes));
final parsedQueries =
await session.parseQueries(queryStrings, parsedTables);
final daoTypes = _readDaoTypes(annotation);

View File

@ -10,7 +10,9 @@ import 'package:moor_generator/src/model/specified_database.dart';
import 'package:moor_generator/src/model/specified_table.dart';
import 'package:moor_generator/src/model/sql_query.dart';
import 'package:moor_generator/src/parser/column_parser.dart';
import 'package:moor_generator/src/parser/moor/moor_analyzer.dart';
import 'package:moor_generator/src/parser/sql/sql_parser.dart';
import 'package:moor_generator/src/parser/sql/type_mapping.dart';
import 'package:moor_generator/src/parser/table_parser.dart';
import 'package:moor_generator/src/parser/use_dao_parser.dart';
import 'package:moor_generator/src/parser/use_moor_parser.dart';
@ -78,7 +80,34 @@ class GeneratorSession {
} else {
return _tableParser.parse(type.element as ClassElement);
}
}));
})).then((list) => List.from(list)); // make growable
}
Future<List<SpecifiedTable>> resolveIncludes(Iterable<String> paths) async {
final mapper = TypeMapper();
final foundTables = <SpecifiedTable>[];
for (var path in paths) {
final asset = AssetId.resolve(path, from: step.inputId);
String content;
try {
content = await step.readAsString(asset);
} catch (e) {
errors.add(MoorError(
critical: true,
message: 'The included file $path could not be found'));
}
final parsed = await MoorAnalyzer(content).analyze();
foundTables.addAll(
parsed.parsedFile.declaredTables.map((t) => t.extractTable(mapper)));
for (var parseError in parsed.errors) {
errors.add(MoorError(message: "Can't parse sql in $path: $parseError"));
}
}
return foundTables;
}
/// Parses a column from a getter [e] declared inside a table class and its

View File

@ -24,7 +24,7 @@ class TableWriter {
void writeTableInfoClass(StringBuffer buffer) {
final dataClass = table.dartTypeName;
final tableDslName = table.fromClass?.name ?? 'dynamic';
final tableDslName = table.fromClass?.name ?? 'Table';
// class UsersTable extends Users implements TableInfo<Users, User> {
buffer