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;
```
- 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

View File

@ -33,6 +33,11 @@ class HaveSameType extends TypeRelationship {
final Typeable 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

View File

@ -2,6 +2,11 @@ part of '../types.dart';
class TypeGraph {
final Map<Typeable, ResolvedType> _knownTypes = {};
final Map<Typeable, bool> _knownNullability = {};
final List<TypeRelationship> _relationships = [];
final Map<Typeable, List<TypeRelationship>> _edges = {};
TypeGraph();
@ -11,6 +16,80 @@ class TypeGraph {
void operator []=(Typeable t, ResolvedType 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);
void start(AstNode root) {
void run(AstNode root) {
visit(root, const NoTypeExpectation());
session.finish();
}
@override

View File

@ -33,13 +33,17 @@ class TypeInferenceSession {
return graph[t];
}
void addRelationship(TypeRelationship relationship) {}
void addRelationship(TypeRelationship relationship) {
graph.addRelation(relationship);
}
void expectIsPossible(ResolvedType r, TypeExpectation expectation) {}
void hintNullability(Typeable t, bool nullable) {
assert(nullable != null);
}
void finish() {}
}
/// 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) {
final context = engine.analyze(sql);
return TypeResolver(TypeInferenceSession(context))..start(context.root);
return TypeResolver(TypeInferenceSession(context))..run(context.root);
}
ResolvedType _resolveFirstVariable(String sql) {