metal-orm 1.0.15 → 1.0.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.
- package/README.md +34 -27
- package/dist/decorators/index.cjs +339 -153
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +1 -5
- package/dist/decorators/index.d.ts +1 -5
- package/dist/decorators/index.js +339 -153
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +838 -484
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -14
- package/dist/index.d.ts +17 -14
- package/dist/index.js +833 -483
- package/dist/index.js.map +1 -1
- package/dist/{select-Bkv8g8u_.d.cts → select-BKZrMRCQ.d.cts} +363 -28
- package/dist/{select-Bkv8g8u_.d.ts → select-BKZrMRCQ.d.ts} +363 -28
- package/package.json +1 -1
- package/src/codegen/naming-strategy.ts +64 -0
- package/src/codegen/typescript.ts +48 -53
- package/src/core/ddl/schema-generator.ts +3 -2
- package/src/core/ddl/schema-introspect.ts +1 -1
- package/src/core/dialect/abstract.ts +28 -0
- package/src/decorators/column.ts +13 -4
- package/src/index.ts +8 -1
- package/src/orm/entity-context.ts +30 -0
- package/src/orm/entity-meta.ts +2 -2
- package/src/orm/entity-metadata.ts +1 -6
- package/src/orm/entity.ts +88 -88
- package/src/orm/execute.ts +42 -25
- package/src/orm/execution-context.ts +12 -0
- package/src/orm/hydration-context.ts +14 -0
- package/src/orm/identity-map.ts +4 -0
- package/src/orm/interceptor-pipeline.ts +29 -0
- package/src/orm/lazy-batch.ts +6 -6
- package/src/orm/orm-session.ts +234 -0
- package/src/orm/orm.ts +58 -0
- package/src/orm/relation-change-processor.ts +5 -1
- package/src/orm/relations/belongs-to.ts +45 -44
- package/src/orm/relations/has-many.ts +44 -43
- package/src/orm/relations/has-one.ts +140 -139
- package/src/orm/relations/many-to-many.ts +46 -45
- package/src/orm/unit-of-work.ts +6 -1
- package/src/query-builder/select.ts +509 -3
- package/src/orm/orm-context.ts +0 -159
|
@@ -1,494 +1,1000 @@
|
|
|
1
1
|
import { TableDef } from '../schema/table.js';
|
|
2
|
+
|
|
2
3
|
import { ColumnDef } from '../schema/column.js';
|
|
4
|
+
|
|
3
5
|
import { SelectQueryNode, SetOperationKind } from '../core/ast/query.js';
|
|
6
|
+
|
|
4
7
|
import { HydrationPlan } from '../core/hydration/types.js';
|
|
8
|
+
|
|
5
9
|
import {
|
|
10
|
+
|
|
6
11
|
ColumnNode,
|
|
12
|
+
|
|
7
13
|
ExpressionNode,
|
|
14
|
+
|
|
8
15
|
FunctionNode,
|
|
16
|
+
|
|
9
17
|
LiteralNode,
|
|
18
|
+
|
|
10
19
|
BinaryExpressionNode,
|
|
20
|
+
|
|
11
21
|
CaseExpressionNode,
|
|
22
|
+
|
|
12
23
|
WindowFunctionNode,
|
|
24
|
+
|
|
13
25
|
exists,
|
|
26
|
+
|
|
14
27
|
notExists
|
|
28
|
+
|
|
15
29
|
} from '../core/ast/expression.js';
|
|
30
|
+
|
|
16
31
|
import { CompiledQuery, Dialect } from '../core/dialect/abstract.js';
|
|
32
|
+
|
|
17
33
|
import { DialectKey, resolveDialectInput } from '../core/dialect/dialect-factory.js';
|
|
18
34
|
|
|
35
|
+
|
|
36
|
+
|
|
19
37
|
type SelectDialectInput = Dialect | DialectKey;
|
|
38
|
+
|
|
20
39
|
import { SelectQueryState } from './select-query-state.js';
|
|
40
|
+
|
|
21
41
|
import { HydrationManager } from './hydration-manager.js';
|
|
42
|
+
|
|
22
43
|
import {
|
|
44
|
+
|
|
23
45
|
resolveSelectQueryBuilderDependencies,
|
|
46
|
+
|
|
24
47
|
SelectQueryBuilderContext,
|
|
48
|
+
|
|
25
49
|
SelectQueryBuilderDependencies,
|
|
50
|
+
|
|
26
51
|
SelectQueryBuilderEnvironment
|
|
52
|
+
|
|
27
53
|
} from './select-query-builder-deps.js';
|
|
54
|
+
|
|
28
55
|
import { QueryAstService } from './query-ast-service.js';
|
|
56
|
+
|
|
29
57
|
import { ColumnSelector } from './column-selector.js';
|
|
58
|
+
|
|
30
59
|
import { RelationManager } from './relation-manager.js';
|
|
60
|
+
|
|
31
61
|
import { RelationIncludeOptions } from './relation-types.js';
|
|
62
|
+
|
|
32
63
|
import { JOIN_KINDS, JoinKind, ORDER_DIRECTIONS, OrderDirection } from '../core/sql/sql.js';
|
|
64
|
+
|
|
33
65
|
import { Entity, RelationMap } from '../schema/types.js';
|
|
34
|
-
|
|
35
|
-
import {
|
|
66
|
+
|
|
67
|
+
import { OrmSession } from '../orm/orm-session.ts';
|
|
68
|
+
|
|
69
|
+
import { ExecutionContext } from '../orm/execution-context.js';
|
|
70
|
+
|
|
71
|
+
import { HydrationContext } from '../orm/hydration-context.js';
|
|
72
|
+
|
|
73
|
+
import { executeHydrated, executeHydratedWithContexts } from '../orm/execute.js';
|
|
74
|
+
|
|
36
75
|
import { createJoinNode } from '../core/ast/join-node.js';
|
|
37
76
|
|
|
77
|
+
|
|
78
|
+
|
|
38
79
|
/**
|
|
80
|
+
|
|
39
81
|
* Main query builder class for constructing SQL SELECT queries
|
|
82
|
+
|
|
40
83
|
* @typeParam T - Result type for projections (unused)
|
|
84
|
+
|
|
41
85
|
* @typeParam TTable - Table definition being queried
|
|
86
|
+
|
|
42
87
|
*/
|
|
88
|
+
|
|
43
89
|
export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
90
|
+
|
|
44
91
|
private readonly env: SelectQueryBuilderEnvironment;
|
|
92
|
+
|
|
45
93
|
private readonly context: SelectQueryBuilderContext;
|
|
94
|
+
|
|
46
95
|
private readonly columnSelector: ColumnSelector;
|
|
96
|
+
|
|
47
97
|
private readonly relationManager: RelationManager;
|
|
98
|
+
|
|
48
99
|
private readonly lazyRelations: Set<string>;
|
|
49
100
|
|
|
101
|
+
|
|
102
|
+
|
|
50
103
|
/**
|
|
104
|
+
|
|
51
105
|
* Creates a new SelectQueryBuilder instance
|
|
106
|
+
|
|
52
107
|
* @param table - Table definition to query
|
|
108
|
+
|
|
53
109
|
* @param state - Optional initial query state
|
|
110
|
+
|
|
54
111
|
* @param hydration - Optional hydration manager
|
|
112
|
+
|
|
55
113
|
* @param dependencies - Optional query builder dependencies
|
|
114
|
+
|
|
56
115
|
*/
|
|
116
|
+
|
|
57
117
|
constructor(
|
|
118
|
+
|
|
58
119
|
table: TTable,
|
|
120
|
+
|
|
59
121
|
state?: SelectQueryState,
|
|
122
|
+
|
|
60
123
|
hydration?: HydrationManager,
|
|
124
|
+
|
|
61
125
|
dependencies?: Partial<SelectQueryBuilderDependencies>,
|
|
126
|
+
|
|
62
127
|
lazyRelations?: Set<string>
|
|
128
|
+
|
|
63
129
|
) {
|
|
130
|
+
|
|
64
131
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
132
|
+
|
|
65
133
|
this.env = { table, deps };
|
|
134
|
+
|
|
66
135
|
const initialState = state ?? deps.createState(table);
|
|
136
|
+
|
|
67
137
|
const initialHydration = hydration ?? deps.createHydration(table);
|
|
138
|
+
|
|
68
139
|
this.context = {
|
|
140
|
+
|
|
69
141
|
state: initialState,
|
|
142
|
+
|
|
70
143
|
hydration: initialHydration
|
|
144
|
+
|
|
71
145
|
};
|
|
146
|
+
|
|
72
147
|
this.lazyRelations = new Set(lazyRelations ?? []);
|
|
148
|
+
|
|
73
149
|
this.columnSelector = new ColumnSelector(this.env);
|
|
150
|
+
|
|
74
151
|
this.relationManager = new RelationManager(this.env);
|
|
152
|
+
|
|
75
153
|
}
|
|
76
154
|
|
|
155
|
+
|
|
156
|
+
|
|
77
157
|
private clone(
|
|
158
|
+
|
|
78
159
|
context: SelectQueryBuilderContext = this.context,
|
|
160
|
+
|
|
79
161
|
lazyRelations = new Set(this.lazyRelations)
|
|
162
|
+
|
|
80
163
|
): SelectQueryBuilder<T, TTable> {
|
|
164
|
+
|
|
81
165
|
return new SelectQueryBuilder(this.env.table as TTable, context.state, context.hydration, this.env.deps, lazyRelations);
|
|
166
|
+
|
|
82
167
|
}
|
|
83
168
|
|
|
169
|
+
|
|
170
|
+
|
|
84
171
|
private resolveQueryNode(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryNode {
|
|
172
|
+
|
|
85
173
|
return typeof (query as any).getAST === 'function'
|
|
174
|
+
|
|
86
175
|
? (query as SelectQueryBuilder<any, TableDef<any>>).getAST()
|
|
176
|
+
|
|
87
177
|
: (query as SelectQueryNode);
|
|
178
|
+
|
|
88
179
|
}
|
|
89
180
|
|
|
181
|
+
|
|
182
|
+
|
|
90
183
|
private createChildBuilder<R, TChild extends TableDef>(table: TChild): SelectQueryBuilder<R, TChild> {
|
|
184
|
+
|
|
91
185
|
return new SelectQueryBuilder(table, undefined, undefined, this.env.deps);
|
|
186
|
+
|
|
92
187
|
}
|
|
93
188
|
|
|
189
|
+
|
|
190
|
+
|
|
94
191
|
private applyAst(
|
|
192
|
+
|
|
95
193
|
context: SelectQueryBuilderContext,
|
|
194
|
+
|
|
96
195
|
mutator: (service: QueryAstService) => SelectQueryState
|
|
196
|
+
|
|
97
197
|
): SelectQueryBuilderContext {
|
|
198
|
+
|
|
98
199
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
200
|
+
|
|
99
201
|
const nextState = mutator(astService);
|
|
202
|
+
|
|
100
203
|
return { state: nextState, hydration: context.hydration };
|
|
204
|
+
|
|
101
205
|
}
|
|
102
206
|
|
|
207
|
+
|
|
208
|
+
|
|
103
209
|
private applyJoin(
|
|
210
|
+
|
|
104
211
|
context: SelectQueryBuilderContext,
|
|
212
|
+
|
|
105
213
|
table: TableDef,
|
|
214
|
+
|
|
106
215
|
condition: BinaryExpressionNode,
|
|
216
|
+
|
|
107
217
|
kind: JoinKind
|
|
218
|
+
|
|
108
219
|
): SelectQueryBuilderContext {
|
|
220
|
+
|
|
109
221
|
const joinNode = createJoinNode(kind, table.name, condition);
|
|
222
|
+
|
|
110
223
|
return this.applyAst(context, service => service.withJoin(joinNode));
|
|
224
|
+
|
|
111
225
|
}
|
|
112
226
|
|
|
227
|
+
|
|
228
|
+
|
|
113
229
|
private applySetOperation(
|
|
230
|
+
|
|
114
231
|
operator: SetOperationKind,
|
|
232
|
+
|
|
115
233
|
query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode
|
|
234
|
+
|
|
116
235
|
): SelectQueryBuilderContext {
|
|
236
|
+
|
|
117
237
|
const subAst = this.resolveQueryNode(query);
|
|
238
|
+
|
|
118
239
|
return this.applyAst(this.context, service => service.withSetOperation(operator, subAst));
|
|
240
|
+
|
|
119
241
|
}
|
|
120
242
|
|
|
243
|
+
|
|
244
|
+
|
|
121
245
|
/**
|
|
246
|
+
|
|
122
247
|
* Selects specific columns for the query
|
|
248
|
+
|
|
123
249
|
* @param columns - Record of column definitions, function nodes, case expressions, or window functions
|
|
250
|
+
|
|
124
251
|
* @returns New query builder instance with selected columns
|
|
252
|
+
|
|
125
253
|
*/
|
|
254
|
+
|
|
126
255
|
select(columns: Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>): SelectQueryBuilder<T, TTable> {
|
|
256
|
+
|
|
127
257
|
return this.clone(this.columnSelector.select(this.context, columns));
|
|
258
|
+
|
|
128
259
|
}
|
|
129
260
|
|
|
261
|
+
|
|
262
|
+
|
|
130
263
|
/**
|
|
264
|
+
|
|
131
265
|
* Selects raw column expressions
|
|
266
|
+
|
|
132
267
|
* @param cols - Column expressions as strings
|
|
268
|
+
|
|
133
269
|
* @returns New query builder instance with raw column selections
|
|
270
|
+
|
|
134
271
|
*/
|
|
272
|
+
|
|
135
273
|
selectRaw(...cols: string[]): SelectQueryBuilder<T, TTable> {
|
|
274
|
+
|
|
136
275
|
return this.clone(this.columnSelector.selectRaw(this.context, cols));
|
|
276
|
+
|
|
137
277
|
}
|
|
138
278
|
|
|
279
|
+
|
|
280
|
+
|
|
139
281
|
/**
|
|
282
|
+
|
|
140
283
|
* Adds a Common Table Expression (CTE) to the query
|
|
284
|
+
|
|
141
285
|
* @param name - Name of the CTE
|
|
286
|
+
|
|
142
287
|
* @param query - Query builder or query node for the CTE
|
|
288
|
+
|
|
143
289
|
* @param columns - Optional column names for the CTE
|
|
290
|
+
|
|
144
291
|
* @returns New query builder instance with the CTE
|
|
292
|
+
|
|
145
293
|
*/
|
|
294
|
+
|
|
146
295
|
with(name: string, query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
|
|
296
|
+
|
|
147
297
|
const subAst = this.resolveQueryNode(query);
|
|
298
|
+
|
|
148
299
|
const nextContext = this.applyAst(this.context, service => service.withCte(name, subAst, columns, false));
|
|
300
|
+
|
|
149
301
|
return this.clone(nextContext);
|
|
302
|
+
|
|
150
303
|
}
|
|
151
304
|
|
|
305
|
+
|
|
306
|
+
|
|
152
307
|
/**
|
|
308
|
+
|
|
153
309
|
* Adds a recursive Common Table Expression (CTE) to the query
|
|
310
|
+
|
|
154
311
|
* @param name - Name of the CTE
|
|
312
|
+
|
|
155
313
|
* @param query - Query builder or query node for the CTE
|
|
314
|
+
|
|
156
315
|
* @param columns - Optional column names for the CTE
|
|
316
|
+
|
|
157
317
|
* @returns New query builder instance with the recursive CTE
|
|
318
|
+
|
|
158
319
|
*/
|
|
320
|
+
|
|
159
321
|
withRecursive(name: string, query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
|
|
322
|
+
|
|
160
323
|
const subAst = this.resolveQueryNode(query);
|
|
324
|
+
|
|
161
325
|
const nextContext = this.applyAst(this.context, service => service.withCte(name, subAst, columns, true));
|
|
326
|
+
|
|
162
327
|
return this.clone(nextContext);
|
|
328
|
+
|
|
163
329
|
}
|
|
164
330
|
|
|
331
|
+
|
|
332
|
+
|
|
165
333
|
/**
|
|
334
|
+
|
|
166
335
|
* Selects a subquery as a column
|
|
336
|
+
|
|
167
337
|
* @param alias - Alias for the subquery column
|
|
338
|
+
|
|
168
339
|
* @param sub - Query builder or query node for the subquery
|
|
340
|
+
|
|
169
341
|
* @returns New query builder instance with the subquery selection
|
|
342
|
+
|
|
170
343
|
*/
|
|
344
|
+
|
|
171
345
|
selectSubquery(alias: string, sub: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
346
|
+
|
|
172
347
|
const query = this.resolveQueryNode(sub);
|
|
348
|
+
|
|
173
349
|
return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
|
|
350
|
+
|
|
174
351
|
}
|
|
175
352
|
|
|
353
|
+
|
|
354
|
+
|
|
176
355
|
/**
|
|
356
|
+
|
|
177
357
|
* Adds an INNER JOIN to the query
|
|
358
|
+
|
|
178
359
|
* @param table - Table to join
|
|
360
|
+
|
|
179
361
|
* @param condition - Join condition expression
|
|
362
|
+
|
|
180
363
|
* @returns New query builder instance with the INNER JOIN
|
|
364
|
+
|
|
181
365
|
*/
|
|
366
|
+
|
|
182
367
|
innerJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
368
|
+
|
|
183
369
|
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
|
|
370
|
+
|
|
184
371
|
return this.clone(nextContext);
|
|
372
|
+
|
|
185
373
|
}
|
|
186
374
|
|
|
375
|
+
|
|
376
|
+
|
|
187
377
|
/**
|
|
378
|
+
|
|
188
379
|
* Adds a LEFT JOIN to the query
|
|
380
|
+
|
|
189
381
|
* @param table - Table to join
|
|
382
|
+
|
|
190
383
|
* @param condition - Join condition expression
|
|
384
|
+
|
|
191
385
|
* @returns New query builder instance with the LEFT JOIN
|
|
386
|
+
|
|
192
387
|
*/
|
|
388
|
+
|
|
193
389
|
leftJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
390
|
+
|
|
194
391
|
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
|
|
392
|
+
|
|
195
393
|
return this.clone(nextContext);
|
|
394
|
+
|
|
196
395
|
}
|
|
197
396
|
|
|
397
|
+
|
|
398
|
+
|
|
198
399
|
/**
|
|
400
|
+
|
|
199
401
|
* Adds a RIGHT JOIN to the query
|
|
402
|
+
|
|
200
403
|
* @param table - Table to join
|
|
404
|
+
|
|
201
405
|
* @param condition - Join condition expression
|
|
406
|
+
|
|
202
407
|
* @returns New query builder instance with the RIGHT JOIN
|
|
408
|
+
|
|
203
409
|
*/
|
|
410
|
+
|
|
204
411
|
rightJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
412
|
+
|
|
205
413
|
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
|
|
414
|
+
|
|
206
415
|
return this.clone(nextContext);
|
|
416
|
+
|
|
207
417
|
}
|
|
208
418
|
|
|
419
|
+
|
|
420
|
+
|
|
209
421
|
/**
|
|
422
|
+
|
|
210
423
|
* Matches records based on a relationship
|
|
424
|
+
|
|
211
425
|
* @param relationName - Name of the relationship to match
|
|
426
|
+
|
|
212
427
|
* @param predicate - Optional predicate expression
|
|
428
|
+
|
|
213
429
|
* @returns New query builder instance with the relationship match
|
|
430
|
+
|
|
214
431
|
*/
|
|
432
|
+
|
|
215
433
|
match(relationName: string, predicate?: ExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
434
|
+
|
|
216
435
|
const nextContext = this.relationManager.match(this.context, relationName, predicate);
|
|
436
|
+
|
|
217
437
|
return this.clone(nextContext);
|
|
438
|
+
|
|
218
439
|
}
|
|
219
440
|
|
|
441
|
+
|
|
442
|
+
|
|
220
443
|
/**
|
|
444
|
+
|
|
221
445
|
* Joins a related table
|
|
446
|
+
|
|
222
447
|
* @param relationName - Name of the relationship to join
|
|
448
|
+
|
|
223
449
|
* @param joinKind - Type of join (defaults to INNER)
|
|
450
|
+
|
|
224
451
|
* @param extraCondition - Optional additional join condition
|
|
452
|
+
|
|
225
453
|
* @returns New query builder instance with the relationship join
|
|
454
|
+
|
|
226
455
|
*/
|
|
456
|
+
|
|
227
457
|
joinRelation(
|
|
458
|
+
|
|
228
459
|
relationName: string,
|
|
460
|
+
|
|
229
461
|
joinKind: JoinKind = JOIN_KINDS.INNER,
|
|
462
|
+
|
|
230
463
|
extraCondition?: ExpressionNode
|
|
464
|
+
|
|
231
465
|
): SelectQueryBuilder<T, TTable> {
|
|
466
|
+
|
|
232
467
|
const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
|
|
468
|
+
|
|
233
469
|
return this.clone(nextContext);
|
|
470
|
+
|
|
234
471
|
}
|
|
235
472
|
|
|
473
|
+
|
|
474
|
+
|
|
236
475
|
/**
|
|
476
|
+
|
|
237
477
|
* Includes related data in the query results
|
|
478
|
+
|
|
238
479
|
* @param relationName - Name of the relationship to include
|
|
480
|
+
|
|
239
481
|
* @param options - Optional include options
|
|
482
|
+
|
|
240
483
|
* @returns New query builder instance with the relationship inclusion
|
|
484
|
+
|
|
241
485
|
*/
|
|
486
|
+
|
|
242
487
|
include(relationName: string, options?: RelationIncludeOptions): SelectQueryBuilder<T, TTable> {
|
|
488
|
+
|
|
243
489
|
const nextContext = this.relationManager.include(this.context, relationName, options);
|
|
490
|
+
|
|
244
491
|
return this.clone(nextContext);
|
|
492
|
+
|
|
245
493
|
}
|
|
246
494
|
|
|
495
|
+
|
|
496
|
+
|
|
247
497
|
includeLazy<K extends keyof RelationMap<TTable>>(relationName: K): SelectQueryBuilder<T, TTable> {
|
|
498
|
+
|
|
248
499
|
const nextLazy = new Set(this.lazyRelations);
|
|
500
|
+
|
|
249
501
|
nextLazy.add(relationName as string);
|
|
502
|
+
|
|
250
503
|
return this.clone(this.context, nextLazy);
|
|
504
|
+
|
|
251
505
|
}
|
|
252
506
|
|
|
507
|
+
|
|
508
|
+
|
|
253
509
|
getLazyRelations(): (keyof RelationMap<TTable>)[] {
|
|
510
|
+
|
|
254
511
|
return Array.from(this.lazyRelations) as (keyof RelationMap<TTable>)[];
|
|
512
|
+
|
|
255
513
|
}
|
|
256
514
|
|
|
515
|
+
|
|
516
|
+
|
|
257
517
|
getTable(): TTable {
|
|
518
|
+
|
|
258
519
|
return this.env.table as TTable;
|
|
520
|
+
|
|
259
521
|
}
|
|
260
522
|
|
|
261
|
-
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
async execute(ctx: OrmSession): Promise<Entity<TTable>[]> {
|
|
526
|
+
|
|
262
527
|
return executeHydrated(ctx, this);
|
|
528
|
+
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
async executeWithContexts(execCtx: ExecutionContext, hydCtx: HydrationContext): Promise<Entity<TTable>[]> {
|
|
534
|
+
|
|
535
|
+
return executeHydratedWithContexts(execCtx, hydCtx, this);
|
|
536
|
+
|
|
263
537
|
}
|
|
264
538
|
|
|
539
|
+
|
|
540
|
+
|
|
265
541
|
/**
|
|
542
|
+
|
|
266
543
|
* Adds a WHERE condition to the query
|
|
544
|
+
|
|
267
545
|
* @param expr - Expression for the WHERE clause
|
|
546
|
+
|
|
268
547
|
* @returns New query builder instance with the WHERE condition
|
|
548
|
+
|
|
269
549
|
*/
|
|
550
|
+
|
|
270
551
|
where(expr: ExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
552
|
+
|
|
271
553
|
const nextContext = this.applyAst(this.context, service => service.withWhere(expr));
|
|
554
|
+
|
|
272
555
|
return this.clone(nextContext);
|
|
556
|
+
|
|
273
557
|
}
|
|
274
558
|
|
|
559
|
+
|
|
560
|
+
|
|
275
561
|
/**
|
|
562
|
+
|
|
276
563
|
* Adds a GROUP BY clause to the query
|
|
564
|
+
|
|
277
565
|
* @param col - Column definition or column node to group by
|
|
566
|
+
|
|
278
567
|
* @returns New query builder instance with the GROUP BY clause
|
|
568
|
+
|
|
279
569
|
*/
|
|
570
|
+
|
|
280
571
|
groupBy(col: ColumnDef | ColumnNode): SelectQueryBuilder<T, TTable> {
|
|
572
|
+
|
|
281
573
|
const nextContext = this.applyAst(this.context, service => service.withGroupBy(col));
|
|
574
|
+
|
|
282
575
|
return this.clone(nextContext);
|
|
576
|
+
|
|
283
577
|
}
|
|
284
578
|
|
|
579
|
+
|
|
580
|
+
|
|
285
581
|
/**
|
|
582
|
+
|
|
286
583
|
* Adds a HAVING condition to the query
|
|
584
|
+
|
|
287
585
|
* @param expr - Expression for the HAVING clause
|
|
586
|
+
|
|
288
587
|
* @returns New query builder instance with the HAVING condition
|
|
588
|
+
|
|
289
589
|
*/
|
|
590
|
+
|
|
290
591
|
having(expr: ExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
592
|
+
|
|
291
593
|
const nextContext = this.applyAst(this.context, service => service.withHaving(expr));
|
|
594
|
+
|
|
292
595
|
return this.clone(nextContext);
|
|
596
|
+
|
|
293
597
|
}
|
|
294
598
|
|
|
599
|
+
|
|
600
|
+
|
|
295
601
|
/**
|
|
602
|
+
|
|
296
603
|
* Adds an ORDER BY clause to the query
|
|
604
|
+
|
|
297
605
|
* @param col - Column definition or column node to order by
|
|
606
|
+
|
|
298
607
|
* @param direction - Order direction (defaults to ASC)
|
|
608
|
+
|
|
299
609
|
* @returns New query builder instance with the ORDER BY clause
|
|
610
|
+
|
|
300
611
|
*/
|
|
612
|
+
|
|
301
613
|
orderBy(col: ColumnDef | ColumnNode, direction: OrderDirection = ORDER_DIRECTIONS.ASC): SelectQueryBuilder<T, TTable> {
|
|
614
|
+
|
|
302
615
|
const nextContext = this.applyAst(this.context, service => service.withOrderBy(col, direction));
|
|
616
|
+
|
|
303
617
|
return this.clone(nextContext);
|
|
618
|
+
|
|
304
619
|
}
|
|
305
620
|
|
|
621
|
+
|
|
622
|
+
|
|
306
623
|
/**
|
|
624
|
+
|
|
307
625
|
* Adds a DISTINCT clause to the query
|
|
626
|
+
|
|
308
627
|
* @param cols - Columns to make distinct
|
|
628
|
+
|
|
309
629
|
* @returns New query builder instance with the DISTINCT clause
|
|
630
|
+
|
|
310
631
|
*/
|
|
632
|
+
|
|
311
633
|
distinct(...cols: (ColumnDef | ColumnNode)[]): SelectQueryBuilder<T, TTable> {
|
|
634
|
+
|
|
312
635
|
return this.clone(this.columnSelector.distinct(this.context, cols));
|
|
636
|
+
|
|
313
637
|
}
|
|
314
638
|
|
|
639
|
+
|
|
640
|
+
|
|
315
641
|
/**
|
|
642
|
+
|
|
316
643
|
* Adds a LIMIT clause to the query
|
|
644
|
+
|
|
317
645
|
* @param n - Maximum number of rows to return
|
|
646
|
+
|
|
318
647
|
* @returns New query builder instance with the LIMIT clause
|
|
648
|
+
|
|
319
649
|
*/
|
|
650
|
+
|
|
320
651
|
limit(n: number): SelectQueryBuilder<T, TTable> {
|
|
652
|
+
|
|
321
653
|
const nextContext = this.applyAst(this.context, service => service.withLimit(n));
|
|
654
|
+
|
|
322
655
|
return this.clone(nextContext);
|
|
656
|
+
|
|
323
657
|
}
|
|
324
658
|
|
|
659
|
+
|
|
660
|
+
|
|
325
661
|
/**
|
|
662
|
+
|
|
326
663
|
* Adds an OFFSET clause to the query
|
|
664
|
+
|
|
327
665
|
* @param n - Number of rows to skip
|
|
666
|
+
|
|
328
667
|
* @returns New query builder instance with the OFFSET clause
|
|
668
|
+
|
|
329
669
|
*/
|
|
670
|
+
|
|
330
671
|
offset(n: number): SelectQueryBuilder<T, TTable> {
|
|
672
|
+
|
|
331
673
|
const nextContext = this.applyAst(this.context, service => service.withOffset(n));
|
|
674
|
+
|
|
332
675
|
return this.clone(nextContext);
|
|
676
|
+
|
|
333
677
|
}
|
|
334
678
|
|
|
679
|
+
|
|
680
|
+
|
|
335
681
|
/**
|
|
682
|
+
|
|
336
683
|
* Combines this query with another using UNION
|
|
684
|
+
|
|
337
685
|
* @param query - Query to union with
|
|
686
|
+
|
|
338
687
|
* @returns New query builder instance with the set operation
|
|
688
|
+
|
|
339
689
|
*/
|
|
690
|
+
|
|
340
691
|
union(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
692
|
+
|
|
341
693
|
return this.clone(this.applySetOperation('UNION', query));
|
|
694
|
+
|
|
342
695
|
}
|
|
343
696
|
|
|
697
|
+
|
|
698
|
+
|
|
344
699
|
/**
|
|
700
|
+
|
|
345
701
|
* Combines this query with another using UNION ALL
|
|
702
|
+
|
|
346
703
|
* @param query - Query to union with
|
|
704
|
+
|
|
347
705
|
* @returns New query builder instance with the set operation
|
|
706
|
+
|
|
348
707
|
*/
|
|
708
|
+
|
|
349
709
|
unionAll(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
710
|
+
|
|
350
711
|
return this.clone(this.applySetOperation('UNION ALL', query));
|
|
712
|
+
|
|
351
713
|
}
|
|
352
714
|
|
|
715
|
+
|
|
716
|
+
|
|
353
717
|
/**
|
|
718
|
+
|
|
354
719
|
* Combines this query with another using INTERSECT
|
|
720
|
+
|
|
355
721
|
* @param query - Query to intersect with
|
|
722
|
+
|
|
356
723
|
* @returns New query builder instance with the set operation
|
|
724
|
+
|
|
357
725
|
*/
|
|
726
|
+
|
|
358
727
|
intersect(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
728
|
+
|
|
359
729
|
return this.clone(this.applySetOperation('INTERSECT', query));
|
|
730
|
+
|
|
360
731
|
}
|
|
361
732
|
|
|
733
|
+
|
|
734
|
+
|
|
362
735
|
/**
|
|
736
|
+
|
|
363
737
|
* Combines this query with another using EXCEPT
|
|
738
|
+
|
|
364
739
|
* @param query - Query to subtract
|
|
740
|
+
|
|
365
741
|
* @returns New query builder instance with the set operation
|
|
742
|
+
|
|
366
743
|
*/
|
|
744
|
+
|
|
367
745
|
except(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
746
|
+
|
|
368
747
|
return this.clone(this.applySetOperation('EXCEPT', query));
|
|
748
|
+
|
|
369
749
|
}
|
|
370
750
|
|
|
751
|
+
|
|
752
|
+
|
|
371
753
|
/**
|
|
754
|
+
|
|
372
755
|
* Adds a WHERE EXISTS condition to the query
|
|
756
|
+
|
|
373
757
|
* @param subquery - Subquery to check for existence
|
|
758
|
+
|
|
374
759
|
* @returns New query builder instance with the WHERE EXISTS condition
|
|
760
|
+
|
|
375
761
|
*/
|
|
762
|
+
|
|
376
763
|
whereExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
764
|
+
|
|
377
765
|
const subAst = this.resolveQueryNode(subquery);
|
|
766
|
+
|
|
378
767
|
return this.where(exists(subAst));
|
|
768
|
+
|
|
379
769
|
}
|
|
380
770
|
|
|
771
|
+
|
|
772
|
+
|
|
381
773
|
/**
|
|
774
|
+
|
|
382
775
|
* Adds a WHERE NOT EXISTS condition to the query
|
|
776
|
+
|
|
383
777
|
* @param subquery - Subquery to check for non-existence
|
|
778
|
+
|
|
384
779
|
* @returns New query builder instance with the WHERE NOT EXISTS condition
|
|
780
|
+
|
|
385
781
|
*/
|
|
782
|
+
|
|
386
783
|
whereNotExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
784
|
+
|
|
387
785
|
const subAst = this.resolveQueryNode(subquery);
|
|
786
|
+
|
|
388
787
|
return this.where(notExists(subAst));
|
|
788
|
+
|
|
389
789
|
}
|
|
390
790
|
|
|
791
|
+
|
|
792
|
+
|
|
391
793
|
/**
|
|
794
|
+
|
|
392
795
|
* Adds a WHERE EXISTS condition based on a relationship
|
|
796
|
+
|
|
393
797
|
* @param relationName - Name of the relationship to check
|
|
798
|
+
|
|
394
799
|
* @param callback - Optional callback to modify the relationship query
|
|
800
|
+
|
|
395
801
|
* @returns New query builder instance with the relationship existence check
|
|
802
|
+
|
|
396
803
|
*/
|
|
804
|
+
|
|
397
805
|
whereHas(
|
|
806
|
+
|
|
398
807
|
relationName: string,
|
|
808
|
+
|
|
399
809
|
callback?: <TChildTable extends TableDef>(
|
|
810
|
+
|
|
400
811
|
qb: SelectQueryBuilder<any, TChildTable>
|
|
812
|
+
|
|
401
813
|
) => SelectQueryBuilder<any, TChildTable>
|
|
814
|
+
|
|
402
815
|
): SelectQueryBuilder<T, TTable> {
|
|
816
|
+
|
|
403
817
|
const relation = this.env.table.relations[relationName];
|
|
818
|
+
|
|
404
819
|
if (!relation) {
|
|
820
|
+
|
|
405
821
|
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
822
|
+
|
|
406
823
|
}
|
|
407
824
|
|
|
825
|
+
|
|
826
|
+
|
|
408
827
|
let subQb = this.createChildBuilder<any, typeof relation.target>(relation.target);
|
|
828
|
+
|
|
409
829
|
if (callback) {
|
|
830
|
+
|
|
410
831
|
subQb = callback(subQb);
|
|
832
|
+
|
|
411
833
|
}
|
|
412
834
|
|
|
835
|
+
|
|
836
|
+
|
|
413
837
|
const subAst = subQb.getAST();
|
|
838
|
+
|
|
414
839
|
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
|
|
840
|
+
|
|
415
841
|
return this.where(exists(finalSubAst));
|
|
842
|
+
|
|
416
843
|
}
|
|
417
844
|
|
|
845
|
+
|
|
846
|
+
|
|
418
847
|
/**
|
|
848
|
+
|
|
419
849
|
* Adds a WHERE NOT EXISTS condition based on a relationship
|
|
850
|
+
|
|
420
851
|
* @param relationName - Name of the relationship to check
|
|
852
|
+
|
|
421
853
|
* @param callback - Optional callback to modify the relationship query
|
|
854
|
+
|
|
422
855
|
* @returns New query builder instance with the relationship non-existence check
|
|
856
|
+
|
|
423
857
|
*/
|
|
858
|
+
|
|
424
859
|
whereHasNot(
|
|
860
|
+
|
|
425
861
|
relationName: string,
|
|
862
|
+
|
|
426
863
|
callback?: <TChildTable extends TableDef>(
|
|
864
|
+
|
|
427
865
|
qb: SelectQueryBuilder<any, TChildTable>
|
|
866
|
+
|
|
428
867
|
) => SelectQueryBuilder<any, TChildTable>
|
|
868
|
+
|
|
429
869
|
): SelectQueryBuilder<T, TTable> {
|
|
870
|
+
|
|
430
871
|
const relation = this.env.table.relations[relationName];
|
|
872
|
+
|
|
431
873
|
if (!relation) {
|
|
874
|
+
|
|
432
875
|
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
876
|
+
|
|
433
877
|
}
|
|
434
878
|
|
|
879
|
+
|
|
880
|
+
|
|
435
881
|
let subQb = this.createChildBuilder<any, typeof relation.target>(relation.target);
|
|
882
|
+
|
|
436
883
|
if (callback) {
|
|
884
|
+
|
|
437
885
|
subQb = callback(subQb);
|
|
886
|
+
|
|
438
887
|
}
|
|
439
888
|
|
|
889
|
+
|
|
890
|
+
|
|
440
891
|
const subAst = subQb.getAST();
|
|
892
|
+
|
|
441
893
|
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
|
|
894
|
+
|
|
442
895
|
return this.where(notExists(finalSubAst));
|
|
896
|
+
|
|
443
897
|
}
|
|
444
898
|
|
|
899
|
+
|
|
900
|
+
|
|
445
901
|
/**
|
|
902
|
+
|
|
446
903
|
* Compiles the query to SQL for a specific dialect
|
|
904
|
+
|
|
447
905
|
* @param dialect - Database dialect to compile for
|
|
906
|
+
|
|
448
907
|
* @returns Compiled query with SQL and parameters
|
|
908
|
+
|
|
449
909
|
*/
|
|
910
|
+
|
|
450
911
|
compile(dialect: SelectDialectInput): CompiledQuery {
|
|
912
|
+
|
|
451
913
|
const resolved = resolveDialectInput(dialect);
|
|
914
|
+
|
|
452
915
|
return resolved.compileSelect(this.context.state.ast);
|
|
916
|
+
|
|
453
917
|
}
|
|
454
918
|
|
|
919
|
+
|
|
920
|
+
|
|
455
921
|
/**
|
|
922
|
+
|
|
456
923
|
* Converts the query to SQL string for a specific dialect
|
|
924
|
+
|
|
457
925
|
* @param dialect - Database dialect to generate SQL for
|
|
926
|
+
|
|
458
927
|
* @returns SQL string representation of the query
|
|
928
|
+
|
|
459
929
|
*/
|
|
930
|
+
|
|
460
931
|
toSql(dialect: SelectDialectInput): string {
|
|
932
|
+
|
|
461
933
|
return this.compile(dialect).sql;
|
|
934
|
+
|
|
462
935
|
}
|
|
463
936
|
|
|
937
|
+
|
|
938
|
+
|
|
464
939
|
/**
|
|
940
|
+
|
|
465
941
|
* Gets the hydration plan for the query
|
|
942
|
+
|
|
466
943
|
* @returns Hydration plan or undefined if none exists
|
|
944
|
+
|
|
467
945
|
*/
|
|
946
|
+
|
|
468
947
|
getHydrationPlan(): HydrationPlan | undefined {
|
|
948
|
+
|
|
469
949
|
return this.context.hydration.getPlan();
|
|
950
|
+
|
|
470
951
|
}
|
|
471
952
|
|
|
953
|
+
|
|
954
|
+
|
|
472
955
|
/**
|
|
956
|
+
|
|
473
957
|
* Gets the Abstract Syntax Tree (AST) representation of the query
|
|
958
|
+
|
|
474
959
|
* @returns Query AST with hydration applied
|
|
960
|
+
|
|
475
961
|
*/
|
|
962
|
+
|
|
476
963
|
getAST(): SelectQueryNode {
|
|
964
|
+
|
|
477
965
|
return this.context.hydration.applyToAst(this.context.state.ast);
|
|
966
|
+
|
|
478
967
|
}
|
|
968
|
+
|
|
479
969
|
}
|
|
480
970
|
|
|
971
|
+
|
|
972
|
+
|
|
481
973
|
/**
|
|
974
|
+
|
|
482
975
|
* Creates a column node for use in expressions
|
|
976
|
+
|
|
483
977
|
* @param table - Table name
|
|
978
|
+
|
|
484
979
|
* @param name - Column name
|
|
980
|
+
|
|
485
981
|
* @returns ColumnNode with the specified table and name
|
|
982
|
+
|
|
486
983
|
*/
|
|
984
|
+
|
|
487
985
|
export const createColumn = (table: string, name: string): ColumnNode => ({ type: 'Column', table, name });
|
|
488
986
|
|
|
987
|
+
|
|
988
|
+
|
|
489
989
|
/**
|
|
990
|
+
|
|
490
991
|
* Creates a literal value node for use in expressions
|
|
992
|
+
|
|
491
993
|
* @param val - Literal value (string or number)
|
|
994
|
+
|
|
492
995
|
* @returns LiteralNode with the specified value
|
|
996
|
+
|
|
493
997
|
*/
|
|
998
|
+
|
|
494
999
|
export const createLiteral = (val: string | number): LiteralNode => ({ type: 'Literal', value: val });
|
|
1000
|
+
|