mirror of https://github.com/AMT-Cheif/drift.git
Initial support for generated columns
This commit is contained in:
parent
5f8b74c8df
commit
3469bade62
|
@ -7,6 +7,7 @@
|
||||||
Thanks to [@westito](https://github.com/westito).
|
Thanks to [@westito](https://github.com/westito).
|
||||||
- Allow the generator to emit correct SQL code when using arrays with the
|
- Allow the generator to emit correct SQL code when using arrays with the
|
||||||
`new_sql_code_generation` option in specific scenarios.
|
`new_sql_code_generation` option in specific scenarios.
|
||||||
|
- Add the `generatedAs` method to declare generated columns for Dart tables.
|
||||||
- Improved support for pausing query stream subscriptions. Instead of buffering events,
|
- Improved support for pausing query stream subscriptions. Instead of buffering events,
|
||||||
query streams will suspend fetching data if all listeners are paused.
|
query streams will suspend fetching data if all listeners are paused.
|
||||||
|
|
||||||
|
|
|
@ -62,32 +62,22 @@ typedef BlobColumn = Column<Uint8List?>;
|
||||||
/// A column that stores floating point numeric values.
|
/// A column that stores floating point numeric values.
|
||||||
typedef RealColumn = Column<double?>;
|
typedef RealColumn = Column<double?>;
|
||||||
|
|
||||||
|
class _BaseColumnBuilder<T> {}
|
||||||
|
|
||||||
/// A column builder is used to specify which columns should appear in a table.
|
/// A column builder is used to specify which columns should appear in a table.
|
||||||
/// All of the methods defined in this class and its subclasses are not meant to
|
/// All of the methods defined in this class and its subclasses are not meant to
|
||||||
/// be called at runtime. Instead, the generator will take a look at your
|
/// be called at runtime. Instead, the generator will take a look at your
|
||||||
/// source code (specifically, it will analyze which of the methods you use) to
|
/// source code (specifically, it will analyze which of the methods you use) to
|
||||||
/// figure out the column structure of a table.
|
/// figure out the column structure of a table.
|
||||||
class ColumnBuilder<T> {}
|
class ColumnBuilder<T> extends _BaseColumnBuilder<T> {}
|
||||||
|
|
||||||
|
/// A column builder for virtual, generated columns.
|
||||||
|
///
|
||||||
|
/// This is a different class so that some methods are not available
|
||||||
|
class VirtualColumnBuilder<T> extends _BaseColumnBuilder<T> {}
|
||||||
|
|
||||||
/// DSL extension to define a column inside a drift table.
|
/// DSL extension to define a column inside a drift table.
|
||||||
extension BuildColumn<T> on ColumnBuilder<T> {
|
extension BuildColumn<T> on ColumnBuilder<T> {
|
||||||
/// By default, the field name will be used as the column name, e.g.
|
|
||||||
/// `IntColumn get id = integer()` will have "id" as its associated name.
|
|
||||||
/// Columns made up of multiple words are expected to be in camelCase and will
|
|
||||||
/// be converted to snake_case (e.g. a getter called accountCreationDate will
|
|
||||||
/// result in an SQL column called account_creation_date).
|
|
||||||
/// To change this default behavior, use something like
|
|
||||||
/// `IntColumn get id = integer((c) => c.named('user_id'))`.
|
|
||||||
///
|
|
||||||
/// Note that using [named] __does not__ have an effect on the json key of an
|
|
||||||
/// object. To change the json key, annotate this column getter with
|
|
||||||
/// [JsonKey].
|
|
||||||
ColumnBuilder<T> named(String name) => _isGenerated();
|
|
||||||
|
|
||||||
/// Marks this column as nullable. Nullable columns should not appear in a
|
|
||||||
/// primary key. Columns are non-null by default.
|
|
||||||
ColumnBuilder<T?> nullable() => _isGenerated();
|
|
||||||
|
|
||||||
/// Tells drift to write a custom constraint after this column definition when
|
/// Tells drift to write a custom constraint after this column definition when
|
||||||
/// writing this column, for instance in a CREATE TABLE statement.
|
/// writing this column, for instance in a CREATE TABLE statement.
|
||||||
///
|
///
|
||||||
|
@ -164,45 +154,6 @@ extension BuildColumn<T> on ColumnBuilder<T> {
|
||||||
/// apply the default value.
|
/// apply the default value.
|
||||||
ColumnBuilder<T> clientDefault(T Function() onInsert) => _isGenerated();
|
ColumnBuilder<T> clientDefault(T Function() onInsert) => _isGenerated();
|
||||||
|
|
||||||
/// Uses a custom [converter] to store custom Dart objects in a single column
|
|
||||||
/// and automatically mapping them from and to sql.
|
|
||||||
///
|
|
||||||
/// An example might look like this:
|
|
||||||
/// ```dart
|
|
||||||
/// // this is the custom object with we want to store in a column. It
|
|
||||||
/// // can be as complex as you want it to be
|
|
||||||
/// class MyCustomObject {
|
|
||||||
/// final String data;
|
|
||||||
/// MyCustomObject(this.data);
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// class CustomConverter extends TypeConverter<MyCustomObject, String> {
|
|
||||||
/// // this class is responsible for turning a custom object into a string.
|
|
||||||
/// // this is easy here, but more complex objects could be serialized using
|
|
||||||
/// // json or any other method of your choice.
|
|
||||||
/// const CustomConverter();
|
|
||||||
/// @override
|
|
||||||
/// MyCustomObject mapToDart(String fromDb) {
|
|
||||||
/// return fromDb == null ? null : MyCustomObject(fromDb);
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// @override
|
|
||||||
/// String mapToSql(MyCustomObject value) {
|
|
||||||
/// return value?.data;
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// In that case, you could have a table with this column
|
|
||||||
/// ```dart
|
|
||||||
/// TextColumn get custom => text().map(const CustomConverter())();
|
|
||||||
/// ```
|
|
||||||
/// The generated row class will then use a `MyFancyClass` instead of a
|
|
||||||
/// `String`, which would usually be used for [Table.text] columns.
|
|
||||||
ColumnBuilder<T> map<Dart>(TypeConverter<Dart, T> converter) =>
|
|
||||||
_isGenerated();
|
|
||||||
|
|
||||||
/// Adds a foreign-key reference from this column.
|
/// Adds a foreign-key reference from this column.
|
||||||
///
|
///
|
||||||
/// The [table] type must be a Dart class name defining a drift table.
|
/// The [table] type must be a Dart class name defining a drift table.
|
||||||
|
@ -242,6 +193,104 @@ extension BuildColumn<T> on ColumnBuilder<T> {
|
||||||
_isGenerated();
|
_isGenerated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Declare a generated column.
|
||||||
|
///
|
||||||
|
/// Generated columns are backed by an expression, declared with
|
||||||
|
/// [generatedAs]:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// class Products extends Table {
|
||||||
|
/// TextColumn get name => text()();
|
||||||
|
///
|
||||||
|
/// RealColumn get price => real()();
|
||||||
|
/// RealColumn get discount => real()();
|
||||||
|
/// RealColumn get tax => real()();
|
||||||
|
/// RealColumn get netPrice => real().generatedAs(
|
||||||
|
/// price * (Constant(1) - discount) * (Constant(1) + tax))();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Generated columns can either be `VIRTUAL` (the default) or `STORED`
|
||||||
|
/// (enabled with the [stored] parameter). Stored generated columns are
|
||||||
|
/// computed on each update and are stored in the database. Virtual columns
|
||||||
|
/// consume less space, but are re-computed on each read.
|
||||||
|
///
|
||||||
|
/// Generated columns can't be updated or inserted (neither with the Dart API
|
||||||
|
/// or though SQL queries), so they are not represented in companions.
|
||||||
|
///
|
||||||
|
/// __Important__: When a generated column can be nullable, don't forget to
|
||||||
|
/// call [BuildGeneralColumn.nullable] on it to reflect this in the generated
|
||||||
|
/// code.
|
||||||
|
/// Also, note that generated columns are part of your databases schema and
|
||||||
|
/// cannot be updated easily. When changing the [generatedAs] expression for a
|
||||||
|
/// column, you need to re-generate the table with a [TableMigration].
|
||||||
|
///
|
||||||
|
/// Note that generated columns are only available in sqlite3 version
|
||||||
|
/// `3.31.0`. When using `sqlite3_flutter_libs` or a web database, this is not
|
||||||
|
/// a problem.
|
||||||
|
VirtualColumnBuilder<T> generatedAs(Expression<T?> generatedAs,
|
||||||
|
{bool stored = false}) =>
|
||||||
|
_isGenerated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Column builders available for both virtual and non-virtual columns.
|
||||||
|
extension BuildGeneralColumn<T> on _BaseColumnBuilder<T> {
|
||||||
|
/// By default, the field name will be used as the column name, e.g.
|
||||||
|
/// `IntColumn get id = integer()` will have "id" as its associated name.
|
||||||
|
/// Columns made up of multiple words are expected to be in camelCase and will
|
||||||
|
/// be converted to snake_case (e.g. a getter called accountCreationDate will
|
||||||
|
/// result in an SQL column called account_creation_date).
|
||||||
|
/// To change this default behavior, use something like
|
||||||
|
/// `IntColumn get id = integer((c) => c.named('user_id'))`.
|
||||||
|
///
|
||||||
|
/// Note that using [named] __does not__ have an effect on the json key of an
|
||||||
|
/// object. To change the json key, annotate this column getter with
|
||||||
|
/// [JsonKey].
|
||||||
|
ColumnBuilder<T> named(String name) => _isGenerated();
|
||||||
|
|
||||||
|
/// Marks this column as nullable. Nullable columns should not appear in a
|
||||||
|
/// primary key. Columns are non-null by default.
|
||||||
|
ColumnBuilder<T?> nullable() => _isGenerated();
|
||||||
|
|
||||||
|
/// Uses a custom [converter] to store custom Dart objects in a single column
|
||||||
|
/// and automatically mapping them from and to sql.
|
||||||
|
///
|
||||||
|
/// An example might look like this:
|
||||||
|
/// ```dart
|
||||||
|
/// // this is the custom object with we want to store in a column. It
|
||||||
|
/// // can be as complex as you want it to be
|
||||||
|
/// class MyCustomObject {
|
||||||
|
/// final String data;
|
||||||
|
/// MyCustomObject(this.data);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// class CustomConverter extends TypeConverter<MyCustomObject, String> {
|
||||||
|
/// // this class is responsible for turning a custom object into a string.
|
||||||
|
/// // this is easy here, but more complex objects could be serialized using
|
||||||
|
/// // json or any other method of your choice.
|
||||||
|
/// const CustomConverter();
|
||||||
|
/// @override
|
||||||
|
/// MyCustomObject mapToDart(String fromDb) {
|
||||||
|
/// return fromDb == null ? null : MyCustomObject(fromDb);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// String mapToSql(MyCustomObject value) {
|
||||||
|
/// return value?.data;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In that case, you could have a table with this column
|
||||||
|
/// ```dart
|
||||||
|
/// TextColumn get custom => text().map(const CustomConverter())();
|
||||||
|
/// ```
|
||||||
|
/// The generated row class will then use a `MyFancyClass` instead of a
|
||||||
|
/// `String`, which would usually be used for [Table.text] columns.
|
||||||
|
ColumnBuilder<T> map<Dart>(TypeConverter<Dart, T> converter) =>
|
||||||
|
_isGenerated();
|
||||||
|
|
||||||
/// Turns this column builder into a column. This method won't actually be
|
/// Turns this column builder into a column. This method won't actually be
|
||||||
/// called in your code. Instead, the generator will take a look at your
|
/// called in your code. Instead, the generator will take a look at your
|
||||||
/// source code to figure out your table structure.
|
/// source code to figure out your table structure.
|
||||||
|
|
|
@ -44,6 +44,10 @@ class GeneratedColumn<T> extends Column<T> {
|
||||||
/// The sql type name, such as TEXT for texts.
|
/// The sql type name, such as TEXT for texts.
|
||||||
final String typeName;
|
final String typeName;
|
||||||
|
|
||||||
|
/// If this column is generated (that is, it is a SQL expression of other)
|
||||||
|
/// columns, contains information about how to generate this column.
|
||||||
|
final GeneratedAs? generatedAs;
|
||||||
|
|
||||||
/// Whether a value is required for this column when inserting a new row.
|
/// Whether a value is required for this column when inserting a new row.
|
||||||
final bool requiredDuringInsert;
|
final bool requiredDuringInsert;
|
||||||
|
|
||||||
|
@ -67,6 +71,7 @@ class GeneratedColumn<T> extends Column<T> {
|
||||||
this.defaultValue,
|
this.defaultValue,
|
||||||
this.additionalChecks,
|
this.additionalChecks,
|
||||||
this.requiredDuringInsert = false,
|
this.requiredDuringInsert = false,
|
||||||
|
this.generatedAs,
|
||||||
}) : _defaultConstraints = defaultConstraints;
|
}) : _defaultConstraints = defaultConstraints;
|
||||||
|
|
||||||
/// Applies a type converter to this column.
|
/// Applies a type converter to this column.
|
||||||
|
@ -86,6 +91,7 @@ class GeneratedColumn<T> extends Column<T> {
|
||||||
defaultValue,
|
defaultValue,
|
||||||
additionalChecks,
|
additionalChecks,
|
||||||
requiredDuringInsert,
|
requiredDuringInsert,
|
||||||
|
generatedAs,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +117,15 @@ class GeneratedColumn<T> extends Column<T> {
|
||||||
if (writeBrackets) into.buffer.write(')');
|
if (writeBrackets) into.buffer.write(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final generated = generatedAs;
|
||||||
|
if (generated != null) {
|
||||||
|
into.buffer.write(' GENERATED ALWAYS AS (');
|
||||||
|
generated.generatedAs.writeInto(into);
|
||||||
|
into.buffer
|
||||||
|
..write(') ')
|
||||||
|
..write(generated.stored ? 'STORED' : 'VIRTUAL');
|
||||||
|
}
|
||||||
|
|
||||||
// these custom constraints refer to builtin constraints from drift
|
// these custom constraints refer to builtin constraints from drift
|
||||||
if (_defaultConstraints != null) {
|
if (_defaultConstraints != null) {
|
||||||
into.buffer
|
into.buffer
|
||||||
|
@ -221,6 +236,7 @@ class GeneratedColumnWithTypeConverter<D, S> extends GeneratedColumn<S> {
|
||||||
Expression<S>? defaultValue,
|
Expression<S>? defaultValue,
|
||||||
VerificationResult Function(S, VerificationMeta)? additionalChecks,
|
VerificationResult Function(S, VerificationMeta)? additionalChecks,
|
||||||
bool requiredDuringInsert,
|
bool requiredDuringInsert,
|
||||||
|
GeneratedAs? generatedAs,
|
||||||
) : super(
|
) : super(
|
||||||
name,
|
name,
|
||||||
tableName,
|
tableName,
|
||||||
|
@ -232,6 +248,7 @@ class GeneratedColumnWithTypeConverter<D, S> extends GeneratedColumn<S> {
|
||||||
defaultValue: defaultValue,
|
defaultValue: defaultValue,
|
||||||
additionalChecks: additionalChecks,
|
additionalChecks: additionalChecks,
|
||||||
requiredDuringInsert: requiredDuringInsert,
|
requiredDuringInsert: requiredDuringInsert,
|
||||||
|
generatedAs: generatedAs,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Compares this column against the mapped [dartValue].
|
/// Compares this column against the mapped [dartValue].
|
||||||
|
@ -241,3 +258,17 @@ class GeneratedColumnWithTypeConverter<D, S> extends GeneratedColumn<S> {
|
||||||
return equals(converter.mapToSql(dartValue) as S);
|
return equals(converter.mapToSql(dartValue) as S);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information filled out by the generator to support generated or virtual
|
||||||
|
/// columns.
|
||||||
|
class GeneratedAs {
|
||||||
|
/// The expression that this column evaluates to.
|
||||||
|
final Expression generatedAs;
|
||||||
|
|
||||||
|
/// Wehter this column is stored in the database, as opposed to being
|
||||||
|
/// `VIRTUAL` and evaluated on each read.
|
||||||
|
final bool stored;
|
||||||
|
|
||||||
|
/// Creates a [GeneratedAs] clause.
|
||||||
|
GeneratedAs(this.generatedAs, this.stored);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: drift
|
name: drift
|
||||||
description: Drift is a reactive library to store relational data in Dart and Flutter applications.
|
description: Drift is a reactive library to store relational data in Dart and Flutter applications.
|
||||||
version: 1.0.0
|
version: 1.1.0-dev
|
||||||
repository: https://github.com/simolus3/moor
|
repository: https://github.com/simolus3/moor
|
||||||
homepage: https://drift.simonbinder.eu/
|
homepage: https://drift.simonbinder.eu/
|
||||||
issue_tracker: https://github.com/simolus3/moor/issues
|
issue_tracker: https://github.com/simolus3/moor/issues
|
||||||
|
|
|
@ -38,6 +38,9 @@ class Categories extends Table with AutoIncrement {
|
||||||
text().named('desc').customConstraint('NOT NULL UNIQUE')();
|
text().named('desc').customConstraint('NOT NULL UNIQUE')();
|
||||||
IntColumn get priority =>
|
IntColumn get priority =>
|
||||||
intEnum<CategoryPriority>().withDefault(const Constant(0))();
|
intEnum<CategoryPriority>().withDefault(const Constant(0))();
|
||||||
|
|
||||||
|
TextColumn get descriptionInUpperCase =>
|
||||||
|
text().generatedAs(description.upper())();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CategoryPriority { low, medium, high }
|
enum CategoryPriority { low, medium, high }
|
||||||
|
|
|
@ -11,8 +11,12 @@ class Category extends DataClass implements Insertable<Category> {
|
||||||
final int id;
|
final int id;
|
||||||
final String description;
|
final String description;
|
||||||
final CategoryPriority priority;
|
final CategoryPriority priority;
|
||||||
|
final String descriptionInUpperCase;
|
||||||
Category(
|
Category(
|
||||||
{required this.id, required this.description, required this.priority});
|
{required this.id,
|
||||||
|
required this.description,
|
||||||
|
required this.priority,
|
||||||
|
required this.descriptionInUpperCase});
|
||||||
factory Category.fromData(Map<String, dynamic> data, {String? prefix}) {
|
factory Category.fromData(Map<String, dynamic> data, {String? prefix}) {
|
||||||
final effectivePrefix = prefix ?? '';
|
final effectivePrefix = prefix ?? '';
|
||||||
return Category(
|
return Category(
|
||||||
|
@ -22,6 +26,8 @@ class Category extends DataClass implements Insertable<Category> {
|
||||||
.mapFromDatabaseResponse(data['${effectivePrefix}desc'])!,
|
.mapFromDatabaseResponse(data['${effectivePrefix}desc'])!,
|
||||||
priority: $CategoriesTable.$converter0.mapToDart(const IntType()
|
priority: $CategoriesTable.$converter0.mapToDart(const IntType()
|
||||||
.mapFromDatabaseResponse(data['${effectivePrefix}priority']))!,
|
.mapFromDatabaseResponse(data['${effectivePrefix}priority']))!,
|
||||||
|
descriptionInUpperCase: const StringType().mapFromDatabaseResponse(
|
||||||
|
data['${effectivePrefix}description_in_upper_case'])!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
|
@ -51,6 +57,8 @@ class Category extends DataClass implements Insertable<Category> {
|
||||||
id: serializer.fromJson<int>(json['id']),
|
id: serializer.fromJson<int>(json['id']),
|
||||||
description: serializer.fromJson<String>(json['description']),
|
description: serializer.fromJson<String>(json['description']),
|
||||||
priority: serializer.fromJson<CategoryPriority>(json['priority']),
|
priority: serializer.fromJson<CategoryPriority>(json['priority']),
|
||||||
|
descriptionInUpperCase:
|
||||||
|
serializer.fromJson<String>(json['descriptionInUpperCase']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
factory Category.fromJsonString(String encodedJson,
|
factory Category.fromJsonString(String encodedJson,
|
||||||
|
@ -65,35 +73,45 @@ class Category extends DataClass implements Insertable<Category> {
|
||||||
'id': serializer.toJson<int>(id),
|
'id': serializer.toJson<int>(id),
|
||||||
'description': serializer.toJson<String>(description),
|
'description': serializer.toJson<String>(description),
|
||||||
'priority': serializer.toJson<CategoryPriority>(priority),
|
'priority': serializer.toJson<CategoryPriority>(priority),
|
||||||
|
'descriptionInUpperCase':
|
||||||
|
serializer.toJson<String>(descriptionInUpperCase),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Category copyWith(
|
Category copyWith(
|
||||||
{int? id, String? description, CategoryPriority? priority}) =>
|
{int? id,
|
||||||
|
String? description,
|
||||||
|
CategoryPriority? priority,
|
||||||
|
String? descriptionInUpperCase}) =>
|
||||||
Category(
|
Category(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
description: description ?? this.description,
|
description: description ?? this.description,
|
||||||
priority: priority ?? this.priority,
|
priority: priority ?? this.priority,
|
||||||
|
descriptionInUpperCase:
|
||||||
|
descriptionInUpperCase ?? this.descriptionInUpperCase,
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('Category(')
|
return (StringBuffer('Category(')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('description: $description, ')
|
..write('description: $description, ')
|
||||||
..write('priority: $priority')
|
..write('priority: $priority, ')
|
||||||
|
..write('descriptionInUpperCase: $descriptionInUpperCase')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(id, description, priority);
|
int get hashCode =>
|
||||||
|
Object.hash(id, description, priority, descriptionInUpperCase);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is Category &&
|
(other is Category &&
|
||||||
other.id == this.id &&
|
other.id == this.id &&
|
||||||
other.description == this.description &&
|
other.description == this.description &&
|
||||||
other.priority == this.priority);
|
other.priority == this.priority &&
|
||||||
|
other.descriptionInUpperCase == this.descriptionInUpperCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CategoriesCompanion extends UpdateCompanion<Category> {
|
class CategoriesCompanion extends UpdateCompanion<Category> {
|
||||||
|
@ -185,8 +203,16 @@ class $CategoriesTable extends Categories
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultValue: const Constant(0))
|
defaultValue: const Constant(0))
|
||||||
.withConverter<CategoryPriority>($CategoriesTable.$converter0);
|
.withConverter<CategoryPriority>($CategoriesTable.$converter0);
|
||||||
|
final VerificationMeta _descriptionInUpperCaseMeta =
|
||||||
|
const VerificationMeta('descriptionInUpperCase');
|
||||||
|
late final GeneratedColumn<String?> descriptionInUpperCase =
|
||||||
|
GeneratedColumn<String?>('description_in_upper_case', aliasedName, false,
|
||||||
|
typeName: 'TEXT',
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
generatedAs: GeneratedAs(description.upper(), false));
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [id, description, priority];
|
List<GeneratedColumn> get $columns =>
|
||||||
|
[id, description, priority, descriptionInUpperCase];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? 'categories';
|
String get aliasedName => _alias ?? 'categories';
|
||||||
@override
|
@override
|
||||||
|
@ -206,6 +232,12 @@ class $CategoriesTable extends Categories
|
||||||
context.missing(_descriptionMeta);
|
context.missing(_descriptionMeta);
|
||||||
}
|
}
|
||||||
context.handle(_priorityMeta, const VerificationResult.success());
|
context.handle(_priorityMeta, const VerificationResult.success());
|
||||||
|
if (data.containsKey('description_in_upper_case')) {
|
||||||
|
context.handle(
|
||||||
|
_descriptionInUpperCaseMeta,
|
||||||
|
descriptionInUpperCase.isAcceptableOrUnknown(
|
||||||
|
data['description_in_upper_case']!, _descriptionInUpperCaseMeta));
|
||||||
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ void main() {
|
||||||
id: 3,
|
id: 3,
|
||||||
description: 'description',
|
description: 'description',
|
||||||
priority: CategoryPriority.low,
|
priority: CategoryPriority.low,
|
||||||
|
descriptionInUpperCase: 'ignored',
|
||||||
);
|
);
|
||||||
final companion = entry.toCompanion(false);
|
final companion = entry.toCompanion(false);
|
||||||
|
|
||||||
|
|
|
@ -386,6 +386,7 @@ void main() {
|
||||||
{
|
{
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'desc': 'description',
|
'desc': 'description',
|
||||||
|
'description_in_upper_case': 'DESCRIPTION',
|
||||||
'priority': 1,
|
'priority': 1,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -107,6 +107,7 @@ void main() {
|
||||||
id: 1,
|
id: 1,
|
||||||
description: 'Description',
|
description: 'Description',
|
||||||
priority: CategoryPriority.low,
|
priority: CategoryPriority.low,
|
||||||
|
descriptionInUpperCase: 'DESCRIPTION',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}, skip: ifOlderThanSqlite335());
|
}, skip: ifOlderThanSqlite335());
|
||||||
|
|
|
@ -26,7 +26,8 @@ void main() {
|
||||||
'SELECT t.id AS "t.id", t.title AS "t.title", '
|
'SELECT t.id AS "t.id", t.title AS "t.title", '
|
||||||
't.content AS "t.content", t.target_date AS "t.target_date", '
|
't.content AS "t.content", t.target_date AS "t.target_date", '
|
||||||
't.category AS "t.category", c.id AS "c.id", c."desc" AS "c.desc", '
|
't.category AS "t.category", c.id AS "c.id", c."desc" AS "c.desc", '
|
||||||
'c.priority AS "c.priority" '
|
'c.priority AS "c.priority", '
|
||||||
|
'c.description_in_upper_case AS "c.description_in_upper_case" '
|
||||||
'FROM todos t LEFT OUTER JOIN categories c ON c.id = t.category;',
|
'FROM todos t LEFT OUTER JOIN categories c ON c.id = t.category;',
|
||||||
argThat(isEmpty)));
|
argThat(isEmpty)));
|
||||||
});
|
});
|
||||||
|
@ -46,6 +47,7 @@ void main() {
|
||||||
't.category': 3,
|
't.category': 3,
|
||||||
'c.id': 3,
|
'c.id': 3,
|
||||||
'c.desc': 'description',
|
'c.desc': 'description',
|
||||||
|
'c.description_in_upper_case': 'DESCRIPTION',
|
||||||
'c.priority': 2,
|
'c.priority': 2,
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
@ -74,6 +76,7 @@ void main() {
|
||||||
id: 3,
|
id: 3,
|
||||||
description: 'description',
|
description: 'description',
|
||||||
priority: CategoryPriority.high,
|
priority: CategoryPriority.high,
|
||||||
|
descriptionInUpperCase: 'DESCRIPTION',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -198,15 +201,22 @@ void main() {
|
||||||
|
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
||||||
return [
|
return [
|
||||||
{'c.id': 3, 'c.desc': 'Description', 'c.priority': 1, 'c3': 11}
|
{
|
||||||
|
'c.id': 3,
|
||||||
|
'c.desc': 'Description',
|
||||||
|
'c.description_in_upper_case': 'DESCRIPTION',
|
||||||
|
'c.priority': 1,
|
||||||
|
'c4': 11
|
||||||
|
}
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
final result = await query.getSingle();
|
final result = await query.getSingle();
|
||||||
|
|
||||||
verify(executor.runSelect(
|
verify(executor.runSelect(
|
||||||
'SELECT c.id AS "c.id", c."desc" AS "c.desc", c.priority AS "c.priority"'
|
'SELECT c.id AS "c.id", c."desc" AS "c.desc", '
|
||||||
', LENGTH(c."desc") AS "c3" '
|
'c.priority AS "c.priority", c.description_in_upper_case AS '
|
||||||
|
'"c.description_in_upper_case", LENGTH(c."desc") AS "c4" '
|
||||||
'FROM categories c;',
|
'FROM categories c;',
|
||||||
[],
|
[],
|
||||||
));
|
));
|
||||||
|
@ -217,6 +227,7 @@ void main() {
|
||||||
Category(
|
Category(
|
||||||
id: 3,
|
id: 3,
|
||||||
description: 'Description',
|
description: 'Description',
|
||||||
|
descriptionInUpperCase: 'DESCRIPTION',
|
||||||
priority: CategoryPriority.medium,
|
priority: CategoryPriority.medium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -239,7 +250,13 @@ void main() {
|
||||||
|
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
||||||
return [
|
return [
|
||||||
{'c.id': 3, 'c.desc': 'Description', 'c.priority': 1, 'c3': 11}
|
{
|
||||||
|
'c.id': 3,
|
||||||
|
'c.desc': 'Description',
|
||||||
|
'c.description_in_upper_case': 'DESCRIPTION',
|
||||||
|
'c.priority': 1,
|
||||||
|
'c4': 11,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -247,7 +264,8 @@ void main() {
|
||||||
|
|
||||||
verify(executor.runSelect(
|
verify(executor.runSelect(
|
||||||
'SELECT c.id AS "c.id", c."desc" AS "c.desc", c.priority AS "c.priority"'
|
'SELECT c.id AS "c.id", c."desc" AS "c.desc", c.priority AS "c.priority"'
|
||||||
', LENGTH(c."desc") AS "c3" '
|
', c.description_in_upper_case AS "c.description_in_upper_case", '
|
||||||
|
'LENGTH(c."desc") AS "c4" '
|
||||||
'FROM categories c '
|
'FROM categories c '
|
||||||
'INNER JOIN todos t ON c.id = t.category;',
|
'INNER JOIN todos t ON c.id = t.category;',
|
||||||
[],
|
[],
|
||||||
|
@ -259,6 +277,7 @@ void main() {
|
||||||
Category(
|
Category(
|
||||||
id: 3,
|
id: 3,
|
||||||
description: 'Description',
|
description: 'Description',
|
||||||
|
descriptionInUpperCase: 'DESCRIPTION',
|
||||||
priority: CategoryPriority.medium,
|
priority: CategoryPriority.medium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -287,7 +306,13 @@ void main() {
|
||||||
|
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
||||||
return [
|
return [
|
||||||
{'c.id': 3, 'c.desc': 'desc', 'c.priority': 0, 'c3': 10}
|
{
|
||||||
|
'c.id': 3,
|
||||||
|
'c.desc': 'desc',
|
||||||
|
'c.priority': 0,
|
||||||
|
'c4': 10,
|
||||||
|
'c.description_in_upper_case': 'DESC',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -295,7 +320,9 @@ void main() {
|
||||||
|
|
||||||
verify(executor.runSelect(
|
verify(executor.runSelect(
|
||||||
'SELECT c.id AS "c.id", c."desc" AS "c.desc", '
|
'SELECT c.id AS "c.id", c."desc" AS "c.desc", '
|
||||||
'c.priority AS "c.priority", COUNT(t.id) AS "c3" '
|
'c.priority AS "c.priority", '
|
||||||
|
'c.description_in_upper_case AS "c.description_in_upper_case", '
|
||||||
|
'COUNT(t.id) AS "c4" '
|
||||||
'FROM categories c INNER JOIN todos t ON t.category = c.id '
|
'FROM categories c INNER JOIN todos t ON t.category = c.id '
|
||||||
'GROUP BY c.id HAVING COUNT(t.id) >= ?;',
|
'GROUP BY c.id HAVING COUNT(t.id) >= ?;',
|
||||||
[10]));
|
[10]));
|
||||||
|
@ -306,6 +333,7 @@ void main() {
|
||||||
Category(
|
Category(
|
||||||
id: 3,
|
id: 3,
|
||||||
description: 'desc',
|
description: 'desc',
|
||||||
|
descriptionInUpperCase: 'DESC',
|
||||||
priority: CategoryPriority.low,
|
priority: CategoryPriority.low,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,7 +30,10 @@ void main() {
|
||||||
'CREATE TABLE IF NOT EXISTS categories '
|
'CREATE TABLE IF NOT EXISTS categories '
|
||||||
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
||||||
'"desc" TEXT NOT NULL UNIQUE, '
|
'"desc" TEXT NOT NULL UNIQUE, '
|
||||||
'priority INTEGER NOT NULL DEFAULT 0);',
|
'priority INTEGER NOT NULL DEFAULT 0, '
|
||||||
|
'description_in_upper_case TEXT NOT NULL GENERATED ALWAYS AS '
|
||||||
|
'(UPPER("desc")) VIRTUAL'
|
||||||
|
');',
|
||||||
[]));
|
[]));
|
||||||
|
|
||||||
verify(mockExecutor.runCustom(
|
verify(mockExecutor.runCustom(
|
||||||
|
|
|
@ -175,6 +175,7 @@ void main() {
|
||||||
{
|
{
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'desc': 'description',
|
'desc': 'description',
|
||||||
|
'description_in_upper_case': 'DESCRIPTION',
|
||||||
'priority': 2,
|
'priority': 2,
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
@ -187,6 +188,7 @@ void main() {
|
||||||
Category(
|
Category(
|
||||||
id: 1,
|
id: 1,
|
||||||
description: 'description',
|
description: 'description',
|
||||||
|
descriptionInUpperCase: 'DESCRIPTION',
|
||||||
priority: CategoryPriority.high,
|
priority: CategoryPriority.high,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -88,7 +88,12 @@ void main() {
|
||||||
await first.first; // subscribe to first stream, then drop subscription
|
await first.first; // subscribe to first stream, then drop subscription
|
||||||
|
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) => Future.value([
|
when(executor.runSelect(any, any)).thenAnswer((_) => Future.value([
|
||||||
{'id': 1, 'desc': 'd', 'priority': 0}
|
{
|
||||||
|
'id': 1,
|
||||||
|
'desc': 'd',
|
||||||
|
'description_in_upper_case': 'D',
|
||||||
|
'priority': 0,
|
||||||
|
}
|
||||||
]));
|
]));
|
||||||
await db
|
await db
|
||||||
.into(db.categories)
|
.into(db.categories)
|
||||||
|
@ -104,7 +109,12 @@ void main() {
|
||||||
final subscription = first.listen((_) {});
|
final subscription = first.listen((_) {});
|
||||||
|
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) => Future.value([
|
when(executor.runSelect(any, any)).thenAnswer((_) => Future.value([
|
||||||
{'id': 1, 'desc': 'd', 'priority': 0}
|
{
|
||||||
|
'id': 1,
|
||||||
|
'desc': 'd',
|
||||||
|
'description_in_upper_case': 'D',
|
||||||
|
'priority': 0,
|
||||||
|
}
|
||||||
]));
|
]));
|
||||||
await db
|
await db
|
||||||
.into(db.categories)
|
.into(db.categories)
|
||||||
|
@ -246,7 +256,12 @@ void main() {
|
||||||
|
|
||||||
// Return a new row on the next select
|
// Return a new row on the next select
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) => Future.value([
|
when(executor.runSelect(any, any)).thenAnswer((_) => Future.value([
|
||||||
{'id': 1, 'desc': 'd', 'priority': 0}
|
{
|
||||||
|
'id': 1,
|
||||||
|
'desc': 'd',
|
||||||
|
'description_in_upper_case': 'D',
|
||||||
|
'priority': 0,
|
||||||
|
}
|
||||||
]));
|
]));
|
||||||
db.markTablesUpdated([db.categories]);
|
db.markTablesUpdated([db.categories]);
|
||||||
await pumpEventQueue();
|
await pumpEventQueue();
|
||||||
|
|
|
@ -35,20 +35,15 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can convert a companion to a row class', () {
|
test('can convert a companion to a row class', () {
|
||||||
const companion = CategoriesCompanion(
|
const companion = SharedTodosCompanion(
|
||||||
id: Value(3),
|
todo: Value(3),
|
||||||
description: Value('description'),
|
user: Value(4),
|
||||||
priority: Value(CategoryPriority.low),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final user = db.categories.mapFromCompanion(companion);
|
final user = db.sharedTodos.mapFromCompanion(companion);
|
||||||
expect(
|
expect(
|
||||||
user,
|
user,
|
||||||
Category(
|
SharedTodo(todo: 3, user: 4),
|
||||||
id: 3,
|
|
||||||
description: 'description',
|
|
||||||
priority: CategoryPriority.low,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ const String _methodCustomConstraint = 'customConstraint';
|
||||||
const String _methodDefault = 'withDefault';
|
const String _methodDefault = 'withDefault';
|
||||||
const String _methodClientDefault = 'clientDefault';
|
const String _methodClientDefault = 'clientDefault';
|
||||||
const String _methodMap = 'map';
|
const String _methodMap = 'map';
|
||||||
|
const String _methodGenerated = 'generatedAs';
|
||||||
|
|
||||||
const String _errorMessage = 'This getter does not create a valid column that '
|
const String _errorMessage = 'This getter does not create a valid column that '
|
||||||
'can be parsed by moor. Please refer to the readme from moor to see how '
|
'can be parsed by moor. Please refer to the readme from moor to see how '
|
||||||
|
@ -70,6 +71,7 @@ class ColumnParser {
|
||||||
Expression? clientDefaultExpression;
|
Expression? clientDefaultExpression;
|
||||||
Expression? createdTypeConverter;
|
Expression? createdTypeConverter;
|
||||||
DartType? typeConverterRuntime;
|
DartType? typeConverterRuntime;
|
||||||
|
ColumnGeneratedAs? generatedAs;
|
||||||
var nullable = false;
|
var nullable = false;
|
||||||
|
|
||||||
final foundFeatures = <ColumnFeature>[];
|
final foundFeatures = <ColumnFeature>[];
|
||||||
|
@ -249,6 +251,32 @@ class ColumnParser {
|
||||||
createdTypeConverter = expression;
|
createdTypeConverter = expression;
|
||||||
typeConverterRuntime = type;
|
typeConverterRuntime = type;
|
||||||
break;
|
break;
|
||||||
|
case _methodGenerated:
|
||||||
|
Expression? generatedExpression;
|
||||||
|
var stored = false;
|
||||||
|
|
||||||
|
for (final expr in remainingExpr.argumentList.arguments) {
|
||||||
|
if (expr is NamedExpression && expr.name.label.name == 'stored') {
|
||||||
|
final storedValue = expr.expression;
|
||||||
|
if (storedValue is BooleanLiteral) {
|
||||||
|
stored = storedValue.value;
|
||||||
|
} else {
|
||||||
|
base.step.reportError(ErrorInDartCode(
|
||||||
|
message: 'Must be a boolean literal',
|
||||||
|
affectedNode: expr,
|
||||||
|
affectedElement: element,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generatedExpression = expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generatedExpression != null) {
|
||||||
|
final code = element.source!.contents.data
|
||||||
|
.substring(generatedExpression.offset, generatedExpression.end);
|
||||||
|
generatedAs = ColumnGeneratedAs(code, stored);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're not at a starting method yet, so we need to go deeper!
|
// We're not at a starting method yet, so we need to go deeper!
|
||||||
|
@ -322,10 +350,11 @@ class ColumnParser {
|
||||||
typeConverter: converter,
|
typeConverter: converter,
|
||||||
declaration: DartColumnDeclaration(element, base.step.file),
|
declaration: DartColumnDeclaration(element, base.step.file),
|
||||||
documentationComment: docString,
|
documentationComment: docString,
|
||||||
|
generatedAs: generatedAs,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnType _startMethodToColumnType(String startMethod) {
|
ColumnType _startMethodToColumnType(String name) {
|
||||||
return const {
|
return const {
|
||||||
startBool: ColumnType.boolean,
|
startBool: ColumnType.boolean,
|
||||||
startString: ColumnType.text,
|
startString: ColumnType.text,
|
||||||
|
@ -334,7 +363,7 @@ class ColumnParser {
|
||||||
startDateTime: ColumnType.datetime,
|
startDateTime: ColumnType.datetime,
|
||||||
startBlob: ColumnType.blob,
|
startBlob: ColumnType.blob,
|
||||||
startReal: ColumnType.real,
|
startReal: ColumnType.real,
|
||||||
}[startMethod]!;
|
}[name]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _readJsonKey(Element getter) {
|
String? _readJsonKey(Element getter) {
|
||||||
|
|
|
@ -55,6 +55,7 @@ class CreateTableReader {
|
||||||
UsedTypeConverter? converter;
|
UsedTypeConverter? converter;
|
||||||
String? defaultValue;
|
String? defaultValue;
|
||||||
String? overriddenJsonKey;
|
String? overriddenJsonKey;
|
||||||
|
ColumnGeneratedAs? generatedAs;
|
||||||
|
|
||||||
final typeName = column.definition?.typeName;
|
final typeName = column.definition?.typeName;
|
||||||
|
|
||||||
|
@ -158,6 +159,7 @@ class CreateTableReader {
|
||||||
typeConverter: converter,
|
typeConverter: converter,
|
||||||
overriddenJsonName: overriddenJsonKey,
|
overriddenJsonName: overriddenJsonKey,
|
||||||
declaration: declaration,
|
declaration: declaration,
|
||||||
|
generatedAs: generatedAs,
|
||||||
);
|
);
|
||||||
|
|
||||||
foundColumns[column.name] = parsed;
|
foundColumns[column.name] = parsed;
|
||||||
|
|
|
@ -106,6 +106,10 @@ class MoorColumn implements HasDeclaration, HasType {
|
||||||
/// Stored as a multi line string with leading triple-slashes `///` for every line
|
/// Stored as a multi line string with leading triple-slashes `///` for every line
|
||||||
final String? documentationComment;
|
final String? documentationComment;
|
||||||
|
|
||||||
|
final ColumnGeneratedAs? generatedAs;
|
||||||
|
|
||||||
|
bool get isGenerated => generatedAs != null;
|
||||||
|
|
||||||
/// The column type from the dsl library. For instance, if a table has
|
/// The column type from the dsl library. For instance, if a table has
|
||||||
/// declared an `IntColumn`, the matching dsl column name would also be an
|
/// declared an `IntColumn`, the matching dsl column name would also be an
|
||||||
/// `IntColumn`.
|
/// `IntColumn`.
|
||||||
|
@ -180,6 +184,7 @@ class MoorColumn implements HasDeclaration, HasType {
|
||||||
this.typeConverter,
|
this.typeConverter,
|
||||||
this.declaration,
|
this.declaration,
|
||||||
this.documentationComment,
|
this.documentationComment,
|
||||||
|
this.generatedAs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,3 +261,10 @@ class ResolvedDartForeignKeyReference extends ColumnFeature {
|
||||||
ResolvedDartForeignKeyReference(
|
ResolvedDartForeignKeyReference(
|
||||||
this.otherTable, this.otherColumn, this.onUpdate, this.onDelete);
|
this.otherTable, this.otherColumn, this.onUpdate, this.onDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ColumnGeneratedAs {
|
||||||
|
final String? dartExpression;
|
||||||
|
final bool stored;
|
||||||
|
|
||||||
|
ColumnGeneratedAs(this.dartExpression, this.stored);
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ class DartColumnDeclaration implements DartDeclaration, ColumnDeclaration {
|
||||||
@override
|
@override
|
||||||
final SourceRange declaration;
|
final SourceRange declaration;
|
||||||
|
|
||||||
/// In the Dart api, columns declared via getters.
|
/// In the Dart api, columns are declared via getters.
|
||||||
@override
|
@override
|
||||||
final Element element;
|
final Element element;
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,8 @@ class MoorTable extends MoorEntityWithResultSet {
|
||||||
|
|
||||||
if (column.defaultArgument != null ||
|
if (column.defaultArgument != null ||
|
||||||
column.clientDefaultCode != null ||
|
column.clientDefaultCode != null ||
|
||||||
column.nullable) {
|
column.nullable ||
|
||||||
|
column.isGenerated) {
|
||||||
// default value would be applied, so it's not required for inserts
|
// default value would be applied, so it's not required for inserts
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,6 +211,9 @@ class DataClassWriter {
|
||||||
..write('final map = <String, Expression> {};');
|
..write('final map = <String, Expression> {};');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in table.columns) {
|
||||||
|
// Generated column - cannot be used for inserts or updates
|
||||||
|
if (column.isGenerated) continue;
|
||||||
|
|
||||||
// We include all columns that are not null. If nullToAbsent is false, we
|
// We include all columns that are not null. If nullToAbsent is false, we
|
||||||
// also include null columns. When generating NNBD code, we can include
|
// also include null columns. When generating NNBD code, we can include
|
||||||
// non-nullable columns without an additional null check.
|
// non-nullable columns without an additional null check.
|
||||||
|
@ -266,6 +269,9 @@ class DataClassWriter {
|
||||||
..write('(');
|
..write('(');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in table.columns) {
|
||||||
|
// Generated columns are not parts of companions.
|
||||||
|
if (column.isGenerated) continue;
|
||||||
|
|
||||||
final dartName = column.dartGetterName;
|
final dartName = column.dartGetterName;
|
||||||
_buffer
|
_buffer
|
||||||
..write(dartName)
|
..write(dartName)
|
||||||
|
|
|
@ -97,6 +97,16 @@ abstract class TableOrViewWriter {
|
||||||
additionalParams['clientDefault'] = column.clientDefaultCode!;
|
additionalParams['clientDefault'] = column.clientDefaultCode!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (column.generatedAs != null) {
|
||||||
|
final generateAs = column.generatedAs!;
|
||||||
|
final code = generateAs.dartExpression;
|
||||||
|
|
||||||
|
if (code != null) {
|
||||||
|
additionalParams['generatedAs'] =
|
||||||
|
'GeneratedAs($code, ${generateAs.stored})';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final innerType = column.innerColumnType(options);
|
final innerType = column.innerColumnType(options);
|
||||||
var type = 'GeneratedColumn<$innerType>';
|
var type = 'GeneratedColumn<$innerType>';
|
||||||
expressionBuffer
|
expressionBuffer
|
||||||
|
|
|
@ -10,6 +10,11 @@ class UpdateCompanionWriter {
|
||||||
|
|
||||||
late StringBuffer _buffer;
|
late StringBuffer _buffer;
|
||||||
|
|
||||||
|
late final List<MoorColumn> columns = [
|
||||||
|
for (final column in table.columns)
|
||||||
|
if (!column.isGenerated) column,
|
||||||
|
];
|
||||||
|
|
||||||
UpdateCompanionWriter(this.table, this.scope) {
|
UpdateCompanionWriter(this.table, this.scope) {
|
||||||
_buffer = scope.leaf();
|
_buffer = scope.leaf();
|
||||||
}
|
}
|
||||||
|
@ -36,7 +41,7 @@ class UpdateCompanionWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _writeFields() {
|
void _writeFields() {
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
final modifier = scope.options.fieldModifier;
|
final modifier = scope.options.fieldModifier;
|
||||||
final type = column.dartTypeCode(scope.generationOptions);
|
final type = column.dartTypeCode(scope.generationOptions);
|
||||||
_buffer.write('$modifier Value<$type> ${column.dartGetterName};\n');
|
_buffer.write('$modifier Value<$type> ${column.dartGetterName};\n');
|
||||||
|
@ -49,7 +54,7 @@ class UpdateCompanionWriter {
|
||||||
}
|
}
|
||||||
_buffer.write('${table.getNameForCompanionClass(scope.options)}({');
|
_buffer.write('${table.getNameForCompanionClass(scope.options)}({');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
_buffer.write('this.${column.dartGetterName} = const Value.absent(),');
|
_buffer.write('this.${column.dartGetterName} = const Value.absent(),');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +78,7 @@ class UpdateCompanionWriter {
|
||||||
// @required String b}): a = Value(a), b = Value(b);
|
// @required String b}): a = Value(a), b = Value(b);
|
||||||
// We don't need to use this. for the initializers, Dart figures that out.
|
// We don't need to use this. for the initializers, Dart figures that out.
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
final param = column.dartGetterName;
|
final param = column.dartGetterName;
|
||||||
|
|
||||||
if (table.isColumnRequiredForInsert(column)) {
|
if (table.isColumnRequiredForInsert(column)) {
|
||||||
|
@ -106,18 +111,17 @@ class UpdateCompanionWriter {
|
||||||
void _writeCustomConstructor() {
|
void _writeCustomConstructor() {
|
||||||
// Prefer a .custom constructor, unless there already is a field called
|
// Prefer a .custom constructor, unless there already is a field called
|
||||||
// "custom", in which case we'll use createCustom
|
// "custom", in which case we'll use createCustom
|
||||||
final constructorName = table.columns
|
final constructorName =
|
||||||
.map((e) => e.dartGetterName)
|
columns.map((e) => e.dartGetterName).any((name) => name == 'custom')
|
||||||
.any((name) => name == 'custom')
|
? 'createCustom'
|
||||||
? 'createCustom'
|
: 'custom';
|
||||||
: 'custom';
|
|
||||||
|
|
||||||
final dartTypeName = table.dartTypeCode(scope.generationOptions);
|
final dartTypeName = table.dartTypeCode(scope.generationOptions);
|
||||||
_buffer
|
_buffer
|
||||||
..write('static Insertable<$dartTypeName> $constructorName')
|
..write('static Insertable<$dartTypeName> $constructorName')
|
||||||
..write('({');
|
..write('({');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
// todo (breaking change): This should not consider type converters.
|
// todo (breaking change): This should not consider type converters.
|
||||||
final typeName = column.dartTypeCode(scope.generationOptions);
|
final typeName = column.dartTypeCode(scope.generationOptions);
|
||||||
final type = scope.nullableType('Expression<$typeName>');
|
final type = scope.nullableType('Expression<$typeName>');
|
||||||
|
@ -128,7 +132,7 @@ class UpdateCompanionWriter {
|
||||||
..write('}) {\n')
|
..write('}) {\n')
|
||||||
..write('return RawValuesInsertable({');
|
..write('return RawValuesInsertable({');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
_buffer
|
_buffer
|
||||||
..write('if (${column.dartGetterName} != null)')
|
..write('if (${column.dartGetterName} != null)')
|
||||||
..write(asDartLiteral(column.name.name))
|
..write(asDartLiteral(column.name.name))
|
||||||
|
@ -143,7 +147,7 @@ class UpdateCompanionWriter {
|
||||||
..write(table.getNameForCompanionClass(scope.options))
|
..write(table.getNameForCompanionClass(scope.options))
|
||||||
..write(' copyWith({');
|
..write(' copyWith({');
|
||||||
var first = true;
|
var first = true;
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
_buffer.write(', ');
|
_buffer.write(', ');
|
||||||
}
|
}
|
||||||
|
@ -157,7 +161,7 @@ class UpdateCompanionWriter {
|
||||||
_buffer
|
_buffer
|
||||||
..write('}) {\n') //
|
..write('}) {\n') //
|
||||||
..write('return ${table.getNameForCompanionClass(scope.options)}(');
|
..write('return ${table.getNameForCompanionClass(scope.options)}(');
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
final name = column.dartGetterName;
|
final name = column.dartGetterName;
|
||||||
_buffer.write('$name: $name ?? this.$name,');
|
_buffer.write('$name: $name ?? this.$name,');
|
||||||
}
|
}
|
||||||
|
@ -173,7 +177,7 @@ class UpdateCompanionWriter {
|
||||||
|
|
||||||
const locals = {'map', 'nullToAbsent', 'converter'};
|
const locals = {'map', 'nullToAbsent', 'converter'};
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
final getterName = column.thisIfNeeded(locals);
|
final getterName = column.thisIfNeeded(locals);
|
||||||
|
|
||||||
_buffer.write('if ($getterName.present) {');
|
_buffer.write('if ($getterName.present) {');
|
||||||
|
@ -212,7 +216,7 @@ class UpdateCompanionWriter {
|
||||||
void _writeToString() {
|
void _writeToString() {
|
||||||
overrideToString(
|
overrideToString(
|
||||||
table.getNameForCompanionClass(scope.options),
|
table.getNameForCompanionClass(scope.options),
|
||||||
[for (final column in table.columns) column.dartGetterName],
|
[for (final column in columns) column.dartGetterName],
|
||||||
_buffer,
|
_buffer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -237,7 +241,7 @@ class UpdateCompanionWriter {
|
||||||
final column =
|
final column =
|
||||||
table.columns.firstWhereOrNull((e) => e.dartGetterName == field.name);
|
table.columns.firstWhereOrNull((e) => e.dartGetterName == field.name);
|
||||||
|
|
||||||
if (column != null) {
|
if (column != null && !column.isGenerated) {
|
||||||
final dartName = column.dartGetterName;
|
final dartName = column.dartGetterName;
|
||||||
_buffer.write('$dartName: Value (_object.$dartName),\n');
|
_buffer.write('$dartName: Value (_object.$dartName),\n');
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue