Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate CREATE TABLE statements in models instead of writer #63

Merged
merged 2 commits into from
Feb 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Future<Person> findPersonByIdAndName(int id, String name);
@Query('SELECT * FROM Person')
Future<List<Person>> findAllPersons(); // select multiple items

@Query('DELETE FROM person')
@Query('DELETE FROM Person')
Future<void> deleteAllPersons(); // query without returning an entity
````

Expand Down
10 changes: 8 additions & 2 deletions floor/lib/src/annotations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,24 @@ class ForeignKey {
final Type entity;

/// [ForeignKeyAction]
/// Action to take when the parent [Entity] is updated from the database.
///
/// By default, [ForeignKeyAction.NO_ACTION] is used.
final int onUpdate;

/// [ForeignKeyAction]
/// Action to take when the parent [Entity] is deleted from the database.
///
/// By default, [ForeignKeyAction.NO_ACTION] is used.
final int onDelete;

/// Declares a foreign key on another [Entity].
const ForeignKey({
@required this.childColumns,
@required this.parentColumns,
@required this.entity,
this.onUpdate,
this.onDelete,
this.onUpdate = ForeignKeyAction.NO_ACTION,
this.onDelete = ForeignKeyAction.NO_ACTION,
});
}

Expand Down
49 changes: 33 additions & 16 deletions floor_generator/lib/model/column.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,26 @@ class Column {
field.displayName;
}

