SQL: Fix issue with IIF function when condition folds (#46290)

Previously, when the condition (1st argument) of the IIF function could
be evaluated (folded) to false, the `IfConditional` was eliminated which
caused `IndexOutOfBoundsException` to be thrown when `info()` and
`resolveType()` methods where called.

Fixes: #46268

(cherry picked from commit 9a885a3ac47bc8f52c07770d1d8d670ce0af1e59)
This commit is contained in:
Marios Trivyzas 2019-09-04 10:32:01 +03:00
parent 3f67cbe974
commit fd0affb503
No known key found for this signature in database
GPG Key ID: 8817B46B0CF36A3F
5 changed files with 62 additions and 3 deletions

View File

@ -52,6 +52,21 @@ ORDER BY emp_no LIMIT 10;
10014 | null 10014 | null
; ;
caseWithConditionsFolded
schema::emp_no:i|CASE_1:byte|CASE_2:i
SELECT emp_no, CASE WHEN NULL = 1 THEN emp_no WHEN 10 < 5 THEN emp_no ELSE languages END AS "CASE_1",
CASE WHEN NULL = 1 THEN languages WHEN 10 > 5 THEN emp_no ELSE languages END AS "CASE_2"
FROM test_emp ORDER BY 1 LIMIT 5;
emp_no | CASE_1 | CASE_2
--------+--------+-------
10001 | 2 | 10001
10002 | 5 | 10002
10003 | 4 | 10003
10004 | 5 | 10004
10005 | 1 | 10005
;
caseWhere caseWhere
SELECT last_name FROM test_emp WHERE CASE WHEN LENGTH(last_name) < 7 THEN 'ShortName' ELSE 'LongName' END = 'LongName' SELECT last_name FROM test_emp WHERE CASE WHEN LENGTH(last_name) < 7 THEN 'ShortName' ELSE 'LongName' END = 'LongName'
ORDER BY emp_no LIMIT 10; ORDER BY emp_no LIMIT 10;
@ -235,6 +250,19 @@ ORDER BY emp_no LIMIT 10;
10014 | null 10014 | null
; ;
iifWithConditionFolded
schema::emp_no:i|IIF_1:i|IIF_2:byte|IIF_3:i
SELECT emp_no, IIF(NULL, emp_no) AS IIF_1, IIF(NULL, emp_no, languages) AS IIF_2, IIF(10 > 5, emp_no, languages) AS IIF_3 FROM test_emp ORDER BY 1 LIMIT 5;
emp_no | IIF_1 | IIF_2 | IIF_3
--------+-------+-------+------
10001 | null | 2 | 10001
10002 | null | 5 | 10002
10003 | null | 4 | 10003
10004 | null | 5 | 10004
10005 | null | 1 | 10005
;
iifWhere iifWhere
SELECT last_name FROM test_emp WHERE IIF(LENGTH(last_name) < 7, 'ShortName') IS NOT NULL ORDER BY emp_no LIMIT 10; SELECT last_name FROM test_emp WHERE IIF(LENGTH(last_name) < 7, 'ShortName') IS NOT NULL ORDER BY emp_no LIMIT 10;

View File

@ -18,6 +18,7 @@ import java.util.List;
import static org.elasticsearch.common.logging.LoggerMessageFormat.format; import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isBoolean; import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isBoolean;
import static org.elasticsearch.xpack.sql.util.CollectionUtils.combine;
public class Iif extends Case { public class Iif extends Case {
@ -25,13 +26,13 @@ public class Iif extends Case {
super(source, Arrays.asList(new IfConditional(source, condition, thenResult), elseResult != null ? elseResult : Literal.NULL)); super(source, Arrays.asList(new IfConditional(source, condition, thenResult), elseResult != null ? elseResult : Literal.NULL));
} }
private Iif(Source source, List<Expression> expressions) { Iif(Source source, List<Expression> expressions) {
super(source, expressions); super(source, expressions);
} }
@Override @Override
protected NodeInfo<? extends Iif> info() { protected NodeInfo<? extends Iif> info() {
return NodeInfo.create(this, Iif::new, conditions().get(0).condition(), conditions().get(0).result(), elseResult()); return NodeInfo.create(this, Iif::new, combine(conditions(), elseResult()));
} }
@Override @Override
@ -41,6 +42,10 @@ public class Iif extends Case {
@Override @Override
protected TypeResolution resolveType() { protected TypeResolution resolveType() {
if (conditions().isEmpty()) {
return TypeResolution.TYPE_RESOLVED;
}
TypeResolution conditionTypeResolution = isBoolean(conditions().get(0).condition(), sourceText(), Expressions.ParamOrdinal.FIRST); TypeResolution conditionTypeResolution = isBoolean(conditions().get(0).condition(), sourceText(), Expressions.ParamOrdinal.FIRST);
if (conditionTypeResolution.unresolved()) { if (conditionTypeResolution.unresolved()) {
return conditionTypeResolution; return conditionTypeResolution;

View File

@ -17,9 +17,11 @@ import org.elasticsearch.xpack.sql.type.DataType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static org.elasticsearch.xpack.sql.expression.Expression.TypeResolution;
import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomIntLiteral; import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomIntLiteral;
import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomStringLiteral; import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomStringLiteral;
import static org.elasticsearch.xpack.sql.tree.Source.EMPTY; import static org.elasticsearch.xpack.sql.tree.Source.EMPTY;
@ -117,6 +119,13 @@ public class CaseTests extends AbstractNodeTestCase<Case, Expression> {
assertEquals(DataType.KEYWORD, c.dataType()); assertEquals(DataType.KEYWORD, c.dataType());
} }
public void testAllConditionsFolded() {
Case c = new Case(EMPTY, Collections.singletonList(Literal.of(EMPTY, "foo")));
assertEquals(DataType.KEYWORD, c.dataType());
assertEquals(TypeResolution.TYPE_RESOLVED, c.typeResolved());
assertNotNull(c.info());
}
private List<Expression> mutateChildren(Case c) { private List<Expression> mutateChildren(Case c) {
boolean removeConditional = randomBoolean(); boolean removeConditional = randomBoolean();
List<Expression> expressions = new ArrayList<>(c.children().size()); List<Expression> expressions = new ArrayList<>(c.children().size());

View File

@ -6,20 +6,24 @@
package org.elasticsearch.xpack.sql.expression.predicate.conditional; package org.elasticsearch.xpack.sql.expression.predicate.conditional;
import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils; import org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Equals; import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Equals;
import org.elasticsearch.xpack.sql.tree.AbstractNodeTestCase; import org.elasticsearch.xpack.sql.tree.AbstractNodeTestCase;
import org.elasticsearch.xpack.sql.tree.NodeSubclassTests; import org.elasticsearch.xpack.sql.tree.NodeSubclassTests;
import org.elasticsearch.xpack.sql.tree.Source; import org.elasticsearch.xpack.sql.tree.Source;
import org.elasticsearch.xpack.sql.tree.SourceTests; import org.elasticsearch.xpack.sql.tree.SourceTests;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomIntLiteral; import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomIntLiteral;
import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomStringLiteral; import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomStringLiteral;
import static org.elasticsearch.xpack.sql.tree.Source.EMPTY;
import static org.elasticsearch.xpack.sql.tree.SourceTests.randomSource; import static org.elasticsearch.xpack.sql.tree.SourceTests.randomSource;
/** /**
@ -74,6 +78,13 @@ public class IifTests extends AbstractNodeTestCase<Iif, Expression> {
newChildren.get(2)))); newChildren.get(2))));
} }
public void testConditionFolded() {
Iif iif = new Iif(EMPTY, Collections.singletonList(Literal.of(EMPTY, "foo")));
assertEquals(DataType.KEYWORD, iif.dataType());
assertEquals(Expression.TypeResolution.TYPE_RESOLVED, iif.typeResolved());
assertNotNull(iif.info());
}
private List<Expression> mutateChildren(Iif iif) { private List<Expression> mutateChildren(Iif iif) {
List<Expression> expressions = new ArrayList<>(3); List<Expression> expressions = new ArrayList<>(3);
Equals eq = (Equals) iif.conditions().get(0).condition(); Equals eq = (Equals) iif.conditions().get(0).condition();

View File

@ -49,9 +49,9 @@ import org.elasticsearch.xpack.sql.expression.predicate.conditional.Case;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce; import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalFunction; import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalFunction;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Greatest; import org.elasticsearch.xpack.sql.expression.predicate.conditional.Greatest;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Iif;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfConditional; import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfConditional;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfNull; import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfNull;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Iif;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Least; import org.elasticsearch.xpack.sql.expression.predicate.conditional.Least;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIf; import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIf;
import org.elasticsearch.xpack.sql.expression.predicate.logical.And; import org.elasticsearch.xpack.sql.expression.predicate.logical.And;
@ -112,6 +112,7 @@ import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.elasticsearch.xpack.sql.expression.Expression.TypeResolution;
import static org.elasticsearch.xpack.sql.expression.Literal.FALSE; import static org.elasticsearch.xpack.sql.expression.Literal.FALSE;
import static org.elasticsearch.xpack.sql.expression.Literal.NULL; import static org.elasticsearch.xpack.sql.expression.Literal.NULL;
import static org.elasticsearch.xpack.sql.expression.Literal.TRUE; import static org.elasticsearch.xpack.sql.expression.Literal.TRUE;
@ -630,6 +631,7 @@ public class OptimizerTests extends ESTestCase {
assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[a{f}#")); assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[a{f}#"));
assertThat(c.conditions().get(1).condition().toString(), startsWith("GreaterThan[a{f}#")); assertThat(c.conditions().get(1).condition().toString(), startsWith("GreaterThan[a{f}#"));
assertFalse(c.foldable()); assertFalse(c.foldable());
assertEquals(TypeResolution.TYPE_RESOLVED, c.typeResolved());
} }
public void testSimplifyCaseConditionsFoldWhenTrue() { public void testSimplifyCaseConditionsFoldWhenTrue() {
@ -661,6 +663,7 @@ public class OptimizerTests extends ESTestCase {
assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[a{f}#")); assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[a{f}#"));
assertThat(c.conditions().get(1).condition().toString(), startsWith("Equals[=1,=1]#")); assertThat(c.conditions().get(1).condition().toString(), startsWith("Equals[=1,=1]#"));
assertFalse(c.foldable()); assertFalse(c.foldable());
assertEquals(TypeResolution.TYPE_RESOLVED, c.typeResolved());
} }
public void testSimplifyCaseConditionsFoldCompletely() { public void testSimplifyCaseConditionsFoldCompletely() {
@ -685,6 +688,7 @@ public class OptimizerTests extends ESTestCase {
assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[=1,=1]#")); assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[=1,=1]#"));
assertTrue(c.foldable()); assertTrue(c.foldable());
assertEquals("foo2", c.fold()); assertEquals("foo2", c.fold());
assertEquals(TypeResolution.TYPE_RESOLVED, c.typeResolved());
} }
public void testSimplifyIif_ConditionTrue() { public void testSimplifyIif_ConditionTrue() {
@ -696,6 +700,7 @@ public class OptimizerTests extends ESTestCase {
assertEquals(1, iif.conditions().size()); assertEquals(1, iif.conditions().size());
assertTrue(iif.foldable()); assertTrue(iif.foldable());
assertEquals("foo", iif.fold()); assertEquals("foo", iif.fold());
assertEquals(TypeResolution.TYPE_RESOLVED, iif.typeResolved());
} }
public void testSimplifyIif_ConditionFalse() { public void testSimplifyIif_ConditionFalse() {
@ -707,6 +712,7 @@ public class OptimizerTests extends ESTestCase {
assertEquals(0, iif.conditions().size()); assertEquals(0, iif.conditions().size());
assertTrue(iif.foldable()); assertTrue(iif.foldable());
assertEquals("bar", iif.fold()); assertEquals("bar", iif.fold());
assertEquals(TypeResolution.TYPE_RESOLVED, iif.typeResolved());
} }
// //