@quereus/quereus 0.5.2 → 0.6.1
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/datetime.js +9 -9
- package/dist/src/func/builtins/datetime.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/json.js +11 -11
- package/dist/src/func/builtins/json.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 +59 -50
- 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/between.js +2 -2
- package/dist/src/runtime/emit/between.js.map +1 -1
- package/dist/src/runtime/emit/binary.d.ts.map +1 -1
- package/dist/src/runtime/emit/binary.js +66 -30
- package/dist/src/runtime/emit/binary.js.map +1 -1
- package/dist/src/runtime/emit/subquery.js +8 -8
- package/dist/src/runtime/emit/subquery.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 +16 -4
- 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/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/datetime.ts +9 -9
- 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/json.ts +11 -11
- package/src/func/builtins/scalar.ts +205 -14
- package/src/func/builtins/schema.ts +18 -18
- package/src/func/builtins/string.ts +93 -80
- 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/between.ts +2 -2
- package/src/runtime/emit/binary.ts +74 -30
- package/src/runtime/emit/subquery.ts +8 -8
- package/src/runtime/emit/temporal-arithmetic.ts +302 -0
- package/src/runtime/emit/unary.ts +17 -4
- 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/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,10 +1,64 @@
|
|
|
1
|
-
import type { SqlValue } from '../../common/types.js';
|
|
1
|
+
import type { SqlValue, DeepReadonly } from '../../common/types.js';
|
|
2
2
|
import { createScalarFunction } from '../registration.js';
|
|
3
3
|
import { compareSqlValues, getSqlDataTypeName } from '../../util/comparison.js';
|
|
4
|
+
import type { LogicalType } from '../../types/logical-type.js';
|
|
5
|
+
import { ANY_TYPE, INTEGER_TYPE, REAL_TYPE } from '../../types/builtin-types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Find the common type among multiple logical types.
|
|
9
|
+
* This implements type promotion rules for polymorphic functions.
|
|
10
|
+
*
|
|
11
|
+
* Rules:
|
|
12
|
+
* 1. If all types are the same, return that type
|
|
13
|
+
* 2. If mixing INTEGER and REAL, return REAL (numeric promotion)
|
|
14
|
+
* 3. Otherwise, return the first type (conservative approach)
|
|
15
|
+
*
|
|
16
|
+
* @param types Array of logical types to find common type for
|
|
17
|
+
* @returns The common logical type
|
|
18
|
+
*/
|
|
19
|
+
function findCommonType(types: ReadonlyArray<DeepReadonly<LogicalType>>): DeepReadonly<LogicalType> {
|
|
20
|
+
if (types.length === 0) return ANY_TYPE;
|
|
21
|
+
if (types.length === 1) return types[0];
|
|
22
|
+
|
|
23
|
+
// Check if all types are the same
|
|
24
|
+
const firstType = types[0];
|
|
25
|
+
const allSame = types.every(t => t.name === firstType.name);
|
|
26
|
+
if (allSame) return firstType;
|
|
27
|
+
|
|
28
|
+
// Check for numeric type promotion (INTEGER + REAL -> REAL)
|
|
29
|
+
const allNumeric = types.every(t => t.isNumeric === true);
|
|
30
|
+
if (allNumeric) {
|
|
31
|
+
// If any type is REAL, return REAL
|
|
32
|
+
const hasReal = types.some(t => t.name === 'REAL');
|
|
33
|
+
if (hasReal) return REAL_TYPE;
|
|
34
|
+
// All INTEGER
|
|
35
|
+
return INTEGER_TYPE;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// For non-numeric types, return the first type (conservative)
|
|
39
|
+
// In a more sophisticated implementation, we could:
|
|
40
|
+
// - Find a common supertype
|
|
41
|
+
// - Return ANY_TYPE if types are incompatible
|
|
42
|
+
// - Throw an error for incompatible types
|
|
43
|
+
return firstType;
|
|
44
|
+
}
|
|
4
45
|
|
|
5
46
|
// --- abs(X) ---
|
|
6
47
|
export const absFunc = createScalarFunction(
|
|
7
|
-
{
|
|
48
|
+
{
|
|
49
|
+
name: 'abs',
|
|
50
|
+
numArgs: 1,
|
|
51
|
+
deterministic: true,
|
|
52
|
+
// Type inference: return the same type as the input for numeric types
|
|
53
|
+
inferReturnType: (argTypes) => ({
|
|
54
|
+
typeClass: 'scalar',
|
|
55
|
+
logicalType: argTypes[0],
|
|
56
|
+
nullable: false,
|
|
57
|
+
isReadOnly: true
|
|
58
|
+
}),
|
|
59
|
+
// Validate that the argument is numeric
|
|
60
|
+
validateArgTypes: (argTypes) => argTypes[0].isNumeric === true
|
|
61
|
+
},
|
|
8
62
|
(arg: SqlValue): SqlValue => {
|
|
9
63
|
if (arg === null) return null;
|
|
10
64
|
if (typeof arg === 'bigint') return arg < 0n ? -arg : arg;
|
|
@@ -16,7 +70,19 @@ export const absFunc = createScalarFunction(
|
|
|
16
70
|
|
|
17
71
|
// --- round(X, Y?) ---
|
|
18
72
|
export const roundFunc = createScalarFunction(
|
|
19
|
-
{
|
|
73
|
+
{
|
|
74
|
+
name: 'round',
|
|
75
|
+
numArgs: -1,
|
|
76
|
+
deterministic: true,
|
|
77
|
+
// Type inference: return the same type as the input for numeric types
|
|
78
|
+
inferReturnType: (argTypes) => ({
|
|
79
|
+
typeClass: 'scalar',
|
|
80
|
+
logicalType: argTypes[0],
|
|
81
|
+
nullable: false,
|
|
82
|
+
isReadOnly: true
|
|
83
|
+
}),
|
|
84
|
+
validateArgTypes: (argTypes) => argTypes[0].isNumeric === true
|
|
85
|
+
},
|
|
20
86
|
(numVal: SqlValue, placesVal?: SqlValue): SqlValue => {
|
|
21
87
|
if (numVal === null) return null;
|
|
22
88
|
const x = Number(numVal);
|
|
@@ -40,7 +106,18 @@ export const roundFunc = createScalarFunction(
|
|
|
40
106
|
|
|
41
107
|
// --- coalesce(...) ---
|
|
42
108
|
export const coalesceFunc = createScalarFunction(
|
|
43
|
-
{
|
|
109
|
+
{
|
|
110
|
+
name: 'coalesce',
|
|
111
|
+
numArgs: -1,
|
|
112
|
+
deterministic: true,
|
|
113
|
+
// Type inference: find the common type among all arguments
|
|
114
|
+
inferReturnType: (argTypes) => ({
|
|
115
|
+
typeClass: 'scalar',
|
|
116
|
+
logicalType: findCommonType(argTypes),
|
|
117
|
+
nullable: true, // coalesce can return null if all args are null
|
|
118
|
+
isReadOnly: true
|
|
119
|
+
})
|
|
120
|
+
},
|
|
44
121
|
(...args: SqlValue[]): SqlValue => {
|
|
45
122
|
for (const arg of args) {
|
|
46
123
|
if (arg !== null) {
|
|
@@ -53,7 +130,18 @@ export const coalesceFunc = createScalarFunction(
|
|
|
53
130
|
|
|
54
131
|
// --- nullif(X, Y) ---
|
|
55
132
|
export const nullifFunc = createScalarFunction(
|
|
56
|
-
{
|
|
133
|
+
{
|
|
134
|
+
name: 'nullif',
|
|
135
|
+
numArgs: 2,
|
|
136
|
+
deterministic: true,
|
|
137
|
+
// Type inference: return the type of the first argument (nullable)
|
|
138
|
+
inferReturnType: (argTypes) => ({
|
|
139
|
+
typeClass: 'scalar',
|
|
140
|
+
logicalType: argTypes[0],
|
|
141
|
+
nullable: true, // nullif can always return null
|
|
142
|
+
isReadOnly: true
|
|
143
|
+
})
|
|
144
|
+
},
|
|
57
145
|
(argX: SqlValue, argY: SqlValue): SqlValue => {
|
|
58
146
|
const comparison = compareSqlValues(argX, argY);
|
|
59
147
|
return comparison === 0 ? null : argX;
|
|
@@ -96,7 +184,18 @@ export const randomblobFunc = createScalarFunction(
|
|
|
96
184
|
|
|
97
185
|
// --- iif(X, Y, Z) ---
|
|
98
186
|
export const iifFunc = createScalarFunction(
|
|
99
|
-
{
|
|
187
|
+
{
|
|
188
|
+
name: 'iif',
|
|
189
|
+
numArgs: 3,
|
|
190
|
+
deterministic: true,
|
|
191
|
+
// Type inference: find the common type between the true and false values
|
|
192
|
+
inferReturnType: (argTypes) => ({
|
|
193
|
+
typeClass: 'scalar',
|
|
194
|
+
logicalType: findCommonType([argTypes[1], argTypes[2]]), // Common type of Y and Z
|
|
195
|
+
nullable: true, // Could return either Y or Z, so nullable if either is
|
|
196
|
+
isReadOnly: true
|
|
197
|
+
})
|
|
198
|
+
},
|
|
100
199
|
(condition: SqlValue, trueVal: SqlValue, falseVal: SqlValue): SqlValue => {
|
|
101
200
|
let isTrue: boolean;
|
|
102
201
|
if (condition === null) {
|
|
@@ -118,7 +217,19 @@ export const iifFunc = createScalarFunction(
|
|
|
118
217
|
|
|
119
218
|
// --- sqrt(X) ---
|
|
120
219
|
export const sqrtFunc = createScalarFunction(
|
|
121
|
-
{
|
|
220
|
+
{
|
|
221
|
+
name: 'sqrt',
|
|
222
|
+
numArgs: 1,
|
|
223
|
+
deterministic: true,
|
|
224
|
+
// Type inference: sqrt always returns REAL (even for INTEGER input)
|
|
225
|
+
inferReturnType: (argTypes) => ({
|
|
226
|
+
typeClass: 'scalar',
|
|
227
|
+
logicalType: argTypes[0].name === 'INTEGER' ? argTypes[0] : argTypes[0], // Keep input type
|
|
228
|
+
nullable: false,
|
|
229
|
+
isReadOnly: true
|
|
230
|
+
}),
|
|
231
|
+
validateArgTypes: (argTypes) => argTypes[0].isNumeric === true
|
|
232
|
+
},
|
|
122
233
|
(arg: SqlValue): SqlValue => {
|
|
123
234
|
if (arg === null) return null;
|
|
124
235
|
const num = Number(arg);
|
|
@@ -149,7 +260,19 @@ export const powerFunc = createScalarFunction(
|
|
|
149
260
|
|
|
150
261
|
// --- floor(X) ---
|
|
151
262
|
export const floorFunc = createScalarFunction(
|
|
152
|
-
{
|
|
263
|
+
{
|
|
264
|
+
name: 'floor',
|
|
265
|
+
numArgs: 1,
|
|
266
|
+
deterministic: true,
|
|
267
|
+
// Type inference: preserve input type
|
|
268
|
+
inferReturnType: (argTypes) => ({
|
|
269
|
+
typeClass: 'scalar',
|
|
270
|
+
logicalType: argTypes[0],
|
|
271
|
+
nullable: false,
|
|
272
|
+
isReadOnly: true
|
|
273
|
+
}),
|
|
274
|
+
validateArgTypes: (argTypes) => argTypes[0].isNumeric === true
|
|
275
|
+
},
|
|
153
276
|
(arg: SqlValue): SqlValue => {
|
|
154
277
|
if (arg === null) return null;
|
|
155
278
|
const num = Number(arg);
|
|
@@ -167,19 +290,41 @@ const ceil = (arg: SqlValue): SqlValue => {
|
|
|
167
290
|
return Math.ceil(num);
|
|
168
291
|
};
|
|
169
292
|
|
|
293
|
+
const ceilTypeInference = {
|
|
294
|
+
inferReturnType: (argTypes: ReadonlyArray<DeepReadonly<LogicalType>>) => ({
|
|
295
|
+
typeClass: 'scalar' as const,
|
|
296
|
+
logicalType: argTypes[0],
|
|
297
|
+
nullable: false,
|
|
298
|
+
isReadOnly: true
|
|
299
|
+
}),
|
|
300
|
+
validateArgTypes: (argTypes: ReadonlyArray<DeepReadonly<LogicalType>>) => argTypes[0].isNumeric === true
|
|
301
|
+
};
|
|
302
|
+
|
|
170
303
|
export const ceilFunc = createScalarFunction(
|
|
171
|
-
{ name: 'ceil', numArgs: 1, deterministic: true },
|
|
304
|
+
{ name: 'ceil', numArgs: 1, deterministic: true, ...ceilTypeInference },
|
|
172
305
|
ceil
|
|
173
306
|
);
|
|
174
307
|
|
|
175
308
|
export const ceilingFunc = createScalarFunction(
|
|
176
|
-
{ name: 'ceiling', numArgs: 1, deterministic: true },
|
|
309
|
+
{ name: 'ceiling', numArgs: 1, deterministic: true, ...ceilTypeInference },
|
|
177
310
|
ceil
|
|
178
311
|
);
|
|
179
312
|
|
|
180
313
|
// Math clamp function
|
|
181
314
|
export const clampFunc = createScalarFunction(
|
|
182
|
-
{
|
|
315
|
+
{
|
|
316
|
+
name: 'clamp',
|
|
317
|
+
numArgs: 3,
|
|
318
|
+
deterministic: true,
|
|
319
|
+
// Type inference: return the type of the first argument (value)
|
|
320
|
+
inferReturnType: (argTypes) => ({
|
|
321
|
+
typeClass: 'scalar',
|
|
322
|
+
logicalType: argTypes[0],
|
|
323
|
+
nullable: true,
|
|
324
|
+
isReadOnly: true
|
|
325
|
+
}),
|
|
326
|
+
validateArgTypes: (argTypes) => argTypes[0].isNumeric === true && argTypes[1].isNumeric === true && argTypes[2].isNumeric === true
|
|
327
|
+
},
|
|
183
328
|
(value: SqlValue, min: SqlValue, max: SqlValue): SqlValue => {
|
|
184
329
|
const v = Number(value);
|
|
185
330
|
const minVal = Number(min);
|
|
@@ -192,7 +337,18 @@ export const clampFunc = createScalarFunction(
|
|
|
192
337
|
|
|
193
338
|
// Greatest-of function
|
|
194
339
|
export const greatestFunc = createScalarFunction(
|
|
195
|
-
{
|
|
340
|
+
{
|
|
341
|
+
name: 'greatest',
|
|
342
|
+
numArgs: -1,
|
|
343
|
+
deterministic: true,
|
|
344
|
+
// Type inference: find the common type among all arguments
|
|
345
|
+
inferReturnType: (argTypes) => ({
|
|
346
|
+
typeClass: 'scalar',
|
|
347
|
+
logicalType: findCommonType(argTypes),
|
|
348
|
+
nullable: true,
|
|
349
|
+
isReadOnly: true
|
|
350
|
+
})
|
|
351
|
+
},
|
|
196
352
|
(...args: SqlValue[]): SqlValue => {
|
|
197
353
|
if (args.length === 0) return null;
|
|
198
354
|
return args.reduce((max, current) => {
|
|
@@ -206,7 +362,18 @@ export const greatestFunc = createScalarFunction(
|
|
|
206
362
|
|
|
207
363
|
// Least-of function
|
|
208
364
|
export const leastFunc = createScalarFunction(
|
|
209
|
-
{
|
|
365
|
+
{
|
|
366
|
+
name: 'least',
|
|
367
|
+
numArgs: -1,
|
|
368
|
+
deterministic: true,
|
|
369
|
+
// Type inference: find the common type among all arguments
|
|
370
|
+
inferReturnType: (argTypes) => ({
|
|
371
|
+
typeClass: 'scalar',
|
|
372
|
+
logicalType: findCommonType(argTypes),
|
|
373
|
+
nullable: true,
|
|
374
|
+
isReadOnly: true
|
|
375
|
+
})
|
|
376
|
+
},
|
|
210
377
|
(...args: SqlValue[]): SqlValue => {
|
|
211
378
|
if (args.length === 0) return null;
|
|
212
379
|
return args.reduce((min, current) => {
|
|
@@ -220,7 +387,31 @@ export const leastFunc = createScalarFunction(
|
|
|
220
387
|
|
|
221
388
|
// Choose function
|
|
222
389
|
export const chooseFunc = createScalarFunction(
|
|
223
|
-
{
|
|
390
|
+
{
|
|
391
|
+
name: 'choose',
|
|
392
|
+
numArgs: -1,
|
|
393
|
+
deterministic: true,
|
|
394
|
+
// Type inference: find the common type among all value arguments (skip index at position 0)
|
|
395
|
+
inferReturnType: (argTypes) => {
|
|
396
|
+
if (argTypes.length < 2) {
|
|
397
|
+
// Need at least index and one value
|
|
398
|
+
return {
|
|
399
|
+
typeClass: 'scalar',
|
|
400
|
+
logicalType: argTypes[0] || ANY_TYPE,
|
|
401
|
+
nullable: true,
|
|
402
|
+
isReadOnly: true
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
// Find common type among all value arguments (skip the index at position 0)
|
|
406
|
+
const valueTypes = argTypes.slice(1);
|
|
407
|
+
return {
|
|
408
|
+
typeClass: 'scalar',
|
|
409
|
+
logicalType: findCommonType(valueTypes),
|
|
410
|
+
nullable: true,
|
|
411
|
+
isReadOnly: true
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
},
|
|
224
415
|
(...args: SqlValue[]): SqlValue => {
|
|
225
416
|
if (args.length === 0) return null;
|
|
226
417
|
const index = Number(args[0]);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Row } from "../../common/types.js";
|
|
2
2
|
import type { SqlValue } from "../../common/types.js";
|
|
3
|
-
import { SqlDataType } from "../../common/types.js";
|
|
4
3
|
import { createIntegratedTableValuedFunction } from "../registration.js";
|
|
5
4
|
import { QuereusError } from "../../common/errors.js";
|
|
6
5
|
import { StatusCode } from "../../common/types.js";
|
|
@@ -8,6 +7,7 @@ import type { Database } from "../../core/database.js";
|
|
|
8
7
|
import type { FunctionSchema } from "../../schema/function.js";
|
|
9
8
|
import { isScalarFunctionSchema, isTableValuedFunctionSchema, isAggregateFunctionSchema, isWindowFunctionSchema } from "../../schema/function.js";
|
|
10
9
|
import { Schema } from "../../schema/schema.js";
|
|
10
|
+
import { INTEGER_TYPE, TEXT_TYPE } from "../../types/builtin-types.js";
|
|
11
11
|
import { ColumnSchema } from "../../schema/column.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -31,10 +31,10 @@ export const schemaFunc = createIntegratedTableValuedFunction(
|
|
|
31
31
|
isReadOnly: true,
|
|
32
32
|
isSet: false,
|
|
33
33
|
columns: [
|
|
34
|
-
{ name: 'type', type: { typeClass: 'scalar',
|
|
35
|
-
{ name: 'name', type: { typeClass: 'scalar',
|
|
36
|
-
{ name: 'tbl_name', type: { typeClass: 'scalar',
|
|
37
|
-
{ name: 'sql', type: { typeClass: 'scalar',
|
|
34
|
+
{ name: 'type', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
35
|
+
{ name: 'name', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
36
|
+
{ name: 'tbl_name', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
37
|
+
{ name: 'sql', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: true, isReadOnly: true }, generated: true }
|
|
38
38
|
],
|
|
39
39
|
keys: [],
|
|
40
40
|
rowConstraints: []
|
|
@@ -100,12 +100,12 @@ export const tableInfoFunc = createIntegratedTableValuedFunction(
|
|
|
100
100
|
isReadOnly: true,
|
|
101
101
|
isSet: false,
|
|
102
102
|
columns: [
|
|
103
|
-
{ name: 'cid', type: { typeClass: 'scalar',
|
|
104
|
-
{ name: 'name', type: { typeClass: 'scalar',
|
|
105
|
-
{ name: 'type', type: { typeClass: 'scalar',
|
|
106
|
-
{ name: 'notnull', type: { typeClass: 'scalar',
|
|
107
|
-
{ name: 'dflt_value', type: { typeClass: 'scalar',
|
|
108
|
-
{ name: 'pk', type: { typeClass: 'scalar',
|
|
103
|
+
{ name: 'cid', type: { typeClass: 'scalar', logicalType: INTEGER_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
104
|
+
{ name: 'name', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
105
|
+
{ name: 'type', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
106
|
+
{ name: 'notnull', type: { typeClass: 'scalar', logicalType: INTEGER_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
107
|
+
{ name: 'dflt_value', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: true, isReadOnly: true }, generated: true },
|
|
108
|
+
{ name: 'pk', type: { typeClass: 'scalar', logicalType: INTEGER_TYPE, nullable: false, isReadOnly: true }, generated: true }
|
|
109
109
|
],
|
|
110
110
|
keys: [],
|
|
111
111
|
rowConstraints: []
|
|
@@ -129,7 +129,7 @@ export const tableInfoFunc = createIntegratedTableValuedFunction(
|
|
|
129
129
|
yield [
|
|
130
130
|
i, // cid
|
|
131
131
|
column.name, // name
|
|
132
|
-
column.
|
|
132
|
+
column.logicalType.name, // type
|
|
133
133
|
column.notNull ? 1 : 0, // notnull
|
|
134
134
|
column.defaultValue?.toString() || null, // dflt_value
|
|
135
135
|
isPrimaryKey ? 1 : 0 // pk
|
|
@@ -154,12 +154,12 @@ export const functionInfoFunc = createIntegratedTableValuedFunction(
|
|
|
154
154
|
isReadOnly: true,
|
|
155
155
|
isSet: false,
|
|
156
156
|
columns: [
|
|
157
|
-
{ name: 'name', type: { typeClass: 'scalar',
|
|
158
|
-
{ name: 'num_args', type: { typeClass: 'scalar',
|
|
159
|
-
{ name: 'type', type: { typeClass: 'scalar',
|
|
160
|
-
{ name: 'deterministic', type: { typeClass: 'scalar',
|
|
161
|
-
{ name: 'flags', type: { typeClass: 'scalar',
|
|
162
|
-
{ name: 'signature', type: { typeClass: 'scalar',
|
|
157
|
+
{ name: 'name', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
158
|
+
{ name: 'num_args', type: { typeClass: 'scalar', logicalType: INTEGER_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
159
|
+
{ name: 'type', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
160
|
+
{ name: 'deterministic', type: { typeClass: 'scalar', logicalType: INTEGER_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
161
|
+
{ name: 'flags', type: { typeClass: 'scalar', logicalType: INTEGER_TYPE, nullable: false, isReadOnly: true }, generated: true },
|
|
162
|
+
{ name: 'signature', type: { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: false, isReadOnly: true }, generated: true }
|
|
163
163
|
],
|
|
164
164
|
keys: [],
|
|
165
165
|
rowConstraints: []
|
|
@@ -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,90 +31,69 @@ 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(
|
|
101
93
|
{ name: 'like', numArgs: 2, deterministic: true },
|
|
102
94
|
(pattern: SqlValue, text: SqlValue): SqlValue => {
|
|
103
95
|
if (text === null || pattern === null) return null;
|
|
104
|
-
return simpleLike(String(pattern), String(text))
|
|
96
|
+
return simpleLike(String(pattern), String(text));
|
|
105
97
|
}
|
|
106
98
|
);
|
|
107
99
|
|
|
@@ -109,13 +101,23 @@ export const globFunc = createScalarFunction(
|
|
|
109
101
|
{ name: 'glob', numArgs: 2, deterministic: true },
|
|
110
102
|
(pattern: SqlValue, text: SqlValue): SqlValue => {
|
|
111
103
|
if (text === null || pattern === null) return null;
|
|
112
|
-
return simpleGlob(String(pattern), String(text))
|
|
104
|
+
return simpleGlob(String(pattern), String(text));
|
|
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
|
}
|