@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
@@ -8,6 +8,7 @@ import { compareSqlValuesFast, resolveCollation } from "../../util/comparison.js
8
8
  import { coerceForComparison, coerceToNumberForArithmetic } from "../../util/coercion.js";
9
9
  import { simpleLike } from "../../util/patterns.js";
10
10
  import type { EmissionContext } from "../emission-context.js";
11
+ import { tryTemporalArithmetic, tryTemporalComparison } from "./temporal-arithmetic.js";
11
12
 
12
13
  export function emitBinaryOp(plan: BinaryOpNode, ctx: EmissionContext): Instruction {
13
14
  // Normalize operator to uppercase for case-insensitive matching of keywords
@@ -73,6 +74,13 @@ export function emitNumericOp(plan: BinaryOpNode, ctx: EmissionContext): Instruc
73
74
  }
74
75
 
75
76
  function run(ctx: RuntimeContext, v1: SqlValue, v2: SqlValue): SqlValue {
77
+ // Try temporal arithmetic first
78
+ const temporalResult = tryTemporalArithmetic(plan.expression.operator, v1, v2);
79
+ if (temporalResult !== undefined) {
80
+ return temporalResult;
81
+ }
82
+
83
+ // Fall back to numeric arithmetic
76
84
  if (v1 !== null && v2 !== null) {
77
85
  if (typeof v1 === 'bigint' || typeof v2 === 'bigint') {
78
86
  try {
@@ -127,16 +135,24 @@ export function emitComparisonOp(plan: BinaryOpNode, ctx: EmissionContext): Inst
127
135
  // Pre-resolve collation function for optimal performance
128
136
  const collationFunc = resolveCollation(collationName);
129
137
 
130
- switch (plan.expression.operator) {
138
+ const operator = plan.expression.operator;
139
+
140
+ switch (operator) {
131
141
  case '=':
132
142
  case '==':
133
143
  run = (ctx: RuntimeContext, v1: SqlValue, v2: SqlValue): SqlValue => {
134
144
  // SQL comparison: NULL = anything -> NULL
135
145
  if (v1 === null || v2 === null) return null;
136
146
 
147
+ // Try temporal comparison first
148
+ const temporalResult = tryTemporalComparison(operator, v1, v2);
149
+ if (temporalResult !== undefined) {
150
+ return temporalResult;
151
+ }
152
+
137
153
  // Apply type coercion before comparison
138
154
  const [coercedV1, coercedV2] = coerceForComparison(v1, v2);
139
- return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) === 0 ? 1 : 0;
155
+ return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) === 0;
140
156
  };
141
157
  break;
142
158
  case '!=':
@@ -145,9 +161,15 @@ export function emitComparisonOp(plan: BinaryOpNode, ctx: EmissionContext): Inst
145
161
  // SQL comparison: NULL != anything -> NULL
146
162
  if (v1 === null || v2 === null) return null;
147
163
 
164
+ // Try temporal comparison first
165
+ const temporalResult = tryTemporalComparison(operator, v1, v2);
166
+ if (temporalResult !== undefined) {
167
+ return temporalResult;
168
+ }
169
+
148
170
  // Apply type coercion before comparison
149
171
  const [coercedV1, coercedV2] = coerceForComparison(v1, v2);
150
- return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) !== 0 ? 1 : 0;
172
+ return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) !== 0;
151
173
  };
152
174
  break;
153
175
  case '<':
@@ -155,9 +177,15 @@ export function emitComparisonOp(plan: BinaryOpNode, ctx: EmissionContext): Inst
155
177
  // SQL comparison: NULL < anything -> NULL
156
178
  if (v1 === null || v2 === null) return null;
157
179
 
180
+ // Try temporal comparison first
181
+ const temporalResult = tryTemporalComparison(operator, v1, v2);
182
+ if (temporalResult !== undefined) {
183
+ return temporalResult;
184
+ }
185
+
158
186
  // Apply type coercion before comparison
159
187
  const [coercedV1, coercedV2] = coerceForComparison(v1, v2);
160
- return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) < 0 ? 1 : 0;
188
+ return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) < 0;
161
189
  };
