Skip to content
Open
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 @@ -367,6 +367,74 @@ private enum UpdateKind {
MERGE,
}

private static final class SelectAnalysis {
private final List<Expression> outputExpressions;
private final List<SelectAlias> aliases;

private SelectAnalysis(List<Expression> outputExpressions, List<SelectAlias> aliases) {
this.outputExpressions =
ImmutableList.copyOf(requireNonNull(outputExpressions, "outputExpressions is null"));
this.aliases = ImmutableList.copyOf(requireNonNull(aliases, "aliases is null"));
}

private List<Expression> getOutputExpressions() {
return outputExpressions;
}

private List<SelectAlias> getAliases() {
return aliases;
}
}

private static final class SelectAlias {
private final String canonicalName;
private final Expression expression;
private final int position;

private SelectAlias(String canonicalName, Expression expression, int position) {
this.canonicalName = requireNonNull(canonicalName, "canonicalName is null");
this.expression = requireNonNull(expression, "expression is null");
this.position = position;
}

private String getCanonicalName() {
return canonicalName;
}

private Expression getExpression() {
return expression;
}

private int getPosition() {
return position;
}
}

private static boolean resolvesToInputColumn(Scope scope, Identifier identifier) {
return scope
.tryResolveField(identifier, QualifiedName.of(identifier.getValue()))
.filter(ResolvedField::isLocal)
.isPresent();
}

private static Optional<SelectAlias> resolveSelectAlias(
Identifier identifier, List<SelectAlias> aliases) {
List<SelectAlias> matches =
aliases.stream()
.filter(alias -> alias.getCanonicalName().equals(identifier.getCanonicalValue()))
.collect(toImmutableList());
if (matches.size() > 1) {
throw new SemanticException(
String.format(
"Column alias '%s' is ambiguous at positions %s",
identifier.getValue(),
matches.stream()
.map(alias -> Integer.toString(alias.getPosition()))
.collect(Collectors.joining(", "))));
}
return matches.stream().findFirst();
}

