New type resolver: Start propagating known types

This commit is contained in:
Simon Binder 2020-01-04 13:22:29 +01:00
parent 373ad320c4
commit c95a5f0aad
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
7 changed files with 116 additions and 4 deletions

View File

@ -8,7 +8,11 @@
``` ```
selectVariable(:variable AS TEXT): SELECT :variable; selectVariable(:variable AS TEXT): SELECT :variable;
``` ```
- Support for triggers! You can declare them in a `.moor` file and - Support for triggers and indices! You can declare them in a `.moor` file with a regular `CREATE TRIGGER`
or `CREATE INDEX` statement. Both triggers and indices will be created in the default `onCreate` function.
To create them in `onUpgrade`, use the new `createIndex` and `createTrigger` functions on a `Migrator`.
- Support for moor-file queries that run on initialization ([#280](https://github.com/simolus3/moor/issues/280))
Declare them like this `@create: INSERT INTO users VALUES ('default', 'user')`
## 2.2.0 ## 2.2.0

View File

@ -33,6 +33,11 @@ class HaveSameType extends TypeRelationship {
final Typeable second; final Typeable second;
HaveSameType(this.first, this.second); HaveSameType(this.first, this.second);
Typeable getOther(Typeable t) {
assert(t == first || t == second);
return t == first ? second : first;
}
} }
/// Dependency declaring that, if no better option is found, [target] should /// Dependency declaring that, if no better option is found, [target] should

View File

@ -2,6 +2,11 @@ part of '../types.dart';
class TypeGraph { class TypeGraph {
final Map<Typeable, ResolvedType> _knownTypes = {}; final Map<Typeable, ResolvedType> _knownTypes = {};
final Map<Typeable, bool> _knownNullability = {};
final List<TypeRelationship> _relationships = [];
final Map<Typeable, List<TypeRelationship>> _edges = {};
TypeGraph(); TypeGraph();
@ -11,6 +16,80 @@ class TypeGraph {
void operator []=(Typeable t, ResolvedType type) { void operator []=(Typeable t, ResolvedType type) {
_knownTypes[t] = type; _knownTypes[t] = type;
if (type.nullable != null) {
// nullability is known
_knownNullability[t] = type.nullable;
}
}
bool knowsType(Typeable t) => _knownTypes.containsKey(t);
void addRelation(TypeRelationship relation) {
_relationships.add(relation);
}
void performResolve() {
_indexRelationships();
final queue = List.of(_knownTypes.keys);
while (queue.isNotEmpty) {
_propagateTypeInfo(queue, queue.removeLast());
}
}
void _propagateTypeInfo(List<Typeable> resolved, Typeable t) {
if (!_edges.containsKey(t)) return;
for (final edge in _edges[t]) {
if (edge is CopyTypeFrom) {
_copyType(resolved, edge.other, edge.target);
} else if (edge is HaveSameType) {
_copyType(resolved, t, edge.getOther(t));
}
}
}
void _copyType(List<Typeable> resolved, Typeable from, Typeable to) {
// if the target hasn't been resolved yet, copy the current type and
// visit the target later
if (!knowsType(to)) {
this[to] = this[from];
resolved.add(to);
}
}
void _indexRelationships() {
_edges.clear();
void put(Typeable t, TypeRelationship r) {
_edges.putIfAbsent(t, () => []).add(r);
}
void putAll(Iterable<Typeable> t, TypeRelationship r) {
for (final element in t) {
put(element, r);
}
}
for (final relation in _relationships) {
if (relation is NullableIfSomeOtherIs) {
putAll(relation.other, relation);
} else if (relation is CopyTypeFrom) {
put(relation.other, relation);
} else if (relation is CopyEncapsulating) {
putAll(relation.from, relation);
} else if (relation is HaveSameType) {
put(relation.first, relation);
put(relation.second, relation);
} else if (relation is DefaultType) {
put(relation.target, relation);
} else if (relation is CopyAndCast) {
put(relation.other, relation);
} else {
throw AssertionError('Unknown type relation: $relation');
}
}
} }
} }

View File

@ -5,8 +5,9 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
TypeResolver(this.session); TypeResolver(this.session);
void start(AstNode root) { void run(AstNode root) {
visit(root, const NoTypeExpectation()); visit(root, const NoTypeExpectation());
session.finish();
} }
@override @override

View File

@ -33,13 +33,17 @@ class TypeInferenceSession {
return graph[t]; return graph[t];
} }
void addRelationship(TypeRelationship relationship) {} void addRelationship(TypeRelationship relationship) {
graph.addRelation(relationship);
}
void expectIsPossible(ResolvedType r, TypeExpectation expectation) {} void expectIsPossible(ResolvedType r, TypeExpectation expectation) {}
void hintNullability(Typeable t, bool nullable) { void hintNullability(Typeable t, bool nullable) {
assert(nullable != null); assert(nullable != null);
} }
void finish() {}
} }
/// Keeps track of resolved variable types so that they can be re-used. /// Keeps track of resolved variable types so that they can be re-used.

View File

@ -0,0 +1,19 @@
import 'package:sqlparser/sqlparser.dart';
import 'package:sqlparser/src/analysis/types2/types.dart';
import 'package:test/test.dart';
class _FakeTypeable implements Typeable {}
void main() {
test('copies types for a CopyTypeFrom relation', () {
final first = _FakeTypeable();
final second = _FakeTypeable();
final graph = TypeGraph();
graph[first] = const ResolvedType.bool();
graph.addRelation(CopyTypeFrom(second, first));
graph.performResolve();
expect(graph[second], const ResolvedType.bool());
});
}

View File

@ -11,7 +11,7 @@ void main() {
TypeResolver _obtainResolver(String sql) { TypeResolver _obtainResolver(String sql) {
final context = engine.analyze(sql); final context = engine.analyze(sql);
return TypeResolver(TypeInferenceSession(context))..start(context.root); return TypeResolver(TypeInferenceSession(context))..run(context.root);
} }
ResolvedType _resolveFirstVariable(String sql) { ResolvedType _resolveFirstVariable(String sql) {