Skip to content

Commit 1d830ac

Browse files
committed
Fix first bug, lol
1 parent c2c401b commit 1d830ac

File tree

2 files changed

+39
-34
lines changed

2 files changed

+39
-34
lines changed

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProject.java

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.elasticsearch.xpack.esql.core.expression.Alias;
1111
import org.elasticsearch.xpack.esql.core.expression.Attribute;
1212
import org.elasticsearch.xpack.esql.core.expression.AttributeMap;
13+
import org.elasticsearch.xpack.esql.core.expression.AttributeSet;
1314
import org.elasticsearch.xpack.esql.core.expression.Expression;
1415
import org.elasticsearch.xpack.esql.core.expression.Expressions;
1516
import org.elasticsearch.xpack.esql.core.expression.NamedExpression;
@@ -64,46 +65,42 @@ protected LogicalPlan rule(Join join) {
6465
// used in the original Project; any such conflict needs to be resolved by copying the attribute under a temporary name via an
6566
// Eval - and using the attribute from said Eval in the new downstream Project.
6667
Set<String> lookupFieldNames = new HashSet<>(Expressions.names(join.rightOutputFields()));
67-
PushDownUtils.AttributeReplacement replacement = PushDownUtils.renameAttributesInExpressions(lookupFieldNames, newProjections);
68+
List<NamedExpression> finalProjections = new ArrayList<>(newProjections.size());
69+
AttributeMap.Builder<Alias> aliasesForReplacedAttributesBuilder = AttributeMap.builder();
70+
AttributeSet leftOutput = updatedJoin.left().outputSet();
6871

69-
List<Expression> conflictFreeProjections = replacement.rewrittenExpressions();
70-
List<Alias> evalAliases = new ArrayList<>(replacement.replacedAttributes().values());
71-
72-
if (evalAliases.isEmpty()) {
73-
// No name conflicts, so no eval needed.
74-
return new Project(project.source(), updatedJoin.replaceLeft(project.child()), newProjections);
75-
}
72+
for (NamedExpression proj : newProjections) {
73+
// TODO: add assert to Project that ensures Alias to attr or pure attr.
74+
Attribute coreAttr = (Attribute) (proj instanceof Alias as ? as.child() : proj);
75+
// Only fields from the left need to be protected from conflicts - because fields from the right shadow them.
76+
if (leftOutput.contains(coreAttr) == false || lookupFieldNames.contains(coreAttr.name()) == false) {
77+
finalProjections.add(proj);
78+
} else {
79+
// Conflict - the core attribute will be shadowed by the `LOOKUP JOIN` and we need to alias it in an upstream Eval.
80+
Alias renaming = aliasesForReplacedAttributesBuilder.computeIfAbsent(coreAttr, a -> {
81+
String tempName = TemporaryNameUtils.locallyUniqueTemporaryName(a.name(), "temp_name");
82+
return new Alias(a.source(), tempName, a, null, true);
83+
});
7684

77-
// The conflict free projections replaced any shadowed attributes by temporary attributes that we'll create in an Eval.
78-
// That's good if the replaced attribute was in an Alias; if the projection was a mere attribute to begin with, we need to
79-
// alias it back to the name/id that's expected in the original output.
80-
List<NamedExpression> finalProjections = new ArrayList<>(conflictFreeProjections.size());
81-
for (int i = 0; i < newProjections.size(); i++) {
82-
Expression conflictFreeProj = conflictFreeProjections.get(i);
83-
if (conflictFreeProj instanceof Alias as) {
84-
// Already aliased - keep it, it's fine if the child was rewritten.
85-
finalProjections.add(as);
86-
} else if (conflictFreeProj instanceof Attribute conflictFreeAttr) {
87-
Attribute expectedOutputAttr = (Attribute) newProjections.get(i);
88-
if (expectedOutputAttr.semanticEquals(conflictFreeAttr)) {
89-
// no conflict, wasn't rewritten
90-
finalProjections.add(expectedOutputAttr);
85+
Attribute renamedAttribute = renaming.toAttribute();
86+
Alias renamedBack;
87+
if (proj instanceof Alias as) {
88+
renamedBack = new Alias(as.source(), as.name(), renamedAttribute, as.id(), as.synthetic());
9189
} else {
92-
Alias renameBack = new Alias(
93-
expectedOutputAttr.source(),
94-
expectedOutputAttr.name(),
95-
conflictFreeAttr,
96-
expectedOutputAttr.id(),
97-
expectedOutputAttr.synthetic()
98-
);
99-
finalProjections.add(renameBack);
90+
// no alias - that means proj == coreAttr
91+
renamedBack = new Alias(coreAttr.source(), coreAttr.name(), renamedAttribute, coreAttr.id(), coreAttr.synthetic());
10092
}
101-
} else {
102-
throw new IllegalStateException("Resolving a list of projections must yield a list of projections again");
93+
finalProjections.add(renamedBack);
10394
}
10495
}
10596

106-
Eval eval = new Eval(project.source(), project.child(), evalAliases);
97+
if (aliasesForReplacedAttributesBuilder.isEmpty()) {
98+
// No name conflicts, so no eval needed.
99+
return new Project(project.source(), updatedJoin.replaceLeft(project.child()), newProjections);
100+
}
101+
102+
List<Alias> renamesForEval = new ArrayList<>(aliasesForReplacedAttributesBuilder.build().values());
103+
Eval eval = new Eval(project.source(), project.child(), renamesForEval);
107104
Join finalJoin = new Join(join.source(), eval, updatedJoin.right(), updatedJoin.config());
108105

109106
return new Project(project.source(), finalJoin, finalProjections);

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6850,7 +6850,15 @@ public void testMultipleLookupShadowing() {
68506850
/**
68516851
* Expects
68526852
*
6853-
* TODO
6853+
* Project[[languages{f}#16, emp_no{f}#13, languages{f}#16 AS language_code#6, language_name{f}#27]]
6854+
* \_Limit[1000[INTEGER],true]
6855+
* \_Join[LEFT,[languages{f}#16],[languages{f}#16],[language_code{f}#26]]
6856+
* |_Limit[1000[INTEGER],true]
6857+
* | \_Join[LEFT,[languages{f}#16],[languages{f}#16],[language_code{f}#24]]
6858+
* | |_Limit[1000[INTEGER],false]
6859+
* | | \_EsRelation[test][_meta_field{f}#19, emp_no{f}#13, first_name{f}#14, ..]
6860+
* | \_EsRelation[languages_lookup][LOOKUP][language_code{f}#24]
6861+
* \_EsRelation[languages_lookup][LOOKUP][language_code{f}#26, language_name{f}#27]
68546862
*/
68556863
public void testMultipleLookupProject() {
68566864
// TODO a test case where pushing down past the RENAME would shadow

0 commit comments

Comments
 (0)