metal-orm 1.1.11 → 1.1.16

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 (182) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/package.json +100 -96
  5. package/scripts/generate-entities/schema.mjs +122 -122
  6. package/scripts/generate-entities/tree-detection.mjs +92 -92
  7. package/scripts/inflection/pt-br.mjs +468 -468
  8. package/scripts/run-eslint.mjs +33 -34
  9. package/scripts/show-sql-seed.sql +102 -0
  10. package/scripts/show-sql.mjs +321 -325
  11. package/src/codegen/naming-strategy.ts +54 -54
  12. package/src/codegen/typescript.ts +482 -482
  13. package/src/core/ast/adapters.ts +1 -1
  14. package/src/core/ast/builders.ts +123 -123
  15. package/src/core/ast/expression-builders.ts +745 -745
  16. package/src/core/ast/expression-nodes.ts +304 -304
  17. package/src/core/ast/expression-visitor.ts +212 -212
  18. package/src/core/ast/expression.ts +20 -20
  19. package/src/core/ast/helpers.ts +14 -14
  20. package/src/core/ast/join.ts +18 -18
  21. package/src/core/ast/query.ts +200 -200
  22. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  23. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  24. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  25. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  26. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  27. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  28. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  29. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  30. package/src/core/ddl/introspect/context.ts +15 -15
  31. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  32. package/src/core/ddl/introspect/registry.ts +40 -40
  33. package/src/core/ddl/introspect/run-select.ts +35 -35
  34. package/src/core/ddl/introspect/utils.ts +56 -56
  35. package/src/core/ddl/naming-strategy.ts +16 -16
  36. package/src/core/ddl/schema-dialect.ts +56 -56
  37. package/src/core/ddl/schema-diff.ts +234 -234
  38. package/src/core/ddl/schema-generator.ts +1 -1
  39. package/src/core/ddl/schema-introspect.ts +25 -25
  40. package/src/core/ddl/sql-writing.ts +171 -171
  41. package/src/core/dialect/abstract.ts +541 -541
  42. package/src/core/dialect/base/cte-compiler.ts +33 -33
  43. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  44. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  45. package/src/core/dialect/base/join-compiler.ts +25 -25
  46. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  47. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  48. package/src/core/dialect/base/returning-strategy.ts +57 -57
  49. package/src/core/dialect/base/sql-dialect.ts +294 -294
  50. package/src/core/dialect/dialect-factory.ts +91 -91
  51. package/src/core/dialect/mssql/functions.ts +120 -120
  52. package/src/core/dialect/mssql/index.ts +317 -317
  53. package/src/core/dialect/mysql/functions.ts +103 -103
  54. package/src/core/dialect/mysql/index.ts +105 -105
  55. package/src/core/dialect/postgres/functions.ts +108 -108
  56. package/src/core/dialect/postgres/index.ts +91 -91
  57. package/src/core/dialect/sqlite/functions.ts +129 -129
  58. package/src/core/dialect/sqlite/index.ts +67 -67
  59. package/src/core/driver/database-driver.ts +19 -19
  60. package/src/core/driver/mssql-driver.ts +23 -23
  61. package/src/core/driver/mysql-driver.ts +23 -23
  62. package/src/core/driver/postgres-driver.ts +23 -23
  63. package/src/core/driver/sqlite-driver.ts +23 -23
  64. package/src/core/execution/db-executor.ts +92 -92
  65. package/src/core/execution/executors/better-sqlite3-executor.ts +90 -90
  66. package/src/core/execution/executors/mssql-executor.ts +184 -184
  67. package/src/core/execution/executors/mysql-executor.ts +88 -88
  68. package/src/core/execution/executors/postgres-executor.ts +26 -26
  69. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  70. package/src/core/execution/pooling/pool-types.ts +30 -30
  71. package/src/core/execution/pooling/pool.ts +275 -275
  72. package/src/core/functions/array.ts +35 -35
  73. package/src/core/functions/control-flow.ts +79 -79
  74. package/src/core/functions/datetime.ts +237 -237
  75. package/src/core/functions/json.ts +70 -70
  76. package/src/core/functions/numeric.ts +297 -297
  77. package/src/core/functions/text.ts +334 -334
  78. package/src/core/functions/types.ts +33 -33
  79. package/src/core/hydration/types.ts +51 -51
  80. package/src/core/sql/sql-operator-config.ts +39 -39
  81. package/src/core/sql/sql.ts +152 -152
  82. package/src/decorators/column-decorator.ts +87 -87
  83. package/src/decorators/decorator-metadata.ts +143 -143
  84. package/src/decorators/entity.ts +93 -93
  85. package/src/decorators/index.ts +39 -39
  86. package/src/decorators/relations.ts +231 -231
  87. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  88. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  89. package/src/decorators/transformers/transformer-executor.ts +171 -171
  90. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  91. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  92. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  93. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  94. package/src/decorators/validators/country-validator-registry.ts +64 -64
  95. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  96. package/src/decorators/validators/country-validators.ts +105 -105
  97. package/src/decorators/validators/validator-metadata.ts +59 -59
  98. package/src/dto/dto-types.ts +226 -226
  99. package/src/dto/filter-types.ts +141 -141
  100. package/src/dto/index.ts +95 -95
  101. package/src/dto/openapi/generators/base.ts +29 -29
  102. package/src/dto/openapi/generators/column.ts +37 -37
  103. package/src/dto/openapi/generators/dto.ts +91 -91
  104. package/src/dto/openapi/generators/filter.ts +75 -75
  105. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  106. package/src/dto/openapi/generators/pagination.ts +111 -111
  107. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  108. package/src/dto/openapi/index.ts +12 -12
  109. package/src/dto/openapi/types.ts +101 -101
  110. package/src/dto/openapi/utilities.ts +95 -95
  111. package/src/dto/pagination-utils.ts +150 -150
  112. package/src/dto/transform.ts +197 -197
  113. package/src/index.ts +86 -86
  114. package/src/orm/als.ts +58 -58
  115. package/src/orm/domain-event-bus.ts +119 -119
  116. package/src/orm/entity-context.ts +86 -86
  117. package/src/orm/entity-materializer.ts +159 -159
  118. package/src/orm/entity-meta.ts +97 -97
  119. package/src/orm/entity-registry.ts +39 -39
  120. package/src/orm/entity-relation-cache.ts +39 -39
  121. package/src/orm/execute.ts +194 -194
  122. package/src/orm/execution-context.ts +18 -18
  123. package/src/orm/hydration-context.ts +26 -26
  124. package/src/orm/hydration.ts +112 -112
  125. package/src/orm/identity-map.ts +61 -61
  126. package/src/orm/interceptor-pipeline.ts +16 -16
  127. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  128. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  129. package/src/orm/lazy-batch/has-many.ts +69 -69
  130. package/src/orm/lazy-batch/has-one.ts +68 -68
  131. package/src/orm/lazy-batch/shared.ts +125 -125
  132. package/src/orm/orm-session.ts +620 -620
  133. package/src/orm/orm.ts +34 -34
  134. package/src/orm/pooled-executor-factory.ts +121 -121
  135. package/src/orm/query-logger.ts +40 -40
  136. package/src/orm/relation-change-processor.ts +420 -420
  137. package/src/orm/relations/belongs-to.ts +143 -143
  138. package/src/orm/relations/has-many.ts +204 -204
  139. package/src/orm/relations/has-one.ts +174 -174
  140. package/src/orm/relations/many-to-many.ts +288 -288
  141. package/src/orm/relations/morph-many.ts +156 -156
  142. package/src/orm/relations/morph-one.ts +151 -151
  143. package/src/orm/relations/morph-to.ts +162 -162
  144. package/src/orm/runtime-types.ts +92 -92
  145. package/src/orm/save-graph.ts +561 -561
  146. package/src/orm/transaction-runner.ts +24 -24
  147. package/src/orm/unit-of-work.ts +426 -426
  148. package/src/query/index.ts +69 -69
  149. package/src/query/target.ts +46 -46
  150. package/src/query-builder/column-selector.ts +81 -81
  151. package/src/query-builder/delete.ts +141 -141
  152. package/src/query-builder/insert-query-state.ts +149 -149
  153. package/src/query-builder/insert.ts +117 -117
  154. package/src/query-builder/query-ast-service.ts +287 -287
  155. package/src/query-builder/query-resolution.ts +78 -78
  156. package/src/query-builder/raw-column-parser.ts +38 -38
  157. package/src/query-builder/relation-filter-utils.ts +153 -153
  158. package/src/query-builder/relation-join-planner.ts +10 -10
  159. package/src/query-builder/relation-manager.ts +87 -87
  160. package/src/query-builder/relation-projection-helper.ts +102 -102
  161. package/src/query-builder/relation-types.ts +9 -9
  162. package/src/query-builder/relation-utils.ts +15 -15
  163. package/src/query-builder/select/cte-facet.ts +40 -40
  164. package/src/query-builder/select/from-facet.ts +80 -80
  165. package/src/query-builder/select/join-facet.ts +62 -62
  166. package/src/query-builder/select/predicate-facet.ts +104 -104
  167. package/src/query-builder/select/projection-facet.ts +70 -70
  168. package/src/query-builder/select/relation-facet.ts +81 -81
  169. package/src/query-builder/select/setop-facet.ts +36 -36
  170. package/src/query-builder/select-helpers.ts +1 -1
  171. package/src/query-builder/select-query-builder-deps.ts +130 -130
  172. package/src/query-builder/select-query-state.ts +207 -207
  173. package/src/query-builder/update.ts +151 -151
  174. package/src/schema/column-types.ts +322 -322
  175. package/src/schema/table-guards.ts +37 -37
  176. package/src/schema/table.ts +281 -281
  177. package/src/tree/index.ts +13 -13
  178. package/src/tree/nested-set-strategy.ts +545 -545
  179. package/src/tree/tree-decorator.ts +397 -397
  180. package/src/tree/tree-manager.ts +754 -754
  181. package/src/tree/tree-query.ts +427 -427
  182. package/src/tree/tree-types.ts +299 -299
