SQL: Preserve original source for cast/convert function (#40271)

Improve rule for pruning cast to preserve the original source
Fix #40239

(cherry picked from commit 7591cb1a1577320b3aec2ec557b0f881b6af744f)
This commit is contained in:
Costin Leau 2019-03-21 14:08:16 +02:00 committed by Costin Leau
parent 1e6941b138
commit dd41ce0763
3 changed files with 24 additions and 28 deletions

View File

@ -85,8 +85,8 @@ SELECT SUM(salary) FROM test_emp;
aggregateWithCastPruned aggregateWithCastPruned
SELECT CAST(SUM(salary) AS INTEGER) FROM test_emp; SELECT CAST(SUM(salary) AS INTEGER) FROM test_emp;
SUM(salary) CAST(SUM(salary) AS INTEGER)
------------- -----------------------------
4824855 4824855
; ;

View File

@ -163,6 +163,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
); );
//new BalanceBooleanTrees()); //new BalanceBooleanTrees());
Batch label = new Batch("Set as Optimized", Limiter.ONCE, Batch label = new Batch("Set as Optimized", Limiter.ONCE,
CleanAliases.INSTANCE,
new SetAsOptimized()); new SetAsOptimized());
return Arrays.asList(operators, aggregate, local, label); return Arrays.asList(operators, aggregate, local, label);
@ -895,7 +896,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
// Check if the groupings (a, y) match the orderings (b, x) through the aggregates' aliases (x, y) // Check if the groupings (a, y) match the orderings (b, x) through the aggregates' aliases (x, y)
// e.g. SELECT a AS x, b AS y ... GROUP BY a, y ORDER BY b, x // e.g. SELECT a AS x, b AS y ... GROUP BY a, y ORDER BY b, x
if ((equalsAsAttribute(child, group) if ((equalsAsAttribute(child, group)
&& (equalsAsAttribute(alias, fieldToOrder) || equalsAsAttribute(child, fieldToOrder))) && (equalsAsAttribute(alias, fieldToOrder) || equalsAsAttribute(child, fieldToOrder)))
|| (equalsAsAttribute(alias, group) || (equalsAsAttribute(alias, group)
&& (equalsAsAttribute(alias, fieldToOrder) || equalsAsAttribute(child, fieldToOrder)))) { && (equalsAsAttribute(alias, fieldToOrder) || equalsAsAttribute(child, fieldToOrder)))) {
isMatching.set(Boolean.TRUE); isMatching.set(Boolean.TRUE);
@ -944,43 +945,22 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
protected LogicalPlan rule(LogicalPlan plan) { protected LogicalPlan rule(LogicalPlan plan) {
final Map<Attribute, Attribute> replacedCast = new LinkedHashMap<>(); final Map<Attribute, Attribute> replacedCast = new LinkedHashMap<>();
// first eliminate casts inside Aliases // eliminate redundant casts
LogicalPlan transformed = plan.transformExpressionsUp(e -> { LogicalPlan transformed = plan.transformExpressionsUp(e -> {
// cast wrapped in an alias
if (e instanceof Alias) {
Alias as = (Alias) e;
if (as.child() instanceof Cast) {
Cast c = (Cast) as.child();
if (c.from() == c.to()) {
Alias newAs = new Alias(as.source(), as.name(), as.qualifier(), c.field(), as.id(), as.synthetic());
replacedCast.put(as.toAttribute(), newAs.toAttribute());
return newAs;
}
}
return e;
}
return e;
});
// then handle stand-alone casts (mixed together the cast rule will kick in before the alias)
transformed = transformed.transformExpressionsUp(e -> {
if (e instanceof Cast) { if (e instanceof Cast) {
Cast c = (Cast) e; Cast c = (Cast) e;
if (c.from() == c.to()) { if (c.from() == c.to()) {
Expression argument = c.field(); Expression argument = c.field();
if (argument instanceof NamedExpression) { Alias as = new Alias(c.source(), c.sourceText(), argument);
replacedCast.put(c.toAttribute(), ((NamedExpression) argument).toAttribute()); replacedCast.put(c.toAttribute(), as.toAttribute());
}
return argument; return as;
} }
} }
return e; return e;
}); });
// replace attributes from previous removed Casts // replace attributes from previous removed Casts
if (!replacedCast.isEmpty()) { if (!replacedCast.isEmpty()) {
return transformed.transformUp(p -> { return transformed.transformUp(p -> {

View File

@ -13,6 +13,7 @@ import org.elasticsearch.xpack.sql.expression.Order;
import org.elasticsearch.xpack.sql.expression.UnresolvedAttribute; import org.elasticsearch.xpack.sql.expression.UnresolvedAttribute;
import org.elasticsearch.xpack.sql.expression.UnresolvedStar; import org.elasticsearch.xpack.sql.expression.UnresolvedStar;
import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction; import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.StringQueryPredicate; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.StringQueryPredicate;
@ -65,6 +66,21 @@ public class SqlParserTests extends ESTestCase {
assertEquals("SCORE()", f.sourceText()); assertEquals("SCORE()", f.sourceText());
} }
public void testSelectCast() {
Cast f = singleProjection(project(parseStatement("SELECT CAST(POWER(languages, 2) AS DOUBLE) FROM foo")), Cast.class);
assertEquals("CAST(POWER(languages, 2) AS DOUBLE)", f.sourceText());
}
public void testSelectCastOperator() {
Cast f = singleProjection(project(parseStatement("SELECT POWER(languages, 2)::DOUBLE FROM foo")), Cast.class);
assertEquals("POWER(languages, 2)::DOUBLE", f.sourceText());
}
public void testSelectCastWithSQLOperator() {
Cast f = singleProjection(project(parseStatement("SELECT CONVERT(POWER(languages, 2), SQL_DOUBLE) FROM foo")), Cast.class);
assertEquals("CONVERT(POWER(languages, 2), SQL_DOUBLE)", f.sourceText());
}
public void testSelectAddWithParanthesis() { public void testSelectAddWithParanthesis() {
Add f = singleProjection(project(parseStatement("SELECT (1 + 2)")), Add.class); Add f = singleProjection(project(parseStatement("SELECT (1 + 2)")), Add.class);
assertEquals("1 + 2", f.sourceText()); assertEquals("1 + 2", f.sourceText());