diff --git a/dbms/src/Databases/DatabasesCommon.cpp b/dbms/src/Databases/DatabasesCommon.cpp index 73717acf5072120f59e08908a4848d0d34a86c2c..2d3bd9395a576d31012241e8b2da24c76025ca33 100644 --- a/dbms/src/Databases/DatabasesCommon.cpp +++ b/dbms/src/Databases/DatabasesCommon.cpp @@ -27,6 +27,8 @@ String getTableDefinitionFromCreateQuery(const ASTPtr & query) /// We remove everything that is not needed for ATTACH from the query. create.attach = true; create.database.clear(); + create.as_database.clear(); + create.as_table.clear(); create.if_not_exists = false; create.is_populate = false; @@ -34,13 +36,6 @@ String getTableDefinitionFromCreateQuery(const ASTPtr & query) if (!create.is_view && !create.is_materialized_view) create.select = nullptr; - /// For "MATERIALIZED VIEW x TO y" it's necessary to save destination table - if (!create.is_materialized_view || create.storage) - { - create.as_database.clear(); - create.as_table.clear(); - } - std::ostringstream statement_stream; formatAST(create, statement_stream, 0, false); statement_stream << '\n'; diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index a4ef2d64c9d4f73f733f1d2e5b7e8f6bbceac6f7..4cbf6b0864c9839fe50c2248be892c40d76efbfb 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -480,12 +480,15 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create) create.attach = true; } + if (create.to_database.empty()) + create.to_database = current_database; + std::unique_ptr interpreter_select; Block as_select_sample; /// For `view` type tables, you may need `sample_block` to get the columns. if (create.select && (!create.attach || (!create.columns && (create.is_view || create.is_materialized_view)))) { - create.select->setDatabaseIfNeeded(database_name); + create.select->setDatabaseIfNeeded(current_database); interpreter_select = std::make_unique(create.select->ptr(), context); as_select_sample = interpreter_select->getSampleBlock(); } diff --git a/dbms/src/Parsers/ASTCreateQuery.h b/dbms/src/Parsers/ASTCreateQuery.h index 895b109174e0dba5363a63b32752d2507d783a5b..5f1c8c668912617a6808dc3ea5c1e814d44742f7 100644 --- a/dbms/src/Parsers/ASTCreateQuery.h +++ b/dbms/src/Parsers/ASTCreateQuery.h @@ -88,6 +88,8 @@ public: String database; String table; ASTExpressionList * columns = nullptr; + String to_database; /// For CREATE MATERIALIZED VIEW mv TO table. + String to_table; ASTStorage * storage = nullptr; String as_database; String as_table; @@ -166,11 +168,18 @@ protected: formatOnCluster(settings); } + if (!to_table.empty()) + { + settings.ostr + << (settings.hilite ? hilite_keyword : "") << " TO " << (settings.hilite ? hilite_none : "") + << (!to_database.empty() ? backQuoteIfNeed(to_database) + "." : "") << backQuoteIfNeed(to_table); + } + if (!as_table.empty()) { - std::string what = (!is_materialized_view ? " AS " : " TO "); - settings.ostr << (settings.hilite ? hilite_keyword : "") << what << (settings.hilite ? hilite_none : "") - << (!as_database.empty() ? backQuoteIfNeed(as_database) + "." : "") << backQuoteIfNeed(as_table); + settings.ostr + << (settings.hilite ? hilite_keyword : "") << " AS " << (settings.hilite ? hilite_none : "") + << (!as_database.empty() ? backQuoteIfNeed(as_database) + "." : "") << backQuoteIfNeed(as_table); } if (columns) diff --git a/dbms/src/Parsers/ParserCreateQuery.cpp b/dbms/src/Parsers/ParserCreateQuery.cpp index 35b9781b2a6851c42b19916cd5227be1f49f4530..6f3d3a4db7cc9405d89e6f5dc3740ad74dada92b 100644 --- a/dbms/src/Parsers/ParserCreateQuery.cpp +++ b/dbms/src/Parsers/ParserCreateQuery.cpp @@ -208,6 +208,8 @@ bool ParserCreateQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) ASTPtr database; ASTPtr table; ASTPtr columns; + ASTPtr to_database; + ASTPtr to_table; ASTPtr storage; ASTPtr as_database; ASTPtr as_table; @@ -219,7 +221,6 @@ bool ParserCreateQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) bool is_materialized_view = false; bool is_populate = false; bool is_temporary = false; - bool to_table = false; if (!s_create.ignore(pos, expected)) { @@ -362,17 +363,13 @@ bool ParserCreateQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) // TO [db.]table if (ParserKeyword{"TO"}.ignore(pos, expected)) { - to_table = true; - - /// FIXME: as_table is ambiguous, it is set in AS clause also - - if (!name_p.parse(pos, as_table, expected)) + if (!name_p.parse(pos, to_table, expected)) return false; if (s_dot.ignore(pos, expected)) { - as_database = as_table; - if (!name_p.parse(pos, as_table, expected)) + to_database = to_table; + if (!name_p.parse(pos, to_table, expected)) return false; } } @@ -422,6 +419,11 @@ bool ParserCreateQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) query->table = typeid_cast(*table).name; query->cluster = cluster_str; + if (to_database) + query->to_database = typeid_cast(*to_database).name; + if (to_table) + query->to_table = typeid_cast(*to_table).name; + query->set(query->columns, columns); query->set(query->storage, storage); if (as_database) diff --git a/dbms/src/Storages/StorageFactory.cpp b/dbms/src/Storages/StorageFactory.cpp index d6bcb9da42f5ecdf324f5ef27eeb3448c360e2ec..5450075fcabfb7a881980eed397bd712cc465cfd 100644 --- a/dbms/src/Storages/StorageFactory.cpp +++ b/dbms/src/Storages/StorageFactory.cpp @@ -388,9 +388,6 @@ StoragePtr StorageFactory::get( checkAllTypesAreAllowedInTable(materialized_columns); checkAllTypesAreAllowedInTable(alias_columns); - if (!query.storage) - throw Exception("Incorrect CREATE query: ENGINE required", ErrorCodes::ENGINE_REQUIRED); - if (query.is_materialized_view) { /// Pass local_context here to convey setting for inner table @@ -400,6 +397,9 @@ StoragePtr StorageFactory::get( attach); } + if (!query.storage) + throw Exception("Incorrect CREATE query: ENGINE required", ErrorCodes::ENGINE_REQUIRED); + ASTStorage & storage_def = *query.storage; const ASTFunction & engine_def = *storage_def.engine; @@ -423,7 +423,7 @@ StoragePtr StorageFactory::get( auto check_arguments_empty = [&] { - if (args_ptr) + if (args_ptr && !args_ptr->empty()) throw Exception( "Engine " + name + " doesn't support any arguments (" + toString(args_ptr->size()) + " given)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); diff --git a/dbms/src/Storages/StorageMaterializedView.cpp b/dbms/src/Storages/StorageMaterializedView.cpp index 778dffdf42c2a8c65ed335235e02028363cc4214..1929ffc815df747ce3b75f386e029a9841d1fa95 100644 --- a/dbms/src/Storages/StorageMaterializedView.cpp +++ b/dbms/src/Storages/StorageMaterializedView.cpp @@ -70,8 +70,10 @@ StorageMaterializedView::StorageMaterializedView( if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); - if (!query.storage && query.as_table.empty()) - throw Exception("ENGINE of MaterializedView should be specified explicitly", ErrorCodes::INCORRECT_QUERY); + if (!query.storage && query.to_table.empty()) + throw Exception( + "You must specify where to save results of a MaterializedView query: either ENGINE or an existing table in a TO clause", + ErrorCodes::INCORRECT_QUERY); extractDependentTable(*query.select, select_database_name, select_table_name); @@ -81,10 +83,10 @@ StorageMaterializedView::StorageMaterializedView( DatabaseAndTableName(database_name, table_name)); // If the destination table is not set, use inner table - if (!query.as_table.empty()) + if (!query.to_table.empty()) { - target_database_name = query.as_database; - target_table_name = query.as_table; + target_database_name = query.to_database; + target_table_name = query.to_table; } else { diff --git a/dbms/tests/queries/0_stateless/00508_materialized_view_to.reference b/dbms/tests/queries/0_stateless/00508_materialized_view_to.reference index 099b7d91c923c6fc0fdaaea4be224d5d9d4fcfca..155b5ffd0828f791c9f45ff1af4bd9e04376f2ba 100644 --- a/dbms/tests/queries/0_stateless/00508_materialized_view_to.reference +++ b/dbms/tests/queries/0_stateless/00508_materialized_view_to.reference @@ -2,3 +2,9 @@ 2 1 2 +1 +2 +3 +1 +2 +3 diff --git a/dbms/tests/queries/0_stateless/00508_materialized_view_to.sql b/dbms/tests/queries/0_stateless/00508_materialized_view_to.sql index e327f131b23778784a1c677c9c14dbc57246959b..9373722d5751e5ba6e5459bbd55207f67c3a73cb 100644 --- a/dbms/tests/queries/0_stateless/00508_materialized_view_to.sql +++ b/dbms/tests/queries/0_stateless/00508_materialized_view_to.sql @@ -5,16 +5,26 @@ DROP TABLE IF EXISTS test.mv; CREATE TABLE test.src (x UInt8) ENGINE = Null; CREATE TABLE test.dst (x UInt8) ENGINE = Memory; -CREATE MATERIALIZED VIEW test.mv TO test.dst AS SELECT * FROM test.src; -INSERT INTO test.src VALUES (1), (2); +USE test; + +CREATE MATERIALIZED VIEW mv TO dst AS SELECT * FROM src; + +INSERT INTO src VALUES (1), (2); +SELECT * FROM mv; -- Detach MV and see if the data is still readable -DETACH TABLE test.mv; -SELECT * FROM test.dst; +DETACH TABLE mv; +SELECT * FROM dst; + +USE default; -- Reattach MV (shortcut) ATTACH TABLE test.mv; +INSERT INTO test.src VALUES (3); + +SELECT * FROM test.mv; + -- Drop the MV and see if the data is still readable DROP TABLE test.mv; SELECT * FROM test.dst;