@@ -1,61 +1,61 @@
1
1
  import {
2
- SelectQueryNode,
3
- InsertQueryNode,
4
- UpdateQueryNode,
5
- DeleteQueryNode,
6
- SetOperationKind,
7
- CommonTableExpressionNode,
8
- OrderingTerm
9
- } from '../ast/query.js';
10
- import {
2
+ SelectQueryNode,
3
+ InsertQueryNode,
4
+ UpdateQueryNode,
5
+ DeleteQueryNode,
6
+ SetOperationKind,
7
+ CommonTableExpressionNode,
8
+ OrderingTerm
9
+ } from '../ast/query.js';
10
+ import {
11
11
  ExpressionNode,
12
12
  BinaryExpressionNode,
13
13
  LogicalExpressionNode,
14
14
  NotExpressionNode,
15
15
  NullExpressionNode,
16
- InExpressionNode,
17
- ExistsExpressionNode,
18
- LiteralNode,
19
- ColumnNode,
20
- OperandNode,
21
- FunctionNode,
22
- JsonPathNode,
23
- ScalarSubqueryNode,
24
- CaseExpressionNode,
25
- CastExpressionNode,
26
- WindowFunctionNode,
27
- BetweenExpressionNode,
28
- ArithmeticExpressionNode,
29
- BitwiseExpressionNode,
30
- CollateExpressionNode,
31
- AliasRefNode,
32
- IsDistinctExpressionNode,
33
- isOperandNode
16
+ InExpressionNode,
17
+ ExistsExpressionNode,
18
+ LiteralNode,
19
+ ColumnNode,
20
+ OperandNode,
21
+ FunctionNode,
22
+ JsonPathNode,
23
+ ScalarSubqueryNode,
24
+ CaseExpressionNode,
25
+ CastExpressionNode,
26
+ WindowFunctionNode,
27
+ BetweenExpressionNode,
28
+ ArithmeticExpressionNode,
29
+ BitwiseExpressionNode,
30
+ CollateExpressionNode,
31
+ AliasRefNode,
32
+ IsDistinctExpressionNode,
33
+ isOperandNode
34
34
  } from '../ast/expression.js';
35
35
  import { ProcedureCallNode } from '../ast/procedure.js';
36
36
  import { DialectName } from '../sql/sql.js';
37
- import type { FunctionStrategy } from '../functions/types.js';
38
- import { StandardFunctionStrategy } from '../functions/standard-strategy.js';
39
- import type { TableFunctionStrategy } from '../functions/table-types.js';
40
- import { StandardTableFunctionStrategy } from '../functions/standard-table-strategy.js';
41
-
42
- /**
43
- * Context for SQL compilation with parameter management
44
- */
45
- export interface CompilerContext {
46
- /** Array of parameters */
47
- params: unknown[];
48
- /** Function to add a parameter and get its placeholder */
49
- addParameter(value: unknown): string;
50
- }
51
-
52
- /**
53
- * Result of SQL compilation
54
- */
37
+ import type { FunctionStrategy } from '../functions/types.js';
38
+ import { StandardFunctionStrategy } from '../functions/standard-strategy.js';
39
+ import type { TableFunctionStrategy } from '../functions/table-types.js';
40
+ import { StandardTableFunctionStrategy } from '../functions/standard-table-strategy.js';
41
+
42
+ /**
43
+ * Context for SQL compilation with parameter management
44
+ */
45
+ export interface CompilerContext {
46
+ /** Array of parameters */
47
+ params: unknown[];
48
+ /** Function to add a parameter and get its placeholder */
49
+ addParameter(value: unknown): string;
50
+ }
51
+
52
+ /**
53
+ * Result of SQL compilation
54
+ */
55
55
  export interface CompiledQuery {
56
- /** Generated SQL string */
57
- sql: string;
58
- /** Parameters for the query */
56
+ /** Generated SQL string */
57
+ sql: string;
58
+ /** Parameters for the query */
59
59
  params: unknown[];
60
60
  }
