metal-orm 1.0.115 → 1.0.116
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/dist/index.cjs +688 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -26
- package/dist/index.d.ts +49 -26
- package/dist/index.js +688 -82
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ast/expression-builders.ts +7 -7
- package/src/core/ast/expression-nodes.ts +5 -5
- package/src/dto/apply-filter.ts +313 -21
- package/src/dto/filter-types.ts +57 -47
- package/src/dto/index.ts +23 -17
- package/src/query-builder/expression-table-mapper.ts +284 -0
- package/src/query-builder/join-utils.ts +14 -0
- package/src/query-builder/relation-alias.ts +1 -1
- package/src/query-builder/relation-include-strategies.ts +1 -21
- package/src/query-builder/relation-include-tree.ts +37 -0
- package/src/query-builder/relation-join-planner.ts +22 -139
- package/src/query-builder/relation-join-strategies.ts +120 -0
- package/src/query-builder/relation-key.ts +4 -0
- package/src/query-builder/relation-manager.ts +8 -8
- package/src/query-builder/relation-service.ts +80 -80
- package/src/query-builder/select-query-state.ts +18 -6
- package/src/query-builder/select.ts +1175 -1158
- package/src/query-builder/table-alias-utils.ts +90 -0
- package/src/query-builder/update-include.ts +102 -0
package/dist/index.cjs
CHANGED
|
@@ -3420,6 +3420,17 @@ var SelectQueryState = class _SelectQueryState {
|
|
|
3420
3420
|
joins: [...this.ast.joins ?? [], join]
|
|
3421
3421
|
});
|
|
3422
3422
|
}
|
|
3423
|
+
/**
|
|
3424
|
+
* Replaces the JOIN list.
|
|
3425
|
+
* @param joins - Join nodes to set
|
|
3426
|
+
* @returns New SelectQueryState with updated JOINs
|
|
3427
|
+
*/
|
|
3428
|
+
withJoins(joins) {
|
|
3429
|
+
return this.clone({
|
|
3430
|
+
...this.ast,
|
|
3431
|
+
joins
|
|
3432
|
+
});
|
|
3433
|
+
}
|
|
3423
3434
|
/**
|
|
3424
3435
|
* Replaces the FROM clause.
|
|
3425
3436
|
* @param from - Table source for the FROM clause
|
|
@@ -4342,9 +4353,6 @@ var buildRelationCorrelation = (root, relation, rootAlias, targetTableName) => {
|
|
|
4342
4353
|
return baseRelationCondition(root, relation, rootAlias, targetTableName);
|
|
4343
4354
|
};
|
|
4344
4355
|
|
|
4345
|
-
// src/core/ast/join-metadata.ts
|
|
4346
|
-
var getJoinRelationName = (join) => join.meta?.relationName;
|
|
4347
|
-
|
|
4348
4356
|
// src/query-builder/relation-filter-utils.ts
|
|
4349
4357
|
var splitFilterExpressions = (filter, allowedTables) => {
|
|
4350
4358
|
const terms = flattenAnd(filter);
|
|
@@ -4482,7 +4490,241 @@ var collectFromOrderingTerm = (term, collector) => {
|
|
|
4482
4490
|
collectFromExpression(term, collector);
|
|
4483
4491
|
};
|
|
4484
4492
|
|
|
4485
|
-
// src/query-builder/
|
|
4493
|
+
// src/query-builder/expression-table-mapper.ts
|
|
4494
|
+
var remapExpressionTable = (expr, fromTable, toTable) => {
|
|
4495
|
+
if (!expr || fromTable === toTable) return expr;
|
|
4496
|
+
return mapExpression(expr, fromTable, toTable);
|
|
4497
|
+
};
|
|
4498
|
+
var mapExpression = (expr, fromTable, toTable) => {
|
|
4499
|
+
switch (expr.type) {
|
|
4500
|
+
case "BinaryExpression": {
|
|
4501
|
+
const left2 = mapOperand(expr.left, fromTable, toTable);
|
|
4502
|
+
const right2 = mapOperand(expr.right, fromTable, toTable);
|
|
4503
|
+
if (left2 === expr.left && right2 === expr.right) return expr;
|
|
4504
|
+
return { ...expr, left: left2, right: right2 };
|
|
4505
|
+
}
|
|
4506
|
+
case "LogicalExpression": {
|
|
4507
|
+
const nextOperands = expr.operands.map((op) => mapExpression(op, fromTable, toTable));
|
|
4508
|
+
if (nextOperands.every((op, i) => op === expr.operands[i])) return expr;
|
|
4509
|
+
return { ...expr, operands: nextOperands };
|
|
4510
|
+
}
|
|
4511
|
+
case "NullExpression": {
|
|
4512
|
+
const left2 = mapOperand(expr.left, fromTable, toTable);
|
|
4513
|
+
if (left2 === expr.left) return expr;
|
|
4514
|
+
return { ...expr, left: left2 };
|
|
4515
|
+
}
|
|
4516
|
+
case "InExpression": {
|
|
4517
|
+
const left2 = mapOperand(expr.left, fromTable, toTable);
|
|
4518
|
+
let right2 = expr.right;
|
|
4519
|
+
if (Array.isArray(expr.right)) {
|
|
4520
|
+
const mapped = expr.right.map((val) => mapOperand(val, fromTable, toTable));
|
|
4521
|
+
if (!mapped.every((val, i) => val === expr.right[i])) {
|
|
4522
|
+
right2 = mapped;
|
|
4523
|
+
}
|
|
4524
|
+
} else if (expr.right.type === "ScalarSubquery") {
|
|
4525
|
+
const mapped = mapScalarSubquery(expr.right, fromTable, toTable);
|
|
4526
|
+
if (mapped !== expr.right) right2 = mapped;
|
|
4527
|
+
}
|
|
4528
|
+
if (left2 === expr.left && right2 === expr.right) return expr;
|
|
4529
|
+
return { ...expr, left: left2, right: right2 };
|
|
4530
|
+
}
|
|
4531
|
+
case "ExistsExpression": {
|
|
4532
|
+
const mapped = mapSelectQuery(expr.subquery, fromTable, toTable);
|
|
4533
|
+
if (mapped === expr.subquery) return expr;
|
|
4534
|
+
return { ...expr, subquery: mapped };
|
|
4535
|
+
}
|
|
4536
|
+
case "BetweenExpression": {
|
|
4537
|
+
const left2 = mapOperand(expr.left, fromTable, toTable);
|
|
4538
|
+
const lower2 = mapOperand(expr.lower, fromTable, toTable);
|
|
4539
|
+
const upper2 = mapOperand(expr.upper, fromTable, toTable);
|
|
4540
|
+
if (left2 === expr.left && lower2 === expr.lower && upper2 === expr.upper) return expr;
|
|
4541
|
+
return { ...expr, left: left2, lower: lower2, upper: upper2 };
|
|
4542
|
+
}
|
|
4543
|
+
case "ArithmeticExpression": {
|
|
4544
|
+
const left2 = mapOperand(expr.left, fromTable, toTable);
|
|
4545
|
+
const right2 = mapOperand(expr.right, fromTable, toTable);
|
|
4546
|
+
if (left2 === expr.left && right2 === expr.right) return expr;
|
|
4547
|
+
return { ...expr, left: left2, right: right2 };
|
|
4548
|
+
}
|
|
4549
|
+
case "BitwiseExpression": {
|
|
4550
|
+
const left2 = mapOperand(expr.left, fromTable, toTable);
|
|
4551
|
+
const right2 = mapOperand(expr.right, fromTable, toTable);
|
|
4552
|
+
if (left2 === expr.left && right2 === expr.right) return expr;
|
|
4553
|
+
return { ...expr, left: left2, right: right2 };
|
|
4554
|
+
}
|
|
4555
|
+
default:
|
|
4556
|
+
return expr;
|
|
4557
|
+
}
|
|
4558
|
+
};
|
|
4559
|
+
var mapColumn = (node, fromTable, toTable) => {
|
|
4560
|
+
if (node.table !== fromTable) return node;
|
|
4561
|
+
return { ...node, table: toTable };
|
|
4562
|
+
};
|
|
4563
|
+
var mapJsonPath = (node, fromTable, toTable) => {
|
|
4564
|
+
const nextColumn = mapColumn(node.column, fromTable, toTable);
|
|
4565
|
+
if (nextColumn === node.column) return node;
|
|
4566
|
+
return { ...node, column: nextColumn };
|
|
4567
|
+
};
|
|
4568
|
+
var mapWindowFunctionArg = (node, fromTable, toTable) => {
|
|
4569
|
+
switch (node.type) {
|
|
4570
|
+
case "Column":
|
|
4571
|
+
return mapColumn(node, fromTable, toTable);
|
|
4572
|
+
case "JsonPath":
|
|
4573
|
+
return mapJsonPath(node, fromTable, toTable);
|
|
4574
|
+
default:
|
|
4575
|
+
return node;
|
|
4576
|
+
}
|
|
4577
|
+
};
|
|
4578
|
+
var mapOperand = (node, fromTable, toTable) => {
|
|
4579
|
+
switch (node.type) {
|
|
4580
|
+
case "Column":
|
|
4581
|
+
return mapColumn(node, fromTable, toTable);
|
|
4582
|
+
case "Function": {
|
|
4583
|
+
const nextArgs = node.args.map((arg) => mapOperand(arg, fromTable, toTable));
|
|
4584
|
+
const nextSeparator = node.separator ? mapOperand(node.separator, fromTable, toTable) : node.separator;
|
|
4585
|
+
const nextOrderBy = node.orderBy?.map((order) => ({
|
|
4586
|
+
...order,
|
|
4587
|
+
term: mapOrderingTerm(order.term, fromTable, toTable)
|
|
4588
|
+
}));
|
|
4589
|
+
const changed = nextArgs.some((arg, i) => arg !== node.args[i]) || nextSeparator !== node.separator || nextOrderBy && node.orderBy && nextOrderBy.some((ob, i) => ob.term !== node.orderBy[i].term);
|
|
4590
|
+
if (!changed) return node;
|
|
4591
|
+
return {
|
|
4592
|
+
...node,
|
|
4593
|
+
args: nextArgs,
|
|
4594
|
+
separator: nextSeparator,
|
|
4595
|
+
orderBy: nextOrderBy
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
case "JsonPath": {
|
|
4599
|
+
return mapJsonPath(node, fromTable, toTable);
|
|
4600
|
+
}
|
|
4601
|
+
case "ScalarSubquery": {
|
|
4602
|
+
return mapScalarSubquery(node, fromTable, toTable);
|
|
4603
|
+
}
|
|
4604
|
+
case "CaseExpression": {
|
|
4605
|
+
const nextConditions = node.conditions.map((cond) => ({
|
|
4606
|
+
when: mapExpression(cond.when, fromTable, toTable),
|
|
4607
|
+
then: mapOperand(cond.then, fromTable, toTable)
|
|
4608
|
+
}));
|
|
4609
|
+
const nextElse = node.else ? mapOperand(node.else, fromTable, toTable) : node.else;
|
|
4610
|
+
const changed = nextConditions.some(
|
|
4611
|
+
(cond, i) => cond.when !== node.conditions[i].when || cond.then !== node.conditions[i].then
|
|
4612
|
+
) || nextElse !== node.else;
|
|
4613
|
+
if (!changed) return node;
|
|
4614
|
+
return { ...node, conditions: nextConditions, else: nextElse };
|
|
4615
|
+
}
|
|
4616
|
+
case "Cast": {
|
|
4617
|
+
const nextExpr = mapOperand(node.expression, fromTable, toTable);
|
|
4618
|
+
if (nextExpr === node.expression) return node;
|
|
4619
|
+
return { ...node, expression: nextExpr };
|
|
4620
|
+
}
|
|
4621
|
+
case "WindowFunction": {
|
|
4622
|
+
const nextArgs = node.args.map((arg) => mapWindowFunctionArg(arg, fromTable, toTable));
|
|
4623
|
+
const nextPartition = node.partitionBy?.map((part) => mapColumn(part, fromTable, toTable));
|
|
4624
|
+
const nextOrderBy = node.orderBy?.map((order) => ({
|
|
4625
|
+
...order,
|
|
4626
|
+
term: mapOrderingTerm(order.term, fromTable, toTable)
|
|
4627
|
+
}));
|
|
4628
|
+
const changed = nextArgs.some((arg, i) => arg !== node.args[i]) || nextPartition && node.partitionBy && nextPartition.some((p, i) => p !== node.partitionBy[i]) || nextOrderBy && node.orderBy && nextOrderBy.some((ob, i) => ob.term !== node.orderBy[i].term);
|
|
4629
|
+
if (!changed) return node;
|
|
4630
|
+
return {
|
|
4631
|
+
...node,
|
|
4632
|
+
args: nextArgs,
|
|
4633
|
+
partitionBy: nextPartition,
|
|
4634
|
+
orderBy: nextOrderBy
|
|
4635
|
+
};
|
|
4636
|
+
}
|
|
4637
|
+
case "Collate": {
|
|
4638
|
+
const nextExpr = mapOperand(node.expression, fromTable, toTable);
|
|
4639
|
+
if (nextExpr === node.expression) return node;
|
|
4640
|
+
return { ...node, expression: nextExpr };
|
|
4641
|
+
}
|
|
4642
|
+
case "ArithmeticExpression": {
|
|
4643
|
+
const left2 = mapOperand(node.left, fromTable, toTable);
|
|
4644
|
+
const right2 = mapOperand(node.right, fromTable, toTable);
|
|
4645
|
+
if (left2 === node.left && right2 === node.right) return node;
|
|
4646
|
+
return { ...node, left: left2, right: right2 };
|
|
4647
|
+
}
|
|
4648
|
+
case "BitwiseExpression": {
|
|
4649
|
+
const left2 = mapOperand(node.left, fromTable, toTable);
|
|
4650
|
+
const right2 = mapOperand(node.right, fromTable, toTable);
|
|
4651
|
+
if (left2 === node.left && right2 === node.right) return node;
|
|
4652
|
+
return { ...node, left: left2, right: right2 };
|
|
4653
|
+
}
|
|
4654
|
+
default:
|
|
4655
|
+
return node;
|
|
4656
|
+
}
|
|
4657
|
+
};
|
|
4658
|
+
var mapOrderingTerm = (term, fromTable, toTable) => {
|
|
4659
|
+
if (isOperandNode(term)) {
|
|
4660
|
+
return mapOperand(term, fromTable, toTable);
|
|
4661
|
+
}
|
|
4662
|
+
return mapExpression(term, fromTable, toTable);
|
|
4663
|
+
};
|
|
4664
|
+
var mapScalarSubquery = (node, fromTable, toTable) => {
|
|
4665
|
+
const mapped = mapSelectQuery(node.query, fromTable, toTable);
|
|
4666
|
+
if (mapped === node.query) return node;
|
|
4667
|
+
return { ...node, query: mapped };
|
|
4668
|
+
};
|
|
4669
|
+
var mapProjectionNode = (node, fromTable, toTable) => {
|
|
4670
|
+
switch (node.type) {
|
|
4671
|
+
case "Column":
|
|
4672
|
+
return mapColumn(node, fromTable, toTable);
|
|
4673
|
+
case "Function":
|
|
4674
|
+
return mapOperand(node, fromTable, toTable);
|
|
4675
|
+
case "CaseExpression":
|
|
4676
|
+
return mapOperand(node, fromTable, toTable);
|
|
4677
|
+
case "Cast":
|
|
4678
|
+
return mapOperand(node, fromTable, toTable);
|
|
4679
|
+
case "WindowFunction":
|
|
4680
|
+
return mapOperand(node, fromTable, toTable);
|
|
4681
|
+
case "ScalarSubquery":
|
|
4682
|
+
return mapScalarSubquery(node, fromTable, toTable);
|
|
4683
|
+
default:
|
|
4684
|
+
return node;
|
|
4685
|
+
}
|
|
4686
|
+
};
|
|
4687
|
+
var mapSelectQuery = (query, fromTable, toTable) => {
|
|
4688
|
+
const nextColumns = query.columns.map((col2) => mapProjectionNode(col2, fromTable, toTable));
|
|
4689
|
+
const nextJoins = query.joins.map((join) => ({
|
|
4690
|
+
...join,
|
|
4691
|
+
condition: mapExpression(join.condition, fromTable, toTable)
|
|
4692
|
+
}));
|
|
4693
|
+
const nextWhere = query.where ? mapExpression(query.where, fromTable, toTable) : query.where;
|
|
4694
|
+
const nextHaving = query.having ? mapExpression(query.having, fromTable, toTable) : query.having;
|
|
4695
|
+
const nextGroupBy = query.groupBy?.map((term) => mapOrderingTerm(term, fromTable, toTable));
|
|
4696
|
+
const nextOrderBy = query.orderBy?.map((ob) => ({
|
|
4697
|
+
...ob,
|
|
4698
|
+
term: mapOrderingTerm(ob.term, fromTable, toTable)
|
|
4699
|
+
}));
|
|
4700
|
+
const nextDistinct = query.distinct?.map((col2) => mapColumn(col2, fromTable, toTable));
|
|
4701
|
+
const nextSetOps = query.setOps?.map((op) => ({ ...op, query: mapSelectQuery(op.query, fromTable, toTable) }));
|
|
4702
|
+
const nextCtes = query.ctes?.map((cte) => ({ ...cte, query: mapSelectQuery(cte.query, fromTable, toTable) }));
|
|
4703
|
+
const changed = nextColumns.some((c, i) => c !== query.columns[i]) || nextJoins.some((j, i) => j.condition !== query.joins[i].condition) || nextWhere !== query.where || nextHaving !== query.having || nextGroupBy && query.groupBy && nextGroupBy.some((t, i) => t !== query.groupBy[i]) || nextOrderBy && query.orderBy && nextOrderBy.some((o, i) => o.term !== query.orderBy[i].term) || nextDistinct && query.distinct && nextDistinct.some((d, i) => d !== query.distinct[i]) || nextSetOps && query.setOps && nextSetOps.some((o, i) => o.query !== query.setOps[i].query) || nextCtes && query.ctes && nextCtes.some((c, i) => c.query !== query.ctes[i].query);
|
|
4704
|
+
if (!changed) return query;
|
|
4705
|
+
return {
|
|
4706
|
+
...query,
|
|
4707
|
+
columns: nextColumns,
|
|
4708
|
+
joins: nextJoins,
|
|
4709
|
+
where: nextWhere,
|
|
4710
|
+
having: nextHaving,
|
|
4711
|
+
groupBy: nextGroupBy,
|
|
4712
|
+
orderBy: nextOrderBy,
|
|
4713
|
+
distinct: nextDistinct,
|
|
4714
|
+
setOps: nextSetOps,
|
|
4715
|
+
ctes: nextCtes
|
|
4716
|
+
};
|
|
4717
|
+
};
|
|
4718
|
+
|
|
4719
|
+
// src/core/ast/join-metadata.ts
|
|
4720
|
+
var getJoinRelationName = (join) => join.meta?.relationName;
|
|
4721
|
+
|
|
4722
|
+
// src/query-builder/join-utils.ts
|
|
4723
|
+
var findJoinIndexByRelationKey = (joins, relationKey) => joins.findIndex((j) => getJoinRelationName(j) === relationKey);
|
|
4724
|
+
var findJoinByRelationKey = (joins, relationKey) => joins.find((j) => getJoinRelationName(j) === relationKey);
|
|
4725
|
+
var hasJoinForRelationKey = (joins, relationKey) => findJoinIndexByRelationKey(joins, relationKey) !== -1;
|
|
4726
|
+
|
|
4727
|
+
// src/query-builder/table-alias-utils.ts
|
|
4486
4728
|
var getExposedName = (ts) => {
|
|
4487
4729
|
if (ts.type === "Table") return ts.alias ?? ts.name;
|
|
4488
4730
|
if (ts.type === "DerivedTable") return ts.alias;
|
|
@@ -4514,69 +4756,118 @@ var ensureCorrelationName = (state, relationName, ts, extraUsed) => {
|
|
|
4514
4756
|
const alias = makeUniqueAlias(relationName, used);
|
|
4515
4757
|
return { ...ts, alias };
|
|
4516
4758
|
};
|
|
4517
|
-
var
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4759
|
+
var getJoinCorrelationName = (state, relationName, fallback) => {
|
|
4760
|
+
const join = findJoinByRelationKey(state.ast.joins, relationName);
|
|
4761
|
+
if (!join) return fallback;
|
|
4762
|
+
const t = join.table;
|
|
4763
|
+
if (t.type === "Table") return t.alias ?? t.name;
|
|
4764
|
+
if (t.type === "DerivedTable") return t.alias;
|
|
4765
|
+
if (t.type === "FunctionTable") return t.alias ?? fallback;
|
|
4766
|
+
return fallback;
|
|
4767
|
+
};
|
|
4768
|
+
var resolveTargetTableName = (target, fallback) => {
|
|
4769
|
+
if (target.type === "Table") return target.alias ?? target.name;
|
|
4770
|
+
if (target.type === "DerivedTable") return target.alias;
|
|
4771
|
+
if (target.type === "FunctionTable") return target.alias ?? fallback;
|
|
4772
|
+
return fallback;
|
|
4773
|
+
};
|
|
4774
|
+
|
|
4775
|
+
// src/query-builder/relation-join-strategies.ts
|
|
4776
|
+
var buildBelongsToManyTargetCondition = (relation, targetName, extra) => {
|
|
4777
|
+
const targetKey = relation.targetKey || findPrimaryKey(relation.target);
|
|
4778
|
+
let condition = eq(
|
|
4779
|
+
{ type: "Column", table: targetName, name: targetKey },
|
|
4780
|
+
{ type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
|
|
4781
|
+
);
|
|
4782
|
+
if (extra) {
|
|
4783
|
+
condition = and(condition, extra);
|
|
4521
4784
|
}
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
};
|
|
4530
|
-
targetTableSource = ensureCorrelationName(
|
|
4531
|
-
state,
|
|
4532
|
-
relationName,
|
|
4533
|
-
targetTableSource,
|
|
4534
|
-
[relation.pivotTable.name]
|
|
4535
|
-
);
|
|
4536
|
-
const targetName2 = this.resolveTargetTableName(targetTableSource, relation);
|
|
4537
|
-
const joins = buildBelongsToManyJoins(
|
|
4538
|
-
this.table,
|
|
4539
|
-
relationName,
|
|
4540
|
-
relation,
|
|
4541
|
-
joinKind,
|
|
4542
|
-
extraCondition,
|
|
4543
|
-
rootAlias,
|
|
4544
|
-
targetTableSource,
|
|
4545
|
-
targetName2
|
|
4546
|
-
);
|
|
4547
|
-
return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
|
|
4548
|
-
}
|
|
4549
|
-
let targetTable = tableSource ?? {
|
|
4785
|
+
return condition;
|
|
4786
|
+
};
|
|
4787
|
+
var addRelationJoin = (params) => {
|
|
4788
|
+
const { state, rootTable, rootAlias, relationKey, relation, joinKind, filter, tableSource } = params;
|
|
4789
|
+
if (relation.type === RelationKinds.BelongsToMany) {
|
|
4790
|
+
const many = relation;
|
|
4791
|
+
let targetSource2 = tableSource ?? {
|
|
4550
4792
|
type: "Table",
|
|
4551
4793
|
name: relation.target.name,
|
|
4552
4794
|
schema: relation.target.schema
|
|
4553
4795
|
};
|
|
4554
|
-
|
|
4555
|
-
const
|
|
4556
|
-
const
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4796
|
+
targetSource2 = ensureCorrelationName(state, relationKey, targetSource2, [many.pivotTable.name]);
|
|
4797
|
+
const targetName2 = resolveTargetTableName(targetSource2, relation.target.name);
|
|
4798
|
+
const extra2 = remapExpressionTable(filter, relation.target.name, targetName2);
|
|
4799
|
+
const joins = buildBelongsToManyJoins(
|
|
4800
|
+
rootTable,
|
|
4801
|
+
relationKey,
|
|
4802
|
+
many,
|
|
4803
|
+
joinKind,
|
|
4804
|
+
extra2,
|
|
4560
4805
|
rootAlias,
|
|
4561
|
-
|
|
4806
|
+
targetSource2,
|
|
4807
|
+
targetName2
|
|
4562
4808
|
);
|
|
4563
|
-
|
|
4564
|
-
return this.astService(state).withJoin(joinNode);
|
|
4809
|
+
return joins.reduce((curr, join) => curr.withJoin(join), state);
|
|
4565
4810
|
}
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4811
|
+
let targetSource = tableSource ?? {
|
|
4812
|
+
type: "Table",
|
|
4813
|
+
name: relation.target.name,
|
|
4814
|
+
schema: relation.target.schema
|
|
4815
|
+
};
|
|
4816
|
+
targetSource = ensureCorrelationName(state, relationKey, targetSource);
|
|
4817
|
+
const targetName = resolveTargetTableName(targetSource, relation.target.name);
|
|
4818
|
+
const extra = remapExpressionTable(filter, relation.target.name, targetName);
|
|
4819
|
+
const condition = buildRelationJoinCondition(rootTable, relation, extra, rootAlias, targetName);
|
|
4820
|
+
const joinNode = createJoinNode(joinKind, targetSource, condition, relationKey);
|
|
4821
|
+
return state.withJoin(joinNode);
|
|
4822
|
+
};
|
|
4823
|
+
var updateRelationJoin = (params) => {
|
|
4824
|
+
const { joins, joinIndex, relation, currentTable, currentAlias, options } = params;
|
|
4825
|
+
const join = joins[joinIndex];
|
|
4826
|
+
const targetName = resolveTargetTableName(join.table, relation.target.name);
|
|
4827
|
+
const extra = remapExpressionTable(options.filter, relation.target.name, targetName);
|
|
4828
|
+
if (relation.type === RelationKinds.BelongsToMany) {
|
|
4829
|
+
const many = relation;
|
|
4830
|
+
const targetCondition = buildBelongsToManyTargetCondition(many, targetName, extra);
|
|
4831
|
+
joins[joinIndex] = {
|
|
4832
|
+
...join,
|
|
4833
|
+
kind: options.joinKind ?? join.kind,
|
|
4834
|
+
condition: targetCondition
|
|
4835
|
+
};
|
|
4836
|
+
if (options.joinKind && joinIndex > 0) {
|
|
4837
|
+
const pivotJoin = joins[joinIndex - 1];
|
|
4838
|
+
const pivotTable = pivotJoin.table.type === "Table" ? pivotJoin.table.name : void 0;
|
|
4839
|
+
if (pivotTable === many.pivotTable.name) {
|
|
4840
|
+
joins[joinIndex - 1] = { ...pivotJoin, kind: options.joinKind };
|
|
4841
|
+
}
|
|
4578
4842
|
}
|
|
4579
|
-
return
|
|
4843
|
+
return joins;
|
|
4844
|
+
}
|
|
4845
|
+
const condition = buildRelationJoinCondition(currentTable, relation, extra, currentAlias, targetName);
|
|
4846
|
+
joins[joinIndex] = {
|
|
4847
|
+
...join,
|
|
4848
|
+
kind: options.joinKind ?? join.kind,
|
|
4849
|
+
condition
|
|
4850
|
+
};
|
|
4851
|
+
return joins;
|
|
4852
|
+
};
|
|
4853
|
+
|
|
4854
|
+
// src/query-builder/relation-join-planner.ts
|
|
4855
|
+
var RelationJoinPlanner = class {
|
|
4856
|
+
constructor(table) {
|
|
4857
|
+
this.table = table;
|
|
4858
|
+
}
|
|
4859
|
+
withJoin(state, relationName, relation, joinKind, extraCondition, tableSource) {
|
|
4860
|
+
const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
|
|
4861
|
+
return addRelationJoin({
|
|
4862
|
+
state,
|
|
4863
|
+
rootTable: this.table,
|
|
4864
|
+
rootAlias,
|
|
4865
|
+
relationKey: relationName,
|
|
4866
|
+
relation,
|
|
4867
|
+
joinKind,
|
|
4868
|
+
filter: extraCondition,
|
|
4869
|
+
tableSource
|
|
4870
|
+
});
|
|
4580
4871
|
}
|
|
4581
4872
|
};
|
|
4582
4873
|
|
|
@@ -4627,15 +4918,6 @@ var RelationCteBuilder = class {
|
|
|
4627
4918
|
};
|
|
4628
4919
|
|
|
4629
4920
|
// src/query-builder/relation-include-strategies.ts
|
|
4630
|
-
var getJoinCorrelationName = (state, relationName, fallback) => {
|
|
4631
|
-
const join = state.ast.joins.find((j) => getJoinRelationName(j) === relationName);
|
|
4632
|
-
if (!join) return fallback;
|
|
4633
|
-
const t = join.table;
|
|
4634
|
-
if (t.type === "Table") return t.alias ?? t.name;
|
|
4635
|
-
if (t.type === "DerivedTable") return t.alias;
|
|
4636
|
-
if (t.type === "FunctionTable") return t.alias ?? fallback;
|
|
4637
|
-
return fallback;
|
|
4638
|
-
};
|
|
4639
4921
|
var buildTypedSelection = (columns, prefix, keys, missingMsg, tableOverride) => {
|
|
4640
4922
|
return keys.reduce((acc, key) => {
|
|
4641
4923
|
const def = columns[key];
|
|
@@ -4762,7 +5044,7 @@ var RelationService = class {
|
|
|
4762
5044
|
table,
|
|
4763
5045
|
(state2, hydration2, columns) => this.selectColumns(state2, hydration2, columns)
|
|
4764
5046
|
);
|
|
4765
|
-
this.joinPlanner = new RelationJoinPlanner(table
|
|
5047
|
+
this.joinPlanner = new RelationJoinPlanner(table);
|
|
4766
5048
|
this.cteBuilder = new RelationCteBuilder(table, createQueryAstService);
|
|
4767
5049
|
}
|
|
4768
5050
|
projectionHelper;
|
|
@@ -4812,7 +5094,7 @@ var RelationService = class {
|
|
|
4812
5094
|
let hydration = this.hydration;
|
|
4813
5095
|
const relation = this.getRelation(relationName);
|
|
4814
5096
|
const aliasPrefix = options?.aliasPrefix ?? relationName;
|
|
4815
|
-
const alreadyJoined = state.ast.joins
|
|
5097
|
+
const alreadyJoined = hasJoinForRelationKey(state.ast.joins, relationName);
|
|
4816
5098
|
const { selfFilters, crossFilters } = splitFilterExpressions(
|
|
4817
5099
|
options?.filter,
|
|
4818
5100
|
/* @__PURE__ */ new Set([relation.target.name])
|
|
@@ -5130,6 +5412,34 @@ var cloneRelationIncludeTree = (tree) => {
|
|
|
5130
5412
|
}
|
|
5131
5413
|
return cloned;
|
|
5132
5414
|
};
|
|
5415
|
+
var getIncludeNode = (tree, segments) => {
|
|
5416
|
+
let current = tree;
|
|
5417
|
+
let node;
|
|
5418
|
+
for (let i = 0; i < segments.length; i += 1) {
|
|
5419
|
+
if (!current) return { exists: false };
|
|
5420
|
+
node = current[segments[i]];
|
|
5421
|
+
if (!node) return { exists: false };
|
|
5422
|
+
if (i < segments.length - 1) {
|
|
5423
|
+
current = node.include;
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
return { options: node?.options, exists: Boolean(node) };
|
|
5427
|
+
};
|
|
5428
|
+
var setIncludeOptions = (tree, segments, options) => {
|
|
5429
|
+
let current = tree;
|
|
5430
|
+
for (let i = 0; i < segments.length; i += 1) {
|
|
5431
|
+
const key = segments[i];
|
|
5432
|
+
const isLeaf = i === segments.length - 1;
|
|
5433
|
+
const existing = current[key] ?? {};
|
|
5434
|
+
if (isLeaf) {
|
|
5435
|
+
current[key] = { ...existing, options };
|
|
5436
|
+
return;
|
|
5437
|
+
}
|
|
5438
|
+
const nextInclude = existing.include ?? {};
|
|
5439
|
+
current[key] = { ...existing, include: nextInclude };
|
|
5440
|
+
current = nextInclude;
|
|
5441
|
+
}
|
|
5442
|
+
};
|
|
5133
5443
|
|
|
5134
5444
|
// src/orm/hydration.ts
|
|
5135
5445
|
var hydrateRows = (rows, plan) => {
|
|
@@ -7780,6 +8090,17 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7780
8090
|
getIncludeTree() {
|
|
7781
8091
|
return cloneRelationIncludeTree(this.includeTree);
|
|
7782
8092
|
}
|
|
8093
|
+
/**
|
|
8094
|
+
* Internal access to builder internals for advanced operations.
|
|
8095
|
+
* @internal
|
|
8096
|
+
*/
|
|
8097
|
+
getInternals() {
|
|
8098
|
+
return {
|
|
8099
|
+
context: this.context,
|
|
8100
|
+
includeTree: this.includeTree,
|
|
8101
|
+
clone: (context, includeTree) => this.clone(context, void 0, void 0, includeTree)
|
|
8102
|
+
};
|
|
8103
|
+
}
|
|
7783
8104
|
/**
|
|
7784
8105
|
* Gets the table definition for this query builder
|
|
7785
8106
|
* @returns Table definition
|
|
@@ -14772,6 +15093,87 @@ function createPooledExecutorFactory(opts) {
|
|
|
14772
15093
|
};
|
|
14773
15094
|
}
|
|
14774
15095
|
|
|
15096
|
+
// src/query-builder/relation-key.ts
|
|
15097
|
+
var buildRelationKey = (segments) => segments.join(RELATION_SEPARATOR);
|
|
15098
|
+
|
|
15099
|
+
// src/query-builder/update-include.ts
|
|
15100
|
+
var updateInclude = (qb, relationPath, updater) => {
|
|
15101
|
+
if (!relationPath || !relationPath.trim()) {
|
|
15102
|
+
return qb;
|
|
15103
|
+
}
|
|
15104
|
+
const segments = relationPath.split(".").filter(Boolean);
|
|
15105
|
+
if (segments.length === 0) {
|
|
15106
|
+
return qb;
|
|
15107
|
+
}
|
|
15108
|
+
const internals = qb.getInternals();
|
|
15109
|
+
const { context, includeTree } = internals;
|
|
15110
|
+
let state = context.state;
|
|
15111
|
+
const hydration = context.hydration;
|
|
15112
|
+
let currentTable = qb.getTable();
|
|
15113
|
+
let currentAlias = getExposedName(state.ast.from) ?? currentTable.name;
|
|
15114
|
+
const includeInfo = getIncludeNode(includeTree, segments);
|
|
15115
|
+
const existingOptions = includeInfo.options ?? {};
|
|
15116
|
+
const nextOptions = updater({ ...existingOptions });
|
|
15117
|
+
let nextIncludeTree = includeTree;
|
|
15118
|
+
const shouldCreateIncludePath = segments.length === 1 || includeInfo.exists;
|
|
15119
|
+
if (shouldCreateIncludePath) {
|
|
15120
|
+
nextIncludeTree = cloneRelationIncludeTree(includeTree);
|
|
15121
|
+
setIncludeOptions(nextIncludeTree, segments, nextOptions);
|
|
15122
|
+
}
|
|
15123
|
+
for (let i = 0; i < segments.length; i += 1) {
|
|
15124
|
+
const segment = segments[i];
|
|
15125
|
+
const relation = currentTable.relations[segment];
|
|
15126
|
+
if (!relation) {
|
|
15127
|
+
throw new Error(`Relation '${segment}' not found on table '${currentTable.name}'`);
|
|
15128
|
+
}
|
|
15129
|
+
const relationKey = buildRelationKey(segments.slice(0, i + 1));
|
|
15130
|
+
const joinIndex = findJoinIndexByRelationKey(state.ast.joins, relationKey);
|
|
15131
|
+
const isLeaf = i === segments.length - 1;
|
|
15132
|
+
if (isLeaf) {
|
|
15133
|
+
if (joinIndex === -1) {
|
|
15134
|
+
const joinKind = nextOptions.joinKind ?? JOIN_KINDS.LEFT;
|
|
15135
|
+
state = addRelationJoin({
|
|
15136
|
+
state,
|
|
15137
|
+
rootTable: currentTable,
|
|
15138
|
+
rootAlias: currentAlias,
|
|
15139
|
+
relationKey,
|
|
15140
|
+
relation,
|
|
15141
|
+
joinKind,
|
|
15142
|
+
filter: nextOptions.filter
|
|
15143
|
+
});
|
|
15144
|
+
} else {
|
|
15145
|
+
const joins = [...state.ast.joins];
|
|
15146
|
+
updateRelationJoin({
|
|
15147
|
+
joins,
|
|
15148
|
+
joinIndex,
|
|
15149
|
+
relation,
|
|
15150
|
+
currentTable,
|
|
15151
|
+
currentAlias,
|
|
15152
|
+
options: nextOptions
|
|
15153
|
+
});
|
|
15154
|
+
state = state.withJoins(joins);
|
|
15155
|
+
}
|
|
15156
|
+
} else if (joinIndex === -1) {
|
|
15157
|
+
const segmentOptions = getIncludeNode(includeTree, segments.slice(0, i + 1)).options;
|
|
15158
|
+
const joinKind = segmentOptions?.joinKind ?? JOIN_KINDS.LEFT;
|
|
15159
|
+
state = addRelationJoin({
|
|
15160
|
+
state,
|
|
15161
|
+
rootTable: currentTable,
|
|
15162
|
+
rootAlias: currentAlias,
|
|
15163
|
+
relationKey,
|
|
15164
|
+
relation,
|
|
15165
|
+
joinKind,
|
|
15166
|
+
filter: segmentOptions?.filter
|
|
15167
|
+
});
|
|
15168
|
+
}
|
|
15169
|
+
const joinForSegment = findJoinByRelationKey(state.ast.joins, relationKey);
|
|
15170
|
+
currentAlias = joinForSegment ? getExposedName(joinForSegment.table) ?? relation.target.name : relation.target.name;
|
|
15171
|
+
currentTable = relation.target;
|
|
15172
|
+
}
|
|
15173
|
+
const nextContext = { state, hydration };
|
|
15174
|
+
return internals.clone(nextContext, nextIncludeTree);
|
|
15175
|
+
};
|
|
15176
|
+
|
|
14775
15177
|
// src/dto/apply-filter.ts
|
|
14776
15178
|
function buildFieldExpression(column, filter) {
|
|
14777
15179
|
const expressions = [];
|
|
@@ -14849,7 +15251,7 @@ function caseInsensitiveLike(column, pattern) {
|
|
|
14849
15251
|
right: { type: "Literal", value: pattern.toLowerCase() }
|
|
14850
15252
|
};
|
|
14851
15253
|
}
|
|
14852
|
-
function applyFilter(qb, tableOrEntity, where) {
|
|
15254
|
+
function applyFilter(qb, tableOrEntity, where, _options) {
|
|
14853
15255
|
if (!where) {
|
|
14854
15256
|
return qb;
|
|
14855
15257
|
}
|
|
@@ -14863,12 +15265,15 @@ function applyFilter(qb, tableOrEntity, where) {
|
|
|
14863
15265
|
continue;
|
|
14864
15266
|
}
|
|
14865
15267
|
const column = table.columns[fieldName];
|
|
14866
|
-
if (
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
15268
|
+
if (column) {
|
|
15269
|
+
const expr = buildFieldExpression(column, fieldFilter);
|
|
15270
|
+
if (expr) {
|
|
15271
|
+
expressions.push(expr);
|
|
15272
|
+
}
|
|
15273
|
+
} else if (table.relations && fieldName in table.relations) {
|
|
15274
|
+
const relationFilter = fieldFilter;
|
|
15275
|
+
const relationName = fieldName;
|
|
15276
|
+
qb = applyRelationFilter(qb, table, relationName, relationFilter);
|
|
14872
15277
|
}
|
|
14873
15278
|
}
|
|
14874
15279
|
if (expressions.length === 0) {
|
|
@@ -14879,6 +15284,199 @@ function applyFilter(qb, tableOrEntity, where) {
|
|
|
14879
15284
|
}
|
|
14880
15285
|
return qb.where(and(...expressions));
|
|
14881
15286
|
}
|
|
15287
|
+
function applyRelationFilter(qb, table, relationName, filter) {
|
|
15288
|
+
const relation = table.relations[relationName];
|
|
15289
|
+
if (!relation) {
|
|
15290
|
+
return qb;
|
|
15291
|
+
}
|
|
15292
|
+
if (filter.some) {
|
|
15293
|
+
const predicate = buildFilterExpression(relation.target, filter.some);
|
|
15294
|
+
if (predicate) {
|
|
15295
|
+
qb = updateInclude(qb, relationName, (opts) => ({
|
|
15296
|
+
...opts,
|
|
15297
|
+
joinKind: JOIN_KINDS.INNER,
|
|
15298
|
+
filter: opts.filter ? and(opts.filter, predicate) : predicate
|
|
15299
|
+
}));
|
|
15300
|
+
const pk = findPrimaryKey(table);
|
|
15301
|
+
const pkColumn = table.columns[pk];
|
|
15302
|
+
qb = pkColumn ? qb.distinct(pkColumn) : qb.distinct({ type: "Column", table: table.name, name: pk });
|
|
15303
|
+
}
|
|
15304
|
+
}
|
|
15305
|
+
if (filter.none) {
|
|
15306
|
+
const predicate = buildFilterExpression(relation.target, filter.none);
|
|
15307
|
+
if (predicate) {
|
|
15308
|
+
qb = qb.whereHasNot(relationName, (subQb) => subQb.where(predicate));
|
|
15309
|
+
}
|
|
15310
|
+
}
|
|
15311
|
+
if (filter.every) {
|
|
15312
|
+
const predicate = buildFilterExpression(relation.target, filter.every);
|
|
15313
|
+
if (predicate) {
|
|
15314
|
+
const pk = findPrimaryKey(table);
|
|
15315
|
+
qb = qb.joinRelation(relationName);
|
|
15316
|
+
qb = qb.groupBy({ type: "Column", table: table.name, name: pk });
|
|
15317
|
+
qb = qb.having(buildEveryHavingClause(relationName, predicate, relation.target));
|
|
15318
|
+
}
|
|
15319
|
+
}
|
|
15320
|
+
if (filter.isEmpty !== void 0) {
|
|
15321
|
+
if (filter.isEmpty) {
|
|
15322
|
+
qb = qb.whereHasNot(relationName);
|
|
15323
|
+
} else {
|
|
15324
|
+
qb = qb.whereHas(relationName);
|
|
15325
|
+
}
|
|
15326
|
+
}
|
|
15327
|
+
if (filter.isNotEmpty !== void 0) {
|
|
15328
|
+
if (filter.isNotEmpty) {
|
|
15329
|
+
qb = qb.whereHas(relationName);
|
|
15330
|
+
} else {
|
|
15331
|
+
qb = qb.whereHasNot(relationName);
|
|
15332
|
+
}
|
|
15333
|
+
}
|
|
15334
|
+
return qb;
|
|
15335
|
+
}
|
|
15336
|
+
var buildRelationSubqueryBase = (table, relation) => {
|
|
15337
|
+
const target = relation.target;
|
|
15338
|
+
if (relation.type === RelationKinds.BelongsToMany) {
|
|
15339
|
+
const many = relation;
|
|
15340
|
+
const localKey = many.localKey || findPrimaryKey(table);
|
|
15341
|
+
const targetKey = many.targetKey || findPrimaryKey(target);
|
|
15342
|
+
const pivot = many.pivotTable;
|
|
15343
|
+
const from2 = {
|
|
15344
|
+
type: "Table",
|
|
15345
|
+
name: pivot.name,
|
|
15346
|
+
schema: pivot.schema
|
|
15347
|
+
};
|
|
15348
|
+
const joins = [
|
|
15349
|
+
createJoinNode(
|
|
15350
|
+
JOIN_KINDS.INNER,
|
|
15351
|
+
{ type: "Table", name: target.name, schema: target.schema },
|
|
15352
|
+
eq(
|
|
15353
|
+
{ type: "Column", table: target.name, name: targetKey },
|
|
15354
|
+
{ type: "Column", table: pivot.name, name: many.pivotForeignKeyToTarget }
|
|
15355
|
+
)
|
|
15356
|
+
)
|
|
15357
|
+
];
|
|
15358
|
+
const correlation2 = eq(
|
|
15359
|
+
{ type: "Column", table: pivot.name, name: many.pivotForeignKeyToRoot },
|
|
15360
|
+
{ type: "Column", table: table.name, name: localKey }
|
|
15361
|
+
);
|
|
15362
|
+
const groupByColumn = {
|
|
15363
|
+
type: "Column",
|
|
15364
|
+
table: pivot.name,
|
|
15365
|
+
name: many.pivotForeignKeyToRoot
|
|
15366
|
+
};
|
|
15367
|
+
return {
|
|
15368
|
+
from: from2,
|
|
15369
|
+
joins,
|
|
15370
|
+
correlation: correlation2,
|
|
15371
|
+
groupByColumn,
|
|
15372
|
+
targetTable: target,
|
|
15373
|
+
targetTableName: target.name
|
|
15374
|
+
};
|
|
15375
|
+
}
|
|
15376
|
+
const from = {
|
|
15377
|
+
type: "Table",
|
|
15378
|
+
name: target.name,
|
|
15379
|
+
schema: target.schema
|
|
15380
|
+
};
|
|
15381
|
+
const correlation = buildRelationCorrelation(table, relation);
|
|
15382
|
+
const groupByColumnName = relation.type === RelationKinds.BelongsTo ? relation.localKey || findPrimaryKey(target) : relation.foreignKey;
|
|
15383
|
+
return {
|
|
15384
|
+
from,
|
|
15385
|
+
joins: [],
|
|
15386
|
+
correlation,
|
|
15387
|
+
groupByColumn: { type: "Column", table: target.name, name: groupByColumnName },
|
|
15388
|
+
targetTable: target,
|
|
15389
|
+
targetTableName: target.name
|
|
15390
|
+
};
|
|
15391
|
+
};
|
|
15392
|
+
function buildRelationFilterExpression(table, relationName, filter) {
|
|
15393
|
+
const relation = table.relations[relationName];
|
|
15394
|
+
if (!relation) {
|
|
15395
|
+
return null;
|
|
15396
|
+
}
|
|
15397
|
+
const expressions = [];
|
|
15398
|
+
const subqueryBase = buildRelationSubqueryBase(table, relation);
|
|
15399
|
+
const buildSubquery = (predicate) => {
|
|
15400
|
+
const where = predicate ? and(subqueryBase.correlation, predicate) : subqueryBase.correlation;
|
|
15401
|
+
return {
|
|
15402
|
+
type: "SelectQuery",
|
|
15403
|
+
from: subqueryBase.from,
|
|
15404
|
+
columns: [],
|
|
15405
|
+
joins: subqueryBase.joins,
|
|
15406
|
+
where
|
|
15407
|
+
};
|
|
15408
|
+
};
|
|
15409
|
+
if (filter.some) {
|
|
15410
|
+
const predicate = buildFilterExpression(relation.target, filter.some);
|
|
15411
|
+
if (predicate) {
|
|
15412
|
+
expressions.push(exists(buildSubquery(predicate)));
|
|
15413
|
+
}
|
|
15414
|
+
}
|
|
15415
|
+
if (filter.none) {
|
|
15416
|
+
const predicate = buildFilterExpression(relation.target, filter.none);
|
|
15417
|
+
if (predicate) {
|
|
15418
|
+
expressions.push(notExists(buildSubquery(predicate)));
|
|
15419
|
+
}
|
|
15420
|
+
}
|
|
15421
|
+
if (filter.every) {
|
|
15422
|
+
const predicate = buildFilterExpression(relation.target, filter.every);
|
|
15423
|
+
if (predicate) {
|
|
15424
|
+
const subquery = {
|
|
15425
|
+
type: "SelectQuery",
|
|
15426
|
+
from: subqueryBase.from,
|
|
15427
|
+
columns: [],
|
|
15428
|
+
joins: subqueryBase.joins,
|
|
15429
|
+
where: subqueryBase.correlation,
|
|
15430
|
+
groupBy: [subqueryBase.groupByColumn],
|
|
15431
|
+
having: buildEveryHavingClause(subqueryBase.targetTableName, predicate, subqueryBase.targetTable)
|
|
15432
|
+
};
|
|
15433
|
+
expressions.push(exists(subquery));
|
|
15434
|
+
}
|
|
15435
|
+
}
|
|
15436
|
+
if (filter.isEmpty !== void 0) {
|
|
15437
|
+
const subquery = buildSubquery();
|
|
15438
|
+
expressions.push(filter.isEmpty ? notExists(subquery) : exists(subquery));
|
|
15439
|
+
}
|
|
15440
|
+
if (filter.isNotEmpty !== void 0) {
|
|
15441
|
+
const subquery = buildSubquery();
|
|
15442
|
+
expressions.push(filter.isNotEmpty ? exists(subquery) : notExists(subquery));
|
|
15443
|
+
}
|
|
15444
|
+
if (expressions.length === 0) {
|
|
15445
|
+
return null;
|
|
15446
|
+
}
|
|
15447
|
+
if (expressions.length === 1) {
|
|
15448
|
+
return expressions[0];
|
|
15449
|
+
}
|
|
15450
|
+
return and(...expressions);
|
|
15451
|
+
}
|
|
15452
|
+
function buildEveryHavingClause(relationName, predicate, targetTable) {
|
|
15453
|
+
const pk = findPrimaryKey(targetTable);
|
|
15454
|
+
const whenClause = {
|
|
15455
|
+
when: predicate,
|
|
15456
|
+
then: { type: "Literal", value: 1 }
|
|
15457
|
+
};
|
|
15458
|
+
const caseExpr = {
|
|
15459
|
+
type: "CaseExpression",
|
|
15460
|
+
conditions: [whenClause],
|
|
15461
|
+
else: { type: "Literal", value: null }
|
|
15462
|
+
};
|
|
15463
|
+
const countMatching = {
|
|
15464
|
+
type: "Function",
|
|
15465
|
+
name: "COUNT",
|
|
15466
|
+
args: [caseExpr]
|
|
15467
|
+
};
|
|
15468
|
+
const countAll2 = {
|
|
15469
|
+
type: "Function",
|
|
15470
|
+
name: "COUNT",
|
|
15471
|
+
args: [{ type: "Column", table: relationName, name: pk }]
|
|
15472
|
+
};
|
|
15473
|
+
return {
|
|
15474
|
+
type: "BinaryExpression",
|
|
15475
|
+
operator: "=",
|
|
15476
|
+
left: countAll2,
|
|
15477
|
+
right: countMatching
|
|
15478
|
+
};
|
|
15479
|
+
}
|
|
14882
15480
|
function isEntityConstructor(value) {
|
|
14883
15481
|
return typeof value === "function" && value.prototype?.constructor === value;
|
|
14884
15482
|
}
|
|
@@ -14896,12 +15494,20 @@ function buildFilterExpression(tableOrEntity, where) {
|
|
|
14896
15494
|
continue;
|
|
14897
15495
|
}
|
|
14898
15496
|
const column = table.columns[fieldName];
|
|
14899
|
-
if (
|
|
14900
|
-
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
|
|
14904
|
-
|
|
15497
|
+
if (column) {
|
|
15498
|
+
const expr = buildFieldExpression(column, fieldFilter);
|
|
15499
|
+
if (expr) {
|
|
15500
|
+
expressions.push(expr);
|
|
15501
|
+
}
|
|
15502
|
+
} else if (table.relations && fieldName in table.relations) {
|
|
15503
|
+
const relationExpr = buildRelationFilterExpression(
|
|
15504
|
+
table,
|
|
15505
|
+
fieldName,
|
|
15506
|
+
fieldFilter
|
|
15507
|
+
);
|
|
15508
|
+
if (relationExpr) {
|
|
15509
|
+
expressions.push(relationExpr);
|
|
15510
|
+
}
|
|
14905
15511
|
}
|
|
14906
15512
|
}
|
|
14907
15513
|
if (expressions.length === 0) {
|