metal-orm 1.0.17 → 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 +4 -3
- package/dist/decorators/index.cjs +192 -46
- 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 +192 -46
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +245 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -29
- package/dist/index.d.ts +16 -29
- package/dist/index.js +243 -66
- package/dist/index.js.map +1 -1
- package/dist/{select-BPCn6MOH.d.cts → select-BuMpVcVt.d.cts} +83 -11
- package/dist/{select-BPCn6MOH.d.ts → select-BuMpVcVt.d.ts} +83 -11
- package/package.json +4 -1
- package/src/codegen/naming-strategy.ts +15 -10
- package/src/core/ast/builders.ts +23 -3
- package/src/core/ast/expression-builders.ts +14 -1
- package/src/core/ast/expression-nodes.ts +11 -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/dialect/base/sql-dialect.ts +58 -46
- package/src/core/dialect/mssql/index.ts +53 -28
- package/src/core/dialect/sqlite/index.ts +22 -13
- package/src/query-builder/column-selector.ts +9 -7
- 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-query-state.ts +19 -7
- package/src/query-builder/select.ts +215 -135
- package/src/schema/column.ts +75 -39
- package/src/schema/types.ts +1 -0
package/README.md
CHANGED
|
@@ -105,9 +105,10 @@ If you like explicit model classes, you can add a thin decorator layer on top of
|
|
|
105
105
|
- `@HasMany({ target, foreignKey, ... })`
|
|
106
106
|
- `@HasOne({ target, foreignKey, ... })`
|
|
107
107
|
- `@BelongsTo({ target, foreignKey, ... })`
|
|
108
|
-
|
|
109
|
-
- `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasOne` / `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables.
|
|
110
|
-
- `selectFromEntity(MyEntity)` lets you start a `SelectQueryBuilder` directly from the class.
|
|
108
|
+
- `@BelongsToMany({ target, pivotTable, ... })`
|
|
109
|
+
- `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasOne` / `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables.
|
|
110
|
+
- `selectFromEntity(MyEntity)` lets you start a `SelectQueryBuilder` directly from the class.
|
|
111
|
+
- **Generate entities from an existing DB**: `node scripts/generate-entities.mjs --dialect=postgres --url=$DATABASE_URL --schema=public --out=src/entities.ts` introspects your schema and spits out `@Entity` / `@Column` classes you can immediately `bootstrapEntities()` with.
|
|
111
112
|
|
|
112
113
|
You don’t have to use decorators, but when you do, you’re still on the same AST + dialect + runtime foundation.
|
|
113
114
|
|
|
@@ -424,6 +424,26 @@ var avg = buildAggregate("AVG");
|
|
|
424
424
|
var min = buildAggregate("MIN");
|
|
425
425
|
var max = buildAggregate("MAX");
|
|
426
426
|
|
|
427
|
+
// src/core/ast/builders.ts
|
|
428
|
+
var buildColumnNode = (table, column) => {
|
|
429
|
+
if (column.type === "Column") {
|
|
430
|
+
return column;
|
|
431
|
+
}
|
|
432
|
+
const def = column;
|
|
433
|
+
const baseTable = def.table ? table.alias && def.table === table.name ? table.alias : def.table : table.alias || table.name;
|
|
434
|
+
return {
|
|
435
|
+
type: "Column",
|
|
436
|
+
table: baseTable,
|
|
437
|
+
name: def.name
|
|
438
|
+
};
|
|
439
|
+
};
|
|
440
|
+
var derivedTable = (query, alias, columnAliases) => ({
|
|
441
|
+
type: "DerivedTable",
|
|
442
|
+
query,
|
|
443
|
+
alias,
|
|
444
|
+
columnAliases
|
|
445
|
+
});
|
|
446
|
+
|
|
427
447
|
// src/core/functions/standard-strategy.ts
|
|
428
448
|
var StandardFunctionStrategy = class _StandardFunctionStrategy {
|
|
429
449
|
constructor() {
|
|
@@ -1113,7 +1133,7 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1113
1133
|
return this.returningStrategy.compileReturning(returning, ctx);
|
|
1114
1134
|
}
|
|
1115
1135
|
compileInsertColumnList(columns) {
|
|
1116
|
-
return columns.map((column) =>
|
|
1136
|
+
return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
|
|
1117
1137
|
}
|
|
1118
1138
|
compileInsertValues(values, ctx) {
|
|
1119
1139
|
return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
@@ -1139,7 +1159,7 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1139
1159
|
compileUpdateAssignments(assignments, ctx) {
|
|
1140
1160
|
return assignments.map((assignment) => {
|
|
1141
1161
|
const col = assignment.column;
|
|
1142
|
-
const target =
|
|
1162
|
+
const target = this.quoteIdentifier(col.name);
|
|
1143
1163
|
const value = this.compileOperand(assignment.value, ctx);
|
|
1144
1164
|
return `${target} = ${value}`;
|
|
1145
1165
|
}).join(", ");
|
|
@@ -1171,12 +1191,29 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1171
1191
|
if (tableSource.type === "FunctionTable") {
|
|
1172
1192
|
return this.compileFunctionTable(tableSource, ctx);
|
|
1173
1193
|
}
|
|
1194
|
+
if (tableSource.type === "DerivedTable") {
|
|
1195
|
+
return this.compileDerivedTable(tableSource, ctx);
|
|
1196
|
+
}
|
|
1174
1197
|
return this.compileTableSource(tableSource);
|
|
1175
1198
|
}
|
|
1176
1199
|
compileFunctionTable(fn, ctx) {
|
|
1177
1200
|
return FunctionTableFormatter.format(fn, ctx, this);
|
|
1178
1201
|
}
|
|
1202
|
+
compileDerivedTable(table, ctx) {
|
|
1203
|
+
if (!table.alias) {
|
|
1204
|
+
throw new Error("Derived tables must have an alias.");
|
|
1205
|
+
}
|
|
1206
|
+
const subquery = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
|
|
1207
|
+
const columns = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
1208
|
+
return `(${subquery}) AS ${this.quoteIdentifier(table.alias)}${columns}`;
|
|
1209
|
+
}
|
|
1179
1210
|
compileTableSource(table) {
|
|
1211
|
+
if (table.type === "FunctionTable") {
|
|
1212
|
+
return this.compileFunctionTable(table);
|
|
1213
|
+
}
|
|
1214
|
+
if (table.type === "DerivedTable") {
|
|
1215
|
+
return this.compileDerivedTable(table);
|
|
1216
|
+
}
|
|
1180
1217
|
const base = this.compileTableName(table);
|
|
1181
1218
|
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
1182
1219
|
}
|
|
@@ -1565,6 +1602,12 @@ var SqliteDialect = class extends SqlDialectBase {
|
|
|
1565
1602
|
const columns = this.formatReturningColumns(returning);
|
|
1566
1603
|
return ` RETURNING ${columns}`;
|
|
1567
1604
|
}
|
|
1605
|
+
formatReturningColumns(returning) {
|
|
1606
|
+
return returning.map((column) => {
|
|
1607
|
+
const alias = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : "";
|
|
1608
|
+
return `${this.quoteIdentifier(column.name)}${alias}`;
|
|
1609
|
+
}).join(", ");
|
|
1610
|
+
}
|
|
1568
1611
|
supportsReturning() {
|
|
1569
1612
|
return true;
|
|
1570
1613
|
}
|
|
@@ -1731,6 +1774,9 @@ var SqlServerDialect = class extends Dialect {
|
|
|
1731
1774
|
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
1732
1775
|
}
|
|
1733
1776
|
compileDeleteAst(ast, ctx) {
|
|
1777
|
+
if (ast.from.type !== "Table") {
|
|
1778
|
+
throw new Error("DELETE only supports base tables in the MSSQL dialect.");
|
|
1779
|
+
}
|
|
1734
1780
|
const table = this.quoteIdentifier(ast.from.name);
|
|
1735
1781
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1736
1782
|
return `DELETE FROM ${table}${whereClause};`;
|
|
@@ -1754,9 +1800,9 @@ var SqlServerDialect = class extends Dialect {
|
|
|
1754
1800
|
return expr;
|
|
1755
1801
|
}).join(", ");
|
|
1756
1802
|
const distinct = ast.distinct ? "DISTINCT " : "";
|
|
1757
|
-
const from =
|
|
1803
|
+
const from = this.compileTableSource(ast.from, ctx);
|
|
1758
1804
|
const joins = ast.joins.map((j) => {
|
|
1759
|
-
const table = this.
|
|
1805
|
+
const table = this.compileTableSource(j.table, ctx);
|
|
1760
1806
|
const cond = this.compileExpression(j.condition, ctx);
|
|
1761
1807
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
1762
1808
|
}).join(" ");
|
|
@@ -1786,6 +1832,21 @@ var SqlServerDialect = class extends Dialect {
|
|
|
1786
1832
|
}
|
|
1787
1833
|
return pagination;
|
|
1788
1834
|
}
|
|
1835
|
+
compileTableSource(table, ctx) {
|
|
1836
|
+
if (table.type === "FunctionTable") {
|
|
1837
|
+
return FunctionTableFormatter.format(table, ctx, this);
|
|
1838
|
+
}
|
|
1839
|
+
if (table.type === "DerivedTable") {
|
|
1840
|
+
return this.compileDerivedTable(table, ctx);
|
|
1841
|
+
}
|
|
1842
|
+
const base = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}` : this.quoteIdentifier(table.name);
|
|
1843
|
+
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
1844
|
+
}
|
|
1845
|
+
compileDerivedTable(table, ctx) {
|
|
1846
|
+
const sub = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
|
|
1847
|
+
const cols = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
1848
|
+
return `(${sub}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
|
|
1849
|
+
}
|
|
1789
1850
|
compileCtes(ast, ctx) {
|
|
1790
1851
|
if (!ast.ctes || ast.ctes.length === 0) return "";
|
|
1791
1852
|
const defs = ast.ctes.map((cte) => {
|
|
@@ -1914,6 +1975,17 @@ var SelectQueryState = class _SelectQueryState {
|
|
|
1914
1975
|
joins: [...this.ast.joins ?? [], join]
|
|
1915
1976
|
});
|
|
1916
1977
|
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Replaces the FROM clause.
|
|
1980
|
+
* @param from - Table source for the FROM clause
|
|
1981
|
+
* @returns New SelectQueryState with updated FROM
|
|
1982
|
+
*/
|
|
1983
|
+
withFrom(from) {
|
|
1984
|
+
return this.clone({
|
|
1985
|
+
...this.ast,
|
|
1986
|
+
from
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1917
1989
|
/**
|
|
1918
1990
|
* Adds a WHERE clause to the query
|
|
1919
1991
|
* @param predicate - WHERE predicate expression
|
|
@@ -2406,19 +2478,6 @@ var buildDefaultHydrationPlan = (table) => ({
|
|
|
2406
2478
|
relations: []
|
|
2407
2479
|
});
|
|
2408
2480
|
|
|
2409
|
-
// src/core/ast/builders.ts
|
|
2410
|
-
var buildColumnNode = (table, column) => {
|
|
2411
|
-
if (column.type === "Column") {
|
|
2412
|
-
return column;
|
|
2413
|
-
}
|
|
2414
|
-
const def = column;
|
|
2415
|
-
return {
|
|
2416
|
-
type: "Column",
|
|
2417
|
-
table: def.table || table.name,
|
|
2418
|
-
name: def.name
|
|
2419
|
-
};
|
|
2420
|
-
};
|
|
2421
|
-
|
|
2422
2481
|
// src/query-builder/raw-column-parser.ts
|
|
2423
2482
|
var parseRawColumn = (col, tableName, ctes) => {
|
|
2424
2483
|
if (col.includes("(")) {
|
|
@@ -2458,6 +2517,8 @@ var QueryAstService = class {
|
|
|
2458
2517
|
const existingAliases = new Set(
|
|
2459
2518
|
this.state.ast.columns.map((c) => c.alias || c.name)
|
|
2460
2519
|
);
|
|
2520
|
+
const from = this.state.ast.from;
|
|
2521
|
+
const rootTableName = from.type === "Table" && from.alias ? from.alias : this.table.name;
|
|
2461
2522
|
const newCols = Object.entries(columns).reduce((acc, [alias, val]) => {
|
|
2462
2523
|
if (existingAliases.has(alias)) return acc;
|
|
2463
2524
|
if (isExpressionSelectionNode(val)) {
|
|
@@ -2465,9 +2526,10 @@ var QueryAstService = class {
|
|
|
2465
2526
|
return acc;
|
|
2466
2527
|
}
|
|
2467
2528
|
const colDef = val;
|
|
2529
|
+
const resolvedTable = colDef.table && colDef.table === this.table.name && from.type === "Table" && from.alias ? from.alias : colDef.table || rootTableName;
|
|
2468
2530
|
acc.push({
|
|
2469
2531
|
type: "Column",
|
|
2470
|
-
table:
|
|
2532
|
+
table: resolvedTable,
|
|
2471
2533
|
name: colDef.name,
|
|
2472
2534
|
alias
|
|
2473
2535
|
});
|
|
@@ -2482,7 +2544,9 @@ var QueryAstService = class {
|
|
|
2482
2544
|
* @returns Column selection result with updated state and added columns
|
|
2483
2545
|
*/
|
|
2484
2546
|
selectRaw(cols) {
|
|
2485
|
-
const
|
|
2547
|
+
const from = this.state.ast.from;
|
|
2548
|
+
const defaultTable = from.type === "Table" && from.alias ? from.alias : this.table.name;
|
|
2549
|
+
const newCols = cols.map((col) => parseRawColumn(col, defaultTable, this.state.ast.ctes));
|
|
2486
2550
|
const nextState = this.state.withColumns(newCols);
|
|
2487
2551
|
return { state: nextState, addedColumns: newCols };
|
|
2488
2552
|
}
|
|
@@ -2518,6 +2582,14 @@ var QueryAstService = class {
|
|
|
2518
2582
|
};
|
|
2519
2583
|
return this.state.withSetOperation(op);
|
|
2520
2584
|
}
|
|
2585
|
+
/**
|
|
2586
|
+
* Replaces the FROM clause for the current query.
|
|
2587
|
+
* @param from - Table source to use in the FROM clause
|
|
2588
|
+
* @returns Updated query state with new FROM
|
|
2589
|
+
*/
|
|
2590
|
+
withFrom(from) {
|
|
2591
|
+
return this.state.withFrom(from);
|
|
2592
|
+
}
|
|
2521
2593
|
/**
|
|
2522
2594
|
* Selects a subquery as a column
|
|
2523
2595
|
* @param alias - Alias for the subquery
|
|
@@ -2551,7 +2623,9 @@ var QueryAstService = class {
|
|
|
2551
2623
|
* @returns Updated query state with GROUP BY clause
|
|
2552
2624
|
*/
|
|
2553
2625
|
withGroupBy(col) {
|
|
2554
|
-
const
|
|
2626
|
+
const from = this.state.ast.from;
|
|
2627
|
+
const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
|
|
2628
|
+
const node = buildColumnNode(tableRef, col);
|
|
2555
2629
|
return this.state.withGroupBy([node]);
|
|
2556
2630
|
}
|
|
2557
2631
|
/**
|
|
@@ -2570,7 +2644,9 @@ var QueryAstService = class {
|
|
|
2570
2644
|
* @returns Updated query state with ORDER BY clause
|
|
2571
2645
|
*/
|
|
2572
2646
|
withOrderBy(col, direction) {
|
|
2573
|
-
const
|
|
2647
|
+
const from = this.state.ast.from;
|
|
2648
|
+
const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
|
|
2649
|
+
const node = buildColumnNode(tableRef, col);
|
|
2574
2650
|
return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
|
|
2575
2651
|
}
|
|
2576
2652
|
/**
|
|
@@ -2674,7 +2750,8 @@ var RelationProjectionHelper = class {
|
|
|
2674
2750
|
var assertNever = (value) => {
|
|
2675
2751
|
throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
|
|
2676
2752
|
};
|
|
2677
|
-
var baseRelationCondition = (root, relation) => {
|
|
2753
|
+
var baseRelationCondition = (root, relation, rootAlias) => {
|
|
2754
|
+
const rootTable = rootAlias || root.name;
|
|
2678
2755
|
const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
|
|
2679
2756
|
const localKey = relation.localKey || defaultLocalKey;
|
|
2680
2757
|
switch (relation.type) {
|
|
@@ -2682,12 +2759,12 @@ var baseRelationCondition = (root, relation) => {
|
|
|
2682
2759
|
case RelationKinds.HasOne:
|
|
2683
2760
|
return eq(
|
|
2684
2761
|
{ type: "Column", table: relation.target.name, name: relation.foreignKey },
|
|
2685
|
-
{ type: "Column", table:
|
|
2762
|
+
{ type: "Column", table: rootTable, name: localKey }
|
|
2686
2763
|
);
|
|
2687
2764
|
case RelationKinds.BelongsTo:
|
|
2688
2765
|
return eq(
|
|
2689
2766
|
{ type: "Column", table: relation.target.name, name: localKey },
|
|
2690
|
-
{ type: "Column", table:
|
|
2767
|
+
{ type: "Column", table: rootTable, name: relation.foreignKey }
|
|
2691
2768
|
);
|
|
2692
2769
|
case RelationKinds.BelongsToMany:
|
|
2693
2770
|
throw new Error("BelongsToMany relations do not support the standard join condition builder");
|
|
@@ -2695,12 +2772,13 @@ var baseRelationCondition = (root, relation) => {
|
|
|
2695
2772
|
return assertNever(relation);
|
|
2696
2773
|
}
|
|
2697
2774
|
};
|
|
2698
|
-
var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) => {
|
|
2775
|
+
var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias) => {
|
|
2699
2776
|
const rootKey = relation.localKey || findPrimaryKey(root);
|
|
2700
2777
|
const targetKey = relation.targetKey || findPrimaryKey(relation.target);
|
|
2778
|
+
const rootTable = rootAlias || root.name;
|
|
2701
2779
|
const pivotCondition = eq(
|
|
2702
2780
|
{ type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToRoot },
|
|
2703
|
-
{ type: "Column", table:
|
|
2781
|
+
{ type: "Column", table: rootTable, name: rootKey }
|
|
2704
2782
|
);
|
|
2705
2783
|
const pivotJoin = createJoinNode(joinKind, relation.pivotTable.name, pivotCondition);
|
|
2706
2784
|
let targetCondition = eq(
|
|
@@ -2718,12 +2796,12 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) =>
|
|
|
2718
2796
|
);
|
|
2719
2797
|
return [pivotJoin, targetJoin];
|
|
2720
2798
|
};
|
|
2721
|
-
var buildRelationJoinCondition = (root, relation, extra) => {
|
|
2722
|
-
const base = baseRelationCondition(root, relation);
|
|
2799
|
+
var buildRelationJoinCondition = (root, relation, extra, rootAlias) => {
|
|
2800
|
+
const base = baseRelationCondition(root, relation, rootAlias);
|
|
2723
2801
|
return extra ? and(base, extra) : base;
|
|
2724
2802
|
};
|
|
2725
|
-
var buildRelationCorrelation = (root, relation) => {
|
|
2726
|
-
return baseRelationCondition(root, relation);
|
|
2803
|
+
var buildRelationCorrelation = (root, relation, rootAlias) => {
|
|
2804
|
+
return baseRelationCondition(root, relation, rootAlias);
|
|
2727
2805
|
};
|
|
2728
2806
|
|
|
2729
2807
|
// src/core/ast/join-metadata.ts
|
|
@@ -2767,7 +2845,7 @@ var RelationService = class {
|
|
|
2767
2845
|
match(relationName, predicate) {
|
|
2768
2846
|
const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
|
|
2769
2847
|
const pk = findPrimaryKey(this.table);
|
|
2770
|
-
const distinctCols = [{ type: "Column", table: this.
|
|
2848
|
+
const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
|
|
2771
2849
|
const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
|
|
2772
2850
|
const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
|
|
2773
2851
|
return { state: nextState, hydration: joined.hydration };
|
|
@@ -2854,9 +2932,13 @@ var RelationService = class {
|
|
|
2854
2932
|
* @param ast - Query AST to modify
|
|
2855
2933
|
* @returns Modified query AST with relation correlation
|
|
2856
2934
|
*/
|
|
2857
|
-
applyRelationCorrelation(relationName, ast) {
|
|
2935
|
+
applyRelationCorrelation(relationName, ast, additionalCorrelation) {
|
|
2858
2936
|
const relation = this.getRelation(relationName);
|
|
2859
|
-
const
|
|
2937
|
+
const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
|
|
2938
|
+
let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
|
|
2939
|
+
if (additionalCorrelation) {
|
|
2940
|
+
correlation = and(correlation, additionalCorrelation);
|
|
2941
|
+
}
|
|
2860
2942
|
const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
|
|
2861
2943
|
return {
|
|
2862
2944
|
...ast,
|
|
@@ -2873,17 +2955,19 @@ var RelationService = class {
|
|
|
2873
2955
|
*/
|
|
2874
2956
|
withJoin(state, relationName, joinKind, extraCondition) {
|
|
2875
2957
|
const relation = this.getRelation(relationName);
|
|
2958
|
+
const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
|
|
2876
2959
|
if (relation.type === RelationKinds.BelongsToMany) {
|
|
2877
2960
|
const joins = buildBelongsToManyJoins(
|
|
2878
2961
|
this.table,
|
|
2879
2962
|
relationName,
|
|
2880
2963
|
relation,
|
|
2881
2964
|
joinKind,
|
|
2882
|
-
extraCondition
|
|
2965
|
+
extraCondition,
|
|
2966
|
+
rootAlias
|
|
2883
2967
|
);
|
|
2884
2968
|
return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
|
|
2885
2969
|
}
|
|
2886
|
-
const condition = buildRelationJoinCondition(this.table, relation, extraCondition);
|
|
2970
|
+
const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
|
|
2887
2971
|
const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
|
|
2888
2972
|
return this.astService(state).withJoin(joinNode);
|
|
2889
2973
|
}
|
|
@@ -2922,6 +3006,11 @@ var RelationService = class {
|
|
|
2922
3006
|
astService(state = this.state) {
|
|
2923
3007
|
return this.createQueryAstService(this.table, state);
|
|
2924
3008
|
}
|
|
3009
|
+
rootTableName() {
|
|
3010
|
+
const from = this.state.ast.from;
|
|
3011
|
+
if (from.type === "Table" && from.alias) return from.alias;
|
|
3012
|
+
return this.table.name;
|
|
3013
|
+
}
|
|
2925
3014
|
};
|
|
2926
3015
|
|
|
2927
3016
|
// src/query-builder/select-query-builder-deps.ts
|
|
@@ -2996,7 +3085,9 @@ var ColumnSelector = class {
|
|
|
2996
3085
|
* @returns Updated query context with DISTINCT clause
|
|
2997
3086
|
*/
|
|
2998
3087
|
distinct(context, columns) {
|
|
2999
|
-
const
|
|
3088
|
+
const from = context.state.ast.from;
|
|
3089
|
+
const tableRef = from.type === "Table" && from.alias ? { ...this.env.table, alias: from.alias } : this.env.table;
|
|
3090
|
+
const nodes = columns.map((col) => buildColumnNode(tableRef, col));
|
|
3000
3091
|
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
3001
3092
|
const nextState = astService.withDistinct(nodes);
|
|
3002
3093
|
return { state: nextState, hydration: context.hydration };
|
|
@@ -3053,8 +3144,8 @@ var RelationManager = class {
|
|
|
3053
3144
|
* @param ast - Query AST to modify
|
|
3054
3145
|
* @returns Modified query AST with relation correlation
|
|
3055
3146
|
*/
|
|
3056
|
-
applyRelationCorrelation(context, relationName, ast) {
|
|
3057
|
-
return this.createService(context).applyRelationCorrelation(relationName, ast);
|
|
3147
|
+
applyRelationCorrelation(context, relationName, ast, additionalCorrelation) {
|
|
3148
|
+
return this.createService(context).applyRelationCorrelation(relationName, ast, additionalCorrelation);
|
|
3058
3149
|
}
|
|
3059
3150
|
/**
|
|
3060
3151
|
* Creates a relation service instance
|
|
@@ -4101,9 +4192,30 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4101
4192
|
clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
|
|
4102
4193
|
return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
|
|
4103
4194
|
}
|
|
4195
|
+
/**
|
|
4196
|
+
* Applies an alias to the root FROM table.
|
|
4197
|
+
* @param alias - Alias to apply
|
|
4198
|
+
*/
|
|
4199
|
+
as(alias) {
|
|
4200
|
+
const from = this.context.state.ast.from;
|
|
4201
|
+
if (from.type !== "Table") {
|
|
4202
|
+
throw new Error("Cannot alias non-table FROM sources");
|
|
4203
|
+
}
|
|
4204
|
+
const nextFrom = { ...from, alias };
|
|
4205
|
+
const nextContext = this.applyAst(this.context, (service) => service.withFrom(nextFrom));
|
|
4206
|
+
return this.clone(nextContext);
|
|
4207
|
+
}
|
|
4104
4208
|
resolveQueryNode(query) {
|
|
4105
4209
|
return typeof query.getAST === "function" ? query.getAST() : query;
|
|
4106
4210
|
}
|
|
4211
|
+
applyCorrelation(ast, correlation) {
|
|
4212
|
+
if (!correlation) return ast;
|
|
4213
|
+
const combinedWhere = ast.where ? and(correlation, ast.where) : correlation;
|
|
4214
|
+
return {
|
|
4215
|
+
...ast,
|
|
4216
|
+
where: combinedWhere
|
|
4217
|
+
};
|
|
4218
|
+
}
|
|
4107
4219
|
createChildBuilder(table) {
|
|
4108
4220
|
return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
|
|
4109
4221
|
}
|
|
@@ -4195,6 +4307,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4195
4307
|
const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
|
|
4196
4308
|
return this.clone(nextContext);
|
|
4197
4309
|
}
|
|
4310
|
+
/**
|
|
4311
|
+
* Replaces the FROM clause with a derived table (subquery with alias)
|
|
4312
|
+
* @param subquery - Subquery to use as the FROM source
|
|
4313
|
+
* @param alias - Alias for the derived table
|
|
4314
|
+
* @param columnAliases - Optional column alias list
|
|
4315
|
+
* @returns New query builder instance with updated FROM
|
|
4316
|
+
*/
|
|
4317
|
+
fromSubquery(subquery, alias, columnAliases) {
|
|
4318
|
+
const subAst = this.resolveQueryNode(subquery);
|
|
4319
|
+
const fromNode = derivedTable(subAst, alias, columnAliases);
|
|
4320
|
+
const nextContext = this.applyAst(this.context, (service) => service.withFrom(fromNode));
|
|
4321
|
+
return this.clone(nextContext);
|
|
4322
|
+
}
|
|
4198
4323
|
/**
|
|
4199
4324
|
|
|
4200
4325
|
* Selects a subquery as a column
|
|
@@ -4210,6 +4335,21 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4210
4335
|
const query = this.resolveQueryNode(sub);
|
|
4211
4336
|
return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
|
|
4212
4337
|
}
|
|
4338
|
+
/**
|
|
4339
|
+
* Adds a JOIN against a derived table (subquery with alias)
|
|
4340
|
+
* @param subquery - Subquery to join
|
|
4341
|
+
* @param alias - Alias for the derived table
|
|
4342
|
+
* @param condition - Join condition expression
|
|
4343
|
+
* @param joinKind - Join kind (defaults to INNER)
|
|
4344
|
+
* @param columnAliases - Optional column alias list for the derived table
|
|
4345
|
+
* @returns New query builder instance with the derived-table join
|
|
4346
|
+
*/
|
|
4347
|
+
joinSubquery(subquery, alias, condition, joinKind = JOIN_KINDS.INNER, columnAliases) {
|
|
4348
|
+
const subAst = this.resolveQueryNode(subquery);
|
|
4349
|
+
const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
|
|
4350
|
+
const nextContext = this.applyAst(this.context, (service) => service.withJoin(joinNode));
|
|
4351
|
+
return this.clone(nextContext);
|
|
4352
|
+
}
|
|
4213
4353
|
/**
|
|
4214
4354
|
|
|
4215
4355
|
* Adds an INNER JOIN to the query
|
|
@@ -4509,9 +4649,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4509
4649
|
* @returns New query builder instance with the WHERE EXISTS condition
|
|
4510
4650
|
|
|
4511
4651
|
*/
|
|
4512
|
-
whereExists(subquery) {
|
|
4652
|
+
whereExists(subquery, correlate) {
|
|
4513
4653
|
const subAst = this.resolveQueryNode(subquery);
|
|
4514
|
-
|
|
4654
|
+
const correlated = this.applyCorrelation(subAst, correlate);
|
|
4655
|
+
return this.where(exists(correlated));
|
|
4515
4656
|
}
|
|
4516
4657
|
/**
|
|
4517
4658
|
|
|
@@ -4522,9 +4663,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4522
4663
|
* @returns New query builder instance with the WHERE NOT EXISTS condition
|
|
4523
4664
|
|
|
4524
4665
|
*/
|
|
4525
|
-
whereNotExists(subquery) {
|
|
4666
|
+
whereNotExists(subquery, correlate) {
|
|
4526
4667
|
const subAst = this.resolveQueryNode(subquery);
|
|
4527
|
-
|
|
4668
|
+
const correlated = this.applyCorrelation(subAst, correlate);
|
|
4669
|
+
return this.where(notExists(correlated));
|
|
4528
4670
|
}
|
|
4529
4671
|
/**
|
|
4530
4672
|
|
|
@@ -4537,17 +4679,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4537
4679
|
* @returns New query builder instance with the relationship existence check
|
|
4538
4680
|
|
|
4539
4681
|
*/
|
|
4540
|
-
whereHas(relationName,
|
|
4682
|
+
whereHas(relationName, callbackOrOptions, maybeOptions) {
|
|
4541
4683
|
const relation = this.env.table.relations[relationName];
|
|
4542
4684
|
if (!relation) {
|
|
4543
4685
|
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
4544
4686
|
}
|
|
4687
|
+
const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
|
|
4688
|
+
const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
|
|
4545
4689
|
let subQb = this.createChildBuilder(relation.target);
|
|
4546
4690
|
if (callback) {
|
|
4547
4691
|
subQb = callback(subQb);
|
|
4548
4692
|
}
|
|
4549
4693
|
const subAst = subQb.getAST();
|
|
4550
|
-
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
|
|
4694
|
+
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
|
|
4551
4695
|
return this.where(exists(finalSubAst));
|
|
4552
4696
|
}
|
|
4553
4697
|
/**
|
|
@@ -4561,17 +4705,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4561
4705
|
* @returns New query builder instance with the relationship non-existence check
|
|
4562
4706
|
|
|
4563
4707
|
*/
|
|
4564
|
-
whereHasNot(relationName,
|
|
4708
|
+
whereHasNot(relationName, callbackOrOptions, maybeOptions) {
|
|
4565
4709
|
const relation = this.env.table.relations[relationName];
|
|
4566
4710
|
if (!relation) {
|
|
4567
4711
|
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
4568
4712
|
}
|
|
4713
|
+
const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
|
|
4714
|
+
const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
|
|
4569
4715
|
let subQb = this.createChildBuilder(relation.target);
|
|
4570
4716
|
if (callback) {
|
|
4571
4717
|
subQb = callback(subQb);
|
|
4572
4718
|
}
|
|
4573
4719
|
const subAst = subQb.getAST();
|
|
4574
|
-
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
|
|
4720
|
+
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
|
|
4575
4721
|
return this.where(notExists(finalSubAst));
|
|
4576
4722
|
}
|
|
4577
4723
|
/**
|