metal-orm 1.0.12 → 1.0.13
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 +17 -15
- package/dist/decorators/index.cjs +302 -32
- 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 +302 -32
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +583 -130
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +583 -130
- package/dist/index.js.map +1 -1
- package/dist/{select-BKlr2ivY.d.cts → select-CCp1oz9p.d.cts} +114 -1
- package/dist/{select-BKlr2ivY.d.ts → select-CCp1oz9p.d.ts} +114 -1
- package/package.json +3 -2
- package/src/core/ast/query.ts +40 -22
- package/src/core/dialect/abstract.ts +122 -37
- package/src/core/dialect/base/sql-dialect.ts +51 -8
- package/src/core/dialect/mssql/index.ts +125 -80
- package/src/core/dialect/postgres/index.ts +5 -6
- package/src/core/dialect/sqlite/index.ts +5 -6
- package/src/orm/execute.ts +25 -16
- package/src/orm/orm-context.ts +60 -55
- package/src/orm/query-logger.ts +38 -0
- package/src/orm/relations/belongs-to.ts +42 -26
- package/src/orm/relations/has-many.ts +41 -25
- package/src/orm/relations/many-to-many.ts +43 -27
- package/src/orm/unit-of-work.ts +60 -23
- package/src/query-builder/hydration-manager.ts +229 -25
- package/src/query-builder/query-ast-service.ts +27 -12
- package/src/query-builder/select-query-state.ts +24 -12
- package/src/query-builder/select.ts +58 -14
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { K as TableHooks, Q as ColumnType, C as ColumnDef, T as TableDef, a0 as CascadeMode, z as SelectQueryBuilder } from '../select-
|
|
1
|
+
import { K as TableHooks, Q as ColumnType, C as ColumnDef, T as TableDef, a0 as CascadeMode, z as SelectQueryBuilder } from '../select-CCp1oz9p.cjs';
|
|
2
2
|
|
|
3
3
|
interface EntityOptions {
|
|
4
4
|
tableName?: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { K as TableHooks, Q as ColumnType, C as ColumnDef, T as TableDef, a0 as CascadeMode, z as SelectQueryBuilder } from '../select-
|
|
1
|
+
import { K as TableHooks, Q as ColumnType, C as ColumnDef, T as TableDef, a0 as CascadeMode, z as SelectQueryBuilder } from '../select-CCp1oz9p.js';
|
|
2
2
|
|
|
3
3
|
interface EntityOptions {
|
|
4
4
|
tableName?: string;
|
package/dist/decorators/index.js
CHANGED
|
@@ -467,6 +467,44 @@ var SelectQueryState = class _SelectQueryState {
|
|
|
467
467
|
ctes: [...this.ast.ctes ?? [], cte]
|
|
468
468
|
});
|
|
469
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* Adds a set operation (UNION/INTERSECT/EXCEPT) to the query
|
|
472
|
+
* @param op - Set operation node to add
|
|
473
|
+
* @returns New SelectQueryState with set operation
|
|
474
|
+
*/
|
|
475
|
+
withSetOperation(op) {
|
|
476
|
+
return this.clone({
|
|
477
|
+
...this.ast,
|
|
478
|
+
setOps: [...this.ast.setOps ?? [], op]
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
// src/core/ast/join-node.ts
|
|
484
|
+
var createJoinNode = (kind, tableName, condition, relationName) => ({
|
|
485
|
+
type: "Join",
|
|
486
|
+
kind,
|
|
487
|
+
table: { type: "Table", name: tableName },
|
|
488
|
+
condition,
|
|
489
|
+
relationName
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// src/core/sql/sql.ts
|
|
493
|
+
var JOIN_KINDS = {
|
|
494
|
+
/** INNER JOIN type */
|
|
495
|
+
INNER: "INNER",
|
|
496
|
+
/** LEFT JOIN type */
|
|
497
|
+
LEFT: "LEFT",
|
|
498
|
+
/** RIGHT JOIN type */
|
|
499
|
+
RIGHT: "RIGHT",
|
|
500
|
+
/** CROSS JOIN type */
|
|
501
|
+
CROSS: "CROSS"
|
|
502
|
+
};
|
|
503
|
+
var ORDER_DIRECTIONS = {
|
|
504
|
+
/** Ascending order */
|
|
505
|
+
ASC: "ASC",
|
|
506
|
+
/** Descending order */
|
|
507
|
+
DESC: "DESC"
|
|
470
508
|
};
|
|
471
509
|
|
|
472
510
|
// src/query-builder/hydration-manager.ts
|
|
@@ -518,8 +556,26 @@ var HydrationManager = class _HydrationManager {
|
|
|
518
556
|
* @returns AST with hydration metadata
|
|
519
557
|
*/
|
|
520
558
|
applyToAst(ast) {
|
|
559
|
+
if (ast.setOps && ast.setOps.length > 0) {
|
|
560
|
+
return ast;
|
|
561
|
+
}
|
|
521
562
|
const plan = this.planner.getPlan();
|
|
522
563
|
if (!plan) return ast;
|
|
564
|
+
const needsPaginationGuard = this.requiresParentPagination(ast, plan);
|
|
565
|
+
const rewritten = needsPaginationGuard ? this.wrapForParentPagination(ast, plan) : ast;
|
|
566
|
+
return this.attachHydrationMeta(rewritten, plan);
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Gets the current hydration plan
|
|
570
|
+
* @returns Hydration plan or undefined if none exists
|
|
571
|
+
*/
|
|
572
|
+
getPlan() {
|
|
573
|
+
return this.planner.getPlan();
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Attaches hydration metadata to a query AST node.
|
|
577
|
+
*/
|
|
578
|
+
attachHydrationMeta(ast, plan) {
|
|
523
579
|
return {
|
|
524
580
|
...ast,
|
|
525
581
|
meta: {
|
|
@@ -529,11 +585,154 @@ var HydrationManager = class _HydrationManager {
|
|
|
529
585
|
};
|
|
530
586
|
}
|
|
531
587
|
/**
|
|
532
|
-
*
|
|
533
|
-
*
|
|
588
|
+
* Determines whether the query needs pagination rewriting to keep LIMIT/OFFSET
|
|
589
|
+
* applied to parent rows when eager-loading multiplicative relations.
|
|
534
590
|
*/
|
|
535
|
-
|
|
536
|
-
|
|
591
|
+
requiresParentPagination(ast, plan) {
|
|
592
|
+
const hasPagination = ast.limit !== void 0 || ast.offset !== void 0;
|
|
593
|
+
return hasPagination && this.hasMultiplyingRelations(plan);
|
|
594
|
+
}
|
|
595
|
+
hasMultiplyingRelations(plan) {
|
|
596
|
+
return plan.relations.some(
|
|
597
|
+
(rel) => rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Rewrites the query using CTEs so LIMIT/OFFSET target distinct parent rows
|
|
602
|
+
* instead of the joined result set.
|
|
603
|
+
*
|
|
604
|
+
* The strategy:
|
|
605
|
+
* - Hoist the original query (minus limit/offset) into a base CTE.
|
|
606
|
+
* - Select distinct parent ids from that base CTE with the original ordering and pagination.
|
|
607
|
+
* - Join the base CTE against the paged ids to retrieve the joined rows for just that page.
|
|
608
|
+
*/
|
|
609
|
+
wrapForParentPagination(ast, plan) {
|
|
610
|
+
const projectionNames = this.getProjectionNames(ast.columns);
|
|
611
|
+
if (!projectionNames) {
|
|
612
|
+
return ast;
|
|
613
|
+
}
|
|
614
|
+
const projectionAliases = this.buildProjectionAliasMap(ast.columns);
|
|
615
|
+
const projectionSet = new Set(projectionNames);
|
|
616
|
+
const rootPkAlias = projectionAliases.get(`${plan.rootTable}.${plan.rootPrimaryKey}`) ?? plan.rootPrimaryKey;
|
|
617
|
+
const baseCteName = this.nextCteName(ast.ctes, "__metal_pagination_base");
|
|
618
|
+
const baseQuery = {
|
|
619
|
+
...ast,
|
|
620
|
+
ctes: void 0,
|
|
621
|
+
limit: void 0,
|
|
622
|
+
offset: void 0,
|
|
623
|
+
orderBy: void 0,
|
|
624
|
+
meta: void 0
|
|
625
|
+
};
|
|
626
|
+
const baseCte = {
|
|
627
|
+
type: "CommonTableExpression",
|
|
628
|
+
name: baseCteName,
|
|
629
|
+
query: baseQuery,
|
|
630
|
+
recursive: false
|
|
631
|
+
};
|
|
632
|
+
const orderBy = this.mapOrderBy(ast.orderBy, plan, projectionAliases, baseCteName, projectionSet);
|
|
633
|
+
if (orderBy === null) {
|
|
634
|
+
return ast;
|
|
635
|
+
}
|
|
636
|
+
const pageCteName = this.nextCteName([...ast.ctes ?? [], baseCte], "__metal_pagination_page");
|
|
637
|
+
const pagingColumns = this.buildPagingColumns(rootPkAlias, orderBy, baseCteName);
|
|
638
|
+
const pageCte = {
|
|
639
|
+
type: "CommonTableExpression",
|
|
640
|
+
name: pageCteName,
|
|
641
|
+
query: {
|
|
642
|
+
type: "SelectQuery",
|
|
643
|
+
from: { type: "Table", name: baseCteName },
|
|
644
|
+
columns: pagingColumns,
|
|
645
|
+
joins: [],
|
|
646
|
+
distinct: [{ type: "Column", table: baseCteName, name: rootPkAlias }],
|
|
647
|
+
orderBy,
|
|
648
|
+
limit: ast.limit,
|
|
649
|
+
offset: ast.offset
|
|
650
|
+
},
|
|
651
|
+
recursive: false
|
|
652
|
+
};
|
|
653
|
+
const joinCondition = eq(
|
|
654
|
+
{ type: "Column", table: baseCteName, name: rootPkAlias },
|
|
655
|
+
{ type: "Column", table: pageCteName, name: rootPkAlias }
|
|
656
|
+
);
|
|
657
|
+
const outerColumns = projectionNames.map((name) => ({
|
|
658
|
+
type: "Column",
|
|
659
|
+
table: baseCteName,
|
|
660
|
+
name,
|
|
661
|
+
alias: name
|
|
662
|
+
}));
|
|
663
|
+
return {
|
|
664
|
+
type: "SelectQuery",
|
|
665
|
+
from: { type: "Table", name: baseCteName },
|
|
666
|
+
columns: outerColumns,
|
|
667
|
+
joins: [createJoinNode(JOIN_KINDS.INNER, pageCteName, joinCondition)],
|
|
668
|
+
orderBy,
|
|
669
|
+
ctes: [...ast.ctes ?? [], baseCte, pageCte]
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
nextCteName(existing, baseName) {
|
|
673
|
+
const names = new Set((existing ?? []).map((cte) => cte.name));
|
|
674
|
+
let candidate = baseName;
|
|
675
|
+
let suffix = 1;
|
|
676
|
+
while (names.has(candidate)) {
|
|
677
|
+
suffix += 1;
|
|
678
|
+
candidate = `${baseName}_${suffix}`;
|
|
679
|
+
}
|
|
680
|
+
return candidate;
|
|
681
|
+
}
|
|
682
|
+
getProjectionNames(columns) {
|
|
683
|
+
const names = [];
|
|
684
|
+
for (const col of columns) {
|
|
685
|
+
const alias = col.alias ?? col.name;
|
|
686
|
+
if (!alias) return void 0;
|
|
687
|
+
names.push(alias);
|
|
688
|
+
}
|
|
689
|
+
return names;
|
|
690
|
+
}
|
|
691
|
+
buildProjectionAliasMap(columns) {
|
|
692
|
+
const map = /* @__PURE__ */ new Map();
|
|
693
|
+
for (const col of columns) {
|
|
694
|
+
if (col.type !== "Column") continue;
|
|
695
|
+
const node = col;
|
|
696
|
+
const key = `${node.table}.${node.name}`;
|
|
697
|
+
map.set(key, node.alias ?? node.name);
|
|
698
|
+
}
|
|
699
|
+
return map;
|
|
700
|
+
}
|
|
701
|
+
mapOrderBy(orderBy, plan, projectionAliases, baseAlias, availableColumns) {
|
|
702
|
+
if (!orderBy || orderBy.length === 0) {
|
|
703
|
+
return void 0;
|
|
704
|
+
}
|
|
705
|
+
const mapped = [];
|
|
706
|
+
for (const ob of orderBy) {
|
|
707
|
+
if (ob.column.table !== plan.rootTable) {
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
const alias = projectionAliases.get(`${ob.column.table}.${ob.column.name}`) ?? ob.column.name;
|
|
711
|
+
if (!availableColumns.has(alias)) {
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
mapped.push({
|
|
715
|
+
type: "OrderBy",
|
|
716
|
+
column: { type: "Column", table: baseAlias, name: alias },
|
|
717
|
+
direction: ob.direction
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
return mapped;
|
|
721
|
+
}
|
|
722
|
+
buildPagingColumns(primaryKey, orderBy, tableAlias) {
|
|
723
|
+
const columns = [{ type: "Column", table: tableAlias, name: primaryKey, alias: primaryKey }];
|
|
724
|
+
if (!orderBy) return columns;
|
|
725
|
+
for (const ob of orderBy) {
|
|
726
|
+
if (!columns.some((col) => col.name === ob.column.name)) {
|
|
727
|
+
columns.push({
|
|
728
|
+
type: "Column",
|
|
729
|
+
table: tableAlias,
|
|
730
|
+
name: ob.column.name,
|
|
731
|
+
alias: ob.column.name
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return columns;
|
|
537
736
|
}
|
|
538
737
|
};
|
|
539
738
|
|
|
@@ -787,6 +986,20 @@ var QueryAstService = class {
|
|
|
787
986
|
};
|
|
788
987
|
return this.state.withCte(cte);
|
|
789
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Adds a set operation (UNION/UNION ALL/INTERSECT/EXCEPT) to the query
|
|
991
|
+
* @param operator - Set operator
|
|
992
|
+
* @param query - Right-hand side query
|
|
993
|
+
* @returns Updated query state with set operation
|
|
994
|
+
*/
|
|
995
|
+
withSetOperation(operator, query) {
|
|
996
|
+
const op = {
|
|
997
|
+
type: "SetOperation",
|
|
998
|
+
operator,
|
|
999
|
+
query
|
|
1000
|
+
};
|
|
1001
|
+
return this.state.withSetOperation(op);
|
|
1002
|
+
}
|
|
790
1003
|
/**
|
|
791
1004
|
* Selects a subquery as a column
|
|
792
1005
|
* @param alias - Alias for the subquery
|
|
@@ -939,15 +1152,6 @@ var RelationProjectionHelper = class {
|
|
|
939
1152
|
}
|
|
940
1153
|
};
|
|
941
1154
|
|
|
942
|
-
// src/core/ast/join-node.ts
|
|
943
|
-
var createJoinNode = (kind, tableName, condition, relationName) => ({
|
|
944
|
-
type: "Join",
|
|
945
|
-
kind,
|
|
946
|
-
table: { type: "Table", name: tableName },
|
|
947
|
-
condition,
|
|
948
|
-
relationName
|
|
949
|
-
});
|
|
950
|
-
|
|
951
1155
|
// src/query-builder/relation-conditions.ts
|
|
952
1156
|
var assertNever = (value) => {
|
|
953
1157
|
throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
|
|
@@ -1003,24 +1207,6 @@ var buildRelationCorrelation = (root, relation) => {
|
|
|
1003
1207
|
return baseRelationCondition(root, relation);
|
|
1004
1208
|
};
|
|
1005
1209
|
|
|
1006
|
-
// src/core/sql/sql.ts
|
|
1007
|
-
var JOIN_KINDS = {
|
|
1008
|
-
/** INNER JOIN type */
|
|
1009
|
-
INNER: "INNER",
|
|
1010
|
-
/** LEFT JOIN type */
|
|
1011
|
-
LEFT: "LEFT",
|
|
1012
|
-
/** RIGHT JOIN type */
|
|
1013
|
-
RIGHT: "RIGHT",
|
|
1014
|
-
/** CROSS JOIN type */
|
|
1015
|
-
CROSS: "CROSS"
|
|
1016
|
-
};
|
|
1017
|
-
var ORDER_DIRECTIONS = {
|
|
1018
|
-
/** Ascending order */
|
|
1019
|
-
ASC: "ASC",
|
|
1020
|
-
/** Descending order */
|
|
1021
|
-
DESC: "DESC"
|
|
1022
|
-
};
|
|
1023
|
-
|
|
1024
1210
|
// src/query-builder/relation-service.ts
|
|
1025
1211
|
var RelationService = class {
|
|
1026
1212
|
/**
|
|
@@ -1466,6 +1652,16 @@ var hasEntityMeta = (entity) => {
|
|
|
1466
1652
|
|
|
1467
1653
|
// src/orm/relations/has-many.ts
|
|
1468
1654
|
var toKey2 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
1655
|
+
var hideInternal = (obj, keys) => {
|
|
1656
|
+
for (const key of keys) {
|
|
1657
|
+
Object.defineProperty(obj, key, {
|
|
1658
|
+
value: obj[key],
|
|
1659
|
+
writable: false,
|
|
1660
|
+
configurable: false,
|
|
1661
|
+
enumerable: false
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1469
1665
|
var DefaultHasManyCollection = class {
|
|
1470
1666
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
|
|
1471
1667
|
this.ctx = ctx;
|
|
@@ -1481,6 +1677,7 @@ var DefaultHasManyCollection = class {
|
|
|
1481
1677
|
this.items = [];
|
|
1482
1678
|
this.added = /* @__PURE__ */ new Set();
|
|
1483
1679
|
this.removed = /* @__PURE__ */ new Set();
|
|
1680
|
+
hideInternal(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
|
|
1484
1681
|
this.hydrateFromCache();
|
|
1485
1682
|
}
|
|
1486
1683
|
async load() {
|
|
@@ -1556,10 +1753,23 @@ var DefaultHasManyCollection = class {
|
|
|
1556
1753
|
this.items = rows.map((row) => this.createEntity(row));
|
|
1557
1754
|
this.loaded = true;
|
|
1558
1755
|
}
|
|
1756
|
+
toJSON() {
|
|
1757
|
+
return this.items;
|
|
1758
|
+
}
|
|
1559
1759
|
};
|
|
1560
1760
|
|
|
1561
1761
|
// src/orm/relations/belongs-to.ts
|
|
1562
1762
|
var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
1763
|
+
var hideInternal2 = (obj, keys) => {
|
|
1764
|
+
for (const key of keys) {
|
|
1765
|
+
Object.defineProperty(obj, key, {
|
|
1766
|
+
value: obj[key],
|
|
1767
|
+
writable: false,
|
|
1768
|
+
configurable: false,
|
|
1769
|
+
enumerable: false
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
};
|
|
1563
1773
|
var DefaultBelongsToReference = class {
|
|
1564
1774
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
|
|
1565
1775
|
this.ctx = ctx;
|
|
@@ -1573,6 +1783,7 @@ var DefaultBelongsToReference = class {
|
|
|
1573
1783
|
this.targetKey = targetKey;
|
|
1574
1784
|
this.loaded = false;
|
|
1575
1785
|
this.current = null;
|
|
1786
|
+
hideInternal2(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
|
|
1576
1787
|
this.populateFromHydrationCache();
|
|
1577
1788
|
}
|
|
1578
1789
|
async load() {
|
|
@@ -1633,10 +1844,23 @@ var DefaultBelongsToReference = class {
|
|
|
1633
1844
|
this.current = this.createEntity(row);
|
|
1634
1845
|
this.loaded = true;
|
|
1635
1846
|
}
|
|
1847
|
+
toJSON() {
|
|
1848
|
+
return this.current;
|
|
1849
|
+
}
|
|
1636
1850
|
};
|
|
1637
1851
|
|
|
1638
1852
|
// src/orm/relations/many-to-many.ts
|
|
1639
1853
|
var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
1854
|
+
var hideInternal3 = (obj, keys) => {
|
|
1855
|
+
for (const key of keys) {
|
|
1856
|
+
Object.defineProperty(obj, key, {
|
|
1857
|
+
value: obj[key],
|
|
1858
|
+
writable: false,
|
|
1859
|
+
configurable: false,
|
|
1860
|
+
enumerable: false
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
};
|
|
1640
1864
|
var DefaultManyToManyCollection = class {
|
|
1641
1865
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
|
|
1642
1866
|
this.ctx = ctx;
|
|
@@ -1650,6 +1874,7 @@ var DefaultManyToManyCollection = class {
|
|
|
1650
1874
|
this.localKey = localKey;
|
|
1651
1875
|
this.loaded = false;
|
|
1652
1876
|
this.items = [];
|
|
1877
|
+
hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
|
|
1653
1878
|
this.hydrateFromCache();
|
|
1654
1879
|
}
|
|
1655
1880
|
async load() {
|
|
@@ -1755,6 +1980,9 @@ var DefaultManyToManyCollection = class {
|
|
|
1755
1980
|
});
|
|
1756
1981
|
this.loaded = true;
|
|
1757
1982
|
}
|
|
1983
|
+
toJSON() {
|
|
1984
|
+
return this.items;
|
|
1985
|
+
}
|
|
1758
1986
|
};
|
|
1759
1987
|
|
|
1760
1988
|
// src/orm/lazy-batch.ts
|
|
@@ -2115,9 +2343,15 @@ var flattenResults = (results) => {
|
|
|
2115
2343
|
return rows;
|
|
2116
2344
|
};
|
|
2117
2345
|
async function executeHydrated(ctx, qb) {
|
|
2118
|
-
const
|
|
2346
|
+
const ast = qb.getAST();
|
|
2347
|
+
const compiled = ctx.dialect.compileSelect(ast);
|
|
2119
2348
|
const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
|
|
2120
2349
|
const rows = flattenResults(executed);
|
|
2350
|
+
if (ast.setOps && ast.setOps.length > 0) {
|
|
2351
|
+
return rows.map(
|
|
2352
|
+
(row) => createEntityProxy(ctx, qb.getTable(), row, qb.getLazyRelations())
|
|
2353
|
+
);
|
|
2354
|
+
}
|
|
2121
2355
|
const hydrated = hydrateRows(rows, qb.getHydrationPlan());
|
|
2122
2356
|
return hydrated.map(
|
|
2123
2357
|
(row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
|
|
@@ -2164,6 +2398,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
2164
2398
|
const joinNode = createJoinNode(kind, table.name, condition);
|
|
2165
2399
|
return this.applyAst(context, (service) => service.withJoin(joinNode));
|
|
2166
2400
|
}
|
|
2401
|
+
applySetOperation(operator, query) {
|
|
2402
|
+
const subAst = this.resolveQueryNode(query);
|
|
2403
|
+
return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
|
|
2404
|
+
}
|
|
2167
2405
|
/**
|
|
2168
2406
|
* Selects specific columns for the query
|
|
2169
2407
|
* @param columns - Record of column definitions, function nodes, case expressions, or window functions
|
|
@@ -2352,6 +2590,38 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
2352
2590
|
const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
|
|
2353
2591
|
return this.clone(nextContext);
|
|
2354
2592
|
}
|
|
2593
|
+
/**
|
|
2594
|
+
* Combines this query with another using UNION
|
|
2595
|
+
* @param query - Query to union with
|
|
2596
|
+
* @returns New query builder instance with the set operation
|
|
2597
|
+
*/
|
|
2598
|
+
union(query) {
|
|
2599
|
+
return this.clone(this.applySetOperation("UNION", query));
|
|
2600
|
+
}
|
|
2601
|
+
/**
|
|
2602
|
+
* Combines this query with another using UNION ALL
|
|
2603
|
+
* @param query - Query to union with
|
|
2604
|
+
* @returns New query builder instance with the set operation
|
|
2605
|
+
*/
|
|
2606
|
+
unionAll(query) {
|
|
2607
|
+
return this.clone(this.applySetOperation("UNION ALL", query));
|
|
2608
|
+
}
|
|
2609
|
+
/**
|
|
2610
|
+
* Combines this query with another using INTERSECT
|
|
2611
|
+
* @param query - Query to intersect with
|
|
2612
|
+
* @returns New query builder instance with the set operation
|
|
2613
|
+
*/
|
|
2614
|
+
intersect(query) {
|
|
2615
|
+
return this.clone(this.applySetOperation("INTERSECT", query));
|
|
2616
|
+
}
|
|
2617
|
+
/**
|
|
2618
|
+
* Combines this query with another using EXCEPT
|
|
2619
|
+
* @param query - Query to subtract
|
|
2620
|
+
* @returns New query builder instance with the set operation
|
|
2621
|
+
*/
|
|
2622
|
+
except(query) {
|
|
2623
|
+
return this.clone(this.applySetOperation("EXCEPT", query));
|
|
2624
|
+
}
|
|
2355
2625
|
/**
|
|
2356
2626
|
* Adds a WHERE EXISTS condition to the query
|
|
2357
2627
|
* @param subquery - Subquery to check for existence
|