mirror of https://github.com/AMT-Cheif/drift.git
Custom table constraints
This commit is contained in:
parent
f15e06fbb0
commit
becb78afbc
|
@ -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):
|
||||
/// ```
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue