metal-orm 1.0.39 → 1.0.40

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