mirror of https://github.com/AMT-Cheif/drift.git
Refactor sql scoping, make aliased tables explicit
This commit is contained in:
parent
aa52c4ba3d
commit
b143ee5a4b
|
@ -9,3 +9,4 @@ pubspec.lock
|
||||||
|
|
||||||
benchmark_results.json
|
benchmark_results.json
|
||||||
.flutter-plugins-dependencies
|
.flutter-plugins-dependencies
|
||||||
|
flutter_export_environment.sh
|
|
@ -187,7 +187,7 @@ packages:
|
||||||
path: "../../../moor_flutter"
|
path: "../../../moor_flutter"
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "2.0.0"
|
version: "2.1.0"
|
||||||
multi_server_socket:
|
multi_server_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -7,17 +7,18 @@ mixin ReferenceOwner {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mixin for classes which can be referenced by a [ReferenceOwner].
|
/// Mixin for classes which can be referenced by a [ReferenceOwner].
|
||||||
mixin Referencable {}
|
mixin Referencable {
|
||||||
|
/// Whether this referencable is still visible in child scopes. This doesn't
|
||||||
/// A referencable which is still visible in child scopes. This doesn't apply to
|
/// apply to many things, basically only to tables.
|
||||||
/// many things, basically only tables.
|
///
|
||||||
///
|
/// For instance: "SELECT *, 1 AS d, (SELECT id FROM demo WHERE id = out.id)
|
||||||
/// For instance: "SELECT *, 1 AS d, (SELECT id FROM demo WHERE id = out.id)
|
/// FROM demo AS out;"
|
||||||
/// FROM demo AS out;"
|
/// is a valid sql query when the demo table has an id column. However,
|
||||||
/// is a valid sql query when the demo table has an id column. However,
|
/// "SELECT *, 1 AS d, (SELECT id FROM demo WHERE id = d) FROM demo AS out;"
|
||||||
/// "SELECT *, 1 AS d, (SELECT id FROM demo WHERE id = d) FROM demo AS out;" is
|
/// is not, the "d" referencable is not visible for the child select
|
||||||
/// not, the "d" referencable is not visible for the child select statement.
|
/// statement.
|
||||||
mixin VisibleToChildren on Referencable {}
|
bool get visibleToChildren => false;
|
||||||
|
}
|
||||||
|
|
||||||
/// Class which keeps track of references for tables, columns and functions in a
|
/// Class which keeps track of references for tables, columns and functions in a
|
||||||
/// query.
|
/// query.
|
||||||
|
@ -86,7 +87,7 @@ class ReferenceScope {
|
||||||
if (scope._references.containsKey(upper)) {
|
if (scope._references.containsKey(upper)) {
|
||||||
final candidates = scope._references[upper];
|
final candidates = scope._references[upper];
|
||||||
final resolved = candidates.whereType<T>().where((x) {
|
final resolved = candidates.whereType<T>().where((x) {
|
||||||
return x is VisibleToChildren || !isAtParent;
|
return x.visibleToChildren || !isAtParent;
|
||||||
});
|
});
|
||||||
if (resolved.isNotEmpty) {
|
if (resolved.isNotEmpty) {
|
||||||
return resolved.first;
|
return resolved.first;
|
||||||
|
@ -102,7 +103,7 @@ class ReferenceScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns everything that is in scope and a subtype of [T].
|
/// Returns everything that is in scope and a subtype of [T].
|
||||||
List<T> allOf<T>() {
|
List<T> allOf<T extends Referencable>() {
|
||||||
var scope = this;
|
var scope = this;
|
||||||
var isInCurrentScope = true;
|
var isInCurrentScope = true;
|
||||||
final collected = <T>[];
|
final collected = <T>[];
|
||||||
|
@ -112,7 +113,8 @@ class ReferenceScope {
|
||||||
scope._references.values.expand((list) => list).whereType<T>();
|
scope._references.values.expand((list) => list).whereType<T>();
|
||||||
|
|
||||||
if (!isInCurrentScope) {
|
if (!isInCurrentScope) {
|
||||||
foundValues = foundValues.whereType<VisibleToChildren>().cast();
|
foundValues =
|
||||||
|
foundValues.where((element) => element.visibleToChildren).cast();
|
||||||
}
|
}
|
||||||
|
|
||||||
collected.addAll(foundValues);
|
collected.addAll(foundValues);
|
||||||
|
|
|
@ -18,6 +18,9 @@ abstract class ResultSet implements ResolvesToResultSet {
|
||||||
@override
|
@override
|
||||||
ResultSet get resultSet => this;
|
ResultSet get resultSet => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get visibleToChildren => false;
|
||||||
|
|
||||||
Column findColumn(String name) {
|
Column findColumn(String name) {
|
||||||
return resolvedColumns.firstWhere((c) => c.name == name,
|
return resolvedColumns.firstWhere((c) => c.name == name,
|
||||||
orElse: () => null);
|
orElse: () => null);
|
||||||
|
@ -34,9 +37,7 @@ class CustomResultSet with ResultSet {
|
||||||
|
|
||||||
/// A database table. The information stored here will be used to resolve
|
/// A database table. The information stored here will be used to resolve
|
||||||
/// references and for type inference.
|
/// references and for type inference.
|
||||||
class Table
|
class Table with ResultSet, HasMetaMixin implements HumanReadable {
|
||||||
with ResultSet, VisibleToChildren, HasMetaMixin
|
|
||||||
implements HumanReadable {
|
|
||||||
/// The name of this table, as it appears in sql statements. This should be
|
/// The name of this table, as it appears in sql statements. This should be
|
||||||
/// the raw name, not an escaped version.
|
/// the raw name, not an escaped version.
|
||||||
///
|
///
|
||||||
|
@ -66,6 +67,9 @@ class Table
|
||||||
/// The ast node that created this table
|
/// The ast node that created this table
|
||||||
final TableInducingStatement definition;
|
final TableInducingStatement definition;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get visibleToChildren => true;
|
||||||
|
|
||||||
TableColumn _rowIdColumn;
|
TableColumn _rowIdColumn;
|
||||||
|
|
||||||
/// Constructs a table from the known [name] and [resolvedColumns].
|
/// Constructs a table from the known [name] and [resolvedColumns].
|
||||||
|
@ -102,3 +106,31 @@ class Table
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TableAlias implements ResultSet, HumanReadable {
|
||||||
|
final ResultSet delegate;
|
||||||
|
final String alias;
|
||||||
|
|
||||||
|
TableAlias(this.delegate, this.alias);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Column> get resolvedColumns => delegate.resolvedColumns;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Column findColumn(String name) => delegate.findColumn(name);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ResultSet get resultSet => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get visibleToChildren => delegate.visibleToChildren;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String humanReadableDescription() {
|
||||||
|
final delegateDescription = delegate is HumanReadable
|
||||||
|
? (delegate as HumanReadable).humanReadableDescription()
|
||||||
|
: delegate.toString();
|
||||||
|
|
||||||
|
return '$alias (alias to $delegateDescription)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -73,12 +73,12 @@ class ColumnResolver extends RecursiveVisitor<void, void> {
|
||||||
@override
|
@override
|
||||||
void visitDoUpdate(DoUpdate e, void arg) {
|
void visitDoUpdate(DoUpdate e, void arg) {
|
||||||
final surroundingInsert = e.parents.whereType<InsertStatement>().first;
|
final surroundingInsert = e.parents.whereType<InsertStatement>().first;
|
||||||
final table = surroundingInsert.resolvedTable;
|
final table = surroundingInsert.table.resultSet;
|
||||||
|
|
||||||
if (table != null) {
|
if (table != null) {
|
||||||
// add "excluded" table qualifier that referring to the row that would
|
// add "excluded" table qualifier that referring to the row that would
|
||||||
// have been inserted had the uniqueness constraint not been violated.
|
// have been inserted had the uniqueness constraint not been violated.
|
||||||
e.scope.register('excluded', table);
|
e.scope.register('excluded', TableAlias(table, 'excluded'));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitChildren(e, arg);
|
visitChildren(e, arg);
|
||||||
|
@ -94,7 +94,6 @@ class ColumnResolver extends RecursiveVisitor<void, void> {
|
||||||
@override
|
@override
|
||||||
void visitInsertStatement(InsertStatement e, void arg) {
|
void visitInsertStatement(InsertStatement e, void arg) {
|
||||||
final table = _resolveTableReference(e.table);
|
final table = _resolveTableReference(e.table);
|
||||||
e.resolvedTable = table;
|
|
||||||
visitChildren(e, arg);
|
visitChildren(e, arg);
|
||||||
e.scope.availableColumns = table.resolvedColumns;
|
e.scope.availableColumns = table.resolvedColumns;
|
||||||
visitChildren(e, arg);
|
visitChildren(e, arg);
|
||||||
|
@ -118,10 +117,10 @@ class ColumnResolver extends RecursiveVisitor<void, void> {
|
||||||
|
|
||||||
final scope = e.scope;
|
final scope = e.scope;
|
||||||
if (e.target.introducesNew) {
|
if (e.target.introducesNew) {
|
||||||
scope.register('new', table);
|
scope.register('new', TableAlias(table, 'new'));
|
||||||
}
|
}
|
||||||
if (e.target.introducesOld) {
|
if (e.target.introducesOld) {
|
||||||
scope.register('old', table);
|
scope.register('old', TableAlias(table, 'old'));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitChildren(e, arg);
|
visitChildren(e, arg);
|
||||||
|
@ -251,7 +250,13 @@ class ColumnResolver extends RecursiveVisitor<void, void> {
|
||||||
ResultSet _resolveTableReference(TableReference r) {
|
ResultSet _resolveTableReference(TableReference r) {
|
||||||
final scope = r.scope;
|
final scope = r.scope;
|
||||||
final resolvedTable = scope.resolve<ResultSet>(r.tableName, orElse: () {
|
final resolvedTable = scope.resolve<ResultSet>(r.tableName, orElse: () {
|
||||||
final available = scope.allOf<Table>().map((t) => t.name);
|
final available = scope.allOf<ResultSet>().map((t) {
|
||||||
|
if (t is HumanReadable) {
|
||||||
|
return (t as HumanReadable).humanReadableDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.toString();
|
||||||
|
});
|
||||||
|
|
||||||
context.reportError(UnresolvedReferenceError(
|
context.reportError(UnresolvedReferenceError(
|
||||||
type: AnalysisErrorType.referencedUnknownTable,
|
type: AnalysisErrorType.referencedUnknownTable,
|
||||||
|
|
|
@ -22,7 +22,7 @@ class WithClause extends AstNode {
|
||||||
bool contentEquals(WithClause other) => other.recursive == recursive;
|
bool contentEquals(WithClause other) => other.recursive == recursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommonTableExpression extends AstNode with ResultSet, VisibleToChildren {
|
class CommonTableExpression extends AstNode with ResultSet {
|
||||||
final String cteTableName;
|
final String cteTableName;
|
||||||
|
|
||||||
/// If this common table expression has explicit column names, e.g. with
|
/// If this common table expression has explicit column names, e.g. with
|
||||||
|
@ -78,4 +78,7 @@ class CommonTableExpression extends AstNode with ResultSet, VisibleToChildren {
|
||||||
|
|
||||||
return _cachedColumns;
|
return _cachedColumns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get visibleToChildren => true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ abstract class TableOrSubquery extends Queryable {}
|
||||||
/// set.
|
/// set.
|
||||||
class TableReference extends TableOrSubquery
|
class TableReference extends TableOrSubquery
|
||||||
with ReferenceOwner
|
with ReferenceOwner
|
||||||
implements Renamable, ResolvesToResultSet, VisibleToChildren {
|
implements Renamable, ResolvesToResultSet {
|
||||||
final String tableName;
|
final String tableName;
|
||||||
Token tableNameToken;
|
Token tableNameToken;
|
||||||
|
|
||||||
|
@ -64,6 +64,9 @@ class TableReference extends TableOrSubquery
|
||||||
ResultSet get resultSet {
|
ResultSet get resultSet {
|
||||||
return resolved as ResultSet;
|
return resolved as ResultSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get visibleToChildren => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A nested select statement that appears after a FROM clause. This is
|
/// A nested select statement that appears after a FROM clause. This is
|
||||||
|
@ -189,6 +192,9 @@ class TableValuedFunction extends Queryable
|
||||||
@override
|
@override
|
||||||
Iterable<AstNode> get childNodes => [parameters];
|
Iterable<AstNode> get childNodes => [parameters];
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get visibleToChildren => false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool contentEquals(TableValuedFunction other) {
|
bool contentEquals(TableValuedFunction other) {
|
||||||
return other.name == name;
|
return other.name == name;
|
||||||
|
|
|
@ -17,8 +17,6 @@ class InsertStatement extends CrudStatement {
|
||||||
final InsertSource source;
|
final InsertSource source;
|
||||||
final UpsertClause upsert;
|
final UpsertClause upsert;
|
||||||
|
|
||||||
ResultSet /*?*/ resolvedTable;
|
|
||||||
|
|
||||||
List<Column> get resolvedTargetColumns {
|
List<Column> get resolvedTargetColumns {
|
||||||
if (targetColumns.isNotEmpty) {
|
if (targetColumns.isNotEmpty) {
|
||||||
return targetColumns.map((c) => c.resolvedColumn).toList();
|
return targetColumns.map((c) => c.resolvedColumn).toList();
|
||||||
|
|
|
@ -122,6 +122,9 @@ class ExpressionResultColumn extends ResultColumn
|
||||||
|
|
||||||
ExpressionResultColumn({@required this.expression, this.as});
|
ExpressionResultColumn({@required this.expression, this.as});
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get visibleToChildren => false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Iterable<AstNode> get childNodes => [expression];
|
Iterable<AstNode> get childNodes => [expression];
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ abstract class TableValuedFunctionHandler {
|
||||||
|
|
||||||
/// An sqlite module, which can be used in a `CREATE VIRTUAL TABLE` statement
|
/// An sqlite module, which can be used in a `CREATE VIRTUAL TABLE` statement
|
||||||
/// to find providers.
|
/// to find providers.
|
||||||
abstract class Module implements Referencable, VisibleToChildren {
|
abstract class Module implements Referencable {
|
||||||
/// The name of this module, which is referenced by the `USING` clause in a
|
/// The name of this module, which is referenced by the `USING` clause in a
|
||||||
/// `CREATE VIRTUAL TABLE` statement.
|
/// `CREATE VIRTUAL TABLE` statement.
|
||||||
final String name;
|
final String name;
|
||||||
|
@ -76,4 +76,7 @@ abstract class Module implements Referencable, VisibleToChildren {
|
||||||
/// refers to this module. The module is responsible for setting
|
/// refers to this module. The module is responsible for setting
|
||||||
/// [Table.definition].
|
/// [Table.definition].
|
||||||
Table parseTable(CreateVirtualTableStatement stmt);
|
Table parseTable(CreateVirtualTableStatement stmt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get visibleToChildren => true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue