metal-orm 1.0.42 → 1.0.44

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 (122) hide show
  1. package/README.md +195 -37
  2. package/dist/index.cjs +1014 -538
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +1267 -371
  5. package/dist/index.d.ts +1267 -371
  6. package/dist/index.js +1012 -536
  7. package/dist/index.js.map +1 -1
  8. package/package.json +8 -2
  9. package/scripts/run-eslint.mjs +34 -0
  10. package/src/codegen/typescript.ts +32 -15
  11. package/src/core/ast/adapters.ts +8 -2
  12. package/src/core/ast/builders.ts +105 -76
  13. package/src/core/ast/expression-builders.ts +430 -392
  14. package/src/core/ast/expression-nodes.ts +14 -5
  15. package/src/core/ast/expression-visitor.ts +56 -14
  16. package/src/core/ast/helpers.ts +23 -0
  17. package/src/core/ast/join-node.ts +18 -2
  18. package/src/core/ast/query.ts +6 -6
  19. package/src/core/ast/window-functions.ts +10 -2
  20. package/src/core/ddl/dialects/base-schema-dialect.ts +37 -4
  21. package/src/core/ddl/dialects/index.ts +1 -0
  22. package/src/core/ddl/dialects/mssql-schema-dialect.ts +5 -0
  23. package/src/core/ddl/dialects/mysql-schema-dialect.ts +3 -0
  24. package/src/core/ddl/dialects/postgres-schema-dialect.ts +14 -1
  25. package/src/core/ddl/dialects/render-reference.test.ts +69 -0
  26. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +10 -0
  27. package/src/core/ddl/introspect/catalogs/index.ts +1 -0
  28. package/src/core/ddl/introspect/catalogs/postgres.ts +2 -0
  29. package/src/core/ddl/introspect/context.ts +6 -0
  30. package/src/core/ddl/introspect/functions/postgres.ts +13 -0
  31. package/src/core/ddl/introspect/mssql.ts +53 -8
  32. package/src/core/ddl/introspect/mysql.ts +32 -6
  33. package/src/core/ddl/introspect/postgres.ts +102 -34
  34. package/src/core/ddl/introspect/registry.ts +14 -0
  35. package/src/core/ddl/introspect/run-select.ts +19 -4
  36. package/src/core/ddl/introspect/sqlite.ts +78 -11
  37. package/src/core/ddl/introspect/types.ts +0 -1
  38. package/src/core/ddl/introspect/utils.ts +21 -3
  39. package/src/core/ddl/naming-strategy.ts +6 -0
  40. package/src/core/ddl/schema-dialect.ts +20 -6
  41. package/src/core/ddl/schema-diff.ts +22 -0
  42. package/src/core/ddl/schema-generator.ts +26 -12
  43. package/src/core/ddl/schema-plan-executor.ts +6 -0
  44. package/src/core/ddl/schema-types.ts +6 -0
  45. package/src/core/ddl/sql-writing.ts +4 -4
  46. package/src/core/dialect/abstract.ts +19 -7
  47. package/src/core/dialect/base/function-table-formatter.ts +3 -2
  48. package/src/core/dialect/base/join-compiler.ts +5 -3
  49. package/src/core/dialect/base/returning-strategy.ts +1 -0
  50. package/src/core/dialect/base/sql-dialect.ts +3 -3
  51. package/src/core/dialect/mssql/functions.ts +24 -25
  52. package/src/core/dialect/mssql/index.ts +1 -4
  53. package/src/core/dialect/mysql/functions.ts +0 -1
  54. package/src/core/dialect/postgres/functions.ts +33 -34
  55. package/src/core/dialect/postgres/index.ts +1 -0
  56. package/src/core/dialect/sqlite/functions.ts +18 -19
  57. package/src/core/dialect/sqlite/index.ts +2 -0
  58. package/src/core/execution/db-executor.ts +1 -1
  59. package/src/core/execution/executors/mysql-executor.ts +2 -2
  60. package/src/core/execution/executors/postgres-executor.ts +1 -1
  61. package/src/core/execution/pooling/pool.ts +12 -5
  62. package/src/core/functions/datetime.ts +58 -34
  63. package/src/core/functions/numeric.ts +96 -31
  64. package/src/core/functions/standard-strategy.ts +35 -0
  65. package/src/core/functions/text.ts +84 -23
  66. package/src/core/functions/types.ts +23 -8
  67. package/src/decorators/bootstrap.ts +42 -11
  68. package/src/decorators/column.ts +20 -11
  69. package/src/decorators/decorator-metadata.ts +30 -9
  70. package/src/decorators/entity.ts +29 -5
  71. package/src/decorators/index.ts +3 -0
  72. package/src/decorators/relations.ts +34 -11
  73. package/src/orm/als.ts +34 -9
  74. package/src/orm/entity-context.ts +62 -8
  75. package/src/orm/entity-meta.ts +8 -8
  76. package/src/orm/entity-metadata.ts +131 -16
  77. package/src/orm/entity.ts +28 -29
  78. package/src/orm/execute.ts +19 -4
  79. package/src/orm/hydration.ts +42 -39
  80. package/src/orm/identity-map.ts +1 -1
  81. package/src/orm/lazy-batch.ts +74 -104
  82. package/src/orm/orm-session.ts +24 -23
  83. package/src/orm/orm.ts +2 -5
  84. package/src/orm/relation-change-processor.ts +12 -11
  85. package/src/orm/relations/belongs-to.ts +11 -11
  86. package/src/orm/relations/has-many.ts +54 -10
  87. package/src/orm/relations/has-one.ts +8 -7
  88. package/src/orm/relations/many-to-many.ts +13 -13
  89. package/src/orm/runtime-types.ts +4 -4
  90. package/src/orm/save-graph.ts +31 -25
  91. package/src/orm/unit-of-work.ts +17 -17
  92. package/src/query/index.ts +74 -0
  93. package/src/query/target.ts +46 -0
  94. package/src/query-builder/delete-query-state.ts +30 -0
  95. package/src/query-builder/delete.ts +64 -18
  96. package/src/query-builder/hydration-manager.ts +52 -5
  97. package/src/query-builder/insert-query-state.ts +30 -0
  98. package/src/query-builder/insert.ts +58 -10
  99. package/src/query-builder/query-ast-service.ts +7 -2
  100. package/src/query-builder/query-resolution.ts +78 -0
  101. package/src/query-builder/raw-column-parser.ts +7 -1
  102. package/src/query-builder/relation-alias.ts +7 -0
  103. package/src/query-builder/relation-conditions.ts +61 -48
  104. package/src/query-builder/relation-service.ts +68 -63
  105. package/src/query-builder/relation-utils.ts +3 -0
  106. package/src/query-builder/select/cte-facet.ts +40 -0
  107. package/src/query-builder/select/from-facet.ts +80 -0
  108. package/src/query-builder/select/join-facet.ts +62 -0
  109. package/src/query-builder/select/predicate-facet.ts +103 -0
  110. package/src/query-builder/select/projection-facet.ts +69 -0
  111. package/src/query-builder/select/relation-facet.ts +81 -0
  112. package/src/query-builder/select/setop-facet.ts +36 -0
  113. package/src/query-builder/select-helpers.ts +15 -2
  114. package/src/query-builder/select-query-builder-deps.ts +19 -1
  115. package/src/query-builder/select-query-state.ts +2 -1
  116. package/src/query-builder/select.ts +795 -1163
  117. package/src/query-builder/update-query-state.ts +52 -0
  118. package/src/query-builder/update.ts +69 -18
  119. package/src/schema/column.ts +26 -26
  120. package/src/schema/table-guards.ts +31 -0
  121. package/src/schema/table.ts +47 -18
  122. package/src/schema/types.ts +22 -22
