@quereus/quereus 0.5.2 → 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 (214) 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/temporal-arithmetic.d.ts +33 -0
  119. package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -0
  120. package/dist/src/runtime/emit/temporal-arithmetic.js +269 -0
  121. package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -0
  122. package/dist/src/runtime/emit/unary.d.ts.map +1 -1
  123. package/dist/src/runtime/emit/unary.js +12 -0
  124. package/dist/src/runtime/emit/unary.js.map +1 -1
  125. package/dist/src/schema/catalog.js +3 -3
  126. package/dist/src/schema/catalog.js.map +1 -1
  127. package/dist/src/schema/column.d.ts +0 -3
  128. package/dist/src/schema/column.d.ts.map +1 -1
  129. package/dist/src/schema/column.js +0 -2
  130. package/dist/src/schema/column.js.map +1 -1
  131. package/dist/src/schema/function.d.ts +29 -1
  132. package/dist/src/schema/function.d.ts.map +1 -1
  133. package/dist/src/schema/function.js.map +1 -1
  134. package/dist/src/schema/table.d.ts +3 -3
  135. package/dist/src/schema/table.d.ts.map +1 -1
  136. package/dist/src/schema/table.js +4 -6
  137. package/dist/src/schema/table.js.map +1 -1
  138. package/dist/src/types/index.d.ts +1 -1
  139. package/dist/src/types/index.d.ts.map +1 -1
  140. package/dist/src/types/index.js +1 -1
  141. package/dist/src/types/index.js.map +1 -1
  142. package/dist/src/types/registry.d.ts.map +1 -1
  143. package/dist/src/types/registry.js +5 -1
  144. package/dist/src/types/registry.js.map +1 -1
  145. package/dist/src/types/temporal-types.d.ts +5 -0
  146. package/dist/src/types/temporal-types.d.ts.map +1 -1
  147. package/dist/src/types/temporal-types.js +122 -0
  148. package/dist/src/types/temporal-types.js.map +1 -1
  149. package/dist/src/util/plan-formatter.d.ts.map +1 -1
  150. package/dist/src/util/plan-formatter.js +1 -5
  151. package/dist/src/util/plan-formatter.js.map +1 -1
  152. package/dist/src/util/row-descriptor.js +2 -2
  153. package/dist/src/util/row-descriptor.js.map +1 -1
  154. package/dist/src/vtab/best-access-plan.d.ts +4 -3
  155. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  156. package/dist/src/vtab/best-access-plan.js.map +1 -1
  157. package/dist/src/vtab/memory/module.js +1 -1
  158. package/dist/src/vtab/memory/module.js.map +1 -1
  159. package/package.json +1 -1
  160. package/src/common/datatype.ts +4 -5
  161. package/src/common/type-inference.ts +13 -22
  162. package/src/core/param.ts +4 -11
  163. package/src/func/builtins/aggregate.ts +24 -2
  164. package/src/func/builtins/builtin-window-functions.ts +10 -10
  165. package/src/func/builtins/conversion.ts +26 -1
  166. package/src/func/builtins/explain.ts +53 -53
  167. package/src/func/builtins/generation.ts +2 -2
  168. package/src/func/builtins/index.ts +20 -1
  169. package/src/func/builtins/json-tvf.ts +17 -17
  170. package/src/func/builtins/scalar.ts +205 -14
  171. package/src/func/builtins/schema.ts +18 -18
  172. package/src/func/builtins/string.ts +91 -78
  173. package/src/func/builtins/timespan.ts +179 -0
  174. package/src/func/registration.ts +35 -5
  175. package/src/index.ts +2 -1
  176. package/src/parser/parser.ts +2 -2
  177. package/src/planner/building/constraint-builder.ts +2 -2
  178. package/src/planner/building/delete.ts +3 -3
  179. package/src/planner/building/function-call.ts +44 -3
  180. package/src/planner/building/insert.ts +3 -3
  181. package/src/planner/building/select.ts +3 -2
  182. package/src/planner/building/update.ts +7 -7
  183. package/src/planner/nodes/aggregate-function.ts +13 -3
  184. package/src/planner/nodes/cte-node.ts +2 -2
  185. package/src/planner/nodes/declarative-schema.ts +3 -3
  186. package/src/planner/nodes/function.ts +8 -3
  187. package/src/planner/nodes/insert-node.ts +1 -1
  188. package/src/planner/nodes/pragma.ts +4 -3
  189. package/src/planner/nodes/reference.ts +1 -1
  190. package/src/planner/nodes/scalar.ts +54 -102
  191. package/src/planner/nodes/sequencing-node.ts +2 -2
  192. package/src/planner/nodes/sink-node.ts +2 -2
  193. package/src/planner/nodes/subquery.ts +5 -7
  194. package/src/planner/nodes/view-reference-node.ts +2 -2
  195. package/src/planner/nodes/window-function.ts +3 -3
  196. package/src/planner/rules/access/rule-select-access-path.ts +1 -1
  197. package/src/planner/rules/retrieve/rule-grow-retrieve.ts +1 -1
  198. package/src/planner/scopes/global.ts +3 -3
  199. package/src/planner/scopes/param.ts +2 -2
  200. package/src/planner/type-utils.ts +6 -14
  201. package/src/runtime/emit/binary.ts +48 -2
  202. package/src/runtime/emit/temporal-arithmetic.ts +302 -0
  203. package/src/runtime/emit/unary.ts +13 -0
  204. package/src/schema/catalog.ts +3 -3
  205. package/src/schema/column.ts +0 -3
  206. package/src/schema/function.ts +29 -1
  207. package/src/schema/table.ts +5 -7
  208. package/src/types/index.ts +1 -1
  209. package/src/types/registry.ts +5 -1
  210. package/src/types/temporal-types.ts +123 -0
  211. package/src/util/plan-formatter.ts +1 -4
  212. package/src/util/row-descriptor.ts +2 -2
  213. package/src/vtab/best-access-plan.ts +4 -3
  214. package/src/vtab/memory/module.ts +1 -1
