metal-orm 1.0.39 → 1.0.41

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 (52) hide show
  1. package/dist/index.cjs +1466 -189
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +723 -51
  4. package/dist/index.d.ts +723 -51
  5. package/dist/index.js +1457 -189
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/codegen/typescript.ts +66 -5
  9. package/src/core/ast/aggregate-functions.ts +15 -15
  10. package/src/core/ast/expression-builders.ts +378 -316
  11. package/src/core/ast/expression-nodes.ts +210 -186
  12. package/src/core/ast/expression-visitor.ts +40 -30
  13. package/src/core/ast/query.ts +164 -132
  14. package/src/core/ast/window-functions.ts +86 -86
  15. package/src/core/dialect/abstract.ts +509 -479
  16. package/src/core/dialect/base/groupby-compiler.ts +6 -6
  17. package/src/core/dialect/base/join-compiler.ts +9 -12
  18. package/src/core/dialect/base/orderby-compiler.ts +20 -6
  19. package/src/core/dialect/base/sql-dialect.ts +237 -138
  20. package/src/core/dialect/mssql/index.ts +164 -185
  21. package/src/core/dialect/sqlite/index.ts +39 -34
  22. package/src/core/execution/db-executor.ts +46 -6
  23. package/src/core/execution/executors/mssql-executor.ts +39 -22
  24. package/src/core/execution/executors/mysql-executor.ts +23 -6
  25. package/src/core/execution/executors/sqlite-executor.ts +29 -3
  26. package/src/core/execution/pooling/pool-types.ts +30 -0
  27. package/src/core/execution/pooling/pool.ts +268 -0
  28. package/src/core/functions/standard-strategy.ts +46 -37
  29. package/src/decorators/bootstrap.ts +7 -7
  30. package/src/index.ts +6 -0
  31. package/src/orm/domain-event-bus.ts +49 -0
  32. package/src/orm/entity-metadata.ts +9 -9
  33. package/src/orm/entity.ts +58 -0
  34. package/src/orm/orm-session.ts +465 -270
  35. package/src/orm/orm.ts +61 -11
  36. package/src/orm/pooled-executor-factory.ts +131 -0
  37. package/src/orm/query-logger.ts +6 -12
  38. package/src/orm/relation-change-processor.ts +75 -0
  39. package/src/orm/relations/many-to-many.ts +4 -2
  40. package/src/orm/save-graph.ts +303 -0
  41. package/src/orm/transaction-runner.ts +3 -3
  42. package/src/orm/unit-of-work.ts +128 -0
  43. package/src/query-builder/delete-query-state.ts +67 -38
  44. package/src/query-builder/delete.ts +37 -1
  45. package/src/query-builder/hydration-manager.ts +93 -79
  46. package/src/query-builder/insert-query-state.ts +131 -61
  47. package/src/query-builder/insert.ts +27 -1
  48. package/src/query-builder/query-ast-service.ts +207 -170
  49. package/src/query-builder/select-query-state.ts +169 -162
  50. package/src/query-builder/select.ts +15 -23
  51. package/src/query-builder/update-query-state.ts +114 -77
  52. package/src/query-builder/update.ts +38 -1
@@ -1,68 +1,85 @@
1
- import { SelectQueryNode } from './query.js';
2
- import { SqlOperator } from '../sql/sql.js';
3
- import { ColumnRef } from './types.js';
4
- import {
5
- ColumnNode,
6
- FunctionNode,
7
- LiteralNode,
8
- JsonPathNode,
9
- OperandNode,
10
- CaseExpressionNode,
11
- WindowFunctionNode,
12
- BinaryExpressionNode,
13
- ExpressionNode,
14
- LogicalExpressionNode,
15
- NullExpressionNode,
16
- InExpressionNode,
17
- ExistsExpressionNode,
18
- BetweenExpressionNode,
19
- isOperandNode
20
- } from './expression-nodes.js';
21
-
22
- export type LiteralValue = LiteralNode['value'];
23
- export type ValueOperandInput = OperandNode | LiteralValue;
24
-
25
- /**
26
- * Converts a primitive or existing operand into an operand node
27
- * @param value - Value or operand to normalize
28
- * @returns OperandNode representing the value
29
- */
30
- export const valueToOperand = (value: ValueOperandInput): OperandNode => {
31
- if (isOperandNode(value)) {
32
- return value;
33
- }
34
-
35
- return {
36
- type: 'Literal',
37
- value
38
- } as LiteralNode;
39
- };
40
-
41
- const toNode = (col: ColumnRef | OperandNode): OperandNode => {
42
- if (isOperandNode(col)) return col as OperandNode;
43
- const def = col as ColumnRef;
44
- return { type: 'Column', table: def.table || 'unknown', name: def.name };
45
- };
46
-
47
- const toLiteralNode = (value: string | number | boolean | null): LiteralNode => ({
48
- type: 'Literal',
49
- value
50
- });
51
-
52
- const isLiteralValue = (value: unknown): value is LiteralValue =>
53
- value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
54
-
55
- export const isValueOperandInput = (value: unknown): value is ValueOperandInput =>
56
- isOperandNode(value) || isLiteralValue(value);
57
-
58
- const toOperand = (val: OperandNode | ColumnRef | LiteralValue): OperandNode => {
59
- if (isLiteralValue(val)) {
60
- return valueToOperand(val);
61
- }
62
-
63
- return toNode(val);
64
- };
65
-
1
+ import { SelectQueryNode } from './query.js';
2
+ import { SqlOperator } from '../sql/sql.js';
3
+ import { ColumnRef } from './types.js';
4
+ import {
5
+ ColumnNode,
6
+ FunctionNode,
7
+ LiteralNode,
8
+ JsonPathNode,
9
+ OperandNode,
10
+ CaseExpressionNode,
11
+ WindowFunctionNode,
12
+ BinaryExpressionNode,
13
+ ExpressionNode,
14
+ LogicalExpressionNode,
15
+ NullExpressionNode,
16
+ InExpressionNode,
17
+ ExistsExpressionNode,
18
+ InExpressionRight,
19
+ ScalarSubqueryNode,
20
+ BetweenExpressionNode,
21
+ isOperandNode,
22
+ AliasRefNode,
23
+ ArithmeticExpressionNode
24
+ } from './expression-nodes.js';
25
+
26
+ export type LiteralValue = LiteralNode['value'];
27
+ export type ValueOperandInput = OperandNode | LiteralValue;
28
+
29
+ /**
30
+ * Converts a primitive or existing operand into an operand node
31
+ * @param value - Value or operand to normalize
32
+ * @returns OperandNode representing the value
33
+ */
34
+ export const valueToOperand = (value: ValueOperandInput): OperandNode => {
35
+ if (isOperandNode(value)) {
36
+ return value;
37
+ }
38
+
39
+ return {
40
+ type: 'Literal',
41
+ value
42
+ } as LiteralNode;
43
+ };
44
+
45
+ const toNode = (col: ColumnRef | OperandNode): OperandNode => {
46
+ if (isOperandNode(col)) return col as OperandNode;
47
+ const def = col as ColumnRef;
48
+ return { type: 'Column', table: def.table || 'unknown', name: def.name };
49
+ };
50
+
51
+ const toLiteralNode = (value: string | number | boolean | null): LiteralNode => ({
52
+ type: 'Literal',
53
+ value
54
+ });
55
+
56
+ const isLiteralValue = (value: unknown): value is LiteralValue =>
57
+ value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
58
+
59
+ export const isValueOperandInput = (value: unknown): value is ValueOperandInput =>
60
+ isOperandNode(value) || isLiteralValue(value);
61
+
62
+ const toOperand = (val: OperandNode | ColumnRef | LiteralValue): OperandNode => {
63
+ if (isLiteralValue(val)) {
64
+ return valueToOperand(val);
65
+ }
66
+
67
+ return toNode(val);
68
+ };
69
+
70
+ export type SelectQueryInput = SelectQueryNode | { getAST(): SelectQueryNode };
71
+
72
+ const hasQueryAst = (value: SelectQueryInput): value is { getAST(): SelectQueryNode } =>
73
+ typeof (value as { getAST?: unknown }).getAST === 'function';
74
+
75
+ const resolveSelectQueryNode = (query: SelectQueryInput): SelectQueryNode =>
76
+ hasQueryAst(query) ? query.getAST() : query;
77
+
78
+ const toScalarSubqueryNode = (query: SelectQueryInput): ScalarSubqueryNode => ({
79
+ type: 'ScalarSubquery',
80
+ query: resolveSelectQueryNode(query)
81
+ });
82
+
66
83
  export const columnOperand = (col: ColumnRef | ColumnNode): ColumnNode => toNode(col) as ColumnNode;