61
61
 
@@ -65,279 +65,279 @@ export interface CompiledProcedureCall extends CompiledQuery {
65
65
  names: string[];
66
66
  };
67
67
  }
68
-
69
- export interface SelectCompiler {
70
- compileSelect(ast: SelectQueryNode): CompiledQuery;
71
- }
72
-
73
- export interface InsertCompiler {
74
- compileInsert(ast: InsertQueryNode): CompiledQuery;
75
- }
76
-
77
- export interface UpdateCompiler {
78
- compileUpdate(ast: UpdateQueryNode): CompiledQuery;
79
- }
80
-
81
- export interface DeleteCompiler {
82
- compileDelete(ast: DeleteQueryNode): CompiledQuery;
83
- }
84
-
85
- /**
86
- * Abstract base class for SQL dialect implementations
87
- */
68
+
69
+ export interface SelectCompiler {
70
+ compileSelect(ast: SelectQueryNode): CompiledQuery;
71
+ }
72
+
73
+ export interface InsertCompiler {
74
+ compileInsert(ast: InsertQueryNode): CompiledQuery;
75
+ }
76
+
77
+ export interface UpdateCompiler {
78
+ compileUpdate(ast: UpdateQueryNode): CompiledQuery;
79
+ }
80
+
81
+ export interface DeleteCompiler {
82
+ compileDelete(ast: DeleteQueryNode): CompiledQuery;
83
+ }
84
+
85
+ /**
86
+ * Abstract base class for SQL dialect implementations
87
+ */
88
88
  export abstract class Dialect
