metal-orm 1.0.8 → 1.0.9
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 +12 -1
- package/dist/decorators/index.cjs +2564 -0
- package/dist/decorators/index.cjs.map +1 -0
- package/dist/decorators/index.d.cts +53 -0
- package/dist/decorators/index.d.ts +53 -0
- package/dist/decorators/index.js +2530 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/index.cjs +4227 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +701 -0
- package/dist/index.d.ts +701 -0
- package/dist/index.js +4131 -0
- package/dist/index.js.map +1 -0
- package/dist/select-654m4qy8.d.cts +1522 -0
- package/dist/select-654m4qy8.d.ts +1522 -0
- package/package.json +27 -20
- package/src/codegen/typescript.ts +405 -393
- package/src/core/ast/aggregate-functions.ts +30 -0
- package/src/core/ast/builders.ts +43 -0
- package/src/core/ast/expression-builders.ts +310 -0
- package/src/core/ast/expression-nodes.ts +211 -0
- package/src/core/ast/expression-visitor.ts +99 -0
- package/src/core/ast/expression.ts +5 -0
- package/src/{utils → core/ast}/join-node.ts +20 -20
- package/src/{ast → core/ast}/join.ts +18 -18
- package/src/{ast → core/ast}/query.ts +113 -113
- package/src/core/ast/window-functions.ts +140 -0
- package/src/{dialect → core/dialect}/abstract.ts +94 -94
- package/src/{dialect → core/dialect}/mssql/index.ts +31 -31
- package/src/{dialect → core/dialect}/mysql/index.ts +31 -31
- package/src/{dialect → core/dialect}/postgres/index.ts +45 -45
- package/src/{dialect → core/dialect}/sqlite/index.ts +45 -45
- package/src/{constants → core/sql}/sql-operator-config.ts +39 -39
- package/src/decorators/bootstrap.ts +126 -0
- package/src/decorators/column.ts +78 -0
- package/src/decorators/entity.ts +36 -0
- package/src/decorators/index.ts +4 -0
- package/src/decorators/relations.ts +107 -0
- package/src/global.d.ts +1 -0
- package/src/index.ts +22 -22
- package/src/orm/db-executor.ts +11 -0
- package/src/orm/domain-event-bus.ts +52 -0
- package/src/{runtime → orm}/entity-meta.ts +52 -52
- package/src/orm/entity-metadata.ts +140 -0
- package/src/{runtime → orm}/entity.ts +252 -252
- package/src/{runtime → orm}/execute.ts +36 -36
- package/src/{runtime → orm}/hydration.ts +103 -103
- package/src/orm/identity-map.ts +37 -0
- package/src/{runtime → orm}/lazy-batch.ts +205 -205
- package/src/orm/orm-context.ts +154 -0
- package/src/orm/relation-change-processor.ts +140 -0
- package/src/{runtime → orm}/relations/belongs-to.ts +92 -92
- package/src/{runtime → orm}/relations/has-many.ts +111 -111
- package/src/{runtime → orm}/relations/many-to-many.ts +149 -149
- package/src/orm/runtime-types.ts +39 -0
- package/src/orm/transaction-runner.ts +17 -0
- package/src/orm/unit-of-work.ts +232 -0
- package/src/{builder/operations → query-builder}/column-selector.ts +78 -78
- package/src/{builder → query-builder}/delete-query-state.ts +38 -42
- package/src/{builder → query-builder}/delete.ts +46 -57
- package/src/{builder → query-builder}/hydration-manager.ts +87 -87
- package/src/{builder → query-builder}/hydration-planner.ts +182 -182
- package/src/{builder → query-builder}/insert-query-state.ts +51 -62
- package/src/{builder → query-builder}/insert.ts +48 -59
- package/src/{builder → query-builder}/query-ast-service.ts +208 -226
- package/src/{utils → query-builder}/raw-column-parser.ts +32 -32
- package/src/{builder → query-builder}/relation-conditions.ts +112 -112
- package/src/{builder/operations → query-builder}/relation-manager.ts +82 -82
- package/src/{builder → query-builder}/relation-projection-helper.ts +101 -101
- package/src/{builder → query-builder}/relation-service.ts +284 -284
- package/src/{builder → query-builder}/relation-types.ts +21 -21
- package/src/{builder → query-builder}/relation-utils.ts +12 -12
- package/src/{builder → query-builder}/select-query-builder-deps.ts +112 -94
- package/src/{builder → query-builder}/select-query-state.ts +179 -179
- package/src/{builder → query-builder}/select.ts +78 -69
- package/src/{builder → query-builder}/update-query-state.ts +55 -59
- package/src/{builder → query-builder}/update.ts +50 -61
- package/src/schema/column.ts +25 -25
- package/src/schema/relation.ts +116 -116
- package/src/schema/table.ts +34 -34
- package/src/schema/types.ts +76 -76
- package/.github/workflows/publish-metal-orm.yml +0 -38
- package/ROADMAP.md +0 -125
- package/docs/CHANGES.md +0 -104
- package/docs/advanced-features.md +0 -176
- package/docs/api-reference.md +0 -31
- package/docs/dml-operations.md +0 -156
- package/docs/getting-started.md +0 -171
- package/docs/hydration.md +0 -115
- package/docs/index.md +0 -36
- package/docs/multi-dialect-support.md +0 -59
- package/docs/query-builder.md +0 -135
- package/docs/runtime.md +0 -105
- package/docs/schema-definition.md +0 -112
- package/metadata.json +0 -5
- package/playground/api/playground-api.ts +0 -94
- package/playground/index.html +0 -15
- package/playground/src/App.css +0 -1
- package/playground/src/App.tsx +0 -114
- package/playground/src/components/CodeDisplay.tsx +0 -43
- package/playground/src/components/QueryExecutor.tsx +0 -189
- package/playground/src/components/ResultsTable.tsx +0 -67
- package/playground/src/components/ResultsTabs.tsx +0 -105
- package/playground/src/components/ScenarioList.tsx +0 -56
- package/playground/src/components/logo.svg +0 -45
- package/playground/src/data/scenarios.ts +0 -2
- package/playground/src/main.tsx +0 -9
- package/playground/src/services/PlaygroundApiService.ts +0 -60
- package/postcss.config.cjs +0 -5
- package/sql_sql-ansi-cheatsheet-2025.md +0 -264
- package/src/ast/expression.ts +0 -658
- package/src/builder/operations/cte-manager.ts +0 -34
- package/src/builder/operations/filter-manager.ts +0 -68
- package/src/builder/operations/join-manager.ts +0 -36
- package/src/builder/operations/pagination-manager.ts +0 -36
- package/src/playground/features/playground/api/types.ts +0 -16
- package/src/playground/features/playground/clients/MockClient.ts +0 -17
- package/src/playground/features/playground/clients/SqliteClient.ts +0 -57
- package/src/playground/features/playground/common/IDatabaseClient.ts +0 -10
- package/src/playground/features/playground/data/scenarios/aggregation.ts +0 -36
- package/src/playground/features/playground/data/scenarios/basics.ts +0 -25
- package/src/playground/features/playground/data/scenarios/edge_cases.ts +0 -57
- package/src/playground/features/playground/data/scenarios/filtering.ts +0 -94
- package/src/playground/features/playground/data/scenarios/hydration.ts +0 -27
- package/src/playground/features/playground/data/scenarios/index.ts +0 -29
- package/src/playground/features/playground/data/scenarios/ordering.ts +0 -25
- package/src/playground/features/playground/data/scenarios/pagination.ts +0 -16
- package/src/playground/features/playground/data/scenarios/relationships.ts +0 -75
- package/src/playground/features/playground/data/scenarios/types.ts +0 -70
- package/src/playground/features/playground/data/schema.ts +0 -91
- package/src/playground/features/playground/data/seed.ts +0 -104
- package/src/playground/features/playground/services/QueryExecutionService.ts +0 -121
- package/src/runtime/orm-context.ts +0 -539
- package/tests/belongs-to-many.test.ts +0 -57
- package/tests/between.test.ts +0 -43
- package/tests/case-expression.test.ts +0 -58
- package/tests/complex-exists.test.ts +0 -230
- package/tests/cte.test.ts +0 -118
- package/tests/dml.test.ts +0 -206
- package/tests/exists.test.ts +0 -127
- package/tests/like.test.ts +0 -33
- package/tests/orm-runtime.test.ts +0 -254
- package/tests/postgres.test.ts +0 -30
- package/tests/right-join.test.ts +0 -89
- package/tests/subquery-having.test.ts +0 -193
- package/tests/window-function.test.ts +0 -151
- package/tsconfig.json +0 -30
- package/tsup.config.ts +0 -10
- package/vite.config.ts +0 -22
- package/vitest.config.ts +0 -14
- /package/src/{constants → core/sql}/sql.ts +0 -0
- /package/src/{runtime → orm}/als.ts +0 -0
- /package/src/{utils → query-builder}/relation-alias.ts +0 -0
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { JoinNode } from '
|
|
2
|
-
import { ExpressionNode } from '
|
|
3
|
-
import { JoinKind } from '../
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Creates a JoinNode ready for AST insertion.
|
|
7
|
-
* Centralizing this avoids copy/pasted object literals when multiple services need to synthesize joins.
|
|
8
|
-
*/
|
|
9
|
-
export const createJoinNode = (
|
|
10
|
-
kind: JoinKind,
|
|
11
|
-
tableName: string,
|
|
12
|
-
condition: ExpressionNode,
|
|
13
|
-
relationName?: string
|
|
14
|
-
): JoinNode => ({
|
|
15
|
-
type: 'Join',
|
|
16
|
-
kind,
|
|
17
|
-
table: { type: 'Table', name: tableName },
|
|
18
|
-
condition,
|
|
19
|
-
relationName
|
|
20
|
-
});
|
|
1
|
+
import { JoinNode } from './join.js';
|
|
2
|
+
import { ExpressionNode } from './expression.js';
|
|
3
|
+
import { JoinKind } from '../sql/sql.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a JoinNode ready for AST insertion.
|
|
7
|
+
* Centralizing this avoids copy/pasted object literals when multiple services need to synthesize joins.
|
|
8
|
+
*/
|
|
9
|
+
export const createJoinNode = (
|
|
10
|
+
kind: JoinKind,
|
|
11
|
+
tableName: string,
|
|
12
|
+
condition: ExpressionNode,
|
|
13
|
+
relationName?: string
|
|
14
|
+
): JoinNode => ({
|
|
15
|
+
type: 'Join',
|
|
16
|
+
kind,
|
|
17
|
+
table: { type: 'Table', name: tableName },
|
|
18
|
+
condition,
|
|
19
|
+
relationName
|
|
20
|
+
});
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { TableNode } from './query';
|
|
2
|
-
import { ExpressionNode } from './expression';
|
|
3
|
-
import { JoinKind } from '../
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* AST node representing a JOIN clause
|
|
7
|
-
*/
|
|
8
|
-
export interface JoinNode {
|
|
9
|
-
type: 'Join';
|
|
10
|
-
/** Type of join (INNER, LEFT, RIGHT, etc.) */
|
|
11
|
-
kind: JoinKind;
|
|
12
|
-
/** Table to join */
|
|
13
|
-
table: TableNode;
|
|
14
|
-
/** Join condition expression */
|
|
15
|
-
condition: ExpressionNode;
|
|
16
|
-
/** Optional relation name for code generation */
|
|
17
|
-
relationName?: string;
|
|
18
|
-
}
|
|
1
|
+
import { TableNode } from './query.js';
|
|
2
|
+
import { ExpressionNode } from './expression.js';
|
|
3
|
+
import { JoinKind } from '../sql/sql.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* AST node representing a JOIN clause
|
|
7
|
+
*/
|
|
8
|
+
export interface JoinNode {
|
|
9
|
+
type: 'Join';
|
|
10
|
+
/** Type of join (INNER, LEFT, RIGHT, etc.) */
|
|
11
|
+
kind: JoinKind;
|
|
12
|
+
/** Table to join */
|
|
13
|
+
table: TableNode;
|
|
14
|
+
/** Join condition expression */
|
|
15
|
+
condition: ExpressionNode;
|
|
16
|
+
/** Optional relation name for code generation */
|
|
17
|
+
relationName?: string;
|
|
18
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ColumnNode,
|
|
3
|
-
FunctionNode,
|
|
4
|
-
ExpressionNode,
|
|
5
|
-
ScalarSubqueryNode,
|
|
6
|
-
CaseExpressionNode,
|
|
7
|
-
WindowFunctionNode,
|
|
8
|
-
OperandNode
|
|
9
|
-
} from './expression';
|
|
10
|
-
import { JoinNode } from './join';
|
|
11
|
-
import { RelationType } from '
|
|
12
|
-
import { OrderDirection } from '../
|
|
1
|
+
import {
|
|
2
|
+
ColumnNode,
|
|
3
|
+
FunctionNode,
|
|
4
|
+
ExpressionNode,
|
|
5
|
+
ScalarSubqueryNode,
|
|
6
|
+
CaseExpressionNode,
|
|
7
|
+
WindowFunctionNode,
|
|
8
|
+
OperandNode
|
|
9
|
+
} from './expression.js';
|
|
10
|
+
import { JoinNode } from './join.js';
|
|
11
|
+
import { RelationType } from '../../schema/relation.js';
|
|
12
|
+
import { OrderDirection } from '../sql/sql.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* AST node representing a table reference in a query
|
|
@@ -35,39 +35,39 @@ export interface OrderByNode {
|
|
|
35
35
|
direction: OrderDirection;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
* Plan describing pivot columns needed for hydration
|
|
40
|
-
*/
|
|
41
|
-
export interface HydrationPivotPlan {
|
|
42
|
-
table: string;
|
|
43
|
-
primaryKey: string;
|
|
44
|
-
aliasPrefix: string;
|
|
45
|
-
columns: string[];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Plan for hydrating relationship data
|
|
50
|
-
*/
|
|
51
|
-
export interface HydrationRelationPlan {
|
|
52
|
-
/** Name of the relationship */
|
|
53
|
-
name: string;
|
|
54
|
-
/** Alias prefix for the relationship */
|
|
55
|
-
aliasPrefix: string;
|
|
56
|
-
/** Type of relationship */
|
|
57
|
-
type: RelationType;
|
|
58
|
-
/** Target table name */
|
|
59
|
-
targetTable: string;
|
|
60
|
-
/** Target table primary key */
|
|
61
|
-
targetPrimaryKey: string;
|
|
62
|
-
/** Foreign key column */
|
|
63
|
-
foreignKey: string;
|
|
64
|
-
/** Local key column */
|
|
65
|
-
localKey: string;
|
|
66
|
-
/** Columns to include */
|
|
67
|
-
columns: string[];
|
|
68
|
-
/** Optional pivot plan for many-to-many relationships */
|
|
69
|
-
pivot?: HydrationPivotPlan;
|
|
70
|
-
}
|
|
38
|
+
/**
|
|
39
|
+
* Plan describing pivot columns needed for hydration
|
|
40
|
+
*/
|
|
41
|
+
export interface HydrationPivotPlan {
|
|
42
|
+
table: string;
|
|
43
|
+
primaryKey: string;
|
|
44
|
+
aliasPrefix: string;
|
|
45
|
+
columns: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Plan for hydrating relationship data
|
|
50
|
+
*/
|
|
51
|
+
export interface HydrationRelationPlan {
|
|
52
|
+
/** Name of the relationship */
|
|
53
|
+
name: string;
|
|
54
|
+
/** Alias prefix for the relationship */
|
|
55
|
+
aliasPrefix: string;
|
|
56
|
+
/** Type of relationship */
|
|
57
|
+
type: RelationType;
|
|
58
|
+
/** Target table name */
|
|
59
|
+
targetTable: string;
|
|
60
|
+
/** Target table primary key */
|
|
61
|
+
targetPrimaryKey: string;
|
|
62
|
+
/** Foreign key column */
|
|
63
|
+
foreignKey: string;
|
|
64
|
+
/** Local key column */
|
|
65
|
+
localKey: string;
|
|
66
|
+
/** Columns to include */
|
|
67
|
+
columns: string[];
|
|
68
|
+
/** Optional pivot plan for many-to-many relationships */
|
|
69
|
+
pivot?: HydrationPivotPlan;
|
|
70
|
+
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* Complete hydration plan for a query
|
|
@@ -109,71 +109,71 @@ export interface CommonTableExpressionNode {
|
|
|
109
109
|
/**
|
|
110
110
|
* AST node representing a complete SELECT query
|
|
111
111
|
*/
|
|
112
|
-
export interface SelectQueryNode {
|
|
113
|
-
type: 'SelectQuery';
|
|
114
|
-
/** Optional CTEs (WITH clauses) */
|
|
115
|
-
ctes?: CommonTableExpressionNode[];
|
|
116
|
-
/** FROM clause table */
|
|
117
|
-
from: TableNode;
|
|
118
|
-
/** SELECT clause columns */
|
|
119
|
-
columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
|
|
120
|
-
/** JOIN clauses */
|
|
121
|
-
joins: JoinNode[];
|
|
122
|
-
/** Optional WHERE clause */
|
|
123
|
-
where?: ExpressionNode;
|
|
124
|
-
/** Optional GROUP BY clause */
|
|
125
|
-
groupBy?: ColumnNode[];
|
|
126
|
-
/** Optional HAVING clause */
|
|
127
|
-
having?: ExpressionNode;
|
|
128
|
-
/** Optional ORDER BY clause */
|
|
129
|
-
orderBy?: OrderByNode[];
|
|
130
|
-
/** Optional LIMIT clause */
|
|
131
|
-
limit?: number;
|
|
132
|
-
/** Optional OFFSET clause */
|
|
133
|
-
offset?: number;
|
|
134
|
-
/** Optional query metadata */
|
|
135
|
-
meta?: QueryMetadata;
|
|
136
|
-
/** Optional DISTINCT clause */
|
|
137
|
-
distinct?: ColumnNode[];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export interface InsertQueryNode {
|
|
141
|
-
type: 'InsertQuery';
|
|
142
|
-
/** Target table */
|
|
143
|
-
into: TableNode;
|
|
144
|
-
/** Column order for inserted values */
|
|
145
|
-
columns: ColumnNode[];
|
|
146
|
-
/** Rows of values to insert */
|
|
147
|
-
values: OperandNode[][];
|
|
148
|
-
/** Optional RETURNING clause */
|
|
149
|
-
returning?: ColumnNode[];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export interface UpdateAssignmentNode {
|
|
153
|
-
/** Column to update */
|
|
154
|
-
column: ColumnNode;
|
|
155
|
-
/** Value to set */
|
|
156
|
-
value: OperandNode;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export interface UpdateQueryNode {
|
|
160
|
-
type: 'UpdateQuery';
|
|
161
|
-
/** Table being updated */
|
|
162
|
-
table: TableNode;
|
|
163
|
-
/** Assignments for SET clause */
|
|
164
|
-
set: UpdateAssignmentNode[];
|
|
165
|
-
/** Optional WHERE clause */
|
|
166
|
-
where?: ExpressionNode;
|
|
167
|
-
/** Optional RETURNING clause */
|
|
168
|
-
returning?: ColumnNode[];
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export interface DeleteQueryNode {
|
|
172
|
-
type: 'DeleteQuery';
|
|
173
|
-
/** Table to delete from */
|
|
174
|
-
from: TableNode;
|
|
175
|
-
/** Optional WHERE clause */
|
|
176
|
-
where?: ExpressionNode;
|
|
177
|
-
/** Optional RETURNING clause */
|
|
178
|
-
returning?: ColumnNode[];
|
|
179
|
-
}
|
|
112
|
+
export interface SelectQueryNode {
|
|
113
|
+
type: 'SelectQuery';
|
|
114
|
+
/** Optional CTEs (WITH clauses) */
|
|
115
|
+
ctes?: CommonTableExpressionNode[];
|
|
116
|
+
/** FROM clause table */
|
|
117
|
+
from: TableNode;
|
|
118
|
+
/** SELECT clause columns */
|
|
119
|
+
columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
|
|
120
|
+
/** JOIN clauses */
|
|
121
|
+
joins: JoinNode[];
|
|
122
|
+
/** Optional WHERE clause */
|
|
123
|
+
where?: ExpressionNode;
|
|
124
|
+
/** Optional GROUP BY clause */
|
|
125
|
+
groupBy?: ColumnNode[];
|
|
126
|
+
/** Optional HAVING clause */
|
|
127
|
+
having?: ExpressionNode;
|
|
128
|
+
/** Optional ORDER BY clause */
|
|
129
|
+
orderBy?: OrderByNode[];
|
|
130
|
+
/** Optional LIMIT clause */
|
|
131
|
+
limit?: number;
|
|
132
|
+
/** Optional OFFSET clause */
|
|
133
|
+
offset?: number;
|
|
134
|
+
/** Optional query metadata */
|
|
135
|
+
meta?: QueryMetadata;
|
|
136
|
+
/** Optional DISTINCT clause */
|
|
137
|
+
distinct?: ColumnNode[];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface InsertQueryNode {
|
|
141
|
+
type: 'InsertQuery';
|
|
142
|
+
/** Target table */
|
|
143
|
+
into: TableNode;
|
|
144
|
+
/** Column order for inserted values */
|
|
145
|
+
columns: ColumnNode[];
|
|
146
|
+
/** Rows of values to insert */
|
|
147
|
+
values: OperandNode[][];
|
|
148
|
+
/** Optional RETURNING clause */
|
|
149
|
+
returning?: ColumnNode[];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface UpdateAssignmentNode {
|
|
153
|
+
/** Column to update */
|
|
154
|
+
column: ColumnNode;
|
|
155
|
+
/** Value to set */
|
|
156
|
+
value: OperandNode;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface UpdateQueryNode {
|
|
160
|
+
type: 'UpdateQuery';
|
|
161
|
+
/** Table being updated */
|
|
162
|
+
table: TableNode;
|
|
163
|
+
/** Assignments for SET clause */
|
|
164
|
+
set: UpdateAssignmentNode[];
|
|
165
|
+
/** Optional WHERE clause */
|
|
166
|
+
where?: ExpressionNode;
|
|
167
|
+
/** Optional RETURNING clause */
|
|
168
|
+
returning?: ColumnNode[];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface DeleteQueryNode {
|
|
172
|
+
type: 'DeleteQuery';
|
|
173
|
+
/** Table to delete from */
|
|
174
|
+
from: TableNode;
|
|
175
|
+
/** Optional WHERE clause */
|
|
176
|
+
where?: ExpressionNode;
|
|
177
|
+
/** Optional RETURNING clause */
|
|
178
|
+
returning?: ColumnNode[];
|
|
179
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { ColumnDef } from '../../schema/column.js';
|
|
2
|
+
import { ColumnNode, LiteralNode, JsonPathNode, WindowFunctionNode } from './expression-nodes.js';
|
|
3
|
+
import { columnOperand } from './expression-builders.js';
|
|
4
|
+
import { OrderDirection } from '../sql/sql.js';
|
|
5
|
+
import { OrderByNode } from './query.js';
|
|
6
|
+
|
|
7
|
+
const buildWindowFunction = (
|
|
8
|
+
name: string,
|
|
9
|
+
args: (ColumnNode | LiteralNode | JsonPathNode)[] = [],
|
|
10
|
+
partitionBy?: ColumnNode[],
|
|
11
|
+
orderBy?: OrderByNode[]
|
|
12
|
+
): WindowFunctionNode => {
|
|
13
|
+
const node: WindowFunctionNode = {
|
|
14
|
+
type: 'WindowFunction',
|
|
15
|
+
name,
|
|
16
|
+
args
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (partitionBy && partitionBy.length) {
|
|
20
|
+
node.partitionBy = partitionBy;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (orderBy && orderBy.length) {
|
|
24
|
+
node.orderBy = orderBy;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return node;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a ROW_NUMBER window function
|
|
32
|
+
* @returns Window function node for ROW_NUMBER
|
|
33
|
+
*/
|
|
34
|
+
export const rowNumber = (): WindowFunctionNode => buildWindowFunction('ROW_NUMBER');
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates a RANK window function
|
|
38
|
+
* @returns Window function node for RANK
|
|
39
|
+
*/
|
|
40
|
+
export const rank = (): WindowFunctionNode => buildWindowFunction('RANK');
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates a DENSE_RANK window function
|
|
44
|
+
* @returns Window function node for DENSE_RANK
|
|
45
|
+
*/
|
|
46
|
+
export const denseRank = (): WindowFunctionNode => buildWindowFunction('DENSE_RANK');
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates an NTILE window function
|
|
50
|
+
* @param n - Number of buckets
|
|
51
|
+
* @returns Window function node for NTILE
|
|
52
|
+
*/
|
|
53
|
+
export const ntile = (n: number): WindowFunctionNode =>
|
|
54
|
+
buildWindowFunction('NTILE', [{ type: 'Literal', value: n }]);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Creates a LAG window function
|
|
58
|
+
* @param col - Column to lag
|
|
59
|
+
* @param offset - Offset (defaults to 1)
|
|
60
|
+
* @param defaultValue - Default value if no row exists
|
|
61
|
+
* @returns Window function node for LAG
|
|
62
|
+
*/
|
|
63
|
+
export const lag = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
|
|
64
|
+
const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
|
|
65
|
+
columnOperand(col),
|
|
66
|
+
{ type: 'Literal', value: offset }
|
|
67
|
+
];
|
|
68
|
+
if (defaultValue !== undefined) {
|
|
69
|
+
args.push({ type: 'Literal', value: defaultValue });
|
|
70
|
+
}
|
|
71
|
+
return buildWindowFunction('LAG', args);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates a LEAD window function
|
|
76
|
+
* @param col - Column to lead
|
|
77
|
+
* @param offset - Offset (defaults to 1)
|
|
78
|
+
* @param defaultValue - Default value if no row exists
|
|
79
|
+
* @returns Window function node for LEAD
|
|
80
|
+
*/
|
|
81
|
+
export const lead = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
|
|
82
|
+
const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
|
|
83
|
+
columnOperand(col),
|
|
84
|
+
{ type: 'Literal', value: offset }
|
|
85
|
+
];
|
|
86
|
+
if (defaultValue !== undefined) {
|
|
87
|
+
args.push({ type: 'Literal', value: defaultValue });
|
|
88
|
+
}
|
|
89
|
+
return buildWindowFunction('LEAD', args);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a FIRST_VALUE window function
|
|
94
|
+
* @param col - Column to get first value from
|
|
95
|
+
* @returns Window function node for FIRST_VALUE
|
|
96
|
+
*/
|
|
97
|
+
export const firstValue = (col: ColumnDef | ColumnNode): WindowFunctionNode =>
|
|
98
|
+
buildWindowFunction('FIRST_VALUE', [columnOperand(col)]);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Creates a LAST_VALUE window function
|
|
102
|
+
* @param col - Column to get last value from
|
|
103
|
+
* @returns Window function node for LAST_VALUE
|
|
104
|
+
*/
|
|
105
|
+
export const lastValue = (col: ColumnDef | ColumnNode): WindowFunctionNode =>
|
|
106
|
+
buildWindowFunction('LAST_VALUE', [columnOperand(col)]);
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Creates a custom window function
|
|
110
|
+
* @param name - Window function name
|
|
111
|
+
* @param args - Function arguments
|
|
112
|
+
* @param partitionBy - Optional PARTITION BY columns
|
|
113
|
+
* @param orderBy - Optional ORDER BY clauses
|
|
114
|
+
* @returns Window function node
|
|
115
|
+
*/
|
|
116
|
+
export const windowFunction = (
|
|
117
|
+
name: string,
|
|
118
|
+
args: (ColumnDef | ColumnNode | LiteralNode | JsonPathNode)[] = [],
|
|
119
|
+
partitionBy?: (ColumnDef | ColumnNode)[],
|
|
120
|
+
orderBy?: { column: ColumnDef | ColumnNode; direction: OrderDirection }[]
|
|
121
|
+
): WindowFunctionNode => {
|
|
122
|
+
const nodeArgs = args.map(arg => {
|
|
123
|
+
if (typeof (arg as LiteralNode).value !== 'undefined') {
|
|
124
|
+
return arg as LiteralNode;
|
|
125
|
+
}
|
|
126
|
+
if ('path' in arg) {
|
|
127
|
+
return arg as JsonPathNode;
|
|
128
|
+
}
|
|
129
|
+
return columnOperand(arg as ColumnDef | ColumnNode);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const partitionNodes = partitionBy?.map(col => columnOperand(col)) ?? undefined;
|
|
133
|
+
const orderNodes: OrderByNode[] | undefined = orderBy?.map(o => ({
|
|
134
|
+
type: 'OrderBy',
|
|
135
|
+
column: columnOperand(o.column),
|
|
136
|
+
direction: o.direction
|
|
137
|
+
}));
|
|
138
|
+
|
|
139
|
+
return buildWindowFunction(name, nodeArgs, partitionNodes, orderNodes);
|
|
140
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../ast/query';
|
|
1
|
+
import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../ast/query.js';
|
|
2
2
|
import {
|
|
3
3
|
ExpressionNode,
|
|
4
4
|
BinaryExpressionNode,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
CaseExpressionNode,
|
|
16
16
|
WindowFunctionNode,
|
|
17
17
|
BetweenExpressionNode
|
|
18
|
-
} from '../ast/expression';
|
|
18
|
+
} from '../ast/expression.js';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Context for SQL compilation with parameter management
|
|
@@ -30,91 +30,91 @@ export interface CompilerContext {
|
|
|
30
30
|
/**
|
|
31
31
|
* Result of SQL compilation
|
|
32
32
|
*/
|
|
33
|
-
export interface CompiledQuery {
|
|
34
|
-
/** Generated SQL string */
|
|
35
|
-
sql: string;
|
|
36
|
-
/** Parameters for the query */
|
|
37
|
-
params: unknown[];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface SelectCompiler {
|
|
41
|
-
compileSelect(ast: SelectQueryNode): CompiledQuery;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface InsertCompiler {
|
|
45
|
-
compileInsert(ast: InsertQueryNode): CompiledQuery;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface UpdateCompiler {
|
|
49
|
-
compileUpdate(ast: UpdateQueryNode): CompiledQuery;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface DeleteCompiler {
|
|
53
|
-
compileDelete(ast: DeleteQueryNode): CompiledQuery;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Abstract base class for SQL dialect implementations
|
|
58
|
-
*/
|
|
59
|
-
export abstract class Dialect
|
|
60
|
-
implements SelectCompiler, InsertCompiler, UpdateCompiler, DeleteCompiler
|
|
61
|
-
{
|
|
33
|
+
export interface CompiledQuery {
|
|
34
|
+
/** Generated SQL string */
|
|
35
|
+
sql: string;
|
|
36
|
+
/** Parameters for the query */
|
|
37
|
+
params: unknown[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface SelectCompiler {
|
|
41
|
+
compileSelect(ast: SelectQueryNode): CompiledQuery;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface InsertCompiler {
|
|
45
|
+
compileInsert(ast: InsertQueryNode): CompiledQuery;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface UpdateCompiler {
|
|
49
|
+
compileUpdate(ast: UpdateQueryNode): CompiledQuery;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface DeleteCompiler {
|
|
53
|
+
compileDelete(ast: DeleteQueryNode): CompiledQuery;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Abstract base class for SQL dialect implementations
|
|
58
|
+
*/
|
|
59
|
+
export abstract class Dialect
|
|
60
|
+
implements SelectCompiler, InsertCompiler, UpdateCompiler, DeleteCompiler
|
|
61
|
+
{
|
|
62
62
|
/**
|
|
63
63
|
* Compiles a SELECT query AST to SQL
|
|
64
64
|
* @param ast - Query AST to compile
|
|
65
65
|
* @returns Compiled query with SQL and parameters
|
|
66
66
|
*/
|
|
67
|
-
compileSelect(ast: SelectQueryNode): CompiledQuery {
|
|
68
|
-
const ctx = this.createCompilerContext();
|
|
69
|
-
const rawSql = this.compileSelectAst(ast, ctx).trim();
|
|
70
|
-
const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
|
|
71
|
-
return {
|
|
72
|
-
sql,
|
|
73
|
-
params: [...ctx.params]
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
compileInsert(ast: InsertQueryNode): CompiledQuery {
|
|
78
|
-
const ctx = this.createCompilerContext();
|
|
79
|
-
const rawSql = this.compileInsertAst(ast, ctx).trim();
|
|
80
|
-
const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
|
|
81
|
-
return {
|
|
82
|
-
sql,
|
|
83
|
-
params: [...ctx.params]
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
compileUpdate(ast: UpdateQueryNode): CompiledQuery {
|
|
88
|
-
const ctx = this.createCompilerContext();
|
|
89
|
-
const rawSql = this.compileUpdateAst(ast, ctx).trim();
|
|
90
|
-
const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
|
|
91
|
-
return {
|
|
92
|
-
sql,
|
|
93
|
-
params: [...ctx.params]
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
compileDelete(ast: DeleteQueryNode): CompiledQuery {
|
|
98
|
-
const ctx = this.createCompilerContext();
|
|
99
|
-
const rawSql = this.compileDeleteAst(ast, ctx).trim();
|
|
100
|
-
const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
|
|
101
|
-
return {
|
|
102
|
-
sql,
|
|
103
|
-
params: [...ctx.params]
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Compiles SELECT query AST to SQL (to be implemented by concrete dialects)
|
|
109
|
-
* @param ast - Query AST
|
|
110
|
-
* @param ctx - Compiler context
|
|
111
|
-
* @returns SQL string
|
|
112
|
-
*/
|
|
113
|
-
protected abstract compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
|
|
114
|
-
|
|
115
|
-
protected abstract compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string;
|
|
116
|
-
protected abstract compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string;
|
|
117
|
-
protected abstract compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string;
|
|
67
|
+
compileSelect(ast: SelectQueryNode): CompiledQuery {
|
|
68
|
+
const ctx = this.createCompilerContext();
|
|
69
|
+
const rawSql = this.compileSelectAst(ast, ctx).trim();
|
|
70
|
+
const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
|
|
71
|
+
return {
|
|
72
|
+
sql,
|
|
73
|
+
params: [...ctx.params]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
compileInsert(ast: InsertQueryNode): CompiledQuery {
|
|
78
|
+
const ctx = this.createCompilerContext();
|
|
79
|
+
const rawSql = this.compileInsertAst(ast, ctx).trim();
|
|
80
|
+
const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
|
|
81
|
+
return {
|
|
82
|
+
sql,
|
|
83
|
+
params: [...ctx.params]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
compileUpdate(ast: UpdateQueryNode): CompiledQuery {
|
|
88
|
+
const ctx = this.createCompilerContext();
|
|
89
|
+
const rawSql = this.compileUpdateAst(ast, ctx).trim();
|
|
90
|
+
const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
|
|
91
|
+
return {
|
|
92
|
+
sql,
|
|
93
|
+
params: [...ctx.params]
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
compileDelete(ast: DeleteQueryNode): CompiledQuery {
|
|
98
|
+
const ctx = this.createCompilerContext();
|
|
99
|
+
const rawSql = this.compileDeleteAst(ast, ctx).trim();
|
|
100
|
+
const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
|
|
101
|
+
return {
|
|
102
|
+
sql,
|
|
103
|
+
params: [...ctx.params]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Compiles SELECT query AST to SQL (to be implemented by concrete dialects)
|
|
109
|
+
* @param ast - Query AST
|
|
110
|
+
* @param ctx - Compiler context
|
|
111
|
+
* @returns SQL string
|
|
112
|
+
*/
|
|
113
|
+
protected abstract compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
|
|
114
|
+
|
|
115
|
+
protected abstract compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string;
|
|
116
|
+
protected abstract compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string;
|
|
117
|
+
protected abstract compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string;
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
120
|
* Quotes an SQL identifier (to be implemented by concrete dialects)
|
|
@@ -129,18 +129,18 @@ export abstract class Dialect
|
|
|
129
129
|
* @param ctx - Compiler context
|
|
130
130
|
* @returns SQL WHERE clause or empty string
|
|
131
131
|
*/
|
|
132
|
-
protected compileWhere(where: ExpressionNode | undefined, ctx: CompilerContext): string {
|
|
133
|
-
if (!where) return '';
|
|
134
|
-
return ` WHERE ${this.compileExpression(where, ctx)}`;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
protected compileReturning(
|
|
138
|
-
returning: ColumnNode[] | undefined,
|
|
139
|
-
ctx: CompilerContext
|
|
140
|
-
): string {
|
|
141
|
-
if (!returning || returning.length === 0) return '';
|
|
142
|
-
throw new Error('RETURNING is not supported by this dialect.');
|
|
143
|
-
}
|
|
132
|
+
protected compileWhere(where: ExpressionNode | undefined, ctx: CompilerContext): string {
|
|
133
|
+
if (!where) return '';
|
|
134
|
+
return ` WHERE ${this.compileExpression(where, ctx)}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
protected compileReturning(
|
|
138
|
+
returning: ColumnNode[] | undefined,
|
|
139
|
+
ctx: CompilerContext
|
|
140
|
+
): string {
|
|
141
|
+
if (!returning || returning.length === 0) return '';
|
|
142
|
+
throw new Error('RETURNING is not supported by this dialect.');
|
|
143
|
+
}
|
|
144
144
|
|
|
145
145
|
/**
|
|
146
146
|
* Generates subquery for EXISTS expressions
|