67
84
  /**
68
85
  * Marks a column reference as an outer-scope reference for correlated subqueries.
@@ -73,258 +90,303 @@ export const outerRef = (col: ColumnRef | ColumnNode): ColumnNode => ({
73
90
  scope: 'outer'
74
91
  });
75
92
 
93
+ /**
94
+ * References a SELECT alias (useful for ORDER BY / GROUP BY).
95
+ */
96
+ export const aliasRef = (name: string): AliasRefNode => ({
97
+ type: 'AliasRef',
98
+ name
99
+ });
100
+
76
101
  /**
77
102
  * Creates an outer-scoped column reference using a specific table or alias name.
78
103
  */
79
104
  export const correlateBy = (table: string, column: string): ColumnNode => outerRef({ name: column, table });
80
-
81
- const createBinaryExpression = (
82
- operator: SqlOperator,
83
- left: OperandNode | ColumnRef,
84
- right: OperandNode | ColumnRef | string | number | boolean | null,
85
- escape?: string
86
- ): BinaryExpressionNode => {
87
- const node: BinaryExpressionNode = {
88
- type: 'BinaryExpression',
89
- left: toNode(left),
90
- operator,
91
- right: toOperand(right)
92
- };
93
-
94
- if (escape !== undefined) {
95
- node.escape = toLiteralNode(escape);
96
- }
97
-
98
- return node;
99
- };
100
-
101
- /**
102
- * Creates an equality expression (left = right)
103
- * @param left - Left operand
104
- * @param right - Right operand
105
- * @returns Binary expression node with equality operator
106
- */
107
- export const eq = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number | boolean): BinaryExpressionNode =>
108
- createBinaryExpression('=', left, right);
109
-
110
- /**
111
- * Creates a not equal expression (left != right)
112
- */
113
- export const neq = (
114
- left: OperandNode | ColumnRef,
115
- right: OperandNode | ColumnRef | string | number | boolean
116
- ): BinaryExpressionNode => createBinaryExpression('!=', left, right);
117
-
118
- /**
119
- * Creates a greater-than expression (left > right)
120
- * @param left - Left operand
121
- * @param right - Right operand
122
- * @returns Binary expression node with greater-than operator
123
- */
124
- export const gt = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
125
- createBinaryExpression('>', left, right);
126
-
127
- /**
128
- * Creates a greater than or equal expression (left >= right)
129
- */
130
- export const gte = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
131
- createBinaryExpression('>=', left, right);
132
-
133
- /**
134
- * Creates a less-than expression (left < right)
135
- * @param left - Left operand
136
- * @param right - Right operand
137
- * @returns Binary expression node with less-than operator
138
- */
139
- export const lt = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
140
- createBinaryExpression('<', left, right);
141
-
142
- /**
143
- * Creates a less than or equal expression (left <= right)
144
- */
145
- export const lte = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
146
- createBinaryExpression('<=', left, right);
147
-
148
- /**
149
- * Creates a LIKE pattern matching expression
150
- * @param left - Left operand
151
- * @param pattern - Pattern to match
152
- * @param escape - Optional escape character
153
- * @returns Binary expression node with LIKE operator
154
- */
155
- export const like = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
156
- createBinaryExpression('LIKE', left, pattern, escape);
157
-
158
- /**
159
- * Creates a NOT LIKE pattern matching expression
160
- * @param left - Left operand
161
- * @param pattern - Pattern to match
162
- * @param escape - Optional escape character
163
- * @returns Binary expression node with NOT LIKE operator
164
- */
165
- export const notLike = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
166
- createBinaryExpression('NOT LIKE', left, pattern, escape);
167
-
168
- /**
169
- * Creates a logical AND expression
170
- * @param operands - Expressions to combine with AND
171
- * @returns Logical expression node with AND operator
172
- */
173
- export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
174
- type: 'LogicalExpression',
175
- operator: 'AND',
176
- operands
177
- });
178
-
179
- /**
180
- * Creates a logical OR expression
181
- * @param operands - Expressions to combine with OR
182
- * @returns Logical expression node with OR operator
183
- */
184
- export const or = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
185
- type: 'LogicalExpression',
186
- operator: 'OR',
187
- operands
188
- });
189
-
190
- /**
191
- * Creates an IS NULL expression
192
- * @param left - Operand to check for null
193
- * @returns Null expression node with IS NULL operator
194
- */
195
- export const isNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
196
- type: 'NullExpression',
197
- left: toNode(left),
198
- operator: 'IS NULL'
199
- });
200
-
201
- /**
202
- * Creates an IS NOT NULL expression
203
- * @param left - Operand to check for non-null
204
- * @returns Null expression node with IS NOT NULL operator
205
- */
206
- export const isNotNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
207
- type: 'NullExpression',
208
- left: toNode(left),
209
- operator: 'IS NOT NULL'
210
- });
211
-
212
- const createInExpression = (
213
- operator: 'IN' | 'NOT IN',
214
- left: OperandNode | ColumnRef,
215
- values: (string | number | LiteralNode)[]
216
- ): InExpressionNode => ({
217
- type: 'InExpression',
218
- left: toNode(left),
219
- operator,
220
- right: values.map(v => toOperand(v))
221
- });
222
-
223
- /**
224
- * Creates an IN expression (value IN list)
225
- * @param left - Operand to check
226
- * @param values - Values to check against
227
- * @returns IN expression node
228
- */
229
- export const inList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
230
- createInExpression('IN', left, values);
231
-
232
- /**
233
- * Creates a NOT IN expression (value NOT IN list)
234
- * @param left - Operand to check
235
- * @param values - Values to check against
236
- * @returns NOT IN expression node
237
- */
238
- export const notInList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
239
- createInExpression('NOT IN', left, values);
240
-
241
- const createBetweenExpression = (
242
- operator: 'BETWEEN' | 'NOT BETWEEN',
243
- left: OperandNode | ColumnRef,
244
- lower: OperandNode | ColumnRef | string | number,
245
- upper: OperandNode | ColumnRef | string | number
246
- ): BetweenExpressionNode => ({
247
- type: 'BetweenExpression',
248
- left: toNode(left),
249
- operator,
250
- lower: toOperand(lower),
251
- upper: toOperand(upper)
252
- });
253
-
254
- /**
255
- * Creates a BETWEEN expression (value BETWEEN lower AND upper)
256
- * @param left - Operand to check
257
- * @param lower - Lower bound
258
- * @param upper - Upper bound
259
- * @returns BETWEEN expression node
260
- */
261
- export const between = (
262
- left: OperandNode | ColumnRef,
263
- lower: OperandNode | ColumnRef | string | number,
264
- upper: OperandNode | ColumnRef | string | number
265
- ): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
266
-
267
- /**
268
- * Creates a NOT BETWEEN expression (value NOT BETWEEN lower AND upper)
269
- * @param left - Operand to check
270
- * @param lower - Lower bound
271
- * @param upper - Upper bound
272
- * @returns NOT BETWEEN expression node
273
- */
274
- export const notBetween = (
275
- left: OperandNode | ColumnRef,
276
- lower: OperandNode | ColumnRef | string | number,
277
- upper: OperandNode | ColumnRef | string | number
278
- ): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
279
-
280
- /**
281
- * Creates a JSON path expression
282
- * @param col - Source column
283
- * @param path - JSON path expression
284
- * @returns JSON path node
285
- */
286
- export const jsonPath = (col: ColumnRef | ColumnNode, path: string): JsonPathNode => ({
287
- type: 'JsonPath',
288
- column: columnOperand(col),
289
- path
290
- });
291
-
292
- /**
293
- * Creates a CASE expression
294
- * @param conditions - Array of WHEN-THEN conditions
295
- * @param elseValue - Optional ELSE value
296
- * @returns CASE expression node
297
- */
298
- export const caseWhen = (
299
- conditions: { when: ExpressionNode; then: OperandNode | ColumnRef | string | number | boolean | null }[],
300
- elseValue?: OperandNode | ColumnRef | string | number | boolean | null
301
- ): CaseExpressionNode => ({
302
- type: 'CaseExpression',
303
- conditions: conditions.map(c => ({
304
- when: c.when,
305
- then: toOperand(c.then)
306
- })),
307
- else: elseValue !== undefined ? toOperand(elseValue) : undefined
308
- });
309
-
310
- /**
311
- * Creates an EXISTS expression
312
- * @param subquery - Subquery to check for existence
313
- * @returns EXISTS expression node
314
- */
315
- export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
316
- type: 'ExistsExpression',
317
- operator: 'EXISTS',
318
- subquery
319
- });
320
-
321
- /**
322
- * Creates a NOT EXISTS expression
323
- * @param subquery - Subquery to check for non-existence
324
- * @returns NOT EXISTS expression node
325
- */
326
- export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
327
- type: 'ExistsExpression',
328
- operator: 'NOT EXISTS',
329
- subquery
330
- });
105
+
106
+ const createBinaryExpression = (
107
+ operator: SqlOperator,
108
+ left: OperandNode | ColumnRef,
109
+ right: OperandNode | ColumnRef | string | number | boolean | null,
110
+ escape?: string
111
+ ): BinaryExpressionNode => {
112
+ const node: BinaryExpressionNode = {
113
+ type: 'BinaryExpression',
114
+ left: toNode(left),
115
+ operator,
116
+ right: toOperand(right)
117
+ };
118
+
119
+ if (escape !== undefined) {
120
+ node.escape = toLiteralNode(escape);
121
+ }
122
+
123
+ return node;
124
+ };
125
+
126
+ /**
127
+ * Creates an equality expression (left = right)
128
+ * @param left - Left operand
129
+ * @param right - Right operand
130
+ * @returns Binary expression node with equality operator
131
+ */
132
+ export const eq = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number | boolean): BinaryExpressionNode =>
133
+ createBinaryExpression('=', left, right);
134
+
135
+ /**
136
+ * Creates a not equal expression (left != right)
137
+ */
138
+ export const neq = (
139
+ left: OperandNode | ColumnRef,
140
+ right: OperandNode | ColumnRef | string | number | boolean
141
+ ): BinaryExpressionNode => createBinaryExpression('!=', left, right);
142
+
143
+ /**
144
+ * Creates a greater-than expression (left > right)
145
+ * @param left - Left operand
146
+ * @param right - Right operand
147
+ * @returns Binary expression node with greater-than operator
148
+ */
149
+ export const gt = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
150
+ createBinaryExpression('>', left, right);
151
+
152
+ /**
153
+ * Creates a greater than or equal expression (left >= right)
154
+ */
155
+ export const gte = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
156
+ createBinaryExpression('>=', left, right);
157
+
158
+ /**
159
+ * Creates a less-than expression (left < right)
160
+ * @param left - Left operand
161
+ * @param right - Right operand
162
+ * @returns Binary expression node with less-than operator
163
+ */
164
+ export const lt = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
165
+ createBinaryExpression('<', left, right);
166
+
167
+ /**
168
+ * Creates a less than or equal expression (left <= right)
169
+ */
170
+ export const lte = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
171
+ createBinaryExpression('<=', left, right);
172
+
173
+ /**
174
+ * Creates a LIKE pattern matching expression
175
+ * @param left - Left operand
176
+ * @param pattern - Pattern to match
177
+ * @param escape - Optional escape character
178
+ * @returns Binary expression node with LIKE operator
179
+ */
180
+ export const like = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
181
+ createBinaryExpression('LIKE', left, pattern, escape);
182
+
183
+ /**
184
+ * Creates a NOT LIKE pattern matching expression
185
+ * @param left - Left operand
186
+ * @param pattern - Pattern to match
187
+ * @param escape - Optional escape character
188
+ * @returns Binary expression node with NOT LIKE operator
189
+ */
190
+ export const notLike = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
191
+ createBinaryExpression('NOT LIKE', left, pattern, escape);
192
+
193
+ /**
194
+ * Creates a logical AND expression
195
+ * @param operands - Expressions to combine with AND
196
+ * @returns Logical expression node with AND operator
197
+ */
198
+ export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
199
+ type: 'LogicalExpression',
200
+ operator: 'AND',
201
+ operands
202
+ });
203
+
204
+ /**
205
+ * Creates a logical OR expression
206
+ * @param operands - Expressions to combine with OR
207
+ * @returns Logical expression node with OR operator
208
+ */
209
+ export const or = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
210
+ type: 'LogicalExpression',
211
+ operator: 'OR',
212
+ operands
213
+ });
214
+
215
+ /**
216
+ * Creates an IS NULL expression
217
+ * @param left - Operand to check for null
218
+ * @returns Null expression node with IS NULL operator
219
+ */
220
+ export const isNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
221
+ type: 'NullExpression',
222
+ left: toNode(left),
223
+ operator: 'IS NULL'
224
+ });
225
+
226
+ /**
227
+ * Creates an IS NOT NULL expression
228
+ * @param left - Operand to check for non-null
229
+ * @returns Null expression node with IS NOT NULL operator
230
+ */
231
+ export const isNotNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
232
+ type: 'NullExpression',
233
+ left: toNode(left),
234
+ operator: 'IS NOT NULL'
235
+ });
236
+
237
+ const createInExpression = (
238
+ operator: 'IN' | 'NOT IN',
239
+ left: OperandNode | ColumnRef,
240
+ right: InExpressionRight
241
+ ): InExpressionNode => ({
242
+ type: 'InExpression',
243
+ left: toNode(left),
244
+ operator,
245
+ right
246
+ });
247
+
248
+ /**
249
+ * Creates an IN expression (value IN list)
250
+ * @param left - Operand to check
251
+ * @param values - Values to check against
252
+ * @returns IN expression node
253
+ */
254
+ export const inList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
255
+ createInExpression('IN', left, values.map(v => toOperand(v)));
256
+
257
+ /**
258
+ * Creates a NOT IN expression (value NOT IN list)
259
+ * @param left - Operand to check
260
+ * @param values - Values to check against
261
+ * @returns NOT IN expression node
262
+ */
263
+ export const notInList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
264
+ createInExpression('NOT IN', left, values.map(v => toOperand(v)));
265
+
266
+ export const inSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
267
+ createInExpression('IN', left, toScalarSubqueryNode(subquery));
268
+
269
+ export const notInSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
270
+ createInExpression('NOT IN', left, toScalarSubqueryNode(subquery));
271
+
272
+ const createBetweenExpression = (
273
+ operator: 'BETWEEN' | 'NOT BETWEEN',
274
+ left: OperandNode | ColumnRef,
275
+ lower: OperandNode | ColumnRef | string | number,
276
+ upper: OperandNode | ColumnRef | string | number
277
+ ): BetweenExpressionNode => ({
278
+ type: 'BetweenExpression',
279
+ left: toNode(left),
280
+ operator,
281
+ lower: toOperand(lower),
282
+ upper: toOperand(upper)
283
+ });
284
+
285
+ /**
286
+ * Creates a BETWEEN expression (value BETWEEN lower AND upper)
287
+ * @param left - Operand to check
288
+ * @param lower - Lower bound
289
+ * @param upper - Upper bound
290
+ * @returns BETWEEN expression node
291
+ */
292
+ export const between = (
293
+ left: OperandNode | ColumnRef,
294
+ lower: OperandNode | ColumnRef | string | number,
295
+ upper: OperandNode | ColumnRef | string | number
296
+ ): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
297
+
298
+ /**
299
+ * Creates a NOT BETWEEN expression (value NOT BETWEEN lower AND upper)
300
+ * @param left - Operand to check
301
+ * @param lower - Lower bound
302
+ * @param upper - Upper bound
303
+ * @returns NOT BETWEEN expression node
304
+ */
305
+ export const notBetween = (
306
+ left: OperandNode | ColumnRef,
307
+ lower: OperandNode | ColumnRef | string | number,
308
+ upper: OperandNode | ColumnRef | string | number
309
+ ): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
310
+
311
+ const createArithmeticExpression = (
312
+ operator: '+' | '-' | '*' | '/',
313
+ left: OperandNode | ColumnRef,
314
+ right: OperandNode | ColumnRef | string | number
315
+ ): ArithmeticExpressionNode => ({
316
+ type: 'ArithmeticExpression',
317
+ left: toOperand(left),
318
+ operator,
319
+ right: toOperand(right)
320
+ });
321
+
322
+ export const add = (
323
+ left: OperandNode | ColumnRef,
324
+ right: OperandNode | ColumnRef | string | number
325
+ ): ArithmeticExpressionNode => createArithmeticExpression('+', left, right);
326
+
327
+ export const sub = (
328
+ left: OperandNode | ColumnRef,
329
+ right: OperandNode | ColumnRef | string | number
330
+ ): ArithmeticExpressionNode => createArithmeticExpression('-', left, right);
331
+
332
+ export const mul = (
333
+ left: OperandNode | ColumnRef,
334
+ right: OperandNode | ColumnRef | string | number
335
+ ): ArithmeticExpressionNode => createArithmeticExpression('*', left, right);
336
+
337
+ export const div = (
338
+ left: OperandNode | ColumnRef,
339
+ right: OperandNode | ColumnRef | string | number
340
+ ): ArithmeticExpressionNode => createArithmeticExpression('/', left, right);
341
+
342
+ /**
343
+ * Creates a JSON path expression
344
+ * @param col - Source column
345
+ * @param path - JSON path expression
346
+ * @returns JSON path node
347
+ */
348
+ export const jsonPath = (col: ColumnRef | ColumnNode, path: string): JsonPathNode => ({
349
+ type: 'JsonPath',
350
+ column: columnOperand(col),
351
+ path
352
+ });
353
+
354
+ /**
355
+ * Creates a CASE expression
356
+ * @param conditions - Array of WHEN-THEN conditions
357
+ * @param elseValue - Optional ELSE value
358
+ * @returns CASE expression node
359
+ */
360
+ export const caseWhen = (
361
+ conditions: { when: ExpressionNode; then: OperandNode | ColumnRef | string | number | boolean | null }[],
362
+ elseValue?: OperandNode | ColumnRef | string | number | boolean | null
363
+ ): CaseExpressionNode => ({
364
+ type: 'CaseExpression',
365
+ conditions: conditions.map(c => ({
366
+ when: c.when,
367
+ then: toOperand(c.then)
368
+ })),
369
+ else: elseValue !== undefined ? toOperand(elseValue) : undefined
370
+ });
371
+
372
+ /**
373
+ * Creates an EXISTS expression
374
+ * @param subquery - Subquery to check for existence
375
+ * @returns EXISTS expression node
376
+ */
377
+ export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
378
+ type: 'ExistsExpression',
379
+ operator: 'EXISTS',
380
+ subquery
381
+ });
382
+
383
+ /**
384
+ * Creates a NOT EXISTS expression
385
+ * @param subquery - Subquery to check for non-existence
386
+ * @returns NOT EXISTS expression node
387
+ */
388
+ export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
389
+ type: 'ExistsExpression',
390
+ operator: 'NOT EXISTS',
391
+ subquery
392
+ });