metal-orm 1.0.16 → 1.0.18
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 +37 -40
- package/dist/decorators/index.cjs +344 -69
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +1 -1
- package/dist/decorators/index.d.ts +1 -1
- package/dist/decorators/index.js +344 -69
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +567 -181
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -30
- package/dist/index.d.ts +66 -30
- package/dist/index.js +559 -181
- package/dist/index.js.map +1 -1
- package/dist/{select-BKZrMRCQ.d.cts → select-BuMpVcVt.d.cts} +265 -74
- package/dist/{select-BKZrMRCQ.d.ts → select-BuMpVcVt.d.ts} +265 -74
- package/package.json +5 -1
- package/src/codegen/naming-strategy.ts +15 -10
- package/src/core/ast/aggregate-functions.ts +50 -4
- package/src/core/ast/builders.ts +23 -3
- package/src/core/ast/expression-builders.ts +36 -16
- package/src/core/ast/expression-nodes.ts +17 -9
- package/src/core/ast/join-node.ts +5 -3
- package/src/core/ast/join.ts +16 -16
- package/src/core/ast/query.ts +44 -29
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +18 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +11 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +9 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
- package/src/core/ddl/introspect/functions/postgres.ts +2 -6
- package/src/core/dialect/abstract.ts +12 -8
- package/src/core/dialect/base/sql-dialect.ts +58 -46
- package/src/core/dialect/mssql/functions.ts +24 -15
- package/src/core/dialect/mssql/index.ts +53 -28
- package/src/core/dialect/postgres/functions.ts +33 -24
- package/src/core/dialect/sqlite/functions.ts +19 -12
- package/src/core/dialect/sqlite/index.ts +22 -13
- package/src/core/functions/datetime.ts +2 -1
- package/src/core/functions/numeric.ts +2 -1
- package/src/core/functions/standard-strategy.ts +52 -12
- package/src/core/functions/text.ts +2 -1
- package/src/core/functions/types.ts +8 -8
- package/src/index.ts +5 -4
- package/src/orm/domain-event-bus.ts +43 -25
- package/src/orm/entity-meta.ts +40 -0
- package/src/orm/execution-context.ts +6 -0
- package/src/orm/hydration-context.ts +6 -4
- package/src/orm/orm-session.ts +35 -24
- package/src/orm/orm.ts +10 -10
- package/src/orm/query-logger.ts +15 -0
- package/src/orm/runtime-types.ts +60 -2
- package/src/orm/transaction-runner.ts +7 -0
- package/src/orm/unit-of-work.ts +1 -0
- package/src/query-builder/column-selector.ts +9 -7
- package/src/query-builder/insert-query-state.ts +13 -3
- package/src/query-builder/query-ast-service.ts +59 -38
- package/src/query-builder/relation-conditions.ts +38 -34
- package/src/query-builder/relation-manager.ts +8 -3
- package/src/query-builder/relation-service.ts +59 -46
- package/src/query-builder/select-helpers.ts +50 -0
- package/src/query-builder/select-query-state.ts +19 -7
- package/src/query-builder/select.ts +339 -167
- package/src/query-builder/update-query-state.ts +31 -9
- package/src/schema/column.ts +75 -39
- package/src/schema/types.ts +17 -6
|
@@ -70,17 +70,17 @@ export class RelationService {
|
|
|
70
70
|
* @param predicate - Optional predicate expression
|
|
71
71
|
* @returns Relation result with updated state and hydration
|
|
72
72
|
*/
|
|
73
|
-
match(
|
|
74
|
-
relationName: string,
|
|
75
|
-
predicate?: ExpressionNode
|
|
76
|
-
): RelationResult {
|
|
77
|
-
const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
|
|
78
|
-
const pk = findPrimaryKey(this.table);
|
|
79
|
-
const distinctCols: ColumnNode[] = [{ type: 'Column', table: this.
|
|
80
|
-
const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
|
|
81
|
-
const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
|
|
82
|
-
return { state: nextState, hydration: joined.hydration };
|
|
83
|
-
}
|
|
73
|
+
match(
|
|
74
|
+
relationName: string,
|
|
75
|
+
predicate?: ExpressionNode
|
|
76
|
+
): RelationResult {
|
|
77
|
+
const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
|
|
78
|
+
const pk = findPrimaryKey(this.table);
|
|
79
|
+
const distinctCols: ColumnNode[] = [{ type: 'Column', table: this.rootTableName(), name: pk }];
|
|
80
|
+
const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
|
|
81
|
+
const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
|
|
82
|
+
return { state: nextState, hydration: joined.hydration };
|
|
83
|
+
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Includes a relation in the query result
|
|
@@ -190,17 +190,22 @@ export class RelationService {
|
|
|
190
190
|
* @param ast - Query AST to modify
|
|
191
191
|
* @returns Modified query AST with relation correlation
|
|
192
192
|
*/
|
|
193
|
-
applyRelationCorrelation(
|
|
194
|
-
relationName: string,
|
|
195
|
-
ast: SelectQueryNode
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
193
|
+
applyRelationCorrelation(
|
|
194
|
+
relationName: string,
|
|
195
|
+
ast: SelectQueryNode,
|
|
196
|
+
additionalCorrelation?: ExpressionNode
|
|
197
|
+
): SelectQueryNode {
|
|
198
|
+
const relation = this.getRelation(relationName);
|
|
199
|
+
const rootAlias = this.state.ast.from.type === 'Table' ? this.state.ast.from.alias : undefined;
|
|
200
|
+
let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
|
|
201
|
+
if (additionalCorrelation) {
|
|
202
|
+
correlation = and(correlation, additionalCorrelation);
|
|
203
|
+
}
|
|
204
|
+
const whereInSubquery = ast.where
|
|
205
|
+
? and(correlation, ast.where)
|
|
206
|
+
: correlation;
|
|
207
|
+
|
|
208
|
+
return {
|
|
204
209
|
...ast,
|
|
205
210
|
where: whereInSubquery
|
|
206
211
|
};
|
|
@@ -214,26 +219,28 @@ export class RelationService {
|
|
|
214
219
|
* @param extraCondition - Additional join condition
|
|
215
220
|
* @returns Updated query state with join
|
|
216
221
|
*/
|
|
217
|
-
private withJoin(
|
|
218
|
-
state: SelectQueryState,
|
|
219
|
-
relationName: string,
|
|
220
|
-
joinKind: JoinKind,
|
|
221
|
-
extraCondition?: ExpressionNode
|
|
222
|
-
): SelectQueryState {
|
|
223
|
-
const relation = this.getRelation(relationName);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
222
|
+
private withJoin(
|
|
223
|
+
state: SelectQueryState,
|
|
224
|
+
relationName: string,
|
|
225
|
+
joinKind: JoinKind,
|
|
226
|
+
extraCondition?: ExpressionNode
|
|
227
|
+
): SelectQueryState {
|
|
228
|
+
const relation = this.getRelation(relationName);
|
|
229
|
+
const rootAlias = state.ast.from.type === 'Table' ? state.ast.from.alias : undefined;
|
|
230
|
+
if (relation.type === RelationKinds.BelongsToMany) {
|
|
231
|
+
const joins = buildBelongsToManyJoins(
|
|
232
|
+
this.table,
|
|
233
|
+
relationName,
|
|
234
|
+
relation as BelongsToManyRelation,
|
|
235
|
+
joinKind,
|
|
236
|
+
extraCondition,
|
|
237
|
+
rootAlias
|
|
238
|
+
);
|
|
239
|
+
return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
|
|
243
|
+
const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
|
|
237
244
|
|
|
238
245
|
return this.astService(state).withJoin(joinNode);
|
|
239
246
|
}
|
|
@@ -277,9 +284,15 @@ export class RelationService {
|
|
|
277
284
|
* @param state - Current query state
|
|
278
285
|
* @returns QueryAstService instance
|
|
279
286
|
*/
|
|
280
|
-
private astService(state: SelectQueryState = this.state): QueryAstService {
|
|
281
|
-
return this.createQueryAstService(this.table, state);
|
|
282
|
-
}
|
|
283
|
-
|
|
287
|
+
private astService(state: SelectQueryState = this.state): QueryAstService {
|
|
288
|
+
return this.createQueryAstService(this.table, state);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private rootTableName(): string {
|
|
292
|
+
const from = this.state.ast.from;
|
|
293
|
+
if (from.type === 'Table' && from.alias) return from.alias;
|
|
294
|
+
return this.table.name;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
284
297
|
|
|
285
298
|
export type { RelationResult } from './relation-projection-helper.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { TableDef } from '../schema/table.js';
|
|
2
|
+
import type { ColumnDef } from '../schema/column.js';
|
|
3
|
+
import { getTableDefFromEntity } from '../decorators/bootstrap.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build a typed selection map from a TableDef.
|
|
7
|
+
*/
|
|
8
|
+
export function sel<
|
|
9
|
+
TTable extends TableDef,
|
|
10
|
+
K extends keyof TTable['columns'] & string
|
|
11
|
+
>(table: TTable, ...cols: K[]): Record<K, TTable['columns'][K]> {
|
|
12
|
+
const selection = {} as Record<K, TTable['columns'][K]>;
|
|
13
|
+
|
|
14
|
+
for (const col of cols) {
|
|
15
|
+
const def = table.columns[col] as TTable['columns'][K];
|
|
16
|
+
if (!def) {
|
|
17
|
+
throw new Error(`Column '${col}' not found on table '${table.name}'`);
|
|
18
|
+
}
|
|
19
|
+
selection[col] = def;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return selection;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type Ctor<T> = { new (...args: any[]): T };
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Build a typed selection map from an entity constructor.
|
|
29
|
+
*/
|
|
30
|
+
export function esel<TEntity, K extends keyof TEntity & string>(
|
|
31
|
+
entity: Ctor<TEntity>,
|
|
32
|
+
...props: K[]
|
|
33
|
+
): Record<K, ColumnDef> {
|
|
34
|
+
const table = getTableDefFromEntity(entity) as TableDef | undefined;
|
|
35
|
+
if (!table) {
|
|
36
|
+
throw new Error(`No table definition registered for entity '${entity.name}'`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const selection = {} as Record<K, ColumnDef>;
|
|
40
|
+
|
|
41
|
+
for (const prop of props) {
|
|
42
|
+
const col = table.columns[prop];
|
|
43
|
+
if (!col) {
|
|
44
|
+
throw new Error(`No column '${prop}' found for entity '${entity.name}'`);
|
|
45
|
+
}
|
|
46
|
+
selection[prop] = col;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return selection;
|
|
50
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TableDef } from '../schema/table.js';
|
|
2
|
-
import { SelectQueryNode, CommonTableExpressionNode, OrderByNode, SetOperationNode } from '../core/ast/query.js';
|
|
2
|
+
import { SelectQueryNode, CommonTableExpressionNode, OrderByNode, SetOperationNode, TableSourceNode } from '../core/ast/query.js';
|
|
3
3
|
import {
|
|
4
4
|
ColumnNode,
|
|
5
5
|
ExpressionNode,
|
|
@@ -74,12 +74,24 @@ export class SelectQueryState {
|
|
|
74
74
|
* @param join - Join node to add
|
|
75
75
|
* @returns New SelectQueryState with added join
|
|
76
76
|
*/
|
|
77
|
-
withJoin(join: JoinNode): SelectQueryState {
|
|
78
|
-
return this.clone({
|
|
79
|
-
...this.ast,
|
|
80
|
-
joins: [...(this.ast.joins ?? []), join]
|
|
81
|
-
});
|
|
82
|
-
}
|
|
77
|
+
withJoin(join: JoinNode): SelectQueryState {
|
|
78
|
+
return this.clone({
|
|
79
|
+
...this.ast,
|
|
80
|
+
joins: [...(this.ast.joins ?? []), join]
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Replaces the FROM clause.
|
|
86
|
+
* @param from - Table source for the FROM clause
|
|
87
|
+
* @returns New SelectQueryState with updated FROM
|
|
88
|
+
*/
|
|
89
|
+
withFrom(from: TableSourceNode): SelectQueryState {
|
|
90
|
+
return this.clone({
|
|
91
|
+
...this.ast,
|
|
92
|
+
from
|
|
93
|
+
});
|
|
94
|
+
}
|
|
83
95
|
|
|
84
96
|
/**
|
|
85
97
|
* Adds a WHERE clause to the query
|