[feature](view) support create or replace view stmt (#40715) (#40915)

pick #40715 to branch-2.1
This commit is contained in:
camby
2024-09-19 01:10:43 +08:00
committed by GitHub
parent 774efe78e6
commit 84f0b1fbfe
9 changed files with 168 additions and 23 deletions

View File

@ -60,7 +60,7 @@ statementBase
properties=propertyClause?
(BROKER extProperties=propertyClause)?
(AS query)? #createTable
| CREATE VIEW (IF NOT EXISTS)? name=multipartIdentifier
| CREATE (OR REPLACE)? VIEW (IF NOT EXISTS)? name=multipartIdentifier
(LEFT_PAREN cols=simpleColumnDefs RIGHT_PAREN)?
(COMMENT STRING_LITERAL)? AS query #createView
| ALTER VIEW name=multipartIdentifier (LEFT_PAREN cols=simpleColumnDefs RIGHT_PAREN)?

View File

@ -961,6 +961,7 @@ nonterminal List<Map<String, String>> opt_with_analysis_properties;
nonterminal String opt_db, procedure_or_function, opt_comment, opt_comment_null, opt_engine;
nonterminal ColumnDef.DefaultValue opt_default_value;
nonterminal Boolean opt_or_replace;
nonterminal Boolean opt_if_exists, opt_if_not_exists;
nonterminal Boolean opt_external;
nonterminal Boolean opt_force;
@ -1892,10 +1893,10 @@ create_stmt ::=
{:
RESULT = new CreateUserStmt(ifNotExists, user, userRole, passwdOptions, comment);
:}
| KW_CREATE KW_VIEW opt_if_not_exists:ifNotExists table_name:viewName
| KW_CREATE opt_or_replace:orReplace KW_VIEW opt_if_not_exists:ifNotExists table_name:viewName
opt_col_with_comment_list:columns opt_comment:comment KW_AS query_stmt:view_def
{:
RESULT = new CreateViewStmt(ifNotExists, viewName, columns, comment, view_def);
RESULT = new CreateViewStmt(ifNotExists, orReplace, viewName, columns, comment, view_def);
:}
| KW_CREATE opt_read_only:isReadOnly KW_REPOSITORY ident:repoName KW_WITH storage_backend:storage
{:
@ -3865,6 +3866,16 @@ opt_index_type ::=
:}
;
opt_or_replace ::=
{:
RESULT = false;
:}
| KW_OR KW_REPLACE
{:
RESULT = true;
:}
;
opt_if_exists ::=
{:
RESULT = false;

View File

@ -68,11 +68,22 @@ public class BaseViewStmt extends DdlStmt {
return tableName.getTbl();
}
public TableName getTableName() {
return tableName;
}
public List<Column> getColumns() {
return finalCols;
}
public List<ColWithComment> getColWithComments() {
return cols;
}
public QueryStmt getViewDefStmt() {
return viewDefStmt;
}
public String getInlineViewDef() {
return inlineViewDef;
}

View File

@ -25,6 +25,7 @@ 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.exceptions.AnalysisException;
import org.apache.doris.qe.ConnectContext;
import com.google.common.base.Strings;
@ -37,12 +38,14 @@ public class CreateViewStmt extends BaseViewStmt {
private static final Logger LOG = LogManager.getLogger(CreateViewStmt.class);
private final boolean ifNotExists;
private final boolean orReplace;
private final String comment;
public CreateViewStmt(boolean ifNotExists, TableName tableName, List<ColWithComment> cols,
public CreateViewStmt(boolean ifNotExists, boolean orReplace, TableName tableName, List<ColWithComment> cols,
String comment, QueryStmt queryStmt) {
super(tableName, cols, queryStmt);
this.ifNotExists = ifNotExists;
this.orReplace = orReplace;
this.comment = Strings.nullToEmpty(comment);
}
@ -50,6 +53,10 @@ public class CreateViewStmt extends BaseViewStmt {
return ifNotExists;
}
public boolean isSetOrReplace() {
return orReplace;
}
public String getComment() {
return comment;
}
@ -63,6 +70,10 @@ public class CreateViewStmt extends BaseViewStmt {
// disallow external catalog
Util.prohibitExternalCatalog(tableName.getCtl(), this.getClass().getSimpleName());
if (orReplace && ifNotExists) {
throw new AnalysisException("[OR REPLACE] and [IF NOT EXISTS] cannot used at the same time");
}
// check privilege
if (!Env.getCurrentEnv().getAccessManager()
.checkTblPriv(ConnectContext.get(), tableName.getCtl(), tableName.getDb(),

View File

@ -5317,33 +5317,49 @@ public class Env {
Database db = getInternalCatalog().getDbOrDdlException(dbName);
// check if table exists in db
boolean replace = false;
if (db.getTable(tableName).isPresent()) {
if (stmt.isSetIfNotExists()) {
LOG.info("create view[{}] which already exists", tableName);
return;
} else if (stmt.isSetOrReplace()) {
replace = true;
LOG.info("view[{}] already exists, need to replace it", tableName);
} else {
ErrorReport.reportDdlException(ErrorCode.ERR_TABLE_EXISTS_ERROR, tableName);
}
}
List<Column> columns = stmt.getColumns();
if (replace) {
AlterViewStmt alterViewStmt = new AlterViewStmt(stmt.getTableName(), stmt.getColWithComments(),
stmt.getViewDefStmt());
alterViewStmt.setInlineViewDef(stmt.getInlineViewDef());
try {
alterView(alterViewStmt);
} catch (UserException e) {
throw new DdlException("failed to replace view[" + tableName + "], reason=" + e.getMessage());
}
LOG.info("successfully replace view[{}]", tableName);
} else {
List<Column> columns = stmt.getColumns();
long tableId = Env.getCurrentEnv().getNextId();
View newView = new View(tableId, tableName, columns);
newView.setComment(stmt.getComment());
newView.setInlineViewDefWithSqlMode(stmt.getInlineViewDef(),
ConnectContext.get().getSessionVariable().getSqlMode());
// init here in case the stmt string from view.toSql() has some syntax error.
try {
newView.init();
} catch (UserException e) {
throw new DdlException("failed to init view stmt, reason=" + e.getMessage());
}
long tableId = Env.getCurrentEnv().getNextId();
View newView = new View(tableId, tableName, columns);
newView.setComment(stmt.getComment());
newView.setInlineViewDefWithSqlMode(stmt.getInlineViewDef(),
ConnectContext.get().getSessionVariable().getSqlMode());
// init here in case the stmt string from view.toSql() has some syntax error.
try {
newView.init();
} catch (UserException e) {
throw new DdlException("failed to init view stmt, reason=" + e.getMessage());
}
if (!((Database) db).createTableWithLock(newView, false, stmt.isSetIfNotExists()).first) {
throw new DdlException("Failed to create view[" + tableName + "].");
if (!((Database) db).createTableWithLock(newView, false, stmt.isSetIfNotExists()).first) {
throw new DdlException("Failed to create view[" + tableName + "].");
}
LOG.info("successfully create view[" + tableName + "-" + newView.getId() + "]");
}
LOG.info("successfully create view[" + tableName + "-" + newView.getId() + "]");
}
public FunctionRegistry getFunctionRegistry() {

View File

@ -2529,7 +2529,11 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
String comment = ctx.STRING_LITERAL() == null ? "" : LogicalPlanBuilderAssistant.escapeBackSlash(
ctx.STRING_LITERAL().getText().substring(1, ctx.STRING_LITERAL().getText().length() - 1));
String querySql = getOriginSql(ctx.query());
CreateViewInfo info = new CreateViewInfo(ctx.EXISTS() != null, new TableNameInfo(nameParts),
if (ctx.REPLACE() != null && ctx.EXISTS() != null) {
throw new AnalysisException("[OR REPLACE] and [IF NOT EXISTS] cannot used at the same time");
}
CreateViewInfo info = new CreateViewInfo(ctx.EXISTS() != null, ctx.REPLACE() != null,
new TableNameInfo(nameParts),
comment, querySql,
ctx.cols == null ? Lists.newArrayList() : visitSimpleColumnDefs(ctx.cols));
return new CreateViewCommand(info);

View File

@ -46,13 +46,15 @@ import java.util.Set;
*/
public class CreateViewInfo extends BaseViewInfo {
private final boolean ifNotExists;
private final boolean orReplace;
private final String comment;
/** constructor*/
public CreateViewInfo(boolean ifNotExists, TableNameInfo viewName, String comment,
public CreateViewInfo(boolean ifNotExists, boolean orReplace, TableNameInfo viewName, String comment,
String querySql, List<SimpleColumnDefinition> simpleColumnDefinitions) {
super(viewName, querySql, simpleColumnDefinitions);
this.ifNotExists = ifNotExists;
this.orReplace = orReplace;
this.comment = comment;
}
@ -93,8 +95,8 @@ public class CreateViewInfo extends BaseViewInfo {
for (SimpleColumnDefinition def : simpleColumnDefinitions) {
cols.add(def.translateToColWithComment());
}
CreateViewStmt createViewStmt = new CreateViewStmt(ifNotExists, viewName.transferToTableName(), cols, comment,
null);
CreateViewStmt createViewStmt = new CreateViewStmt(ifNotExists, orReplace, viewName.transferToTableName(), cols,
comment, null);
// expand star(*) in project list and replace table name with qualifier
String rewrittenSql = rewriteSql(ctx.getStatementContext().getIndexInSqlToString());

View File

@ -0,0 +1,10 @@
-- This file is automatically generated. You should know what you did if you want to edit this
-- !sql_1 --
1 1 1
-- !sql_2 --
2 2 2
-- !sql_3 --
1 1 1

View File

@ -0,0 +1,80 @@
// 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_or_replace_view") {
sql "SET enable_nereids_planner=true"
// create two test tables and insert some data
sql """DROP TABLE IF EXISTS test_create_or_replace_view_tbl1"""
sql """
CREATE TABLE IF NOT EXISTS test_create_or_replace_view_tbl1
(k1 int, k2 int, v int)
DUPLICATE KEY(k1) DISTRIBUTED BY HASH(k1) BUCKETS 1
PROPERTIES( "replication_num" = "1");
"""
sql """DROP TABLE IF EXISTS test_create_or_replace_view_tbl2"""
sql """
CREATE TABLE IF NOT EXISTS test_create_or_replace_view_tbl2
(k1 int, k2 int, v int)
DUPLICATE KEY(k1) DISTRIBUTED BY HASH(k1) BUCKETS 1
PROPERTIES( "replication_num" = "1");
"""
sql """INSERT INTO test_create_or_replace_view_tbl1 VALUES(1,1,1)"""
sql """INSERT INTO test_create_or_replace_view_tbl2 VALUES(2,2,2)"""
sql "sync"
// create view
sql "drop view if exists view_test_create_or_replace_view"
sql """
CREATE VIEW IF NOT EXISTS view_test_create_or_replace_view
AS SELECT * FROM test_create_or_replace_view_tbl1;
"""
qt_sql_1 """select * from view_test_create_or_replace_view"""
// create or replace view in nereids
sql """
CREATE OR REPLACE VIEW view_test_create_or_replace_view
AS SELECT * FROM test_create_or_replace_view_tbl2;
"""
qt_sql_2 """select * from view_test_create_or_replace_view"""
test {
sql """
CREATE OR REPLACE VIEW IF NOT EXISTS view_test_create_or_replace_view
AS SELECT * FROM test_create_or_replace_view_tbl1;
"""
exception "[OR REPLACE] and [IF NOT EXISTS] cannot used at the same time"
}
// create or replace view in non-nereids
sql "SET enable_nereids_planner=false"
sql """
CREATE OR REPLACE VIEW view_test_create_or_replace_view
AS SELECT * FROM test_create_or_replace_view_tbl1;
"""
qt_sql_3 """select * from view_test_create_or_replace_view"""
test {
sql """
CREATE OR REPLACE VIEW IF NOT EXISTS view_test_create_or_replace_view
AS SELECT * FROM test_create_or_replace_view_tbl1;
"""
exception "[OR REPLACE] and [IF NOT EXISTS] cannot used at the same time"
}
sql """drop view if exists view_test_create_or_replace_view"""
sql """DROP TABLE IF EXISTS test_create_or_replace_view_tbl1"""
sql """DROP TABLE IF EXISTS test_create_or_replace_view_tbl2"""
}