mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 00:30:49 +00:00
Merge pull request #66590 from vitlibar/move-view-targets-to-separate-ast
Move view targets to separate AST class ASTViewTargets
This commit is contained in:
commit
4f02ded96e
@ -105,7 +105,7 @@ bool compareRestoredTableDef(const IAST & restored_table_create_query, const IAS
|
||||
auto new_query = query.clone();
|
||||
adjustCreateQueryForBackup(new_query, global_context);
|
||||
ASTCreateQuery & create = typeid_cast<ASTCreateQuery &>(*new_query);
|
||||
create.setUUID({});
|
||||
create.resetUUIDs();
|
||||
create.if_not_exists = false;
|
||||
return new_query;
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <Backups/RestoreCoordinationLocal.h>
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/formatAST.h>
|
||||
#include <Common/logger_useful.h>
|
||||
|
||||
@ -67,7 +68,7 @@ void RestoreCoordinationLocal::generateUUIDForTable(ASTCreateQuery & create_quer
|
||||
auto it = create_query_uuids.find(query_str);
|
||||
if (it != create_query_uuids.end())
|
||||
{
|
||||
create_query.setUUID(it->second);
|
||||
it->second.copyToQuery(create_query);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -79,7 +80,8 @@ void RestoreCoordinationLocal::generateUUIDForTable(ASTCreateQuery & create_quer
|
||||
return;
|
||||
}
|
||||
|
||||
auto new_uuids = create_query.generateRandomUUID(/* always_generate_new_uuid= */ true);
|
||||
CreateQueryUUIDs new_uuids{create_query, /* generate_random= */ true, /* force_random= */ true};
|
||||
new_uuids.copyToQuery(create_query);
|
||||
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
|
@ -1,16 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <Backups/IRestoreCoordination.h>
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/CreateQueryUUIDs.h>
|
||||
#include <Common/Logger.h>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Poco { class Logger; }
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
class ASTCreateQuery;
|
||||
|
||||
|
||||
/// Implementation of the IRestoreCoordination interface performing coordination in memory.
|
||||
class RestoreCoordinationLocal : public IRestoreCoordination
|
||||
@ -55,7 +56,7 @@ private:
|
||||
|
||||
std::set<std::pair<String /* database_zk_path */, String /* table_name */>> acquired_tables_in_replicated_databases;
|
||||
std::unordered_set<String /* table_zk_path */> acquired_data_in_replicated_tables;
|
||||
std::unordered_map<String, ASTCreateQuery::UUIDs> create_query_uuids;
|
||||
std::unordered_map<String, CreateQueryUUIDs> create_query_uuids;
|
||||
std::unordered_set<String /* root_zk_path */> acquired_data_in_keeper_map_tables;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <Backups/RestoreCoordinationRemote.h>
|
||||
#include <Backups/BackupCoordinationStageSync.h>
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/CreateQueryUUIDs.h>
|
||||
#include <Parsers/formatAST.h>
|
||||
#include <Functions/UserDefined/UserDefinedSQLObjectType.h>
|
||||
#include <Common/ZooKeeper/KeeperException.h>
|
||||
@ -269,7 +270,8 @@ bool RestoreCoordinationRemote::acquireInsertingDataForKeeperMap(const String &
|
||||
void RestoreCoordinationRemote::generateUUIDForTable(ASTCreateQuery & create_query)
|
||||
{
|
||||
String query_str = serializeAST(create_query);
|
||||
String new_uuids_str = create_query.generateRandomUUID(/* always_generate_new_uuid= */ true).toString();
|
||||
CreateQueryUUIDs new_uuids{create_query, /* generate_random= */ true, /* force_random= */ true};
|
||||
String new_uuids_str = new_uuids.toString();
|
||||
|
||||
auto holder = with_retries.createRetriesControlHolder("generateUUIDForTable");
|
||||
holder.retries_ctl.retryLoop(
|
||||
@ -281,11 +283,14 @@ void RestoreCoordinationRemote::generateUUIDForTable(ASTCreateQuery & create_que
|
||||
Coordination::Error res = zk->tryCreate(path, new_uuids_str, zkutil::CreateMode::Persistent);
|
||||
|
||||
if (res == Coordination::Error::ZOK)
|
||||
{
|
||||
new_uuids.copyToQuery(create_query);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res == Coordination::Error::ZNODEEXISTS)
|
||||
{
|
||||
create_query.setUUID(ASTCreateQuery::UUIDs::fromString(zk->get(path)));
|
||||
CreateQueryUUIDs::fromString(zk->get(path)).copyToQuery(create_query);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -80,13 +80,20 @@ namespace
|
||||
/// CREATE TABLE or CREATE DICTIONARY or CREATE VIEW or CREATE TEMPORARY TABLE or CREATE DATABASE query.
|
||||
void visitCreateQuery(const ASTCreateQuery & create)
|
||||
{
|
||||
QualifiedTableName to_table{create.to_table_id.database_name, create.to_table_id.table_name};
|
||||
if (!to_table.table.empty())
|
||||
if (create.targets)
|
||||
{
|
||||
for (const auto & target : create.targets->targets)
|
||||
{
|
||||
const auto & table_id = target.table_id;
|
||||
if (!table_id.table_name.empty())
|
||||
{
|
||||
/// TO target_table (for materialized views)
|
||||
if (to_table.database.empty())
|
||||
to_table.database = current_database;
|
||||
dependencies.emplace(to_table);
|
||||
QualifiedTableName target_name{table_id.database_name, table_id.table_name};
|
||||
if (target_name.database.empty())
|
||||
target_name.database = current_database;
|
||||
dependencies.emplace(target_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QualifiedTableName as_table{create.as_database, create.as_table};
|
||||
|
@ -86,12 +86,19 @@ namespace
|
||||
create.as_table = as_table_new.table;
|
||||
}
|
||||
|
||||
QualifiedTableName to_table{create.to_table_id.database_name, create.to_table_id.table_name};
|
||||
if (!to_table.table.empty() && !to_table.database.empty())
|
||||
if (create.targets)
|
||||
{
|
||||
auto to_table_new = data.renaming_map.getNewTableName(to_table);
|
||||
if (to_table_new != to_table)
|
||||
create.to_table_id = StorageID{to_table_new.database, to_table_new.table};
|
||||
for (auto & target : create.targets->targets)
|
||||
{
|
||||
auto & table_id = target.table_id;
|
||||
if (!table_id.database_name.empty() && !table_id.table_name.empty())
|
||||
{
|
||||
QualifiedTableName target_name{table_id.database_name, table_id.table_name};
|
||||
auto new_target_name = data.renaming_map.getNewTableName(target_name);
|
||||
if (new_target_name != target_name)
|
||||
table_id = StorageID{new_target_name.database, new_target_name.table};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -729,12 +729,45 @@ void DatabaseReplicated::checkQueryValid(const ASTPtr & query, ContextPtr query_
|
||||
|
||||
if (auto * create = query->as<ASTCreateQuery>())
|
||||
{
|
||||
bool replicated_table = create->storage && create->storage->engine &&
|
||||
(startsWith(create->storage->engine->name, "Replicated") || startsWith(create->storage->engine->name, "Shared"));
|
||||
if (!replicated_table || !create->storage->engine->arguments)
|
||||
if (create->storage)
|
||||
checkTableEngine(*create, *create->storage, query_context);
|
||||
|
||||
if (create->targets)
|
||||
{
|
||||
for (const auto & inner_table_engine : create->targets->getInnerEngines())
|
||||
checkTableEngine(*create, *inner_table_engine, query_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto * query_alter = query->as<ASTAlterQuery>())
|
||||
{
|
||||
for (const auto & command : query_alter->command_list->children)
|
||||
{
|
||||
if (!isSupportedAlterTypeForOnClusterDDLQuery(command->as<ASTAlterCommand&>().type))
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Unsupported type of ALTER query");
|
||||
}
|
||||
}
|
||||
|
||||
if (auto * query_drop = query->as<ASTDropQuery>())
|
||||
{
|
||||
if (query_drop->kind == ASTDropQuery::Kind::Detach && query_context->getSettingsRef().database_replicated_always_detach_permanently)
|
||||
query_drop->permanently = true;
|
||||
if (query_drop->kind == ASTDropQuery::Kind::Detach && !query_drop->permanently)
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "DETACH TABLE is not allowed for Replicated databases. "
|
||||
"Use DETACH TABLE PERMANENTLY or SYSTEM RESTART REPLICA or set "
|
||||
"database_replicated_always_detach_permanently to 1");
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseReplicated::checkTableEngine(const ASTCreateQuery & query, ASTStorage & storage, ContextPtr query_context) const
|
||||
{
|
||||
bool replicated_table = storage.engine &&
|
||||
(startsWith(storage.engine->name, "Replicated") || startsWith(storage.engine->name, "Shared"));
|
||||
if (!replicated_table || !storage.engine->arguments)
|
||||
return;
|
||||
|
||||
ASTs & args_ref = create->storage->engine->arguments->children;
|
||||
ASTs & args_ref = storage.engine->arguments->children;
|
||||
ASTs args = args_ref;
|
||||
if (args.size() < 2)
|
||||
return;
|
||||
@ -766,7 +799,7 @@ void DatabaseReplicated::checkQueryValid(const ASTPtr & query, ContextPtr query_
|
||||
/// NOTE: we cannot check here that substituted values will be actually different on shards and replicas.
|
||||
|
||||
Macros::MacroExpansionInfo info;
|
||||
info.table_id = {getDatabaseName(), create->getTable(), create->uuid};
|
||||
info.table_id = {getDatabaseName(), query.getTable(), query.uuid};
|
||||
info.shard = getShardName();
|
||||
info.replica = getReplicaName();
|
||||
query_context->getMacros()->expand(maybe_path, info);
|
||||
@ -805,27 +838,6 @@ void DatabaseReplicated::checkQueryValid(const ASTPtr & query, ContextPtr query_
|
||||
"If you really want to specify it explicitly, then you should use some macros "
|
||||
"to distinguish different shards and replicas");
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto * query_alter = query->as<ASTAlterQuery>())
|
||||
{
|
||||
for (const auto & command : query_alter->command_list->children)
|
||||
{
|
||||
if (!isSupportedAlterTypeForOnClusterDDLQuery(command->as<ASTAlterCommand&>().type))
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Unsupported type of ALTER query");
|
||||
}
|
||||
}
|
||||
|
||||
if (auto * query_drop = query->as<ASTDropQuery>())
|
||||
{
|
||||
if (query_drop->kind == ASTDropQuery::Kind::Detach && query_context->getSettingsRef().database_replicated_always_detach_permanently)
|
||||
query_drop->permanently = true;
|
||||
if (query_drop->kind == ASTDropQuery::Kind::Detach && !query_drop->permanently)
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "DETACH TABLE is not allowed for Replicated databases. "
|
||||
"Use DETACH TABLE PERMANENTLY or SYSTEM RESTART REPLICA or set "
|
||||
"database_replicated_always_detach_permanently to 1");
|
||||
}
|
||||
}
|
||||
|
||||
BlockIO DatabaseReplicated::tryEnqueueReplicatedDDL(const ASTPtr & query, ContextPtr query_context, QueryFlags flags)
|
||||
{
|
||||
@ -1312,11 +1324,9 @@ ASTPtr DatabaseReplicated::parseQueryFromMetadataInZooKeeper(const String & node
|
||||
if (create.uuid == UUIDHelpers::Nil || create.getTable() != TABLE_WITH_UUID_NAME_PLACEHOLDER || create.database)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Got unexpected query from {}: {}", node_name, query);
|
||||
|
||||
bool is_materialized_view_with_inner_table = create.is_materialized_view && create.to_table_id.empty();
|
||||
|
||||
create.setDatabase(getDatabaseName());
|
||||
create.setTable(unescapeForFileName(node_name));
|
||||
create.attach = is_materialized_view_with_inner_table;
|
||||
create.attach = create.is_materialized_view_with_inner_table();
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ private:
|
||||
void fillClusterAuthInfo(String collection_name, const Poco::Util::AbstractConfiguration & config);
|
||||
|
||||
void checkQueryValid(const ASTPtr & query, ContextPtr query_context) const;
|
||||
void checkTableEngine(const ASTCreateQuery & query, ASTStorage & storage, ContextPtr query_context) const;
|
||||
|
||||
void recoverLostReplica(const ZooKeeperPtr & current_zookeeper, UInt32 our_log_ptr, UInt32 & max_log_ptr);
|
||||
|
||||
|
@ -969,9 +969,6 @@ void InterpreterCreateQuery::setEngine(ASTCreateQuery & create) const
|
||||
if (create.is_dictionary || create.is_ordinary_view || create.is_live_view || create.is_window_view)
|
||||
return;
|
||||
|
||||
if (create.is_materialized_view && create.to_table_id)
|
||||
return;
|
||||
|
||||
if (create.temporary)
|
||||
{
|
||||
/// Some part of storage definition is specified, but ENGINE is not: just set the one from default_temporary_table_engine setting.
|
||||
@ -986,22 +983,44 @@ void InterpreterCreateQuery::setEngine(ASTCreateQuery & create) const
|
||||
}
|
||||
|
||||
if (!create.storage->engine)
|
||||
{
|
||||
setDefaultTableEngine(*create.storage, getContext()->getSettingsRef().default_temporary_table_engine.value);
|
||||
}
|
||||
|
||||
checkTemporaryTableEngineName(create.storage->engine->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (create.storage)
|
||||
if (create.is_materialized_view)
|
||||
{
|
||||
/// A materialized view with an external target doesn't need a table engine.
|
||||
if (create.is_materialized_view_with_external_target())
|
||||
return;
|
||||
|
||||
if (auto to_engine = create.getTargetInnerEngine(ViewTarget::To))
|
||||
{
|
||||
/// This materialized view already has a storage definition.
|
||||
if (!to_engine->engine)
|
||||
{
|
||||
/// Some part of storage definition (such as PARTITION BY) is specified, but ENGINE is not: just set default one.
|
||||
setDefaultTableEngine(*to_engine, getContext()->getSettingsRef().default_table_engine.value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (create.storage)
|
||||
{
|
||||
/// This table already has a storage definition.
|
||||
if (!create.storage->engine)
|
||||
{
|
||||
/// Some part of storage definition (such as PARTITION BY) is specified, but ENGINE is not: just set default one.
|
||||
setDefaultTableEngine(*create.storage, getContext()->getSettingsRef().default_table_engine.value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/// We'll try to extract a storage definition from clause `AS`:
|
||||
/// CREATE TABLE table_name AS other_table_name
|
||||
std::shared_ptr<ASTStorage> storage_def;
|
||||
if (!create.as_table.empty())
|
||||
{
|
||||
/// NOTE Getting the structure from the table specified in the AS is done not atomically with the creation of the table.
|
||||
@ -1017,12 +1036,14 @@ void InterpreterCreateQuery::setEngine(ASTCreateQuery & create) const
|
||||
if (as_create.is_ordinary_view)
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "Cannot CREATE a table AS {}, it is a View", qualified_name);
|
||||
|
||||
if (as_create.is_materialized_view && as_create.to_table_id)
|
||||
if (as_create.is_materialized_view_with_external_target())
|
||||
{
|
||||
throw Exception(
|
||||
ErrorCodes::INCORRECT_QUERY,
|
||||
"Cannot CREATE a table AS {}, it is a Materialized View without storage. Use \"AS `{}`\" instead",
|
||||
"Cannot CREATE a table AS {}, it is a Materialized View without storage. Use \"AS {}\" instead",
|
||||
qualified_name,
|
||||
as_create.to_table_id.getQualifiedName());
|
||||
as_create.getTargetTableID(ViewTarget::To).getFullTableName());
|
||||
}
|
||||
|
||||
if (as_create.is_live_view)
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "Cannot CREATE a table AS {}, it is a Live View", qualified_name);
|
||||
@ -1033,18 +1054,37 @@ void InterpreterCreateQuery::setEngine(ASTCreateQuery & create) const
|
||||
if (as_create.is_dictionary)
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "Cannot CREATE a table AS {}, it is a Dictionary", qualified_name);
|
||||
|
||||
if (as_create.storage)
|
||||
create.set(create.storage, as_create.storage->ptr());
|
||||
if (as_create.is_materialized_view)
|
||||
{
|
||||
storage_def = as_create.getTargetInnerEngine(ViewTarget::To);
|
||||
}
|
||||
else if (as_create.as_table_function)
|
||||
{
|
||||
create.set(create.as_table_function, as_create.as_table_function->ptr());
|
||||
else
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot set engine, it's a bug.");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (as_create.storage)
|
||||
{
|
||||
storage_def = typeid_cast<std::shared_ptr<ASTStorage>>(as_create.storage->ptr());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot set engine, it's a bug.");
|
||||
}
|
||||
}
|
||||
|
||||
create.set(create.storage, std::make_shared<ASTStorage>());
|
||||
setDefaultTableEngine(*create.storage, getContext()->getSettingsRef().default_table_engine.value);
|
||||
if (!storage_def)
|
||||
{
|
||||
/// Set ENGINE by default.
|
||||
storage_def = std::make_shared<ASTStorage>();
|
||||
setDefaultTableEngine(*storage_def, getContext()->getSettingsRef().default_table_engine.value);
|
||||
}
|
||||
|
||||
/// Use the found table engine to modify the create query.
|
||||
if (create.is_materialized_view)
|
||||
create.setTargetInnerEngine(ViewTarget::To, storage_def);
|
||||
else
|
||||
create.set(create.storage, storage_def);
|
||||
}
|
||||
|
||||
void InterpreterCreateQuery::assertOrSetUUID(ASTCreateQuery & create, const DatabasePtr & database) const
|
||||
@ -1086,11 +1126,11 @@ void InterpreterCreateQuery::assertOrSetUUID(ASTCreateQuery & create, const Data
|
||||
kind_upper, create.table);
|
||||
}
|
||||
|
||||
create.generateRandomUUID();
|
||||
create.generateRandomUUIDs();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool has_uuid = create.uuid != UUIDHelpers::Nil || create.to_inner_uuid != UUIDHelpers::Nil;
|
||||
bool has_uuid = (create.uuid != UUIDHelpers::Nil) || create.hasInnerUUIDs();
|
||||
if (has_uuid && !is_on_cluster && !internal)
|
||||
{
|
||||
/// We don't show the following error message either
|
||||
@ -1105,8 +1145,7 @@ void InterpreterCreateQuery::assertOrSetUUID(ASTCreateQuery & create, const Data
|
||||
/// The database doesn't support UUID so we'll ignore it. The UUID could be set here because of either
|
||||
/// a) the initiator of `ON CLUSTER` query generated it to ensure the same UUIDs are used on different hosts; or
|
||||
/// b) `RESTORE from backup` query generated it to ensure the same UUIDs are used on different hosts.
|
||||
create.uuid = UUIDHelpers::Nil;
|
||||
create.to_inner_uuid = UUIDHelpers::Nil;
|
||||
create.resetUUIDs();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1130,6 +1169,14 @@ void checkTableCanBeAddedWithNoCyclicDependencies(const ASTCreateQuery & create,
|
||||
DatabaseCatalog::instance().checkTableCanBeAddedWithNoCyclicDependencies(qualified_name, ref_dependencies, loading_dependencies);
|
||||
}
|
||||
|
||||
bool isReplicated(const ASTStorage & storage)
|
||||
{
|
||||
if (!storage.engine)
|
||||
return false;
|
||||
const auto & storage_name = storage.engine->name;
|
||||
return storage_name.starts_with("Replicated") || storage_name.starts_with("Shared");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
|
||||
@ -1246,8 +1293,9 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
|
||||
|
||||
if (!create.temporary && !create.database)
|
||||
create.setDatabase(current_database);
|
||||
if (create.to_table_id && create.to_table_id.database_name.empty())
|
||||
create.to_table_id.database_name = current_database;
|
||||
|
||||
if (create.targets)
|
||||
create.targets->setCurrentDatabase(current_database);
|
||||
|
||||
if (create.select && create.isView())
|
||||
{
|
||||
@ -1281,12 +1329,9 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
|
||||
TableProperties properties = getTablePropertiesAndNormalizeCreateQuery(create, mode);
|
||||
|
||||
/// Check type compatible for materialized dest table and select columns
|
||||
if (create.select && create.is_materialized_view && create.to_table_id && mode <= LoadingStrictnessLevel::CREATE)
|
||||
if (create.is_materialized_view_with_external_target() && create.select && mode <= LoadingStrictnessLevel::CREATE)
|
||||
{
|
||||
if (StoragePtr to_table = DatabaseCatalog::instance().tryGetTable(
|
||||
{create.to_table_id.database_name, create.to_table_id.table_name, create.to_table_id.uuid},
|
||||
getContext()
|
||||
))
|
||||
if (StoragePtr to_table = DatabaseCatalog::instance().tryGetTable(create.getTargetTableID(ViewTarget::To), getContext()))
|
||||
{
|
||||
Block input_block;
|
||||
|
||||
@ -1332,11 +1377,17 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
|
||||
if (!allow_heavy_create && database && database->getEngineName() == "Replicated" && (create.select || create.is_populate))
|
||||
{
|
||||
bool is_storage_replicated = false;
|
||||
if (create.storage && create.storage->engine)
|
||||
{
|
||||
const auto & storage_name = create.storage->engine->name;
|
||||
if (storage_name.starts_with("Replicated") || storage_name.starts_with("Shared"))
|
||||
|
||||
if (create.storage && isReplicated(*create.storage))
|
||||
is_storage_replicated = true;
|
||||
|
||||
if (create.targets)
|
||||
{
|
||||
for (const auto & inner_table_engine : create.targets->getInnerEngines())
|
||||
{
|
||||
if (isReplicated(*inner_table_engine))
|
||||
is_storage_replicated = true;
|
||||
}
|
||||
}
|
||||
|
||||
const bool allow_create_select_for_replicated = (create.isView() && !create.is_populate) || create.is_create_empty || !is_storage_replicated;
|
||||
@ -1795,7 +1846,7 @@ void InterpreterCreateQuery::prepareOnClusterQuery(ASTCreateQuery & create, Cont
|
||||
|
||||
/// For CREATE query generate UUID on initiator, so it will be the same on all hosts.
|
||||
/// It will be ignored if database does not support UUIDs.
|
||||
create.generateRandomUUID();
|
||||
create.generateRandomUUIDs();
|
||||
|
||||
/// For cross-replication cluster we cannot use UUID in replica path.
|
||||
String cluster_name_expanded = local_context->getMacros()->expand(cluster_name);
|
||||
@ -1917,8 +1968,15 @@ AccessRightsElements InterpreterCreateQuery::getRequiredAccess() const
|
||||
}
|
||||
}
|
||||
|
||||
if (create.to_table_id)
|
||||
required_access.emplace_back(AccessType::SELECT | AccessType::INSERT, create.to_table_id.database_name, create.to_table_id.table_name);
|
||||
if (create.targets)
|
||||
{
|
||||
for (const auto & target : create.targets->targets)
|
||||
{
|
||||
const auto & target_id = target.table_id;
|
||||
if (target_id)
|
||||
required_access.emplace_back(AccessType::SELECT | AccessType::INSERT, target_id.database_name, target_id.table_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (create.storage && create.storage->engine)
|
||||
required_access.emplace_back(AccessType::TABLE_ENGINE, create.storage->engine->name);
|
||||
|
@ -94,7 +94,8 @@ QueryPipeline InterpreterShowCreateQuery::executeImpl()
|
||||
{
|
||||
auto & create = create_query->as<ASTCreateQuery &>();
|
||||
create.uuid = UUIDHelpers::Nil;
|
||||
create.to_inner_uuid = UUIDHelpers::Nil;
|
||||
if (create.targets)
|
||||
create.targets->resetInnerUUIDs();
|
||||
}
|
||||
|
||||
MutableColumnPtr column = ColumnString::create();
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include <Parsers/ASTExpressionList.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTSelectWithUnionQuery.h>
|
||||
#include <Parsers/CommonParsers.h>
|
||||
#include <Parsers/CreateQueryUUIDs.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <Interpreters/StorageID.h>
|
||||
#include <IO/Operators.h>
|
||||
@ -240,12 +242,12 @@ ASTPtr ASTCreateQuery::clone() const
|
||||
res->set(res->columns_list, columns_list->clone());
|
||||
if (storage)
|
||||
res->set(res->storage, storage->clone());
|
||||
if (inner_storage)
|
||||
res->set(res->inner_storage, inner_storage->clone());
|
||||
if (select)
|
||||
res->set(res->select, select->clone());
|
||||
if (table_overrides)
|
||||
res->set(res->table_overrides, table_overrides->clone());
|
||||
if (targets)
|
||||
res->set(res->targets, targets->clone());
|
||||
|
||||
if (dictionary)
|
||||
{
|
||||
@ -398,20 +400,18 @@ void ASTCreateQuery::formatQueryImpl(const FormatSettings & settings, FormatStat
|
||||
refresh_strategy->formatImpl(settings, state, frame);
|
||||
}
|
||||
|
||||
if (to_table_id)
|
||||
if (auto to_table_id = getTargetTableID(ViewTarget::To))
|
||||
{
|
||||
assert((is_materialized_view || is_window_view) && to_inner_uuid == UUIDHelpers::Nil);
|
||||
settings.ostr
|
||||
<< (settings.hilite ? hilite_keyword : "") << " TO " << (settings.hilite ? hilite_none : "")
|
||||
settings.ostr << " " << (settings.hilite ? hilite_keyword : "") << toStringView(Keyword::TO)
|
||||
<< (settings.hilite ? hilite_none : "") << " "
|
||||
<< (!to_table_id.database_name.empty() ? backQuoteIfNeed(to_table_id.database_name) + "." : "")
|
||||
<< backQuoteIfNeed(to_table_id.table_name);
|
||||
}
|
||||
|
||||
if (to_inner_uuid != UUIDHelpers::Nil)
|
||||
if (auto to_inner_uuid = getTargetInnerUUID(ViewTarget::To); to_inner_uuid != UUIDHelpers::Nil)
|
||||
{
|
||||
assert(is_materialized_view && !to_table_id);
|
||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " TO INNER UUID " << (settings.hilite ? hilite_none : "")
|
||||
<< quoteString(toString(to_inner_uuid));
|
||||
settings.ostr << " " << (settings.hilite ? hilite_keyword : "") << toStringView(Keyword::TO_INNER_UUID)
|
||||
<< (settings.hilite ? hilite_none : "") << " " << quoteString(toString(to_inner_uuid));
|
||||
}
|
||||
|
||||
bool should_add_empty = is_create_empty;
|
||||
@ -471,14 +471,17 @@ void ASTCreateQuery::formatQueryImpl(const FormatSettings & settings, FormatStat
|
||||
|
||||
frame.expression_list_always_start_on_new_line = false;
|
||||
|
||||
if (inner_storage)
|
||||
if (storage)
|
||||
storage->formatImpl(settings, state, frame);
|
||||
|
||||
if (auto inner_storage = getTargetInnerEngine(ViewTarget::Inner))
|
||||
{
|
||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " INNER" << (settings.hilite ? hilite_none : "");
|
||||
settings.ostr << " " << (settings.hilite ? hilite_keyword : "") << toStringView(Keyword::INNER) << (settings.hilite ? hilite_none : "");
|
||||
inner_storage->formatImpl(settings, state, frame);
|
||||
}
|
||||
|
||||
if (storage)
|
||||
storage->formatImpl(settings, state, frame);
|
||||
if (auto to_storage = getTargetInnerEngine(ViewTarget::To))
|
||||
to_storage->formatImpl(settings, state, frame);
|
||||
|
||||
if (dictionary)
|
||||
dictionary->formatImpl(settings, state, frame);
|
||||
@ -538,48 +541,57 @@ bool ASTCreateQuery::isParameterizedView() const
|
||||
}
|
||||
|
||||
|
||||
ASTCreateQuery::UUIDs::UUIDs(const ASTCreateQuery & query)
|
||||
: uuid(query.uuid)
|
||||
, to_inner_uuid(query.to_inner_uuid)
|
||||
void ASTCreateQuery::generateRandomUUIDs()
|
||||
{
|
||||
CreateQueryUUIDs{*this, /* generate_random= */ true}.copyToQuery(*this);
|
||||
}
|
||||
|
||||
String ASTCreateQuery::UUIDs::toString() const
|
||||
void ASTCreateQuery::resetUUIDs()
|
||||
{
|
||||
WriteBufferFromOwnString out;
|
||||
out << "{" << uuid << "," << to_inner_uuid << "}";
|
||||
return out.str();
|
||||
CreateQueryUUIDs{}.copyToQuery(*this);
|
||||
}
|
||||
|
||||
ASTCreateQuery::UUIDs ASTCreateQuery::UUIDs::fromString(const String & str)
|
||||
|
||||
StorageID ASTCreateQuery::getTargetTableID(ViewTarget::Kind target_kind) const
|
||||
{
|
||||
ReadBufferFromString in{str};
|
||||
ASTCreateQuery::UUIDs res;
|
||||
in >> "{" >> res.uuid >> "," >> res.to_inner_uuid >> "}";
|
||||
return res;
|
||||
if (targets)
|
||||
return targets->getTableID(target_kind);
|
||||
return StorageID::createEmpty();
|
||||
}
|
||||
|
||||
ASTCreateQuery::UUIDs ASTCreateQuery::generateRandomUUID(bool always_generate_new_uuid)
|
||||
bool ASTCreateQuery::hasTargetTableID(ViewTarget::Kind target_kind) const
|
||||
{
|
||||
if (always_generate_new_uuid)
|
||||
setUUID({});
|
||||
|
||||
if (uuid == UUIDHelpers::Nil)
|
||||
uuid = UUIDHelpers::generateV4();
|
||||
|
||||
/// If destination table (to_table_id) is not specified for materialized view,
|
||||
/// then MV will create inner table. We should generate UUID of inner table here.
|
||||
bool need_uuid_for_inner_table = !attach && is_materialized_view && !to_table_id;
|
||||
if (need_uuid_for_inner_table && (to_inner_uuid == UUIDHelpers::Nil))
|
||||
to_inner_uuid = UUIDHelpers::generateV4();
|
||||
|
||||
return UUIDs{*this};
|
||||
if (targets)
|
||||
return targets->hasTableID(target_kind);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ASTCreateQuery::setUUID(const UUIDs & uuids)
|
||||
UUID ASTCreateQuery::getTargetInnerUUID(ViewTarget::Kind target_kind) const
|
||||
{
|
||||
uuid = uuids.uuid;
|
||||
to_inner_uuid = uuids.to_inner_uuid;
|
||||
if (targets)
|
||||
return targets->getInnerUUID(target_kind);
|
||||
return UUIDHelpers::Nil;
|
||||
}
|
||||
|
||||
bool ASTCreateQuery::hasInnerUUIDs() const
|
||||
{
|
||||
if (targets)
|
||||
return targets->hasInnerUUIDs();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<ASTStorage> ASTCreateQuery::getTargetInnerEngine(ViewTarget::Kind target_kind) const
|
||||
{
|
||||
if (targets)
|
||||
return targets->getInnerEngine(target_kind);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ASTCreateQuery::setTargetInnerEngine(ViewTarget::Kind target_kind, ASTPtr storage_def)
|
||||
{
|
||||
if (!targets)
|
||||
set(targets, std::make_shared<ASTViewTargets>());
|
||||
targets->setInnerEngine(target_kind, storage_def);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <Parsers/ASTDictionary.h>
|
||||
#include <Parsers/ASTDictionaryAttributeDeclaration.h>
|
||||
#include <Parsers/ASTTableOverrides.h>
|
||||
#include <Parsers/ASTViewTargets.h>
|
||||
#include <Parsers/ASTSQLSecurity.h>
|
||||
#include <Parsers/ASTRefreshStrategy.h>
|
||||
#include <Interpreters/StorageID.h>
|
||||
@ -15,6 +16,7 @@ namespace DB
|
||||
class ASTFunction;
|
||||
class ASTSetQuery;
|
||||
class ASTSelectWithUnionQuery;
|
||||
struct CreateQueryUUIDs;
|
||||
|
||||
|
||||
class ASTStorage : public IAST
|
||||
@ -101,17 +103,15 @@ public:
|
||||
bool has_uuid{false}; // CREATE TABLE x UUID '...'
|
||||
|
||||
ASTColumns * columns_list = nullptr;
|
||||
|
||||
StorageID to_table_id = StorageID::createEmpty(); /// For CREATE MATERIALIZED VIEW mv TO table.
|
||||
UUID to_inner_uuid = UUIDHelpers::Nil; /// For materialized view with inner table
|
||||
ASTStorage * inner_storage = nullptr; /// For window view with inner table
|
||||
ASTStorage * storage = nullptr;
|
||||
|
||||
ASTPtr watermark_function;
|
||||
ASTPtr lateness_function;
|
||||
String as_database;
|
||||
String as_table;
|
||||
IAST * as_table_function = nullptr;
|
||||
ASTSelectWithUnionQuery * select = nullptr;
|
||||
ASTViewTargets * targets = nullptr;
|
||||
IAST * comment = nullptr;
|
||||
ASTPtr sql_security = nullptr;
|
||||
|
||||
@ -153,17 +153,26 @@ public:
|
||||
|
||||
QueryKind getQueryKind() const override { return QueryKind::Create; }
|
||||
|
||||
struct UUIDs
|
||||
{
|
||||
UUID uuid = UUIDHelpers::Nil;
|
||||
UUID to_inner_uuid = UUIDHelpers::Nil;
|
||||
UUIDs() = default;
|
||||
explicit UUIDs(const ASTCreateQuery & query);
|
||||
String toString() const;
|
||||
static UUIDs fromString(const String & str);
|
||||
};
|
||||
UUIDs generateRandomUUID(bool always_generate_new_uuid = false);
|
||||
void setUUID(const UUIDs & uuids);
|
||||
/// Generates a random UUID for this create query if it's not specified already.
|
||||
/// The function also generates random UUIDs for inner target tables if this create query implies that
|
||||
/// (for example, if it's a `CREATE MATERIALIZED VIEW` query with an inner storage).
|
||||
void generateRandomUUIDs();
|
||||
|
||||
/// Removes UUID from this create query.
|
||||
/// The function also removes UUIDs for inner target tables from this create query (see also generateRandomUUID()).
|
||||
void resetUUIDs();
|
||||
|
||||
/// Returns information about a target table.
|
||||
/// If that information isn't specified in this create query (or even not allowed) then the function returns an empty value.
|
||||
StorageID getTargetTableID(ViewTarget::Kind target_kind) const;
|
||||
bool hasTargetTableID(ViewTarget::Kind target_kind) const;
|
||||
UUID getTargetInnerUUID(ViewTarget::Kind target_kind) const;
|
||||
bool hasInnerUUIDs() const;
|
||||
std::shared_ptr<ASTStorage> getTargetInnerEngine(ViewTarget::Kind target_kind) const;
|
||||
void setTargetInnerEngine(ViewTarget::Kind target_kind, ASTPtr storage_def);
|
||||
|
||||
bool is_materialized_view_with_external_target() const { return is_materialized_view && hasTargetTableID(ViewTarget::To); }
|
||||
bool is_materialized_view_with_inner_table() const { return is_materialized_view && !hasTargetTableID(ViewTarget::To); }
|
||||
|
||||
protected:
|
||||
void formatQueryImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
@ -171,8 +180,8 @@ protected:
|
||||
void forEachPointerToChild(std::function<void(void**)> f) override
|
||||
{
|
||||
f(reinterpret_cast<void **>(&columns_list));
|
||||
f(reinterpret_cast<void **>(&inner_storage));
|
||||
f(reinterpret_cast<void **>(&storage));
|
||||
f(reinterpret_cast<void **>(&targets));
|
||||
f(reinterpret_cast<void **>(&as_table_function));
|
||||
f(reinterpret_cast<void **>(&select));
|
||||
f(reinterpret_cast<void **>(&comment));
|
||||
|
300
src/Parsers/ASTViewTargets.cpp
Normal file
300
src/Parsers/ASTViewTargets.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
#include <Parsers/ASTViewTargets.h>
|
||||
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/CommonParsers.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
std::string_view toString(ViewTarget::Kind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ViewTarget::To: return "to";
|
||||
case ViewTarget::Inner: return "inner";
|
||||
}
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "{} doesn't support kind {}", __FUNCTION__, kind);
|
||||
}
|
||||
|
||||
void parseFromString(ViewTarget::Kind & out, std::string_view str)
|
||||
{
|
||||
for (auto kind : magic_enum::enum_values<ViewTarget::Kind>())
|
||||
{
|
||||
if (toString(kind) == str)
|
||||
{
|
||||
out = kind;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "{}: Unexpected string {}", __FUNCTION__, str);
|
||||
}
|
||||
|
||||
|
||||
std::vector<ViewTarget::Kind> ASTViewTargets::getKinds() const
|
||||
{
|
||||
std::vector<ViewTarget::Kind> kinds;
|
||||
kinds.reserve(targets.size());
|
||||
for (const auto & target : targets)
|
||||
kinds.push_back(target.kind);
|
||||
return kinds;
|
||||
}
|
||||
|
||||
|
||||
void ASTViewTargets::setTableID(ViewTarget::Kind kind, const StorageID & table_id_)
|
||||
{
|
||||
for (auto & target : targets)
|
||||
{
|
||||
if (target.kind == kind)
|
||||
{
|
||||
target.table_id = table_id_;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (table_id_)
|
||||
targets.emplace_back(kind).table_id = table_id_;
|
||||
}
|
||||
|
||||
StorageID ASTViewTargets::getTableID(ViewTarget::Kind kind) const
|
||||
{
|
||||
if (const auto * target = tryGetTarget(kind))
|
||||
return target->table_id;
|
||||
return StorageID::createEmpty();
|
||||
}
|
||||
|
||||
bool ASTViewTargets::hasTableID(ViewTarget::Kind kind) const
|
||||
{
|
||||
if (const auto * target = tryGetTarget(kind))
|
||||
return !target->table_id.empty();
|
||||
return false;
|
||||
}
|
||||
|
||||
void ASTViewTargets::setCurrentDatabase(const String & current_database)
|
||||
{
|
||||
for (auto & target : targets)
|
||||
{
|
||||
auto & table_id = target.table_id;
|
||||
if (!table_id.table_name.empty() && table_id.database_name.empty())
|
||||
table_id.database_name = current_database;
|
||||
}
|
||||
}
|
||||
|
||||
void ASTViewTargets::setInnerUUID(ViewTarget::Kind kind, const UUID & inner_uuid_)
|
||||
{
|
||||
for (auto & target : targets)
|
||||
{
|
||||
if (target.kind == kind)
|
||||
{
|
||||
target.inner_uuid = inner_uuid_;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (inner_uuid_ != UUIDHelpers::Nil)
|
||||
targets.emplace_back(kind).inner_uuid = inner_uuid_;
|
||||
}
|
||||
|
||||
UUID ASTViewTargets::getInnerUUID(ViewTarget::Kind kind) const
|
||||
{
|
||||
if (const auto * target = tryGetTarget(kind))
|
||||
return target->inner_uuid;
|
||||
return UUIDHelpers::Nil;
|
||||
}
|
||||
|
||||
bool ASTViewTargets::hasInnerUUID(ViewTarget::Kind kind) const
|
||||
{
|
||||
return getInnerUUID(kind) != UUIDHelpers::Nil;
|
||||
}
|
||||
|
||||
void ASTViewTargets::resetInnerUUIDs()
|
||||
{
|
||||
for (auto & target : targets)
|
||||
target.inner_uuid = UUIDHelpers::Nil;
|
||||
}
|
||||
|
||||
bool ASTViewTargets::hasInnerUUIDs() const
|
||||
{
|
||||
for (const auto & target : targets)
|
||||
{
|
||||
if (target.inner_uuid != UUIDHelpers::Nil)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ASTViewTargets::setInnerEngine(ViewTarget::Kind kind, ASTPtr storage_def)
|
||||
{
|
||||
auto new_inner_engine = typeid_cast<std::shared_ptr<ASTStorage>>(storage_def);
|
||||
if (!new_inner_engine && storage_def)
|
||||
throw Exception(DB::ErrorCodes::LOGICAL_ERROR, "Bad cast from type {} to ASTStorage", storage_def->getID());
|
||||
|
||||
for (auto & target : targets)
|
||||
{
|
||||
if (target.kind == kind)
|
||||
{
|
||||
if (target.inner_engine == new_inner_engine)
|
||||
return;
|
||||
if (new_inner_engine)
|
||||
children.push_back(new_inner_engine);
|
||||
if (target.inner_engine)
|
||||
std::erase(children, target.inner_engine);
|
||||
target.inner_engine = new_inner_engine;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_inner_engine)
|
||||
{
|
||||
targets.emplace_back(kind).inner_engine = new_inner_engine;
|
||||
children.push_back(new_inner_engine);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ASTStorage> ASTViewTargets::getInnerEngine(ViewTarget::Kind kind) const
|
||||
{
|
||||
if (const auto * target = tryGetTarget(kind))
|
||||
return target->inner_engine;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ASTStorage>> ASTViewTargets::getInnerEngines() const
|
||||
{
|
||||
std::vector<std::shared_ptr<ASTStorage>> res;
|
||||
res.reserve(targets.size());
|
||||
for (const auto & target : targets)
|
||||
{
|
||||
if (target.inner_engine)
|
||||
res.push_back(target.inner_engine);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const ViewTarget * ASTViewTargets::tryGetTarget(ViewTarget::Kind kind) const
|
||||
{
|
||||
for (const auto & target : targets)
|
||||
{
|
||||
if (target.kind == kind)
|
||||
return ⌖
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ASTPtr ASTViewTargets::clone() const
|
||||
{
|
||||
auto res = std::make_shared<ASTViewTargets>(*this);
|
||||
res->children.clear();
|
||||
for (auto & target : res->targets)
|
||||
{
|
||||
if (target.inner_engine)
|
||||
{
|
||||
target.inner_engine = typeid_cast<std::shared_ptr<ASTStorage>>(target.inner_engine->clone());
|
||||
res->children.push_back(target.inner_engine);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void ASTViewTargets::formatImpl(const FormatSettings & s, FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
for (const auto & target : targets)
|
||||
formatTarget(target, s, state, frame);
|
||||
}
|
||||
|
||||
void ASTViewTargets::formatTarget(ViewTarget::Kind kind, const FormatSettings & s, FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
for (const auto & target : targets)
|
||||
{
|
||||
if (target.kind == kind)
|
||||
formatTarget(target, s, state, frame);
|
||||
}
|
||||
}
|
||||
|
||||
void ASTViewTargets::formatTarget(const ViewTarget & target, const FormatSettings & s, FormatState & state, FormatStateStacked frame)
|
||||
{
|
||||
if (target.table_id)
|
||||
{
|
||||
auto keyword = getKeywordForTableID(target.kind);
|
||||
if (!keyword)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "No keyword for table name of kind {}", toString(target.kind));
|
||||
s.ostr << " " << (s.hilite ? hilite_keyword : "") << toStringView(*keyword)
|
||||
<< (s.hilite ? hilite_none : "") << " "
|
||||
<< (!target.table_id.database_name.empty() ? backQuoteIfNeed(target.table_id.database_name) + "." : "")
|
||||
<< backQuoteIfNeed(target.table_id.table_name);
|
||||
}
|
||||
|
||||
if (target.inner_uuid != UUIDHelpers::Nil)
|
||||
{
|
||||
auto keyword = getKeywordForInnerUUID(target.kind);
|
||||
if (!keyword)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "No prefix keyword for inner UUID of kind {}", toString(target.kind));
|
||||
s.ostr << " " << (s.hilite ? hilite_keyword : "") << toStringView(*keyword)
|
||||
<< (s.hilite ? hilite_none : "") << " " << quoteString(toString(target.inner_uuid));
|
||||
}
|
||||
|
||||
if (target.inner_engine)
|
||||
{
|
||||
auto keyword = getKeywordForInnerStorage(target.kind);
|
||||
if (!keyword)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "No prefix keyword for table engine of kind {}", toString(target.kind));
|
||||
s.ostr << " " << (s.hilite ? hilite_keyword : "") << toStringView(*keyword) << (s.hilite ? hilite_none : "");
|
||||
target.inner_engine->formatImpl(s, state, frame);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Keyword> ASTViewTargets::getKeywordForTableID(ViewTarget::Kind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ViewTarget::To: return Keyword::TO; /// TO mydb.mydata
|
||||
case ViewTarget::Inner: return std::nullopt;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::optional<Keyword> ASTViewTargets::getKeywordForInnerStorage(ViewTarget::Kind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ViewTarget::To: return std::nullopt; /// ENGINE = MergeTree()
|
||||
case ViewTarget::Inner: return Keyword::INNER; /// INNER ENGINE = MergeTree()
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::optional<Keyword> ASTViewTargets::getKeywordForInnerUUID(ViewTarget::Kind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ViewTarget::To: return Keyword::TO_INNER_UUID; /// TO INNER UUID 'XXX'
|
||||
case ViewTarget::Inner: return std::nullopt;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void ASTViewTargets::forEachPointerToChild(std::function<void(void**)> f)
|
||||
{
|
||||
for (auto & target : targets)
|
||||
{
|
||||
if (target.inner_engine)
|
||||
{
|
||||
ASTStorage * new_inner_engine = target.inner_engine.get();
|
||||
f(reinterpret_cast<void **>(&new_inner_engine));
|
||||
if (new_inner_engine != target.inner_engine.get())
|
||||
{
|
||||
if (new_inner_engine)
|
||||
target.inner_engine = typeid_cast<std::shared_ptr<ASTStorage>>(new_inner_engine->ptr());
|
||||
else
|
||||
target.inner_engine.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
115
src/Parsers/ASTViewTargets.h
Normal file
115
src/Parsers/ASTViewTargets.h
Normal file
@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include <Parsers/IAST.h>
|
||||
#include <Interpreters/StorageID.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
class ASTStorage;
|
||||
enum class Keyword : size_t;
|
||||
|
||||
/// Information about target tables (external or inner) of a materialized view or a window view.
|
||||
/// See ASTViewTargets for more details.
|
||||
struct ViewTarget
|
||||
{
|
||||
enum Kind
|
||||
{
|
||||
/// If `kind == ViewTarget::To` then `ViewTarget` contains information about the "TO" table of a materialized view or a window view:
|
||||
/// CREATE MATERIALIZED VIEW db.mv_name {TO [db.]to_target | ENGINE to_engine} AS SELECT ...
|
||||
/// or
|
||||
/// CREATE WINDOW VIEW db.wv_name {TO [db.]to_target | ENGINE to_engine} AS SELECT ...
|
||||
To,
|
||||
|
||||
/// If `kind == ViewTarget::Inner` then `ViewTarget` contains information about the "INNER" table of a window view:
|
||||
/// CREATE WINDOW VIEW db.wv_name {INNER ENGINE inner_engine} AS SELECT ...
|
||||
Inner,
|
||||
};
|
||||
|
||||
Kind kind = To;
|
||||
|
||||
/// StorageID of the target table, if it's not inner.
|
||||
/// That storage ID can be seen for example after "TO" in a statement like CREATE MATERIALIZED VIEW ... TO ...
|
||||
StorageID table_id = StorageID::createEmpty();
|
||||
|
||||
/// UUID of the target table, if it's inner.
|
||||
/// The UUID is calculated automatically and can be seen for example after "TO INNER UUID" in a statement like
|
||||
/// CREATE MATERIALIZED VIEW ... TO INNER UUID ...
|
||||
UUID inner_uuid = UUIDHelpers::Nil;
|
||||
|
||||
/// Table engine of the target table, if it's inner.
|
||||
/// That engine can be seen for example after "ENGINE" in a statement like CREATE MATERIALIZED VIEW ... ENGINE ...
|
||||
std::shared_ptr<ASTStorage> inner_engine;
|
||||
};
|
||||
|
||||
/// Converts ViewTarget::Kind to a string.
|
||||
std::string_view toString(ViewTarget::Kind kind);
|
||||
void parseFromString(ViewTarget::Kind & out, std::string_view str);
|
||||
|
||||
|
||||
/// Information about all target tables (external or inner) of a view.
|
||||
///
|
||||
/// For example, for a materialized view:
|
||||
/// CREATE MATERIALIZED VIEW db.mv_name [TO [db.]to_target | ENGINE to_engine] AS SELECT ...
|
||||
/// this class contains information about the "TO" table: its name and database (if it's external), its UUID and engine (if it's inner).
|
||||
///
|
||||
/// For a window view:
|
||||
/// CREATE WINDOW VIEW db.wv_name [TO [db.]to_target | ENGINE to_engine] [INNER ENGINE inner_engine] AS SELECT ...
|
||||
/// this class contains information about both the "TO" table and the "INNER" table.
|
||||
class ASTViewTargets : public IAST
|
||||
{
|
||||
public:
|
||||
std::vector<ViewTarget> targets;
|
||||
|
||||
/// Sets the StorageID of the target table, if it's not inner.
|
||||
/// That storage ID can be seen for example after "TO" in a statement like CREATE MATERIALIZED VIEW ... TO ...
|
||||
void setTableID(ViewTarget::Kind kind, const StorageID & table_id_);
|
||||
StorageID getTableID(ViewTarget::Kind kind) const;
|
||||
bool hasTableID(ViewTarget::Kind kind) const;
|
||||
|
||||
/// Replaces an empty database in the StorageID of the target table with a specified database.
|
||||
void setCurrentDatabase(const String & current_database);
|
||||
|
||||
/// Sets the UUID of the target table, if it's inner.
|
||||
/// The UUID is calculated automatically and can be seen for example after "TO INNER UUID" in a statement like
|
||||
/// CREATE MATERIALIZED VIEW ... TO INNER UUID ...
|
||||
void setInnerUUID(ViewTarget::Kind kind, const UUID & inner_uuid_);
|
||||
UUID getInnerUUID(ViewTarget::Kind kind) const;
|
||||
bool hasInnerUUID(ViewTarget::Kind kind) const;
|
||||
|
||||
void resetInnerUUIDs();
|
||||
bool hasInnerUUIDs() const;
|
||||
|
||||
/// Sets the table engine of the target table, if it's inner.
|
||||
/// That engine can be seen for example after "ENGINE" in a statement like CREATE MATERIALIZED VIEW ... ENGINE ...
|
||||
void setInnerEngine(ViewTarget::Kind kind, ASTPtr storage_def);
|
||||
std::shared_ptr<ASTStorage> getInnerEngine(ViewTarget::Kind kind) const;
|
||||
std::vector<std::shared_ptr<ASTStorage>> getInnerEngines() const;
|
||||
|
||||
/// Returns a list of all kinds of views in this ASTViewTargets.
|
||||
std::vector<ViewTarget::Kind> getKinds() const;
|
||||
|
||||
/// Returns information about a target table.
|
||||
/// The function returns null if such target doesn't exist.
|
||||
const ViewTarget * tryGetTarget(ViewTarget::Kind kind) const;
|
||||
|
||||
String getID(char) const override { return "ViewTargets"; }
|
||||
|
||||
ASTPtr clone() const override;
|
||||
|
||||
void formatImpl(const FormatSettings & s, FormatState & state, FormatStateStacked frame) const override;
|
||||
|
||||
/// Formats information only about a specific target table.
|
||||
void formatTarget(ViewTarget::Kind kind, const FormatSettings & s, FormatState & state, FormatStateStacked frame) const;
|
||||
static void formatTarget(const ViewTarget & target, const FormatSettings & s, FormatState & state, FormatStateStacked frame);
|
||||
|
||||
/// Helper functions for class ParserViewTargets. Returns a prefix keyword matching a specified target kind.
|
||||
static std::optional<Keyword> getKeywordForTableID(ViewTarget::Kind kind);
|
||||
static std::optional<Keyword> getKeywordForInnerUUID(ViewTarget::Kind kind);
|
||||
static std::optional<Keyword> getKeywordForInnerStorage(ViewTarget::Kind kind);
|
||||
|
||||
protected:
|
||||
void forEachPointerToChild(std::function<void(void**)> f) override;
|
||||
};
|
||||
|
||||
}
|
168
src/Parsers/CreateQueryUUIDs.cpp
Normal file
168
src/Parsers/CreateQueryUUIDs.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
#include <Parsers/CreateQueryUUIDs.h>
|
||||
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <IO/ReadBufferFromString.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
CreateQueryUUIDs::CreateQueryUUIDs(const ASTCreateQuery & query, bool generate_random, bool force_random)
|
||||
{
|
||||
if (!generate_random || !force_random)
|
||||
{
|
||||
uuid = query.uuid;
|
||||
if (query.targets)
|
||||
{
|
||||
for (const auto & target : query.targets->targets)
|
||||
setTargetInnerUUID(target.kind, target.inner_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (generate_random)
|
||||
{
|
||||
if (uuid == UUIDHelpers::Nil)
|
||||
uuid = UUIDHelpers::generateV4();
|
||||
|
||||
/// For an ATTACH query we should never generate UUIDs for its inner target tables
|
||||
/// because for an ATTACH query those inner target tables probably already exist and can be accessible by names.
|
||||
/// If we generate random UUIDs for already existing tables then those UUIDs will not be correct making those inner target table inaccessible.
|
||||
/// Thus it's not safe for example to replace
|
||||
/// "ATTACH MATERIALIZED VIEW mv AS SELECT a FROM b" with
|
||||
/// "ATTACH MATERIALIZED VIEW mv TO INNER UUID "XXXX" AS SELECT a FROM b"
|
||||
/// This replacement is safe only for CREATE queries when inner target tables don't exist yet.
|
||||
if (!query.attach)
|
||||
{
|
||||
auto generate_target_uuid = [&](ViewTarget::Kind target_kind)
|
||||
{
|
||||
if ((query.getTargetInnerUUID(target_kind) == UUIDHelpers::Nil) && query.getTargetTableID(target_kind).empty())
|
||||
setTargetInnerUUID(target_kind, UUIDHelpers::generateV4());
|
||||
};
|
||||
|
||||
/// If destination table (to_table_id) is not specified for materialized view,
|
||||
/// then MV will create inner table. We should generate UUID of inner table here.
|
||||
if (query.is_materialized_view)
|
||||
generate_target_uuid(ViewTarget::To);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CreateQueryUUIDs::empty() const
|
||||
{
|
||||
if (uuid != UUIDHelpers::Nil)
|
||||
return false;
|
||||
for (const auto & [_, inner_uuid] : targets_inner_uuids)
|
||||
{
|
||||
if (inner_uuid != UUIDHelpers::Nil)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String CreateQueryUUIDs::toString() const
|
||||
{
|
||||
WriteBufferFromOwnString out;
|
||||
out << "{";
|
||||
bool need_comma = false;
|
||||
auto add_name_and_uuid_to_string = [&](std::string_view name_, const UUID & uuid_)
|
||||
{
|
||||
if (std::exchange(need_comma, true))
|
||||
out << ", ";
|
||||
out << "\"" << name_ << "\": \"" << uuid_ << "\"";
|
||||
};
|
||||
if (uuid != UUIDHelpers::Nil)
|
||||
add_name_and_uuid_to_string("uuid", uuid);
|
||||
for (const auto & [kind, inner_uuid] : targets_inner_uuids)
|
||||
{
|
||||
if (inner_uuid != UUIDHelpers::Nil)
|
||||
add_name_and_uuid_to_string(::DB::toString(kind), inner_uuid);
|
||||
}
|
||||
out << "}";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
CreateQueryUUIDs CreateQueryUUIDs::fromString(const String & str)
|
||||
{
|
||||
ReadBufferFromString in{str};
|
||||
CreateQueryUUIDs res;
|
||||
skipWhitespaceIfAny(in);
|
||||
in >> "{";
|
||||
skipWhitespaceIfAny(in);
|
||||
char c;
|
||||
while (in.peek(c) && c != '}')
|
||||
{
|
||||
String name;
|
||||
String value;
|
||||
readDoubleQuotedString(name, in);
|
||||
skipWhitespaceIfAny(in);
|
||||
in >> ":";
|
||||
skipWhitespaceIfAny(in);
|
||||
readDoubleQuotedString(value, in);
|
||||
skipWhitespaceIfAny(in);
|
||||
if (name == "uuid")
|
||||
{
|
||||
res.uuid = parse<UUID>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewTarget::Kind kind;
|
||||
parseFromString(kind, name);
|
||||
res.setTargetInnerUUID(kind, parse<UUID>(value));
|
||||
}
|
||||
if (in.peek(c) && c == ',')
|
||||
{
|
||||
in.ignore(1);
|
||||
skipWhitespaceIfAny(in);
|
||||
}
|
||||
}
|
||||
in >> "}";
|
||||
return res;
|
||||
}
|
||||
|
||||
void CreateQueryUUIDs::setTargetInnerUUID(ViewTarget::Kind kind, const UUID & new_inner_uuid)
|
||||
{
|
||||
for (auto & pair : targets_inner_uuids)
|
||||
{
|
||||
if (pair.first == kind)
|
||||
{
|
||||
pair.second = new_inner_uuid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (new_inner_uuid != UUIDHelpers::Nil)
|
||||
targets_inner_uuids.emplace_back(kind, new_inner_uuid);
|
||||
}
|
||||
|
||||
UUID CreateQueryUUIDs::getTargetInnerUUID(ViewTarget::Kind kind) const
|
||||
{
|
||||
for (const auto & pair : targets_inner_uuids)
|
||||
{
|
||||
if (pair.first == kind)
|
||||
return pair.second;
|
||||
}
|
||||
return UUIDHelpers::Nil;
|
||||
}
|
||||
|
||||
void CreateQueryUUIDs::copyToQuery(ASTCreateQuery & query) const
|
||||
{
|
||||
query.uuid = uuid;
|
||||
|
||||
if (query.targets)
|
||||
query.targets->resetInnerUUIDs();
|
||||
|
||||
if (!targets_inner_uuids.empty())
|
||||
{
|
||||
if (!query.targets)
|
||||
query.set(query.targets, std::make_shared<ASTViewTargets>());
|
||||
|
||||
for (const auto & [kind, inner_uuid] : targets_inner_uuids)
|
||||
{
|
||||
if (inner_uuid != UUIDHelpers::Nil)
|
||||
query.targets->setInnerUUID(kind, inner_uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
40
src/Parsers/CreateQueryUUIDs.h
Normal file
40
src/Parsers/CreateQueryUUIDs.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <Parsers/ASTViewTargets.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
class ASTCreateQuery;
|
||||
|
||||
/// The UUID of a table or a database defined with a CREATE QUERY along with the UUIDs of its inner targets.
|
||||
struct CreateQueryUUIDs
|
||||
{
|
||||
CreateQueryUUIDs() = default;
|
||||
|
||||
/// Collect UUIDs from ASTCreateQuery.
|
||||
/// Parameters:
|
||||
/// `generate_random` - if it's true then unspecified in the query UUIDs will be generated randomly;
|
||||
/// `force_random` - if it's true then all UUIDs (even specified in the query) will be (re)generated randomly.
|
||||
explicit CreateQueryUUIDs(const ASTCreateQuery & query, bool generate_random = false, bool force_random = false);
|
||||
|
||||
bool empty() const;
|
||||
explicit operator bool() const { return !empty(); }
|
||||
|
||||
String toString() const;
|
||||
static CreateQueryUUIDs fromString(const String & str);
|
||||
|
||||
void setTargetInnerUUID(ViewTarget::Kind kind, const UUID & new_inner_uuid);
|
||||
UUID getTargetInnerUUID(ViewTarget::Kind kind) const;
|
||||
|
||||
/// Copies UUIDs to ASTCreateQuery.
|
||||
void copyToQuery(ASTCreateQuery & query) const;
|
||||
|
||||
/// UUID of the table.
|
||||
UUID uuid = UUIDHelpers::Nil;
|
||||
|
||||
/// UUIDs of its target table (or tables).
|
||||
std::vector<std::pair<ViewTarget::Kind, UUID>> targets_inner_uuids;
|
||||
};
|
||||
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
#include <Parsers/ParserSelectWithUnionQuery.h>
|
||||
#include <Parsers/ParserSetQuery.h>
|
||||
#include <Parsers/ParserRefreshStrategy.h>
|
||||
#include <Parsers/ParserViewTargets.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Parsers/ASTColumnDeclaration.h>
|
||||
|
||||
@ -693,7 +694,8 @@ bool ParserCreateTableQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
|
||||
|
||||
ASTPtr table;
|
||||
ASTPtr columns_list;
|
||||
ASTPtr storage;
|
||||
std::shared_ptr<ASTStorage> storage;
|
||||
ASTPtr targets;
|
||||
ASTPtr as_database;
|
||||
ASTPtr as_table;
|
||||
ASTPtr as_table_function;
|
||||
@ -773,6 +775,17 @@ bool ParserCreateTableQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
|
||||
return true;
|
||||
}
|
||||
|
||||
auto parse_storage = [&]
|
||||
{
|
||||
chassert(!storage);
|
||||
ASTPtr ast;
|
||||
if (!storage_p.parse(pos, ast, expected))
|
||||
return false;
|
||||
|
||||
storage = typeid_cast<std::shared_ptr<ASTStorage>>(ast);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto need_parse_as_select = [&is_create_empty, &pos, &expected]()
|
||||
{
|
||||
if (ParserKeyword{Keyword::EMPTY_AS}.ignore(pos, expected))
|
||||
@ -798,7 +811,7 @@ bool ParserCreateTableQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
|
||||
if (!s_rparen.ignore(pos, expected))
|
||||
return false;
|
||||
|
||||
auto storage_parse_result = storage_p.parse(pos, storage, expected);
|
||||
auto storage_parse_result = parse_storage();
|
||||
|
||||
if ((storage_parse_result || is_temporary) && need_parse_as_select())
|
||||
{
|
||||
@ -820,7 +833,7 @@ bool ParserCreateTableQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
|
||||
*/
|
||||
else
|
||||
{
|
||||
storage_p.parse(pos, storage, expected);
|
||||
parse_storage();
|
||||
|
||||
/// CREATE|ATTACH TABLE ... AS ...
|
||||
if (need_parse_as_select())
|
||||
@ -843,7 +856,7 @@ bool ParserCreateTableQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
|
||||
|
||||
/// Optional - ENGINE can be specified.
|
||||
if (!storage)
|
||||
storage_p.parse(pos, storage, expected);
|
||||
parse_storage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -904,6 +917,7 @@ bool ParserCreateTableQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
|
||||
tryGetIdentifierNameInto(as_database, query->as_database);
|
||||
tryGetIdentifierNameInto(as_table, query->as_table);
|
||||
query->set(query->select, select);
|
||||
query->set(query->targets, targets);
|
||||
query->is_create_empty = is_create_empty;
|
||||
|
||||
if (from_path)
|
||||
@ -977,6 +991,13 @@ bool ParserCreateLiveViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<ASTViewTargets> targets;
|
||||
if (to_table)
|
||||
{
|
||||
targets = std::make_shared<ASTViewTargets>();
|
||||
targets->setTableID(ViewTarget::To, to_table->as<ASTTableIdentifier>()->getTableId());
|
||||
}
|
||||
|
||||
/// Optional - a list of columns can be specified. It must fully comply with SELECT.
|
||||
if (s_lparen.ignore(pos, expected))
|
||||
{
|
||||
@ -1017,14 +1038,12 @@ bool ParserCreateLiveViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
||||
if (query->table)
|
||||
query->children.push_back(query->table);
|
||||
|
||||
if (to_table)
|
||||
query->to_table_id = to_table->as<ASTTableIdentifier>()->getTableId();
|
||||
|
||||
query->set(query->columns_list, columns_list);
|
||||
|
||||
tryGetIdentifierNameInto(as_database, query->as_database);
|
||||
tryGetIdentifierNameInto(as_table, query->as_table);
|
||||
query->set(query->select, select);
|
||||
query->set(query->targets, targets);
|
||||
|
||||
if (comment)
|
||||
query->set(query->comment, comment);
|
||||
@ -1139,6 +1158,18 @@ bool ParserCreateWindowViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
|
||||
storage_p.parse(pos, storage, expected);
|
||||
}
|
||||
|
||||
std::shared_ptr<ASTViewTargets> targets;
|
||||
if (to_table || storage || inner_storage)
|
||||
{
|
||||
targets = std::make_shared<ASTViewTargets>();
|
||||
if (to_table)
|
||||
targets->setTableID(ViewTarget::To, to_table->as<ASTTableIdentifier>()->getTableId());
|
||||
if (storage)
|
||||
targets->setInnerEngine(ViewTarget::To, storage);
|
||||
if (inner_storage)
|
||||
targets->setInnerEngine(ViewTarget::Inner, inner_storage);
|
||||
}
|
||||
|
||||
// WATERMARK
|
||||
if (s_watermark.ignore(pos, expected))
|
||||
{
|
||||
@ -1195,12 +1226,8 @@ bool ParserCreateWindowViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
|
||||
if (query->table)
|
||||
query->children.push_back(query->table);
|
||||
|
||||
if (to_table)
|
||||
query->to_table_id = to_table->as<ASTTableIdentifier>()->getTableId();
|
||||
|
||||
query->set(query->columns_list, columns_list);
|
||||
query->set(query->storage, storage);
|
||||
query->set(query->inner_storage, inner_storage);
|
||||
|
||||
query->is_watermark_strictly_ascending = is_watermark_strictly_ascending;
|
||||
query->is_watermark_ascending = is_watermark_ascending;
|
||||
query->is_watermark_bounded = is_watermark_bounded;
|
||||
@ -1213,6 +1240,7 @@ bool ParserCreateWindowViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
|
||||
tryGetIdentifierNameInto(as_database, query->as_database);
|
||||
tryGetIdentifierNameInto(as_table, query->as_table);
|
||||
query->set(query->select, select);
|
||||
query->set(query->targets, targets);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1436,6 +1464,7 @@ bool ParserCreateDatabaseQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
ParserKeyword s_create(Keyword::CREATE);
|
||||
@ -1622,13 +1651,8 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
||||
if (query->table)
|
||||
query->children.push_back(query->table);
|
||||
|
||||
if (to_table)
|
||||
query->to_table_id = to_table->as<ASTTableIdentifier>()->getTableId();
|
||||
if (to_inner_uuid)
|
||||
query->to_inner_uuid = parseFromString<UUID>(to_inner_uuid->as<ASTLiteral>()->value.get<String>());
|
||||
|
||||
query->set(query->columns_list, columns_list);
|
||||
query->set(query->storage, storage);
|
||||
|
||||
if (refresh_strategy)
|
||||
query->set(query->refresh_strategy, refresh_strategy);
|
||||
if (comment)
|
||||
@ -1639,29 +1663,41 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
||||
if (query->columns_list && query->columns_list->primary_key)
|
||||
{
|
||||
/// If engine is not set will use default one
|
||||
if (!query->storage)
|
||||
query->set(query->storage, std::make_shared<ASTStorage>());
|
||||
else if (query->storage->primary_key)
|
||||
if (!storage)
|
||||
storage = std::make_shared<ASTStorage>();
|
||||
auto & storage_ref = typeid_cast<ASTStorage &>(*storage);
|
||||
if (storage_ref.primary_key)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Multiple primary keys are not allowed.");
|
||||
|
||||
query->storage->primary_key = query->columns_list->primary_key;
|
||||
|
||||
storage_ref.primary_key = query->columns_list->primary_key;
|
||||
}
|
||||
|
||||
if (query->columns_list && (query->columns_list->primary_key_from_columns))
|
||||
{
|
||||
/// If engine is not set will use default one
|
||||
if (!query->storage)
|
||||
query->set(query->storage, std::make_shared<ASTStorage>());
|
||||
else if (query->storage->primary_key)
|
||||
if (!storage)
|
||||
storage = std::make_shared<ASTStorage>();
|
||||
auto & storage_ref = typeid_cast<ASTStorage &>(*storage);
|
||||
if (storage_ref.primary_key)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Multiple primary keys are not allowed.");
|
||||
storage_ref.primary_key = query->columns_list->primary_key_from_columns;
|
||||
}
|
||||
|
||||
query->storage->primary_key = query->columns_list->primary_key_from_columns;
|
||||
std::shared_ptr<ASTViewTargets> targets;
|
||||
if (to_table || to_inner_uuid || storage)
|
||||
{
|
||||
targets = std::make_shared<ASTViewTargets>();
|
||||
if (to_table)
|
||||
targets->setTableID(ViewTarget::To, to_table->as<ASTTableIdentifier>()->getTableId());
|
||||
if (to_inner_uuid)
|
||||
targets->setInnerUUID(ViewTarget::To, parseFromString<UUID>(to_inner_uuid->as<ASTLiteral>()->value.safeGet<String>()));
|
||||
if (storage)
|
||||
targets->setInnerEngine(ViewTarget::To, storage);
|
||||
}
|
||||
|
||||
tryGetIdentifierNameInto(as_database, query->as_database);
|
||||
tryGetIdentifierNameInto(as_table, query->as_table);
|
||||
query->set(query->select, select);
|
||||
query->set(query->targets, targets);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
88
src/Parsers/ParserViewTargets.cpp
Normal file
88
src/Parsers/ParserViewTargets.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include <Parsers/ParserViewTargets.h>
|
||||
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Parsers/ASTViewTargets.h>
|
||||
#include <Parsers/ExpressionElementParsers.h>
|
||||
#include <Parsers/ParserCreateQuery.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
ParserViewTargets::ParserViewTargets()
|
||||
{
|
||||
for (auto kind : magic_enum::enum_values<ViewTarget::Kind>())
|
||||
accept_kinds.push_back(kind);
|
||||
}
|
||||
|
||||
bool ParserViewTargets::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
ParserStringLiteral literal_p;
|
||||
ParserStorage storage_p{ParserStorage::TABLE_ENGINE};
|
||||
ParserCompoundIdentifier table_name_p(/*table_name_with_optional_uuid*/ true, /*allow_query_parameter*/ true);
|
||||
|
||||
std::shared_ptr<ASTViewTargets> res;
|
||||
|
||||
auto result = [&] -> ASTViewTargets &
|
||||
{
|
||||
if (!res)
|
||||
res = std::make_shared<ASTViewTargets>();
|
||||
return *res;
|
||||
};
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto start = pos;
|
||||
for (auto kind : accept_kinds)
|
||||
{
|
||||
auto current = pos;
|
||||
|
||||
auto keyword = ASTViewTargets::getKeywordForInnerUUID(kind);
|
||||
if (keyword && ParserKeyword{*keyword}.ignore(pos, expected))
|
||||
{
|
||||
ASTPtr ast;
|
||||
if (literal_p.parse(pos, ast, expected))
|
||||
{
|
||||
result().setInnerUUID(kind, parseFromString<UUID>(ast->as<ASTLiteral>()->value.safeGet<String>()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos = current;
|
||||
|
||||
keyword = ASTViewTargets::getKeywordForInnerStorage(kind);
|
||||
if (keyword && ParserKeyword{*keyword}.ignore(pos, expected))
|
||||
{
|
||||
ASTPtr ast;
|
||||
if (storage_p.parse(pos, ast, expected))
|
||||
{
|
||||
result().setInnerEngine(kind, ast);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos = current;
|
||||
|
||||
keyword = ASTViewTargets::getKeywordForTableID(kind);
|
||||
if (keyword && ParserKeyword{*keyword}.ignore(pos, expected))
|
||||
{
|
||||
ASTPtr ast;
|
||||
if (table_name_p.parse(pos, ast, expected))
|
||||
{
|
||||
result().setTableID(kind, ast->as<ASTTableIdentifier>()->getTableId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos = current;
|
||||
}
|
||||
if (pos == start)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!res || res->targets.empty())
|
||||
return false;
|
||||
|
||||
node = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
29
src/Parsers/ParserViewTargets.h
Normal file
29
src/Parsers/ParserViewTargets.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Parsers/IParserBase.h>
|
||||
#include <Parsers/ASTViewTargets.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/// Parses information about target tables (external or inner) of a materialized view or a window view.
|
||||
/// The function parses one or multiple parts of a CREATE query looking like this:
|
||||
/// TO db.table_name
|
||||
/// TO INNER UUID 'XXX'
|
||||
/// {ENGINE / INNER ENGINE} TableEngine(arguments) [ORDER BY ...] [SETTINGS ...]
|
||||
/// Returns ASTViewTargets if succeeded.
|
||||
class ParserViewTargets : public IParserBase
|
||||
{
|
||||
public:
|
||||
ParserViewTargets();
|
||||
explicit ParserViewTargets(const std::vector<ViewTarget::Kind> & accept_kinds_) : accept_kinds(accept_kinds_) { }
|
||||
|
||||
protected:
|
||||
const char * getName() const override { return "ViewTargets"; }
|
||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||
|
||||
std::vector<ViewTarget::Kind> accept_kinds;
|
||||
};
|
||||
|
||||
}
|
@ -93,11 +93,6 @@ StorageMaterializedView::StorageMaterializedView(
|
||||
{
|
||||
StorageInMemoryMetadata storage_metadata;
|
||||
storage_metadata.setColumns(columns_);
|
||||
auto * storage_def = query.storage;
|
||||
if (storage_def && storage_def->primary_key)
|
||||
storage_metadata.primary_key = KeyDescription::getKeyFromAST(storage_def->primary_key->ptr(),
|
||||
storage_metadata.columns,
|
||||
local_context->getGlobalContext());
|
||||
|
||||
if (query.sql_security)
|
||||
storage_metadata.setSQLSecurity(query.sql_security->as<ASTSQLSecurity &>());
|
||||
@ -110,12 +105,21 @@ StorageMaterializedView::StorageMaterializedView(
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "SELECT query is not specified for {}", getName());
|
||||
|
||||
/// If the destination table is not set, use inner table
|
||||
has_inner_table = query.to_table_id.empty();
|
||||
if (has_inner_table && !query.storage)
|
||||
auto to_table_id = query.getTargetTableID(ViewTarget::To);
|
||||
has_inner_table = to_table_id.empty();
|
||||
auto to_inner_uuid = query.getTargetInnerUUID(ViewTarget::To);
|
||||
auto to_table_engine = query.getTargetInnerEngine(ViewTarget::To);
|
||||
|
||||
if (has_inner_table && !to_table_engine)
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY,
|
||||
"You must specify where to save results of a MaterializedView query: "
|
||||
"either ENGINE or an existing table in a TO clause");
|
||||
|
||||
if (to_table_engine && to_table_engine->primary_key)
|
||||
storage_metadata.primary_key = KeyDescription::getKeyFromAST(to_table_engine->primary_key->ptr(),
|
||||
storage_metadata.columns,
|
||||
local_context->getGlobalContext());
|
||||
|
||||
auto select = SelectQueryDescription::getSelectQueryFromASTForMatView(query.select->clone(), query.refresh_strategy != nullptr, local_context);
|
||||
if (select.select_table_id)
|
||||
{
|
||||
@ -135,25 +139,25 @@ StorageMaterializedView::StorageMaterializedView(
|
||||
|
||||
setInMemoryMetadata(storage_metadata);
|
||||
|
||||
bool point_to_itself_by_uuid = has_inner_table && query.to_inner_uuid != UUIDHelpers::Nil
|
||||
&& query.to_inner_uuid == table_id_.uuid;
|
||||
bool point_to_itself_by_name = !has_inner_table && query.to_table_id.database_name == table_id_.database_name
|
||||
&& query.to_table_id.table_name == table_id_.table_name;
|
||||
bool point_to_itself_by_uuid = has_inner_table && to_inner_uuid != UUIDHelpers::Nil
|
||||
&& to_inner_uuid == table_id_.uuid;
|
||||
bool point_to_itself_by_name = !has_inner_table && to_table_id.database_name == table_id_.database_name
|
||||
&& to_table_id.table_name == table_id_.table_name;
|
||||
if (point_to_itself_by_uuid || point_to_itself_by_name)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Materialized view {} cannot point to itself", table_id_.getFullTableName());
|
||||
|
||||
if (!has_inner_table)
|
||||
{
|
||||
target_table_id = query.to_table_id;
|
||||
target_table_id = to_table_id;
|
||||
}
|
||||
else if (LoadingStrictnessLevel::ATTACH <= mode)
|
||||
{
|
||||
/// If there is an ATTACH request, then the internal table must already be created.
|
||||
target_table_id = StorageID(getStorageID().database_name, generateInnerTableName(getStorageID()), query.to_inner_uuid);
|
||||
target_table_id = StorageID(getStorageID().database_name, generateInnerTableName(getStorageID()), to_inner_uuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
const String & engine = query.storage->engine->name;
|
||||
const String & engine = to_table_engine->engine->name;
|
||||
const auto & storage_features = StorageFactory::instance().getStorageFeatures(engine);
|
||||
|
||||
/// We will create a query to create an internal table.
|
||||
@ -161,8 +165,8 @@ StorageMaterializedView::StorageMaterializedView(
|
||||
auto manual_create_query = std::make_shared<ASTCreateQuery>();
|
||||
manual_create_query->setDatabase(getStorageID().database_name);
|
||||
manual_create_query->setTable(generateInnerTableName(getStorageID()));
|
||||
manual_create_query->uuid = query.to_inner_uuid;
|
||||
manual_create_query->has_uuid = query.to_inner_uuid != UUIDHelpers::Nil;
|
||||
manual_create_query->uuid = to_inner_uuid;
|
||||
manual_create_query->has_uuid = to_inner_uuid != UUIDHelpers::Nil;
|
||||
|
||||
auto new_columns_list = std::make_shared<ASTColumns>();
|
||||
new_columns_list->set(new_columns_list->columns, query.columns_list->columns->ptr());
|
||||
@ -184,7 +188,9 @@ StorageMaterializedView::StorageMaterializedView(
|
||||
}
|
||||
|
||||
manual_create_query->set(manual_create_query->columns_list, new_columns_list);
|
||||
manual_create_query->set(manual_create_query->storage, query.storage->ptr());
|
||||
|
||||
if (to_table_engine)
|
||||
manual_create_query->set(manual_create_query->storage, to_table_engine);
|
||||
|
||||
InterpreterCreateQuery create_interpreter(manual_create_query, create_context);
|
||||
create_interpreter.setInternal(true);
|
||||
|
@ -484,7 +484,8 @@ protected:
|
||||
if (ast_create && !context->getSettingsRef().show_table_uuid_in_table_create_query_if_not_nil)
|
||||
{
|
||||
ast_create->uuid = UUIDHelpers::Nil;
|
||||
ast_create->to_inner_uuid = UUIDHelpers::Nil;
|
||||
if (ast_create->targets)
|
||||
ast_create->targets->resetInnerUUIDs();
|
||||
}
|
||||
|
||||
if (columns_mask[src_index++])
|
||||
|
@ -1209,8 +1209,11 @@ StorageWindowView::StorageWindowView(
|
||||
setInMemoryMetadata(storage_metadata);
|
||||
|
||||
/// If the target table is not set, use inner target table
|
||||
has_inner_target_table = query.to_table_id.empty();
|
||||
if (has_inner_target_table && !query.storage)
|
||||
auto to_table_id = query.getTargetTableID(ViewTarget::To);
|
||||
has_inner_target_table = to_table_id.empty();
|
||||
auto to_table_engine = query.getTargetInnerEngine(ViewTarget::To);
|
||||
|
||||
if (has_inner_target_table && !to_table_engine)
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY,
|
||||
"You must specify where to save results of a WindowView query: "
|
||||
"either ENGINE or an existing table in a TO clause");
|
||||
@ -1225,12 +1228,12 @@ StorageWindowView::StorageWindowView(
|
||||
|
||||
auto inner_query = initInnerQuery(query.select->list_of_selects->children.at(0)->as<ASTSelectQuery &>(), context_);
|
||||
|
||||
if (query.inner_storage)
|
||||
inner_table_engine = query.inner_storage->clone();
|
||||
if (auto inner_storage = query.getTargetInnerEngine(ViewTarget::Inner))
|
||||
inner_table_engine = inner_storage->clone();
|
||||
inner_table_id = StorageID(getStorageID().database_name, generateInnerTableName(getStorageID()));
|
||||
inner_fetch_query = generateInnerFetchQuery(inner_table_id);
|
||||
|
||||
target_table_id = has_inner_target_table ? StorageID(table_id_.database_name, generateTargetTableName(table_id_)) : query.to_table_id;
|
||||
target_table_id = has_inner_target_table ? StorageID(table_id_.database_name, generateTargetTableName(table_id_)) : to_table_id;
|
||||
|
||||
if (is_proctime)
|
||||
next_fire_signal = getWindowUpperBound(now());
|
||||
@ -1255,7 +1258,7 @@ StorageWindowView::StorageWindowView(
|
||||
new_columns_list->set(new_columns_list->columns, query.columns_list->columns->ptr());
|
||||
|
||||
target_create_query->set(target_create_query->columns_list, new_columns_list);
|
||||
target_create_query->set(target_create_query->storage, query.storage->ptr());
|
||||
target_create_query->set(target_create_query->storage, to_table_engine);
|
||||
|
||||
InterpreterCreateQuery create_interpreter_(target_create_query, create_context_);
|
||||
create_interpreter_.setInternal(true);
|
||||
|
Loading…
Reference in New Issue
Block a user