162
190
  break;
163
191
  case '<=':
@@ -165,9 +193,15 @@ export function emitComparisonOp(plan: BinaryOpNode, ctx: EmissionContext): Inst
165
193
  // SQL comparison: NULL <= anything -> NULL
166
194
  if (v1 === null || v2 === null) return null;
167
195
 
196
+ // Try temporal comparison first
197
+ const temporalResult = tryTemporalComparison(operator, v1, v2);
198
+ if (temporalResult !== undefined) {
199
+ return temporalResult;
200
+ }
201
+
168
202
  // Apply type coercion before comparison
169
203
  const [coercedV1, coercedV2] = coerceForComparison(v1, v2);
170
- return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) <= 0 ? 1 : 0;
204
+ return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) <= 0;
171
205
  };
172
206
  break;
173
207
  case '>':
@@ -177,11 +211,15 @@ export function emitComparisonOp(plan: BinaryOpNode, ctx: EmissionContext): Inst
177
211
  return null;
178
212
  }
179
213
 
214
+ // Try temporal comparison first
215
+ const temporalResult = tryTemporalComparison(operator, v1, v2);
216
+ if (temporalResult !== undefined) {
217
+ return temporalResult;
218
+ }
219
+
180
220
  // Apply type coercion before comparison
181
221
  const [coercedV1, coercedV2] = coerceForComparison(v1, v2);
182
- const comparisonResult = compareSqlValuesFast(coercedV1, coercedV2, collationFunc);
183
- const finalResult = comparisonResult > 0 ? 1 : 0;
184
- return finalResult;
222
+ return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) > 0;
185
223
  };
186
224
  break;
187
225
  case '>=':
@@ -189,13 +227,19 @@ export function emitComparisonOp(plan: BinaryOpNode, ctx: EmissionContext): Inst
189
227
  // SQL comparison: NULL >= anything -> NULL
190
228
  if (v1 === null || v2 === null) return null;
191
229
 
230
+ // Try temporal comparison first
231
+ const temporalResult = tryTemporalComparison(operator, v1, v2);
232
+ if (temporalResult !== undefined) {
233
+ return temporalResult;
234
+ }
235
+
192
236
  // Apply type coercion before comparison
193
237
  const [coercedV1, coercedV2] = coerceForComparison(v1, v2);
194
- return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) >= 0 ? 1 : 0;
238
+ return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) >= 0;
195
239
  };
196
240
  break;
197
241
  default:
198
- throw new QuereusError(`Unsupported comparison operator: ${plan.expression.operator}`, StatusCode.UNSUPPORTED);
242
+ throw new QuereusError(`Unsupported comparison operator: ${operator}`, StatusCode.UNSUPPORTED);
199
243
  }
200
244
 
201
245
  const leftExpr = emitPlanNode(plan.left, ctx);
@@ -237,38 +281,38 @@ export function emitLogicalOp(plan: BinaryOpNode, ctx: EmissionContext): Instruc
237
281
  // SQL three-valued logic