@@ -20,8 +20,8 @@ import {
20
20
  } from './relation-conditions.js';
21
21
  import { JoinKind, JOIN_KINDS } from '../core/sql/sql.js';
22
22
  import { RelationIncludeOptions } from './relation-types.js';
23
- import { createJoinNode } from '../core/ast/join-node.js';
24
- import { getJoinRelationName } from '../core/ast/join-metadata.js';
23
+ import { createJoinNode } from '../core/ast/join-node.js';
24
+ import { getJoinRelationName } from '../core/ast/join-metadata.js';
25
25
  import { makeRelationAlias } from './relation-alias.js';
26
26
  import { buildDefaultPivotColumns } from './relation-utils.js';
27
27
 
@@ -70,17 +70,17 @@ export class RelationService {
70
70
  * @param predicate - Optional predicate expression
71
71
  * @returns Relation result with updated state and hydration
72
72
  */
73
- match(
74
- relationName: string,
75
- predicate?: ExpressionNode
76
- ): RelationResult {
77
- const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
78
- const pk = findPrimaryKey(this.table);
79
- const distinctCols: ColumnNode[] = [{ type: 'Column', table: this.rootTableName(), name: pk }];
80
- const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
81
- const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
82
- return { state: nextState, hydration: joined.hydration };
83
- }
73
+ match(
74
+ relationName: string,
75
+ predicate?: ExpressionNode
76
+ ): RelationResult {
77
+ const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
78
+ const pk = findPrimaryKey(this.table);
79
+ const distinctCols: ColumnNode[] = [{ type: 'Column', table: this.rootTableName(), name: pk }];
80
+ const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
81
+ const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
82
+ return { state: nextState, hydration: joined.hydration };
83
+ }
84
84
 
85
85
  /**
86
86
  * Includes a relation in the query result
@@ -94,7 +94,7 @@ export class RelationService {
94
94
 
95
95
  const relation = this.getRelation(relationName);
96
96
  const aliasPrefix = options?.aliasPrefix ?? relationName;
97
- const alreadyJoined = state.ast.joins.some(j => getJoinRelationName(j) === relationName);
97
+ const alreadyJoined = state.ast.joins.some(j => getJoinRelationName(j) === relationName);
98
98
 
99
99
  if (!alreadyJoined) {
100
100
  const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
@@ -114,7 +114,7 @@ export class RelationService {
114
114
  prefix: string,
115
115
  keys: string[],
116
116
  missingMsg: (col: string) => string
117
- ) : Record<string, ColumnDef> => {
117
+ ): Record<string, ColumnDef> => {
118
118
  return keys.reduce((acc, key) => {
119
119
  const def = columns[key];
120
120
  if (!def) {
@@ -190,22 +190,22 @@ export class RelationService {
190
190
  * @param ast - Query AST to modify
191
191
  * @returns Modified query AST with relation correlation
192
192
  */
193
- applyRelationCorrelation(
194
- relationName: string,
195
- ast: SelectQueryNode,
196
- additionalCorrelation?: ExpressionNode
197
- ): SelectQueryNode {
198
- const relation = this.getRelation(relationName);
199
- const rootAlias = this.state.ast.from.type === 'Table' ? this.state.ast.from.alias : undefined;
200
- let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
201
- if (additionalCorrelation) {
202
- correlation = and(correlation, additionalCorrelation);
203
- }
204
- const whereInSubquery = ast.where
205
- ? and(correlation, ast.where)
206
- : correlation;
207
-
208
- return {
193
+ applyRelationCorrelation(
194
+ relationName: string,
195
+ ast: SelectQueryNode,
196
+ additionalCorrelation?: ExpressionNode
197
+ ): SelectQueryNode {
198
+ const relation = this.getRelation(relationName);
199
+ const rootAlias = this.state.ast.from.type === 'Table' ? this.state.ast.from.alias : undefined;
200
+ let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
201
+ if (additionalCorrelation) {
202
+ correlation = and(correlation, additionalCorrelation);
203
+ }
204
+ const whereInSubquery = ast.where
205
+ ? and(correlation, ast.where)
206
+ : correlation;
207
+
208
+ return {
209
209
  ...ast,
210
210
  where: whereInSubquery
211
211
  };
@@ -219,28 +219,33 @@ export class RelationService {
219
219
  * @param extraCondition - Additional join condition
220
220
  * @returns Updated query state with join
221
221
  */
222
- private withJoin(
223
- state: SelectQueryState,
224
- relationName: string,
225
- joinKind: JoinKind,
226
- extraCondition?: ExpressionNode
227
- ): SelectQueryState {
228
- const relation = this.getRelation(relationName);
229
- const rootAlias = state.ast.from.type === 'Table' ? state.ast.from.alias : undefined;
230
- if (relation.type === RelationKinds.BelongsToMany) {
231
- const joins = buildBelongsToManyJoins(
232
- this.table,
233
- relationName,
234
- relation as BelongsToManyRelation,
235
- joinKind,
236
- extraCondition,
237
- rootAlias
238
- );
239
- return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
240
- }
241
-
242
- const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
243
- const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
222
+ private withJoin(
223
+ state: SelectQueryState,
224
+ relationName: string,
225
+ joinKind: JoinKind,
226
+ extraCondition?: ExpressionNode
227
+ ): SelectQueryState {
228
+ const relation = this.getRelation(relationName);
229
+ const rootAlias = state.ast.from.type === 'Table' ? state.ast.from.alias : undefined;
230
+ if (relation.type === RelationKinds.BelongsToMany) {
231
+ const joins = buildBelongsToManyJoins(
232
+ this.table,
233
+ relationName,
234
+ relation as BelongsToManyRelation,
235
+ joinKind,
236
+ extraCondition,
237
+ rootAlias
238
+ );
239
+ return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
240
+ }
241
+
242
+ const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
243
+ const joinNode = createJoinNode(
244
+ joinKind,
245
+ { type: 'Table', name: relation.target.name, schema: relation.target.schema },
246
+ condition,
247
+ relationName
248
+ );
244
249
 
245
250
  return this.astService(state).withJoin(joinNode);
246
251
  }
@@ -284,15 +289,15 @@ export class RelationService {
284
289
  * @param state - Current query state
285
290
  * @returns QueryAstService instance
286
291
  */
287
- private astService(state: SelectQueryState = this.state): QueryAstService {
288
- return this.createQueryAstService(this.table, state);
289
- }
290
-
291
- private rootTableName(): string {
292
- const from = this.state.ast.from;
293
- if (from.type === 'Table' && from.alias) return from.alias;
294
- return this.table.name;
295
- }
296
- }
292
+ private astService(state: SelectQueryState = this.state): QueryAstService {
293
+ return this.createQueryAstService(this.table, state);
294
+ }
295
+
296
+ private rootTableName(): string {
297
+ const from = this.state.ast.from;
298
+ if (from.type === 'Table' && from.alias) return from.alias;
299
+ return this.table.name;
300
+ }
301
+ }
297
302
 
298
303
  export type { RelationResult } from './relation-projection-helper.js';
@@ -2,6 +2,9 @@ import { BelongsToManyRelation } from '../schema/relation.js';
2
2
 
3
3
  /**
4
4
  * Builds a default set of pivot columns, excluding keys used for joins.
5
+ * @param rel - The BelongsToMany relation definition
6
+ * @param pivotPk - The primary key column name of the pivot table
7
+ * @returns Array of column names that can be included in pivot table selections
5
8
  */
6
9
  export const buildDefaultPivotColumns = (
7
10
  rel: BelongsToManyRelation,
@@ -0,0 +1,40 @@
1
+ import { SelectQueryNode } from '../../core/ast/query.js';
2
+ import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps.js';
3
+ import { QueryAstService } from '../query-ast-service.js';
4
+ import { SelectQueryState } from '../select-query-state.js';
5
+
6
+ /**
7
+ * Facet responsible for Common Table Expressions (WITH clauses)
8
+ */
9
+ export class SelectCTEFacet {
10
+ /**
11
+ * Creates a new SelectCTEFacet instance
12
+ * @param env - Query builder environment
13
+ * @param createAstService - Function to create AST service
14
+ */
15
+ constructor(
16
+ private readonly env: SelectQueryBuilderEnvironment,
17
+ private readonly createAstService: (state: SelectQueryState) => QueryAstService
18
+ ) { }
19
+
20
+ /**
21
+ * Adds a Common Table Expression to the query
22
+ * @param context - Current query context
23
+ * @param name - CTE name
24
+ * @param subAst - CTE query AST
25
+ * @param columns - Optional column names
26
+ * @param recursive - Whether the CTE is recursive
27
+ * @returns Updated query context with CTE
28
+ */
29
+ withCTE(
30
+ context: SelectQueryBuilderContext,
31
+ name: string,
32
+ subAst: SelectQueryNode,
33
+ columns: string[] | undefined,
34
+ recursive: boolean
35
+ ): SelectQueryBuilderContext {
36
+ const astService = this.createAstService(context.state);
37
+ const nextState = astService.withCte(name, subAst, columns, recursive);
38
+ return { state: nextState, hydration: context.hydration };
39
+ }
40
+ }
@@ -0,0 +1,80 @@
1
+ import { SelectQueryNode } from '../../core/ast/query.js';
2
+ import { OperandNode } from '../../core/ast/expression.js';
3
+ import { derivedTable, fnTable } from '../../core/ast/builders.js';
4
+ import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps.js';
5
+ import { QueryAstService } from '../query-ast-service.js';
6
+ import { SelectQueryState } from '../select-query-state.js';
7
+
8
+ /**
9
+ * Facet responsible for FROM clause operations
10
+ */
11
+ export class SelectFromFacet {
12
+ /**
13
+ * Creates a new SelectFromFacet instance
14
+ * @param env - Query builder environment
15
+ * @param createAstService - Function to create AST service
16
+ */
17
+ constructor(
18
+ private readonly env: SelectQueryBuilderEnvironment,
19
+ private readonly createAstService: (state: SelectQueryState) => QueryAstService
20
+ ) { }
21
+
22
+ /**
23
+ * Applies an alias to the FROM table
24
+ * @param context - Current query context
25
+ * @param alias - Alias to apply
26
+ * @returns Updated query context with aliased FROM
27
+ */
28
+ as(context: SelectQueryBuilderContext, alias: string): SelectQueryBuilderContext {
29
+ const from = context.state.ast.from;
30
+ if (from.type !== 'Table') {
31
+ throw new Error('Cannot alias non-table FROM sources');
32
+ }
33
+ const nextFrom = { ...from, alias };
34
+ const astService = this.createAstService(context.state);
35
+ const nextState = astService.withFrom(nextFrom);
36
+ return { state: nextState, hydration: context.hydration };
37
+ }
38
+
39
+ /**
40
+ * Sets the FROM clause to a subquery
41
+ * @param context - Current query context
42
+ * @param subAst - Subquery AST
43
+ * @param alias - Alias for the subquery
44
+ * @param columnAliases - Optional column aliases
45
+ * @returns Updated query context with subquery FROM
46
+ */
47
+ fromSubquery(
48
+ context: SelectQueryBuilderContext,
49
+ subAst: SelectQueryNode,
50
+ alias: string,
51
+ columnAliases?: string[]
52
+ ): SelectQueryBuilderContext {
53
+ const fromNode = derivedTable(subAst, alias, columnAliases);
54
+ const astService = this.createAstService(context.state);
55
+ const nextState = astService.withFrom(fromNode);
56
+ return { state: nextState, hydration: context.hydration };
57
+ }
58
+
59
+ /**
60
+ * Sets the FROM clause to a function table
61
+ * @param context - Current query context
62
+ * @param name - Function name
63
+ * @param args - Function arguments
64
+ * @param alias - Optional alias for the function table
65
+ * @param options - Optional function table options
66
+ * @returns Updated query context with function table FROM
67
+ */
68
+ fromFunctionTable(
69
+ context: SelectQueryBuilderContext,
70
+ name: string,
71
+ args: OperandNode[],
72
+ alias?: string,
73
+ options?: { lateral?: boolean; withOrdinality?: boolean; columnAliases?: string[]; schema?: string }
74
+ ): SelectQueryBuilderContext {
75
+ const functionTable = fnTable(name, args, alias, options);
76
+ const astService = this.createAstService(context.state);
77
+ const nextState = astService.withFrom(functionTable);
78
+ return { state: nextState, hydration: context.hydration };
79
+ }
80
+ }
@@ -0,0 +1,62 @@
1
+ import { TableDef } from '../../schema/table.js';
2
+ import { BinaryExpressionNode } from '../../core/ast/expression.js';
3
+ import { SelectQueryNode } from '../../core/ast/query.js';
4
+ import { JoinKind } from '../../core/sql/sql.js';
5
+ import { derivedTable, fnTable } from '../../core/ast/builders.js';
6
+ import { createJoinNode } from '../../core/ast/join-node.js';
7
+ import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps.js';
8
+ import { QueryAstService } from '../query-ast-service.js';
9
+ import { OperandNode } from '../../core/ast/expression.js';
10
+ import { SelectQueryState } from '../select-query-state.js';
11
+
12
+ /**
13
+ * Facet responsible for JOIN operations
14
+ */
15
+ export class SelectJoinFacet {
16
+ constructor(
17
+ private readonly env: SelectQueryBuilderEnvironment,
18
+ private readonly createAstService: (state: SelectQueryState) => QueryAstService
19
+ ) { }
20
+
21
+ applyJoin(
22
+ context: SelectQueryBuilderContext,
23
+ table: TableDef,
24
+ condition: BinaryExpressionNode,
25
+ kind: JoinKind
26
+ ): SelectQueryBuilderContext {
27
+ const joinNode = createJoinNode(kind, { type: 'Table', name: table.name, schema: table.schema }, condition);
28
+ const astService = this.createAstService(context.state);
29
+ const nextState = astService.withJoin(joinNode);
30
+ return { state: nextState, hydration: context.hydration };
31
+ }
32
+
33
+ joinSubquery(
34
+ context: SelectQueryBuilderContext,
35
+ subAst: SelectQueryNode,
36
+ alias: string,
37
+ condition: BinaryExpressionNode,
38
+ joinKind: JoinKind,
39
+ columnAliases?: string[]
40
+ ): SelectQueryBuilderContext {
41
+ const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
42
+ const astService = this.createAstService(context.state);
43
+ const nextState = astService.withJoin(joinNode);
44
+ return { state: nextState, hydration: context.hydration };
45
+ }
46
+
47
+ joinFunctionTable(
48
+ context: SelectQueryBuilderContext,
49
+ name: string,
50
+ args: OperandNode[],
51
+ alias: string,
52
+ condition: BinaryExpressionNode,
53
+ joinKind: JoinKind,
54
+ options?: { lateral?: boolean; withOrdinality?: boolean; columnAliases?: string[]; schema?: string }
55
+ ): SelectQueryBuilderContext {
56
+ const functionTable = fnTable(name, args, alias, options);
57
+ const joinNode = createJoinNode(joinKind, functionTable, condition);
58
+ const astService = this.createAstService(context.state);
59
+ const nextState = astService.withJoin(joinNode);
60
+ return { state: nextState, hydration: context.hydration };
61
+ }
62
+ }
@@ -0,0 +1,103 @@
1
+ import { ColumnDef } from '../../schema/column.js';
2
+ import { ExpressionNode } from '../../core/ast/expression.js';
3
+ import { OrderingTerm } from '../../core/ast/query.js';
4
+ import { OrderDirection } from '../../core/sql/sql.js';
5
+ import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps.js';
6
+ import { QueryAstService } from '../query-ast-service.js';
7
+ import { SelectQueryState } from '../select-query-state.js';
8
+
9
+ /**
10
+ * Facet responsible for filtering and ordering operations
11
+ */
12
+ export class SelectPredicateFacet {
13
+ /**
14
+ * Creates a new SelectPredicateFacet instance
15
+ * @param env - Query builder environment
16
+ * @param createAstService - Function to create AST service
17
+ */
18
+ constructor(
19
+ private readonly env: SelectQueryBuilderEnvironment,
20
+ private readonly createAstService: (state: SelectQueryState) => QueryAstService
21
+ ) { }
22
+
23
+ /**
24
+ * Adds a WHERE condition to the query
25
+ * @param context - Current query context
26
+ * @param expr - WHERE expression
27
+ * @returns Updated query context with WHERE condition
28
+ */
29
+ where(context: SelectQueryBuilderContext, expr: ExpressionNode): SelectQueryBuilderContext {
30
+ const astService = this.createAstService(context.state);
31
+ const nextState = astService.withWhere(expr);
32
+ return { state: nextState, hydration: context.hydration };
33
+ }
34
+
35
+ /**
36
+ * Adds a GROUP BY clause to the query
37
+ * @param context - Current query context
38
+ * @param term - Column or ordering term to group by
39
+ * @returns Updated query context with GROUP BY clause
40
+ */
41
+ groupBy(context: SelectQueryBuilderContext, term: ColumnDef | OrderingTerm): SelectQueryBuilderContext {
42
+ const astService = this.createAstService(context.state);
43
+ const nextState = astService.withGroupBy(term);
44
+ return { state: nextState, hydration: context.hydration };
45
+ }
46
+
47
+ /**
48
+ * Adds a HAVING condition to the query
49
+ * @param context - Current query context
50
+ * @param expr - HAVING expression
51
+ * @returns Updated query context with HAVING condition
52
+ */
53
+ having(context: SelectQueryBuilderContext, expr: ExpressionNode): SelectQueryBuilderContext {
54
+ const astService = this.createAstService(context.state);
55
+ const nextState = astService.withHaving(expr);
56
+ return { state: nextState, hydration: context.hydration };
57
+ }
58
+
59
+ /**
60
+ * Adds an ORDER BY clause to the query
61
+ * @param context - Current query context
62
+ * @param term - Column or ordering term to order by
63
+ * @param direction - Order direction
64
+ * @param nulls - Nulls ordering
65
+ * @param collation - Collation
66
+ * @returns Updated query context with ORDER BY clause
67
+ */
68
+ orderBy(
69
+ context: SelectQueryBuilderContext,
70
+ term: ColumnDef | OrderingTerm,
71
+ direction: OrderDirection,
72
+ nulls?: 'FIRST' | 'LAST',
73
+ collation?: string
74
+ ): SelectQueryBuilderContext {
75
+ const astService = this.createAstService(context.state);
76
+ const nextState = astService.withOrderBy(term, direction, nulls, collation);
77
+ return { state: nextState, hydration: context.hydration };
78
+ }
79
+
80
+ /**
81
+ * Adds a LIMIT clause to the query
82
+ * @param context - Current query context
83
+ * @param n - Maximum number of rows
84
+ * @returns Updated query context with LIMIT clause
85
+ */
86
+ limit(context: SelectQueryBuilderContext, n: number): SelectQueryBuilderContext {
87
+ const astService = this.createAstService(context.state);
88
+ const nextState = astService.withLimit(n);
89
+ return { state: nextState, hydration: context.hydration };
90
+ }
91
+
92
+ /**
93
+ * Adds an OFFSET clause to the query
94
+ * @param context - Current query context
95
+ * @param n - Number of rows to skip
96
+ * @returns Updated query context with OFFSET clause
97
+ */
98
+ offset(context: SelectQueryBuilderContext, n: number): SelectQueryBuilderContext {
99
+ const astService = this.createAstService(context.state);
100
+ const nextState = astService.withOffset(n);
101
+ return { state: nextState, hydration: context.hydration };
102
+ }
103
+ }
@@ -0,0 +1,69 @@
1
+ import { ColumnDef } from '../../schema/column.js';
2
+ import { ColumnNode, FunctionNode, CaseExpressionNode, WindowFunctionNode } from '../../core/ast/expression.js';
3
+ import { SelectQueryBuilderContext } from '../select-query-builder-deps.js';
4
+ import { ColumnSelector } from '../column-selector.js';
5
+ import { SelectQueryNode } from '../../core/ast/query.js';
6
+
7
+ type ColumnSelectionValue = ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode;
8
+
9
+ /**
10
+ * Facet responsible for projection operations (SELECT, DISTINCT, etc.)
11
+ */
12
+ export class SelectProjectionFacet {
13
+ /**
14
+ * Creates a new SelectProjectionFacet instance
15
+ * @param columnSelector - Column selector dependency
16
+ */
17
+ constructor(private readonly columnSelector: ColumnSelector) { }
18
+
19
+ /**
20
+ * Selects columns for the query
21
+ * @param context - Current query context
22
+ * @param columns - Columns to select
23
+ * @returns Updated query context with selected columns
24
+ */
25
+ select(
26
+ context: SelectQueryBuilderContext,
27
+ columns: Record<string, ColumnSelectionValue>
28
+ ): SelectQueryBuilderContext {
29
+ return { ...context, state: this.columnSelector.select(context, columns).state };
30
+ }
31
+
32
+ /**
33
+ * Selects raw column expressions
34
+ * @param context - Current query context
35
+ * @param cols - Raw column expressions
36
+ * @returns Updated query context with raw column selections
37
+ */
38
+ selectRaw(context: SelectQueryBuilderContext, cols: string[]): SelectQueryBuilderContext {
39
+ return { ...context, state: this.columnSelector.selectRaw(context, cols).state };
40
+ }
41
+
42
+ /**
43
+ * Selects a subquery as a column
44
+ * @param context - Current query context
45
+ * @param alias - Alias for the subquery
46
+ * @param query - Subquery to select
47
+ * @returns Updated query context with subquery selection
48
+ */
49
+ selectSubquery(
50
+ context: SelectQueryBuilderContext,
51
+ alias: string,
52
+ query: SelectQueryNode
53
+ ): SelectQueryBuilderContext {
54
+ return { ...context, state: this.columnSelector.selectSubquery(context, alias, query).state };
55
+ }
56
+
57
+ /**
58
+ * Adds DISTINCT clause to the query
59
+ * @param context - Current query context
60
+ * @param cols - Columns to make distinct
61
+ * @returns Updated query context with DISTINCT clause
62
+ */
63
+ distinct(
64
+ context: SelectQueryBuilderContext,
65
+ cols: (ColumnDef | ColumnNode)[]
66
+ ): SelectQueryBuilderContext {
67
+ return { ...context, state: this.columnSelector.distinct(context, cols).state };
68
+ }
69
+ }
@@ -0,0 +1,81 @@
1
+ import { ExpressionNode } from '../../core/ast/expression.js';
2
+ import { JoinKind } from '../../core/sql/sql.js';
3
+ import { SelectQueryBuilderContext } from '../select-query-builder-deps.js';
4
+ import { RelationManager } from '../relation-manager.js';
5
+ import { RelationIncludeOptions } from '../relation-types.js';
6
+ import { SelectQueryNode } from '../../core/ast/query.js';
7
+
8
+ /**
9
+ * Facet responsible for relation operations (include, match, whereHas, etc.)
10
+ */
11
+ export class SelectRelationFacet {
12
+ /**
13
+ * Creates a new SelectRelationFacet instance
14
+ * @param relationManager - Relation manager dependency
15
+ */
16
+ constructor(private readonly relationManager: RelationManager) { }
17
+
18
+ /**
19
+ * Matches records based on a relationship
20
+ * @param context - Current query context
21
+ * @param relationName - Name of the relationship
22
+ * @param predicate - Optional predicate
23
+ * @returns Updated query context with relation match
24
+ */
25
+ match(
26
+ context: SelectQueryBuilderContext,
27
+ relationName: string,
28
+ predicate?: ExpressionNode
29
+ ): SelectQueryBuilderContext {
30
+ return this.relationManager.match(context, relationName, predicate);
31
+ }
32
+
33
+ /**
34
+ * Joins a related table
35
+ * @param context - Current query context
36
+ * @param relationName - Name of the relationship
37
+ * @param joinKind - Type of join
38
+ * @param extraCondition - Optional additional condition
39
+ * @returns Updated query context with relation join
40
+ */
41
+ joinRelation(
42
+ context: SelectQueryBuilderContext,
43
+ relationName: string,
44
+ joinKind: JoinKind,
45
+ extraCondition?: ExpressionNode
46
+ ): SelectQueryBuilderContext {
47
+ return this.relationManager.joinRelation(context, relationName, joinKind, extraCondition);
48
+ }
49
+
50
+ /**
51
+ * Includes related data in the query results
52
+ * @param context - Current query context
53
+ * @param relationName - Name of the relationship to include
54
+ * @param options - Optional include options
55
+ * @returns Updated query context with relation inclusion
56
+ */
57
+ include(
58
+ context: SelectQueryBuilderContext,
59
+ relationName: string,
60
+ options?: RelationIncludeOptions
61
+ ): SelectQueryBuilderContext {
62
+ return this.relationManager.include(context, relationName, options);
63
+ }
64
+
65
+ /**
66
+ * Applies correlation for relation-based subqueries
67
+ * @param context - Current query context
68
+ * @param relationName - Name of the relationship
69
+ * @param subAst - Subquery AST
70
+ * @param extraCorrelate - Optional additional correlation
71
+ * @returns Modified subquery AST with correlation
72
+ */
73
+ applyRelationCorrelation(
74
+ context: SelectQueryBuilderContext,
75
+ relationName: string,
76
+ subAst: SelectQueryNode,
77
+ extraCorrelate?: ExpressionNode
78
+ ): SelectQueryNode {
79
+ return this.relationManager.applyRelationCorrelation(context, relationName, subAst, extraCorrelate);
80
+ }
81
+ }
@@ -0,0 +1,36 @@
1
+ import { SelectQueryNode, SetOperationKind } from '../../core/ast/query.js';
2
+ import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps.js';
3
+ import { QueryAstService } from '../query-ast-service.js';
4
+ import { SelectQueryState } from '../select-query-state.js';
5
+
6
+ /**
7
+ * Facet responsible for set operations (UNION, INTERSECT, EXCEPT)
8
+ */
9
+ export class SelectSetOpFacet {
10
+ /**
11
+ * Creates a new SelectSetOpFacet instance
12
+ * @param env - Query builder environment
13
+ * @param createAstService - Function to create AST service
14
+ */
15
+ constructor(
16
+ private readonly env: SelectQueryBuilderEnvironment,
17
+ private readonly createAstService: (state: SelectQueryState) => QueryAstService
18
+ ) { }
19
+
20
+ /**
21
+ * Applies a set operation to the query
22
+ * @param context - Current query context
23
+ * @param operator - Set operation kind
24
+ * @param subAst - Subquery AST to combine
25
+ * @returns Updated query context with set operation
26
+ */
27
+ applySetOperation(
28
+ context: SelectQueryBuilderContext,
29
+ operator: SetOperationKind,
30
+ subAst: SelectQueryNode
31
+ ): SelectQueryBuilderContext {
32
+ const astService = this.createAstService(context.state);
33
+ const nextState = astService.withSetOperation(operator, subAst);
34
+ return { state: nextState, hydration: context.hydration };
35
+ }
36
+ }