diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index 0b3a5b5c5b7888..18c67286ee9296 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -60,6 +60,9 @@ statementBase properties=propertyClause? (BROKER extProperties=propertyClause)? (AS query)? #createTable + | CREATE VIEW (IF NOT EXISTS)? name=multipartIdentifier + (LEFT_PAREN cols=simpleColumnDefs RIGHT_PAREN)? + (COMMENT STRING_LITERAL)? AS query #createView | explain? INSERT (INTO | OVERWRITE TABLE) (tableName=multipartIdentifier | DORIS_INTERNAL_TABLE_ID LEFT_PAREN tableId=INTEGER_VALUE RIGHT_PAREN) partitionSpec? // partition define diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/BaseViewStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/BaseViewStmt.java index 8114448f0d4e15..d8740f03f523b9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/BaseViewStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/BaseViewStmt.java @@ -25,7 +25,6 @@ import org.apache.doris.common.UserException; import org.apache.doris.common.util.ToSqlContext; -import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.logging.log4j.LogManager; @@ -50,7 +49,6 @@ public class BaseViewStmt extends DdlStmt { protected QueryStmt cloneStmt; public BaseViewStmt(TableName tableName, List cols, QueryStmt queryStmt) { - Preconditions.checkNotNull(queryStmt); this.tableName = tableName; this.cols = cols; this.viewDefStmt = queryStmt; diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateViewStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateViewStmt.java index ba5007ccce85cd..2eed97f0a018e5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateViewStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateViewStmt.java @@ -17,6 +17,7 @@ package org.apache.doris.analysis; +import org.apache.doris.catalog.Column; import org.apache.doris.catalog.Env; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; @@ -92,4 +93,12 @@ public void analyze(Analyzer analyzer) throws UserException { } } } + + public void setInlineViewDef(String querySql) { + inlineViewDef = querySql; + } + + public void setFinalColumns(List columns) { + finalCols.addAll(columns); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java index 7b444995120cab..403c605ba75992 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java @@ -54,6 +54,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import javax.annotation.concurrent.GuardedBy; /** @@ -121,6 +122,11 @@ public class StatementContext { private BitSet disableRules; + // for create view support in nereids + // key is the start and end position of the sql substring that needs to be replaced, + // and value is the new string used for replacement. + private TreeMap, String> indexInSqlToString = new TreeMap<>(new Pair.PairComparator<>()); + public StatementContext() { this.connectContext = ConnectContext.get(); } @@ -354,4 +360,12 @@ public boolean isHasUnknownColStats() { public void setHasUnknownColStats(boolean hasUnknownColStats) { this.hasUnknownColStats = hasUnknownColStats; } + + public TreeMap, String> getIndexInSqlToString() { + return indexInSqlToString; + } + + public void addIndexInSqlToString(Pair pair, String replacement) { + indexInSqlToString.put(pair, replacement); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java index 4514ea05bfb9c9..34217b249fc510 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.analyzer; import org.apache.doris.analysis.TableScanParams; +import org.apache.doris.common.Pair; import org.apache.doris.nereids.exceptions.UnboundException; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.LogicalProperties; @@ -55,35 +56,44 @@ public class UnboundRelation extends LogicalRelation implements Unbound, BlockFu private final Optional tableSample; private final Optional indexName; private TableScanParams scanParams; + // the start and end position of the sql substring(e.g. "t1", "db1.t1", "ctl1.db1.t1") + private final Optional> indexInSqlString; public UnboundRelation(RelationId id, List nameParts) { this(id, nameParts, Optional.empty(), Optional.empty(), ImmutableList.of(), false, ImmutableList.of(), - ImmutableList.of(), Optional.empty(), Optional.empty(), null); + ImmutableList.of(), Optional.empty(), Optional.empty(), null, Optional.empty()); } public UnboundRelation(RelationId id, List nameParts, List partNames, boolean isTempPart) { this(id, nameParts, Optional.empty(), Optional.empty(), partNames, isTempPart, ImmutableList.of(), - ImmutableList.of(), Optional.empty(), Optional.empty(), null); + ImmutableList.of(), Optional.empty(), Optional.empty(), null, Optional.empty()); } public UnboundRelation(RelationId id, List nameParts, List partNames, boolean isTempPart, List tabletIds, List hints, Optional tableSample, Optional indexName) { this(id, nameParts, Optional.empty(), Optional.empty(), - partNames, isTempPart, tabletIds, hints, tableSample, indexName, null); + partNames, isTempPart, tabletIds, hints, tableSample, indexName, null, Optional.empty()); } public UnboundRelation(RelationId id, List nameParts, List partNames, boolean isTempPart, List tabletIds, List hints, Optional tableSample, Optional indexName, TableScanParams scanParams) { this(id, nameParts, Optional.empty(), Optional.empty(), - partNames, isTempPart, tabletIds, hints, tableSample, indexName, scanParams); + partNames, isTempPart, tabletIds, hints, tableSample, indexName, scanParams, Optional.empty()); } public UnboundRelation(RelationId id, List nameParts, Optional groupExpression, Optional logicalProperties, List partNames, boolean isTempPart, List tabletIds, List hints, Optional tableSample, Optional indexName) { this(id, nameParts, groupExpression, logicalProperties, partNames, - isTempPart, tabletIds, hints, tableSample, indexName, null); + isTempPart, tabletIds, hints, tableSample, indexName, null, Optional.empty()); + } + + public UnboundRelation(RelationId id, List nameParts, List partNames, boolean isTempPart, + List tabletIds, List hints, Optional tableSample, Optional indexName, + TableScanParams scanParams, Optional> indexInSqlString) { + this(id, nameParts, Optional.empty(), Optional.empty(), + partNames, isTempPart, tabletIds, hints, tableSample, indexName, scanParams, indexInSqlString); } /** @@ -92,7 +102,7 @@ public UnboundRelation(RelationId id, List nameParts, Optional nameParts, Optional groupExpression, Optional logicalProperties, List partNames, boolean isTempPart, List tabletIds, List hints, Optional tableSample, Optional indexName, - TableScanParams scanParams) { + TableScanParams scanParams, Optional> indexInSqlString) { super(id, PlanType.LOGICAL_UNBOUND_RELATION, groupExpression, logicalProperties); this.nameParts = ImmutableList.copyOf(Objects.requireNonNull(nameParts, "nameParts should not null")); this.partNames = ImmutableList.copyOf(Objects.requireNonNull(partNames, "partNames should not null")); @@ -102,6 +112,7 @@ public UnboundRelation(RelationId id, List nameParts, Optional getNameParts() { @@ -122,14 +133,14 @@ public LogicalProperties computeLogicalProperties() { public Plan withGroupExpression(Optional groupExpression) { return new UnboundRelation(relationId, nameParts, groupExpression, Optional.of(getLogicalProperties()), - partNames, isTempPart, tabletIds, hints, tableSample, indexName, null); + partNames, isTempPart, tabletIds, hints, tableSample, indexName, null, indexInSqlString); } @Override public Plan withGroupExprLogicalPropChildren(Optional groupExpression, Optional logicalProperties, List children) { return new UnboundRelation(relationId, nameParts, groupExpression, logicalProperties, partNames, - isTempPart, tabletIds, hints, tableSample, indexName, null); + isTempPart, tabletIds, hints, tableSample, indexName, null, indexInSqlString); } @Override @@ -187,4 +198,8 @@ public Optional getTableSample() { public TableScanParams getScanParams() { return scanParams; } + + public Optional> getIndexInSqlString() { + return indexInSqlString; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java index 43705611562b71..cf73f877d604eb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java @@ -17,6 +17,7 @@ package org.apache.doris.nereids.analyzer; +import org.apache.doris.common.Pair; import org.apache.doris.nereids.exceptions.UnboundException; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; @@ -28,17 +29,26 @@ import java.util.List; import java.util.Objects; +import java.util.Optional; /** * Star expression. */ public class UnboundStar extends NamedExpression implements LeafExpression, Unbound, PropagateNullable { - private final List qualifier; + // the start and end position of the sql substring(e.g. "*", "table.*") + private final Optional> indexInSqlString; public UnboundStar(List qualifier) { super(ImmutableList.of()); this.qualifier = Objects.requireNonNull(ImmutableList.copyOf(qualifier), "qualifier can not be null"); + this.indexInSqlString = Optional.empty(); + } + + public UnboundStar(List qualifier, Optional> indexInSqlString) { + super(ImmutableList.of()); + this.qualifier = Objects.requireNonNull(ImmutableList.copyOf(qualifier), "qualifier can not be null"); + this.indexInSqlString = indexInSqlString; } @Override @@ -71,6 +81,10 @@ public boolean equals(Object o) { return qualifier.equals(that.qualifier); } + public Optional> getIndexInSqlString() { + return indexInSqlString; + } + @Override public int hashCode() { return Objects.hash(super.hashCode(), qualifier); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index a9ba3603f53442..31935c4a12123c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -77,6 +77,7 @@ import org.apache.doris.nereids.DorisParser.CreateProcedureContext; import org.apache.doris.nereids.DorisParser.CreateRowPolicyContext; import org.apache.doris.nereids.DorisParser.CreateTableContext; +import org.apache.doris.nereids.DorisParser.CreateViewContext; import org.apache.doris.nereids.DorisParser.CteContext; import org.apache.doris.nereids.DorisParser.DataTypeWithNullableContext; import org.apache.doris.nereids.DorisParser.DateCeilContext; @@ -360,6 +361,7 @@ import org.apache.doris.nereids.trees.plans.commands.CreatePolicyCommand; import org.apache.doris.nereids.trees.plans.commands.CreateProcedureCommand; import org.apache.doris.nereids.trees.plans.commands.CreateTableCommand; +import org.apache.doris.nereids.trees.plans.commands.CreateViewCommand; import org.apache.doris.nereids.trees.plans.commands.DeleteFromCommand; import org.apache.doris.nereids.trees.plans.commands.DeleteFromUsingCommand; import org.apache.doris.nereids.trees.plans.commands.DropConstraintCommand; @@ -386,6 +388,7 @@ import org.apache.doris.nereids.trees.plans.commands.info.ColumnDefinition; import org.apache.doris.nereids.trees.plans.commands.info.CreateMTMVInfo; import org.apache.doris.nereids.trees.plans.commands.info.CreateTableInfo; +import org.apache.doris.nereids.trees.plans.commands.info.CreateViewInfo; import org.apache.doris.nereids.trees.plans.commands.info.DMLCommandType; import org.apache.doris.nereids.trees.plans.commands.info.DefaultValue; import org.apache.doris.nereids.trees.plans.commands.info.DistributionDescriptor; @@ -472,6 +475,15 @@ */ @SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "OptionalGetWithoutIsPresent"}) public class LogicalPlanBuilder extends DorisParserBaseVisitor { + private final boolean forCreateView; + + public LogicalPlanBuilder() { + forCreateView = false; + } + + public LogicalPlanBuilder(boolean forCreateView) { + this.forCreateView = forCreateView; + } @SuppressWarnings("unchecked") protected T typedVisit(ParseTree ctx) { @@ -1329,11 +1341,16 @@ public LogicalPlan visitTableName(TableNameContext ctx) { scanParams = new TableScanParams(ctx.optScanParams().funcName.getText(), map); } + MultipartIdentifierContext identifier = ctx.multipartIdentifier(); TableSample tableSample = ctx.sample() == null ? null : (TableSample) visit(ctx.sample()); - LogicalPlan checkedRelation = LogicalPlanBuilderAssistant.withCheckPolicy( + UnboundRelation relation = forCreateView ? new UnboundRelation(StatementScopeIdGenerator.newRelationId(), + tableId, partitionNames, isTempPart, tabletIdLists, relationHints, + Optional.ofNullable(tableSample), indexName, scanParams, + Optional.of(Pair.of(identifier.start.getStartIndex(), identifier.stop.getStopIndex()))) : new UnboundRelation(StatementScopeIdGenerator.newRelationId(), tableId, partitionNames, isTempPart, tabletIdLists, relationHints, - Optional.ofNullable(tableSample), indexName, scanParams)); + Optional.ofNullable(tableSample), indexName, scanParams); + LogicalPlan checkedRelation = LogicalPlanBuilderAssistant.withCheckPolicy(relation); LogicalPlan plan = withTableAlias(checkedRelation, ctx.tableAlias()); for (LateralViewContext lateralViewContext : ctx.lateralView()) { plan = withGenerate(plan, lateralViewContext); @@ -1382,7 +1399,9 @@ public Expression visitStar(StarContext ctx) { } else { target = ImmutableList.of(); } - return new UnboundStar(target); + return forCreateView + ? new UnboundStar(target, Optional.of(Pair.of(ctx.start.getStartIndex(), ctx.stop.getStopIndex()))) + : new UnboundStar(target); }); } @@ -2413,6 +2432,19 @@ private LogicalPlan plan(ParserRuleContext tree) { * create table parsing * ******************************************************************************************** */ + @Override + public LogicalPlan visitCreateView(CreateViewContext ctx) { + List nameParts = visitMultipartIdentifier(ctx.name); + String comment = ctx.STRING_LITERAL() == null ? "" : LogicalPlanBuilderAssistant.escapeBackSlash( + ctx.STRING_LITERAL().getText().substring(1, ctx.STRING_LITERAL().getText().length() - 1)); + LogicalPlan logicalPlan = visitQuery(ctx.query()); + String querySql = getOriginSql(ctx.query()); + CreateViewInfo info = new CreateViewInfo(ctx.EXISTS() != null, new TableNameInfo(nameParts), + comment, logicalPlan, querySql, + ctx.cols == null ? Lists.newArrayList() : visitSimpleColumnDefs(ctx.cols)); + return new CreateViewCommand(info); + } + @Override public LogicalPlan visitCreateTable(CreateTableContext ctx) { String ctlName = null; @@ -2858,8 +2890,11 @@ private LogicalPlan withSelectQuerySpecification( if (!expressions.stream().allMatch(UnboundSlot.class::isInstance)) { throw new ParseException("only column name is supported in except clause", selectColumnCtx); } - project = new LogicalProject<>(ImmutableList.of(new UnboundStar(ImmutableList.of())), - expressions, isDistinct, aggregate); + UnboundStar star = forCreateView ? new UnboundStar(ImmutableList.of(), + Optional.of(Pair.of(selectColumnCtx.start.getStartIndex(), + selectColumnCtx.stop.getStopIndex()))) + : new UnboundStar(ImmutableList.of()); + project = new LogicalProject<>(ImmutableList.of(star), expressions, isDistinct, aggregate); } else { List projects = getNamedExpressions(selectColumnCtx.namedExpressionSeq()); project = new LogicalProject<>(projects, ImmutableList.of(), isDistinct, aggregate); @@ -3038,8 +3073,10 @@ private LogicalPlan withProjection(LogicalPlan input, SelectColumnClauseContext if (!expressions.stream().allMatch(UnboundSlot.class::isInstance)) { throw new ParseException("only column name is supported in except clause", selectCtx); } - return new LogicalProject<>(ImmutableList.of(new UnboundStar(ImmutableList.of())), - expressions, isDistinct, input); + UnboundStar star = forCreateView ? new UnboundStar(ImmutableList.of(), + Optional.of(Pair.of(selectCtx.start.getStartIndex(), selectCtx.stop.getStopIndex()))) : + new UnboundStar(ImmutableList.of()); + return new LogicalProject<>(ImmutableList.of(star), expressions, isDistinct, input); } else { List projects = getNamedExpressions(selectCtx.namedExpressionSeq()); return new LogicalProject<>(projects, Collections.emptyList(), isDistinct, input); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java index 0ee3f5d068f9ce..da6881a4905468 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java @@ -164,7 +164,20 @@ private T parse(String sql, @Nullable LogicalPlanBuilder logicalPlanBuilder, return (T) realLogicalPlanBuilder.visit(tree); } - private ParserRuleContext toAst(String sql, Function parseFunction) { + public LogicalPlan parseForCreateView(String sql) { + return parseForCreateViewInternal(sql, null, DorisParser::singleStatement); + } + + private T parseForCreateViewInternal(String sql, @Nullable LogicalPlanBuilder logicalPlanBuilder, + Function parseFunction) { + ParserRuleContext tree = toAst(sql, parseFunction); + LogicalPlanBuilder realLogicalPlanBuilder = logicalPlanBuilder == null + ? new LogicalPlanBuilder(true) : logicalPlanBuilder; + return (T) realLogicalPlanBuilder.visit(tree); + } + + /** toAst */ + public static ParserRuleContext toAst(String sql, Function parseFunction) { DorisLexer lexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(sql))); CommonTokenStream tokenStream = new CommonTokenStream(lexer); DorisParser parser = new DorisParser(tokenStream); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java index 8c8a447e830ea8..97cd6cda6699fc 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java @@ -28,6 +28,7 @@ import org.apache.doris.nereids.analyzer.UnboundOneRowRelation; import org.apache.doris.nereids.analyzer.UnboundResultSink; import org.apache.doris.nereids.analyzer.UnboundSlot; +import org.apache.doris.nereids.analyzer.UnboundStar; import org.apache.doris.nereids.analyzer.UnboundTVFRelation; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.parser.LogicalPlanBuilder; @@ -570,6 +571,7 @@ private Plan bindProject(MatchingContext> ctx) { () -> analyzer.analyzeToSet(project.getExcepts())); Builder boundProjections = ImmutableList.builderWithExpectedSize(project.arity()); + StatementContext statementContext = ctx.statementContext; for (Expression expression : project.getProjects()) { Expression expr = analyzer.analyze(expression); if (!(expr instanceof BoundStar)) { @@ -581,6 +583,13 @@ private Plan bindProject(MatchingContext> ctx) { slots = Utils.filterImmutableList(slots, slot -> !boundExcepts.get().contains(slot)); } boundProjections.addAll(slots); + + // for create view stmt expand star + List slotsForLambda = slots; + UnboundStar unboundStar = (UnboundStar) expression; + unboundStar.getIndexInSqlString().ifPresent(pair -> + statementContext.addIndexInSqlToString(pair, toSqlWithBackquote(slotsForLambda)) + ); } } return project.withProjects(boundProjections.build()); @@ -974,6 +983,11 @@ private interface CustomSlotBinderAnalyzer { List bindSlot(ExpressionAnalyzer analyzer, UnboundSlot unboundSlot); } + public String toSqlWithBackquote(List slots) { + return slots.stream().map(slot -> ((SlotReference) slot).getQualifiedNameWithBackquote()) + .collect(Collectors.joining(", ")); + } + private boolean hasAggregateFunction(Expression expression, FunctionRegistry functionRegistry) { return expression.anyMatch(expr -> { if (expr instanceof AggregateFunction) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java index 33fba7f6ec3227..f036a73a6ed3e9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java @@ -31,6 +31,7 @@ import org.apache.doris.datasource.hive.HMSExternalTable; import org.apache.doris.nereids.CTEContext; import org.apache.doris.nereids.CascadesContext; +import org.apache.doris.nereids.StatementContext; import org.apache.doris.nereids.analyzer.Unbound; import org.apache.doris.nereids.analyzer.UnboundRelation; import org.apache.doris.nereids.analyzer.UnboundResultSink; @@ -62,6 +63,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalTestScan; import org.apache.doris.nereids.trees.plans.logical.LogicalView; import org.apache.doris.nereids.util.RelationUtil; +import org.apache.doris.nereids.util.Utils; import org.apache.doris.qe.ConnectContext; import com.google.common.base.Preconditions; @@ -239,6 +241,12 @@ private LogicalPlan makeOlapScan(TableIf table, UnboundRelation unboundRelation, private LogicalPlan getLogicalPlan(TableIf table, UnboundRelation unboundRelation, List tableQualifier, CascadesContext cascadesContext) { + // for create view stmt replace tablename to ctl.db.tablename + unboundRelation.getIndexInSqlString().ifPresent(pair -> { + StatementContext statementContext = cascadesContext.getStatementContext(); + statementContext.addIndexInSqlToString(pair, + Utils.qualifiedNameWithBackquote(tableQualifier)); + }); List qualifierWithoutTableName = Lists.newArrayList(); qualifierWithoutTableName.addAll(tableQualifier.subList(0, tableQualifier.size() - 1)); switch (table.getType()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java index 760836455127ea..3d11a7d011da15 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java @@ -19,6 +19,7 @@ import org.apache.doris.catalog.Column; import org.apache.doris.catalog.TableIf; +import org.apache.doris.nereids.exceptions.UnboundException; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.trees.plans.algebra.Relation; import org.apache.doris.nereids.types.DataType; @@ -296,4 +297,8 @@ private static Supplier> buildInternalName( internalName.isPresent() ? internalName : Optional.of(name.get())); } } + + public String getQualifiedNameWithBackquote() throws UnboundException { + return Utils.qualifiedNameWithBackquote(getQualifier(), getName()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/WindowFrame.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/WindowFrame.java index 90449ed0f86bad..5cbb93ce3748ea 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/WindowFrame.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/WindowFrame.java @@ -99,7 +99,7 @@ public String toSql() { StringBuilder sb = new StringBuilder(); sb.append(frameUnits + " "); if (rightBoundary != null) { - sb.append("BETWEEN " + leftBoundary + " AND " + rightBoundary); + sb.append("BETWEEN " + leftBoundary.toSql() + " AND " + rightBoundary.toSql()); } else { sb.append(leftBoundary); } @@ -215,6 +215,32 @@ public String toString() { return sb.toString(); } + /** toSql*/ + public String toSql() { + StringBuilder sb = new StringBuilder(); + boundOffset.ifPresent(value -> sb.append(value + " ")); + switch (frameBoundType) { + case UNBOUNDED_PRECEDING: + sb.append("UNBOUNDED PRECEDING"); + break; + case UNBOUNDED_FOLLOWING: + sb.append("UNBOUNDED FOLLOWING"); + break; + case CURRENT_ROW: + sb.append("CURRENT ROW"); + break; + case PRECEDING: + sb.append("PRECEDING"); + break; + case FOLLOWING: + sb.append("FOLLOWING"); + break; + default: + break; + } + return sb.toString(); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Lambda.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Lambda.java index 1d6c725db09d90..e8261f6391dda9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Lambda.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Lambda.java @@ -132,7 +132,7 @@ public String toSql() { if (argumentNames.size() > 1) { argStr = argumentNames.stream().collect(Collectors.joining(", ", "(", ")")); } - builder.append(String.format("%s -> %s", argStr, getLambdaFunction().toString())); + builder.append(String.format("%s -> %s", argStr, getLambdaFunction().toSql())); for (int i = 1; i < getArguments().size(); i++) { builder.append(", ").append(getArgument(i).toSql()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java index 1c493deae033cb..daab18438b4774 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java @@ -148,5 +148,6 @@ public enum PlanType { CREATE_PROCEDURE_COMMAND, DROP_PROCEDURE_COMMAND, SHOW_PROCEDURE_COMMAND, - SHOW_CREATE_PROCEDURE_COMMAND + SHOW_CREATE_PROCEDURE_COMMAND, + CREATE_VIEW_COMMAND } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateViewCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateViewCommand.java new file mode 100644 index 00000000000000..d78308664b5e38 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateViewCommand.java @@ -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.CreateViewStmt; +import org.apache.doris.catalog.Env; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.info.CreateViewInfo; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +/** CreateViewCommand */ +public class CreateViewCommand extends Command { + private final CreateViewInfo createViewInfo; + + public CreateViewCommand(CreateViewInfo createViewInfo) { + super(PlanType.CREATE_VIEW_COMMAND); + this.createViewInfo = createViewInfo; + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + createViewInfo.init(ctx); + createViewInfo.validate(ctx); + CreateViewStmt createViewStmt = createViewInfo.translateToLegacyStmt(ctx); + Env.getCurrentEnv().createView(createViewStmt); + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitCreateViewCommand(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateViewInfo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateViewInfo.java new file mode 100644 index 00000000000000..632e884685652f --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateViewInfo.java @@ -0,0 +1,329 @@ +// 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.ColWithComment; +import org.apache.doris.analysis.CreateViewStmt; +import org.apache.doris.catalog.Column; +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 { + private final boolean ifNotExists; + private final TableNameInfo viewName; + private final String comment; + private final LogicalPlan logicalQuery; + private final String querySql; + private final List simpleColumnDefinitions; + private final List finalCols = Lists.newArrayList(); + private Plan analyzedPlan; + + /** constructor*/ + public CreateViewInfo(boolean ifNotExists, TableNameInfo viewName, String comment, LogicalPlan logicalQuery, + String querySql, List 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 */ + public void init(ConnectContext ctx) throws UserException { + analyzeAndFillRewriteSqlMap(querySql, ctx); + OutermostPlanFinderContext outermostPlanFinderContext = new OutermostPlanFinderContext(); + analyzedPlan.accept(OutermostPlanFinder.INSTANCE, outermostPlanFinderContext); + List 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); + viewName.analyze(ctx); + FeNameFormat.checkTableName(viewName.getTbl()); + // disallow external catalog + Util.prohibitExternalCatalog(viewName.getCtl(), "CreateViewStmt"); + // check privilege + if (!Env.getCurrentEnv().getAccessManager().checkTblPriv(ConnectContext.get(), viewName.getDb(), + viewName.getTbl(), PrivPredicate.CREATE)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "CREATE"); + } + Set 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 CreateViewStmt translateToLegacyStmt(ConnectContext ctx) { + List cols = Lists.newArrayList(); + for (SimpleColumnDefinition def : simpleColumnDefinitions) { + cols.add(def.translateToColWithComment()); + } + CreateViewStmt createViewStmt = new CreateViewStmt(ifNotExists, viewName.transferToTableName(), cols, comment, + null); + // expand star(*) in project list and replace table name with qualifier + String rewrittenSql = rewriteSql(ctx.getStatementContext().getIndexInSqlToString()); + + // rewrite project alias + rewrittenSql = rewriteProjectsToUserDefineAlias(rewrittenSql); + + createViewStmt.setInlineViewDef(rewrittenSql); + 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, String> indexStringSqlMap) { + StringBuilder builder = new StringBuilder(); + int beg = 0; + for (Map.Entry, String> entry : indexStringSqlMap.entrySet()) { + Pair 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 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 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 { + 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 cteAnchor, + OutermostPlanFinderContext ctx) { + if (ctx.found) { + return null; + } + return super.visit(cteAnchor, ctx); + } + + @Override + public Void visitLogicalCTEProducer(LogicalCTEProducer cteProducer, + OutermostPlanFinderContext ctx) { + return null; + } + } + + /** traverse ast to find the outermost project list location information in sql*/ + private static class IndexFinder extends DorisParserBaseVisitor { + private boolean found = false; + private int startIndex; + private int stopIndex; + private List 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 getIndex() { + return Pair.of(startIndex, stopIndex); + } + + public List getNamedExpressionContexts() { + return namedExpressionContexts; + } + } + + private static class AnalyzerForCreateView extends AbstractBatchJobExecutor { + private final List jobs; + + public AnalyzerForCreateView(CascadesContext cascadesContext) { + super(cascadesContext); + jobs = buildAnalyzeViewJobsForStar(); + } + + public void analyze() { + execute(); + } + + @Override + public List getJobs() { + return jobs; + } + + private static List buildAnalyzeViewJobsForStar() { + return jobs( + topDown(new EliminateLogicalSelectHint()), + topDown(new AnalyzeCTE()), + bottomUp( + new BindRelation(), + new CheckPolicy(), + new BindExpression() + ) + ); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SimpleColumnDefinition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SimpleColumnDefinition.java index 51080bf45d2372..e4e7f2013cb341 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SimpleColumnDefinition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SimpleColumnDefinition.java @@ -17,6 +17,8 @@ package org.apache.doris.nereids.trees.plans.commands.info; +import org.apache.doris.analysis.ColWithComment; + /** * column def for mv */ @@ -39,4 +41,8 @@ public String getName() { public String getComment() { return comment; } + + public ColWithComment translateToColWithComment() { + return new ColWithComment(name, comment); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java index 6bcc41e39e45f9..527ccb1ffe080b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java @@ -26,6 +26,7 @@ import org.apache.doris.nereids.trees.plans.commands.CreatePolicyCommand; import org.apache.doris.nereids.trees.plans.commands.CreateProcedureCommand; import org.apache.doris.nereids.trees.plans.commands.CreateTableCommand; +import org.apache.doris.nereids.trees.plans.commands.CreateViewCommand; import org.apache.doris.nereids.trees.plans.commands.DeleteFromCommand; import org.apache.doris.nereids.trees.plans.commands.DeleteFromUsingCommand; import org.apache.doris.nereids.trees.plans.commands.DropConstraintCommand; @@ -156,4 +157,8 @@ default R visitShowProcedureStatusCommand(ShowProcedureStatusCommand showProcedu default R visitShowCreateProcedureCommand(ShowCreateProcedureCommand showCreateProcedureCommand, C context) { return visitCommand(showCreateProcedureCommand, context); } + + default R visitCreateViewCommand(CreateViewCommand createViewCommand, C context) { + return visitCommand(createViewCommand, context); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java index c28b18e697d34c..852e148ef1d9cb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java @@ -128,6 +128,23 @@ public static String qualifiedName(List qualifier, String name) { return StringUtils.join(qualifiedNameParts(qualifier, name), "."); } + /** get qualified name with Backtick */ + public static String qualifiedNameWithBackquote(List qualifiers, String name) { + List fullName = new ArrayList<>(qualifiers); + fullName.add(name); + return qualifiedNameWithBackquote(fullName); + } + + /** get qualified name with Backtick */ + public static String qualifiedNameWithBackquote(List qualifiers) { + List qualifierWithBackquote = Lists.newArrayListWithCapacity(qualifiers.size()); + for (String qualifier : qualifiers) { + String escapeQualifier = qualifier.replace("`", "``"); + qualifierWithBackquote.add('`' + escapeQualifier + '`'); + } + return StringUtils.join(qualifierWithBackquote, "."); + } + /** * Get sql string for plan. * diff --git a/regression-test/data/ddl_p0/test_create_view_nereids.out b/regression-test/data/ddl_p0/test_create_view_nereids.out new file mode 100644 index 00000000000000..1fda4f63f57f79 --- /dev/null +++ b/regression-test/data/ddl_p0/test_create_view_nereids.out @@ -0,0 +1,228 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !test_view_1 -- +1 [1, 2, 3] +2 [10, -2, 8] +3 [-1, 20, 0] + +-- !test_view_2 -- +1 [1, 2, 3] [1, 1, 1] +2 [10, -2, 8] [1, 0, 1] +3 [-1, 20, 0] [0, 1, 0] + +-- !test_view_3 -- +1 [1, 2, 3] [1, 2, 3] [1, 2, 3] +2 [10, -2, 8] [10, 8] [10, 8] +3 [-1, 20, 0] [20] [20] + +-- !test_view_4 -- +1 [1, 2, 3] [1, 2, 3] [1, 2, 3] +2 [10, -2, 8] [10, 8] [10, 8] +3 [-1, 20, 0] [20] [20] + +-- !test_view_5 -- +1 [1, 2, 3] [1, 1, 1] +2 [10, -2, 8] [1, 0, 1] +3 [-1, 20, 0] [0, 1, 0] + +-- !test_view_6 -- +v1 CREATE VIEW `v1` COMMENT 'VIEW' AS SELECT\n error_code, \n 1, \n 'string', \n now(), \n dayofyear(op_time), \n cast (source AS BIGINT), \n min(`timestamp`) OVER (\n ORDER BY \n op_time DESC ROWS BETWEEN UNBOUNDED PRECEDING\n AND 1 FOLLOWING\n ), \n 1 > 2,\n 2 + 3,\n 1 IN (1, 2, 3, 4), \n remark LIKE '%like', \n CASE WHEN remark = 's' THEN 1 ELSE 2 END,\n TRUE | FALSE \n FROM \n `internal`.`regression_test_ddl_p0`.`view_column_name_test_nereids`; + +-- !test_with_as -- +1 1 2 +1 1 4 +1 3 6 +2 1 3 +2 1 4 +2 1 7 +2 3 5 +2 3 9 +2 4 2 +3 2 8 +3 5 \N +3 5 6 +3 5 6 +3 5 8 +4 5 6 +6 \N 6 +6 7 1 + +-- !test_with_as_sql -- +test_view_with_as CREATE VIEW `test_view_with_as` COMMENT 'VIEW' AS (\n with t1 as (select `internal`.`regression_test_ddl_p0`.`mal_test_view`.`pk`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`a`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`b` from `internal`.`regression_test_ddl_p0`.`mal_test_view`), t2 as (select `internal`.`regression_test_ddl_p0`.`mal_test_view`.`pk`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`a`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`b` from `internal`.`regression_test_ddl_p0`.`mal_test_view`), \n t3 as (select `internal`.`regression_test_ddl_p0`.`mal_test_view`.`pk`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`a`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`b` from `internal`.`regression_test_ddl_p0`.`mal_test_view`) SELECT `t1`.`pk`, `t1`.`a`, `t1`.`b` FROM t1); utf8mb4 utf8mb4_0900_bin + +-- !test_union -- +1 1 2 +1 1 2 +1 1 4 +1 1 4 +1 3 6 +1 3 6 +2 1 3 +2 1 3 +2 1 4 +2 1 4 +2 1 7 +2 1 7 +2 3 5 +2 3 5 +2 3 9 +2 3 9 +2 4 2 +2 4 2 +3 2 8 +3 2 8 +3 5 \N +3 5 \N +3 5 6 +3 5 6 +3 5 6 +3 5 6 +3 5 8 +3 5 8 +4 5 6 +4 5 6 +6 \N 6 +6 \N 6 +6 7 1 +6 7 1 + +-- !test_union_sql -- +test_view_union CREATE VIEW `test_view_union` COMMENT 'VIEW' AS (select `internal`.`regression_test_ddl_p0`.`mal_test_view`.`pk` AS `c1`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`a` AS `c2`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`b` AS `c3` from `internal`.`regression_test_ddl_p0`.`mal_test_view` Union all SELECT `internal`.`regression_test_ddl_p0`.`mal_test_view`.`pk`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`a`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`b` FROM `internal`.`regression_test_ddl_p0`.`mal_test_view`); utf8mb4 utf8mb4_0900_bin + +-- !test_count_star -- +17 + +-- !test_count_star_sql -- +test_view_count_star CREATE VIEW `test_view_count_star` COMMENT 'VIEW' AS (select count(*) AS `c1` from `internal`.`regression_test_ddl_p0`.`mal_test_view` having count(*) > 0); utf8mb4 utf8mb4_0900_bin + +-- !test_expression -- +\N \N 6 +2 4 2 +2 4 3 +2 4 4 +2 4 4 +2 4 7 +3 5 8 +4 6 5 +4 6 6 +4 6 9 +5 7 2 +6 8 \N +6 8 6 +6 8 6 +6 8 6 +6 8 8 +8 10 1 + +-- !test_expression_sql -- +test_view_expression CREATE VIEW `test_view_expression` COMMENT 'VIEW' AS (select a+1 AS `c1`, abs(a)+2+1 AS `c2`, cast(b as varchar(10)) AS `c3` from `internal`.`regression_test_ddl_p0`.`mal_test_view`); utf8mb4 utf8mb4_0900_bin + +-- !test_alias -- +\N \N 6 +2 4 2 +2 4 3 +2 4 4 +2 4 4 +2 4 7 +3 5 8 +4 6 5 +4 6 6 +4 6 9 +5 7 2 +6 8 \N +6 8 6 +6 8 6 +6 8 6 +6 8 8 +8 10 1 + +-- !test_alias_sql -- +test_view_alias CREATE VIEW `test_view_alias` COMMENT 'VIEW' AS (\n select c8 AS `c1`, c2 AS `c2`, c1 AS `c3` from (select a+1 c8,abs(a)+2+1 as c2, cast(b as varchar(10)) as c1 from `internal`.`regression_test_ddl_p0`.`mal_test_view`) t); utf8mb4 utf8mb4_0900_bin + +-- !test_star_except -- +\N 6 +1 2 +1 3 +1 4 +1 4 +1 7 +2 8 +3 5 +3 6 +3 9 +4 2 +5 \N +5 6 +5 6 +5 6 +5 8 +7 1 + +-- !test_star_except_sql -- +test_view_star_except CREATE VIEW `test_view_star_except` COMMENT 'VIEW' AS select `internal`.`regression_test_ddl_p0`.`mal_test_view`.`a`, `internal`.`regression_test_ddl_p0`.`mal_test_view`.`b` from `internal`.`regression_test_ddl_p0`.`mal_test_view`; utf8mb4 utf8mb4_0900_bin + +-- !test_create_view_from_view -- +1 1 2 +1 1 4 +1 3 6 +2 1 3 +2 1 4 +2 1 7 +2 3 5 +2 3 9 +2 4 2 +3 2 8 +3 5 \N +3 5 6 +3 5 6 +3 5 8 +4 5 6 +6 \N 6 +6 7 1 + +-- !test_create_view_from_view_sql -- +test_view_from_view CREATE VIEW `test_view_from_view` COMMENT 'VIEW' AS select `internal`.`regression_test_ddl_p0`.`test_view_with_as`.`pk` AS `c1`, `internal`.`regression_test_ddl_p0`.`test_view_with_as`.`a` AS `c2`, `internal`.`regression_test_ddl_p0`.`test_view_with_as`.`b` AS `c3` from `internal`.`regression_test_ddl_p0`.`test_view_with_as`; utf8mb4 utf8mb4_0900_bin + +-- !test_backquote_in_view_define -- +\N 6 +1 2 +1 3 +1 4 +1 4 +1 7 +2 8 +3 5 +3 6 +3 9 +4 2 +5 \N +5 6 +5 6 +5 6 +5 8 +7 1 + +-- !test_backquote_in_view_define_sql -- +test_backquote_in_view_define CREATE VIEW `test_backquote_in_view_define` COMMENT 'VIEW' AS select a AS `ab``c`, b AS `c2` from `internal`.`regression_test_ddl_p0`.`mal_test_view`; utf8mb4 utf8mb4_0900_bin + +-- !test_backquote_in_table_alias -- +\N 6 +1 2 +1 3 +1 4 +1 4 +1 7 +2 8 +3 5 +3 6 +3 9 +4 2 +5 \N +5 6 +5 6 +5 6 +5 8 +7 1 + +-- !test_backquote_in_table_alias_sql -- +test_backquote_in_table_alias CREATE VIEW `test_backquote_in_table_alias` COMMENT 'VIEW' AS select `ab``c`.`a` AS `c1`, `ab``c`.`b` AS `c2` from (select a,b from `internal`.`regression_test_ddl_p0`.`mal_test_view`) `ab``c`; utf8mb4 utf8mb4_0900_bin + diff --git a/regression-test/data/show_p0/test_show_create_table_and_views_nereids.out b/regression-test/data/show_p0/test_show_create_table_and_views_nereids.out new file mode 100644 index 00000000000000..3a5728d3d9e50a --- /dev/null +++ b/regression-test/data/show_p0/test_show_create_table_and_views_nereids.out @@ -0,0 +1,46 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !show -- +show_create_table_and_views_nereids_table CREATE TABLE `show_create_table_and_views_nereids_table` (\n `user_id` LARGEINT NOT NULL,\n `good_id` LARGEINT NOT NULL,\n `cost` BIGINT SUM NULL DEFAULT "0"\n) ENGINE=OLAP\nAGGREGATE KEY(`user_id`, `good_id`)\nCOMMENT 'OLAP'\nPARTITION BY RANGE(`good_id`)\n(PARTITION p1 VALUES [("-170141183460469231731687303715884105728"), ("100")),\nPARTITION p2 VALUES [("100"), ("200")),\nPARTITION p3 VALUES [("200"), ("300")),\nPARTITION p4 VALUES [("300"), ("400")),\nPARTITION p5 VALUES [("400"), ("500")),\nPARTITION p6 VALUES [("500"), ("600")),\nPARTITION p7 VALUES [("600"), (MAXVALUE)))\nDISTRIBUTED BY HASH(`user_id`) BUCKETS 2\nPROPERTIES (\n"replication_allocation" = "tag.location.default: 1",\n"min_load_replica_num" = "-1",\n"is_being_synced" = "false",\n"storage_medium" = "hdd",\n"storage_format" = "V2",\n"inverted_index_storage_format" = "V1",\n"light_schema_change" = "true",\n"disable_auto_compaction" = "false",\n"binlog.enable" = "false",\n"binlog.ttl_seconds" = "86400",\n"binlog.max_bytes" = "9223372036854775807",\n"binlog.max_history_nums" = "9223372036854775807",\n"enable_single_replica_compaction" = "false",\n"group_commit_interval_ms" = "10000",\n"group_commit_data_bytes" = "134217728"\n); + +-- !select -- +1 1 30 +1 2 5 +1 3 10 +1 300 2 +2 1 100 +2 2 10 +2 3 44 +2 200 1111 +3 1 10 +3 2 1 +23 900 1 +100 100 1 +200 20 1 +300 20 1 + +-- !select -- +1 5 +2 10 +3 1 + +-- !show -- +show_create_table_and_views_nereids_view CREATE VIEW `show_create_table_and_views_nereids_view` COMMENT 'VIEW' AS SELECT user_id AS `user_id`, cost AS `cost` FROM `internal`.`show_create_table_and_views_nereids_db`.`show_create_table_and_views_nereids_table`\n WHERE good_id = 2; utf8mb4 utf8mb4_0900_bin + +-- !select -- +1 47 +2 1265 +3 11 +23 1 +100 1 +200 1 +300 1 + +-- !show -- +show_create_table_and_views_nereids_table CREATE TABLE `show_create_table_and_views_nereids_table` (\n `user_id` LARGEINT NOT NULL,\n `good_id` LARGEINT NOT NULL,\n `cost` BIGINT SUM NULL DEFAULT "0"\n) ENGINE=OLAP\nAGGREGATE KEY(`user_id`, `good_id`)\nCOMMENT 'OLAP'\nPARTITION BY RANGE(`good_id`)\n(PARTITION p1 VALUES [("-170141183460469231731687303715884105728"), ("100")),\nPARTITION p2 VALUES [("100"), ("200")),\nPARTITION p3 VALUES [("200"), ("300")),\nPARTITION p4 VALUES [("300"), ("400")),\nPARTITION p5 VALUES [("400"), ("500")),\nPARTITION p6 VALUES [("500"), ("600")),\nPARTITION p7 VALUES [("600"), (MAXVALUE)))\nDISTRIBUTED BY HASH(`user_id`) BUCKETS 2\nPROPERTIES (\n"replication_allocation" = "tag.location.default: 1",\n"min_load_replica_num" = "-1",\n"is_being_synced" = "false",\n"storage_medium" = "hdd",\n"storage_format" = "V2",\n"inverted_index_storage_format" = "V1",\n"light_schema_change" = "true",\n"disable_auto_compaction" = "false",\n"binlog.enable" = "false",\n"binlog.ttl_seconds" = "86400",\n"binlog.max_bytes" = "9223372036854775807",\n"binlog.max_history_nums" = "9223372036854775807",\n"enable_single_replica_compaction" = "false",\n"group_commit_interval_ms" = "10000",\n"group_commit_data_bytes" = "134217728"\n); + +-- !show -- +show_create_table_and_views_nereids_like CREATE TABLE `show_create_table_and_views_nereids_like` (\n `user_id` LARGEINT NOT NULL,\n `good_id` LARGEINT NOT NULL,\n `cost` BIGINT SUM NULL DEFAULT "0"\n) ENGINE=OLAP\nAGGREGATE KEY(`user_id`, `good_id`)\nCOMMENT 'OLAP'\nPARTITION BY RANGE(`good_id`)\n(PARTITION p1 VALUES [("-170141183460469231731687303715884105728"), ("100")),\nPARTITION p2 VALUES [("100"), ("200")),\nPARTITION p3 VALUES [("200"), ("300")),\nPARTITION p4 VALUES [("300"), ("400")),\nPARTITION p5 VALUES [("400"), ("500")),\nPARTITION p6 VALUES [("500"), ("600")),\nPARTITION p7 VALUES [("600"), (MAXVALUE)))\nDISTRIBUTED BY HASH(`user_id`) BUCKETS 2\nPROPERTIES (\n"replication_allocation" = "tag.location.default: 1",\n"min_load_replica_num" = "-1",\n"is_being_synced" = "false",\n"storage_medium" = "hdd",\n"storage_format" = "V2",\n"inverted_index_storage_format" = "V1",\n"light_schema_change" = "true",\n"disable_auto_compaction" = "false",\n"binlog.enable" = "false",\n"binlog.ttl_seconds" = "86400",\n"binlog.max_bytes" = "9223372036854775807",\n"binlog.max_history_nums" = "9223372036854775807",\n"enable_single_replica_compaction" = "false",\n"group_commit_interval_ms" = "10000",\n"group_commit_data_bytes" = "134217728"\n); + +-- !show -- +show_create_table_and_views_nereids_like_with_rollup CREATE TABLE `show_create_table_and_views_nereids_like_with_rollup` (\n `user_id` LARGEINT NOT NULL,\n `good_id` LARGEINT NOT NULL,\n `cost` BIGINT SUM NULL DEFAULT "0"\n) ENGINE=OLAP\nAGGREGATE KEY(`user_id`, `good_id`)\nCOMMENT 'OLAP'\nPARTITION BY RANGE(`good_id`)\n(PARTITION p1 VALUES [("-170141183460469231731687303715884105728"), ("100")),\nPARTITION p2 VALUES [("100"), ("200")),\nPARTITION p3 VALUES [("200"), ("300")),\nPARTITION p4 VALUES [("300"), ("400")),\nPARTITION p5 VALUES [("400"), ("500")),\nPARTITION p6 VALUES [("500"), ("600")),\nPARTITION p7 VALUES [("600"), (MAXVALUE)))\nDISTRIBUTED BY HASH(`user_id`) BUCKETS 2\nPROPERTIES (\n"replication_allocation" = "tag.location.default: 1",\n"min_load_replica_num" = "-1",\n"is_being_synced" = "false",\n"storage_medium" = "hdd",\n"storage_format" = "V2",\n"inverted_index_storage_format" = "V1",\n"light_schema_change" = "true",\n"disable_auto_compaction" = "false",\n"binlog.enable" = "false",\n"binlog.ttl_seconds" = "86400",\n"binlog.max_bytes" = "9223372036854775807",\n"binlog.max_history_nums" = "9223372036854775807",\n"enable_single_replica_compaction" = "false",\n"group_commit_interval_ms" = "10000",\n"group_commit_data_bytes" = "134217728"\n); + diff --git a/regression-test/suites/ddl_p0/test_create_view.groovy b/regression-test/suites/ddl_p0/test_create_view.groovy index 6020aa310509dd..e66798e50bbb19 100644 --- a/regression-test/suites/ddl_p0/test_create_view.groovy +++ b/regression-test/suites/ddl_p0/test_create_view.groovy @@ -16,7 +16,7 @@ // under the License. suite("test_create_view") { - + sql "SET enable_nereids_planner=false;" sql """DROP TABLE IF EXISTS count_distinct""" sql """ CREATE TABLE IF NOT EXISTS count_distinct @@ -42,6 +42,7 @@ suite("test_create_view") { "dynamic_partition.buckets" = "3" ); """ + sql "drop view if exists test_count_distinct" sql """ CREATE VIEW IF NOT EXISTS test_count_distinct ( @@ -203,4 +204,4 @@ suite("test_create_view") { view_column_name_test """ qt_test_view_6 """ SHOW VIEW FROM view_column_name_test;""" -} +} \ No newline at end of file diff --git a/regression-test/suites/ddl_p0/test_create_view_nereids.groovy b/regression-test/suites/ddl_p0/test_create_view_nereids.groovy new file mode 100644 index 00000000000000..48885a47fd157a --- /dev/null +++ b/regression-test/suites/ddl_p0/test_create_view_nereids.groovy @@ -0,0 +1,280 @@ +// 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. + +suite("test_create_view_nereids") { + sql "SET enable_nereids_planner=true;" + sql "SET enable_fallback_to_original_planner=false;" + sql """DROP TABLE IF EXISTS count_distinct_nereids""" + sql """ + CREATE TABLE IF NOT EXISTS count_distinct_nereids + ( + RQ DATE NOT NULL COMMENT "日期", + v1 VARCHAR(100) NOT NULL COMMENT "字段1", + v2 VARCHAR(100) NOT NULL COMMENT "字段2", + v3 VARCHAR(100) REPLACE_IF_NOT_NULL COMMENT "字段3" + ) + AGGREGATE KEY(RQ,v1,v2) + PARTITION BY RANGE(RQ) + ( + PARTITION p20220908 VALUES LESS THAN ('2022-09-09') + ) + DISTRIBUTED BY HASH(v1,v2) BUCKETS 3 + PROPERTIES( + "replication_num" = "1", + "dynamic_partition.enable" = "true", + "dynamic_partition.time_unit" = "DAY", + "dynamic_partition.start" = "-3", + "dynamic_partition.end" = "3", + "dynamic_partition.prefix" = "p", + "dynamic_partition.buckets" = "3" + ); + """ + sql """ + CREATE VIEW IF NOT EXISTS test_count_distinct_nereids + ( + RQ comment "日期", + v1 comment "v1", + v2 comment "v2", + v3 comment "v3" + ) + AS + select aa.RQ as RQ, aa.v1 as v1,aa.v2 as v2 , bb.v3 as v3 from + ( + select RQ, count(distinct v1) as v1 , count(distinct v2 ) as v2 + from count_distinct_nereids + group by RQ + ) aa + LEFT JOIN + ( + select RQ, max(v3) as v3 + from count_distinct_nereids + group by RQ + ) bb + on aa.RQ = bb.RQ; + """ + + sql """select * from test_count_distinct_nereids""" + sql """DROP VIEW IF EXISTS test_count_distinct_nereids""" + sql """DROP TABLE IF EXISTS count_distinct_nereids""" + + sql """DROP TABLE IF EXISTS test_view_t1""" + sql """ + CREATE TABLE `test_view_t1` ( + k1 int, + k2 date, + v1 int + ) ENGINE=OLAP + UNIQUE KEY(`k1`,`k2`) + COMMENT '测试' + PARTITION BY RANGE(k2) ( + PARTITION p1 VALUES [('2023-07-01'), ('2023-07-10')), + PARTITION p2 VALUES [('2023-07-11'), ('2023-07-20')) + ) + DISTRIBUTED BY HASH(`k1`) BUCKETS 3 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + );""" + sql """DROP TABLE IF EXISTS test_view_t2""" + sql """ + CREATE TABLE `test_view_t2` ( + k1 int, + k2 date, + v1 int + ) ENGINE=OLAP + UNIQUE KEY(`k1`,`k2`) + COMMENT '测试' + PARTITION BY RANGE(k2) ( + PARTITION p1 VALUES [('2023-07-01'), ('2023-07-05')), + PARTITION p2 VALUES [('2023-07-05'), ('2023-07-15')) + ) + DISTRIBUTED BY HASH(`k1`) BUCKETS 3 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); """ + sql """ + CREATE VIEW IF NOT EXISTS my_view_nereids AS + SELECT test_view_t1.* FROM test_view_t1 PARTITION(p1) JOIN test_view_t2 PARTITION(p2) ON test_view_t1.k1 = test_view_t2.k1; """ + sql """SELECT * FROM my_view_nereids""" + sql """DROP VIEW IF EXISTS my_view_nereids""" + sql """DROP TABLE IF EXISTS test_view_t1""" + sql """DROP TABLE IF EXISTS test_view_t2""" + + + sql """DROP TABLE IF EXISTS view_baseall_nereids""" + sql """DROP VIEW IF EXISTS test_view7_nereids""" + sql """DROP VIEW IF EXISTS test_view8""" + sql """ + CREATE TABLE `view_baseall_nereids` ( + `k1` int(11) NULL, + `k3` array NULL + ) ENGINE=OLAP + DUPLICATE KEY(`k1`) + COMMENT 'OLAP' + DISTRIBUTED BY HASH(`k1`) BUCKETS 5 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1", + "is_being_synced" = "false", + "storage_format" = "V2", + "light_schema_change" = "true", + "disable_auto_compaction" = "false", + "enable_single_replica_compaction" = "false" + ); + """ + sql """insert into view_baseall_nereids values(1,[1,2,3]);""" + sql """insert into view_baseall_nereids values(2,[10,-2,8]);""" + sql """insert into view_baseall_nereids values(3,[-1,20,0]);""" + + qt_test_view_1 """ select * from view_baseall_nereids order by k1; """ + qt_test_view_2 """ select *, array_map(x->x>0,k3) from view_baseall_nereids order by k1; """ + qt_test_view_3 """ select *, array_filter(x->x>0,k3),array_filter(`k3`, array_map(x -> x > 0, `k3`)) from view_baseall_nereids order by k1; """ + + + sql """ + create view IF NOT EXISTS test_view7_nereids (k1,k2,k3,k4) as + select *, array_filter(x->x>0,k3),array_filter(`k3`, array_map(x -> x > 0, `k3`)) from view_baseall_nereids order by k1; + """ + qt_test_view_4 """ select * from test_view7_nereids order by k1; """ + + sql """ + create view IF NOT EXISTS test_view8_nereids (k1,k2,k3) as + select *, array_map(x->x>0,k3) from view_baseall_nereids order by k1; + """ + qt_test_view_5 """ select * from test_view8_nereids order by k1; """ + + sql """DROP TABLE IF EXISTS view_column_name_test_nereids""" + sql """ + CREATE TABLE IF NOT EXISTS view_column_name_test_nereids + ( + `timestamp` DATE NOT NULL COMMENT "['0000-01-01', '9999-12-31']", + `type` TINYINT NOT NULL COMMENT "[-128, 127]", + `error_code` INT COMMENT "[-2147483648, 2147483647]", + `error_msg` VARCHAR(300) COMMENT "[1-65533]", + `op_id` BIGINT COMMENT "[-9223372036854775808, 9223372036854775807]", + `op_time` DATETIME COMMENT "['0000-01-01 00:00:00', '9999-12-31 23:59:59']", + `target` float COMMENT "4 字节", + `source` double COMMENT "8 字节", + `lost_cost` decimal(12,2) COMMENT "", + `remark` string COMMENT "1m size", + `op_userid` LARGEINT COMMENT "[-2^127 + 1 ~ 2^127 - 1]", + `plate` SMALLINT COMMENT "[-32768, 32767]", + `iscompleted` boolean COMMENT "true 或者 false" + ) + DISTRIBUTED BY HASH(`type`) BUCKETS 1 + PROPERTIES ('replication_num' = '1'); + """ + + sql """ + DROP VIEW IF EXISTS v1 + """ + sql """ + CREATE VIEW v1 AS + SELECT + error_code, + 1, + 'string', + now(), + dayofyear(op_time), + cast (source AS BIGINT), + min(`timestamp`) OVER ( + ORDER BY + op_time DESC ROWS BETWEEN UNBOUNDED PRECEDING + AND 1 FOLLOWING + ), + 1 > 2, + 2 + 3, + 1 IN (1, 2, 3, 4), + remark LIKE '%like', + CASE WHEN remark = 's' THEN 1 ELSE 2 END, + TRUE | FALSE + FROM + view_column_name_test_nereids + """ + qt_test_view_6 """ SHOW VIEW FROM view_column_name_test_nereids;""" + + // test with as + sql """ + DROP TABLE IF EXISTS mal_test_view + """ + + sql """ + create table mal_test_view(pk int, a int, b int) distributed by hash(pk) buckets 10 + properties('replication_num' = '1'); + """ + + sql """ + insert into mal_test_view values(2,1,3),(1,1,2),(3,5,6),(6,null,6),(4,5,6),(2,1,4),(2,3,5),(1,1,4) + ,(3,5,6),(3,5,null),(6,7,1),(2,1,7),(2,4,2),(2,3,9),(1,3,6),(3,5,8),(3,2,8); + """ + sql "DROP VIEW if exists test_view_with_as" + sql """CREATE VIEW if not exists test_view_with_as AS ( + with t1 as (select * from mal_test_view), t2 as (select * from mal_test_view), + t3 as (select * from mal_test_view) SELECT * FROM t1);""" + qt_test_with_as "select * from test_view_with_as order by pk, a, b" + qt_test_with_as_sql "show create view test_view_with_as" + + // test union + sql "DROP VIEW if exists test_view_union" + sql """CREATE VIEW test_view_union(c1,c2,c3) AS + (select * from mal_test_view Union all SELECT * FROM mal_test_view);""" + qt_test_union "select c1,c2,c3 from test_view_union order by c1,c2,c3" + qt_test_union_sql "show create view test_view_union" + + // test count(*) + sql "drop view if exists test_view_count_star;" + sql "CREATE VIEW test_view_count_star(c1) AS (select count(*) from mal_test_view having count(*) > 0);" + qt_test_count_star "select c1 from test_view_count_star order by c1" + qt_test_count_star_sql "show create view test_view_count_star" + + // test expression + sql "drop view if exists test_view_expression;" + sql """CREATE VIEW test_view_expression(c1,c2,c3) AS (select a+1,abs(a)+2+1 as c2, cast(b as varchar(10)) as c1 from mal_test_view);""" + qt_test_expression "select * from test_view_expression order by c1,c2,c3" + qt_test_expression_sql "show create view test_view_expression;" + + // test alias + sql "drop view if exists test_view_alias;" + sql """CREATE VIEW test_view_alias(c1,c2,c3) AS ( + select c8 as c9, c2 as c3, c1 as c4 from (select a+1 c8,abs(a)+2+1 as c2, cast(b as varchar(10)) as c1 from mal_test_view) t);""" + qt_test_alias "select * from test_view_alias order by c1,c2,c3;" + qt_test_alias_sql "show create view test_view_alias;" + + // test * except + sql "drop view if exists test_view_star_except;" + sql """ + create view test_view_star_except as select * except(pk) from mal_test_view; + """ + qt_test_star_except "select * from test_view_star_except order by a, b;" + qt_test_star_except_sql "show create view test_view_star_except;" + + // test create view from view + sql "drop view if exists test_view_from_view" + sql "create view test_view_from_view(c1,c2,c3) as select * from test_view_with_as" + qt_test_create_view_from_view "select * from test_view_from_view order by c1,c2,c3" + qt_test_create_view_from_view_sql "show create view test_view_from_view" + + // test backquote in name + + sql "drop view if exists test_backquote_in_view_define;" + sql "create view test_backquote_in_view_define(`ab``c`, c2) as select a,b from mal_test_view;" + qt_test_backquote_in_view_define "select * from test_backquote_in_view_define order by `ab``c`, c2;" + qt_test_backquote_in_view_define_sql "show create view test_backquote_in_view_define;" + + sql "drop view if exists test_backquote_in_table_alias;" + sql "create view test_backquote_in_table_alias(c1, c2) as select * from (select a,b from mal_test_view) `ab``c`;" + qt_test_backquote_in_table_alias "select * from test_backquote_in_table_alias order by c1, c2;" + qt_test_backquote_in_table_alias_sql "show create view test_backquote_in_table_alias;" +} diff --git a/regression-test/suites/show_p0/test_show_create_table_and_views.groovy b/regression-test/suites/show_p0/test_show_create_table_and_views.groovy index 6c6322e5c4aa03..5872cc95d5ae5c 100644 --- a/regression-test/suites/show_p0/test_show_create_table_and_views.groovy +++ b/regression-test/suites/show_p0/test_show_create_table_and_views.groovy @@ -30,6 +30,7 @@ suite("test_show_create_table_and_views", "show") { String rollupName = "${suiteName}_rollup" String likeName = "${suiteName}_like" + sql "SET enable_nereids_planner=false;" sql "CREATE DATABASE IF NOT EXISTS ${dbName}" sql "DROP TABLE IF EXISTS ${dbName}.${tableName}" sql """ @@ -76,6 +77,7 @@ suite("test_show_create_table_and_views", "show") { qt_show "SHOW CREATE TABLE ${dbName}.${tableName}" qt_select "SELECT * FROM ${dbName}.${tableName} ORDER BY user_id, good_id" + sql "drop view if exists ${dbName}.${viewName};" // create view and show sql """ CREATE VIEW IF NOT EXISTS ${dbName}.${viewName} (user_id, cost) diff --git a/regression-test/suites/show_p0/test_show_create_table_and_views_nereids.groovy b/regression-test/suites/show_p0/test_show_create_table_and_views_nereids.groovy new file mode 100644 index 00000000000000..88fd00444adec6 --- /dev/null +++ b/regression-test/suites/show_p0/test_show_create_table_and_views_nereids.groovy @@ -0,0 +1,128 @@ +// 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. + +suite("test_show_create_table_and_views_nereids", "show") { + def ret = sql "SHOW FRONTEND CONFIG like '%enable_feature_binlog%';" + logger.info("${ret}") + if (ret.size() != 0 && ret[0].size() > 1 && ret[0][1] == 'false') { + logger.info("enable_feature_binlog=false in frontend config, no need to run this case.") + return + } + + String suiteName = "show_create_table_and_views_nereids" + String dbName = "${suiteName}_db" + String tableName = "${suiteName}_table" + String viewName = "${suiteName}_view" + String rollupName = "${suiteName}_rollup" + String likeName = "${suiteName}_like" + + sql "SET enable_nereids_planner=true;" + sql "SET enable_fallback_to_original_planner=false;" + + sql "CREATE DATABASE IF NOT EXISTS ${dbName}" + sql "DROP TABLE IF EXISTS ${dbName}.${tableName}" + sql """ + CREATE TABLE ${dbName}.${tableName} ( + `user_id` LARGEINT NOT NULL, + `good_id` LARGEINT NOT NULL, + `cost` BIGINT SUM DEFAULT "0" + ) + AGGREGATE KEY(`user_id`, `good_id`) + PARTITION BY RANGE(`good_id`) + ( + PARTITION p1 VALUES LESS THAN ("100"), + PARTITION p2 VALUES LESS THAN ("200"), + PARTITION p3 VALUES LESS THAN ("300"), + PARTITION p4 VALUES LESS THAN ("400"), + PARTITION p5 VALUES LESS THAN ("500"), + PARTITION p6 VALUES LESS THAN ("600"), + PARTITION p7 VALUES LESS THAN MAXVALUE + ) + DISTRIBUTED BY HASH(`user_id`) BUCKETS 2 + PROPERTIES ( + "replication_num" = "1" + ) + """ + + sql """INSERT INTO ${dbName}.${tableName} VALUES + (1, 1, 10), + (1, 1, 20), + (1, 2, 5), + (1, 3, 10), + (2, 1, 0), + (2, 1, 100), + (3, 1, 10), + (2, 2, 10), + (2, 3, 44), + (3, 2, 1), + (100, 100, 1), + (200, 20, 1), + (300, 20, 1), + (1, 300, 2), + (2, 200, 1111), + (23, 900, 1)""" + + qt_show "SHOW CREATE TABLE ${dbName}.${tableName}" + qt_select "SELECT * FROM ${dbName}.${tableName} ORDER BY user_id, good_id" + + sql "drop view if exists ${dbName}.${viewName};" + // create view and show + sql """ + CREATE VIEW IF NOT EXISTS ${dbName}.${viewName} (user_id, cost) + AS + SELECT user_id, cost FROM ${dbName}.${tableName} + WHERE good_id = 2 + """ + qt_select "SELECT * FROM ${dbName}.${viewName} ORDER BY user_id" + qt_show "SHOW CREATE VIEW ${dbName}.${viewName}" + + // create rollup + sql """ALTER TABLE ${dbName}.${tableName} + ADD ROLLUP ${rollupName} (user_id, cost) + """ + + def isAlterTableFinish = { -> + def records = sql """SHOW ALTER TABLE ROLLUP FROM ${dbName}""" + for (def row in records) { + if (row[5] == "${rollupName}" && row[8] == "FINISHED") { + return true + } + } + false + } + while (!isAlterTableFinish()) { + Thread.sleep(100) + } + + qt_select "SELECT user_id, SUM(cost) FROM ${dbName}.${tableName} GROUP BY user_id ORDER BY user_id" + qt_show "SHOW CREATE TABLE ${dbName}.${tableName}" + + // create like + sql "CREATE TABLE ${dbName}.${likeName} LIKE ${dbName}.${tableName}" + qt_show "SHOW CREATE TABLE ${dbName}.${likeName}" + + // create like with rollup + sql "CREATE TABLE ${dbName}.${likeName}_with_rollup LIKE ${dbName}.${tableName} WITH ROLLUP" + qt_show "SHOW CREATE TABLE ${dbName}.${likeName}_with_rollup" + + sql "DROP TABLE IF EXISTS ${dbName}.${likeName}_with_rollup FORCE" + sql "DROP TABLE ${dbName}.${likeName} FORCE" + sql "DROP VIEW ${dbName}.${viewName}" + sql "DROP TABLE ${dbName}.${tableName} FORCE" + sql "DROP DATABASE ${dbName} FORCE" +} +