mirror of https://github.com/AMT-Cheif/drift.git
parent
5cbc331dda
commit
a037de6621
|
@ -1,7 +1,7 @@
|
||||||
## 3.3.1
|
## 3.3.1
|
||||||
|
|
||||||
- Support changing `onData` handlers for query streams.
|
- Support changing `onData` handlers for query streams.
|
||||||
This fixes a bug ocurring when using `queryStream.first` multiple times.
|
This fixes a bug occurring when using `queryStream.first` multiple times.
|
||||||
|
|
||||||
## 3.3.0
|
## 3.3.0
|
||||||
|
|
||||||
|
|
|
@ -187,8 +187,10 @@ class _LintingVisitor extends RecursiveVisitor<void, void> {
|
||||||
relevantNode: e.table,
|
relevantNode: e.table,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
final notPresent = required.where((c) => !targeted
|
final notPresent = required.where((c) {
|
||||||
.any((t) => t.name.toUpperCase() == c.name.name.toUpperCase()));
|
return !targeted
|
||||||
|
.any((t) => t?.name?.toUpperCase() == c.name.name.toUpperCase());
|
||||||
|
});
|
||||||
|
|
||||||
if (notPresent.isNotEmpty) {
|
if (notPresent.isNotEmpty) {
|
||||||
final msg = notPresent.map((c) => c.name.name).join(', ');
|
final msg = notPresent.map((c) => c.name.name).join(', ');
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import 'package:moor_generator/src/analyzer/options.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../utils.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Regression test for https://github.com/simolus3/moor/issues/754
|
||||||
|
test('supports fts5 tables with external content', () async {
|
||||||
|
final state = TestState.withContent({
|
||||||
|
'foo|lib/a.moor': '''
|
||||||
|
CREATE TABLE tbl(a INTEGER PRIMARY KEY, b TEXT, c TEXT);
|
||||||
|
CREATE VIRTUAL TABLE fts_idx USING fts5(b, c, content='tbl', content_rowid='a');
|
||||||
|
|
||||||
|
-- Triggers to keep the FTS index up to date.
|
||||||
|
CREATE TRIGGER tbl_ai AFTER INSERT ON tbl BEGIN
|
||||||
|
INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER tbl_ad AFTER DELETE ON tbl BEGIN
|
||||||
|
INSERT INTO fts_idx(fts_idx, rowid, b, c) VALUES('delete', old.a, old.b, old.c);
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER tbl_au AFTER UPDATE ON tbl BEGIN
|
||||||
|
INSERT INTO fts_idx(fts_idx, rowid, b, c) VALUES('delete', old.a, old.b, old.c);
|
||||||
|
INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
|
||||||
|
END;
|
||||||
|
''',
|
||||||
|
}, options: const MoorOptions(modules: [SqlModule.fts5]));
|
||||||
|
|
||||||
|
final result = await state.analyze('package:foo/a.moor');
|
||||||
|
|
||||||
|
// The generator used to crash while analyzing, so consider the test passed
|
||||||
|
// if it can analyze the file and sees that there aren't any errors.
|
||||||
|
expect(result.errors.errors, isEmpty);
|
||||||
|
});
|
||||||
|
}
|
|
@ -8,11 +8,21 @@ class SchemaFromCreateTable {
|
||||||
|
|
||||||
const SchemaFromCreateTable({this.moorExtensions = false});
|
const SchemaFromCreateTable({this.moorExtensions = false});
|
||||||
|
|
||||||
|
/// Reads a [Table] schema from the [stmt] inducing a table (either a
|
||||||
|
/// [CreateTableStatement] or a [CreateVirtualTableStatement]).
|
||||||
|
///
|
||||||
|
/// This method might throw an exception if the table could not be read.
|
||||||
Table read(TableInducingStatement stmt) {
|
Table read(TableInducingStatement stmt) {
|
||||||
if (stmt is CreateTableStatement) {
|
if (stmt is CreateTableStatement) {
|
||||||
return _readCreateTable(stmt);
|
return _readCreateTable(stmt);
|
||||||
} else if (stmt is CreateVirtualTableStatement) {
|
} else if (stmt is CreateVirtualTableStatement) {
|
||||||
final module = stmt.scope.resolve<Module>(stmt.moduleName);
|
final module = stmt.scope.resolve<Module>(stmt.moduleName);
|
||||||
|
|
||||||
|
if (module == null) {
|
||||||
|
throw CantReadSchemaException('Unknown module "${stmt.moduleName}", '
|
||||||
|
'did you register it?');
|
||||||
|
}
|
||||||
|
|
||||||
return module.parseTable(stmt);
|
return module.parseTable(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,3 +132,15 @@ class SchemaFromCreateTable {
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
BasicType columnAffinity(String typeName) => resolveColumnType(typeName).type;
|
BasicType columnAffinity(String typeName) => resolveColumnType(typeName).type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Thrown when a table schema could not be read.
|
||||||
|
class CantReadSchemaException implements Exception {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
CantReadSchemaException(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Could not read table schema: $message';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -84,16 +84,17 @@ class ReferenceResolver extends RecursiveVisitor<void, void> {
|
||||||
|
|
||||||
Column _resolveRowIdAlias(Reference e) {
|
Column _resolveRowIdAlias(Reference e) {
|
||||||
// to resolve those aliases when they're not bound to a table, the
|
// to resolve those aliases when they're not bound to a table, the
|
||||||
// surrounding select statement may only read from one table
|
// surrounding statement may only read from one table
|
||||||
final select = e.parents.firstWhere((node) => node is SelectStatement,
|
final stmt = e.enclosingOfType<HasPrimarySource>();
|
||||||
orElse: () => null) as SelectStatement;
|
|
||||||
|
|
||||||
if (select == null) return null;
|
if (stmt == null) return null;
|
||||||
if (select.from is! TableReference) {
|
|
||||||
|
final from = stmt.table;
|
||||||
|
if (from is! TableReference) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final table = (select.from as TableReference).resolved as Table;
|
final table = (from as TableReference).resolved as Table;
|
||||||
if (table == null) return null;
|
if (table == null) return null;
|
||||||
|
|
||||||
// table.findColumn contains logic to resolve row id aliases
|
// table.findColumn contains logic to resolve row id aliases
|
||||||
|
|
|
@ -113,6 +113,19 @@ abstract class AstNode with HasMetaMixin implements SyntacticEntity {
|
||||||
yield* allDescendants;
|
yield* allDescendants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds the first element in [selfAndParents] of the type [T].
|
||||||
|
///
|
||||||
|
/// Returns `null` if there's no node of type [T] surrounding this ast node.
|
||||||
|
T /*?*/ enclosingOfType<T extends AstNode>() {
|
||||||
|
for (final element in selfAndParents) {
|
||||||
|
if (element is T) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// The [ReferenceScope], which contains available tables, column references
|
/// The [ReferenceScope], which contains available tables, column references
|
||||||
/// and functions for this node.
|
/// and functions for this node.
|
||||||
ReferenceScope get scope {
|
ReferenceScope get scope {
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
part of '../ast.dart';
|
part of '../ast.dart';
|
||||||
|
|
||||||
class DeleteStatement extends CrudStatement implements StatementWithWhere {
|
class DeleteStatement extends CrudStatement
|
||||||
|
implements StatementWithWhere, HasPrimarySource {
|
||||||
TableReference from;
|
TableReference from;
|
||||||
@override
|
@override
|
||||||
Expression where;
|
Expression where;
|
||||||
|
|
||||||
|
@override
|
||||||
|
TableReference get table => from;
|
||||||
|
|
||||||
DeleteStatement({WithClause withClause, @required this.from, this.where})
|
DeleteStatement({WithClause withClause, @required this.from, this.where})
|
||||||
: super._(withClause);
|
: super._(withClause);
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,9 @@ enum InsertMode {
|
||||||
insertOrIgnore
|
insertOrIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertStatement extends CrudStatement {
|
class InsertStatement extends CrudStatement implements HasPrimarySource {
|
||||||
final InsertMode mode;
|
final InsertMode mode;
|
||||||
|
@override
|
||||||
TableReference table;
|
TableReference table;
|
||||||
final List<Reference> targetColumns;
|
final List<Reference> targetColumns;
|
||||||
InsertSource source;
|
InsertSource source;
|
||||||
|
|
|
@ -14,7 +14,7 @@ abstract class BaseSelectStatement extends CrudStatement with ResultSet {
|
||||||
abstract class SelectStatementNoCompound implements BaseSelectStatement {}
|
abstract class SelectStatementNoCompound implements BaseSelectStatement {}
|
||||||
|
|
||||||
class SelectStatement extends BaseSelectStatement
|
class SelectStatement extends BaseSelectStatement
|
||||||
implements StatementWithWhere, SelectStatementNoCompound {
|
implements StatementWithWhere, SelectStatementNoCompound, HasPrimarySource {
|
||||||
final bool distinct;
|
final bool distinct;
|
||||||
final List<ResultColumn> columns;
|
final List<ResultColumn> columns;
|
||||||
Queryable /*?*/ from;
|
Queryable /*?*/ from;
|
||||||
|
@ -27,6 +27,9 @@ class SelectStatement extends BaseSelectStatement
|
||||||
OrderByBase orderBy;
|
OrderByBase orderBy;
|
||||||
LimitBase limit;
|
LimitBase limit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Queryable get table => from;
|
||||||
|
|
||||||
SelectStatement(
|
SelectStatement(
|
||||||
{WithClause withClause,
|
{WithClause withClause,
|
||||||
this.distinct = false,
|
this.distinct = false,
|
||||||
|
|
|
@ -12,6 +12,18 @@ abstract class CrudStatement extends Statement {
|
||||||
CrudStatement._(this.withClause);
|
CrudStatement._(this.withClause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interfaces for statements that have a primary source table on which they
|
||||||
|
/// operate.
|
||||||
|
/// This includes delete, update and insert statements. It also includes the
|
||||||
|
/// common [SelectStatement], but not compound select statements or `VALUES`
|
||||||
|
/// statements.
|
||||||
|
abstract class HasPrimarySource extends Statement {
|
||||||
|
/// The primary table this statement operates on. This is the part after the
|
||||||
|
/// `FROM` for select and delete statements, the part after the `INTO` for
|
||||||
|
/// inserts and the name after the `UPDATE` for updates.
|
||||||
|
Queryable get table;
|
||||||
|
}
|
||||||
|
|
||||||
/// Interface for statements that have a primary where clause (select, update,
|
/// Interface for statements that have a primary where clause (select, update,
|
||||||
/// delete).
|
/// delete).
|
||||||
abstract class StatementWithWhere extends Statement implements HasWhereClause {}
|
abstract class StatementWithWhere extends Statement implements HasWhereClause {}
|
||||||
|
|
|
@ -16,8 +16,10 @@ const Map<TokenType, FailureMode> _tokensToMode = {
|
||||||
TokenType.ignore: FailureMode.ignore,
|
TokenType.ignore: FailureMode.ignore,
|
||||||
};
|
};
|
||||||
|
|
||||||
class UpdateStatement extends CrudStatement implements StatementWithWhere {
|
class UpdateStatement extends CrudStatement
|
||||||
|
implements StatementWithWhere, HasPrimarySource {
|
||||||
final FailureMode or;
|
final FailureMode or;
|
||||||
|
@override
|
||||||
TableReference table;
|
TableReference table;
|
||||||
final List<SetComponent> set;
|
final List<SetComponent> set;
|
||||||
@override
|
@override
|
||||||
|
|
Loading…
Reference in New Issue