Skip to content

Commit

Permalink
[BugFix] Fix partition resolve bug for materialized view (#34880)
Browse files Browse the repository at this point in the history
Signed-off-by: shuming.li <ming.moriarty@gmail.com>
  • Loading branch information
LiShuMing authored Nov 14, 2023
1 parent 28ca296 commit b4c7c21
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 152 deletions.
135 changes: 0 additions & 135 deletions fe/fe-core/src/main/java/com/starrocks/sql/analyzer/AnalyzerUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,13 @@
import com.starrocks.sql.ast.QueryStatement;
import com.starrocks.sql.ast.RangePartitionDesc;
import com.starrocks.sql.ast.Relation;
import com.starrocks.sql.ast.SelectListItem;
import com.starrocks.sql.ast.SelectRelation;
import com.starrocks.sql.ast.SetOperationRelation;
import com.starrocks.sql.ast.SingleRangePartitionDesc;
import com.starrocks.sql.ast.StatementBase;
import com.starrocks.sql.ast.SubqueryRelation;
import com.starrocks.sql.ast.TableRelation;
import com.starrocks.sql.ast.UpdateStmt;
import com.starrocks.sql.ast.ValuesRelation;
import com.starrocks.sql.ast.ViewRelation;
import com.starrocks.sql.common.ErrorType;
import com.starrocks.sql.common.StarRocksPlannerException;
Expand Down Expand Up @@ -1341,138 +1339,6 @@ private static ColumnDef findPartitionDefByName(List<ColumnDef> columnDefs, Stri
return null;
}

private static class SlotRefResolverFactory {
public static class AstVisitors {
public AstVisitor<Expr, Relation> exprShuttle;
public AstVisitor<Expr, SlotRef> slotRefResolver;
}

public static AstVisitors createAstVisitors() {
AstVisitors visitors = new AstVisitors();

visitors.exprShuttle = new AstVisitor<Expr, Relation>() {
@Override
public Expr visitExpression(Expr expr, Relation node) {
expr = expr.clone();
for (int i = 0; i < expr.getChildren().size(); i++) {
Expr child = expr.getChild(i);
expr.setChild(i, child.accept(this, node));
}
return expr;
}

@Override
public Expr visitSlot(SlotRef slotRef, Relation node) {
String tableName = slotRef.getTblNameWithoutAnalyzed().getTbl();
if (node.getAlias() != null && !node.getAlias().getTbl().equalsIgnoreCase(tableName)) {
return slotRef;
}
return node.accept(visitors.slotRefResolver, slotRef);
}
};

visitors.slotRefResolver = new AstVisitor<Expr, SlotRef>() {
@Override
public Expr visitSelect(SelectRelation node, SlotRef slot) {
for (SelectListItem selectListItem : node.getSelectList().getItems()) {
TableName tableName = slot.getTblNameWithoutAnalyzed();
if (selectListItem.getAlias() == null) {
if (selectListItem.getExpr() instanceof SlotRef) {
SlotRef result = (SlotRef) selectListItem.getExpr();
if (result.getColumnName().equalsIgnoreCase(slot.getColumnName())
&& (tableName == null || tableName.equals(result.getTblNameWithoutAnalyzed()))) {
return selectListItem.getExpr().accept(visitors.exprShuttle, node.getRelation());
}
}
} else {
if (tableName != null && tableName.isFullyQualified()) {
continue;
}
if (selectListItem.getAlias().equalsIgnoreCase(slot.getColumnName())) {
return selectListItem.getExpr().accept(visitors.exprShuttle, node.getRelation());
}
}
}
return node.getRelation().accept(this, slot);
}
@Override
public Expr visitSubquery(SubqueryRelation node, SlotRef slot) {
String tableName = slot.getTblNameWithoutAnalyzed().getTbl();
if (!node.getAlias().getTbl().equalsIgnoreCase(tableName)) {
return null;
}
slot = (SlotRef) slot.clone();
slot.setTblName(null); //clear table name here, not check it inside
return node.getQueryStatement().getQueryRelation().accept(this, slot);
}
@Override
public Expr visitTable(TableRelation node, SlotRef slot) {
TableName tableName = slot.getTblNameWithoutAnalyzed();
if (node.getName().equals(tableName)) {
return slot;
}
if (tableName != null && !node.getResolveTableName().equals(tableName)) {
return null;
}
slot = (SlotRef) slot.clone();
slot.setTblName(node.getName());
return slot;
}
@Override
public Expr visitView(ViewRelation node, SlotRef slot) {
TableName tableName = slot.getTblNameWithoutAnalyzed();
if (tableName != null && !node.getResolveTableName().equals(tableName)) {
return null;
}
slot = (SlotRef) slot.clone();
slot.setTblName(null); //clear table name here, not check it inside
return node.getQueryStatement().getQueryRelation().accept(this, slot);
}
@Override
public Expr visitJoin(JoinRelation node, SlotRef slot) {
Relation leftRelation = node.getLeft();
Expr leftExpr = leftRelation.accept(this, slot);
if (leftExpr != null) {
return leftExpr;
}
Relation rightRelation = node.getRight();
Expr rightExpr = rightRelation.accept(this, slot);
if (rightExpr != null) {
return rightExpr;
}
return null;
}

@Override
public Expr visitSetOp(SetOperationRelation node, SlotRef slot) {
for (Relation relation : node.getRelations()) {
Expr resolved = relation.accept(this, slot);
if (resolved != null) {
return resolved;
}
}
return null;
}

@Override
public SlotRef visitValues(ValuesRelation node, SlotRef slot) {
return null;
}
};

return visitors;
}
}

public static Expr resolveSlotRef(SlotRef slotRef, QueryStatement queryStatement) {
SlotRefResolverFactory.AstVisitors visitors = SlotRefResolverFactory.createAstVisitors();
return queryStatement.getQueryRelation().accept(visitors.slotRefResolver, slotRef);
}
public static Expr resolveExpr(Expr expr, QueryStatement queryStatement) {
SlotRefResolverFactory.AstVisitors visitors = SlotRefResolverFactory.createAstVisitors();
return expr.accept(visitors.exprShuttle, queryStatement.getQueryRelation());
}

public static boolean containsIgnoreCase(List<String> list, String soughtFor) {
for (String current : list) {
if (current.equalsIgnoreCase(soughtFor)) {
Expand Down Expand Up @@ -1502,5 +1368,4 @@ public static Expr renameSlotRef(Expr expr, String newColName) {
}
return expr;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ private boolean isValidPartitionExpr(Expr partitionExpr) {

private void checkPartitionColumnExprs(CreateMaterializedViewStatement statement,
Map<Column, Expr> columnExprMap,
ConnectContext connectContext) {
ConnectContext connectContext) throws SemanticException {
ExpressionPartitionDesc expressionPartitionDesc = statement.getPartitionExpDesc();
Column partitionColumn = statement.getPartitionColumn();

Expand All @@ -622,7 +622,8 @@ private void checkPartitionColumnExprs(CreateMaterializedViewStatement statement
partitionColumnExpr = resolvePartitionExpr(partitionColumnExpr, connectContext, statement.getQueryStatement());
} catch (Exception e) {
LOG.warn("resolve partition column failed", e);
throw new SemanticException("resolve partition column failed", statement.getPartitionExpDesc().getPos());
throw new SemanticException("Resolve partition column failed:" + e.getMessage(),
statement.getPartitionExpDesc().getPos());
}

if (expressionPartitionDesc.isFunction()) {
Expand Down Expand Up @@ -672,7 +673,7 @@ private void checkPartitionColumnExprs(CreateMaterializedViewStatement statement
private Expr resolvePartitionExpr(Expr partitionColumnExpr,
ConnectContext connectContext,
QueryStatement queryStatement) {
Expr expr = AnalyzerUtils.resolveExpr(partitionColumnExpr, queryStatement);
Expr expr = SlotRefResolver.resolveExpr(partitionColumnExpr, queryStatement);
SlotRef slot;
if (expr instanceof SlotRef) {
slot = (SlotRef) expr;
Expand Down Expand Up @@ -709,11 +710,11 @@ private void checkPartitionExpPatterns(CreateMaterializedViewStatement statement
MaterializedViewPartitionFunctionChecker.FN_NAME_TO_PATTERN.get(functionName);
if (checkPartitionFunction == null) {
throw new SemanticException("Materialized view partition function " +
functionName + " is not support", functionCallExpr.getPos());
functionName + " is not support: " + expr.toSqlWithoutTbl(), functionCallExpr.getPos());
}
if (!checkPartitionFunction.check(functionCallExpr)) {
throw new SemanticException("Materialized view partition function " +
functionName + " check failed", functionCallExpr.getPos());
functionName + " check failed: " + expr.toSqlWithoutTbl(), functionCallExpr.getPos());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Copyright 2021-present StarRocks, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.starrocks.sql.analyzer;

import com.starrocks.analysis.Expr;
import com.starrocks.analysis.SlotRef;
import com.starrocks.analysis.TableName;
import com.starrocks.sql.ast.AstVisitor;
import com.starrocks.sql.ast.FieldReference;
import com.starrocks.sql.ast.JoinRelation;
import com.starrocks.sql.ast.QueryStatement;
import com.starrocks.sql.ast.Relation;
import com.starrocks.sql.ast.SelectListItem;
import com.starrocks.sql.ast.SelectRelation;
import com.starrocks.sql.ast.SetOperationRelation;
import com.starrocks.sql.ast.SubqueryRelation;
import com.starrocks.sql.ast.TableRelation;
import com.starrocks.sql.ast.ValuesRelation;
import com.starrocks.sql.ast.ViewRelation;

/**
* Resolve the expression(eg: partition column expression) through join/sub-query/view/set operator fo find the slot ref
* which it comes from.
*/
public class SlotRefResolver {
private static final AstVisitor<Expr, Relation> EXPR_SHUTTLE = new AstVisitor<Expr, Relation>() {
@Override
public Expr visitExpression(Expr expr, Relation node) {
expr = expr.clone();
for (int i = 0; i < expr.getChildren().size(); i++) {
Expr child = expr.getChild(i);
expr.setChild(i, child.accept(this, node));
}
return expr;
}

@Override
public Expr visitSlot(SlotRef slotRef, Relation node) {
String tableName = slotRef.getTblNameWithoutAnalyzed().getTbl();
if (node.getAlias() != null && !node.getAlias().getTbl().equalsIgnoreCase(tableName)) {
return slotRef;
}
return node.accept(SLOT_REF_RESOLVER, slotRef);
}

@Override
public Expr visitFieldReference(FieldReference fieldReference, Relation node) {
Field field = node.getScope().getRelationFields()
.getFieldByIndex(fieldReference.getFieldIndex());
return new SlotRef(field.getRelationAlias(), field.getName());
}
};

private static final AstVisitor<Expr, SlotRef> SLOT_REF_RESOLVER = new AstVisitor<Expr, SlotRef>() {
@Override
public Expr visitSelect(SelectRelation node, SlotRef slot) {
for (SelectListItem selectListItem : node.getSelectList().getItems()) {
TableName tableName = slot.getTblNameWithoutAnalyzed();
if (selectListItem.getAlias() == null) {
if (selectListItem.getExpr() instanceof SlotRef) {
SlotRef result = (SlotRef) selectListItem.getExpr();
if (result.getColumnName().equalsIgnoreCase(slot.getColumnName())
&& (tableName == null || tableName.equals(result.getTblNameWithoutAnalyzed()))) {
return selectListItem.getExpr().accept(EXPR_SHUTTLE, node.getRelation());
}
}
} else {
if (tableName != null && tableName.isFullyQualified()) {
continue;
}
if (selectListItem.getAlias().equalsIgnoreCase(slot.getColumnName())) {
return selectListItem.getExpr().accept(EXPR_SHUTTLE, node.getRelation());
}
}
}
return node.getRelation().accept(this, slot);
}

@Override
public Expr visitSubquery(SubqueryRelation node, SlotRef slot) {
String tableName = slot.getTblNameWithoutAnalyzed().getTbl();
if (!node.getAlias().getTbl().equalsIgnoreCase(tableName)) {
return null;
}
slot = (SlotRef) slot.clone();
slot.setTblName(null); //clear table name here, not check it inside
return node.getQueryStatement().getQueryRelation().accept(this, slot);
}

@Override
public Expr visitTable(TableRelation node, SlotRef slot) {
TableName tableName = slot.getTblNameWithoutAnalyzed();
if (node.getName().equals(tableName)) {
return slot;
}
if (tableName != null && !node.getResolveTableName().equals(tableName)) {
return null;
}
slot = (SlotRef) slot.clone();
slot.setTblName(node.getName());
return slot;
}

@Override
public Expr visitView(ViewRelation node, SlotRef slot) {
TableName tableName = slot.getTblNameWithoutAnalyzed();
if (tableName != null && !node.getResolveTableName().equals(tableName)) {
return null;
}
slot = (SlotRef) slot.clone();
slot.setTblName(null); //clear table name here, not check it inside
return node.getQueryStatement().getQueryRelation().accept(this, slot);
}

@Override
public Expr visitJoin(JoinRelation node, SlotRef slot) {
Relation leftRelation = node.getLeft();
Expr leftExpr = leftRelation.accept(this, slot);
if (leftExpr != null) {
return leftExpr;
}
Relation rightRelation = node.getRight();
Expr rightExpr = rightRelation.accept(this, slot);
if (rightExpr != null) {
return rightExpr;
}
return null;
}

@Override
public Expr visitSetOp(SetOperationRelation node, SlotRef slot) {
for (Relation relation : node.getRelations()) {
Expr resolved = relation.accept(this, slot);
if (resolved != null) {
return resolved;
}
}
return null;
}

@Override
public SlotRef visitValues(ValuesRelation node, SlotRef slot) {
return null;
}
};

/**
* Resolve the expression through join/sub-query/view/set operator fo find the slot ref
* which it comes from.
*/
public static Expr resolveExpr(Expr expr, QueryStatement queryStatement) {
return expr.accept(EXPR_SHUTTLE, queryStatement.getQueryRelation());
}
}
Loading

0 comments on commit b4c7c21

Please sign in to comment.