89
89
  implements SelectCompiler, InsertCompiler, UpdateCompiler, DeleteCompiler {
90
- /** Dialect identifier used for function rendering and formatting */
91
- protected abstract readonly dialect: DialectName;
92
-
93
- /**
94
- * Compiles a SELECT query AST to SQL
95
- * @param ast - Query AST to compile
96
- * @returns Compiled query with SQL and parameters
97
- */
98
- compileSelect(ast: SelectQueryNode): CompiledQuery {
99
- const ctx = this.createCompilerContext();
100
- const normalized = this.normalizeSelectAst(ast);
101
- const rawSql = this.compileSelectAst(normalized, ctx).trim();
102
- const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
103
- return {
104
- sql,
105
- params: [...ctx.params]
106
- };
107
- }
108
-
109
- compileInsert(ast: InsertQueryNode): CompiledQuery {
110
- const ctx = this.createCompilerContext();
111
- const rawSql = this.compileInsertAst(ast, ctx).trim();
112
- const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
113
- return {
114
- sql,
115
- params: [...ctx.params]
116
- };
117
- }
118
-
119
- compileUpdate(ast: UpdateQueryNode): CompiledQuery {
120
- const ctx = this.createCompilerContext();
121
- const rawSql = this.compileUpdateAst(ast, ctx).trim();
122
- const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
123
- return {
124
- sql,
125
- params: [...ctx.params]
126
- };
127
- }
128
-
90
+ /** Dialect identifier used for function rendering and formatting */
91
+ protected abstract readonly dialect: DialectName;
92
+
93
+ /**
94
+ * Compiles a SELECT query AST to SQL
95
+ * @param ast - Query AST to compile
96
+ * @returns Compiled query with SQL and parameters
97
+ */
98
+ compileSelect(ast: SelectQueryNode): CompiledQuery {
99
+ const ctx = this.createCompilerContext();
100
+ const normalized = this.normalizeSelectAst(ast);
101
+ const rawSql = this.compileSelectAst(normalized, ctx).trim();
102
+ const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
103
+ return {
104
+ sql,
105
+ params: [...ctx.params]
106
+ };
107
+ }
108
+
109
+ compileInsert(ast: InsertQueryNode): CompiledQuery {
110
+ const ctx = this.createCompilerContext();
111
+ const rawSql = this.compileInsertAst(ast, ctx).trim();
112
+ const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
113
+ return {
114
+ sql,
115
+ params: [...ctx.params]
116
+ };
117
+ }
118
+
119
+ compileUpdate(ast: UpdateQueryNode): CompiledQuery {
120
+ const ctx = this.createCompilerContext();
121
+ const rawSql = this.compileUpdateAst(ast, ctx).trim();
122
+ const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
123
+ return {
124
+ sql,
125
+ params: [...ctx.params]
126
+ };
127
+ }
128
+
129
129
  compileDelete(ast: DeleteQueryNode): CompiledQuery {
130
- const ctx = this.createCompilerContext();
131
- const rawSql = this.compileDeleteAst(ast, ctx).trim();
132
- const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
133
- return {
134
- sql,
135
- params: [...ctx.params]
136
- };
130
+ const ctx = this.createCompilerContext();
131
+ const rawSql = this.compileDeleteAst(ast, ctx).trim();
132
+ const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
133
+ return {
134
+ sql,
135
+ params: [...ctx.params]
136
+ };
137
137
  }
138
138
 
139
139
  abstract compileProcedureCall(ast: ProcedureCallNode): CompiledProcedureCall;
140
-
141
- supportsDmlReturningClause(): boolean {
142
- return false;
143
- }
144
-
145
- /**
146
- * Compiles SELECT query AST to SQL (to be implemented by concrete dialects)
147
- * @param ast - Query AST
148
- * @param ctx - Compiler context
149
- * @returns SQL string
150
- */
151
- protected abstract compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
152
-
153
- protected abstract compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string;
154
- protected abstract compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string;
155
- protected abstract compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string;
156
-
157
- /**
158
- * Quotes an SQL identifier (to be implemented by concrete dialects)
159
- * @param id - Identifier to quote
160
- * @returns Quoted identifier
161
- */
162
- abstract quoteIdentifier(id: string): string;
163
-
164
- /**
165
- * Compiles a WHERE clause
166
- * @param where - WHERE expression
167
- * @param ctx - Compiler context
168
- * @returns SQL WHERE clause or empty string
169
- */
170
- protected compileWhere(where: ExpressionNode | undefined, ctx: CompilerContext): string {
171
- if (!where) return '';
172
- return ` WHERE ${this.compileExpression(where, ctx)}`;
173
- }
174
-
175
- protected compileReturning(
176
- returning: ColumnNode[] | undefined,
177
- _ctx: CompilerContext
178
- ): string {
179
- void _ctx;
180
- if (!returning || returning.length === 0) return '';
181
- throw new Error('RETURNING is not supported by this dialect.');
182
- }
183
-
184
- /**
185
- * Generates subquery for EXISTS expressions
186
- * Rule: Always forces SELECT 1, ignoring column list
187
- * Maintains FROM, JOINs, WHERE, GROUP BY, ORDER BY, LIMIT/OFFSET
188
- * Does not add ';' at the end
189
- * @param ast - Query AST
190
- * @param ctx - Compiler context
191
- * @returns SQL for EXISTS subquery
192
- */
193
- protected compileSelectForExists(ast: SelectQueryNode, ctx: CompilerContext): string {
194
- const normalized = this.normalizeSelectAst(ast);
195
- const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, '');
196
-
197
- // When the subquery is a set operation, wrap it as a derived table to keep valid syntax.
198
- if (normalized.setOps && normalized.setOps.length > 0) {
199
- return `SELECT 1 FROM (${full}) AS _exists`;
200
- }
201
-
202
- const upper = full.toUpperCase();
203
- const fromIndex = upper.indexOf(' FROM ');
204
- if (fromIndex === -1) {
205
- return full;
206
- }
207
-
208
- const tail = full.slice(fromIndex);
209
- return `SELECT 1${tail}`;
210
- }
211
-
212
- /**
213
- * Creates a new compiler context
214
- * @returns Compiler context with parameter management
215
- */
216
- protected createCompilerContext(): CompilerContext {
217
- const params: unknown[] = [];
218
- let counter = 0;
219
- return {
220
- params,
221
- addParameter: (value: unknown) => {
222
- counter += 1;
223
- params.push(value);
224
- return this.formatPlaceholder(counter);
225
- }
226
- };
227
- }
228
-
229
- /**
230
- * Formats a parameter placeholder
231
- * @param index - Parameter index
232
- * @returns Formatted placeholder string
233
- */
234
- protected formatPlaceholder(_index: number): string {
235
- void _index;
236
- return '?';
237
- }
238
-
239
- /**
240
- * Whether the current dialect supports a given set operation.
241
- * Override in concrete dialects to restrict support.
242
- */
243
- protected supportsSetOperation(_kind: SetOperationKind): boolean {
244
- void _kind;
245
- return true;
246
- }
247
-
248
- /**
249
- * Validates set-operation semantics:
250
- * - Ensures the dialect supports requested operators.
251
- * - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
252
- * @param ast - Query to validate
253
- * @param isOutermost - Whether this node is the outermost compound query
254
- */
255
- protected validateSetOperations(ast: SelectQueryNode, isOutermost = true): void {
256
- const hasSetOps = !!(ast.setOps && ast.setOps.length);
257
- if (!isOutermost && (ast.orderBy || ast.limit !== undefined || ast.offset !== undefined)) {
258
- throw new Error('ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.');
259
- }
260
-
261
- if (hasSetOps) {
262
- for (const op of ast.setOps!) {
263
- if (!this.supportsSetOperation(op.operator)) {
264
- throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
265
- }
266
- this.validateSetOperations(op.query, false);
267
- }
268
- }
269
- }
270
-
271
- /**
272
- * Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
273
- * @param ast - Query AST
274
- * @returns Normalized AST without inner CTEs and a list of hoisted CTEs
275
- */
276
- private hoistCtes(ast: SelectQueryNode): { normalized: SelectQueryNode; hoistedCtes: CommonTableExpressionNode[] } {
277
- let hoisted: CommonTableExpressionNode[] = [];
278
-
279
- const normalizedSetOps = ast.setOps?.map(op => {
280
- const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
281
- const childCtes = child.ctes ?? [];
282
- if (childCtes.length) {
283
- hoisted = hoisted.concat(childCtes);
284
- }
285
- hoisted = hoisted.concat(childHoisted);
286
- const queryWithoutCtes = childCtes.length ? { ...child, ctes: undefined } : child;
287
- return { ...op, query: queryWithoutCtes };
288
- });
289
-
290
- const normalized: SelectQueryNode = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
291
- return { normalized, hoistedCtes: hoisted };
292
- }
293
-
294
- /**
295
- * Normalizes a SELECT AST before compilation (validation + CTE hoisting).
296
- * @param ast - Query AST
297
- * @returns Normalized query AST
298
- */
299
- protected normalizeSelectAst(ast: SelectQueryNode): SelectQueryNode {
300
- this.validateSetOperations(ast, true);
301
- const { normalized, hoistedCtes } = this.hoistCtes(ast);
302
- const combinedCtes = [...(normalized.ctes ?? []), ...hoistedCtes];
303
- return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
304
- }
305
-
306
- private readonly expressionCompilers: Map<string, (node: ExpressionNode, ctx: CompilerContext) => string>;
307
- private readonly operandCompilers: Map<string, (node: OperandNode, ctx: CompilerContext) => string>;
308
- protected readonly functionStrategy: FunctionStrategy;
309
- protected readonly tableFunctionStrategy: TableFunctionStrategy;
310
-
311
- protected constructor(functionStrategy?: FunctionStrategy, tableFunctionStrategy?: TableFunctionStrategy) {
312
- this.expressionCompilers = new Map();
313
- this.operandCompilers = new Map();
314
- this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
315
- this.tableFunctionStrategy = tableFunctionStrategy || new StandardTableFunctionStrategy();
316
- this.registerDefaultOperandCompilers();
317
- this.registerDefaultExpressionCompilers();
318
- }
319
-
320
- /**
321
- * Creates a new Dialect instance (for testing purposes)
322
- * @param functionStrategy - Optional function strategy
323
- * @returns New Dialect instance
324
- */
325
- static create(functionStrategy?: FunctionStrategy, tableFunctionStrategy?: TableFunctionStrategy): Dialect {
326
- // Create a minimal concrete implementation for testing
327
- class TestDialect extends Dialect {
328
- protected readonly dialect: DialectName = 'sqlite';
329
- quoteIdentifier(id: string): string {
330
- return `"${id}"`;
331
- }
332
- protected compileSelectAst(): never {
333
- throw new Error('Not implemented');
334
- }
335
- protected compileInsertAst(): never {
336
- throw new Error('Not implemented');
337
- }
338
- protected compileUpdateAst(): never {
339
- throw new Error('Not implemented');
340
- }
140
+
141
+ supportsDmlReturningClause(): boolean {
142
+ return false;
143
+ }
144
+
145
+ /**
146
+ * Compiles SELECT query AST to SQL (to be implemented by concrete dialects)
147
+ * @param ast - Query AST
148
+ * @param ctx - Compiler context
149
+ * @returns SQL string
150
+ */
151
+ protected abstract compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
152
+
153
+ protected abstract compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string;
154
+ protected abstract compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string;
155
+ protected abstract compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string;
156
+
157
+ /**
158
+ * Quotes an SQL identifier (to be implemented by concrete dialects)
159
+ * @param id - Identifier to quote
160
+ * @returns Quoted identifier
161
+ */
162
+ abstract quoteIdentifier(id: string): string;
163
+
164
+ /**
165
+ * Compiles a WHERE clause
166
+ * @param where - WHERE expression
167
+ * @param ctx - Compiler context
168
+ * @returns SQL WHERE clause or empty string
169
+ */
170
+ protected compileWhere(where: ExpressionNode | undefined, ctx: CompilerContext): string {
171
+ if (!where) return '';
172
+ return ` WHERE ${this.compileExpression(where, ctx)}`;
173
+ }
174
+
175
+ protected compileReturning(
176
+ returning: ColumnNode[] | undefined,
177
+ _ctx: CompilerContext
178
+ ): string {
179
+ void _ctx;
180
+ if (!returning || returning.length === 0) return '';
181
+ throw new Error('RETURNING is not supported by this dialect.');
182
+ }
183
+
184
+ /**
185
+ * Generates subquery for EXISTS expressions
186
+ * Rule: Always forces SELECT 1, ignoring column list
187
+ * Maintains FROM, JOINs, WHERE, GROUP BY, ORDER BY, LIMIT/OFFSET
188
+ * Does not add ';' at the end
189
+ * @param ast - Query AST
190
+ * @param ctx - Compiler context
191
+ * @returns SQL for EXISTS subquery
192
+ */
193
+ protected compileSelectForExists(ast: SelectQueryNode, ctx: CompilerContext): string {
194
+ const normalized = this.normalizeSelectAst(ast);
195
+ const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, '');
196
+
197
+ // When the subquery is a set operation, wrap it as a derived table to keep valid syntax.
198
+ if (normalized.setOps && normalized.setOps.length > 0) {
199
+ return `SELECT 1 FROM (${full}) AS _exists`;
200
+ }
201
+
202
+ const upper = full.toUpperCase();
203
+ const fromIndex = upper.indexOf(' FROM ');
204
+ if (fromIndex === -1) {
205
+ return full;
206
+ }
207
+
208
+ const tail = full.slice(fromIndex);
209
+ return `SELECT 1${tail}`;
210
+ }
211
+
212
+ /**
213
+ * Creates a new compiler context
214
+ * @returns Compiler context with parameter management
215
+ */
216
+ protected createCompilerContext(): CompilerContext {
217
+ const params: unknown[] = [];
218
+ let counter = 0;
219
+ return {
220
+ params,
221
+ addParameter: (value: unknown) => {
222
+ counter += 1;
223
+ params.push(value);
224
+ return this.formatPlaceholder(counter);
225
+ }
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Formats a parameter placeholder
231
+ * @param index - Parameter index
232
+ * @returns Formatted placeholder string
233
+ */
234
+ protected formatPlaceholder(_index: number): string {
235
+ void _index;
236
+ return '?';
237
+ }
238
+
239
+ /**
240
+ * Whether the current dialect supports a given set operation.
241
+ * Override in concrete dialects to restrict support.
242
+ */
243
+ protected supportsSetOperation(_kind: SetOperationKind): boolean {
244
+ void _kind;
245
+ return true;
246
+ }
247
+
248
+ /**
249
+ * Validates set-operation semantics:
250
+ * - Ensures the dialect supports requested operators.
251
+ * - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
252
+ * @param ast - Query to validate
253
+ * @param isOutermost - Whether this node is the outermost compound query
254
+ */
255
+ protected validateSetOperations(ast: SelectQueryNode, isOutermost = true): void {
256
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
257
+ if (!isOutermost && (ast.orderBy || ast.limit !== undefined || ast.offset !== undefined)) {
258
+ throw new Error('ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.');
259
+ }
260
+
261
+ if (hasSetOps) {
262
+ for (const op of ast.setOps!) {
263
+ if (!this.supportsSetOperation(op.operator)) {
264
+ throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
265
+ }
266
+ this.validateSetOperations(op.query, false);
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
273
+ * @param ast - Query AST
274
+ * @returns Normalized AST without inner CTEs and a list of hoisted CTEs
275
+ */
276
+ private hoistCtes(ast: SelectQueryNode): { normalized: SelectQueryNode; hoistedCtes: CommonTableExpressionNode[] } {
277
+ let hoisted: CommonTableExpressionNode[] = [];
278
+
279
+ const normalizedSetOps = ast.setOps?.map(op => {
280
+ const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
281
+ const childCtes = child.ctes ?? [];
282
+ if (childCtes.length) {
283
+ hoisted = hoisted.concat(childCtes);
284
+ }
285
+ hoisted = hoisted.concat(childHoisted);
286
+ const queryWithoutCtes = childCtes.length ? { ...child, ctes: undefined } : child;
287
+ return { ...op, query: queryWithoutCtes };
288
+ });
289
+
290
+ const normalized: SelectQueryNode = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
291
+ return { normalized, hoistedCtes: hoisted };
292
+ }
293
+
294
+ /**
295
+ * Normalizes a SELECT AST before compilation (validation + CTE hoisting).
296
+ * @param ast - Query AST
297
+ * @returns Normalized query AST
298
+ */
299
+ protected normalizeSelectAst(ast: SelectQueryNode): SelectQueryNode {
300
+ this.validateSetOperations(ast, true);
301
+ const { normalized, hoistedCtes } = this.hoistCtes(ast);
302
+ const combinedCtes = [...(normalized.ctes ?? []), ...hoistedCtes];
303
+ return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
304
+ }
305
+
306
+ private readonly expressionCompilers: Map<string, (node: ExpressionNode, ctx: CompilerContext) => string>;
307
+ private readonly operandCompilers: Map<string, (node: OperandNode, ctx: CompilerContext) => string>;
308
+ protected readonly functionStrategy: FunctionStrategy;
309
+ protected readonly tableFunctionStrategy: TableFunctionStrategy;
310
+
311
+ protected constructor(functionStrategy?: FunctionStrategy, tableFunctionStrategy?: TableFunctionStrategy) {
312
+ this.expressionCompilers = new Map();
313
+ this.operandCompilers = new Map();
314
+ this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
315
+ this.tableFunctionStrategy = tableFunctionStrategy || new StandardTableFunctionStrategy();
316
+ this.registerDefaultOperandCompilers();
317
+ this.registerDefaultExpressionCompilers();
318
+ }
319
+
320
+ /**
321
+ * Creates a new Dialect instance (for testing purposes)
322
+ * @param functionStrategy - Optional function strategy
323
+ * @returns New Dialect instance
324
+ */
325
+ static create(functionStrategy?: FunctionStrategy, tableFunctionStrategy?: TableFunctionStrategy): Dialect {
326
+ // Create a minimal concrete implementation for testing
327
+ class TestDialect extends Dialect {
328
+ protected readonly dialect: DialectName = 'sqlite';
329
+ quoteIdentifier(id: string): string {
330
+ return `"${id}"`;
331
+ }
332
+ protected compileSelectAst(): never {
333
+ throw new Error('Not implemented');
334
+ }
335
+ protected compileInsertAst(): never {
336
+ throw new Error('Not implemented');
337
+ }
338
+ protected compileUpdateAst(): never {
339
+ throw new Error('Not implemented');
340
+ }
341
341
  protected compileDeleteAst(): never {
342
342
  throw new Error('Not implemented');
343
343
  }
@@ -345,79 +345,79 @@ export abstract class Dialect
345
345
  throw new Error('Not implemented');
346
346
  }
347
347
  }
348
- return new TestDialect(functionStrategy, tableFunctionStrategy);
349
- }
350
-
351
- /**
352
- * Registers an expression compiler for a specific node type
353
- * @param type - Expression node type
354
- * @param compiler - Compiler function
355
- */
356
- protected registerExpressionCompiler<T extends ExpressionNode>(type: T['type'], compiler: (node: T, ctx: CompilerContext) => string): void {
357
- this.expressionCompilers.set(type, compiler as (node: ExpressionNode, ctx: CompilerContext) => string);
358
- }
359
-
360
- /**
361
- * Registers an operand compiler for a specific node type
362
- * @param type - Operand node type
363
- * @param compiler - Compiler function
364
- */
365
- protected registerOperandCompiler<T extends OperandNode>(type: T['type'], compiler: (node: T, ctx: CompilerContext) => string): void {
366
- this.operandCompilers.set(type, compiler as (node: OperandNode, ctx: CompilerContext) => string);
367
- }
368
-
369
- /**
370
- * Compiles an expression node
371
- * @param node - Expression node to compile
372
- * @param ctx - Compiler context
373
- * @returns Compiled SQL expression
374
- */
375
- protected compileExpression(node: ExpressionNode, ctx: CompilerContext): string {
376
- const compiler = this.expressionCompilers.get(node.type);
377
- if (!compiler) {
378
- throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
379
- }
380
- return compiler(node, ctx);
381
- }
382
-
383
- /**
384
- * Compiles an operand node
385
- * @param node - Operand node to compile
386
- * @param ctx - Compiler context
387
- * @returns Compiled SQL operand
388
- */
389
- protected compileOperand(node: OperandNode, ctx: CompilerContext): string {
390
- const compiler = this.operandCompilers.get(node.type);
391
- if (!compiler) {
392
- throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
393
- }
394
- return compiler(node, ctx);
395
- }
396
-
397
- /**
398
- * Compiles an ordering term (operand, expression, or alias reference).
399
- */
400
- protected compileOrderingTerm(term: OrderingTerm, ctx: CompilerContext): string {
401
- if (isOperandNode(term)) {
402
- return this.compileOperand(term, ctx);
403
- }
404
- // At this point, term must be an ExpressionNode
405
- const expr = this.compileExpression(term as ExpressionNode, ctx);
406
- return `(${expr})`;
407
- }
408
-
409
- private registerDefaultExpressionCompilers(): void {
410
- this.registerExpressionCompiler('BinaryExpression', (binary: BinaryExpressionNode, ctx) => {
411
- const left = this.compileOperand(binary.left, ctx);
412
- const right = this.compileOperand(binary.right, ctx);
413
- const base = `${left} ${binary.operator} ${right}`;
414
- if (binary.escape) {
415
- const escapeOperand = this.compileOperand(binary.escape, ctx);
416
- return `${base} ESCAPE ${escapeOperand}`;
417
- }
418
- return base;
419
- });
420
-
348
+ return new TestDialect(functionStrategy, tableFunctionStrategy);
349
+ }
350
+
351
+ /**
352
+ * Registers an expression compiler for a specific node type
353
+ * @param type - Expression node type
354
+ * @param compiler - Compiler function
355
+ */
356
+ protected registerExpressionCompiler<T extends ExpressionNode>(type: T['type'], compiler: (node: T, ctx: CompilerContext) => string): void {
357
+ this.expressionCompilers.set(type, compiler as (node: ExpressionNode, ctx: CompilerContext) => string);
358
+ }
359
+
360
+ /**
361
+ * Registers an operand compiler for a specific node type
362
+ * @param type - Operand node type
363
+ * @param compiler - Compiler function
364
+ */
365
+ protected registerOperandCompiler<T extends OperandNode>(type: T['type'], compiler: (node: T, ctx: CompilerContext) => string): void {
366
+ this.operandCompilers.set(type, compiler as (node: OperandNode, ctx: CompilerContext) => string);
367
+ }
368
+
369
+ /**
370
+ * Compiles an expression node
371
+ * @param node - Expression node to compile
372
+ * @param ctx - Compiler context
373
+ * @returns Compiled SQL expression
374
+ */
375
+ protected compileExpression(node: ExpressionNode, ctx: CompilerContext): string {
376
+ const compiler = this.expressionCompilers.get(node.type);
377
+ if (!compiler) {
378
+ throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
379
+ }
380
+ return compiler(node, ctx);
381
+ }
382
+
383
+ /**
384
+ * Compiles an operand node
385
+ * @param node - Operand node to compile
386
+ * @param ctx - Compiler context
387
+ * @returns Compiled SQL operand
388
+ */
389
+ protected compileOperand(node: OperandNode, ctx: CompilerContext): string {
390
+ const compiler = this.operandCompilers.get(node.type);
391
+ if (!compiler) {
392
+ throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
393
+ }
394
+ return compiler(node, ctx);
395
+ }
396
+
397
+ /**
398
+ * Compiles an ordering term (operand, expression, or alias reference).
399
+ */
400
+ protected compileOrderingTerm(term: OrderingTerm, ctx: CompilerContext): string {
401
+ if (isOperandNode(term)) {
402
+ return this.compileOperand(term, ctx);
403
+ }
404
+ // At this point, term must be an ExpressionNode
405
+ const expr = this.compileExpression(term as ExpressionNode, ctx);
406
+ return `(${expr})`;
407
+ }
408
+
409
+ private registerDefaultExpressionCompilers(): void {
410
+ this.registerExpressionCompiler('BinaryExpression', (binary: BinaryExpressionNode, ctx) => {
411
+ const left = this.compileOperand(binary.left, ctx);
412
+ const right = this.compileOperand(binary.right, ctx);
413
+ const base = `${left} ${binary.operator} ${right}`;
414
+ if (binary.escape) {
415
+ const escapeOperand = this.compileOperand(binary.escape, ctx);
416
+ return `${base} ESCAPE ${escapeOperand}`;
417
+ }
418
+ return base;
419
+ });
420
+
421
421
  this.registerExpressionCompiler('LogicalExpression', (logical: LogicalExpressionNode, ctx) => {
422
422
  if (logical.operands.length === 0) return '';
423
423
  const parts = logical.operands.map(op => {
@@ -436,156 +436,156 @@ export abstract class Dialect
436
436
  const left = this.compileOperand(nullExpr.left, ctx);
437
437
  return `${left} ${nullExpr.operator}`;
438
438
  });
439
-
440
- this.registerExpressionCompiler('InExpression', (inExpr: InExpressionNode, ctx) => {
441
- const left = this.compileOperand(inExpr.left, ctx);
442
- if (Array.isArray(inExpr.right)) {
443
- const values = inExpr.right.map(v => this.compileOperand(v, ctx)).join(', ');
444
- return `${left} ${inExpr.operator} (${values})`;
445
- }
446
- const subquerySql = this.compileSelectAst(inExpr.right.query, ctx).trim().replace(/;$/, '');
447
- return `${left} ${inExpr.operator} (${subquerySql})`;
448
- });
449
-
450
- this.registerExpressionCompiler('ExistsExpression', (existsExpr: ExistsExpressionNode, ctx) => {
451
- const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
452
- return `${existsExpr.operator} (${subquerySql})`;
453
- });
454
-
455
- this.registerExpressionCompiler('BetweenExpression', (betweenExpr: BetweenExpressionNode, ctx) => {
456
- const left = this.compileOperand(betweenExpr.left, ctx);
457
- const lower = this.compileOperand(betweenExpr.lower, ctx);
458
- const upper = this.compileOperand(betweenExpr.upper, ctx);
459
- return `${left} ${betweenExpr.operator} ${lower} AND ${upper}`;
460
- });
461
-
462
- this.registerExpressionCompiler('ArithmeticExpression', (arith: ArithmeticExpressionNode, ctx) => {
463
- const left = this.compileOperand(arith.left, ctx);
464
- const right = this.compileOperand(arith.right, ctx);
465
- return `${left} ${arith.operator} ${right}`;
466
- });
467
-
468
- this.registerExpressionCompiler('BitwiseExpression', (bitwise: BitwiseExpressionNode, ctx) => {
469
- const left = this.compileOperand(bitwise.left, ctx);
470
- const right = this.compileOperand(bitwise.right, ctx);
471
- return `${left} ${bitwise.operator} ${right}`;
472
- });
473
-
474
- this.registerExpressionCompiler('IsDistinctExpression', (node: IsDistinctExpressionNode, ctx) => {
475
- const left = this.compileOperand(node.left, ctx);
476
- const right = this.compileOperand(node.right, ctx);
477
- return `${left} ${node.operator} ${right}`;
478
- });
479
- }
480
-
481
- private registerDefaultOperandCompilers(): void {
482
- this.registerOperandCompiler('Literal', (literal: LiteralNode, ctx) => ctx.addParameter(literal.value));
483
-
484
- this.registerOperandCompiler('AliasRef', (alias: AliasRefNode, _ctx) => {
485
- void _ctx;
486
- return this.quoteIdentifier(alias.name);
487
- });
488
-
489
- this.registerOperandCompiler('Column', (column: ColumnNode, _ctx) => {
490
- void _ctx;
491
- return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
492
- });
493
- this.registerOperandCompiler('Function', (fnNode: FunctionNode, ctx) =>
494
- this.compileFunctionOperand(fnNode, ctx)
495
- );
496
- this.registerOperandCompiler('JsonPath', (path: JsonPathNode, _ctx) => {
497
- void _ctx;
498
- return this.compileJsonPath(path);
499
- });
500
-
501
- this.registerOperandCompiler('ScalarSubquery', (node: ScalarSubqueryNode, ctx) => {
502
- const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, '');
503
- return `(${sql})`;
504
- });
505
-
506
- this.registerOperandCompiler('CaseExpression', (node: CaseExpressionNode, ctx) => {
507
- const parts = ['CASE'];
508
- for (const { when, then } of node.conditions) {
509
- parts.push(`WHEN ${this.compileExpression(when, ctx)} THEN ${this.compileOperand(then, ctx)}`);
510
- }
511
- if (node.else) {
512
- parts.push(`ELSE ${this.compileOperand(node.else, ctx)}`);
513
- }
514
- parts.push('END');
515
- return parts.join(' ');
516
- });
517
-
518
- this.registerOperandCompiler('Cast', (node: CastExpressionNode, ctx) => {
519
- const value = this.compileOperand(node.expression, ctx);
520
- return `CAST(${value} AS ${node.castType})`;
521
- });
522
-
523
- this.registerOperandCompiler('WindowFunction', (node: WindowFunctionNode, ctx) => {
524
- let result = `${node.name}(`;
525
- if (node.args.length > 0) {
526
- result += node.args.map(arg => this.compileOperand(arg, ctx)).join(', ');
527
- }
528
- result += ') OVER (';
529
-
530
- const parts: string[] = [];
531
-
532
- if (node.partitionBy && node.partitionBy.length > 0) {
533
- const partitionClause = 'PARTITION BY ' + node.partitionBy.map(col =>
534
- `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`
535
- ).join(', ');
536
- parts.push(partitionClause);
537
- }
538
-
539
- if (node.orderBy && node.orderBy.length > 0) {
540
- const orderClause = 'ORDER BY ' + node.orderBy.map(o => {
541
- const term = this.compileOrderingTerm(o.term, ctx);
542
- const collation = o.collation ? ` COLLATE ${o.collation}` : '';
543
- const nulls = o.nulls ? ` NULLS ${o.nulls}` : '';
544
- return `${term} ${o.direction}${collation}${nulls}`;
545
- }).join(', ');
546
- parts.push(orderClause);
547
- }
548
-
549
- result += parts.join(' ');
550
- result += ')';
551
-
552
- return result;
553
- });
554
- this.registerOperandCompiler('ArithmeticExpression', (node: ArithmeticExpressionNode, ctx) => {
555
- const left = this.compileOperand(node.left, ctx);
556
- const right = this.compileOperand(node.right, ctx);
557
- return `(${left} ${node.operator} ${right})`;
558
- });
559
- this.registerOperandCompiler('BitwiseExpression', (node: BitwiseExpressionNode, ctx) => {
560
- const left = this.compileOperand(node.left, ctx);
561
- const right = this.compileOperand(node.right, ctx);
562
- return `(${left} ${node.operator} ${right})`;
563
- });
564
- this.registerOperandCompiler('Collate', (node: CollateExpressionNode, ctx) => {
565
- const expr = this.compileOperand(node.expression, ctx);
566
- return `${expr} COLLATE ${node.collation}`;
567
- });
568
- }
569
-
570
- // Default fallback, should be overridden by dialects if supported
571
- protected compileJsonPath(_node: JsonPathNode): string {
572
- void _node;
573
- throw new Error("JSON Path not supported by this dialect");
574
- }
575
-
576
- /**
577
- * Compiles a function operand, using the dialect's function strategy.
578
- */
579
- protected compileFunctionOperand(fnNode: FunctionNode, ctx: CompilerContext): string {
580
- const compiledArgs = fnNode.args.map(arg => this.compileOperand(arg, ctx));
581
- const renderer = this.functionStrategy.getRenderer(fnNode.name);
582
- if (renderer) {
583
- return renderer({
584
- node: fnNode,
585
- compiledArgs,
586
- compileOperand: operand => this.compileOperand(operand, ctx)
587
- });
588
- }
589
- return `${fnNode.name}(${compiledArgs.join(', ')})`;
590
- }
591
- }
439
+
440
+ this.registerExpressionCompiler('InExpression', (inExpr: InExpressionNode, ctx) => {
441
+ const left = this.compileOperand(inExpr.left, ctx);
442
+ if (Array.isArray(inExpr.right)) {
443
+ const values = inExpr.right.map(v => this.compileOperand(v, ctx)).join(', ');
444
+ return `${left} ${inExpr.operator} (${values})`;
445
+ }
446
+ const subquerySql = this.compileSelectAst(inExpr.right.query, ctx).trim().replace(/;$/, '');
447
+ return `${left} ${inExpr.operator} (${subquerySql})`;
448
+ });
449
+
450
+ this.registerExpressionCompiler('ExistsExpression', (existsExpr: ExistsExpressionNode, ctx) => {
451
+ const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
452
+ return `${existsExpr.operator} (${subquerySql})`;
453
+ });
454
+
455
+ this.registerExpressionCompiler('BetweenExpression', (betweenExpr: BetweenExpressionNode, ctx) => {
456
+ const left = this.compileOperand(betweenExpr.left, ctx);
457
+ const lower = this.compileOperand(betweenExpr.lower, ctx);
458
+ const upper = this.compileOperand(betweenExpr.upper, ctx);
459
+ return `${left} ${betweenExpr.operator} ${lower} AND ${upper}`;
460
+ });
461
+
462
+ this.registerExpressionCompiler('ArithmeticExpression', (arith: ArithmeticExpressionNode, ctx) => {
463
+ const left = this.compileOperand(arith.left, ctx);
464
+ const right = this.compileOperand(arith.right, ctx);
465
+ return `${left} ${arith.operator} ${right}`;
466
+ });
467
+
468
+ this.registerExpressionCompiler('BitwiseExpression', (bitwise: BitwiseExpressionNode, ctx) => {
469
+ const left = this.compileOperand(bitwise.left, ctx);
470
+ const right = this.compileOperand(bitwise.right, ctx);
471
+ return `${left} ${bitwise.operator} ${right}`;
472
+ });
473
+
474
+ this.registerExpressionCompiler('IsDistinctExpression', (node: IsDistinctExpressionNode, ctx) => {
475
+ const left = this.compileOperand(node.left, ctx);
476
+ const right = this.compileOperand(node.right, ctx);
477
+ return `${left} ${node.operator} ${right}`;
478
+ });
479
+ }
480
+
481
+ private registerDefaultOperandCompilers(): void {
482
+ this.registerOperandCompiler('Literal', (literal: LiteralNode, ctx) => ctx.addParameter(literal.value));
483
+
484
+ this.registerOperandCompiler('AliasRef', (alias: AliasRefNode, _ctx) => {
485
+ void _ctx;
486
+ return this.quoteIdentifier(alias.name);
487
+ });
488
+
489
+ this.registerOperandCompiler('Column', (column: ColumnNode, _ctx) => {
490
+ void _ctx;
491
+ return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
492
+ });
493
+ this.registerOperandCompiler('Function', (fnNode: FunctionNode, ctx) =>
494
+ this.compileFunctionOperand(fnNode, ctx)
495
+ );
496
+ this.registerOperandCompiler('JsonPath', (path: JsonPathNode, _ctx) => {
497
+ void _ctx;
498
+ return this.compileJsonPath(path);
499
+ });
500
+
501
+ this.registerOperandCompiler('ScalarSubquery', (node: ScalarSubqueryNode, ctx) => {
502
+ const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, '');
503
+ return `(${sql})`;
504
+ });
505
+
506
+ this.registerOperandCompiler('CaseExpression', (node: CaseExpressionNode, ctx) => {
507
+ const parts = ['CASE'];
508
+ for (const { when, then } of node.conditions) {
509
+ parts.push(`WHEN ${this.compileExpression(when, ctx)} THEN ${this.compileOperand(then, ctx)}`);
510
+ }
511
+ if (node.else) {
512
+ parts.push(`ELSE ${this.compileOperand(node.else, ctx)}`);
513
+ }
514
+ parts.push('END');
515
+ return parts.join(' ');
516
+ });
517
+
518
+ this.registerOperandCompiler('Cast', (node: CastExpressionNode, ctx) => {
519
+ const value = this.compileOperand(node.expression, ctx);
520
+ return `CAST(${value} AS ${node.castType})`;
521
+ });
522
+
523
+ this.registerOperandCompiler('WindowFunction', (node: WindowFunctionNode, ctx) => {
524
+ let result = `${node.name}(`;
525
+ if (node.args.length > 0) {
526
+ result += node.args.map(arg => this.compileOperand(arg, ctx)).join(', ');
527
+ }
528
+ result += ') OVER (';
529
+
530
+ const parts: string[] = [];
531
+
532
+ if (node.partitionBy && node.partitionBy.length > 0) {
533
+ const partitionClause = 'PARTITION BY ' + node.partitionBy.map(col =>
534
+ `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`
535
+ ).join(', ');
536
+ parts.push(partitionClause);
537
+ }
538
+
539
+ if (node.orderBy && node.orderBy.length > 0) {
540
+ const orderClause = 'ORDER BY ' + node.orderBy.map(o => {
541
+ const term = this.compileOrderingTerm(o.term, ctx);
542
+ const collation = o.collation ? ` COLLATE ${o.collation}` : '';
543
+ const nulls = o.nulls ? ` NULLS ${o.nulls}` : '';
544
+ return `${term} ${o.direction}${collation}${nulls}`;
545
+ }).join(', ');
546
+ parts.push(orderClause);
547
+ }
548
+
549
+ result += parts.join(' ');
550
+ result += ')';
551
+
552
+ return result;
553
+ });
554
+ this.registerOperandCompiler('ArithmeticExpression', (node: ArithmeticExpressionNode, ctx) => {
555
+ const left = this.compileOperand(node.left, ctx);
556
+ const right = this.compileOperand(node.right, ctx);
557
+ return `(${left} ${node.operator} ${right})`;
558
+ });
559
+ this.registerOperandCompiler('BitwiseExpression', (node: BitwiseExpressionNode, ctx) => {
560
+ const left = this.compileOperand(node.left, ctx);
561
+ const right = this.compileOperand(node.right, ctx);
562
+ return `(${left} ${node.operator} ${right})`;
563
+ });
564
+ this.registerOperandCompiler('Collate', (node: CollateExpressionNode, ctx) => {
565
+ const expr = this.compileOperand(node.expression, ctx);
566
+ return `${expr} COLLATE ${node.collation}`;
567
+ });
568
+ }
569
+
570
+ // Default fallback, should be overridden by dialects if supported
571
+ protected compileJsonPath(_node: JsonPathNode): string {
572
+ void _node;
573
+ throw new Error("JSON Path not supported by this dialect");
574
+ }
575
+
576
+ /**
577
+ * Compiles a function operand, using the dialect's function strategy.
578
+ */
579
+ protected compileFunctionOperand(fnNode: FunctionNode, ctx: CompilerContext): string {
580
+ const compiledArgs = fnNode.args.map(arg => this.compileOperand(arg, ctx));
581
+ const renderer = this.functionStrategy.getRenderer(fnNode.name);
582
+ if (renderer) {
583
+ return renderer({
584
+ node: fnNode,
585
+ compiledArgs,
586
+ compileOperand: operand => this.compileOperand(operand, ctx)
587
+ });
588
+ }
589
+ return `${fnNode.name}(${compiledArgs.join(', ')})`;
590
+ }
591
+ }