mirror of https://github.com/AMT-Cheif/drift.git
Fix nullability propagation for type analysis
This commit is contained in:
parent
a463476c44
commit
9c80fb047b
|
@ -35,7 +35,8 @@ class ResolvedType {
|
||||||
ResolvedType get withoutNullabilityInfo {
|
ResolvedType get withoutNullabilityInfo {
|
||||||
return nullable == null
|
return nullable == null
|
||||||
? this
|
? this
|
||||||
: ResolvedType(type: type, hint: hint, isArray: isArray);
|
: ResolvedType(
|
||||||
|
type: type, hint: hint, isArray: isArray, nullable: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResolvedType withNullable(bool nullable) {
|
ResolvedType withNullable(bool nullable) {
|
||||||
|
|
|
@ -41,6 +41,14 @@ class TypeGraph {
|
||||||
bool knowsType(Typeable? t) =>
|
bool knowsType(Typeable? t) =>
|
||||||
_knownTypes.containsKey(variables.normalize(t));
|
_knownTypes.containsKey(variables.normalize(t));
|
||||||
|
|
||||||
|
bool knowsNullability(Typeable? t) {
|
||||||
|
final normalized = variables.normalize(t);
|
||||||
|
final knownType = _knownTypes[normalized];
|
||||||
|
|
||||||
|
return (knownType != null && knownType.nullable != null) ||
|
||||||
|
_knownNullability.containsKey(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
void addRelation(TypeRelation relation) {
|
void addRelation(TypeRelation relation) {
|
||||||
_relations.add(relation);
|
_relations.add(relation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -323,7 +323,7 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
|
||||||
break;
|
break;
|
||||||
case TokenType.doublePipe:
|
case TokenType.doublePipe:
|
||||||
// string concatenation.
|
// string concatenation.
|
||||||
session._checkAndResolve(e, _textType, arg);
|
session._checkAndResolve(e, _textType.withoutNullabilityInfo, arg);
|
||||||
session._addRelation(NullableIfSomeOtherIs(e, [e.left, e.right]));
|
session._addRelation(NullableIfSomeOtherIs(e, [e.left, e.right]));
|
||||||
const childExpectation = ExactTypeExpectation.laxly(_textType);
|
const childExpectation = ExactTypeExpectation.laxly(_textType);
|
||||||
visit(e.left, childExpectation);
|
visit(e.left, childExpectation);
|
||||||
|
@ -502,7 +502,7 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
|
||||||
case 'trim':
|
case 'trim':
|
||||||
case 'upper':
|
case 'upper':
|
||||||
nullableIfChildIs();
|
nullableIfChildIs();
|
||||||
return _textType;
|
return _textType.withoutNullabilityInfo;
|
||||||
case 'group_concat':
|
case 'group_concat':
|
||||||
return _textType.withNullable(true);
|
return _textType.withNullable(true);
|
||||||
case 'date':
|
case 'date':
|
||||||
|
@ -668,8 +668,15 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
|
||||||
void _lazyCopy(Typeable to, Typeable? from, {bool makeNullable = false}) {
|
void _lazyCopy(Typeable to, Typeable? from, {bool makeNullable = false}) {
|
||||||
if (session.graph.knowsType(from)) {
|
if (session.graph.knowsType(from)) {
|
||||||
var type = session.typeOf(from)!;
|
var type = session.typeOf(from)!;
|
||||||
if (makeNullable) type = type.withNullable(true);
|
if (makeNullable) {
|
||||||
session._markTypeResolved(to, type);
|
type = type.withNullable(true);
|
||||||
|
session._markTypeResolved(to, type);
|
||||||
|
} else if (session.graph.knowsNullability(from)) {
|
||||||
|
session._markTypeResolved(to, type);
|
||||||
|
} else {
|
||||||
|
session._markTypeResolved(to, type);
|
||||||
|
session._addRelation(NullableIfSomeOtherIs(to, [from!]));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
session._addRelation(CopyTypeFrom(to, from, makeNullable: makeNullable));
|
session._addRelation(CopyTypeFrom(to, from, makeNullable: makeNullable));
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ WITH RECURSIVE
|
||||||
SELECT x+1 FROM cnt
|
SELECT x+1 FROM cnt
|
||||||
LIMIT 1000000
|
LIMIT 1000000
|
||||||
)
|
)
|
||||||
SELECT x FROM cnt;
|
SELECT x FROM cnt;
|
||||||
''');
|
''');
|
||||||
|
|
||||||
final expressions = content.root.allDescendants.whereType<Expression>();
|
final expressions = content.root.allDescendants.whereType<Expression>();
|
||||||
|
@ -92,4 +92,20 @@ WITH RECURSIVE
|
||||||
everyElement(isNotNull),
|
everyElement(isNotNull),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('concatenation is nullable when any part is', () {
|
||||||
|
// https://github.com/simolus3/moor/issues/1719
|
||||||
|
|
||||||
|
final engine = SqlEngine()
|
||||||
|
..registerTableFromSql('CREATE TABLE foobar (foo TEXT, bar TEXT);');
|
||||||
|
|
||||||
|
final content =
|
||||||
|
engine.analyze("SELECT foo, bar, foo || ' ' || bar FROM foobar;");
|
||||||
|
|
||||||
|
final columns = (content.root as SelectStatement).resolvedColumns!;
|
||||||
|
expect(
|
||||||
|
columns.map(content.typeOf),
|
||||||
|
everyElement(isA<ResolveResult>()
|
||||||
|
.having((e) => e.type?.nullable, 'type.nullable', isTrue)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue