[Feat](nereids) nereids add alter view (#33970)
nereids support alter view stmt. e.g. ALTER VIEW example_db.example_view ( c1 COMMENT "column 1", c2 COMMENT "column 2", c3 COMMENT "column 3" ) AS SELECT k1, k2, SUM(v1) FROM example_table GROUP BY k1, k2
This commit is contained in:
@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.doris.analysis;
|
||||
|
||||
import org.apache.doris.catalog.Column;
|
||||
import org.apache.doris.catalog.DatabaseIf;
|
||||
import org.apache.doris.catalog.Env;
|
||||
import org.apache.doris.catalog.TableIf;
|
||||
@ -77,6 +78,14 @@ public class AlterViewStmt extends BaseViewStmt {
|
||||
createColumnAndViewDefs(analyzer);
|
||||
}
|
||||
|
||||
public void setInlineViewDef(String querySql) {
|
||||
inlineViewDef = querySql;
|
||||
}
|
||||
|
||||
public void setFinalColumns(List<Column> columns) {
|
||||
finalCols.addAll(columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@ -48,6 +48,7 @@ import org.apache.doris.nereids.DorisParser.AggStateDataTypeContext;
|
||||
import org.apache.doris.nereids.DorisParser.AliasQueryContext;
|
||||
import org.apache.doris.nereids.DorisParser.AliasedQueryContext;
|
||||
import org.apache.doris.nereids.DorisParser.AlterMTMVContext;
|
||||
import org.apache.doris.nereids.DorisParser.AlterViewContext;
|
||||
import org.apache.doris.nereids.DorisParser.ArithmeticBinaryContext;
|
||||
import org.apache.doris.nereids.DorisParser.ArithmeticUnaryContext;
|
||||
import org.apache.doris.nereids.DorisParser.ArrayLiteralContext;
|
||||
@ -353,6 +354,7 @@ import org.apache.doris.nereids.trees.plans.algebra.Aggregate;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.SetOperation.Qualifier;
|
||||
import org.apache.doris.nereids.trees.plans.commands.AddConstraintCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.AlterMTMVCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.AlterViewCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.CallCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.CancelMTMVTaskCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.Command;
|
||||
@ -382,6 +384,7 @@ import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVInfo;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVPropertyInfo;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVRefreshInfo;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVRenameInfo;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.AlterViewInfo;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.BulkLoadDataDesc;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.BulkStorageDesc;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.CancelMTMVTaskInfo;
|
||||
@ -804,6 +807,16 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
|
||||
return new AlterMTMVCommand(alterMTMVInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogicalPlan visitAlterView(AlterViewContext ctx) {
|
||||
List<String> nameParts = visitMultipartIdentifier(ctx.name);
|
||||
LogicalPlan logicalPlan = visitQuery(ctx.query());
|
||||
String querySql = getOriginSql(ctx.query());
|
||||
AlterViewInfo info = new AlterViewInfo(new TableNameInfo(nameParts), logicalPlan, querySql,
|
||||
ctx.cols == null ? Lists.newArrayList() : visitSimpleColumnDefs(ctx.cols));
|
||||
return new AlterViewCommand(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogicalPlan visitShowConstraint(ShowConstraintContext ctx) {
|
||||
List<String> parts = visitMultipartIdentifier(ctx.table);
|
||||
|
||||
@ -151,5 +151,6 @@ public enum PlanType {
|
||||
DROP_PROCEDURE_COMMAND,
|
||||
SHOW_PROCEDURE_COMMAND,
|
||||
SHOW_CREATE_PROCEDURE_COMMAND,
|
||||
CREATE_VIEW_COMMAND
|
||||
CREATE_VIEW_COMMAND,
|
||||
ALTER_VIEW_COMMAND
|
||||
}
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you 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
|
||||
//
|
||||
// http://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 org.apache.doris.nereids.trees.plans.commands;
|
||||
|
||||
import org.apache.doris.analysis.AlterViewStmt;
|
||||
import org.apache.doris.catalog.Env;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.AlterViewInfo;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
import org.apache.doris.qe.StmtExecutor;
|
||||
|
||||
/**AlterViewCommand*/
|
||||
public class AlterViewCommand extends Command implements ForwardWithSync {
|
||||
private final AlterViewInfo alterViewInfo;
|
||||
|
||||
public AlterViewCommand(AlterViewInfo alterViewInfo) {
|
||||
super(PlanType.ALTER_VIEW_COMMAND);
|
||||
this.alterViewInfo = alterViewInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ConnectContext ctx, StmtExecutor executor) throws Exception {
|
||||
alterViewInfo.init(ctx);
|
||||
alterViewInfo.validate(ctx);
|
||||
AlterViewStmt alterViewStmt = alterViewInfo.translateToLegacyStmt(ctx);
|
||||
Env.getCurrentEnv().alterView(alterViewStmt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitAlterViewCommand(this, context);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you 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
|
||||
//
|
||||
// http://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 org.apache.doris.nereids.trees.plans.commands.info;
|
||||
|
||||
import org.apache.doris.analysis.AlterViewStmt;
|
||||
import org.apache.doris.analysis.ColWithComment;
|
||||
import org.apache.doris.analysis.TableName;
|
||||
import org.apache.doris.catalog.Column;
|
||||
import org.apache.doris.catalog.DatabaseIf;
|
||||
import org.apache.doris.catalog.Env;
|
||||
import org.apache.doris.catalog.TableIf;
|
||||
import org.apache.doris.catalog.View;
|
||||
import org.apache.doris.common.ErrorCode;
|
||||
import org.apache.doris.common.ErrorReport;
|
||||
import org.apache.doris.common.FeNameFormat;
|
||||
import org.apache.doris.common.UserException;
|
||||
import org.apache.doris.common.util.Util;
|
||||
import org.apache.doris.mysql.privilege.PrivPredicate;
|
||||
import org.apache.doris.nereids.NereidsPlanner;
|
||||
import org.apache.doris.nereids.analyzer.UnboundResultSink;
|
||||
import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.trees.expressions.Slot;
|
||||
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFileSink;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** AlterViewInfo */
|
||||
public class AlterViewInfo extends BaseViewInfo {
|
||||
/** constructor*/
|
||||
public AlterViewInfo(TableNameInfo viewName, LogicalPlan logicalQuery,
|
||||
String querySql, List<SimpleColumnDefinition> simpleColumnDefinitions) {
|
||||
super(viewName, logicalQuery, querySql, simpleColumnDefinitions);
|
||||
if (logicalQuery instanceof LogicalFileSink) {
|
||||
throw new AnalysisException("Not support OUTFILE clause in ALTER VIEW statement");
|
||||
}
|
||||
}
|
||||
|
||||
/** init */
|
||||
public void init(ConnectContext ctx) throws UserException {
|
||||
if (viewName == null) {
|
||||
ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_TABLES_USED);
|
||||
}
|
||||
viewName.analyze(ctx);
|
||||
FeNameFormat.checkTableName(viewName.getTbl());
|
||||
// disallow external catalog
|
||||
Util.prohibitExternalCatalog(viewName.getCtl(), "AlterViewStmt");
|
||||
|
||||
DatabaseIf db = Env.getCurrentInternalCatalog().getDbOrAnalysisException(viewName.getDb());
|
||||
TableIf table = db.getTableOrAnalysisException(viewName.getTbl());
|
||||
if (!(table instanceof View)) {
|
||||
throw new org.apache.doris.common.AnalysisException(
|
||||
String.format("ALTER VIEW not allowed on a table:%s.%s", viewName.getDb(), viewName.getTbl()));
|
||||
}
|
||||
|
||||
// check privilege
|
||||
if (!Env.getCurrentEnv().getAccessManager().checkTblPriv(ctx, new TableName(viewName.getCtl(), viewName.getDb(),
|
||||
viewName.getTbl()), PrivPredicate.ALTER)) {
|
||||
ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLE_ACCESS_DENIED_ERROR,
|
||||
PrivPredicate.ALTER.getPrivs().toString(), viewName.getTbl());
|
||||
}
|
||||
analyzeAndFillRewriteSqlMap(querySql, ctx);
|
||||
OutermostPlanFinderContext outermostPlanFinderContext = new OutermostPlanFinderContext();
|
||||
analyzedPlan.accept(OutermostPlanFinder.INSTANCE, outermostPlanFinderContext);
|
||||
List<Slot> outputs = outermostPlanFinderContext.outermostPlan.getOutput();
|
||||
createFinalCols(outputs);
|
||||
}
|
||||
|
||||
/**validate*/
|
||||
public void validate(ConnectContext ctx) throws UserException {
|
||||
NereidsPlanner planner = new NereidsPlanner(ctx.getStatementContext());
|
||||
planner.plan(new UnboundResultSink<>(logicalQuery), PhysicalProperties.ANY, ExplainLevel.NONE);
|
||||
Set<String> colSets = Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
|
||||
for (Column col : finalCols) {
|
||||
if (!colSets.add(col.getName())) {
|
||||
ErrorReport.reportAnalysisException(ErrorCode.ERR_DUP_FIELDNAME, col.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**translateToLegacyStmt*/
|
||||
public AlterViewStmt translateToLegacyStmt(ConnectContext ctx) {
|
||||
List<ColWithComment> cols = Lists.newArrayList();
|
||||
for (SimpleColumnDefinition def : simpleColumnDefinitions) {
|
||||
cols.add(def.translateToColWithComment());
|
||||
}
|
||||
AlterViewStmt alterViewStmt = new AlterViewStmt(viewName.transferToTableName(), cols,
|
||||
null);
|
||||
// expand star(*) in project list and replace table name with qualifier
|
||||
String rewrittenSql = rewriteSql(ctx.getStatementContext().getIndexInSqlToString());
|
||||
|
||||
// rewrite project alias
|
||||
rewrittenSql = rewriteProjectsToUserDefineAlias(rewrittenSql);
|
||||
|
||||
alterViewStmt.setInlineViewDef(rewrittenSql);
|
||||
alterViewStmt.setFinalColumns(finalCols);
|
||||
return alterViewStmt;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,261 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you 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
|
||||
//
|
||||
// http://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 org.apache.doris.nereids.trees.plans.commands.info;
|
||||
|
||||
import org.apache.doris.catalog.Column;
|
||||
import org.apache.doris.common.ErrorCode;
|
||||
import org.apache.doris.common.ErrorReport;
|
||||
import org.apache.doris.common.Pair;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.DorisParser;
|
||||
import org.apache.doris.nereids.DorisParser.NamedExpressionContext;
|
||||
import org.apache.doris.nereids.DorisParser.NamedExpressionSeqContext;
|
||||
import org.apache.doris.nereids.DorisParserBaseVisitor;
|
||||
import org.apache.doris.nereids.StatementContext;
|
||||
import org.apache.doris.nereids.analyzer.UnboundResultSink;
|
||||
import org.apache.doris.nereids.jobs.executor.AbstractBatchJobExecutor;
|
||||
import org.apache.doris.nereids.jobs.rewrite.RewriteJob;
|
||||
import org.apache.doris.nereids.parser.NereidsParser;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.rules.analysis.AnalyzeCTE;
|
||||
import org.apache.doris.nereids.rules.analysis.BindExpression;
|
||||
import org.apache.doris.nereids.rules.analysis.BindRelation;
|
||||
import org.apache.doris.nereids.rules.analysis.CheckPolicy;
|
||||
import org.apache.doris.nereids.rules.analysis.EliminateLogicalSelectHint;
|
||||
import org.apache.doris.nereids.trees.expressions.Slot;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalCTEAnchor;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalCTEProducer;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.RuleNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** BaseViewInfo */
|
||||
public class BaseViewInfo {
|
||||
protected final TableNameInfo viewName;
|
||||
protected final LogicalPlan logicalQuery;
|
||||
protected final String querySql;
|
||||
protected final List<SimpleColumnDefinition> simpleColumnDefinitions;
|
||||
protected final List<Column> finalCols = Lists.newArrayList();
|
||||
protected Plan analyzedPlan;
|
||||
|
||||
public BaseViewInfo(TableNameInfo viewName, LogicalPlan logicalQuery,
|
||||
String querySql, List<SimpleColumnDefinition> simpleColumnDefinitions) {
|
||||
this.viewName = viewName;
|
||||
this.logicalQuery = logicalQuery;
|
||||
this.querySql = querySql;
|
||||
this.simpleColumnDefinitions = simpleColumnDefinitions;
|
||||
}
|
||||
|
||||
protected void analyzeAndFillRewriteSqlMap(String sql, ConnectContext ctx) {
|
||||
StatementContext stmtCtx = ctx.getStatementContext();
|
||||
LogicalPlan parsedViewPlan = new NereidsParser().parseForCreateView(sql);
|
||||
if (parsedViewPlan instanceof UnboundResultSink) {
|
||||
parsedViewPlan = (LogicalPlan) ((UnboundResultSink<?>) parsedViewPlan).child();
|
||||
}
|
||||
CascadesContext viewContextForStar = CascadesContext.initContext(
|
||||
stmtCtx, parsedViewPlan, PhysicalProperties.ANY);
|
||||
AnalyzerForCreateView analyzerForStar = new AnalyzerForCreateView(viewContextForStar);
|
||||
analyzerForStar.analyze();
|
||||
analyzedPlan = viewContextForStar.getRewritePlan();
|
||||
}
|
||||
|
||||
protected String rewriteSql(Map<Pair<Integer, Integer>, String> indexStringSqlMap) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int beg = 0;
|
||||
for (Map.Entry<Pair<Integer, Integer>, String> entry : indexStringSqlMap.entrySet()) {
|
||||
Pair<Integer, Integer> index = entry.getKey();
|
||||
builder.append(querySql, beg, index.first);
|
||||
builder.append(entry.getValue());
|
||||
beg = index.second + 1;
|
||||
}
|
||||
builder.append(querySql, beg, querySql.length());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected String rewriteProjectsToUserDefineAlias(String resSql) {
|
||||
IndexFinder finder = new IndexFinder();
|
||||
ParserRuleContext tree = NereidsParser.toAst(resSql, DorisParser::singleStatement);
|
||||
finder.visit(tree);
|
||||
if (simpleColumnDefinitions.isEmpty()) {
|
||||
return resSql;
|
||||
}
|
||||
List<NamedExpressionContext> namedExpressionContexts = finder.getNamedExpressionContexts();
|
||||
StringBuilder replaceWithColsBuilder = new StringBuilder();
|
||||
for (int i = 0; i < namedExpressionContexts.size(); ++i) {
|
||||
NamedExpressionContext namedExpressionContext = namedExpressionContexts.get(i);
|
||||
int start = namedExpressionContext.expression().start.getStartIndex();
|
||||
int stop = namedExpressionContext.expression().stop.getStopIndex();
|
||||
replaceWithColsBuilder.append(resSql, start, stop + 1);
|
||||
replaceWithColsBuilder.append(" AS `");
|
||||
String escapeBacktick = finalCols.get(i).getName().replace("`", "``");
|
||||
replaceWithColsBuilder.append(escapeBacktick);
|
||||
replaceWithColsBuilder.append('`');
|
||||
if (i != namedExpressionContexts.size() - 1) {
|
||||
replaceWithColsBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
String replaceWithCols = replaceWithColsBuilder.toString();
|
||||
return StringUtils.overlay(resSql, replaceWithCols, finder.getIndex().first,
|
||||
finder.getIndex().second + 1);
|
||||
}
|
||||
|
||||
protected void createFinalCols(List<Slot> outputs) throws org.apache.doris.common.AnalysisException {
|
||||
if (simpleColumnDefinitions.isEmpty()) {
|
||||
for (Slot output : outputs) {
|
||||
Column column = new Column(output.getName(), output.getDataType().toCatalogDataType());
|
||||
finalCols.add(column);
|
||||
}
|
||||
} else {
|
||||
if (outputs.size() != simpleColumnDefinitions.size()) {
|
||||
ErrorReport.reportAnalysisException(ErrorCode.ERR_VIEW_WRONG_LIST);
|
||||
}
|
||||
for (int i = 0; i < simpleColumnDefinitions.size(); ++i) {
|
||||
Column column = new Column(simpleColumnDefinitions.get(i).getName(),
|
||||
outputs.get(i).getDataType().toCatalogDataType());
|
||||
column.setComment(simpleColumnDefinitions.get(i).getComment());
|
||||
finalCols.add(column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**OutermostPlanFinderContext*/
|
||||
protected static class OutermostPlanFinderContext {
|
||||
public Plan outermostPlan = null;
|
||||
public boolean found = false;
|
||||
}
|
||||
|
||||
/**OutermostPlanFinder*/
|
||||
protected static class OutermostPlanFinder extends
|
||||
DefaultPlanVisitor<Void, OutermostPlanFinderContext> {
|
||||
public static final OutermostPlanFinder INSTANCE = new OutermostPlanFinder();
|
||||
|
||||
@Override
|
||||
public Void visit(Plan plan, OutermostPlanFinderContext ctx) {
|
||||
if (ctx.found) {
|
||||
return null;
|
||||
}
|
||||
ctx.outermostPlan = plan;
|
||||
ctx.found = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLogicalCTEAnchor(LogicalCTEAnchor<? extends Plan, ? extends Plan> cteAnchor,
|
||||
OutermostPlanFinderContext ctx) {
|
||||
if (ctx.found) {
|
||||
return null;
|
||||
}
|
||||
return super.visit(cteAnchor, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLogicalCTEProducer(LogicalCTEProducer<? extends Plan> cteProducer,
|
||||
OutermostPlanFinderContext ctx) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** traverse ast to find the outermost project list location information in sql*/
|
||||
protected static class IndexFinder extends DorisParserBaseVisitor<Void> {
|
||||
private boolean found = false;
|
||||
private int startIndex;
|
||||
private int stopIndex;
|
||||
private List<NamedExpressionContext> namedExpressionContexts = Lists.newArrayList();
|
||||
|
||||
@Override
|
||||
public Void visitChildren(RuleNode node) {
|
||||
if (found) {
|
||||
return null;
|
||||
}
|
||||
int n = node.getChildCount();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
ParseTree c = node.getChild(i);
|
||||
c.accept(this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitCte(DorisParser.CteContext ctx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitSelectColumnClause(DorisParser.SelectColumnClauseContext ctx) {
|
||||
if (found) {
|
||||
return null;
|
||||
}
|
||||
startIndex = ctx.getStart().getStartIndex();
|
||||
stopIndex = ctx.getStop().getStopIndex();
|
||||
found = true;
|
||||
|
||||
NamedExpressionSeqContext namedExpressionSeqContext = ctx.namedExpressionSeq();
|
||||
namedExpressionContexts = namedExpressionSeqContext.namedExpression();
|
||||
return null;
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getIndex() {
|
||||
return Pair.of(startIndex, stopIndex);
|
||||
}
|
||||
|
||||
public List<NamedExpressionContext> getNamedExpressionContexts() {
|
||||
return namedExpressionContexts;
|
||||
}
|
||||
}
|
||||
|
||||
/**AnalyzerForCreateView*/
|
||||
protected static class AnalyzerForCreateView extends AbstractBatchJobExecutor {
|
||||
private final List<RewriteJob> jobs;
|
||||
|
||||
public AnalyzerForCreateView(CascadesContext cascadesContext) {
|
||||
super(cascadesContext);
|
||||
jobs = buildAnalyzeViewJobsForStar();
|
||||
}
|
||||
|
||||
public void analyze() {
|
||||
execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RewriteJob> getJobs() {
|
||||
return jobs;
|
||||
}
|
||||
|
||||
private static List<RewriteJob> buildAnalyzeViewJobsForStar() {
|
||||
return jobs(
|
||||
topDown(new EliminateLogicalSelectHint()),
|
||||
topDown(new AnalyzeCTE()),
|
||||
bottomUp(
|
||||
new BindRelation(),
|
||||
new CheckPolicy(),
|
||||
new BindExpression()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,74 +25,41 @@ import org.apache.doris.catalog.Env;
|
||||
import org.apache.doris.common.ErrorCode;
|
||||
import org.apache.doris.common.ErrorReport;
|
||||
import org.apache.doris.common.FeNameFormat;
|
||||
import org.apache.doris.common.Pair;
|
||||
import org.apache.doris.common.UserException;
|
||||
import org.apache.doris.common.util.Util;
|
||||
import org.apache.doris.mysql.privilege.PrivPredicate;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.DorisParser;
|
||||
import org.apache.doris.nereids.DorisParser.NamedExpressionContext;
|
||||
import org.apache.doris.nereids.DorisParser.NamedExpressionSeqContext;
|
||||
import org.apache.doris.nereids.DorisParserBaseVisitor;
|
||||
import org.apache.doris.nereids.NereidsPlanner;
|
||||
import org.apache.doris.nereids.StatementContext;
|
||||
import org.apache.doris.nereids.analyzer.UnboundResultSink;
|
||||
import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
import org.apache.doris.nereids.jobs.executor.AbstractBatchJobExecutor;
|
||||
import org.apache.doris.nereids.jobs.rewrite.RewriteJob;
|
||||
import org.apache.doris.nereids.parser.NereidsParser;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.rules.analysis.AnalyzeCTE;
|
||||
import org.apache.doris.nereids.rules.analysis.BindExpression;
|
||||
import org.apache.doris.nereids.rules.analysis.BindRelation;
|
||||
import org.apache.doris.nereids.rules.analysis.CheckPolicy;
|
||||
import org.apache.doris.nereids.rules.analysis.EliminateLogicalSelectHint;
|
||||
import org.apache.doris.nereids.trees.expressions.Slot;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalCTEAnchor;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalCTEProducer;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFileSink;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.RuleNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* CreateViewInfo
|
||||
*/
|
||||
public class CreateViewInfo {
|
||||
public class CreateViewInfo extends BaseViewInfo {
|
||||
private final boolean ifNotExists;
|
||||
private final TableNameInfo viewName;
|
||||
private final String comment;
|
||||
private final LogicalPlan logicalQuery;
|
||||
private final String querySql;
|
||||
private final List<SimpleColumnDefinition> simpleColumnDefinitions;
|
||||
private final List<Column> finalCols = Lists.newArrayList();
|
||||
private Plan analyzedPlan;
|
||||
|
||||
/** constructor*/
|
||||
public CreateViewInfo(boolean ifNotExists, TableNameInfo viewName, String comment, LogicalPlan logicalQuery,
|
||||
String querySql, List<SimpleColumnDefinition> simpleColumnDefinitions) {
|
||||
super(viewName, logicalQuery, querySql, simpleColumnDefinitions);
|
||||
this.ifNotExists = ifNotExists;
|
||||
this.viewName = viewName;
|
||||
this.comment = comment;
|
||||
if (logicalQuery instanceof LogicalFileSink) {
|
||||
throw new AnalysisException("Not support OUTFILE clause in CREATE VIEW statement");
|
||||
}
|
||||
this.logicalQuery = logicalQuery;
|
||||
this.querySql = querySql;
|
||||
this.simpleColumnDefinitions = simpleColumnDefinitions;
|
||||
}
|
||||
|
||||
/** init */
|
||||
@ -144,188 +111,4 @@ public class CreateViewInfo {
|
||||
createViewStmt.setFinalColumns(finalCols);
|
||||
return createViewStmt;
|
||||
}
|
||||
|
||||
private void analyzeAndFillRewriteSqlMap(String sql, ConnectContext ctx) {
|
||||
StatementContext stmtCtx = ctx.getStatementContext();
|
||||
LogicalPlan parsedViewPlan = new NereidsParser().parseForCreateView(sql);
|
||||
if (parsedViewPlan instanceof UnboundResultSink) {
|
||||
parsedViewPlan = (LogicalPlan) ((UnboundResultSink<?>) parsedViewPlan).child();
|
||||
}
|
||||
CascadesContext viewContextForStar = CascadesContext.initContext(
|
||||
stmtCtx, parsedViewPlan, PhysicalProperties.ANY);
|
||||
AnalyzerForCreateView analyzerForStar = new AnalyzerForCreateView(viewContextForStar);
|
||||
analyzerForStar.analyze();
|
||||
analyzedPlan = viewContextForStar.getRewritePlan();
|
||||
}
|
||||
|
||||
private String rewriteSql(Map<Pair<Integer, Integer>, String> indexStringSqlMap) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int beg = 0;
|
||||
for (Map.Entry<Pair<Integer, Integer>, String> entry : indexStringSqlMap.entrySet()) {
|
||||
Pair<Integer, Integer> index = entry.getKey();
|
||||
builder.append(querySql, beg, index.first);
|
||||
builder.append(entry.getValue());
|
||||
beg = index.second + 1;
|
||||
}
|
||||
builder.append(querySql, beg, querySql.length());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String rewriteProjectsToUserDefineAlias(String resSql) {
|
||||
IndexFinder finder = new IndexFinder();
|
||||
ParserRuleContext tree = NereidsParser.toAst(resSql, DorisParser::singleStatement);
|
||||
finder.visit(tree);
|
||||
if (simpleColumnDefinitions.isEmpty()) {
|
||||
return resSql;
|
||||
}
|
||||
List<NamedExpressionContext> namedExpressionContexts = finder.getNamedExpressionContexts();
|
||||
StringBuilder replaceWithColsBuilder = new StringBuilder();
|
||||
for (int i = 0; i < namedExpressionContexts.size(); ++i) {
|
||||
NamedExpressionContext namedExpressionContext = namedExpressionContexts.get(i);
|
||||
int start = namedExpressionContext.expression().start.getStartIndex();
|
||||
int stop = namedExpressionContext.expression().stop.getStopIndex();
|
||||
replaceWithColsBuilder.append(resSql, start, stop + 1);
|
||||
replaceWithColsBuilder.append(" AS `");
|
||||
String escapeBacktick = finalCols.get(i).getName().replace("`", "``");
|
||||
replaceWithColsBuilder.append(escapeBacktick);
|
||||
replaceWithColsBuilder.append('`');
|
||||
if (i != namedExpressionContexts.size() - 1) {
|
||||
replaceWithColsBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
String replaceWithCols = replaceWithColsBuilder.toString();
|
||||
return StringUtils.overlay(resSql, replaceWithCols, finder.getIndex().first,
|
||||
finder.getIndex().second + 1);
|
||||
}
|
||||
|
||||
private void createFinalCols(List<Slot> outputs) throws org.apache.doris.common.AnalysisException {
|
||||
if (simpleColumnDefinitions.isEmpty()) {
|
||||
for (Slot output : outputs) {
|
||||
Column column = new Column(output.getName(), output.getDataType().toCatalogDataType());
|
||||
finalCols.add(column);
|
||||
}
|
||||
} else {
|
||||
if (outputs.size() != simpleColumnDefinitions.size()) {
|
||||
ErrorReport.reportAnalysisException(ErrorCode.ERR_VIEW_WRONG_LIST);
|
||||
}
|
||||
for (int i = 0; i < simpleColumnDefinitions.size(); ++i) {
|
||||
Column column = new Column(simpleColumnDefinitions.get(i).getName(),
|
||||
outputs.get(i).getDataType().toCatalogDataType());
|
||||
column.setComment(simpleColumnDefinitions.get(i).getComment());
|
||||
finalCols.add(column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class OutermostPlanFinderContext {
|
||||
public Plan outermostPlan = null;
|
||||
public boolean found = false;
|
||||
}
|
||||
|
||||
private static class OutermostPlanFinder extends DefaultPlanVisitor<Void, OutermostPlanFinderContext> {
|
||||
public static final OutermostPlanFinder INSTANCE = new OutermostPlanFinder();
|
||||
|
||||
@Override
|
||||
public Void visit(Plan plan, OutermostPlanFinderContext ctx) {
|
||||
if (ctx.found) {
|
||||
return null;
|
||||
}
|
||||
ctx.outermostPlan = plan;
|
||||
ctx.found = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLogicalCTEAnchor(LogicalCTEAnchor<? extends Plan, ? extends Plan> cteAnchor,
|
||||
OutermostPlanFinderContext ctx) {
|
||||
if (ctx.found) {
|
||||
return null;
|
||||
}
|
||||
return super.visit(cteAnchor, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLogicalCTEProducer(LogicalCTEProducer<? extends Plan> cteProducer,
|
||||
OutermostPlanFinderContext ctx) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** traverse ast to find the outermost project list location information in sql*/
|
||||
private static class IndexFinder extends DorisParserBaseVisitor<Void> {
|
||||
private boolean found = false;
|
||||
private int startIndex;
|
||||
private int stopIndex;
|
||||
private List<NamedExpressionContext> namedExpressionContexts = Lists.newArrayList();
|
||||
|
||||
@Override
|
||||
public Void visitChildren(RuleNode node) {
|
||||
if (found) {
|
||||
return null;
|
||||
}
|
||||
int n = node.getChildCount();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
ParseTree c = node.getChild(i);
|
||||
c.accept(this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitCte(DorisParser.CteContext ctx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitSelectColumnClause(DorisParser.SelectColumnClauseContext ctx) {
|
||||
if (found) {
|
||||
return null;
|
||||
}
|
||||
startIndex = ctx.getStart().getStartIndex();
|
||||
stopIndex = ctx.getStop().getStopIndex();
|
||||
found = true;
|
||||
|
||||
NamedExpressionSeqContext namedExpressionSeqContext = ctx.namedExpressionSeq();
|
||||
namedExpressionContexts = namedExpressionSeqContext.namedExpression();
|
||||
return null;
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getIndex() {
|
||||
return Pair.of(startIndex, stopIndex);
|
||||
}
|
||||
|
||||
public List<NamedExpressionContext> getNamedExpressionContexts() {
|
||||
return namedExpressionContexts;
|
||||
}
|
||||
}
|
||||
|
||||
private static class AnalyzerForCreateView extends AbstractBatchJobExecutor {
|
||||
private final List<RewriteJob> jobs;
|
||||
|
||||
public AnalyzerForCreateView(CascadesContext cascadesContext) {
|
||||
super(cascadesContext);
|
||||
jobs = buildAnalyzeViewJobsForStar();
|
||||
}
|
||||
|
||||
public void analyze() {
|
||||
execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RewriteJob> getJobs() {
|
||||
return jobs;
|
||||
}
|
||||
|
||||
private static List<RewriteJob> buildAnalyzeViewJobsForStar() {
|
||||
return jobs(
|
||||
topDown(new EliminateLogicalSelectHint()),
|
||||
topDown(new AnalyzeCTE()),
|
||||
bottomUp(
|
||||
new BindRelation(),
|
||||
new CheckPolicy(),
|
||||
new BindExpression()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.plans.visitor;
|
||||
|
||||
import org.apache.doris.nereids.trees.plans.commands.AddConstraintCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.AlterMTMVCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.AlterViewCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.CallCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.CancelMTMVTaskCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.Command;
|
||||
@ -161,4 +162,8 @@ public interface CommandVisitor<R, C> {
|
||||
default R visitCreateViewCommand(CreateViewCommand createViewCommand, C context) {
|
||||
return visitCommand(createViewCommand, context);
|
||||
}
|
||||
|
||||
default R visitAlterViewCommand(AlterViewCommand alterViewCommand, C context) {
|
||||
return visitCommand(alterViewCommand, context);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user