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.
- package/README.md +195 -37
- package/dist/index.cjs +1014 -538
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1267 -371
- package/dist/index.d.ts +1267 -371
- package/dist/index.js +1012 -536
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/scripts/run-eslint.mjs +34 -0
- package/src/codegen/typescript.ts +32 -15
- package/src/core/ast/adapters.ts +8 -2
- package/src/core/ast/builders.ts +105 -76
- package/src/core/ast/expression-builders.ts +430 -392
- package/src/core/ast/expression-nodes.ts +14 -5
- package/src/core/ast/expression-visitor.ts +56 -14
- package/src/core/ast/helpers.ts +23 -0
- package/src/core/ast/join-node.ts +18 -2
- package/src/core/ast/query.ts +6 -6
- package/src/core/ast/window-functions.ts +10 -2
- package/src/core/ddl/dialects/base-schema-dialect.ts +37 -4
- package/src/core/ddl/dialects/index.ts +1 -0
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +5 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +3 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +14 -1
- package/src/core/ddl/dialects/render-reference.test.ts +69 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +10 -0
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +2 -0
- package/src/core/ddl/introspect/context.ts +6 -0
- package/src/core/ddl/introspect/functions/postgres.ts +13 -0
- package/src/core/ddl/introspect/mssql.ts +53 -8
- package/src/core/ddl/introspect/mysql.ts +32 -6
- package/src/core/ddl/introspect/postgres.ts +102 -34
- package/src/core/ddl/introspect/registry.ts +14 -0
- package/src/core/ddl/introspect/run-select.ts +19 -4
- package/src/core/ddl/introspect/sqlite.ts +78 -11
- package/src/core/ddl/introspect/types.ts +0 -1
- package/src/core/ddl/introspect/utils.ts +21 -3
- package/src/core/ddl/naming-strategy.ts +6 -0
- package/src/core/ddl/schema-dialect.ts +20 -6
- package/src/core/ddl/schema-diff.ts +22 -0
- package/src/core/ddl/schema-generator.ts +26 -12
- package/src/core/ddl/schema-plan-executor.ts +6 -0
- package/src/core/ddl/schema-types.ts +6 -0
- package/src/core/ddl/sql-writing.ts +4 -4
- package/src/core/dialect/abstract.ts +19 -7
- package/src/core/dialect/base/function-table-formatter.ts +3 -2
- package/src/core/dialect/base/join-compiler.ts +5 -3
- package/src/core/dialect/base/returning-strategy.ts +1 -0
- package/src/core/dialect/base/sql-dialect.ts +3 -3
- package/src/core/dialect/mssql/functions.ts +24 -25
- package/src/core/dialect/mssql/index.ts +1 -4
- package/src/core/dialect/mysql/functions.ts +0 -1
- package/src/core/dialect/postgres/functions.ts +33 -34
- package/src/core/dialect/postgres/index.ts +1 -0
- package/src/core/dialect/sqlite/functions.ts +18 -19
- package/src/core/dialect/sqlite/index.ts +2 -0
- package/src/core/execution/db-executor.ts +1 -1
- package/src/core/execution/executors/mysql-executor.ts +2 -2
- package/src/core/execution/executors/postgres-executor.ts +1 -1
- package/src/core/execution/pooling/pool.ts +12 -5
- package/src/core/functions/datetime.ts +58 -34
- package/src/core/functions/numeric.ts +96 -31
- package/src/core/functions/standard-strategy.ts +35 -0
- package/src/core/functions/text.ts +84 -23
- package/src/core/functions/types.ts +23 -8
- package/src/decorators/bootstrap.ts +42 -11
- package/src/decorators/column.ts +20 -11
- package/src/decorators/decorator-metadata.ts +30 -9
- package/src/decorators/entity.ts +29 -5
- package/src/decorators/index.ts +3 -0
- package/src/decorators/relations.ts +34 -11
- package/src/orm/als.ts +34 -9
- package/src/orm/entity-context.ts +62 -8
- package/src/orm/entity-meta.ts +8 -8
- package/src/orm/entity-metadata.ts +131 -16
- package/src/orm/entity.ts +28 -29
- package/src/orm/execute.ts +19 -4
- package/src/orm/hydration.ts +42 -39
- package/src/orm/identity-map.ts +1 -1
- package/src/orm/lazy-batch.ts +74 -104
- package/src/orm/orm-session.ts +24 -23
- package/src/orm/orm.ts +2 -5
- package/src/orm/relation-change-processor.ts +12 -11
- package/src/orm/relations/belongs-to.ts +11 -11
- package/src/orm/relations/has-many.ts +54 -10
- package/src/orm/relations/has-one.ts +8 -7
- package/src/orm/relations/many-to-many.ts +13 -13
- package/src/orm/runtime-types.ts +4 -4
- package/src/orm/save-graph.ts +31 -25
- package/src/orm/unit-of-work.ts +17 -17
- package/src/query/index.ts +74 -0
- package/src/query/target.ts +46 -0
- package/src/query-builder/delete-query-state.ts +30 -0
- package/src/query-builder/delete.ts +64 -18
- package/src/query-builder/hydration-manager.ts +52 -5
- package/src/query-builder/insert-query-state.ts +30 -0
- package/src/query-builder/insert.ts +58 -10
- package/src/query-builder/query-ast-service.ts +7 -2
- package/src/query-builder/query-resolution.ts +78 -0
- package/src/query-builder/raw-column-parser.ts +7 -1
- package/src/query-builder/relation-alias.ts +7 -0
- package/src/query-builder/relation-conditions.ts +61 -48
- package/src/query-builder/relation-service.ts +68 -63
- package/src/query-builder/relation-utils.ts +3 -0
- package/src/query-builder/select/cte-facet.ts +40 -0
- package/src/query-builder/select/from-facet.ts +80 -0
- package/src/query-builder/select/join-facet.ts +62 -0
- package/src/query-builder/select/predicate-facet.ts +103 -0
- package/src/query-builder/select/projection-facet.ts +69 -0
- package/src/query-builder/select/relation-facet.ts +81 -0
- package/src/query-builder/select/setop-facet.ts +36 -0
- package/src/query-builder/select-helpers.ts +15 -2
- package/src/query-builder/select-query-builder-deps.ts +19 -1
- package/src/query-builder/select-query-state.ts +2 -1
- package/src/query-builder/select.ts +795 -1163
- package/src/query-builder/update-query-state.ts +52 -0
- package/src/query-builder/update.ts +69 -18
- package/src/schema/column.ts +26 -26
- package/src/schema/table-guards.ts +31 -0
- package/src/schema/table.ts +47 -18
- 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
|
-
)
|
|
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(
|
|
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
|
+
}
|