mirror of https://github.com/AMT-Cheif/drift.git
New type resolver: Start propagating known types
This commit is contained in:
parent
373ad320c4
commit
c95a5f0aad
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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());
|
||||||
|
});
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue