Skip to content

Commit c771d46

Browse files
committed
SQL: Make Literal a NamedExpression (#33583)
* SQL: Make Literal a NamedExpression Literal now is a NamedExpression reducing the need for Aliases for folded expressions leading to simpler optimization rules. Fix #33523 (cherry picked from commit 91bca17)
1 parent 3bb572c commit c771d46

File tree

10 files changed

+214
-222
lines changed

10 files changed

+214
-222
lines changed

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

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,28 @@
77

88
import org.elasticsearch.xpack.sql.tree.Location;
99

10+
import java.util.List;
1011
import java.util.Objects;
1112

1213
import static java.util.Collections.emptyList;
1314

14-
import java.util.List;
15-
1615
/**
17-
* {@link Expression}s that can be converted into Elasticsearch
18-
* sorts, aggregations, or queries. They can also be extracted
19-
* from the result of a search.
16+
* {@link Expression}s that can be materialized and represent the result columns sent to the client.
17+
* Typically are converted into constants, functions or Elasticsearch order-bys,
18+
* aggregations, or queries. They can also be extracted from the result of a search.
2019
*
2120
* In the statement {@code SELECT ABS(foo), A, B+C FROM ...} the three named
22-
* expressions (ABS(foo), A, B+C) get converted to attributes and the user can
21+
* expressions {@code ABS(foo), A, B+C} get converted to attributes and the user can
2322
* only see Attributes.
2423
*
25-
* In the statement {@code SELECT foo FROM TABLE WHERE foo > 10 + 1} 10+1 is an
26-
* expression. It's not named - meaning there's no alias for it (defined by the
27-
* user) and as such there's no attribute - no column to be returned to the user.
28-
* It's an expression used for filtering so it doesn't appear in the result set
29-
* (derived table). "foo" on the other hand is an expression, a named expression
30-
* (it has a name) and also an attribute - it's a column in the result set.
24+
* In the statement {@code SELECT foo FROM TABLE WHERE foo > 10 + 1} both {@code foo} and
25+
* {@code 10 + 1} are named expressions, the first due to the SELECT, the second due to being a function.
26+
* However since {@code 10 + 1} is used for filtering it doesn't appear appear in the result set
27+
* (derived table) and as such it is never translated to an attribute.
28+
* "foo" on the other hand is since it's a column in the result set.
3129
*
32-
* Another example {@code SELECT foo FROM ... WHERE bar > 10 +1} "foo" gets
33-
* converted into an Attribute, bar does not. That's because bar is used for
30+
* Another example {@code SELECT foo FROM ... WHERE bar > 10 +1} {@code foo} gets
31+
* converted into an Attribute, bar does not. That's because {@code bar} is used for
3432
* filtering alone but it's not part of the projection meaning the user doesn't
3533
* need it in the derived table.
3634
*/

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,7 @@ public static AttributeSet references(List<? extends Expression> exps) {
8282
}
8383

8484
public static String name(Expression e) {
85-
if (e instanceof NamedExpression) {
86-
return ((NamedExpression) e).name();
87-
} else if (e instanceof Literal) {
88-
return e.toString();
89-
} else {
90-
return e.nodeName();
91-
}
85+
return e instanceof NamedExpression ? ((NamedExpression) e).name() : e.nodeName();
9286
}
9387

9488
public static List<String> names(Collection<? extends Expression> e) {
@@ -105,7 +99,7 @@ public static Attribute attribute(Expression e) {
10599
return ((NamedExpression) e).toAttribute();
106100
}
107101
if (e != null && e.foldable()) {
108-
return new LiteralAttribute(Literal.of(e));
102+
return Literal.of(e).toAttribute();
109103
}
110104
return null;
111105
}

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

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,28 @@
1212
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
1313
import org.elasticsearch.xpack.sql.type.DataTypes;
1414

15+
import java.util.List;
1516
import java.util.Objects;
1617

17-
public class Literal extends LeafExpression {
18+
import static java.util.Collections.emptyList;
19+
20+
/**
21+
* SQL Literal or constant.
22+
*/
23+
public class Literal extends NamedExpression {
24+
1825
public static final Literal TRUE = Literal.of(Location.EMPTY, Boolean.TRUE);
1926
public static final Literal FALSE = Literal.of(Location.EMPTY, Boolean.FALSE);
2027

2128
private final Object value;
2229
private final DataType dataType;
2330

2431
public Literal(Location location, Object value, DataType dataType) {
25-
super(location);
32+
this(location, null, value, dataType);
33+
}
34+
35+
public Literal(Location location, String name, Object value, DataType dataType) {
36+
super(location, name == null ? String.valueOf(value) : name, emptyList(), null);
2637
this.dataType = dataType;
2738
this.value = DataTypeConversion.convert(value, dataType);
2839
}
@@ -61,48 +72,83 @@ public Object fold() {
6172
return value;
6273
}
6374

75+
@Override
76+
public Attribute toAttribute() {
77+
return new LiteralAttribute(location(), name(), null, false, id(), false, dataType, this);
78+
}
79+
80+
@Override
81+
public Expression replaceChildren(List<Expression> newChildren) {
82+
throw new UnsupportedOperationException("this type of node doesn't have any children to replace");
83+
}
84+
85+
@Override
86+
public AttributeSet references() {
87+
return AttributeSet.EMPTY;
88+
}
6489

6590
@Override
6691
public int hashCode() {
67-
return Objects.hash(value, dataType);
92+
return Objects.hash(name(), value, dataType);
6893
}
6994

7095
@Override
7196
public boolean equals(Object obj) {
7297
if (this == obj) {
7398
return true;
7499
}
75-
76100
if (obj == null || getClass() != obj.getClass()) {
77101
return false;
78102
}
79103

80104
Literal other = (Literal) obj;
81-
return Objects.equals(value, other.value)
105+
return Objects.equals(name(), other.name())
106+
&& Objects.equals(value, other.value)
82107
&& Objects.equals(dataType, other.dataType);
83108
}
84109

85110
@Override
86111
public String toString() {
87-
return Objects.toString(value);
112+
String s = String.valueOf(value);
113+
return name().equals(s) ? s : name() + "=" + value;
88114
}
89115

116+
/**
117+
* Utility method for creating 'in-line' Literals (out of values instead of expressions).
118+
*/
90119
public static Literal of(Location loc, Object value) {
91120
if (value instanceof Literal) {
92121
return (Literal) value;
93122
}
94123
return new Literal(loc, value, DataTypes.fromJava(value));
95124
}
96125

126+
/**
127+
* Utility method for creating a literal out of a foldable expression.
128+
* Throws an exception if the expression is not foldable.
129+
*/
97130
public static Literal of(Expression foldable) {
98-
if (foldable instanceof Literal) {
99-
return (Literal) foldable;
100-
}
131+
return of((String) null, foldable);
132+
}
101133

134+
public static Literal of(String name, Expression foldable) {
102135
if (!foldable.foldable()) {
103136
throw new SqlIllegalArgumentException("Foldable expression required for Literal creation; received unfoldable " + foldable);
104137
}
105138

106-
return new Literal(foldable.location(), foldable.fold(), foldable.dataType());
139+
if (foldable instanceof Literal) {
140+
Literal l = (Literal) foldable;
141+
if (name == null || l.name().equals(name)) {
142+
return l;
143+
}
144+
}
145+
146+
Object fold = foldable.fold();
147+
148+
if (name == null) {
149+
name = foldable instanceof NamedExpression ? ((NamedExpression) foldable).name() : String.valueOf(fold);
150+
}
151+
152+
return new Literal(foldable.location(), name, fold, foldable.dataType());
107153
}
108-
}
154+
}

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,12 @@ public class LiteralAttribute extends TypedAttribute {
1515

1616
private final Literal literal;
1717

18-
public LiteralAttribute(Literal literal) {
19-
this(literal.location(), String.valueOf(literal.fold()), null, false, null, false, literal.dataType(), literal);
20-
}
21-
2218
public LiteralAttribute(Location location, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic,
2319
DataType dataType, Literal literal) {
2420
super(location, name, dataType, qualifier, nullable, id, synthetic);
2521
this.literal = literal;
2622
}
2723

28-
public Literal literal() {
29-
return literal;
30-
}
31-
3224
@Override
3325
protected NodeInfo<LiteralAttribute> info() {
3426
return NodeInfo.create(this, LiteralAttribute::new,
@@ -49,4 +41,4 @@ public ProcessorDefinition asProcessorDefinition() {
4941
protected String label() {
5042
return "c";
5143
}
52-
}
44+
}

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

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import org.elasticsearch.xpack.sql.expression.Expression;
1111
import org.elasticsearch.xpack.sql.expression.Expressions;
1212
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
13-
import org.elasticsearch.xpack.sql.expression.LiteralAttribute;
1413
import org.elasticsearch.xpack.sql.expression.function.Function;
1514
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute;
1615
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition;
@@ -69,11 +68,9 @@ protected ScriptTemplate asScript(Expression exp) {
6968
if (attr instanceof AggregateFunctionAttribute) {
7069
return asScriptFrom((AggregateFunctionAttribute) attr);
7170
}
72-
if (attr instanceof LiteralAttribute) {
73-
return asScriptFrom((LiteralAttribute) attr);
71+
if (attr instanceof FieldAttribute) {
72+
return asScriptFrom((FieldAttribute) attr);
7473
}
75-
// fall-back to
76-
return asScriptFrom((FieldAttribute) attr);
7774
}
7875
throw new SqlIllegalArgumentException("Cannot evaluate script for expression {}", exp);
7976
}
@@ -102,12 +99,6 @@ protected ScriptTemplate asScriptFrom(AggregateFunctionAttribute aggregate) {
10299
aggregate.dataType());
103100
}
104101

105-
protected ScriptTemplate asScriptFrom(LiteralAttribute literal) {
106-
return new ScriptTemplate(formatScript("{}"),
107-
paramsBuilder().variable(literal.literal()).build(),
108-
literal.dataType());
109-
}
110-
111102
protected String formatScript(String scriptTemplate) {
112103
return formatTemplate(scriptTemplate);
113104
}

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/E.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class E extends MathFunction {
2121
private static final ScriptTemplate TEMPLATE = new ScriptTemplate("Math.E", Params.EMPTY, DataType.DOUBLE);
2222

2323
public E(Location location) {
24-
super(location, new Literal(location, Math.E, DataType.DOUBLE));
24+
super(location, new Literal(location, "E", Math.E, DataType.DOUBLE));
2525
}
2626

2727
@Override

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Pi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class Pi extends MathFunction {
2121
private static final ScriptTemplate TEMPLATE = new ScriptTemplate("Math.PI", Params.EMPTY, DataType.DOUBLE);
2222

2323
public Pi(Location location) {
24-
super(location, new Literal(location, Math.PI, DataType.DOUBLE));
24+
super(location, new Literal(location, "PI", Math.PI, DataType.DOUBLE));
2525
}
2626

2727
@Override

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,36 +1118,12 @@ static class ConstantFolding extends OptimizerExpressionRule {
11181118

11191119
@Override
11201120
protected Expression rule(Expression e) {
1121-
// handle aliases to avoid double aliasing of functions
1122-
// alias points to function which gets folded and wrapped in an alias that is
1123-
// aliases
11241121
if (e instanceof Alias) {
11251122
Alias a = (Alias) e;
1126-
Expression fold = fold(a.child());
1127-
if (fold != a.child()) {
1128-
return new Alias(a.location(), a.name(), null, fold, a.id());
1129-
}
1130-
return a;
1131-
}
1132-
1133-
Expression fold = fold(e);
1134-
if (fold != e) {
1135-
// preserve the name through an alias
1136-
if (e instanceof NamedExpression) {
1137-
NamedExpression ne = (NamedExpression) e;
1138-
return new Alias(e.location(), ne.name(), null, fold, ne.id());
1139-
}
1140-
return fold;
1123+
return a.child().foldable() ? Literal.of(a.name(), a.child()) : a;
11411124
}
1142-
return e;
1143-
}
11441125

1145-
private Expression fold(Expression e) {
1146-
// literals are always foldable, so avoid creating a duplicate
1147-
if (e.foldable() && !(e instanceof Literal)) {
1148-
return new Literal(e.location(), e.fold(), e.dataType());
1149-
}
1150-
return e;
1126+
return e.foldable() ? Literal.of(e) : e;
11511127
}
11521128
}
11531129

@@ -1836,14 +1812,11 @@ protected LogicalPlan rule(LogicalPlan plan) {
18361812
private List<Object> extractConstants(List<? extends NamedExpression> named) {
18371813
List<Object> values = new ArrayList<>();
18381814
for (NamedExpression n : named) {
1839-
if (n instanceof Alias) {
1840-
Alias a = (Alias) n;
1841-
if (a.child().foldable()) {
1842-
values.add(a.child().fold());
1843-
}
1844-
else {
1845-
return values;
1846-
}
1815+
if (n.foldable()) {
1816+
values.add(n.fold());
1817+
} else {
1818+
// not everything is foldable, bail-out early
1819+
return values;
18471820
}
18481821
}
18491822
return values;

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/LiteralTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ protected Literal randomInstance() {
6161

6262
@Override
6363
protected Literal copy(Literal instance) {
64-
return new Literal(instance.location(), instance.value(), instance.dataType());
64+
return new Literal(instance.location(), instance.name(), instance.value(), instance.dataType());
6565
}
6666

6767
@Override

0 commit comments

Comments
 (0)