bool get isNullable {
bool get isPrimaryKey => field.metadata.any(isPrimaryKeyAnnotation);

/// The database column definition.
String get definition {
final columnSpecification = StringBuffer();

if (isPrimaryKey) {
return false;
columnSpecification.write(' PRIMARY KEY');
}
if (!_hasColumnInfoAnnotation) {
return true; // all Dart fields are nullable by default
if (_autoGenerate) {
columnSpecification.write(' AUTOINCREMENT');
}
if (!_isNullable) {
columnSpecification.write(' NOT NULL');
}
return field.metadata
.firstWhere(isColumnInfoAnnotation)
.computeConstantValue()
.getField(AnnotationField.COLUMN_INFO_NULLABLE)
.toBoolValue() ??
true;
}

bool get _hasColumnInfoAnnotation {
return field.metadata.any(isColumnInfoAnnotation);
return '`$name` $_type$columnSpecification';
}

String get type {
String get _type {
final type = field.type;
if (isInt(type)) {
return SqlType.INTEGER;
Expand All @@ -57,9 +57,11 @@ class Column {
);
}

bool get isPrimaryKey => field.metadata.any(isPrimaryKeyAnnotation);
bool get _hasColumnInfoAnnotation {
return field.metadata.any(isColumnInfoAnnotation);
}

bool get autoGenerate {
bool get _autoGenerate {
if (!isPrimaryKey) {
return false;
}
Expand All @@ -70,4 +72,19 @@ class Column {
.toBoolValue() ??
false;
}

bool get _isNullable {
if (isPrimaryKey) {
return false;
}
if (!_hasColumnInfoAnnotation) {
return true; // all Dart fields are nullable by default
}
return field.metadata
.firstWhere(isColumnInfoAnnotation)
.computeConstantValue()
.getField(AnnotationField.COLUMN_INFO_NULLABLE)
.toBoolValue() ??
true;
}
}
26 changes: 20 additions & 6 deletions floor_generator/lib/model/entity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,25 @@ class Entity {

List<ForeignKey> get foreignKeys {
return clazz.metadata
.firstWhere(isEntityAnnotation)
.computeConstantValue()
.getField(AnnotationField.ENTITY_FOREIGN_KEYS)
?.toListValue()
?.map((object) => ForeignKey(clazz, object))
?.toList();
.firstWhere(isEntityAnnotation)
.computeConstantValue()
.getField(AnnotationField.ENTITY_FOREIGN_KEYS)
?.toListValue()
?.map((object) => ForeignKey(clazz, object))
?.toList() ??
[];
}

String getCreateTableStatement(final LibraryReader library) {
final databaseDefinition =
columns.map((column) => column.definition).toList();

final foreignKeyDefinitions = foreignKeys
.map((foreignKey) => foreignKey.getDefinition(library))
.toList();

databaseDefinition.addAll(foreignKeyDefinitions);

return "'CREATE TABLE IF NOT EXISTS `$name` (${databaseDefinition.join(', ')})'";
}
}
71 changes: 52 additions & 19 deletions floor_generator/lib/model/foreign_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,18 @@ class ForeignKey {

ForeignKey(this.entityClass, this.object);

String getDefinition(final LibraryReader library) {
final onUpdateAction = _getAction(_onUpdate);
final onDeleteAction = _getAction(_onDelete);

return 'FOREIGN KEY (${_childColumns.join(', ')}) '
' REFERENCES `${_getParentName(library)}` (${_parentColumns.join(', ')})'
' ON UPDATE $onUpdateAction'
' ON DELETE $onDeleteAction';
}

/// Returns the parent column name referenced with this foreign key.
String getParentName(final LibraryReader library) {
String _getParentName(final LibraryReader library) {
final entityClassName =
object.getField(ForeignKeyField.ENTITY)?.toTypeValue()?.displayName ??
(throw InvalidGenerationSourceError(
Expand All @@ -34,31 +44,54 @@ class ForeignKey {
.name;
}

List<String> get childColumns {
return _getColumns(ForeignKeyField.CHILD_COLUMNS) ??
(throw InvalidGenerationSourceError(
'No child columns defined for foreign key',
element: entityClass,
));
List<String> get _childColumns {
final columns = _getColumns(ForeignKeyField.CHILD_COLUMNS);
if (columns.isEmpty) {
throw InvalidGenerationSourceError(
'No child columns defined for foreign key',
element: entityClass,
);
}
return columns;
}

List<String> get parentColumns {
return _getColumns(ForeignKeyField.PARENT_COLUMNS) ??
(throw InvalidGenerationSourceError(
'No parent columns defined for foreign key',
element: entityClass,
));
List<String> get _parentColumns {
final columns = _getColumns(ForeignKeyField.PARENT_COLUMNS);
if (columns.isEmpty) {
throw InvalidGenerationSourceError(
'No parent columns defined for foreign key',
element: entityClass,
);
}
return columns;
}

int get onUpdate => object.getField(ForeignKeyField.ON_UPDATE)?.toIntValue();
int get _onUpdate => object.getField(ForeignKeyField.ON_UPDATE)?.toIntValue();

int get onDelete => object.getField(ForeignKeyField.ON_DELETE)?.toIntValue();
int get _onDelete => object.getField(ForeignKeyField.ON_DELETE)?.toIntValue();

String _getAction(final int action) {
switch (action) {
case ForeignKeyAction.RESTRICT:
return 'RESTRICT';
case ForeignKeyAction.SET_NULL:
return 'SET_NULL';
case ForeignKeyAction.SET_DEFAULT:
return 'SET_DEFAULT';
case ForeignKeyAction.CASCADE:
return 'CASCADE';
case ForeignKeyAction.NO_ACTION:
default:
return 'NO_ACTION';
}
}

List<String> _getColumns(final String foreignKeyField) {
return object
.getField(foreignKeyField)
?.toListValue()
?.map((object) => object.toStringValue())
?.toList();
.getField(foreignKeyField)
?.toListValue()
?.map((object) => object.toStringValue())
?.toList() ??
[];
}
}
59 changes: 3 additions & 56 deletions floor_generator/lib/writer/database_writer.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:code_builder/code_builder.dart';
import 'package:floor_generator/misc/annotation_expression.dart';
import 'package:floor_generator/misc/constants.dart';
import 'package:floor_generator/misc/type_utils.dart';
import 'package:floor_generator/model/database.dart';
import 'package:floor_generator/model/delete_method.dart';
Expand Down Expand Up @@ -155,60 +154,8 @@ class DatabaseWriter implements Writer {
}

List<String> _generateCreateTableSqlStatements(final List<Entity> entities) {
return entities.map(_generateSql).toList();
}

String _generateSql(final Entity entity) {
final foreignKeys = _generateForeignKeys(entity) ?? '';

final columns = entity.columns.map((column) {
final primaryKey = column.isPrimaryKey ? ' PRIMARY KEY' : '';
final autoIncrement = column.autoGenerate ? ' AUTOINCREMENT' : '';
final nullable = column.isNullable ? '' : ' NOT NULL';

return '`${column.name}` ${column.type}$primaryKey$autoIncrement$nullable';
}).join(', ');

return "'CREATE TABLE IF NOT EXISTS `${entity.name}` ($columns$foreignKeys)'";
}

String _generateForeignKeys(final Entity entity) {
return entity.foreignKeys?.map((foreignKey) {
final childColumns = foreignKey.childColumns.join(', ');
final parentColumns = foreignKey.parentColumns.join(', ');
final parentName = foreignKey.getParentName(library);

final onUpdate = _getOnUpdateAction(foreignKey.onUpdate) ?? '';
final onDelete = _getOnDeleteAction(foreignKey.onDelete) ?? '';

return ', FOREIGN KEY ($childColumns) REFERENCES `$parentName` ($parentColumns)$onUpdate$onDelete';
})?.join();
}

String _getOnUpdateAction(final int action) {
final updateAction = _getAction(action);
return updateAction != null ? ' ON UPDATE $updateAction' : null;
}

String _getOnDeleteAction(final int action) {
final deleteAction = _getAction(action);
return deleteAction != null ? ' ON DELETE $deleteAction' : null;
}

String _getAction(final int action) {
switch (action) {
case ForeignKeyAction.NO_ACTION:
return 'NO_ACTION';
case ForeignKeyAction.RESTRICT:
return 'RESTRICT';
case ForeignKeyAction.SET_NULL:
return 'SET_NULL';
case ForeignKeyAction.SET_DEFAULT:
return 'SET_DEFAULT';
case ForeignKeyAction.CASCADE:
return 'CASCADE';
default:
return null;
}
return entities
.map((entity) => entity.getCreateTableStatement(library))
.toList();
}
}
Loading