/
table.dart
406 lines (367 loc) · 15.3 KB
/
table.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
part of 'dsl.dart';
/// Base class for dsl [Table]s and [View]s.
abstract class HasResultSet {
/// Default constant constructor.
const HasResultSet();
}
/// Subclasses represent a table in a database generated by drift.
///
/// For more information on how to write tables, see [the documentation](https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/)
abstract class Table extends HasResultSet {
/// Defines a table to be used with drift.
const Table();
/// The sql table name to be used. By default, drift will use the snake_case
/// representation of your class name as the sql table name. For instance, a
/// [Table] class named `LocalSettings` will be called `local_settings` by
/// default.
/// You can change that behavior by overriding this method to use a custom
/// name. Please note that you must directly return a string literal by using
/// a getter. For instance `@override String get tableName => 'my_table';` is
/// valid, whereas `@override final String tableName = 'my_table';` or
/// `@override String get tableName => createMyTableName();` is not.
@visibleForOverriding
String? get tableName => null;
/// Whether to append a `WITHOUT ROWID` clause in the `CREATE TABLE`
/// statement. This is intended to be used by generated code only.
bool get withoutRowId => false;
/// Drift will write some table constraints automatically, for instance when
/// you override [primaryKey]. You can turn this behavior off if you want to.
/// This is intended to be used by generated code only.
bool get dontWriteConstraints => false;
/// Whether this table is `STRICT`.
///
/// Strict tables enforce stronger type constraints for inserts and updates.
/// Support for strict tables was added in sqlite3 version 37.
/// This field is intended to be used by generated code only.
bool get isStrict => false;
/// Override this to specify custom primary keys:
/// ```dart
/// class IngredientInRecipes extends Table {
/// @override
/// Set<Column> get primaryKey => {recipe, ingredient};
///
/// IntColumn get recipe => integer()();
/// IntColumn get ingredient => integer()();
///
/// IntColumn get amountInGrams => integer().named('amount')();
///}
/// ```
/// The getter must return a set literal using the `=>` syntax so that the
/// drift generator can understand the code.
/// Also, please note that it's an error to have an
/// [BuildIntColumn.autoIncrement] column and a custom primary key.
/// As an auto-incremented `IntColumn` is recognized by drift to be the
/// primary key, doing so will result in an exception thrown at runtime.
@visibleForOverriding
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
/// List<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.
///
/// See also:
/// - https://www.sqlite.org/syntax/table-constraint.html, which defines what
/// table constraints are supported.
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):
/// ```
/// IntColumn get id => integer().autoIncrement()();
/// ```
///
/// In sqlite3, an integer column stores 64-big integers. This column maps
/// values to an [int] in Dart, which works well on native platforms. On the
/// web, be aware that [int]s are [double]s internally which means that only
/// integers smaller than 2⁵² can safely be stored.
/// If you need web support __and__ a column that potential stores integers
/// larger than what fits into 52 bits, consider using a [int64] column
/// instead. That column stores the same value in a database, but makes drift
/// report the values as a [BigInt] in Dart.
@protected
ColumnBuilder<int> integer() => _isGenerated();
/// Use this as the body of a getter to declare a column that holds a 64-big
/// integer as a [BigInt].
///
/// The main purpose of this column is to support large integers for web apps
/// compiled to JavaScript, where using an [int] does not reliably work for
/// numbers larger than 2⁵².
/// It stores the exact same data as an [integer] column (and supports the
/// same options), but instructs drift to generate a data class with a
/// [BigInt] field and a database conversion aware of large intergers.
///
/// __Note__: The use of [int64] is only necessary for apps that need to work
/// on the web __and__ use columns that are likely to store values larger than
/// 2⁵². In all other cases, using [integer] directly is much more efficient
/// and recommended.
@protected
ColumnBuilder<BigInt> int64() => _isGenerated();
/// Creates a column to store an `enum` class [T].
///
/// In the database, the column will be represented as an integer
/// corresponding to the enum's index. Note that this can invalidate your data
/// if you add another value to the enum class.
@protected
ColumnBuilder<int> intEnum<T extends Enum>() => _isGenerated();
/// Use this as the body of a getter to declare a column that holds strings.
/// Example (inside the body of a table class):
/// ```
/// TextColumn get name => text()();
/// ```
@protected
ColumnBuilder<String> text() => _isGenerated();
/// Creates a column to store an `enum` class [T].
///
/// In the database, the column will be represented as text corresponding to
/// the name of the enum entries. Note that this can invalidate your data if
/// you rename the entries of the enum class.
ColumnBuilder<String> textEnum<T extends Enum>() => _isGenerated();
/// Use this as the body of a getter to declare a column that holds bools.
/// Example (inside the body of a table class):
/// ```
/// BoolColumn get isAwesome => boolean()();
/// ```
@protected
ColumnBuilder<bool> boolean() => _isGenerated();
/// Use this as the body of a getter to declare a column that holds date and
/// time.
///
/// Drift supports two modes for storing date times: As unix timestamp with
/// second accuracy (the default) and as ISO 8601 string with microsecond
/// accuracy. For more information between the modes, and information on how
/// to change them, see [the documentation].
///
/// Note that [DateTime] values are stored on a second-accuracy.
/// Example (inside the body of a table class):
/// ```
/// DateTimeColumn get accountCreatedAt => dateTime()();
/// ```
/// [the documentation]: https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/#supported-column-types
@protected
ColumnBuilder<DateTime> dateTime() => _isGenerated();
/// Use this as the body of a getter to declare a column that holds arbitrary
/// data blobs, stored as an [Uint8List]. Example:
/// ```
/// BlobColumn get payload => blob()();
/// ```
@protected
ColumnBuilder<Uint8List> blob() => _isGenerated();
/// Use this as the body of a getter to declare a column that holds floating
/// point numbers. Example
/// ```
/// RealColumn get averageSpeed => real()();
/// ```
@protected
ColumnBuilder<double> real() => _isGenerated();
/// Defines a column with a custom [type] when used as a getter.
///
/// For more information on custom types and when they can be useful, see
/// https://drift.simonbinder.eu/docs/sql-api/types/.
///
/// For most users, [TypeConverter]s are a more appropriate tool to store
/// custom values in the database.
@protected
ColumnBuilder<T> customType<T extends Object>(UserDefinedSqlType<T> type) =>
_isGenerated();
}
/// Subclasses represent a view in a database generated by drift.
///
/// For more information on how to define views in Dart, see
/// [the documentation](https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/#views)
abstract class View extends HasResultSet {
/// Defines a view to be used with drift.
const View();
/// The select method can be used in [as] to define the select query backing
/// this view.
///
/// The select statement should select all columns defined on this view.
@protected
View select(List<Expression> columns) => _isGenerated();
/// This method should be called on [select] to define the main table of this
/// view:
///
/// ```dart
/// abstract class CategoryTodoCount extends View {
/// TodosTable get todos;
/// Categories get categories;
///
/// Expression<int> get itemCount => todos.id.count();
///
/// @override
/// Query as() => select([categories.description, itemCount])
/// .from(categories)
/// .join([innerJoin(todos, todos.category.equalsExp(categories.id))]);
/// }
/// ```
@protected
SimpleSelectStatement from(Table table) => _isGenerated();
/// This method is overridden by Dart-defined views to declare the right
/// query to run.
@visibleForOverriding
Query as();
}
/// Annotations for Dart table classes to define a [SQL index](https://sqlite.org/lang_createindex.html)
/// to add to the table.
///
/// ```dart
/// @TableIndex(name: 'item_title', columns: {#title})
/// class TodoItems extends Table {
/// IntColumn get id => integer().autoIncrement()();
/// TextColumn get title => text()();
/// TextColumn get content => text().nullable()();
/// }
/// ```
@Target({TargetKind.classType})
final class TableIndex {
/// The name of the index in SQL.
///
/// Please note that the name of every table, view, index and other elements
/// in a database must be unique. For this reason, the names of indices are
/// commonly prefixed with the name of the table they're referencing.
final String name;
/// Whether this index is `UNIQUE`, meaning that the database will forbid
/// multiple rows in the annotated table from having the same values in the
/// indexed columns.
final bool unique;
/// The columns of the table that should be part of the index.
///
/// Columns are referenced with a [Symbol] of their getter name used in the
/// column definition. For instance, a table declaring a column as
/// `IntColumn get nextUpdateSnapshot => ...()` could reference this column
/// using `#nextUpdateSnapshot`.
final Set<Symbol> columns;
/// An annotation for Dart-defined drift tables telling drift to add an SQL
/// index to the table.
///
/// See the class documentation at [TableIndex] for an example.
const TableIndex({
required this.name,
required this.columns,
this.unique = false,
});
}
/// A class to be used as an annotation on [Table] classes to customize the
/// name for the data class that will be generated for the table class. The data
/// class is a dart object that will be used to represent a row in the table.
///
/// {@template drift_custom_data_class}
/// By default, drift will attempt to use the singular form of the table name
/// when naming data classes (e.g. a table named "Users" will generate a data
/// class called "User"). However, this doesn't work for irregular plurals and
/// you might want to choose a different name, for which this annotation can be
/// used.
/// {@endtemplate}
@Target({TargetKind.classType})
class DataClassName {
/// The overridden name to use when generating the data class for a table.
/// {@macro drift_custom_data_class}
final String name;
/// The parent type of the data class generated by drift.
///
/// The [extending] type must refer to an interface type (usually just a
/// class name), and the parent class must extend [DataClass].
///
/// The extended class can optionally have a type parameter, which is
/// instantiated to the actual data class generated by drift.
///
/// For example,
///
/// ```dart
/// abstract class BaseModel extends DataClass {
/// abstract final String id;
/// }
///
/// abstract class TypedBaseModel<T> extends DataClass {
///
/// }
///
/// @DataClassName('Company', extending: BaseModel)
/// class Companies extends Table {
/// TextColumn get id => text()();
/// TextColumn get name => text().named('name')();
/// }
///
/// // The actual generated class will extend `TypedBaseModel<Employee>`.
/// @DataClassName('Employee', extending: TypedBaseModel)
/// class Employees extends Table {
/// TextColumn get id => text()();
/// }
/// ```
final Type? extending;
/// Customize the data class name for a given table.
/// {@macro drift_custom_data_class}
const DataClassName(this.name, {this.extending});
}
/// An annotation specifying an existing class to be used as a data class.
@Target({TargetKind.classType})
class UseRowClass {
/// The existing class
///
/// This type must refer to an existing class. All other types, like functions
/// or types with arguments, are not allowed.
final Type type;
/// The name of the constructor to use.
///
/// When this option is not set, the default (unnamed) constructor will be
/// used to map database rows to the desired row class.
final String constructor;
/// Generate a `toInsertable()` extension function for [type] mapping all
/// fields to an insertable object.
///
/// This can be useful when a custom data class should be used for inserts or
/// updates.
@Deprecated('Use `write_to_columns_mixins` build option instead')
final bool generateInsertable;
/// Customize the class used by drift to hold an instance of an annotated
/// table.
///
/// For details, see the overall documentation on [UseRowClass].
const UseRowClass(this.type,
{this.constructor = '', this.generateInsertable = false});
}
/// An annotation specifying view properties
@Target({TargetKind.classType})
class DriftView {
/// The sql view name to be used. By default, drift will use the snake_case
/// representation of your class name as the sql view name. For instance, a
/// [View] class named `UserView` will be called `user_view` by
/// default.
final String? name;
/// The name for the data class that will be generated for the view class.
/// The data class is a dart object that will be used to represent a result of
/// the view.
/// {@template drift_custom_data_class}
/// By default, drift will attempt to use the view name followed by "Data"
/// when naming data classes (e.g. a view named "UserView" will generate a
/// data class called "UserViewData").
/// {@endtemplate}
final String? dataClassName;
/// The parent class of generated data class. Class must extends [DataClass]!
final Type? extending;
/// Customize view name and data class name
const DriftView({this.name, this.dataClassName, this.extending});
}