rawsql-ts 0.27.0 → 0.29.0
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/esm/index.min.js +1 -1
- package/dist/esm/index.min.js.map +4 -4
- package/dist/esm/transformers/ConditionDeduplicationOptimizer.d.ts +22 -0
- package/dist/esm/transformers/ConditionDeduplicationOptimizer.js +198 -0
- package/dist/esm/transformers/ConditionDeduplicationOptimizer.js.map +1 -0
- package/dist/esm/transformers/ConditionOptimization.d.ts +35 -1
- package/dist/esm/transformers/ConditionOptimization.js +359 -24
- package/dist/esm/transformers/ConditionOptimization.js.map +1 -1
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.d.ts +13 -0
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js +246 -27
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js.map +1 -1
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.d.ts +12 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js +211 -18
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js.map +1 -1
- package/dist/esm/transformers/TopLevelAndConditionDeduper.d.ts +10 -0
- package/dist/esm/transformers/TopLevelAndConditionDeduper.js +66 -0
- package/dist/esm/transformers/TopLevelAndConditionDeduper.js.map +1 -0
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/transformers/ConditionDeduplicationOptimizer.d.ts +22 -0
- package/dist/src/transformers/ConditionOptimization.d.ts +35 -1
- package/dist/src/transformers/ParameterConditionPlacementOptimizer.d.ts +13 -0
- package/dist/src/transformers/StaticPredicatePlacementOptimizer.d.ts +12 -0
- package/dist/src/transformers/TopLevelAndConditionDeduper.d.ts +10 -0
- package/dist/transformers/ConditionDeduplicationOptimizer.js +202 -0
- package/dist/transformers/ConditionDeduplicationOptimizer.js.map +1 -0
- package/dist/transformers/ConditionOptimization.js +362 -22
- package/dist/transformers/ConditionOptimization.js.map +1 -1
- package/dist/transformers/ParameterConditionPlacementOptimizer.js +245 -26
- package/dist/transformers/ParameterConditionPlacementOptimizer.js.map +1 -1
- package/dist/transformers/StaticPredicatePlacementOptimizer.js +210 -17
- package/dist/transformers/StaticPredicatePlacementOptimizer.js.map +1 -1
- package/dist/transformers/TopLevelAndConditionDeduper.js +72 -0
- package/dist/transformers/TopLevelAndConditionDeduper.js.map +1 -0
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -8,9 +8,18 @@ const SelectQueryParser_1 = require("../parsers/SelectQueryParser");
|
|
|
8
8
|
const ValueParser_1 = require("../parsers/ValueParser");
|
|
9
9
|
const SqlComponentFormatter_1 = require("./SqlComponentFormatter");
|
|
10
10
|
const SelectOutputCollector_1 = require("./SelectOutputCollector");
|
|
11
|
+
const TopLevelAndConditionDeduper_1 = require("./TopLevelAndConditionDeduper");
|
|
11
12
|
const SUPPORTED_OPERATORS = new Set(["=", "<>", "!=", "<", "<=", ">", ">=", "like", "ilike", "in"]);
|
|
12
13
|
const VOLATILE_OR_UNSUPPORTED_FUNCTION_REASON = "Condition contains a function call; volatile and expression predicates are not moved in the safe-only implementation.";
|
|
13
14
|
const normalizeIdentifier = (value) => value.trim().toLowerCase();
|
|
15
|
+
const isCaseSensitiveIdentifier = (value) => /[A-Z]/.test(value.trim());
|
|
16
|
+
const identifiersEqual = (left, right) => {
|
|
17
|
+
const trimmedLeft = left.trim();
|
|
18
|
+
const trimmedRight = right.trim();
|
|
19
|
+
return isCaseSensitiveIdentifier(trimmedLeft) || isCaseSensitiveIdentifier(trimmedRight)
|
|
20
|
+
? trimmedLeft === trimmedRight
|
|
21
|
+
: trimmedLeft.toLowerCase() === trimmedRight.toLowerCase();
|
|
22
|
+
};
|
|
14
23
|
const unwrapParens = (expression) => {
|
|
15
24
|
let candidate = expression;
|
|
16
25
|
while (candidate instanceof ValueComponent_1.ParenExpression) {
|
|
@@ -62,8 +71,8 @@ const columnReferenceText = (reference) => {
|
|
|
62
71
|
return namespace ? `${namespace}.${reference.column.name}` : reference.column.name;
|
|
63
72
|
};
|
|
64
73
|
const sameColumnReference = (left, right) => {
|
|
65
|
-
return
|
|
66
|
-
&&
|
|
74
|
+
return identifiersEqual(left.column.name, right.column.name)
|
|
75
|
+
&& identifiersEqual(left.getNamespace(), right.getNamespace());
|
|
67
76
|
};
|
|
68
77
|
const appendUnique = (items, value) => {
|
|
69
78
|
if (!items.includes(value)) {
|
|
@@ -123,7 +132,11 @@ class ParameterConditionPlacementOptimizer {
|
|
|
123
132
|
continue;
|
|
124
133
|
}
|
|
125
134
|
let appliedReason = "";
|
|
126
|
-
if (target.kind === "
|
|
135
|
+
if (target.kind === "join_on") {
|
|
136
|
+
this.appendJoinOnCondition(target.join, candidate.expression, options);
|
|
137
|
+
appliedReason = target.reason;
|
|
138
|
+
}
|
|
139
|
+
else if (target.kind === "simple") {
|
|
127
140
|
const targetColumns = this.resolveTargetColumns(query, target.query, candidate.references);
|
|
128
141
|
if ("code" in targetColumns) {
|
|
129
142
|
skipped.push(this.makeSkipped(term, targetColumns, options));
|
|
@@ -136,6 +149,7 @@ class ParameterConditionPlacementOptimizer {
|
|
|
136
149
|
}
|
|
137
150
|
const movedCondition = this.rebaseCondition(candidate.expression, targetColumns, options);
|
|
138
151
|
target.query.appendWhere(movedCondition);
|
|
152
|
+
(0, TopLevelAndConditionDeduper_1.dedupeWhereTopLevelAndConditions)(target.query, options);
|
|
139
153
|
appliedReason = placement.reason;
|
|
140
154
|
}
|
|
141
155
|
else {
|
|
@@ -156,6 +170,7 @@ class ParameterConditionPlacementOptimizer {
|
|
|
156
170
|
for (const branch of target.branches) {
|
|
157
171
|
const movedCondition = this.rebaseCondition(candidate.expression, branch.targetColumns, options);
|
|
158
172
|
branch.query.appendWhere(movedCondition);
|
|
173
|
+
(0, TopLevelAndConditionDeduper_1.dedupeWhereTopLevelAndConditions)(branch.query, options);
|
|
159
174
|
}
|
|
160
175
|
appliedReason = placements.some(item => /group by/i.test(item.reason))
|
|
161
176
|
? "Condition is distributed to every UNION branch by output column position; grouped branches only receive GROUP BY-key predicates."
|
|
@@ -354,9 +369,15 @@ class ParameterConditionPlacementOptimizer {
|
|
|
354
369
|
}
|
|
355
370
|
return null;
|
|
356
371
|
}
|
|
372
|
+
if (candidate instanceof ValueComponent_1.UnaryExpression) {
|
|
373
|
+
const operator = candidate.operator.value.trim().toLowerCase();
|
|
374
|
+
if (operator === "not") {
|
|
375
|
+
return visit(candidate.expression);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
357
378
|
return {
|
|
358
379
|
code: "UNSUPPORTED_PARAMETER_CONDITION",
|
|
359
|
-
reason: "Only simple binary, BETWEEN, and whole OR/AND parameter predicates are moved in the safe-only implementation."
|
|
380
|
+
reason: "Only simple binary, BETWEEN, and whole OR/AND/NOT parameter predicates are moved in the safe-only implementation."
|
|
360
381
|
};
|
|
361
382
|
};
|
|
362
383
|
return visit(expression);
|
|
@@ -377,6 +398,7 @@ class ParameterConditionPlacementOptimizer {
|
|
|
377
398
|
&& right.values.every(value => unwrapParens(value) instanceof ValueComponent_1.ParameterExpression);
|
|
378
399
|
}
|
|
379
400
|
resolveTarget(root, candidate) {
|
|
401
|
+
var _a;
|
|
380
402
|
const boundary = this.findRootQueryBoundary(root);
|
|
381
403
|
if (boundary) {
|
|
382
404
|
return boundary;
|
|
@@ -404,12 +426,25 @@ class ParameterConditionPlacementOptimizer {
|
|
|
404
426
|
};
|
|
405
427
|
}
|
|
406
428
|
const binding = bindings[0];
|
|
429
|
+
if ((_a = binding.join) === null || _a === void 0 ? void 0 : _a.lateral) {
|
|
430
|
+
return {
|
|
431
|
+
code: "LATERAL_JOIN_BOUNDARY",
|
|
432
|
+
reason: "Condition crosses a LATERAL JOIN boundary; moving it may change semantics."
|
|
433
|
+
};
|
|
434
|
+
}
|
|
407
435
|
const nullableSide = this.findNullableSideBoundary(root.fromClause, binding);
|
|
408
436
|
if (nullableSide) {
|
|
409
437
|
return nullableSide;
|
|
410
438
|
}
|
|
411
439
|
const upstream = this.resolveUpstreamQuery(root, binding, candidate.references);
|
|
412
440
|
if ("code" in upstream) {
|
|
441
|
+
if (upstream.code === "NO_SAFE_UPSTREAM_QUERY") {
|
|
442
|
+
const joinOnTarget = this.resolveBaseTableJoinOnTarget(root, binding);
|
|
443
|
+
if (!("code" in joinOnTarget)) {
|
|
444
|
+
return joinOnTarget;
|
|
445
|
+
}
|
|
446
|
+
return joinOnTarget;
|
|
447
|
+
}
|
|
413
448
|
return upstream;
|
|
414
449
|
}
|
|
415
450
|
return upstream;
|
|
@@ -491,10 +526,10 @@ class ParameterConditionPlacementOptimizer {
|
|
|
491
526
|
};
|
|
492
527
|
}
|
|
493
528
|
const bindings = this.getSourceBindings(fromClause);
|
|
494
|
-
const namespace =
|
|
529
|
+
const namespace = column.getNamespace();
|
|
495
530
|
const columnName = column.column.name;
|
|
496
531
|
if (namespace) {
|
|
497
|
-
const matches = bindings.filter(binding =>
|
|
532
|
+
const matches = bindings.filter(binding => identifiersEqual(binding.alias, namespace));
|
|
498
533
|
if (matches.length !== 1) {
|
|
499
534
|
return {
|
|
500
535
|
code: "AMBIGUOUS_COLUMN_SOURCE",
|
|
@@ -502,6 +537,9 @@ class ParameterConditionPlacementOptimizer {
|
|
|
502
537
|
};
|
|
503
538
|
}
|
|
504
539
|
const matchCount = this.getOutputColumnMatchCount(contextRoot, matches[0], columnName);
|
|
540
|
+
if (matchCount === 0 && this.isBaseTableBinding(contextRoot, matches[0])) {
|
|
541
|
+
return matches[0];
|
|
542
|
+
}
|
|
505
543
|
if (matchCount === 0) {
|
|
506
544
|
return {
|
|
507
545
|
code: "COLUMN_NOT_AVAILABLE_UPSTREAM",
|
|
@@ -630,7 +668,7 @@ class ParameterConditionPlacementOptimizer {
|
|
|
630
668
|
resolveTargetColumns(root, query, references) {
|
|
631
669
|
const resolved = [];
|
|
632
670
|
for (const reference of references) {
|
|
633
|
-
const matches = this.
|
|
671
|
+
const matches = this.collectDirectOutputMatches(root, query, reference.column.name);
|
|
634
672
|
if (matches.length !== 1) {
|
|
635
673
|
return {
|
|
636
674
|
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
@@ -657,6 +695,57 @@ class ParameterConditionPlacementOptimizer {
|
|
|
657
695
|
}
|
|
658
696
|
return resolved;
|
|
659
697
|
}
|
|
698
|
+
resolveBaseTableJoinOnTarget(root, binding) {
|
|
699
|
+
var _a, _b, _c;
|
|
700
|
+
if (!root.fromClause || !this.isBaseTableBinding(root, binding)) {
|
|
701
|
+
return {
|
|
702
|
+
code: "NO_SAFE_UPSTREAM_QUERY",
|
|
703
|
+
reason: "The referenced source is a base table, so there is no upstream query block to move into."
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
if (this.hasOuterJoin(root.fromClause)) {
|
|
707
|
+
return {
|
|
708
|
+
code: "OUTER_JOIN_BOUNDARY",
|
|
709
|
+
reason: "Condition crosses an OUTER JOIN boundary; moving it into JOIN ON may change semantics."
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
const join = binding.isPrimary
|
|
713
|
+
? (_b = (_a = root.fromClause.joins) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null
|
|
714
|
+
: binding.join;
|
|
715
|
+
if (join === null || join === void 0 ? void 0 : join.lateral) {
|
|
716
|
+
return {
|
|
717
|
+
code: "LATERAL_JOIN_BOUNDARY",
|
|
718
|
+
reason: "Condition crosses a LATERAL JOIN boundary; moving it into JOIN ON may change semantics."
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
if (!join || !this.isInnerJoin(join)) {
|
|
722
|
+
return {
|
|
723
|
+
code: "NO_SAFE_JOIN_ON_TARGET",
|
|
724
|
+
reason: "Base-table conditions are moved only into INNER JOIN ON clauses in the safe-only implementation."
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
if (!(join.condition instanceof Clause_1.JoinOnClause)) {
|
|
728
|
+
return {
|
|
729
|
+
code: "NO_SAFE_JOIN_ON_TARGET",
|
|
730
|
+
reason: "Base-table conditions are moved only into existing JOIN ON clauses, not USING or conditionless joins."
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
return {
|
|
734
|
+
kind: "join_on",
|
|
735
|
+
join,
|
|
736
|
+
scopeId: `join_on:${(_c = join.source.getAliasName()) !== null && _c !== void 0 ? _c : "unknown"}`,
|
|
737
|
+
reason: binding.isPrimary
|
|
738
|
+
? "Condition references the primary source of an INNER JOIN; it is moved into the first JOIN ON clause."
|
|
739
|
+
: "Condition references the joined source of an INNER JOIN; it is moved into that JOIN ON clause."
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
appendJoinOnCondition(join, expression, options) {
|
|
743
|
+
if (!(join.condition instanceof Clause_1.JoinOnClause)) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
join.condition.condition = new ValueComponent_1.BinaryExpression(join.condition.condition, "and", cloneValueComponent(expression, options));
|
|
747
|
+
join.condition.condition = (0, TopLevelAndConditionDeduper_1.dedupeTopLevelAndConditions)(join.condition.condition, options);
|
|
748
|
+
}
|
|
660
749
|
resolveTargetColumnByOutputIndex(root, query, outputIndex, sourceColumn) {
|
|
661
750
|
const output = this.collectSelectOutputs(root, query)[outputIndex];
|
|
662
751
|
if (!output) {
|
|
@@ -665,6 +754,15 @@ class ParameterConditionPlacementOptimizer {
|
|
|
665
754
|
reason: `UNION branch does not expose column '${sourceColumn.column.name}' at output position ${outputIndex + 1}.`
|
|
666
755
|
};
|
|
667
756
|
}
|
|
757
|
+
else if (identifiersEqual(output.name, sourceColumn.column.name)) {
|
|
758
|
+
const matches = this.collectDirectOutputMatches(root, query, sourceColumn.column.name);
|
|
759
|
+
if (matches.length > 1) {
|
|
760
|
+
return {
|
|
761
|
+
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
762
|
+
reason: `UNION branch exposes multiple '${sourceColumn.column.name}' columns.`
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
}
|
|
668
766
|
if (!(output.value instanceof ValueComponent_1.ColumnReference)) {
|
|
669
767
|
return {
|
|
670
768
|
code: "EXPRESSION_OUTPUT_UNSUPPORTED",
|
|
@@ -707,7 +805,10 @@ class ParameterConditionPlacementOptimizer {
|
|
|
707
805
|
return { query, targetColumns: [...targetColumns] };
|
|
708
806
|
}
|
|
709
807
|
const upstream = this.resolveUpstreamQuery(contextRoot, binding, targetColumns.map(item => item.targetColumn));
|
|
710
|
-
if ("code" in upstream
|
|
808
|
+
if ("code" in upstream) {
|
|
809
|
+
return { query, targetColumns: [...targetColumns] };
|
|
810
|
+
}
|
|
811
|
+
if (upstream.kind !== "simple") {
|
|
711
812
|
return { query, targetColumns: [...targetColumns] };
|
|
712
813
|
}
|
|
713
814
|
const upstreamColumns = this.resolveTargetColumns(contextRoot, upstream.query, targetColumns.map(item => item.targetColumn));
|
|
@@ -732,9 +833,9 @@ class ParameterConditionPlacementOptimizer {
|
|
|
732
833
|
};
|
|
733
834
|
}
|
|
734
835
|
const bindings = this.getSourceBindings(query.fromClause);
|
|
735
|
-
const namespace =
|
|
836
|
+
const namespace = column.getNamespace();
|
|
736
837
|
if (namespace) {
|
|
737
|
-
const matches = bindings.filter(binding =>
|
|
838
|
+
const matches = bindings.filter(binding => identifiersEqual(binding.alias, namespace));
|
|
738
839
|
return matches.length === 1
|
|
739
840
|
? null
|
|
740
841
|
: {
|
|
@@ -765,7 +866,7 @@ class ParameterConditionPlacementOptimizer {
|
|
|
765
866
|
if (sameColumnReference(left, right)) {
|
|
766
867
|
return true;
|
|
767
868
|
}
|
|
768
|
-
if (
|
|
869
|
+
if (!identifiersEqual(left.column.name, right.column.name)) {
|
|
769
870
|
return false;
|
|
770
871
|
}
|
|
771
872
|
const leftSource = this.resolveColumnSourceAlias(query, left);
|
|
@@ -777,12 +878,12 @@ class ParameterConditionPlacementOptimizer {
|
|
|
777
878
|
return null;
|
|
778
879
|
}
|
|
779
880
|
const bindings = this.getSourceBindings(query.fromClause);
|
|
780
|
-
const namespace =
|
|
881
|
+
const namespace = column.getNamespace();
|
|
781
882
|
if (namespace) {
|
|
782
|
-
const matches = bindings.filter(binding =>
|
|
783
|
-
return matches.length === 1 ?
|
|
883
|
+
const matches = bindings.filter(binding => identifiersEqual(binding.alias, namespace));
|
|
884
|
+
return matches.length === 1 ? matches[0].alias : null;
|
|
784
885
|
}
|
|
785
|
-
return bindings.length === 1 ?
|
|
886
|
+
return bindings.length === 1 ? bindings[0].alias : null;
|
|
786
887
|
}
|
|
787
888
|
rebaseCondition(expression, targetColumns, options) {
|
|
788
889
|
const cloned = cloneValueComponent(expression, options);
|
|
@@ -800,33 +901,45 @@ class ParameterConditionPlacementOptimizer {
|
|
|
800
901
|
source: fromClause.source,
|
|
801
902
|
alias: (_a = fromClause.source.getAliasName()) !== null && _a !== void 0 ? _a : "",
|
|
802
903
|
join: null,
|
|
904
|
+
joinIndex: -1,
|
|
803
905
|
isPrimary: true
|
|
804
906
|
}];
|
|
805
|
-
for (
|
|
907
|
+
for (let index = 0; index < ((_b = fromClause.joins) !== null && _b !== void 0 ? _b : []).length; index += 1) {
|
|
908
|
+
const join = fromClause.joins[index];
|
|
806
909
|
bindings.push({
|
|
807
910
|
source: join.source,
|
|
808
911
|
alias: (_c = join.source.getAliasName()) !== null && _c !== void 0 ? _c : "",
|
|
809
912
|
join,
|
|
913
|
+
joinIndex: index,
|
|
810
914
|
isPrimary: false
|
|
811
915
|
});
|
|
812
916
|
}
|
|
813
917
|
return bindings;
|
|
814
918
|
}
|
|
919
|
+
isBaseTableBinding(root, binding) {
|
|
920
|
+
const source = binding.source.datasource;
|
|
921
|
+
return source instanceof Clause_1.TableSource && !this.findCte(root, source.table.name);
|
|
922
|
+
}
|
|
923
|
+
isInnerJoin(join) {
|
|
924
|
+
const joinType = join.joinType.value.trim().toLowerCase();
|
|
925
|
+
return joinType === "join" || joinType === "inner join";
|
|
926
|
+
}
|
|
815
927
|
findNullableSideBoundary(fromClause, binding) {
|
|
816
928
|
var _a;
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
return joinType.includes("right") || joinType.includes("full");
|
|
821
|
-
});
|
|
822
|
-
return rightOrFull
|
|
929
|
+
const joins = (_a = fromClause.joins) !== null && _a !== void 0 ? _a : [];
|
|
930
|
+
if (binding.isPrimary) {
|
|
931
|
+
return this.hasLaterJoinThatNullsPriorSources(joins, -1)
|
|
823
932
|
? {
|
|
824
933
|
code: "OUTER_JOIN_NULLABLE_SIDE",
|
|
825
934
|
reason: "Condition crosses OUTER JOIN nullable side; moving it may change semantics."
|
|
826
935
|
}
|
|
827
936
|
: null;
|
|
828
937
|
}
|
|
829
|
-
const
|
|
938
|
+
const join = binding.join;
|
|
939
|
+
if (!join) {
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
const joinType = join.joinType.value.toLowerCase();
|
|
830
943
|
if (joinType.includes("left")) {
|
|
831
944
|
return {
|
|
832
945
|
code: "OUTER_JOIN_NULLABLE_SIDE",
|
|
@@ -839,8 +952,22 @@ class ParameterConditionPlacementOptimizer {
|
|
|
839
952
|
reason: "Condition crosses FULL JOIN nullable side; moving it may change semantics."
|
|
840
953
|
};
|
|
841
954
|
}
|
|
955
|
+
if (this.hasLaterJoinThatNullsPriorSources(joins, binding.joinIndex)) {
|
|
956
|
+
return {
|
|
957
|
+
code: "OUTER_JOIN_NULLABLE_SIDE",
|
|
958
|
+
reason: "Condition crosses later RIGHT/FULL JOIN nullable side; moving it may change semantics."
|
|
959
|
+
};
|
|
960
|
+
}
|
|
842
961
|
return null;
|
|
843
962
|
}
|
|
963
|
+
hasLaterJoinThatNullsPriorSources(joins, sourceJoinIndex) {
|
|
964
|
+
return joins
|
|
965
|
+
.slice(sourceJoinIndex + 1)
|
|
966
|
+
.some(join => {
|
|
967
|
+
const joinType = join.joinType.value.toLowerCase();
|
|
968
|
+
return joinType.includes("right") || joinType.includes("full");
|
|
969
|
+
});
|
|
970
|
+
}
|
|
844
971
|
hasOuterJoin(fromClause) {
|
|
845
972
|
var _a;
|
|
846
973
|
return ((_a = fromClause.joins) !== null && _a !== void 0 ? _a : []).some(join => {
|
|
@@ -864,12 +991,12 @@ class ParameterConditionPlacementOptimizer {
|
|
|
864
991
|
if ("code" in branches) {
|
|
865
992
|
return 0;
|
|
866
993
|
}
|
|
867
|
-
return this.
|
|
994
|
+
return this.collectDirectOutputMatches(root, branches[0], columnName).length;
|
|
868
995
|
}
|
|
869
996
|
if (!(target instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
870
997
|
return 0;
|
|
871
998
|
}
|
|
872
|
-
return this.
|
|
999
|
+
return this.collectDirectOutputMatches(root, target, columnName, true).length;
|
|
873
1000
|
}
|
|
874
1001
|
collectUnionBranches(query) {
|
|
875
1002
|
const branches = [];
|
|
@@ -900,7 +1027,7 @@ class ParameterConditionPlacementOptimizer {
|
|
|
900
1027
|
resolveUnionOutputIndex(root, firstBranch, outputColumnName) {
|
|
901
1028
|
const matches = [];
|
|
902
1029
|
this.collectSelectOutputs(root, firstBranch).forEach((item, index) => {
|
|
903
|
-
if (
|
|
1030
|
+
if (identifiersEqual(item.name, outputColumnName)) {
|
|
904
1031
|
matches.push(index);
|
|
905
1032
|
}
|
|
906
1033
|
});
|
|
@@ -923,6 +1050,98 @@ class ParameterConditionPlacementOptimizer {
|
|
|
923
1050
|
const collector = new SelectOutputCollector_1.SelectOutputCollector(null, commonTables.length > 0 ? commonTables : null);
|
|
924
1051
|
return collector.collect(query);
|
|
925
1052
|
}
|
|
1053
|
+
collectDirectOutputMatches(root, query, columnName, includeUnprovenPotential = false) {
|
|
1054
|
+
const collectedMatches = this.collectSelectOutputs(root, query).filter(item => identifiersEqual(item.name, columnName));
|
|
1055
|
+
if (collectedMatches.length > 0) {
|
|
1056
|
+
return this.hasExplicitOutputMatch(query, columnName)
|
|
1057
|
+
? [...collectedMatches, ...this.inferWildcardOutputMatches(root, query, columnName, true)]
|
|
1058
|
+
: collectedMatches;
|
|
1059
|
+
}
|
|
1060
|
+
return this.inferWildcardOutputMatches(root, query, columnName, includeUnprovenPotential);
|
|
1061
|
+
}
|
|
1062
|
+
hasExplicitOutputMatch(query, columnName) {
|
|
1063
|
+
return query.selectClause.items.some(item => {
|
|
1064
|
+
if (item.identifier) {
|
|
1065
|
+
return identifiersEqual(item.identifier.name, columnName);
|
|
1066
|
+
}
|
|
1067
|
+
return item.value instanceof ValueComponent_1.ColumnReference
|
|
1068
|
+
&& item.value.column.name !== "*"
|
|
1069
|
+
&& identifiersEqual(item.value.column.name, columnName);
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
inferWildcardOutputMatches(root, query, columnName, includeUnprovenPotential) {
|
|
1073
|
+
const matches = [];
|
|
1074
|
+
query.selectClause.items.forEach((item, outputIndex) => {
|
|
1075
|
+
if (item.identifier || !(item.value instanceof ValueComponent_1.ColumnReference) || item.value.column.name !== "*") {
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
const targetColumn = this.inferWildcardTargetColumn(root, query, item.value, columnName, includeUnprovenPotential);
|
|
1079
|
+
if (!targetColumn) {
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
if (targetColumn === "ambiguous") {
|
|
1083
|
+
matches.push(this.createInferredOutputColumn(columnName, new ValueComponent_1.ColumnReference(null, columnName), outputIndex), this.createInferredOutputColumn(columnName, new ValueComponent_1.ColumnReference(null, columnName), outputIndex));
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
matches.push(this.createInferredOutputColumn(columnName, targetColumn, outputIndex));
|
|
1087
|
+
});
|
|
1088
|
+
return matches;
|
|
1089
|
+
}
|
|
1090
|
+
inferWildcardTargetColumn(root, query, wildcard, columnName, includeUnprovenPotential) {
|
|
1091
|
+
if (!query.fromClause) {
|
|
1092
|
+
return null;
|
|
1093
|
+
}
|
|
1094
|
+
const bindings = this.getSourceBindings(query.fromClause);
|
|
1095
|
+
if (wildcard.namespaces === null) {
|
|
1096
|
+
if (bindings.length !== 1) {
|
|
1097
|
+
return "ambiguous";
|
|
1098
|
+
}
|
|
1099
|
+
return this.resolveWildcardColumnFromBinding(root, query, bindings[0], columnName, includeUnprovenPotential);
|
|
1100
|
+
}
|
|
1101
|
+
const namespace = wildcard.getNamespace();
|
|
1102
|
+
const matches = bindings.filter(binding => identifiersEqual(binding.alias, namespace));
|
|
1103
|
+
if (matches.length === 0) {
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
return matches.length === 1
|
|
1107
|
+
? this.resolveWildcardColumnFromBinding(root, query, matches[0], columnName, includeUnprovenPotential)
|
|
1108
|
+
: "ambiguous";
|
|
1109
|
+
}
|
|
1110
|
+
resolveWildcardColumnFromBinding(root, query, binding, columnName, includeUnprovenPotential) {
|
|
1111
|
+
const matches = this.collectSourceOutputMatches(root, query, binding.source, columnName);
|
|
1112
|
+
if (matches.length > 1) {
|
|
1113
|
+
return "ambiguous";
|
|
1114
|
+
}
|
|
1115
|
+
if (matches.length === 1) {
|
|
1116
|
+
const value = matches[0].value;
|
|
1117
|
+
return value instanceof ValueComponent_1.ColumnReference ? value : null;
|
|
1118
|
+
}
|
|
1119
|
+
return includeUnprovenPotential && binding.source.datasource instanceof Clause_1.TableSource
|
|
1120
|
+
? this.createColumnForBinding(binding, columnName)
|
|
1121
|
+
: null;
|
|
1122
|
+
}
|
|
1123
|
+
collectSourceOutputMatches(root, query, source, columnName) {
|
|
1124
|
+
var _a, _b, _c, _d;
|
|
1125
|
+
const commonTables = [
|
|
1126
|
+
...((_b = (_a = query.withClause) === null || _a === void 0 ? void 0 : _a.tables) !== null && _b !== void 0 ? _b : []),
|
|
1127
|
+
...((_d = (_c = root.withClause) === null || _c === void 0 ? void 0 : _c.tables) !== null && _d !== void 0 ? _d : [])
|
|
1128
|
+
];
|
|
1129
|
+
const collector = new SelectOutputCollector_1.SelectOutputCollector(null, commonTables.length > 0 ? commonTables : null);
|
|
1130
|
+
return collector.collect(source).filter(item => identifiersEqual(item.name, columnName));
|
|
1131
|
+
}
|
|
1132
|
+
createColumnForBinding(binding, columnName) {
|
|
1133
|
+
return new ValueComponent_1.ColumnReference(binding.alias ? [binding.alias] : null, columnName);
|
|
1134
|
+
}
|
|
1135
|
+
createInferredOutputColumn(name, value, outputIndex) {
|
|
1136
|
+
return {
|
|
1137
|
+
name,
|
|
1138
|
+
value,
|
|
1139
|
+
outputIndex,
|
|
1140
|
+
sourceAlias: value.getNamespace() || null,
|
|
1141
|
+
sourceName: null,
|
|
1142
|
+
sourceColumnName: name
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
926
1145
|
resolveSourceQueryForColumns(root, source) {
|
|
927
1146
|
var _a;
|
|
928
1147
|
if (source.datasource instanceof Clause_1.SubQuerySource) {
|