Make `sum()` nullable

This commit is contained in:
Simon Binder 2023-10-22 12:33:03 +02:00
parent 61bd7f5ec2
commit d70de95382
5 changed files with 37 additions and 7 deletions

View File

@ -91,6 +91,13 @@ class CopyAndCast extends TypeRelation implements DirectedRelation {
final Typeable other;
final CastMode cast;
final bool dropTypeHint;
final bool makeNullable;
CopyAndCast(this.target, this.other, this.cast, {this.dropTypeHint = false});
CopyAndCast(
this.target,
this.other,
this.cast, {
this.dropTypeHint = false,
this.makeNullable = false,
});
}

View File

@ -112,8 +112,12 @@ class TypeGraph {
_copyType(resolved, t, other);
}
} else if (edge is CopyAndCast) {
_copyType(resolved, t, edge.target,
this[t]!.cast(edge.cast, edge.dropTypeHint));
var copied = this[t]!.cast(edge.cast, edge.dropTypeHint);
if (edge.makeNullable) {
copied = copied.withNullable(true);
}
_copyType(resolved, t, edge.target, copied);
} else if (edge is MultiSourceRelation) {
// handle many-to-one changes, if all targets have been resolved or
// lax handling is enabled.

View File

@ -549,10 +549,18 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
// ignore: dead_code
throw AssertionError(); // required so that this switch compiles
case 'sum':
checkArgumentCount(1);
// The result of `sum()` is `NULL` if there are no input rows.
session._addRelation(CopyAndCast(
e,
params.first,
CastMode.numeric,
dropTypeHint: true,
makeNullable: true,
));
session._addRelation(
CopyAndCast(e, params.first, CastMode.numeric, dropTypeHint: true));
session._addRelation(DefaultType(e, defaultType: _realType));
nullableIfChildIs();
DefaultType(e, defaultType: _realType.withNullable(true)));
return null;
case 'lower':
case 'ltrim':

View File

@ -15,4 +15,14 @@ void main() {
message: 'iif expects 3 arguments, got 2.'),
]);
});
test('sum', () {
final engine = SqlEngine();
final result = engine.analyze('SELECT sum(1, 2, 3)');
result.expectError(
'1, 2, 3',
type: AnalysisErrorType.invalidAmountOfParameters,
);
});
}

View File

@ -53,7 +53,8 @@ const Map<String, ResolvedType?> _types = {
"SELECT 'a' -> ? = 'b'": ResolvedType(type: BasicType.text, nullable: false),
"SELECT 'a' ->> ? = 'b'": ResolvedType(type: BasicType.text, nullable: false),
'SELECT MAX(id, ?) FROM demo': ResolvedType(type: BasicType.int),
'SELECT SUM(id = 2) = ? FROM demo': ResolvedType(type: BasicType.int),
'SELECT SUM(id = 2) = ? FROM demo':
ResolvedType(type: BasicType.int, nullable: true),
"SELECT unixepoch('now') = ?":
ResolvedType(type: BasicType.int, nullable: true, hints: [IsDateTime()]),
"SELECT datetime('now') = ?":