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
|
/// @override
|
||||||
/// Set<Column> get primaryKey => {recipe, ingredient};
|
/// Set<Column> get primaryKey => {recipe, ingredient};
|
||||||
///
|
///
|
||||||
/// IntColumn get recipe => integer().autoIncrement()();
|
/// IntColumn get recipe => integer()();
|
||||||
/// IntColumn get ingredient => integer().autoIncrement()();
|
/// IntColumn get ingredient => integer()();
|
||||||
///
|
///
|
||||||
/// IntColumn get amountInGrams => integer().named('amount')();
|
/// IntColumn get amountInGrams => integer().named('amount')();
|
||||||
///}
|
///}
|
||||||
|
@ -39,6 +39,14 @@ abstract class Table {
|
||||||
@visibleForOverriding
|
@visibleForOverriding
|
||||||
Set<Column> get primaryKey => null;
|
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.
|
/// Use this as the body of a getter to declare a column that holds integers.
|
||||||
/// Example (inside the body of a table class):
|
/// Example (inside the body of a table class):
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -10,7 +10,7 @@ const Map<JoinType, String> _joinKeywords = {
|
||||||
JoinType.cross: 'CROSS',
|
JoinType.cross: 'CROSS',
|
||||||
};
|
};
|
||||||
|
|
||||||
class Join<T, D> extends Component {
|
class Join<T extends Table, D> extends Component {
|
||||||
final JoinType type;
|
final JoinType type;
|
||||||
final TableInfo<T, D> table;
|
final TableInfo<T, D> table;
|
||||||
final Expression<bool, BoolType> on;
|
final Expression<bool, BoolType> on;
|
||||||
|
@ -35,7 +35,8 @@ class Join<T, D> extends Component {
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
/// - http://www.sqlitetutorial.net/sqlite-inner-join/
|
/// - 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);
|
return Join(JoinType.inner, other, on);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +45,8 @@ Join innerJoin<T, D>(TableInfo<T, D> other, Expression<bool, BoolType> on) {
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
/// - http://www.sqlitetutorial.net/sqlite-left-join/
|
/// - 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);
|
return Join(JoinType.leftOuter, other, on);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ abstract class DatabaseConnectionUser {
|
||||||
/// innerJoin(destination, routes.startPoint.equalsExp(destination.id)),
|
/// 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;
|
return table.createAlias(alias).asDslTable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ mixin QueryEngine on DatabaseConnectionUser {
|
||||||
/// to write data into the [table] by using [InsertStatement.insert].
|
/// to write data into the [table] by using [InsertStatement.insert].
|
||||||
@protected
|
@protected
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
InsertStatement<T> into<T>(TableInfo<dynamic, T> table) =>
|
InsertStatement<T> into<T>(TableInfo<Table, T> table) =>
|
||||||
InsertStatement<T>(this, table);
|
InsertStatement<T>(this, table);
|
||||||
|
|
||||||
/// Starts an [UpdateStatement] for the given table. You can use that
|
/// 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].
|
/// clause on that table and then use [UpdateStatement.write].
|
||||||
@protected
|
@protected
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
UpdateStatement<Tbl, ReturnType> update<Tbl, ReturnType>(
|
UpdateStatement<Tbl, ReturnType> update<Tbl extends Table, ReturnType>(
|
||||||
TableInfo<Tbl, ReturnType> table) =>
|
TableInfo<Tbl, ReturnType> table) =>
|
||||||
UpdateStatement(this, table);
|
UpdateStatement(this, table);
|
||||||
|
|
||||||
|
@ -104,17 +104,17 @@ mixin QueryEngine on DatabaseConnectionUser {
|
||||||
/// stream of data
|
/// stream of data
|
||||||
@protected
|
@protected
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
SimpleSelectStatement<Table, ReturnType> select<Table, ReturnType>(
|
SimpleSelectStatement<T, ReturnType> select<T extends Table, ReturnType>(
|
||||||
TableInfo<Table, ReturnType> table) {
|
TableInfo<T, ReturnType> table) {
|
||||||
return SimpleSelectStatement<Table, ReturnType>(this, table);
|
return SimpleSelectStatement<T, ReturnType>(this, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts a [DeleteStatement] that can be used to delete rows from a table.
|
/// Starts a [DeleteStatement] that can be used to delete rows from a table.
|
||||||
@protected
|
@protected
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
DeleteStatement<Table, Entity> delete<Table, Entity>(
|
DeleteStatement<T, Entity> delete<T extends Table, Entity>(
|
||||||
TableInfo<Table, Entity> table) {
|
TableInfo<T, Entity> table) {
|
||||||
return DeleteStatement<Table, Entity>(this, table);
|
return DeleteStatement<T, Entity>(this, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a custom delete or update statement and returns the amount of
|
/// 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
|
/// Creates the given table if it doesn't exist
|
||||||
Future<void> createTable(TableInfo table) async {
|
Future<void> createTable(TableInfo table) async {
|
||||||
final sql = StringBuffer();
|
final sql = StringBuffer()
|
||||||
|
..write('CREATE TABLE IF NOT EXISTS ${table.$tableName} (');
|
||||||
// ignore: cascade_invocations
|
|
||||||
sql.write('CREATE TABLE IF NOT EXISTS ${table.$tableName} (');
|
|
||||||
|
|
||||||
var hasAutoIncrement = false;
|
var hasAutoIncrement = false;
|
||||||
for (var i = 0; i < table.$columns.length; i++) {
|
for (var i = 0; i < table.$columns.length; i++) {
|
||||||
final column = table.$columns[i];
|
final column = table.$columns[i];
|
||||||
|
|
||||||
if (column is GeneratedIntColumn && column.hasAutoIncrement)
|
if (column is GeneratedIntColumn && column.hasAutoIncrement) {
|
||||||
hasAutoIncrement = true;
|
hasAutoIncrement = true;
|
||||||
|
}
|
||||||
|
|
||||||
// ignore: cascade_invocations
|
|
||||||
column.writeColumnDefinition(sql);
|
column.writeColumnDefinition(sql);
|
||||||
|
|
||||||
if (i < table.$columns.length - 1) sql.write(', ');
|
if (i < table.$columns.length - 1) sql.write(', ');
|
||||||
|
@ -84,6 +82,16 @@ class Migrator {
|
||||||
sql.write(')');
|
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(');');
|
sql.write(');');
|
||||||
|
|
||||||
return issueCustomQuery(sql.toString());
|
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/statements/query.dart';
|
||||||
import 'package:moor/src/runtime/structure/table_info.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> {
|
with SingleTableQueryMixin<T, D> {
|
||||||
/// This constructor should be called by [GeneratedDatabase.delete] for you.
|
/// This constructor should be called by [GeneratedDatabase.delete] for you.
|
||||||
DeleteStatement(QueryEngine database, TableInfo<T, D> table)
|
DeleteStatement(QueryEngine database, TableInfo<T, D> table)
|
||||||
|
|
|
@ -9,7 +9,7 @@ class InsertStatement<DataClass> {
|
||||||
@protected
|
@protected
|
||||||
final QueryEngine database;
|
final QueryEngine database;
|
||||||
@protected
|
@protected
|
||||||
final TableInfo<dynamic, DataClass> table;
|
final TableInfo<Table, DataClass> table;
|
||||||
|
|
||||||
bool _orReplace = false;
|
bool _orReplace = false;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:meta/meta.dart';
|
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/component.dart';
|
||||||
import 'package:moor/src/runtime/components/limit.dart';
|
import 'package:moor/src/runtime/components/limit.dart';
|
||||||
import 'package:moor/src/runtime/components/order_by.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,
|
/// Statement that operates with data that already exists (select, delete,
|
||||||
/// update).
|
/// update).
|
||||||
abstract class Query<Table, DataClass> {
|
abstract class Query<T extends Table, DataClass> {
|
||||||
@protected
|
@protected
|
||||||
QueryEngine database;
|
QueryEngine database;
|
||||||
TableInfo<Table, DataClass> table;
|
TableInfo<T, DataClass> table;
|
||||||
|
|
||||||
Query(this.database, this.table);
|
Query(this.database, this.table);
|
||||||
|
|
||||||
|
@ -67,8 +68,8 @@ abstract class Query<Table, DataClass> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin SingleTableQueryMixin<Table, DataClass> on Query<Table, DataClass> {
|
mixin SingleTableQueryMixin<T extends Table, DataClass> on Query<T, DataClass> {
|
||||||
void where(Expression<bool, BoolType> filter(Table tbl)) {
|
void where(Expression<bool, BoolType> filter(T tbl)) {
|
||||||
final predicate = filter(table.asDslTable);
|
final predicate = filter(table.asDslTable);
|
||||||
|
|
||||||
if (whereExpr == null) {
|
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]
|
/// 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
|
/// is provided as well, the first [offset] rows will be skipped and not
|
||||||
/// included in the result.
|
/// included in the result.
|
||||||
|
|
|
@ -13,8 +13,8 @@ import 'package:moor/src/runtime/structure/table_info.dart';
|
||||||
|
|
||||||
typedef OrderingTerm OrderClauseGenerator<T>(T tbl);
|
typedef OrderingTerm OrderClauseGenerator<T>(T tbl);
|
||||||
|
|
||||||
class JoinedSelectStatement<FirstT, FirstD> extends Query<FirstT, FirstD>
|
class JoinedSelectStatement<FirstT extends Table, FirstD>
|
||||||
with LimitContainerMixin {
|
extends Query<FirstT, FirstD> with LimitContainerMixin {
|
||||||
JoinedSelectStatement(
|
JoinedSelectStatement(
|
||||||
QueryEngine database, TableInfo<FirstT, FirstD> table, this._joins)
|
QueryEngine database, TableInfo<FirstT, FirstD> table, this._joins)
|
||||||
: super(database, table);
|
: super(database, table);
|
||||||
|
@ -142,7 +142,7 @@ class JoinedSelectStatement<FirstT, FirstD> extends Query<FirstT, FirstD>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A select statement that doesn't use joins
|
/// 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> {
|
with SingleTableQueryMixin<T, D>, LimitContainerMixin<T, D> {
|
||||||
SimpleSelectStatement(QueryEngine database, TableInfo<T, D> table)
|
SimpleSelectStatement(QueryEngine database, TableInfo<T, D> table)
|
||||||
: super(database, table);
|
: super(database, table);
|
||||||
|
@ -288,7 +288,7 @@ class TypedResult {
|
||||||
final QueryRow rawData;
|
final QueryRow rawData;
|
||||||
|
|
||||||
/// Reads all data that belongs to the given [table] from this row.
|
/// 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;
|
return _parsedData[table] as D;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:async';
|
||||||
import 'package:moor/moor.dart';
|
import 'package:moor/moor.dart';
|
||||||
import 'package:moor/src/runtime/components/component.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> {
|
with SingleTableQueryMixin<T, D> {
|
||||||
UpdateStatement(QueryEngine database, TableInfo<T, D> table)
|
UpdateStatement(QueryEngine database, TableInfo<T, D> table)
|
||||||
: super(database, 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
|
/// Base class for generated classes. [TableDsl] is the type specified by the
|
||||||
/// user that extends [Table], [DataClass] is the type of the data class
|
/// user that extends [Table], [DataClass] is the type of the data class
|
||||||
/// generated from the table.
|
/// generated from the table.
|
||||||
mixin TableInfo<TableDsl, DataClass> {
|
mixin TableInfo<TableDsl extends Table, DataClass> {
|
||||||
/// Type system sugar. Implementations are likely to inherit from both
|
/// Type system sugar. Implementations are likely to inherit from both
|
||||||
/// [TableInfo] and [TableDsl] and can thus just return their instance.
|
/// [TableInfo] and [TableDsl] and can thus just return their instance.
|
||||||
TableDsl get asDslTable;
|
TableDsl get asDslTable;
|
||||||
|
|
|
@ -11,7 +11,9 @@ class Todos extends Table {
|
||||||
|
|
||||||
DateTimeColumn get targetDate => dateTime().nullable()();
|
DateTimeColumn get targetDate => dateTime().nullable()();
|
||||||
|
|
||||||
IntColumn get category => integer().nullable()();
|
IntColumn get category => integer()
|
||||||
|
.nullable()
|
||||||
|
.customConstraint('NULLABLE REFERENCES categories(id)')();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DataClassName('Category')
|
@DataClassName('Category')
|
||||||
|
|
|
@ -126,11 +126,8 @@ class $TodosTable extends Todos with TableInfo<$TodosTable, TodoEntry> {
|
||||||
GeneratedIntColumn _constructCategory() {
|
GeneratedIntColumn _constructCategory() {
|
||||||
var cName = 'category';
|
var cName = 'category';
|
||||||
if (_alias != null) cName = '$_alias.$cName';
|
if (_alias != null) cName = '$_alias.$cName';
|
||||||
return GeneratedIntColumn(
|
return GeneratedIntColumn('category', $tableName, true,
|
||||||
'category',
|
$customConstraints: 'NULLABLE REFERENCES categories(id)');
|
||||||
$tableName,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
Loading…
Reference in New Issue