mirror of https://github.com/AMT-Cheif/drift.git
Recognize boolean and datetime column in moor
This commit is contained in:
parent
fb7c3c2a9a
commit
ed03bff4c2
|
@ -97,6 +97,15 @@ invalid.
|
|||
variable can clash with the explicit index, which is why moor forbids
|
||||
it. Of course, `id IN ? OR title = ?` will work as expected.
|
||||
|
||||
## Supported column types
|
||||
|
||||
We use [this algorithm](https://www.sqlite.org/datatype3.html#determination_of_column_affinity)
|
||||
to determine the column type based on the declared type name.
|
||||
|
||||
Additionally, columns that have the type name `BOOLEAN` or `DATETIME` will have
|
||||
`bool` or `DateTime` as their Dart counterpart. Both will be
|
||||
written as an `INTEGER` column when the table gets created.
|
||||
|
||||
## Imports
|
||||
{{% alert title="Limited support" %}}
|
||||
> Importing a moor file from another moor file will work as expected.
|
||||
|
|
|
@ -57,5 +57,10 @@ Make sure that your project depends on moor 2.0 or later. Then
|
|||
Unfortunately, we can't support IntelliJ and Android Studio yet. Please vote on
|
||||
[this issue](https://youtrack.jetbrains.com/issue/WEB-41424) to help us here!
|
||||
|
||||
As a workaround, you can configure IntellIJ to recognize moor files as sql. Moor-only
|
||||
features like imports and Dart templates will report errors, but the rest of the
|
||||
syntax works well. See [this comment](https://github.com/simolus3/moor/issues/150#issuecomment-538582696)
|
||||
on how to set this up.
|
||||
|
||||
If you're looking for support for an other IDE that uses the Dart analysis server,
|
||||
please create an issue. We can probably make that happen.
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
- Introduced `isBetween` and `isBetweenValues` methods for comparable expressions (int, double, datetime)
|
||||
to check values for both an upper and lower bound
|
||||
- Automatically map `BOOLEAN` and `DATETIME` columns declared in a sql file to the appropriate type
|
||||
(both used to be `double` before).
|
||||
|
||||
## 2.0.0
|
||||
This is the first major update after the initial release and moor and we have a lot to cover:
|
||||
|
|
|
@ -176,7 +176,7 @@ class GeneratedBoolColumn extends GeneratedColumn<bool, BoolType>
|
|||
$customConstraints: $customConstraints, defaultValue: defaultValue);
|
||||
|
||||
@override
|
||||
final String typeName = 'BOOLEAN';
|
||||
final String typeName = 'INTEGER';
|
||||
|
||||
@override
|
||||
void writeCustomConstraints(StringBuffer into) {
|
||||
|
|
|
@ -645,16 +645,25 @@ class ConfigTable extends Table with TableInfo<ConfigTable, Config> {
|
|||
class MytableData extends DataClass implements Insertable<MytableData> {
|
||||
final int someid;
|
||||
final String sometext;
|
||||
MytableData({@required this.someid, this.sometext});
|
||||
final bool somebool;
|
||||
final DateTime somedate;
|
||||
MytableData(
|
||||
{@required this.someid, this.sometext, this.somebool, this.somedate});
|
||||
factory MytableData.fromData(Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
final intType = db.typeSystem.forDartType<int>();
|
||||
final stringType = db.typeSystem.forDartType<String>();
|
||||
final boolType = db.typeSystem.forDartType<bool>();
|
||||
final dateTimeType = db.typeSystem.forDartType<DateTime>();
|
||||
return MytableData(
|
||||
someid: intType.mapFromDatabaseResponse(data['${effectivePrefix}someid']),
|
||||
sometext: stringType
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}sometext']),
|
||||
somebool:
|
||||
boolType.mapFromDatabaseResponse(data['${effectivePrefix}somebool']),
|
||||
somedate: dateTimeType
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}somedate']),
|
||||
);
|
||||
}
|
||||
factory MytableData.fromJson(Map<String, dynamic> json,
|
||||
|
@ -662,6 +671,8 @@ class MytableData extends DataClass implements Insertable<MytableData> {
|
|||
return MytableData(
|
||||
someid: serializer.fromJson<int>(json['someid']),
|
||||
sometext: serializer.fromJson<String>(json['sometext']),
|
||||
somebool: serializer.fromJson<bool>(json['somebool']),
|
||||
somedate: serializer.fromJson<DateTime>(json['somedate']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
|
@ -670,6 +681,8 @@ class MytableData extends DataClass implements Insertable<MytableData> {
|
|||
return {
|
||||
'someid': serializer.toJson<int>(someid),
|
||||
'sometext': serializer.toJson<String>(sometext),
|
||||
'somebool': serializer.toJson<bool>(somebool),
|
||||
'somedate': serializer.toJson<DateTime>(somedate),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -681,47 +694,74 @@ class MytableData extends DataClass implements Insertable<MytableData> {
|
|||
sometext: sometext == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(sometext),
|
||||
somebool: somebool == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(somebool),
|
||||
somedate: somedate == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(somedate),
|
||||
) as T;
|
||||
}
|
||||
|
||||
MytableData copyWith({int someid, String sometext}) => MytableData(
|
||||
MytableData copyWith(
|
||||
{int someid, String sometext, bool somebool, DateTime somedate}) =>
|
||||
MytableData(
|
||||
someid: someid ?? this.someid,
|
||||
sometext: sometext ?? this.sometext,
|
||||
somebool: somebool ?? this.somebool,
|
||||
somedate: somedate ?? this.somedate,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('MytableData(')
|
||||
..write('someid: $someid, ')
|
||||
..write('sometext: $sometext')
|
||||
..write('sometext: $sometext, ')
|
||||
..write('somebool: $somebool, ')
|
||||
..write('somedate: $somedate')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(someid.hashCode, sometext.hashCode));
|
||||
int get hashCode => $mrjf($mrjc(someid.hashCode,
|
||||
$mrjc(sometext.hashCode, $mrjc(somebool.hashCode, somedate.hashCode))));
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
identical(this, other) ||
|
||||
(other is MytableData &&
|
||||
other.someid == this.someid &&
|
||||
other.sometext == this.sometext);
|
||||
other.sometext == this.sometext &&
|
||||
other.somebool == this.somebool &&
|
||||
other.somedate == this.somedate);
|
||||
}
|
||||
|
||||
class MytableCompanion extends UpdateCompanion<MytableData> {
|
||||
final Value<int> someid;
|
||||
final Value<String> sometext;
|
||||
final Value<bool> somebool;
|
||||
final Value<DateTime> somedate;
|
||||
const MytableCompanion({
|
||||
this.someid = const Value.absent(),
|
||||
this.sometext = const Value.absent(),
|
||||
this.somebool = const Value.absent(),
|
||||
this.somedate = const Value.absent(),
|
||||
});
|
||||
MytableCompanion.insert({
|
||||
this.someid = const Value.absent(),
|
||||
this.sometext = const Value.absent(),
|
||||
this.somebool = const Value.absent(),
|
||||
this.somedate = const Value.absent(),
|
||||
});
|
||||
MytableCompanion copyWith({Value<int> someid, Value<String> sometext}) {
|
||||
MytableCompanion copyWith(
|
||||
{Value<int> someid,
|
||||
Value<String> sometext,
|
||||
Value<bool> somebool,
|
||||
Value<DateTime> somedate}) {
|
||||
return MytableCompanion(
|
||||
someid: someid ?? this.someid,
|
||||
sometext: sometext ?? this.sometext,
|
||||
somebool: somebool ?? this.somebool,
|
||||
somedate: somedate ?? this.somedate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -746,8 +786,24 @@ class Mytable extends Table with TableInfo<Mytable, MytableData> {
|
|||
$customConstraints: '');
|
||||
}
|
||||
|
||||
final VerificationMeta _someboolMeta = const VerificationMeta('somebool');
|
||||
GeneratedBoolColumn _somebool;
|
||||
GeneratedBoolColumn get somebool => _somebool ??= _constructSomebool();
|
||||
GeneratedBoolColumn _constructSomebool() {
|
||||
return GeneratedBoolColumn('somebool', $tableName, true,
|
||||
$customConstraints: '');
|
||||
}
|
||||
|
||||
final VerificationMeta _somedateMeta = const VerificationMeta('somedate');
|
||||
GeneratedDateTimeColumn _somedate;
|
||||
GeneratedDateTimeColumn get somedate => _somedate ??= _constructSomedate();
|
||||
GeneratedDateTimeColumn _constructSomedate() {
|
||||
return GeneratedDateTimeColumn('somedate', $tableName, true,
|
||||
$customConstraints: '');
|
||||
}
|
||||
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [someid, sometext];
|
||||
List<GeneratedColumn> get $columns => [someid, sometext, somebool, somedate];
|
||||
@override
|
||||
Mytable get asDslTable => this;
|
||||
@override
|
||||
|
@ -770,6 +826,18 @@ class Mytable extends Table with TableInfo<Mytable, MytableData> {
|
|||
} else if (sometext.isRequired && isInserting) {
|
||||
context.missing(_sometextMeta);
|
||||
}
|
||||
if (d.somebool.present) {
|
||||
context.handle(_someboolMeta,
|
||||
somebool.isAcceptableValue(d.somebool.value, _someboolMeta));
|
||||
} else if (somebool.isRequired && isInserting) {
|
||||
context.missing(_someboolMeta);
|
||||
}
|
||||
if (d.somedate.present) {
|
||||
context.handle(_somedateMeta,
|
||||
somedate.isAcceptableValue(d.somedate.value, _somedateMeta));
|
||||
} else if (somedate.isRequired && isInserting) {
|
||||
context.missing(_somedateMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -790,6 +858,12 @@ class Mytable extends Table with TableInfo<Mytable, MytableData> {
|
|||
if (d.sometext.present) {
|
||||
map['sometext'] = Variable<String, StringType>(d.sometext.value);
|
||||
}
|
||||
if (d.somebool.present) {
|
||||
map['somebool'] = Variable<bool, BoolType>(d.somebool.value);
|
||||
}
|
||||
if (d.somedate.present) {
|
||||
map['somedate'] = Variable<DateTime, DateTimeType>(d.somedate.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ create table config (
|
|||
|
||||
CREATE TABLE mytable (
|
||||
someid INTEGER NOT NULL PRIMARY KEY,
|
||||
sometext TEXT
|
||||
sometext TEXT,
|
||||
somebool BOOLEAN,
|
||||
somedate DATETIME
|
||||
);
|
||||
|
||||
readConfig: SELECT * FROM config WHERE config_key = ?;
|
||||
|
|
|
@ -21,7 +21,9 @@ const _createConfig = 'CREATE TABLE IF NOT EXISTS config ('
|
|||
|
||||
const _createMyTable = 'CREATE TABLE IF NOT EXISTS mytable ('
|
||||
'someid INTEGER NOT NULL PRIMARY KEY, '
|
||||
'sometext VARCHAR);';
|
||||
'sometext VARCHAR, '
|
||||
'somebool INTEGER, '
|
||||
'somedate INTEGER);';
|
||||
|
||||
void main() {
|
||||
// see ../data/tables/tables.moor
|
||||
|
|
|
@ -37,7 +37,7 @@ void main() {
|
|||
'CREATE TABLE IF NOT EXISTS users '
|
||||
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
||||
'name VARCHAR NOT NULL, '
|
||||
'is_awesome BOOLEAN 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, '
|
||||
'creation_time INTEGER NOT NULL '
|
||||
"DEFAULT (strftime('%s', CURRENT_TIMESTAMP)));",
|
||||
|
@ -70,7 +70,7 @@ void main() {
|
|||
'CREATE TABLE IF NOT EXISTS users '
|
||||
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
||||
'name VARCHAR NOT NULL, '
|
||||
'is_awesome BOOLEAN 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, '
|
||||
'creation_time INTEGER NOT NULL '
|
||||
"DEFAULT (strftime('%s', CURRENT_TIMESTAMP)));",
|
||||
|
@ -88,7 +88,7 @@ void main() {
|
|||
.addColumn(db.users, db.users.isAwesome);
|
||||
|
||||
verify(mockQueryExecutor.call('ALTER TABLE users ADD COLUMN '
|
||||
'is_awesome BOOLEAN NOT NULL DEFAULT 1 CHECK (is_awesome in (0, 1));'));
|
||||
'is_awesome INTEGER NOT NULL DEFAULT 1 CHECK (is_awesome in (0, 1));'));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class CreateTableReader {
|
|||
CreateTableReader(this.stmt, this.step);
|
||||
|
||||
SpecifiedTable extractTable(TypeMapper mapper) {
|
||||
final table = SchemaFromCreateTable().read(stmt);
|
||||
final table = SchemaFromCreateTable(moorExtensions: true).read(stmt);
|
||||
|
||||
final foundColumns = <String, SpecifiedColumn>{};
|
||||
final primaryKey = <SpecifiedColumn>{};
|
||||
|
|
|
@ -8,7 +8,9 @@ import 'relative_file.moor';
|
|||
|
||||
CREATE TABLE users(
|
||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR NOT NULL CHECK(LENGTH(name) BETWEEN 5 AND 30)
|
||||
name VARCHAR NOT NULL CHECK(LENGTH(name) BETWEEN 5 AND 30),
|
||||
field BOOLEAN,
|
||||
another DATETIME
|
||||
);
|
||||
|
||||
usersWithLongName: SELECT * FROM users WHERE LENGTH(name) > 25
|
||||
|
@ -22,6 +24,9 @@ usersWithLongName: SELECT * FROM users WHERE LENGTH(name) > 25
|
|||
|
||||
final table = result.declaredTables.single;
|
||||
expect(table.sqlName, 'users');
|
||||
expect(table.columns.map((c) => c.name.name), ['id', 'name']);
|
||||
expect(table.columns.map((c) => c.name.name),
|
||||
['id', 'name', 'field', 'another']);
|
||||
expect(table.columns.map((c) => c.dartTypeName),
|
||||
['int', 'String', 'bool', 'DateTime']);
|
||||
});
|
||||
}
|
|
@ -2,6 +2,12 @@ part of '../analysis.dart';
|
|||
|
||||
/// Reads the [Table] definition from a [CreateTableStatement].
|
||||
class SchemaFromCreateTable {
|
||||
/// Whether we should provide additional type hints for nonstandard `BOOL`
|
||||
/// and `DATETIME` columns.
|
||||
final bool moorExtensions;
|
||||
|
||||
SchemaFromCreateTable({this.moorExtensions = false});
|
||||
|
||||
Table read(CreateTableStatement stmt) {
|
||||
return Table(
|
||||
name: stmt.tableName,
|
||||
|
@ -13,10 +19,12 @@ class SchemaFromCreateTable {
|
|||
}
|
||||
|
||||
TableColumn _readColumn(ColumnDefinition definition) {
|
||||
final affinity = columnAffinity(definition.typeName);
|
||||
final typeName = definition.typeName.toUpperCase();
|
||||
|
||||
final type = resolveColumnType(typeName);
|
||||
final nullable = !definition.constraints.any((c) => c is NotNull);
|
||||
|
||||
final resolvedType = ResolvedType(type: affinity, nullable: nullable);
|
||||
final resolvedType = type.withNullable(nullable);
|
||||
|
||||
return TableColumn(
|
||||
definition.columnName,
|
||||
|
@ -25,29 +33,46 @@ class SchemaFromCreateTable {
|
|||
);
|
||||
}
|
||||
|
||||
/// Looks up the correct column affinity for a declared type name with the
|
||||
/// rules described here:
|
||||
/// Resolves a column type via its typename, see the linked rules below.
|
||||
/// Additionally, if [moorExtensions] are enabled, we support [IsBoolean] and
|
||||
/// [IsDateTime] hints if the type name contains `BOOL` or `DATE`,
|
||||
/// respectively.
|
||||
/// https://www.sqlite.org/datatype3.html#determination_of_column_affinity
|
||||
@visibleForTesting
|
||||
BasicType columnAffinity(String typeName) {
|
||||
ResolvedType resolveColumnType(String typeName) {
|
||||
if (typeName == null) {
|
||||
return BasicType.blob;
|
||||
return const ResolvedType(type: BasicType.blob);
|
||||
}
|
||||
|
||||
final upper = typeName.toUpperCase();
|
||||
if (upper.contains('INT')) {
|
||||
return BasicType.int;
|
||||
return const ResolvedType(type: BasicType.int);
|
||||
}
|
||||
if (upper.contains('CHAR') ||
|
||||
upper.contains('CLOB') ||
|
||||
upper.contains('TEXT')) {
|
||||
return BasicType.text;
|
||||
return const ResolvedType(type: BasicType.text);
|
||||
}
|
||||
|
||||
if (upper.contains('BLOB')) {
|
||||
return BasicType.blob;
|
||||
return const ResolvedType(type: BasicType.blob);
|
||||
}
|
||||
|
||||
return BasicType.real;
|
||||
if (moorExtensions) {
|
||||
if (upper.contains('BOOL')) {
|
||||
return const ResolvedType.bool();
|
||||
}
|
||||
if (upper.contains('DATE')) {
|
||||
return const ResolvedType(
|
||||
type: BasicType.int, hint: const IsDateTime());
|
||||
}
|
||||
}
|
||||
|
||||
return const ResolvedType(type: BasicType.real);
|
||||
}
|
||||
|
||||
/// Looks up the correct column affinity for a declared type name with the
|
||||
/// rules described here:
|
||||
/// https://www.sqlite.org/datatype3.html#determination_of_column_affinity
|
||||
@visibleForTesting
|
||||
BasicType columnAffinity(String typeName) => resolveColumnType(typeName).type;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,11 @@ class ResolvedType {
|
|||
/// [ResolvedType.hint] to [IsBoolean].
|
||||
abstract class TypeHint {
|
||||
const TypeHint();
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
@override
|
||||
bool operator ==(other) => other.runtimeType == runtimeType;
|
||||
}
|
||||
|
||||
/// Type hint to mark that this type will contain a boolean value.
|
||||
|
|
|
@ -61,4 +61,22 @@ void main() {
|
|||
|
||||
expect(table.tableConstraints, hasLength(2));
|
||||
});
|
||||
|
||||
test('supports booleans when moor extensions are enabled', () {
|
||||
final engine = SqlEngine(useMoorExtensions: true);
|
||||
final stmt = engine.parse('''
|
||||
CREATE TABLE foo (
|
||||
a BOOL, b DATETIME, c DATE, d BOOLEAN NOT NULL
|
||||
)
|
||||
''').rootNode;
|
||||
|
||||
final table = SchemaFromCreateTable(moorExtensions: true)
|
||||
.read(stmt as CreateTableStatement);
|
||||
expect(table.resolvedColumns.map((c) => c.type), const [
|
||||
ResolvedType(type: BasicType.int, hint: IsBoolean(), nullable: true),
|
||||
ResolvedType(type: BasicType.int, hint: IsDateTime(), nullable: true),
|
||||
ResolvedType(type: BasicType.int, hint: IsDateTime(), nullable: true),
|
||||
ResolvedType(type: BasicType.int, hint: IsBoolean(), nullable: false),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue