@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.
Files changed (226) hide show
  1. package/README.md +5 -1
  2. package/dist/src/common/datatype.d.ts +4 -5
  3. package/dist/src/common/datatype.d.ts.map +1 -1
  4. package/dist/src/common/datatype.js.map +1 -1
  5. package/dist/src/common/type-inference.d.ts +3 -6
  6. package/dist/src/common/type-inference.d.ts.map +1 -1
  7. package/dist/src/common/type-inference.js +17 -22
  8. package/dist/src/common/type-inference.js.map +1 -1
  9. package/dist/src/core/param.d.ts.map +1 -1
  10. package/dist/src/core/param.js +3 -18
  11. package/dist/src/core/param.js.map +1 -1
  12. package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
  13. package/dist/src/func/builtins/aggregate.js +24 -2
  14. package/dist/src/func/builtins/aggregate.js.map +1 -1
  15. package/dist/src/func/builtins/builtin-window-functions.js +10 -10
  16. package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
  17. package/dist/src/func/builtins/conversion.d.ts +10 -0
  18. package/dist/src/func/builtins/conversion.d.ts.map +1 -1
  19. package/dist/src/func/builtins/conversion.js +20 -1
  20. package/dist/src/func/builtins/conversion.js.map +1 -1
  21. package/dist/src/func/builtins/datetime.js +9 -9
  22. package/dist/src/func/builtins/datetime.js.map +1 -1
  23. package/dist/src/func/builtins/explain.js +53 -53
  24. package/dist/src/func/builtins/explain.js.map +1 -1
  25. package/dist/src/func/builtins/generation.js +2 -2
  26. package/dist/src/func/builtins/generation.js.map +1 -1
  27. package/dist/src/func/builtins/index.d.ts.map +1 -1
  28. package/dist/src/func/builtins/index.js +16 -1
  29. package/dist/src/func/builtins/index.js.map +1 -1
  30. package/dist/src/func/builtins/json-tvf.js +17 -17
  31. package/dist/src/func/builtins/json-tvf.js.map +1 -1
  32. package/dist/src/func/builtins/json.js +11 -11
  33. package/dist/src/func/builtins/json.js.map +1 -1
  34. package/dist/src/func/builtins/scalar.d.ts.map +1 -1
  35. package/dist/src/func/builtins/scalar.js +202 -13
  36. package/dist/src/func/builtins/scalar.js.map +1 -1
  37. package/dist/src/func/builtins/schema.js +18 -18
  38. package/dist/src/func/builtins/schema.js.map +1 -1
  39. package/dist/src/func/builtins/string.d.ts.map +1 -1
  40. package/dist/src/func/builtins/string.js +59 -50
  41. package/dist/src/func/builtins/string.js.map +1 -1
  42. package/dist/src/func/builtins/timespan.d.ts +45 -0
  43. package/dist/src/func/builtins/timespan.d.ts.map +1 -0
  44. package/dist/src/func/builtins/timespan.js +147 -0
  45. package/dist/src/func/builtins/timespan.js.map +1 -0
  46. package/dist/src/func/registration.d.ts +26 -0
  47. package/dist/src/func/registration.d.ts.map +1 -1
  48. package/dist/src/func/registration.js +9 -5
  49. package/dist/src/func/registration.js.map +1 -1
  50. package/dist/src/index.d.ts +1 -1
  51. package/dist/src/index.d.ts.map +1 -1
  52. package/dist/src/index.js +1 -1
  53. package/dist/src/index.js.map +1 -1
  54. package/dist/src/parser/parser.js +2 -2
  55. package/dist/src/parser/parser.js.map +1 -1
  56. package/dist/src/planner/building/constraint-builder.js +2 -2
  57. package/dist/src/planner/building/constraint-builder.js.map +1 -1
  58. package/dist/src/planner/building/delete.js +3 -3
  59. package/dist/src/planner/building/delete.js.map +1 -1
  60. package/dist/src/planner/building/function-call.d.ts.map +1 -1
  61. package/dist/src/planner/building/function-call.js +24 -4
  62. package/dist/src/planner/building/function-call.js.map +1 -1
  63. package/dist/src/planner/building/insert.js +3 -3
  64. package/dist/src/planner/building/insert.js.map +1 -1
  65. package/dist/src/planner/building/select.d.ts.map +1 -1
  66. package/dist/src/planner/building/select.js +3 -2
  67. package/dist/src/planner/building/select.js.map +1 -1
  68. package/dist/src/planner/building/update.js +7 -7
  69. package/dist/src/planner/building/update.js.map +1 -1
  70. package/dist/src/planner/nodes/aggregate-function.d.ts +2 -1
  71. package/dist/src/planner/nodes/aggregate-function.d.ts.map +1 -1
  72. package/dist/src/planner/nodes/aggregate-function.js +10 -3
  73. package/dist/src/planner/nodes/aggregate-function.js.map +1 -1
  74. package/dist/src/planner/nodes/cte-node.d.ts.map +1 -1
  75. package/dist/src/planner/nodes/cte-node.js +2 -2
  76. package/dist/src/planner/nodes/cte-node.js.map +1 -1
  77. package/dist/src/planner/nodes/declarative-schema.js +3 -3
  78. package/dist/src/planner/nodes/declarative-schema.js.map +1 -1
  79. package/dist/src/planner/nodes/function.d.ts +2 -1
  80. package/dist/src/planner/nodes/function.d.ts.map +1 -1
  81. package/dist/src/planner/nodes/function.js +6 -3
  82. package/dist/src/planner/nodes/function.js.map +1 -1
  83. package/dist/src/planner/nodes/insert-node.js +1 -1
  84. package/dist/src/planner/nodes/insert-node.js.map +1 -1
  85. package/dist/src/planner/nodes/pragma.d.ts +1 -1
  86. package/dist/src/planner/nodes/pragma.d.ts.map +1 -1
  87. package/dist/src/planner/nodes/pragma.js +3 -3
  88. package/dist/src/planner/nodes/pragma.js.map +1 -1
  89. package/dist/src/planner/nodes/reference.js +1 -1
  90. package/dist/src/planner/nodes/reference.js.map +1 -1
  91. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  92. package/dist/src/planner/nodes/scalar.js +55 -101
  93. package/dist/src/planner/nodes/scalar.js.map +1 -1
  94. package/dist/src/planner/nodes/sequencing-node.js +2 -2
  95. package/dist/src/planner/nodes/sequencing-node.js.map +1 -1
  96. package/dist/src/planner/nodes/sink-node.js +2 -2
  97. package/dist/src/planner/nodes/sink-node.js.map +1 -1
  98. package/dist/src/planner/nodes/subquery.d.ts.map +1 -1
  99. package/dist/src/planner/nodes/subquery.js +4 -7
  100. package/dist/src/planner/nodes/subquery.js.map +1 -1
  101. package/dist/src/planner/nodes/view-reference-node.d.ts.map +1 -1
  102. package/dist/src/planner/nodes/view-reference-node.js +2 -2
  103. package/dist/src/planner/nodes/view-reference-node.js.map +1 -1
  104. package/dist/src/planner/nodes/window-function.js +3 -3
  105. package/dist/src/planner/nodes/window-function.js.map +1 -1
  106. package/dist/src/planner/rules/access/rule-select-access-path.js +1 -1
  107. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  108. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +1 -1
  109. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  110. package/dist/src/planner/scopes/global.js +3 -3
  111. package/dist/src/planner/scopes/global.js.map +1 -1
  112. package/dist/src/planner/scopes/param.d.ts.map +1 -1
  113. package/dist/src/planner/scopes/param.js +2 -2
  114. package/dist/src/planner/scopes/param.js.map +1 -1
  115. package/dist/src/planner/type-utils.d.ts +2 -12
  116. package/dist/src/planner/type-utils.d.ts.map +1 -1
  117. package/dist/src/planner/type-utils.js +6 -21
  118. package/dist/src/planner/type-utils.js.map +1 -1
  119. package/dist/src/runtime/emit/between.js +2 -2
  120. package/dist/src/runtime/emit/between.js.map +1 -1
  121. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  122. package/dist/src/runtime/emit/binary.js +66 -30
  123. package/dist/src/runtime/emit/binary.js.map +1 -1
  124. package/dist/src/runtime/emit/subquery.js +8 -8
  125. package/dist/src/runtime/emit/subquery.js.map +1 -1
  126. package/dist/src/runtime/emit/temporal-arithmetic.d.ts +33 -0
  127. package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -0
  128. package/dist/src/runtime/emit/temporal-arithmetic.js +269 -0
  129. package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -0
  130. package/dist/src/runtime/emit/unary.d.ts.map +1 -1
  131. package/dist/src/runtime/emit/unary.js +16 -4
  132. package/dist/src/runtime/emit/unary.js.map +1 -1
  133. package/dist/src/schema/catalog.js +3 -3
  134. package/dist/src/schema/catalog.js.map +1 -1
  135. package/dist/src/schema/column.d.ts +0 -3
  136. package/dist/src/schema/column.d.ts.map +1 -1
  137. package/dist/src/schema/column.js +0 -2
  138. package/dist/src/schema/column.js.map +1 -1
  139. package/dist/src/schema/function.d.ts +29 -1
  140. package/dist/src/schema/function.d.ts.map +1 -1
  141. package/dist/src/schema/function.js.map +1 -1
  142. package/dist/src/schema/table.d.ts +3 -3
  143. package/dist/src/schema/table.d.ts.map +1 -1
  144. package/dist/src/schema/table.js +4 -6
  145. package/dist/src/schema/table.js.map +1 -1
  146. package/dist/src/types/index.d.ts +1 -1
  147. package/dist/src/types/index.d.ts.map +1 -1
  148. package/dist/src/types/index.js +1 -1
  149. package/dist/src/types/index.js.map +1 -1
  150. package/dist/src/types/registry.d.ts.map +1 -1
  151. package/dist/src/types/registry.js +5 -1
  152. package/dist/src/types/registry.js.map +1 -1
  153. package/dist/src/types/temporal-types.d.ts +5 -0
  154. package/dist/src/types/temporal-types.d.ts.map +1 -1
  155. package/dist/src/types/temporal-types.js +122 -0
  156. package/dist/src/types/temporal-types.js.map +1 -1
  157. package/dist/src/util/plan-formatter.d.ts.map +1 -1
  158. package/dist/src/util/plan-formatter.js +1 -5
  159. package/dist/src/util/plan-formatter.js.map +1 -1
  160. package/dist/src/util/row-descriptor.js +2 -2
  161. package/dist/src/util/row-descriptor.js.map +1 -1
  162. package/dist/src/vtab/best-access-plan.d.ts +4 -3
  163. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  164. package/dist/src/vtab/best-access-plan.js.map +1 -1
  165. package/dist/src/vtab/memory/module.js +1 -1
  166. package/dist/src/vtab/memory/module.js.map +1 -1
  167. package/package.json +1 -1
  168. package/src/common/datatype.ts +4 -5
  169. package/src/common/type-inference.ts +13 -22
  170. package/src/core/param.ts +4 -11
  171. package/src/func/builtins/aggregate.ts +24 -2
  172. package/src/func/builtins/builtin-window-functions.ts +10 -10
  173. package/src/func/builtins/conversion.ts +26 -1
  174. package/src/func/builtins/datetime.ts +9 -9
  175. package/src/func/builtins/explain.ts +53 -53
  176. package/src/func/builtins/generation.ts +2 -2
  177. package/src/func/builtins/index.ts +20 -1
  178. package/src/func/builtins/json-tvf.ts +17 -17
  179. package/src/func/builtins/json.ts +11 -11
  180. package/src/func/builtins/scalar.ts +205 -14
  181. package/src/func/builtins/schema.ts +18 -18
  182. package/src/func/builtins/string.ts +93 -80
  183. package/src/func/builtins/timespan.ts +179 -0
  184. package/src/func/registration.ts +35 -5
  185. package/src/index.ts +2 -1
  186. package/src/parser/parser.ts +2 -2
  187. package/src/planner/building/constraint-builder.ts +2 -2
  188. package/src/planner/building/delete.ts +3 -3
  189. package/src/planner/building/function-call.ts +44 -3
  190. package/src/planner/building/insert.ts +3 -3
  191. package/src/planner/building/select.ts +3 -2
  192. package/src/planner/building/update.ts +7 -7
  193. package/src/planner/nodes/aggregate-function.ts +13 -3
  194. package/src/planner/nodes/cte-node.ts +2 -2
  195. package/src/planner/nodes/declarative-schema.ts +3 -3
  196. package/src/planner/nodes/function.ts +8 -3
  197. package/src/planner/nodes/insert-node.ts +1 -1
  198. package/src/planner/nodes/pragma.ts +4 -3
  199. package/src/planner/nodes/reference.ts +1 -1
  200. package/src/planner/nodes/scalar.ts +54 -102
  201. package/src/planner/nodes/sequencing-node.ts +2 -2
  202. package/src/planner/nodes/sink-node.ts +2 -2
  203. package/src/planner/nodes/subquery.ts +5 -7
  204. package/src/planner/nodes/view-reference-node.ts +2 -2
  205. package/src/planner/nodes/window-function.ts +3 -3
  206. package/src/planner/rules/access/rule-select-access-path.ts +1 -1
  207. package/src/planner/rules/retrieve/rule-grow-retrieve.ts +1 -1
  208. package/src/planner/scopes/global.ts +3 -3
  209. package/src/planner/scopes/param.ts +2 -2
  210. package/src/planner/type-utils.ts +6 -14
  211. package/src/runtime/emit/between.ts +2 -2
  212. package/src/runtime/emit/binary.ts +74 -30
  213. package/src/runtime/emit/subquery.ts +8 -8
  214. package/src/runtime/emit/temporal-arithmetic.ts +302 -0
  215. package/src/runtime/emit/unary.ts +17 -4
  216. package/src/schema/catalog.ts +3 -3
  217. package/src/schema/column.ts +0 -3
  218. package/src/schema/function.ts +29 -1
  219. package/src/schema/table.ts +5 -7
  220. package/src/types/index.ts +1 -1
  221. package/src/types/registry.ts +5 -1
  222. package/src/types/temporal-types.ts +123 -0
  223. package/src/util/plan-formatter.ts +1 -4
  224. package/src/util/row-descriptor.ts +2 -2
  225. package/src/vtab/best-access-plan.ts +4 -3
  226. 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
