@quereus/quereus 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/src/common/datatype.d.ts +4 -5
- package/dist/src/common/datatype.d.ts.map +1 -1
- package/dist/src/common/datatype.js.map +1 -1
- package/dist/src/common/type-inference.d.ts +3 -6
- package/dist/src/common/type-inference.d.ts.map +1 -1
- package/dist/src/common/type-inference.js +17 -22
- package/dist/src/common/type-inference.js.map +1 -1
- package/dist/src/core/param.d.ts.map +1 -1
- package/dist/src/core/param.js +3 -18
- package/dist/src/core/param.js.map +1 -1
- package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
- package/dist/src/func/builtins/aggregate.js +24 -2
- package/dist/src/func/builtins/aggregate.js.map +1 -1
- package/dist/src/func/builtins/builtin-window-functions.js +10 -10
- package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
- package/dist/src/func/builtins/conversion.d.ts +10 -0
- package/dist/src/func/builtins/conversion.d.ts.map +1 -1
- package/dist/src/func/builtins/conversion.js +20 -1
- package/dist/src/func/builtins/conversion.js.map +1 -1
- package/dist/src/func/builtins/explain.js +53 -53
- package/dist/src/func/builtins/explain.js.map +1 -1
- package/dist/src/func/builtins/generation.js +2 -2
- package/dist/src/func/builtins/generation.js.map +1 -1
- package/dist/src/func/builtins/index.d.ts.map +1 -1
- package/dist/src/func/builtins/index.js +16 -1
- package/dist/src/func/builtins/index.js.map +1 -1
- package/dist/src/func/builtins/json-tvf.js +17 -17
- package/dist/src/func/builtins/json-tvf.js.map +1 -1
- package/dist/src/func/builtins/scalar.d.ts.map +1 -1
- package/dist/src/func/builtins/scalar.js +202 -13
- package/dist/src/func/builtins/scalar.js.map +1 -1
- package/dist/src/func/builtins/schema.js +18 -18
- package/dist/src/func/builtins/schema.js.map +1 -1
- package/dist/src/func/builtins/string.d.ts.map +1 -1
- package/dist/src/func/builtins/string.js +56 -47
- package/dist/src/func/builtins/string.js.map +1 -1
- package/dist/src/func/builtins/timespan.d.ts +45 -0
- package/dist/src/func/builtins/timespan.d.ts.map +1 -0
- package/dist/src/func/builtins/timespan.js +147 -0
- package/dist/src/func/builtins/timespan.js.map +1 -0
- package/dist/src/func/registration.d.ts +26 -0
- package/dist/src/func/registration.d.ts.map +1 -1
- package/dist/src/func/registration.js +9 -5
- package/dist/src/func/registration.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/parser.js +2 -2
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/planner/building/constraint-builder.js +2 -2
- package/dist/src/planner/building/constraint-builder.js.map +1 -1
- package/dist/src/planner/building/delete.js +3 -3
- package/dist/src/planner/building/delete.js.map +1 -1
- package/dist/src/planner/building/function-call.d.ts.map +1 -1
- package/dist/src/planner/building/function-call.js +24 -4
- package/dist/src/planner/building/function-call.js.map +1 -1
- package/dist/src/planner/building/insert.js +3 -3
- package/dist/src/planner/building/insert.js.map +1 -1
- package/dist/src/planner/building/select.d.ts.map +1 -1
- package/dist/src/planner/building/select.js +3 -2
- package/dist/src/planner/building/select.js.map +1 -1
- package/dist/src/planner/building/update.js +7 -7
- package/dist/src/planner/building/update.js.map +1 -1
- package/dist/src/planner/nodes/aggregate-function.d.ts +2 -1
- package/dist/src/planner/nodes/aggregate-function.d.ts.map +1 -1
- package/dist/src/planner/nodes/aggregate-function.js +10 -3
- package/dist/src/planner/nodes/aggregate-function.js.map +1 -1
- package/dist/src/planner/nodes/cte-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/cte-node.js +2 -2
- package/dist/src/planner/nodes/cte-node.js.map +1 -1
- package/dist/src/planner/nodes/declarative-schema.js +3 -3
- package/dist/src/planner/nodes/declarative-schema.js.map +1 -1
- package/dist/src/planner/nodes/function.d.ts +2 -1
- package/dist/src/planner/nodes/function.d.ts.map +1 -1
- package/dist/src/planner/nodes/function.js +6 -3
- package/dist/src/planner/nodes/function.js.map +1 -1
- package/dist/src/planner/nodes/insert-node.js +1 -1
- package/dist/src/planner/nodes/insert-node.js.map +1 -1
- package/dist/src/planner/nodes/pragma.d.ts +1 -1
- package/dist/src/planner/nodes/pragma.d.ts.map +1 -1
- package/dist/src/planner/nodes/pragma.js +3 -3
- package/dist/src/planner/nodes/pragma.js.map +1 -1
- package/dist/src/planner/nodes/reference.js +1 -1
- package/dist/src/planner/nodes/reference.js.map +1 -1
- package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
- package/dist/src/planner/nodes/scalar.js +55 -101
- package/dist/src/planner/nodes/scalar.js.map +1 -1
- package/dist/src/planner/nodes/sequencing-node.js +2 -2
- package/dist/src/planner/nodes/sequencing-node.js.map +1 -1
- package/dist/src/planner/nodes/sink-node.js +2 -2
- package/dist/src/planner/nodes/sink-node.js.map +1 -1
- package/dist/src/planner/nodes/subquery.d.ts.map +1 -1
- package/dist/src/planner/nodes/subquery.js +4 -7
- package/dist/src/planner/nodes/subquery.js.map +1 -1
- package/dist/src/planner/nodes/view-reference-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/view-reference-node.js +2 -2
- package/dist/src/planner/nodes/view-reference-node.js.map +1 -1
- package/dist/src/planner/nodes/window-function.js +3 -3
- package/dist/src/planner/nodes/window-function.js.map +1 -1
- package/dist/src/planner/rules/access/rule-select-access-path.js +1 -1
- package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +1 -1
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
- package/dist/src/planner/scopes/global.js +3 -3
- package/dist/src/planner/scopes/global.js.map +1 -1
- package/dist/src/planner/scopes/param.d.ts.map +1 -1
- package/dist/src/planner/scopes/param.js +2 -2
- package/dist/src/planner/scopes/param.js.map +1 -1
- package/dist/src/planner/type-utils.d.ts +2 -12
- package/dist/src/planner/type-utils.d.ts.map +1 -1
- package/dist/src/planner/type-utils.js +6 -21
- package/dist/src/planner/type-utils.js.map +1 -1
- package/dist/src/runtime/emit/binary.d.ts.map +1 -1
- package/dist/src/runtime/emit/binary.js +40 -2
- package/dist/src/runtime/emit/binary.js.map +1 -1
- package/dist/src/runtime/emit/set-operation.d.ts.map +1 -1
- package/dist/src/runtime/emit/set-operation.js +33 -22
- package/dist/src/runtime/emit/set-operation.js.map +1 -1
- package/dist/src/runtime/emit/temporal-arithmetic.d.ts +33 -0
- package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -0
- package/dist/src/runtime/emit/temporal-arithmetic.js +269 -0
- package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -0
- package/dist/src/runtime/emit/unary.d.ts.map +1 -1
- package/dist/src/runtime/emit/unary.js +12 -0
- package/dist/src/runtime/emit/unary.js.map +1 -1
- package/dist/src/schema/catalog.js +3 -3
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/column.d.ts +0 -3
- package/dist/src/schema/column.d.ts.map +1 -1
- package/dist/src/schema/column.js +0 -2
- package/dist/src/schema/column.js.map +1 -1
- package/dist/src/schema/function.d.ts +29 -1
- package/dist/src/schema/function.d.ts.map +1 -1
- package/dist/src/schema/function.js.map +1 -1
- package/dist/src/schema/table.d.ts +3 -3
- package/dist/src/schema/table.d.ts.map +1 -1
- package/dist/src/schema/table.js +4 -6
- package/dist/src/schema/table.js.map +1 -1
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/registry.d.ts.map +1 -1
- package/dist/src/types/registry.js +5 -1
- package/dist/src/types/registry.js.map +1 -1
- package/dist/src/types/temporal-types.d.ts +5 -0
- package/dist/src/types/temporal-types.d.ts.map +1 -1
- package/dist/src/types/temporal-types.js +122 -0
- package/dist/src/types/temporal-types.js.map +1 -1
- package/dist/src/util/ast-stringify.js +1 -1
- package/dist/src/util/ast-stringify.js.map +1 -1
- package/dist/src/util/plan-formatter.d.ts.map +1 -1
- package/dist/src/util/plan-formatter.js +1 -5
- package/dist/src/util/plan-formatter.js.map +1 -1
- package/dist/src/util/row-descriptor.js +2 -2
- package/dist/src/util/row-descriptor.js.map +1 -1
- package/dist/src/vtab/best-access-plan.d.ts +4 -3
- package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
- package/dist/src/vtab/best-access-plan.js.map +1 -1
- package/dist/src/vtab/memory/module.js +1 -1
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/package.json +1 -1
- package/src/common/datatype.ts +4 -5
- package/src/common/type-inference.ts +13 -22
- package/src/core/param.ts +4 -11
- package/src/func/builtins/aggregate.ts +24 -2
- package/src/func/builtins/builtin-window-functions.ts +10 -10
- package/src/func/builtins/conversion.ts +26 -1
- package/src/func/builtins/explain.ts +53 -53
- package/src/func/builtins/generation.ts +2 -2
- package/src/func/builtins/index.ts +20 -1
- package/src/func/builtins/json-tvf.ts +17 -17
- package/src/func/builtins/scalar.ts +205 -14
- package/src/func/builtins/schema.ts +18 -18
- package/src/func/builtins/string.ts +91 -78
- package/src/func/builtins/timespan.ts +179 -0
- package/src/func/registration.ts +35 -5
- package/src/index.ts +2 -1
- package/src/parser/parser.ts +2 -2
- package/src/planner/building/constraint-builder.ts +2 -2
- package/src/planner/building/delete.ts +3 -3
- package/src/planner/building/function-call.ts +44 -3
- package/src/planner/building/insert.ts +3 -3
- package/src/planner/building/select.ts +3 -2
- package/src/planner/building/update.ts +7 -7
- package/src/planner/nodes/aggregate-function.ts +13 -3
- package/src/planner/nodes/cte-node.ts +2 -2
- package/src/planner/nodes/declarative-schema.ts +3 -3
- package/src/planner/nodes/function.ts +8 -3
- package/src/planner/nodes/insert-node.ts +1 -1
- package/src/planner/nodes/pragma.ts +4 -3
- package/src/planner/nodes/reference.ts +1 -1
- package/src/planner/nodes/scalar.ts +54 -102
- package/src/planner/nodes/sequencing-node.ts +2 -2
- package/src/planner/nodes/sink-node.ts +2 -2
- package/src/planner/nodes/subquery.ts +5 -7
- package/src/planner/nodes/view-reference-node.ts +2 -2
- package/src/planner/nodes/window-function.ts +3 -3
- package/src/planner/rules/access/rule-select-access-path.ts +1 -1
- package/src/planner/rules/retrieve/rule-grow-retrieve.ts +1 -1
- package/src/planner/scopes/global.ts +3 -3
- package/src/planner/scopes/param.ts +2 -2
- package/src/planner/type-utils.ts +6 -14
- package/src/runtime/emit/binary.ts +48 -2
- package/src/runtime/emit/set-operation.ts +52 -22
- package/src/runtime/emit/temporal-arithmetic.ts +302 -0
- package/src/runtime/emit/unary.ts +13 -0
- package/src/schema/catalog.ts +3 -3
- package/src/schema/column.ts +0 -3
- package/src/schema/function.ts +29 -1
- package/src/schema/table.ts +5 -7
- package/src/types/index.ts +1 -1
- package/src/types/registry.ts +5 -1
- package/src/types/temporal-types.ts +123 -0
- package/src/util/ast-stringify.ts +1 -1
- package/src/util/plan-formatter.ts +1 -4
- package/src/util/row-descriptor.ts +2 -2
- package/src/vtab/best-access-plan.ts +4 -3
- package/src/vtab/memory/module.ts +1 -1
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import { createAggregateFunction, createScalarFunction, createTableValuedFunction } from '../registration.js';
|
|
2
|
-
import type { Row, SqlValue } from '../../common/types.js';
|
|
2
|
+
import type { Row, SqlValue, DeepReadonly } from '../../common/types.js';
|
|
3
3
|
import { createLogger } from '../../common/logger.js';
|
|
4
4
|
import { simpleLike, simpleGlob } from '../../util/patterns.js';
|
|
5
|
+
import { INTEGER_TYPE, TEXT_TYPE } from '../../types/builtin-types.js';
|
|
6
|
+
import type { LogicalType } from '../../types/logical-type.js';
|
|
5
7
|
|
|
6
8
|
const log = createLogger('func:builtins:scalar');
|
|
7
9
|
const warnLog = log.extend('warn');
|
|
8
10
|
|
|
9
11
|
// --- length(X) ---
|
|
10
12
|
export const lengthFunc = createScalarFunction(
|
|
11
|
-
{
|
|
13
|
+
{
|
|
14
|
+
name: 'length',
|
|
15
|
+
numArgs: 1,
|
|
16
|
+
deterministic: true,
|
|
17
|
+
// Type inference: length always returns INTEGER
|
|
18
|
+
inferReturnType: () => ({
|
|
19
|
+
typeClass: 'scalar',
|
|
20
|
+
logicalType: INTEGER_TYPE,
|
|
21
|
+
nullable: false,
|
|
22
|
+
isReadOnly: true
|
|
23
|
+
})
|
|
24
|
+
},
|
|
12
25
|
(arg: SqlValue): SqlValue => {
|
|
13
26
|
if (arg === null) return null;
|
|
14
27
|
if (typeof arg === 'string') return arg.length;
|
|
@@ -18,83 +31,62 @@ export const lengthFunc = createScalarFunction(
|
|
|
18
31
|
);
|
|
19
32
|
|
|
20
33
|
// --- substr(X, Y, Z?) --- Also SUBSTRING
|
|
21
|
-
export const substrFunc = createScalarFunction(
|
|
22
|
-
{ name: 'substr', numArgs: -1, deterministic: true },
|
|
23
|
-
(str: SqlValue, start: SqlValue, len?: SqlValue): SqlValue => {
|
|
24
|
-
if (str === null || start === null) return null;
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
let z = len === undefined ? undefined : Number(len);
|
|
35
|
+
const substrImpl = (str: SqlValue, start: SqlValue, len?: SqlValue): SqlValue => {
|
|
36
|
+
if (str === null || start === null) return null;
|
|
29
37
|
|
|
30
|
-
|
|
38
|
+
const s = String(str); // Coerce main arg to string
|
|
39
|
+
let y = Number(start);
|
|
40
|
+
let z = len === undefined ? undefined : Number(len);
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
y = Math.trunc(y);
|
|
34
|
-
z = z === undefined ? undefined : Math.trunc(z);
|
|
42
|
+
if (isNaN(y) || (z !== undefined && isNaN(z))) return null;
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
// SQLite uses 1-based indexing, negative start counts from end
|
|
45
|
+
y = Math.trunc(y);
|
|
46
|
+
z = z === undefined ? undefined : Math.trunc(z);
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} else if (y < 0) {
|
|
42
|
-
begin = strLen + y;
|
|
43
|
-
} else { // y == 0
|
|
44
|
-
begin = 0;
|
|
45
|
-
}
|
|
46
|
-
begin = Math.max(0, begin); // Clamp start index
|
|
47
|
-
|
|
48
|
-
let end: number;
|
|
49
|
-
if (z === undefined) {
|
|
50
|
-
end = strLen; // No length means to end of string
|
|
51
|
-
} else if (z >= 0) {
|
|
52
|
-
end = begin + z;
|
|
53
|
-
} else { // Negative length is not standard SQL, SQLite returns empty string
|
|
54
|
-
end = begin;
|
|
55
|
-
}
|
|
48
|
+
const strLen = s.length;
|
|
49
|
+
let begin: number;
|
|
56
50
|
|
|
57
|
-
|
|
51
|
+
if (y > 0) {
|
|
52
|
+
begin = y - 1;
|
|
53
|
+
} else if (y < 0) {
|
|
54
|
+
begin = strLen + y;
|
|
55
|
+
} else { // y == 0
|
|
56
|
+
begin = 0;
|
|
57
|
+
}
|
|
58
|
+
begin = Math.max(0, begin); // Clamp start index
|
|
59
|
+
|
|
60
|
+
let end: number;
|
|
61
|
+
if (z === undefined) {
|
|
62
|
+
end = strLen; // No length means to end of string
|
|
63
|
+
} else if (z >= 0) {
|
|
64
|
+
end = begin + z;
|
|
65
|
+
} else { // Negative length is not standard SQL, SQLite returns empty string
|
|
66
|
+
end = begin;
|
|
58
67
|
}
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
export const substringFunc = createScalarFunction(
|
|
62
|
-
{ name: 'substring', numArgs: -1, deterministic: true },
|
|
63
|
-
(str: SqlValue, start: SqlValue, len?: SqlValue): SqlValue => {
|
|
64
|
-
if (str === null || start === null) return null;
|
|
65
|
-
|
|
66
|
-
const s = String(str);
|
|
67
|
-
let y = Number(start);
|
|
68
|
-
let z = len === undefined ? undefined : Number(len);
|
|
69
|
-
|
|
70
|
-
if (isNaN(y) || (z !== undefined && isNaN(z))) return null;
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
return s.substring(begin, end);
|
|
70
|
+
};
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
const substrTypeInference = {
|
|
73
|
+
// Type inference: substr always returns TEXT
|
|
74
|
+
inferReturnType: (_argTypes: ReadonlyArray<DeepReadonly<LogicalType>>) => ({
|
|
75
|
+
typeClass: 'scalar' as const,
|
|
76
|
+
logicalType: TEXT_TYPE,
|
|
77
|
+
nullable: false,
|
|
78
|
+
isReadOnly: true
|
|
79
|
+
})
|
|
80
|
+
};
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
} else {
|
|
83
|
-
begin = 0;
|
|
84
|
-
}
|
|
85
|
-
begin = Math.max(0, begin);
|
|
86
|
-
|
|
87
|
-
let end: number;
|
|
88
|
-
if (z === undefined) {
|
|
89
|
-
end = strLen;
|
|
90
|
-
} else if (z >= 0) {
|
|
91
|
-
end = begin + z;
|
|
92
|
-
} else {
|
|
93
|
-
end = begin;
|
|
94
|
-
}
|
|
82
|
+
export const substrFunc = createScalarFunction(
|
|
83
|
+
{ name: 'substr', numArgs: -1, deterministic: true, ...substrTypeInference },
|
|
84
|
+
substrImpl
|
|
85
|
+
);
|
|
95
86
|
|
|
96
|
-
|
|
97
|
-
}
|
|
87
|
+
export const substringFunc = createScalarFunction(
|
|
88
|
+
{ name: 'substring', numArgs: -1, deterministic: true, ...substrTypeInference },
|
|
89
|
+
substrImpl
|
|
98
90
|
);
|
|
99
91
|
|
|
100
92
|
export const likeFunc = createScalarFunction(
|
|
@@ -113,9 +105,19 @@ export const globFunc = createScalarFunction(
|
|
|
113
105
|
}
|
|
114
106
|
);
|
|
115
107
|
|
|
108
|
+
// Common type inference for string functions that return TEXT
|
|
109
|
+
const textReturnTypeInference = {
|
|
110
|
+
inferReturnType: (_argTypes: ReadonlyArray<DeepReadonly<LogicalType>>) => ({
|
|
111
|
+
typeClass: 'scalar' as const,
|
|
112
|
+
logicalType: TEXT_TYPE,
|
|
113
|
+
nullable: false,
|
|
114
|
+
isReadOnly: true
|
|
115
|
+
})
|
|
116
|
+
};
|
|
117
|
+
|
|
116
118
|
// --- trim(X, Y?) ---
|
|
117
119
|
export const trimFunc = createScalarFunction(
|
|
118
|
-
{ name: 'trim', numArgs: -1, deterministic: true },
|
|
120
|
+
{ name: 'trim', numArgs: -1, deterministic: true, ...textReturnTypeInference },
|
|
119
121
|
(strVal: SqlValue, charsVal?: SqlValue): SqlValue => {
|
|
120
122
|
if (strVal === null) return null;
|
|
121
123
|
const str = String(strVal);
|
|
@@ -138,7 +140,7 @@ export const trimFunc = createScalarFunction(
|
|
|
138
140
|
|
|
139
141
|
// --- ltrim(X, Y?) ---
|
|
140
142
|
export const ltrimFunc = createScalarFunction(
|
|
141
|
-
{ name: 'ltrim', numArgs: -1, deterministic: true },
|
|
143
|
+
{ name: 'ltrim', numArgs: -1, deterministic: true, ...textReturnTypeInference },
|
|
142
144
|
(strVal: SqlValue, charsVal?: SqlValue): SqlValue => {
|
|
143
145
|
if (strVal === null) return null;
|
|
144
146
|
const str = String(strVal);
|
|
@@ -161,7 +163,7 @@ export const ltrimFunc = createScalarFunction(
|
|
|
161
163
|
|
|
162
164
|
// --- rtrim(X, Y?) ---
|
|
163
165
|
export const rtrimFunc = createScalarFunction(
|
|
164
|
-
{ name: 'rtrim', numArgs: -1, deterministic: true },
|
|
166
|
+
{ name: 'rtrim', numArgs: -1, deterministic: true, ...textReturnTypeInference },
|
|
165
167
|
(strVal: SqlValue, charsVal?: SqlValue): SqlValue => {
|
|
166
168
|
if (strVal === null) return null;
|
|
167
169
|
const str = String(strVal);
|
|
@@ -184,7 +186,7 @@ export const rtrimFunc = createScalarFunction(
|
|
|
184
186
|
|
|
185
187
|
// --- replace(X, Y, Z) ---
|
|
186
188
|
export const replaceFunc = createScalarFunction(
|
|
187
|
-
{ name: 'replace', numArgs: 3, deterministic: true },
|
|
189
|
+
{ name: 'replace', numArgs: 3, deterministic: true, ...textReturnTypeInference },
|
|
188
190
|
(strVal: SqlValue, patternVal: SqlValue, replacementVal: SqlValue): SqlValue => {
|
|
189
191
|
if (strVal === null || patternVal === null || replacementVal === null) return null;
|
|
190
192
|
|
|
@@ -199,7 +201,18 @@ export const replaceFunc = createScalarFunction(
|
|
|
199
201
|
|
|
200
202
|
// --- instr(X, Y) ---
|
|
201
203
|
export const instrFunc = createScalarFunction(
|
|
202
|
-
{
|
|
204
|
+
{
|
|
205
|
+
name: 'instr',
|
|
206
|
+
numArgs: 2,
|
|
207
|
+
deterministic: true,
|
|
208
|
+
// Type inference: instr returns INTEGER
|
|
209
|
+
inferReturnType: () => ({
|
|
210
|
+
typeClass: 'scalar',
|
|
211
|
+
logicalType: INTEGER_TYPE,
|
|
212
|
+
nullable: false,
|
|
213
|
+
isReadOnly: true
|
|
214
|
+
})
|
|
215
|
+
},
|
|
203
216
|
(strVal: SqlValue, subVal: SqlValue): SqlValue => {
|
|
204
217
|
if (strVal === null || subVal === null) return 0;
|
|
205
218
|
|
|
@@ -216,7 +229,7 @@ export const instrFunc = createScalarFunction(
|
|
|
216
229
|
|
|
217
230
|
// String reverse function
|
|
218
231
|
export const reverseFunc = createScalarFunction(
|
|
219
|
-
{ name: 'reverse', numArgs: 1, deterministic: true },
|
|
232
|
+
{ name: 'reverse', numArgs: 1, deterministic: true, ...textReturnTypeInference },
|
|
220
233
|
(str: SqlValue): SqlValue => {
|
|
221
234
|
if (typeof str !== 'string') return null;
|
|
222
235
|
return str.split('').reverse().join('');
|
|
@@ -225,7 +238,7 @@ export const reverseFunc = createScalarFunction(
|
|
|
225
238
|
|
|
226
239
|
// Left padding function
|
|
227
240
|
export const lpadFunc = createScalarFunction(
|
|
228
|
-
{ name: 'lpad', numArgs: 3, deterministic: true },
|
|
241
|
+
{ name: 'lpad', numArgs: 3, deterministic: true, ...textReturnTypeInference },
|
|
229
242
|
(str: SqlValue, len: SqlValue, pad: SqlValue): SqlValue => {
|
|
230
243
|
if (typeof str !== 'string' || typeof len !== 'number' || typeof pad !== 'string') return null;
|
|
231
244
|
|
|
@@ -239,7 +252,7 @@ export const lpadFunc = createScalarFunction(
|
|
|
239
252
|
|
|
240
253
|
// Right padding function
|
|
241
254
|
export const rpadFunc = createScalarFunction(
|
|
242
|
-
{ name: 'rpad', numArgs: 3, deterministic: true },
|
|
255
|
+
{ name: 'rpad', numArgs: 3, deterministic: true, ...textReturnTypeInference },
|
|
243
256
|
(str: SqlValue, len: SqlValue, pad: SqlValue): SqlValue => {
|
|
244
257
|
if (typeof str !== 'string' || typeof len !== 'number' || typeof pad !== 'string') return null;
|
|
245
258
|
|
|
@@ -278,7 +291,7 @@ export const stringConcatFunc = createAggregateFunction(
|
|
|
278
291
|
|
|
279
292
|
// --- lower(X) ---
|
|
280
293
|
export const lowerFunc = createScalarFunction(
|
|
281
|
-
{ name: 'lower', numArgs: 1, deterministic: true },
|
|
294
|
+
{ name: 'lower', numArgs: 1, deterministic: true, ...textReturnTypeInference },
|
|
282
295
|
(arg: SqlValue): SqlValue => {
|
|
283
296
|
return typeof arg === 'string' ? arg.toLowerCase() : null;
|
|
284
297
|
}
|
|
@@ -286,7 +299,7 @@ export const lowerFunc = createScalarFunction(
|
|
|
286
299
|
|
|
287
300
|
// --- upper(X) ---
|
|
288
301
|
export const upperFunc = createScalarFunction(
|
|
289
|
-
{ name: 'upper', numArgs: 1, deterministic: true },
|
|
302
|
+
{ name: 'upper', numArgs: 1, deterministic: true, ...textReturnTypeInference },
|
|
290
303
|
(arg: SqlValue): SqlValue => {
|
|
291
304
|
return typeof arg === 'string' ? arg.toUpperCase() : null;
|
|
292
305
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Temporal } from 'temporal-polyfill';
|
|
2
|
+
import { StatusCode, type SqlValue } from '../../common/types.js';
|
|
3
|
+
import { createScalarFunction } from '../registration.js';
|
|
4
|
+
import { QuereusError } from '../../common/errors.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Helper to parse a value as a Temporal.Duration
|
|
8
|
+
*/
|
|
9
|
+
function parseDuration(value: SqlValue): Temporal.Duration | null {
|
|
10
|
+
if (value === null) return null;
|
|
11
|
+
if (typeof value !== 'string') return null;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
return Temporal.Duration.from(value);
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// --- Extraction Functions ---
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* timespan_years() - Extract years component from timespan
|
|
24
|
+
*/
|
|
25
|
+
export const timespanYearsFunc = createScalarFunction(
|
|
26
|
+
{ name: 'timespan_years', numArgs: 1, deterministic: true },
|
|
27
|
+
(value: SqlValue): SqlValue => {
|
|
28
|
+
const duration = parseDuration(value);
|
|
29
|
+
if (!duration) return null;
|
|
30
|
+
return duration.years;
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* timespan_months() - Extract months component from timespan
|
|
36
|
+
*/
|
|
37
|
+
export const timespanMonthsFunc = createScalarFunction(
|
|
38
|
+
{ name: 'timespan_months', numArgs: 1, deterministic: true },
|
|
39
|
+
(value: SqlValue): SqlValue => {
|
|
40
|
+
const duration = parseDuration(value);
|
|
41
|
+
if (!duration) return null;
|
|
42
|
+
return duration.months;
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* timespan_weeks() - Extract weeks component from timespan
|
|
48
|
+
*/
|
|
49
|
+
export const timespanWeeksFunc = createScalarFunction(
|
|
50
|
+
{ name: 'timespan_weeks', numArgs: 1, deterministic: true },
|
|
51
|
+
(value: SqlValue): SqlValue => {
|
|
52
|
+
const duration = parseDuration(value);
|
|
53
|
+
if (!duration) return null;
|
|
54
|
+
return duration.weeks;
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* timespan_days() - Extract days component from timespan
|
|
60
|
+
*/
|
|
61
|
+
export const timespanDaysFunc = createScalarFunction(
|
|
62
|
+
{ name: 'timespan_days', numArgs: 1, deterministic: true },
|
|
63
|
+
(value: SqlValue): SqlValue => {
|
|
64
|
+
const duration = parseDuration(value);
|
|
65
|
+
if (!duration) return null;
|
|
66
|
+
return duration.days;
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* timespan_hours() - Extract hours component from timespan
|
|
72
|
+
*/
|
|
73
|
+
export const timespanHoursFunc = createScalarFunction(
|
|
74
|
+
{ name: 'timespan_hours', numArgs: 1, deterministic: true },
|
|
75
|
+
(value: SqlValue): SqlValue => {
|
|
76
|
+
const duration = parseDuration(value);
|
|
77
|
+
if (!duration) return null;
|
|
78
|
+
return duration.hours;
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* timespan_minutes() - Extract minutes component from timespan
|
|
84
|
+
*/
|
|
85
|
+
export const timespanMinutesFunc = createScalarFunction(
|
|
86
|
+
{ name: 'timespan_minutes', numArgs: 1, deterministic: true },
|
|
87
|
+
(value: SqlValue): SqlValue => {
|
|
88
|
+
const duration = parseDuration(value);
|
|
89
|
+
if (!duration) return null;
|
|
90
|
+
return duration.minutes;
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* timespan_seconds() - Extract seconds component from timespan
|
|
96
|
+
*/
|
|
97
|
+
export const timespanSecondsFunc = createScalarFunction(
|
|
98
|
+
{ name: 'timespan_seconds', numArgs: 1, deterministic: true },
|
|
99
|
+
(value: SqlValue): SqlValue => {
|
|
100
|
+
const duration = parseDuration(value);
|
|
101
|
+
if (!duration) return null;
|
|
102
|
+
return duration.seconds + duration.milliseconds / 1000 + duration.microseconds / 1000000 + duration.nanoseconds / 1000000000;
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// --- Total Functions ---
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* timespan_total_seconds() - Convert entire timespan to seconds
|
|
110
|
+
*/
|
|
111
|
+
export const timespanTotalSecondsFunc = createScalarFunction(
|
|
112
|
+
{ name: 'timespan_total_seconds', numArgs: 1, deterministic: true },
|
|
113
|
+
(value: SqlValue): SqlValue => {
|
|
114
|
+
const duration = parseDuration(value);
|
|
115
|
+
if (!duration) return null;
|
|
116
|
+
try {
|
|
117
|
+
// Use a reference date for calendar units (weeks, months, years)
|
|
118
|
+
const referenceDate = Temporal.PlainDate.from('2024-01-01');
|
|
119
|
+
return duration.total({ unit: 'seconds', relativeTo: referenceDate });
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* timespan_total_minutes() - Convert entire timespan to minutes
|
|
128
|
+
*/
|
|
129
|
+
export const timespanTotalMinutesFunc = createScalarFunction(
|
|
130
|
+
{ name: 'timespan_total_minutes', numArgs: 1, deterministic: true },
|
|
131
|
+
(value: SqlValue): SqlValue => {
|
|
132
|
+
const duration = parseDuration(value);
|
|
133
|
+
if (!duration) return null;
|
|
134
|
+
try {
|
|
135
|
+
// Use a reference date for calendar units (weeks, months, years)
|
|
136
|
+
const referenceDate = Temporal.PlainDate.from('2024-01-01');
|
|
137
|
+
return duration.total({ unit: 'minutes', relativeTo: referenceDate });
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* timespan_total_hours() - Convert entire timespan to hours
|
|
146
|
+
*/
|
|
147
|
+
export const timespanTotalHoursFunc = createScalarFunction(
|
|
148
|
+
{ name: 'timespan_total_hours', numArgs: 1, deterministic: true },
|
|
149
|
+
(value: SqlValue): SqlValue => {
|
|
150
|
+
const duration = parseDuration(value);
|
|
151
|
+
if (!duration) return null;
|
|
152
|
+
try {
|
|
153
|
+
// Use a reference date for calendar units (weeks, months, years)
|
|
154
|
+
const referenceDate = Temporal.PlainDate.from('2024-01-01');
|
|
155
|
+
return duration.total({ unit: 'hours', relativeTo: referenceDate });
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* timespan_total_days() - Convert entire timespan to days
|
|
164
|
+
*/
|
|
165
|
+
export const timespanTotalDaysFunc = createScalarFunction(
|
|
166
|
+
{ name: 'timespan_total_days', numArgs: 1, deterministic: true },
|
|
167
|
+
(value: SqlValue): SqlValue => {
|
|
168
|
+
const duration = parseDuration(value);
|
|
169
|
+
if (!duration) return null;
|
|
170
|
+
try {
|
|
171
|
+
// Use a reference date for calendar units (weeks, months, years)
|
|
172
|
+
const referenceDate = Temporal.PlainDate.from('2024-01-01');
|
|
173
|
+
return duration.total({ unit: 'days', relativeTo: referenceDate });
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
|
package/src/func/registration.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { AggregateFinalizer, AggregateReducer, IntegratedTableValuedFunc, ScalarFunc, TableValuedFunc, ScalarFunctionSchema,
|
|
2
2
|
TableValuedFunctionSchema, AggregateFunctionSchema } from '../schema/function.js';
|
|
3
3
|
import { FunctionFlags } from '../common/constants.js';
|
|
4
|
-
import { SqlDataType } from '../common/types.js';
|
|
5
4
|
import type { ScalarType, RelationType } from '../common/datatype.js';
|
|
5
|
+
import { REAL_TYPE } from '../types/builtin-types.js';
|
|
6
|
+
import type { LogicalType } from '../types/logical-type.js';
|
|
7
|
+
import type { DeepReadonly } from '../common/types.js';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Configuration options for scalar functions
|
|
@@ -18,6 +20,18 @@ interface ScalarFuncOptions {
|
|
|
18
20
|
deterministic?: boolean;
|
|
19
21
|
/** Return type information */
|
|
20
22
|
returnType?: ScalarType;
|
|
23
|
+
/**
|
|
24
|
+
* Optional type inference function for polymorphic functions.
|
|
25
|
+
* If provided, this function will be called at planning time to determine
|
|
26
|
+
* the return type based on the actual argument types.
|
|
27
|
+
*/
|
|
28
|
+
inferReturnType?: (argTypes: ReadonlyArray<DeepReadonly<LogicalType>>) => ScalarType;
|
|
29
|
+
/**
|
|
30
|
+
* Optional argument type validation function.
|
|
31
|
+
* If provided, this function will be called at planning time to validate
|
|
32
|
+
* that the argument types are acceptable for this function.
|
|
33
|
+
*/
|
|
34
|
+
validateArgTypes?: (argTypes: ReadonlyArray<DeepReadonly<LogicalType>>) => boolean;
|
|
21
35
|
}
|
|
22
36
|
|
|
23
37
|
/**
|
|
@@ -56,6 +70,18 @@ interface AggregateFuncOptions {
|
|
|
56
70
|
initialValue?: AggValue;
|
|
57
71
|
/** Return type information */
|
|
58
72
|
returnType?: ScalarType;
|
|
73
|
+
/**
|
|
74
|
+
* Optional type inference function for polymorphic aggregate functions.
|
|
75
|
+
* If provided, this function will be called at planning time to determine
|
|
76
|
+
* the return type based on the actual argument types.
|
|
77
|
+
*/
|
|
78
|
+
inferReturnType?: (argTypes: ReadonlyArray<DeepReadonly<LogicalType>>) => ScalarType;
|
|
79
|
+
/**
|
|
80
|
+
* Optional argument type validation function.
|
|
81
|
+
* If provided, this function will be called at planning time to validate
|
|
82
|
+
* that the argument types are acceptable for this function.
|
|
83
|
+
*/
|
|
84
|
+
validateArgTypes?: (argTypes: ReadonlyArray<DeepReadonly<LogicalType>>) => boolean;
|
|
59
85
|
}
|
|
60
86
|
|
|
61
87
|
/**
|
|
@@ -69,7 +95,7 @@ interface AggregateFuncOptions {
|
|
|
69
95
|
export function createScalarFunction(options: ScalarFuncOptions, jsFunc: ScalarFunc): ScalarFunctionSchema {
|
|
70
96
|
const returnType: ScalarType = options.returnType ?? {
|
|
71
97
|
typeClass: 'scalar',
|
|
72
|
-
|
|
98
|
+
logicalType: REAL_TYPE,
|
|
73
99
|
nullable: true,
|
|
74
100
|
isReadOnly: true
|
|
75
101
|
};
|
|
@@ -79,7 +105,9 @@ export function createScalarFunction(options: ScalarFuncOptions, jsFunc: ScalarF
|
|
|
79
105
|
numArgs: options.numArgs,
|
|
80
106
|
flags: options.flags ?? (FunctionFlags.UTF8 | (options.deterministic !== false ? FunctionFlags.DETERMINISTIC : 0)),
|
|
81
107
|
returnType,
|
|
82
|
-
implementation: jsFunc
|
|
108
|
+
implementation: jsFunc,
|
|
109
|
+
inferReturnType: options.inferReturnType,
|
|
110
|
+
validateArgTypes: options.validateArgTypes
|
|
83
111
|
};
|
|
84
112
|
}
|
|
85
113
|
|
|
@@ -154,7 +182,7 @@ export function createAggregateFunction(
|
|
|
154
182
|
): AggregateFunctionSchema {
|
|
155
183
|
const returnType: ScalarType = options.returnType ?? {
|
|
156
184
|
typeClass: 'scalar',
|
|
157
|
-
|
|
185
|
+
logicalType: REAL_TYPE,
|
|
158
186
|
nullable: true,
|
|
159
187
|
isReadOnly: true
|
|
160
188
|
};
|
|
@@ -166,6 +194,8 @@ export function createAggregateFunction(
|
|
|
166
194
|
returnType,
|
|
167
195
|
stepFunction: stepFunc,
|
|
168
196
|
finalizeFunction: finalizeFunc,
|
|
169
|
-
initialValue: options.initialValue
|
|
197
|
+
initialValue: options.initialValue,
|
|
198
|
+
inferReturnType: options.inferReturnType,
|
|
199
|
+
validateArgTypes: options.validateArgTypes
|
|
170
200
|
};
|
|
171
201
|
}
|
package/src/index.ts
CHANGED
package/src/parser/parser.ts
CHANGED
|
@@ -1512,8 +1512,8 @@ export class Parser {
|
|
|
1512
1512
|
}
|
|
1513
1513
|
|
|
1514
1514
|
// Function call (with optional window function support)
|
|
1515
|
-
if (this.checkIdentifierLike(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like']) && this.checkNext(1, TokenType.LPAREN)) {
|
|
1516
|
-
const name = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected function name.");
|
|
1515
|
+
if (this.checkIdentifierLike(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like', 'replace']) && this.checkNext(1, TokenType.LPAREN)) {
|
|
1516
|
+
const name = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like', 'replace'], "Expected function name.");
|
|
1517
1517
|
|
|
1518
1518
|
this.consume(TokenType.LPAREN, "Expected '(' after function name.");
|
|
1519
1519
|
|
|
@@ -83,7 +83,7 @@ export function buildConstraintChecks(
|
|
|
83
83
|
if (newAttrId !== undefined) {
|
|
84
84
|
const newColumnType = {
|
|
85
85
|
typeClass: 'scalar' as const,
|
|
86
|
-
|
|
86
|
+
logicalType: tableColumn.logicalType,
|
|
87
87
|
nullable: !tableColumn.notNull,
|
|
88
88
|
isReadOnly: false
|
|
89
89
|
};
|
|
@@ -104,7 +104,7 @@ export function buildConstraintChecks(
|
|
|
104
104
|
if (oldAttrId !== undefined) {
|
|
105
105
|
const oldColumnType = {
|
|
106
106
|
typeClass: 'scalar' as const,
|
|
107
|
-
|
|
107
|
+
logicalType: tableColumn.logicalType,
|
|
108
108
|
nullable: true, // OLD values can be NULL (especially for INSERT)
|
|
109
109
|
isReadOnly: false
|
|
110
110
|
};
|
|
@@ -36,7 +36,7 @@ export function buildDeleteStmt(
|
|
|
36
36
|
name: contextVar.name,
|
|
37
37
|
type: {
|
|
38
38
|
typeClass: 'scalar' as const,
|
|
39
|
-
|
|
39
|
+
logicalType: contextVar.logicalType,
|
|
40
40
|
nullable: !contextVar.notNull,
|
|
41
41
|
isReadOnly: true
|
|
42
42
|
},
|
|
@@ -77,7 +77,7 @@ export function buildDeleteStmt(
|
|
|
77
77
|
name: col.name,
|
|
78
78
|
type: {
|
|
79
79
|
typeClass: 'scalar' as const,
|
|
80
|
-
|
|
80
|
+
logicalType: col.logicalType,
|
|
81
81
|
nullable: !col.notNull,
|
|
82
82
|
isReadOnly: false
|
|
83
83
|
},
|
|
@@ -89,7 +89,7 @@ export function buildDeleteStmt(
|
|
|
89
89
|
name: col.name,
|
|
90
90
|
type: {
|
|
91
91
|
typeClass: 'scalar' as const,
|
|
92
|
-
|
|
92
|
+
logicalType: col.logicalType,
|
|
93
93
|
nullable: true, // NEW values are always NULL for DELETE
|
|
94
94
|
isReadOnly: false
|
|
95
95
|
},
|
|
@@ -5,11 +5,12 @@ import { QuereusError } from "../../common/errors.js";
|
|
|
5
5
|
import { StatusCode } from "../../common/types.js";
|
|
6
6
|
import * as AST from "../../parser/ast.js";
|
|
7
7
|
import { ScalarPlanNode } from "../nodes/plan-node.js";
|
|
8
|
-
import { isAggregateFunctionSchema } from '../../schema/function.js';
|
|
8
|
+
import { isAggregateFunctionSchema, isScalarFunctionSchema } from '../../schema/function.js';
|
|
9
9
|
import { buildExpression } from "./expression.js";
|
|
10
10
|
import { ScalarFunctionCallNode } from "../nodes/function.js";
|
|
11
11
|
import { resolveFunctionSchema } from "./schema-resolution.js";
|
|
12
12
|
import { CapabilityDetectors } from '../framework/characteristics.js';
|
|
13
|
+
import type { ScalarType } from "../../common/datatype.js";
|
|
13
14
|
|
|
14
15
|
export function buildFunctionCall(ctx: PlanningContext, expr: AST.FunctionExpr, allowAggregates: boolean): ScalarPlanNode {
|
|
15
16
|
// In HAVING context, check if this function matches an existing aggregate
|
|
@@ -70,6 +71,25 @@ export function buildFunctionCall(ctx: PlanningContext, expr: AST.FunctionExpr,
|
|
|
70
71
|
// Build arguments for aggregate function
|
|
71
72
|
const args = expr.args.map(arg => buildExpression(ctx, arg, false)); // Aggregates can't contain other aggregates
|
|
72
73
|
|
|
74
|
+
// Perform type inference if available
|
|
75
|
+
let inferredType: ScalarType | undefined;
|
|
76
|
+
if (functionSchema.inferReturnType) {
|
|
77
|
+
const argTypes = args.map(arg => arg.getType().logicalType);
|
|
78
|
+
|
|
79
|
+
// Validate argument types if validator is provided
|
|
80
|
+
if (functionSchema.validateArgTypes && !functionSchema.validateArgTypes(argTypes)) {
|
|
81
|
+
throw new QuereusError(
|
|
82
|
+
`Invalid argument types for aggregate function ${expr.name}`,
|
|
83
|
+
StatusCode.MISMATCH,
|
|
84
|
+
undefined,
|
|
85
|
+
expr.loc?.start.line,
|
|
86
|
+
expr.loc?.start.column
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
inferredType = functionSchema.inferReturnType(argTypes);
|
|
91
|
+
}
|
|
92
|
+
|
|
73
93
|
return new AggregateFunctionCallNode(
|
|
74
94
|
ctx.scope,
|
|
75
95
|
expr,
|
|
@@ -78,11 +98,32 @@ export function buildFunctionCall(ctx: PlanningContext, expr: AST.FunctionExpr,
|
|
|
78
98
|
args,
|
|
79
99
|
expr.distinct ?? false, // Use the distinct field from the AST
|
|
80
100
|
undefined, // orderBy - TODO: parse from expr
|
|
81
|
-
undefined // filter - TODO: parse from expr
|
|
101
|
+
undefined, // filter - TODO: parse from expr
|
|
102
|
+
inferredType
|
|
82
103
|
);
|
|
83
104
|
} else {
|
|
84
105
|
// Regular scalar function
|
|
85
106
|
const args = expr.args.map(arg => buildExpression(ctx, arg, allowAggregates));
|
|
86
|
-
|
|
107
|
+
|
|
108
|
+
// Perform type inference if available
|
|
109
|
+
let inferredType: ScalarType | undefined;
|
|
110
|
+
if (isScalarFunctionSchema(functionSchema) && functionSchema.inferReturnType) {
|
|
111
|
+
const argTypes = args.map(arg => arg.getType().logicalType);
|
|
112
|
+
|
|
113
|
+
// Validate argument types if validator is provided
|
|
114
|
+
if (functionSchema.validateArgTypes && !functionSchema.validateArgTypes(argTypes)) {
|
|
115
|
+
throw new QuereusError(
|
|
116
|
+
`Invalid argument types for function ${expr.name}`,
|
|
117
|
+
StatusCode.MISMATCH,
|
|
118
|
+
undefined,
|
|
119
|
+
expr.loc?.start.line,
|
|
120
|
+
expr.loc?.start.column
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
inferredType = functionSchema.inferReturnType(argTypes);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return new ScalarFunctionCallNode(ctx.scope, expr, functionSchema, args, inferredType);
|
|
87
128
|
}
|
|
88
129
|
}
|