metal-orm 1.0.2 → 1.0.4
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 +20 -0
- package/package.json +1 -1
- package/src/ast/expression.ts +433 -175
- package/src/ast/join.ts +8 -1
- package/src/ast/query.ts +64 -9
- package/src/builder/hydration-manager.ts +42 -11
- package/src/builder/hydration-planner.ts +80 -31
- package/src/builder/operations/column-selector.ts +37 -1
- package/src/builder/operations/cte-manager.ts +16 -0
- package/src/builder/operations/filter-manager.ts +32 -0
- package/src/builder/operations/join-manager.ts +17 -7
- package/src/builder/operations/pagination-manager.ts +19 -0
- package/src/builder/operations/relation-manager.ts +58 -3
- package/src/builder/query-ast-service.ts +100 -29
- package/src/builder/relation-conditions.ts +30 -1
- package/src/builder/relation-projection-helper.ts +43 -1
- package/src/builder/relation-service.ts +68 -13
- package/src/builder/relation-types.ts +6 -0
- package/src/builder/select-query-builder-deps.ts +64 -3
- package/src/builder/select-query-state.ts +72 -0
- package/src/builder/select.ts +166 -0
- package/src/codegen/typescript.ts +142 -44
- package/src/constants/sql-operator-config.ts +36 -0
- package/src/constants/sql.ts +125 -58
- package/src/dialect/abstract.ts +97 -22
- package/src/dialect/mssql/index.ts +27 -0
- package/src/dialect/mysql/index.ts +22 -0
- package/src/dialect/postgres/index.ts +22 -0
- package/src/dialect/sqlite/index.ts +22 -0
- package/src/runtime/als.ts +15 -1
- package/src/runtime/hydration.ts +20 -15
- package/src/schema/column.ts +45 -5
- package/src/schema/relation.ts +49 -2
- package/src/schema/table.ts +27 -3
- package/src/utils/join-node.ts +20 -0
- package/src/utils/raw-column-parser.ts +32 -0
- package/src/utils/relation-alias.ts +43 -0
package/src/ast/join.ts
CHANGED
|
@@ -2,10 +2,17 @@ import { TableNode } from './query';
|
|
|
2
2
|
import { ExpressionNode } from './expression';
|
|
3
3
|
import { JoinKind } from '../constants/sql';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* AST node representing a JOIN clause
|
|
7
|
+
*/
|
|
5
8
|
export interface JoinNode {
|
|
6
9
|
type: 'Join';
|
|
10
|
+
/** Type of join (INNER, LEFT, RIGHT, etc.) */
|
|
7
11
|
kind: JoinKind;
|
|
12
|
+
/** Table to join */
|
|
8
13
|
table: TableNode;
|
|
14
|
+
/** Join condition expression */
|
|
9
15
|
condition: ExpressionNode;
|
|
10
|
-
|
|
16
|
+
/** Optional relation name for code generation */
|
|
17
|
+
relationName?: string;
|
|
11
18
|
}
|
package/src/ast/query.ts
CHANGED
|
@@ -1,63 +1,118 @@
|
|
|
1
|
-
import { ColumnNode, FunctionNode, ExpressionNode, ScalarSubqueryNode, CaseExpressionNode, WindowFunctionNode } from './expression';
|
|
2
|
-
import { JoinNode } from './join';
|
|
3
|
-
import { RelationType } from '../schema/relation';
|
|
4
|
-
import { OrderDirection } from '../constants/sql';
|
|
1
|
+
import { ColumnNode, FunctionNode, ExpressionNode, ScalarSubqueryNode, CaseExpressionNode, WindowFunctionNode } from './expression';
|
|
2
|
+
import { JoinNode } from './join';
|
|
3
|
+
import { RelationType } from '../schema/relation';
|
|
4
|
+
import { OrderDirection } from '../constants/sql';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* AST node representing a table reference in a query
|
|
8
|
+
*/
|
|
6
9
|
export interface TableNode {
|
|
7
10
|
type: 'Table';
|
|
11
|
+
/** Table name */
|
|
8
12
|
name: string;
|
|
13
|
+
/** Optional schema name */
|
|
9
14
|
schema?: string;
|
|
15
|
+
/** Optional table alias */
|
|
10
16
|
alias?: string;
|
|
11
17
|
}
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
/**
|
|
20
|
+
* AST node representing an ORDER BY clause
|
|
21
|
+
*/
|
|
22
|
+
export interface OrderByNode {
|
|
23
|
+
type: 'OrderBy';
|
|
24
|
+
/** Column to order by */
|
|
25
|
+
column: ColumnNode;
|
|
26
|
+
/** Order direction (ASC or DESC) */
|
|
27
|
+
direction: OrderDirection;
|
|
28
|
+
}
|
|
18
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Plan for hydrating relationship data
|
|
32
|
+
*/
|
|
19
33
|
export interface HydrationRelationPlan {
|
|
34
|
+
/** Name of the relationship */
|
|
20
35
|
name: string;
|
|
36
|
+
/** Alias prefix for the relationship */
|
|
21
37
|
aliasPrefix: string;
|
|
38
|
+
/** Type of relationship */
|
|
22
39
|
type: RelationType;
|
|
40
|
+
/** Target table name */
|
|
23
41
|
targetTable: string;
|
|
42
|
+
/** Target table primary key */
|
|
24
43
|
targetPrimaryKey: string;
|
|
44
|
+
/** Foreign key column */
|
|
25
45
|
foreignKey: string;
|
|
46
|
+
/** Local key column */
|
|
26
47
|
localKey: string;
|
|
48
|
+
/** Columns to include */
|
|
27
49
|
columns: string[];
|
|
28
50
|
}
|
|
29
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Complete hydration plan for a query
|
|
54
|
+
*/
|
|
30
55
|
export interface HydrationPlan {
|
|
56
|
+
/** Root table name */
|
|
31
57
|
rootTable: string;
|
|
58
|
+
/** Root table primary key */
|
|
32
59
|
rootPrimaryKey: string;
|
|
60
|
+
/** Root table columns */
|
|
33
61
|
rootColumns: string[];
|
|
62
|
+
/** Relationship hydration plans */
|
|
34
63
|
relations: HydrationRelationPlan[];
|
|
35
64
|
}
|
|
36
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Query metadata including hydration information
|
|
68
|
+
*/
|
|
37
69
|
export interface QueryMetadata {
|
|
70
|
+
/** Optional hydration plan */
|
|
38
71
|
hydration?: HydrationPlan;
|
|
39
72
|
}
|
|
40
73
|
|
|
74
|
+
/**
|
|
75
|
+
* AST node representing a Common Table Expression (CTE)
|
|
76
|
+
*/
|
|
41
77
|
export interface CommonTableExpressionNode {
|
|
42
78
|
type: 'CommonTableExpression';
|
|
79
|
+
/** CTE name */
|
|
43
80
|
name: string;
|
|
81
|
+
/** Optional column names */
|
|
44
82
|
columns?: string[];
|
|
83
|
+
/** CTE query */
|
|
45
84
|
query: SelectQueryNode;
|
|
85
|
+
/** Whether the CTE is recursive */
|
|
46
86
|
recursive: boolean;
|
|
47
87
|
}
|
|
48
88
|
|
|
89
|
+
/**
|
|
90
|
+
* AST node representing a complete SELECT query
|
|
91
|
+
*/
|
|
49
92
|
export interface SelectQueryNode {
|
|
50
93
|
type: 'SelectQuery';
|
|
94
|
+
/** Optional CTEs (WITH clauses) */
|
|
51
95
|
ctes?: CommonTableExpressionNode[];
|
|
96
|
+
/** FROM clause table */
|
|
52
97
|
from: TableNode;
|
|
98
|
+
/** SELECT clause columns */
|
|
53
99
|
columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
|
|
100
|
+
/** JOIN clauses */
|
|
54
101
|
joins: JoinNode[];
|
|
102
|
+
/** Optional WHERE clause */
|
|
55
103
|
where?: ExpressionNode;
|
|
104
|
+
/** Optional GROUP BY clause */
|
|
56
105
|
groupBy?: ColumnNode[];
|
|
106
|
+
/** Optional HAVING clause */
|
|
57
107
|
having?: ExpressionNode;
|
|
108
|
+
/** Optional ORDER BY clause */
|
|
58
109
|
orderBy?: OrderByNode[];
|
|
110
|
+
/** Optional LIMIT clause */
|
|
59
111
|
limit?: number;
|
|
112
|
+
/** Optional OFFSET clause */
|
|
60
113
|
offset?: number;
|
|
114
|
+
/** Optional query metadata */
|
|
61
115
|
meta?: QueryMetadata;
|
|
116
|
+
/** Optional DISTINCT clause */
|
|
62
117
|
distinct?: ColumnNode[];
|
|
63
118
|
}
|
|
@@ -4,26 +4,49 @@ import { SelectQueryNode, HydrationPlan } from '../ast/query';
|
|
|
4
4
|
import { HydrationPlanner } from './hydration-planner';
|
|
5
5
|
import { SelectQueryState, ProjectionNode } from './select-query-state';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Manages hydration planning for query results
|
|
9
|
+
*/
|
|
7
10
|
export class HydrationManager {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new HydrationManager instance
|
|
13
|
+
* @param table - Table definition
|
|
14
|
+
* @param planner - Hydration planner
|
|
15
|
+
*/
|
|
8
16
|
constructor(
|
|
9
17
|
private readonly table: TableDef,
|
|
10
|
-
private readonly planner
|
|
18
|
+
private readonly planner: HydrationPlanner
|
|
11
19
|
) {}
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new HydrationManager with updated planner
|
|
23
|
+
* @param nextPlanner - Updated hydration planner
|
|
24
|
+
* @returns New HydrationManager instance
|
|
25
|
+
*/
|
|
17
26
|
private clone(nextPlanner: HydrationPlanner): HydrationManager {
|
|
18
27
|
return new HydrationManager(this.table, nextPlanner);
|
|
19
28
|
}
|
|
20
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Handles column selection for hydration planning
|
|
32
|
+
* @param state - Current query state
|
|
33
|
+
* @param newColumns - Newly selected columns
|
|
34
|
+
* @returns Updated HydrationManager with captured columns
|
|
35
|
+
*/
|
|
21
36
|
onColumnsSelected(state: SelectQueryState, newColumns: ProjectionNode[]): HydrationManager {
|
|
22
|
-
const
|
|
23
|
-
const updated = planner.captureRootColumns(state.ast.columns);
|
|
37
|
+
const updated = this.planner.captureRootColumns(newColumns);
|
|
24
38
|
return this.clone(updated);
|
|
25
39
|
}
|
|
26
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Handles relation inclusion for hydration planning
|
|
43
|
+
* @param state - Current query state
|
|
44
|
+
* @param relation - Relation definition
|
|
45
|
+
* @param relationName - Name of the relation
|
|
46
|
+
* @param aliasPrefix - Alias prefix for the relation
|
|
47
|
+
* @param targetColumns - Target columns to include
|
|
48
|
+
* @returns Updated HydrationManager with included relation
|
|
49
|
+
*/
|
|
27
50
|
onRelationIncluded(
|
|
28
51
|
state: SelectQueryState,
|
|
29
52
|
relation: RelationDef,
|
|
@@ -31,14 +54,18 @@ export class HydrationManager {
|
|
|
31
54
|
aliasPrefix: string,
|
|
32
55
|
targetColumns: string[]
|
|
33
56
|
): HydrationManager {
|
|
34
|
-
const
|
|
35
|
-
const withRoots = planner.captureRootColumns(state.ast.columns);
|
|
57
|
+
const withRoots = this.planner.captureRootColumns(state.ast.columns);
|
|
36
58
|
const next = withRoots.includeRelation(relation, relationName, aliasPrefix, targetColumns);
|
|
37
59
|
return this.clone(next);
|
|
38
60
|
}
|
|
39
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Applies hydration plan to the AST
|
|
64
|
+
* @param ast - Query AST to modify
|
|
65
|
+
* @returns AST with hydration metadata
|
|
66
|
+
*/
|
|
40
67
|
applyToAst(ast: SelectQueryNode): SelectQueryNode {
|
|
41
|
-
const plan = this.planner
|
|
68
|
+
const plan = this.planner.getPlan();
|
|
42
69
|
if (!plan) return ast;
|
|
43
70
|
return {
|
|
44
71
|
...ast,
|
|
@@ -49,7 +76,11 @@ export class HydrationManager {
|
|
|
49
76
|
};
|
|
50
77
|
}
|
|
51
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Gets the current hydration plan
|
|
81
|
+
* @returns Hydration plan or undefined if none exists
|
|
82
|
+
*/
|
|
52
83
|
getPlan(): HydrationPlan | undefined {
|
|
53
|
-
return this.planner
|
|
84
|
+
return this.planner.getPlan();
|
|
54
85
|
}
|
|
55
86
|
}
|
|
@@ -1,27 +1,37 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table';
|
|
2
|
-
import { RelationDef } from '../schema/relation';
|
|
3
|
-
import {
|
|
4
|
-
import { HydrationPlan, HydrationRelationPlan } from '../ast/query';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export const isRelationAlias = (alias?: string): boolean => alias ? alias.includes('__') : false;
|
|
1
|
+
import { TableDef } from '../schema/table';
|
|
2
|
+
import { RelationDef, RelationKinds } from '../schema/relation';
|
|
3
|
+
import { ProjectionNode } from './select-query-state';
|
|
4
|
+
import { HydrationPlan, HydrationRelationPlan } from '../ast/query';
|
|
5
|
+
import { isRelationAlias } from '../utils/relation-alias';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Finds the primary key column name for a table
|
|
9
|
+
* @param table - Table definition
|
|
10
|
+
* @returns Name of the primary key column, defaults to 'id'
|
|
11
|
+
*/
|
|
12
|
+
export const findPrimaryKey = (table: TableDef): string => {
|
|
13
|
+
const pk = Object.values(table.columns).find(c => c.primary);
|
|
14
|
+
return pk?.name || 'id';
|
|
15
|
+
};
|
|
19
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Manages hydration planning for query results
|
|
19
|
+
*/
|
|
20
20
|
export class HydrationPlanner {
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new HydrationPlanner instance
|
|
23
|
+
* @param table - Table definition
|
|
24
|
+
* @param plan - Optional existing hydration plan
|
|
25
|
+
*/
|
|
21
26
|
constructor(private readonly table: TableDef, private readonly plan?: HydrationPlan) { }
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Captures root table columns for hydration planning
|
|
30
|
+
* @param columns - Columns to capture
|
|
31
|
+
* @returns Updated HydrationPlanner with captured columns
|
|
32
|
+
*/
|
|
33
|
+
captureRootColumns(columns: ProjectionNode[]): HydrationPlanner {
|
|
34
|
+
const currentPlan = this.getPlanOrDefault();
|
|
25
35
|
const rootCols = new Set(currentPlan.rootColumns);
|
|
26
36
|
let changed = false;
|
|
27
37
|
|
|
@@ -44,6 +54,14 @@ export class HydrationPlanner {
|
|
|
44
54
|
});
|
|
45
55
|
}
|
|
46
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Includes a relation in the hydration plan
|
|
59
|
+
* @param rel - Relation definition
|
|
60
|
+
* @param relationName - Name of the relation
|
|
61
|
+
* @param aliasPrefix - Alias prefix for relation columns
|
|
62
|
+
* @param columns - Columns to include from the relation
|
|
63
|
+
* @returns Updated HydrationPlanner with included relation
|
|
64
|
+
*/
|
|
47
65
|
includeRelation(rel: RelationDef, relationName: string, aliasPrefix: string, columns: string[]): HydrationPlanner {
|
|
48
66
|
const currentPlan = this.getPlanOrDefault();
|
|
49
67
|
const relations = currentPlan.relations.filter(r => r.name !== relationName);
|
|
@@ -54,24 +72,55 @@ export class HydrationPlanner {
|
|
|
54
72
|
});
|
|
55
73
|
}
|
|
56
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Gets the current hydration plan
|
|
77
|
+
* @returns Current hydration plan or undefined
|
|
78
|
+
*/
|
|
57
79
|
getPlan(): HydrationPlan | undefined {
|
|
58
80
|
return this.plan;
|
|
59
81
|
}
|
|
60
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Gets the current hydration plan or creates a default one
|
|
85
|
+
* @returns Current hydration plan or default plan
|
|
86
|
+
*/
|
|
61
87
|
private getPlanOrDefault(): HydrationPlan {
|
|
62
88
|
return this.plan ?? buildDefaultHydrationPlan(this.table);
|
|
63
89
|
}
|
|
64
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Builds a relation plan for hydration
|
|
93
|
+
* @param rel - Relation definition
|
|
94
|
+
* @param relationName - Name of the relation
|
|
95
|
+
* @param aliasPrefix - Alias prefix for relation columns
|
|
96
|
+
* @param columns - Columns to include from the relation
|
|
97
|
+
* @returns Hydration relation plan
|
|
98
|
+
*/
|
|
65
99
|
private buildRelationPlan(rel: RelationDef, relationName: string, aliasPrefix: string, columns: string[]): HydrationRelationPlan {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
const localKeyFallback =
|
|
101
|
+
rel.type === RelationKinds.HasMany ? findPrimaryKey(this.table) : findPrimaryKey(rel.target);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
name: relationName,
|
|
105
|
+
aliasPrefix,
|
|
106
|
+
type: rel.type,
|
|
107
|
+
targetTable: rel.target.name,
|
|
108
|
+
targetPrimaryKey: findPrimaryKey(rel.target),
|
|
109
|
+
foreignKey: rel.foreignKey,
|
|
110
|
+
localKey: rel.localKey || localKeyFallback,
|
|
111
|
+
columns
|
|
112
|
+
};
|
|
113
|
+
}
|
|
77
114
|
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Builds a default hydration plan for a table
|
|
118
|
+
* @param table - Table definition
|
|
119
|
+
* @returns Default hydration plan
|
|
120
|
+
*/
|
|
121
|
+
const buildDefaultHydrationPlan = (table: TableDef): HydrationPlan => ({
|
|
122
|
+
rootTable: table.name,
|
|
123
|
+
rootPrimaryKey: findPrimaryKey(table),
|
|
124
|
+
rootColumns: [],
|
|
125
|
+
relations: []
|
|
126
|
+
});
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import { ColumnDef } from '../../schema/column';
|
|
2
|
-
import { CaseExpressionNode, ColumnNode, FunctionNode,
|
|
2
|
+
import { CaseExpressionNode, ColumnNode, FunctionNode, WindowFunctionNode } from '../../ast/expression';
|
|
3
|
+
import { SelectQueryNode } from '../../ast/query';
|
|
3
4
|
import { buildColumnNode } from '../query-ast-service';
|
|
4
5
|
import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Type for column selection input
|
|
9
|
+
*/
|
|
6
10
|
type ColumnSelectionInput = Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>;
|
|
7
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Handles column selection operations for the query builder
|
|
14
|
+
*/
|
|
8
15
|
export class ColumnSelector {
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new ColumnSelector instance
|
|
18
|
+
* @param env - Query builder environment
|
|
19
|
+
*/
|
|
9
20
|
constructor(private readonly env: SelectQueryBuilderEnvironment) {}
|
|
10
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Selects columns for the query
|
|
24
|
+
* @param context - Current query context
|
|
25
|
+
* @param columns - Columns to select
|
|
26
|
+
* @returns Updated query context with selected columns
|
|
27
|
+
*/
|
|
11
28
|
select(context: SelectQueryBuilderContext, columns: ColumnSelectionInput): SelectQueryBuilderContext {
|
|
12
29
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
13
30
|
const { state: nextState, addedColumns } = astService.select(columns);
|
|
@@ -17,12 +34,25 @@ export class ColumnSelector {
|
|
|
17
34
|
};
|
|
18
35
|
}
|
|
19
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Selects raw column expressions
|
|
39
|
+
* @param context - Current query context
|
|
40
|
+
* @param columns - Raw column expressions
|
|
41
|
+
* @returns Updated query context with raw column selections
|
|
42
|
+
*/
|
|
20
43
|
selectRaw(context: SelectQueryBuilderContext, columns: string[]): SelectQueryBuilderContext {
|
|
21
44
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
22
45
|
const nextState = astService.selectRaw(columns).state;
|
|
23
46
|
return { state: nextState, hydration: context.hydration };
|
|
24
47
|
}
|
|
25
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Selects a subquery as a column
|
|
51
|
+
* @param context - Current query context
|
|
52
|
+
* @param alias - Alias for the subquery
|
|
53
|
+
* @param query - Subquery to select
|
|
54
|
+
* @returns Updated query context with subquery selection
|
|
55
|
+
*/
|
|
26
56
|
selectSubquery(
|
|
27
57
|
context: SelectQueryBuilderContext,
|
|
28
58
|
alias: string,
|
|
@@ -33,6 +63,12 @@ export class ColumnSelector {
|
|
|
33
63
|
return { state: nextState, hydration: context.hydration };
|
|
34
64
|
}
|
|
35
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Adds DISTINCT clause to the query
|
|
68
|
+
* @param context - Current query context
|
|
69
|
+
* @param columns - Columns to make distinct
|
|
70
|
+
* @returns Updated query context with DISTINCT clause
|
|
71
|
+
*/
|
|
36
72
|
distinct(context: SelectQueryBuilderContext, columns: (ColumnDef | ColumnNode)[]): SelectQueryBuilderContext {
|
|
37
73
|
const nodes = columns.map(col => buildColumnNode(this.env.table, col));
|
|
38
74
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { SelectQueryNode } from '../../ast/query';
|
|
2
2
|
import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Manages Common Table Expressions (CTEs) for query building
|
|
6
|
+
*/
|
|
4
7
|
export class CteManager {
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new CteManager instance
|
|
10
|
+
* @param env - Query builder environment
|
|
11
|
+
*/
|
|
5
12
|
constructor(private readonly env: SelectQueryBuilderEnvironment) {}
|
|
6
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Adds a Common Table Expression (CTE) to the query
|
|
16
|
+
* @param context - Current query context
|
|
17
|
+
* @param name - Name of the CTE
|
|
18
|
+
* @param query - Query for the CTE
|
|
19
|
+
* @param columns - Optional column names for the CTE
|
|
20
|
+
* @param recursive - Whether the CTE is recursive
|
|
21
|
+
* @returns Updated query context with CTE
|
|
22
|
+
*/
|
|
7
23
|
withCte(
|
|
8
24
|
context: SelectQueryBuilderContext,
|
|
9
25
|
name: string,
|
|
@@ -3,27 +3,59 @@ import { ColumnNode, ExpressionNode } from '../../ast/expression';
|
|
|
3
3
|
import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
|
|
4
4
|
import { OrderDirection } from '../../constants/sql';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Manages filtering, ordering, and grouping operations for queries
|
|
8
|
+
*/
|
|
6
9
|
export class FilterManager {
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new FilterManager instance
|
|
12
|
+
* @param env - Query builder environment
|
|
13
|
+
*/
|
|
7
14
|
constructor(private readonly env: SelectQueryBuilderEnvironment) {}
|
|
8
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Adds a WHERE clause to the query
|
|
18
|
+
* @param context - Current query context
|
|
19
|
+
* @param expr - Expression for the WHERE clause
|
|
20
|
+
* @returns Updated query context with WHERE clause
|
|
21
|
+
*/
|
|
9
22
|
where(context: SelectQueryBuilderContext, expr: ExpressionNode): SelectQueryBuilderContext {
|
|
10
23
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
11
24
|
const nextState = astService.withWhere(expr);
|
|
12
25
|
return { state: nextState, hydration: context.hydration };
|
|
13
26
|
}
|
|
14
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Adds a GROUP BY clause to the query
|
|
30
|
+
* @param context - Current query context
|
|
31
|
+
* @param column - Column to group by
|
|
32
|
+
* @returns Updated query context with GROUP BY clause
|
|
33
|
+
*/
|
|
15
34
|
groupBy(context: SelectQueryBuilderContext, column: ColumnDef | ColumnNode): SelectQueryBuilderContext {
|
|
16
35
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
17
36
|
const nextState = astService.withGroupBy(column);
|
|
18
37
|
return { state: nextState, hydration: context.hydration };
|
|
19
38
|
}
|
|
20
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Adds a HAVING clause to the query
|
|
42
|
+
* @param context - Current query context
|
|
43
|
+
* @param expr - Expression for the HAVING clause
|
|
44
|
+
* @returns Updated query context with HAVING clause
|
|
45
|
+
*/
|
|
21
46
|
having(context: SelectQueryBuilderContext, expr: ExpressionNode): SelectQueryBuilderContext {
|
|
22
47
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
23
48
|
const nextState = astService.withHaving(expr);
|
|
24
49
|
return { state: nextState, hydration: context.hydration };
|
|
25
50
|
}
|
|
26
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Adds an ORDER BY clause to the query
|
|
54
|
+
* @param context - Current query context
|
|
55
|
+
* @param column - Column to order by
|
|
56
|
+
* @param direction - Order direction (ASC/DESC)
|
|
57
|
+
* @returns Updated query context with ORDER BY clause
|
|
58
|
+
*/
|
|
27
59
|
orderBy(
|
|
28
60
|
context: SelectQueryBuilderContext,
|
|
29
61
|
column: ColumnDef | ColumnNode,
|
|
@@ -1,24 +1,34 @@
|
|
|
1
1
|
import { BinaryExpressionNode } from '../../ast/expression';
|
|
2
|
-
import { JoinNode } from '../../ast/join';
|
|
3
2
|
import { TableDef } from '../../schema/table';
|
|
4
3
|
import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
|
|
5
4
|
import { JoinKind } from '../../constants/sql';
|
|
5
|
+
import { createJoinNode } from '../../utils/join-node';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Manages JOIN operations for query building
|
|
9
|
+
*/
|
|
7
10
|
export class JoinManager {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new JoinManager instance
|
|
13
|
+
* @param env - Query builder environment
|
|
14
|
+
*/
|
|
8
15
|
constructor(private readonly env: SelectQueryBuilderEnvironment) {}
|
|
9
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Adds a JOIN clause to the query
|
|
19
|
+
* @param context - Current query context
|
|
20
|
+
* @param table - Table to join
|
|
21
|
+
* @param condition - Join condition expression
|
|
22
|
+
* @param kind - Type of join (INNER, LEFT, RIGHT, etc.)
|
|
23
|
+
* @returns Updated query context with JOIN clause
|
|
24
|
+
*/
|
|
10
25
|
join(
|
|
11
26
|
context: SelectQueryBuilderContext,
|
|
12
27
|
table: TableDef,
|
|
13
28
|
condition: BinaryExpressionNode,
|
|
14
29
|
kind: JoinKind
|
|
15
30
|
): SelectQueryBuilderContext {
|
|
16
|
-
const joinNode
|
|
17
|
-
type: 'Join',
|
|
18
|
-
kind,
|
|
19
|
-
table: { type: 'Table', name: table.name },
|
|
20
|
-
condition
|
|
21
|
-
};
|
|
31
|
+
const joinNode = createJoinNode(kind, table.name, condition);
|
|
22
32
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
23
33
|
const nextState = astService.withJoin(joinNode);
|
|
24
34
|
return { state: nextState, hydration: context.hydration };
|
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Manages pagination operations (LIMIT and OFFSET) for queries
|
|
5
|
+
*/
|
|
3
6
|
export class PaginationManager {
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new PaginationManager instance
|
|
9
|
+
* @param env - Query builder environment
|
|
10
|
+
*/
|
|
4
11
|
constructor(private readonly env: SelectQueryBuilderEnvironment) {}
|
|
5
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Adds a LIMIT clause to the query
|
|
15
|
+
* @param context - Current query context
|
|
16
|
+
* @param value - Maximum number of rows to return
|
|
17
|
+
* @returns Updated query context with LIMIT clause
|
|
18
|
+
*/
|
|
6
19
|
limit(context: SelectQueryBuilderContext, value: number): SelectQueryBuilderContext {
|
|
7
20
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
8
21
|
const nextState = astService.withLimit(value);
|
|
9
22
|
return { state: nextState, hydration: context.hydration };
|
|
10
23
|
}
|
|
11
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Adds an OFFSET clause to the query
|
|
27
|
+
* @param context - Current query context
|
|
28
|
+
* @param value - Number of rows to skip
|
|
29
|
+
* @returns Updated query context with OFFSET clause
|
|
30
|
+
*/
|
|
12
31
|
offset(context: SelectQueryBuilderContext, value: number): SelectQueryBuilderContext {
|
|
13
32
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
14
33
|
const nextState = astService.withOffset(value);
|