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
package/src/orm/save-graph.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
EntityInstance,
|
|
3
|
+
HasManyCollection,
|
|
4
|
+
HasOneReference,
|
|
5
|
+
BelongsToReference,
|
|
6
|
+
ManyToManyCollection
|
|
7
|
+
} from '../schema/types.js';
|
|
2
8
|
import {
|
|
3
9
|
RelationKinds,
|
|
4
10
|
type BelongsToManyRelation,
|
|
@@ -19,12 +25,12 @@ export interface SaveGraphOptions {
|
|
|
19
25
|
pruneMissing?: boolean;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
type AnyEntity = Record<string,
|
|
28
|
+
type AnyEntity = Record<string, unknown>;
|
|
23
29
|
|
|
24
30
|
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
25
31
|
|
|
26
|
-
const pickColumns = (table: TableDef, payload: AnyEntity): Record<string,
|
|
27
|
-
const columns: Record<string,
|
|
32
|
+
const pickColumns = (table: TableDef, payload: AnyEntity): Record<string, unknown> => {
|
|
33
|
+
const columns: Record<string, unknown> = {};
|
|
28
34
|
for (const key of Object.keys(table.columns)) {
|
|
29
35
|
if (payload[key] !== undefined) {
|
|
30
36
|
columns[key] = payload[key];
|
|
@@ -71,7 +77,7 @@ const isEntityInCollection = (items: AnyEntity[], pkName: string, entity: AnyEnt
|
|
|
71
77
|
return items.some(item => toKey(item[pkName]) === toKey(entityPk));
|
|
72
78
|
};
|
|
73
79
|
|
|
74
|
-
const findInCollectionByPk = (items: AnyEntity[], pkName: string, pkValue:
|
|
80
|
+
const findInCollectionByPk = (items: AnyEntity[], pkName: string, pkValue: unknown): AnyEntity | undefined => {
|
|
75
81
|
if (pkValue === undefined || pkValue === null) return undefined;
|
|
76
82
|
return items.find(item => toKey(item[pkName]) === toKey(pkValue));
|
|
77
83
|
};
|
|
@@ -85,12 +91,12 @@ const handleHasMany = async (
|
|
|
85
91
|
options: SaveGraphOptions
|
|
86
92
|
): Promise<void> => {
|
|
87
93
|
if (!Array.isArray(payload)) return;
|
|
88
|
-
const collection = root[relationName]
|
|
94
|
+
const collection = root[relationName] as unknown as HasManyCollection<unknown>;
|
|
89
95
|
await collection.load();
|
|
90
96
|
|
|
91
97
|
const targetTable = relation.target;
|
|
92
98
|
const targetPk = findPrimaryKey(targetTable);
|
|
93
|
-
const existing = collection.getItems();
|
|
99
|
+
const existing = collection.getItems() as unknown as AnyEntity[];
|
|
94
100
|
const seen = new Set<string>();
|
|
95
101
|
|
|
96
102
|
for (const item of payload) {
|
|
@@ -103,10 +109,10 @@ const handleHasMany = async (
|
|
|
103
109
|
(pkValue !== undefined && pkValue !== null ? session.getEntity(targetTable, pkValue) : undefined);
|
|
104
110
|
|
|
105
111
|
const entity = current ?? ensureEntity(session, targetTable, asObj);
|
|
106
|
-
assignColumns(targetTable, entity, asObj);
|
|
107
|
-
await applyGraphToEntity(session, targetTable, entity, asObj, options);
|
|
112
|
+
assignColumns(targetTable, entity as AnyEntity, asObj);
|
|
113
|
+
await applyGraphToEntity(session, targetTable, entity as AnyEntity, asObj, options);
|
|
108
114
|
|
|
109
|
-
if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
|
|
115
|
+
if (!isEntityInCollection(collection.getItems() as unknown as AnyEntity[], targetPk, entity as unknown as AnyEntity)) {
|
|
110
116
|
collection.attach(entity);
|
|
111
117
|
}
|
|
112
118
|
|
|
@@ -133,7 +139,7 @@ const handleHasOne = async (
|
|
|
133
139
|
payload: unknown,
|
|
134
140
|
options: SaveGraphOptions
|
|
135
141
|
): Promise<void> => {
|
|
136
|
-
const ref = root[relationName]
|
|
142
|
+
const ref = root[relationName] as unknown as HasOneReference<unknown>;
|
|
137
143
|
if (payload === undefined) return;
|
|
138
144
|
if (payload === null) {
|
|
139
145
|
ref.set(null);
|
|
@@ -143,13 +149,13 @@ const handleHasOne = async (
|
|
|
143
149
|
if (typeof payload === 'number' || typeof payload === 'string') {
|
|
144
150
|
const entity = ref.set({ [pk]: payload });
|
|
145
151
|
if (entity) {
|
|
146
|
-
await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
|
|
152
|
+
await applyGraphToEntity(session, relation.target, entity as AnyEntity, { [pk]: payload }, options);
|
|
147
153
|
}
|
|
148
154
|
return;
|
|
149
155
|
}
|
|
150
156
|
const attached = ref.set(payload as AnyEntity);
|
|
151
157
|
if (attached) {
|
|
152
|
-
await applyGraphToEntity(session, relation.target, attached, payload as AnyEntity, options);
|
|
158
|
+
await applyGraphToEntity(session, relation.target, attached as AnyEntity, payload as AnyEntity, options);
|
|
153
159
|
}
|
|
154
160
|
};
|
|
155
161
|
|
|
@@ -161,7 +167,7 @@ const handleBelongsTo = async (
|
|
|
161
167
|
payload: unknown,
|
|
162
168
|
options: SaveGraphOptions
|
|
163
169
|
): Promise<void> => {
|
|
164
|
-
const ref = root[relationName]
|
|
170
|
+
const ref = root[relationName] as unknown as BelongsToReference<unknown>;
|
|
165
171
|
if (payload === undefined) return;
|
|
166
172
|
if (payload === null) {
|
|
167
173
|
ref.set(null);
|
|
@@ -171,13 +177,13 @@ const handleBelongsTo = async (
|
|
|
171
177
|
if (typeof payload === 'number' || typeof payload === 'string') {
|
|
172
178
|
const entity = ref.set({ [pk]: payload });
|
|
173
179
|
if (entity) {
|
|
174
|
-
await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
|
|
180
|
+
await applyGraphToEntity(session, relation.target, entity as AnyEntity, { [pk]: payload }, options);
|
|
175
181
|
}
|
|
176
182
|
return;
|
|
177
183
|
}
|
|
178
184
|
const attached = ref.set(payload as AnyEntity);
|
|
179
185
|
if (attached) {
|
|
180
|
-
await applyGraphToEntity(session, relation.target, attached, payload as AnyEntity, options);
|
|
186
|
+
await applyGraphToEntity(session, relation.target, attached as AnyEntity, payload as AnyEntity, options);
|
|
181
187
|
}
|
|
182
188
|
};
|
|
183
189
|
|
|
@@ -190,7 +196,7 @@ const handleBelongsToMany = async (
|
|
|
190
196
|
options: SaveGraphOptions
|
|
191
197
|
): Promise<void> => {
|
|
192
198
|
if (!Array.isArray(payload)) return;
|
|
193
|
-
const collection = root[relationName]
|
|
199
|
+
const collection = root[relationName] as unknown as ManyToManyCollection<unknown>;
|
|
194
200
|
await collection.load();
|
|
195
201
|
|
|
196
202
|
const targetTable = relation.target;
|
|
@@ -212,10 +218,10 @@ const handleBelongsToMany = async (
|
|
|
212
218
|
? session.getEntity(targetTable, pkValue) ?? ensureEntity(session, targetTable, asObj)
|
|
213
219
|
: ensureEntity(session, targetTable, asObj);
|
|
214
220
|
|
|
215
|
-
assignColumns(targetTable, entity, asObj);
|
|
216
|
-
await applyGraphToEntity(session, targetTable, entity, asObj, options);
|
|
221
|
+
assignColumns(targetTable, entity as AnyEntity, asObj);
|
|
222
|
+
await applyGraphToEntity(session, targetTable, entity as AnyEntity, asObj, options);
|
|
217
223
|
|
|
218
|
-
if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
|
|
224
|
+
if (!isEntityInCollection(collection.getItems() as unknown as AnyEntity[], targetPk, entity as unknown as AnyEntity)) {
|
|
219
225
|
collection.attach(entity);
|
|
220
226
|
}
|
|
221
227
|
|
|
@@ -225,7 +231,7 @@ const handleBelongsToMany = async (
|
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
if (options.pruneMissing) {
|
|
228
|
-
for (const item of [...collection.getItems()]) {
|
|
234
|
+
for (const item of [...collection.getItems()] as unknown as AnyEntity[]) {
|
|
229
235
|
const pkValue = item[targetPk];
|
|
230
236
|
if (pkValue !== undefined && pkValue !== null && !seen.has(toKey(pkValue))) {
|
|
231
237
|
collection.detach(item);
|
|
@@ -272,7 +278,7 @@ const applyGraphToEntity = async (
|
|
|
272
278
|
|
|
273
279
|
export const saveGraph = async <TTable extends TableDef>(
|
|
274
280
|
session: OrmSession,
|
|
275
|
-
entityClass: EntityConstructor
|
|
281
|
+
entityClass: EntityConstructor,
|
|
276
282
|
payload: AnyEntity,
|
|
277
283
|
options: SaveGraphOptions = {}
|
|
278
284
|
): Promise<EntityInstance<TTable>> => {
|
|
@@ -282,11 +288,11 @@ export const saveGraph = async <TTable extends TableDef>(
|
|
|
282
288
|
}
|
|
283
289
|
|
|
284
290
|
const root = ensureEntity<TTable>(session, table as TTable, payload);
|
|
285
|
-
await applyGraphToEntity(session, table, root, payload, options);
|
|
291
|
+
await applyGraphToEntity(session, table, root as AnyEntity, payload, options);
|
|
286
292
|
return root;
|
|
287
293
|
};
|
|
288
294
|
|
|
289
|
-
export const saveGraphInternal = async <TCtor extends EntityConstructor
|
|
295
|
+
export const saveGraphInternal = async <TCtor extends EntityConstructor>(
|
|
290
296
|
session: OrmSession,
|
|
291
297
|
entityClass: TCtor,
|
|
292
298
|
payload: AnyEntity,
|
|
@@ -298,6 +304,6 @@ export const saveGraphInternal = async <TCtor extends EntityConstructor<any>>(
|
|
|
298
304
|
}
|
|
299
305
|
|
|
300
306
|
const root = ensureEntity(session, table, payload);
|
|
301
|
-
await applyGraphToEntity(session, table, root, payload, options);
|
|
307
|
+
await applyGraphToEntity(session, table, root as AnyEntity, payload, options);
|
|
302
308
|
return root as unknown as InstanceType<TCtor>;
|
|
303
309
|
};
|
package/src/orm/unit-of-work.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { TrackedEntity } from './runtime-types.js';
|
|
|
14
14
|
* Unit of Work pattern implementation for tracking entity changes.
|
|
15
15
|
*/
|
|
16
16
|
export class UnitOfWork {
|
|
17
|
-
private readonly trackedEntities = new Map<
|
|
17
|
+
private readonly trackedEntities = new Map<unknown, TrackedEntity>();
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Creates a new UnitOfWork instance.
|
|
@@ -51,7 +51,7 @@ export class UnitOfWork {
|
|
|
51
51
|
* @param pk - The primary key value
|
|
52
52
|
* @returns The entity or undefined if not found
|
|
53
53
|
*/
|
|
54
|
-
getEntity(table: TableDef, pk: string | number):
|
|
54
|
+
getEntity(table: TableDef, pk: string | number): unknown | undefined {
|
|
55
55
|
return this.identityMap.getEntity(table, pk);
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -69,7 +69,7 @@ export class UnitOfWork {
|
|
|
69
69
|
* @param entity - The entity to find
|
|
70
70
|
* @returns The tracked entity or undefined if not found
|
|
71
71
|
*/
|
|
72
|
-
findTracked(entity:
|
|
72
|
+
findTracked(entity: unknown): TrackedEntity | undefined {
|
|
73
73
|
return this.trackedEntities.get(entity);
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -79,7 +79,7 @@ export class UnitOfWork {
|
|
|
79
79
|
* @param pk - The primary key value
|
|
80
80
|
* @param entity - The entity instance
|
|
81
81
|
*/
|
|
82
|
-
setEntity(table: TableDef, pk: string | number, entity:
|
|
82
|
+
setEntity(table: TableDef, pk: string | number, entity: unknown): void {
|
|
83
83
|
if (pk === null || pk === undefined) return;
|
|
84
84
|
let tracked = this.trackedEntities.get(entity);
|
|
85
85
|
if (!tracked) {
|
|
@@ -88,7 +88,7 @@ export class UnitOfWork {
|
|
|
88
88
|
entity,
|
|
89
89
|
pk,
|
|
90
90
|
status: EntityStatus.Managed,
|
|
91
|
-
original: this.createSnapshot(table, entity)
|
|
91
|
+
original: this.createSnapshot(table, entity as Record<string, unknown>)
|
|
92
92
|
};
|
|
93
93
|
this.trackedEntities.set(entity, tracked);
|
|
94
94
|
} else {
|
|
@@ -104,7 +104,7 @@ export class UnitOfWork {
|
|
|
104
104
|
* @param entity - The entity instance
|
|
105
105
|
* @param pk - Optional primary key value
|
|
106
106
|
*/
|
|
107
|
-
trackNew(table: TableDef, entity:
|
|
107
|
+
trackNew(table: TableDef, entity: unknown, pk?: string | number): void {
|
|
108
108
|
const tracked: TrackedEntity = {
|
|
109
109
|
table,
|
|
110
110
|
entity,
|
|
@@ -124,13 +124,13 @@ export class UnitOfWork {
|
|
|
124
124
|
* @param pk - The primary key value
|
|
125
125
|
* @param entity - The entity instance
|
|
126
126
|
*/
|
|
127
|
-
trackManaged(table: TableDef, pk: string | number, entity:
|
|
127
|
+
trackManaged(table: TableDef, pk: string | number, entity: unknown): void {
|
|
128
128
|
const tracked: TrackedEntity = {
|
|
129
129
|
table,
|
|
130
130
|
entity,
|
|
131
131
|
pk,
|
|
132
132
|
status: EntityStatus.Managed,
|
|
133
|
-
original: this.createSnapshot(table, entity)
|
|
133
|
+
original: this.createSnapshot(table, entity as Record<string, unknown>)
|
|
134
134
|
};
|
|
135
135
|
this.trackedEntities.set(entity, tracked);
|
|
136
136
|
this.registerIdentity(tracked);
|
|
@@ -140,7 +140,7 @@ export class UnitOfWork {
|
|
|
140
140
|
* Marks an entity as dirty (modified).
|
|
141
141
|
* @param entity - The entity to mark as dirty
|
|
142
142
|
*/
|
|
143
|
-
markDirty(entity:
|
|
143
|
+
markDirty(entity: unknown): void {
|
|
144
144
|
const tracked = this.trackedEntities.get(entity);
|
|
145
145
|
if (!tracked) return;
|
|
146
146
|
if (tracked.status === EntityStatus.New || tracked.status === EntityStatus.Removed) return;
|
|
@@ -151,7 +151,7 @@ export class UnitOfWork {
|
|
|
151
151
|
* Marks an entity as removed.
|
|
152
152
|
* @param entity - The entity to mark as removed
|
|
153
153
|
*/
|
|
154
|
-
markRemoved(entity:
|
|
154
|
+
markRemoved(entity: unknown): void {
|
|
155
155
|
const tracked = this.trackedEntities.get(entity);
|
|
156
156
|
if (!tracked) return;
|
|
157
157
|
tracked.status = EntityStatus.Removed;
|
|
@@ -194,7 +194,7 @@ export class UnitOfWork {
|
|
|
194
194
|
private async flushInsert(tracked: TrackedEntity): Promise<void> {
|
|
195
195
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
196
196
|
|
|
197
|
-
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
197
|
+
const payload = this.extractColumns(tracked.table, tracked.entity as Record<string, unknown>);
|
|
198
198
|
let builder = new InsertQueryBuilder(tracked.table).values(payload);
|
|
199
199
|
if (this.dialect.supportsReturning()) {
|
|
200
200
|
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
@@ -204,7 +204,7 @@ export class UnitOfWork {
|
|
|
204
204
|
this.applyReturningResults(tracked, results);
|
|
205
205
|
|
|
206
206
|
tracked.status = EntityStatus.Managed;
|
|
207
|
-
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
207
|
+
tracked.original = this.createSnapshot(tracked.table, tracked.entity as Record<string, unknown>);
|
|
208
208
|
tracked.pk = this.getPrimaryKeyValue(tracked);
|
|
209
209
|
this.registerIdentity(tracked);
|
|
210
210
|
|
|
@@ -241,7 +241,7 @@ export class UnitOfWork {
|
|
|
241
241
|
this.applyReturningResults(tracked, results);
|
|
242
242
|
|
|
243
243
|
tracked.status = EntityStatus.Managed;
|
|
244
|
-
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
244
|
+
tracked.original = this.createSnapshot(tracked.table, tracked.entity as Record<string, unknown>);
|
|
245
245
|
this.registerIdentity(tracked);
|
|
246
246
|
|
|
247
247
|
await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
|
|
@@ -279,7 +279,7 @@ export class UnitOfWork {
|
|
|
279
279
|
tracked: TrackedEntity
|
|
280
280
|
): Promise<void> {
|
|
281
281
|
if (!hook) return;
|
|
282
|
-
await hook(this.hookContext()
|
|
282
|
+
await hook(this.hookContext(), tracked.entity);
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
/**
|
|
@@ -305,7 +305,7 @@ export class UnitOfWork {
|
|
|
305
305
|
* @param entity - The entity instance
|
|
306
306
|
* @returns Object with column values
|
|
307
307
|
*/
|
|
308
|
-
private extractColumns(table: TableDef, entity:
|
|
308
|
+
private extractColumns(table: TableDef, entity: Record<string, unknown>): Record<string, unknown> {
|
|
309
309
|
const payload: Record<string, unknown> = {};
|
|
310
310
|
for (const column of Object.keys(table.columns)) {
|
|
311
311
|
if (entity[column] === undefined) continue;
|
|
@@ -381,8 +381,8 @@ export class UnitOfWork {
|
|
|
381
381
|
* @param entity - The entity instance
|
|
382
382
|
* @returns Object with entity state
|
|
383
383
|
*/
|
|
384
|
-
private createSnapshot(table: TableDef, entity:
|
|
385
|
-
const snapshot: Record<string,
|
|
384
|
+
private createSnapshot(table: TableDef, entity: Record<string, unknown>): Record<string, unknown> {
|
|
385
|
+
const snapshot: Record<string, unknown> = {};
|
|
386
386
|
for (const column of Object.keys(table.columns)) {
|
|
387
387
|
snapshot[column] = entity[column];
|
|
388
388
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { SelectQueryBuilder } from '../query-builder/select.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 { QueryTarget, resolveTable } from './target.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a SELECT query builder for the specified table or entity.
|
|
10
|
+
*
|
|
11
|
+
* @template TTable - The table definition type
|
|
12
|
+
* @param target - The table definition or entity constructor to query from
|
|
13
|
+
* @returns A new SelectQueryBuilder instance for building SELECT queries
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const query = selectFrom(UserTable).select('id', 'name');
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export const selectFrom = <TTable extends TableDef>(target: QueryTarget<TTable>): SelectQueryBuilder<unknown, TTable> => {
|
|
21
|
+
const table = resolveTable(target);
|
|
22
|
+
return new SelectQueryBuilder(table);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates an INSERT query builder for the specified table or entity.
|
|
27
|
+
*
|
|
28
|
+
* @template TTable - The table definition type
|
|
29
|
+
* @param target - The table definition or entity constructor to insert into
|
|
30
|
+
* @returns A new InsertQueryBuilder instance for building INSERT queries
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const query = insertInto(UserTable).values({ name: 'John', email: 'john@example.com' });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export const insertInto = <TTable extends TableDef>(target: QueryTarget<TTable>): InsertQueryBuilder<unknown> => {
|
|
38
|
+
const table = resolveTable(target);
|
|
39
|
+
return new InsertQueryBuilder(table);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates an UPDATE query builder for the specified table or entity.
|
|
44
|
+
*
|
|
45
|
+
* @template TTable - The table definition type
|
|
46
|
+
* @param target - The table definition or entity constructor to update
|
|
47
|
+
* @returns A new UpdateQueryBuilder instance for building UPDATE queries
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const query = update(UserTable).set({ name: 'Jane' }).where(eq(UserTable.id, 1));
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const update = <TTable extends TableDef>(target: QueryTarget<TTable>): UpdateQueryBuilder<unknown> => {
|
|
55
|
+
const table = resolveTable(target);
|
|
56
|
+
return new UpdateQueryBuilder(table);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates a DELETE query builder for the specified table or entity.
|
|
61
|
+
*
|
|
62
|
+
* @template TTable - The table definition type
|
|
63
|
+
* @param target - The table definition or entity constructor to delete from
|
|
64
|
+
* @returns A new DeleteQueryBuilder instance for building DELETE queries
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const query = deleteFrom(UserTable).where(eq(UserTable.id, 1));
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export const deleteFrom = <TTable extends TableDef>(target: QueryTarget<TTable>): DeleteQueryBuilder<unknown> => {
|
|
72
|
+
const table = resolveTable(target);
|
|
73
|
+
return new DeleteQueryBuilder(table);
|
|
74
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { isTableDef } from '../schema/table-guards.js';
|
|
3
|
+
import { EntityConstructor } from '../orm/entity-metadata.js';
|
|
4
|
+
import { getTableDefFromEntity } from '../decorators/bootstrap.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Represents a target for query operations, which can be either a table definition
|
|
8
|
+
* or an entity constructor. This type allows flexible targeting of database tables
|
|
9
|
+
* through either direct table definitions or entity classes decorated with ORM metadata.
|
|
10
|
+
*
|
|
11
|
+
* @template TTable - The table definition type, defaults to TableDef
|
|
12
|
+
*/
|
|
13
|
+
export type QueryTarget<TTable extends TableDef = TableDef> = TTable | EntityConstructor;
|
|
14
|
+
|
|
15
|
+
const resolveEntityTarget = <TTable extends TableDef>(ctor: EntityConstructor): TTable => {
|
|
16
|
+
const table = getTableDefFromEntity(ctor);
|
|
17
|
+
if (!table) {
|
|
18
|
+
throw new Error(`Entity '${ctor.name}' is not registered with decorators`);
|
|
19
|
+
}
|
|
20
|
+
return table as TTable;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolves a QueryTarget to its corresponding table definition.
|
|
25
|
+
*
|
|
26
|
+
* If the target is already a TableDef, it returns it directly.
|
|
27
|
+
* If the target is an EntityConstructor, it retrieves the associated table definition
|
|
28
|
+
* from the entity's metadata.
|
|
29
|
+
*
|
|
30
|
+
* @template TTable - The table definition type
|
|
31
|
+
* @param target - The query target to resolve
|
|
32
|
+
* @returns The resolved table definition
|
|
33
|
+
* @throws Error if the entity constructor is not registered with decorators
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const table = resolveTable(UserTable); // Returns UserTable directly
|
|
38
|
+
* const table2 = resolveTable(UserEntity); // Returns table def from UserEntity metadata
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export const resolveTable = <TTable extends TableDef>(target: QueryTarget<TTable>): TTable => {
|
|
42
|
+
if (isTableDef(target)) {
|
|
43
|
+
return target as TTable;
|
|
44
|
+
}
|
|
45
|
+
return resolveEntityTarget(target as EntityConstructor);
|
|
46
|
+
};
|
|
@@ -14,6 +14,11 @@ export class DeleteQueryState {
|
|
|
14
14
|
public readonly table: TableDef;
|
|
15
15
|
public readonly ast: DeleteQueryNode;
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new DeleteQueryState instance
|
|
19
|
+
* @param table - The table definition for the DELETE query
|
|
20
|
+
* @param ast - Optional initial AST node, defaults to a basic DELETE query
|
|
21
|
+
*/
|
|
17
22
|
constructor(table: TableDef, ast?: DeleteQueryNode) {
|
|
18
23
|
this.table = table;
|
|
19
24
|
this.ast = ast ?? {
|
|
@@ -27,6 +32,11 @@ export class DeleteQueryState {
|
|
|
27
32
|
return new DeleteQueryState(this.table, nextAst);
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Adds a WHERE clause to the DELETE query
|
|
37
|
+
* @param expr - The expression to use as the WHERE condition
|
|
38
|
+
* @returns A new DeleteQueryState with the WHERE clause added
|
|
39
|
+
*/
|
|
30
40
|
withWhere(expr: ExpressionNode): DeleteQueryState {
|
|
31
41
|
return this.clone({
|
|
32
42
|
...this.ast,
|
|
@@ -34,6 +44,11 @@ export class DeleteQueryState {
|
|
|
34
44
|
});
|
|
35
45
|
}
|
|
36
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Adds a RETURNING clause to the DELETE query
|
|
49
|
+
* @param columns - The columns to return after deletion
|
|
50
|
+
* @returns A new DeleteQueryState with the RETURNING clause added
|
|
51
|
+
*/
|
|
37
52
|
withReturning(columns: ColumnNode[]): DeleteQueryState {
|
|
38
53
|
return this.clone({
|
|
39
54
|
...this.ast,
|
|
@@ -41,6 +56,11 @@ export class DeleteQueryState {
|
|
|
41
56
|
});
|
|
42
57
|
}
|
|
43
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Adds a USING clause to the DELETE query
|
|
61
|
+
* @param source - The table source to use in the USING clause
|
|
62
|
+
* @returns A new DeleteQueryState with the USING clause added
|
|
63
|
+
*/
|
|
44
64
|
withUsing(source: TableSourceNode): DeleteQueryState {
|
|
45
65
|
return this.clone({
|
|
46
66
|
...this.ast,
|
|
@@ -48,6 +68,11 @@ export class DeleteQueryState {
|
|
|
48
68
|
});
|
|
49
69
|
}
|
|
50
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Adds a JOIN clause to the DELETE query
|
|
73
|
+
* @param join - The join node to add
|
|
74
|
+
* @returns A new DeleteQueryState with the JOIN clause added
|
|
75
|
+
*/
|
|
51
76
|
withJoin(join: JoinNode): DeleteQueryState {
|
|
52
77
|
return this.clone({
|
|
53
78
|
...this.ast,
|
|
@@ -55,6 +80,11 @@ export class DeleteQueryState {
|
|
|
55
80
|
});
|
|
56
81
|
}
|
|
57
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Sets an alias for the table in the DELETE query
|
|
85
|
+
* @param alias - The alias to assign to the table
|
|
86
|
+
* @returns A new DeleteQueryState with the table alias set
|
|
87
|
+
*/
|
|
58
88
|
withTableAlias(alias: string): DeleteQueryState {
|
|
59
89
|
return this.clone({
|
|
60
90
|
...this.ast,
|
|
@@ -2,12 +2,14 @@ import { TableDef } from '../schema/table.js';
|
|
|
2
2
|
import { ColumnDef } from '../schema/column.js';
|
|
3
3
|
import { ColumnNode, ExpressionNode } from '../core/ast/expression.js';
|
|
4
4
|
import { JOIN_KINDS, JoinKind } from '../core/sql/sql.js';
|
|
5
|
-
import { CompiledQuery,
|
|
5
|
+
import { CompiledQuery, Dialect } from '../core/dialect/abstract.js';
|
|
6
6
|
import { DialectKey, resolveDialectInput } from '../core/dialect/dialect-factory.js';
|
|
7
7
|
import { TableSourceNode, DeleteQueryNode } from '../core/ast/query.js';
|
|
8
8
|
import { DeleteQueryState } from './delete-query-state.js';
|
|
9
9
|
import { createJoinNode } from '../core/ast/join-node.js';
|
|
10
10
|
import { buildColumnNode } from '../core/ast/builders.js';
|
|
11
|
+
import { OrmSession } from '../orm/orm-session.js';
|
|
12
|
+
import { QueryResult } from '../core/execution/db-executor.js';
|
|
11
13
|
|
|
12
14
|
type DeleteDialectInput = Dialect | DialectKey;
|
|
13
15
|
|
|
@@ -18,6 +20,11 @@ export class DeleteQueryBuilder<T> {
|
|
|
18
20
|
private readonly table: TableDef;
|
|
19
21
|
private readonly state: DeleteQueryState;
|
|
20
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new DeleteQueryBuilder instance
|
|
25
|
+
* @param table - The table definition for the DELETE query
|
|
26
|
+
* @param state - Optional initial query state, defaults to a new DeleteQueryState
|
|
27
|
+
*/
|
|
21
28
|
constructor(table: TableDef, state?: DeleteQueryState) {
|
|
22
29
|
this.table = table;
|
|
23
30
|
this.state = state ?? new DeleteQueryState(table);
|
|
@@ -27,18 +34,41 @@ export class DeleteQueryBuilder<T> {
|
|
|
27
34
|
return new DeleteQueryBuilder(this.table, state);
|
|
28
35
|
}
|
|
29
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Adds a WHERE clause to the DELETE query
|
|
39
|
+
* @param expr - The expression to use as the WHERE condition
|
|
40
|
+
* @returns A new DeleteQueryBuilder with the WHERE clause added
|
|
41
|
+
*/
|
|
30
42
|
where(expr: ExpressionNode): DeleteQueryBuilder<T> {
|
|
31
43
|
return this.clone(this.state.withWhere(expr));
|
|
32
44
|
}
|
|
33
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Sets an alias for the table in the DELETE query
|
|
48
|
+
* @param alias - The alias to assign to the table
|
|
49
|
+
* @returns A new DeleteQueryBuilder with the table alias set
|
|
50
|
+
*/
|
|
34
51
|
as(alias: string): DeleteQueryBuilder<T> {
|
|
35
52
|
return this.clone(this.state.withTableAlias(alias));
|
|
36
53
|
}
|
|
37
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Adds a USING clause to the DELETE query
|
|
57
|
+
* @param source - The table source to use in the USING clause
|
|
58
|
+
* @returns A new DeleteQueryBuilder with the USING clause added
|
|
59
|
+
*/
|
|
38
60
|
using(source: TableDef | TableSourceNode): DeleteQueryBuilder<T> {
|
|
39
61
|
return this.clone(this.state.withUsing(this.resolveTableSource(source)));
|
|
40
62
|
}
|
|
41
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Adds a JOIN clause to the DELETE query
|
|
66
|
+
* @param table - The table to join with
|
|
67
|
+
* @param condition - The join condition expression
|
|
68
|
+
* @param kind - The type of join (defaults to INNER)
|
|
69
|
+
* @param relationName - Optional name for the relation
|
|
70
|
+
* @returns A new DeleteQueryBuilder with the JOIN clause added
|
|
71
|
+
*/
|
|
42
72
|
join(
|
|
43
73
|
table: TableDef | TableSourceNode | string,
|
|
44
74
|
condition: ExpressionNode,
|
|
@@ -50,6 +80,11 @@ export class DeleteQueryBuilder<T> {
|
|
|
50
80
|
return this.clone(this.state.withJoin(joinNode));
|
|
51
81
|
}
|
|
52
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Adds a RETURNING clause to the DELETE query
|
|
85
|
+
* @param columns - The columns to return after deletion
|
|
86
|
+
* @returns A new DeleteQueryBuilder with the RETURNING clause added
|
|
87
|
+
*/
|
|
53
88
|
returning(...columns: (ColumnDef | ColumnNode)[]): DeleteQueryBuilder<T> {
|
|
54
89
|
if (!columns.length) return this;
|
|
55
90
|
const nodes = columns.map(column => buildColumnNode(this.table, column));
|
|
@@ -68,28 +103,39 @@ export class DeleteQueryBuilder<T> {
|
|
|
68
103
|
return this.resolveTableSource(table);
|
|
69
104
|
}
|
|
70
105
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
compile(dialect: DeleteDialectInput): CompiledQuery
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// DeleteCompiler path – old behavior
|
|
81
|
-
return (arg as DeleteCompiler).compileDelete(this.state.ast);
|
|
82
|
-
}
|
|
106
|
+
/**
|
|
107
|
+
* Compiles the DELETE query for the specified dialect
|
|
108
|
+
* @param dialect - The SQL dialect to compile for
|
|
109
|
+
* @returns The compiled query with SQL and parameters
|
|
110
|
+
*/
|
|
111
|
+
compile(dialect: DeleteDialectInput): CompiledQuery {
|
|
112
|
+
const resolved = resolveDialectInput(dialect);
|
|
113
|
+
return resolved.compileDelete(this.state.ast);
|
|
114
|
+
}
|
|
83
115
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Returns the SQL string for the DELETE query
|
|
118
|
+
* @param dialect - The SQL dialect to generate SQL for
|
|
119
|
+
* @returns The SQL string representation of the query
|
|
120
|
+
*/
|
|
121
|
+
toSql(dialect: DeleteDialectInput): string {
|
|
122
|
+
return this.compile(dialect).sql;
|
|
87
123
|
}
|
|
88
124
|
|
|
89
|
-
|
|
90
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Executes the DELETE query using the provided session
|
|
127
|
+
* @param session - The ORM session to execute the query with
|
|
128
|
+
* @returns A promise that resolves to the query results
|
|
129
|
+
*/
|
|
130
|
+
async execute(session: OrmSession): Promise<QueryResult[]> {
|
|
131
|
+
const compiled = this.compile(session.dialect);
|
|
132
|
+
return session.executor.executeSql(compiled.sql, compiled.params);
|
|
91
133
|
}
|
|
92
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Returns the Abstract Syntax Tree (AST) representation of the query
|
|
137
|
+
* @returns The AST node for the DELETE query
|
|
138
|
+
*/
|
|
93
139
|
getAST(): DeleteQueryNode {
|
|
94
140
|
return this.state.ast;
|
|
95
141
|
}
|