@@ -1,13 +1,14 @@
1
1
  import { PlanNode, type ScalarPlanNode } from "./plan-node.js";
2
2
  import type { ScalarType } from "../../common/datatype.js";
3
3
  import type { RelationalPlanNode } from "./plan-node.js";
4
- import { type CompareFn, SqlDataType } from "../../common/types.js";
4
+ import { type CompareFn } from "../../common/types.js";
5
5
  import { PlanNodeType } from "./plan-node-type.js";
6
6
  import type { Scope } from "../scopes/scope.js";
7
7
  import { compareSqlValues } from "../../util/comparison.js";
8
8
  import type { Expression } from "../../parser/ast.js";
9
9
  import { formatExpression, formatScalarType } from "../../util/plan-formatter.js";
10
10
  import { quereusError } from "../../common/errors.js";
11
+ import { BLOB_TYPE, INTEGER_TYPE } from "../../types/builtin-types.js";
11
12
  import { StatusCode } from "../../common/types.js";
12
13
 
13
14
  export class ScalarSubqueryNode extends PlanNode implements ScalarPlanNode {
@@ -31,10 +32,9 @@ export class ScalarSubqueryNode extends PlanNode implements ScalarPlanNode {
31
32
  // Fallback to nullable BLOB if we can't determine type
32
33
  return {
33
34
  typeClass: 'scalar',
34
- affinity: SqlDataType.BLOB,
35
+ logicalType: BLOB_TYPE,
35
36
  nullable: true,
36
37
  isReadOnly: true,
37
- datatype: SqlDataType.BLOB,
38
38
  };
39
39
  }
40
40
 
@@ -103,10 +103,9 @@ export class InNode extends PlanNode implements ScalarPlanNode {
103
103
  getType(): ScalarType {
104
104
  return {
105
105
  typeClass: 'scalar',
106
- affinity: SqlDataType.INTEGER,
106
+ logicalType: INTEGER_TYPE,
107
107
  nullable: false,
108
108
  isReadOnly: true,
109
- datatype: SqlDataType.INTEGER,
110
109
  }
111
110
  }
112
111
 
@@ -216,10 +215,9 @@ export class ExistsNode extends PlanNode implements ScalarPlanNode {
216
215
  getType(): ScalarType {
217
216
  return {
218
217
  typeClass: 'scalar',
219
- affinity: SqlDataType.INTEGER,
218
+ logicalType: INTEGER_TYPE,
220
219
  nullable: false,
221
220
  isReadOnly: true,
222
- datatype: SqlDataType.INTEGER,
223
221
  };
224
222
  }
225
223
 
@@ -1,10 +1,10 @@
1
1
  import { PlanNode, type ZeroAryRelationalNode, type Attribute } from './plan-node.js';
2
2
  import type { RelationType } 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 type { ViewSchema } from '../../schema/view.js';
7
6
  import { Cached } from '../../util/cached.js';
7
+ import { TEXT_TYPE } from '../../types/builtin-types.js';
8
8
 
9
9
  /**
10
10
  * Plan node for referencing a view in a FROM clause.
@@ -36,7 +36,7 @@ export class ViewReferenceNode extends PlanNode implements ZeroAryRelationalNode
36
36
  name: columnName,
37
37
  type: {
38
38
  typeClass: 'scalar' as const,
39
- affinity: SqlDataType.TEXT,
39
+ logicalType: TEXT_TYPE,
40
40
  nullable: true,
41
41
  isReadOnly: false
42
42
  }, // Default type, should be inferred
@@ -4,8 +4,8 @@ import type { ScalarType } from '../../common/datatype.js';
4
4
  import type { Scope } from '../scopes/scope.js';
5
5
  import type { WindowFunctionExpr } from '../../parser/ast.js';
6
6
  import { Cached } from '../../util/cached.js';
7
- import { SqlDataType } from '../../common/types.js';
8
7
  import { formatScalarType } from '../../util/plan-formatter.js';
8
+ import { INTEGER_TYPE, REAL_TYPE } from '../../types/builtin-types.js';
9
9
 
10
10
  /**
11
11
  * Represents a window function call in the query plan.
@@ -30,10 +30,10 @@ export class WindowFunctionCallNode extends PlanNode implements ZeroAryScalarNod
30
30
  // Most window functions return numeric types
31
31
  // row_number() specifically returns an integer
32
32
  if (this.functionName === 'row_number') {
33
- return { typeClass: 'scalar', affinity: SqlDataType.INTEGER, nullable: false } satisfies ScalarType;
33
+ return { typeClass: 'scalar', logicalType: INTEGER_TYPE, nullable: false } satisfies ScalarType;
34
34
  }
35
35
  // Other window functions would have their own type inference
36
- return { typeClass: 'scalar', affinity: SqlDataType.NUMERIC, nullable: false } satisfies ScalarType;
36
+ return { typeClass: 'scalar', logicalType: REAL_TYPE, nullable: false } satisfies ScalarType;
37
37
  });
38
38
  }
39
39
 
@@ -120,7 +120,7 @@ function createIndexBasedAccess(retrieveNode: RetrieveNode, context: OptContext)
120
120
  columns: tableSchema.columns.map((col, index) => ({
121
121
  index,
122
122
  name: col.name,
123
- type: col.affinity,
123
+ type: col.logicalType,
124
124
  isPrimaryKey: col.primaryKey || false,
125
125
  isUnique: col.primaryKey || false // For now, assume only PK columns are unique
126
126
  } as ColumnMeta)),
@@ -231,7 +231,7 @@ function fallbackIndexSupports(
231
231
  columns: tableSchema.columns.map((col, index) => ({
232
232
  index,
233
233
  name: col.name,
234
- type: col.affinity,
234
+ type: col.logicalType,
235
235
  isPrimaryKey: col.primaryKey || false,
236
236
  isUnique: col.primaryKey || false
237
237
  })),
@@ -5,8 +5,8 @@ import * as AST from "../../parser/ast.js";
5
5
  import { FunctionReferenceNode, TableReferenceNode } from "../nodes/reference.js";
6
6
  import { Ambiguous } from "./scope.js";
7
7
  import type { ScalarType } from "../../common/datatype.js";
8
- import { SqlDataType } from "../../common/types.js";
9
8
  import { isScalarFunctionSchema } from "../../schema/function.js";
9
+ import { REAL_TYPE } from "../../types/builtin-types.js";
10
10
 
11
11
  export class GlobalScope extends BaseScope {
12
12
  constructor(public readonly manager: SchemaManager) {
@@ -25,7 +25,7 @@ export class GlobalScope extends BaseScope {
25
25
  // Get the proper scalar type from the function schema
26
26
  const scalarType: ScalarType = isScalarFunctionSchema(func)
27
27
  ? func.returnType
28
- : { typeClass: 'scalar', affinity: SqlDataType.NUMERIC, nullable: true, isReadOnly: true };
28
+ : { typeClass: 'scalar', logicalType: REAL_TYPE, nullable: true, isReadOnly: true };
29
29
 
30
30
  return new FunctionReferenceNode(this, func, scalarType);
31
31
  }
@@ -54,7 +54,7 @@ export class GlobalScope extends BaseScope {
54
54
  // Get the proper scalar type from the function schema
55
55
  const scalarType: ScalarType = isScalarFunctionSchema(func)
56
56
  ? func.returnType
57
- : { typeClass: 'scalar', affinity: SqlDataType.NUMERIC, nullable: true, isReadOnly: true };
57
+ : { typeClass: 'scalar', logicalType: REAL_TYPE, nullable: true, isReadOnly: true };
58
58
 
59
59
  return new FunctionReferenceNode(this, func, scalarType);
60
60
  }
@@ -3,13 +3,13 @@ import { ParameterReferenceNode } from '../nodes/reference.js'; // Corrected imp
3
3
  import { BaseScope } from './base.js';
4
4
  import { Ambiguous, type Scope } from './scope.js';
5
5
  import type { ScalarType } from '../../common/datatype.js';
6
- import { SqlDataType } from '../../common/types.js';
7
6
  import type { PlanNode } from '../nodes/plan-node.js';
7
+ import { TEXT_TYPE } from '../../types/builtin-types.js';
8
8
 
9
9
  // Default type for parameters when not otherwise specified.
10
10
  const DEFAULT_PARAMETER_TYPE: ScalarType = {
11
11
  typeClass: 'scalar',
12
- affinity: SqlDataType.TEXT,
12
+ logicalType: TEXT_TYPE,
13
13
  nullable: true,
14
14
  };
15
15
 
@@ -4,8 +4,7 @@ import type { RelationType, ColumnDef, ScalarType, ColRef } from '../common/data
4
4
  import { SqlDataType, StatusCode, type DeepReadonly, type SqlValue } from '../common/types.js'; // Import SqlValue and ensure SqlDataType is not type-only
5
5
  import type { AstNode } from '../parser/ast.js';
6
6
  import { QuereusError } from '../common/errors.js';
7
- // Note: getAffinity from '../schema/column.js' is used by the ColumnSchema type from table.js itself if that's what we use.
8
- // If tableSchema.columns are of type from '../schema/column.js', then their affinity is already SqlDataType.
7
+ import { inferLogicalTypeFromValue } from '../common/type-inference.js';
9
8
 
10
9
  /**
11
10
  * Converts a TableSchema (from src/schema/table.ts) to a RelationType (from src/common/datatype.ts).
@@ -17,7 +16,7 @@ export function relationTypeFromTableSchema(tableSchema: TableSchema): RelationT
17
16
  name: col.name,
18
17
  type: {
19
18
  typeClass: 'scalar',
20
- affinity: col.affinity,
19
+ logicalType: col.logicalType,
21
20
  collationName: col.collation,
22
21
  nullable: !col.notNull,
23
22
  isReadOnly: false,
@@ -53,18 +52,11 @@ export function relationTypeFromTableSchema(tableSchema: TableSchema): RelationT
53
52
  * @returns A ScalarType representing the inferred type of the value.
54
53
  */
55
54
  export function getParameterScalarType(value: SqlValue): ScalarType {
56
- let affinity: SqlDataType;
57
- if (value === null) affinity = SqlDataType.NULL;
58
- else if (typeof value === 'number') affinity = SqlDataType.REAL;
59
- else if (typeof value === 'bigint') affinity = SqlDataType.INTEGER;
60
- else if (typeof value === 'string') affinity = SqlDataType.TEXT;
61
- else if (value instanceof Uint8Array) affinity = SqlDataType.BLOB;
62
- else if (typeof value === 'boolean') affinity = SqlDataType.INTEGER; // Store booleans as INTEGER
63
- else affinity = SqlDataType.BLOB; // Default for unknown types that might pass as SqlValue
55
+ const logicalType = inferLogicalTypeFromValue(value);
64
56
 
65
57
  return {
66
58
  typeClass: 'scalar',
67
- affinity: affinity,
59
+ logicalType,
68
60
  nullable: true, // No guarantees about the value, so it's nullable
69
61
  isReadOnly: true, // Parameters are read-only within the query execution context
70
62
  };
@@ -80,12 +72,12 @@ export function checkRelationsAssignable(source: RelationType, target: RelationT
80
72
  return checkColumnsAssignable(source.columns, target.columns, astNode);
81
73
  }
82
74
 
83
- export function columnSchemaToDef(colName: string, colDef: ColumnSchema): { name: string; type: { typeClass: "scalar"; affinity: SqlDataType; collationName: string; nullable: boolean; isReadOnly: false; }; generated: boolean; } {
75
+ export function columnSchemaToDef(colName: string, colDef: ColumnSchema): ColumnDef {
84
76
  return {
85
77
  name: colName,
86
78
  type: {
87
79
  typeClass: 'scalar',
88
- affinity: colDef.affinity,
80
+ logicalType: colDef.logicalType,
89
81
  collationName: colDef.collation,
90
82
  nullable: !colDef.notNull,
91
83
  isReadOnly: false,
@@ -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,13 +135,21 @@ 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
155
  return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) === 0 ? 1 : 0;
@@ -145,6 +161,12 @@ 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
172
  return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) !== 0 ? 1 : 0;
@@ -155,6 +177,12 @@ 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
188
  return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) < 0 ? 1 : 0;
@@ -165,6 +193,12 @@ 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
204
  return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) <= 0 ? 1 : 0;
@@ -177,6 +211,12 @@ 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
222
  const comparisonResult = compareSqlValuesFast(coercedV1, coercedV2, collationFunc);
@@ -189,13 +229,19 @@ export function emitComparisonOp(plan: BinaryOpNode, ctx: EmissionContext): Inst
189
229
  // SQL comparison: NULL >= anything -> NULL
190
230
  if (v1 === null || v2 === null) return null;
191
231
 
232
+ // Try temporal comparison first
233
+ const temporalResult = tryTemporalComparison(operator, v1, v2);
234
+ if (temporalResult !== undefined) {
235
+ return temporalResult;
236
+ }
237
+
192
238
  // Apply type coercion before comparison
193
239
  const [coercedV1, coercedV2] = coerceForComparison(v1, v2);
194
240
  return compareSqlValuesFast(coercedV1, coercedV2, collationFunc) >= 0 ? 1 : 0;
195
241
  };
196
242
  break;
197
243
  default:
198
- throw new QuereusError(`Unsupported comparison operator: ${plan.expression.operator}`, StatusCode.UNSUPPORTED);
244
+ throw new QuereusError(`Unsupported comparison operator: ${operator}`, StatusCode.UNSUPPORTED);
199
245
  }
200
246
 
201
247
  const leftExpr = emitPlanNode(plan.left, 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 (0 or 1) 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 ? 1 : 0;
288
+ case '!=':
289
+ case '<>':
290
+ return cmp !== 0 ? 1 : 0;
291
+ case '<':
292
+ return cmp < 0 ? 1 : 0;
293
+ case '<=':
294
+ return cmp <= 0 ? 1 : 0;
295
+ case '>':
296
+ return cmp > 0 ? 1 : 0;
297
+ case '>=':
298
+ return cmp >= 0 ? 1 : 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
@@ -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