@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
@@ -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
  }
@@ -197,7 +197,7 @@ export function buildInsertStmt(
197
197
  name: contextVar.name,
198
198
  type: {
199
199
  typeClass: 'scalar' as const,
200
- affinity: contextVar.affinity,
200
+ logicalType: contextVar.logicalType,
201
201
  nullable: !contextVar.notNull,
202
202
  isReadOnly: true
203
203
  },
@@ -287,7 +287,7 @@ export function buildInsertStmt(
287
287
  name: col.name,
288
288
  type: {
289
289
  typeClass: 'scalar' as const,
290
- affinity: col.affinity,
290
+ logicalType: col.logicalType,
291
291
  nullable: true, // OLD values are always NULL for INSERT
292
292
  isReadOnly: false
293
293
  },
@@ -299,7 +299,7 @@ export function buildInsertStmt(
299
299
  name: col.name,
300
300
  type: {
301
301
  typeClass: 'scalar' as const,
302
- affinity: col.affinity,
302
+ logicalType: col.logicalType,
303
303
  nullable: !col.notNull,
304
304
  isReadOnly: false
305
305
  },
@@ -18,6 +18,7 @@ import { InternalRecursiveCTERefNode as _InternalRecursiveCTERefNode } from '../
18
18
  import type { CTEScopeNode, CTEPlanNode } from '../nodes/cte-node.js';
19
19
  import { JoinNode } from '../nodes/join-node.js';
20
20
  import { ColumnReferenceNode } from '../nodes/reference.js';
21
+ import { TEXT_TYPE } from '../../types/builtin-types.js';
21
22
  import { ValuesNode } from '../nodes/values-node.js';
22
23
  import { createLogger } from '../../common/logger.js';
23
24
 
@@ -440,7 +441,7 @@ export function buildFrom(fromClause: AST.FromClause, parentContext: PlanningCon
440
441
  columnNames.forEach((colName, i) => {
441
442
  if (i < subqueryAttributes.length) {
442
443
  const attr = subqueryAttributes[i];
443
- const columnType = fromTable.getType().columns[i]?.type || { typeClass: 'scalar', affinity: 'TEXT', nullable: true, isReadOnly: true };
444
+ const columnType = fromTable.getType().columns[i]?.type || { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: true, isReadOnly: true };
444
445
  subqueryScope.registerSymbol(colName.toLowerCase(), (exp, s) =>
445
446
  new ColumnReferenceNode(s, exp as AST.ColumnExpr, columnType, attr.id, i));
446
447
  }
@@ -479,7 +480,7 @@ export function buildFrom(fromClause: AST.FromClause, parentContext: PlanningCon
479
480
  columnNames.forEach((colName, i) => {
480
481
  if (i < mutatingAttributes.length) {
481
482
  const attr = mutatingAttributes[i];
482
- const columnType = fromTable.getType().columns[i]?.type || { typeClass: 'scalar', affinity: 'TEXT', nullable: true, isReadOnly: false };
483
+ const columnType = fromTable.getType().columns[i]?.type || { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: true, isReadOnly: false };
483
484
  mutatingScope.registerSymbol(colName.toLowerCase(), (exp, s) =>
484
485
  new ColumnReferenceNode(s, exp as AST.ColumnExpr, columnType, attr.id, i));
485
486
  }
@@ -36,7 +36,7 @@ export function buildUpdateStmt(
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
  },
@@ -86,7 +86,7 @@ export function buildUpdateStmt(
86
86
  name: col.name,
87
87
  type: {
88
88
  typeClass: 'scalar' as const,
89
- affinity: col.affinity,
89
+ logicalType: col.logicalType,
90
90
  nullable: !col.notNull,
91
91
  isReadOnly: false
92
92
  },
@@ -98,7 +98,7 @@ export function buildUpdateStmt(
98
98
  name: col.name,
99
99
  type: {
100
100
  typeClass: 'scalar' as const,
101
- affinity: col.affinity,
101
+ logicalType: col.logicalType,
102
102
  nullable: !col.notNull,
103
103
  isReadOnly: false
104
104
  },
@@ -151,7 +151,7 @@ export function buildUpdateStmt(
151
151
  exp as AST.ColumnExpr,
152
152
  {
153
153
  typeClass: 'scalar',
154
- affinity: tableColumn.affinity,
154
+ logicalType: tableColumn.logicalType,
155
155
  nullable: !tableColumn.notNull,
156
156
  isReadOnly: false
157
157
  },
@@ -168,7 +168,7 @@ export function buildUpdateStmt(
168
168
  exp as AST.ColumnExpr,
169
169
  {
170
170
  typeClass: 'scalar',
171
- affinity: tableColumn.affinity,
171
+ logicalType: tableColumn.logicalType,
172
172
  nullable: !tableColumn.notNull,
173
173
  isReadOnly: false
174
174
  },
@@ -184,7 +184,7 @@ export function buildUpdateStmt(
184
184
  exp as AST.ColumnExpr,
185
185
  {
186
186
  typeClass: 'scalar',
187
- affinity: tableColumn.affinity,
187
+ logicalType: tableColumn.logicalType,
188
188
  nullable: !tableColumn.notNull,
189
189
  isReadOnly: false
190
190
  },
@@ -200,7 +200,7 @@ export function buildUpdateStmt(
200
200
  exp as AST.ColumnExpr,
201
201
  {
202
202
  typeClass: 'scalar',
203
- affinity: tableColumn.affinity,
203
+ logicalType: tableColumn.logicalType,
204
204
  nullable: !tableColumn.notNull,
205
205
  isReadOnly: false
206
206
  },
@@ -6,6 +6,7 @@ import type { FunctionSchema } from '../../schema/function.js';
6
6
  import { isAggregateFunctionSchema } from '../../schema/function.js';
7
7
  import type * as AST from '../../parser/ast.js';
8
8
  import { formatExpressionList, formatScalarType } from '../../util/plan-formatter.js';
9
+ import { NULL_TYPE } from '../../types/builtin-types.js';
9
10
 
10
11
  /**
11
12
  * Represents an aggregate function call within a SQL query.
@@ -13,6 +14,7 @@ import { formatExpressionList, formatScalarType } from '../../util/plan-formatte
13
14
  */
14
15
  export class AggregateFunctionCallNode extends PlanNode implements ScalarPlanNode {
15
16
  readonly nodeType = PlanNodeType.ScalarFunctionCall; // Using same type as scalar functions
17
+ private readonly _inferredType?: ScalarType;
16
18
 
17
19
  constructor(
18
20
  scope: Scope,
@@ -22,12 +24,19 @@ export class AggregateFunctionCallNode extends PlanNode implements ScalarPlanNod
22
24
  public readonly args: ReadonlyArray<ScalarPlanNode>,
23
25
  public readonly isDistinct: boolean = false,
24
26
  public readonly orderBy?: ReadonlyArray<{ expression: ScalarPlanNode; direction: 'asc' | 'desc' }>,
25
- public readonly filter?: ScalarPlanNode
27
+ public readonly filter?: ScalarPlanNode,
28
+ inferredType?: ScalarType
26
29
  ) {
27
30
  super(scope);
31
+ this._inferredType = inferredType;
28
32
  }
29
33
 
30
34
  getType(): ScalarType {
35
+ // Use inferred type if available
36
+ if (this._inferredType) {
37
+ return this._inferredType;
38
+ }
39
+
31
40
  // Get the return type from the function schema
32
41
  if (isAggregateFunctionSchema(this.functionSchema)) {
33
42
  return this.functionSchema.returnType;
@@ -36,7 +45,7 @@ export class AggregateFunctionCallNode extends PlanNode implements ScalarPlanNod
36
45
  // Fallback for non-aggregate functions (shouldn't happen)
37
46
  return {
38
47
  typeClass: 'scalar',
39
- affinity: 0,
48
+ logicalType: NULL_TYPE,
40
49
  nullable: true, // Aggregates can return NULL
41
50
  isReadOnly: true
42
51
  };
@@ -109,7 +118,8 @@ export class AggregateFunctionCallNode extends PlanNode implements ScalarPlanNod
109
118
  newArgs,
110
119
  this.isDistinct,
111
120
  newOrderBy,
112
- newFilter
121
+ newFilter,
122
+ this._inferredType
113
123
  );
114
124
  }
115
125
 
@@ -1,10 +1,10 @@
1
1
  import { PlanNode, type UnaryRelationalNode, type RelationalPlanNode, type Attribute, type TableDescriptor, isRelationalNode } from './plan-node.js';
2
2
  import type { RelationType, ScalarType } from '../../common/datatype.js';
3
- import { SqlDataType } from '../../common/types.js';
4
3
  import { PlanNodeType } from './plan-node-type.js';
5
4
  import type { Scope } from '../scopes/scope.js';
6
5
  import { Cached } from '../../util/cached.js';
7
6
  import type { CTECapable } from '../framework/characteristics.js';
7
+ import { TEXT_TYPE } from '../../types/builtin-types.js';
8
8
 
9
9
  /**
10
10
  * Narrow contract that any node must satisfy to be placed in the CTE lookup map
@@ -74,7 +74,7 @@ export class CTENode extends PlanNode implements CTEPlanNode, CTEScopeNode, CTEC
74
74
  }
75
75
  // Fallback: generic TEXT scalar if nothing else is available (should not normally happen)
76
76
  if (!resolvedType) {
77
- resolvedType = { typeClass: 'scalar', affinity: SqlDataType.TEXT, nullable: true, isReadOnly: false } satisfies ScalarType;
77
+ resolvedType = { typeClass: 'scalar', logicalType: TEXT_TYPE, nullable: true, isReadOnly: false } satisfies ScalarType;
78
78
  }
79
79
  return {
80
80
  id: srcAttr?.id ?? PlanNode.nextAttrId(),
@@ -3,7 +3,7 @@ import { PlanNodeType } from './plan-node-type.js';
3
3
  import type { Scope } from '../scopes/scope.js';
4
4
  import type * as AST from '../../parser/ast.js';
5
5
  import { RelationType, type VoidType } from '../../common/datatype.js';
6
- import { SqlDataType } from '../../common/types.js';
6
+ import { TEXT_TYPE } from '../../types/builtin-types.js';
7
7
 
8
8
  /**
9
9
  * DECLARE SCHEMA statement plan node
@@ -66,7 +66,7 @@ export class DiffSchemaNode extends PlanNode implements RelationalPlanNode {
66
66
  name: 'ddl',
67
67
  type: {
68
68
  typeClass: 'scalar',
69
- affinity: SqlDataType.TEXT,
69
+ logicalType: TEXT_TYPE,
70
70
  nullable: false,
71
71
  isReadOnly: true,
72
72
  },
@@ -172,7 +172,7 @@ export class ExplainSchemaNode extends PlanNode implements RelationalPlanNode {
172
172
  name: 'info',
173
173
  type: {
174
174
  typeClass: 'scalar',
175
- affinity: SqlDataType.TEXT,
175
+ logicalType: TEXT_TYPE,
176
176
  nullable: false,
177
177
  isReadOnly: true,
178
178
  },
@@ -9,18 +9,22 @@ import { FunctionFlags } from '../../common/constants.js';
9
9
 
10
10
  export class ScalarFunctionCallNode extends PlanNode implements NaryScalarNode {
11
11
  override readonly nodeType = PlanNodeType.ScalarFunctionCall;
12
+ private readonly _inferredType?: ScalarType;
12
13
 
13
14
  constructor(
14
15
  scope: Scope,
15
16
  public readonly expression: AST.FunctionExpr,
16
17
  public readonly functionSchema: FunctionSchema,
17
- public readonly operands: ScalarPlanNode[]
18
+ public readonly operands: ScalarPlanNode[],
19
+ inferredType?: ScalarType
18
20
  ) {
19
21
  super(scope);
22
+ this._inferredType = inferredType;
20
23
  }
21
24
 
22
25
  getType(): ScalarType {
23
- return this.functionSchema.returnType as ScalarType;
26
+ // Use inferred type if available, otherwise use schema's return type
27
+ return this._inferredType ?? (this.functionSchema.returnType as ScalarType);
24
28
  }
25
29
 
26
30
  getChildren(): readonly ScalarPlanNode[] {
@@ -54,7 +58,8 @@ export class ScalarFunctionCallNode extends PlanNode implements NaryScalarNode {
54
58
  this.scope,
55
59
  this.expression,
56
60
  this.functionSchema,
57
- newChildren as ScalarPlanNode[]
61
+ newChildren as ScalarPlanNode[],
62
+ this._inferredType
58
63
  );
59
64
  }
60
65
 
@@ -50,7 +50,7 @@ export class InsertNode extends PlanNode implements RelationalPlanNode {
50
50
  name: col.name,
51
51
  type: {
52
52
  typeClass: 'scalar',
53
- affinity: col.affinity,
53
+ logicalType: col.logicalType,
54
54
  nullable: isOld ? true : !col.notNull, // OLD values can be null, NEW follows column constraints
55
55
  isReadOnly: false
56
56
  },