mirror of https://github.com/AMT-Cheif/drift.git
Merge pull request #1774 from westito/unique-constraint
Unique constraint DSL for Dart tables
This commit is contained in:
commit
5981d409c5
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
// #docregion unique
|
||||||
|
class WithUniqueConstraints extends Table {
|
||||||
|
IntColumn get a => integer().unique()();
|
||||||
|
|
||||||
|
IntColumn get b => integer()();
|
||||||
|
IntColumn get c => integer()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Set<Column>> get uniqueKeys => [
|
||||||
|
{b, c}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Effectively, this table has two unique key sets: (a) and (b, c).
|
||||||
|
}
|
||||||
|
// #enddocregion unique
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
data:
|
data:
|
||||||
title: "Dart tables"
|
title: "Dart tables"
|
||||||
description: Further information on Dart tables
|
description: Further information on defining tables in Dart.
|
||||||
weight: 150
|
weight: 150
|
||||||
template: layouts/docs/single
|
template: layouts/docs/single
|
||||||
---
|
---
|
||||||
|
@ -11,6 +11,8 @@ __Prefer sql?__ If you prefer, you can also declare tables via `CREATE TABLE` st
|
||||||
Drift's sql analyzer will generate matching Dart code. [Details]({{ "starting_with_sql.md" | pageUrl }}).
|
Drift's sql analyzer will generate matching Dart code. [Details]({{ "starting_with_sql.md" | pageUrl }}).
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% assign snippets = 'package:moor_documentation/snippets/tables/advanced.dart.excerpt.json' | readString | json_decode %}
|
||||||
|
|
||||||
As shown in the [getting started guide]({{ "index.md" | pageUrl }}), sql tables can be written in Dart:
|
As shown in the [getting started guide]({{ "index.md" | pageUrl }}), sql tables can be written in Dart:
|
||||||
```dart
|
```dart
|
||||||
class Todos extends Table {
|
class Todos extends Table {
|
||||||
|
@ -128,6 +130,19 @@ Note that the primary key must essentially be constant so that the generator can
|
||||||
- it must be defined with the `=>` syntax, function bodies aren't supported
|
- it must be defined with the `=>` syntax, function bodies aren't supported
|
||||||
- it must return a set literal without collection elements like `if`, `for` or spread operators
|
- it must return a set literal without collection elements like `if`, `for` or spread operators
|
||||||
|
|
||||||
|
## Unique Constraints
|
||||||
|
|
||||||
|
Starting from version 1.6.0, `UNIQUE` SQL constraints can be defined on Dart tables too.
|
||||||
|
A unique constraint contains one or more columns. The combination of all columns in a constraint
|
||||||
|
must be unique in the table, or the databas will report an error on inserts.
|
||||||
|
|
||||||
|
With drift, a unique constraint can be added to a single column by marking it as `.unique()` in
|
||||||
|
the column builder.
|
||||||
|
A unique set spanning multiple columns can be added by overriding the `uniqueKeys` getter in the
|
||||||
|
`Table` class:
|
||||||
|
|
||||||
|
{% include "blocks/snippet" snippets = snippets name = 'unique' %}
|
||||||
|
|
||||||
## Supported column types
|
## Supported column types
|
||||||
|
|
||||||
Drift supports a variety of column types out of the box. You can store custom classes in columns by using
|
Drift supports a variety of column types out of the box. You can store custom classes in columns by using
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
## 1.6.0-dev
|
## 1.6.0-dev
|
||||||
|
|
||||||
|
- Add the `unique()` method to columns and the `uniqueKeys` override to tables
|
||||||
|
to define unique constraints in Dart tables.
|
||||||
- Add the very experimental `package:drift/wasm.dart` library. It uses WebAssembly
|
- Add the very experimental `package:drift/wasm.dart` library. It uses WebAssembly
|
||||||
to access sqlite3 without any external JavaScript libraries, but requires you to
|
to access sqlite3 without any external JavaScript libraries, but requires you to
|
||||||
add a [WebAssembly module](https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3#wasm-web-support)
|
add a [WebAssembly module](https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3#wasm-web-support)
|
||||||
|
|
|
@ -172,6 +172,8 @@ class $TodoCategoriesTable extends TodoCategories
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
TodoCategory map(Map<String, dynamic> data, {String? tablePrefix}) {
|
TodoCategory map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return TodoCategory.fromData(data,
|
return TodoCategory.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -460,6 +462,8 @@ class $TodoItemsTable extends TodoItems
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
TodoItem map(Map<String, dynamic> data, {String? tablePrefix}) {
|
TodoItem map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return TodoItem.fromData(data,
|
return TodoItem.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
|
@ -95,8 +95,8 @@ extension BuildColumn<T> on ColumnBuilder<T> {
|
||||||
/// [constraint] if that is desired.
|
/// [constraint] if that is desired.
|
||||||
///
|
///
|
||||||
/// This can be used to implement constraints that drift does not (yet)
|
/// This can be used to implement constraints that drift does not (yet)
|
||||||
/// support (e.g. unique keys, etc.). If you've found a common use-case for
|
/// support. If you've found a common use-case for this, it should be
|
||||||
/// this, it should be considered a limitation of drift itself. Please feel
|
/// considered a limitation of drift itself. Please feel
|
||||||
/// free to open an issue at https://github.com/simolus3/drift/issues/new to
|
/// free to open an issue at https://github.com/simolus3/drift/issues/new to
|
||||||
/// report that.
|
/// report that.
|
||||||
///
|
///
|
||||||
|
@ -252,6 +252,12 @@ extension BuildGeneralColumn<T> on _BaseColumnBuilder<T> {
|
||||||
/// primary key. Columns are non-null by default.
|
/// primary key. Columns are non-null by default.
|
||||||
ColumnBuilder<T?> nullable() => _isGenerated();
|
ColumnBuilder<T?> nullable() => _isGenerated();
|
||||||
|
|
||||||
|
/// Adds UNIQUE constraint to column.
|
||||||
|
///
|
||||||
|
/// Unique constraints spanning multiple keys can be added to a table by
|
||||||
|
/// overriding [Table.uniqueKeys].
|
||||||
|
ColumnBuilder<T?> unique() => _isGenerated();
|
||||||
|
|
||||||
/// Uses a custom [converter] to store custom Dart objects in a single column
|
/// Uses a custom [converter] to store custom Dart objects in a single column
|
||||||
/// and automatically mapping them from and to sql.
|
/// and automatically mapping them from and to sql.
|
||||||
///
|
///
|
||||||
|
|
|
@ -62,6 +62,34 @@ abstract class Table extends HasResultSet {
|
||||||
@visibleForOverriding
|
@visibleForOverriding
|
||||||
Set<Column>? get primaryKey => null;
|
Set<Column>? get primaryKey => null;
|
||||||
|
|
||||||
|
/// Unique constraints in this table.
|
||||||
|
///
|
||||||
|
/// When two rows have the same value in _any_ set specified in [uniqueKeys],
|
||||||
|
/// the database will reject the second row for inserts.
|
||||||
|
///
|
||||||
|
/// Override this to specify unique keys:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// class IngredientInRecipes extends Table {
|
||||||
|
/// @override
|
||||||
|
/// Set<Column> get uniqueKeys =>
|
||||||
|
/// [{recipe, ingredient}, {recipe, amountInGrams}];
|
||||||
|
///
|
||||||
|
/// IntColumn get recipe => integer()();
|
||||||
|
/// IntColumn get ingredient => integer()();
|
||||||
|
///
|
||||||
|
/// IntColumn get amountInGrams => integer().named('amount')();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The getter must return a list of set literals using the `=>` syntax so
|
||||||
|
/// that the drift generator can understand the code.
|
||||||
|
///
|
||||||
|
/// Note that individual columns can also be marked as unique with
|
||||||
|
/// [BuildGeneralColumn.unique]. This is equivalent to adding a single-element
|
||||||
|
/// set to this list.
|
||||||
|
@visibleForOverriding
|
||||||
|
List<Set<Column>>? get uniqueKeys => null;
|
||||||
|
|
||||||
/// Custom table constraints that should be added to the table.
|
/// Custom table constraints that should be added to the table.
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
|
|
|
@ -288,6 +288,21 @@ class Migrator {
|
||||||
context.buffer.write(')');
|
context.buffer.write(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (table.uniqueKeys.isNotEmpty) {
|
||||||
|
for (final key in table.uniqueKeys) {
|
||||||
|
context.buffer.write(', UNIQUE (');
|
||||||
|
final uqList = key.toList(growable: false);
|
||||||
|
for (var i = 0; i < uqList.length; i++) {
|
||||||
|
final column = uqList[i];
|
||||||
|
|
||||||
|
context.buffer.write(escapeIfNeeded(column.name));
|
||||||
|
|
||||||
|
if (i != uqList.length - 1) context.buffer.write(', ');
|
||||||
|
}
|
||||||
|
context.buffer.write(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final constraints = dslTable.customConstraints;
|
final constraints = dslTable.customConstraints;
|
||||||
|
|
||||||
for (var i = 0; i < constraints.length; i++) {
|
for (var i = 0; i < constraints.length; i++) {
|
||||||
|
|
|
@ -28,6 +28,14 @@ mixin TableInfo<TableDsl extends Table, D> on Table
|
||||||
@override
|
@override
|
||||||
Set<Column> get primaryKey => $primaryKey;
|
Set<Column> get primaryKey => $primaryKey;
|
||||||
|
|
||||||
|
/// The unique key of this table. Can be empty if no custom primary key has
|
||||||
|
/// been specified.
|
||||||
|
///
|
||||||
|
/// Additional to the [Table.primaryKey] columns declared by an user, this
|
||||||
|
/// also contains auto-increment integers, which are primary key by default.
|
||||||
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => const [];
|
||||||
|
|
||||||
/// The table name in the sql table. This can be an alias for the actual table
|
/// The table name in the sql table. This can be an alias for the actual table
|
||||||
/// name. See [actualTableName] for a table name that is not aliased.
|
/// name. See [actualTableName] for a table name that is not aliased.
|
||||||
@Deprecated('Use aliasedName instead')
|
@Deprecated('Use aliasedName instead')
|
||||||
|
|
|
@ -271,6 +271,8 @@ class ConfigTable extends Table with TableInfo<ConfigTable, Config> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {configKey};
|
Set<GeneratedColumn> get $primaryKey => {configKey};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
Config map(Map<String, dynamic> data, {String? tablePrefix}) {
|
Config map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return Config.fromData(data,
|
return Config.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -458,6 +460,8 @@ class WithDefaults extends Table with TableInfo<WithDefaults, WithDefault> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
WithDefault map(Map<String, dynamic> data, {String? tablePrefix}) {
|
WithDefault map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return WithDefault.fromData(data,
|
return WithDefault.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -546,6 +550,8 @@ class NoIds extends Table with TableInfo<NoIds, NoIdRow> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {payload};
|
Set<GeneratedColumn> get $primaryKey => {payload};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
NoIdRow map(Map<String, dynamic> data, {String? tablePrefix}) {
|
NoIdRow map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
return NoIdRow(
|
return NoIdRow(
|
||||||
|
@ -767,6 +773,8 @@ class WithConstraints extends Table
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
WithConstraint map(Map<String, dynamic> data, {String? tablePrefix}) {
|
WithConstraint map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return WithConstraint.fromData(data,
|
return WithConstraint.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -1036,6 +1044,8 @@ class Mytable extends Table with TableInfo<Mytable, MytableData> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {someid};
|
Set<GeneratedColumn> get $primaryKey => {someid};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
MytableData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
MytableData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return MytableData.fromData(data,
|
return MytableData.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -1256,6 +1266,8 @@ class Email extends Table
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
EMail map(Map<String, dynamic> data, {String? tablePrefix}) {
|
EMail map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return EMail.fromData(data,
|
return EMail.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -1443,6 +1455,8 @@ class WeirdTable extends Table with TableInfo<WeirdTable, WeirdData> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
WeirdData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
WeirdData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return WeirdData.fromData(data,
|
return WeirdData.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
|
@ -16,13 +16,19 @@ class TodosTable extends Table with AutoIncrement {
|
||||||
TextColumn get title => text().withLength(min: 4, max: 16).nullable()();
|
TextColumn get title => text().withLength(min: 4, max: 16).nullable()();
|
||||||
TextColumn get content => text()();
|
TextColumn get content => text()();
|
||||||
@JsonKey('target_date')
|
@JsonKey('target_date')
|
||||||
DateTimeColumn get targetDate => dateTime().nullable()();
|
DateTimeColumn get targetDate => dateTime().nullable().unique()();
|
||||||
|
|
||||||
IntColumn get category => integer().references(Categories, #id).nullable()();
|
IntColumn get category => integer().references(Categories, #id).nullable()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Set<Column>>? get uniqueKeys => [
|
||||||
|
{title, category},
|
||||||
|
{title, targetDate},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class Users extends Table with AutoIncrement {
|
class Users extends Table with AutoIncrement {
|
||||||
TextColumn get name => text().withLength(min: 6, max: 32)();
|
TextColumn get name => text().withLength(min: 6, max: 32).unique()();
|
||||||
BoolColumn get isAwesome => boolean().withDefault(const Constant(true))();
|
BoolColumn get isAwesome => boolean().withDefault(const Constant(true))();
|
||||||
|
|
||||||
BlobColumn get profilePicture => blob()();
|
BlobColumn get profilePicture => blob()();
|
||||||
|
|
|
@ -249,6 +249,8 @@ class $CategoriesTable extends Categories
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
Category map(Map<String, dynamic> data, {String? tablePrefix}) {
|
Category map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return Category.fromData(data,
|
return Category.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -503,7 +505,9 @@ class $TodosTableTable extends TodosTable
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumn<DateTime?> targetDate = GeneratedColumn<DateTime?>(
|
late final GeneratedColumn<DateTime?> targetDate = GeneratedColumn<DateTime?>(
|
||||||
'target_date', aliasedName, true,
|
'target_date', aliasedName, true,
|
||||||
type: const IntType(), requiredDuringInsert: false);
|
type: const IntType(),
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultConstraints: 'UNIQUE');
|
||||||
final VerificationMeta _categoryMeta = const VerificationMeta('category');
|
final VerificationMeta _categoryMeta = const VerificationMeta('category');
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumn<int?> category = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> category = GeneratedColumn<int?>(
|
||||||
|
@ -552,6 +556,11 @@ class $TodosTableTable extends TodosTable
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [
|
||||||
|
{title, category},
|
||||||
|
{title, targetDate},
|
||||||
|
];
|
||||||
|
@override
|
||||||
TodoEntry map(Map<String, dynamic> data, {String? tablePrefix}) {
|
TodoEntry map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return TodoEntry.fromData(data,
|
return TodoEntry.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -782,7 +791,8 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
additionalChecks:
|
additionalChecks:
|
||||||
GeneratedColumn.checkTextLength(minTextLength: 6, maxTextLength: 32),
|
GeneratedColumn.checkTextLength(minTextLength: 6, maxTextLength: 32),
|
||||||
type: const StringType(),
|
type: const StringType(),
|
||||||
requiredDuringInsert: true);
|
requiredDuringInsert: true,
|
||||||
|
defaultConstraints: 'UNIQUE');
|
||||||
final VerificationMeta _isAwesomeMeta = const VerificationMeta('isAwesome');
|
final VerificationMeta _isAwesomeMeta = const VerificationMeta('isAwesome');
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumn<bool?> isAwesome = GeneratedColumn<bool?>(
|
late final GeneratedColumn<bool?> isAwesome = GeneratedColumn<bool?>(
|
||||||
|
@ -850,6 +860,8 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
User map(Map<String, dynamic> data, {String? tablePrefix}) {
|
User map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return User.fromData(data,
|
return User.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -1030,6 +1042,8 @@ class $SharedTodosTable extends SharedTodos
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {todo, user};
|
Set<GeneratedColumn> get $primaryKey => {todo, user};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
SharedTodo map(Map<String, dynamic> data, {String? tablePrefix}) {
|
SharedTodo map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return SharedTodo.fromData(data,
|
return SharedTodo.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -1184,6 +1198,8 @@ class $TableWithoutPKTable extends TableWithoutPK
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
CustomRowClass map(Map<String, dynamic> data, {String? tablePrefix}) {
|
CustomRowClass map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
return CustomRowClass.map(
|
return CustomRowClass.map(
|
||||||
|
@ -1341,6 +1357,8 @@ class $PureDefaultsTable extends PureDefaults
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {txt};
|
Set<GeneratedColumn> get $primaryKey => {txt};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
PureDefault map(Map<String, dynamic> data, {String? tablePrefix}) {
|
PureDefault map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return PureDefault.fromData(data,
|
return PureDefault.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
|
@ -61,7 +61,7 @@ void main() {
|
||||||
);
|
);
|
||||||
await db.into(db.users).insert(
|
await db.into(db.users).insert(
|
||||||
UsersCompanion.insert(
|
UsersCompanion.insert(
|
||||||
name: 'User name',
|
name: 'User name 2',
|
||||||
profilePicture: Uint8List(0),
|
profilePicture: Uint8List(0),
|
||||||
creationTime: Value(secondTime)),
|
creationTime: Value(secondTime)),
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,7 +19,7 @@ void main() {
|
||||||
b.insertAll(db.users, [
|
b.insertAll(db.users, [
|
||||||
for (var i = 0; i < 1000; i++)
|
for (var i = 0; i < 1000; i++)
|
||||||
UsersCompanion.insert(
|
UsersCompanion.insert(
|
||||||
name: 'user name', profilePicture: Uint8List(0)),
|
name: 'user name $i', profilePicture: Uint8List(0)),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,9 @@ void main() {
|
||||||
verify(mockExecutor.runCustom(
|
verify(mockExecutor.runCustom(
|
||||||
'CREATE TABLE IF NOT EXISTS todos '
|
'CREATE TABLE IF NOT EXISTS todos '
|
||||||
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, title TEXT NULL, '
|
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, title TEXT NULL, '
|
||||||
'content TEXT NOT NULL, target_date INTEGER NULL, '
|
'content TEXT NOT NULL, target_date INTEGER NULL UNIQUE, '
|
||||||
'category INTEGER NULL REFERENCES categories (id));',
|
'category INTEGER NULL REFERENCES categories (id), '
|
||||||
|
'UNIQUE (title, category), UNIQUE (title, target_date));',
|
||||||
[]));
|
[]));
|
||||||
|
|
||||||
verify(mockExecutor.runCustom(
|
verify(mockExecutor.runCustom(
|
||||||
|
@ -39,7 +40,7 @@ void main() {
|
||||||
verify(mockExecutor.runCustom(
|
verify(mockExecutor.runCustom(
|
||||||
'CREATE TABLE IF NOT EXISTS users '
|
'CREATE TABLE IF NOT EXISTS users '
|
||||||
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
||||||
'name TEXT NOT NULL, '
|
'name TEXT NOT NULL UNIQUE, '
|
||||||
'is_awesome INTEGER NOT NULL DEFAULT 1 CHECK (is_awesome IN (0, 1)), '
|
'is_awesome INTEGER NOT NULL DEFAULT 1 CHECK (is_awesome IN (0, 1)), '
|
||||||
'profile_picture BLOB NOT NULL, '
|
'profile_picture BLOB NOT NULL, '
|
||||||
'creation_time INTEGER NOT NULL '
|
'creation_time INTEGER NOT NULL '
|
||||||
|
@ -91,7 +92,7 @@ void main() {
|
||||||
verify(mockExecutor.runCustom(
|
verify(mockExecutor.runCustom(
|
||||||
'CREATE TABLE IF NOT EXISTS users '
|
'CREATE TABLE IF NOT EXISTS users '
|
||||||
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
||||||
'name TEXT NOT NULL, '
|
'name TEXT NOT NULL UNIQUE, '
|
||||||
'is_awesome INTEGER NOT NULL DEFAULT 1 CHECK (is_awesome IN (0, 1)), '
|
'is_awesome INTEGER NOT NULL DEFAULT 1 CHECK (is_awesome IN (0, 1)), '
|
||||||
'profile_picture BLOB NOT NULL, '
|
'profile_picture BLOB NOT NULL, '
|
||||||
'creation_time INTEGER NOT NULL '
|
'creation_time INTEGER NOT NULL '
|
||||||
|
|
|
@ -23,6 +23,7 @@ const String _methodReferences = 'references';
|
||||||
const String _methodAutoIncrement = 'autoIncrement';
|
const String _methodAutoIncrement = 'autoIncrement';
|
||||||
const String _methodWithLength = 'withLength';
|
const String _methodWithLength = 'withLength';
|
||||||
const String _methodNullable = 'nullable';
|
const String _methodNullable = 'nullable';
|
||||||
|
const String _methodUnique = 'unique';
|
||||||
const String _methodCustomConstraint = 'customConstraint';
|
const String _methodCustomConstraint = 'customConstraint';
|
||||||
const String _methodDefault = 'withDefault';
|
const String _methodDefault = 'withDefault';
|
||||||
const String _methodClientDefault = 'clientDefault';
|
const String _methodClientDefault = 'clientDefault';
|
||||||
|
@ -218,6 +219,9 @@ class ColumnParser {
|
||||||
case _methodNullable:
|
case _methodNullable:
|
||||||
nullable = true;
|
nullable = true;
|
||||||
break;
|
break;
|
||||||
|
case _methodUnique:
|
||||||
|
foundFeatures.add(const UniqueKey());
|
||||||
|
break;
|
||||||
case _methodCustomConstraint:
|
case _methodCustomConstraint:
|
||||||
foundCustomConstraint = base.readStringLiteral(
|
foundCustomConstraint = base.readStringLiteral(
|
||||||
remainingExpr.argumentList.arguments.first, () {
|
remainingExpr.argumentList.arguments.first, () {
|
||||||
|
@ -355,6 +359,17 @@ class ColumnParser {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (foundFeatures.contains(const UniqueKey()) &&
|
||||||
|
foundFeatures.contains(const PrimaryKey())) {
|
||||||
|
base.step.reportError(
|
||||||
|
ErrorInDartCode(
|
||||||
|
severity: Severity.error,
|
||||||
|
affectedElement: getter.declaredElement,
|
||||||
|
message: 'Primary key column cannot have UNIQUE constraint',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final docString =
|
final docString =
|
||||||
getter.documentationComment?.tokens.map((t) => t.toString()).join('\n');
|
getter.documentationComment?.tokens.map((t) => t.toString()).join('\n');
|
||||||
return MoorColumn(
|
return MoorColumn(
|
||||||
|
|
|
@ -12,6 +12,7 @@ class TableParser {
|
||||||
|
|
||||||
final columns = (await _parseColumns(element)).toList();
|
final columns = (await _parseColumns(element)).toList();
|
||||||
final primaryKey = await _readPrimaryKey(element, columns);
|
final primaryKey = await _readPrimaryKey(element, columns);
|
||||||
|
final uniqueKeys = await _readUniqueKeys(element, columns);
|
||||||
|
|
||||||
final dataClassInfo = _readDataClassInformation(columns, element);
|
final dataClassInfo = _readDataClassInformation(columns, element);
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ class TableParser {
|
||||||
existingRowClass: dataClassInfo.existingClass,
|
existingRowClass: dataClassInfo.existingClass,
|
||||||
customParentClass: dataClassInfo.extending,
|
customParentClass: dataClassInfo.extending,
|
||||||
primaryKey: primaryKey,
|
primaryKey: primaryKey,
|
||||||
|
uniqueKeys: uniqueKeys,
|
||||||
overrideWithoutRowId: await _overrideWithoutRowId(element),
|
overrideWithoutRowId: await _overrideWithoutRowId(element),
|
||||||
declaration: DartTableDeclaration(element, base.step.file),
|
declaration: DartTableDeclaration(element, base.step.file),
|
||||||
);
|
);
|
||||||
|
@ -34,6 +36,38 @@ class TableParser {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (primaryKey != null &&
|
||||||
|
primaryKey.length == 1 &&
|
||||||
|
primaryKey.first.features.contains(const UniqueKey())) {
|
||||||
|
base.step.errors.report(ErrorInDartCode(
|
||||||
|
message: 'Primary key column cannot have UNIQUE constraint',
|
||||||
|
affectedElement: element,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniqueKeys != null &&
|
||||||
|
uniqueKeys.any((key) =>
|
||||||
|
uniqueKeys.length == 1 &&
|
||||||
|
key.first.features.contains(const UniqueKey()))) {
|
||||||
|
base.step.errors.report(ErrorInDartCode(
|
||||||
|
message:
|
||||||
|
'Column provided in a single-column uniqueKey set already has a '
|
||||||
|
'column-level UNIQUE constraint',
|
||||||
|
affectedElement: element,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniqueKeys != null &&
|
||||||
|
primaryKey != null &&
|
||||||
|
uniqueKeys
|
||||||
|
.any((unique) => const SetEquality().equals(unique, primaryKey))) {
|
||||||
|
base.step.errors.report(ErrorInDartCode(
|
||||||
|
message: 'The uniqueKeys override contains the primary key, which is '
|
||||||
|
'already unique by default.',
|
||||||
|
affectedElement: element,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
var index = 0;
|
var index = 0;
|
||||||
for (final converter in table.converters) {
|
for (final converter in table.converters) {
|
||||||
converter
|
converter
|
||||||
|
@ -188,6 +222,68 @@ class TableParser {
|
||||||
return parsedPrimaryKey;
|
return parsedPrimaryKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Set<MoorColumn>>?> _readUniqueKeys(
|
||||||
|
ClassElement element, List<MoorColumn> columns) async {
|
||||||
|
final uniqueKeyGetter = element.lookUpGetter('uniqueKeys', element.library);
|
||||||
|
|
||||||
|
if (uniqueKeyGetter == null || uniqueKeyGetter.isFromDefaultTable) {
|
||||||
|
// resolved uniqueKeys is from the Table dsl superclass. That means there
|
||||||
|
// is no unique key list
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ast =
|
||||||
|
await base.loadElementDeclaration(uniqueKeyGetter) as MethodDeclaration;
|
||||||
|
final body = ast.body;
|
||||||
|
if (body is! ExpressionFunctionBody) {
|
||||||
|
base.step.reportError(ErrorInDartCode(
|
||||||
|
affectedElement: uniqueKeyGetter,
|
||||||
|
message: 'This must return a list of set literal using the => '
|
||||||
|
'syntax!'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final expression = body.expression;
|
||||||
|
final parsedUniqueKeys = <Set<MoorColumn>>[];
|
||||||
|
|
||||||
|
if (expression is ListLiteral) {
|
||||||
|
for (final keySet in expression.elements) {
|
||||||
|
if (keySet is SetOrMapLiteral) {
|
||||||
|
final uniqueKey = <MoorColumn>{};
|
||||||
|
for (final entry in keySet.elements) {
|
||||||
|
if (entry is Identifier) {
|
||||||
|
final column = columns.singleWhereOrNull(
|
||||||
|
(column) => column.dartGetterName == entry.name);
|
||||||
|
if (column == null) {
|
||||||
|
base.step.reportError(
|
||||||
|
ErrorInDartCode(
|
||||||
|
affectedElement: uniqueKeyGetter,
|
||||||
|
affectedNode: entry,
|
||||||
|
message: 'Column not found in this table',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
uniqueKey.add(column);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print('Unexpected entry in expression.elements: $entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsedUniqueKeys.add(uniqueKey);
|
||||||
|
} else {
|
||||||
|
base.step.reportError(ErrorInDartCode(
|
||||||
|
affectedElement: uniqueKeyGetter,
|
||||||
|
message: 'This must return a set list literal!'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
base.step.reportError(ErrorInDartCode(
|
||||||
|
affectedElement: uniqueKeyGetter,
|
||||||
|
message: 'This must return a set list literal!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedUniqueKeys;
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool?> _overrideWithoutRowId(ClassElement element) async {
|
Future<bool?> _overrideWithoutRowId(ClassElement element) async {
|
||||||
final getter = element.lookUpGetter('withoutRowId', element.library);
|
final getter = element.lookUpGetter('withoutRowId', element.library);
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,11 @@ class PrimaryKey extends ColumnFeature {
|
||||||
const PrimaryKey();
|
const PrimaryKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A `UNIQUE` column constraint.
|
||||||
|
class UniqueKey extends ColumnFeature {
|
||||||
|
const UniqueKey();
|
||||||
|
}
|
||||||
|
|
||||||
class AutoIncrement extends PrimaryKey {
|
class AutoIncrement extends PrimaryKey {
|
||||||
static const AutoIncrement _instance = AutoIncrement._();
|
static const AutoIncrement _instance = AutoIncrement._();
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,10 @@ class MoorTable extends MoorEntityWithResultSet {
|
||||||
/// For the full primary key, see [fullPrimaryKey].
|
/// For the full primary key, see [fullPrimaryKey].
|
||||||
final Set<MoorColumn>? primaryKey;
|
final Set<MoorColumn>? primaryKey;
|
||||||
|
|
||||||
|
/// The set of unique keys if they have been explicitly defined by
|
||||||
|
/// overriding `uniqueKeys` in the table class.
|
||||||
|
final List<Set<MoorColumn>>? uniqueKeys;
|
||||||
|
|
||||||
/// The primary key for this table.
|
/// The primary key for this table.
|
||||||
///
|
///
|
||||||
/// Unlikely [primaryKey], this method is not limited to the `primaryKey`
|
/// Unlikely [primaryKey], this method is not limited to the `primaryKey`
|
||||||
|
@ -146,6 +150,7 @@ class MoorTable extends MoorEntityWithResultSet {
|
||||||
required this.sqlName,
|
required this.sqlName,
|
||||||
required this.dartTypeName,
|
required this.dartTypeName,
|
||||||
this.primaryKey,
|
this.primaryKey,
|
||||||
|
this.uniqueKeys,
|
||||||
String? overriddenName,
|
String? overriddenName,
|
||||||
this.overrideWithoutRowId,
|
this.overrideWithoutRowId,
|
||||||
this.overrideTableConstraints,
|
this.overrideTableConstraints,
|
||||||
|
|
|
@ -28,9 +28,21 @@ abstract class TableOrViewWriter {
|
||||||
: 'PRIMARY KEY');
|
: 'PRIMARY KEY');
|
||||||
|
|
||||||
wrotePkConstraint = true;
|
wrotePkConstraint = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wrotePkConstraint) {
|
||||||
|
for (final feature in column.features) {
|
||||||
|
if (feature is UniqueKey) {
|
||||||
|
defaultConstraints.add('UNIQUE');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final feature in column.features) {
|
||||||
if (feature is ResolvedDartForeignKeyReference) {
|
if (feature is ResolvedDartForeignKeyReference) {
|
||||||
final tableName = escapeIfNeeded(feature.otherTable.sqlName);
|
final tableName = escapeIfNeeded(feature.otherTable.sqlName);
|
||||||
final columnName = escapeIfNeeded(feature.otherColumn.name.name);
|
final columnName = escapeIfNeeded(feature.otherColumn.name.name);
|
||||||
|
@ -324,6 +336,7 @@ class TableWriter extends TableOrViewWriter {
|
||||||
|
|
||||||
_writeValidityCheckMethod();
|
_writeValidityCheckMethod();
|
||||||
_writePrimaryKeyOverride();
|
_writePrimaryKeyOverride();
|
||||||
|
_writeUniqueKeyOverride();
|
||||||
|
|
||||||
writeMappingMethod(scope);
|
writeMappingMethod(scope);
|
||||||
// _writeReverseMappingMethod();
|
// _writeReverseMappingMethod();
|
||||||
|
@ -425,6 +438,32 @@ class TableWriter extends TableOrViewWriter {
|
||||||
buffer.write('};\n');
|
buffer.write('};\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _writeUniqueKeyOverride() {
|
||||||
|
buffer.write('@override\nList<Set<GeneratedColumn>> get uniqueKeys => ');
|
||||||
|
final uniqueKeys = table.uniqueKeys ?? [];
|
||||||
|
|
||||||
|
if (uniqueKeys.isEmpty) {
|
||||||
|
buffer.write('[];');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.write('[');
|
||||||
|
for (final uniqueKey in uniqueKeys) {
|
||||||
|
buffer.write('{');
|
||||||
|
final uqList = uniqueKey.toList();
|
||||||
|
for (var i = 0; i < uqList.length; i++) {
|
||||||
|
final pk = uqList[i];
|
||||||
|
|
||||||
|
buffer.write(pk.dartGetterName);
|
||||||
|
if (i != uqList.length - 1) {
|
||||||
|
buffer.write(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.write('},\n');
|
||||||
|
}
|
||||||
|
buffer.write('];\n');
|
||||||
|
}
|
||||||
|
|
||||||
void _writeAliasGenerator() {
|
void _writeAliasGenerator() {
|
||||||
final typeName = table.entityInfoName;
|
final typeName = table.entityInfoName;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../utils.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('does not allow autoIncrement() to have a unique constraint', () async {
|
||||||
|
final state = TestState.withContent({
|
||||||
|
'a|lib/main.dart': '''
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
class Test extends Table {
|
||||||
|
IntColumn get a => integer().autoIncrement().unique()();
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
});
|
||||||
|
addTearDown(state.close);
|
||||||
|
|
||||||
|
(await state.analyze('package:a/main.dart')).expectDartError(
|
||||||
|
'Primary key column cannot have UNIQUE constraint', 'a');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not allow primary key to have a unique constraint', () async {
|
||||||
|
final state = TestState.withContent({
|
||||||
|
'a|lib/main.dart': '''
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
class Test extends Table {
|
||||||
|
IntColumn get a => integer().unique()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {a};
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
});
|
||||||
|
addTearDown(state.close);
|
||||||
|
|
||||||
|
(await state.analyze('package:a/main.dart')).expectDartError(
|
||||||
|
'Primary key column cannot have UNIQUE constraint', 'Test');
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'does not allow primary key to have a unique constraint through override',
|
||||||
|
() async {
|
||||||
|
final state = TestState.withContent({
|
||||||
|
'a|lib/main.dart': '''
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
class Test extends Table {
|
||||||
|
IntColumn get a => integer()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Set<Column>> get uniqueKeys => [{a}];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {a};
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
});
|
||||||
|
addTearDown(state.close);
|
||||||
|
|
||||||
|
(await state.analyze('package:a/main.dart')).expectDartError(
|
||||||
|
'The uniqueKeys override contains the primary key, which is already '
|
||||||
|
'unique by default.',
|
||||||
|
'Test');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('warns about duplicate unique declarations', () async {
|
||||||
|
final state = TestState.withContent({
|
||||||
|
'a|lib/main.dart': '''
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
class Test extends Table {
|
||||||
|
IntColumn get a => integer().unique()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Set<Column>> get uniqueKeys => [{a}];
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
});
|
||||||
|
addTearDown(state.close);
|
||||||
|
|
||||||
|
(await state.analyze('package:a/main.dart')).expectDartError(
|
||||||
|
contains('already has a column-level UNIQUE constraint'), 'Test');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parses unique key definitions', () async {
|
||||||
|
final state = TestState.withContent({
|
||||||
|
'a|lib/main.dart': '''
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
class Test extends Table {
|
||||||
|
IntColumn get a => integer().unique()();
|
||||||
|
IntColumn get b => integer().unique()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Set<Column>> get uniqueKeys => [{a}, {b}, {a, b}];
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
});
|
||||||
|
addTearDown(state.close);
|
||||||
|
|
||||||
|
final file = await state.analyze('package:a/main.dart');
|
||||||
|
expect(file.errors.errors, isEmpty);
|
||||||
|
|
||||||
|
final table = file.currentResult!.declaredTables.single;
|
||||||
|
expect(table.uniqueKeys, hasLength(3));
|
||||||
|
expect(table.uniqueKeys![0].map((e) => e.name.name), ['a']);
|
||||||
|
expect(table.uniqueKeys![1].map((e) => e.name.name), ['b']);
|
||||||
|
expect(table.uniqueKeys![2].map((e) => e.name.name), ['a', 'b']);
|
||||||
|
});
|
||||||
|
}
|
|
@ -166,6 +166,8 @@ class Entries extends Table with TableInfo<Entries, Entrie> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
Entrie map(Map<String, dynamic> data, {String? tablePrefix}) {
|
Entrie map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return Entrie.fromData(data,
|
return Entrie.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
|
@ -166,6 +166,8 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
User map(Map<String, dynamic> data, {String? tablePrefix}) {
|
User map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return User.fromData(data,
|
return User.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -414,6 +416,8 @@ class Groups extends Table with TableInfo<Groups, Group> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
Group map(Map<String, dynamic> data, {String? tablePrefix}) {
|
Group map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return Group.fromData(data,
|
return Group.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
|
@ -166,6 +166,8 @@ class Entries extends Table with TableInfo<Entries, Entrie> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
Entrie map(Map<String, dynamic> data, {String? tablePrefix}) {
|
Entrie map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return Entrie.fromData(data,
|
return Entrie.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
|
@ -171,6 +171,8 @@ class Users extends Table with TableInfo<Users, User> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
User map(Map<String, dynamic> data, {String tablePrefix}) {
|
User map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||||
return User.fromData(data,
|
return User.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
|
@ -126,9 +126,10 @@ class KeyValuesCompanion extends UpdateCompanion<KeyValue> {
|
||||||
|
|
||||||
class $KeyValuesTable extends KeyValues
|
class $KeyValuesTable extends KeyValues
|
||||||
with TableInfo<$KeyValuesTable, KeyValue> {
|
with TableInfo<$KeyValuesTable, KeyValue> {
|
||||||
final GeneratedDatabase _db;
|
@override
|
||||||
|
final GeneratedDatabase attachedDatabase;
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$KeyValuesTable(this._db, [this._alias]);
|
$KeyValuesTable(this.attachedDatabase, [this._alias]);
|
||||||
final VerificationMeta _keyMeta = const VerificationMeta('key');
|
final VerificationMeta _keyMeta = const VerificationMeta('key');
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumn<String?> key = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> key = GeneratedColumn<String?>(
|
||||||
|
@ -168,6 +169,8 @@ class $KeyValuesTable extends KeyValues
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {key};
|
Set<GeneratedColumn> get $primaryKey => {key};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
KeyValue map(Map<String, dynamic> data, {String? tablePrefix}) {
|
KeyValue map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return KeyValue.fromData(data,
|
return KeyValue.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -175,7 +178,7 @@ class $KeyValuesTable extends KeyValues
|
||||||
|
|
||||||
@override
|
@override
|
||||||
$KeyValuesTable createAlias(String alias) {
|
$KeyValuesTable createAlias(String alias) {
|
||||||
return $KeyValuesTable(_db, alias);
|
return $KeyValuesTable(attachedDatabase, alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -307,6 +307,8 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
User map(Map<String, dynamic> data, {String? tablePrefix}) {
|
User map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return User.fromData(data,
|
return User.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -531,6 +533,8 @@ class $FriendshipsTable extends Friendships
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {firstUser, secondUser};
|
Set<GeneratedColumn> get $primaryKey => {firstUser, secondUser};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
Friendship map(Map<String, dynamic> data, {String? tablePrefix}) {
|
Friendship map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
return Friendship.fromData(data,
|
return Friendship.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
|
@ -137,6 +137,8 @@ class $FoosTable extends Foos with TableInfo<$FoosTable, Foo> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
Foo map(Map<String, dynamic> data, {String tablePrefix}) {
|
Foo map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||||
return Foo.fromData(data,
|
return Foo.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
@ -278,6 +280,8 @@ class $BarsTable extends Bars with TableInfo<$BarsTable, Bar> {
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
@override
|
@override
|
||||||
|
List<Set<GeneratedColumn>> get uniqueKeys => [];
|
||||||
|
@override
|
||||||
Bar map(Map<String, dynamic> data, {String tablePrefix}) {
|
Bar map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||||
return Bar.fromData(data,
|
return Bar.fromData(data,
|
||||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
|
Loading…
Reference in New Issue