238
282
  switch (operator) {
239
283
  case 'AND': {
240
- // NULL AND x -> NULL if x is true or NULL, otherwise 0
241
- // 0 AND x -> 0
242
- // 1 AND x -> x
284
+ // NULL AND x -> NULL if x is true or NULL, otherwise false
285
+ // false AND x -> false
286
+ // true AND x -> x
243
287
  if (v1 === null) {
244
- return (v2 === null || v2) ? null : 0;
288
+ return (v2 === null || v2) ? null : false;
245
289
  }
246
- if (!v1) return 0;
247
- return v2 === null ? null : (v2 ? 1 : 0);
290
+ if (!v1) return false;
291
+ return v2 === null ? null : (v2 ? true : false);
248
292
  }
249
293
 
250
294
  case 'OR': {
251
- // NULL OR x -> NULL if x is false or NULL, otherwise 1
252
- // 1 OR x -> 1
253
- // 0 OR x -> x
295
+ // NULL OR x -> NULL if x is false or NULL, otherwise true
296
+ // true OR x -> true
297
+ // false OR x -> x
254
298
  if (v1 === null) {
255
- return (v2 === null || !v2) ? null : 1;
299
+ return (v2 === null || !v2) ? null : true;
256
300
  }
257
- if (v1) return 1;
258
- return v2 === null ? null : (v2 ? 1 : 0);
301
+ if (v1) return true;
302
+ return v2 === null ? null : (v2 ? true : false);
259
303
  }
260
304
 
261
305
  case 'XOR': {
262
306
  // NULL XOR x -> NULL
263
307
  // x XOR NULL -> NULL
264
- // 0 XOR 0 -> 0
265
- // 0 XOR 1 -> 1
266
- // 1 XOR 0 -> 1
267
- // 1 XOR 1 -> 0
308
+ // false XOR false -> false
309
+ // false XOR true -> true
310
+ // true XOR false -> true
311
+ // true XOR true -> false
268
312
  if (v1 === null || v2 === null) return null;
269
- const b1 = v1 ? 1 : 0;
270
- const b2 = v2 ? 1 : 0;
271
- return b1 !== b2 ? 1 : 0;
313
+ const b1 = !!v1;
314
+ const b2 = !!v2;
315
+ return b1 !== b2;
272
316
  }
273
317
 
274
318
  default:
@@ -298,7 +342,7 @@ export function emitLikeOp(plan: BinaryOpNode, ctx: EmissionContext): Instructio
298
342
  const textStr = String(text);
299
343
  const patternStr = String(pattern);
300
344
 
301
- return simpleLike(patternStr, textStr) ? 1 : 0;
345
+ return simpleLike(patternStr, textStr);
302
346
  }
303
347
 
304
348
  const leftExpr = emitPlanNode(plan.left, ctx);
@@ -63,13 +63,13 @@ export function emitIn(plan: InNode, ctx: EmissionContext): Instruction {
63
63
  }
64
64
  // Check for match immediately - no need to materialize
65
65
  if (compareSqlValuesFast(condition, rowValue, collation) === 0) {
66
- return 1; // Found a match
66
+ return true; // Found a match
67
67
  }
68
68
  }
69
69
  }
70
70
 
71
71
  // No match found - if any value was NULL, result is NULL
72
- return hasNull ? null : 0;
72
+ return hasNull ? null : false;
73
73
  }
74
74
 
75
75
  const sourceInstruction = emitPlanNode(plan.source, ctx);
@@ -103,11 +103,11 @@ export function emitIn(plan: InNode, ctx: EmissionContext): Instruction {
103
103
  // Check if condition exists in pre-built tree
104
104
  const path = tree.find(condition);
105
105
  if (path.on) {
106
- return 1; // Found a match
106
+ return true; // Found a match
107
107
  }
108
108
 
109
109
  // No match found - if any value was NULL, result is NULL
110
- return hasNull ? null : 0;
110
+ return hasNull ? null : false;
111
111
  }
112
112
 
113
113
  const values = plan.values.map(val => (val as unknown as ConstantNode).getValue());
@@ -163,12 +163,12 @@ export function emitIn(plan: InNode, ctx: EmissionContext): Instruction {
163
163
  continue;
164
164
  }
165
165
  if (compareSqlValuesFast(condition, value, collation) === 0) {
166
- return 1; // Found a match
166
+ return true; // Found a match
167
167
  }
168
168
  }
169
169
 
170
170
  // No match found - if any value was NULL, result is NULL
171
- return hasNull ? null : 0;
171
+ return hasNull ? null : false;
172
172
  }
