@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
@@ -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
  },
@@ -1,4 +1,4 @@
1
- import { SqlDataType, type SqlValue } from '../../common/types.js';
1
+ import type { SqlValue } from '../../common/types.js';
2
2
  import * as AST from '../../parser/ast.js';
3
3
  import { Attribute, type RelationalPlanNode } from './plan-node.js';
4
4
  import { PlanNodeType } from './plan-node-type.js';
@@ -6,6 +6,7 @@ import { expressionToString } from '../../util/ast-stringify.js';
6
6
  import { PlanNode } from './plan-node.js';
7
7
  import { RelationType } from '../../common/datatype.js';
8
8
  import { Scope } from '../scopes/scope.js';
9
+ import { TEXT_TYPE } from '../../types/builtin-types.js';
9
10
 
10
11
  export class PragmaPlanNode extends PlanNode implements RelationalPlanNode {
11
12
  override readonly nodeType = PlanNodeType.Pragma;
@@ -29,7 +30,7 @@ export class PragmaPlanNode extends PlanNode implements RelationalPlanNode {
29
30
  name: "name",
30
31
  type: {
31
32
  typeClass: 'scalar',
32
- affinity: SqlDataType.TEXT,
33
+ logicalType: TEXT_TYPE,
33
34
  nullable: false,
34
35
  isReadOnly: true,
35
36
  },
@@ -39,7 +40,7 @@ export class PragmaPlanNode extends PlanNode implements RelationalPlanNode {
39
40
  name: "value",
40
41
  type: {
41
42
  typeClass: 'scalar',
42
- affinity: SqlDataType.TEXT,
43
+ logicalType: TEXT_TYPE,
43
44
  nullable: false,
44
45
  },
45
46
  generated: true,
@@ -38,7 +38,7 @@ export class TableReferenceNode extends PlanNode implements ZeroAryRelationalNod
38
38
  name: column.name,
39
39
  type: {
40
40
  typeClass: 'scalar' as const,
41
- affinity: column.affinity,
41
+ logicalType: column.logicalType,
42
42
  nullable: !column.notNull,
43
43
  isReadOnly: false,
44
44
  collationName: column.collation
@@ -8,6 +8,8 @@ import { Cached } from "../../util/cached.js";
8
8
  import { formatExpression, formatScalarType } from "../../util/plan-formatter.js";
9
9
  import { quereusError } from '../../common/errors.js';
10
10
  import { StatusCode } from '../../common/types.js';
11
+ import { NULL_TYPE, INTEGER_TYPE, REAL_TYPE, TEXT_TYPE, BLOB_TYPE, BOOLEAN_TYPE } from "../../types/builtin-types.js";
12
+ import { typeRegistry } from "../../types/registry.js";
11
13
 
12
14
  export class UnaryOpNode extends PlanNode implements UnaryScalarNode {
13
15
  readonly nodeType = PlanNodeType.UnaryOp;
@@ -25,36 +27,31 @@ export class UnaryOpNode extends PlanNode implements UnaryScalarNode {
25
27
  generateType = (): ScalarType => {
26
28
  const operandType = this.operand.getType();
27
29
 
28
- let datatype: SqlDataType | undefined;
29
- let affinity: SqlDataType = operandType.affinity;
30
+ let logicalType = operandType.logicalType;
30
31
  let nullable = operandType.nullable;
31
32
 
32
33
  switch (this.expression.operator) {
33
34
  case 'NOT':
34
35
  case 'IS NULL':
35
36
  case 'IS NOT NULL':
36
- datatype = SqlDataType.INTEGER;
37
- affinity = SqlDataType.INTEGER;
37
+ logicalType = BOOLEAN_TYPE;
38
38
  nullable = false; // Boolean results are never null
39
39
  break;
40
40
  case '-':
41
41
  case '+':
42
- // Numeric unary operators preserve type but may change nullability
43
- datatype = operandType.datatype;
42
+ // Numeric unary operators preserve type
44
43
  break;
45
44
  case '~':
46
45
  // Bitwise NOT - results in integer
47
- datatype = SqlDataType.INTEGER;
48
- affinity = SqlDataType.INTEGER;
46
+ logicalType = INTEGER_TYPE;
49
47
  break;
50
48
  }
51
49
 
52
50
  return {
53
51
  typeClass: 'scalar',
54
- affinity,
52
+ logicalType,
55
53
  nullable,
56
54
  isReadOnly: operandType.isReadOnly,
57
- datatype,
58
55
  collationName: operandType.collationName,
59
56
  };
60
57
  }
@@ -130,17 +127,11 @@ export class BinaryOpNode extends PlanNode implements BinaryScalarNode {
130
127
  const leftType = this.left.getType();
131
128
  const rightType = this.right.getType();
132
129
 
133
- const affinity = leftType.affinity;
130
+ let logicalType = leftType.logicalType;
134
131
 
135
- let datatype: SqlDataType | undefined;
136
132
  switch (this.expression.operator) {
137
133
  case 'OR':
138
134
  case 'AND':
139
- case '+':
140
- case '-':
141
- case '*':
142
- case '/':
143
- case '%':
144
135
  case '=':
145
136
  case '!=':
146
137
  case '<':
@@ -150,10 +141,33 @@ export class BinaryOpNode extends PlanNode implements BinaryScalarNode {
150
141
  case 'IS':
151
142
  case 'IS NOT':
152
143
  case 'IN':
153
- datatype = SqlDataType.INTEGER;
144
+ // Comparison and logical operators return boolean
145
+ logicalType = BOOLEAN_TYPE;
146
+ break;
147
+ case '+':
148
+ case '-':
149
+ case '*':
150
+ case '/':
151
+ case '%':
152
+ // Arithmetic operators - implement numeric type promotion
153
+ // Rules: INTEGER + INTEGER -> INTEGER, INTEGER + REAL -> REAL, REAL + REAL -> REAL
154
+ if (leftType.logicalType.isNumeric && rightType.logicalType.isNumeric) {
155
+ // Both operands are numeric
156
+ if (leftType.logicalType.name === 'REAL' || rightType.logicalType.name === 'REAL') {
157
+ // If either is REAL, result is REAL
158
+ logicalType = REAL_TYPE;
159
+ } else {
160
+ // Both are INTEGER, result is INTEGER
161
+ logicalType = INTEGER_TYPE;
162
+ }
163
+ } else {
164
+ // Non-numeric operands - use left operand type (fallback)
165
+ logicalType = leftType.logicalType;
166
+ }
154
167
  break;
155
168
  case '||':
156
- datatype = SqlDataType.TEXT;
169
+ // String concatenation
170
+ logicalType = TEXT_TYPE;
157
171
  break;
158
172
  };
159
173
 
@@ -162,10 +176,9 @@ export class BinaryOpNode extends PlanNode implements BinaryScalarNode {
162
176
 
163
177
  return {
164
178
  typeClass: 'scalar',
165
- affinity,
179
+ logicalType,
166
180
  nullable: leftType.nullable || rightType.nullable,
167
181
  isReadOnly: leftType.isReadOnly || rightType.isReadOnly,
168
- datatype,
169
182
  collationName,
170
183
  };
171
184
  }
@@ -252,55 +265,49 @@ export class LiteralNode extends PlanNode implements ZeroAryScalarNode, Constant
252
265
  if (value === null) {
253
266
  return {
254
267
  typeClass: 'scalar',
255
- affinity: SqlDataType.NULL,
268
+ logicalType: NULL_TYPE,
256
269
  nullable: true,
257
270
  isReadOnly: true,
258
- datatype: SqlDataType.NULL,
259
271
  };
260
272
  }
261
273
  if (typeof value === 'number') {
262
274
  return {
263
275
  typeClass: 'scalar',
264
- affinity: SqlDataType.REAL,
276
+ logicalType: REAL_TYPE,
265
277
  nullable: false,
266
278
  isReadOnly: true,
267
- datatype: SqlDataType.REAL,
268
279
  };
269
280
  }
270
281
  if (typeof value === 'bigint') {
271
282
  return {
272
283
  typeClass: 'scalar',
273
- affinity: SqlDataType.INTEGER,
284
+ logicalType: INTEGER_TYPE,
274
285
  nullable: false,
275
286
  isReadOnly: true,
276
- datatype: SqlDataType.INTEGER,
277
287
  };
278
288
  }
279
289
  if (typeof value === 'string') {
280
290
  return {
281
291
  typeClass: 'scalar',
282
- affinity: SqlDataType.TEXT,
292
+ logicalType: TEXT_TYPE,
283
293
  nullable: false,
284
294
  isReadOnly: true,
285
- datatype: SqlDataType.TEXT,
286
295
  };
287
296
  }
288
297
  if (typeof value === 'boolean') {
289
298
  return {
290
299
  typeClass: 'scalar',
291
- affinity: SqlDataType.INTEGER,
300
+ logicalType: BOOLEAN_TYPE,
292
301
  nullable: false,
293
302
  isReadOnly: true,
294
- datatype: SqlDataType.INTEGER,
295
303
  };
296
304
  }
297
305
  if (value instanceof Uint8Array) {
298
306
  return {
299
307
  typeClass: 'scalar',
300
- affinity: SqlDataType.BLOB,
308
+ logicalType: BLOB_TYPE,
301
309
  nullable: false,
302
310
  isReadOnly: true,
303
- datatype: SqlDataType.BLOB,
304
311
  };
305
312
  }
306
313
  quereusError(`Unknown literal type ${typeof value}`, StatusCode.INTERNAL);
@@ -377,16 +384,15 @@ export class CaseExprNode extends PlanNode implements NaryScalarNode {
377
384
  // No THEN clauses and no ELSE - should not happen in valid SQL
378
385
  return {
379
386
  typeClass: 'scalar',
380
- affinity: SqlDataType.NULL,
387
+ logicalType: NULL_TYPE,
381
388
  nullable: true,
382
389
  isReadOnly: true,
383
- datatype: SqlDataType.NULL,
384
390
  };
385
391
  }
386
392
 
387
393
  // Use the first result expression as the base type
388
394
  const firstType = resultExpressions[0].getType();
389
- let affinity = firstType.affinity;
395
+ let logicalType = firstType.logicalType;
390
396
  let nullable = firstType.nullable;
391
397
  let isReadOnly = firstType.isReadOnly;
392
398
  let collationName = firstType.collationName;
@@ -411,9 +417,9 @@ export class CaseExprNode extends PlanNode implements NaryScalarNode {
411
417
  }
412
418
 
413
419
  // TODO: Implement proper type coercion rules for SQL
414
- // For now, if types differ, default to TEXT affinity
415
- if (exprType.affinity !== affinity) {
416
- affinity = SqlDataType.TEXT;
420
+ // For now, if types differ, default to TEXT
421
+ if (exprType.logicalType !== logicalType) {
422
+ logicalType = TEXT_TYPE;
417
423
  }
418
424
  }
419
425
 
@@ -424,11 +430,10 @@ export class CaseExprNode extends PlanNode implements NaryScalarNode {
424
430
 
425
431
  return {
426
432
  typeClass: 'scalar',
427
- affinity,
433
+ logicalType,
428
434
  nullable,
429
435
  isReadOnly,
430
436
  collationName,
431
- // Don't set datatype since it can vary based on runtime conditions
432
437
  };
433
438
  }
434
439
 
@@ -558,69 +563,17 @@ export class CastNode extends PlanNode implements UnaryScalarNode {
558
563
 
559
564
  generateType = (): ScalarType => {
560
565
  const operandType = this.operand.getType();
561
- const targetType = this.expression.targetType.toUpperCase();
562
-
563
- // Determine the SQL data type and affinity based on the target type
564
- let datatype: SqlDataType;
565
- let affinity: SqlDataType;
566
-
567
- switch (targetType) {
568
- case 'INTEGER':
569
- case 'INT':
570
- case 'TINYINT':
571
- case 'SMALLINT':
572
- case 'MEDIUMINT':
573
- case 'BIGINT':
574
- case 'UNSIGNED BIG INT':
575
- case 'INT2':
576
- case 'INT8':
577
- datatype = SqlDataType.INTEGER;
578
- affinity = SqlDataType.INTEGER;
579
- break;
580
- case 'REAL':
581
- case 'DOUBLE':
582
- case 'DOUBLE PRECISION':
583
- case 'FLOAT':
584
- datatype = SqlDataType.REAL;
585
- affinity = SqlDataType.REAL;
586
- break;
587
- case 'TEXT':
588
- case 'CHARACTER':
589
- case 'VARCHAR':
590
- case 'VARYING CHARACTER':
591
- case 'NCHAR':
592
- case 'NATIVE CHARACTER':
593
- case 'NVARCHAR':
594
- case 'CLOB':
595
- datatype = SqlDataType.TEXT;
596
- affinity = SqlDataType.TEXT;
597
- break;
598
- case 'BLOB':
599
- datatype = SqlDataType.BLOB;
600
- affinity = SqlDataType.BLOB;
601
- break;
602
- case 'NUMERIC':
603
- case 'DECIMAL':
604
- case 'BOOLEAN':
605
- case 'DATE':
606
- case 'DATETIME':
607
- datatype = SqlDataType.NUMERIC;
608
- affinity = SqlDataType.NUMERIC;
609
- break;
610
- default:
611
- // For unknown types, default to BLOB affinity
612
- datatype = SqlDataType.BLOB;
613
- affinity = SqlDataType.BLOB;
614
- break;
615
- }
566
+ const targetType = this.expression.targetType;
567
+
568
+ // Look up the logical type from the type registry
569
+ const logicalType = typeRegistry.getTypeOrDefault(targetType);
616
570
 
617
571
  return {
618
572
  typeClass: 'scalar',
619
- affinity,
573
+ logicalType,
620
574
  nullable: operandType.nullable, // CAST preserves nullability
621
575
  isReadOnly: operandType.isReadOnly,
622
- datatype,
623
- collationName: affinity === SqlDataType.TEXT ? operandType.collationName : undefined,
576
+ collationName: logicalType.isTextual ? operandType.collationName : undefined,
624
577
  };
625
578
  }
626
579
 
@@ -756,13 +709,12 @@ export class BetweenNode extends PlanNode implements TernaryScalarNode {
756
709
  }
757
710
 
758
711
  getType(): ScalarType {
759
- // BETWEEN always returns INTEGER (0 or 1)
712
+ // BETWEEN always returns BOOLEAN
760
713
  return {
761
714
  typeClass: 'scalar',
762
- affinity: SqlDataType.INTEGER,
715
+ logicalType: BOOLEAN_TYPE,
763
716
  nullable: false,
764
717
  isReadOnly: true,
765
- datatype: SqlDataType.INTEGER,
766
718
  };
767
719
  }
768
720
 
@@ -3,7 +3,7 @@ import { isRelationalNode, PlanNode, type Attribute, type RelationalPlanNode, ty
3
3
  import type { RelationType } from '../../common/datatype.js';
4
4
  import type { Scope } from '../scopes/scope.js';
5
5
  import { Cached } from '../../util/cached.js';
6
- import { SqlDataType } from '../../common/types.js';
6
+ import { INTEGER_TYPE } from '../../types/builtin-types.js';
7
7
 
8
8
  /**
9
9
  * Represents a sequencing operation that adds a row number column to convert bags to sets.
@@ -31,7 +31,7 @@ export class SequencingNode extends PlanNode implements UnaryRelationalNode {
31
31
  name: this.sequenceColumnName,
32
32
  type: {
33
33
  typeClass: 'scalar' as const,
34
- affinity: SqlDataType.INTEGER,
34
+ logicalType: INTEGER_TYPE,
35
35
  nullable: false,
36
36
  isReadOnly: true
37
37
  },
@@ -2,7 +2,7 @@ import { PlanNodeType } from './plan-node-type.js';
2
2
  import { PlanNode, type RelationalPlanNode } from './plan-node.js';
3
3
  import type { ScalarType } from '../../common/datatype.js';
4
4
  import type { Scope } from '../scopes/scope.js';
5
- import { SqlDataType } from '../../common/types.js';
5
+ import { INTEGER_TYPE } from '../../types/builtin-types.js';
6
6
 
7
7
  /**
8
8
  * A sink node that consumes an async iterable for side effects.
@@ -25,7 +25,7 @@ export class SinkNode extends PlanNode {
25
25
  return {
26
26
  typeClass: 'scalar',
27
27
  isReadOnly: true,
28
- affinity: SqlDataType.INTEGER,
28
+ logicalType: INTEGER_TYPE,
29
29
  nullable: false
30
30
  };
31
31
  }