Support for STRICT tables in drift files

This commit is contained in:
Simon Binder 2021-12-03 22:11:44 +01:00
parent 6b5acdf3c6
commit 44281bebaf
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
14 changed files with 69 additions and 8 deletions

View File

@ -12,7 +12,7 @@ jobs:
name: "Compile sqlite3 for tests"
runs-on: ubuntu-20.04
env:
SQLITE_VERSION: "3350500"
SQLITE_VERSION: "3370000"
steps:
- uses: actions/checkout@v2

View File

@ -7,6 +7,7 @@
Thanks to [@westito](https://github.com/westito).
- Allow the generator to emit correct SQL code when using arrays with the
`new_sql_code_generation` option in specific scenarios.
- Add support for [strict tables](https://sqlite.org/stricttables.html) in `.drift` files.
- Add the `generatedAs` method to declare generated columns for Dart tables.
- Add `OrderingTerm.random` to fetch rows in a random order.
- Improved support for pausing query stream subscriptions. Instead of buffering events,

View File

@ -17,7 +17,7 @@ targets:
sql:
dialect: sqlite
options:
version: "3.35"
version: "3.37"
modules:
- json1
- fts5

View File

@ -34,6 +34,13 @@ abstract class Table extends HasResultSet {
/// 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 {

View File

@ -289,9 +289,12 @@ class Migrator {
context.buffer.write(')');
// == true because of nullability
if (dslTable.withoutRowId == true) {
if (dslTable.withoutRowId) {
context.buffer.write(' WITHOUT ROWID');
}
if (dslTable.isStrict) {
context.buffer.write(' STRICT');
}
context.buffer.write(';');
}

View File

@ -284,6 +284,8 @@ class ConfigTable extends Table with TableInfo<ConfigTable, Config> {
static TypeConverter<SyncType?, int> $converter1 =
const EnumIndexConverter<SyncType>(SyncType.values);
@override
bool get isStrict => true;
@override
bool get dontWriteConstraints => true;
}

View File

@ -23,7 +23,7 @@ create table config (
config_value TEXT,
sync_state INTEGER MAPPED BY `const SyncTypeConverter()`,
sync_state_implicit ENUM(SyncType)
) AS "Config";
) STRICT AS "Config";
CREATE INDEX IF NOT EXISTS value_idx ON config (config_value);

View File

@ -22,7 +22,7 @@ const _createConfig = 'CREATE TABLE IF NOT EXISTS config ('
'config_key TEXT not null primary key, '
'config_value TEXT, '
'sync_state INTEGER, '
'sync_state_implicit INTEGER);';
'sync_state_implicit INTEGER) STRICT;';
const _createMyTable = 'CREATE TABLE IF NOT EXISTS mytable ('
'someid INTEGER NOT NULL, '

View File

@ -228,6 +228,7 @@ class CreateTableReader {
overrideDontWriteConstraints: true,
declaration: MoorTableDeclaration(stmt, step.file),
existingRowClass: existingRowClass,
isStrict: table.isStrict,
)..parserTable = table;
// Having a mapping from parser table to moor tables helps with IDE features

View File

@ -103,6 +103,10 @@ class MoorTable extends MoorEntityWithResultSet {
/// getter on the table class with this value.
final bool? overrideWithoutRowId;
/// Whether this table is defined as `STRICT`. Support for strict tables has
/// been added in sqlite 3.37.
final bool isStrict;
/// When non-null, the generated table class will override the
/// `dontWriteConstraint` getter on the table class with this value.
final bool? overrideDontWriteConstraints;
@ -145,6 +149,7 @@ class MoorTable extends MoorEntityWithResultSet {
this.overrideDontWriteConstraints,
this.declaration,
this.existingRowClass,
this.isStrict = false,
}) : _overriddenName = overriddenName {
_attachToConverters();
}

View File

@ -444,6 +444,12 @@ class TableWriter extends TableOrViewWriter {
..write('bool get withoutRowId => $value;\n');
}
if (table.isStrict) {
buffer
..write('@override\n')
..write('bool get isStrict => true;\n');
}
if (table.overrideTableConstraints != null) {
final value =
table.overrideTableConstraints!.map(asDartLiteral).join(', ');

View File

@ -74,6 +74,7 @@ class SchemaFromCreateTable {
primaryKeyColumnsInStrictTable: stmt.isStrict ? primaryKey : null)
],
withoutRowId: stmt.withoutRowId,
isStrict: stmt.isStrict,
tableConstraints: stmt.tableConstraints,
definition: stmt,
);
@ -167,11 +168,13 @@ class SchemaFromCreateTable {
bool isValidTypeNameForStrictTable(String typeName) {
// See https://www.sqlite.org/draft/stricttables.html
const allowed = {'INT', 'INTEGER', 'REAL', 'TEXT', 'BLOB', 'ANY'};
const alsoAllowedInMoor = {'ENUM', 'BOOL', 'DATE'};
const alsoAllowedInMoor = {'BOOL', 'DATE'};
if (allowed.contains(typeName.toUpperCase()) ||
final upper = typeName.toUpperCase();
if (allowed.contains(upper) ||
(moorExtensions &&
alsoAllowedInMoor.contains(typeName.toUpperCase()))) {
(alsoAllowedInMoor.contains(upper) || upper.contains('ENUM')))) {
return true;
}

View File

@ -25,6 +25,8 @@ class Table extends NamedResultSet with HasMetaMixin implements HumanReadable {
/// Whether this table was created with an "WITHOUT ROWID" modifier
final bool withoutRowId;
final bool isStrict;
/// Additional constraints set on this table.
final List<TableConstraint> tableConstraints;
@ -44,6 +46,7 @@ class Table extends NamedResultSet with HasMetaMixin implements HumanReadable {
required this.name,
required this.resolvedColumns,
this.withoutRowId = false,
this.isStrict = false,
this.tableConstraints = const [],
this.definition,
this.isVirtual = false,

View File

@ -190,4 +190,34 @@ void main() {
expect(table.resolvedColumns.single.type.nullable, isFalse);
});
});
group('sets withoutRowid and isStrict', () {
final engine = SqlEngine(EngineOptions(version: SqliteVersion.v3_37));
void testWith(String suffix, bool withoutRowid, bool strict) {
final stmt =
engine.parse('CREATE TABLE foo (bar TEXT) $suffix;').rootNode;
final table = engine.schemaReader.read(stmt as CreateTableStatement);
expect(table.withoutRowId, withoutRowid);
expect(table.isStrict, strict);
}
test('when the table is neither', () {
testWith('', false, false);
});
test('when the table is without rowid', () {
testWith('WITHOUT ROWID', true, false);
});
test('when the table is strict', () {
testWith('STRICT', false, true);
});
test('when the table is both', () {
testWith('WITHOUT ROWID, STRICT', true, true);
testWith('STRICT, WITHOUT ROWID', true, true);
});
});
}