173
173
 
174
174
  const conditionExpr = emitPlanNode(plan.condition, ctx);
@@ -188,9 +188,9 @@ export function emitIn(plan: InNode, ctx: EmissionContext): Instruction {
188
188
  export function emitExists(plan: ExistsNode, ctx: EmissionContext): Instruction {
189
189
  async function run(_rctx: RuntimeContext, input: AsyncIterable<Row>): Promise<SqlValue> {
190
190
  for await (const _row of input) {
191
- return 1; // First row => TRUE
191
+ return true; // First row => TRUE
192
192
  }
193
- return 0; // Empty => FALSE
193
+ return false; // Empty => FALSE
194
194
  }
195
195
 
196
196
  const innerInstruction = emitPlanNode(plan.subquery, ctx);
@@ -0,0 +1,302 @@
1
+ import { StatusCode } from "../../common/types.js";
2
+ import { QuereusError } from "../../common/errors.js";
3
+ import type { SqlValue } from "../../common/types.js";
4
+ import type { Instruction, InstructionRun, RuntimeContext } from "../types.js";
5
+ import type { BinaryOpNode } from "../../planner/nodes/scalar.js";
6
+ import { emitPlanNode } from "../emitters.js";
7
+ import type { EmissionContext } from "../emission-context.js";
8
+ import { Temporal } from 'temporal-polyfill';
9
+ import { TIMESPAN_TYPE } from "../../types/temporal-types.js";
10
+
11
+ /**
12
+ * Check if a value is a date string (YYYY-MM-DD format)
13
+ */
14
+ function isDateValue(v: SqlValue): boolean {
15
+ if (typeof v !== 'string') return false;
16
+ // Simple check for ISO 8601 date format
17
+ return /^\d{4}-\d{2}-\d{2}$/.test(v);
18
+ }
19
+
20
+ /**
21
+ * Check if a value is a time string (HH:MM:SS format)
22
+ */
23
+ function isTimeValue(v: SqlValue): boolean {
24
+ if (typeof v !== 'string') return false;
25
+ // Simple check for ISO 8601 time format
26
+ return /^\d{2}:\d{2}:\d{2}/.test(v);
27
+ }
28
+
29
+ /**
30
+ * Check if a value is a datetime string (ISO 8601 format)
31
+ */
32
+ function isDateTimeValue(v: SqlValue): boolean {
33
+ if (typeof v !== 'string') return false;
34
+ // Check for ISO 8601 datetime format (with T separator)
35
+ return v.includes('T') && /^\d{4}-\d{2}-\d{2}T/.test(v);
36
+ }
37
+
38
+ /**
39
+ * Check if a value is a timespan/duration string (ISO 8601 duration format)
40
+ */
41
+ function isTimespanValue(v: SqlValue): boolean {
42
+ if (typeof v !== 'string') return false;
43
+ // ISO 8601 duration starts with P (or -P for negative)
44
+ return v.startsWith('P') || v.startsWith('-P');
45
+ }
46
+
47
+ /**
48
+ * Try to perform temporal arithmetic on two values.
49
+ * Returns the result if successful, or undefined if the values are not temporal types.
50
+ * Throws QuereusError if the operation is invalid.
51
+ */
52
+ export function tryTemporalArithmetic(operator: string, v1: SqlValue, v2: SqlValue): SqlValue | undefined {
53
+ if (v1 === null || v2 === null) return null;
54
+
55
+ // Detect types at runtime
56
+ const isV1Date = isDateValue(v1);
57
+ const isV1Time = isTimeValue(v1);
58
+ const isV1DateTime = isDateTimeValue(v1);
59
+ const isV1Timespan = isTimespanValue(v1);
60
+
61
+ const isV2Date = isDateValue(v2);
62
+ const isV2Time = isTimeValue(v2);
63
+ const isV2DateTime = isDateTimeValue(v2);
64
+ const isV2Timespan = isTimespanValue(v2);
65
+
66
+ // If neither operand is temporal, return undefined to signal non-temporal operation
67
+ const isV1Temporal = isV1Date || isV1Time || isV1DateTime || isV1Timespan;
68
+ const isV2Temporal = isV2Date || isV2Time || isV2DateTime || isV2Timespan;
69
+ if (!isV1Temporal && !isV2Temporal) {
70
+ return undefined;
71
+ }
72
+
73
+ try {
74
+
75
+ // DATE/DATETIME - DATE/DATETIME → TIMESPAN
76
+ if (operator === '-' &&
77
+ (isV1Date || isV1DateTime) &&
78
+ (isV2Date || isV2DateTime)) {
79
+
80
+ // Parse both values as dates
81
+ const date1 = isV1DateTime
82
+ ? Temporal.PlainDateTime.from(v1 as string).toPlainDate()
83
+ : Temporal.PlainDate.from(v1 as string);
84
+ const date2 = isV2DateTime
85
+ ? Temporal.PlainDateTime.from(v2 as string).toPlainDate()
86
+ : Temporal.PlainDate.from(v2 as string);
87
+
88
+ const duration = date1.since(date2);
89
+ return duration.toString();
90
+ }
91
+
92
+ // TIME - TIME → TIMESPAN
93
+ if (operator === '-' && isV1Time && isV2Time) {
94
+ const time1 = Temporal.PlainTime.from(v1 as string);
95
+ const time2 = Temporal.PlainTime.from(v2 as string);
96
+ const duration = time1.since(time2);
97
+ return duration.toString();
98
+ }
99
+
100
+ // DATE + TIMESPAN → DATE
101
+ if (operator === '+' && isV1Date && isV2Timespan) {
102
+ const date = Temporal.PlainDate.from(v1 as string);
103
+ const duration = Temporal.Duration.from(v2 as string);
104
+ const result = date.add(duration);
105
+ return result.toString();
106
+ }
107
+
108
+ // TIMESPAN + DATE → DATE (commutative)
109
+ if (operator === '+' && isV1Timespan && isV2Date) {
110
+ const duration = Temporal.Duration.from(v1 as string);
111
+ const date = Temporal.PlainDate.from(v2 as string);
112
+ const result = date.add(duration);
113
+ return result.toString();
114
+ }
115
+
116
+ // DATE - TIMESPAN → DATE
117
+ if (operator === '-' && isV1Date && isV2Timespan) {
118
+ const date = Temporal.PlainDate.from(v1 as string);
119
+ const duration = Temporal.Duration.from(v2 as string);
120
+ const result = date.subtract(duration);
121
+ return result.toString();
122
+ }
123
+
124
+ // DATETIME + TIMESPAN → DATETIME
125
+ if (operator === '+' && isV1DateTime && isV2Timespan) {
126
+ const dt = Temporal.PlainDateTime.from(v1 as string);
127
+ const duration = Temporal.Duration.from(v2 as string);
128
+ const result = dt.add(duration);
129
+ return result.toString();
130
+ }
131
+
132
+ // TIMESPAN + DATETIME → DATETIME (commutative)
133
+ if (operator === '+' && isV1Timespan && isV2DateTime) {
134
+ const duration = Temporal.Duration.from(v1 as string);
135
+ const dt = Temporal.PlainDateTime.from(v2 as string);
136
+ const result = dt.add(duration);
137
+ return result.toString();
138
+ }
139
+
140
+ // DATETIME - TIMESPAN → DATETIME
141
+ if (operator === '-' && isV1DateTime && isV2Timespan) {
142
+ const dt = Temporal.PlainDateTime.from(v1 as string);
143
+ const duration = Temporal.Duration.from(v2 as string);
144
+ const result = dt.subtract(duration);
145
+ return result.toString();
146
+ }
147
+
148
+ // TIME + TIMESPAN → TIME
149
+ if (operator === '+' && isV1Time && isV2Timespan) {
150
+ const time = Temporal.PlainTime.from(v1 as string);
151
+ const duration = Temporal.Duration.from(v2 as string);
152
+ const result = time.add(duration);
153
+ return result.toString();
154
+ }
155
+
156
+ // TIMESPAN + TIME → TIME (commutative)
157
+ if (operator === '+' && isV1Timespan && isV2Time) {
158
+ const duration = Temporal.Duration.from(v1 as string);
159
+ const time = Temporal.PlainTime.from(v2 as string);
160
+ const result = time.add(duration);
161
+ return result.toString();
162
+ }
163
+
164
+ // TIME - TIMESPAN → TIME
165
+ if (operator === '-' && isV1Time && isV2Timespan) {
166
+ const time = Temporal.PlainTime.from(v1 as string);
167
+ const duration = Temporal.Duration.from(v2 as string);
168
+ const result = time.subtract(duration);
169
+ return result.toString();
170
+ }
171
+
172
+ // TIMESPAN + TIMESPAN → TIMESPAN
173
+ if (operator === '+' && isV1Timespan && isV2Timespan) {
174
+ const d1 = Temporal.Duration.from(v1 as string);
175
+ const d2 = Temporal.Duration.from(v2 as string);
176
+ const result = d1.add(d2);
177
+ return result.toString();
178
+ }
179
+
180
+ // TIMESPAN - TIMESPAN → TIMESPAN
181
+ if (operator === '-' && isV1Timespan && isV2Timespan) {
182
+ const d1 = Temporal.Duration.from(v1 as string);
183
+ const d2 = Temporal.Duration.from(v2 as string);
184
+ const result = d1.subtract(d2);
185
+ return result.toString();
186
+ }
187
+
188
+ // TIMESPAN * NUMBER → TIMESPAN
189
+ if (operator === '*' && isV1Timespan && typeof v2 === 'number') {
190
+ const duration = Temporal.Duration.from(v1 as string);
191
+ // Convert to seconds, multiply, convert back
192
+ const totalSeconds = duration.total({ unit: 'seconds' });
193
+ const newDuration = Temporal.Duration.from({ seconds: totalSeconds * v2 });
194
+ return newDuration.toString();
195
+ }
196
+
197
+ // NUMBER * TIMESPAN → TIMESPAN (commutative)
198
+ if (operator === '*' && typeof v1 === 'number' && isV2Timespan) {
199
+ const duration = Temporal.Duration.from(v2 as string);
200
+ const totalSeconds = duration.total({ unit: 'seconds' });
201
+ const newDuration = Temporal.Duration.from({ seconds: totalSeconds * v1 });
202
+ return newDuration.toString();
203
+ }
204
+
205
+ // TIMESPAN / NUMBER → TIMESPAN
206
+ if (operator === '/' && isV1Timespan && typeof v2 === 'number') {
207
+ if (v2 === 0) return null;
208
+ const duration = Temporal.Duration.from(v1 as string);
209
+ const totalSeconds = duration.total({ unit: 'seconds' });
210
+ const newDuration = Temporal.Duration.from({ seconds: totalSeconds / v2 });
211
+ return newDuration.toString();
212
+ }
213
+
214
+ // TIMESPAN / TIMESPAN → NUMBER (ratio)
215
+ if (operator === '/' && isV1Timespan && isV2Timespan) {
216
+ const d1 = Temporal.Duration.from(v1 as string);
217
+ const d2 = Temporal.Duration.from(v2 as string);
218
+ const total1 = d1.total({ unit: 'seconds' });
219
+ const total2 = d2.total({ unit: 'seconds' });
220
+ if (total2 === 0) return null;
221
+ return total1 / total2;
222
+ }
223
+
224
+ // If we get here, the operation is not supported
225
+ throw new QuereusError(
226
+ `Unsupported temporal operation`,
227
+ StatusCode.UNSUPPORTED
228
+ );
229
+ } catch (e) {
230
+ // Invalid temporal operation - return null
231
+ if (e instanceof QuereusError) throw e;
232
+ return null;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Emit temporal arithmetic operations
238
+ * Handles operations between temporal types (DATE, TIME, DATETIME, TIMESPAN)
239
+ */
240
+ export function emitTemporalArithmetic(plan: BinaryOpNode, ctx: EmissionContext): Instruction {
241
+ const operator = plan.expression.operator;
242
+
243
+ function run(ctx: RuntimeContext, v1: SqlValue, v2: SqlValue): SqlValue {
244
+ return tryTemporalArithmetic(operator, v1, v2) ?? null;
245
+ }
246
+
247
+ const leftExpr = emitPlanNode(plan.left, ctx);
248
+ const rightExpr = emitPlanNode(plan.right, ctx);
249
+
250
+ return {
251
+ params: [leftExpr, rightExpr],
252
+ run: run as InstructionRun,
253
+ note: `${operator}(temporal)`
254
+ };
255
+ }
256
+
257
+ /**
258
+ * Attempts to perform temporal comparison. Returns undefined if not a temporal comparison.
259
+ * This allows the caller to fall back to standard comparison logic.
260
+ *
261
+ * Temporal types that need special comparison logic (beyond lexicographic string comparison):
262
+ * - TIMESPAN: Durations need to be compared semantically, not lexicographically
263
+ * (e.g., "PT30M" > "PT1H" lexicographically, but 30 minutes < 1 hour semantically)
264
+ *
265
+ * Note: DATE, TIME, and DATETIME use ISO 8601 format which compares correctly lexicographically,
266
+ * so they don't need special handling here.
267
+ *
268
+ * @param operator The comparison operator (=, !=, <, <=, >, >=)
269
+ * @param v1 First value
270
+ * @param v2 Second value
271
+ * @returns Comparison result (boolean) if temporal comparison, undefined otherwise
272
+ */
273
+ export function tryTemporalComparison(operator: string, v1: SqlValue, v2: SqlValue): SqlValue | undefined {
274
+ // Check if both values are timespans
275
+ // Timespans are the only temporal type that needs special comparison logic
276
+ // because ISO 8601 duration strings don't compare correctly lexicographically
277
+ if (!isTimespanValue(v1) || !isTimespanValue(v2)) {
278
+ return undefined;
279
+ }
280
+
281
+ // Use the TIMESPAN_TYPE's compare function
282
+ const cmp = TIMESPAN_TYPE.compare!(v1, v2);
283
+
284
+ switch (operator) {
285
+ case '=':
286
+ case '==':
287
+ return cmp === 0;
288
+ case '!=':
289
+ case '<>':
290
+ return cmp !== 0;
291
+ case '<':
292
+ return cmp < 0;
293
+ case '<=':
294
+ return cmp <= 0;
295
+ case '>':
296
+ return cmp > 0;
297
+ case '>=':
298
+ return cmp >= 0;
299
+ default:
300
+ return undefined;
301
+ }
302
+ }
@@ -6,6 +6,7 @@ import type { UnaryOpNode } from "../../planner/nodes/scalar.js";
6
6
  import { emitPlanNode } from "../emitters.js";
7
7
  import type { EmissionContext } from "../emission-context.js";
8
8
  import { isTruthy } from "../../util/comparison.js";
9
+ import { Temporal } from 'temporal-polyfill';
9
10
 
10
11
  export function emitUnaryOp(plan: UnaryOpNode, ctx: EmissionContext): Instruction {
11
12
  // Select the operation function at emit time
@@ -18,23 +19,23 @@ export function emitUnaryOp(plan: UnaryOpNode, ctx: EmissionContext): Instructio
18
19
  switch (operator) {
19
20
  case 'NOT':
20
21
  run = (ctx: RuntimeContext, operand: SqlValue) => {
21
- // SQL NOT: NULL -> NULL, 0 -> 1, anything else -> 0
22
+ // SQL NOT: NULL -> NULL, false -> true, true -> false
22
23
  if (operand === null) return null;
23
- return isTruthy(operand) ? 0 : 1;
24
+ return !isTruthy(operand);
24
25
  };
25
26
  note = 'NOT';
26
27
  break;
27
28
 
28
29
  case 'IS NULL':
29
30
  run = (ctx: RuntimeContext, operand: SqlValue) => {
30
- return operand === null ? 1 : 0;
31
+ return operand === null;
31
32
  };
32
33
  note = 'IS NULL';
33
34
  break;
34
35
 
35
36
  case 'IS NOT NULL':
36
37
  run = (ctx: RuntimeContext, operand: SqlValue) => {
37
- return operand !== null ? 1 : 0;
38
+ return operand !== null;
38
39
  };
39
40
  note = 'IS NOT NULL';
40
41
  break;
@@ -42,6 +43,18 @@ export function emitUnaryOp(plan: UnaryOpNode, ctx: EmissionContext): Instructio
42
43
  case '-':
43
44
  run = (ctx: RuntimeContext, operand: SqlValue) => {
44
45
  if (operand === null) return null;
46
+
47
+ // Check if it's a timespan (ISO 8601 duration string)
48
+ if (typeof operand === 'string' && (operand.startsWith('P') || operand.startsWith('-P'))) {
49
+ try {
50
+ const duration = Temporal.Duration.from(operand);
51
+ return duration.negated().toString();
52
+ } catch {
53
+ // Not a valid duration, fall through to numeric handling
54
+ }
55
+ }
56
+
57
+ // Numeric negation
45
58
  if (typeof operand === 'number') return -operand;
46
59
  if (typeof operand === 'bigint') return -operand;
47
60
  // Try to convert to number
@@ -101,7 +101,7 @@ function tableSchemaToCatalog(tableSchema: TableSchema): CatalogTable {
101
101
 
102
102
  const columns = tableSchema.columns.map(col => ({
103
103
  name: col.name,
104
- type: col.affinity?.toString() || 'TEXT',
104
+ type: col.logicalType.name,
105
105
  notNull: col.notNull,
106
106
  primaryKey: col.primaryKey
107
107
  }));
@@ -165,8 +165,8 @@ function generateTableDDL(tableSchema: TableSchema): string {
165
165
  const columnDefs: string[] = [];
166
166
  for (const col of tableSchema.columns) {
167
167
  let colDef = `"${col.name}"`;
168
- if (col.affinity) {
169
- colDef += ` ${col.affinity}`;
168
+ if (col.logicalType) {
169
+ colDef += ` ${col.logicalType.name}`;
170
170
  }
171
171
  if (col.notNull) {
172
172
  colDef += ' NOT NULL';
@@ -10,8 +10,6 @@ export interface ColumnSchema {
10
10
  name: string;
11
11
  /** Logical type definition */
12
12
  logicalType: LogicalType;
13
- /** Data type affinity (TEXT, INTEGER, REAL, BLOB, NUMERIC) - kept for backward compatibility during transition */
14
- affinity: SqlDataType;
15
13
  /** Whether the column has a NOT NULL constraint */
16
14
  notNull: boolean;
17
15
  /** Whether the column is part of the primary key */
@@ -43,7 +41,6 @@ export function createDefaultColumnSchema(name: string, defaultNotNull: boolean
43
41
  return {
44
42
  name: name,
45
43
  logicalType: TEXT_TYPE,
46
- affinity: SqlDataType.TEXT,
47
44
  notNull: defaultNotNull, // Third Manifesto: default to NOT NULL
48
45
  primaryKey: false,
49
46
  pkOrder: 0,