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
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { eq } from '../core/ast/expression.js';
|
|
2
|
+
import type { Dialect, CompiledQuery } from '../core/dialect/abstract.js';
|
|
3
|
+
import { InsertQueryBuilder } from '../query-builder/insert.js';
|
|
4
|
+
import { UpdateQueryBuilder } from '../query-builder/update.js';
|
|
5
|
+
import { DeleteQueryBuilder } from '../query-builder/delete.js';
|
|
6
|
+
import { findPrimaryKey } from '../query-builder/hydration-planner.js';
|
|
7
|
+
import type { TableDef, TableHooks } from '../schema/table.js';
|
|
8
|
+
import type { DbExecutor } from './db-executor.js';
|
|
9
|
+
import { IdentityMap } from './identity-map.js';
|
|
10
|
+
import { EntityStatus } from './runtime-types.js';
|
|
11
|
+
import type { TrackedEntity } from './runtime-types.js';
|
|
12
|
+
|
|
13
|
+
export class UnitOfWork {
|
|
14
|
+
private readonly trackedEntities = new Map<any, TrackedEntity>();
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly dialect: Dialect,
|
|
18
|
+
private readonly executor: DbExecutor,
|
|
19
|
+
private readonly identityMap: IdentityMap,
|
|
20
|
+
private readonly hookContext: () => unknown
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
get identityBuckets(): Map<string, Map<string, TrackedEntity>> {
|
|
24
|
+
return this.identityMap.bucketsMap;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getTracked(): TrackedEntity[] {
|
|
28
|
+
return Array.from(this.trackedEntities.values());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getEntity(table: TableDef, pk: string | number): any | undefined {
|
|
32
|
+
return this.identityMap.getEntity(table, pk);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getEntitiesForTable(table: TableDef): TrackedEntity[] {
|
|
36
|
+
return this.identityMap.getEntitiesForTable(table);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
findTracked(entity: any): TrackedEntity | undefined {
|
|
40
|
+
return this.trackedEntities.get(entity);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setEntity(table: TableDef, pk: string | number, entity: any): void {
|
|
44
|
+
if (pk === null || pk === undefined) return;
|
|
45
|
+
let tracked = this.trackedEntities.get(entity);
|
|
46
|
+
if (!tracked) {
|
|
47
|
+
tracked = {
|
|
48
|
+
table,
|
|
49
|
+
entity,
|
|
50
|
+
pk,
|
|
51
|
+
status: EntityStatus.Managed,
|
|
52
|
+
original: this.createSnapshot(table, entity)
|
|
53
|
+
};
|
|
54
|
+
this.trackedEntities.set(entity, tracked);
|
|
55
|
+
} else {
|
|
56
|
+
tracked.pk = pk;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.registerIdentity(tracked);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
trackNew(table: TableDef, entity: any, pk?: string | number): void {
|
|
63
|
+
const tracked: TrackedEntity = {
|
|
64
|
+
table,
|
|
65
|
+
entity,
|
|
66
|
+
pk: pk ?? null,
|
|
67
|
+
status: EntityStatus.New,
|
|
68
|
+
original: null
|
|
69
|
+
};
|
|
70
|
+
this.trackedEntities.set(entity, tracked);
|
|
71
|
+
if (pk != null) {
|
|
72
|
+
this.registerIdentity(tracked);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
trackManaged(table: TableDef, pk: string | number, entity: any): void {
|
|
77
|
+
const tracked: TrackedEntity = {
|
|
78
|
+
table,
|
|
79
|
+
entity,
|
|
80
|
+
pk,
|
|
81
|
+
status: EntityStatus.Managed,
|
|
82
|
+
original: this.createSnapshot(table, entity)
|
|
83
|
+
};
|
|
84
|
+
this.trackedEntities.set(entity, tracked);
|
|
85
|
+
this.registerIdentity(tracked);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
markDirty(entity: any): void {
|
|
89
|
+
const tracked = this.trackedEntities.get(entity);
|
|
90
|
+
if (!tracked) return;
|
|
91
|
+
if (tracked.status === EntityStatus.New || tracked.status === EntityStatus.Removed) return;
|
|
92
|
+
tracked.status = EntityStatus.Dirty;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
markRemoved(entity: any): void {
|
|
96
|
+
const tracked = this.trackedEntities.get(entity);
|
|
97
|
+
if (!tracked) return;
|
|
98
|
+
tracked.status = EntityStatus.Removed;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async flush(): Promise<void> {
|
|
102
|
+
const toFlush = Array.from(this.trackedEntities.values());
|
|
103
|
+
for (const tracked of toFlush) {
|
|
104
|
+
switch (tracked.status) {
|
|
105
|
+
case EntityStatus.New:
|
|
106
|
+
await this.flushInsert(tracked);
|
|
107
|
+
break;
|
|
108
|
+
case EntityStatus.Dirty:
|
|
109
|
+
await this.flushUpdate(tracked);
|
|
110
|
+
break;
|
|
111
|
+
case EntityStatus.Removed:
|
|
112
|
+
await this.flushDelete(tracked);
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private async flushInsert(tracked: TrackedEntity): Promise<void> {
|
|
121
|
+
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
122
|
+
|
|
123
|
+
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
124
|
+
const builder = new InsertQueryBuilder(tracked.table).values(payload);
|
|
125
|
+
const compiled = builder.compile(this.dialect);
|
|
126
|
+
await this.executeCompiled(compiled);
|
|
127
|
+
|
|
128
|
+
tracked.status = EntityStatus.Managed;
|
|
129
|
+
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
130
|
+
tracked.pk = this.getPrimaryKeyValue(tracked);
|
|
131
|
+
this.registerIdentity(tracked);
|
|
132
|
+
|
|
133
|
+
await this.runHook(tracked.table.hooks?.afterInsert, tracked);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private async flushUpdate(tracked: TrackedEntity): Promise<void> {
|
|
137
|
+
if (tracked.pk == null) return;
|
|
138
|
+
const changes = this.computeChanges(tracked);
|
|
139
|
+
if (!Object.keys(changes).length) {
|
|
140
|
+
tracked.status = EntityStatus.Managed;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
await this.runHook(tracked.table.hooks?.beforeUpdate, tracked);
|
|
145
|
+
|
|
146
|
+
const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
|
|
147
|
+
if (!pkColumn) return;
|
|
148
|
+
|
|
149
|
+
const builder = new UpdateQueryBuilder(tracked.table)
|
|
150
|
+
.set(changes)
|
|
151
|
+
.where(eq(pkColumn, tracked.pk));
|
|
152
|
+
|
|
153
|
+
const compiled = builder.compile(this.dialect);
|
|
154
|
+
await this.executeCompiled(compiled);
|
|
155
|
+
|
|
156
|
+
tracked.status = EntityStatus.Managed;
|
|
157
|
+
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
158
|
+
this.registerIdentity(tracked);
|
|
159
|
+
|
|
160
|
+
await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private async flushDelete(tracked: TrackedEntity): Promise<void> {
|
|
164
|
+
if (tracked.pk == null) return;
|
|
165
|
+
await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
|
|
166
|
+
|
|
167
|
+
const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
|
|
168
|
+
if (!pkColumn) return;
|
|
169
|
+
|
|
170
|
+
const builder = new DeleteQueryBuilder(tracked.table).where(eq(pkColumn, tracked.pk));
|
|
171
|
+
const compiled = builder.compile(this.dialect);
|
|
172
|
+
await this.executeCompiled(compiled);
|
|
173
|
+
|
|
174
|
+
tracked.status = EntityStatus.Detached;
|
|
175
|
+
this.trackedEntities.delete(tracked.entity);
|
|
176
|
+
this.identityMap.remove(tracked);
|
|
177
|
+
|
|
178
|
+
await this.runHook(tracked.table.hooks?.afterDelete, tracked);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private async runHook(
|
|
182
|
+
hook: TableHooks[keyof TableHooks] | undefined,
|
|
183
|
+
tracked: TrackedEntity
|
|
184
|
+
): Promise<void> {
|
|
185
|
+
if (!hook) return;
|
|
186
|
+
await hook(this.hookContext() as any, tracked.entity);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private computeChanges(tracked: TrackedEntity): Record<string, unknown> {
|
|
190
|
+
const snapshot = tracked.original ?? {};
|
|
191
|
+
const changes: Record<string, unknown> = {};
|
|
192
|
+
for (const column of Object.keys(tracked.table.columns)) {
|
|
193
|
+
const current = tracked.entity[column];
|
|
194
|
+
if (snapshot[column] !== current) {
|
|
195
|
+
changes[column] = current;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return changes;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private extractColumns(table: TableDef, entity: any): Record<string, unknown> {
|
|
202
|
+
const payload: Record<string, unknown> = {};
|
|
203
|
+
for (const column of Object.keys(table.columns)) {
|
|
204
|
+
payload[column] = entity[column];
|
|
205
|
+
}
|
|
206
|
+
return payload;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private async executeCompiled(compiled: CompiledQuery): Promise<void> {
|
|
210
|
+
await this.executor.executeSql(compiled.sql, compiled.params);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private registerIdentity(tracked: TrackedEntity): void {
|
|
214
|
+
if (tracked.pk == null) return;
|
|
215
|
+
this.identityMap.register(tracked);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private createSnapshot(table: TableDef, entity: any): Record<string, any> {
|
|
219
|
+
const snapshot: Record<string, any> = {};
|
|
220
|
+
for (const column of Object.keys(table.columns)) {
|
|
221
|
+
snapshot[column] = entity[column];
|
|
222
|
+
}
|
|
223
|
+
return snapshot;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private getPrimaryKeyValue(tracked: TrackedEntity): string | number | null {
|
|
227
|
+
const key = findPrimaryKey(tracked.table);
|
|
228
|
+
const val = tracked.entity[key];
|
|
229
|
+
if (val === undefined || val === null) return null;
|
|
230
|
+
return val;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import { ColumnDef } from '
|
|
2
|
-
import { CaseExpressionNode, ColumnNode, FunctionNode, WindowFunctionNode } from '
|
|
3
|
-
import { SelectQueryNode } from '
|
|
4
|
-
import { buildColumnNode } from '../
|
|
5
|
-
import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Type for column selection input
|
|
9
|
-
*/
|
|
10
|
-
type ColumnSelectionInput = Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Handles column selection operations for the query builder
|
|
14
|
-
*/
|
|
15
|
-
export class ColumnSelector {
|
|
16
|
-
/**
|
|
17
|
-
* Creates a new ColumnSelector instance
|
|
18
|
-
* @param env - Query builder environment
|
|
19
|
-
*/
|
|
20
|
-
constructor(private readonly env: SelectQueryBuilderEnvironment) {}
|
|
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
|
-
*/
|
|
28
|
-
select(context: SelectQueryBuilderContext, columns: ColumnSelectionInput): SelectQueryBuilderContext {
|
|
29
|
-
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
30
|
-
const { state: nextState, addedColumns } = astService.select(columns);
|
|
31
|
-
return {
|
|
32
|
-
state: nextState,
|
|
33
|
-
hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
|
|
34
|
-
};
|
|
35
|
-
}
|
|
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
|
-
*/
|
|
43
|
-
selectRaw(context: SelectQueryBuilderContext, columns: string[]): SelectQueryBuilderContext {
|
|
44
|
-
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
45
|
-
const nextState = astService.selectRaw(columns).state;
|
|
46
|
-
return { state: nextState, hydration: context.hydration };
|
|
47
|
-
}
|
|
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
|
-
*/
|
|
56
|
-
selectSubquery(
|
|
57
|
-
context: SelectQueryBuilderContext,
|
|
58
|
-
alias: string,
|
|
59
|
-
query: SelectQueryNode
|
|
60
|
-
): SelectQueryBuilderContext {
|
|
61
|
-
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
62
|
-
const nextState = astService.selectSubquery(alias, query);
|
|
63
|
-
return { state: nextState, hydration: context.hydration };
|
|
64
|
-
}
|
|
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
|
-
*/
|
|
72
|
-
distinct(context: SelectQueryBuilderContext, columns: (ColumnDef | ColumnNode)[]): SelectQueryBuilderContext {
|
|
73
|
-
const nodes = columns.map(col => buildColumnNode(this.env.table, col));
|
|
74
|
-
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
75
|
-
const nextState = astService.withDistinct(nodes);
|
|
76
|
-
return { state: nextState, hydration: context.hydration };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
1
|
+
import { ColumnDef } from '../schema/column.js';
|
|
2
|
+
import { CaseExpressionNode, ColumnNode, FunctionNode, WindowFunctionNode } from '../core/ast/expression.js';
|
|
3
|
+
import { SelectQueryNode } from '../core/ast/query.js';
|
|
4
|
+
import { buildColumnNode } from '../core/ast/builders.js';
|
|
5
|
+
import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from './select-query-builder-deps.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Type for column selection input
|
|
9
|
+
*/
|
|
10
|
+
type ColumnSelectionInput = Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Handles column selection operations for the query builder
|
|
14
|
+
*/
|
|
15
|
+
export class ColumnSelector {
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new ColumnSelector instance
|
|
18
|
+
* @param env - Query builder environment
|
|
19
|
+
*/
|
|
20
|
+
constructor(private readonly env: SelectQueryBuilderEnvironment) {}
|
|
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
|
+
*/
|
|
28
|
+
select(context: SelectQueryBuilderContext, columns: ColumnSelectionInput): SelectQueryBuilderContext {
|
|
29
|
+
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
30
|
+
const { state: nextState, addedColumns } = astService.select(columns);
|
|
31
|
+
return {
|
|
32
|
+
state: nextState,
|
|
33
|
+
hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
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
|
+
*/
|
|
43
|
+
selectRaw(context: SelectQueryBuilderContext, columns: string[]): SelectQueryBuilderContext {
|
|
44
|
+
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
45
|
+
const nextState = astService.selectRaw(columns).state;
|
|
46
|
+
return { state: nextState, hydration: context.hydration };
|
|
47
|
+
}
|
|
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
|
+
*/
|
|
56
|
+
selectSubquery(
|
|
57
|
+
context: SelectQueryBuilderContext,
|
|
58
|
+
alias: string,
|
|
59
|
+
query: SelectQueryNode
|
|
60
|
+
): SelectQueryBuilderContext {
|
|
61
|
+
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
62
|
+
const nextState = astService.selectSubquery(alias, query);
|
|
63
|
+
return { state: nextState, hydration: context.hydration };
|
|
64
|
+
}
|
|
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
|
+
*/
|
|
72
|
+
distinct(context: SelectQueryBuilderContext, columns: (ColumnDef | ColumnNode)[]): SelectQueryBuilderContext {
|
|
73
|
+
const nodes = columns.map(col => buildColumnNode(this.env.table, col));
|
|
74
|
+
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
75
|
+
const nextState = astService.withDistinct(nodes);
|
|
76
|
+
return { state: nextState, hydration: context.hydration };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,42 +1,38 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table';
|
|
2
|
-
import { ColumnNode, ExpressionNode } from '../ast/expression';
|
|
3
|
-
import { TableNode, DeleteQueryNode } from '../ast/query';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
returning: [...columns]
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { ColumnNode, ExpressionNode } from '../core/ast/expression.js';
|
|
3
|
+
import { TableNode, DeleteQueryNode } from '../core/ast/query.js';
|
|
4
|
+
import { createTableNode } from '../core/ast/builders.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Maintains immutable state for DELETE queries
|
|
8
|
+
*/
|
|
9
|
+
export class DeleteQueryState {
|
|
10
|
+
public readonly table: TableDef;
|
|
11
|
+
public readonly ast: DeleteQueryNode;
|
|
12
|
+
|
|
13
|
+
constructor(table: TableDef, ast?: DeleteQueryNode) {
|
|
14
|
+
this.table = table;
|
|
15
|
+
this.ast = ast ?? {
|
|
16
|
+
type: 'DeleteQuery',
|
|
17
|
+
from: createTableNode(table)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private clone(nextAst: DeleteQueryNode): DeleteQueryState {
|
|
22
|
+
return new DeleteQueryState(this.table, nextAst);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
withWhere(expr: ExpressionNode): DeleteQueryState {
|
|
26
|
+
return this.clone({
|
|
27
|
+
...this.ast,
|
|
28
|
+
where: expr
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
withReturning(columns: ColumnNode[]): DeleteQueryState {
|
|
33
|
+
return this.clone({
|
|
34
|
+
...this.ast,
|
|
35
|
+
returning: [...columns]
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,57 +1,46 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table';
|
|
2
|
-
import { ColumnDef } from '../schema/column';
|
|
3
|
-
import { ColumnNode, ExpressionNode } from '../ast/expression';
|
|
4
|
-
import { CompiledQuery, DeleteCompiler } from '../dialect/abstract';
|
|
5
|
-
import { DeleteQueryNode } from '../ast/query';
|
|
6
|
-
import { DeleteQueryState } from './delete-query-state';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return compiler.compileDelete(this.state.ast);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
toSql(compiler: DeleteCompiler): string {
|
|
51
|
-
return this.compile(compiler).sql;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
getAST(): DeleteQueryNode {
|
|
55
|
-
return this.state.ast;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { ColumnDef } from '../schema/column.js';
|
|
3
|
+
import { ColumnNode, ExpressionNode } from '../core/ast/expression.js';
|
|
4
|
+
import { CompiledQuery, DeleteCompiler } from '../core/dialect/abstract.js';
|
|
5
|
+
import { DeleteQueryNode } from '../core/ast/query.js';
|
|
6
|
+
import { DeleteQueryState } from './delete-query-state.js';
|
|
7
|
+
import { buildColumnNode } from '../core/ast/builders.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Builder for DELETE queries
|
|
11
|
+
*/
|
|
12
|
+
export class DeleteQueryBuilder<T> {
|
|
13
|
+
private readonly table: TableDef;
|
|
14
|
+
private readonly state: DeleteQueryState;
|
|
15
|
+
|
|
16
|
+
constructor(table: TableDef, state?: DeleteQueryState) {
|
|
17
|
+
this.table = table;
|
|
18
|
+
this.state = state ?? new DeleteQueryState(table);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private clone(state: DeleteQueryState): DeleteQueryBuilder<T> {
|
|
22
|
+
return new DeleteQueryBuilder(this.table, state);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
where(expr: ExpressionNode): DeleteQueryBuilder<T> {
|
|
26
|
+
return this.clone(this.state.withWhere(expr));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
returning(...columns: (ColumnDef | ColumnNode)[]): DeleteQueryBuilder<T> {
|
|
30
|
+
if (!columns.length) return this;
|
|
31
|
+
const nodes = columns.map(column => buildColumnNode(this.table, column));
|
|
32
|
+
return this.clone(this.state.withReturning(nodes));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
compile(compiler: DeleteCompiler): CompiledQuery {
|
|
36
|
+
return compiler.compileDelete(this.state.ast);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
toSql(compiler: DeleteCompiler): string {
|
|
40
|
+
return this.compile(compiler).sql;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getAST(): DeleteQueryNode {
|
|
44
|
+
return this.state.ast;
|
|
45
|
+
}
|
|
46
|
+
}
|