@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.
Files changed (221) 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/explain.js +53 -53
  22. package/dist/src/func/builtins/explain.js.map +1 -1
  23. package/dist/src/func/builtins/generation.js +2 -2
  24. package/dist/src/func/builtins/generation.js.map +1 -1
  25. package/dist/src/func/builtins/index.d.ts.map +1 -1
  26. package/dist/src/func/builtins/index.js +16 -1
  27. package/dist/src/func/builtins/index.js.map +1 -1
  28. package/dist/src/func/builtins/json-tvf.js +17 -17
  29. package/dist/src/func/builtins/json-tvf.js.map +1 -1
  30. package/dist/src/func/builtins/scalar.d.ts.map +1 -1
  31. package/dist/src/func/builtins/scalar.js +202 -13
  32. package/dist/src/func/builtins/scalar.js.map +1 -1
  33. package/dist/src/func/builtins/schema.js +18 -18
  34. package/dist/src/func/builtins/schema.js.map +1 -1
  35. package/dist/src/func/builtins/string.d.ts.map +1 -1
  36. package/dist/src/func/builtins/string.js +56 -47
  37. package/dist/src/func/builtins/string.js.map +1 -1
  38. package/dist/src/func/builtins/timespan.d.ts +45 -0
  39. package/dist/src/func/builtins/timespan.d.ts.map +1 -0
  40. package/dist/src/func/builtins/timespan.js +147 -0
  41. package/dist/src/func/builtins/timespan.js.map +1 -0
  42. package/dist/src/func/registration.d.ts +26 -0
  43. package/dist/src/func/registration.d.ts.map +1 -1
  44. package/dist/src/func/registration.js +9 -5
  45. package/dist/src/func/registration.js.map +1 -1
  46. package/dist/src/index.d.ts +1 -1
  47. package/dist/src/index.d.ts.map +1 -1
  48. package/dist/src/index.js +1 -1
  49. package/dist/src/index.js.map +1 -1
  50. package/dist/src/parser/parser.js +2 -2
  51. package/dist/src/parser/parser.js.map +1 -1
  52. package/dist/src/planner/building/constraint-builder.js +2 -2
  53. package/dist/src/planner/building/constraint-builder.js.map +1 -1
  54. package/dist/src/planner/building/delete.js +3 -3
  55. package/dist/src/planner/building/delete.js.map +1 -1
  56. package/dist/src/planner/building/function-call.d.ts.map +1 -1
  57. package/dist/src/planner/building/function-call.js +24 -4
  58. package/dist/src/planner/building/function-call.js.map +1 -1
  59. package/dist/src/planner/building/insert.js +3 -3
  60. package/dist/src/planner/building/insert.js.map +1 -1
  61. package/dist/src/planner/building/select.d.ts.map +1 -1
  62. package/dist/src/planner/building/select.js +3 -2
  63. package/dist/src/planner/building/select.js.map +1 -1
  64. package/dist/src/planner/building/update.js +7 -7
  65. package/dist/src/planner/building/update.js.map +1 -1
  66. package/dist/src/planner/nodes/aggregate-function.d.ts +2 -1
  67. package/dist/src/planner/nodes/aggregate-function.d.ts.map +1 -1
  68. package/dist/src/planner/nodes/aggregate-function.js +10 -3
  69. package/dist/src/planner/nodes/aggregate-function.js.map +1 -1
  70. package/dist/src/planner/nodes/cte-node.d.ts.map +1 -1
  71. package/dist/src/planner/nodes/cte-node.js +2 -2
  72. package/dist/src/planner/nodes/cte-node.js.map +1 -1
  73. package/dist/src/planner/nodes/declarative-schema.js +3 -3
  74. package/dist/src/planner/nodes/declarative-schema.js.map +1 -1
  75. package/dist/src/planner/nodes/function.d.ts +2 -1
  76. package/dist/src/planner/nodes/function.d.ts.map +1 -1
  77. package/dist/src/planner/nodes/function.js +6 -3
  78. package/dist/src/planner/nodes/function.js.map +1 -1
  79. package/dist/src/planner/nodes/insert-node.js +1 -1
  80. package/dist/src/planner/nodes/insert-node.js.map +1 -1
  81. package/dist/src/planner/nodes/pragma.d.ts +1 -1
  82. package/dist/src/planner/nodes/pragma.d.ts.map +1 -1
  83. package/dist/src/planner/nodes/pragma.js +3 -3
  84. package/dist/src/planner/nodes/pragma.js.map +1 -1
  85. package/dist/src/planner/nodes/reference.js +1 -1
  86. package/dist/src/planner/nodes/reference.js.map +1 -1
  87. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  88. package/dist/src/planner/nodes/scalar.js +55 -101
  89. package/dist/src/planner/nodes/scalar.js.map +1 -1
  90. package/dist/src/planner/nodes/sequencing-node.js +2 -2
  91. package/dist/src/planner/nodes/sequencing-node.js.map +1 -1
  92. package/dist/src/planner/nodes/sink-node.js +2 -2
  93. package/dist/src/planner/nodes/sink-node.js.map +1 -1
  94. package/dist/src/planner/nodes/subquery.d.ts.map +1 -1
  95. package/dist/src/planner/nodes/subquery.js +4 -7
  96. package/dist/src/planner/nodes/subquery.js.map +1 -1
  97. package/dist/src/planner/nodes/view-reference-node.d.ts.map +1 -1
  98. package/dist/src/planner/nodes/view-reference-node.js +2 -2
  99. package/dist/src/planner/nodes/view-reference-node.js.map +1 -1
  100. package/dist/src/planner/nodes/window-function.js +3 -3
  101. package/dist/src/planner/nodes/window-function.js.map +1 -1
  102. package/dist/src/planner/rules/access/rule-select-access-path.js +1 -1
  103. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  104. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +1 -1
  105. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  106. package/dist/src/planner/scopes/global.js +3 -3
  107. package/dist/src/planner/scopes/global.js.map +1 -1
  108. package/dist/src/planner/scopes/param.d.ts.map +1 -1
  109. package/dist/src/planner/scopes/param.js +2 -2
  110. package/dist/src/planner/scopes/param.js.map +1 -1
  111. package/dist/src/planner/type-utils.d.ts +2 -12
  112. package/dist/src/planner/type-utils.d.ts.map +1 -1
  113. package/dist/src/planner/type-utils.js +6 -21
  114. package/dist/src/planner/type-utils.js.map +1 -1
  115. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  116. package/dist/src/runtime/emit/binary.js +40 -2
  117. package/dist/src/runtime/emit/binary.js.map +1 -1
  118. package/dist/src/runtime/emit/set-operation.d.ts.map +1 -1
  119. package/dist/src/runtime/emit/set-operation.js +33 -22
  120. package/dist/src/runtime/emit/set-operation.js.map +1 -1
  121. package/dist/src/runtime/emit/temporal-arithmetic.d.ts +33 -0
  122. package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -0
  123. package/dist/src/runtime/emit/temporal-arithmetic.js +269 -0
  124. package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -0
  125. package/dist/src/runtime/emit/unary.d.ts.map +1 -1
  126. package/dist/src/runtime/emit/unary.js +12 -0
  127. package/dist/src/runtime/emit/unary.js.map +1 -1
  128. package/dist/src/schema/catalog.js +3 -3
  129. package/dist/src/schema/catalog.js.map +1 -1
  130. package/dist/src/schema/column.d.ts +0 -3
  131. package/dist/src/schema/column.d.ts.map +1 -1
  132. package/dist/src/schema/column.js +0 -2
  133. package/dist/src/schema/column.js.map +1 -1
  134. package/dist/src/schema/function.d.ts +29 -1
  135. package/dist/src/schema/function.d.ts.map +1 -1
  136. package/dist/src/schema/function.js.map +1 -1
  137. package/dist/src/schema/table.d.ts +3 -3
  138. package/dist/src/schema/table.d.ts.map +1 -1
  139. package/dist/src/schema/table.js +4 -6
  140. package/dist/src/schema/table.js.map +1 -1
  141. package/dist/src/types/index.d.ts +1 -1
  142. package/dist/src/types/index.d.ts.map +1 -1
  143. package/dist/src/types/index.js +1 -1
  144. package/dist/src/types/index.js.map +1 -1
  145. package/dist/src/types/registry.d.ts.map +1 -1
  146. package/dist/src/types/registry.js +5 -1
  147. package/dist/src/types/registry.js.map +1 -1
  148. package/dist/src/types/temporal-types.d.ts +5 -0
  149. package/dist/src/types/temporal-types.d.ts.map +1 -1
  150. package/dist/src/types/temporal-types.js +122 -0
  151. package/dist/src/types/temporal-types.js.map +1 -1
  152. package/dist/src/util/ast-stringify.js +1 -1
  153. package/dist/src/util/ast-stringify.js.map +1 -1
  154. package/dist/src/util/plan-formatter.d.ts.map +1 -1
  155. package/dist/src/util/plan-formatter.js +1 -5
  156. package/dist/src/util/plan-formatter.js.map +1 -1
  157. package/dist/src/util/row-descriptor.js +2 -2
  158. package/dist/src/util/row-descriptor.js.map +1 -1
  159. package/dist/src/vtab/best-access-plan.d.ts +4 -3
  160. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  161. package/dist/src/vtab/best-access-plan.js.map +1 -1
  162. package/dist/src/vtab/memory/module.js +1 -1
  163. package/dist/src/vtab/memory/module.js.map +1 -1
  164. package/package.json +1 -1
  165. package/src/common/datatype.ts +4 -5
  166. package/src/common/type-inference.ts +13 -22
  167. package/src/core/param.ts +4 -11
  168. package/src/func/builtins/aggregate.ts +24 -2
  169. package/src/func/builtins/builtin-window-functions.ts +10 -10
  170. package/src/func/builtins/conversion.ts +26 -1
  171. package/src/func/builtins/explain.ts +53 -53
  172. package/src/func/builtins/generation.ts +2 -2
  173. package/src/func/builtins/index.ts +20 -1
  174. package/src/func/builtins/json-tvf.ts +17 -17
  175. package/src/func/builtins/scalar.ts +205 -14
  176. package/src/func/builtins/schema.ts +18 -18
  177. package/src/func/builtins/string.ts +91 -78
  178. package/src/func/builtins/timespan.ts +179 -0
  179. package/src/func/registration.ts +35 -5
  180. package/src/index.ts +2 -1
  181. package/src/parser/parser.ts +2 -2
  182. package/src/planner/building/constraint-builder.ts +2 -2
  183. package/src/planner/building/delete.ts +3 -3
  184. package/src/planner/building/function-call.ts +44 -3
  185. package/src/planner/building/insert.ts +3 -3
  186. package/src/planner/building/select.ts +3 -2
  187. package/src/planner/building/update.ts +7 -7
  188. package/src/planner/nodes/aggregate-function.ts +13 -3
  189. package/src/planner/nodes/cte-node.ts +2 -2
  190. package/src/planner/nodes/declarative-schema.ts +3 -3
  191. package/src/planner/nodes/function.ts +8 -3
  192. package/src/planner/nodes/insert-node.ts +1 -1
  193. package/src/planner/nodes/pragma.ts +4 -3
  194. package/src/planner/nodes/reference.ts +1 -1
  195. package/src/planner/nodes/scalar.ts +54 -102
  196. package/src/planner/nodes/sequencing-node.ts +2 -2
  197. package/src/planner/nodes/sink-node.ts +2 -2
  198. package/src/planner/nodes/subquery.ts +5 -7
  199. package/src/planner/nodes/view-reference-node.ts +2 -2
  200. package/src/planner/nodes/window-function.ts +3 -3
  201. package/src/planner/rules/access/rule-select-access-path.ts +1 -1
  202. package/src/planner/rules/retrieve/rule-grow-retrieve.ts +1 -1
  203. package/src/planner/scopes/global.ts +3 -3
  204. package/src/planner/scopes/param.ts +2 -2
  205. package/src/planner/type-utils.ts +6 -14
  206. package/src/runtime/emit/binary.ts +48 -2
  207. package/src/runtime/emit/set-operation.ts +52 -22
  208. package/src/runtime/emit/temporal-arithmetic.ts +302 -0
  209. package/src/runtime/emit/unary.ts +13 -0
  210. package/src/schema/catalog.ts +3 -3
  211. package/src/schema/column.ts +0 -3
  212. package/src/schema/function.ts +29 -1
  213. package/src/schema/table.ts +5 -7
  214. package/src/types/index.ts +1 -1
  215. package/src/types/registry.ts +5 -1
  216. package/src/types/temporal-types.ts +123 -0
  217. package/src/util/ast-stringify.ts +1 -1
  218. package/src/util/plan-formatter.ts +1 -4
  219. package/src/util/row-descriptor.ts +2 -2
  220. package/src/vtab/best-access-plan.ts +4 -3
  221. 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
- { 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,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
- 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(
@@ -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
- { 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
  }
@@ -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
+
@@ -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
- affinity: SqlDataType.NUMERIC,
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
- affinity: SqlDataType.NUMERIC,
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
@@ -63,7 +63,8 @@ export {
63
63
  export {
64
64
  DATE_TYPE,
65
65
  TIME_TYPE,
66
- DATETIME_TYPE
66
+ DATETIME_TYPE,
67
+ TIMESPAN_TYPE
67
68
  } from './types/temporal-types.js';
68
69
  export { JSON_TYPE } from './types/json-type.js';
69
70
  export {
@@ -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
- affinity: tableColumn.affinity,
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
- affinity: tableColumn.affinity,
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
- affinity: contextVar.affinity,
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
- affinity: col.affinity,
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
- affinity: col.affinity,
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
- return new ScalarFunctionCallNode(ctx.scope, expr, functionSchema, args);
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
  }