Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ public LogicalPlan visitAggregation(Aggregation node, AnalysisContext context) {
for (UnresolvedExpression expr : node.getAggExprList()) {
NamedExpression aggExpr = namedExpressionAnalyzer.analyze(expr, context);
aggregatorBuilder.add(
new NamedAggregator(aggExpr.getName(), (Aggregator) aggExpr.getDelegated()));
new NamedAggregator(aggExpr.getNameOrAlias(), (Aggregator) aggExpr.getDelegated()));
}

ImmutableList.Builder<NamedExpression> groupbyBuilder = new ImmutableList.Builder<>();
Expand All @@ -347,7 +347,8 @@ public LogicalPlan visitAggregation(Aggregation node, AnalysisContext context) {
newEnv.define(
new Symbol(Namespace.FIELD_NAME, aggregator.getName()), aggregator.type()));
groupBys.forEach(
group -> newEnv.define(new Symbol(Namespace.FIELD_NAME, group.getName()), group.type()));
group ->
newEnv.define(new Symbol(Namespace.FIELD_NAME, group.getNameOrAlias()), group.type()));
return new LogicalAggregation(child, aggregators, groupBys);
}

Expand Down Expand Up @@ -440,7 +441,8 @@ public LogicalPlan visitProject(Project node, AnalysisContext context) {
context.push();
TypeEnvironment newEnv = context.peek();
namedExpressions.forEach(
expr -> newEnv.define(new Symbol(Namespace.FIELD_NAME, expr.getName()), expr.type()));
expr ->
newEnv.define(new Symbol(Namespace.FIELD_NAME, expr.getNameOrAlias()), expr.type()));
List<NamedExpression> namedParseExpressions = context.getNamedParseExpressions();
return new LogicalProject(child, namedExpressions, namedParseExpressions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ private Expression visitMetadata(
private Expression visitIdentifier(String ident, AnalysisContext context) {
// ParseExpression will always override ReferenceExpression when ident conflicts
for (NamedExpression expr : context.getNamedParseExpressions()) {
if (expr.getName().equals(ident) && expr.getDelegated() instanceof ParseExpression) {
if (expr.getNameOrAlias().equals(ident) && expr.getDelegated() instanceof ParseExpression) {
return expr.getDelegated();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public Void visitAggregation(LogicalAggregation plan, Void context) {
groupBy ->
expressionMap.put(
groupBy.getDelegated(),
new ReferenceExpression(groupBy.getName(), groupBy.type())));
new ReferenceExpression(groupBy.getNameOrAlias(), groupBy.type())));
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import lombok.RequiredArgsConstructor;
import org.opensearch.sql.ast.AbstractNodeVisitor;
import org.opensearch.sql.ast.expression.Alias;
import org.opensearch.sql.ast.expression.QualifiedName;
import org.opensearch.sql.ast.expression.UnresolvedExpression;
import org.opensearch.sql.expression.DSL;
import org.opensearch.sql.expression.NamedExpression;
Expand All @@ -27,6 +28,18 @@ public NamedExpression analyze(UnresolvedExpression expression, AnalysisContext

@Override
public NamedExpression visitAlias(Alias node, AnalysisContext context) {
return DSL.named(node.getName(), node.getDelegated().accept(expressionAnalyzer, context));
return DSL.named(
unqualifiedNameIfFieldOnly(node, context),
node.getDelegated().accept(expressionAnalyzer, context),
node.getAlias());
}

private String unqualifiedNameIfFieldOnly(Alias node, AnalysisContext context) {
UnresolvedExpression selectItem = node.getDelegated();
if (selectItem instanceof QualifiedName) {
QualifierAnalyzer qualifierAnalyzer = new QualifierAnalyzer(context);
return qualifierAnalyzer.unqualified((QualifiedName) selectItem);
}
return node.getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public List<NamedExpression> visitAlias(Alias node, AnalysisContext context) {
}

Expression expr = referenceIfSymbolDefined(node, context);
return Collections.singletonList(DSL.named(node.getName(), expr));
return Collections.singletonList(
DSL.named(unqualifiedNameIfFieldOnly(node, context), expr, node.getAlias()));
}

/**
Expand All @@ -76,7 +77,7 @@ public List<NamedExpression> visitAlias(Alias node, AnalysisContext context) {
* aggExpr)) Agg(Alias("AVG(age)", aggExpr))
* <li>SELECT length(name), AVG(age) FROM s BY length(name) Project(Alias("name", expr),
* Alias("AVG(age)", aggExpr)) Agg(Alias("AVG(age)", aggExpr))
* <li>SELECT length(name) as l, AVG(age) FROM s BY l Project(Alias("l", expr),
* <li>SELECT length(name) as l, AVG(age) FROM s BY l Project(Alias("name", expr, l),
* Alias("AVG(age)", aggExpr)) Agg(Alias("AVG(age)", aggExpr), Alias("length(name)",
* groupExpr))
* </ol>
Expand All @@ -88,9 +89,7 @@ private Expression referenceIfSymbolDefined(Alias expr, AnalysisContext context)
// (OVER clause) and thus depends on name in alias to be replaced correctly
return optimizer.optimize(
DSL.named(
delegatedExpr.toString(),
delegatedExpr.accept(expressionAnalyzer, context),
expr.getName()),
expr.getName(), delegatedExpr.accept(expressionAnalyzer, context), expr.getAlias()),
context);
}

Expand Down Expand Up @@ -129,4 +128,21 @@ public List<NamedExpression> visitNestedAllTupleFields(
})
.collect(Collectors.toList());
}

/**
* Get unqualified name if select item is just a field. For example, suppose an index named
* "accounts", return "age" for "SELECT accounts.age". But do nothing for expression in "SELECT
* ABS(accounts.age)". Note that an assumption is made implicitly that original name field in
* Alias must be the same as the values in QualifiedName. This is true because AST builder does
* this. Otherwise, what unqualified() returns will override Alias's name as NamedExpression's
* name even though the QualifiedName doesn't have qualifier.
*/
private String unqualifiedNameIfFieldOnly(Alias node, AnalysisContext context) {
UnresolvedExpression selectItem = node.getDelegated();
if (selectItem instanceof QualifiedName) {
QualifierAnalyzer qualifierAnalyzer = new QualifierAnalyzer(context);
return qualifierAnalyzer.unqualified((QualifiedName) selectItem);
}
return node.getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public LogicalPlan visitAlias(Alias node, AnalysisContext context) {
List<Pair<SortOption, Expression>> sortList = analyzeSortList(unresolved, context);

WindowDefinition windowDefinition = new WindowDefinition(partitionByList, sortList);
NamedExpression namedWindowFunction = new NamedExpression(node.getName(), windowFunction);
NamedExpression namedWindowFunction =
new NamedExpression(node.getName(), windowFunction, node.getAlias());
List<Pair<SortOption, Expression>> allSortItems = windowDefinition.getAllSortItems();

if (allSortItems.isEmpty()) {
Expand Down
6 changes: 1 addition & 5 deletions core/src/main/java/org/opensearch/sql/ast/dsl/AstDSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,7 @@ public Alias alias(String name, UnresolvedExpression expr) {

@Deprecated
public Alias alias(String name, UnresolvedExpression expr, String alias) {
if (alias == null) {
return new Alias(name, expr);
} else {
return new Alias(alias, expr);
}
return new Alias(name, expr, alias);
}

public NestedAllTupleFields nestedAllTupleFields(String path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package org.opensearch.sql.ast.expression;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand All @@ -17,6 +18,7 @@
* restoring the info in toString() method which is inaccurate because original info is already
* lost.
*/
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Getter
@RequiredArgsConstructor
Expand All @@ -29,6 +31,9 @@ public class Alias extends UnresolvedExpression {
/** Expression aliased. */
private final UnresolvedExpression delegated;

/** TODO. Optional field alias. */
private String alias;

@Override
public <T, C> T accept(AbstractNodeVisitor<T, C> nodeVisitor, C context) {
return nodeVisitor.visitAlias(this, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public static NamedExpression named(String name, Expression expression) {

@Deprecated
public static NamedExpression named(String name, Expression expression, String alias) {
return new NamedExpression(alias, expression);
return new NamedExpression(name, expression, alias);
}

public static NamedAggregator named(String name, Aggregator aggregator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package org.opensearch.sql.expression;

import com.google.common.base.Strings;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand All @@ -17,7 +19,8 @@
* Please see more details in associated unresolved expression operator<br>
* {@link org.opensearch.sql.ast.expression.Alias}.
*/
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@EqualsAndHashCode
@Getter
@RequiredArgsConstructor
public class NamedExpression implements Expression {
Expand All @@ -28,6 +31,18 @@ public class NamedExpression implements Expression {
/** Expression that being named. */
private final Expression delegated;

/** Optional alias. */
private String alias;

/**
* Get expression name using name or its alias (if it's present).
*
* @return expression name
*/
public String getNameOrAlias() {
return Strings.isNullOrEmpty(alias) ? name : alias;
}

@Override
public ExprValue valueOf(Environment<Expression, ExprValue> valueEnv) {
return delegated.valueOf(valueEnv);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* Aggregator is not well fit into Expression, because it has side effect. But we still want to make
* it implement {@link Expression} interface to make {@link ExpressionAnalyzer} easier.
*/
@EqualsAndHashCode(callSuper = false)
@EqualsAndHashCode
@RequiredArgsConstructor
public abstract class Aggregator<S extends AggregationState>
implements FunctionImplementation, Expression {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ public ExprValue next() {
ExprValue exprValue = expr.valueOf(inputValue.bindingTuples());
Optional<NamedExpression> optionalParseExpression =
namedParseExpressions.stream()
.filter(parseExpr -> parseExpr.getName().equals(expr.getName()))
.filter(parseExpr -> parseExpr.getNameOrAlias().equals(expr.getNameOrAlias()))
.findFirst();
if (optionalParseExpression.isEmpty()) {
mapBuilder.put(expr.getName(), exprValue);
mapBuilder.put(expr.getNameOrAlias(), exprValue);
continue;
}

Expand All @@ -77,13 +77,13 @@ public ExprValue next() {
// source field will be missing after stats command, read from inputValue if it exists
// otherwise do nothing since it should not appear as a field
ExprValue tupleValue =
ExprValueUtils.getTupleValue(inputValue).get(parseExpression.getName());
ExprValueUtils.getTupleValue(inputValue).get(parseExpression.getNameOrAlias());
if (tupleValue != null) {
mapBuilder.put(parseExpression.getName(), tupleValue);
mapBuilder.put(parseExpression.getNameOrAlias(), tupleValue);
}
} else {
ExprValue parsedValue = parseExpression.valueOf(inputValue.bindingTuples());
mapBuilder.put(parseExpression.getName(), parsedValue);
mapBuilder.put(parseExpression.getNameOrAlias(), parsedValue);
}
}
return ExprTupleValue.fromExprValueMap(mapBuilder.build());
Expand All @@ -95,8 +95,7 @@ public ExecutionEngine.Schema schema() {
getProjectList().stream()
.map(
expr -> // the column name is the delegated expression string from NamedExpression
new ExecutionEngine.Schema.Column(
expr.getDelegated().toString(), expr.getName(), expr.type()))
new ExecutionEngine.Schema.Column(expr.getName(), expr.getAlias(), expr.type()))
.collect(Collectors.toList()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public List<ExprValue> results() {
ImmutableList.Builder<ExprValue> builder = new ImmutableList.Builder<>();
for (ExprValue tuple : entry.getValue().results()) {
LinkedHashMap<String, ExprValue> tmp = new LinkedHashMap<>();
tmp.put(bucketExpr.getName(), entry.getKey());
tmp.put(bucketExpr.getNameOrAlias(), entry.getKey());
tmp.putAll(tuple.tupleValue());
builder.add(ExprTupleValue.fromExprValueMap(tmp));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ public void named_expression() {
@Test
public void named_expression_with_alias() {
assertAnalyzeEqual(
DSL.named("int", DSL.ref("integer_value", INTEGER)),
AstDSL.alias("int", AstDSL.qualifiedName("integer_value")));
DSL.named("integer_value", DSL.ref("integer_value", INTEGER), "int"),
AstDSL.alias("integer_value", AstDSL.qualifiedName("integer_value"), "int"));
}

@Test
public void field_name_with_qualifier() {
analysisContext.peek().define(new Symbol(Namespace.INDEX_NAME, "index_alias"), STRUCT);
assertAnalyzeEqual(
DSL.named("integer_alias.integer_value", DSL.ref("integer_value", INTEGER)),
DSL.named("integer_value", DSL.ref("integer_value", INTEGER)),
AstDSL.alias(
"integer_alias.integer_value", AstDSL.qualifiedName("index_alias", "integer_value")));
}
Expand All @@ -56,9 +56,9 @@ public void field_name_with_qualifier() {
public void field_name_with_qualifier_quoted() {
analysisContext.peek().define(new Symbol(Namespace.INDEX_NAME, "index_alias"), STRUCT);
assertAnalyzeEqual(
DSL.named("`integer_alias`.integer_value", DSL.ref("integer_value", INTEGER)),
DSL.named("integer_value", DSL.ref("integer_value", INTEGER)),
AstDSL.alias(
"`integer_alias`.integer_value", // qualifier in SELECT is quoted originally
"integer_value", // qualifier in SELECT is quoted originally
AstDSL.qualifiedName("index_alias", "integer_value")));
}

Expand All @@ -82,7 +82,6 @@ protected List<NamedExpression> analyze(UnresolvedExpression unresolvedExpressio

protected void assertAnalyzeEqual(
NamedExpression expected, UnresolvedExpression unresolvedExpression) {
List<NamedExpression> actual = analyze(unresolvedExpression);
assertEquals(Arrays.asList(expected), actual);
assertEquals(Arrays.asList(expected), analyze(unresolvedExpression));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public String toString() {
public int compareTo(ExprValue o) {
throw new RuntimeException();
}

@Override
public Object valueForCalcite() {
throw new UnsupportedOperationException("valueForCalcite not supported");
}
};
static final FunctionName SAMPLE_NAME = FunctionName.of("sample");
static final FunctionSignature SAMPLE_SIGNATURE_A =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,12 @@ public void project_schema() {
project(
inputPlan,
DSL.named("response", DSL.ref("response", INTEGER)),
DSL.named("act", DSL.ref("action", STRING)));
DSL.named("action", DSL.ref("action", STRING), "act"));

assertThat(
project.schema().getColumns(),
contains(
new ExecutionEngine.Schema.Column("response", "response", INTEGER),
new ExecutionEngine.Schema.Column("response", null, INTEGER),
new ExecutionEngine.Schema.Column("action", "act", STRING)));
}

Expand Down
10 changes: 5 additions & 5 deletions docs/user/general/identifiers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ The first example is to show a column name qualified by full table name original

os> SELECT city, accounts.age, ABS(accounts.balance) FROM accounts WHERE accounts.age < 30;
fetched rows / total rows = 1/1
+-------+-----+-----------------------+
| city | age | ABS(accounts.balance) |
|-------+-----+-----------------------|
| Nogal | 28 | 32838 |
+-------+-----+-----------------------+
+-------+-----+--------------+
Copy link
Collaborator Author

@penghuo penghuo Mar 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaking IT make sense to me, it is align with Spark-SQL response.

| city | age | ABS(balance) |
|-------+-----+--------------|
| Nogal | 28 | 32838 |
+-------+-----+--------------+

The second example is to show a field name qualified by index alias specified. Similarly, the alias qualifier is optional in this case::

Expand Down
5 changes: 4 additions & 1 deletion integ-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ apply plugin: 'java'
apply plugin: 'io.freefair.lombok'
apply plugin: 'com.wiredforcode.spawn'

String baseVersion = "2.17.0"
String baseVersion = "2.19.0"
String bwcVersion = baseVersion + ".0";
String baseName = "sqlBwcCluster"
String bwcFilePath = "src/test/resources/bwc/"
Expand Down Expand Up @@ -493,6 +493,9 @@ integTest {

// Exclude this IT, because they executed in another task (:integTestWithSecurity)
exclude 'org/opensearch/sql/security/**'

// TODO. Exclude Remote IT.
exclude 'org/opensearch/sql/calcite/remote/**'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exclude remote IT for CI, add back when shadowJar problem solved.

}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.calcite.remote;

import org.opensearch.sql.ppl.PPLPluginIT;
Expand Down
Loading
Loading