Skip to content

Commit 66fccef

Browse files
committed
SQL: Enchance output of explain for optimized plan
When a literal expression is replaced with its evaluated value, in the optimizer, then use this new evaluated value in the explain plan instead of the original expression.
1 parent 6abddd8 commit 66fccef

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/Function.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
import org.elasticsearch.xpack.sql.expression.Expression;
99
import org.elasticsearch.xpack.sql.expression.ExpressionId;
1010
import org.elasticsearch.xpack.sql.expression.Expressions;
11+
import org.elasticsearch.xpack.sql.expression.Literal;
1112
import org.elasticsearch.xpack.sql.expression.NamedExpression;
1213
import org.elasticsearch.xpack.sql.tree.Location;
1314
import org.elasticsearch.xpack.sql.util.StringUtils;
1415

1516
import java.util.List;
17+
import java.util.Objects;
1618
import java.util.StringJoiner;
1719

1820
/**
@@ -51,7 +53,7 @@ public boolean nullable() {
5153

5254
@Override
5355
public String toString() {
54-
return name() + "#" + id();
56+
return functionName + functionArgsForToString() + "#" + id();
5557
}
5658

5759
public String functionName() {
@@ -75,4 +77,31 @@ protected String functionArgs() {
7577
public boolean functionEquals(Function f) {
7678
return f != null && getClass() == f.getClass() && arguments().equals(f.arguments());
7779
}
80+
81+
/**
82+
* Build the string representation of the function arguments for the {@link #toString()}.
83+
* - If an argument is a Function call {@code toString()} to work recursively
84+
* - If an argument is a Literal then just use the evaluated value.
85+
* - If an argument is a NamedExpression then use it's {@code name()} to avoid long confusing output
86+
* - If none of those resolve to the {@code toString()} method of the argument
87+
*
88+
* @return String representation of the function args for the {@link #toString} representation
89+
*/
90+
private String functionArgsForToString() {
91+
StringJoiner sj = new StringJoiner(",", "(", ")");
92+
for (Expression child : children()) {
93+
String val;
94+
if (child instanceof Function) {
95+
val = child.toString();
96+
} else if (child instanceof Literal) {
97+
val = Objects.toString(((Literal) child).value());
98+
} else if (child instanceof NamedExpression && child.resolved()) {
99+
val = Expressions.name(child);
100+
} else {
101+
val = child.toString();
102+
}
103+
sj.add(val);
104+
}
105+
return sj.toString();
106+
}
78107
}

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Sub;
1717
import org.elasticsearch.xpack.sql.type.DataType;
1818

19-
import static org.hamcrest.core.StringStartsWith.startsWith;
20-
2119
public class ExpressionTests extends ESTestCase {
2220

2321
private final SqlParser parser = new SqlParser();
@@ -137,7 +135,7 @@ public void testComplexArithmetic() {
137135
Expression expr = parser.createExpression("-(((a-2)-(-3))+b)");
138136
assertEquals(Neg.class, expr.getClass());
139137
Neg neg = (Neg) expr;
140-
assertThat(neg.name(), startsWith("-(((a) - 2) - -3) + (b)#"));
138+
assertEquals("-ADD(SUB(SUB(?a,2),-3),?b)", neg.name().replaceAll("#\\d+", ""));
141139
assertEquals(1, neg.children().size());
142140
assertEquals(Add.class, neg.children().get(0).getClass());
143141
Add add = (Add) neg.children().get(0);

x-pack/qa/sql/no-security/src/test/java/org/elasticsearch/xpack/qa/sql/nosecurity/CliExplainIT.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,22 @@ public void testExplainWithCount() throws IOException {
160160
assertThat(readLine(), startsWith("}]"));
161161
assertEquals("", readLine());
162162
}
163+
164+
public void testOptimizedPlanWithFoldableLiteralExpressions() throws IOException {
165+
index("test", body -> body.field("test_field", "test_value1").field("i", 1));
166+
index("test", body -> body.field("test_field", "test_value2").field("i", 2));
167+
168+
assertThat(command("EXPLAIN (PLAN OPTIMIZED) SELECT POWER(i, (1 + 2)) FROM test"), containsString("plan"));
169+
assertThat(readLine(), startsWith("----------"));
170+
assertEquals("Project[[POWER(i,3)]]", readLine().replaceAll("#\\d+", ""));
171+
assertThat(readLine(), startsWith("\\_EsRelation[test][i{f}"));
172+
assertEquals("", readLine());
173+
174+
assertThat(command("EXPLAIN (PLAN OPTIMIZED) SELECT POWER(i, (1 - 2)) * ABS(10 + 20) FROM test"),
175+
containsString("plan"));
176+
assertThat(readLine(), startsWith("----------"));
177+
assertEquals("Project[[MUL(POWER(i,-1),30)]]", readLine().replaceAll("#\\d+", ""));
178+
assertThat(readLine(), startsWith("\\_EsRelation[test][i{f}#"));
179+
assertEquals("", readLine());
180+
}
163181
}

0 commit comments

Comments
 (0)