Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/123813.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 123813
summary: ensure that LOOKUP JOIN correctly enforces constraints on valid fields and index resolution.
area: ES|QL
type: enhancement
issues: [120189]
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ public void postAnalysisVerification(Failures failures) {
right().forEachDown(EsRelation.class, esr -> {
var indexNameWithModes = esr.indexNameWithModes();
if (indexNameWithModes.size() != 1) {
failures.add(
fail(esr, "invalid [{}] resolution in lookup mode to [{}] indices", esr.indexPattern(), indexNameWithModes.size())
);
String errorMessage = indexNameWithModes.isEmpty()
? "Index [" + esr.indexPattern() + "] exists, but no valid fields for LOOKUP JOIN were found"
: "Invalid [" + esr.indexPattern() + "] resolution in lookup mode to [" + indexNameWithModes.size() + "] indices";
failures.add(fail(esr, errorMessage));
} else if (indexNameWithModes.values().iterator().next() != IndexMode.LOOKUP) {
failures.add(
fail(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@

package org.elasticsearch.xpack.esql.plan.logical;

import java.util.Collections;
import java.util.Map;

import org.elasticsearch.index.IndexMode;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.esql.common.Failures;
import org.elasticsearch.xpack.esql.core.expression.Alias;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
import org.elasticsearch.xpack.esql.core.expression.AttributeSet;
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
import org.elasticsearch.xpack.esql.core.expression.Literal;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.core.type.EsField;
import org.elasticsearch.xpack.esql.plan.logical.join.Join;
import org.elasticsearch.xpack.esql.plan.logical.join.JoinConfig;
import org.elasticsearch.xpack.esql.plan.logical.join.JoinTypes;
Expand All @@ -23,6 +30,8 @@
import java.util.List;
import java.util.Set;

import org.elasticsearch.xpack.esql.plan.logical.join.LookupJoin;

public class JoinTests extends ESTestCase {
@AwaitsFix(bugUrl = "Test needs updating to the new JOIN planning")
public void testExpressionsAndReferences() {
Expand Down Expand Up @@ -99,4 +108,53 @@ public void testTransformExprs() {
private static Alias aliasForLiteral(String name) {
return new Alias(Source.EMPTY, name, new Literal(Source.EMPTY, 1, DataType.INTEGER));
}

public void testLookupJoinErrorMessage() {
Failures failures = new Failures();

String indexPattern = "test1";
Map<String, IndexMode> indexNameWithModes = Collections.emptyMap();

EsRelation fakeRelation = new EsRelation(Source.EMPTY, indexPattern, IndexMode.LOOKUP, indexNameWithModes, List.of());

EsField empNoEsField = new EsField("emp_no", DataType.INTEGER, Collections.emptyMap(), false);
FieldAttribute empNoField = new FieldAttribute(Source.EMPTY, "emp_no", empNoEsField);
Alias empNoAlias = new Alias(Source.EMPTY, "emp_no", empNoField);
LogicalPlan left = new Row(Source.EMPTY, List.of(empNoAlias));

LookupJoin lookupJoin = new LookupJoin(Source.EMPTY, left, fakeRelation,
new JoinConfig(JoinTypes.LEFT, List.of(), List.of(), List.of()));

lookupJoin.postAnalysisVerification(failures);

String expectedMessage = "Index [test1] exists, but no valid fields for LOOKUP JOIN were found";
assertTrue(failures.toString().contains(expectedMessage));
}

public void testLookupJoinErrorMessage_MultipleIndices() {
Failures failures = new Failures();

String indexPattern = "test1";
Map<String, IndexMode> indexNameWithModes = Map.of(
"test1", IndexMode.LOOKUP,
"test2", IndexMode.LOOKUP
);

EsField languagesEsField = new EsField("languages", DataType.KEYWORD, Collections.emptyMap(), true);
FieldAttribute languagesField = new FieldAttribute(Source.EMPTY, "languages", languagesEsField);
EsRelation fakeRelation = new EsRelation(Source.EMPTY, indexPattern, IndexMode.LOOKUP, indexNameWithModes, List.of(languagesField));

EsField empNoEsField = new EsField("emp_no", DataType.INTEGER, Collections.emptyMap(), false);
FieldAttribute empNoField = new FieldAttribute(Source.EMPTY, "emp_no", empNoEsField);
Alias empNoAlias = new Alias(Source.EMPTY, "emp_no", empNoField);
LogicalPlan left = new Row(Source.EMPTY, List.of(empNoAlias));

LookupJoin lookupJoin = new LookupJoin(Source.EMPTY, left, fakeRelation,
new JoinConfig(JoinTypes.LEFT, List.of(), List.of(), List.of()));

lookupJoin.postAnalysisVerification(failures);

String expectedMessage = "Invalid [test1] resolution in lookup mode to [2] indices";
assertTrue(failures.toString().contains(expectedMessage));
}
}
Loading