Custom table constraints

This commit is contained in:
Simon Binder 2019-04-19 20:29:30 +02:00
parent f15e06fbb0
commit becb78afbc
No known key found for this signature in database
GPG Key ID: B807FDF954BA00CF
12 changed files with 57 additions and 39 deletions

View File

@ -25,8 +25,8 @@ abstract class Table {
/// @override
/// Set<Column> get primaryKey => {recipe, ingredient};
///
/// IntColumn get recipe => integer().autoIncrement()();
/// IntColumn get ingredient => integer().autoIncrement()();
/// IntColumn get recipe => integer()();
/// IntColumn get ingredient => integer()();
///
/// IntColumn get amountInGrams => integer().named('amount')();
///}
@ -39,6 +39,14 @@ abstract class Table {
@visibleForOverriding
Set<Column> get primaryKey => null;
/// Custom table constraints that should be added to the table.
///
/// See also:
/// - https://www.sqlite.org/syntax/table-constraint.html, which defines what
/// table constraints are supported.
@visibleForOverriding
List<String> get customConstraints => [];
/// Use this as the body of a getter to declare a column that holds integers.
/// Example (inside the body of a table class):
/// ```

View File

@ -10,7 +10,7 @@ const Map<JoinType, String> _joinKeywords = {
JoinType.cross: 'CROSS',
};
class Join<T, D> extends Component {
class Join<T extends Table, D> extends Component {
final JoinType type;
final TableInfo<T, D> table;
final Expression<bool, BoolType> on;
@ -35,7 +35,8 @@ class Join<T, D> extends Component {
///
/// See also:
/// - http://www.sqlitetutorial.net/sqlite-inner-join/
Join innerJoin<T, D>(TableInfo<T, D> other, Expression<bool, BoolType> on) {
Join innerJoin<T extends Table, D>(
TableInfo<T, D> other, Expression<bool, BoolType> on) {
return Join(JoinType.inner, other, on);
}
@ -44,7 +45,8 @@ Join innerJoin<T, D>(TableInfo<T, D> other, Expression<bool, BoolType> on) {
///
/// See also:
/// - http://www.sqlitetutorial.net/sqlite-left-join/
Join leftOuterJoin<T, D>(TableInfo<T, D> other, Expression<bool, BoolType> on) {
Join leftOuterJoin<T extends Table, D>(
TableInfo<T, D> other, Expression<bool, BoolType> on) {
return Join(JoinType.leftOuter, other, on);
}

View File

@ -75,7 +75,7 @@ abstract class DatabaseConnectionUser {
/// innerJoin(destination, routes.startPoint.equalsExp(destination.id)),
/// ]);
/// ```
T alias<T, D>(TableInfo<T, D> table, String alias) {
T alias<T extends Table, D>(TableInfo<T, D> table, String alias) {
return table.createAlias(alias).asDslTable;
}
}
@ -87,7 +87,7 @@ mixin QueryEngine on DatabaseConnectionUser {
/// to write data into the [table] by using [InsertStatement.insert].
@protected
@visibleForTesting
InsertStatement<T> into<T>(TableInfo<dynamic, T> table) =>
InsertStatement<T> into<T>(TableInfo<Table, T> table) =>
InsertStatement<T>(this, table);
/// Starts an [UpdateStatement] for the given table. You can use that
@ -95,7 +95,7 @@ mixin QueryEngine on DatabaseConnectionUser {
/// clause on that table and then use [UpdateStatement.write].
@protected
@visibleForTesting
UpdateStatement<Tbl, ReturnType> update<Tbl, ReturnType>(
UpdateStatement<Tbl, ReturnType> update<Tbl extends Table, ReturnType>(
TableInfo<Tbl, ReturnType> table) =>
UpdateStatement(this, table);
@ -104,17 +104,17 @@ mixin QueryEngine on DatabaseConnectionUser {
/// stream of data
@protected
@visibleForTesting
SimpleSelectStatement<Table, ReturnType> select<Table, ReturnType>(
TableInfo<Table, ReturnType> table) {
return SimpleSelectStatement<Table, ReturnType>(this, table);
SimpleSelectStatement<T, ReturnType> select<T extends Table, ReturnType>(
TableInfo<T, ReturnType> table) {
return SimpleSelectStatement<T, ReturnType>(this, table);
}
/// Starts a [DeleteStatement] that can be used to delete rows from a table.
@protected
@visibleForTesting
DeleteStatement<Table, Entity> delete<Table, Entity>(
TableInfo<Table, Entity> table) {
return DeleteStatement<Table, Entity>(this, table);
DeleteStatement<T, Entity> delete<T extends Table, Entity>(
TableInfo<T, Entity> table) {
return DeleteStatement<T, Entity>(this, table);
}
/// Executes a custom delete or update statement and returns the amount of

View File

@ -53,19 +53,17 @@ class Migrator {
/// Creates the given table if it doesn't exist
Future<void> createTable(TableInfo table) async {
final sql = StringBuffer();
// ignore: cascade_invocations
sql.write('CREATE TABLE IF NOT EXISTS ${table.$tableName} (');
final sql = StringBuffer()
..write('CREATE TABLE IF NOT EXISTS ${table.$tableName} (');
var hasAutoIncrement = false;
for (var i = 0; i < table.$columns.length; i++) {
final column = table.$columns[i];
if (column is GeneratedIntColumn && column.hasAutoIncrement)
if (column is GeneratedIntColumn && column.hasAutoIncrement) {
hasAutoIncrement = true;
}
// ignore: cascade_invocations
column.writeColumnDefinition(sql);
if (i < table.$columns.length - 1) sql.write(', ');
@ -84,6 +82,16 @@ class Migrator {
sql.write(')');
}
final constraints = table.asDslTable.customConstraints ?? [];
for (var i = 0; i < constraints.length; i++) {
if (i != 0) {
sql.write(', ');
}
sql.write(constraints[i]);
}
sql.write(');');
return issueCustomQuery(sql.toString());

View File

@ -5,7 +5,7 @@ import 'package:moor/src/runtime/components/component.dart';
import 'package:moor/src/runtime/statements/query.dart';
import 'package:moor/src/runtime/structure/table_info.dart';
class DeleteStatement<T, D> extends Query<T, D>
class DeleteStatement<T extends Table, D> extends Query<T, D>
with SingleTableQueryMixin<T, D> {
/// This constructor should be called by [GeneratedDatabase.delete] for you.
DeleteStatement(QueryEngine database, TableInfo<T, D> table)

View File

@ -9,7 +9,7 @@ class InsertStatement<DataClass> {
@protected
final QueryEngine database;
@protected
final TableInfo<dynamic, DataClass> table;
final TableInfo<Table, DataClass> table;
bool _orReplace = false;

View File

@ -1,4 +1,5 @@
import 'package:meta/meta.dart';
import 'package:moor/src/dsl/table.dart';
import 'package:moor/src/runtime/components/component.dart';
import 'package:moor/src/runtime/components/limit.dart';
import 'package:moor/src/runtime/components/order_by.dart';
@ -12,10 +13,10 @@ import 'package:moor/src/runtime/structure/table_info.dart';
/// Statement that operates with data that already exists (select, delete,
/// update).
abstract class Query<Table, DataClass> {
abstract class Query<T extends Table, DataClass> {
@protected
QueryEngine database;
TableInfo<Table, DataClass> table;
TableInfo<T, DataClass> table;
Query(this.database, this.table);
@ -67,8 +68,8 @@ abstract class Query<Table, DataClass> {
}
}
mixin SingleTableQueryMixin<Table, DataClass> on Query<Table, DataClass> {
void where(Expression<bool, BoolType> filter(Table tbl)) {
mixin SingleTableQueryMixin<T extends Table, DataClass> on Query<T, DataClass> {
void where(Expression<bool, BoolType> filter(T tbl)) {
final predicate = filter(table.asDslTable);
if (whereExpr == null) {
@ -119,7 +120,7 @@ mixin SingleTableQueryMixin<Table, DataClass> on Query<Table, DataClass> {
}
}
mixin LimitContainerMixin<T, D> on Query<T, D> {
mixin LimitContainerMixin<T extends Table, D> on Query<T, D> {
/// Limits the amount of rows returned by capping them at [limit]. If [offset]
/// is provided as well, the first [offset] rows will be skipped and not
/// included in the result.

View File

@ -13,8 +13,8 @@ import 'package:moor/src/runtime/structure/table_info.dart';
typedef OrderingTerm OrderClauseGenerator<T>(T tbl);
class JoinedSelectStatement<FirstT, FirstD> extends Query<FirstT, FirstD>
with LimitContainerMixin {
class JoinedSelectStatement<FirstT extends Table, FirstD>
extends Query<FirstT, FirstD> with LimitContainerMixin {
JoinedSelectStatement(
QueryEngine database, TableInfo<FirstT, FirstD> table, this._joins)
: super(database, table);
@ -142,7 +142,7 @@ class JoinedSelectStatement<FirstT, FirstD> extends Query<FirstT, FirstD>
}
/// A select statement that doesn't use joins
class SimpleSelectStatement<T, D> extends Query<T, D>
class SimpleSelectStatement<T extends Table, D> extends Query<T, D>
with SingleTableQueryMixin<T, D>, LimitContainerMixin<T, D> {
SimpleSelectStatement(QueryEngine database, TableInfo<T, D> table)
: super(database, table);
@ -288,7 +288,7 @@ class TypedResult {
final QueryRow rawData;
/// Reads all data that belongs to the given [table] from this row.
D readTable<T, D>(TableInfo<T, D> table) {
D readTable<T extends Table, D>(TableInfo<T, D> table) {
return _parsedData[table] as D;
}
}

View File

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:moor/moor.dart';
import 'package:moor/src/runtime/components/component.dart';
class UpdateStatement<T, D> extends Query<T, D>
class UpdateStatement<T extends Table, D> extends Query<T, D>
with SingleTableQueryMixin<T, D> {
UpdateStatement(QueryEngine database, TableInfo<T, D> table)
: super(database, table);

View File

@ -4,7 +4,7 @@ import 'package:moor/src/runtime/expressions/variables.dart';
/// Base class for generated classes. [TableDsl] is the type specified by the
/// user that extends [Table], [DataClass] is the type of the data class
/// generated from the table.
mixin TableInfo<TableDsl, DataClass> {
mixin TableInfo<TableDsl extends Table, DataClass> {
/// Type system sugar. Implementations are likely to inherit from both
/// [TableInfo] and [TableDsl] and can thus just return their instance.
TableDsl get asDslTable;

View File

@ -11,7 +11,9 @@ class Todos extends Table {
DateTimeColumn get targetDate => dateTime().nullable()();
IntColumn get category => integer().nullable()();
IntColumn get category => integer()
.nullable()
.customConstraint('NULLABLE REFERENCES categories(id)')();
}
@DataClassName('Category')

View File

@ -126,11 +126,8 @@ class $TodosTable extends Todos with TableInfo<$TodosTable, TodoEntry> {
GeneratedIntColumn _constructCategory() {
var cName = 'category';
if (_alias != null) cName = '$_alias.$cName';
return GeneratedIntColumn(
'category',
$tableName,
true,
);
return GeneratedIntColumn('category', $tableName, true,
$customConstraints: 'NULLABLE REFERENCES categories(id)');
}
@override