/**
* Visitor context represents local query scope (if exists). The invariant is that the local query
* scopes hierarchy should always have outer query scope (if provided) as ancestor.
Expand Down Expand Up @@ -901,7 +969,12 @@ public Scope visitQuery(Query node, Optional<Scope> context) {
List<Expression> orderByExpressions = emptyList();
if (node.getOrderBy().isPresent()) {
orderByExpressions =
analyzeOrderBy(node, getSortItemsFromOrderBy(node.getOrderBy()), queryBodyScope);
analyzeOrderBy(
node,
getSortItemsFromOrderBy(node.getOrderBy()),
queryBodyScope,
queryBodyScope,
emptyList());

if ((queryBodyScope.getOuterQueryParent().isPresent() || !isTopLevel)
&& !node.getLimit().isPresent()
Expand Down Expand Up @@ -1193,9 +1266,10 @@ public Scope visitQuerySpecification(QuerySpecification node, Optional<Scope> sc

node.getWhere().ifPresent(where -> analyzeWhere(node, sourceScope, where));

List<Expression> outputExpressions = analyzeSelect(node, sourceScope);
SelectAnalysis selectAnalysis = analyzeSelect(node, sourceScope);
List<Expression> outputExpressions = selectAnalysis.getOutputExpressions();
Analysis.GroupingSetAnalysis groupByAnalysis =
analyzeGroupBy(node, sourceScope, outputExpressions);
analyzeGroupBy(node, sourceScope, outputExpressions, selectAnalysis.getAliases());
analyzeHaving(node, sourceScope);

Scope outputScope = computeAndAssignOutputScope(node, scope, sourceScope);
Expand All @@ -1213,7 +1287,13 @@ public Scope visitQuerySpecification(QuerySpecification node, Optional<Scope> sc
OrderBy orderBy = node.getOrderBy().get();
orderByScope = Optional.of(computeAndAssignOrderByScope(orderBy, sourceScope, outputScope));

orderByExpressions = analyzeOrderBy(node, orderBy.getSortItems(), orderByScope.get());
orderByExpressions =
analyzeOrderBy(
node,
orderBy.getSortItems(),
sourceScope,
orderByScope.get(),
selectAnalysis.getAliases());

if ((sourceScope.getOuterQueryParent().isPresent() || !isTopLevel)
&& !node.getLimit().isPresent()
Expand Down Expand Up @@ -1569,15 +1649,18 @@ private void analyzeWhere(Node node, Scope scope, Expression predicate) {
analysis.setWhere(node, predicate);
}

private List<Expression> analyzeSelect(QuerySpecification node, Scope scope) {
private SelectAnalysis analyzeSelect(QuerySpecification node, Scope scope) {
ImmutableList.Builder<Expression> outputExpressionBuilder = ImmutableList.builder();
ImmutableList.Builder<Analysis.SelectExpression> selectExpressionBuilder =
ImmutableList.builder();
ImmutableList.Builder<SelectAlias> selectAliasBuilder = ImmutableList.builder();

int outputPosition = 1;
for (SelectItem item : node.getSelect().getSelectItems()) {
if (item instanceof AllColumns) {
analyzeSelectAllColumns(
(AllColumns) item, node, scope, outputExpressionBuilder, selectExpressionBuilder);
outputPosition +=
analyzeSelectAllColumns(
(AllColumns) item, node, scope, outputExpressionBuilder, selectExpressionBuilder);
} else if (item instanceof SingleColumn) {
SingleColumn singleColumn = (SingleColumn) item;
Expression selectExpression = singleColumn.getExpression();
Expand All @@ -1594,10 +1677,17 @@ private List<Expression> analyzeSelect(QuerySpecification node, Scope scope) {
for (Expression expression : expandedExpressions) {
analyzeSelectSingleColumn(
expression, node, scope, outputExpressionBuilder, selectExpressionBuilder);
outputPosition++;
}
} else {
analyzeSelectSingleColumn(
selectExpression, node, scope, outputExpressionBuilder, selectExpressionBuilder);
if (singleColumn.getAlias().isPresent()) {
Identifier alias = singleColumn.getAlias().get();
selectAliasBuilder.add(
new SelectAlias(alias.getCanonicalValue(), selectExpression, outputPosition));
}
outputPosition++;
}
} else {
throw new IllegalArgumentException(
Expand All @@ -1610,7 +1700,7 @@ private List<Expression> analyzeSelect(QuerySpecification node, Scope scope) {
analysis.setContainsSelectDistinct();
}

return outputExpressionBuilder.build();
return new SelectAnalysis(outputExpressionBuilder.build(), selectAliasBuilder.build());
}

/**
Expand Down Expand Up @@ -2412,7 +2502,7 @@ public List<Expression> visitWhenClause(WhenClause node, Scope context) {
}
}

private void analyzeSelectAllColumns(
private int analyzeSelectAllColumns(
AllColumns allColumns,
QuerySpecification node,
Scope scope,
Expand Down Expand Up @@ -2458,7 +2548,7 @@ private void analyzeSelectAllColumns(
() ->
new NoSuchElementException(
DataNodeQueryMessages.NO_VALUE_PRESENT)));
analyzeAllColumnsFromTable(
return analyzeAllColumnsFromTable(
fields,
allColumns,
node,
Expand All @@ -2467,7 +2557,6 @@ private void analyzeSelectAllColumns(
selectExpressionBuilder,
relationType,
local);
return;
}
}
// identifierChainBasis.get().getBasisType == FIELD or target expression isn't a
Expand Down Expand Up @@ -2497,7 +2586,7 @@ private void analyzeSelectAllColumns(
DataNodeQueryMessages.SELECT_NOT_ALLOWED_FROM_RELATION_THAT_HAS_NO);
}

analyzeAllColumnsFromTable(
return analyzeAllColumnsFromTable(
fields,
allColumns,
node,
Expand Down Expand Up @@ -2550,7 +2639,7 @@ private List<Field> filterInaccessibleFields(List<Field> fields) {
return fields.stream().filter(accessibleFields.build()::contains).collect(toImmutableList());
}

private void analyzeAllColumnsFromTable(
private int analyzeAllColumnsFromTable(
List<Field> fields,
AllColumns allColumns,
QuerySpecification node,
Expand Down Expand Up @@ -2611,6 +2700,7 @@ private void analyzeAllColumnsFromTable(
}
}
analysis.setSelectAllResultFields(allColumns, itemOutputFieldBuilder.build());
return fields.size();
}

// private void analyzeAllFieldsFromRowTypeExpression(
Expand Down Expand Up @@ -2681,7 +2771,10 @@ private void analyzeSelectSingleColumn(
}

private Analysis.GroupingSetAnalysis analyzeGroupBy(
QuerySpecification node, Scope scope, List<Expression> outputExpressions) {
QuerySpecification node,
Scope scope,
List<Expression> outputExpressions,
List<SelectAlias> selectAliases) {
if (node.getGroupBy().isPresent()) {
ImmutableList.Builder<List<Set<FieldId>>> cubes = ImmutableList.builder();
ImmutableList.Builder<List<Set<FieldId>>> rollups = ImmutableList.builder();
Expand All @@ -2706,6 +2799,7 @@ private Analysis.GroupingSetAnalysis analyzeGroupBy(
column = outputExpressions.get(toIntExact(ordinal - 1));
verifyNoAggregateWindowOrGroupingFunctions(column, "GROUP BY clause");
} else {
column = resolveGroupBySelectAlias(column, scope, selectAliases);
verifyNoAggregateWindowOrGroupingFunctions(column, "GROUP BY clause");
analyzeExpression(column, scope);
}
Expand Down Expand Up @@ -2807,6 +2901,22 @@ private Analysis.GroupingSetAnalysis analyzeGroupBy(
return result;
}

private Expression resolveGroupBySelectAlias(
Expression expression, Scope scope, List<SelectAlias> selectAliases) {
if (!(expression instanceof Identifier)) {
return expression;
}

Identifier identifier = (Identifier) expression;
if (resolvesToInputColumn(scope, identifier)) {
return expression;
}

return resolveSelectAlias(identifier, selectAliases)
.map(SelectAlias::getExpression)
.orElse(expression);
}

private boolean isDateBinGapFill(Expression column) {
return column instanceof FunctionCall
&& DATE_BIN
Expand Down Expand Up @@ -4102,11 +4212,16 @@ private FieldReference getFieldReferenceForFillGroup(
}

private List<Expression> analyzeOrderBy(
Node node, List<SortItem> sortItems, Scope orderByScope) {
Node node,
List<SortItem> sortItems,
Scope sourceScope,
Scope orderByScope,
List<SelectAlias> selectAliases) {
ImmutableList.Builder<Expression> orderByFieldsBuilder = ImmutableList.builder();

for (SortItem item : sortItems) {
Expression expression = item.getSortKey();
Scope expressionScope = sourceScope;

if (expression instanceof LongLiteral) {
// this is an ordinal in the output tuple
Expand All @@ -4118,6 +4233,13 @@ private List<Expression> analyzeOrderBy(
}

expression = new FieldReference(toIntExact(ordinal - 1));
expressionScope = orderByScope;
} else {
Optional<SelectAlias> selectAlias = resolveOrderBySelectAlias(expression, selectAliases);
if (selectAlias.isPresent()) {
expression = new FieldReference(selectAlias.get().getPosition() - 1);
expressionScope = orderByScope;
}
}

ExpressionAnalysis expressionAnalysis =
Expand All @@ -4127,7 +4249,7 @@ private List<Expression> analyzeOrderBy(
sessionContext,
statementAnalyzerFactory,
accessControl,
orderByScope,
expressionScope,
analysis,
expression,
WarningCollector.NOOP,
Expand All @@ -4148,6 +4270,15 @@ private List<Expression> analyzeOrderBy(
return orderByFieldsBuilder.build();
}

private Optional<SelectAlias> resolveOrderBySelectAlias(
Expression expression, List<SelectAlias> selectAliases) {
if (!(expression instanceof Identifier)) {
return Optional.empty();
}

return resolveSelectAlias((Identifier) expression, selectAliases);
}

private void analyzeOffset(Offset node, Scope scope) {
long rowCount;
if (node.getRowCount() instanceof LongLiteral) {
Expand Down Expand Up @@ -5452,7 +5583,11 @@ private static boolean hasScopeAsLocalParent(Scope root, Scope parent) {
}

static void verifyNoAggregateWindowOrGroupingFunctions(Expression predicate, String clause) {
List<FunctionCall> aggregates = extractAggregateFunctions(ImmutableList.of(predicate));
List<FunctionCall> aggregates =
ImmutableList.<FunctionCall>builder()
.addAll(extractAggregateFunctions(ImmutableList.of(predicate)))
.addAll(extractWindowFunctions(ImmutableList.of(predicate)))
.build();

if (!aggregates.isEmpty()) {
throw new SemanticException(
Expand Down
Loading