- { name: 'abs', numArgs: 1, deterministic: true },
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
- { name: 'round', numArgs: -1, deterministic: true },
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
- { name: 'coalesce', numArgs: -1, deterministic: true },
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
- { name: 'nullif', numArgs: 2, deterministic: true },
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
- { name: 'iif', numArgs: 3, deterministic: true },
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
- { name: 'sqrt', numArgs: 1, deterministic: true },
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
- { name: 'floor', numArgs: 1, deterministic: true },
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
- { name: 'clamp', numArgs: 3, deterministic: true },
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
- { name: 'greatest', numArgs: -1, deterministic: true },
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
- { name: 'least', numArgs: -1, deterministic: true },
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
- { name: 'choose', numArgs: -1, deterministic: true },
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', affinity: SqlDataType.TEXT, nullable: false, isReadOnly: true }, generated: true },
35
- { name: 'name', type: { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: false, isReadOnly: true }, generated: true },
36
- { name: 'tbl_name', type: { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: false, isReadOnly: true }, generated: true },
37
- { name: 'sql', type: { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: true, isReadOnly: true }, generated: true }
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', affinity: SqlDataType.INTEGER, nullable: false, isReadOnly: true }, generated: true },
104
- { name: 'name', type: { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: false, isReadOnly: true }, generated: true },
105
- { name: 'type', type: { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: false, isReadOnly: true }, generated: true },
106
- { name: 'notnull', type: { typeClass: 'scalar', affinity: SqlDataType.INTEGER, nullable: false, isReadOnly: true }, generated: true },
107
- { name: 'dflt_value', type: { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: true, isReadOnly: true }, generated: true },
108
- { name: 'pk', type: { typeClass: 'scalar', affinity: SqlDataType.INTEGER, nullable: false, isReadOnly: true }, generated: true }
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.affinity || SqlDataType.TEXT, // type
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', affinity: SqlDataType.TEXT, nullable: false, isReadOnly: true }, generated: true },
158
- { name: 'num_args', type: { typeClass: 'scalar', affinity: SqlDataType.INTEGER, nullable: false, isReadOnly: true }, generated: true },
159
- { name: 'type', type: { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: false, isReadOnly: true }, generated: true },
160
- { name: 'deterministic', type: { typeClass: 'scalar', affinity: SqlDataType.INTEGER, nullable: false, isReadOnly: true }, generated: true },
161
- { name: 'flags', type: { typeClass: 'scalar', affinity: SqlDataType.INTEGER, nullable: false, isReadOnly: true }, generated: true },
162
- { name: 'signature', type: { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: false, isReadOnly: true }, generated: true }
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
- { name: 'length', numArgs: 1, deterministic: true },
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
- const s = String(str); // Coerce main arg to string
27
- let y = Number(start);
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
- if (isNaN(y) || (z !== undefined && isNaN(z))) return null;
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
- // SQLite uses 1-based indexing, negative start counts from end
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
- const strLen = s.length;
37
- let begin: number;
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
- if (y > 0) {
40
- begin = y - 1;
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
- return s.substring(begin, end);
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
- y = Math.trunc(y);
73
- z = z === undefined ? undefined : Math.trunc(z);
69
+ return s.substring(begin, end);
70
+ };
74
71
 
75
- const strLen = s.length;
76
- let begin: number;
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
- if (y > 0) {
79
- begin = y - 1;
80
- } else if (y < 0) {
81
- begin = strLen + y;
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
- return s.substring(begin, end);
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)) ? 1 : 0;
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)) ? 1 : 0;
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
- { name: 'instr', numArgs: 2, deterministic: true },
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
  }