From e21ab223c5ae7af17b6a1722b559dfcea17bf851 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Tue, 8 Mar 2022 16:11:21 +0800 Subject: [PATCH 001/377] finish dev --- programs/copier/ClusterCopierApp.cpp | 8 ++++++++ programs/extract-from-config/ExtractFromConfig.cpp | 14 ++++++++++++-- programs/server/Server.cpp | 6 ++++-- src/Interpreters/Context.cpp | 7 ++++--- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/programs/copier/ClusterCopierApp.cpp b/programs/copier/ClusterCopierApp.cpp index d1ea59ed119..5f8bb84f47b 100644 --- a/programs/copier/ClusterCopierApp.cpp +++ b/programs/copier/ClusterCopierApp.cpp @@ -12,6 +12,11 @@ namespace fs = std::filesystem; namespace DB { +namespace ErrorCodes +{ + extern const int EXCESSIVE_ELEMENT_IN_CONFIG; +} + /// ClusterCopierApp void ClusterCopierApp::initialize(Poco::Util::Application & self) @@ -192,6 +197,9 @@ void ClusterCopierApp::mainImpl() if (!task_file.empty()) copier->uploadTaskDescription(task_path, task_file, config().getBool("task-upload-force", false)); + if (config().has("zookeeper") || config().has("keeper")) + throw Exception("Both zookeeper and keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); + copier->init(); copier->process(ConnectionTimeouts::getTCPTimeoutsWithoutFailover(context->getSettingsRef())); diff --git a/programs/extract-from-config/ExtractFromConfig.cpp b/programs/extract-from-config/ExtractFromConfig.cpp index 3fd665bcb26..e346ea83888 100644 --- a/programs/extract-from-config/ExtractFromConfig.cpp +++ b/programs/extract-from-config/ExtractFromConfig.cpp @@ -12,6 +12,10 @@ #include #include +namespace DB::ErrorCodes +{ + extern const int EXCESSIVE_ELEMENT_IN_CONFIG; +} static void setupLogging(const std::string & log_level) { @@ -32,8 +36,14 @@ static std::string extractFromConfig( if (has_zk_includes && process_zk_includes) { DB::ConfigurationPtr bootstrap_configuration(new Poco::Util::XMLConfiguration(config_xml)); - zkutil::ZooKeeperPtr zookeeper = std::make_shared( - *bootstrap_configuration, "zookeeper", nullptr); + + if (bootstrap_configuration->has("zookeeper") && bootstrap_configuration->has("keeper")) + throw DB::Exception(DB::ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG, "Both zookeeper and keeper are specified"); + + zkutil::ZooKeeperPtr zookeeper; + zookeeper = std::make_shared( + *bootstrap_configuration, bootstrap_configuration->has("zookeeper") ? "zookeeper" : "keeper", nullptr); + zkutil::ZooKeeperNodeCache zk_node_cache([&] { return zookeeper; }); config_xml = processor.processConfig(&has_zk_includes, &zk_node_cache); } diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index b856131d821..72b5eb6540a 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -592,7 +592,9 @@ if (ThreadFuzzer::instance().isEffective()) ConnectionCollector::init(global_context, config().getUInt("max_threads_for_connection_collector", 10)); - bool has_zookeeper = config().has("zookeeper"); + if (config().has("zookeeper") && config().has("keeper")) + throw Exception("Both zookeeper and keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); + bool has_zookeeper = config().has("zookeeper") || config().has("keeper"); zkutil::ZooKeeperNodeCache main_config_zk_node_cache([&] { return global_context->getZooKeeper(); }); zkutil::EventPtr main_config_zk_changed_event = std::make_shared(); @@ -955,7 +957,7 @@ if (ThreadFuzzer::instance().isEffective()) { /// We do not load ZooKeeper configuration on the first config loading /// because TestKeeper server is not started yet. - if (config->has("zookeeper")) + if (config->has("zookeeper") || config->has("keeper")) global_context->reloadZooKeeperIfChanged(config); global_context->reloadAuxiliaryZooKeepersConfigIfChanged(config); diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index b03875eed7a..756afed415a 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -1847,7 +1847,8 @@ zkutil::ZooKeeperPtr Context::getZooKeeper() const const auto & config = shared->zookeeper_config ? *shared->zookeeper_config : getConfigRef(); if (!shared->zookeeper) - shared->zookeeper = std::make_shared(config, "zookeeper", getZooKeeperLog()); + shared->zookeeper + = std::make_shared(config, config.has("zookeeper") ? "zookeeper" : "keeper", getZooKeeperLog()); else if (shared->zookeeper->expired()) shared->zookeeper = shared->zookeeper->startNewSession(); @@ -2058,7 +2059,7 @@ void Context::reloadZooKeeperIfChanged(const ConfigurationPtr & config) const { std::lock_guard lock(shared->zookeeper_mutex); shared->zookeeper_config = config; - reloadZooKeeperIfChangedImpl(config, "zookeeper", shared->zookeeper, getZooKeeperLog()); + reloadZooKeeperIfChangedImpl(config, config->has("zookeeper") ? "zookeeper" : "keeper", shared->zookeeper, getZooKeeperLog()); } void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & config) @@ -2082,7 +2083,7 @@ void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & bool Context::hasZooKeeper() const { - return getConfigRef().has("zookeeper"); + return getConfigRef().has("zookeeper") || getConfigRef().has("keeper"); } bool Context::hasAuxiliaryZooKeeper(const String & name) const From d78493eeb2242531ead1b6fce8033cdd0220e6a4 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Tue, 8 Mar 2022 16:41:16 +0800 Subject: [PATCH 002/377] commit again --- programs/copier/ClusterCopierApp.cpp | 2 +- programs/server/Server.cpp | 1 + src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp | 2 +- src/Interpreters/Context.cpp | 2 +- .../examples/get_abandonable_lock_in_all_partitions.cpp | 2 +- src/Storages/examples/get_current_inserts_in_replicated.cpp | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/programs/copier/ClusterCopierApp.cpp b/programs/copier/ClusterCopierApp.cpp index 5f8bb84f47b..6e674473af3 100644 --- a/programs/copier/ClusterCopierApp.cpp +++ b/programs/copier/ClusterCopierApp.cpp @@ -197,7 +197,7 @@ void ClusterCopierApp::mainImpl() if (!task_file.empty()) copier->uploadTaskDescription(task_path, task_file, config().getBool("task-upload-force", false)); - if (config().has("zookeeper") || config().has("keeper")) + if (config().has("zookeeper") && config().has("keeper")) throw Exception("Both zookeeper and keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); copier->init(); diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 72b5eb6540a..049d97f03f6 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -594,6 +594,7 @@ if (ThreadFuzzer::instance().isEffective()) if (config().has("zookeeper") && config().has("keeper")) throw Exception("Both zookeeper and keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); + bool has_zookeeper = config().has("zookeeper") || config().has("keeper"); zkutil::ZooKeeperNodeCache main_config_zk_node_cache([&] { return global_context->getZooKeeper(); }); diff --git a/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp b/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp index cf819121234..e91296af61b 100644 --- a/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp +++ b/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp @@ -25,7 +25,7 @@ int main(int argc, char ** argv) DB::ConfigProcessor processor(argv[1], false, true); auto config = processor.loadConfig().configuration; - zkutil::ZooKeeper zk(*config, "zookeeper", nullptr); + zkutil::ZooKeeper zk(*config, config->has("zookeeper") ? "zookeeper" : "keeper", nullptr); zkutil::EventPtr watch = std::make_shared(); /// NOTE: setting watches in multiple threads because doing it in a single thread is too slow. diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 756afed415a..b7759e461ca 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -1883,7 +1883,7 @@ bool Context::tryCheckClientConnectionToMyKeeperCluster() const try { /// If our server is part of main Keeper cluster - if (checkZooKeeperConfigIsLocal(getConfigRef(), "zookeeper")) + if (checkZooKeeperConfigIsLocal(getConfigRef(), "zookeeper") || checkZooKeeperConfigIsLocal(getConfigRef(), "keeper")) { LOG_DEBUG(shared->log, "Keeper server is participant of the main zookeeper cluster, will try to connect to it"); getZooKeeper(); diff --git a/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp b/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp index e2145f5afb0..d947af71a60 100644 --- a/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp +++ b/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp @@ -26,7 +26,7 @@ try auto config = processor.loadConfig().configuration; String root_path = argv[2]; - zkutil::ZooKeeper zk(*config, "zookeeper", nullptr); + zkutil::ZooKeeper zk(*config, config->has("zookeeper") ? "zookeeper" : "keeper", nullptr); String temp_path = root_path + "/temp"; String blocks_path = root_path + "/block_numbers"; diff --git a/src/Storages/examples/get_current_inserts_in_replicated.cpp b/src/Storages/examples/get_current_inserts_in_replicated.cpp index d7dedbcab9c..2355851bd89 100644 --- a/src/Storages/examples/get_current_inserts_in_replicated.cpp +++ b/src/Storages/examples/get_current_inserts_in_replicated.cpp @@ -29,7 +29,7 @@ try auto config = processor.loadConfig().configuration; String zookeeper_path = argv[2]; - auto zookeeper = std::make_shared(*config, "zookeeper", nullptr); + auto zookeeper = std::make_shared(*config, config->has("zookeeper") ? "zookeeper" : "keeper", nullptr); std::unordered_map> current_inserts; From 3b957367e160bb986d590fe1ccf98ca4951a4833 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Tue, 8 Mar 2022 17:03:24 +0800 Subject: [PATCH 003/377] add tests --- .../test_zookeeper_config/configs/zookeeper_config_root_a.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_zookeeper_config/configs/zookeeper_config_root_a.xml b/tests/integration/test_zookeeper_config/configs/zookeeper_config_root_a.xml index 6c413378524..d3c62862002 100644 --- a/tests/integration/test_zookeeper_config/configs/zookeeper_config_root_a.xml +++ b/tests/integration/test_zookeeper_config/configs/zookeeper_config_root_a.xml @@ -1,5 +1,5 @@ - + zoo1 2181 @@ -14,5 +14,5 @@ 3000 /root_a - + From e2bb06dbb5eab4db2326862cc6b4b9ee27d9ac5a Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Tue, 8 Mar 2022 17:14:55 +0800 Subject: [PATCH 004/377] add tests --- tests/integration/helpers/zookeeper_secure_config.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/helpers/zookeeper_secure_config.xml b/tests/integration/helpers/zookeeper_secure_config.xml index b3b20eb3429..7f4e6f8a12b 100644 --- a/tests/integration/helpers/zookeeper_secure_config.xml +++ b/tests/integration/helpers/zookeeper_secure_config.xml @@ -1,5 +1,5 @@ - + zoo1 2281 @@ -13,5 +13,5 @@ 2281 3000 - + From 22a938fff02bb8592947c5f0ce604d5c96c76ec2 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Tue, 8 Mar 2022 19:16:09 +0800 Subject: [PATCH 005/377] solve issue: https://github.com/ClickHouse/ClickHouse/issues/34767 --- programs/server/Server.cpp | 4 +- src/Common/ZooKeeper/ZooKeeper.cpp | 56 ++++++++++++++++--- .../examples/zk_many_watches_reconnect.cpp | 2 +- src/Interpreters/Context.cpp | 21 +++++-- ...get_abandonable_lock_in_all_partitions.cpp | 2 +- .../get_current_inserts_in_replicated.cpp | 7 ++- 6 files changed, 74 insertions(+), 18 deletions(-) diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 049d97f03f6..1ed0d27c80d 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -595,7 +595,7 @@ if (ThreadFuzzer::instance().isEffective()) if (config().has("zookeeper") && config().has("keeper")) throw Exception("Both zookeeper and keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); - bool has_zookeeper = config().has("zookeeper") || config().has("keeper"); + bool has_zookeeper = config().has("zookeeper") || config().has("keeper") || config().has("keeper_server"); zkutil::ZooKeeperNodeCache main_config_zk_node_cache([&] { return global_context->getZooKeeper(); }); zkutil::EventPtr main_config_zk_changed_event = std::make_shared(); @@ -958,7 +958,7 @@ if (ThreadFuzzer::instance().isEffective()) { /// We do not load ZooKeeper configuration on the first config loading /// because TestKeeper server is not started yet. - if (config->has("zookeeper") || config->has("keeper")) + if (config->has("zookeeper") || config->has("keeper") || config->has("keeper_server")) global_context->reloadZooKeeperIfChanged(config); global_context->reloadAuxiliaryZooKeepersConfigIfChanged(config); diff --git a/src/Common/ZooKeeper/ZooKeeper.cpp b/src/Common/ZooKeeper/ZooKeeper.cpp index b1574341c40..19647166b2a 100644 --- a/src/Common/ZooKeeper/ZooKeeper.cpp +++ b/src/Common/ZooKeeper/ZooKeeper.cpp @@ -175,14 +175,32 @@ ZooKeeper::ZooKeeper(const Strings & hosts_, const std::string & identity_, int3 struct ZooKeeperArgs { +public: ZooKeeperArgs(const Poco::Util::AbstractConfiguration & config, const std::string & config_name) { - Poco::Util::AbstractConfiguration::Keys keys; - config.keys(config_name, keys); - session_timeout_ms = Coordination::DEFAULT_SESSION_TIMEOUT_MS; operation_timeout_ms = Coordination::DEFAULT_OPERATION_TIMEOUT_MS; implementation = "zookeeper"; + + if (endsWith(config_name, "keeper_server.raft_configuration")) + initFromSectionKeeperServer(config, config_name); + else + initFromSectionKeeper(config, config_name); + + if (!chroot.empty()) + { + if (chroot.front() != '/') + throw KeeperException(std::string("Root path in config file should start with '/', but got ") + chroot, Coordination::Error::ZBADARGUMENTS); + if (chroot.back() == '/') + chroot.pop_back(); + } + } + +private: + void initFromSectionKeeper(const Poco::Util::AbstractConfiguration & config, const std::string & config_name) + { + Poco::Util::AbstractConfiguration::Keys keys; + config.keys(config_name, keys); for (const auto & key : keys) { if (startsWith(key, "node")) @@ -216,16 +234,38 @@ struct ZooKeeperArgs else throw KeeperException(std::string("Unknown key ") + key + " in config file", Coordination::Error::ZBADARGUMENTS); } + } - if (!chroot.empty()) + void initFromSectionKeeperServer(const Poco::Util::AbstractConfiguration & config, const std::string & config_name) + { + Poco::Util::AbstractConfiguration::Keys keys; + config.keys(config_name, keys); + + bool secure = false; + for (const auto & key : keys) { - if (chroot.front() != '/') - throw KeeperException(std::string("Root path in config file should start with '/', but got ") + chroot, Coordination::Error::ZBADARGUMENTS); - if (chroot.back() == '/') - chroot.pop_back(); + if (key == "server") + { + hosts.push_back( + // (config.getBool(config_name + "." + key + ".secure", false) ? "secure://" : "") + + config.getString(config_name + "." + key + ".hostname") + ":" + + config.getString(config_name + "." + key + ".port", "9234") + ); + } + else if (key == "secure") + { + secure = config.getBool(config_name + "." + key, false); + } + } + + if (secure && !hosts.empty()) + { + for (auto & host : hosts) + host = "secure://" + host; } } +public: Strings hosts; std::string identity; int session_timeout_ms; diff --git a/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp b/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp index e91296af61b..b68035b4c4c 100644 --- a/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp +++ b/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp @@ -25,7 +25,7 @@ int main(int argc, char ** argv) DB::ConfigProcessor processor(argv[1], false, true); auto config = processor.loadConfig().configuration; - zkutil::ZooKeeper zk(*config, config->has("zookeeper") ? "zookeeper" : "keeper", nullptr); + zkutil::ZooKeeper zk(*config, config->has("zookeeper") ? "zookeeper" : config->has("keeper") ? "keeper" : "keeper_server", nullptr); zkutil::EventPtr watch = std::make_shared(); /// NOTE: setting watches in multiple threads because doing it in a single thread is too slow. diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index b7759e461ca..a8efe834034 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -1847,8 +1847,12 @@ zkutil::ZooKeeperPtr Context::getZooKeeper() const const auto & config = shared->zookeeper_config ? *shared->zookeeper_config : getConfigRef(); if (!shared->zookeeper) - shared->zookeeper - = std::make_shared(config, config.has("zookeeper") ? "zookeeper" : "keeper", getZooKeeperLog()); + shared->zookeeper = std::make_shared( + config, + config.has("zookeeper") ? "zookeeper" + : config.has("keeper") ? "keeper" + : "keeper_server", + getZooKeeperLog()); else if (shared->zookeeper->expired()) shared->zookeeper = shared->zookeeper->startNewSession(); @@ -1883,7 +1887,8 @@ bool Context::tryCheckClientConnectionToMyKeeperCluster() const try { /// If our server is part of main Keeper cluster - if (checkZooKeeperConfigIsLocal(getConfigRef(), "zookeeper") || checkZooKeeperConfigIsLocal(getConfigRef(), "keeper")) + if (checkZooKeeperConfigIsLocal(getConfigRef(), "zookeeper") || checkZooKeeperConfigIsLocal(getConfigRef(), "keeper") + || (!getConfigRef().has("zookeeper") && !getConfigRef().has("keeper") && getConfigRef().has("keeper_server"))) { LOG_DEBUG(shared->log, "Keeper server is participant of the main zookeeper cluster, will try to connect to it"); getZooKeeper(); @@ -2059,7 +2064,13 @@ void Context::reloadZooKeeperIfChanged(const ConfigurationPtr & config) const { std::lock_guard lock(shared->zookeeper_mutex); shared->zookeeper_config = config; - reloadZooKeeperIfChangedImpl(config, config->has("zookeeper") ? "zookeeper" : "keeper", shared->zookeeper, getZooKeeperLog()); + reloadZooKeeperIfChangedImpl( + config, + config->has("zookeeper") ? "zookeeper" + : config->has("keeper") ? "keeper" + : "keeper_server", + shared->zookeeper, + getZooKeeperLog()); } void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & config) @@ -2083,7 +2094,7 @@ void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & bool Context::hasZooKeeper() const { - return getConfigRef().has("zookeeper") || getConfigRef().has("keeper"); + return getConfigRef().has("zookeeper") || getConfigRef().has("keeper") || getConfigRef().has("keeper_server"); } bool Context::hasAuxiliaryZooKeeper(const String & name) const diff --git a/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp b/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp index d947af71a60..ee068d1bf26 100644 --- a/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp +++ b/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp @@ -26,7 +26,7 @@ try auto config = processor.loadConfig().configuration; String root_path = argv[2]; - zkutil::ZooKeeper zk(*config, config->has("zookeeper") ? "zookeeper" : "keeper", nullptr); + zkutil::ZooKeeper zk(*config, config->has("zookeeper") ? "zookeeper" : config->has("keeper") ? "keeper" : "keeper_server", nullptr); String temp_path = root_path + "/temp"; String blocks_path = root_path + "/block_numbers"; diff --git a/src/Storages/examples/get_current_inserts_in_replicated.cpp b/src/Storages/examples/get_current_inserts_in_replicated.cpp index 2355851bd89..c4ffa08ce27 100644 --- a/src/Storages/examples/get_current_inserts_in_replicated.cpp +++ b/src/Storages/examples/get_current_inserts_in_replicated.cpp @@ -29,7 +29,12 @@ try auto config = processor.loadConfig().configuration; String zookeeper_path = argv[2]; - auto zookeeper = std::make_shared(*config, config->has("zookeeper") ? "zookeeper" : "keeper", nullptr); + auto zookeeper = std::make_shared( + *config, + config->has("zookeeper") ? "zookeeper" + : config->has("keeper") ? "keeper" + : "keeper_server", + nullptr); std::unordered_map> current_inserts; From 08c3b361080ca2af99f6df1e143b5c9d8441b324 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Wed, 9 Mar 2022 12:25:30 +0800 Subject: [PATCH 006/377] commit again --- src/Common/ZooKeeper/ZooKeeper.cpp | 54 ++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/Common/ZooKeeper/ZooKeeper.cpp b/src/Common/ZooKeeper/ZooKeeper.cpp index 19647166b2a..c4a562ae6b7 100644 --- a/src/Common/ZooKeeper/ZooKeeper.cpp +++ b/src/Common/ZooKeeper/ZooKeeper.cpp @@ -182,7 +182,7 @@ public: operation_timeout_ms = Coordination::DEFAULT_OPERATION_TIMEOUT_MS; implementation = "zookeeper"; - if (endsWith(config_name, "keeper_server.raft_configuration")) + if (endsWith(config_name, "keeper_server")) initFromSectionKeeperServer(config, config_name); else initFromSectionKeeper(config, config_name); @@ -238,30 +238,48 @@ private: void initFromSectionKeeperServer(const Poco::Util::AbstractConfiguration & config, const std::string & config_name) { - Poco::Util::AbstractConfiguration::Keys keys; + Poco::Util::AbstractConfiguration::Keys keys; config.keys(config_name, keys); - + bool secure = false; + String tcp_port; + String tcp_port_secure; for (const auto & key : keys) { - if (key == "server") + if (key == "tcp_port_secure") + { + secure = true; + tcp_port_secure = config.getString(config_name + "." + key); + } + else if (key == "tcp_port") + { + tcp_port = config.getString(config_name + "." + key); + } + else if (key == "coordination_settings") + { + if (config.has(config_name + "." + key + ".operation_timeout_ms")) + operation_timeout_ms = config.getInt(config_name + "." + key + ".operation_timeout_ms"); + if (config.has(config_name + "." + key + ".session_timeout_ms")) + session_timeout_ms = config.getInt(config_name + "." + key + ".session_timeout_ms"); + } + + /// TODO: consider digest + } + + if (secure && tcp_port_secure.empty()) + throw KeeperException("No tcp_port_secure in config file", Coordination::Error::ZBADARGUMENTS); + if (!secure && tcp_port.empty()) + throw KeeperException("No tcp_port in config file", Coordination::Error::ZBADARGUMENTS); + + config.keys(config_name + ".raft_configuration", keys); + for (const auto & key : keys) + { + if (startsWith(key, "server")) { hosts.push_back( - // (config.getBool(config_name + "." + key + ".secure", false) ? "secure://" : "") + - config.getString(config_name + "." + key + ".hostname") + ":" - + config.getString(config_name + "." + key + ".port", "9234") - ); + (secure ? "secure://" : "") + config.getString(config_name + ".raft_configuration." + key + ".hostname") + ":" + + (secure ? tcp_port_secure : tcp_port)); } - else if (key == "secure") - { - secure = config.getBool(config_name + "." + key, false); - } - } - - if (secure && !hosts.empty()) - { - for (auto & host : hosts) - host = "secure://" + host; } } From 4e3098bbc21f13a827af6a4132341bfd3df1d2fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=89=AC?= <654010905@qq.com> Date: Wed, 28 Dec 2022 15:06:57 +0800 Subject: [PATCH 007/377] Update programs/extract-from-config/ExtractFromConfig.cpp Co-authored-by: Antonio Andelic --- programs/extract-from-config/ExtractFromConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/extract-from-config/ExtractFromConfig.cpp b/programs/extract-from-config/ExtractFromConfig.cpp index 2d50ba240a7..75b0d311fdb 100644 --- a/programs/extract-from-config/ExtractFromConfig.cpp +++ b/programs/extract-from-config/ExtractFromConfig.cpp @@ -96,7 +96,7 @@ static std::vector extractFromConfig( DB::ConfigurationPtr bootstrap_configuration(new Poco::Util::XMLConfiguration(config_xml)); if (bootstrap_configuration->has("zookeeper") && bootstrap_configuration->has("keeper")) - throw DB::Exception(DB::ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG, "Both zookeeper and keeper are specified"); + throw DB::Exception(DB::ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG, "Both ZooKeeper and Keeper are specified"); zkutil::ZooKeeperPtr zookeeper; zookeeper = std::make_shared( From 24052916121e479233d4c3661c9182a109849dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=89=AC?= <654010905@qq.com> Date: Wed, 28 Dec 2022 15:07:06 +0800 Subject: [PATCH 008/377] Update programs/copier/ClusterCopierApp.cpp Co-authored-by: Antonio Andelic --- programs/copier/ClusterCopierApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/copier/ClusterCopierApp.cpp b/programs/copier/ClusterCopierApp.cpp index 024cab2a32d..e8d62224c48 100644 --- a/programs/copier/ClusterCopierApp.cpp +++ b/programs/copier/ClusterCopierApp.cpp @@ -198,7 +198,7 @@ void ClusterCopierApp::mainImpl() copier->uploadTaskDescription(task_path, task_file, config().getBool("task-upload-force", false)); if (config().has("zookeeper") && config().has("keeper")) - throw Exception("Both zookeeper and keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); + throw Exception("Both ZooKeeper and Keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); copier->init(); copier->process(ConnectionTimeouts::getTCPTimeoutsWithoutFailover(context->getSettingsRef())); From 56b6378c121fd4ee17e8465ee21f47f502575235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=89=AC?= <654010905@qq.com> Date: Wed, 28 Dec 2022 15:08:50 +0800 Subject: [PATCH 009/377] Update programs/server/Server.cpp Co-authored-by: Antonio Andelic --- programs/server/Server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 759e652562c..69e56abed16 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -767,7 +767,7 @@ try ConnectionCollector::init(global_context, config().getUInt("max_threads_for_connection_collector", 10)); if (config().has("zookeeper") && config().has("keeper")) - throw Exception("Both zookeeper and keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); + throw Exception("Both ZooKeeper and Keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); bool has_zookeeper = config().has("zookeeper") || config().has("keeper") || config().has("keeper_server"); From 866c8d0fd0ced14a71b59625e521fcb49799e947 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Wed, 28 Dec 2022 16:04:34 +0800 Subject: [PATCH 010/377] change as request --- src/Common/ZooKeeper/ZooKeeper.cpp | 13 +++++++++++++ src/Common/ZooKeeper/ZooKeeper.h | 2 ++ .../examples/zk_many_watches_reconnect.cpp | 2 +- src/Interpreters/Context.cpp | 15 ++------------- .../get_abandonable_lock_in_all_partitions.cpp | 2 +- .../get_current_inserts_in_replicated.cpp | 7 +------ 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/Common/ZooKeeper/ZooKeeper.cpp b/src/Common/ZooKeeper/ZooKeeper.cpp index ab5d918e1f0..6bb7b465a6d 100644 --- a/src/Common/ZooKeeper/ZooKeeper.cpp +++ b/src/Common/ZooKeeper/ZooKeeper.cpp @@ -29,6 +29,7 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; extern const int NOT_IMPLEMENTED; extern const int BAD_ARGUMENTS; + extern const int NO_ELEMENTS_IN_CONFIG; } } @@ -1333,4 +1334,16 @@ String getSequentialNodeName(const String & prefix, UInt64 number) return name; } +String getZookeeperConfigName(const Poco::Util::AbstractConfiguration & config) +{ + if (config.has("zookeeper")) + return "zookeeper"; + else if (config.has("keeper")) + return "keeper"; + else if (config.has("keeper_server")) + return "keeper_server"; + else + throw DB::Exception("There is no Zookeeper configuration in server config", DB::ErrorCodes::NO_ELEMENTS_IN_CONFIG); +} + } diff --git a/src/Common/ZooKeeper/ZooKeeper.h b/src/Common/ZooKeeper/ZooKeeper.h index 9de8241cfbe..67ae166452f 100644 --- a/src/Common/ZooKeeper/ZooKeeper.h +++ b/src/Common/ZooKeeper/ZooKeeper.h @@ -645,4 +645,6 @@ String extractZooKeeperPath(const String & path, bool check_starts_with_slash, P String getSequentialNodeName(const String & prefix, UInt64 number); +String getZookeeperConfigName(const Poco::Util::AbstractConfiguration & config); + } diff --git a/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp b/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp index b68035b4c4c..aad8913ca8b 100644 --- a/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp +++ b/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp @@ -25,7 +25,7 @@ int main(int argc, char ** argv) DB::ConfigProcessor processor(argv[1], false, true); auto config = processor.loadConfig().configuration; - zkutil::ZooKeeper zk(*config, config->has("zookeeper") ? "zookeeper" : config->has("keeper") ? "keeper" : "keeper_server", nullptr); + zkutil::ZooKeeper zk(*config, zkutil::getZookeeperConfigName(*config), nullptr); zkutil::EventPtr watch = std::make_shared(); /// NOTE: setting watches in multiple threads because doing it in a single thread is too slow. diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 5fb2715c6ce..2713ee5b50e 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -2233,12 +2233,7 @@ zkutil::ZooKeeperPtr Context::getZooKeeper() const const auto & config = shared->zookeeper_config ? *shared->zookeeper_config : getConfigRef(); if (!shared->zookeeper) - shared->zookeeper = std::make_shared( - config, - config.has("zookeeper") ? "zookeeper" - : config.has("keeper") ? "keeper" - : "keeper_server", - getZooKeeperLog()); + shared->zookeeper = std::make_shared(config, zkutil::getZookeeperConfigName(config), getZooKeeperLog()); else if (shared->zookeeper->expired()) { Stopwatch watch; @@ -2478,13 +2473,7 @@ void Context::reloadZooKeeperIfChanged(const ConfigurationPtr & config) const { std::lock_guard lock(shared->zookeeper_mutex); shared->zookeeper_config = config; - reloadZooKeeperIfChangedImpl( - config, - config->has("zookeeper") ? "zookeeper" - : config->has("keeper") ? "keeper" - : "keeper_server", - shared->zookeeper, - getZooKeeperLog()); + reloadZooKeeperIfChangedImpl(config, zkutil::getZookeeperConfigName(*config), shared->zookeeper, getZooKeeperLog()); } void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & config) diff --git a/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp b/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp index ee068d1bf26..e1faa67eb45 100644 --- a/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp +++ b/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp @@ -26,7 +26,7 @@ try auto config = processor.loadConfig().configuration; String root_path = argv[2]; - zkutil::ZooKeeper zk(*config, config->has("zookeeper") ? "zookeeper" : config->has("keeper") ? "keeper" : "keeper_server", nullptr); + zkutil::ZooKeeper zk(*config, zkutil::getZookeeperConfigName(*config), nullptr); String temp_path = root_path + "/temp"; String blocks_path = root_path + "/block_numbers"; diff --git a/src/Storages/examples/get_current_inserts_in_replicated.cpp b/src/Storages/examples/get_current_inserts_in_replicated.cpp index 36584b8593a..9ba75b81440 100644 --- a/src/Storages/examples/get_current_inserts_in_replicated.cpp +++ b/src/Storages/examples/get_current_inserts_in_replicated.cpp @@ -29,12 +29,7 @@ try auto config = processor.loadConfig().configuration; String zookeeper_path = argv[2]; - auto zookeeper = std::make_shared( - *config, - config->has("zookeeper") ? "zookeeper" - : config->has("keeper") ? "keeper" - : "keeper_server", - nullptr); + auto zookeeper = std::make_shared(*config, zkutil::getZookeeperConfigName(*config), nullptr); std::unordered_map> current_inserts; From 28f726f82532f85214d71d4de4358cf4dad39239 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Wed, 28 Dec 2022 16:58:21 +0800 Subject: [PATCH 011/377] add zookeeper integration tests --- .../__init__.py | 0 .../configs/keeper_config.xml | 17 ++++++ .../configs/remote_servers.xml | 18 +++++++ .../configs/zookeeper_config.xml | 17 ++++++ .../test_alternative_keeper_config/test.py | 54 +++++++++++++++++++ 5 files changed, 106 insertions(+) create mode 100644 tests/integration/test_alternative_keeper_config/__init__.py create mode 100644 tests/integration/test_alternative_keeper_config/configs/keeper_config.xml create mode 100644 tests/integration/test_alternative_keeper_config/configs/remote_servers.xml create mode 100644 tests/integration/test_alternative_keeper_config/configs/zookeeper_config.xml create mode 100644 tests/integration/test_alternative_keeper_config/test.py diff --git a/tests/integration/test_alternative_keeper_config/__init__.py b/tests/integration/test_alternative_keeper_config/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/integration/test_alternative_keeper_config/configs/keeper_config.xml b/tests/integration/test_alternative_keeper_config/configs/keeper_config.xml new file mode 100644 index 00000000000..bd783b83853 --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/configs/keeper_config.xml @@ -0,0 +1,17 @@ + + + + zoo1 + 2181 + + + zoo2 + 2181 + + + zoo3 + 2181 + + 3000 + + diff --git a/tests/integration/test_alternative_keeper_config/configs/remote_servers.xml b/tests/integration/test_alternative_keeper_config/configs/remote_servers.xml new file mode 100644 index 00000000000..e77cc5c65e6 --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/configs/remote_servers.xml @@ -0,0 +1,18 @@ + + + + + + node1 + 9000 + + + + node2 + 9000 + + + + + + diff --git a/tests/integration/test_alternative_keeper_config/configs/zookeeper_config.xml b/tests/integration/test_alternative_keeper_config/configs/zookeeper_config.xml new file mode 100644 index 00000000000..7a0d7c1de92 --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/configs/zookeeper_config.xml @@ -0,0 +1,17 @@ + + + + zoo1 + 2181 + + + zoo2 + 2181 + + + zoo3 + 2181 + + 3000 + + diff --git a/tests/integration/test_alternative_keeper_config/test.py b/tests/integration/test_alternative_keeper_config/test.py new file mode 100644 index 00000000000..86877e677ba --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/test.py @@ -0,0 +1,54 @@ +import time +import pytest +import logging +from helpers.cluster import ClickHouseCluster +from tests.integration.helpers.test_tools import TSV + +cluster = ClickHouseCluster(__file__) + +node1 = cluster.add_instance( + "node1", + with_zookeeper=True, + main_configs=["configs/remote_servers.xml", "configs/keeper_config.xml"], + macros={"replica": "node1"}, +) + +node2 = cluster.add_instance( + "node2", + with_zookeeper=True, + main_configs=["configs/remote_servers.xml", "configs/zookeeper_config.xml"], + macros={"replica": "node2"}, +) + + +@pytest.fixture(scope="module", autouse=True) +def started_cluster(): + try: + cluster.start() + yield cluster + + finally: + cluster.shutdown() + + +def test_create_insert(started_cluster): + node1.query("DROP TABLE IF EXISTS tbl ON CLUSTER 'test_cluster' NO DELAY") + node1.query( + """ + CREATE TABLE tbl ON CLUSTER 'test_cluster' ( + id Int64, + str String + ) ENGINE=ReplicatedMergeTree('/clickhouse/tables/tbl/', '{replica}') + ORDER BY id + """ + ) + + node1.query("INSERT INTO tbl VALUES (1, 'str1')") + node2.query("INSERT INTO tbl VALUES (1, 'str1')") # Test deduplication + node2.query("INSERT INTO tbl VALUES (2, 'str2')") + + expected = [[1, "str1"], [2, "str2"]] + assert node1.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) + assert node2.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) + assert node1.query("CHECK TABLE tbl") == "1\n" + assert node2.query("CHECK TABLE tbl") == "1\n" From dc34393f668919684a2a1c20aec18aac8364e271 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Wed, 28 Dec 2022 18:27:36 +0800 Subject: [PATCH 012/377] add another test --- .../configs_keeper_server/enable_keeper1.xml | 33 ++++++++++ .../configs_keeper_server/enable_keeper2.xml | 33 ++++++++++ .../configs_keeper_server/remote_servers.xml | 18 ++++++ .../configs_keeper_server/use_keeper.xml | 12 ++++ .../test_keeper_server.py | 61 +++++++++++++++++++ 5 files changed, 157 insertions(+) create mode 100644 tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper1.xml create mode 100644 tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper2.xml create mode 100644 tests/integration/test_alternative_keeper_config/configs_keeper_server/remote_servers.xml create mode 100644 tests/integration/test_alternative_keeper_config/configs_keeper_server/use_keeper.xml create mode 100644 tests/integration/test_alternative_keeper_config/test_keeper_server.py diff --git a/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper1.xml b/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper1.xml new file mode 100644 index 00000000000..7c2e283e89f --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper1.xml @@ -0,0 +1,33 @@ + + + 9181 + 1 + /var/lib/clickhouse/coordination/log + /var/lib/clickhouse/coordination/snapshots + + + 5000 + 10000 + 75 + trace + + + + + 1 + node1 + 9234 + true + 3 + + + 2 + node2 + 9234 + true + true + 2 + + + + diff --git a/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper2.xml b/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper2.xml new file mode 100644 index 00000000000..618e6a04aec --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper2.xml @@ -0,0 +1,33 @@ + + + 9181 + 2 + /var/lib/clickhouse/coordination/log + /var/lib/clickhouse/coordination/snapshots + + + 5000 + 10000 + 75 + trace + + + + + 1 + node1 + 9234 + true + 3 + + + 2 + node2 + 9234 + true + true + 2 + + + + diff --git a/tests/integration/test_alternative_keeper_config/configs_keeper_server/remote_servers.xml b/tests/integration/test_alternative_keeper_config/configs_keeper_server/remote_servers.xml new file mode 100644 index 00000000000..e77cc5c65e6 --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/configs_keeper_server/remote_servers.xml @@ -0,0 +1,18 @@ + + + + + + node1 + 9000 + + + + node2 + 9000 + + + + + + diff --git a/tests/integration/test_alternative_keeper_config/configs_keeper_server/use_keeper.xml b/tests/integration/test_alternative_keeper_config/configs_keeper_server/use_keeper.xml new file mode 100644 index 00000000000..b250f06cf81 --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/configs_keeper_server/use_keeper.xml @@ -0,0 +1,12 @@ + + + + node1 + 9181 + + + node2 + 9181 + + + diff --git a/tests/integration/test_alternative_keeper_config/test_keeper_server.py b/tests/integration/test_alternative_keeper_config/test_keeper_server.py new file mode 100644 index 00000000000..7311b32e0af --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/test_keeper_server.py @@ -0,0 +1,61 @@ +import time +import pytest +import logging +from helpers.cluster import ClickHouseCluster +from tests.integration.helpers.test_tools import TSV + +cluster = ClickHouseCluster(__file__) + +node1 = cluster.add_instance( + "node1", + with_zookeeper=True, + main_configs=[ + "configs_keeper_server/remote_servers.xml", + "configs_keeper_server/enable_keeper1.xml", + "configs_keeper_server/use_keeper.xml", + ], + macros={"replica": "node1"}, +) + +node2 = cluster.add_instance( + "node2", + with_zookeeper=True, + main_configs=[ + "configs_keeper_server/remote_servers.xml", + "configs_keeper_server/enable_keeper2.xml", + ], + macros={"replica": "node2"}, +) + + +@pytest.fixture(scope="module", autouse=True) +def started_cluster(): + try: + cluster.start() + yield cluster + + finally: + cluster.shutdown() + + +def test_create_insert(started_cluster): + node1.query("DROP TABLE IF EXISTS tbl ON CLUSTER 'test_cluster' NO DELAY") + node1.query( + """ + CREATE TABLE tbl ON CLUSTER 'test_cluster' ( + id Int64, + str String + ) ENGINE=ReplicatedMergeTree('/clickhouse/tables/tbl/', '{replica}') + ORDER BY id + """ + ) + + node1.query("INSERT INTO tbl VALUES (1, 'str1')") + node2.query("INSERT INTO tbl VALUES (1, 'str1')") # Test deduplication + node2.query("INSERT INTO tbl VALUES (2, 'str2')") + + expected = [[1, "str1"], [2, "str2"]] + assert node1.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) + assert node2.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) + assert node1.query("CHECK TABLE tbl") == "1\n" + assert node2.query("CHECK TABLE tbl") == "1\n" From 84f33a3114a9feb2421d27aa100f6f528534a317 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Thu, 29 Dec 2022 14:56:09 +0800 Subject: [PATCH 013/377] commit again --- tests/integration/test_alternative_keeper_config/test.py | 4 ++-- .../test_alternative_keeper_config/test_keeper_server.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/test_alternative_keeper_config/test.py b/tests/integration/test_alternative_keeper_config/test.py index 86877e677ba..f2afc9c77ca 100644 --- a/tests/integration/test_alternative_keeper_config/test.py +++ b/tests/integration/test_alternative_keeper_config/test.py @@ -1,6 +1,6 @@ -import time +#!/usr/bin/env python3 + import pytest -import logging from helpers.cluster import ClickHouseCluster from tests.integration.helpers.test_tools import TSV diff --git a/tests/integration/test_alternative_keeper_config/test_keeper_server.py b/tests/integration/test_alternative_keeper_config/test_keeper_server.py index 7311b32e0af..711be4c7136 100644 --- a/tests/integration/test_alternative_keeper_config/test_keeper_server.py +++ b/tests/integration/test_alternative_keeper_config/test_keeper_server.py @@ -1,6 +1,6 @@ -import time +#!/usr/bin/env python3 + import pytest -import logging from helpers.cluster import ClickHouseCluster from tests.integration.helpers.test_tools import TSV From d5dcf9377599589a41d010c8e523a3f11fb82d32 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Thu, 29 Dec 2022 16:37:20 +0800 Subject: [PATCH 014/377] fix import erorr --- tests/integration/test_alternative_keeper_config/test.py | 2 +- .../test_alternative_keeper_config/test_keeper_server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_alternative_keeper_config/test.py b/tests/integration/test_alternative_keeper_config/test.py index f2afc9c77ca..8784cb9be56 100644 --- a/tests/integration/test_alternative_keeper_config/test.py +++ b/tests/integration/test_alternative_keeper_config/test.py @@ -2,7 +2,7 @@ import pytest from helpers.cluster import ClickHouseCluster -from tests.integration.helpers.test_tools import TSV +from helpers.test_tools import TSV cluster = ClickHouseCluster(__file__) diff --git a/tests/integration/test_alternative_keeper_config/test_keeper_server.py b/tests/integration/test_alternative_keeper_config/test_keeper_server.py index 711be4c7136..9c61e076671 100644 --- a/tests/integration/test_alternative_keeper_config/test_keeper_server.py +++ b/tests/integration/test_alternative_keeper_config/test_keeper_server.py @@ -2,7 +2,7 @@ import pytest from helpers.cluster import ClickHouseCluster -from tests.integration.helpers.test_tools import TSV +from helpers.test_tools import TSV cluster = ClickHouseCluster(__file__) From f9e81ca7dea94e019062655f08a70aaa152260a5 Mon Sep 17 00:00:00 2001 From: Ilya Golshtein Date: Wed, 8 Feb 2023 23:30:16 +0300 Subject: [PATCH 015/377] secure in named collections - initial --- .../ClickHouseDictionarySource.cpp | 14 +- .../ExternalDataSourceConfiguration.cpp | 149 +++++++++++++----- .../ExternalDataSourceConfiguration.h | 3 +- 3 files changed, 126 insertions(+), 40 deletions(-) diff --git a/src/Dictionaries/ClickHouseDictionarySource.cpp b/src/Dictionaries/ClickHouseDictionarySource.cpp index b962a6ab529..45ba865f2ff 100644 --- a/src/Dictionaries/ClickHouseDictionarySource.cpp +++ b/src/Dictionaries/ClickHouseDictionarySource.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -230,6 +231,7 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) std::string table = config.getString(settings_config_prefix + ".table", ""); UInt16 port = static_cast(config.getUInt(settings_config_prefix + ".port", default_port)); auto has_config_key = [](const String & key) { return dictionary_allowed_keys.contains(key); }; + bool clickhouse_secure = config.getBool(settings_config_prefix + ".secure", false); auto named_collection = created_from_ddl ? getExternalDataSourceConfiguration(config, settings_config_prefix, global_context, has_config_key) @@ -245,6 +247,16 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) db = configuration.database; table = configuration.table; port = configuration.port; + clickhouse_secure = configuration.secure; + + // const auto & storage_specific_args = named_collection->specific_args; + // for (const auto & [arg_name, arg_value] : storage_specific_args) + // { + // if (arg_name == "secure") + // { + // clickhouse_secure = checkAndGetLiteralArgument(arg_value, "secure"); + // } + // } } ClickHouseDictionarySource::Configuration configuration{ @@ -261,7 +273,7 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) .update_lag = config.getUInt64(settings_config_prefix + ".update_lag", 1), .port = port, .is_local = isLocalAddress({host, port}, default_port), - .secure = config.getBool(settings_config_prefix + ".secure", false)}; + .secure = clickhouse_secure}; // config.getBool(settings_config_prefix + ".secure", false)}; ContextMutablePtr context; diff --git a/src/Storages/ExternalDataSourceConfiguration.cpp b/src/Storages/ExternalDataSourceConfiguration.cpp index d7c3fe44f38..0f7964f8fcc 100644 --- a/src/Storages/ExternalDataSourceConfiguration.cpp +++ b/src/Storages/ExternalDataSourceConfiguration.cpp @@ -35,12 +35,11 @@ namespace ErrorCodes IMPLEMENT_SETTINGS_TRAITS(EmptySettingsTraits, EMPTY_SETTINGS) -static const std::unordered_set dictionary_allowed_keys = { - "host", "port", "user", "password", "quota_key", "db", - "database", "table", "schema", "replica", - "update_field", "update_lag", "invalidate_query", "query", - "where", "name", "secure", "uri", "collection"}; - +// static const std::unordered_set dictionary_allowed_keys = { +// "host", "port", "user", "password", "quota_key", "db", +// "database", "table", "schema", "replica", +// "update_field", "update_lag", "invalidate_query", "query", +// "where", "name", "secure", "uri", "collection"}; template SettingsChanges getSettingsChangesFromConfig( @@ -83,6 +82,7 @@ void ExternalDataSourceConfiguration::set(const ExternalDataSourceConfiguration { host = conf.host; port = conf.port; + secure = conf.secure; username = conf.username; password = conf.password; quota_key = conf.quota_key; @@ -93,6 +93,54 @@ void ExternalDataSourceConfiguration::set(const ExternalDataSourceConfiguration addresses_expr = conf.addresses_expr; } +namespace +{ +void initExternalDataSourceConfiguration(ExternalDataSourceConfiguration & configuration) +{ + configuration = ExternalDataSourceConfiguration(); + configuration.username = ""; +} + +void readNamedCollection(const Poco::Util::AbstractConfiguration & config, + std::string_view collection_prefix, + ExternalDataSourceConfiguration & configuration) +{ + auto get_path = [collection_prefix](std::string_view fname) + { + return fmt::format("{}.{}", collection_prefix, fname); + }; + + configuration.host = config.getString(get_path("host"), configuration.host); + configuration.port = config.getInt(get_path("port"), configuration.port); + configuration.secure = config.getBool(get_path("secure"), configuration.secure); + configuration.username = config.getString(get_path("user"), configuration.username); + configuration.password = config.getString(get_path("password"), configuration.password); + configuration.quota_key = config.getString(get_path("quota_key"), configuration.quota_key); + configuration.database = config.getString(get_path("db"), config.getString(get_path("database"), configuration.database)); + configuration.table = config.getString(get_path("table"), config.getString(get_path("collection"), configuration.table)); + configuration.schema = config.getString(get_path("schema"), configuration.schema); + configuration.addresses_expr = config.getString(get_path("addresses_expr"), configuration.addresses_expr); +} + +using ConfigWithPrefix = std::pair; + +/// Logical priority is from left to right. +/// If first element of config_with_prefix_vect does not have a particular field, +/// second element is used, etc. +/// Technically values are overwritten from right to left. +/// If no luck, default values come into play. +void readNamedCollection(const std::vector & config_with_prefix_vect, + ExternalDataSourceConfiguration & configuration) +{ + initExternalDataSourceConfiguration(configuration); + + for (auto it = std::crbegin(config_with_prefix_vect); it != std::crend(config_with_prefix_vect); ++it) + { + readNamedCollection((*it).first, (*it).second, configuration); + } +} +} + template std::optional getExternalDataSourceConfiguration( @@ -121,15 +169,7 @@ std::optional getExternalDataSourceConfiguration( SettingsChanges config_settings = getSettingsChangesFromConfig(storage_settings, config, collection_prefix); - configuration.host = config.getString(collection_prefix + ".host", ""); - configuration.port = config.getInt(collection_prefix + ".port", 0); - configuration.username = config.getString(collection_prefix + ".user", ""); - configuration.password = config.getString(collection_prefix + ".password", ""); - configuration.quota_key = config.getString(collection_prefix + ".quota_key", ""); - configuration.database = config.getString(collection_prefix + ".database", ""); - configuration.table = config.getString(collection_prefix + ".table", config.getString(collection_prefix + ".collection", "")); - configuration.schema = config.getString(collection_prefix + ".schema", ""); - configuration.addresses_expr = config.getString(collection_prefix + ".addresses_expr", ""); + readNamedCollection({{config, collection_prefix}}, configuration); if (!configuration.addresses_expr.empty() && !configuration.host.empty()) throw Exception(ErrorCodes::BAD_ARGUMENTS, "Cannot have `addresses_expr` and `host`, `port` in configuration at the same time"); @@ -169,6 +209,8 @@ std::optional getExternalDataSourceConfiguration( configuration.host = arg_value.safeGet(); else if (arg_name == "port") configuration.port = arg_value.safeGet(); + else if (arg_name == "secure") + configuration.secure = arg_value.safeGet(); else if (arg_name == "user") configuration.username = arg_value.safeGet(); else if (arg_name == "password") @@ -223,6 +265,7 @@ std::optional getExternalDataSourceConfiguration( { validateConfigKeys(dict_config, dict_config_prefix, has_config_key); ExternalDataSourceConfiguration configuration; + StorageSpecificArgs non_common_args; auto collection_name = dict_config.getString(dict_config_prefix + ".name", ""); if (!collection_name.empty()) @@ -238,15 +281,28 @@ std::optional getExternalDataSourceConfiguration( if (!config.has(collection_prefix)) throw Exception(ErrorCodes::BAD_ARGUMENTS, "There is no collection named `{}` in config", collection_name); - configuration.host = dict_config.getString(dict_config_prefix + ".host", config.getString(collection_prefix + ".host", "")); - configuration.port = dict_config.getInt(dict_config_prefix + ".port", config.getUInt(collection_prefix + ".port", 0)); - configuration.username = dict_config.getString(dict_config_prefix + ".user", config.getString(collection_prefix + ".user", "")); - configuration.password = dict_config.getString(dict_config_prefix + ".password", config.getString(collection_prefix + ".password", "")); - configuration.quota_key = dict_config.getString(dict_config_prefix + ".quota_key", config.getString(collection_prefix + ".quota_key", "")); - configuration.database = dict_config.getString(dict_config_prefix + ".db", config.getString(dict_config_prefix + ".database", - config.getString(collection_prefix + ".db", config.getString(collection_prefix + ".database", "")))); - configuration.table = dict_config.getString(dict_config_prefix + ".table", config.getString(collection_prefix + ".table", "")); - configuration.schema = dict_config.getString(dict_config_prefix + ".schema", config.getString(collection_prefix + ".schema", "")); + + // configuration.host = dict_config.getString(dict_config_prefix + ".host", config.getString(collection_prefix + ".host", "")); + // configuration.port = dict_config.getInt(dict_config_prefix + ".port", config.getUInt(collection_prefix + ".port", 0)); + // configuration.secure = dict_config.getInt(dict_config_prefix + ".secure", config.getUInt(collection_prefix + ".secure", 0)); + // configuration.username = dict_config.getString(dict_config_prefix + ".user", config.getString(collection_prefix + ".user", "")); + // configuration.password = dict_config.getString(dict_config_prefix + ".password", config.getString(collection_prefix + ".password", "")); + // configuration.quota_key = dict_config.getString(dict_config_prefix + ".quota_key", config.getString(collection_prefix + ".quota_key", "")); + // configuration.database = dict_config.getString(dict_config_prefix + ".db", config.getString(dict_config_prefix + ".database", + // config.getString(collection_prefix + ".db", config.getString(collection_prefix + ".database", "")))); + // configuration.table = dict_config.getString(dict_config_prefix + ".table", config.getString(collection_prefix + ".table", "")); + // configuration.schema = dict_config.getString(dict_config_prefix + ".schema", config.getString(collection_prefix + ".schema", "")); + + + readNamedCollection({{dict_config, dict_config_prefix}, {config, collection_prefix}}, configuration); + + + // if (dict_config.has(dict_config_prefix + ".secure") || config.has(collection_prefix + ".secure")) + // { + // non_common_args.emplace_back(std::make_pair("secure", + // dict_config.getString(dict_config_prefix + ".secure", config.getString(collection_prefix + ".secure", "")))); + // } + if (configuration.host.empty() || configuration.port == 0 || configuration.username.empty() || configuration.table.empty()) { @@ -254,7 +310,7 @@ std::optional getExternalDataSourceConfiguration( "Named collection of connection parameters is missing some " "of the parameters and dictionary parameters are not added"); } - return ExternalDataSourceInfo{ .configuration = configuration, .specific_args = {}, .settings_changes = config_settings }; + return ExternalDataSourceInfo{ .configuration = configuration, .specific_args = non_common_args, .settings_changes = config_settings }; } return std::nullopt; } @@ -333,14 +389,18 @@ ExternalDataSourcesByPriority getExternalDataSourceConfigurationByPriority( } else { - common_configuration.host = dict_config.getString(dict_config_prefix + ".host", ""); - common_configuration.port = dict_config.getUInt(dict_config_prefix + ".port", 0); - common_configuration.username = dict_config.getString(dict_config_prefix + ".user", ""); - common_configuration.password = dict_config.getString(dict_config_prefix + ".password", ""); - common_configuration.quota_key = dict_config.getString(dict_config_prefix + ".quota_key", ""); - common_configuration.database = dict_config.getString(dict_config_prefix + ".db", dict_config.getString(dict_config_prefix + ".database", "")); - common_configuration.table = dict_config.getString(fmt::format("{}.table", dict_config_prefix), ""); - common_configuration.schema = dict_config.getString(fmt::format("{}.schema", dict_config_prefix), ""); + // common_configuration.host = dict_config.getString(dict_config_prefix + ".host", ""); + // common_configuration.port = dict_config.getUInt(dict_config_prefix + ".port", 0); + // common_configuration.secure = dict_config.getBool(dict_config_prefix + ".secure", 0); + // common_configuration.username = dict_config.getString(dict_config_prefix + ".user", ""); + // common_configuration.password = dict_config.getString(dict_config_prefix + ".password", ""); + // common_configuration.quota_key = dict_config.getString(dict_config_prefix + ".quota_key", ""); + // common_configuration.database = dict_config.getString(dict_config_prefix + ".db", dict_config.getString(dict_config_prefix + ".database", "")); + // common_configuration.table = dict_config.getString(fmt::format("{}.table", dict_config_prefix), ""); + // common_configuration.schema = dict_config.getString(fmt::format("{}.schema", dict_config_prefix), ""); + + readNamedCollection({{dict_config, dict_config_prefix}}, common_configuration); + } ExternalDataSourcesByPriority configuration @@ -365,11 +425,17 @@ ExternalDataSourcesByPriority getExternalDataSourceConfigurationByPriority( validateConfigKeys(dict_config, replica_name, has_config_key); size_t priority = dict_config.getInt(replica_name + ".priority", 0); - replica_configuration.host = dict_config.getString(replica_name + ".host", common_configuration.host); - replica_configuration.port = dict_config.getUInt(replica_name + ".port", common_configuration.port); - replica_configuration.username = dict_config.getString(replica_name + ".user", common_configuration.username); - replica_configuration.password = dict_config.getString(replica_name + ".password", common_configuration.password); - replica_configuration.quota_key = dict_config.getString(replica_name + ".quota_key", common_configuration.quota_key); + // replica_configuration.host = dict_config.getString(replica_name + ".host", common_configuration.host); + // replica_configuration.port = dict_config.getUInt(replica_name + ".port", common_configuration.port); + // replica_configuration.secure = dict_config.getUInt(replica_name + ".secure", common_configuration.secure); + // replica_configuration.username = dict_config.getString(replica_name + ".user", common_configuration.username); + // replica_configuration.password = dict_config.getString(replica_name + ".password", common_configuration.password); + // replica_configuration.quota_key = dict_config.getString(replica_name + ".quota_key", common_configuration.quota_key); + + + readNamedCollection({{dict_config, replica_name}, + {dict_config, dict_config_prefix}}, replica_configuration); + if (replica_configuration.host.empty() || replica_configuration.port == 0 || replica_configuration.username.empty() || replica_configuration.password.empty()) @@ -378,6 +444,13 @@ ExternalDataSourcesByPriority getExternalDataSourceConfigurationByPriority( "Named collection of connection parameters is missing some " "of the parameters and no other dictionary parameters are added"); } + if (replica_configuration.database != common_configuration.database + || replica_configuration.table != common_configuration.table + || replica_configuration.schema != common_configuration.schema) + { + throw Exception(ErrorCodes::BAD_ARGUMENTS, + "Named collection of connection parameters is not consistent"); + } configuration.replicas_configurations[priority].emplace_back(replica_configuration); } diff --git a/src/Storages/ExternalDataSourceConfiguration.h b/src/Storages/ExternalDataSourceConfiguration.h index 65ef7d28b1b..6f116295bde 100644 --- a/src/Storages/ExternalDataSourceConfiguration.h +++ b/src/Storages/ExternalDataSourceConfiguration.h @@ -18,6 +18,7 @@ struct ExternalDataSourceConfiguration { String host; UInt16 port = 0; + bool secure = false; String username = "default"; String password; String quota_key; @@ -63,7 +64,7 @@ struct ExternalDataSourceInfo * configuration values, i.e. ENGINE = PostgreSQL(postgresql_configuration, database = 'postgres_database'); * * Any key-value engine argument except common (`host`, `port`, `username`, `password`, `database`) - * is returned in EngineArgs struct. + * is returned in StorageSpecificArgs struct. */ template std::optional getExternalDataSourceConfiguration( From 3b72b3f13b36bde864995ce492b0892f4f2d88b0 Mon Sep 17 00:00:00 2001 From: Ilya Golshtein Date: Fri, 10 Feb 2023 13:42:11 +0300 Subject: [PATCH 016/377] secure in named collection - switched to specific_args, tests added --- .../ClickHouseDictionarySource.cpp | 36 +++-- .../ExternalDataSourceConfiguration.cpp | 54 ++------ .../ExternalDataSourceConfiguration.h | 1 - .../test_dictionaries_ddl/configs/client.xml | 10 ++ .../configs/named_coll.xml | 21 +++ .../test_dictionaries_ddl/configs/server.crt | 18 +++ .../test_dictionaries_ddl/configs/server.key | 28 ++++ .../configs/ssl_conf.xml | 16 +++ .../integration/test_dictionaries_ddl/test.py | 124 +++++++++++++++++- 9 files changed, 249 insertions(+), 59 deletions(-) create mode 100644 tests/integration/test_dictionaries_ddl/configs/client.xml create mode 100644 tests/integration/test_dictionaries_ddl/configs/named_coll.xml create mode 100644 tests/integration/test_dictionaries_ddl/configs/server.crt create mode 100644 tests/integration/test_dictionaries_ddl/configs/server.key create mode 100644 tests/integration/test_dictionaries_ddl/configs/ssl_conf.xml diff --git a/src/Dictionaries/ClickHouseDictionarySource.cpp b/src/Dictionaries/ClickHouseDictionarySource.cpp index 45ba865f2ff..f3c5672a8b1 100644 --- a/src/Dictionaries/ClickHouseDictionarySource.cpp +++ b/src/Dictionaries/ClickHouseDictionarySource.cpp @@ -207,6 +207,18 @@ std::string ClickHouseDictionarySource::doInvalidateQuery(const std::string & re } } +void logConfig(const Poco::Util::AbstractConfiguration & config, std::string start_from = "dictionary", size_t level = 0) +{ + Poco::Util::AbstractConfiguration::Keys keys; + config.keys(start_from, keys); + for (const auto & key : keys) + { + LOG_TRACE(&Poco::Logger::get("ClickHouseDictionarySource"), + "logConfig key: {}{}", std::string(level, ' '), key); + logConfig(config, start_from + "." + key, level + 1); + } +} + void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) { auto create_table_source = [=](const DictionaryStructure & dict_struct, @@ -217,6 +229,8 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) const std::string & default_database [[maybe_unused]], bool created_from_ddl) -> DictionarySourcePtr { + logConfig(config); + bool secure = config.getBool(config_prefix + ".secure", false); UInt16 default_port = getPortFromContext(global_context, secure); @@ -231,7 +245,7 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) std::string table = config.getString(settings_config_prefix + ".table", ""); UInt16 port = static_cast(config.getUInt(settings_config_prefix + ".port", default_port)); auto has_config_key = [](const String & key) { return dictionary_allowed_keys.contains(key); }; - bool clickhouse_secure = config.getBool(settings_config_prefix + ".secure", false); + bool secure_from_named = false; auto named_collection = created_from_ddl ? getExternalDataSourceConfiguration(config, settings_config_prefix, global_context, has_config_key) @@ -247,16 +261,16 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) db = configuration.database; table = configuration.table; port = configuration.port; - clickhouse_secure = configuration.secure; - // const auto & storage_specific_args = named_collection->specific_args; - // for (const auto & [arg_name, arg_value] : storage_specific_args) - // { - // if (arg_name == "secure") - // { - // clickhouse_secure = checkAndGetLiteralArgument(arg_value, "secure"); - // } - // } + const auto & storage_specific_args = named_collection->specific_args; + for (const auto & [arg_name, arg_value] : storage_specific_args) + { + if (arg_name == "secure") + { + secure_from_named = checkAndGetLiteralArgument(arg_value, "secure"); + LOG_TRACE(&Poco::Logger::get("ClickHouseDictionarySource"), "clickhouse_secure {}", secure_from_named); + } + } } ClickHouseDictionarySource::Configuration configuration{ @@ -273,7 +287,7 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) .update_lag = config.getUInt64(settings_config_prefix + ".update_lag", 1), .port = port, .is_local = isLocalAddress({host, port}, default_port), - .secure = clickhouse_secure}; // config.getBool(settings_config_prefix + ".secure", false)}; + .secure = config.getBool(settings_config_prefix + ".secure", secure_from_named)}; ContextMutablePtr context; diff --git a/src/Storages/ExternalDataSourceConfiguration.cpp b/src/Storages/ExternalDataSourceConfiguration.cpp index 0f7964f8fcc..20f8920ff5a 100644 --- a/src/Storages/ExternalDataSourceConfiguration.cpp +++ b/src/Storages/ExternalDataSourceConfiguration.cpp @@ -35,12 +35,6 @@ namespace ErrorCodes IMPLEMENT_SETTINGS_TRAITS(EmptySettingsTraits, EMPTY_SETTINGS) -// static const std::unordered_set dictionary_allowed_keys = { -// "host", "port", "user", "password", "quota_key", "db", -// "database", "table", "schema", "replica", -// "update_field", "update_lag", "invalidate_query", "query", -// "where", "name", "secure", "uri", "collection"}; - template SettingsChanges getSettingsChangesFromConfig( const BaseSettings & settings, const Poco::Util::AbstractConfiguration & config, const String & config_prefix) @@ -82,7 +76,6 @@ void ExternalDataSourceConfiguration::set(const ExternalDataSourceConfiguration { host = conf.host; port = conf.port; - secure = conf.secure; username = conf.username; password = conf.password; quota_key = conf.quota_key; @@ -112,7 +105,6 @@ void readNamedCollection(const Poco::Util::AbstractConfiguration & config, configuration.host = config.getString(get_path("host"), configuration.host); configuration.port = config.getInt(get_path("port"), configuration.port); - configuration.secure = config.getBool(get_path("secure"), configuration.secure); configuration.username = config.getString(get_path("user"), configuration.username); configuration.password = config.getString(get_path("password"), configuration.password); configuration.quota_key = config.getString(get_path("quota_key"), configuration.quota_key); @@ -209,8 +201,8 @@ std::optional getExternalDataSourceConfiguration( configuration.host = arg_value.safeGet(); else if (arg_name == "port") configuration.port = arg_value.safeGet(); - else if (arg_name == "secure") - configuration.secure = arg_value.safeGet(); + // else if (arg_name == "secure") + // configuration.secure = arg_value.safeGet(); else if (arg_name == "user") configuration.username = arg_value.safeGet(); else if (arg_name == "password") @@ -281,27 +273,15 @@ std::optional getExternalDataSourceConfiguration( if (!config.has(collection_prefix)) throw Exception(ErrorCodes::BAD_ARGUMENTS, "There is no collection named `{}` in config", collection_name); - - // configuration.host = dict_config.getString(dict_config_prefix + ".host", config.getString(collection_prefix + ".host", "")); - // configuration.port = dict_config.getInt(dict_config_prefix + ".port", config.getUInt(collection_prefix + ".port", 0)); - // configuration.secure = dict_config.getInt(dict_config_prefix + ".secure", config.getUInt(collection_prefix + ".secure", 0)); - // configuration.username = dict_config.getString(dict_config_prefix + ".user", config.getString(collection_prefix + ".user", "")); - // configuration.password = dict_config.getString(dict_config_prefix + ".password", config.getString(collection_prefix + ".password", "")); - // configuration.quota_key = dict_config.getString(dict_config_prefix + ".quota_key", config.getString(collection_prefix + ".quota_key", "")); - // configuration.database = dict_config.getString(dict_config_prefix + ".db", config.getString(dict_config_prefix + ".database", - // config.getString(collection_prefix + ".db", config.getString(collection_prefix + ".database", "")))); - // configuration.table = dict_config.getString(dict_config_prefix + ".table", config.getString(collection_prefix + ".table", "")); - // configuration.schema = dict_config.getString(dict_config_prefix + ".schema", config.getString(collection_prefix + ".schema", "")); - - readNamedCollection({{dict_config, dict_config_prefix}, {config, collection_prefix}}, configuration); - // if (dict_config.has(dict_config_prefix + ".secure") || config.has(collection_prefix + ".secure")) - // { - // non_common_args.emplace_back(std::make_pair("secure", - // dict_config.getString(dict_config_prefix + ".secure", config.getString(collection_prefix + ".secure", "")))); - // } + if (dict_config.has(dict_config_prefix + ".secure") || config.has(collection_prefix + ".secure")) + { + uint64_t secure = dict_config.getBool(dict_config_prefix + ".secure", config.getBool(collection_prefix + ".secure", false)); + + non_common_args.emplace_back(std::make_pair("secure", std::make_shared(secure))); + } if (configuration.host.empty() || configuration.port == 0 || configuration.username.empty() || configuration.table.empty()) @@ -389,18 +369,7 @@ ExternalDataSourcesByPriority getExternalDataSourceConfigurationByPriority( } else { - // common_configuration.host = dict_config.getString(dict_config_prefix + ".host", ""); - // common_configuration.port = dict_config.getUInt(dict_config_prefix + ".port", 0); - // common_configuration.secure = dict_config.getBool(dict_config_prefix + ".secure", 0); - // common_configuration.username = dict_config.getString(dict_config_prefix + ".user", ""); - // common_configuration.password = dict_config.getString(dict_config_prefix + ".password", ""); - // common_configuration.quota_key = dict_config.getString(dict_config_prefix + ".quota_key", ""); - // common_configuration.database = dict_config.getString(dict_config_prefix + ".db", dict_config.getString(dict_config_prefix + ".database", "")); - // common_configuration.table = dict_config.getString(fmt::format("{}.table", dict_config_prefix), ""); - // common_configuration.schema = dict_config.getString(fmt::format("{}.schema", dict_config_prefix), ""); - readNamedCollection({{dict_config, dict_config_prefix}}, common_configuration); - } ExternalDataSourcesByPriority configuration @@ -425,13 +394,6 @@ ExternalDataSourcesByPriority getExternalDataSourceConfigurationByPriority( validateConfigKeys(dict_config, replica_name, has_config_key); size_t priority = dict_config.getInt(replica_name + ".priority", 0); - // replica_configuration.host = dict_config.getString(replica_name + ".host", common_configuration.host); - // replica_configuration.port = dict_config.getUInt(replica_name + ".port", common_configuration.port); - // replica_configuration.secure = dict_config.getUInt(replica_name + ".secure", common_configuration.secure); - // replica_configuration.username = dict_config.getString(replica_name + ".user", common_configuration.username); - // replica_configuration.password = dict_config.getString(replica_name + ".password", common_configuration.password); - // replica_configuration.quota_key = dict_config.getString(replica_name + ".quota_key", common_configuration.quota_key); - readNamedCollection({{dict_config, replica_name}, {dict_config, dict_config_prefix}}, replica_configuration); diff --git a/src/Storages/ExternalDataSourceConfiguration.h b/src/Storages/ExternalDataSourceConfiguration.h index 6f116295bde..3a841d45884 100644 --- a/src/Storages/ExternalDataSourceConfiguration.h +++ b/src/Storages/ExternalDataSourceConfiguration.h @@ -18,7 +18,6 @@ struct ExternalDataSourceConfiguration { String host; UInt16 port = 0; - bool secure = false; String username = "default"; String password; String quota_key; diff --git a/tests/integration/test_dictionaries_ddl/configs/client.xml b/tests/integration/test_dictionaries_ddl/configs/client.xml new file mode 100644 index 00000000000..15d83a7b1ab --- /dev/null +++ b/tests/integration/test_dictionaries_ddl/configs/client.xml @@ -0,0 +1,10 @@ + + + + none + + AcceptCertificateHandler + + + + diff --git a/tests/integration/test_dictionaries_ddl/configs/named_coll.xml b/tests/integration/test_dictionaries_ddl/configs/named_coll.xml new file mode 100644 index 00000000000..b14113a301d --- /dev/null +++ b/tests/integration/test_dictionaries_ddl/configs/named_coll.xml @@ -0,0 +1,21 @@ + + + + localhost + default + 9440 + 0 + + + localhost + default + 9440 + 1 + + + localhost + default + 9440 + + + diff --git a/tests/integration/test_dictionaries_ddl/configs/server.crt b/tests/integration/test_dictionaries_ddl/configs/server.crt new file mode 100644 index 00000000000..6f4deca038f --- /dev/null +++ b/tests/integration/test_dictionaries_ddl/configs/server.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+zCCAeOgAwIBAgIJAIhI9ozZJ+TWMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV +BAMMCWxvY2FsaG9zdDAeFw0xOTA0MjIwNDMyNTJaFw0yMDA0MjEwNDMyNTJaMBQx +EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK+wVUEdqF2uXvN0MJBgnAHyXi6JTi4p/F6igsrCjSNjJWzHH0vQmK8ujfcF +CkifW88i+W5eHctuEtQqNHK+t9x9YiZtXrj6m/XkOXs20mYgENSmbbbHbriTPnZB +zZrq6UqMlwIHNNAa+I3NMORQxVRaI0ybXnGVO5elr70xHpk03xL0JWKHpEqYp4db +2aBQgF6y3Ww4khxjIYqpUYXWXGFnVIRU7FKVEAM1xyKqvQzXjQ5sVM/wyHknveEF +3b/X4ggN+KNl5KOc0cWDh1/XaatJAPaUUPqZcq76tynLbP64Xm3dxHcj+gtRkO67 +ef6MSg6l63m3XQP6Qb+MIkd06OsCAwEAAaNQME4wHQYDVR0OBBYEFDmODTO8QLDN +ykR3x0LIOnjNhrKhMB8GA1UdIwQYMBaAFDmODTO8QLDNykR3x0LIOnjNhrKhMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAwaiJc7uqEpnH3aukbftDwX +m8GfEnj1HVdgg+9GGNq+9rvUYBF6gdPmjRCX9dO0cclLFx8jc2org0rTSq9WoOhX +E6qL4Eqrmc5SE3Y9jZM0h6GRD4oXK014FmtZ3T6ddZU3dQLj3BS2r1XrvmubTvGN +ZuTJNY8nx8Hh6H5XINmsEjUF9E5hog+PwCE03xt2adIdYL+gsbxASeNYyeUFpZv5 +zcXR3VoakBWnAaOVgCHq2qh96QAnL7ZKzFkGf/MdwV10KU3dmb+ICbQUUdf9Gc17 +aaDCIRws312F433FdXBkGs2UkB7ZZme9dfn6O1QbeTNvex2VLMqYx/CTkfFbOQA= +-----END CERTIFICATE----- diff --git a/tests/integration/test_dictionaries_ddl/configs/server.key b/tests/integration/test_dictionaries_ddl/configs/server.key new file mode 100644 index 00000000000..6eddb3295db --- /dev/null +++ b/tests/integration/test_dictionaries_ddl/configs/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvsFVBHahdrl7z +dDCQYJwB8l4uiU4uKfxeooLKwo0jYyVsxx9L0JivLo33BQpIn1vPIvluXh3LbhLU +KjRyvrfcfWImbV64+pv15Dl7NtJmIBDUpm22x264kz52Qc2a6ulKjJcCBzTQGviN +zTDkUMVUWiNMm15xlTuXpa+9MR6ZNN8S9CVih6RKmKeHW9mgUIBest1sOJIcYyGK +qVGF1lxhZ1SEVOxSlRADNcciqr0M140ObFTP8Mh5J73hBd2/1+IIDfijZeSjnNHF +g4df12mrSQD2lFD6mXKu+rcpy2z+uF5t3cR3I/oLUZDuu3n+jEoOpet5t10D+kG/ +jCJHdOjrAgMBAAECggEARF66zrxb6RkSmmt8+rKeA6PuQu3sHsr4C1vyyjUr97l9 +tvdGlpp20LWtSZQMjHZ3pARYTTsTHTeY3DgQcRcHNicVKx8k3ZepWeeW9vw+pL+V +zSt3RsoVrH6gsCSrfr4sS3aqzX9AbjwQvh48CJ3mLQ1m70kHV+xbZIh1+4pB/hyP +1wKyUE18ZkOptXvO/TtoHzLQCecpkXtWzmry1Eh2isvXA+NMrAtLibGsyM1mtm7i +5ozevzHabvvCDBEe+KgZdONgVhhhvm2eOd+/s4w3rw4ETud4fI/ZAJyWXhiIKFnA +VJbElWruSAoVBW7p2bsF5PbmVzvo8vXL+VylxYD+AQKBgQDhLoRKTVhNkn/QjKxq +sdOh+QZra0LzjVpAmkQzu7wZMSHEz9qePQciDQQrYKrmRF1vNcIRCVUTqWYheJ/1 +lKRrCGa0ab6k96zkWMqLHD5u+UeJV7r1dJIx08ME9kNJ+x/XtB8klRIji16NiQUS +qc6p8z0M2AnbJzsRfWZRH8FeYwKBgQDHu8dzdtVGI7MtxfPOE/bfajiopDg8BdTC +pdug2T8XofRHRq7Q+0vYjTAZFT/slib91Pk6VvvPdo9VBZiL4omv4dAq6mOOdX/c +U14mJe1X5GCrr8ExZ8BfNJ3t/6sV1fcxyJwAw7iBguqxA2JqdM/wFk10K8XqvzVn +CD6O9yGt2QKBgFX1BMi8N538809vs41S7l9hCQNOQZNo/O+2M5yv6ECRkbtoQKKw +1x03bMUGNJaLuELweXE5Z8GGo5bZTe5X3F+DKHlr+DtO1C+ieUaa9HY2MAmMdLCn +2/qrREGLo+oEs4YKmuzC/taUp/ZNPKOAMISNdluFyFVg51pozPrgrVbTAoGBAKkE +LBl3O67o0t0vH8sJdeVFG8EJhlS0koBMnfgVHqC++dm+5HwPyvTrNQJkyv1HaqNt +r6FArkG3ED9gRuBIyT6+lctbIPgSUip9mbQqcBfqOCvQxGksZMur2ODncz09HLtS +CUFUXjOqNzOnq4ZuZu/Bz7U4vXiSaXxQq6+LTUKxAoGAFZU/qrI06XxnrE9A1X0W +l7DSkpZaDcu11NrZ473yONih/xOZNh4SSBpX8a7F6Pmh9BdtGqphML8NFPvQKcfP +b9H2iid2tc292uyrUEb5uTMmv61zoTwtitqLzO0+tS6PT3fXobX+eyeEWKzPBljL +HFtxG5CCXpkdnWRmaJnhTzA= +-----END PRIVATE KEY----- diff --git a/tests/integration/test_dictionaries_ddl/configs/ssl_conf.xml b/tests/integration/test_dictionaries_ddl/configs/ssl_conf.xml new file mode 100644 index 00000000000..e1b06aeb44b --- /dev/null +++ b/tests/integration/test_dictionaries_ddl/configs/ssl_conf.xml @@ -0,0 +1,16 @@ + + + + + + /etc/clickhouse-server/config.d/server.crt + /etc/clickhouse-server/config.d/server.key + none + true + true + sslv2,sslv3 + true + + + 9440 + diff --git a/tests/integration/test_dictionaries_ddl/test.py b/tests/integration/test_dictionaries_ddl/test.py index cb70deef72b..d728dead6a8 100644 --- a/tests/integration/test_dictionaries_ddl/test.py +++ b/tests/integration/test_dictionaries_ddl/test.py @@ -15,6 +15,13 @@ node1 = cluster.add_instance( "node1", with_mysql=True, dictionaries=["configs/dictionaries/simple_dictionary.xml"], + main_configs=[ + "configs/ssl_conf.xml", + "configs/client.xml", + "configs/named_coll.xml", + "configs/server.crt", + "configs/server.key", + ], user_configs=["configs/user_admin.xml", "configs/user_default.xml"], ) node2 = cluster.add_instance( @@ -108,6 +115,7 @@ def started_cluster(): ), ], ) + def test_create_and_select_mysql(started_cluster, clickhouse, name, layout): mysql_conn = create_mysql_conn( "root", "clickhouse", started_cluster.mysql_ip, started_cluster.mysql_port @@ -207,7 +215,6 @@ def test_create_and_select_mysql(started_cluster, clickhouse, name, layout): ) == str(hex(977))[2:] + "\n" clickhouse.query(f"drop dictionary default.{name}") - def test_restricted_database(started_cluster): for node in [node1, node2]: node.query("CREATE DATABASE IF NOT EXISTS restricted_db", user="admin") @@ -463,3 +470,118 @@ def test_clickhouse_remote(started_cluster): node3.query( "select dictGetUInt8('test.clickhouse_remote', 'SomeValue1', toUInt64(17))" ) == "17\n" + +# https://github.com/ClickHouse/ClickHouse/issues/38450 +# suggests placing 'secure' in named_collections +def test_secure(started_cluster): + node1.query("DROP TABLE IF EXISTS test.foo_dict") + node1.query("CREATE TABLE test.foo_dict(`id` UInt64, `value` String) ENGINE = Log") + node1.query("INSERT INTO test.foo_dict values (1, 'value1')") + + # No named collection, secure is set in DDL + node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") + node1.query( + """ + CREATE DICTIONARY test.clickhouse_secure( + id UInt64, + value String + ) + PRIMARY KEY id + LAYOUT(FLAT()) + SOURCE(CLICKHOUSE( + HOST 'localhost' + PORT 9440 USER 'default' + TABLE 'foo_dict' DB 'test' + SECURE 1 + )) + LIFETIME(MIN 1 MAX 10) + """ + ) + value = node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; + assert(value == "value1\n") + + # Secure set in named collection + node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") + node1.query( + """ + CREATE DICTIONARY test.clickhouse_secure( + id UInt64, + value String + ) + PRIMARY KEY id + LAYOUT(FLAT()) + SOURCE(CLICKHOUSE( + NAME 'nc_secure_1' + TABLE 'foo_dict' DB 'test' + )) + LIFETIME(MIN 1 MAX 10) + """ + ) + value = node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; + assert(value == "value1\n") + + # Secure is not set + node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") + node1.query( + """ + CREATE DICTIONARY test.clickhouse_secure( + id UInt64, + value String + ) + PRIMARY KEY id + LAYOUT(FLAT()) + SOURCE(CLICKHOUSE( + NAME 'nc_no_secure' + TABLE 'foo_dict' DB 'test' + )) + LIFETIME(MIN 1 MAX 10) + """ + ) + with pytest.raises(QueryRuntimeException) as excinfo: + node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; + assert "Unexpected packet from server localhost:9440" in str(excinfo.value) + + # Secure is set to 0 in named collection + node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") + node1.query( + """ + CREATE DICTIONARY test.clickhouse_secure( + id UInt64, + value String + ) + PRIMARY KEY id + LAYOUT(FLAT()) + SOURCE(CLICKHOUSE( + NAME 'nc_secure_0' + TABLE 'foo_dict' DB 'test' + )) + LIFETIME(MIN 1 MAX 10) + """ + ) + with pytest.raises(QueryRuntimeException) as excinfo: + node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; + assert "Unexpected packet from server localhost:9440" in str(excinfo.value) + + # Secure is set to 0 in named collection and in 1 in DDL + node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") + node1.query( + """ + CREATE DICTIONARY test.clickhouse_secure( + id UInt64, + value String + ) + PRIMARY KEY id + LAYOUT(FLAT()) + SOURCE(CLICKHOUSE( + NAME 'nc_secure_0' + TABLE 'foo_dict' DB 'test' + SECURE 1 + )) + LIFETIME(MIN 1 MAX 10) + """ + ) + value = node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; + assert(value == "value1\n") + + node1.query("DROP DICTIONARY test.clickhouse_secure") + node1.query("DROP TABLE test.foo_dict") From 38ea27489c018771c3773449d77cd0e469c92693 Mon Sep 17 00:00:00 2001 From: Ilya Golshtein Date: Mon, 13 Feb 2023 01:04:38 +0300 Subject: [PATCH 017/377] secure in named collections - small cleanup --- docs/en/operations/named-collections.md | 2 ++ src/Dictionaries/ClickHouseDictionarySource.cpp | 15 --------------- src/Storages/ExternalDataSourceConfiguration.cpp | 2 -- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/docs/en/operations/named-collections.md b/docs/en/operations/named-collections.md index f3cfa4a5372..0189cc0a8e2 100644 --- a/docs/en/operations/named-collections.md +++ b/docs/en/operations/named-collections.md @@ -244,10 +244,12 @@ Example of configuration: system foo secret + 1 ``` +`secure` is not needed for connection because of `remoteSecure`, but it can be used for dictionaries. ### Example of using named collections with the `remote`/`remoteSecure` functions diff --git a/src/Dictionaries/ClickHouseDictionarySource.cpp b/src/Dictionaries/ClickHouseDictionarySource.cpp index f3c5672a8b1..6c1304ea027 100644 --- a/src/Dictionaries/ClickHouseDictionarySource.cpp +++ b/src/Dictionaries/ClickHouseDictionarySource.cpp @@ -207,18 +207,6 @@ std::string ClickHouseDictionarySource::doInvalidateQuery(const std::string & re } } -void logConfig(const Poco::Util::AbstractConfiguration & config, std::string start_from = "dictionary", size_t level = 0) -{ - Poco::Util::AbstractConfiguration::Keys keys; - config.keys(start_from, keys); - for (const auto & key : keys) - { - LOG_TRACE(&Poco::Logger::get("ClickHouseDictionarySource"), - "logConfig key: {}{}", std::string(level, ' '), key); - logConfig(config, start_from + "." + key, level + 1); - } -} - void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) { auto create_table_source = [=](const DictionaryStructure & dict_struct, @@ -229,8 +217,6 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) const std::string & default_database [[maybe_unused]], bool created_from_ddl) -> DictionarySourcePtr { - logConfig(config); - bool secure = config.getBool(config_prefix + ".secure", false); UInt16 default_port = getPortFromContext(global_context, secure); @@ -268,7 +254,6 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) if (arg_name == "secure") { secure_from_named = checkAndGetLiteralArgument(arg_value, "secure"); - LOG_TRACE(&Poco::Logger::get("ClickHouseDictionarySource"), "clickhouse_secure {}", secure_from_named); } } } diff --git a/src/Storages/ExternalDataSourceConfiguration.cpp b/src/Storages/ExternalDataSourceConfiguration.cpp index 20f8920ff5a..a5b5a8ff72c 100644 --- a/src/Storages/ExternalDataSourceConfiguration.cpp +++ b/src/Storages/ExternalDataSourceConfiguration.cpp @@ -201,8 +201,6 @@ std::optional getExternalDataSourceConfiguration( configuration.host = arg_value.safeGet(); else if (arg_name == "port") configuration.port = arg_value.safeGet(); - // else if (arg_name == "secure") - // configuration.secure = arg_value.safeGet(); else if (arg_name == "user") configuration.username = arg_value.safeGet(); else if (arg_name == "password") From 00bcd272a89b1a8b8410b95a9094aae7ec6ff9a8 Mon Sep 17 00:00:00 2001 From: Ilya Golshtein Date: Mon, 13 Feb 2023 10:54:28 +0300 Subject: [PATCH 018/377] secure in named collections - make style check happy --- src/Storages/ExternalDataSourceConfiguration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Storages/ExternalDataSourceConfiguration.cpp b/src/Storages/ExternalDataSourceConfiguration.cpp index a5b5a8ff72c..9f7b05979e1 100644 --- a/src/Storages/ExternalDataSourceConfiguration.cpp +++ b/src/Storages/ExternalDataSourceConfiguration.cpp @@ -278,7 +278,7 @@ std::optional getExternalDataSourceConfiguration( { uint64_t secure = dict_config.getBool(dict_config_prefix + ".secure", config.getBool(collection_prefix + ".secure", false)); - non_common_args.emplace_back(std::make_pair("secure", std::make_shared(secure))); + non_common_args.emplace_back(std::make_pair("secure", std::make_shared(secure))); } From f937dc3500ae7164019fd9b9eed448af472d085d Mon Sep 17 00:00:00 2001 From: Ilya Golshtein Date: Mon, 13 Feb 2023 14:10:56 +0300 Subject: [PATCH 019/377] secure in named collections - test.py reformatted by black --- .../integration/test_dictionaries_ddl/test.py | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/tests/integration/test_dictionaries_ddl/test.py b/tests/integration/test_dictionaries_ddl/test.py index d728dead6a8..29da9d7af7b 100644 --- a/tests/integration/test_dictionaries_ddl/test.py +++ b/tests/integration/test_dictionaries_ddl/test.py @@ -115,7 +115,6 @@ def started_cluster(): ), ], ) - def test_create_and_select_mysql(started_cluster, clickhouse, name, layout): mysql_conn = create_mysql_conn( "root", "clickhouse", started_cluster.mysql_ip, started_cluster.mysql_port @@ -215,6 +214,7 @@ def test_create_and_select_mysql(started_cluster, clickhouse, name, layout): ) == str(hex(977))[2:] + "\n" clickhouse.query(f"drop dictionary default.{name}") + def test_restricted_database(started_cluster): for node in [node1, node2]: node.query("CREATE DATABASE IF NOT EXISTS restricted_db", user="admin") @@ -471,6 +471,7 @@ def test_clickhouse_remote(started_cluster): "select dictGetUInt8('test.clickhouse_remote', 'SomeValue1', toUInt64(17))" ) == "17\n" + # https://github.com/ClickHouse/ClickHouse/issues/38450 # suggests placing 'secure' in named_collections def test_secure(started_cluster): @@ -481,7 +482,7 @@ def test_secure(started_cluster): # No named collection, secure is set in DDL node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") node1.query( - """ + """ CREATE DICTIONARY test.clickhouse_secure( id UInt64, value String @@ -496,14 +497,16 @@ def test_secure(started_cluster): )) LIFETIME(MIN 1 MAX 10) """ - ) - value = node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; - assert(value == "value1\n") + ) + value = node1.query( + "SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))" + ) + assert value == "value1\n" # Secure set in named collection node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") node1.query( - """ + """ CREATE DICTIONARY test.clickhouse_secure( id UInt64, value String @@ -516,14 +519,16 @@ def test_secure(started_cluster): )) LIFETIME(MIN 1 MAX 10) """ - ) - value = node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; - assert(value == "value1\n") + ) + value = node1.query( + "SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))" + ) + assert value == "value1\n" # Secure is not set node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") node1.query( - """ + """ CREATE DICTIONARY test.clickhouse_secure( id UInt64, value String @@ -536,15 +541,15 @@ def test_secure(started_cluster): )) LIFETIME(MIN 1 MAX 10) """ - ) + ) with pytest.raises(QueryRuntimeException) as excinfo: - node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; + node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") assert "Unexpected packet from server localhost:9440" in str(excinfo.value) # Secure is set to 0 in named collection node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") node1.query( - """ + """ CREATE DICTIONARY test.clickhouse_secure( id UInt64, value String @@ -557,15 +562,15 @@ def test_secure(started_cluster): )) LIFETIME(MIN 1 MAX 10) """ - ) + ) with pytest.raises(QueryRuntimeException) as excinfo: - node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; + node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") assert "Unexpected packet from server localhost:9440" in str(excinfo.value) # Secure is set to 0 in named collection and in 1 in DDL node1.query("DROP DICTIONARY IF EXISTS test.clickhouse_secure") node1.query( - """ + """ CREATE DICTIONARY test.clickhouse_secure( id UInt64, value String @@ -579,9 +584,11 @@ def test_secure(started_cluster): )) LIFETIME(MIN 1 MAX 10) """ - ) - value = node1.query("SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))") ; - assert(value == "value1\n") + ) + value = node1.query( + "SELECT dictGet('test.clickhouse_secure', 'value', toUInt64(1))" + ) + assert value == "value1\n" node1.query("DROP DICTIONARY test.clickhouse_secure") node1.query("DROP TABLE test.foo_dict") From 85e0a57974b4000807def54ce1f88648646cb4ec Mon Sep 17 00:00:00 2001 From: Nikolay Degterinsky Date: Wed, 1 Mar 2023 15:37:20 +0000 Subject: [PATCH 020/377] Add function ULIDStringToDateTime --- src/Functions/FunctionsCodingULID.cpp | 160 ++++++++++++++++++ .../0_stateless/02668_ulid_decoding.reference | 1 + .../0_stateless/02668_ulid_decoding.sql | 3 + 3 files changed, 164 insertions(+) create mode 100644 src/Functions/FunctionsCodingULID.cpp create mode 100644 tests/queries/0_stateless/02668_ulid_decoding.reference create mode 100644 tests/queries/0_stateless/02668_ulid_decoding.sql diff --git a/src/Functions/FunctionsCodingULID.cpp b/src/Functions/FunctionsCodingULID.cpp new file mode 100644 index 00000000000..cc4210e1480 --- /dev/null +++ b/src/Functions/FunctionsCodingULID.cpp @@ -0,0 +1,160 @@ +#include "config.h" + +#if USE_ULID + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int BAD_ARGUMENTS; + extern const int ILLEGAL_TYPE_OF_ARGUMENT; + extern const int ILLEGAL_COLUMN; +} + +class FunctionULIDStringToDateTime : public IFunction +{ +public: + static constexpr size_t ULID_LENGTH = 26; + static constexpr UInt32 DATETIME_SCALE = 3; + + static constexpr auto name = "ULIDStringToDateTime"; + + static FunctionPtr create(ContextPtr /*context*/) + { + return std::make_shared(); + } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 1; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + if (arguments.size() != 1) + throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Wrong number of arguments for function {}: 1 expected.", getName()); + + const auto * arg_fixed_string = checkAndGetDataType(arguments[0].type.get()); + const auto * arg_string = checkAndGetDataType(arguments[0].type.get()); + + if (!arg_string && !(arg_fixed_string && arg_fixed_string->getN() == ULID_LENGTH)) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of argument of function {}. Must be String or FixedString(26).", + arguments[0].type->getName(), + getName()); + + return std::make_shared(DATETIME_SCALE, ""); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override + { + auto col_res = ColumnDateTime64::create(input_rows_count, DATETIME_SCALE); + auto & vec_res = col_res->getData(); + + const ColumnPtr column = arguments[0].column; + + const auto * column_fixed_string = checkAndGetColumn(column.get()); + const auto * column_string = checkAndGetColumn(column.get()); + + if (column_fixed_string) + { + if (column_fixed_string->getN() != ULID_LENGTH) + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of argument of function {}, expected String or FixedString({})", + arguments[0].name, getName(), ULID_LENGTH + ); + + const auto & vec_src = column_fixed_string->getChars(); + + for (size_t i = 0; i < input_rows_count; ++i) + { + DateTime64 time = decode(vec_src.data() + i * ULID_LENGTH); + vec_res[i] = time; + } + } + else if (column_string) + { + const auto & vec_src = column_string->getChars(); + const auto & offsets_src = column_string->getOffsets(); + + size_t src_offset = 0; + + for (size_t i = 0; i < input_rows_count; ++i) + { + DateTime64 time = 0; + + size_t string_size = offsets_src[i] - src_offset; + if (string_size == ULID_LENGTH + 1) + time = decode(vec_src.data() + offsets_src[i]); + + src_offset += string_size; + vec_res[i] = time; + } + } + else + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of argument of function {}, expected String or FixedString({})", + arguments[0].name, getName(), ULID_LENGTH + ); + + return col_res; + } + + static DateTime64 decode(const UInt8 * data) + { + unsigned char buffer[16]; + int ret = ulid_decode(buffer, reinterpret_cast(data)); + if (ret != 0) + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Cannot decode ULID"); + + /// Timestamp in milliseconds is the first 48 bits of the decoded ULID + Int64 ms = 0; + memcpy(reinterpret_cast(&ms) + 2, buffer, 6); + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::reverse(reinterpret_cast(&ms), reinterpret_cast(&ms) + sizeof(Int64)); +#endif + + return DecimalUtils::decimalFromComponents(ms / intExp10(DATETIME_SCALE), ms % intExp10(DATETIME_SCALE), DATETIME_SCALE); + } +}; + + +REGISTER_FUNCTION(ULIDStringToDateTime) +{ + factory.registerFunction( + { + R"( +Decodes ULID and returns its timestammp as DateTime64(3). +This function takes as an argument ULID of type String or FixedString(26). +)", + Documentation::Examples{{"ulid", "SELECT ULIDStringToDateTime(generateULID())"}}, + Documentation::Categories{"ULID"} + }, + FunctionFactory::CaseSensitive); +} + +} + +#endif diff --git a/tests/queries/0_stateless/02668_ulid_decoding.reference b/tests/queries/0_stateless/02668_ulid_decoding.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/tests/queries/0_stateless/02668_ulid_decoding.reference @@ -0,0 +1 @@ +1 diff --git a/tests/queries/0_stateless/02668_ulid_decoding.sql b/tests/queries/0_stateless/02668_ulid_decoding.sql new file mode 100644 index 00000000000..042c8e52b36 --- /dev/null +++ b/tests/queries/0_stateless/02668_ulid_decoding.sql @@ -0,0 +1,3 @@ +-- Tags: no-fasttest + +SELECT dateDiff('s', ULIDStringToDateTime(generateULID()), now()) = 0; From d0988911851745062e526e2f1d903819cd64ccd8 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Tue, 7 Mar 2023 12:58:55 +0000 Subject: [PATCH 021/377] Skip merges if no need --- .../MergeTree/MergeTreeDataMergerMutator.cpp | 2 +- .../02473_optimize_old_parts.reference | 3 -- .../0_stateless/02473_optimize_old_parts.sql | 30 ++----------- ...76_optimize_old_parts_replicated.reference | 7 +++ .../02676_optimize_old_parts_replicated.sql | 45 +++++++++++++++++++ 5 files changed, 56 insertions(+), 31 deletions(-) create mode 100644 tests/queries/0_stateless/02676_optimize_old_parts_replicated.reference create mode 100644 tests/queries/0_stateless/02676_optimize_old_parts_replicated.sql diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index 0d123623f05..37cb5afef1a 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -388,7 +388,7 @@ SelectPartsDecision MergeTreeDataMergerMutator::selectPartsToMerge( if (static_cast(best_partition_it->second.min_age) >= data_settings->min_age_to_force_merge_seconds) return selectAllPartsToMergeWithinPartition( - future_part, can_merge_callback, best_partition_it->first, true, metadata_snapshot, txn, out_disable_reason); + future_part, can_merge_callback, best_partition_it->first, true, metadata_snapshot, txn, out_disable_reason, true); } if (out_disable_reason) diff --git a/tests/queries/0_stateless/02473_optimize_old_parts.reference b/tests/queries/0_stateless/02473_optimize_old_parts.reference index 9002d73ff27..134f4765edb 100644 --- a/tests/queries/0_stateless/02473_optimize_old_parts.reference +++ b/tests/queries/0_stateless/02473_optimize_old_parts.reference @@ -4,9 +4,6 @@ With merge any part range 1 With merge partition only 1 -With merge replicated any part range -1 -With merge replicated partition only 1 With merge partition only and new parts 3 diff --git a/tests/queries/0_stateless/02473_optimize_old_parts.sql b/tests/queries/0_stateless/02473_optimize_old_parts.sql index c2bd37033c1..ba639ba9570 100644 --- a/tests/queries/0_stateless/02473_optimize_old_parts.sql +++ b/tests/queries/0_stateless/02473_optimize_old_parts.sql @@ -2,7 +2,6 @@ DROP TABLE IF EXISTS test_without_merge; DROP TABLE IF EXISTS test_with_merge; -DROP TABLE IF EXISTS test_replicated; SELECT 'Without merge'; @@ -40,34 +39,11 @@ INSERT INTO test_with_merge SELECT 3; SELECT sleepEachRow(1) FROM numbers(9) FORMAT Null; SELECT count(*) FROM system.parts WHERE database = currentDatabase() AND table='test_with_merge' AND active; +SELECT sleepEachRow(1) FROM numbers(9) FORMAT Null; +SELECT (now() - modification_time) > 5 FROM system.parts WHERE database = currentDatabase() AND table='test_with_merge' AND active; + DROP TABLE test_with_merge; -SELECT 'With merge replicated any part range'; - -CREATE TABLE test_replicated (i Int64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{database}/test02473', 'node') ORDER BY i -SETTINGS min_age_to_force_merge_seconds=3, min_age_to_force_merge_on_partition_only=false; -INSERT INTO test_replicated SELECT 1; -INSERT INTO test_replicated SELECT 2; -INSERT INTO test_replicated SELECT 3; - -SELECT sleepEachRow(1) FROM numbers(9) FORMAT Null; -SELECT count(*) FROM system.parts WHERE database = currentDatabase() AND table='test_replicated' AND active; - -DROP TABLE test_replicated; - -SELECT 'With merge replicated partition only'; - -CREATE TABLE test_replicated (i Int64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{database}/test02473_partition_only', 'node') ORDER BY i -SETTINGS min_age_to_force_merge_seconds=3, min_age_to_force_merge_on_partition_only=true; -INSERT INTO test_replicated SELECT 1; -INSERT INTO test_replicated SELECT 2; -INSERT INTO test_replicated SELECT 3; - -SELECT sleepEachRow(1) FROM numbers(9) FORMAT Null; -SELECT count(*) FROM system.parts WHERE database = currentDatabase() AND table='test_replicated' AND active; - -DROP TABLE test_replicated; - SELECT 'With merge partition only and new parts'; CREATE TABLE test_with_merge (i Int64) ENGINE = MergeTree ORDER BY i diff --git a/tests/queries/0_stateless/02676_optimize_old_parts_replicated.reference b/tests/queries/0_stateless/02676_optimize_old_parts_replicated.reference new file mode 100644 index 00000000000..0f3c482f188 --- /dev/null +++ b/tests/queries/0_stateless/02676_optimize_old_parts_replicated.reference @@ -0,0 +1,7 @@ +Without merge +3 +With merge replicated any part range +1 +With merge replicated partition only +1 +1 diff --git a/tests/queries/0_stateless/02676_optimize_old_parts_replicated.sql b/tests/queries/0_stateless/02676_optimize_old_parts_replicated.sql new file mode 100644 index 00000000000..9abbef0f2d2 --- /dev/null +++ b/tests/queries/0_stateless/02676_optimize_old_parts_replicated.sql @@ -0,0 +1,45 @@ +-- Tags: long + +DROP TABLE IF EXISTS test_without_merge; +DROP TABLE IF EXISTS test_replicated; + +SELECT 'Without merge'; + +CREATE TABLE test_without_merge (i Int64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{database}/test02676_without_merge', 'node') ORDER BY i; +INSERT INTO test_without_merge SELECT 1; +INSERT INTO test_without_merge SELECT 2; +INSERT INTO test_without_merge SELECT 3; + +SELECT sleepEachRow(1) FROM numbers(9) FORMAT Null; +SELECT count(*) FROM system.parts WHERE database = currentDatabase() AND table='test_without_merge' AND active; + +DROP TABLE test_without_merge; + +SELECT 'With merge replicated any part range'; + +CREATE TABLE test_replicated (i Int64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{database}/test02676', 'node') ORDER BY i +SETTINGS min_age_to_force_merge_seconds=3, min_age_to_force_merge_on_partition_only=false; +INSERT INTO test_replicated SELECT 1; +INSERT INTO test_replicated SELECT 2; +INSERT INTO test_replicated SELECT 3; + +SELECT sleepEachRow(1) FROM numbers(9) FORMAT Null; +SELECT count(*) FROM system.parts WHERE database = currentDatabase() AND table='test_replicated' AND active; + +DROP TABLE test_replicated; + +SELECT 'With merge replicated partition only'; + +CREATE TABLE test_replicated (i Int64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{database}/test02676_partition_only', 'node') ORDER BY i +SETTINGS min_age_to_force_merge_seconds=3, min_age_to_force_merge_on_partition_only=true; +INSERT INTO test_replicated SELECT 1; +INSERT INTO test_replicated SELECT 2; +INSERT INTO test_replicated SELECT 3; + +SELECT sleepEachRow(1) FROM numbers(9) FORMAT Null; +SELECT count(*) FROM system.parts WHERE database = currentDatabase() AND table='test_replicated' AND active; + +SELECT sleepEachRow(1) FROM numbers(9) FORMAT Null; +SELECT (now() - modification_time) > 5 FROM system.parts WHERE database = currentDatabase() AND table='test_replicated' AND active; + +DROP TABLE test_replicated; From 5f5e15f8c121bffd3ae511f4f038dc3b8bd65dd7 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Tue, 7 Mar 2023 18:28:10 +0000 Subject: [PATCH 022/377] Update sort description to respect aliases in header --- src/Processors/QueryPlan/ExpressionStep.cpp | 60 +++++++++++++++++++ src/Processors/QueryPlan/QueryPlan.cpp | 19 ++++++ ..._input_stream_properties_explain.reference | 4 +- ...ting_by_input_stream_properties_explain.sh | 4 -- 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index dcfa6e5a891..3557e9aa685 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -87,10 +87,70 @@ void ExpressionStep::describeActions(JSONBuilder::JSONMap & map) const map.add("Expression", expression->toTree()); } +namespace +{ + const ActionsDAG::Node * getOriginalNodeForOutputAlias(const ActionsDAGPtr & actions, const String & output_name) + { + /// find alias in output + const ActionsDAG::Node * output_alias = nullptr; + for (const auto * node : actions->getOutputs()) + { + if (node->result_name == output_name) + { + output_alias = node; + break; + } + } + if (!output_alias) + { + // logDebug("getOriginalNodeForOutputAlias: no output alias found", output_name); + return nullptr; + } + + /// find original(non alias) node it refers to + const ActionsDAG::Node * node = output_alias; + while (node && node->type == ActionsDAG::ActionType::ALIAS) + { + chassert(!node->children.empty()); + node = node->children.front(); + } + if (node && node->type != ActionsDAG::ActionType::INPUT) + return nullptr; + + return node; + } + +} + void ExpressionStep::updateOutputStream() { output_stream = createOutputStream( input_streams.front(), ExpressionTransform::transformHeader(input_streams.front().header, *actions_dag), getDataStreamTraits()); + + const ActionsDAGPtr & actions = actions_dag; + LOG_DEBUG(&Poco::Logger::get(__PRETTY_FUNCTION__), "ActionsDAG dump:\n{}", actions->dumpDAG()); + + const auto & input_sort_description = getInputStreams().front().sort_description; + for (size_t i = 0, s = input_sort_description.size(); i < s; ++i) + { + const auto & desc = input_sort_description[i]; + String alias; + const auto & origin_column = desc.column_name; + for (const auto & column : output_stream->header) + { + const auto * original_node = getOriginalNodeForOutputAlias(actions, column.name); + if (original_node && original_node->result_name == origin_column) + { + alias = column.name; + break; + } + } + + if (alias.empty()) + return; + + output_stream->sort_description[i].column_name = alias; + } } } diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index 8b666bba7da..601952e6944 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -452,6 +453,22 @@ void QueryPlan::explainPipeline(WriteBuffer & buffer, const ExplainPipelineOptio } } +void updateRootInputStream(QueryPlan::Node & root) +{ + auto* to = root.step.get(); + const auto * from = root.children.front()->step.get(); + + auto * to_update = dynamic_cast(to); + if (!to_update) + return; + const auto * update_from = dynamic_cast(from); + if (!update_from) + return; + + if (update_from->hasOutputStream()) + to_update->updateInputStream(update_from->getOutputStream()); +} + void QueryPlan::optimize(const QueryPlanOptimizationSettings & optimization_settings) { /// optimization need to be applied before "mergeExpressions" optimization @@ -462,6 +479,8 @@ void QueryPlan::optimize(const QueryPlanOptimizationSettings & optimization_sett QueryPlanOptimizations::optimizeTreeFirstPass(optimization_settings, *root, nodes); QueryPlanOptimizations::optimizeTreeSecondPass(optimization_settings, *root, nodes); + + updateRootInputStream(*root); } void QueryPlan::explainEstimate(MutableColumns & columns) diff --git a/tests/queries/0_stateless/02377_optimize_sorting_by_input_stream_properties_explain.reference b/tests/queries/0_stateless/02377_optimize_sorting_by_input_stream_properties_explain.reference index 9d78707429e..0b34e878cee 100644 --- a/tests/queries/0_stateless/02377_optimize_sorting_by_input_stream_properties_explain.reference +++ b/tests/queries/0_stateless/02377_optimize_sorting_by_input_stream_properties_explain.reference @@ -74,10 +74,10 @@ Sorting (Stream): a ASC Sorting (Stream): a ASC -- aliases DONT break sorting order -- QUERY: set optimize_read_in_order=1;set max_threads=3;set query_plan_remove_redundant_sorting=0;EXPLAIN PLAN actions=1, header=1, sorting=1 SELECT a, b FROM (SELECT x AS a, y AS b FROM (SELECT a AS x, b AS y FROM optimize_sorting) ORDER BY x, y) -Sorting (Global): x ASC, y ASC +Sorting (Global): a ASC, b ASC Sorting (Sorting for ORDER BY) Sorting (Global): x ASC, y ASC -Sorting (Stream): a ASC, b ASC +Sorting (Stream): x ASC, y ASC Sorting (Stream): a ASC, b ASC -- actions chain breaks sorting order: input(column a)->sipHash64(column a)->alias(sipHash64(column a), a)->plus(alias a, 1) -- QUERY: set optimize_read_in_order=1;set max_threads=3;set query_plan_remove_redundant_sorting=0;EXPLAIN PLAN actions=1, header=1, sorting=1 SELECT a, z FROM (SELECT sipHash64(a) AS a, a + 1 AS z FROM (SELECT a FROM optimize_sorting ORDER BY a + 1)) ORDER BY a + 1 diff --git a/tests/queries/0_stateless/02377_optimize_sorting_by_input_stream_properties_explain.sh b/tests/queries/0_stateless/02377_optimize_sorting_by_input_stream_properties_explain.sh index 0678ff63e3f..e4cab987d35 100755 --- a/tests/queries/0_stateless/02377_optimize_sorting_by_input_stream_properties_explain.sh +++ b/tests/queries/0_stateless/02377_optimize_sorting_by_input_stream_properties_explain.sh @@ -62,10 +62,6 @@ explain_sortmode "$MAKE_OUTPUT_STABLE;EXPLAIN PLAN actions=1, header=1, sorting= echo "-- aliases break sorting order" explain_sortmode "$MAKE_OUTPUT_STABLE;EXPLAIN PLAN actions=1, header=1, sorting=1 SELECT a FROM (SELECT sipHash64(a) AS a FROM (SELECT a FROM optimize_sorting ORDER BY a)) ORDER BY a" -# FIXME: we still do full sort here, - it's because, for most inner subqueury, sorting description contains original column names but header contains only aliases on those columns: -#| Header: x Int32 │ -#│ y Int32 │ -#│ Sort Mode: Chunk: a ASC, b ASC │ echo "-- aliases DONT break sorting order" explain_sortmode "$MAKE_OUTPUT_STABLE;EXPLAIN PLAN actions=1, header=1, sorting=1 SELECT a, b FROM (SELECT x AS a, y AS b FROM (SELECT a AS x, b AS y FROM optimize_sorting) ORDER BY x, y)" From e88912009a901ddf04757038d326241de4d915d8 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Tue, 7 Mar 2023 20:49:55 +0000 Subject: [PATCH 023/377] Update data streams for entire plan --- src/Processors/QueryPlan/ExpressionStep.cpp | 3 + src/Processors/QueryPlan/QueryPlan.cpp | 34 +++--- src/Processors/QueryPlan/QueryPlanVisitor.h | 111 ++++++++++++++++++++ 3 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 src/Processors/QueryPlan/QueryPlanVisitor.h diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index 3557e9aa685..1425f281d22 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -127,6 +127,9 @@ void ExpressionStep::updateOutputStream() output_stream = createOutputStream( input_streams.front(), ExpressionTransform::transformHeader(input_streams.front().header, *actions_dag), getDataStreamTraits()); + if (!getDataStreamTraits().preserves_sorting) + return; + const ActionsDAGPtr & actions = actions_dag; LOG_DEBUG(&Poco::Logger::get(__PRETTY_FUNCTION__), "ActionsDAG dump:\n{}", actions->dumpDAG()); diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index 601952e6944..3d5bd0db6d0 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -453,20 +454,29 @@ void QueryPlan::explainPipeline(WriteBuffer & buffer, const ExplainPipelineOptio } } -void updateRootInputStream(QueryPlan::Node & root) +static void updateDataStreams(QueryPlan::Node & root) { - auto* to = root.step.get(); - const auto * from = root.children.front()->step.get(); + class UpdateDataStreams : public QueryPlanVisitor + { + public: + explicit UpdateDataStreams(QueryPlan::Node * root_) : QueryPlanVisitor(root_) { } - auto * to_update = dynamic_cast(to); - if (!to_update) - return; - const auto * update_from = dynamic_cast(from); - if (!update_from) - return; + static bool visitTopDownImpl(QueryPlan::Node * /*current_node*/, QueryPlan::Node * /*parent_node*/) { return true; } - if (update_from->hasOutputStream()) - to_update->updateInputStream(update_from->getOutputStream()); + static void visitBottomUpImpl(QueryPlan::Node * current_node, QueryPlan::Node * parent_node) + { + if (!parent_node || parent_node->children.size() != 1) + return; + + if (!current_node->step->hasOutputStream()) + return; + + if (auto * parent_transform_step = dynamic_cast(parent_node->step.get()); parent_transform_step) + parent_transform_step->updateInputStream(current_node->step->getOutputStream()); + } + }; + + UpdateDataStreams(&root).visit(); } void QueryPlan::optimize(const QueryPlanOptimizationSettings & optimization_settings) @@ -480,7 +490,7 @@ void QueryPlan::optimize(const QueryPlanOptimizationSettings & optimization_sett QueryPlanOptimizations::optimizeTreeFirstPass(optimization_settings, *root, nodes); QueryPlanOptimizations::optimizeTreeSecondPass(optimization_settings, *root, nodes); - updateRootInputStream(*root); + updateDataStreams(*root); } void QueryPlan::explainEstimate(MutableColumns & columns) diff --git a/src/Processors/QueryPlan/QueryPlanVisitor.h b/src/Processors/QueryPlan/QueryPlanVisitor.h new file mode 100644 index 00000000000..0f265216649 --- /dev/null +++ b/src/Processors/QueryPlan/QueryPlanVisitor.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +namespace DB +{ +template +class QueryPlanVisitor +{ +protected: + struct FrameWithParent + { + QueryPlan::Node * node = nullptr; + QueryPlan::Node * parent_node = nullptr; + size_t next_child = 0; + }; + + using StackWithParent = std::vector; + + QueryPlan::Node * root = nullptr; + StackWithParent stack; + +public: + explicit QueryPlanVisitor(QueryPlan::Node * root_) : root(root_) { } + + void visit() + { + stack.push_back({.node = root}); + + while (!stack.empty()) + { + auto & frame = stack.back(); + + QueryPlan::Node * current_node = frame.node; + QueryPlan::Node * parent_node = frame.parent_node; + + logStep("back", current_node); + + /// top-down visit + if (0 == frame.next_child) + { + logStep("top-down", current_node); + if (!visitTopDown(current_node, parent_node)) + continue; + } + /// Traverse all children + if (frame.next_child < frame.node->children.size()) + { + auto next_frame = FrameWithParent{.node = current_node->children[frame.next_child], .parent_node = current_node}; + ++frame.next_child; + logStep("push", next_frame.node); + stack.push_back(next_frame); + continue; + } + + /// bottom-up visit + logStep("bottom-up", current_node); + visitBottomUp(current_node, parent_node); + + logStep("pop", current_node); + stack.pop_back(); + } + } + + bool visitTopDown(QueryPlan::Node * current_node, QueryPlan::Node * parent_node) + { + return getDerived().visitTopDownImpl(current_node, parent_node); + } + void visitBottomUp(QueryPlan::Node * current_node, QueryPlan::Node * parent_node) + { + getDerived().visitBottomUpImpl(current_node, parent_node); + } + +private: + Derived & getDerived() { return *static_cast(this); } + + const Derived & getDerived() const { return *static_cast(this); } + + std::unordered_map address2name; + std::unordered_map name_gen; + + std::string getStepId(const IQueryPlanStep* step) + { + const auto step_name = step->getName(); + auto it = address2name.find(step); + if (it != address2name.end()) + return it->second; + + const auto seq_num = name_gen[step_name]++; + return address2name.insert({step, fmt::format("{}{}", step_name, seq_num)}).first->second; + } + +protected: + void logStep(const char * prefix, const QueryPlan::Node * node) + { + if constexpr (debug_logging) + { + const IQueryPlanStep * current_step = node->step.get(); + LOG_DEBUG( + &Poco::Logger::get("QueryPlanVisitor"), + "{}: {}: {}", + prefix, + getStepId(current_step), + reinterpret_cast(current_step)); + } + } +}; + +} From 6f2851ef702268f3813251d7dfa32bae805707c9 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Tue, 7 Mar 2023 22:16:22 +0000 Subject: [PATCH 024/377] Update FilterStep --- src/Interpreters/ActionsDAG.cpp | 28 +++++ src/Interpreters/ActionsDAG.h | 2 + src/Processors/QueryPlan/ExpressionStep.cpp | 35 ------ src/Processors/QueryPlan/FilterStep.cpp | 28 +++++ .../Optimizations/removeRedundantDistinct.cpp | 31 ------ .../Optimizations/removeRedundantSorting.cpp | 103 +----------------- 6 files changed, 59 insertions(+), 168 deletions(-) diff --git a/src/Interpreters/ActionsDAG.cpp b/src/Interpreters/ActionsDAG.cpp index daba4c1608d..afa8071adfb 100644 --- a/src/Interpreters/ActionsDAG.cpp +++ b/src/Interpreters/ActionsDAG.cpp @@ -2288,4 +2288,32 @@ ActionsDAGPtr ActionsDAG::buildFilterActionsDAG( return result_dag; } +const ActionsDAG::Node * getOriginalNodeForOutputAlias(const ActionsDAGPtr & actions, const String & output_name) +{ + /// find alias in output + const ActionsDAG::Node * output_alias = nullptr; + for (const auto * node : actions->getOutputs()) + { + if (node->result_name == output_name) + { + output_alias = node; + break; + } + } + if (!output_alias) + return nullptr; + + /// find original(non alias) node it refers to + const ActionsDAG::Node * node = output_alias; + while (node && node->type == ActionsDAG::ActionType::ALIAS) + { + chassert(!node->children.empty()); + node = node->children.front(); + } + if (node && node->type != ActionsDAG::ActionType::INPUT) + return nullptr; + + return node; +} + } diff --git a/src/Interpreters/ActionsDAG.h b/src/Interpreters/ActionsDAG.h index 0182db8e027..b20d4959bc9 100644 --- a/src/Interpreters/ActionsDAG.h +++ b/src/Interpreters/ActionsDAG.h @@ -367,6 +367,8 @@ private: static ActionsDAGPtr cloneActionsForConjunction(NodeRawConstPtrs conjunction, const ColumnsWithTypeAndName & all_inputs); }; +const ActionsDAG::Node * getOriginalNodeForOutputAlias(const ActionsDAGPtr & actions, const String & output_name); + /// This is an ugly way to bypass impossibility to forward declare ActionDAG::Node. struct ActionDAGNodes { diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index 1425f281d22..85b466cd934 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -87,41 +87,6 @@ void ExpressionStep::describeActions(JSONBuilder::JSONMap & map) const map.add("Expression", expression->toTree()); } -namespace -{ - const ActionsDAG::Node * getOriginalNodeForOutputAlias(const ActionsDAGPtr & actions, const String & output_name) - { - /// find alias in output - const ActionsDAG::Node * output_alias = nullptr; - for (const auto * node : actions->getOutputs()) - { - if (node->result_name == output_name) - { - output_alias = node; - break; - } - } - if (!output_alias) - { - // logDebug("getOriginalNodeForOutputAlias: no output alias found", output_name); - return nullptr; - } - - /// find original(non alias) node it refers to - const ActionsDAG::Node * node = output_alias; - while (node && node->type == ActionsDAG::ActionType::ALIAS) - { - chassert(!node->children.empty()); - node = node->children.front(); - } - if (node && node->type != ActionsDAG::ActionType::INPUT) - return nullptr; - - return node; - } - -} - void ExpressionStep::updateOutputStream() { output_stream = createOutputStream( diff --git a/src/Processors/QueryPlan/FilterStep.cpp b/src/Processors/QueryPlan/FilterStep.cpp index 4699a7c1908..6f26939ff4e 100644 --- a/src/Processors/QueryPlan/FilterStep.cpp +++ b/src/Processors/QueryPlan/FilterStep.cpp @@ -120,6 +120,34 @@ void FilterStep::updateOutputStream() input_streams.front(), FilterTransform::transformHeader(input_streams.front().header, actions_dag.get(), filter_column_name, remove_filter_column), getDataStreamTraits()); + + if (!getDataStreamTraits().preserves_sorting) + return; + + const ActionsDAGPtr & actions = actions_dag; + // LOG_DEBUG(&Poco::Logger::get(__PRETTY_FUNCTION__), "ActionsDAG dump:\n{}", actions->dumpDAG()); + + const auto & input_sort_description = getInputStreams().front().sort_description; + for (size_t i = 0, s = input_sort_description.size(); i < s; ++i) + { + const auto & desc = input_sort_description[i]; + String alias; + const auto & origin_column = desc.column_name; + for (const auto & column : output_stream->header) + { + const auto * original_node = getOriginalNodeForOutputAlias(actions, column.name); + if (original_node && original_node->result_name == origin_column) + { + alias = column.name; + break; + } + } + + if (alias.empty()) + return; + + output_stream->sort_description[i].column_name = alias; + } } } diff --git a/src/Processors/QueryPlan/Optimizations/removeRedundantDistinct.cpp b/src/Processors/QueryPlan/Optimizations/removeRedundantDistinct.cpp index 02725dc3122..6685e46d780 100644 --- a/src/Processors/QueryPlan/Optimizations/removeRedundantDistinct.cpp +++ b/src/Processors/QueryPlan/Optimizations/removeRedundantDistinct.cpp @@ -63,37 +63,6 @@ namespace return non_const_columns; } - const ActionsDAG::Node * getOriginalNodeForOutputAlias(const ActionsDAGPtr & actions, const String & output_name) - { - /// find alias in output - const ActionsDAG::Node * output_alias = nullptr; - for (const auto * node : actions->getOutputs()) - { - if (node->result_name == output_name) - { - output_alias = node; - break; - } - } - if (!output_alias) - { - logDebug("getOriginalNodeForOutputAlias: no output alias found", output_name); - return nullptr; - } - - /// find original(non alias) node it refers to - const ActionsDAG::Node * node = output_alias; - while (node && node->type == ActionsDAG::ActionType::ALIAS) - { - chassert(!node->children.empty()); - node = node->children.front(); - } - if (node && node->type != ActionsDAG::ActionType::INPUT) - return nullptr; - - return node; - } - bool compareAggregationKeysWithDistinctColumns( const Names & aggregation_keys, const DistinctColumns & distinct_columns, const ActionsDAGPtr & path_actions) { diff --git a/src/Processors/QueryPlan/Optimizations/removeRedundantSorting.cpp b/src/Processors/QueryPlan/Optimizations/removeRedundantSorting.cpp index 41e30dee83e..188d7f6f117 100644 --- a/src/Processors/QueryPlan/Optimizations/removeRedundantSorting.cpp +++ b/src/Processors/QueryPlan/Optimizations/removeRedundantSorting.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,108 +19,6 @@ namespace DB::QueryPlanOptimizations { -template -class QueryPlanVisitor -{ -protected: - struct FrameWithParent - { - QueryPlan::Node * node = nullptr; - QueryPlan::Node * parent_node = nullptr; - size_t next_child = 0; - }; - - using StackWithParent = std::vector; - - QueryPlan::Node * root = nullptr; - StackWithParent stack; - -public: - explicit QueryPlanVisitor(QueryPlan::Node * root_) : root(root_) { } - - void visit() - { - stack.push_back({.node = root}); - - while (!stack.empty()) - { - auto & frame = stack.back(); - - QueryPlan::Node * current_node = frame.node; - QueryPlan::Node * parent_node = frame.parent_node; - - logStep("back", current_node); - - /// top-down visit - if (0 == frame.next_child) - { - logStep("top-down", current_node); - if (!visitTopDown(current_node, parent_node)) - continue; - } - /// Traverse all children - if (frame.next_child < frame.node->children.size()) - { - auto next_frame = FrameWithParent{.node = current_node->children[frame.next_child], .parent_node = current_node}; - ++frame.next_child; - logStep("push", next_frame.node); - stack.push_back(next_frame); - continue; - } - - /// bottom-up visit - logStep("bottom-up", current_node); - visitBottomUp(current_node, parent_node); - - logStep("pop", current_node); - stack.pop_back(); - } - } - - bool visitTopDown(QueryPlan::Node * current_node, QueryPlan::Node * parent_node) - { - return getDerived().visitTopDownImpl(current_node, parent_node); - } - void visitBottomUp(QueryPlan::Node * current_node, QueryPlan::Node * parent_node) - { - getDerived().visitBottomUpImpl(current_node, parent_node); - } - -private: - Derived & getDerived() { return *static_cast(this); } - - const Derived & getDerived() const { return *static_cast(this); } - - std::unordered_map address2name; - std::unordered_map name_gen; - - std::string getStepId(const IQueryPlanStep* step) - { - const auto step_name = step->getName(); - auto it = address2name.find(step); - if (it != address2name.end()) - return it->second; - - const auto seq_num = name_gen[step_name]++; - return address2name.insert({step, fmt::format("{}{}", step_name, seq_num)}).first->second; - } - -protected: - void logStep(const char * prefix, const QueryPlan::Node * node) - { - if constexpr (debug_logging) - { - const IQueryPlanStep * current_step = node->step.get(); - LOG_DEBUG( - &Poco::Logger::get("QueryPlanVisitor"), - "{}: {}: {}", - prefix, - getStepId(current_step), - reinterpret_cast(current_step)); - } - } -}; - constexpr bool debug_logging_enabled = false; class RemoveRedundantSorting : public QueryPlanVisitor From 79d4d029b87ebb9dd3cd61e3774ef16dbbc133a7 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Tue, 7 Mar 2023 22:40:11 +0000 Subject: [PATCH 025/377] Fix Distinct properties --- src/Processors/QueryPlan/DistinctStep.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Processors/QueryPlan/DistinctStep.cpp b/src/Processors/QueryPlan/DistinctStep.cpp index 323ef0bbdab..f903fc973ad 100644 --- a/src/Processors/QueryPlan/DistinctStep.cpp +++ b/src/Processors/QueryPlan/DistinctStep.cpp @@ -26,13 +26,14 @@ static bool checkColumnsAlreadyDistinct(const Names & columns, const NameSet & d static ITransformingStep::Traits getTraits(bool pre_distinct, bool already_distinct_columns) { + const bool preserves_number_of_streams = pre_distinct || already_distinct_columns; return ITransformingStep::Traits { { .preserves_distinct_columns = already_distinct_columns, /// Will be calculated separately otherwise .returns_single_stream = !pre_distinct && !already_distinct_columns, - .preserves_number_of_streams = pre_distinct || already_distinct_columns, - .preserves_sorting = true, /// Sorting is preserved indeed because of implementation. + .preserves_number_of_streams = preserves_number_of_streams, + .preserves_sorting = preserves_number_of_streams, }, { .preserves_number_of_rows = false, From e1b309a904e4d89c968549b4a8c1716bab53f0df Mon Sep 17 00:00:00 2001 From: Nikolay Degterinsky Date: Wed, 8 Mar 2023 06:01:33 +0000 Subject: [PATCH 026/377] Better docs, add timezone --- .../sql-reference/functions/ulid-functions.md | 33 +++++++++++++ src/Functions/FunctionsCodingULID.cpp | 46 ++++++++++++++----- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/docs/en/sql-reference/functions/ulid-functions.md b/docs/en/sql-reference/functions/ulid-functions.md index 94167945f76..2bff5da1c1a 100644 --- a/docs/en/sql-reference/functions/ulid-functions.md +++ b/docs/en/sql-reference/functions/ulid-functions.md @@ -48,6 +48,39 @@ SELECT generateULID(1), generateULID(2) └────────────────────────────┴────────────────────────────┘ ``` +## ULIDStringToDateTime + +This function extracts the timestamp from a ULID. + +**Syntax** + +``` sql +ULIDStringToDateTime(ulid[, timezone]) +``` + +**Arguments** + +- `ulid` — Input UUID. [String](/docs/en/sql-reference/data-types/string.md) or [FixedString(26)](/docs/en/sql-reference/data-types/fixedstring.md). +- `timezone` — [Timezone name](../../operations/server-configuration-parameters/settings.md#server_configuration_parameters-timezone) for the returned value (optional). [String](../../sql-reference/data-types/string.md). + +**Returned value** + +- Timestamp with milliseconds precision. + +Type: [DateTime64(3)](/docs/en/sql-reference/data-types/datetime64.md). + +**Usage example** + +``` sql +SELECT ULIDStringToDateTime('01GNB2S2FGN2P93QPXDNB4EN2R') +``` + +``` text +┌─ULIDStringToDateTime('01GNB2S2FGN2P93QPXDNB4EN2R')─┐ +│ 2022-12-28 00:40:37.616 │ +└────────────────────────────────────────────────────┘ +``` + ## See Also - [UUID](../../sql-reference/functions/uuid-functions.md) diff --git a/src/Functions/FunctionsCodingULID.cpp b/src/Functions/FunctionsCodingULID.cpp index cc4210e1480..07be6f57cac 100644 --- a/src/Functions/FunctionsCodingULID.cpp +++ b/src/Functions/FunctionsCodingULID.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -41,14 +42,18 @@ public: String getName() const override { return name; } - size_t getNumberOfArguments() const override { return 1; } + bool isVariadic() const override { return true; } + size_t getNumberOfArguments() const override { return 0; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { - if (arguments.size() != 1) - throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Wrong number of arguments for function {}: 1 expected.", getName()); + if (arguments.size() < 1 || arguments.size() > 2) + throw Exception( + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, + "Wrong number of arguments for function {}: should be 1 or 2", + getName()); const auto * arg_fixed_string = checkAndGetDataType(arguments[0].type.get()); const auto * arg_string = checkAndGetDataType(arguments[0].type.get()); @@ -60,7 +65,18 @@ public: arguments[0].type->getName(), getName()); - return std::make_shared(DATETIME_SCALE, ""); + String timezone; + if (arguments.size() == 2) + { + timezone = extractTimeZoneNameFromColumn(*arguments[1].column); + + if (timezone.empty()) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Function {} supports a 2nd argument (optional) that must be a valid time zone", + getName()); + } + + return std::make_shared(DATETIME_SCALE, timezone); } bool useDefaultImplementationForConstants() const override { return true; } @@ -105,7 +121,7 @@ public: size_t string_size = offsets_src[i] - src_offset; if (string_size == ULID_LENGTH + 1) - time = decode(vec_src.data() + offsets_src[i]); + time = decode(vec_src.data() + src_offset); src_offset += string_size; vec_res[i] = time; @@ -126,15 +142,18 @@ public: unsigned char buffer[16]; int ret = ulid_decode(buffer, reinterpret_cast(data)); if (ret != 0) - throw Exception(ErrorCodes::BAD_ARGUMENTS, "Cannot decode ULID"); + throw Exception( + ErrorCodes::BAD_ARGUMENTS, + "Cannot parse ULID {}", + std::string_view(reinterpret_cast(data), ULID_LENGTH) + ); /// Timestamp in milliseconds is the first 48 bits of the decoded ULID Int64 ms = 0; memcpy(reinterpret_cast(&ms) + 2, buffer, 6); -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - std::reverse(reinterpret_cast(&ms), reinterpret_cast(&ms) + sizeof(Int64)); -#endif + if constexpr (std::endian::native == std::endian::little) + std::reverse(reinterpret_cast(&ms), reinterpret_cast(&ms) + sizeof(Int64)); return DecimalUtils::decimalFromComponents(ms / intExp10(DATETIME_SCALE), ms % intExp10(DATETIME_SCALE), DATETIME_SCALE); } @@ -146,10 +165,13 @@ REGISTER_FUNCTION(ULIDStringToDateTime) factory.registerFunction( { R"( -Decodes ULID and returns its timestammp as DateTime64(3). -This function takes as an argument ULID of type String or FixedString(26). +This function extracts the timestamp from a ULID and returns it as a DateTime64(3) typed value. +The function expects the ULID to be provided as the first argument, which can be either a String or a FixedString(26) data type. +An optional second argument can be passed to specify a timezone for the timestamp. )", - Documentation::Examples{{"ulid", "SELECT ULIDStringToDateTime(generateULID())"}}, + Documentation::Examples{ + {"ulid", "SELECT ULIDStringToDateTime(generateULID())"}, + {"timezone", "SELECT ULIDStringToDateTime(generateULID(), 'Asia/Istanbul')"}}, Documentation::Categories{"ULID"} }, FunctionFactory::CaseSensitive); From 68858635647c6cb86003db3971bf6e94d417a073 Mon Sep 17 00:00:00 2001 From: Maksym Sobolyev Date: Wed, 8 Mar 2023 16:18:13 -0800 Subject: [PATCH 027/377] Use "SELECT FROM ONLY xyz", not "SELECT FROM xyz" while replicating postgresql tables, to properly handle inherited tables. Currently, it would fetch same data twice - once from the child tables and then from the parent table. --- src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp b/src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp index f9bfe1d174a..29314523860 100644 --- a/src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp +++ b/src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp @@ -354,7 +354,7 @@ StorageInfo PostgreSQLReplicationHandler::loadFromSnapshot(postgres::Connection /// Load from snapshot, which will show table state before creation of replication slot. /// Already connected to needed database, no need to add it to query. auto quoted_name = doubleQuoteWithSchema(table_name); - query_str = fmt::format("SELECT * FROM {}", quoted_name); + query_str = fmt::format("SELECT * FROM ONLY {}", quoted_name); LOG_DEBUG(log, "Loading PostgreSQL table {}.{}", postgres_database, quoted_name); auto table_structure = fetchTableStructure(*tx, table_name); From 5bc21538e516f01c634f51330639d8721b41c9af Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 9 Mar 2023 10:31:55 +0000 Subject: [PATCH 028/377] Enable use_environment_credentials by default --- src/Backups/BackupIO_S3.cpp | 2 +- src/Coordination/KeeperSnapshotManagerS3.cpp | 2 +- src/Disks/ObjectStorages/S3/diskSettings.cpp | 2 +- src/Storages/StorageS3.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Backups/BackupIO_S3.cpp b/src/Backups/BackupIO_S3.cpp index 2f315e8d488..2be5484262a 100644 --- a/src/Backups/BackupIO_S3.cpp +++ b/src/Backups/BackupIO_S3.cpp @@ -65,7 +65,7 @@ namespace settings.auth_settings.server_side_encryption_customer_key_base64, std::move(headers), settings.auth_settings.use_environment_credentials.value_or( - context->getConfigRef().getBool("s3.use_environment_credentials", false)), + context->getConfigRef().getBool("s3.use_environment_credentials", true)), settings.auth_settings.use_insecure_imds_request.value_or( context->getConfigRef().getBool("s3.use_insecure_imds_request", false))); } diff --git a/src/Coordination/KeeperSnapshotManagerS3.cpp b/src/Coordination/KeeperSnapshotManagerS3.cpp index 7b47324a890..108578f03a7 100644 --- a/src/Coordination/KeeperSnapshotManagerS3.cpp +++ b/src/Coordination/KeeperSnapshotManagerS3.cpp @@ -102,7 +102,7 @@ void KeeperSnapshotManagerS3::updateS3Configuration(const Poco::Util::AbstractCo credentials.GetAWSSecretKey(), auth_settings.server_side_encryption_customer_key_base64, std::move(headers), - auth_settings.use_environment_credentials.value_or(false), + auth_settings.use_environment_credentials.value_or(true), auth_settings.use_insecure_imds_request.value_or(false)); auto new_client = std::make_shared(std::move(new_uri), std::move(auth_settings), std::move(client)); diff --git a/src/Disks/ObjectStorages/S3/diskSettings.cpp b/src/Disks/ObjectStorages/S3/diskSettings.cpp index e0e4735f519..0fd04fdcc69 100644 --- a/src/Disks/ObjectStorages/S3/diskSettings.cpp +++ b/src/Disks/ObjectStorages/S3/diskSettings.cpp @@ -151,7 +151,7 @@ std::unique_ptr getClient( config.getString(config_prefix + ".secret_access_key", ""), config.getString(config_prefix + ".server_side_encryption_customer_key_base64", ""), {}, - config.getBool(config_prefix + ".use_environment_credentials", config.getBool("s3.use_environment_credentials", false)), + config.getBool(config_prefix + ".use_environment_credentials", config.getBool("s3.use_environment_credentials", true)), config.getBool(config_prefix + ".use_insecure_imds_request", config.getBool("s3.use_insecure_imds_request", false))); } diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index ed290c38c1f..e29c5e17cc1 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -1265,7 +1265,7 @@ void StorageS3::updateConfiguration(ContextPtr ctx, StorageS3::Configuration & u credentials.GetAWSSecretKey(), upd.auth_settings.server_side_encryption_customer_key_base64, std::move(headers), - upd.auth_settings.use_environment_credentials.value_or(ctx->getConfigRef().getBool("s3.use_environment_credentials", false)), + upd.auth_settings.use_environment_credentials.value_or(ctx->getConfigRef().getBool("s3.use_environment_credentials", true)), upd.auth_settings.use_insecure_imds_request.value_or(ctx->getConfigRef().getBool("s3.use_insecure_imds_request", false))); } @@ -1281,7 +1281,7 @@ void StorageS3::processNamedCollectionResult(StorageS3::Configuration & configur configuration.auth_settings.access_key_id = collection.getOrDefault("access_key_id", ""); configuration.auth_settings.secret_access_key = collection.getOrDefault("secret_access_key", ""); - configuration.auth_settings.use_environment_credentials = collection.getOrDefault("use_environment_credentials", 0); + configuration.auth_settings.use_environment_credentials = collection.getOrDefault("use_environment_credentials", 1); configuration.format = collection.getOrDefault("format", "auto"); configuration.compression_method = collection.getOrDefault("compression_method", collection.getOrDefault("compression", "auto")); From aa56dddc56b25c4eac828b45a3769d2054030133 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 13 Mar 2023 08:08:31 +0000 Subject: [PATCH 029/377] Add CNF --- src/Analyzer/Passes/CNF.cpp | 131 ++++++++++++++++++++++++++++++++++++ src/Analyzer/Passes/CNF.h | 46 +++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 src/Analyzer/Passes/CNF.cpp create mode 100644 src/Analyzer/Passes/CNF.h diff --git a/src/Analyzer/Passes/CNF.cpp b/src/Analyzer/Passes/CNF.cpp new file mode 100644 index 00000000000..9d69024f43d --- /dev/null +++ b/src/Analyzer/Passes/CNF.cpp @@ -0,0 +1,131 @@ +#include + +#include +#include + +#include +#include + +#include + +#include + +namespace DB::Analyzer +{ + +namespace +{ + +bool isLogicalFunction(const FunctionNode & function_node) +{ + const std::string_view name = function_node.getFunctionName(); + return name == "and" || name == "or" || name == "not"; +} + +class SplitMultiLogicVisitor : public InDepthQueryTreeVisitorWithContext +{ +public: + using Base = InDepthQueryTreeVisitorWithContext; + + explicit SplitMultiLogicVisitor(ContextPtr context) + : Base(std::move(context)) + {} + + static bool needChildVisit(QueryTreeNodePtr & parent, QueryTreeNodePtr &) + { + auto * function_node = parent->as(); + + if (!function_node) + return false; + + return isLogicalFunction(*function_node); + } + + void visitImpl(QueryTreeNodePtr & node) + { + auto * function_node = node->as(); + if (!function_node || !isLogicalFunction(*function_node)) + { + ++atom_count; + return; + } + + const auto & name = function_node->getFunctionName(); + + if (name == "and" || name == "or") + { + auto function_resolver = FunctionFactory::instance().get(name, getContext()); + + const auto & arguments = function_node->getArguments().getNodes(); + if (arguments.size() > 2) + { + QueryTreeNodePtr current = arguments[0]; + for (size_t i = 1; i < arguments.size(); ++i) + { + QueryTreeNodes new_arguments; + new_arguments.reserve(2); + new_arguments.push_back(std::move(current)); + new_arguments.push_back(arguments[i]); + auto new_function_node = std::make_shared(); + new_function_node->getArguments().getNodes() = std::move(new_arguments); + new_function_node->resolveAsFunction(function_resolver); + current = std::move(new_function_node); + } + + auto & new_function_node = current->as(); + function_node->getArguments().getNodes() = std::move(new_function_node.getArguments().getNodes()); + function_node->resolveAsFunction(function_resolver); + } + } + else + { + assert(name == "not"); + } + } + + size_t atom_count = 0; +}; + +} + +bool CNF::AtomicFormula::operator==(const AtomicFormula & rhs) const +{ + return negative == rhs.negative && node_with_hash == rhs.node_with_hash; +} + +std::string CNF::dump() const +{ + WriteBufferFromOwnString res; + bool first = true; + for (const auto & group : statements) + { + if (!first) + res << " AND "; + first = false; + res << "("; + bool first_in_group = true; + for (const auto & atom : group) + { + if (!first_in_group) + res << " OR "; + first_in_group = false; + if (atom.negative) + res << " NOT "; + res << atom.node_with_hash.node->formatASTForErrorMessage(); + } + res << ")"; + } + + return res.str(); +} + +std::optional CNF::tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t) +{ + auto node_cloned = node->clone(); + SplitMultiLogicVisitor split_visitor(std::move(context)); + split_visitor.visit(node_cloned); +// size_t num_atoms = countAtoms(node); + return std::nullopt; +} + +} diff --git a/src/Analyzer/Passes/CNF.h b/src/Analyzer/Passes/CNF.h new file mode 100644 index 00000000000..cdf28eedd9e --- /dev/null +++ b/src/Analyzer/Passes/CNF.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include + +#include + + +namespace DB::Analyzer +{ + +class CNF +{ +public: + struct AtomicFormula + { + bool negative = false; + QueryTreeNodePtrWithHash node_with_hash; + + bool operator==(const AtomicFormula & rhs) const; + }; + + using OrGroup = std::unordered_set; + using AndGroup = std::unordered_set; + + std::string dump() const; + + static constexpr size_t DEFAULT_MAX_GROWTH_MULTIPLIER = 20; + + static std::optional tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier = DEFAULT_MAX_GROWTH_MULTIPLIER); +private: + AndGroup statements; +}; + +} + +template <> +struct std::hash +{ + size_t operator()(const DB::Analyzer::CNF::AtomicFormula & atomic_formula) const + { + return std::hash()(atomic_formula.node_with_hash); + } +}; From e0e06ceeeff72f8e6b875c6be748e3f3ab21f0b2 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Tue, 14 Mar 2023 12:58:12 +0000 Subject: [PATCH 030/377] Fix: 02343_group_by_use_nulls --- src/Processors/QueryPlan/RollupStep.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Processors/QueryPlan/RollupStep.cpp b/src/Processors/QueryPlan/RollupStep.cpp index 3305f24602f..baf766e142d 100644 --- a/src/Processors/QueryPlan/RollupStep.cpp +++ b/src/Processors/QueryPlan/RollupStep.cpp @@ -53,7 +53,9 @@ void RollupStep::transformPipeline(QueryPipelineBuilder & pipeline, const BuildQ void RollupStep::updateOutputStream() { output_stream = createOutputStream( - input_streams.front(), appendGroupingSetColumn(params.getHeader(input_streams.front().header, final)), getDataStreamTraits()); + input_streams.front(), + generateOutputHeader(params.getHeader(input_streams.front().header, final), params.keys, use_nulls), + getDataStreamTraits()); /// Aggregation keys are distinct for (const auto & key : params.keys) From 80d248aa06c36e56fb8b019fa90ea6a4542f62f7 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 13 Mar 2023 15:52:35 +0000 Subject: [PATCH 031/377] Finish first part of CNF --- src/Analyzer/Passes/CNF.cpp | 415 ++++++++++++++++-- src/Analyzer/Passes/CNF.h | 49 ++- src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 68 +++ src/Analyzer/Passes/ConvertQueryToCNFPass.h | 18 + src/Analyzer/QueryTreePassManager.cpp | 4 +- 5 files changed, 506 insertions(+), 48 deletions(-) create mode 100644 src/Analyzer/Passes/ConvertQueryToCNFPass.cpp create mode 100644 src/Analyzer/Passes/ConvertQueryToCNFPass.h diff --git a/src/Analyzer/Passes/CNF.cpp b/src/Analyzer/Passes/CNF.cpp index 9d69024f43d..23d132540f2 100644 --- a/src/Analyzer/Passes/CNF.cpp +++ b/src/Analyzer/Passes/CNF.cpp @@ -9,6 +9,7 @@ #include #include +#include "Interpreters/ActionsDAG.h" namespace DB::Analyzer { @@ -22,55 +23,60 @@ bool isLogicalFunction(const FunctionNode & function_node) return name == "and" || name == "or" || name == "not"; } -class SplitMultiLogicVisitor : public InDepthQueryTreeVisitorWithContext +template +QueryTreeNodePtr createFunctionNode(const FunctionOverloadResolverPtr & function_resolver, Args &&... args) +{ + auto function_node = std::make_shared(function_resolver->getName()); + auto & new_arguments = function_node->getArguments().getNodes(); + new_arguments.reserve(sizeof...(args)); + (new_arguments.push_back(std::forward(args)), ...); + function_node->resolveAsFunction(function_resolver); + return function_node; +} + +size_t countAtoms(const QueryTreeNodePtr & node) +{ + checkStackSize(); + + const auto * function_node = node->as(); + if (!function_node || !isLogicalFunction(*function_node)) + return 1; + + size_t atom_count = 0; + const auto & arguments = function_node->getArguments().getNodes(); + for (const auto & argument : arguments) + atom_count += countAtoms(argument); + + return atom_count; +} + +class SplitMultiLogicVisitor { public: - using Base = InDepthQueryTreeVisitorWithContext; - explicit SplitMultiLogicVisitor(ContextPtr context) - : Base(std::move(context)) + : current_context(std::move(context)) {} - static bool needChildVisit(QueryTreeNodePtr & parent, QueryTreeNodePtr &) + void visit(QueryTreeNodePtr & node) { - auto * function_node = parent->as(); + checkStackSize(); - if (!function_node) - return false; - - return isLogicalFunction(*function_node); - } - - void visitImpl(QueryTreeNodePtr & node) - { auto * function_node = node->as(); if (!function_node || !isLogicalFunction(*function_node)) - { - ++atom_count; return; - } const auto & name = function_node->getFunctionName(); if (name == "and" || name == "or") { - auto function_resolver = FunctionFactory::instance().get(name, getContext()); + auto function_resolver = FunctionFactory::instance().get(name, current_context); const auto & arguments = function_node->getArguments().getNodes(); if (arguments.size() > 2) { QueryTreeNodePtr current = arguments[0]; for (size_t i = 1; i < arguments.size(); ++i) - { - QueryTreeNodes new_arguments; - new_arguments.reserve(2); - new_arguments.push_back(std::move(current)); - new_arguments.push_back(arguments[i]); - auto new_function_node = std::make_shared(); - new_function_node->getArguments().getNodes() = std::move(new_arguments); - new_function_node->resolveAsFunction(function_resolver); - current = std::move(new_function_node); - } + current = createFunctionNode(function_resolver, std::move(current), arguments[i]); auto & new_function_node = current->as(); function_node->getArguments().getNodes() = std::move(new_function_node.getArguments().getNodes()); @@ -81,9 +87,185 @@ public: { assert(name == "not"); } + + auto & arguments = function_node->getArguments().getNodes(); + for (auto & argument : arguments) + visit(argument); } - size_t atom_count = 0; +private: + ContextPtr current_context; +}; + +class PushNotVisitor +{ +public: + explicit PushNotVisitor(ContextPtr context) + : current_context(std::move(context)) + {} + + void visit(QueryTreeNodePtr & node, bool add_negation) + { + checkStackSize(); + + auto * function_node = node->as(); + + if (!function_node || !isLogicalFunction(*function_node)) + { + if (add_negation) + node = createFunctionNode(FunctionFactory::instance().get("not", current_context), std::move(node)); + return; + } + + std::string_view function_name = function_node->getFunctionName(); + if (function_name == "and" || function_name == "or") + { + if (add_negation) + { + auto function_resolver = FunctionFactory::instance().get(function_name == "and" ? "or" : "and", current_context); + function_node->resolveAsFunction(function_resolver); + } + + auto & arguments = function_node->getArguments().getNodes(); + for (auto & argument : arguments) + visit(argument, add_negation); + return; + } + + assert(function_name == "not"); + auto & arguments = function_node->getArguments().getNodes(); + assert(arguments.size() == 1); + node = arguments[0]; + visit(node, !add_negation); + } + +private: + ContextPtr current_context; +}; + +class PushOrVisitor +{ +public: + PushOrVisitor(ContextPtr context, size_t max_atoms_, size_t num_atoms_) + : max_atoms(max_atoms_) + , num_atoms(num_atoms_) + , and_resolver(FunctionFactory::instance().get("and", context)) + , or_resolver(FunctionFactory::instance().get("or", context)) + {} + + bool visit(QueryTreeNodePtr & node) + { + if (max_atoms && num_atoms > max_atoms) + return false; + + checkStackSize(); + + auto * function_node = node->as(); + + if (!function_node) + return true; + + std::string_view name = function_node->getFunctionName(); + + if (name == "or" || name == "and") + { + auto & arguments = function_node->getArguments().getNodes(); + for (auto & argument : arguments) + visit(argument); + } + + if (name == "or") + { + auto & arguments = function_node->getArguments().getNodes(); + assert(arguments.size() == 2); + + size_t and_node_id = arguments.size(); + + for (size_t i = 0; i < arguments.size(); ++i) + { + auto & argument = arguments[i]; + if (auto * argument_function_node = argument->as(); + argument_function_node && argument_function_node->getFunctionName() == "and") + and_node_id = i; + } + + if (and_node_id == arguments.size()) + return true; + + auto & other_node = arguments[1 - and_node_id]; + auto & and_function_arguments = arguments[and_node_id]->as().getArguments().getNodes(); + + auto lhs = createFunctionNode(or_resolver, other_node->clone(), std::move(and_function_arguments[0])); + num_atoms += countAtoms(other_node); + + auto rhs = createFunctionNode(or_resolver, std::move(other_node), std::move(and_function_arguments[1])); + node = createFunctionNode(and_resolver, std::move(lhs), std::move(rhs)); + + visit(node); + } + + return true; + } + +private: + size_t max_atoms; + size_t num_atoms; + + const FunctionOverloadResolverPtr and_resolver; + const FunctionOverloadResolverPtr or_resolver; +}; + +class CollectGroupsVisitor +{ +public: + void visit(QueryTreeNodePtr & node) + { + CNF::OrGroup or_group; + visitImpl(node, or_group); + if (!or_group.empty()) + and_group.insert(std::move(or_group)); + } + + CNF::AndGroup and_group; + +private: + void visitImpl(QueryTreeNodePtr & node, CNF::OrGroup & or_group) + { + checkStackSize(); + + auto * function_node = node->as(); + if (!function_node || !isLogicalFunction(*function_node)) + { + or_group.insert(CNF::AtomicFormula{false, std::move(node)}); + return; + } + + std::string_view name = function_node->getFunctionName(); + + if (name == "and") + { + auto & arguments = function_node->getArguments().getNodes(); + for (auto & argument : arguments) + { + CNF::OrGroup argument_or_group; + visitImpl(argument, argument_or_group); + if (!argument_or_group.empty()) + and_group.insert(std::move(argument_or_group)); + } + } + else if (name == "or") + { + auto & arguments = function_node->getArguments().getNodes(); + for (auto & argument : arguments) + visitImpl(argument, or_group); + } + else + { + assert(name == "not"); + auto & arguments = function_node->getArguments().getNodes(); + or_group.insert(CNF::AtomicFormula{true, std::move(arguments[0])}); + } + } }; } @@ -93,6 +275,13 @@ bool CNF::AtomicFormula::operator==(const AtomicFormula & rhs) const return negative == rhs.negative && node_with_hash == rhs.node_with_hash; } +bool CNF::AtomicFormula::operator<(const AtomicFormula & rhs) const +{ + return node_with_hash.hash == rhs.node_with_hash.hash + ? negative < rhs.negative + : node_with_hash.hash < rhs.node_with_hash.hash; +} + std::string CNF::dump() const { WriteBufferFromOwnString res; @@ -119,13 +308,171 @@ std::string CNF::dump() const return res.str(); } -std::optional CNF::tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t) +CNF & CNF::transformGroups(std::function fn) { + AndGroup result; + + for (const auto & group : statements) + { + auto new_group = fn(group); + if (!new_group.empty()) + result.insert(std::move(new_group)); + } + + statements = std::move(result); + return *this; +} + +CNF & CNF::transformAtoms(std::function fn) +{ + transformGroups([fn](const OrGroup & group) + { + OrGroup result; + for (const auto & atom : group) + { + auto new_atom = fn(atom); + if (new_atom.node_with_hash.node) + result.insert(std::move(new_atom)); + } + + return result; + }); + + return *this; +} + +CNF & CNF::pushNotIntoFunctions(const ContextPtr & context) +{ + transformAtoms([&](const AtomicFormula & atom) + { + if (!atom.negative) + return atom; + + static const std::unordered_map inverse_relations = { + {"equals", "notEquals"}, + {"less", "greaterOrEquals"}, + {"lessOrEquals", "greater"}, + {"in", "notIn"}, + {"like", "notLike"}, + {"empty", "notEmpty"}, + {"notEquals", "equals"}, + {"greaterOrEquals", "less"}, + {"greater", "lessOrEquals"}, + {"notIn", "in"}, + {"notLike", "like"}, + {"notEmpty", "empty"}, + }; + + auto * function_node = atom.node_with_hash.node->as(); + if (!function_node) + return atom; + + if (auto it = inverse_relations.find(function_node->getFunctionName()); it != inverse_relations.end()) + { + auto inverse_function_resolver = FunctionFactory::instance().get(it->second, context); + function_node->resolveAsFunction(inverse_function_resolver); + return AtomicFormula{!atom.negative, atom.node_with_hash.node}; + } + + return atom; + }); + + std::cout << "gorup: " << statements.size() << std::endl; + + return *this; +} + +CNF::CNF(AndGroup statements_) + : statements(std::move(statements_)) +{} + +std::optional CNF::tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier) +{ + auto * function_node = node->as(); + + if (!function_node || !isLogicalFunction(*function_node)) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot convert nodes that are not logical functions to CNF"); + auto node_cloned = node->clone(); - SplitMultiLogicVisitor split_visitor(std::move(context)); - split_visitor.visit(node_cloned); -// size_t num_atoms = countAtoms(node); - return std::nullopt; + + size_t atom_count = countAtoms(node_cloned); + size_t max_atoms = max_growth_multiplier ? std::max(MAX_ATOMS_WITHOUT_CHECK, atom_count * max_growth_multiplier) : 0; + + { + SplitMultiLogicVisitor visitor(context); + visitor.visit(node_cloned); + } + + { + PushNotVisitor visitor(context); + visitor.visit(node_cloned, false); + } + + if (PushOrVisitor visitor(context, max_atoms, atom_count); + !visitor.visit(node_cloned)) + return std::nullopt; + + CollectGroupsVisitor collect_visitor; + collect_visitor.visit(node_cloned); + + if (collect_visitor.and_group.empty()) + return std::nullopt; + + return CNF{std::move(collect_visitor.and_group)}; +} + +QueryTreeNodePtr CNF::toQueryTree(ContextPtr context) const +{ + if (statements.empty()) + return nullptr; + + QueryTreeNodes and_arguments; + and_arguments.reserve(statements.size()); + + auto not_resolver = FunctionFactory::instance().get("not", context); + auto or_resolver = FunctionFactory::instance().get("or", context); + auto and_resolver = FunctionFactory::instance().get("and", context); + + const auto function_node_from_atom = [&](const auto & atom) -> QueryTreeNodePtr + { + auto cloned_node = atom.node_with_hash.node->clone(); + if (atom.negative) + return createFunctionNode(not_resolver, std::move(cloned_node)); + + return std::move(cloned_node); + }; + + for (const auto & or_group : statements) + { + if (or_group.size() == 1) + { + const auto & atom = *or_group.begin(); + and_arguments.push_back(function_node_from_atom(atom)); + } + else + { + QueryTreeNodes or_arguments; + or_arguments.reserve(or_group.size()); + + for (const auto & atom : or_group) + or_arguments.push_back(function_node_from_atom(atom)); + + auto or_function = std::make_shared("or"); + or_function->getArguments().getNodes() = std::move(or_arguments); + or_function->resolveAsFunction(or_resolver); + + and_arguments.push_back(std::move(or_function)); + } + } + + if (and_arguments.size() == 1) + return std::move(and_arguments[0]); + + auto and_function = std::make_shared("and"); + and_function->getArguments().getNodes() = std::move(and_arguments); + and_function->resolveAsFunction(and_resolver); + + return and_function; } } diff --git a/src/Analyzer/Passes/CNF.h b/src/Analyzer/Passes/CNF.h index cdf28eedd9e..9736a356d63 100644 --- a/src/Analyzer/Passes/CNF.h +++ b/src/Analyzer/Passes/CNF.h @@ -3,11 +3,12 @@ #include #include +#include + #include #include - namespace DB::Analyzer { @@ -20,27 +21,49 @@ public: QueryTreeNodePtrWithHash node_with_hash; bool operator==(const AtomicFormula & rhs) const; + bool operator<(const AtomicFormula & rhs) const; }; - using OrGroup = std::unordered_set; - using AndGroup = std::unordered_set; + struct SetAtomicFormulaHash + { + size_t operator()(const std::set & or_group) const + { + SipHash hash; + for (const auto & atomic_formula : or_group) + { + SipHash atomic_formula_hash; + atomic_formula_hash.update(atomic_formula.negative); + atomic_formula_hash.update(atomic_formula.node_with_hash.hash); + + hash.update(atomic_formula_hash.get64()); + } + + return hash.get64(); + } + }; + + // Different hash is generated for different order, so we use std::set + using OrGroup = std::set; + using AndGroup = std::unordered_set; std::string dump() const; static constexpr size_t DEFAULT_MAX_GROWTH_MULTIPLIER = 20; + static constexpr size_t MAX_ATOMS_WITHOUT_CHECK = 200; + + CNF & transformAtoms(std::function fn); + + /// Convert "NOT fn" to a single node representing inverse of "fn" + CNF & pushNotIntoFunctions(const ContextPtr & context); static std::optional tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier = DEFAULT_MAX_GROWTH_MULTIPLIER); + + QueryTreeNodePtr toQueryTree(ContextPtr context) const; private: + explicit CNF(AndGroup statements_); + + CNF & transformGroups(std::function fn); AndGroup statements; }; - -} -template <> -struct std::hash -{ - size_t operator()(const DB::Analyzer::CNF::AtomicFormula & atomic_formula) const - { - return std::hash()(atomic_formula.node_with_hash); - } -}; +} diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp new file mode 100644 index 00000000000..1c7e9a5753d --- /dev/null +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -0,0 +1,68 @@ +#include + +#include +#include +#include + +namespace DB +{ + +namespace +{ + +bool isLogicalFunction(const FunctionNode & function_node) +{ + const std::string_view name = function_node.getFunctionName(); + return name == "and" || name == "or" || name == "not"; +} + +class ConvertQueryToCNFVisitor : public InDepthQueryTreeVisitorWithContext +{ +public: + using Base = InDepthQueryTreeVisitorWithContext; + using Base::Base; + + bool needChildVisit(VisitQueryTreeNodeType & parent, VisitQueryTreeNodeType &) + { + if (!getSettings().convert_query_to_cnf) + return false; + + auto * function_node = parent->as(); + return !function_node || !isLogicalFunction(*function_node); + } + + void visitImpl(QueryTreeNodePtr & node) + { + if (!getSettings().convert_query_to_cnf) + return; + + auto * function_node = node->as(); + + if (!function_node || !isLogicalFunction(*function_node)) + return; + + const auto & context = getContext(); + auto cnf_form = Analyzer::CNF::tryBuildCNF(node, context); + if (!cnf_form) + return; + + cnf_form->pushNotIntoFunctions(context); + std::cout << "CNF " << cnf_form->dump() << std::endl; + auto new_node = cnf_form->toQueryTree(context); + if (!new_node) + return; + + node = std::move(new_node); + } +}; + +} + +void ConvertQueryToCnfPass::run(QueryTreeNodePtr query_tree_node, ContextPtr context) +{ + ConvertQueryToCNFVisitor visitor(std::move(context)); + visitor.visit(query_tree_node); +} + + +} diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.h b/src/Analyzer/Passes/ConvertQueryToCNFPass.h new file mode 100644 index 00000000000..fd2544ec228 --- /dev/null +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace DB +{ + +class ConvertQueryToCnfPass final : public IQueryTreePass +{ +public: + String getName() override { return "ConvertQueryToCnfPass"; } + + String getDescription() override { return "Convery query to CNF"; } + + void run(QueryTreeNodePtr query_tree_node, ContextPtr context) override; +}; + +} diff --git a/src/Analyzer/QueryTreePassManager.cpp b/src/Analyzer/QueryTreePassManager.cpp index 9c0f2381c31..a6ac79c8702 100644 --- a/src/Analyzer/QueryTreePassManager.cpp +++ b/src/Analyzer/QueryTreePassManager.cpp @@ -41,7 +41,7 @@ #include #include #include - +#include namespace DB { @@ -235,6 +235,8 @@ void addQueryTreePasses(QueryTreePassManager & manager) manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); + manager.addPass(std::make_unique()); + manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); From 0ecce85c4879ee15cbd5362f1ec03844b35865ca Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Tue, 14 Mar 2023 16:37:14 +0000 Subject: [PATCH 032/377] Crutch for 02404_memory_bound_merging --- src/Processors/QueryPlan/MergingAggregatedStep.cpp | 5 +++++ src/Processors/QueryPlan/MergingAggregatedStep.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/Processors/QueryPlan/MergingAggregatedStep.cpp b/src/Processors/QueryPlan/MergingAggregatedStep.cpp index e4fc332a1fd..369254cc1b2 100644 --- a/src/Processors/QueryPlan/MergingAggregatedStep.cpp +++ b/src/Processors/QueryPlan/MergingAggregatedStep.cpp @@ -75,6 +75,9 @@ MergingAggregatedStep::MergingAggregatedStep( void MergingAggregatedStep::applyOrder(SortDescription sort_description, DataStream::SortScope sort_scope) { + is_order_overwritten = true; + overwritten_sort_scope = sort_scope; + auto & input_stream = input_streams.front(); input_stream.sort_scope = sort_scope; input_stream.sort_description = sort_description; @@ -157,6 +160,8 @@ void MergingAggregatedStep::describeActions(JSONBuilder::JSONMap & map) const void MergingAggregatedStep::updateOutputStream() { output_stream = createOutputStream(input_streams.front(), params.getHeader(input_streams.front().header, final), getDataStreamTraits()); + if (is_order_overwritten) /// overwrite order again + applyOrder(group_by_sort_description, overwritten_sort_scope); /// Aggregation keys are distinct for (const auto & key : params.keys) diff --git a/src/Processors/QueryPlan/MergingAggregatedStep.h b/src/Processors/QueryPlan/MergingAggregatedStep.h index 2dea289ca89..e798dabc3ba 100644 --- a/src/Processors/QueryPlan/MergingAggregatedStep.h +++ b/src/Processors/QueryPlan/MergingAggregatedStep.h @@ -50,6 +50,9 @@ private: const size_t memory_bound_merging_max_block_bytes; SortDescription group_by_sort_description; + bool is_order_overwritten = false; + DataStream::SortScope overwritten_sort_scope = DataStream::SortScope::None; + /// These settings are used to determine if we should resize pipeline to 1 at the end. const bool should_produce_results_in_order_of_bucket_number; const bool memory_bound_merging_of_aggregation_results_enabled; From e952ecae8c536d17a91e6071554b7b5f98b06080 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 14 Mar 2023 22:10:52 +0100 Subject: [PATCH 033/377] Add Decimal256 to a lot of places --- .../AggregateFunctionGroupArrayMoving.cpp | 8 +- .../AggregateFunctionStatisticsSimple.cpp | 8 +- .../AggregateFunctionStatisticsSimple.h | 152 ++++++------------ .../AggregateFunctionSum.cpp | 2 +- src/AggregateFunctions/HelpersMinMaxAny.h | 6 + src/AggregateFunctions/Moments.h | 103 ------------ .../QuantileExactWeighted.h | 2 +- .../QuantileInterpolatedWeighted.h | 2 +- src/Client/QueryFuzzer.cpp | 5 +- src/Core/PostgreSQLProtocol.cpp | 1 + src/DataTypes/DataTypesDecimal.cpp | 6 +- src/DataTypes/convertMySQLDataType.cpp | 2 + src/DataTypes/getLeastSupertype.cpp | 16 +- src/DataTypes/getMostSubtype.cpp | 18 +++ .../FunctionsBinaryRepresentation.cpp | 1 + src/Functions/FunctionsExternalDictionaries.h | 12 -- src/Functions/FunctionsJSON.cpp | 30 ---- src/Functions/FunctionsRound.h | 3 +- src/Functions/array/arrayAggregation.cpp | 15 +- src/Functions/array/arrayCompact.cpp | 4 +- src/Functions/array/arrayCumSum.cpp | 10 +- .../array/arrayCumSumNonNegative.cpp | 10 +- src/Functions/array/arrayDifference.cpp | 4 +- src/Interpreters/convertFieldToType.cpp | 2 + 24 files changed, 137 insertions(+), 285 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp b/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp index 5ee0be5cbce..14ba4d27cec 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp +++ b/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp @@ -25,14 +25,18 @@ namespace template struct MovingSum { - using Data = MovingSumData, Decimal128, NearestFieldType>>; + using Data = MovingSumData, + std::conditional_t, + NearestFieldType>>; using Function = MovingImpl; }; template struct MovingAvg { - using Data = MovingAvgData, Decimal128, Float64>>; + using Data = MovingAvgData, + std::conditional_t, + Float64>>; using Function = MovingImpl; }; diff --git a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.cpp b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.cpp index d06c1619b9f..a22d783de4c 100644 --- a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.cpp +++ b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.cpp @@ -23,12 +23,8 @@ AggregateFunctionPtr createAggregateFunctionStatisticsUnary( assertNoParameters(name, parameters); assertUnary(name, argument_types); - AggregateFunctionPtr res; const DataTypePtr & data_type = argument_types[0]; - if (isDecimal(data_type)) - res.reset(createWithDecimalType(*data_type, *data_type, argument_types)); - else - res.reset(createWithNumericType(*data_type, argument_types)); + AggregateFunctionPtr res{createWithNumericType(*data_type, argument_types)}; if (!res) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument for aggregate function {}", @@ -68,7 +64,7 @@ void registerAggregateFunctionsStatisticsSimple(AggregateFunctionFactory & facto factory.registerFunction("covarPop", createAggregateFunctionStatisticsBinary); factory.registerFunction("corr", createAggregateFunctionStatisticsBinary, AggregateFunctionFactory::CaseInsensitive); - /// Synonims for compatibility. + /// Synonyms for compatibility. factory.registerAlias("VAR_SAMP", "varSamp", AggregateFunctionFactory::CaseInsensitive); factory.registerAlias("VAR_POP", "varPop", AggregateFunctionFactory::CaseInsensitive); factory.registerAlias("STDDEV_SAMP", "stddevSamp", AggregateFunctionFactory::CaseInsensitive); diff --git a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h index 9ef62363a75..66ecc6eecd6 100644 --- a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h +++ b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h @@ -49,7 +49,7 @@ struct StatFuncOneArg using Type1 = T; using Type2 = T; using ResultType = std::conditional_t, Float32, Float64>; - using Data = std::conditional_t, VarMomentsDecimal, VarMoments>; + using Data = VarMoments; static constexpr StatisticsFunctionKind kind = _kind; static constexpr UInt32 num_args = 1; @@ -82,13 +82,8 @@ public: explicit AggregateFunctionVarianceSimple(const DataTypes & argument_types_) : IAggregateFunctionDataHelper>(argument_types_, {}, std::make_shared>()) - , src_scale(0) - {} - - AggregateFunctionVarianceSimple(const IDataType & data_type, const DataTypes & argument_types_) - : IAggregateFunctionDataHelper>(argument_types_, {}, std::make_shared>()) - , src_scale(getDecimalScale(data_type)) - {} + { + } String getName() const override { @@ -158,110 +153,57 @@ public: const auto & data = this->data(place); auto & dst = static_cast(to).getData(); - if constexpr (is_decimal) + if constexpr (StatFunc::kind == StatisticsFunctionKind::varPop) + dst.push_back(data.getPopulation()); + if constexpr (StatFunc::kind == StatisticsFunctionKind::varSamp) + dst.push_back(data.getSample()); + if constexpr (StatFunc::kind == StatisticsFunctionKind::stddevPop) + dst.push_back(sqrt(data.getPopulation())); + if constexpr (StatFunc::kind == StatisticsFunctionKind::stddevSamp) + dst.push_back(sqrt(data.getSample())); + if constexpr (StatFunc::kind == StatisticsFunctionKind::skewPop) { - if constexpr (StatFunc::kind == StatisticsFunctionKind::varPop) - dst.push_back(data.getPopulation(src_scale * 2)); - if constexpr (StatFunc::kind == StatisticsFunctionKind::varSamp) - dst.push_back(data.getSample(src_scale * 2)); - if constexpr (StatFunc::kind == StatisticsFunctionKind::stddevPop) - dst.push_back(sqrt(data.getPopulation(src_scale * 2))); - if constexpr (StatFunc::kind == StatisticsFunctionKind::stddevSamp) - dst.push_back(sqrt(data.getSample(src_scale * 2))); - if constexpr (StatFunc::kind == StatisticsFunctionKind::skewPop) - { - Float64 var_value = data.getPopulation(src_scale * 2); + ResultType var_value = data.getPopulation(); - if (var_value > 0) - dst.push_back(data.getMoment3(src_scale * 3) / pow(var_value, 1.5)); - else - dst.push_back(std::numeric_limits::quiet_NaN()); - } - if constexpr (StatFunc::kind == StatisticsFunctionKind::skewSamp) - { - Float64 var_value = data.getSample(src_scale * 2); - - if (var_value > 0) - dst.push_back(data.getMoment3(src_scale * 3) / pow(var_value, 1.5)); - else - dst.push_back(std::numeric_limits::quiet_NaN()); - } - if constexpr (StatFunc::kind == StatisticsFunctionKind::kurtPop) - { - Float64 var_value = data.getPopulation(src_scale * 2); - - if (var_value > 0) - dst.push_back(data.getMoment4(src_scale * 4) / pow(var_value, 2)); - else - dst.push_back(std::numeric_limits::quiet_NaN()); - } - if constexpr (StatFunc::kind == StatisticsFunctionKind::kurtSamp) - { - Float64 var_value = data.getSample(src_scale * 2); - - if (var_value > 0) - dst.push_back(data.getMoment4(src_scale * 4) / pow(var_value, 2)); - else - dst.push_back(std::numeric_limits::quiet_NaN()); - } + if (var_value > 0) + dst.push_back(static_cast(data.getMoment3() / pow(var_value, 1.5))); + else + dst.push_back(std::numeric_limits::quiet_NaN()); } - else + if constexpr (StatFunc::kind == StatisticsFunctionKind::skewSamp) { - if constexpr (StatFunc::kind == StatisticsFunctionKind::varPop) - dst.push_back(data.getPopulation()); - if constexpr (StatFunc::kind == StatisticsFunctionKind::varSamp) - dst.push_back(data.getSample()); - if constexpr (StatFunc::kind == StatisticsFunctionKind::stddevPop) - dst.push_back(sqrt(data.getPopulation())); - if constexpr (StatFunc::kind == StatisticsFunctionKind::stddevSamp) - dst.push_back(sqrt(data.getSample())); - if constexpr (StatFunc::kind == StatisticsFunctionKind::skewPop) - { - ResultType var_value = data.getPopulation(); + ResultType var_value = data.getSample(); - if (var_value > 0) - dst.push_back(static_cast(data.getMoment3() / pow(var_value, 1.5))); - else - dst.push_back(std::numeric_limits::quiet_NaN()); - } - if constexpr (StatFunc::kind == StatisticsFunctionKind::skewSamp) - { - ResultType var_value = data.getSample(); - - if (var_value > 0) - dst.push_back(static_cast(data.getMoment3() / pow(var_value, 1.5))); - else - dst.push_back(std::numeric_limits::quiet_NaN()); - } - if constexpr (StatFunc::kind == StatisticsFunctionKind::kurtPop) - { - ResultType var_value = data.getPopulation(); - - if (var_value > 0) - dst.push_back(static_cast(data.getMoment4() / pow(var_value, 2))); - else - dst.push_back(std::numeric_limits::quiet_NaN()); - } - if constexpr (StatFunc::kind == StatisticsFunctionKind::kurtSamp) - { - ResultType var_value = data.getSample(); - - if (var_value > 0) - dst.push_back(static_cast(data.getMoment4() / pow(var_value, 2))); - else - dst.push_back(std::numeric_limits::quiet_NaN()); - } - if constexpr (StatFunc::kind == StatisticsFunctionKind::covarPop) - dst.push_back(data.getPopulation()); - if constexpr (StatFunc::kind == StatisticsFunctionKind::covarSamp) - dst.push_back(data.getSample()); - if constexpr (StatFunc::kind == StatisticsFunctionKind::corr) - dst.push_back(data.get()); + if (var_value > 0) + dst.push_back(static_cast(data.getMoment3() / pow(var_value, 1.5))); + else + dst.push_back(std::numeric_limits::quiet_NaN()); } + if constexpr (StatFunc::kind == StatisticsFunctionKind::kurtPop) + { + ResultType var_value = data.getPopulation(); + + if (var_value > 0) + dst.push_back(static_cast(data.getMoment4() / pow(var_value, 2))); + else + dst.push_back(std::numeric_limits::quiet_NaN()); + } + if constexpr (StatFunc::kind == StatisticsFunctionKind::kurtSamp) + { + ResultType var_value = data.getSample(); + + if (var_value > 0) + dst.push_back(static_cast(data.getMoment4() / pow(var_value, 2))); + else + dst.push_back(std::numeric_limits::quiet_NaN()); + } + if constexpr (StatFunc::kind == StatisticsFunctionKind::covarPop) + dst.push_back(data.getPopulation()); + if constexpr (StatFunc::kind == StatisticsFunctionKind::covarSamp) + dst.push_back(data.getSample()); + if constexpr (StatFunc::kind == StatisticsFunctionKind::corr) + dst.push_back(data.get()); } - -private: - UInt32 src_scale; }; diff --git a/src/AggregateFunctions/AggregateFunctionSum.cpp b/src/AggregateFunctions/AggregateFunctionSum.cpp index 4f2a935d9e5..e393cb6dd38 100644 --- a/src/AggregateFunctions/AggregateFunctionSum.cpp +++ b/src/AggregateFunctions/AggregateFunctionSum.cpp @@ -19,7 +19,7 @@ namespace template struct SumSimple { - /// @note It uses slow Decimal128 (cause we need such a variant). sumWithOverflow is faster for Decimal32/64 + /// @note It uses slow Decimal128/256 (cause we need such a variant). sumWithOverflow is faster for Decimal32/64 using ResultType = std::conditional_t, std::conditional_t, Decimal256, Decimal128>, NearestFieldType>; diff --git a/src/AggregateFunctions/HelpersMinMaxAny.h b/src/AggregateFunctions/HelpersMinMaxAny.h index 1af51c3f8e6..026a206b109 100644 --- a/src/AggregateFunctions/HelpersMinMaxAny.h +++ b/src/AggregateFunctions/HelpersMinMaxAny.h @@ -41,6 +41,8 @@ static IAggregateFunction * createAggregateFunctionSingleValue(const String & na return new AggregateFunctionTemplate>>(argument_type); if (which.idx == TypeIndex::Decimal128) return new AggregateFunctionTemplate>>(argument_type); + if (which.idx == TypeIndex::Decimal256) + return new AggregateFunctionTemplate>>(argument_type); if (which.idx == TypeIndex::String) return new AggregateFunctionTemplate>(argument_type); @@ -72,6 +74,8 @@ static IAggregateFunction * createAggregateFunctionArgMinMaxSecond(const DataTyp return new AggregateFunctionArgMinMax>>>(res_type, val_type); if (which.idx == TypeIndex::Decimal128) return new AggregateFunctionArgMinMax>>>(res_type, val_type); + if (which.idx == TypeIndex::Decimal256) + return new AggregateFunctionArgMinMax>>>(res_type, val_type); if (which.idx == TypeIndex::String) return new AggregateFunctionArgMinMax>>(res_type, val_type); @@ -106,6 +110,8 @@ static IAggregateFunction * createAggregateFunctionArgMinMax(const String & name return createAggregateFunctionArgMinMaxSecond>(res_type, val_type); if (which.idx == TypeIndex::Decimal128) return createAggregateFunctionArgMinMaxSecond>(res_type, val_type); + if (which.idx == TypeIndex::Decimal256) + return createAggregateFunctionArgMinMaxSecond>(res_type, val_type); if (which.idx == TypeIndex::String) return createAggregateFunctionArgMinMaxSecond(res_type, val_type); diff --git a/src/AggregateFunctions/Moments.h b/src/AggregateFunctions/Moments.h index 0466d01fe79..1fe97f0fe46 100644 --- a/src/AggregateFunctions/Moments.h +++ b/src/AggregateFunctions/Moments.h @@ -115,109 +115,6 @@ struct VarMoments } }; -template -class VarMomentsDecimal -{ -public: - using NativeType = typename T::NativeType; - - void add(NativeType x) - { - ++m0; - getM(1) += x; - - NativeType tmp; - bool overflow = common::mulOverflow(x, x, tmp) || common::addOverflow(getM(2), tmp, getM(2)); - if constexpr (_level >= 3) - overflow = overflow || common::mulOverflow(tmp, x, tmp) || common::addOverflow(getM(3), tmp, getM(3)); - if constexpr (_level >= 4) - overflow = overflow || common::mulOverflow(tmp, x, tmp) || common::addOverflow(getM(4), tmp, getM(4)); - - if (overflow) - throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Decimal math overflow"); - } - - void merge(const VarMomentsDecimal & rhs) - { - m0 += rhs.m0; - getM(1) += rhs.getM(1); - - bool overflow = common::addOverflow(getM(2), rhs.getM(2), getM(2)); - if constexpr (_level >= 3) - overflow = overflow || common::addOverflow(getM(3), rhs.getM(3), getM(3)); - if constexpr (_level >= 4) - overflow = overflow || common::addOverflow(getM(4), rhs.getM(4), getM(4)); - - if (overflow) - throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Decimal math overflow"); - } - - void write(WriteBuffer & buf) const { writePODBinary(*this, buf); } - void read(ReadBuffer & buf) { readPODBinary(*this, buf); } - - Float64 getPopulation(UInt32 scale) const - { - if (m0 == 0) - return std::numeric_limits::infinity(); - - NativeType tmp; - if (common::mulOverflow(getM(1), getM(1), tmp) || - common::subOverflow(getM(2), NativeType(tmp / m0), tmp)) - throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Decimal math overflow"); - return std::max(Float64{}, DecimalUtils::convertTo(T(tmp / m0), scale)); - } - - Float64 getSample(UInt32 scale) const - { - if (m0 == 0) - return std::numeric_limits::quiet_NaN(); - if (m0 == 1) - return std::numeric_limits::infinity(); - - NativeType tmp; - if (common::mulOverflow(getM(1), getM(1), tmp) || - common::subOverflow(getM(2), NativeType(tmp / m0), tmp)) - throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Decimal math overflow"); - return std::max(Float64{}, DecimalUtils::convertTo(T(tmp / (m0 - 1)), scale)); - } - - Float64 getMoment3(UInt32 scale) const - { - if (m0 == 0) - return std::numeric_limits::infinity(); - - NativeType tmp; - if (common::mulOverflow(2 * getM(1), getM(1), tmp) || - common::subOverflow(3 * getM(2), NativeType(tmp / m0), tmp) || - common::mulOverflow(tmp, getM(1), tmp) || - common::subOverflow(getM(3), NativeType(tmp / m0), tmp)) - throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Decimal math overflow"); - return DecimalUtils::convertTo(T(tmp / m0), scale); - } - - Float64 getMoment4(UInt32 scale) const - { - if (m0 == 0) - return std::numeric_limits::infinity(); - - NativeType tmp; - if (common::mulOverflow(3 * getM(1), getM(1), tmp) || - common::subOverflow(6 * getM(2), NativeType(tmp / m0), tmp) || - common::mulOverflow(tmp, getM(1), tmp) || - common::subOverflow(4 * getM(3), NativeType(tmp / m0), tmp) || - common::mulOverflow(tmp, getM(1), tmp) || - common::subOverflow(getM(4), NativeType(tmp / m0), tmp)) - throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Decimal math overflow"); - return DecimalUtils::convertTo(T(tmp / m0), scale); - } - -private: - UInt64 m0{}; - NativeType m[_level]{}; - - NativeType & getM(size_t i) { return m[i - 1]; } - const NativeType & getM(size_t i) const { return m[i - 1]; } -}; /** Calculating multivariate central moments diff --git a/src/AggregateFunctions/QuantileExactWeighted.h b/src/AggregateFunctions/QuantileExactWeighted.h index 48ba253cb95..c6a779ede61 100644 --- a/src/AggregateFunctions/QuantileExactWeighted.h +++ b/src/AggregateFunctions/QuantileExactWeighted.h @@ -33,7 +33,7 @@ struct QuantileExactWeighted using Weight = UInt64; using UnderlyingType = NativeType; - using Hasher = std::conditional_t, Int128Hash, HashCRC32>; + using Hasher = HashCRC32; /// When creating, the hash table must be small. using Map = HashMapWithStackMemory; diff --git a/src/AggregateFunctions/QuantileInterpolatedWeighted.h b/src/AggregateFunctions/QuantileInterpolatedWeighted.h index eef4f566889..5b1eb315af3 100644 --- a/src/AggregateFunctions/QuantileInterpolatedWeighted.h +++ b/src/AggregateFunctions/QuantileInterpolatedWeighted.h @@ -34,7 +34,7 @@ struct QuantileInterpolatedWeighted using Weight = UInt64; using UnderlyingType = NativeType; - using Hasher = std::conditional_t, Int128Hash, HashCRC32>; + using Hasher = HashCRC32; /// When creating, the hash table must be small. using Map = HashMapWithStackMemory; diff --git a/src/Client/QueryFuzzer.cpp b/src/Client/QueryFuzzer.cpp index e150717db95..411a48b74c2 100644 --- a/src/Client/QueryFuzzer.cpp +++ b/src/Client/QueryFuzzer.cpp @@ -107,8 +107,9 @@ Field QueryFuzzer::fuzzField(Field field) type_index = 1; } else if (type == Field::Types::Decimal32 - || type == Field::Types::Decimal64 - || type == Field::Types::Decimal128) + || type == Field::Types::Decimal64 + || type == Field::Types::Decimal128 + || type == Field::Types::Decimal256) { type_index = 2; } diff --git a/src/Core/PostgreSQLProtocol.cpp b/src/Core/PostgreSQLProtocol.cpp index 553d195605a..5a96ed7a500 100644 --- a/src/Core/PostgreSQLProtocol.cpp +++ b/src/Core/PostgreSQLProtocol.cpp @@ -37,6 +37,7 @@ ColumnTypeSpec convertTypeIndexToPostgresColumnTypeSpec(TypeIndex type_index) case TypeIndex::Decimal32: case TypeIndex::Decimal64: case TypeIndex::Decimal128: + case TypeIndex::Decimal256: return {ColumnType::NUMERIC, -1}; case TypeIndex::UUID: diff --git a/src/DataTypes/DataTypesDecimal.cpp b/src/DataTypes/DataTypesDecimal.cpp index 1ae0d8a6db1..6b9a9a29588 100644 --- a/src/DataTypes/DataTypesDecimal.cpp +++ b/src/DataTypes/DataTypesDecimal.cpp @@ -40,8 +40,10 @@ bool DataTypeDecimal::equals(const IDataType & rhs) const template DataTypePtr DataTypeDecimal::promoteNumericType() const { - using PromotedType = DataTypeDecimal; - return std::make_shared(PromotedType::maxPrecision(), this->scale); + if (sizeof(T) <= sizeof(Decimal128)) + return std::make_shared>(DataTypeDecimal::maxPrecision(), this->scale); + else + return std::make_shared>(DataTypeDecimal::maxPrecision(), this->scale); } template diff --git a/src/DataTypes/convertMySQLDataType.cpp b/src/DataTypes/convertMySQLDataType.cpp index db064a7f06f..bb848bf1526 100644 --- a/src/DataTypes/convertMySQLDataType.cpp +++ b/src/DataTypes/convertMySQLDataType.cpp @@ -115,6 +115,8 @@ DataTypePtr convertMySQLDataType(MultiEnum type_support, res = std::make_shared>(precision, scale); else if (precision <= DecimalUtils::max_precision) res = std::make_shared>(precision, scale); + else if (precision <= DecimalUtils::max_precision) + res = std::make_shared>(precision, scale); } /// Also String is fallback for all unknown types. diff --git a/src/DataTypes/getLeastSupertype.cpp b/src/DataTypes/getLeastSupertype.cpp index b50aeb76e67..ba43def73f7 100644 --- a/src/DataTypes/getLeastSupertype.cpp +++ b/src/DataTypes/getLeastSupertype.cpp @@ -528,10 +528,11 @@ DataTypePtr getLeastSupertype(const DataTypes & types) size_t have_decimal32 = type_ids.count(TypeIndex::Decimal32); size_t have_decimal64 = type_ids.count(TypeIndex::Decimal64); size_t have_decimal128 = type_ids.count(TypeIndex::Decimal128); + size_t have_decimal256 = type_ids.count(TypeIndex::Decimal256); - if (have_decimal32 || have_decimal64 || have_decimal128) + if (have_decimal32 || have_decimal64 || have_decimal128 || have_decimal256) { - size_t num_supported = have_decimal32 + have_decimal64 + have_decimal128; + size_t num_supported = have_decimal32 + have_decimal64 + have_decimal128 + have_decimal256; std::vector int_ids = {TypeIndex::Int8, TypeIndex::UInt8, TypeIndex::Int16, TypeIndex::UInt16, TypeIndex::Int32, TypeIndex::UInt32, TypeIndex::Int64, TypeIndex::UInt64}; @@ -552,8 +553,13 @@ DataTypePtr getLeastSupertype(const DataTypes & types) for (const auto & type : types) { auto type_id = type->getTypeId(); - if (type_id != TypeIndex::Decimal32 && type_id != TypeIndex::Decimal64 && type_id != TypeIndex::Decimal128) + if (type_id != TypeIndex::Decimal32 + && type_id != TypeIndex::Decimal64 + && type_id != TypeIndex::Decimal128 + && type_id != TypeIndex::Decimal256) + { continue; + } UInt32 scale = getDecimalScale(*type); if (scale > max_scale) @@ -571,11 +577,13 @@ DataTypePtr getLeastSupertype(const DataTypes & types) min_precision = DataTypeDecimal::maxPrecision(); } - if (min_precision > DataTypeDecimal::maxPrecision()) + if (min_precision > DataTypeDecimal::maxPrecision()) return throwOrReturn(types, "because the least supertype is Decimal(" + toString(min_precision) + ',' + toString(max_scale) + ')', ErrorCodes::NO_COMMON_TYPE); + if (have_decimal256 || min_precision > DataTypeDecimal::maxPrecision()) + return std::make_shared>(DataTypeDecimal::maxPrecision(), max_scale); if (have_decimal128 || min_precision > DataTypeDecimal::maxPrecision()) return std::make_shared>(DataTypeDecimal::maxPrecision(), max_scale); if (have_decimal64 || min_precision > DataTypeDecimal::maxPrecision()) diff --git a/src/DataTypes/getMostSubtype.cpp b/src/DataTypes/getMostSubtype.cpp index e5d529205d2..95a80d79a38 100644 --- a/src/DataTypes/getMostSubtype.cpp +++ b/src/DataTypes/getMostSubtype.cpp @@ -280,6 +280,10 @@ DataTypePtr getMostSubtype(const DataTypes & types, bool throw_if_result_is_noth minimize(min_bits_of_unsigned_integer, 32); else if (typeid_cast(type.get())) minimize(min_bits_of_unsigned_integer, 64); + else if (typeid_cast(type.get())) + minimize(min_bits_of_unsigned_integer, 128); + else if (typeid_cast(type.get())) + minimize(min_bits_of_unsigned_integer, 256); else if (typeid_cast(type.get())) minimize(min_bits_of_signed_integer, 8); else if (typeid_cast(type.get())) @@ -288,6 +292,10 @@ DataTypePtr getMostSubtype(const DataTypes & types, bool throw_if_result_is_noth minimize(min_bits_of_signed_integer, 32); else if (typeid_cast(type.get())) minimize(min_bits_of_signed_integer, 64); + else if (typeid_cast(type.get())) + minimize(min_bits_of_signed_integer, 128); + else if (typeid_cast(type.get())) + minimize(min_bits_of_signed_integer, 256); else if (typeid_cast(type.get())) minimize(min_mantissa_bits_of_floating, 24); else if (typeid_cast(type.get())) @@ -326,6 +334,10 @@ DataTypePtr getMostSubtype(const DataTypes & types, bool throw_if_result_is_noth return std::make_shared(); else if (min_bits_of_unsigned_integer <= 64) return std::make_shared(); + else if (min_bits_of_unsigned_integer <= 128) + return std::make_shared(); + else if (min_bits_of_unsigned_integer <= 256) + return std::make_shared(); else throw Exception(ErrorCodes::NO_COMMON_TYPE, "Logical error: {} but as all data types are integers, " @@ -343,6 +355,10 @@ DataTypePtr getMostSubtype(const DataTypes & types, bool throw_if_result_is_noth return std::make_shared(); else if (min_bits_of_signed_integer <= 64) return std::make_shared(); + else if (min_bits_of_signed_integer <= 128) + return std::make_shared(); + else if (min_bits_of_signed_integer <= 256) + return std::make_shared(); else throw Exception(ErrorCodes::NO_COMMON_TYPE, "Logical error: {} but as all data types are integers, " @@ -351,6 +367,8 @@ DataTypePtr getMostSubtype(const DataTypes & types, bool throw_if_result_is_noth } } + /// TODO: Decimals + /// All other data types (UUID, AggregateFunction, Enum...) are compatible only if they are the same (checked in trivial cases). return get_nothing_or_throw(""); } diff --git a/src/Functions/FunctionsBinaryRepresentation.cpp b/src/Functions/FunctionsBinaryRepresentation.cpp index d44323f8bf3..c3a8f51ee4b 100644 --- a/src/Functions/FunctionsBinaryRepresentation.cpp +++ b/src/Functions/FunctionsBinaryRepresentation.cpp @@ -299,6 +299,7 @@ public: tryExecuteDecimal(column, res_column) || tryExecuteDecimal(column, res_column) || tryExecuteDecimal(column, res_column) || + tryExecuteDecimal(column, res_column) || tryExecuteUUID(column, res_column) || tryExecuteIPv4(column, res_column) || tryExecuteIPv6(column, res_column)) diff --git a/src/Functions/FunctionsExternalDictionaries.h b/src/Functions/FunctionsExternalDictionaries.h index d3be5b1200b..f0dd5553f5e 100644 --- a/src/Functions/FunctionsExternalDictionaries.h +++ b/src/Functions/FunctionsExternalDictionaries.h @@ -721,9 +721,6 @@ struct NameDictGetDateTime { static constexpr auto name = "dictGetDateTime"; }; struct NameDictGetUUID { static constexpr auto name = "dictGetUUID"; }; struct NameDictGetIPv4 { static constexpr auto name = "dictGetIPv4"; }; struct NameDictGetIPv6 { static constexpr auto name = "dictGetIPv6"; }; -struct NameDictGetDecimal32 { static constexpr auto name = "dictGetDecimal32"; }; -struct NameDictGetDecimal64 { static constexpr auto name = "dictGetDecimal64"; }; -struct NameDictGetDecimal128 { static constexpr auto name = "dictGetDecimal128"; }; struct NameDictGetString { static constexpr auto name = "dictGetString"; }; using FunctionDictGetUInt8 = FunctionDictGet; @@ -741,9 +738,6 @@ using FunctionDictGetDateTime = FunctionDictGet; using FunctionDictGetIPv4 = FunctionDictGet; using FunctionDictGetIPv6 = FunctionDictGet; -using FunctionDictGetDecimal32 = FunctionDictGet, NameDictGetDecimal32>; -using FunctionDictGetDecimal64 = FunctionDictGet, NameDictGetDecimal64>; -using FunctionDictGetDecimal128 = FunctionDictGet, NameDictGetDecimal128>; using FunctionDictGetString = FunctionDictGet; template @@ -764,9 +758,6 @@ struct NameDictGetDateTimeOrDefault { static constexpr auto name = "dictGetDateT struct NameDictGetUUIDOrDefault { static constexpr auto name = "dictGetUUIDOrDefault"; }; struct NameDictGetIPv4OrDefault { static constexpr auto name = "dictGetIPv4OrDefault"; }; struct NameDictGetIPv6OrDefault { static constexpr auto name = "dictGetIPv6OrDefault"; }; -struct NameDictGetDecimal32OrDefault { static constexpr auto name = "dictGetDecimal32OrDefault"; }; -struct NameDictGetDecimal64OrDefault { static constexpr auto name = "dictGetDecimal64OrDefault"; }; -struct NameDictGetDecimal128OrDefault { static constexpr auto name = "dictGetDecimal128OrDefault"; }; struct NameDictGetStringOrDefault { static constexpr auto name = "dictGetStringOrDefault"; }; using FunctionDictGetUInt8OrDefault = FunctionDictGetOrDefault; @@ -784,9 +775,6 @@ using FunctionDictGetDateTimeOrDefault = FunctionDictGetOrDefault; using FunctionDictGetIPv4OrDefault = FunctionDictGetOrDefault; using FunctionDictGetIPv6OrDefault = FunctionDictGetOrDefault; -using FunctionDictGetDecimal32OrDefault = FunctionDictGetOrDefault, NameDictGetDecimal32OrDefault>; -using FunctionDictGetDecimal64OrDefault = FunctionDictGetOrDefault, NameDictGetDecimal64OrDefault>; -using FunctionDictGetDecimal128OrDefault = FunctionDictGetOrDefault, NameDictGetDecimal128OrDefault>; using FunctionDictGetStringOrDefault = FunctionDictGetOrDefault; class FunctionDictGetOrNull final : public IFunction diff --git a/src/Functions/FunctionsJSON.cpp b/src/Functions/FunctionsJSON.cpp index 5df0d1831af..8bcb1f4d849 100644 --- a/src/Functions/FunctionsJSON.cpp +++ b/src/Functions/FunctionsJSON.cpp @@ -750,42 +750,12 @@ public: }; -template -using JSONExtractInt8Impl = JSONExtractNumericImpl; -template -using JSONExtractUInt8Impl = JSONExtractNumericImpl; -template -using JSONExtractInt16Impl = JSONExtractNumericImpl; -template -using JSONExtractUInt16Impl = JSONExtractNumericImpl; -template -using JSONExtractInt32Impl = JSONExtractNumericImpl; -template -using JSONExtractUInt32Impl = JSONExtractNumericImpl; template using JSONExtractInt64Impl = JSONExtractNumericImpl; template using JSONExtractUInt64Impl = JSONExtractNumericImpl; template -using JSONExtractInt128Impl = JSONExtractNumericImpl; -template -using JSONExtractUInt128Impl = JSONExtractNumericImpl; -template -using JSONExtractInt256Impl = JSONExtractNumericImpl; -template -using JSONExtractUInt256Impl = JSONExtractNumericImpl; -template -using JSONExtractFloat32Impl = JSONExtractNumericImpl; -template using JSONExtractFloat64Impl = JSONExtractNumericImpl; -template -using JSONExtractDecimal32Impl = JSONExtractNumericImpl; -template -using JSONExtractDecimal64Impl = JSONExtractNumericImpl; -template -using JSONExtractDecimal128Impl = JSONExtractNumericImpl; -template -using JSONExtractDecimal256Impl = JSONExtractNumericImpl; template diff --git a/src/Functions/FunctionsRound.h b/src/Functions/FunctionsRound.h index 2f2a0c3a1a9..3d1028c6d35 100644 --- a/src/Functions/FunctionsRound.h +++ b/src/Functions/FunctionsRound.h @@ -703,7 +703,8 @@ public: && !executeNum(in, out, boundaries) && !executeDecimal(in, out, boundaries) && !executeDecimal(in, out, boundaries) - && !executeDecimal(in, out, boundaries)) + && !executeDecimal(in, out, boundaries) + && !executeDecimal(in, out, boundaries)) { throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function {}", in->getName(), getName()); } diff --git a/src/Functions/array/arrayAggregation.cpp b/src/Functions/array/arrayAggregation.cpp index 75ea3a81a7d..59991b7b313 100644 --- a/src/Functions/array/arrayAggregation.cpp +++ b/src/Functions/array/arrayAggregation.cpp @@ -38,7 +38,7 @@ enum class AggregateOperation * During array aggregation we derive result type from operation. * For array min or array max we use array element as result type. * For array average we use Float64. - * For array sum for for big integers, we use same type representation, decimal numbers we use Decimal128, + * For array sum for big integers, we use same type representation, decimal numbers up to 128-bit will use Decimal128, then Decimal256. * for floating point numbers Float64, for numeric unsigned Int64, and for numeric signed UInt64. */ @@ -77,10 +77,13 @@ struct ArrayAggregateResultImpl std::conditional_t, UInt128, std::conditional_t, Int256, std::conditional_t, UInt256, - std::conditional_t, Decimal128, + std::conditional_t, Decimal128, + std::conditional_t, Decimal128, + std::conditional_t, Decimal128, + std::conditional_t, Decimal256, std::conditional_t, Float64, std::conditional_t, Int64, - UInt64>>>>>>>; + UInt64>>>>>>>>>>; }; template @@ -364,8 +367,11 @@ struct ArrayAggregateImpl executeType(mapped, offsets, res) || executeType(mapped, offsets, res) || executeType(mapped, offsets, res) || - executeType(mapped, offsets, res)) + executeType(mapped, offsets, res) || + executeType(mapped, offsets, res)) + { return res; + } else throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Unexpected column for arraySum: {}", mapped->getName()); } @@ -396,4 +402,3 @@ REGISTER_FUNCTION(ArrayAggregation) } } - diff --git a/src/Functions/array/arrayCompact.cpp b/src/Functions/array/arrayCompact.cpp index 5c899d4d97e..15df9f0d008 100644 --- a/src/Functions/array/arrayCompact.cpp +++ b/src/Functions/array/arrayCompact.cpp @@ -151,7 +151,8 @@ struct ArrayCompactImpl executeType(mapped, array, res)) || executeType(mapped, array, res) || executeType(mapped, array, res) || - executeType(mapped, array, res)) + executeType(mapped, array, res) || + executeType(mapped, array, res)) { executeGeneric(mapped, array, res); } @@ -168,4 +169,3 @@ REGISTER_FUNCTION(ArrayCompact) } } - diff --git a/src/Functions/array/arrayCumSum.cpp b/src/Functions/array/arrayCumSum.cpp index 489014ebe55..b1077bfb18b 100644 --- a/src/Functions/array/arrayCumSum.cpp +++ b/src/Functions/array/arrayCumSum.cpp @@ -41,7 +41,11 @@ struct ArrayCumSumImpl if (which.isDecimal()) { UInt32 scale = getDecimalScale(*expression_return); - DataTypePtr nested = std::make_shared>(DecimalUtils::max_precision, scale); + DataTypePtr nested; + if (which.isDecimal256()) + nested = std::make_shared>(DecimalUtils::max_precision, scale); + else + nested = std::make_shared>(DecimalUtils::max_precision, scale); return std::make_shared(nested); } @@ -151,7 +155,8 @@ struct ArrayCumSumImpl executeType(mapped, array, res) || executeType(mapped, array, res) || executeType(mapped, array, res) || - executeType(mapped, array, res)) + executeType(mapped, array, res) || + executeType(mapped, array, res)) return res; else throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Unexpected column for arrayCumSum: {}", mapped->getName()); @@ -168,4 +173,3 @@ REGISTER_FUNCTION(ArrayCumSum) } } - diff --git a/src/Functions/array/arrayCumSumNonNegative.cpp b/src/Functions/array/arrayCumSumNonNegative.cpp index c0062fd8230..6b20ad35afc 100644 --- a/src/Functions/array/arrayCumSumNonNegative.cpp +++ b/src/Functions/array/arrayCumSumNonNegative.cpp @@ -42,7 +42,11 @@ struct ArrayCumSumNonNegativeImpl if (which.isDecimal()) { UInt32 scale = getDecimalScale(*expression_return); - DataTypePtr nested = std::make_shared>(DecimalUtils::max_precision, scale); + DataTypePtr nested; + if (which.isDecimal256()) + nested = std::make_shared>(DecimalUtils::max_precision, scale); + else + nested = std::make_shared>(DecimalUtils::max_precision, scale); return std::make_shared(nested); } @@ -116,7 +120,8 @@ struct ArrayCumSumNonNegativeImpl executeType(mapped, array, res) || executeType(mapped, array, res) || executeType(mapped, array, res) || - executeType(mapped, array, res)) + executeType(mapped, array, res) || + executeType(mapped, array, res)) return res; else throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Unexpected column for arrayCumSumNonNegativeImpl: {}", mapped->getName()); @@ -133,4 +138,3 @@ REGISTER_FUNCTION(ArrayCumSumNonNegative) } } - diff --git a/src/Functions/array/arrayDifference.cpp b/src/Functions/array/arrayDifference.cpp index ab7b94f98f1..2852c5b967c 100644 --- a/src/Functions/array/arrayDifference.cpp +++ b/src/Functions/array/arrayDifference.cpp @@ -145,7 +145,8 @@ struct ArrayDifferenceImpl executeType(mapped, array, res) || executeType(mapped, array, res) || executeType(mapped, array, res) || - executeType(mapped, array, res)) + executeType(mapped, array, res) || + executeType(mapped, array, res)) return res; else throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Unexpected column for arrayDifference: {}", mapped->getName()); @@ -161,4 +162,3 @@ REGISTER_FUNCTION(ArrayDifference) } } - diff --git a/src/Interpreters/convertFieldToType.cpp b/src/Interpreters/convertFieldToType.cpp index 9e4f543db43..1e69a73e9f2 100644 --- a/src/Interpreters/convertFieldToType.cpp +++ b/src/Interpreters/convertFieldToType.cpp @@ -149,6 +149,8 @@ Field convertDecimalType(const Field & from, const To & type) return convertDecimalToDecimalType(from, type); if (from.getType() == Field::Types::Decimal128) return convertDecimalToDecimalType(from, type); + if (from.getType() == Field::Types::Decimal256) + return convertDecimalToDecimalType(from, type); if (from.getType() == Field::Types::Float64) return convertFloatToDecimalType(from, type); From d38fc79f4f6496394d7d7c40d0d4b8a78b03746c Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 14 Mar 2023 22:11:45 +0100 Subject: [PATCH 034/377] Add a test --- .../02685_decimal256_various.reference | 18 +++++++++++++++ .../0_stateless/02685_decimal256_various.sql | 23 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/queries/0_stateless/02685_decimal256_various.reference create mode 100644 tests/queries/0_stateless/02685_decimal256_various.sql diff --git a/tests/queries/0_stateless/02685_decimal256_various.reference b/tests/queries/0_stateless/02685_decimal256_various.reference new file mode 100644 index 00000000000..7e941ddcc18 --- /dev/null +++ b/tests/queries/0_stateless/02685_decimal256_various.reference @@ -0,0 +1,18 @@ +1.1 +1 +1.1 +1.2 +1.234567890123456789012345678901 +1.234567890123456789012345678901 +1.23456789012345678901 +356C760E4FC986A2A39F1A950F00000000000000000000000000000000000000 +0011010101101100011101100000111001001111110010011000011010100010101000111001111100011010100101010000111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +1.234567890123456789012345678901 +4.65 +4.65 +[1.2,3.45,1.2] +1 +0 +1 +Array(Decimal(76, 30)) +Decimal(76, 30) diff --git a/tests/queries/0_stateless/02685_decimal256_various.sql b/tests/queries/0_stateless/02685_decimal256_various.sql new file mode 100644 index 00000000000..4837cf6beb5 --- /dev/null +++ b/tests/queries/0_stateless/02685_decimal256_various.sql @@ -0,0 +1,23 @@ +SELECT 1.1::Decimal(60, 30); +SELECT round(1.1::Decimal(60, 30)); +SELECT round(1.1::Decimal(60, 30), 1); +SELECT round(1.234567890123456789012345678901::Decimal(60, 30), 1); +SELECT round(1.234567890123456789012345678901::Decimal(60, 30), 30); +SELECT round(1.234567890123456789012345678901::Decimal(60, 30), 31); +SELECT round(1.234567890123456789012345678901::Decimal(60, 30), 20); + +SELECT hex(1.234567890123456789012345678901::Decimal(60, 30)); +SELECT bin(1.234567890123456789012345678901::Decimal(60, 30)); +SELECT reinterpret(unhex(hex(1.234567890123456789012345678901::Decimal(60, 30))), 'Decimal(60, 30)'); + +SELECT arraySum([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +SELECT arraySum([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)]); + +SELECT arrayCompact([1.2::Decimal(60, 30) AS x, x, x, x, 3.45::Decimal(3, 2) AS y, y, x, x]); + +SELECT 1.2::Decimal(2, 1) IN (1.2::Decimal(60, 30), 3.4::Decimal(60, 30)); +SELECT 1.23::Decimal(3, 2) IN (1.2::Decimal(60, 30), 3.4::Decimal(60, 30)); +SELECT 1.2::Decimal(60, 30) IN (1.2::Decimal(2, 1)); + +SELECT toTypeName([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)]); +SELECT toTypeName(arraySum([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)])); From e074a154dda03bc942785b221f4932a3a1c5bd37 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 14 Mar 2023 22:47:22 +0100 Subject: [PATCH 035/377] Fix style --- src/AggregateFunctions/Moments.h | 1 - src/Functions/array/mapOp.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/AggregateFunctions/Moments.h b/src/AggregateFunctions/Moments.h index 1fe97f0fe46..a718e0c52a1 100644 --- a/src/AggregateFunctions/Moments.h +++ b/src/AggregateFunctions/Moments.h @@ -16,7 +16,6 @@ struct Settings; namespace ErrorCodes { extern const int BAD_ARGUMENTS; - extern const int DECIMAL_OVERFLOW; } diff --git a/src/Functions/array/mapOp.cpp b/src/Functions/array/mapOp.cpp index 2e13d35488f..35aa5e532cc 100644 --- a/src/Functions/array/mapOp.cpp +++ b/src/Functions/array/mapOp.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "Columns/ColumnMap.h" #include "DataTypes/DataTypeMap.h" From 399634a1bcbbc162e588d0de73d8c9f31c16f732 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 15 Mar 2023 00:38:46 +0100 Subject: [PATCH 036/377] More tests --- src/DataTypes/getLeastSupertype.cpp | 2 - src/DataTypes/getMostSubtype.cpp | 24 ++++++- src/Functions/array/mapOp.cpp | 4 ++ src/Loggers/OwnPatternFormatter.cpp | 1 - .../02685_decimal256_various.reference | 66 +++++++++++++++++++ .../0_stateless/02685_decimal256_various.sql | 31 +++++++++ 6 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/DataTypes/getLeastSupertype.cpp b/src/DataTypes/getLeastSupertype.cpp index ba43def73f7..9f9d7fea428 100644 --- a/src/DataTypes/getLeastSupertype.cpp +++ b/src/DataTypes/getLeastSupertype.cpp @@ -15,11 +15,9 @@ #include #include #include -#include #include #include #include -#include namespace DB diff --git a/src/DataTypes/getMostSubtype.cpp b/src/DataTypes/getMostSubtype.cpp index 95a80d79a38..33b5735456e 100644 --- a/src/DataTypes/getMostSubtype.cpp +++ b/src/DataTypes/getMostSubtype.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace DB @@ -367,7 +368,28 @@ DataTypePtr getMostSubtype(const DataTypes & types, bool throw_if_result_is_noth } } - /// TODO: Decimals + /// Decimals + { + bool all_decimals = true; + UInt32 min_scale = std::numeric_limits::max(); + UInt32 min_precision = std::numeric_limits::max(); + for (const auto & type : types) + { + if (isDecimal(type)) + { + min_scale = std::min(min_scale, getDecimalScale(*type)); + min_precision = std::min(min_precision, getDecimalPrecision(*type)); + } + else + { + all_decimals = false; + break; + } + } + + if (all_decimals) + return createDecimal(min_precision, min_scale); + } /// All other data types (UUID, AggregateFunction, Enum...) are compatible only if they are the same (checked in trivial cases). return get_nothing_or_throw(""); diff --git a/src/Functions/array/mapOp.cpp b/src/Functions/array/mapOp.cpp index 35aa5e532cc..613fd934c41 100644 --- a/src/Functions/array/mapOp.cpp +++ b/src/Functions/array/mapOp.cpp @@ -293,6 +293,10 @@ private: return execute2(row_count, args, res_type); case TypeIndex::Float64: return execute2(row_count, args, res_type); + case TypeIndex::Decimal128: + return execute2(row_count, args, res_type); + case TypeIndex::Decimal256: + return execute2(row_count, args, res_type); default: throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal column type {} for values in arguments of function {}", res_value_type->getName(), getName()); diff --git a/src/Loggers/OwnPatternFormatter.cpp b/src/Loggers/OwnPatternFormatter.cpp index 02a2c2e510b..ccf6c479b80 100644 --- a/src/Loggers/OwnPatternFormatter.cpp +++ b/src/Loggers/OwnPatternFormatter.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include diff --git a/tests/queries/0_stateless/02685_decimal256_various.reference b/tests/queries/0_stateless/02685_decimal256_various.reference index 7e941ddcc18..fb87d4653f6 100644 --- a/tests/queries/0_stateless/02685_decimal256_various.reference +++ b/tests/queries/0_stateless/02685_decimal256_various.reference @@ -1,18 +1,84 @@ +-- { echoOn } + +SELECT 1.1::Decimal(60, 30); 1.1 +SELECT round(1.1::Decimal(60, 30)); 1 +SELECT round(1.1::Decimal(60, 30), 1); 1.1 +SELECT round(1.234567890123456789012345678901::Decimal(60, 30), 1); 1.2 +SELECT round(1.234567890123456789012345678901::Decimal(60, 30), 30); 1.234567890123456789012345678901 +SELECT round(1.234567890123456789012345678901::Decimal(60, 30), 31); 1.234567890123456789012345678901 +SELECT round(1.234567890123456789012345678901::Decimal(60, 30), 20); 1.23456789012345678901 +SELECT hex(1.234567890123456789012345678901::Decimal(60, 30)); 356C760E4FC986A2A39F1A950F00000000000000000000000000000000000000 +SELECT bin(1.234567890123456789012345678901::Decimal(60, 30)); 0011010101101100011101100000111001001111110010011000011010100010101000111001111100011010100101010000111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +SELECT reinterpret(unhex(hex(1.234567890123456789012345678901::Decimal(60, 30))), 'Decimal(60, 30)'); 1.234567890123456789012345678901 +SELECT arraySum([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); 4.65 +SELECT arraySum([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)]); 4.65 +SELECT arrayMin([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +1.2 +SELECT arrayMax([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +3.45 +SELECT arrayAvg([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +2.325 +SELECT round(arrayProduct([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]), 6); +4.14 +SELECT toTypeName(arrayProduct([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)])); +Float64 +SELECT arrayCumSum([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +[1.2,4.65] +SELECT arrayCumSumNonNegative([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +[1.2,4.65] +SELECT arrayDifference([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +[0,2.25] +SELECT arrayCompact([1.2::Decimal(60, 30) AS x, x, x, x, 3.45::Decimal(3, 2) AS y, y, x, x]); [1.2,3.45,1.2] +SELECT 1.2::Decimal(2, 1) IN (1.2::Decimal(60, 30), 3.4::Decimal(60, 30)); 1 +SELECT 1.23::Decimal(3, 2) IN (1.2::Decimal(60, 30), 3.4::Decimal(60, 30)); 0 +SELECT 1.2::Decimal(60, 30) IN (1.2::Decimal(2, 1)); 1 +SELECT toTypeName([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)]); Array(Decimal(76, 30)) +SELECT toTypeName(arraySum([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)])); Decimal(76, 30) +SELECT arrayJoin(sumMap(x)) FROM (SELECT [('Hello', 1.2::Decimal256(30)), ('World', 3.4::Decimal256(30))]::Map(String, Decimal256(30)) AS x UNION ALL SELECT [('World', 5.6::Decimal256(30)), ('GoodBye', -111.222::Decimal256(30))]::Map(String, Decimal256(30))) ORDER BY 1; +('GoodBye',-111.222) +('Hello',1.2) +('World',9) +SELECT mapAdd(map('Hello', 1.2::Decimal128(30), 'World', 3.4::Decimal128(30)), map('World', 5.6::Decimal128(30), 'GoodBye', -111.222::Decimal128(30))); +{'GoodBye':-111.222,'Hello':1.2,'World':9} +SELECT mapSubtract(map('Hello', 1.2::Decimal128(30), 'World', 3.4::Decimal128(30)), map('World', 5.6::Decimal128(30), 'GoodBye', -111.222::Decimal128(30))); +{'GoodBye':111.222,'Hello':1.2,'World':-2.2} +SELECT arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(UInt256))); +[2,3] +SELECT toTypeName(arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(UInt128)))); +Array(UInt128) +SELECT toTypeName(arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(Int128)))); +Array(Int128) +SELECT arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(Int128))); +[2,3] +SELECT arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(Int8))); +[2,3] +SELECT toTypeName(arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(Int8)))); +Array(Int8) +SELECT arraySort(arrayIntersect([1.1::Decimal256(70), 2.34::Decimal256(60), 3.456::Decimal256(50)], [2.34::Decimal256(65), 3.456::Decimal256(55), 4.5678::Decimal256(45)])); +[2.34,3.456] +SELECT arraySort(arrayIntersect([1.1::Decimal256(1)], [1.12::Decimal256(2)])); -- Note: this is correct but the semantics has to be clarified in the docs. +[1.1] +SELECT arraySort(arrayIntersect([1.1::Decimal256(2)], [1.12::Decimal256(2)])); +[] +SELECT arraySort(arrayIntersect([1.1::Decimal128(1)], [1.12::Decimal128(2)])); -- Note: this is correct but the semantics has to be clarified in the docs. +[1.1] +SELECT arraySort(arrayIntersect([1.1::Decimal128(2)], [1.12::Decimal128(2)])); +[] diff --git a/tests/queries/0_stateless/02685_decimal256_various.sql b/tests/queries/0_stateless/02685_decimal256_various.sql index 4837cf6beb5..cdfb1967c34 100644 --- a/tests/queries/0_stateless/02685_decimal256_various.sql +++ b/tests/queries/0_stateless/02685_decimal256_various.sql @@ -1,3 +1,5 @@ +-- { echoOn } + SELECT 1.1::Decimal(60, 30); SELECT round(1.1::Decimal(60, 30)); SELECT round(1.1::Decimal(60, 30), 1); @@ -13,6 +15,17 @@ SELECT reinterpret(unhex(hex(1.234567890123456789012345678901::Decimal(60, 30))) SELECT arraySum([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); SELECT arraySum([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)]); +SELECT arrayMin([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +SELECT arrayMax([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +SELECT arrayAvg([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); + +SELECT round(arrayProduct([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]), 6); +SELECT toTypeName(arrayProduct([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)])); + +SELECT arrayCumSum([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +SELECT arrayCumSumNonNegative([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); +SELECT arrayDifference([1.2::Decimal(60, 30), 3.45::Decimal(61, 29)]); + SELECT arrayCompact([1.2::Decimal(60, 30) AS x, x, x, x, 3.45::Decimal(3, 2) AS y, y, x, x]); SELECT 1.2::Decimal(2, 1) IN (1.2::Decimal(60, 30), 3.4::Decimal(60, 30)); @@ -21,3 +34,21 @@ SELECT 1.2::Decimal(60, 30) IN (1.2::Decimal(2, 1)); SELECT toTypeName([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)]); SELECT toTypeName(arraySum([1.2::Decimal(60, 30), 3.45::Decimal(3, 2)])); + +SELECT arrayJoin(sumMap(x)) FROM (SELECT [('Hello', 1.2::Decimal256(30)), ('World', 3.4::Decimal256(30))]::Map(String, Decimal256(30)) AS x UNION ALL SELECT [('World', 5.6::Decimal256(30)), ('GoodBye', -111.222::Decimal256(30))]::Map(String, Decimal256(30))) ORDER BY 1; + +SELECT mapAdd(map('Hello', 1.2::Decimal128(30), 'World', 3.4::Decimal128(30)), map('World', 5.6::Decimal128(30), 'GoodBye', -111.222::Decimal128(30))); +SELECT mapSubtract(map('Hello', 1.2::Decimal128(30), 'World', 3.4::Decimal128(30)), map('World', 5.6::Decimal128(30), 'GoodBye', -111.222::Decimal128(30))); + +SELECT arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(UInt256))); +SELECT toTypeName(arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(UInt128)))); +SELECT toTypeName(arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(Int128)))); +SELECT arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(Int128))); +SELECT arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(Int8))); +SELECT toTypeName(arraySort(arrayIntersect([1, 2, 3]::Array(UInt256), [2, 3, 4]::Array(Int8)))); + +SELECT arraySort(arrayIntersect([1.1::Decimal256(70), 2.34::Decimal256(60), 3.456::Decimal256(50)], [2.34::Decimal256(65), 3.456::Decimal256(55), 4.5678::Decimal256(45)])); +SELECT arraySort(arrayIntersect([1.1::Decimal256(1)], [1.12::Decimal256(2)])); -- Note: this is correct but the semantics has to be clarified in the docs. +SELECT arraySort(arrayIntersect([1.1::Decimal256(2)], [1.12::Decimal256(2)])); +SELECT arraySort(arrayIntersect([1.1::Decimal128(1)], [1.12::Decimal128(2)])); -- Note: this is correct but the semantics has to be clarified in the docs. +SELECT arraySort(arrayIntersect([1.1::Decimal128(2)], [1.12::Decimal128(2)])); From d1172690c6d91144ca3ccabf367d2efa38107296 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 15 Mar 2023 00:41:08 +0100 Subject: [PATCH 037/377] Add a test --- .../02686_postgres_protocol_decimal_256.reference | 5 +++++ .../02686_postgres_protocol_decimal_256.sh | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/queries/0_stateless/02686_postgres_protocol_decimal_256.reference create mode 100755 tests/queries/0_stateless/02686_postgres_protocol_decimal_256.sh diff --git a/tests/queries/0_stateless/02686_postgres_protocol_decimal_256.reference b/tests/queries/0_stateless/02686_postgres_protocol_decimal_256.reference new file mode 100644 index 00000000000..5e61b14b9a1 --- /dev/null +++ b/tests/queries/0_stateless/02686_postgres_protocol_decimal_256.reference @@ -0,0 +1,5 @@ + test +------ + 1.23 +(1 row) + diff --git a/tests/queries/0_stateless/02686_postgres_protocol_decimal_256.sh b/tests/queries/0_stateless/02686_postgres_protocol_decimal_256.sh new file mode 100755 index 00000000000..2a94f940327 --- /dev/null +++ b/tests/queries/0_stateless/02686_postgres_protocol_decimal_256.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Tags: no-parallel, no-fasttest +# Tag no-fasttest: needs psql + +CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CUR_DIR"/../shell_config.sh + +echo " +DROP USER IF EXISTS postgresql_user; +CREATE USER postgresql_user HOST IP '127.0.0.1' IDENTIFIED WITH no_password; +" | $CLICKHOUSE_CLIENT -n + +psql --host localhost --port ${CLICKHOUSE_PORT_POSTGRESQL} ${CLICKHOUSE_DATABASE} --user postgresql_user -c "SELECT 1.23::Decimal256(70) AS test;" From c07de4719f927a59968b5b9cabdfea41c1426096 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 15 Mar 2023 02:00:50 +0100 Subject: [PATCH 038/377] Fix some tests --- .../AggregateFunctionStatisticsSimple.cpp | 6 +++++- .../00700_decimal_empty_aggregates.reference | 4 ++-- tests/queries/0_stateless/00862_decimal_in.reference | 6 ++++++ tests/queries/0_stateless/00862_decimal_in.sql | 10 +++++++--- .../01018_empty_aggregation_filling.reference | 4 ++-- .../02421_decimal_in_precision_issue_41125.reference | 3 +++ .../02421_decimal_in_precision_issue_41125.sql | 6 +++--- 7 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.cpp b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.cpp index a22d783de4c..49934098e53 100644 --- a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.cpp +++ b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.cpp @@ -23,8 +23,12 @@ AggregateFunctionPtr createAggregateFunctionStatisticsUnary( assertNoParameters(name, parameters); assertUnary(name, argument_types); + AggregateFunctionPtr res; const DataTypePtr & data_type = argument_types[0]; - AggregateFunctionPtr res{createWithNumericType(*data_type, argument_types)}; + if (isDecimal(data_type)) + res.reset(createWithDecimalType(*data_type, argument_types)); + else + res.reset(createWithNumericType(*data_type, argument_types)); if (!res) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument for aggregate function {}", diff --git a/tests/queries/0_stateless/00700_decimal_empty_aggregates.reference b/tests/queries/0_stateless/00700_decimal_empty_aggregates.reference index 2c29b72f50c..20098e638d5 100644 --- a/tests/queries/0_stateless/00700_decimal_empty_aggregates.reference +++ b/tests/queries/0_stateless/00700_decimal_empty_aggregates.reference @@ -45,11 +45,11 @@ [0,0,0,0,0,0,0,0,0,0,0] [0,0,0,0,0,0,0,0,0,0,0] [0,0,0,0,0,0,0,0,0,0,0] -inf inf inf Float64 Float64 Float64 +nan nan nan Float64 Float64 Float64 nan nan nan nan nan nan Float64 Float64 Float64 nan nan nan -inf inf inf Float64 Float64 Float64 +nan nan nan Float64 Float64 Float64 nan nan nan nan nan nan Float64 Float64 Float64 nan nan nan diff --git a/tests/queries/0_stateless/00862_decimal_in.reference b/tests/queries/0_stateless/00862_decimal_in.reference index 0cd93f69c38..f60b8081af4 100644 --- a/tests/queries/0_stateless/00862_decimal_in.reference +++ b/tests/queries/0_stateless/00862_decimal_in.reference @@ -16,3 +16,9 @@ 64 64 64 64 64 64 +256 256 +256 256 +256 256 +256 256 +256 256 +256 256 diff --git a/tests/queries/0_stateless/00862_decimal_in.sql b/tests/queries/0_stateless/00862_decimal_in.sql index b5c058119a2..3aa7bb18cb4 100644 --- a/tests/queries/0_stateless/00862_decimal_in.sql +++ b/tests/queries/0_stateless/00862_decimal_in.sql @@ -5,7 +5,7 @@ CREATE TABLE temp y Nullable(Decimal(38, 2)) ) ENGINE = Memory; -INSERT INTO temp VALUES (32, 32), (64, 64), (128, 128); +INSERT INTO temp VALUES (32, 32), (64, 64), (128, 128), (256, 256); SELECT * FROM temp WHERE x IN (toDecimal128(128, 1)); SELECT * FROM temp WHERE x IN (toDecimal128(128, 2)); @@ -28,7 +28,11 @@ SELECT * FROM temp WHERE y IN (toDecimal64(64, 1)); SELECT * FROM temp WHERE y IN (toDecimal64(64, 2)); SELECT * FROM temp WHERE y IN (toDecimal64(64, 3)); -SELECT * FROM temp WHERE x IN (toDecimal256(256, 1)); -- { serverError 53 } -SELECT * FROM temp WHERE y IN (toDecimal256(256, 1)); -- { serverError 53 } +SELECT * FROM temp WHERE x IN (toDecimal256(256, 1)); +SELECT * FROM temp WHERE x IN (toDecimal256(256, 2)); +SELECT * FROM temp WHERE x IN (toDecimal256(256, 3)); +SELECT * FROM temp WHERE y IN (toDecimal256(256, 1)); +SELECT * FROM temp WHERE y IN (toDecimal256(256, 2)); +SELECT * FROM temp WHERE y IN (toDecimal256(256, 3)); DROP TABLE IF EXISTS temp; diff --git a/tests/queries/0_stateless/01018_empty_aggregation_filling.reference b/tests/queries/0_stateless/01018_empty_aggregation_filling.reference index c29807a7e15..975b48c57f9 100644 --- a/tests/queries/0_stateless/01018_empty_aggregation_filling.reference +++ b/tests/queries/0_stateless/01018_empty_aggregation_filling.reference @@ -54,7 +54,7 @@ hello 2011-04-05 14:19:19 -123.45 -123.45 -inf -inf +nan +nan -123.45 -123.45 diff --git a/tests/queries/0_stateless/02421_decimal_in_precision_issue_41125.reference b/tests/queries/0_stateless/02421_decimal_in_precision_issue_41125.reference index d3d171221e8..71c9a23879f 100644 --- a/tests/queries/0_stateless/02421_decimal_in_precision_issue_41125.reference +++ b/tests/queries/0_stateless/02421_decimal_in_precision_issue_41125.reference @@ -8,3 +8,6 @@ 1 1 1 +1 +1 +1 diff --git a/tests/queries/0_stateless/02421_decimal_in_precision_issue_41125.sql b/tests/queries/0_stateless/02421_decimal_in_precision_issue_41125.sql index f5978a34061..f5d182be3e3 100644 --- a/tests/queries/0_stateless/02421_decimal_in_precision_issue_41125.sql +++ b/tests/queries/0_stateless/02421_decimal_in_precision_issue_41125.sql @@ -8,16 +8,16 @@ INSERT INTO dtest VALUES ('33', '44.4', '35'); SELECT count() == 0 FROM dtest WHERE a IN toDecimal32('33.3000', 4); SELECT count() == 0 FROM dtest WHERE a IN toDecimal64('33.3000', 4); SELECT count() == 0 FROM dtest WHERE a IN toDecimal128('33.3000', 4); -SELECT count() == 0 FROM dtest WHERE a IN toDecimal256('33.3000', 4); -- { serverError 53 } +SELECT count() == 0 FROM dtest WHERE a IN toDecimal256('33.3000', 4); SELECT count() == 0 FROM dtest WHERE b IN toDecimal32('44.4000', 0); SELECT count() == 0 FROM dtest WHERE b IN toDecimal64('44.4000', 0); SELECT count() == 0 FROM dtest WHERE b IN toDecimal128('44.4000', 0); -SELECT count() == 0 FROM dtest WHERE b IN toDecimal256('44.4000', 0); -- { serverError 53 } +SELECT count() == 0 FROM dtest WHERE b IN toDecimal256('44.4000', 0); SELECT count() == 1 FROM dtest WHERE b IN toDecimal32('44.4000', 4); SELECT count() == 1 FROM dtest WHERE b IN toDecimal64('44.4000', 4); SELECT count() == 1 FROM dtest WHERE b IN toDecimal128('44.4000', 4); -SELECT count() == 1 FROM dtest WHERE b IN toDecimal256('44.4000', 4); -- { serverError 53 } +SELECT count() == 1 FROM dtest WHERE b IN toDecimal256('44.4000', 4); DROP TABLE IF EXISTS dtest; From a7957adaf86a5c515d99e7fc4a3c916d298a4097 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 15 Mar 2023 02:20:22 +0100 Subject: [PATCH 039/377] Fix test --- .../AggregateFunctionStatisticsSimple.h | 12 ++++++++++-- .../0_stateless/00700_decimal_aggregates.reference | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h index 66ecc6eecd6..c412b15dc58 100644 --- a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h +++ b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h @@ -82,7 +82,11 @@ public: explicit AggregateFunctionVarianceSimple(const DataTypes & argument_types_) : IAggregateFunctionDataHelper>(argument_types_, {}, std::make_shared>()) + , src_scale(0) { + chassert(!argument_types_.empty()); + if (isDecimal(argument_types_.front())) + src_scale = getDecimalScale(*argument_types_.front()); } String getName() const override @@ -124,8 +128,9 @@ public: { if constexpr (is_decimal) { - this->data(place).add(static_cast( - static_cast(*columns[0]).getData()[row_num].value)); + this->data(place).add( + convertFromDecimal, DataTypeFloat64>( + static_cast(*columns[0]).getData()[row_num], src_scale)); } else this->data(place).add( @@ -204,6 +209,9 @@ public: if constexpr (StatFunc::kind == StatisticsFunctionKind::corr) dst.push_back(data.get()); } + +private: + UInt32 src_scale; }; diff --git a/tests/queries/0_stateless/00700_decimal_aggregates.reference b/tests/queries/0_stateless/00700_decimal_aggregates.reference index 159091d867e..acf41546f5c 100644 --- a/tests/queries/0_stateless/00700_decimal_aggregates.reference +++ b/tests/queries/0_stateless/00700_decimal_aggregates.reference @@ -67,9 +67,9 @@ [-50,-40,-30,-20,-10,0,10,20,30,40,50] [-16.66666666,-13.33333333,-10,-6.66666666,-3.33333333,0,3.33333333,6.66666666,10,13.33333333,16.66666666] [-10,-8,-6,-4,-2,0,2,4,6,8,10] -850 94.44444438684269 34 Float64 Float64 Float64 +850 94.4444443868427 34.00000000000001 Float64 Float64 Float64 850 94.4444443868427 34.00000000000001 -858.5 95.38888883071111 34.34 Float64 Float64 Float64 +858.5 95.38888883071112 34.34 Float64 Float64 Float64 858.5 95.38888883071112 34.34 29.154759474226502 9.718253155111915 5.830951894845301 Float64 Float64 Float64 29.154759474226502 9.718253155111915 5.830951894845301 From 4b85a5e9ecb96c297b21d2b618ed295e0f523628 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 15 Mar 2023 02:29:07 +0100 Subject: [PATCH 040/377] Add a test for #44864 --- .../queries/0_stateless/02685_decimal256_various.reference | 6 ++++++ tests/queries/0_stateless/02685_decimal256_various.sql | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/tests/queries/0_stateless/02685_decimal256_various.reference b/tests/queries/0_stateless/02685_decimal256_various.reference index fb87d4653f6..a0628bb1109 100644 --- a/tests/queries/0_stateless/02685_decimal256_various.reference +++ b/tests/queries/0_stateless/02685_decimal256_various.reference @@ -82,3 +82,9 @@ SELECT arraySort(arrayIntersect([1.1::Decimal128(1)], [1.12::Decimal128(2)])); - [1.1] SELECT arraySort(arrayIntersect([1.1::Decimal128(2)], [1.12::Decimal128(2)])); [] +select coalesce(cast('123', 'Nullable(Decimal(20, 10))'), 0); +123 +select coalesce(cast('123', 'Nullable(Decimal(40, 10))'), 0); +123 +select coalesce(cast('123', 'Decimal(40, 10)'), 0); +123 diff --git a/tests/queries/0_stateless/02685_decimal256_various.sql b/tests/queries/0_stateless/02685_decimal256_various.sql index cdfb1967c34..6f358df1994 100644 --- a/tests/queries/0_stateless/02685_decimal256_various.sql +++ b/tests/queries/0_stateless/02685_decimal256_various.sql @@ -52,3 +52,7 @@ SELECT arraySort(arrayIntersect([1.1::Decimal256(1)], [1.12::Decimal256(2)])); - SELECT arraySort(arrayIntersect([1.1::Decimal256(2)], [1.12::Decimal256(2)])); SELECT arraySort(arrayIntersect([1.1::Decimal128(1)], [1.12::Decimal128(2)])); -- Note: this is correct but the semantics has to be clarified in the docs. SELECT arraySort(arrayIntersect([1.1::Decimal128(2)], [1.12::Decimal128(2)])); + +select coalesce(cast('123', 'Nullable(Decimal(20, 10))'), 0); +select coalesce(cast('123', 'Nullable(Decimal(40, 10))'), 0); +select coalesce(cast('123', 'Decimal(40, 10)'), 0); From 28a7b419dd6a37e590d62338cae02fcf4309cd15 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 15 Mar 2023 02:35:41 +0100 Subject: [PATCH 041/377] Add a test for #28335 --- .../queries/0_stateless/02685_decimal256_various.reference | 7 +++++++ tests/queries/0_stateless/02685_decimal256_various.sql | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/tests/queries/0_stateless/02685_decimal256_various.reference b/tests/queries/0_stateless/02685_decimal256_various.reference index a0628bb1109..848c5e0b163 100644 --- a/tests/queries/0_stateless/02685_decimal256_various.reference +++ b/tests/queries/0_stateless/02685_decimal256_various.reference @@ -88,3 +88,10 @@ select coalesce(cast('123', 'Nullable(Decimal(40, 10))'), 0); 123 select coalesce(cast('123', 'Decimal(40, 10)'), 0); 123 +DROP TABLE IF EXISTS decimal_insert_cast_issue; +create table decimal_insert_cast_issue (a Decimal(76, 0)) engine = TinyLog; +SET param_param = 1; +INSERT INTO decimal_insert_cast_issue VALUES ({param:Nullable(Decimal(41, 0))}); +SELECT * FROM decimal_insert_cast_issue; +1 +DROP TABLE decimal_insert_cast_issue; diff --git a/tests/queries/0_stateless/02685_decimal256_various.sql b/tests/queries/0_stateless/02685_decimal256_various.sql index 6f358df1994..545eaefe35e 100644 --- a/tests/queries/0_stateless/02685_decimal256_various.sql +++ b/tests/queries/0_stateless/02685_decimal256_various.sql @@ -56,3 +56,10 @@ SELECT arraySort(arrayIntersect([1.1::Decimal128(2)], [1.12::Decimal128(2)])); select coalesce(cast('123', 'Nullable(Decimal(20, 10))'), 0); select coalesce(cast('123', 'Nullable(Decimal(40, 10))'), 0); select coalesce(cast('123', 'Decimal(40, 10)'), 0); + +DROP TABLE IF EXISTS decimal_insert_cast_issue; +create table decimal_insert_cast_issue (a Decimal(76, 0)) engine = TinyLog; +SET param_param = 1; +INSERT INTO decimal_insert_cast_issue VALUES ({param:Nullable(Decimal(41, 0))}); +SELECT * FROM decimal_insert_cast_issue; +DROP TABLE decimal_insert_cast_issue; From f859b3258a986cab8603c3822dda256e9ceae230 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 15 Mar 2023 04:45:26 +0100 Subject: [PATCH 042/377] Add support for Decimal256 in MySQL tables --- tests/integration/test_mysql_database_engine/test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/integration/test_mysql_database_engine/test.py b/tests/integration/test_mysql_database_engine/test.py index 65fd54c7f34..cac021247c2 100644 --- a/tests/integration/test_mysql_database_engine/test.py +++ b/tests/integration/test_mysql_database_engine/test.py @@ -396,7 +396,7 @@ def arryToString(expected_clickhouse_values): # if expected_clickhouse_values is "", compare MySQL and ClickHouse query results directly @pytest.mark.parametrize( - "case_name, mysql_type, expected_ch_type, mysql_values, expected_clickhouse_values , setting_mysql_datatypes_support_level", + "case_name, mysql_type, expected_ch_type, mysql_values, expected_clickhouse_values, setting_mysql_datatypes_support_level", [ pytest.param( "common_types", @@ -725,11 +725,10 @@ def arryToString(expected_clickhouse_values): "decimal,datetime64", id="datetime_6_1", ), - # right now precision bigger than 39 is not supported by ClickHouse's Decimal, hence fall back to String pytest.param( "decimal_40_6", "decimal(40, 6) NOT NULL", - "String", + "Decimal(40, 6)", decimal_values, "", "decimal,datetime64", From 0f6e4d3934cda52145c7b7754c4e985b4e6b4947 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Wed, 15 Mar 2023 11:41:29 +0000 Subject: [PATCH 043/377] Start implementing constraint optimizer --- src/Analyzer/Passes/CNF.cpp | 190 +++++-- src/Analyzer/Passes/CNF.h | 26 +- src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 440 ++++++++++++++- src/Analyzer/Passes/ConvertQueryToCNFPass.h | 8 +- src/Analyzer/QueryTreePassManager.cpp | 2 +- .../AddIndexConstraintsOptimizer.cpp | 42 +- src/Interpreters/ComparisonGraph.cpp | 529 +++++++++++------- src/Interpreters/ComparisonGraph.h | 97 ++-- .../SubstituteColumnOptimizer.cpp | 6 +- src/Interpreters/TreeCNFConverter.cpp | 70 +-- src/Interpreters/TreeCNFConverter.h | 68 +++ .../WhereConstraintsOptimizer.cpp | 12 +- src/Storages/ConstraintsDescription.cpp | 98 +++- src/Storages/ConstraintsDescription.h | 31 +- ...ergeTreeIndexHypothesisMergedCondition.cpp | 12 +- .../MergeTreeIndexHypothesisMergedCondition.h | 6 +- 16 files changed, 1227 insertions(+), 410 deletions(-) diff --git a/src/Analyzer/Passes/CNF.cpp b/src/Analyzer/Passes/CNF.cpp index 23d132540f2..3d95200a948 100644 --- a/src/Analyzer/Passes/CNF.cpp +++ b/src/Analyzer/Passes/CNF.cpp @@ -2,6 +2,9 @@ #include #include +#include + +#include #include #include @@ -9,9 +12,17 @@ #include #include -#include "Interpreters/ActionsDAG.h" -namespace DB::Analyzer +namespace DB +{ + +namespace ErrorCodes +{ + extern const int TOO_MANY_TEMPORARY_COLUMNS; + extern const int LOGICAL_ERROR; +} + +namespace Analyzer { namespace @@ -268,6 +279,22 @@ private: } }; +std::optional tryInvertFunction( + const CNF::AtomicFormula & atom, const ContextPtr & context, const std::unordered_map & inverse_relations) +{ + auto * function_node = atom.node_with_hash.node->as(); + if (!function_node) + return std::nullopt; + + if (auto it = inverse_relations.find(function_node->getFunctionName()); it != inverse_relations.end()) + { + auto inverse_function_resolver = FunctionFactory::instance().get(it->second, context); + function_node->resolveAsFunction(inverse_function_resolver); + return CNF::AtomicFormula{!atom.negative, atom.node_with_hash.node}; + } + + return std::nullopt; +} } bool CNF::AtomicFormula::operator==(const AtomicFormula & rhs) const @@ -345,43 +372,123 @@ CNF & CNF::pushNotIntoFunctions(const ContextPtr & context) { transformAtoms([&](const AtomicFormula & atom) { - if (!atom.negative) - return atom; - - static const std::unordered_map inverse_relations = { - {"equals", "notEquals"}, - {"less", "greaterOrEquals"}, - {"lessOrEquals", "greater"}, - {"in", "notIn"}, - {"like", "notLike"}, - {"empty", "notEmpty"}, - {"notEquals", "equals"}, - {"greaterOrEquals", "less"}, - {"greater", "lessOrEquals"}, - {"notIn", "in"}, - {"notLike", "like"}, - {"notEmpty", "empty"}, - }; - - auto * function_node = atom.node_with_hash.node->as(); - if (!function_node) - return atom; - - if (auto it = inverse_relations.find(function_node->getFunctionName()); it != inverse_relations.end()) - { - auto inverse_function_resolver = FunctionFactory::instance().get(it->second, context); - function_node->resolveAsFunction(inverse_function_resolver); - return AtomicFormula{!atom.negative, atom.node_with_hash.node}; - } - - return atom; + return pushNotIntoFunction(atom, context); }); - std::cout << "gorup: " << statements.size() << std::endl; + return *this; +} + +CNF::AtomicFormula CNF::pushNotIntoFunction(const AtomicFormula & atom, const ContextPtr & context) +{ + if (!atom.negative) + return atom; + + static const std::unordered_map inverse_relations = { + {"equals", "notEquals"}, + {"less", "greaterOrEquals"}, + {"lessOrEquals", "greater"}, + {"in", "notIn"}, + {"like", "notLike"}, + {"empty", "notEmpty"}, + {"notEquals", "equals"}, + {"greaterOrEquals", "less"}, + {"greater", "lessOrEquals"}, + {"notIn", "in"}, + {"notLike", "like"}, + {"notEmpty", "empty"}, + }; + + if (auto inverted_atom = tryInvertFunction(atom, context, inverse_relations); + inverted_atom.has_value()) + return std::move(*inverted_atom); + + return atom; +} + +CNF & CNF::pullNotOutFunctions(const ContextPtr & context) +{ + transformAtoms([&](const AtomicFormula & atom) + { + static const std::unordered_map inverse_relations = { + {"notEquals", "equals"}, + {"greaterOrEquals", "less"}, + {"greater", "lessOrEquals"}, + {"notIn", "in"}, + {"notLike", "like"}, + {"notEmpty", "empty"}, + }; + + if (auto inverted_atom = tryInvertFunction(atom, context, inverse_relations); + inverted_atom.has_value()) + return std::move(*inverted_atom); + + return atom; + }); return *this; } +CNF & CNF::filterAlwaysTrueGroups(std::function predicate) +{ + AndGroup filtered; + for (const auto & or_group : statements) + { + if (predicate(or_group)) + filtered.insert(or_group); + } + + statements = std::move(filtered); + return *this; +} + +CNF & CNF::filterAlwaysFalseAtoms(std::function predicate) +{ + AndGroup filtered; + for (const auto & or_group : statements) + { + OrGroup filtered_group; + for (const auto & atom : or_group) + { + if (predicate(atom)) + filtered_group.insert(atom); + } + + if (!filtered_group.empty()) + filtered.insert(std::move(filtered_group)); + else + { + filtered.clear(); + filtered_group.insert(AtomicFormula{false, QueryTreeNodePtrWithHash{std::make_shared(static_cast(0))}}); + filtered.insert(std::move(filtered_group)); + break; + } + } + + statements = std::move(filtered); + return *this; +} + +CNF & CNF::reduce() +{ + while (true) + { + AndGroup new_statements = reduceOnceCNFStatements(statements); + if (statements == new_statements) + { + statements = filterCNFSubsets(statements); + return *this; + } + else + statements = new_statements; + } +} + +void CNF::appendGroup(const AndGroup & and_group) +{ + for (const auto & or_group : and_group) + statements.emplace(or_group); +} + CNF::CNF(AndGroup statements_) : statements(std::move(statements_)) {} @@ -421,10 +528,21 @@ std::optional CNF::tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr co return CNF{std::move(collect_visitor.and_group)}; } +CNF CNF::toCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier) +{ + auto cnf = tryBuildCNF(node, context, max_growth_multiplier); + if (!cnf) + throw Exception(ErrorCodes::TOO_MANY_TEMPORARY_COLUMNS, + "Cannot convert expression '{}' to CNF, because it produces to many clauses." + "Size of boolean formula in CNF can be exponential of size of source formula."); + + return *cnf; +} + QueryTreeNodePtr CNF::toQueryTree(ContextPtr context) const { - if (statements.empty()) - return nullptr; + if (statements.empty()) + return nullptr; QueryTreeNodes and_arguments; and_arguments.reserve(statements.size()); @@ -476,3 +594,5 @@ QueryTreeNodePtr CNF::toQueryTree(ContextPtr context) const } } + +} diff --git a/src/Analyzer/Passes/CNF.h b/src/Analyzer/Passes/CNF.h index 9736a356d63..238e32c4ce1 100644 --- a/src/Analyzer/Passes/CNF.h +++ b/src/Analyzer/Passes/CNF.h @@ -37,7 +37,7 @@ public: hash.update(atomic_formula_hash.get64()); } - + return hash.get64(); } }; @@ -52,17 +52,33 @@ public: static constexpr size_t MAX_ATOMS_WITHOUT_CHECK = 200; CNF & transformAtoms(std::function fn); + CNF & transformGroups(std::function fn); + + CNF & filterAlwaysTrueGroups(std::function predicate); + CNF & filterAlwaysFalseAtoms(std::function predicate); + + CNF & reduce(); + + void appendGroup(const AndGroup & and_group); /// Convert "NOT fn" to a single node representing inverse of "fn" CNF & pushNotIntoFunctions(const ContextPtr & context); + CNF & pullNotOutFunctions(const ContextPtr & context); - static std::optional tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier = DEFAULT_MAX_GROWTH_MULTIPLIER); + static AtomicFormula pushNotIntoFunction(const AtomicFormula & atom, const ContextPtr & context); - QueryTreeNodePtr toQueryTree(ContextPtr context) const; -private: explicit CNF(AndGroup statements_); - CNF & transformGroups(std::function fn); + static std::optional tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier = DEFAULT_MAX_GROWTH_MULTIPLIER); + static CNF toCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier = DEFAULT_MAX_GROWTH_MULTIPLIER); + + QueryTreeNodePtr toQueryTree(ContextPtr context) const; + + const auto & getStatements() const + { + return statements; + } +private: AndGroup statements; }; diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp index 1c7e9a5753d..278cadf2638 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -2,7 +2,14 @@ #include #include +#include +#include +#include #include +#include + +#include +#include "Analyzer/HashUtils.h" namespace DB { @@ -16,53 +23,438 @@ bool isLogicalFunction(const FunctionNode & function_node) return name == "and" || name == "or" || name == "not"; } +std::optional tryConvertQueryToCNF(const QueryTreeNodePtr & node, const ContextPtr & context) +{ + auto * function_node = node->as(); + + if (!function_node || !isLogicalFunction(*function_node)) + return std::nullopt; + + auto cnf_form = Analyzer::CNF::tryBuildCNF(node, context); + if (!cnf_form) + return std::nullopt; + + cnf_form->pushNotIntoFunctions(context); + return cnf_form; +} + +enum class MatchState : uint8_t +{ + FULL_MATCH, /// a = b + PARTIAL_MATCH, /// a = not b + NONE, +}; + +MatchState match(const Analyzer::CNF::AtomicFormula & a, const Analyzer::CNF::AtomicFormula & b) +{ + using enum MatchState; + if (a.node_with_hash.hash != b.node_with_hash.hash) + return NONE; + + return a.negative == b.negative ? FULL_MATCH : PARTIAL_MATCH; +} + +bool checkIfGroupAlwaysTrueFullMatch(const Analyzer::CNF::OrGroup & group, const ConstraintsDescription & constraints_description, const ContextPtr & context) +{ + /// We have constraints in CNF. + /// CNF is always true => Each OR group in CNF is always true. + /// So, we try to check whether we have al least one OR group from CNF as subset in our group. + /// If we've found one then our group is always true too. + + const auto & query_tree_constraint = constraints_description.getQueryTreeData(context); + const auto & constraints_data = query_tree_constraint.getConstraintData(); + std::vector found(constraints_data.size()); + for (size_t i = 0; i < constraints_data.size(); ++i) + found[i] = constraints_data[i].size(); + + for (const auto & atom : group) + { + const auto constraint_atom_ids = query_tree_constraint.getAtomIds(atom.node_with_hash); + if (constraint_atom_ids) + { + const auto constraint_atoms = query_tree_constraint.getAtomsById(*constraint_atom_ids); + for (size_t i = 0; i < constraint_atoms.size(); ++i) + { + if (match(constraint_atoms[i], atom) == MatchState::FULL_MATCH) + { + if ((--found[(*constraint_atom_ids)[i].group_id]) == 0) + return true; + } + } + } + } + return false; +} + +bool checkIfGroupAlwaysTrueGraph(const Analyzer::CNF::OrGroup & group, const ComparisonGraph & graph) +{ + /// We try to find at least one atom that is always true by using comparison graph. + for (const auto & atom : group) + { + const auto * function_node = atom.node_with_hash.node->as(); + if (function_node) + { + const auto & arguments = function_node->getArguments().getNodes(); + if (arguments.size() == 2) + { + const auto expected = ComparisonGraph::atomToCompareResult(atom); + if (graph.isAlwaysCompare(expected, arguments[0], arguments[1])) + return true; + } + } + } + + return false; +} + +bool checkIfAtomAlwaysFalseFullMatch(const Analyzer::CNF::AtomicFormula & atom, const ConstraintsDescription & constraints_description, const ContextPtr & context) +{ + const auto & query_tree_constraint = constraints_description.getQueryTreeData(context); + const auto constraint_atom_ids = query_tree_constraint.getAtomIds(atom.node_with_hash); + if (constraint_atom_ids) + { + for (const auto & constraint_atom : query_tree_constraint.getAtomsById(*constraint_atom_ids)) + { + const auto match_result = match(constraint_atom, atom); + if (match_result == MatchState::PARTIAL_MATCH) + return true; + } + } + + return false; +} + +bool checkIfAtomAlwaysFalseGraph(const Analyzer::CNF::AtomicFormula & atom, const ComparisonGraph & graph) +{ + const auto * function_node = atom.node_with_hash.node->as(); + if (!function_node) + return false; + + const auto & arguments = function_node->getArguments().getNodes(); + if (arguments.size() != 2) + return false; + + /// TODO: special support for != + const auto expected = ComparisonGraph::atomToCompareResult(atom); + return !graph.isPossibleCompare(expected, arguments[0], arguments[1]); +} + +void replaceToConstants(QueryTreeNodePtr & term, const ComparisonGraph & graph) +{ + const auto equal_constant = graph.getEqualConst(term); + if (equal_constant) + { + term = (*equal_constant)->clone(); + return; + } + + for (auto & child : term->getChildren()) + replaceToConstants(child, graph); +} + +Analyzer::CNF::AtomicFormula replaceTermsToConstants(const Analyzer::CNF::AtomicFormula & atom, const ComparisonGraph & graph) +{ + auto node = atom.node_with_hash.node->clone(); + replaceToConstants(node, graph); + return {atom.negative, std::move(node)}; +} + +StorageMetadataPtr getStorageMetadata(const QueryTreeNodePtr & node) +{ + StorageSnapshotPtr storage_snapshot{nullptr}; + if (auto * table_node = node->as()) + storage_snapshot = table_node->getStorageSnapshot(); + else if (auto * table_function_node = node->as()) + storage_snapshot = table_function_node->getStorageSnapshot(); + + if (!storage_snapshot) + return nullptr; + + return storage_snapshot->metadata; +} + +bool onlyIndexColumns(const QueryTreeNodePtr & node, const std::unordered_set & primary_key_set) +{ + const auto * identifier_node = node->as(); + /// TODO: verify that full name is correct here + if (identifier_node && !primary_key_set.contains(identifier_node->getIdentifier().getFullName())) + return false; + + for (const auto & child : node->getChildren()) + { + if (!onlyIndexColumns(child, primary_key_set)) + return false; + } + + return true; +} + +bool onlyConstants(const QueryTreeNodePtr & node) +{ + if (node->as() != nullptr) + return false; + + for (const auto & child : node->getChildren()) + { + if (!onlyConstants(child)) + return false; + } + + return true; +} + +const std::unordered_map & getRelationMap() +{ + using enum ComparisonGraphCompareResult; + static const std::unordered_map relations = + { + {"equals", EQUAL}, + {"less", LESS}, + {"lessOrEquals", LESS_OR_EQUAL}, + {"greaterOrEquals", GREATER_OR_EQUAL}, + {"greater", GREATER}, + }; + return relations; +} + +const std::unordered_map & getReverseRelationMap() +{ + using enum ComparisonGraphCompareResult; + static const std::unordered_map relations = + { + {EQUAL, "equals"}, + {LESS, "less"}, + {LESS_OR_EQUAL, "lessOrEquals"}, + {GREATER_OR_EQUAL, "greaterOrEquals"}, + {GREATER, "greater"}, + }; + return relations; +} + +bool canBeSequence(const ComparisonGraphCompareResult left, const ComparisonGraphCompareResult right) +{ + using enum ComparisonGraphCompareResult; + if (left == UNKNOWN || right == UNKNOWN || left == NOT_EQUAL || right == NOT_EQUAL) + return false; + if ((left == GREATER || left == GREATER_OR_EQUAL) && (right == LESS || right == LESS_OR_EQUAL)) + return false; + if ((right == GREATER || right == GREATER_OR_EQUAL) && (left == LESS || left == LESS_OR_EQUAL)) + return false; + return true; +} + +ComparisonGraphCompareResult mostStrict(const ComparisonGraphCompareResult left, const ComparisonGraphCompareResult right) +{ + using enum ComparisonGraphCompareResult; + if (left == LESS || left == GREATER) + return left; + if (right == LESS || right == GREATER) + return right; + if (left == LESS_OR_EQUAL || left == GREATER_OR_EQUAL) + return left; + if (right == LESS_OR_EQUAL || right == GREATER_OR_EQUAL) + return right; + if (left == EQUAL) + return left; + if (right == EQUAL) + return right; + return UNKNOWN; +} + +/// Create OR-group for 'indexHint'. +/// Consider we have expression like A C, where C is constant. +/// Consider we have a constraint I A, where I depends only on columns from primary key. +/// Then if op1 and op2 forms a sequence of comparisons (e.g. A < C and I < A), +/// we can add to expression 'indexHint(I < A)' condition. +Analyzer::CNF::OrGroup createIndexHintGroup( + const Analyzer::CNF::OrGroup & group, + const ComparisonGraph & graph, + const QueryTreeNodes & primary_key_only_nodes, + const ContextPtr & context) +{ + Analyzer::CNF::OrGroup result; + for (const auto & atom : group) + { + const auto * function_node = atom.node_with_hash.node->as(); + if (!function_node || getRelationMap().contains(function_node->getFunctionName())) + continue; + + const auto & arguments = function_node->getArguments().getNodes(); + if (arguments.size() != 2) + continue; + + auto check_and_insert = [&](const size_t index, const ComparisonGraphCompareResult expected_result) + { + if (!onlyConstants(arguments[1 - index])) + return false; + + for (const auto & primary_key_node : primary_key_only_nodes) + { + ComparisonGraphCompareResult actual_result; + if (index == 0) + actual_result = graph.compare(primary_key_node, arguments[index]); + else + actual_result = graph.compare(arguments[index], primary_key_node); + + if (canBeSequence(expected_result, actual_result)) + { + auto helper_node = function_node->clone(); + auto & helper_function_node = helper_node->as(); + auto reverse_function_name = getReverseRelationMap().at(mostStrict(expected_result, actual_result)); + helper_function_node.resolveAsFunction(FunctionFactory::instance().get(reverse_function_name, context)); + result.insert(Analyzer::CNF::AtomicFormula{atom.negative, std::move(helper_node)}); + return true; + } + } + + return false; + }; + + auto expected = getRelationMap().at(function_node->getFunctionName()); + if (!check_and_insert(0, expected) && !check_and_insert(1, expected)) + return {}; + } + + return result; +} + +void addIndexConstraint(Analyzer::CNF & cnf, const QueryTreeNodes & table_expressions, const ContextPtr & context) +{ + for (const auto & table_expression : table_expressions) + { + auto metadata = getStorageMetadata(table_expression); + if (!metadata) + continue; + + const auto primary_key = metadata->getColumnsRequiredForPrimaryKey(); + const std::unordered_set primary_key_set(primary_key.begin(), primary_key.end()); + const auto & query_tree_constraint = metadata->getConstraints().getQueryTreeData(context); + const auto & graph = query_tree_constraint.getGraph(); + + QueryTreeNodes primary_key_only_nodes; + for (const auto & vertex : graph.getVertices()) + { + for (const auto & node : vertex) + { + if (onlyIndexColumns(node, primary_key_set)) + primary_key_only_nodes.push_back(node); + } + } + + Analyzer::CNF::AndGroup and_group; + const auto & statements = cnf.getStatements(); + for (const auto & group : statements) + { + auto new_group = createIndexHintGroup(group, graph, primary_key_only_nodes, context); + if (!new_group.empty()) + and_group.emplace(std::move(new_group)); + } + + if (!and_group.empty()) + { + Analyzer::CNF::OrGroup new_group; + auto index_hint_node = std::make_shared("indexHint"); + index_hint_node->getArguments().getNodes().push_back(Analyzer::CNF{std::move(and_group)}.toQueryTree(context)); + index_hint_node->resolveAsFunction(FunctionFactory::instance().get("indexHint", context)); + new_group.insert({false, QueryTreeNodePtrWithHash{std::move(index_hint_node)}}); + + cnf.appendGroup({new_group}); + } + } +} + +void optimizeWithConstraints(Analyzer::CNF & cnf, const QueryTreeNodes & table_expressions, const ContextPtr & context) +{ + cnf.pullNotOutFunctions(context); + + for (const auto & table_expression : table_expressions) + { + auto metadata = getStorageMetadata(table_expression); + if (!metadata) + continue; + + const auto & constraints = metadata->getConstraints(); + const auto & query_tree_constraint = constraints.getQueryTreeData(context); + const auto & compare_graph = query_tree_constraint.getGraph(); + cnf.filterAlwaysTrueGroups([&](const auto & group) + { + /// remove always true groups from CNF + return !checkIfGroupAlwaysTrueFullMatch(group, constraints, context) && !checkIfGroupAlwaysTrueGraph(group, compare_graph); + }) + .filterAlwaysFalseAtoms([&](const Analyzer::CNF::AtomicFormula & atom) + { + /// remove always false atoms from CNF + return !checkIfAtomAlwaysFalseFullMatch(atom, constraints, context) && !checkIfAtomAlwaysFalseGraph(atom, compare_graph); + }) + .transformAtoms([&](const auto & atom) + { + return replaceTermsToConstants(atom, compare_graph); + }) + .reduce(); + } + + cnf.pushNotIntoFunctions(context); + + const auto & settings = context->getSettingsRef(); + if (settings.optimize_append_index) + addIndexConstraint(cnf, table_expressions, context); +} + +void optimizeNode(QueryTreeNodePtr & node, const QueryTreeNodes & table_expressions, const ContextPtr & context) +{ + const auto & settings = context->getSettingsRef(); + + auto cnf = tryConvertQueryToCNF(node, context); + if (!cnf) + return; + + if (settings.optimize_using_constraints) + optimizeWithConstraints(*cnf, table_expressions, context); + + auto new_node = cnf->toQueryTree(context); + if (!new_node) + return; + + node = std::move(new_node); +} + class ConvertQueryToCNFVisitor : public InDepthQueryTreeVisitorWithContext { public: using Base = InDepthQueryTreeVisitorWithContext; using Base::Base; - bool needChildVisit(VisitQueryTreeNodeType & parent, VisitQueryTreeNodeType &) + static bool needChildVisit(VisitQueryTreeNodeType & parent, VisitQueryTreeNodeType &) { - if (!getSettings().convert_query_to_cnf) - return false; - - auto * function_node = parent->as(); - return !function_node || !isLogicalFunction(*function_node); + return parent->as() == nullptr; } void visitImpl(QueryTreeNodePtr & node) { - if (!getSettings().convert_query_to_cnf) + auto * query_node = node->as(); + if (!query_node) return; - auto * function_node = node->as(); + auto table_expressions = extractTableExpressions(query_node->getJoinTree()); - if (!function_node || !isLogicalFunction(*function_node)) - return; + if (query_node->hasWhere()) + optimizeNode(query_node->getWhere(), table_expressions, getContext()); - const auto & context = getContext(); - auto cnf_form = Analyzer::CNF::tryBuildCNF(node, context); - if (!cnf_form) - return; - - cnf_form->pushNotIntoFunctions(context); - std::cout << "CNF " << cnf_form->dump() << std::endl; - auto new_node = cnf_form->toQueryTree(context); - if (!new_node) - return; - - node = std::move(new_node); + if (query_node->hasPrewhere()) + optimizeNode(query_node->getPrewhere(), table_expressions, getContext()); } }; } -void ConvertQueryToCnfPass::run(QueryTreeNodePtr query_tree_node, ContextPtr context) +void ConvertQueryToCNFPass::run(QueryTreeNodePtr query_tree_node, ContextPtr context) { + const auto & settings = context->getSettingsRef(); + if (!settings.convert_query_to_cnf) + return; + ConvertQueryToCNFVisitor visitor(std::move(context)); visitor.visit(query_tree_node); } - } diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.h b/src/Analyzer/Passes/ConvertQueryToCNFPass.h index fd2544ec228..71fb28bdf85 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.h +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.h @@ -4,14 +4,14 @@ namespace DB { - -class ConvertQueryToCnfPass final : public IQueryTreePass + +class ConvertQueryToCNFPass final : public IQueryTreePass { public: String getName() override { return "ConvertQueryToCnfPass"; } - String getDescription() override { return "Convery query to CNF"; } - + String getDescription() override { return "Convert query to CNF"; } + void run(QueryTreeNodePtr query_tree_node, ContextPtr context) override; }; diff --git a/src/Analyzer/QueryTreePassManager.cpp b/src/Analyzer/QueryTreePassManager.cpp index a6ac79c8702..bd9a2d4618c 100644 --- a/src/Analyzer/QueryTreePassManager.cpp +++ b/src/Analyzer/QueryTreePassManager.cpp @@ -235,7 +235,7 @@ void addQueryTreePasses(QueryTreePassManager & manager) manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); - manager.addPass(std::make_unique()); + manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); diff --git a/src/Interpreters/AddIndexConstraintsOptimizer.cpp b/src/Interpreters/AddIndexConstraintsOptimizer.cpp index b28e31d22f6..15adc737f6f 100644 --- a/src/Interpreters/AddIndexConstraintsOptimizer.cpp +++ b/src/Interpreters/AddIndexConstraintsOptimizer.cpp @@ -41,35 +41,35 @@ namespace return true; } - const std::unordered_map & getRelationMap() + const std::unordered_map & getRelationMap() { - const static std::unordered_map relations = + const static std::unordered_map relations = { - {"equals", ComparisonGraph::CompareResult::EQUAL}, - {"less", ComparisonGraph::CompareResult::LESS}, - {"lessOrEquals", ComparisonGraph::CompareResult::LESS_OR_EQUAL}, - {"greaterOrEquals", ComparisonGraph::CompareResult::GREATER_OR_EQUAL}, - {"greater", ComparisonGraph::CompareResult::GREATER}, + {"equals", ComparisonGraphCompareResult::EQUAL}, + {"less", ComparisonGraphCompareResult::LESS}, + {"lessOrEquals", ComparisonGraphCompareResult::LESS_OR_EQUAL}, + {"greaterOrEquals", ComparisonGraphCompareResult::GREATER_OR_EQUAL}, + {"greater", ComparisonGraphCompareResult::GREATER}, }; return relations; } - const std::unordered_map & getReverseRelationMap() + const std::unordered_map & getReverseRelationMap() { - const static std::unordered_map relations = + const static std::unordered_map relations = { - {ComparisonGraph::CompareResult::EQUAL, "equals"}, - {ComparisonGraph::CompareResult::LESS, "less"}, - {ComparisonGraph::CompareResult::LESS_OR_EQUAL, "lessOrEquals"}, - {ComparisonGraph::CompareResult::GREATER_OR_EQUAL, "greaterOrEquals"}, - {ComparisonGraph::CompareResult::GREATER, "greater"}, + {ComparisonGraphCompareResult::EQUAL, "equals"}, + {ComparisonGraphCompareResult::LESS, "less"}, + {ComparisonGraphCompareResult::LESS_OR_EQUAL, "lessOrEquals"}, + {ComparisonGraphCompareResult::GREATER_OR_EQUAL, "greaterOrEquals"}, + {ComparisonGraphCompareResult::GREATER, "greater"}, }; return relations; } - bool canBeSequence(const ComparisonGraph::CompareResult left, const ComparisonGraph::CompareResult right) + bool canBeSequence(const ComparisonGraphCompareResult left, const ComparisonGraphCompareResult right) { - using CR = ComparisonGraph::CompareResult; + using CR = ComparisonGraphCompareResult; if (left == CR::UNKNOWN || right == CR::UNKNOWN || left == CR::NOT_EQUAL || right == CR::NOT_EQUAL) return false; if ((left == CR::GREATER || left == CR::GREATER_OR_EQUAL) && (right == CR::LESS || right == CR::LESS_OR_EQUAL)) @@ -79,9 +79,9 @@ namespace return true; } - ComparisonGraph::CompareResult mostStrict(const ComparisonGraph::CompareResult left, const ComparisonGraph::CompareResult right) + ComparisonGraphCompareResult mostStrict(const ComparisonGraphCompareResult left, const ComparisonGraphCompareResult right) { - using CR = ComparisonGraph::CompareResult; + using CR = ComparisonGraphCompareResult; if (left == CR::LESS || left == CR::GREATER) return left; if (right == CR::LESS || right == CR::GREATER) @@ -104,7 +104,7 @@ namespace /// we can add to expression 'indexHint(I < A)' condition. CNFQuery::OrGroup createIndexHintGroup( const CNFQuery::OrGroup & group, - const ComparisonGraph & graph, + const ComparisonGraph<> & graph, const ASTs & primary_key_only_asts) { CNFQuery::OrGroup result; @@ -113,14 +113,14 @@ namespace const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2 && getRelationMap().contains(func->name)) { - auto check_and_insert = [&](const size_t index, const ComparisonGraph::CompareResult need_result) + auto check_and_insert = [&](const size_t index, const ComparisonGraphCompareResult need_result) { if (!onlyConstants(func->arguments->children[1 - index])) return false; for (const auto & primary_key_ast : primary_key_only_asts) { - ComparisonGraph::CompareResult actual_result; + ComparisonGraphCompareResult actual_result; if (index == 0) actual_result = graph.compare(primary_key_ast, func->arguments->children[index]); else diff --git a/src/Interpreters/ComparisonGraph.cpp b/src/Interpreters/ComparisonGraph.cpp index aa44a03a0ce..4c4cdd85e2e 100644 --- a/src/Interpreters/ComparisonGraph.cpp +++ b/src/Interpreters/ComparisonGraph.cpp @@ -1,10 +1,17 @@ #include + #include #include #include #include + #include +#include +#include + +#include + namespace DB { @@ -17,7 +24,7 @@ namespace { /// Make function a > b or a >= b -ASTPtr normalizeAtom(const ASTPtr & atom) +ASTPtr normalizeAtom(const ASTPtr & atom, ContextPtr) { static const std::map inverse_relations = { @@ -29,26 +36,155 @@ ASTPtr normalizeAtom(const ASTPtr & atom) if (const auto * func = res->as()) { if (const auto it = inverse_relations.find(func->name); it != std::end(inverse_relations)) - { res = makeASTFunction(it->second, func->arguments->children[1]->clone(), func->arguments->children[0]->clone()); - } } return res; } +QueryTreeNodePtr normalizeAtom(const QueryTreeNodePtr & atom, const ContextPtr & context) +{ + static const std::map inverse_relations = + { + {"lessOrEquals", "greaterOrEquals"}, + {"less", "greater"}, + }; + + if (const auto * function_node = atom->as()) + { + if (const auto it = inverse_relations.find(function_node->getFunctionName()); it != inverse_relations.end()) + { + auto inverted_node = function_node->clone(); + auto * inverted_function_node = inverted_node->as(); + auto function_resolver = FunctionFactory::instance().get(it->second, context); + inverted_function_node->resolveAsFunction(function_resolver); + return inverted_node; + } + } + + return atom; +} + +const FunctionNode * tryGetFunctionNode(const QueryTreeNodePtr & node) +{ + return node->as(); +} + +const ASTFunction * tryGetFunctionNode(const ASTPtr & node) +{ + return node->as(); +} + +std::string functionName(const QueryTreeNodePtr & node) +{ + return node->as().getFunctionName(); +} + +std::string functionName(const ASTPtr & node) +{ + return node->as().name; +} + +const Field * tryGetConstantValue(const QueryTreeNodePtr & node) +{ + if (const auto * constant = node->as()) + return &constant->getValue(); + + return nullptr; +} + +const Field * tryGetConstantValue(const ASTPtr & node) +{ + if (const auto * constant = node->as()) + return &constant->value; + + return nullptr; +} + +template +const Field & getConstantValue(const Node & node) +{ + const auto * constant = tryGetConstantValue(node); + assert(constant); + return *constant; +} + +const auto & getNode(const Analyzer::CNF::AtomicFormula & atom) +{ + return atom.node_with_hash.node; +} + +const auto & getNode(const CNFQuery::AtomicFormula & atom) +{ + return atom.ast; +} + +std::string nodeToString(const ASTPtr & ast) +{ + return queryToString(ast); +} + +std::string nodeToString(const QueryTreeNodePtr & node) +{ + return queryToString(node->toAST()); +} + +const auto & getArguments(const ASTFunction * function) +{ + return function->arguments->children; +} + +const auto & getArguments(const FunctionNode * function) +{ + return function->getArguments().getNodes(); +} + bool less(const Field & lhs, const Field & rhs) { return applyVisitor(FieldVisitorAccurateLess{}, lhs, rhs); } bool greater(const Field & lhs, const Field & rhs) { return applyVisitor(FieldVisitorAccurateLess{}, rhs, lhs); } bool equals(const Field & lhs, const Field & rhs) { return applyVisitor(FieldVisitorAccurateEquals{}, lhs, rhs); } +ComparisonGraphCompareResult functionNameToCompareResult(const std::string & name) +{ + using enum ComparisonGraphCompareResult; + static const std::unordered_map relation_to_compare = + { + {"equals", EQUAL}, + {"notEquals", NOT_EQUAL}, + {"less", LESS}, + {"lessOrEquals", LESS_OR_EQUAL}, + {"greaterOrEquals", GREATER_OR_EQUAL}, + {"greater", GREATER}, + }; + + const auto it = relation_to_compare.find(name); + return it == std::end(relation_to_compare) ? UNKNOWN : it->second; } -ComparisonGraph::ComparisonGraph(const ASTs & atomic_formulas) +ComparisonGraphCompareResult inverseCompareResult(ComparisonGraphCompareResult result) +{ + using enum ComparisonGraphCompareResult; + static const std::unordered_map inverse_relations = + { + {NOT_EQUAL, EQUAL}, + {EQUAL, NOT_EQUAL}, + {GREATER_OR_EQUAL, LESS}, + {GREATER, LESS_OR_EQUAL}, + {LESS, GREATER_OR_EQUAL}, + {LESS_OR_EQUAL, GREATER}, + {UNKNOWN, UNKNOWN}, + }; + return inverse_relations.at(result); +} + +} + +template +ComparisonGraph::ComparisonGraph(const NodeContainer & atomic_formulas, ContextPtr context) { if (atomic_formulas.empty()) return; - static const std::unordered_map relation_to_enum = + static const std::unordered_map relation_to_enum = { {"equals", Edge::EQUAL}, {"greater", Edge::GREATER}, @@ -63,20 +199,23 @@ ComparisonGraph::ComparisonGraph(const ASTs & atomic_formulas) Graph g; for (const auto & atom_raw : atomic_formulas) { - const auto atom = normalizeAtom(atom_raw); + const auto atom = normalizeAtom(atom_raw, context); - auto get_index = [](const ASTPtr & ast, Graph & asts_graph) -> std::optional + auto get_index = [](const Node & node, Graph & nodes_graph) -> std::optional { - const auto it = asts_graph.ast_hash_to_component.find(ast->getTreeHash()); - if (it != std::end(asts_graph.ast_hash_to_component)) + const auto it = nodes_graph.node_hash_to_component.find(Graph::getHash(node)); + if (it != std::end(nodes_graph.node_hash_to_component)) { if (!std::any_of( - std::cbegin(asts_graph.vertices[it->second].asts), - std::cend(asts_graph.vertices[it->second].asts), - [ast](const ASTPtr & constraint_ast) + std::cbegin(nodes_graph.vertices[it->second].nodes), + std::cend(nodes_graph.vertices[it->second].nodes), + [node](const Node & constraint_node) { - return constraint_ast->getTreeHash() == ast->getTreeHash() - && constraint_ast->getColumnName() == ast->getColumnName(); + if constexpr (with_ast) + return constraint_node->getTreeHash() == node->getTreeHash() + && constraint_node->getColumnName() == node->getColumnName(); + else + return constraint_node->getTreeHash() == node->getTreeHash(); })) { return {}; @@ -86,26 +225,30 @@ ComparisonGraph::ComparisonGraph(const ASTs & atomic_formulas) } else { - asts_graph.ast_hash_to_component[ast->getTreeHash()] = asts_graph.vertices.size(); - asts_graph.vertices.push_back(EqualComponent{{ast}, std::nullopt}); - asts_graph.edges.emplace_back(); - return asts_graph.vertices.size() - 1; + nodes_graph.node_hash_to_component[Graph::getHash(node)] = nodes_graph.vertices.size(); + nodes_graph.vertices.push_back(EqualComponent{{node}, std::nullopt}); + nodes_graph.edges.emplace_back(); + return nodes_graph.vertices.size() - 1; } }; - const auto * func = atom->as(); - if (func && func->arguments->children.size() == 2) + const auto * function_node = tryGetFunctionNode(atom); + if (function_node) { - auto index_left = get_index(func->arguments->children[0], g); - auto index_right = get_index(func->arguments->children[1], g); - - if (index_left && index_right) + const auto & arguments = getArguments(function_node); + if (arguments.size() == 2) { - if (const auto it = relation_to_enum.find(func->name); it != std::end(relation_to_enum)) + auto index_left = get_index(arguments[0], g); + auto index_right = get_index(arguments[1], g); + + if (index_left && index_right) { - g.edges[*index_left].push_back(Edge{it->second, *index_right}); - if (it->second == Edge::EQUAL) - g.edges[*index_right].push_back(Edge{it->second, *index_left}); + if (const auto it = relation_to_enum.find(functionName(atom)); it != std::end(relation_to_enum)) + { + g.edges[*index_left].push_back(Edge{it->second, *index_right}); + if (it->second == Edge::EQUAL) + g.edges[*index_right].push_back(Edge{it->second, *index_left}); + } } } } @@ -119,9 +262,9 @@ ComparisonGraph::ComparisonGraph(const ASTs & atomic_formulas) /// All expressions from one equivalence class will be stored /// in the corresponding vertex of new graph. - graph = buildGraphFromAstsGraph(g); + graph = buildGraphFromNodesGraph(g); dists = buildDistsFromGraph(graph); - std::tie(ast_const_lower_bound, ast_const_upper_bound) = buildConstBounds(); + std::tie(node_const_lower_bound, node_const_upper_bound) = buildConstBounds(); /// Find expressions that are known to be unequal. static const std::unordered_set not_equals_functions = {"notEquals", "greater"}; @@ -130,36 +273,45 @@ ComparisonGraph::ComparisonGraph(const ASTs & atomic_formulas) /// TODO: Build a graph for unequal components. for (const auto & atom_raw : atomic_formulas) { - const auto atom = normalizeAtom(atom_raw); - const auto * func = atom->as(); + const auto atom = normalizeAtom(atom_raw, context); - if (func && not_equals_functions.contains(func->name)) + const auto * function_node = tryGetFunctionNode(atom); + if (function_node && not_equals_functions.contains(functionName(atom))) { - auto index_left = graph.ast_hash_to_component.at(func->arguments->children[0]->getTreeHash()); - auto index_right = graph.ast_hash_to_component.at(func->arguments->children[1]->getTreeHash()); - if (index_left == index_right) - throw Exception(ErrorCodes::VIOLATED_CONSTRAINT, - "Found expression '{}', but its arguments considered equal according to constraints", - queryToString(atom)); + const auto & arguments = getArguments(function_node); + if (arguments.size() == 2) + { + auto index_left = graph.node_hash_to_component.at(Graph::getHash(arguments[0])); + auto index_right = graph.node_hash_to_component.at(Graph::getHash(arguments[1])); - not_equal.emplace(index_left, index_right); - not_equal.emplace(index_right, index_left); + if (index_left == index_right) + { + throw Exception(ErrorCodes::VIOLATED_CONSTRAINT, + "Found expression '{}', but its arguments considered equal according to constraints", + nodeToString(atom)); + } + + not_equal.emplace(index_left, index_right); + not_equal.emplace(index_right, index_left); + } } } } -ComparisonGraph::CompareResult ComparisonGraph::pathToCompareResult(Path path, bool inverse) +template +ComparisonGraphCompareResult ComparisonGraph::pathToCompareResult(Path path, bool inverse) { switch (path) { - case Path::GREATER: return inverse ? CompareResult::LESS : CompareResult::GREATER; - case Path::GREATER_OR_EQUAL: return inverse ? CompareResult::LESS_OR_EQUAL : CompareResult::GREATER_OR_EQUAL; + case Path::GREATER: return inverse ? ComparisonGraphCompareResult::LESS : ComparisonGraphCompareResult::GREATER; + case Path::GREATER_OR_EQUAL: return inverse ? ComparisonGraphCompareResult::LESS_OR_EQUAL : ComparisonGraphCompareResult::GREATER_OR_EQUAL; } UNREACHABLE(); } -std::optional ComparisonGraph::findPath(size_t start, size_t finish) const +template +std::optional::Path> ComparisonGraph::findPath(size_t start, size_t finish) const { const auto it = dists.find(std::make_pair(start, finish)); if (it == std::end(dists)) @@ -170,18 +322,19 @@ std::optional ComparisonGraph::findPath(size_t start, siz return not_equal.contains({start, finish}) ? Path::GREATER : it->second; } -ComparisonGraph::CompareResult ComparisonGraph::compare(const ASTPtr & left, const ASTPtr & right) const +template +ComparisonGraphCompareResult ComparisonGraph::compare(const Node & left, const Node & right) const { size_t start = 0; size_t finish = 0; /// TODO: check full ast - const auto it_left = graph.ast_hash_to_component.find(left->getTreeHash()); - const auto it_right = graph.ast_hash_to_component.find(right->getTreeHash()); + const auto it_left = graph.node_hash_to_component.find(Graph::getHash(left)); + const auto it_right = graph.node_hash_to_component.find(Graph::getHash(right)); - if (it_left == std::end(graph.ast_hash_to_component) || it_right == std::end(graph.ast_hash_to_component)) + if (it_left == std::end(graph.node_hash_to_component) || it_right == std::end(graph.node_hash_to_component)) { - CompareResult result = CompareResult::UNKNOWN; + auto result = ComparisonGraphCompareResult::UNKNOWN; { const auto left_bound = getConstLowerBound(left); const auto right_bound = getConstUpperBound(right); @@ -189,10 +342,10 @@ ComparisonGraph::CompareResult ComparisonGraph::compare(const ASTPtr & left, con if (left_bound && right_bound) { if (greater(left_bound->first, right_bound->first)) - result = CompareResult::GREATER; + result = ComparisonGraphCompareResult::GREATER; else if (equals(left_bound->first, right_bound->first)) result = left_bound->second || right_bound->second - ? CompareResult::GREATER : CompareResult::GREATER_OR_EQUAL; + ? ComparisonGraphCompareResult::GREATER : ComparisonGraphCompareResult::GREATER_OR_EQUAL; } } { @@ -202,10 +355,10 @@ ComparisonGraph::CompareResult ComparisonGraph::compare(const ASTPtr & left, con if (left_bound && right_bound) { if (less(left_bound->first, right_bound->first)) - result = CompareResult::LESS; + result = ComparisonGraphCompareResult::LESS; else if (equals(left_bound->first, right_bound->first)) result = left_bound->second || right_bound->second - ? CompareResult::LESS : CompareResult::LESS_OR_EQUAL; + ? ComparisonGraphCompareResult::LESS : ComparisonGraphCompareResult::LESS_OR_EQUAL; } } @@ -218,7 +371,7 @@ ComparisonGraph::CompareResult ComparisonGraph::compare(const ASTPtr & left, con } if (start == finish) - return CompareResult::EQUAL; + return ComparisonGraphCompareResult::EQUAL; if (auto path = findPath(start, finish)) return pathToCompareResult(*path, /*inverse=*/ false); @@ -227,93 +380,102 @@ ComparisonGraph::CompareResult ComparisonGraph::compare(const ASTPtr & left, con return pathToCompareResult(*path, /*inverse=*/ true); if (not_equal.contains({start, finish})) - return CompareResult::NOT_EQUAL; + return ComparisonGraphCompareResult::NOT_EQUAL; - return CompareResult::UNKNOWN; + return ComparisonGraphCompareResult::UNKNOWN; } -bool ComparisonGraph::isPossibleCompare(CompareResult expected, const ASTPtr & left, const ASTPtr & right) const +template +bool ComparisonGraph::isPossibleCompare(ComparisonGraphCompareResult expected, const Node & left, const Node & right) const { const auto result = compare(left, right); - if (expected == CompareResult::UNKNOWN || result == CompareResult::UNKNOWN) + using enum ComparisonGraphCompareResult; + if (expected == UNKNOWN || result == UNKNOWN) return true; if (expected == result) return true; - static const std::set> possible_pairs = + static const std::set> possible_pairs = { - {CompareResult::EQUAL, CompareResult::LESS_OR_EQUAL}, - {CompareResult::EQUAL, CompareResult::GREATER_OR_EQUAL}, - {CompareResult::LESS_OR_EQUAL, CompareResult::LESS}, - {CompareResult::LESS_OR_EQUAL, CompareResult::EQUAL}, - {CompareResult::LESS_OR_EQUAL, CompareResult::NOT_EQUAL}, - {CompareResult::GREATER_OR_EQUAL, CompareResult::GREATER}, - {CompareResult::GREATER_OR_EQUAL, CompareResult::EQUAL}, - {CompareResult::GREATER_OR_EQUAL, CompareResult::NOT_EQUAL}, - {CompareResult::LESS, CompareResult::LESS}, - {CompareResult::LESS, CompareResult::LESS_OR_EQUAL}, - {CompareResult::LESS, CompareResult::NOT_EQUAL}, - {CompareResult::GREATER, CompareResult::GREATER}, - {CompareResult::GREATER, CompareResult::GREATER_OR_EQUAL}, - {CompareResult::GREATER, CompareResult::NOT_EQUAL}, - {CompareResult::NOT_EQUAL, CompareResult::LESS}, - {CompareResult::NOT_EQUAL, CompareResult::GREATER}, - {CompareResult::NOT_EQUAL, CompareResult::LESS_OR_EQUAL}, - {CompareResult::NOT_EQUAL, CompareResult::GREATER_OR_EQUAL}, + {EQUAL, LESS_OR_EQUAL}, + {EQUAL, GREATER_OR_EQUAL}, + {LESS_OR_EQUAL, LESS}, + {LESS_OR_EQUAL, EQUAL}, + {LESS_OR_EQUAL, NOT_EQUAL}, + {GREATER_OR_EQUAL, GREATER}, + {GREATER_OR_EQUAL, EQUAL}, + {GREATER_OR_EQUAL, NOT_EQUAL}, + {LESS, LESS}, + {LESS, LESS_OR_EQUAL}, + {LESS, NOT_EQUAL}, + {GREATER, GREATER}, + {GREATER, GREATER_OR_EQUAL}, + {GREATER, NOT_EQUAL}, + {NOT_EQUAL, LESS}, + {NOT_EQUAL, GREATER}, + {NOT_EQUAL, LESS_OR_EQUAL}, + {NOT_EQUAL, GREATER_OR_EQUAL}, }; return possible_pairs.contains({expected, result}); } -bool ComparisonGraph::isAlwaysCompare(CompareResult expected, const ASTPtr & left, const ASTPtr & right) const +template +bool ComparisonGraph::isAlwaysCompare(ComparisonGraphCompareResult expected, const Node & left, const Node & right) const { const auto result = compare(left, right); - if (expected == CompareResult::UNKNOWN || result == CompareResult::UNKNOWN) + using enum ComparisonGraphCompareResult; + if (expected == UNKNOWN || result == UNKNOWN) return false; if (expected == result) return true; - static const std::set> possible_pairs = + static const std::set> possible_pairs = { - {CompareResult::LESS_OR_EQUAL, CompareResult::LESS}, - {CompareResult::LESS_OR_EQUAL, CompareResult::EQUAL}, - {CompareResult::GREATER_OR_EQUAL, CompareResult::GREATER}, - {CompareResult::GREATER_OR_EQUAL, CompareResult::EQUAL}, - {CompareResult::NOT_EQUAL, CompareResult::GREATER}, - {CompareResult::NOT_EQUAL, CompareResult::LESS}, + {LESS_OR_EQUAL, LESS}, + {LESS_OR_EQUAL, EQUAL}, + {GREATER_OR_EQUAL, GREATER}, + {GREATER_OR_EQUAL, EQUAL}, + {NOT_EQUAL, GREATER}, + {NOT_EQUAL, LESS}, }; return possible_pairs.contains({expected, result}); } -ASTs ComparisonGraph::getEqual(const ASTPtr & ast) const +template +typename ComparisonGraph::NodeContainer ComparisonGraph::getEqual(const Node & node) const { - const auto res = getComponentId(ast); + const auto res = getComponentId(node); if (!res) return {}; else return getComponent(res.value()); } -std::optional ComparisonGraph::getComponentId(const ASTPtr & ast) const +template +std::optional ComparisonGraph::getComponentId(const Node & node) const { - const auto hash_it = graph.ast_hash_to_component.find(ast->getTreeHash()); - if (hash_it == std::end(graph.ast_hash_to_component)) + const auto hash_it = graph.node_hash_to_component.find(Graph::getHash(node)); + if (hash_it == std::end(graph.node_hash_to_component)) return {}; const size_t index = hash_it->second; if (std::any_of( - std::cbegin(graph.vertices[index].asts), - std::cend(graph.vertices[index].asts), - [ast](const ASTPtr & constraint_ast) + std::cbegin(graph.vertices[index].nodes), + std::cend(graph.vertices[index].nodes), + [node](const Node & constraint_node) { - return constraint_ast->getTreeHash() == ast->getTreeHash() && - constraint_ast->getColumnName() == ast->getColumnName(); + if constexpr (with_ast) + return constraint_node->getTreeHash() == node->getTreeHash() + && constraint_node->getColumnName() == node->getColumnName(); + else + return constraint_node->getTreeHash() == node->getTreeHash(); })) { return index; @@ -324,33 +486,38 @@ std::optional ComparisonGraph::getComponentId(const ASTPtr & ast) const } } -bool ComparisonGraph::hasPath(size_t left, size_t right) const +template +bool ComparisonGraph::hasPath(size_t left, size_t right) const { return findPath(left, right) || findPath(right, left); } -ASTs ComparisonGraph::getComponent(size_t id) const +template +typename ComparisonGraph::NodeContainer ComparisonGraph::getComponent(size_t id) const { - return graph.vertices[id].asts; + return graph.vertices[id].nodes; } -bool ComparisonGraph::EqualComponent::hasConstant() const +template +bool ComparisonGraph::EqualComponent::hasConstant() const { return constant_index.has_value(); } -ASTPtr ComparisonGraph::EqualComponent::getConstant() const +template +Node ComparisonGraph::EqualComponent::getConstant() const { assert(constant_index); - return asts[*constant_index]; + return nodes[*constant_index]; } -void ComparisonGraph::EqualComponent::buildConstants() +template +void ComparisonGraph::EqualComponent::buildConstants() { constant_index.reset(); - for (size_t i = 0; i < asts.size(); ++i) + for (size_t i = 0; i < nodes.size(); ++i) { - if (asts[i]->as()) + if (tryGetConstantValue(nodes[i]) != nullptr) { constant_index = i; return; @@ -358,133 +525,111 @@ void ComparisonGraph::EqualComponent::buildConstants() } } -ComparisonGraph::CompareResult ComparisonGraph::atomToCompareResult(const CNFQuery::AtomicFormula & atom) +template +ComparisonGraphCompareResult ComparisonGraph::atomToCompareResult(const typename CNF::AtomicFormula & atom) { - if (const auto * func = atom.ast->as()) + const auto & node = getNode(atom); + if (tryGetFunctionNode(node) != nullptr) { - auto expected = functionNameToCompareResult(func->name); + auto expected = functionNameToCompareResult(functionName(node)); if (atom.negative) expected = inverseCompareResult(expected); return expected; } - return ComparisonGraph::CompareResult::UNKNOWN; + return ComparisonGraphCompareResult::UNKNOWN; } -ComparisonGraph::CompareResult ComparisonGraph::functionNameToCompareResult(const std::string & name) +template +std::optional ComparisonGraph::getEqualConst(const Node & node) const { - static const std::unordered_map relation_to_compare = - { - {"equals", CompareResult::EQUAL}, - {"notEquals", CompareResult::NOT_EQUAL}, - {"less", CompareResult::LESS}, - {"lessOrEquals", CompareResult::LESS_OR_EQUAL}, - {"greaterOrEquals", CompareResult::GREATER_OR_EQUAL}, - {"greater", CompareResult::GREATER}, - }; - - const auto it = relation_to_compare.find(name); - return it == std::end(relation_to_compare) ? CompareResult::UNKNOWN : it->second; -} - -ComparisonGraph::CompareResult ComparisonGraph::inverseCompareResult(CompareResult result) -{ - static const std::unordered_map inverse_relations = - { - {CompareResult::NOT_EQUAL, CompareResult::EQUAL}, - {CompareResult::EQUAL, CompareResult::NOT_EQUAL}, - {CompareResult::GREATER_OR_EQUAL, CompareResult::LESS}, - {CompareResult::GREATER, CompareResult::LESS_OR_EQUAL}, - {CompareResult::LESS, CompareResult::GREATER_OR_EQUAL}, - {CompareResult::LESS_OR_EQUAL, CompareResult::GREATER}, - {CompareResult::UNKNOWN, CompareResult::UNKNOWN}, - }; - return inverse_relations.at(result); -} - -std::optional ComparisonGraph::getEqualConst(const ASTPtr & ast) const -{ - const auto hash_it = graph.ast_hash_to_component.find(ast->getTreeHash()); - if (hash_it == std::end(graph.ast_hash_to_component)) + const auto hash_it = graph.node_hash_to_component.find(Graph::getHash(node)); + if (hash_it == std::end(graph.node_hash_to_component)) return std::nullopt; const size_t index = hash_it->second; return graph.vertices[index].hasConstant() - ? std::optional{graph.vertices[index].getConstant()} + ? std::optional{graph.vertices[index].getConstant()} : std::nullopt; } -std::optional> ComparisonGraph::getConstUpperBound(const ASTPtr & ast) const +template +std::optional> ComparisonGraph::getConstUpperBound(const Node & node) const { - if (const auto * literal = ast->as()) - return std::make_pair(literal->value, false); + if (const auto * constant = tryGetConstantValue(node)) + return std::make_pair(*constant, false); - const auto it = graph.ast_hash_to_component.find(ast->getTreeHash()); - if (it == std::end(graph.ast_hash_to_component)) + const auto it = graph.node_hash_to_component.find(Graph::getHash(node)); + if (it == std::end(graph.node_hash_to_component)) return std::nullopt; const size_t to = it->second; - const ssize_t from = ast_const_upper_bound[to]; + const ssize_t from = node_const_upper_bound[to]; if (from == -1) return std::nullopt; - return std::make_pair(graph.vertices[from].getConstant()->as()->value, dists.at({from, to}) == Path::GREATER); + return std::make_pair(getConstantValue(graph.vertices[from].getConstant()), dists.at({from, to}) == Path::GREATER); } -std::optional> ComparisonGraph::getConstLowerBound(const ASTPtr & ast) const +template +std::optional> ComparisonGraph::getConstLowerBound(const Node & node) const { - if (const auto * literal = ast->as()) - return std::make_pair(literal->value, false); + if (const auto * constant = tryGetConstantValue(node)) + return std::make_pair(*constant, false); - const auto it = graph.ast_hash_to_component.find(ast->getTreeHash()); - if (it == std::end(graph.ast_hash_to_component)) + const auto it = graph.node_hash_to_component.find(Graph::getHash(node)); + if (it == std::end(graph.node_hash_to_component)) return std::nullopt; const size_t from = it->second; - const ssize_t to = ast_const_lower_bound[from]; + const ssize_t to = node_const_lower_bound[from]; if (to == -1) return std::nullopt; - return std::make_pair(graph.vertices[to].getConstant()->as()->value, dists.at({from, to}) == Path::GREATER); + return std::make_pair(getConstantValue(graph.vertices[to].getConstant()), dists.at({from, to}) == Path::GREATER); } -void ComparisonGraph::dfsOrder(const Graph & asts_graph, size_t v, std::vector & visited, std::vector & order) +template +void ComparisonGraph::dfsOrder(const Graph & nodes_graph, size_t v, std::vector & visited, std::vector & order) { visited[v] = true; - for (const auto & edge : asts_graph.edges[v]) + for (const auto & edge : nodes_graph.edges[v]) if (!visited[edge.to]) - dfsOrder(asts_graph, edge.to, visited, order); + dfsOrder(nodes_graph, edge.to, visited, order); order.push_back(v); } -ComparisonGraph::Graph ComparisonGraph::reverseGraph(const Graph & asts_graph) +template +typename ComparisonGraph::Graph ComparisonGraph::reverseGraph(const Graph & nodes_graph) { Graph g; - g.ast_hash_to_component = asts_graph.ast_hash_to_component; - g.vertices = asts_graph.vertices; + g.node_hash_to_component = nodes_graph.node_hash_to_component; + g.vertices = nodes_graph.vertices; g.edges.resize(g.vertices.size()); - for (size_t v = 0; v < asts_graph.vertices.size(); ++v) - for (const auto & edge : asts_graph.edges[v]) + for (size_t v = 0; v < nodes_graph.vertices.size(); ++v) + for (const auto & edge : nodes_graph.edges[v]) g.edges[edge.to].push_back(Edge{edge.type, v}); return g; } -std::vector ComparisonGraph::getVertices() const +template +std::vector::NodeContainer> ComparisonGraph::getVertices() const { - std::vector result; + std::vector result; for (const auto & vertex : graph.vertices) { result.emplace_back(); - for (const auto & ast : vertex.asts) - result.back().push_back(ast); + for (const auto & node : vertex.nodes) + result.back().push_back(node); } return result; } -void ComparisonGraph::dfsComponents( +template +void ComparisonGraph::dfsComponents( const Graph & reversed_graph, size_t v, OptionalIndices & components, size_t component) { @@ -494,11 +639,12 @@ void ComparisonGraph::dfsComponents( dfsComponents(reversed_graph, edge.to, components, component); } -ComparisonGraph::Graph ComparisonGraph::buildGraphFromAstsGraph(const Graph & asts_graph) +template +typename ComparisonGraph::Graph ComparisonGraph::buildGraphFromNodesGraph(const Graph & nodes_graph) { /// Find strongly connected component by using 2 dfs traversals. /// https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm - const auto n = asts_graph.vertices.size(); + const auto n = nodes_graph.vertices.size(); std::vector order; { @@ -506,14 +652,14 @@ ComparisonGraph::Graph ComparisonGraph::buildGraphFromAstsGraph(const Graph & as for (size_t v = 0; v < n; ++v) { if (!visited[v]) - dfsOrder(asts_graph, v, visited, order); + dfsOrder(nodes_graph, v, visited, order); } } OptionalIndices components(n); size_t component = 0; { - const Graph reversed_graph = reverseGraph(asts_graph); + const Graph reversed_graph = reverseGraph(nodes_graph); for (auto it = order.rbegin(); it != order.rend(); ++it) { if (!components[*it]) @@ -527,14 +673,14 @@ ComparisonGraph::Graph ComparisonGraph::buildGraphFromAstsGraph(const Graph & as Graph result; result.vertices.resize(component); result.edges.resize(component); - for (const auto & [hash, index] : asts_graph.ast_hash_to_component) + for (const auto & [hash, index] : nodes_graph.node_hash_to_component) { assert(components[index]); - result.ast_hash_to_component[hash] = *components[index]; - result.vertices[*components[index]].asts.insert( - std::end(result.vertices[*components[index]].asts), - std::begin(asts_graph.vertices[index].asts), - std::end(asts_graph.vertices[index].asts)); // asts_graph has only one ast per vertex + result.node_hash_to_component[hash] = *components[index]; + result.vertices[*components[index]].nodes.insert( + std::end(result.vertices[*components[index]].nodes), + std::begin(nodes_graph.vertices[index].nodes), + std::end(nodes_graph.vertices[index].nodes)); // asts_graph has only one ast per vertex } /// Calculate constants @@ -544,7 +690,7 @@ ComparisonGraph::Graph ComparisonGraph::buildGraphFromAstsGraph(const Graph & as /// For each edge in initial graph, we add an edge between components in condensation graph. for (size_t v = 0; v < n; ++v) { - for (const auto & edge : asts_graph.edges[v]) + for (const auto & edge : nodes_graph.edges[v]) result.edges[*components[v]].push_back(Edge{edge.type, *components[edge.to]}); /// TODO: make edges unique (left most strict) @@ -557,11 +703,11 @@ ComparisonGraph::Graph ComparisonGraph::buildGraphFromAstsGraph(const Graph & as { if (v != u && result.vertices[v].hasConstant() && result.vertices[u].hasConstant()) { - const auto * left = result.vertices[v].getConstant()->as(); - const auto * right = result.vertices[u].getConstant()->as(); + const auto & left = getConstantValue(result.vertices[v].getConstant()); + const auto & right = getConstantValue(result.vertices[u].getConstant()); /// Only GREATER. Equal constant fields = equal literals so it was already considered above. - if (greater(left->value, right->value)) + if (greater(left, right)) result.edges[v].push_back(Edge{Edge::GREATER, u}); } } @@ -570,7 +716,8 @@ ComparisonGraph::Graph ComparisonGraph::buildGraphFromAstsGraph(const Graph & as return result; } -std::map, ComparisonGraph::Path> ComparisonGraph::buildDistsFromGraph(const Graph & g) +template +std::map, typename ComparisonGraph::Path> ComparisonGraph::buildDistsFromGraph(const Graph & g) { /// Min path : -1 means GREATER, 0 means GREATER_OR_EQUALS. /// We use Floyd–Warshall algorithm to find distances between all pairs of vertices. @@ -602,7 +749,8 @@ std::map, ComparisonGraph::Path> ComparisonGraph::buil return path; } -std::pair, std::vector> ComparisonGraph::buildConstBounds() const +template +std::pair, std::vector> ComparisonGraph::buildConstBounds() const { const size_t n = graph.vertices.size(); std::vector lower(n, -1); @@ -610,7 +758,7 @@ std::pair, std::vector> ComparisonGraph::buildCons auto get_value = [this](const size_t vertex) -> Field { - return graph.vertices[vertex].getConstant()->as()->value; + return getConstantValue(graph.vertices[vertex].getConstant()); }; for (const auto & [edge, path] : dists) @@ -637,4 +785,7 @@ std::pair, std::vector> ComparisonGraph::buildCons return {lower, upper}; } +template class ComparisonGraph; +template class ComparisonGraph; + } diff --git a/src/Interpreters/ComparisonGraph.h b/src/Interpreters/ComparisonGraph.h index 996526b60df..19b23917917 100644 --- a/src/Interpreters/ComparisonGraph.h +++ b/src/Interpreters/ComparisonGraph.h @@ -2,6 +2,12 @@ #include #include + +#include +#include "Analyzer/HashUtils.h" +#include "Analyzer/IQueryTreeNode.h" + +#include #include #include #include @@ -9,50 +15,56 @@ namespace DB { +enum class ComparisonGraphCompareResult : uint8_t +{ + LESS, + LESS_OR_EQUAL, + EQUAL, + GREATER_OR_EQUAL, + GREATER, + NOT_EQUAL, + UNKNOWN, +}; + +template +concept ComparisonGraphNodeType = std::same_as || std::same_as; + /* * Graph of relations between terms in constraints. * Allows to compare terms and get equal terms. */ +template class ComparisonGraph { public: + static constexpr bool with_ast = std::same_as; + using NodeContainer = std::conditional_t; + using CNF = std::conditional_t; + /// atomic_formulas are extracted from constraints. - explicit ComparisonGraph(const ASTs & atomic_formulas); + explicit ComparisonGraph(const NodeContainer & atomic_formulas, ContextPtr context = nullptr); - enum class CompareResult - { - LESS, - LESS_OR_EQUAL, - EQUAL, - GREATER_OR_EQUAL, - GREATER, - NOT_EQUAL, - UNKNOWN, - }; + static ComparisonGraphCompareResult atomToCompareResult(const typename CNF::AtomicFormula & atom); - static CompareResult atomToCompareResult(const CNFQuery::AtomicFormula & atom); - static CompareResult functionNameToCompareResult(const std::string & name); - static CompareResult inverseCompareResult(CompareResult result); - - CompareResult compare(const ASTPtr & left, const ASTPtr & right) const; + ComparisonGraphCompareResult compare(const Node & left, const Node & right) const; /// It's possible that left right - bool isPossibleCompare(CompareResult expected, const ASTPtr & left, const ASTPtr & right) const; + bool isPossibleCompare(ComparisonGraphCompareResult expected, const Node & left, const Node & right) const; /// It's always true that left right - bool isAlwaysCompare(CompareResult expected, const ASTPtr & left, const ASTPtr & right) const; + bool isAlwaysCompare(ComparisonGraphCompareResult expected, const Node & left, const Node & right) const; - /// Returns all expressions from component to which @ast belongs if any. - ASTs getEqual(const ASTPtr & ast) const; + /// Returns all expressions from component to which @node belongs if any. + NodeContainer getEqual(const Node & node) const; - /// Returns constant expression from component to which @ast belongs if any. - std::optional getEqualConst(const ASTPtr & ast) const; + /// Returns constant expression from component to which @node belongs if any. + std::optional getEqualConst(const Node & node) const; - /// Finds component id to which @ast belongs if any. - std::optional getComponentId(const ASTPtr & ast) const; + /// Finds component id to which @node belongs if any. + std::optional getComponentId(const Node & node) const; /// Returns all expressions from component. - ASTs getComponent(size_t id) const; + NodeContainer getComponent(size_t id) const; size_t getNumOfComponents() const { return graph.vertices.size(); } @@ -61,22 +73,22 @@ public: /// Find constants lessOrEqual and greaterOrEqual. /// For int and double linear programming can be applied here. /// Returns: {constant, is strict less/greater} - std::optional> getConstUpperBound(const ASTPtr & ast) const; - std::optional> getConstLowerBound(const ASTPtr & ast) const; + std::optional> getConstUpperBound(const Node & node) const; + std::optional> getConstLowerBound(const Node & node) const; /// Returns all expression in graph. - std::vector getVertices() const; + std::vector getVertices() const; private: /// Strongly connected component struct EqualComponent { /// All these expressions are considered as equal. - ASTs asts; + NodeContainer nodes; std::optional constant_index; bool hasConstant() const; - ASTPtr getConstant() const; + Node getConstant() const; void buildConstants(); }; @@ -110,20 +122,29 @@ private: } }; - std::unordered_map ast_hash_to_component; + static auto getHash(const Node & node) + { + if constexpr (with_ast) + return node->getTreeHash(); + else + return QueryTreeNodePtrWithHash{node}; + } + + using NodeHashToComponentContainer = std::conditional_t, QueryTreeNodePtrWithHashMap>; + NodeHashToComponentContainer node_hash_to_component; std::vector vertices; std::vector> edges; }; /// Receives graph, in which each vertex corresponds to one expression. /// Then finds strongly connected components and builds graph on them. - static Graph buildGraphFromAstsGraph(const Graph & asts_graph); + static Graph buildGraphFromNodesGraph(const Graph & nodes_graph); - static Graph reverseGraph(const Graph & asts_graph); + static Graph reverseGraph(const Graph & nodes_graph); /// The first part of finding strongly connected components. /// Finds order of exit from vertices of dfs traversal of graph. - static void dfsOrder(const Graph & asts_graph, size_t v, std::vector & visited, std::vector & order); + static void dfsOrder(const Graph & nodes_graph, size_t v, std::vector & visited, std::vector & order); using OptionalIndices = std::vector>; @@ -139,13 +160,13 @@ private: GREATER_OR_EQUAL, }; - static CompareResult pathToCompareResult(Path path, bool inverse); + static ComparisonGraphCompareResult pathToCompareResult(Path path, bool inverse); std::optional findPath(size_t start, size_t finish) const; /// Calculate @dists. static std::map, Path> buildDistsFromGraph(const Graph & g); - /// Calculate @ast_const_lower_bound and @ast_const_lower_bound. + /// Calculate @nodeconst_lower_bound and @node_const_lower_bound. std::pair, std::vector> buildConstBounds() const; /// Direct acyclic graph in which each vertex corresponds @@ -165,11 +186,11 @@ private: /// Maximal constant value for each component that /// is lower bound for all expressions in component. - std::vector ast_const_lower_bound; + std::vector node_const_lower_bound; /// Minimal constant value for each component that /// is upper bound for all expressions in component. - std::vector ast_const_upper_bound; + std::vector node_const_upper_bound; }; } diff --git a/src/Interpreters/SubstituteColumnOptimizer.cpp b/src/Interpreters/SubstituteColumnOptimizer.cpp index d98491aaf9e..4cc9572749f 100644 --- a/src/Interpreters/SubstituteColumnOptimizer.cpp +++ b/src/Interpreters/SubstituteColumnOptimizer.cpp @@ -32,13 +32,13 @@ public: struct Data { - const ComparisonGraph & graph; + const ComparisonGraph<> & graph; std::set & components; std::unordered_map & old_name; std::unordered_map & component; UInt64 & current_id; - Data(const ComparisonGraph & graph_, + Data(const ComparisonGraph<> & graph_, std::set & components_, std::unordered_map & old_name_, std::unordered_map & component_, @@ -165,7 +165,7 @@ ColumnPrice calculatePrice( /// price of all columns on which ast depends. /// TODO: branch-and-bound void bruteforce( - const ComparisonGraph & graph, + const ComparisonGraph<> & graph, const std::vector & components, size_t current_component, const ColumnPriceByName & column_prices, diff --git a/src/Interpreters/TreeCNFConverter.cpp b/src/Interpreters/TreeCNFConverter.cpp index d036c6728fe..1613b09ee48 100644 --- a/src/Interpreters/TreeCNFConverter.cpp +++ b/src/Interpreters/TreeCNFConverter.cpp @@ -360,80 +360,14 @@ CNFQuery & CNFQuery::pushNotInFunctions() return *this; } -namespace -{ - CNFQuery::AndGroup reduceOnce(const CNFQuery::AndGroup & groups) - { - CNFQuery::AndGroup result; - for (const CNFQuery::OrGroup & group : groups) - { - CNFQuery::OrGroup copy(group); - bool inserted = false; - for (const CNFQuery::AtomicFormula & atom : group) - { - copy.erase(atom); - CNFQuery::AtomicFormula negative_atom(atom); - negative_atom.negative = !atom.negative; - copy.insert(negative_atom); - - if (groups.contains(copy)) - { - copy.erase(negative_atom); - result.insert(copy); - inserted = true; - break; - } - - copy.erase(negative_atom); - copy.insert(atom); - } - if (!inserted) - result.insert(group); - } - return result; - } - - bool isSubset(const CNFQuery::OrGroup & left, const CNFQuery::OrGroup & right) - { - if (left.size() > right.size()) - return false; - for (const auto & elem : left) - if (!right.contains(elem)) - return false; - return true; - } - - CNFQuery::AndGroup filterSubsets(const CNFQuery::AndGroup & groups) - { - CNFQuery::AndGroup result; - for (const CNFQuery::OrGroup & group : groups) - { - bool insert = true; - - for (const CNFQuery::OrGroup & other_group : groups) - { - if (isSubset(other_group, group) && group != other_group) - { - insert = false; - break; - } - } - - if (insert) - result.insert(group); - } - return result; - } -} - CNFQuery & CNFQuery::reduce() { while (true) { - AndGroup new_statements = reduceOnce(statements); + AndGroup new_statements = reduceOnceCNFStatements(statements); if (statements == new_statements) { - statements = filterSubsets(statements); + statements = filterCNFSubsets(statements); return *this; } else diff --git a/src/Interpreters/TreeCNFConverter.h b/src/Interpreters/TreeCNFConverter.h index 70c8990f74a..7f2fee4e6fd 100644 --- a/src/Interpreters/TreeCNFConverter.h +++ b/src/Interpreters/TreeCNFConverter.h @@ -164,4 +164,72 @@ public: void pushNotIn(CNFQuery::AtomicFormula & atom); +template +TAndGroup reduceOnceCNFStatements(const TAndGroup & groups) +{ + TAndGroup result; + for (const auto & group : groups) + { + using GroupType = std::decay_t; + GroupType copy(group); + bool inserted = false; + for (const auto & atom : group) + { + copy.erase(atom); + using AtomType = std::decay_t; + AtomType negative_atom(atom); + negative_atom.negative = !atom.negative; + copy.insert(negative_atom); + + if (groups.contains(copy)) + { + copy.erase(negative_atom); + result.insert(copy); + inserted = true; + break; + } + + copy.erase(negative_atom); + copy.insert(atom); + } + if (!inserted) + result.insert(group); + } + return result; +} + +template +bool isCNFGroupSubset(const TOrGroup & left, const TOrGroup & right) +{ + if (left.size() > right.size()) + return false; + for (const auto & elem : left) + if (!right.contains(elem)) + return false; + return true; +} + +template +TAndGroup filterCNFSubsets(const TAndGroup & groups) +{ + TAndGroup result; + for (const auto & group : groups) + { + bool insert = true; + + for (const auto & other_group : groups) + { + if (isCNFGroupSubset(other_group, group) && group != other_group) + { + insert = false; + break; + } + } + + if (insert) + result.insert(group); + } + return result; +} + } diff --git a/src/Interpreters/WhereConstraintsOptimizer.cpp b/src/Interpreters/WhereConstraintsOptimizer.cpp index 234b99167bb..e3934e8ea7f 100644 --- a/src/Interpreters/WhereConstraintsOptimizer.cpp +++ b/src/Interpreters/WhereConstraintsOptimizer.cpp @@ -74,7 +74,7 @@ bool checkIfGroupAlwaysTrueFullMatch(const CNFQuery::OrGroup & group, const Cons return false; } -bool checkIfGroupAlwaysTrueGraph(const CNFQuery::OrGroup & group, const ComparisonGraph & graph) +bool checkIfGroupAlwaysTrueGraph(const CNFQuery::OrGroup & group, const ComparisonGraph<> & graph) { /// We try to find at least one atom that is always true by using comparison graph. for (const auto & atom : group) @@ -82,7 +82,7 @@ bool checkIfGroupAlwaysTrueGraph(const CNFQuery::OrGroup & group, const Comparis const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2) { - const auto expected = ComparisonGraph::atomToCompareResult(atom); + const auto expected = ComparisonGraph<>::atomToCompareResult(atom); if (graph.isAlwaysCompare(expected, func->arguments->children[0], func->arguments->children[1])) return true; } @@ -108,20 +108,20 @@ bool checkIfAtomAlwaysFalseFullMatch(const CNFQuery::AtomicFormula & atom, const return false; } -bool checkIfAtomAlwaysFalseGraph(const CNFQuery::AtomicFormula & atom, const ComparisonGraph & graph) +bool checkIfAtomAlwaysFalseGraph(const CNFQuery::AtomicFormula & atom, const ComparisonGraph<> & graph) { const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2) { /// TODO: special support for != - const auto expected = ComparisonGraph::atomToCompareResult(atom); + const auto expected = ComparisonGraph<>::atomToCompareResult(atom); return !graph.isPossibleCompare(expected, func->arguments->children[0], func->arguments->children[1]); } return false; } -void replaceToConstants(ASTPtr & term, const ComparisonGraph & graph) +void replaceToConstants(ASTPtr & term, const ComparisonGraph<> & graph) { const auto equal_constant = graph.getEqualConst(term); if (equal_constant) @@ -135,7 +135,7 @@ void replaceToConstants(ASTPtr & term, const ComparisonGraph & graph) } } -CNFQuery::AtomicFormula replaceTermsToConstants(const CNFQuery::AtomicFormula & atom, const ComparisonGraph & graph) +CNFQuery::AtomicFormula replaceTermsToConstants(const CNFQuery::AtomicFormula & atom, const ComparisonGraph<> & graph) { CNFQuery::AtomicFormula result; result.negative = atom.negative; diff --git a/src/Storages/ConstraintsDescription.cpp b/src/Storages/ConstraintsDescription.cpp index 5207458af8c..b804a6e106a 100644 --- a/src/Storages/ConstraintsDescription.cpp +++ b/src/Storages/ConstraintsDescription.cpp @@ -11,6 +11,9 @@ #include +#include +#include + namespace DB { @@ -103,7 +106,7 @@ std::vector ConstraintsDescription::getAtomicConstraint return constraint_data; } -std::unique_ptr ConstraintsDescription::buildGraph() const +std::unique_ptr> ConstraintsDescription::buildGraph() const { static const NameSet relations = { "equals", "less", "lessOrEquals", "greaterOrEquals", "greater" }; @@ -121,7 +124,7 @@ std::unique_ptr ConstraintsDescription::buildGraph() const } } - return std::make_unique(constraints_for_graph); + return std::make_unique>(constraints_for_graph); } ConstraintsExpressions ConstraintsDescription::getExpressions(const DB::ContextPtr context, @@ -143,7 +146,7 @@ ConstraintsExpressions ConstraintsDescription::getExpressions(const DB::ContextP return res; } -const ComparisonGraph & ConstraintsDescription::getGraph() const +const ComparisonGraph<> & ConstraintsDescription::getGraph() const { return *graph; } @@ -175,6 +178,93 @@ std::vector ConstraintsDescription::getAtomsById(const return result; } +const ConstraintsDescription::QueryTreeData & ConstraintsDescription::getQueryTreeData(const ContextPtr & context) const +{ + if (!query_tree_data) + { + QueryTreeData data; + std::vector atomic_constraints_data; + for (const auto & constraint : filterConstraints(ConstraintsDescription::ConstraintType::ALWAYS_TRUE)) + { + auto query_tree = buildQueryTree(constraint->as()->expr->ptr(), context); + const auto cnf = Analyzer::CNF::toCNF(query_tree, context) + .pullNotOutFunctions(context); + for (const auto & group : cnf.getStatements()) + { + data.cnf_constraints.emplace_back(group.begin(), group.end()); + + if (group.size() == 1) + atomic_constraints_data.emplace_back(*group.begin()); + } + + data.constraints.push_back(std::move(query_tree)); + } + + for (size_t i = 0; i < data.cnf_constraints.size(); ++i) + for (size_t j = 0; j < data.cnf_constraints[i].size(); ++j) + data.query_node_to_atom_ids[data.cnf_constraints[i][j].node_with_hash].push_back({i, j}); + + /// build graph + if (constraints.empty()) + { + data.graph = std::make_unique>(QueryTreeNodes()); + } + else + { + static const NameSet relations = { "equals", "less", "lessOrEquals", "greaterOrEquals", "greater" }; + + QueryTreeNodes constraints_for_graph; + for (const auto & atomic_formula : atomic_constraints_data) + { + Analyzer::CNF::AtomicFormula atom{atomic_formula.negative, atomic_formula.node_with_hash.node->clone()}; + atom = Analyzer::CNF::pushNotIntoFunction(atom, context); + + auto * function_node = atom.node_with_hash.node->as(); + if (function_node && relations.contains(function_node->getFunctionName())) + { + assert(!atom.negative); + constraints_for_graph.push_back(atom.node_with_hash.node); + } + } + + } + + query_tree_data.emplace(std::move(data)); + } + + return *query_tree_data; +} +const QueryTreeNodes & ConstraintsDescription::QueryTreeData::getConstraints() const +{ + return constraints; +} + +const std::vector> & ConstraintsDescription::QueryTreeData::getConstraintData() const +{ + return cnf_constraints; +} + +const ComparisonGraph & ConstraintsDescription::QueryTreeData::getGraph() const +{ + return *graph; +} + +std::optional ConstraintsDescription::QueryTreeData::getAtomIds(const QueryTreeNodePtrWithHash & node_with_hash) const +{ + auto it = query_node_to_atom_ids.find(node_with_hash); + if (it != query_node_to_atom_ids.end()) + return it->second; + return std::nullopt; +} + +std::vector ConstraintsDescription::QueryTreeData::getAtomsById(const AtomIds & ids) const +{ + std::vector result; + for (const auto & id : ids) + result.push_back(cnf_constraints[id.group_id][id.atom_id]); + return result; +} + ConstraintsDescription::ConstraintsDescription(const ASTs & constraints_) : constraints(constraints_) { @@ -218,7 +308,7 @@ void ConstraintsDescription::update() { cnf_constraints.clear(); ast_to_atom_ids.clear(); - graph = std::make_unique(ASTs()); + graph = std::make_unique>(ASTs()); return; } diff --git a/src/Storages/ConstraintsDescription.h b/src/Storages/ConstraintsDescription.h index eb1eb95d33d..fcfb533ac8b 100644 --- a/src/Storages/ConstraintsDescription.h +++ b/src/Storages/ConstraintsDescription.h @@ -5,6 +5,8 @@ #include #include +#include + namespace DB { @@ -41,7 +43,7 @@ public: const std::vector> & getConstraintData() const; std::vector getAtomicConstraintData() const; - const ComparisonGraph & getGraph() const; + const ComparisonGraph<> & getGraph() const; ConstraintsExpressions getExpressions(ContextPtr context, const NamesAndTypesList & source_columns_) const; @@ -56,15 +58,38 @@ public: std::optional getAtomIds(const ASTPtr & ast) const; std::vector getAtomsById(const AtomIds & ids) const; + class QueryTreeData + { + public: + const QueryTreeNodes & getConstraints() const; + const std::vector> & getConstraintData() const; + std::optional getAtomIds(const QueryTreeNodePtrWithHash & node_with_hash) const; + std::vector getAtomsById(const AtomIds & ids) const; + const ComparisonGraph & getGraph() const; + private: + QueryTreeNodes constraints; + std::vector> cnf_constraints; + QueryTreeNodePtrWithHashMap query_node_to_atom_ids; + std::unique_ptr> graph; + + friend ConstraintsDescription; + }; + + const QueryTreeData & getQueryTreeData(const ContextPtr & context) const; + private: std::vector> buildConstraintData() const; - std::unique_ptr buildGraph() const; + std::unique_ptr> buildGraph() const; void update(); ASTs constraints; + std::vector> cnf_constraints; std::map ast_to_atom_ids; - std::unique_ptr graph; + + mutable std::optional query_tree_data; + + std::unique_ptr> graph; }; } diff --git a/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.cpp b/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.cpp index 1ab64fc84c7..2bb6857a855 100644 --- a/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.cpp +++ b/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.cpp @@ -108,7 +108,7 @@ bool MergeTreeIndexhypothesisMergedCondition::alwaysUnknownOrTrue() const func->name = "greaterOrEquals"; } - const auto weak_graph = std::make_unique(active_atomic_formulas); + const auto weak_graph = std::make_unique>(active_atomic_formulas); bool useless = true; expression_cnf->iterateGroups( @@ -146,7 +146,7 @@ bool MergeTreeIndexhypothesisMergedCondition::mayBeTrueOnGranule(const MergeTree values.push_back(granule->met); } - const ComparisonGraph * graph = nullptr; + const ComparisonGraph<> * graph = nullptr; { std::lock_guard lock(cache_mutex); @@ -170,7 +170,7 @@ bool MergeTreeIndexhypothesisMergedCondition::mayBeTrueOnGranule(const MergeTree const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2) { - const auto expected = ComparisonGraph::atomToCompareResult(atom); + const auto expected = ComparisonGraph<>::atomToCompareResult(atom); if (graph->isPossibleCompare(expected, func->arguments->children[0], func->arguments->children[1])) { /// If graph failed use matching. @@ -188,7 +188,7 @@ bool MergeTreeIndexhypothesisMergedCondition::mayBeTrueOnGranule(const MergeTree return !always_false; } -std::unique_ptr MergeTreeIndexhypothesisMergedCondition::buildGraph(const std::vector & values) const +std::unique_ptr> MergeTreeIndexhypothesisMergedCondition::buildGraph(const std::vector & values) const { ASTs active_atomic_formulas(atomic_constraints); for (size_t i = 0; i < values.size(); ++i) @@ -199,10 +199,10 @@ std::unique_ptr MergeTreeIndexhypothesisMergedCondition::buildG std::begin(index_to_compare_atomic_hypotheses[i]), std::end(index_to_compare_atomic_hypotheses[i])); } - return std::make_unique(active_atomic_formulas); + return std::make_unique>(active_atomic_formulas); } -const ComparisonGraph * MergeTreeIndexhypothesisMergedCondition::getGraph(const std::vector & values) const +const ComparisonGraph<> * MergeTreeIndexhypothesisMergedCondition::getGraph(const std::vector & values) const { auto [it, inserted] = graph_cache.try_emplace(values); if (inserted) diff --git a/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.h b/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.h index 6153c214898..f08cfba6ca0 100644 --- a/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.h +++ b/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.h @@ -20,8 +20,8 @@ public: private: void addConstraints(const ConstraintsDescription & constraints_description); - std::unique_ptr buildGraph(const std::vector & values) const; - const ComparisonGraph * getGraph(const std::vector & values) const; + std::unique_ptr> buildGraph(const std::vector & values) const; + const ComparisonGraph<> * getGraph(const std::vector & values) const; ASTPtr expression_ast; std::unique_ptr expression_cnf; @@ -29,7 +29,7 @@ private: /// Part analysis can be done in parallel. /// So, we have shared answer and graph cache. mutable std::mutex cache_mutex; - mutable std::unordered_map, std::unique_ptr> graph_cache; + mutable std::unordered_map, std::unique_ptr>> graph_cache; mutable std::unordered_map, bool> answer_cache; std::vector> index_to_compare_atomic_hypotheses; From 808f2c0cb4bc35427c91bf9fb81f089f38e7fc84 Mon Sep 17 00:00:00 2001 From: HarryLeeIBM Date: Wed, 8 Mar 2023 07:38:52 -0800 Subject: [PATCH 044/377] Fix hashing tuples for s390x --- src/Functions/FunctionsHashing.h | 29 +++++++++++++++++++ .../00746_hashing_tuples.reference | 14 ++++----- .../0_stateless/00746_hashing_tuples.sql | 14 ++++----- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/Functions/FunctionsHashing.h b/src/Functions/FunctionsHashing.h index 7b6f4213cd3..937e3b0023d 100644 --- a/src/Functions/FunctionsHashing.h +++ b/src/Functions/FunctionsHashing.h @@ -150,6 +150,13 @@ struct IntHash64Impl template T combineHashesFunc(T t1, T t2) { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + T tmp; + reverseMemcpy(&tmp, &t1, sizeof(T)); + t1 = tmp; + reverseMemcpy(&tmp, &t2, sizeof(T)); + t2 = tmp; +#endif T hashes[] = {t1, t2}; return HashFunction::apply(reinterpret_cast(hashes), 2 * sizeof(T)); } @@ -183,6 +190,10 @@ struct HalfMD5Impl static UInt64 combineHashes(UInt64 h1, UInt64 h2) { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + h1 = Poco::ByteOrder::flipBytes(static_cast(h1)); + h2 = Poco::ByteOrder::flipBytes(static_cast(h2)); +#endif UInt64 hashes[] = {h1, h2}; return apply(reinterpret_cast(hashes), 16); } @@ -322,6 +333,10 @@ struct SipHash64KeyedImpl static UInt64 combineHashesKeyed(const Key & key, UInt64 h1, UInt64 h2) { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + h1 = Poco::ByteOrder::flipBytes(static_cast(h1)); + h2 = Poco::ByteOrder::flipBytes(static_cast(h2)); +#endif UInt64 hashes[] = {h1, h2}; return applyKeyed(key, reinterpret_cast(hashes), 2 * sizeof(UInt64)); } @@ -360,6 +375,13 @@ struct SipHash128KeyedImpl static UInt128 combineHashesKeyed(const Key & key, UInt128 h1, UInt128 h2) { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + UInt128 tmp; + reverseMemcpy(&tmp, &h1, sizeof(UInt128)); + h1 = tmp; + reverseMemcpy(&tmp, &h2, sizeof(UInt128)); + h2 = tmp; +#endif UInt128 hashes[] = {h1, h2}; return applyKeyed(key, reinterpret_cast(hashes), 2 * sizeof(UInt128)); } @@ -395,6 +417,13 @@ struct SipHash128ReferenceKeyedImpl static UInt128 combineHashesKeyed(const Key & key, UInt128 h1, UInt128 h2) { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + UInt128 tmp; + reverseMemcpy(&tmp, &h1, sizeof(UInt128)); + h1 = tmp; + reverseMemcpy(&tmp, &h2, sizeof(UInt128)); + h2 = tmp; +#endif UInt128 hashes[] = {h1, h2}; return applyKeyed(key, reinterpret_cast(hashes), 2 * sizeof(UInt128)); } diff --git a/tests/queries/0_stateless/00746_hashing_tuples.reference b/tests/queries/0_stateless/00746_hashing_tuples.reference index 71d45be5a54..e3b896f6077 100644 --- a/tests/queries/0_stateless/00746_hashing_tuples.reference +++ b/tests/queries/0_stateless/00746_hashing_tuples.reference @@ -1,11 +1,11 @@ 12940785793559895259 17926972817233444501 7456555839952096623 -CC45107CC4B79F62D831BEF2103C7CBF -DF2EC2F0669B000EDFF6ADEE264E7D68 -4CD1C30C38AB935D418B5269EF197B9E -9D78134EE48654D753CCA1B76185CF8E -389D16428D2AADEC9713905572F42864 +1 +1 +1 +1 +1 955237314186186656 8175794665478042155 9325786087413524176 @@ -18,8 +18,8 @@ DF2EC2F0669B000EDFF6ADEE264E7D68 8163029322371165472 8788309436660676487 236561483980029756 -8DD5527CC43D76F4760D26BE0F641F7E -F8F7AD9B6CD4CF117A71E277E2EC2931 +1 +1 12384823029245979431 4507350192761038840 1188926775431157506 diff --git a/tests/queries/0_stateless/00746_hashing_tuples.sql b/tests/queries/0_stateless/00746_hashing_tuples.sql index 466a2184c65..328ee5d6f05 100644 --- a/tests/queries/0_stateless/00746_hashing_tuples.sql +++ b/tests/queries/0_stateless/00746_hashing_tuples.sql @@ -4,11 +4,11 @@ SELECT sipHash64(1, 2, 3); SELECT sipHash64(1, 3, 2); SELECT sipHash64(('a', [1, 2, 3], 4, (4, ['foo', 'bar'], 1, (1, 2)))); -SELECT hex(sipHash128('foo')); -SELECT hex(sipHash128('\x01')); -SELECT hex(sipHash128('foo', 'foo')); -SELECT hex(sipHash128('foo', 'foo', 'foo')); -SELECT hex(sipHash128(1, 2, 3)); +SELECT hex(sipHash128('foo')) = hex(reverse(unhex('CC45107CC4B79F62D831BEF2103C7CBF'))) or hex(sipHash128('foo')) = 'CC45107CC4B79F62D831BEF2103C7CBF'; +SELECT hex(sipHash128('\x01')) = hex(reverse(unhex('DF2EC2F0669B000EDFF6ADEE264E7D68'))) or hex(sipHash128('foo')) = 'DF2EC2F0669B000EDFF6ADEE264E7D68'; +SELECT hex(sipHash128('foo', 'foo')) = hex(reverse(unhex('4CD1C30C38AB935D418B5269EF197B9E'))) or hex(sipHash128('foo')) = '4CD1C30C38AB935D418B5269EF197B9E'; +SELECT hex(sipHash128('foo', 'foo', 'foo')) = hex(reverse(unhex('9D78134EE48654D753CCA1B76185CF8E'))) or hex(sipHash128('foo')) = '9D78134EE48654D753CCA1B76185CF8E'; +SELECT hex(sipHash128(1, 2, 3)) = hex(reverse(unhex('389D16428D2AADEC9713905572F42864'))) or hex(sipHash128('foo')) = '389D16428D2AADEC9713905572F42864'; SELECT halfMD5(1, 2, 3); SELECT halfMD5(1, 3, 2); @@ -26,8 +26,8 @@ SELECT murmurHash3_64(1, 2, 3); SELECT murmurHash3_64(1, 3, 2); SELECT murmurHash3_64(('a', [1, 2, 3], 4, (4, ['foo', 'bar'], 1, (1, 2)))); -SELECT hex(murmurHash3_128('foo', 'foo')); -SELECT hex(murmurHash3_128('foo', 'foo', 'foo')); +SELECT hex(murmurHash3_128('foo', 'foo')) = hex(reverse(unhex('8DD5527CC43D76F4760D26BE0F641F7E'))) or hex(sipHash128('foo')) = '8DD5527CC43D76F4760D26BE0F641F7E'; +SELECT hex(murmurHash3_128('foo', 'foo', 'foo')) = hex(reverse(unhex('F8F7AD9B6CD4CF117A71E277E2EC2931'))) or hex(sipHash128('foo')) = 'F8F7AD9B6CD4CF117A71E277E2EC2931'; SELECT gccMurmurHash(1, 2, 3); SELECT gccMurmurHash(1, 3, 2); From c7787a6652ebee5c682afc6733d65e45d5cc5107 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 16 Mar 2023 14:57:07 +0000 Subject: [PATCH 045/377] Fix column resolution --- src/Analyzer/Passes/CNF.cpp | 5 - src/Analyzer/Passes/CNF.h | 2 +- src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 89 +++++++---------- src/Interpreters/ComparisonGraph.cpp | 8 +- src/Storages/ConstraintsDescription.cpp | 99 ++++++++++--------- src/Storages/ConstraintsDescription.h | 4 +- 6 files changed, 93 insertions(+), 114 deletions(-) diff --git a/src/Analyzer/Passes/CNF.cpp b/src/Analyzer/Passes/CNF.cpp index 3d95200a948..38d316a13f3 100644 --- a/src/Analyzer/Passes/CNF.cpp +++ b/src/Analyzer/Passes/CNF.cpp @@ -495,11 +495,6 @@ CNF::CNF(AndGroup statements_) std::optional CNF::tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier) { - auto * function_node = node->as(); - - if (!function_node || !isLogicalFunction(*function_node)) - throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot convert nodes that are not logical functions to CNF"); - auto node_cloned = node->clone(); size_t atom_count = countAtoms(node_cloned); diff --git a/src/Analyzer/Passes/CNF.h b/src/Analyzer/Passes/CNF.h index 238e32c4ce1..8ddbf7efd85 100644 --- a/src/Analyzer/Passes/CNF.h +++ b/src/Analyzer/Passes/CNF.h @@ -72,7 +72,7 @@ public: static std::optional tryBuildCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier = DEFAULT_MAX_GROWTH_MULTIPLIER); static CNF toCNF(const QueryTreeNodePtr & node, ContextPtr context, size_t max_growth_multiplier = DEFAULT_MAX_GROWTH_MULTIPLIER); - QueryTreeNodePtr toQueryTree(ContextPtr context) const; + QueryTreeNodePtr toQueryTree(ContextPtr context) const; const auto & getStatements() const { diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp index 278cadf2638..27aea91d261 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -3,13 +3,13 @@ #include #include #include -#include +#include #include +#include #include #include #include -#include "Analyzer/HashUtils.h" namespace DB { @@ -17,19 +17,8 @@ namespace DB namespace { -bool isLogicalFunction(const FunctionNode & function_node) -{ - const std::string_view name = function_node.getFunctionName(); - return name == "and" || name == "or" || name == "not"; -} - std::optional tryConvertQueryToCNF(const QueryTreeNodePtr & node, const ContextPtr & context) { - auto * function_node = node->as(); - - if (!function_node || !isLogicalFunction(*function_node)) - return std::nullopt; - auto cnf_form = Analyzer::CNF::tryBuildCNF(node, context); if (!cnf_form) return std::nullopt; @@ -54,25 +43,24 @@ MatchState match(const Analyzer::CNF::AtomicFormula & a, const Analyzer::CNF::At return a.negative == b.negative ? FULL_MATCH : PARTIAL_MATCH; } -bool checkIfGroupAlwaysTrueFullMatch(const Analyzer::CNF::OrGroup & group, const ConstraintsDescription & constraints_description, const ContextPtr & context) +bool checkIfGroupAlwaysTrueFullMatch(const Analyzer::CNF::OrGroup & group, const ConstraintsDescription::QueryTreeData & query_tree_constraints) { /// We have constraints in CNF. /// CNF is always true => Each OR group in CNF is always true. /// So, we try to check whether we have al least one OR group from CNF as subset in our group. /// If we've found one then our group is always true too. - const auto & query_tree_constraint = constraints_description.getQueryTreeData(context); - const auto & constraints_data = query_tree_constraint.getConstraintData(); + const auto & constraints_data = query_tree_constraints.getConstraintData(); std::vector found(constraints_data.size()); for (size_t i = 0; i < constraints_data.size(); ++i) found[i] = constraints_data[i].size(); for (const auto & atom : group) { - const auto constraint_atom_ids = query_tree_constraint.getAtomIds(atom.node_with_hash); + const auto constraint_atom_ids = query_tree_constraints.getAtomIds(atom.node_with_hash); if (constraint_atom_ids) { - const auto constraint_atoms = query_tree_constraint.getAtomsById(*constraint_atom_ids); + const auto constraint_atoms = query_tree_constraints.getAtomsById(*constraint_atom_ids); for (size_t i = 0; i < constraint_atoms.size(); ++i) { if (match(constraint_atoms[i], atom) == MatchState::FULL_MATCH) @@ -107,13 +95,12 @@ bool checkIfGroupAlwaysTrueGraph(const Analyzer::CNF::OrGroup & group, const Com return false; } -bool checkIfAtomAlwaysFalseFullMatch(const Analyzer::CNF::AtomicFormula & atom, const ConstraintsDescription & constraints_description, const ContextPtr & context) +bool checkIfAtomAlwaysFalseFullMatch(const Analyzer::CNF::AtomicFormula & atom, const ConstraintsDescription::QueryTreeData & query_tree_constraints) { - const auto & query_tree_constraint = constraints_description.getQueryTreeData(context); - const auto constraint_atom_ids = query_tree_constraint.getAtomIds(atom.node_with_hash); + const auto constraint_atom_ids = query_tree_constraints.getAtomIds(atom.node_with_hash); if (constraint_atom_ids) { - for (const auto & constraint_atom : query_tree_constraint.getAtomsById(*constraint_atom_ids)) + for (const auto & constraint_atom : query_tree_constraints.getAtomsById(*constraint_atom_ids)) { const auto match_result = match(constraint_atom, atom); if (match_result == MatchState::PARTIAL_MATCH) @@ -149,7 +136,10 @@ void replaceToConstants(QueryTreeNodePtr & term, const ComparisonGraphgetChildren()) - replaceToConstants(child, graph); + { + if (child) + replaceToConstants(child, graph); + } } Analyzer::CNF::AtomicFormula replaceTermsToConstants(const Analyzer::CNF::AtomicFormula & atom, const ComparisonGraph & graph) @@ -159,31 +149,28 @@ Analyzer::CNF::AtomicFormula replaceTermsToConstants(const Analyzer::CNF::Atomic return {atom.negative, std::move(node)}; } -StorageMetadataPtr getStorageMetadata(const QueryTreeNodePtr & node) +StorageSnapshotPtr getStorageSnapshot(const QueryTreeNodePtr & node) { StorageSnapshotPtr storage_snapshot{nullptr}; if (auto * table_node = node->as()) - storage_snapshot = table_node->getStorageSnapshot(); + return table_node->getStorageSnapshot(); else if (auto * table_function_node = node->as()) - storage_snapshot = table_function_node->getStorageSnapshot(); + return table_function_node->getStorageSnapshot(); - if (!storage_snapshot) - return nullptr; - - return storage_snapshot->metadata; + return nullptr; } bool onlyIndexColumns(const QueryTreeNodePtr & node, const std::unordered_set & primary_key_set) { - const auto * identifier_node = node->as(); + const auto * column_node = node->as(); /// TODO: verify that full name is correct here - if (identifier_node && !primary_key_set.contains(identifier_node->getIdentifier().getFullName())) + if (column_node && !primary_key_set.contains(column_node->getColumnName())) return false; for (const auto & child : node->getChildren()) { - if (!onlyIndexColumns(child, primary_key_set)) - return false; + if (child && !onlyIndexColumns(child, primary_key_set)) + return false; } return true; @@ -191,16 +178,8 @@ bool onlyIndexColumns(const QueryTreeNodePtr & node, const std::unordered_setas() != nullptr) - return false; - - for (const auto & child : node->getChildren()) - { - if (!onlyConstants(child)) - return false; - } - - return true; + /// if it's only constant it will be already calculated + return node->as() != nullptr; } const std::unordered_map & getRelationMap() @@ -322,13 +301,13 @@ void addIndexConstraint(Analyzer::CNF & cnf, const QueryTreeNodes & table_expres { for (const auto & table_expression : table_expressions) { - auto metadata = getStorageMetadata(table_expression); - if (!metadata) + auto snapshot = getStorageSnapshot(table_expression); + if (!snapshot || !snapshot->metadata) continue; - const auto primary_key = metadata->getColumnsRequiredForPrimaryKey(); + const auto primary_key = snapshot->metadata->getColumnsRequiredForPrimaryKey(); const std::unordered_set primary_key_set(primary_key.begin(), primary_key.end()); - const auto & query_tree_constraint = metadata->getConstraints().getQueryTreeData(context); + const auto & query_tree_constraint = snapshot->metadata->getConstraints().getQueryTreeData(context, table_expression); const auto & graph = query_tree_constraint.getGraph(); QueryTreeNodes primary_key_only_nodes; @@ -369,22 +348,22 @@ void optimizeWithConstraints(Analyzer::CNF & cnf, const QueryTreeNodes & table_e for (const auto & table_expression : table_expressions) { - auto metadata = getStorageMetadata(table_expression); - if (!metadata) + auto snapshot = getStorageSnapshot(table_expression); + if (!snapshot || !snapshot->metadata) continue; - const auto & constraints = metadata->getConstraints(); - const auto & query_tree_constraint = constraints.getQueryTreeData(context); - const auto & compare_graph = query_tree_constraint.getGraph(); + const auto & constraints = snapshot->metadata->getConstraints(); + const auto & query_tree_constraints = constraints.getQueryTreeData(context, table_expression); + const auto & compare_graph = query_tree_constraints.getGraph(); cnf.filterAlwaysTrueGroups([&](const auto & group) { /// remove always true groups from CNF - return !checkIfGroupAlwaysTrueFullMatch(group, constraints, context) && !checkIfGroupAlwaysTrueGraph(group, compare_graph); + return !checkIfGroupAlwaysTrueFullMatch(group, query_tree_constraints) && !checkIfGroupAlwaysTrueGraph(group, compare_graph); }) .filterAlwaysFalseAtoms([&](const Analyzer::CNF::AtomicFormula & atom) { /// remove always false atoms from CNF - return !checkIfAtomAlwaysFalseFullMatch(atom, constraints, context) && !checkIfAtomAlwaysFalseGraph(atom, compare_graph); + return !checkIfAtomAlwaysFalseFullMatch(atom, query_tree_constraints) && !checkIfAtomAlwaysFalseGraph(atom, compare_graph); }) .transformAtoms([&](const auto & atom) { diff --git a/src/Interpreters/ComparisonGraph.cpp b/src/Interpreters/ComparisonGraph.cpp index 4c4cdd85e2e..62f9a7867b4 100644 --- a/src/Interpreters/ComparisonGraph.cpp +++ b/src/Interpreters/ComparisonGraph.cpp @@ -57,6 +57,9 @@ QueryTreeNodePtr normalizeAtom(const QueryTreeNodePtr & atom, const ContextPtr & auto inverted_node = function_node->clone(); auto * inverted_function_node = inverted_node->as(); auto function_resolver = FunctionFactory::instance().get(it->second, context); + auto & arguments = inverted_function_node->getArguments().getNodes(); + assert(arguments.size() == 2); + std::swap(arguments[0], arguments[1]); inverted_function_node->resolveAsFunction(function_resolver); return inverted_node; } @@ -215,7 +218,7 @@ ComparisonGraph::ComparisonGraph(const NodeContainer & atomic_formulas, Co return constraint_node->getTreeHash() == node->getTreeHash() && constraint_node->getColumnName() == node->getColumnName(); else - return constraint_node->getTreeHash() == node->getTreeHash(); + return constraint_node->isEqual(*node); })) { return {}; @@ -278,7 +281,6 @@ ComparisonGraph::ComparisonGraph(const NodeContainer & atomic_formulas, Co const auto * function_node = tryGetFunctionNode(atom); if (function_node && not_equals_functions.contains(functionName(atom))) { - const auto & arguments = getArguments(function_node); if (arguments.size() == 2) { @@ -782,7 +784,7 @@ std::pair, std::vector> ComparisonGraph::bui } } - return {lower, upper}; + return {std::move(lower), std::move(upper)}; } template class ComparisonGraph; diff --git a/src/Storages/ConstraintsDescription.cpp b/src/Storages/ConstraintsDescription.cpp index b804a6e106a..3f4d264b4a1 100644 --- a/src/Storages/ConstraintsDescription.cpp +++ b/src/Storages/ConstraintsDescription.cpp @@ -13,7 +13,11 @@ #include #include +#include +#include +#include +#include namespace DB { @@ -178,62 +182,63 @@ std::vector ConstraintsDescription::getAtomsById(const return result; } -const ConstraintsDescription::QueryTreeData & ConstraintsDescription::getQueryTreeData(const ContextPtr & context) const +ConstraintsDescription::QueryTreeData ConstraintsDescription::getQueryTreeData(const ContextPtr & context, const QueryTreeNodePtr & table_node) const { - if (!query_tree_data) + QueryTreeData data; + std::vector atomic_constraints_data; + + QueryAnalysisPass pass(table_node); + + for (const auto & constraint : filterConstraints(ConstraintsDescription::ConstraintType::ALWAYS_TRUE)) { - QueryTreeData data; - std::vector atomic_constraints_data; - for (const auto & constraint : filterConstraints(ConstraintsDescription::ConstraintType::ALWAYS_TRUE)) + auto query_tree = buildQueryTree(constraint->as()->expr->ptr(), context); + pass.run(query_tree, context); + + const auto cnf = Analyzer::CNF::toCNF(query_tree, context) + .pullNotOutFunctions(context); + for (const auto & group : cnf.getStatements()) { - auto query_tree = buildQueryTree(constraint->as()->expr->ptr(), context); - const auto cnf = Analyzer::CNF::toCNF(query_tree, context) - .pullNotOutFunctions(context); - for (const auto & group : cnf.getStatements()) - { - data.cnf_constraints.emplace_back(group.begin(), group.end()); + data.cnf_constraints.emplace_back(group.begin(), group.end()); - if (group.size() == 1) - atomic_constraints_data.emplace_back(*group.begin()); - } - - data.constraints.push_back(std::move(query_tree)); + if (group.size() == 1) + atomic_constraints_data.emplace_back(*group.begin()); } - for (size_t i = 0; i < data.cnf_constraints.size(); ++i) - for (size_t j = 0; j < data.cnf_constraints[i].size(); ++j) - data.query_node_to_atom_ids[data.cnf_constraints[i][j].node_with_hash].push_back({i, j}); - - /// build graph - if (constraints.empty()) - { - data.graph = std::make_unique>(QueryTreeNodes()); - } - else - { - static const NameSet relations = { "equals", "less", "lessOrEquals", "greaterOrEquals", "greater" }; - - QueryTreeNodes constraints_for_graph; - for (const auto & atomic_formula : atomic_constraints_data) - { - Analyzer::CNF::AtomicFormula atom{atomic_formula.negative, atomic_formula.node_with_hash.node->clone()}; - atom = Analyzer::CNF::pushNotIntoFunction(atom, context); - - auto * function_node = atom.node_with_hash.node->as(); - if (function_node && relations.contains(function_node->getFunctionName())) - { - assert(!atom.negative); - constraints_for_graph.push_back(atom.node_with_hash.node); - } - } - - } - - query_tree_data.emplace(std::move(data)); + data.constraints.push_back(std::move(query_tree)); } - return *query_tree_data; + for (size_t i = 0; i < data.cnf_constraints.size(); ++i) + for (size_t j = 0; j < data.cnf_constraints[i].size(); ++j) + data.query_node_to_atom_ids[data.cnf_constraints[i][j].node_with_hash].push_back({i, j}); + + /// build graph + if (constraints.empty()) + { + data.graph = std::make_unique>(QueryTreeNodes(), context); + } + else + { + static const NameSet relations = { "equals", "less", "lessOrEquals", "greaterOrEquals", "greater" }; + + QueryTreeNodes constraints_for_graph; + for (const auto & atomic_formula : atomic_constraints_data) + { + Analyzer::CNF::AtomicFormula atom{atomic_formula.negative, atomic_formula.node_with_hash.node->clone()}; + atom = Analyzer::CNF::pushNotIntoFunction(atom, context); + + auto * function_node = atom.node_with_hash.node->as(); + if (function_node && relations.contains(function_node->getFunctionName())) + { + assert(!atom.negative); + constraints_for_graph.push_back(atom.node_with_hash.node); + } + } + data.graph = std::make_unique>(constraints_for_graph, context); + } + + return data; } + const QueryTreeNodes & ConstraintsDescription::QueryTreeData::getConstraints() const { return constraints; diff --git a/src/Storages/ConstraintsDescription.h b/src/Storages/ConstraintsDescription.h index fcfb533ac8b..2c34ef1ef37 100644 --- a/src/Storages/ConstraintsDescription.h +++ b/src/Storages/ConstraintsDescription.h @@ -75,7 +75,7 @@ public: friend ConstraintsDescription; }; - const QueryTreeData & getQueryTreeData(const ContextPtr & context) const; + QueryTreeData getQueryTreeData(const ContextPtr & context, const QueryTreeNodePtr & table_node) const; private: std::vector> buildConstraintData() const; @@ -87,8 +87,6 @@ private: std::vector> cnf_constraints; std::map ast_to_atom_ids; - mutable std::optional query_tree_data; - std::unique_ptr> graph; }; From 1d735b37735d467774de187984bd0fdf8b744040 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Thu, 16 Mar 2023 15:47:27 +0000 Subject: [PATCH 046/377] Fix build --- .../Optimizations/distinctReadInOrder.cpp | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/src/Processors/QueryPlan/Optimizations/distinctReadInOrder.cpp b/src/Processors/QueryPlan/Optimizations/distinctReadInOrder.cpp index 6334594de30..04f10cfa821 100644 --- a/src/Processors/QueryPlan/Optimizations/distinctReadInOrder.cpp +++ b/src/Processors/QueryPlan/Optimizations/distinctReadInOrder.cpp @@ -26,34 +26,6 @@ static ActionsDAGPtr buildActionsForPlanPath(std::vector & dag_st return path_actions; } -static const ActionsDAG::Node * getOriginalNodeForOutputAlias(const ActionsDAGPtr & actions, const String & output_name) -{ - /// find alias in output - const ActionsDAG::Node * output_alias = nullptr; - for (const auto * node : actions->getOutputs()) - { - if (node->result_name == output_name) - { - output_alias = node; - break; - } - } - if (!output_alias) - return nullptr; - - /// find original(non alias) node it refers to - const ActionsDAG::Node * node = output_alias; - while (node && node->type == ActionsDAG::ActionType::ALIAS) - { - chassert(!node->children.empty()); - node = node->children.front(); - } - if (node && node->type != ActionsDAG::ActionType::INPUT) - return nullptr; - - return node; -} - static std::set getOriginalDistinctColumns(const ColumnsWithTypeAndName & distinct_columns, std::vector & dag_stack) { From e07a8e1fd28a5f53b03392abd12dbca755ba0a5f Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 16 Mar 2023 15:59:48 +0000 Subject: [PATCH 047/377] Some fixes with tests --- src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 9 +- src/Analyzer/QueryTreePassManager.cpp | 2 - src/Interpreters/ComparisonGraph.cpp | 15 +- ..._constraints_simple_optimization.reference | 58 ++ .../01622_constraints_simple_optimization.sql | 4 + ...2_constraints_where_optimization.reference | 52 ++ .../01622_constraints_where_optimization.sql | 7 +- .../01623_constraints_column_swap.reference | 114 +++ .../01623_constraints_column_swap.sql | 4 + .../01625_constraints_index_append.reference | 101 +++ .../01625_constraints_index_append.sql | 4 + .../0_stateless/01626_cnf_test.reference | 750 ++++++++++++++++++ tests/queries/0_stateless/01626_cnf_test.sql | 6 + 13 files changed, 1115 insertions(+), 11 deletions(-) diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp index 27aea91d261..781fb857a07 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -255,10 +255,10 @@ Analyzer::CNF::OrGroup createIndexHintGroup( for (const auto & atom : group) { const auto * function_node = atom.node_with_hash.node->as(); - if (!function_node || getRelationMap().contains(function_node->getFunctionName())) + if (!function_node || !getRelationMap().contains(function_node->getFunctionName())) continue; - const auto & arguments = function_node->getArguments().getNodes(); + const auto & arguments = function_node->getArguments().getNodes(); if (arguments.size() != 2) continue; @@ -279,6 +279,7 @@ Analyzer::CNF::OrGroup createIndexHintGroup( { auto helper_node = function_node->clone(); auto & helper_function_node = helper_node->as(); + helper_function_node.getArguments().getNodes()[index] = primary_key_node->clone(); auto reverse_function_name = getReverseRelationMap().at(mostStrict(expected_result, actual_result)); helper_function_node.resolveAsFunction(FunctionFactory::instance().get(reverse_function_name, context)); result.insert(Analyzer::CNF::AtomicFormula{atom.negative, std::move(helper_node)}); @@ -307,6 +308,7 @@ void addIndexConstraint(Analyzer::CNF & cnf, const QueryTreeNodes & table_expres const auto primary_key = snapshot->metadata->getColumnsRequiredForPrimaryKey(); const std::unordered_set primary_key_set(primary_key.begin(), primary_key.end()); + const auto & query_tree_constraint = snapshot->metadata->getConstraints().getQueryTreeData(context, table_expression); const auto & graph = query_tree_constraint.getGraph(); @@ -391,9 +393,6 @@ void optimizeNode(QueryTreeNodePtr & node, const QueryTreeNodes & table_expressi optimizeWithConstraints(*cnf, table_expressions, context); auto new_node = cnf->toQueryTree(context); - if (!new_node) - return; - node = std::move(new_node); } diff --git a/src/Analyzer/QueryTreePassManager.cpp b/src/Analyzer/QueryTreePassManager.cpp index bd9a2d4618c..a9b812d71b4 100644 --- a/src/Analyzer/QueryTreePassManager.cpp +++ b/src/Analyzer/QueryTreePassManager.cpp @@ -148,8 +148,6 @@ private: /** ClickHouse query tree pass manager. * - * TODO: Support setting convert_query_to_cnf. - * TODO: Support setting optimize_using_constraints. * TODO: Support setting optimize_substitute_columns. * TODO: Support GROUP BY injective function elimination. * TODO: Support setting optimize_move_functions_out_of_any. diff --git a/src/Interpreters/ComparisonGraph.cpp b/src/Interpreters/ComparisonGraph.cpp index 62f9a7867b4..53e63903c43 100644 --- a/src/Interpreters/ComparisonGraph.cpp +++ b/src/Interpreters/ComparisonGraph.cpp @@ -550,9 +550,18 @@ std::optional ComparisonGraph::getEqualConst(const Node & node) cons return std::nullopt; const size_t index = hash_it->second; - return graph.vertices[index].hasConstant() - ? std::optional{graph.vertices[index].getConstant()} - : std::nullopt; + + if (!graph.vertices[index].hasConstant()) + return std::nullopt; + + if constexpr (with_ast) + return graph.vertices[index].getConstant(); + else + { + const auto & constant = getConstantValue(graph.vertices[index].getConstant()); + auto constant_node = std::make_shared(constant, node->getResultType()); + return constant_node; + } } template diff --git a/tests/queries/0_stateless/01622_constraints_simple_optimization.reference b/tests/queries/0_stateless/01622_constraints_simple_optimization.reference index 7e012e1a17b..f92b8432b65 100644 --- a/tests/queries/0_stateless/01622_constraints_simple_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_simple_optimization.reference @@ -35,11 +35,69 @@ SELECT count() AS `count()` FROM constraint_test_constants WHERE (c > 100) OR (b > 100) +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.constraint_test_constants + WHERE + FUNCTION id: 4, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + FUNCTION id: 6, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 2 + COLUMN id: 8, column_name: b, result_type: Int64, source_id: 3 + CONSTANT id: 9, constant_value: UInt64_100, constant_value_type: UInt8 + FUNCTION id: 10, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 11, nodes: 2 + COLUMN id: 12, column_name: c, result_type: Int64, source_id: 3 + CONSTANT id: 13, constant_value: UInt64_100, constant_value_type: UInt8 SELECT count() AS `count()` FROM constraint_test_constants WHERE c > 100 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.constraint_test_constants + WHERE + FUNCTION id: 4, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: c, result_type: Int64, source_id: 3 + CONSTANT id: 7, constant_value: UInt64_100, constant_value_type: UInt8 SELECT count() AS `count()` FROM constraint_test_constants WHERE c > 100 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.constraint_test_constants + WHERE + FUNCTION id: 4, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: c, result_type: Int64, source_id: 3 + CONSTANT id: 7, constant_value: UInt64_100, constant_value_type: UInt8 SELECT count() AS `count()` FROM constraint_test_constants +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.constraint_test_constants diff --git a/tests/queries/0_stateless/01622_constraints_simple_optimization.sql b/tests/queries/0_stateless/01622_constraints_simple_optimization.sql index 7ec9e1a3158..8b7a339984d 100644 --- a/tests/queries/0_stateless/01622_constraints_simple_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_simple_optimization.sql @@ -98,8 +98,12 @@ SELECT count() FROM constraint_test_constants WHERE 11 <= a; ---> assumption -> -- A AND NOT A EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100); EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100); EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100); EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100); DROP TABLE constraint_test_constants; diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.reference b/tests/queries/0_stateless/01622_constraints_where_optimization.reference index c7c516025f2..52aca371a6a 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.reference @@ -1,14 +1,66 @@ SELECT count() FROM t_constraints_where WHERE 0 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + WHERE + CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE 0 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + WHERE + CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE 0 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + WHERE + CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE b < 8 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + WHERE + FUNCTION id: 4, function_name: less, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: b, result_type: UInt32, source_id: 3 + CONSTANT id: 7, constant_value: UInt64_8, constant_value_type: UInt8 SELECT count() FROM t_constraints_where +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.sql b/tests/queries/0_stateless/01622_constraints_where_optimization.sql index 6a9d1ba9f6b..33fa62368b0 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.sql @@ -8,9 +8,13 @@ CREATE TABLE t_constraints_where(a UInt32, b UInt32, CONSTRAINT c1 ASSUME b >= 5 INSERT INTO t_constraints_where VALUES (1, 7); EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 15; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 15; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 20; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 20; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) DROP TABLE t_constraints_where; @@ -18,6 +22,7 @@ CREATE TABLE t_constraints_where(a UInt32, b UInt32, CONSTRAINT c1 ASSUME b < 10 INSERT INTO t_constraints_where VALUES (1, 7); -EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumtion -> (b < 20) -> 0; +EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumption -> (b < 20) -> 0; +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumption -> (b < 20) -> 0; DROP TABLE t_constraints_where; diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.reference b/tests/queries/0_stateless/01623_constraints_column_swap.reference index 7ae4516fe9e..eceaa7122dc 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.reference +++ b/tests/queries/0_stateless/01623_constraints_column_swap.reference @@ -3,21 +3,135 @@ SELECT (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 1 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 5, nodes: 1 + COLUMN id: 6, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 9, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 10, nodes: 2 + COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 + CONSTANT id: 12, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 7, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 13, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 14, nodes: 2 + FUNCTION id: 15, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 16, nodes: 1 + COLUMN id: 6, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 17, constant_value: UInt64_1, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 0 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 5, nodes: 1 + COLUMN id: 6, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 9, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 10, nodes: 2 + COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 + CONSTANT id: 12, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 7, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 13, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 14, nodes: 2 + FUNCTION id: 15, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 16, nodes: 1 + COLUMN id: 6, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 17, constant_value: UInt64_0, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 0 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 5, nodes: 1 + COLUMN id: 6, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 9, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 10, nodes: 2 + COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 + CONSTANT id: 12, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 7, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 13, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 14, nodes: 2 + COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 + CONSTANT id: 15, constant_value: UInt64_0, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 1 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 5, nodes: 1 + COLUMN id: 6, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 9, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 10, nodes: 2 + COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 + CONSTANT id: 12, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 7, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 13, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 14, nodes: 2 + COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 + CONSTANT id: 15, constant_value: UInt64_1, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)` FROM column_swap_test_test WHERE b = 0 diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.sql b/tests/queries/0_stateless/01623_constraints_column_swap.sql index c81b37c8428..359826c9879 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.sql +++ b/tests/queries/0_stateless/01623_constraints_column_swap.sql @@ -13,9 +13,13 @@ INSERT INTO column_swap_test_test VALUES (1, 'cat', 1), (2, 'dog', 2); INSERT INTO column_swap_test_test SELECT number AS i, format('test {} kek {}', toString(number), toString(number + 10)) AS a, 1 AS b FROM system.numbers LIMIT 1000000; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10 FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, a FROM column_swap_test_test WHERE cityHash64(a) = 0; diff --git a/tests/queries/0_stateless/01625_constraints_index_append.reference b/tests/queries/0_stateless/01625_constraints_index_append.reference index 0df5c429d9e..518cfb53453 100644 --- a/tests/queries/0_stateless/01625_constraints_index_append.reference +++ b/tests/queries/0_stateless/01625_constraints_index_append.reference @@ -2,14 +2,115 @@ SELECT i AS i FROM index_append_test_test PREWHERE a = 0 WHERE (a = 0) AND indexHint((i + 40) > 0) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.index_append_test_test + WHERE + FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + FUNCTION id: 6, function_name: indexHint, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 1 + FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 9, nodes: 2 + FUNCTION id: 10, function_name: plus, function_type: ordinary, result_type: Int64 + ARGUMENTS + LIST id: 11, nodes: 2 + COLUMN id: 12, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 13, constant_value: UInt64_40, constant_value_type: UInt8 + CONSTANT id: 14, constant_value: UInt64_0, constant_value_type: UInt8 + FUNCTION id: 15, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 16, nodes: 2 + COLUMN id: 17, column_name: a, result_type: UInt32, source_id: 3 + CONSTANT id: 18, constant_value: UInt64_0, constant_value_type: UInt8 SELECT i AS i FROM index_append_test_test PREWHERE a < 0 +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.index_append_test_test + WHERE + FUNCTION id: 4, function_name: less, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: a, result_type: UInt32, source_id: 3 + CONSTANT id: 7, constant_value: UInt64_0, constant_value_type: UInt8 SELECT i AS i FROM index_append_test_test PREWHERE a >= 0 WHERE (a >= 0) AND indexHint((i + 40) > 0) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.index_append_test_test + WHERE + FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + FUNCTION id: 6, function_name: indexHint, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 1 + FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 9, nodes: 2 + FUNCTION id: 10, function_name: plus, function_type: ordinary, result_type: Int64 + ARGUMENTS + LIST id: 11, nodes: 2 + COLUMN id: 12, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 13, constant_value: UInt64_40, constant_value_type: UInt8 + CONSTANT id: 14, constant_value: UInt64_0, constant_value_type: UInt8 + FUNCTION id: 15, function_name: greaterOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 16, nodes: 2 + COLUMN id: 17, column_name: a, result_type: UInt32, source_id: 3 + CONSTANT id: 18, constant_value: UInt64_0, constant_value_type: UInt8 SELECT i AS i FROM index_append_test_test PREWHERE (2 * b) < 100 WHERE ((2 * b) < 100) AND indexHint(i < 100) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.index_append_test_test + WHERE + FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + FUNCTION id: 6, function_name: indexHint, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 1 + FUNCTION id: 8, function_name: less, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 9, nodes: 2 + COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 11, constant_value: UInt64_100, constant_value_type: UInt8 + FUNCTION id: 12, function_name: less, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 13, nodes: 2 + FUNCTION id: 14, function_name: multiply, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 15, nodes: 2 + CONSTANT id: 16, constant_value: UInt64_2, constant_value_type: UInt8 + COLUMN id: 17, column_name: b, result_type: UInt64, source_id: 3 + CONSTANT id: 18, constant_value: UInt64_100, constant_value_type: UInt8 diff --git a/tests/queries/0_stateless/01625_constraints_index_append.sql b/tests/queries/0_stateless/01625_constraints_index_append.sql index fbffc9c7f10..29a27804200 100644 --- a/tests/queries/0_stateless/01625_constraints_index_append.sql +++ b/tests/queries/0_stateless/01625_constraints_index_append.sql @@ -10,8 +10,12 @@ CREATE TABLE index_append_test_test (i Int64, a UInt32, b UInt64, CONSTRAINT c1 INSERT INTO index_append_test_test VALUES (1, 10, 1), (2, 20, 2); EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a = 0; +EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a = 0; EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a < 0; +EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a < 0; EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a >= 0; +EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a >= 0; EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE 2 * b < 100; +EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE 2 * b < 100; DROP TABLE index_append_test_test; diff --git a/tests/queries/0_stateless/01626_cnf_test.reference b/tests/queries/0_stateless/01626_cnf_test.reference index 081215c9fb2..846bfd45670 100644 --- a/tests/queries/0_stateless/01626_cnf_test.reference +++ b/tests/queries/0_stateless/01626_cnf_test.reference @@ -1,18 +1,768 @@ SELECT i FROM cnf_test WHERE (i <= 2) AND (i <= 1) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.cnf_test + WHERE + FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + FUNCTION id: 6, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 2 + COLUMN id: 8, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 9, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 10, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 11, nodes: 2 + COLUMN id: 12, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 13, constant_value: UInt64_2, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE (i <= 2) OR (i <= 1) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.cnf_test + WHERE + FUNCTION id: 4, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + FUNCTION id: 6, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 2 + COLUMN id: 8, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 9, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 10, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 11, nodes: 2 + COLUMN id: 12, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 13, constant_value: UInt64_2, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE ((i > 2) OR (i > 5) OR (i > 3)) AND ((i > 2) OR (i > 5) OR (i > 4)) AND ((i > 2) OR (i > 6) OR (i > 3)) AND ((i > 2) OR (i > 6) OR (i > 4)) AND ((i > 1) OR (i > 5) OR (i > 3)) AND ((i > 1) OR (i > 5) OR (i > 4)) AND ((i > 1) OR (i > 6) OR (i > 3)) AND ((i > 1) OR (i > 6) OR (i > 4)) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.cnf_test + WHERE + FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 8 + FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 3 + FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 9, nodes: 2 + COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 11, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 12, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 13, nodes: 2 + COLUMN id: 14, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 15, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 16, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 17, nodes: 2 + COLUMN id: 18, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 19, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 20, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 21, nodes: 3 + FUNCTION id: 22, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 23, nodes: 2 + COLUMN id: 24, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 25, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 26, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 27, nodes: 2 + COLUMN id: 28, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 29, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 30, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 31, nodes: 2 + COLUMN id: 32, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 33, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 34, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 35, nodes: 3 + FUNCTION id: 36, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 37, nodes: 2 + COLUMN id: 38, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 39, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 40, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 41, nodes: 2 + COLUMN id: 42, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 43, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 44, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 45, nodes: 2 + COLUMN id: 46, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 47, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 48, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 49, nodes: 3 + FUNCTION id: 50, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 51, nodes: 2 + COLUMN id: 52, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 53, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 54, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 55, nodes: 2 + COLUMN id: 56, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 57, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 58, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 59, nodes: 2 + COLUMN id: 60, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 61, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 62, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 63, nodes: 3 + FUNCTION id: 64, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 65, nodes: 2 + COLUMN id: 66, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 67, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 68, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 69, nodes: 2 + COLUMN id: 70, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 71, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 72, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 73, nodes: 2 + COLUMN id: 74, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 75, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 76, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 77, nodes: 3 + FUNCTION id: 78, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 79, nodes: 2 + COLUMN id: 80, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 81, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 82, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 83, nodes: 2 + COLUMN id: 84, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 85, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 86, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 87, nodes: 2 + COLUMN id: 88, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 89, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 90, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 91, nodes: 3 + FUNCTION id: 92, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 93, nodes: 2 + COLUMN id: 94, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 95, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 96, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 97, nodes: 2 + COLUMN id: 98, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 99, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 100, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 101, nodes: 2 + COLUMN id: 102, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 103, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 104, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 105, nodes: 3 + FUNCTION id: 106, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 107, nodes: 2 + COLUMN id: 108, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 109, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 110, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 111, nodes: 2 + COLUMN id: 112, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 113, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 114, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 115, nodes: 2 + COLUMN id: 116, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 117, constant_value: UInt64_4, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE ((i <= 3) OR (i <= 2) OR (i <= 5)) AND ((i <= 3) OR (i <= 2) OR (i <= 6)) AND ((i <= 3) OR (i <= 5) OR (i <= 1)) AND ((i <= 3) OR (i <= 6) OR (i <= 1)) AND ((i <= 2) OR (i <= 5) OR (i <= 4)) AND ((i <= 2) OR (i <= 6) OR (i <= 4)) AND ((i <= 5) OR (i <= 1) OR (i <= 4)) AND ((i <= 6) OR (i <= 1) OR (i <= 4)) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.cnf_test + WHERE + FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 8 + FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 3 + FUNCTION id: 8, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 9, nodes: 2 + COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 11, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 12, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 13, nodes: 2 + COLUMN id: 14, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 15, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 16, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 17, nodes: 2 + COLUMN id: 18, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 19, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 20, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 21, nodes: 3 + FUNCTION id: 22, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 23, nodes: 2 + COLUMN id: 24, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 25, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 26, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 27, nodes: 2 + COLUMN id: 28, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 29, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 30, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 31, nodes: 2 + COLUMN id: 32, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 33, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 34, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 35, nodes: 3 + FUNCTION id: 36, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 37, nodes: 2 + COLUMN id: 38, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 39, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 40, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 41, nodes: 2 + COLUMN id: 42, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 43, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 44, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 45, nodes: 2 + COLUMN id: 46, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 47, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 48, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 49, nodes: 3 + FUNCTION id: 50, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 51, nodes: 2 + COLUMN id: 52, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 53, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 54, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 55, nodes: 2 + COLUMN id: 56, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 57, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 58, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 59, nodes: 2 + COLUMN id: 60, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 61, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 62, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 63, nodes: 3 + FUNCTION id: 64, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 65, nodes: 2 + COLUMN id: 66, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 67, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 68, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 69, nodes: 2 + COLUMN id: 70, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 71, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 72, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 73, nodes: 2 + COLUMN id: 74, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 75, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 76, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 77, nodes: 3 + FUNCTION id: 78, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 79, nodes: 2 + COLUMN id: 80, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 81, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 82, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 83, nodes: 2 + COLUMN id: 84, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 85, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 86, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 87, nodes: 2 + COLUMN id: 88, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 89, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 90, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 91, nodes: 3 + FUNCTION id: 92, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 93, nodes: 2 + COLUMN id: 94, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 95, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 96, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 97, nodes: 2 + COLUMN id: 98, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 99, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 100, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 101, nodes: 2 + COLUMN id: 102, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 103, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 104, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 105, nodes: 3 + FUNCTION id: 106, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 107, nodes: 2 + COLUMN id: 108, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 109, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 110, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 111, nodes: 2 + COLUMN id: 112, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 113, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 114, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 115, nodes: 2 + COLUMN id: 116, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 117, constant_value: UInt64_6, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE ((i > 2) OR (i > 5) OR (i > 3)) AND ((i > 2) OR (i > 5) OR (i > 4)) AND ((i > 2) OR (i > 5) OR (i > 8)) AND ((i > 2) OR (i > 6) OR (i > 3)) AND ((i > 2) OR (i > 6) OR (i > 4)) AND ((i > 2) OR (i > 6) OR (i > 8)) AND ((i > 1) OR (i > 5) OR (i > 3)) AND ((i > 1) OR (i > 5) OR (i > 4)) AND ((i > 1) OR (i > 5) OR (i > 8)) AND ((i > 1) OR (i > 6) OR (i > 3)) AND ((i > 1) OR (i > 6) OR (i > 4)) AND ((i > 1) OR (i > 6) OR (i > 8)) AND ((i > 5) OR (i > 3) OR (i > 7)) AND ((i > 5) OR (i > 4) OR (i > 7)) AND ((i > 5) OR (i > 8) OR (i > 7)) AND ((i > 6) OR (i > 3) OR (i > 7)) AND ((i > 6) OR (i > 4) OR (i > 7)) AND ((i > 6) OR (i > 8) OR (i > 7)) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.cnf_test + WHERE + FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 18 + FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 3 + FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 9, nodes: 2 + COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 11, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 12, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 13, nodes: 2 + COLUMN id: 14, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 15, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 16, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 17, nodes: 2 + COLUMN id: 18, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 19, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 20, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 21, nodes: 3 + FUNCTION id: 22, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 23, nodes: 2 + COLUMN id: 24, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 25, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 26, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 27, nodes: 2 + COLUMN id: 28, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 29, constant_value: UInt64_7, constant_value_type: UInt8 + FUNCTION id: 30, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 31, nodes: 2 + COLUMN id: 32, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 33, constant_value: UInt64_8, constant_value_type: UInt8 + FUNCTION id: 34, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 35, nodes: 3 + FUNCTION id: 36, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 37, nodes: 2 + COLUMN id: 38, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 39, constant_value: UInt64_7, constant_value_type: UInt8 + FUNCTION id: 40, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 41, nodes: 2 + COLUMN id: 42, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 43, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 44, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 45, nodes: 2 + COLUMN id: 46, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 47, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 48, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 49, nodes: 3 + FUNCTION id: 50, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 51, nodes: 2 + COLUMN id: 52, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 53, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 54, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 55, nodes: 2 + COLUMN id: 56, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 57, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 58, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 59, nodes: 2 + COLUMN id: 60, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 61, constant_value: UInt64_7, constant_value_type: UInt8 + FUNCTION id: 62, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 63, nodes: 3 + FUNCTION id: 64, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 65, nodes: 2 + COLUMN id: 66, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 67, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 68, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 69, nodes: 2 + COLUMN id: 70, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 71, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 72, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 73, nodes: 2 + COLUMN id: 74, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 75, constant_value: UInt64_8, constant_value_type: UInt8 + FUNCTION id: 76, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 77, nodes: 3 + FUNCTION id: 78, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 79, nodes: 2 + COLUMN id: 80, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 81, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 82, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 83, nodes: 2 + COLUMN id: 84, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 85, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 86, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 87, nodes: 2 + COLUMN id: 88, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 89, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 90, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 91, nodes: 3 + FUNCTION id: 92, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 93, nodes: 2 + COLUMN id: 94, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 95, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 96, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 97, nodes: 2 + COLUMN id: 98, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 99, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 100, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 101, nodes: 2 + COLUMN id: 102, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 103, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 104, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 105, nodes: 3 + FUNCTION id: 106, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 107, nodes: 2 + COLUMN id: 108, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 109, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 110, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 111, nodes: 2 + COLUMN id: 112, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 113, constant_value: UInt64_7, constant_value_type: UInt8 + FUNCTION id: 114, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 115, nodes: 2 + COLUMN id: 116, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 117, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 118, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 119, nodes: 3 + FUNCTION id: 120, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 121, nodes: 2 + COLUMN id: 122, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 123, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 124, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 125, nodes: 2 + COLUMN id: 126, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 127, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 128, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 129, nodes: 2 + COLUMN id: 130, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 131, constant_value: UInt64_8, constant_value_type: UInt8 + FUNCTION id: 132, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 133, nodes: 3 + FUNCTION id: 134, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 135, nodes: 2 + COLUMN id: 136, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 137, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 138, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 139, nodes: 2 + COLUMN id: 140, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 141, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 142, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 143, nodes: 2 + COLUMN id: 144, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 145, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 146, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 147, nodes: 3 + FUNCTION id: 148, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 149, nodes: 2 + COLUMN id: 150, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 151, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 152, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 153, nodes: 2 + COLUMN id: 154, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 155, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 156, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 157, nodes: 2 + COLUMN id: 158, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 159, constant_value: UInt64_8, constant_value_type: UInt8 + FUNCTION id: 160, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 161, nodes: 3 + FUNCTION id: 162, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 163, nodes: 2 + COLUMN id: 164, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 165, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 166, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 167, nodes: 2 + COLUMN id: 168, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 169, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 170, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 171, nodes: 2 + COLUMN id: 172, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 173, constant_value: UInt64_8, constant_value_type: UInt8 + FUNCTION id: 174, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 175, nodes: 3 + FUNCTION id: 176, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 177, nodes: 2 + COLUMN id: 178, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 179, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 180, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 181, nodes: 2 + COLUMN id: 182, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 183, constant_value: UInt64_7, constant_value_type: UInt8 + FUNCTION id: 184, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 185, nodes: 2 + COLUMN id: 186, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 187, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 188, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 189, nodes: 3 + FUNCTION id: 190, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 191, nodes: 2 + COLUMN id: 192, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 193, constant_value: UInt64_7, constant_value_type: UInt8 + FUNCTION id: 194, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 195, nodes: 2 + COLUMN id: 196, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 197, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 198, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 199, nodes: 2 + COLUMN id: 200, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 201, constant_value: UInt64_8, constant_value_type: UInt8 + FUNCTION id: 202, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 203, nodes: 3 + FUNCTION id: 204, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 205, nodes: 2 + COLUMN id: 206, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 207, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 208, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 209, nodes: 2 + COLUMN id: 210, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 211, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 212, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 213, nodes: 2 + COLUMN id: 214, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 215, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 216, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 217, nodes: 3 + FUNCTION id: 218, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 219, nodes: 2 + COLUMN id: 220, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 221, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 222, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 223, nodes: 2 + COLUMN id: 224, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 225, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 226, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 227, nodes: 2 + COLUMN id: 228, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 229, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 230, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 231, nodes: 3 + FUNCTION id: 232, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 233, nodes: 2 + COLUMN id: 234, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 235, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 236, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 237, nodes: 2 + COLUMN id: 238, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 239, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 240, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 241, nodes: 2 + COLUMN id: 242, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 243, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 244, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 245, nodes: 3 + FUNCTION id: 246, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 247, nodes: 2 + COLUMN id: 248, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 249, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 250, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 251, nodes: 2 + COLUMN id: 252, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 253, constant_value: UInt64_6, constant_value_type: UInt8 + FUNCTION id: 254, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 255, nodes: 2 + COLUMN id: 256, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 257, constant_value: UInt64_4, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE ((i > 2) OR (i > 1) OR (i > 7)) AND (i <= 5) AND (i <= 6) AND ((i > 3) OR (i > 4) OR (i > 8)) +QUERY id: 0 + PROJECTION COLUMNS + i Int64 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.cnf_test + WHERE + FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 4 + FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 7, nodes: 3 + FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 9, nodes: 2 + COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 11, constant_value: UInt64_2, constant_value_type: UInt8 + FUNCTION id: 12, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 13, nodes: 2 + COLUMN id: 14, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 15, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 16, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 17, nodes: 2 + COLUMN id: 18, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 19, constant_value: UInt64_7, constant_value_type: UInt8 + FUNCTION id: 20, function_name: or, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 21, nodes: 3 + FUNCTION id: 22, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 23, nodes: 2 + COLUMN id: 24, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 25, constant_value: UInt64_3, constant_value_type: UInt8 + FUNCTION id: 26, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 27, nodes: 2 + COLUMN id: 28, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 29, constant_value: UInt64_4, constant_value_type: UInt8 + FUNCTION id: 30, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 31, nodes: 2 + COLUMN id: 32, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 33, constant_value: UInt64_8, constant_value_type: UInt8 + FUNCTION id: 34, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 35, nodes: 2 + COLUMN id: 36, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 37, constant_value: UInt64_5, constant_value_type: UInt8 + FUNCTION id: 38, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 39, nodes: 2 + COLUMN id: 40, column_name: i, result_type: Int64, source_id: 3 + CONSTANT id: 41, constant_value: UInt64_6, constant_value_type: UInt8 diff --git a/tests/queries/0_stateless/01626_cnf_test.sql b/tests/queries/0_stateless/01626_cnf_test.sql index 8db732bc227..d54b636f748 100644 --- a/tests/queries/0_stateless/01626_cnf_test.sql +++ b/tests/queries/0_stateless/01626_cnf_test.sql @@ -5,14 +5,20 @@ DROP TABLE IF EXISTS cnf_test; CREATE TABLE cnf_test (i Int64) ENGINE = MergeTree() ORDER BY i; EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE NOT ((i > 1) OR (i > 2)); +EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE NOT ((i > 1) OR (i > 2)); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE NOT ((i > 1) AND (i > 2)); +EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE NOT ((i > 1) AND (i > 2)); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE ((i > 1) AND (i > 2)) OR ((i > 3) AND (i > 4)) OR ((i > 5) AND (i > 6)); +EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE ((i > 1) AND (i > 2)) OR ((i > 3) AND (i > 4)) OR ((i > 5) AND (i > 6)); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE NOT (((i > 1) OR (i > 2)) AND ((i > 3) OR (i > 4)) AND ((i > 5) OR (i > 6))); +EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE NOT (((i > 1) OR (i > 2)) AND ((i > 3) OR (i > 4)) AND ((i > 5) OR (i > 6))); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE ((i > 1) AND (i > 2) AND (i > 7)) OR ((i > 3) AND (i > 4) AND (i > 8)) OR ((i > 5) AND (i > 6)); +EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE ((i > 1) AND (i > 2) AND (i > 7)) OR ((i > 3) AND (i > 4) AND (i > 8)) OR ((i > 5) AND (i > 6)); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE ((i > 1) OR (i > 2) OR (i > 7)) AND ((i > 3) OR (i > 4) OR (i > 8)) AND NOT ((i > 5) OR (i > 6)); +EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE ((i > 1) OR (i > 2) OR (i > 7)) AND ((i > 3) OR (i > 4) OR (i > 8)) AND NOT ((i > 5) OR (i > 6)); DROP TABLE cnf_test; From 1ba75c6408830751b5d57625faa89a4164163ea4 Mon Sep 17 00:00:00 2001 From: HarryLeeIBM Date: Thu, 16 Mar 2023 10:03:43 -0700 Subject: [PATCH 048/377] Refactored by using std::byteswap --- src/Functions/FunctionsHashing.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Functions/FunctionsHashing.h b/src/Functions/FunctionsHashing.h index 937e3b0023d..a24b5109963 100644 --- a/src/Functions/FunctionsHashing.h +++ b/src/Functions/FunctionsHashing.h @@ -30,7 +30,7 @@ # include #endif -#include +#include #include #include #include @@ -184,15 +184,15 @@ struct HalfMD5Impl #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ return buf.uint64_data; /// No need to flip bytes on big endian machines #else - return Poco::ByteOrder::flipBytes(static_cast(buf.uint64_data)); /// Compatibility with existing code. Cast need for old poco AND macos where UInt64 != uint64_t + return std::byteswap(buf.uint64_data); /// Compatibility with existing code. Cast need for old poco AND macos where UInt64 != uint64_t #endif } static UInt64 combineHashes(UInt64 h1, UInt64 h2) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - h1 = Poco::ByteOrder::flipBytes(static_cast(h1)); - h2 = Poco::ByteOrder::flipBytes(static_cast(h2)); + h1 = std::byteswap(h1); + h2 = std::byteswap(h2); #endif UInt64 hashes[] = {h1, h2}; return apply(reinterpret_cast(hashes), 16); @@ -334,8 +334,8 @@ struct SipHash64KeyedImpl static UInt64 combineHashesKeyed(const Key & key, UInt64 h1, UInt64 h2) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - h1 = Poco::ByteOrder::flipBytes(static_cast(h1)); - h2 = Poco::ByteOrder::flipBytes(static_cast(h2)); + h1 = std::byteswap(h1); + h2 = std::byteswap(h2); #endif UInt64 hashes[] = {h1, h2}; return applyKeyed(key, reinterpret_cast(hashes), 2 * sizeof(UInt64)); From 65e5bfe8207898ca8ea670ec8ef65f89c68ef712 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 16 Mar 2023 17:19:24 +0000 Subject: [PATCH 049/377] Use set --- src/Analyzer/Passes/CNF.cpp | 1 - src/Analyzer/Passes/CNF.h | 20 +------------------ src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 7 +------ 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/Analyzer/Passes/CNF.cpp b/src/Analyzer/Passes/CNF.cpp index 38d316a13f3..68a95a509f8 100644 --- a/src/Analyzer/Passes/CNF.cpp +++ b/src/Analyzer/Passes/CNF.cpp @@ -19,7 +19,6 @@ namespace DB namespace ErrorCodes { extern const int TOO_MANY_TEMPORARY_COLUMNS; - extern const int LOGICAL_ERROR; } namespace Analyzer diff --git a/src/Analyzer/Passes/CNF.h b/src/Analyzer/Passes/CNF.h index 8ddbf7efd85..ec639cd6679 100644 --- a/src/Analyzer/Passes/CNF.h +++ b/src/Analyzer/Passes/CNF.h @@ -24,27 +24,9 @@ public: bool operator<(const AtomicFormula & rhs) const; }; - struct SetAtomicFormulaHash - { - size_t operator()(const std::set & or_group) const - { - SipHash hash; - for (const auto & atomic_formula : or_group) - { - SipHash atomic_formula_hash; - atomic_formula_hash.update(atomic_formula.negative); - atomic_formula_hash.update(atomic_formula.node_with_hash.hash); - - hash.update(atomic_formula_hash.get64()); - } - - return hash.get64(); - } - }; - // Different hash is generated for different order, so we use std::set using OrGroup = std::set; - using AndGroup = std::unordered_set; + using AndGroup = std::set; std::string dump() const; diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp index 781fb857a07..f807b096a78 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -37,7 +37,7 @@ enum class MatchState : uint8_t MatchState match(const Analyzer::CNF::AtomicFormula & a, const Analyzer::CNF::AtomicFormula & b) { using enum MatchState; - if (a.node_with_hash.hash != b.node_with_hash.hash) + if (a.node_with_hash != b.node_with_hash) return NONE; return a.negative == b.negative ? FULL_MATCH : PARTIAL_MATCH; @@ -402,11 +402,6 @@ public: using Base = InDepthQueryTreeVisitorWithContext; using Base::Base; - static bool needChildVisit(VisitQueryTreeNodeType & parent, VisitQueryTreeNodeType &) - { - return parent->as() == nullptr; - } - void visitImpl(QueryTreeNodePtr & node) { auto * query_node = node->as(); From 3210331480139c1a28755cf7b422c03fc83d7ed4 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 16 Mar 2023 17:20:21 +0000 Subject: [PATCH 050/377] Remove query tree tests --- ..._constraints_simple_optimization.reference | 58 -- .../01622_constraints_simple_optimization.sql | 4 - ...2_constraints_where_optimization.reference | 52 -- .../01622_constraints_where_optimization.sql | 5 - .../01623_constraints_column_swap.reference | 114 --- .../01623_constraints_column_swap.sql | 8 +- .../01625_constraints_index_append.reference | 101 --- .../01625_constraints_index_append.sql | 4 - .../0_stateless/01626_cnf_test.reference | 750 ------------------ tests/queries/0_stateless/01626_cnf_test.sql | 6 - 10 files changed, 4 insertions(+), 1098 deletions(-) diff --git a/tests/queries/0_stateless/01622_constraints_simple_optimization.reference b/tests/queries/0_stateless/01622_constraints_simple_optimization.reference index f92b8432b65..7e012e1a17b 100644 --- a/tests/queries/0_stateless/01622_constraints_simple_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_simple_optimization.reference @@ -35,69 +35,11 @@ SELECT count() AS `count()` FROM constraint_test_constants WHERE (c > 100) OR (b > 100) -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.constraint_test_constants - WHERE - FUNCTION id: 4, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - FUNCTION id: 6, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 2 - COLUMN id: 8, column_name: b, result_type: Int64, source_id: 3 - CONSTANT id: 9, constant_value: UInt64_100, constant_value_type: UInt8 - FUNCTION id: 10, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 11, nodes: 2 - COLUMN id: 12, column_name: c, result_type: Int64, source_id: 3 - CONSTANT id: 13, constant_value: UInt64_100, constant_value_type: UInt8 SELECT count() AS `count()` FROM constraint_test_constants WHERE c > 100 -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.constraint_test_constants - WHERE - FUNCTION id: 4, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - COLUMN id: 6, column_name: c, result_type: Int64, source_id: 3 - CONSTANT id: 7, constant_value: UInt64_100, constant_value_type: UInt8 SELECT count() AS `count()` FROM constraint_test_constants WHERE c > 100 -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.constraint_test_constants - WHERE - FUNCTION id: 4, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - COLUMN id: 6, column_name: c, result_type: Int64, source_id: 3 - CONSTANT id: 7, constant_value: UInt64_100, constant_value_type: UInt8 SELECT count() AS `count()` FROM constraint_test_constants -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.constraint_test_constants diff --git a/tests/queries/0_stateless/01622_constraints_simple_optimization.sql b/tests/queries/0_stateless/01622_constraints_simple_optimization.sql index 8b7a339984d..7ec9e1a3158 100644 --- a/tests/queries/0_stateless/01622_constraints_simple_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_simple_optimization.sql @@ -98,12 +98,8 @@ SELECT count() FROM constraint_test_constants WHERE 11 <= a; ---> assumption -> -- A AND NOT A EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100); -EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100); EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100); -EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100); EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100); -EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100); EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100); -EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100); DROP TABLE constraint_test_constants; diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.reference b/tests/queries/0_stateless/01622_constraints_where_optimization.reference index 52aca371a6a..c7c516025f2 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.reference @@ -1,66 +1,14 @@ SELECT count() FROM t_constraints_where WHERE 0 -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.t_constraints_where - WHERE - CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE 0 -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.t_constraints_where - WHERE - CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE 0 -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.t_constraints_where - WHERE - CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE b < 8 -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.t_constraints_where - WHERE - FUNCTION id: 4, function_name: less, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - COLUMN id: 6, column_name: b, result_type: UInt32, source_id: 3 - CONSTANT id: 7, constant_value: UInt64_8, constant_value_type: UInt8 SELECT count() FROM t_constraints_where -QUERY id: 0 - PROJECTION COLUMNS - count() UInt64 - PROJECTION - LIST id: 1, nodes: 1 - FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 - JOIN TREE - TABLE id: 3, table_name: default.t_constraints_where diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.sql b/tests/queries/0_stateless/01622_constraints_where_optimization.sql index 33fa62368b0..1b297942a74 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.sql @@ -8,13 +8,9 @@ CREATE TABLE t_constraints_where(a UInt32, b UInt32, CONSTRAINT c1 ASSUME b >= 5 INSERT INTO t_constraints_where VALUES (1, 7); EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 15; -- assumption -> 0 -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 15; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 20; -- assumption -> 0 -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 20; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) DROP TABLE t_constraints_where; @@ -23,6 +19,5 @@ CREATE TABLE t_constraints_where(a UInt32, b UInt32, CONSTRAINT c1 ASSUME b < 10 INSERT INTO t_constraints_where VALUES (1, 7); EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumption -> (b < 20) -> 0; -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumption -> (b < 20) -> 0; DROP TABLE t_constraints_where; diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.reference b/tests/queries/0_stateless/01623_constraints_column_swap.reference index eceaa7122dc..7ae4516fe9e 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.reference +++ b/tests/queries/0_stateless/01623_constraints_column_swap.reference @@ -3,135 +3,21 @@ SELECT (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 1 -QUERY id: 0 - PROJECTION COLUMNS - plus(cityHash64(a), 10) UInt64 - plus(b, 3) UInt64 - PROJECTION - LIST id: 1, nodes: 2 - FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 3, nodes: 2 - FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 5, nodes: 1 - COLUMN id: 6, column_name: a, result_type: String, source_id: 7 - CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 - FUNCTION id: 9, function_name: plus, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 10, nodes: 2 - COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 - CONSTANT id: 12, constant_value: UInt64_3, constant_value_type: UInt8 - JOIN TREE - TABLE id: 7, table_name: default.column_swap_test_test - WHERE - FUNCTION id: 13, function_name: equals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 14, nodes: 2 - FUNCTION id: 15, function_name: cityHash64, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 16, nodes: 1 - COLUMN id: 6, column_name: a, result_type: String, source_id: 7 - CONSTANT id: 17, constant_value: UInt64_1, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 0 -QUERY id: 0 - PROJECTION COLUMNS - plus(cityHash64(a), 10) UInt64 - plus(b, 3) UInt64 - PROJECTION - LIST id: 1, nodes: 2 - FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 3, nodes: 2 - FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 5, nodes: 1 - COLUMN id: 6, column_name: a, result_type: String, source_id: 7 - CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 - FUNCTION id: 9, function_name: plus, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 10, nodes: 2 - COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 - CONSTANT id: 12, constant_value: UInt64_3, constant_value_type: UInt8 - JOIN TREE - TABLE id: 7, table_name: default.column_swap_test_test - WHERE - FUNCTION id: 13, function_name: equals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 14, nodes: 2 - FUNCTION id: 15, function_name: cityHash64, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 16, nodes: 1 - COLUMN id: 6, column_name: a, result_type: String, source_id: 7 - CONSTANT id: 17, constant_value: UInt64_0, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 0 -QUERY id: 0 - PROJECTION COLUMNS - plus(cityHash64(a), 10) UInt64 - plus(b, 3) UInt64 - PROJECTION - LIST id: 1, nodes: 2 - FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 3, nodes: 2 - FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 5, nodes: 1 - COLUMN id: 6, column_name: a, result_type: String, source_id: 7 - CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 - FUNCTION id: 9, function_name: plus, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 10, nodes: 2 - COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 - CONSTANT id: 12, constant_value: UInt64_3, constant_value_type: UInt8 - JOIN TREE - TABLE id: 7, table_name: default.column_swap_test_test - WHERE - FUNCTION id: 13, function_name: equals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 14, nodes: 2 - COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 - CONSTANT id: 15, constant_value: UInt64_0, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 1 -QUERY id: 0 - PROJECTION COLUMNS - plus(cityHash64(a), 10) UInt64 - plus(b, 3) UInt64 - PROJECTION - LIST id: 1, nodes: 2 - FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 3, nodes: 2 - FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 5, nodes: 1 - COLUMN id: 6, column_name: a, result_type: String, source_id: 7 - CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 - FUNCTION id: 9, function_name: plus, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 10, nodes: 2 - COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 - CONSTANT id: 12, constant_value: UInt64_3, constant_value_type: UInt8 - JOIN TREE - TABLE id: 7, table_name: default.column_swap_test_test - WHERE - FUNCTION id: 13, function_name: equals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 14, nodes: 2 - COLUMN id: 11, column_name: b, result_type: UInt64, source_id: 7 - CONSTANT id: 15, constant_value: UInt64_1, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)` FROM column_swap_test_test WHERE b = 0 diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.sql b/tests/queries/0_stateless/01623_constraints_column_swap.sql index 359826c9879..873ebbed729 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.sql +++ b/tests/queries/0_stateless/01623_constraints_column_swap.sql @@ -13,13 +13,13 @@ INSERT INTO column_swap_test_test VALUES (1, 'cat', 1), (2, 'dog', 2); INSERT INTO column_swap_test_test SELECT number AS i, format('test {} kek {}', toString(number), toString(number + 10)) AS a, 1 AS b FROM system.numbers LIMIT 1000000; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; +--EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; +--EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; +--EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; +--EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10 FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, a FROM column_swap_test_test WHERE cityHash64(a) = 0; diff --git a/tests/queries/0_stateless/01625_constraints_index_append.reference b/tests/queries/0_stateless/01625_constraints_index_append.reference index 518cfb53453..0df5c429d9e 100644 --- a/tests/queries/0_stateless/01625_constraints_index_append.reference +++ b/tests/queries/0_stateless/01625_constraints_index_append.reference @@ -2,115 +2,14 @@ SELECT i AS i FROM index_append_test_test PREWHERE a = 0 WHERE (a = 0) AND indexHint((i + 40) > 0) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.index_append_test_test - WHERE - FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - FUNCTION id: 6, function_name: indexHint, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 1 - FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 9, nodes: 2 - FUNCTION id: 10, function_name: plus, function_type: ordinary, result_type: Int64 - ARGUMENTS - LIST id: 11, nodes: 2 - COLUMN id: 12, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 13, constant_value: UInt64_40, constant_value_type: UInt8 - CONSTANT id: 14, constant_value: UInt64_0, constant_value_type: UInt8 - FUNCTION id: 15, function_name: equals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 16, nodes: 2 - COLUMN id: 17, column_name: a, result_type: UInt32, source_id: 3 - CONSTANT id: 18, constant_value: UInt64_0, constant_value_type: UInt8 SELECT i AS i FROM index_append_test_test PREWHERE a < 0 -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.index_append_test_test - WHERE - FUNCTION id: 4, function_name: less, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - COLUMN id: 6, column_name: a, result_type: UInt32, source_id: 3 - CONSTANT id: 7, constant_value: UInt64_0, constant_value_type: UInt8 SELECT i AS i FROM index_append_test_test PREWHERE a >= 0 WHERE (a >= 0) AND indexHint((i + 40) > 0) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.index_append_test_test - WHERE - FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - FUNCTION id: 6, function_name: indexHint, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 1 - FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 9, nodes: 2 - FUNCTION id: 10, function_name: plus, function_type: ordinary, result_type: Int64 - ARGUMENTS - LIST id: 11, nodes: 2 - COLUMN id: 12, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 13, constant_value: UInt64_40, constant_value_type: UInt8 - CONSTANT id: 14, constant_value: UInt64_0, constant_value_type: UInt8 - FUNCTION id: 15, function_name: greaterOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 16, nodes: 2 - COLUMN id: 17, column_name: a, result_type: UInt32, source_id: 3 - CONSTANT id: 18, constant_value: UInt64_0, constant_value_type: UInt8 SELECT i AS i FROM index_append_test_test PREWHERE (2 * b) < 100 WHERE ((2 * b) < 100) AND indexHint(i < 100) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.index_append_test_test - WHERE - FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - FUNCTION id: 6, function_name: indexHint, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 1 - FUNCTION id: 8, function_name: less, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 9, nodes: 2 - COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 11, constant_value: UInt64_100, constant_value_type: UInt8 - FUNCTION id: 12, function_name: less, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 13, nodes: 2 - FUNCTION id: 14, function_name: multiply, function_type: ordinary, result_type: UInt64 - ARGUMENTS - LIST id: 15, nodes: 2 - CONSTANT id: 16, constant_value: UInt64_2, constant_value_type: UInt8 - COLUMN id: 17, column_name: b, result_type: UInt64, source_id: 3 - CONSTANT id: 18, constant_value: UInt64_100, constant_value_type: UInt8 diff --git a/tests/queries/0_stateless/01625_constraints_index_append.sql b/tests/queries/0_stateless/01625_constraints_index_append.sql index 29a27804200..fbffc9c7f10 100644 --- a/tests/queries/0_stateless/01625_constraints_index_append.sql +++ b/tests/queries/0_stateless/01625_constraints_index_append.sql @@ -10,12 +10,8 @@ CREATE TABLE index_append_test_test (i Int64, a UInt32, b UInt64, CONSTRAINT c1 INSERT INTO index_append_test_test VALUES (1, 10, 1), (2, 20, 2); EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a = 0; -EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a = 0; EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a < 0; -EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a < 0; EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a >= 0; -EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a >= 0; EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE 2 * b < 100; -EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE 2 * b < 100; DROP TABLE index_append_test_test; diff --git a/tests/queries/0_stateless/01626_cnf_test.reference b/tests/queries/0_stateless/01626_cnf_test.reference index 846bfd45670..081215c9fb2 100644 --- a/tests/queries/0_stateless/01626_cnf_test.reference +++ b/tests/queries/0_stateless/01626_cnf_test.reference @@ -1,768 +1,18 @@ SELECT i FROM cnf_test WHERE (i <= 2) AND (i <= 1) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.cnf_test - WHERE - FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - FUNCTION id: 6, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 2 - COLUMN id: 8, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 9, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 10, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 11, nodes: 2 - COLUMN id: 12, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 13, constant_value: UInt64_2, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE (i <= 2) OR (i <= 1) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.cnf_test - WHERE - FUNCTION id: 4, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 2 - FUNCTION id: 6, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 2 - COLUMN id: 8, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 9, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 10, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 11, nodes: 2 - COLUMN id: 12, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 13, constant_value: UInt64_2, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE ((i > 2) OR (i > 5) OR (i > 3)) AND ((i > 2) OR (i > 5) OR (i > 4)) AND ((i > 2) OR (i > 6) OR (i > 3)) AND ((i > 2) OR (i > 6) OR (i > 4)) AND ((i > 1) OR (i > 5) OR (i > 3)) AND ((i > 1) OR (i > 5) OR (i > 4)) AND ((i > 1) OR (i > 6) OR (i > 3)) AND ((i > 1) OR (i > 6) OR (i > 4)) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.cnf_test - WHERE - FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 8 - FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 3 - FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 9, nodes: 2 - COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 11, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 12, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 13, nodes: 2 - COLUMN id: 14, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 15, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 16, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 17, nodes: 2 - COLUMN id: 18, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 19, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 20, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 21, nodes: 3 - FUNCTION id: 22, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 23, nodes: 2 - COLUMN id: 24, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 25, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 26, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 27, nodes: 2 - COLUMN id: 28, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 29, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 30, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 31, nodes: 2 - COLUMN id: 32, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 33, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 34, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 35, nodes: 3 - FUNCTION id: 36, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 37, nodes: 2 - COLUMN id: 38, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 39, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 40, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 41, nodes: 2 - COLUMN id: 42, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 43, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 44, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 45, nodes: 2 - COLUMN id: 46, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 47, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 48, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 49, nodes: 3 - FUNCTION id: 50, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 51, nodes: 2 - COLUMN id: 52, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 53, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 54, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 55, nodes: 2 - COLUMN id: 56, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 57, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 58, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 59, nodes: 2 - COLUMN id: 60, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 61, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 62, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 63, nodes: 3 - FUNCTION id: 64, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 65, nodes: 2 - COLUMN id: 66, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 67, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 68, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 69, nodes: 2 - COLUMN id: 70, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 71, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 72, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 73, nodes: 2 - COLUMN id: 74, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 75, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 76, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 77, nodes: 3 - FUNCTION id: 78, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 79, nodes: 2 - COLUMN id: 80, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 81, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 82, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 83, nodes: 2 - COLUMN id: 84, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 85, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 86, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 87, nodes: 2 - COLUMN id: 88, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 89, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 90, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 91, nodes: 3 - FUNCTION id: 92, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 93, nodes: 2 - COLUMN id: 94, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 95, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 96, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 97, nodes: 2 - COLUMN id: 98, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 99, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 100, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 101, nodes: 2 - COLUMN id: 102, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 103, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 104, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 105, nodes: 3 - FUNCTION id: 106, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 107, nodes: 2 - COLUMN id: 108, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 109, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 110, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 111, nodes: 2 - COLUMN id: 112, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 113, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 114, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 115, nodes: 2 - COLUMN id: 116, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 117, constant_value: UInt64_4, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE ((i <= 3) OR (i <= 2) OR (i <= 5)) AND ((i <= 3) OR (i <= 2) OR (i <= 6)) AND ((i <= 3) OR (i <= 5) OR (i <= 1)) AND ((i <= 3) OR (i <= 6) OR (i <= 1)) AND ((i <= 2) OR (i <= 5) OR (i <= 4)) AND ((i <= 2) OR (i <= 6) OR (i <= 4)) AND ((i <= 5) OR (i <= 1) OR (i <= 4)) AND ((i <= 6) OR (i <= 1) OR (i <= 4)) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.cnf_test - WHERE - FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 8 - FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 3 - FUNCTION id: 8, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 9, nodes: 2 - COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 11, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 12, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 13, nodes: 2 - COLUMN id: 14, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 15, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 16, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 17, nodes: 2 - COLUMN id: 18, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 19, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 20, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 21, nodes: 3 - FUNCTION id: 22, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 23, nodes: 2 - COLUMN id: 24, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 25, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 26, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 27, nodes: 2 - COLUMN id: 28, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 29, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 30, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 31, nodes: 2 - COLUMN id: 32, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 33, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 34, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 35, nodes: 3 - FUNCTION id: 36, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 37, nodes: 2 - COLUMN id: 38, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 39, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 40, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 41, nodes: 2 - COLUMN id: 42, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 43, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 44, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 45, nodes: 2 - COLUMN id: 46, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 47, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 48, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 49, nodes: 3 - FUNCTION id: 50, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 51, nodes: 2 - COLUMN id: 52, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 53, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 54, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 55, nodes: 2 - COLUMN id: 56, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 57, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 58, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 59, nodes: 2 - COLUMN id: 60, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 61, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 62, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 63, nodes: 3 - FUNCTION id: 64, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 65, nodes: 2 - COLUMN id: 66, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 67, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 68, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 69, nodes: 2 - COLUMN id: 70, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 71, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 72, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 73, nodes: 2 - COLUMN id: 74, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 75, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 76, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 77, nodes: 3 - FUNCTION id: 78, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 79, nodes: 2 - COLUMN id: 80, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 81, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 82, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 83, nodes: 2 - COLUMN id: 84, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 85, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 86, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 87, nodes: 2 - COLUMN id: 88, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 89, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 90, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 91, nodes: 3 - FUNCTION id: 92, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 93, nodes: 2 - COLUMN id: 94, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 95, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 96, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 97, nodes: 2 - COLUMN id: 98, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 99, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 100, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 101, nodes: 2 - COLUMN id: 102, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 103, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 104, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 105, nodes: 3 - FUNCTION id: 106, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 107, nodes: 2 - COLUMN id: 108, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 109, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 110, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 111, nodes: 2 - COLUMN id: 112, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 113, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 114, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 115, nodes: 2 - COLUMN id: 116, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 117, constant_value: UInt64_6, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE ((i > 2) OR (i > 5) OR (i > 3)) AND ((i > 2) OR (i > 5) OR (i > 4)) AND ((i > 2) OR (i > 5) OR (i > 8)) AND ((i > 2) OR (i > 6) OR (i > 3)) AND ((i > 2) OR (i > 6) OR (i > 4)) AND ((i > 2) OR (i > 6) OR (i > 8)) AND ((i > 1) OR (i > 5) OR (i > 3)) AND ((i > 1) OR (i > 5) OR (i > 4)) AND ((i > 1) OR (i > 5) OR (i > 8)) AND ((i > 1) OR (i > 6) OR (i > 3)) AND ((i > 1) OR (i > 6) OR (i > 4)) AND ((i > 1) OR (i > 6) OR (i > 8)) AND ((i > 5) OR (i > 3) OR (i > 7)) AND ((i > 5) OR (i > 4) OR (i > 7)) AND ((i > 5) OR (i > 8) OR (i > 7)) AND ((i > 6) OR (i > 3) OR (i > 7)) AND ((i > 6) OR (i > 4) OR (i > 7)) AND ((i > 6) OR (i > 8) OR (i > 7)) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.cnf_test - WHERE - FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 18 - FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 3 - FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 9, nodes: 2 - COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 11, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 12, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 13, nodes: 2 - COLUMN id: 14, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 15, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 16, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 17, nodes: 2 - COLUMN id: 18, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 19, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 20, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 21, nodes: 3 - FUNCTION id: 22, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 23, nodes: 2 - COLUMN id: 24, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 25, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 26, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 27, nodes: 2 - COLUMN id: 28, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 29, constant_value: UInt64_7, constant_value_type: UInt8 - FUNCTION id: 30, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 31, nodes: 2 - COLUMN id: 32, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 33, constant_value: UInt64_8, constant_value_type: UInt8 - FUNCTION id: 34, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 35, nodes: 3 - FUNCTION id: 36, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 37, nodes: 2 - COLUMN id: 38, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 39, constant_value: UInt64_7, constant_value_type: UInt8 - FUNCTION id: 40, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 41, nodes: 2 - COLUMN id: 42, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 43, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 44, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 45, nodes: 2 - COLUMN id: 46, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 47, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 48, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 49, nodes: 3 - FUNCTION id: 50, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 51, nodes: 2 - COLUMN id: 52, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 53, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 54, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 55, nodes: 2 - COLUMN id: 56, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 57, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 58, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 59, nodes: 2 - COLUMN id: 60, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 61, constant_value: UInt64_7, constant_value_type: UInt8 - FUNCTION id: 62, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 63, nodes: 3 - FUNCTION id: 64, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 65, nodes: 2 - COLUMN id: 66, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 67, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 68, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 69, nodes: 2 - COLUMN id: 70, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 71, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 72, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 73, nodes: 2 - COLUMN id: 74, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 75, constant_value: UInt64_8, constant_value_type: UInt8 - FUNCTION id: 76, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 77, nodes: 3 - FUNCTION id: 78, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 79, nodes: 2 - COLUMN id: 80, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 81, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 82, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 83, nodes: 2 - COLUMN id: 84, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 85, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 86, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 87, nodes: 2 - COLUMN id: 88, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 89, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 90, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 91, nodes: 3 - FUNCTION id: 92, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 93, nodes: 2 - COLUMN id: 94, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 95, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 96, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 97, nodes: 2 - COLUMN id: 98, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 99, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 100, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 101, nodes: 2 - COLUMN id: 102, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 103, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 104, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 105, nodes: 3 - FUNCTION id: 106, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 107, nodes: 2 - COLUMN id: 108, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 109, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 110, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 111, nodes: 2 - COLUMN id: 112, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 113, constant_value: UInt64_7, constant_value_type: UInt8 - FUNCTION id: 114, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 115, nodes: 2 - COLUMN id: 116, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 117, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 118, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 119, nodes: 3 - FUNCTION id: 120, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 121, nodes: 2 - COLUMN id: 122, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 123, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 124, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 125, nodes: 2 - COLUMN id: 126, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 127, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 128, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 129, nodes: 2 - COLUMN id: 130, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 131, constant_value: UInt64_8, constant_value_type: UInt8 - FUNCTION id: 132, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 133, nodes: 3 - FUNCTION id: 134, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 135, nodes: 2 - COLUMN id: 136, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 137, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 138, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 139, nodes: 2 - COLUMN id: 140, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 141, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 142, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 143, nodes: 2 - COLUMN id: 144, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 145, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 146, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 147, nodes: 3 - FUNCTION id: 148, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 149, nodes: 2 - COLUMN id: 150, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 151, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 152, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 153, nodes: 2 - COLUMN id: 154, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 155, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 156, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 157, nodes: 2 - COLUMN id: 158, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 159, constant_value: UInt64_8, constant_value_type: UInt8 - FUNCTION id: 160, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 161, nodes: 3 - FUNCTION id: 162, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 163, nodes: 2 - COLUMN id: 164, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 165, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 166, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 167, nodes: 2 - COLUMN id: 168, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 169, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 170, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 171, nodes: 2 - COLUMN id: 172, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 173, constant_value: UInt64_8, constant_value_type: UInt8 - FUNCTION id: 174, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 175, nodes: 3 - FUNCTION id: 176, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 177, nodes: 2 - COLUMN id: 178, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 179, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 180, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 181, nodes: 2 - COLUMN id: 182, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 183, constant_value: UInt64_7, constant_value_type: UInt8 - FUNCTION id: 184, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 185, nodes: 2 - COLUMN id: 186, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 187, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 188, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 189, nodes: 3 - FUNCTION id: 190, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 191, nodes: 2 - COLUMN id: 192, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 193, constant_value: UInt64_7, constant_value_type: UInt8 - FUNCTION id: 194, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 195, nodes: 2 - COLUMN id: 196, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 197, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 198, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 199, nodes: 2 - COLUMN id: 200, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 201, constant_value: UInt64_8, constant_value_type: UInt8 - FUNCTION id: 202, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 203, nodes: 3 - FUNCTION id: 204, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 205, nodes: 2 - COLUMN id: 206, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 207, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 208, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 209, nodes: 2 - COLUMN id: 210, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 211, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 212, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 213, nodes: 2 - COLUMN id: 214, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 215, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 216, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 217, nodes: 3 - FUNCTION id: 218, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 219, nodes: 2 - COLUMN id: 220, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 221, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 222, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 223, nodes: 2 - COLUMN id: 224, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 225, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 226, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 227, nodes: 2 - COLUMN id: 228, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 229, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 230, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 231, nodes: 3 - FUNCTION id: 232, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 233, nodes: 2 - COLUMN id: 234, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 235, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 236, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 237, nodes: 2 - COLUMN id: 238, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 239, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 240, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 241, nodes: 2 - COLUMN id: 242, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 243, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 244, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 245, nodes: 3 - FUNCTION id: 246, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 247, nodes: 2 - COLUMN id: 248, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 249, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 250, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 251, nodes: 2 - COLUMN id: 252, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 253, constant_value: UInt64_6, constant_value_type: UInt8 - FUNCTION id: 254, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 255, nodes: 2 - COLUMN id: 256, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 257, constant_value: UInt64_4, constant_value_type: UInt8 SELECT i FROM cnf_test WHERE ((i > 2) OR (i > 1) OR (i > 7)) AND (i <= 5) AND (i <= 6) AND ((i > 3) OR (i > 4) OR (i > 8)) -QUERY id: 0 - PROJECTION COLUMNS - i Int64 - PROJECTION - LIST id: 1, nodes: 1 - COLUMN id: 2, column_name: i, result_type: Int64, source_id: 3 - JOIN TREE - TABLE id: 3, table_name: default.cnf_test - WHERE - FUNCTION id: 4, function_name: and, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 5, nodes: 4 - FUNCTION id: 6, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 7, nodes: 3 - FUNCTION id: 8, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 9, nodes: 2 - COLUMN id: 10, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 11, constant_value: UInt64_2, constant_value_type: UInt8 - FUNCTION id: 12, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 13, nodes: 2 - COLUMN id: 14, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 15, constant_value: UInt64_1, constant_value_type: UInt8 - FUNCTION id: 16, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 17, nodes: 2 - COLUMN id: 18, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 19, constant_value: UInt64_7, constant_value_type: UInt8 - FUNCTION id: 20, function_name: or, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 21, nodes: 3 - FUNCTION id: 22, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 23, nodes: 2 - COLUMN id: 24, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 25, constant_value: UInt64_3, constant_value_type: UInt8 - FUNCTION id: 26, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 27, nodes: 2 - COLUMN id: 28, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 29, constant_value: UInt64_4, constant_value_type: UInt8 - FUNCTION id: 30, function_name: greater, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 31, nodes: 2 - COLUMN id: 32, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 33, constant_value: UInt64_8, constant_value_type: UInt8 - FUNCTION id: 34, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 35, nodes: 2 - COLUMN id: 36, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 37, constant_value: UInt64_5, constant_value_type: UInt8 - FUNCTION id: 38, function_name: lessOrEquals, function_type: ordinary, result_type: UInt8 - ARGUMENTS - LIST id: 39, nodes: 2 - COLUMN id: 40, column_name: i, result_type: Int64, source_id: 3 - CONSTANT id: 41, constant_value: UInt64_6, constant_value_type: UInt8 diff --git a/tests/queries/0_stateless/01626_cnf_test.sql b/tests/queries/0_stateless/01626_cnf_test.sql index d54b636f748..8db732bc227 100644 --- a/tests/queries/0_stateless/01626_cnf_test.sql +++ b/tests/queries/0_stateless/01626_cnf_test.sql @@ -5,20 +5,14 @@ DROP TABLE IF EXISTS cnf_test; CREATE TABLE cnf_test (i Int64) ENGINE = MergeTree() ORDER BY i; EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE NOT ((i > 1) OR (i > 2)); -EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE NOT ((i > 1) OR (i > 2)); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE NOT ((i > 1) AND (i > 2)); -EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE NOT ((i > 1) AND (i > 2)); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE ((i > 1) AND (i > 2)) OR ((i > 3) AND (i > 4)) OR ((i > 5) AND (i > 6)); -EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE ((i > 1) AND (i > 2)) OR ((i > 3) AND (i > 4)) OR ((i > 5) AND (i > 6)); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE NOT (((i > 1) OR (i > 2)) AND ((i > 3) OR (i > 4)) AND ((i > 5) OR (i > 6))); -EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE NOT (((i > 1) OR (i > 2)) AND ((i > 3) OR (i > 4)) AND ((i > 5) OR (i > 6))); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE ((i > 1) AND (i > 2) AND (i > 7)) OR ((i > 3) AND (i > 4) AND (i > 8)) OR ((i > 5) AND (i > 6)); -EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE ((i > 1) AND (i > 2) AND (i > 7)) OR ((i > 3) AND (i > 4) AND (i > 8)) OR ((i > 5) AND (i > 6)); EXPLAIN SYNTAX SELECT i FROM cnf_test WHERE ((i > 1) OR (i > 2) OR (i > 7)) AND ((i > 3) OR (i > 4) OR (i > 8)) AND NOT ((i > 5) OR (i > 6)); -EXPLAIN QUERY TREE SELECT i FROM cnf_test WHERE ((i > 1) OR (i > 2) OR (i > 7)) AND ((i > 3) OR (i > 4) OR (i > 8)) AND NOT ((i > 5) OR (i > 6)); DROP TABLE cnf_test; From f4b6205cab3d42b9058e9f594e911fb011325df2 Mon Sep 17 00:00:00 2001 From: HarryLeeIBM Date: Thu, 16 Mar 2023 13:29:33 -0700 Subject: [PATCH 051/377] Fix sql in previous commit --- tests/queries/0_stateless/00746_hashing_tuples.sql | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/queries/0_stateless/00746_hashing_tuples.sql b/tests/queries/0_stateless/00746_hashing_tuples.sql index 328ee5d6f05..f17ad6fa77f 100644 --- a/tests/queries/0_stateless/00746_hashing_tuples.sql +++ b/tests/queries/0_stateless/00746_hashing_tuples.sql @@ -5,10 +5,10 @@ SELECT sipHash64(1, 3, 2); SELECT sipHash64(('a', [1, 2, 3], 4, (4, ['foo', 'bar'], 1, (1, 2)))); SELECT hex(sipHash128('foo')) = hex(reverse(unhex('CC45107CC4B79F62D831BEF2103C7CBF'))) or hex(sipHash128('foo')) = 'CC45107CC4B79F62D831BEF2103C7CBF'; -SELECT hex(sipHash128('\x01')) = hex(reverse(unhex('DF2EC2F0669B000EDFF6ADEE264E7D68'))) or hex(sipHash128('foo')) = 'DF2EC2F0669B000EDFF6ADEE264E7D68'; -SELECT hex(sipHash128('foo', 'foo')) = hex(reverse(unhex('4CD1C30C38AB935D418B5269EF197B9E'))) or hex(sipHash128('foo')) = '4CD1C30C38AB935D418B5269EF197B9E'; -SELECT hex(sipHash128('foo', 'foo', 'foo')) = hex(reverse(unhex('9D78134EE48654D753CCA1B76185CF8E'))) or hex(sipHash128('foo')) = '9D78134EE48654D753CCA1B76185CF8E'; -SELECT hex(sipHash128(1, 2, 3)) = hex(reverse(unhex('389D16428D2AADEC9713905572F42864'))) or hex(sipHash128('foo')) = '389D16428D2AADEC9713905572F42864'; +SELECT hex(sipHash128('\x01')) = hex(reverse(unhex('DF2EC2F0669B000EDFF6ADEE264E7D68'))) or hex(sipHash128('\x01')) = 'DF2EC2F0669B000EDFF6ADEE264E7D68'; +SELECT hex(sipHash128('foo', 'foo')) = hex(reverse(unhex('4CD1C30C38AB935D418B5269EF197B9E'))) or hex(sipHash128('foo', 'foo')) = '4CD1C30C38AB935D418B5269EF197B9E'; +SELECT hex(sipHash128('foo', 'foo', 'foo')) = hex(reverse(unhex('9D78134EE48654D753CCA1B76185CF8E'))) or hex(sipHash128('foo', 'foo', 'foo')) = '9D78134EE48654D753CCA1B76185CF8E'; +SELECT hex(sipHash128(1, 2, 3)) = hex(reverse(unhex('389D16428D2AADEC9713905572F42864'))) or hex(sipHash128(1, 2, 3)) = '389D16428D2AADEC9713905572F42864'; SELECT halfMD5(1, 2, 3); SELECT halfMD5(1, 3, 2); @@ -26,8 +26,8 @@ SELECT murmurHash3_64(1, 2, 3); SELECT murmurHash3_64(1, 3, 2); SELECT murmurHash3_64(('a', [1, 2, 3], 4, (4, ['foo', 'bar'], 1, (1, 2)))); -SELECT hex(murmurHash3_128('foo', 'foo')) = hex(reverse(unhex('8DD5527CC43D76F4760D26BE0F641F7E'))) or hex(sipHash128('foo')) = '8DD5527CC43D76F4760D26BE0F641F7E'; -SELECT hex(murmurHash3_128('foo', 'foo', 'foo')) = hex(reverse(unhex('F8F7AD9B6CD4CF117A71E277E2EC2931'))) or hex(sipHash128('foo')) = 'F8F7AD9B6CD4CF117A71E277E2EC2931'; +SELECT hex(murmurHash3_128('foo', 'foo')) = hex(reverse(unhex('8DD5527CC43D76F4760D26BE0F641F7E'))) or hex(murmurHash3_128('foo', 'foo')) = '8DD5527CC43D76F4760D26BE0F641F7E'; +SELECT hex(murmurHash3_128('foo', 'foo', 'foo')) = hex(reverse(unhex('F8F7AD9B6CD4CF117A71E277E2EC2931'))) or hex(murmurHash3_128('foo', 'foo', 'foo')) = 'F8F7AD9B6CD4CF117A71E277E2EC2931'; SELECT gccMurmurHash(1, 2, 3); SELECT gccMurmurHash(1, 3, 2); From 45512333392114649a77bd6dccf26d331397a355 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Thu, 16 Mar 2023 20:50:21 +0000 Subject: [PATCH 052/377] Remove leftovers --- src/Processors/QueryPlan/ExpressionStep.cpp | 2 -- src/Processors/QueryPlan/FilterStep.cpp | 2 -- .../QueryPlan/updateDataStreams.cpp | 30 ------------------- 3 files changed, 34 deletions(-) delete mode 100644 src/Processors/QueryPlan/updateDataStreams.cpp diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index 22216cbfd27..8a1e10f0643 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -80,8 +80,6 @@ void ExpressionStep::updateOutputStream() return; const ActionsDAGPtr & actions = actions_dag; - LOG_DEBUG(&Poco::Logger::get(__PRETTY_FUNCTION__), "ActionsDAG dump:\n{}", actions->dumpDAG()); - const auto & input_sort_description = getInputStreams().front().sort_description; for (size_t i = 0, s = input_sort_description.size(); i < s; ++i) { diff --git a/src/Processors/QueryPlan/FilterStep.cpp b/src/Processors/QueryPlan/FilterStep.cpp index afca6903623..d63fded918d 100644 --- a/src/Processors/QueryPlan/FilterStep.cpp +++ b/src/Processors/QueryPlan/FilterStep.cpp @@ -110,8 +110,6 @@ void FilterStep::updateOutputStream() return; const ActionsDAGPtr & actions = actions_dag; - // LOG_DEBUG(&Poco::Logger::get(__PRETTY_FUNCTION__), "ActionsDAG dump:\n{}", actions->dumpDAG()); - const auto & input_sort_description = getInputStreams().front().sort_description; for (size_t i = 0, s = input_sort_description.size(); i < s; ++i) { diff --git a/src/Processors/QueryPlan/updateDataStreams.cpp b/src/Processors/QueryPlan/updateDataStreams.cpp deleted file mode 100644 index 6e4c9570143..00000000000 --- a/src/Processors/QueryPlan/updateDataStreams.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include "Processors/QueryPlan/ITransformingStep.h" - -namespace DB -{ - -// constexpr bool debug_logging_enabled = false; - -// class UpdateDataStreams : public QueryPlanVisitor -// { -// public: -// explicit UpdateDataStreams(QueryPlan::Node * root_) : QueryPlanVisitor(root_) { } - -// static bool visitTopDownImpl(QueryPlan::Node * /*current_node*/, QueryPlan::Node * /*parent_node*/) -// { -// return true; -// } - -// static void visitBottomUpImpl(QueryPlan::Node * current_node, QueryPlan::Node * parent_node) -// { -// if (parent_node->children.size() != 1) -// return; - -// chassert(current_node->step->hasOutputStream()); - -// if (auto * parent_transform_step = dynamic_cast(parent_node->step.get()); parent_transform_step) -// parent_transform_step->updateInputStream(current_node->step->getOutputStream()); -// } -// }; -} From d6efe7fc212c73b865d18352fed44b325dd2a5c1 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Fri, 17 Mar 2023 08:08:07 +0000 Subject: [PATCH 053/377] Fix --- .../tests/gtest_comparison_graph.cpp | 48 ++++++++--------- ..._constraints_simple_optimization.reference | 36 +++++++++++++ .../01622_constraints_simple_optimization.sql | 4 ++ ...2_constraints_where_optimization.reference | 52 +++++++++++++++++++ .../01622_constraints_where_optimization.sql | 5 ++ .../01625_constraints_index_append.reference | 8 +++ .../01625_constraints_index_append.sh | 35 +++++++++++++ .../01625_constraints_index_append.sql | 17 ------ 8 files changed, 164 insertions(+), 41 deletions(-) create mode 100755 tests/queries/0_stateless/01625_constraints_index_append.sh delete mode 100644 tests/queries/0_stateless/01625_constraints_index_append.sql diff --git a/src/Interpreters/tests/gtest_comparison_graph.cpp b/src/Interpreters/tests/gtest_comparison_graph.cpp index 72e72b4b802..32dcb3de6b3 100644 --- a/src/Interpreters/tests/gtest_comparison_graph.cpp +++ b/src/Interpreters/tests/gtest_comparison_graph.cpp @@ -9,11 +9,11 @@ using namespace DB; -static ComparisonGraph getGraph(const String & query) +static ComparisonGraph<> getGraph(const String & query) { ParserExpressionList parser(false); ASTPtr ast = parseQuery(parser, query, 0, 0); - return ComparisonGraph(ast->children); + return ComparisonGraph<>(ast->children); } TEST(ComparisonGraph, Bounds) @@ -47,8 +47,8 @@ TEST(ComparisonGraph, Bounds) auto x = std::make_shared("x"); auto y = std::make_shared("y"); - ASSERT_EQ(graph.compare(x, y), ComparisonGraph::CompareResult::LESS); - ASSERT_EQ(graph.compare(y, x), ComparisonGraph::CompareResult::GREATER); + ASSERT_EQ(graph.compare(x, y), ComparisonGraphCompareResult::LESS); + ASSERT_EQ(graph.compare(y, x), ComparisonGraphCompareResult::GREATER); } } @@ -93,7 +93,7 @@ TEST(ComparisonGraph, Components) TEST(ComparisonGraph, Compare) { - using CompareResult = ComparisonGraph::CompareResult; + using enum ComparisonGraphCompareResult; { String query = "a >= b, c >= b"; @@ -102,7 +102,7 @@ TEST(ComparisonGraph, Compare) auto a = std::make_shared("a"); auto c = std::make_shared("c"); - ASSERT_EQ(graph.compare(a, c), CompareResult::UNKNOWN); + ASSERT_EQ(graph.compare(a, c), UNKNOWN); } { @@ -113,9 +113,9 @@ TEST(ComparisonGraph, Compare) auto b = std::make_shared("b"); auto c = std::make_shared("c"); - ASSERT_EQ(graph.compare(a, c), CompareResult::GREATER); - ASSERT_EQ(graph.compare(a, b), CompareResult::GREATER_OR_EQUAL); - ASSERT_EQ(graph.compare(b, c), CompareResult::GREATER); + ASSERT_EQ(graph.compare(a, c), GREATER); + ASSERT_EQ(graph.compare(a, b), GREATER_OR_EQUAL); + ASSERT_EQ(graph.compare(b, c), GREATER); } { @@ -126,9 +126,9 @@ TEST(ComparisonGraph, Compare) auto b = std::make_shared("b"); auto c = std::make_shared("c"); - ASSERT_EQ(graph.compare(a, b), CompareResult::NOT_EQUAL); - ASSERT_EQ(graph.compare(a, c), CompareResult::GREATER); - ASSERT_EQ(graph.compare(b, c), CompareResult::UNKNOWN); + ASSERT_EQ(graph.compare(a, b), NOT_EQUAL); + ASSERT_EQ(graph.compare(a, c), GREATER); + ASSERT_EQ(graph.compare(b, c), UNKNOWN); } { @@ -154,17 +154,17 @@ TEST(ComparisonGraph, Compare) auto lit_3 = std::make_shared(3u); auto lit_4 = std::make_shared(4u); - ASSERT_EQ(graph.compare(lit_3, a), CompareResult::LESS_OR_EQUAL); - ASSERT_FALSE(graph.isAlwaysCompare(CompareResult::LESS, lit_3, a)); - ASSERT_TRUE(graph.isAlwaysCompare(CompareResult::LESS, lit_2, a)); + ASSERT_EQ(graph.compare(lit_3, a), LESS_OR_EQUAL); + ASSERT_FALSE(graph.isAlwaysCompare(LESS, lit_3, a)); + ASSERT_TRUE(graph.isAlwaysCompare(LESS, lit_2, a)); - ASSERT_EQ(graph.compare(b, lit_2), CompareResult::GREATER); - ASSERT_EQ(graph.compare(b, lit_3), CompareResult::GREATER); - ASSERT_EQ(graph.compare(b, lit_4), CompareResult::UNKNOWN); + ASSERT_EQ(graph.compare(b, lit_2), GREATER); + ASSERT_EQ(graph.compare(b, lit_3), GREATER); + ASSERT_EQ(graph.compare(b, lit_4), UNKNOWN); - ASSERT_EQ(graph.compare(d, lit_2), CompareResult::GREATER); - ASSERT_EQ(graph.compare(d, lit_3), CompareResult::GREATER_OR_EQUAL); - ASSERT_EQ(graph.compare(d, lit_4), CompareResult::UNKNOWN); + ASSERT_EQ(graph.compare(d, lit_2), GREATER); + ASSERT_EQ(graph.compare(d, lit_3), GREATER_OR_EQUAL); + ASSERT_EQ(graph.compare(d, lit_4), UNKNOWN); } { @@ -176,8 +176,8 @@ TEST(ComparisonGraph, Compare) auto lit_3 = std::make_shared(3); auto lit_15 = std::make_shared(15); - ASSERT_EQ(graph.compare(a, lit_8), CompareResult::UNKNOWN); - ASSERT_EQ(graph.compare(a, lit_3), CompareResult::GREATER); - ASSERT_EQ(graph.compare(a, lit_15), CompareResult::LESS); + ASSERT_EQ(graph.compare(a, lit_8), UNKNOWN); + ASSERT_EQ(graph.compare(a, lit_3), GREATER); + ASSERT_EQ(graph.compare(a, lit_15), LESS); } } diff --git a/tests/queries/0_stateless/01622_constraints_simple_optimization.reference b/tests/queries/0_stateless/01622_constraints_simple_optimization.reference index 7e012e1a17b..529351180b3 100644 --- a/tests/queries/0_stateless/01622_constraints_simple_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_simple_optimization.reference @@ -38,8 +38,44 @@ WHERE (c > 100) OR (b > 100) SELECT count() AS `count()` FROM constraint_test_constants WHERE c > 100 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.constraint_test_constants + WHERE + FUNCTION id: 4, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: c, result_type: Int64, source_id: 3 + CONSTANT id: 7, constant_value: UInt64_100, constant_value_type: UInt8 SELECT count() AS `count()` FROM constraint_test_constants WHERE c > 100 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.constraint_test_constants + WHERE + FUNCTION id: 4, function_name: greater, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: c, result_type: Int64, source_id: 3 + CONSTANT id: 7, constant_value: UInt64_100, constant_value_type: UInt8 SELECT count() AS `count()` FROM constraint_test_constants +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.constraint_test_constants diff --git a/tests/queries/0_stateless/01622_constraints_simple_optimization.sql b/tests/queries/0_stateless/01622_constraints_simple_optimization.sql index 7ec9e1a3158..21d75a48587 100644 --- a/tests/queries/0_stateless/01622_constraints_simple_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_simple_optimization.sql @@ -98,8 +98,12 @@ SELECT count() FROM constraint_test_constants WHERE 11 <= a; ---> assumption -> -- A AND NOT A EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100); +-- EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100); ---> the order of the generated checks is not consistent EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100); EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100); EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100); DROP TABLE constraint_test_constants; diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.reference b/tests/queries/0_stateless/01622_constraints_where_optimization.reference index c7c516025f2..52aca371a6a 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.reference @@ -1,14 +1,66 @@ SELECT count() FROM t_constraints_where WHERE 0 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + WHERE + CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE 0 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + WHERE + CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE 0 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + WHERE + CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 SELECT count() FROM t_constraints_where WHERE b < 8 +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + WHERE + FUNCTION id: 4, function_name: less, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: b, result_type: UInt32, source_id: 3 + CONSTANT id: 7, constant_value: UInt64_8, constant_value_type: UInt8 SELECT count() FROM t_constraints_where +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.sql b/tests/queries/0_stateless/01622_constraints_where_optimization.sql index 1b297942a74..33fa62368b0 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.sql @@ -8,9 +8,13 @@ CREATE TABLE t_constraints_where(a UInt32, b UInt32, CONSTRAINT c1 ASSUME b >= 5 INSERT INTO t_constraints_where VALUES (1, 7); EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 15; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 15; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 20; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 20; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) DROP TABLE t_constraints_where; @@ -19,5 +23,6 @@ CREATE TABLE t_constraints_where(a UInt32, b UInt32, CONSTRAINT c1 ASSUME b < 10 INSERT INTO t_constraints_where VALUES (1, 7); EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumption -> (b < 20) -> 0; +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumption -> (b < 20) -> 0; DROP TABLE t_constraints_where; diff --git a/tests/queries/0_stateless/01625_constraints_index_append.reference b/tests/queries/0_stateless/01625_constraints_index_append.reference index 0df5c429d9e..591d8a85897 100644 --- a/tests/queries/0_stateless/01625_constraints_index_append.reference +++ b/tests/queries/0_stateless/01625_constraints_index_append.reference @@ -2,14 +2,22 @@ SELECT i AS i FROM index_append_test_test PREWHERE a = 0 WHERE (a = 0) AND indexHint((i + 40) > 0) +SETTINGS convert_query_to_cnf = 1, optimize_using_constraints = 1, optimize_move_to_prewhere = 1, optimize_substitute_columns = 1, optimize_append_index = 1 +1 SELECT i AS i FROM index_append_test_test PREWHERE a < 0 +SETTINGS convert_query_to_cnf = 1, optimize_using_constraints = 1, optimize_move_to_prewhere = 1, optimize_substitute_columns = 1, optimize_append_index = 1 +0 SELECT i AS i FROM index_append_test_test PREWHERE a >= 0 WHERE (a >= 0) AND indexHint((i + 40) > 0) +SETTINGS convert_query_to_cnf = 1, optimize_using_constraints = 1, optimize_move_to_prewhere = 1, optimize_substitute_columns = 1, optimize_append_index = 1 +1 SELECT i AS i FROM index_append_test_test PREWHERE (2 * b) < 100 WHERE ((2 * b) < 100) AND indexHint(i < 100) +SETTINGS convert_query_to_cnf = 1, optimize_using_constraints = 1, optimize_move_to_prewhere = 1, optimize_substitute_columns = 1, optimize_append_index = 1 +1 diff --git a/tests/queries/0_stateless/01625_constraints_index_append.sh b/tests/queries/0_stateless/01625_constraints_index_append.sh new file mode 100755 index 00000000000..f17ea422409 --- /dev/null +++ b/tests/queries/0_stateless/01625_constraints_index_append.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +# We should have correct env vars from shell_config.sh to run this test + +$CLICKHOUSE_CLIENT --query "DROP TABLE IF EXISTS index_append_test_test;" + +$CLICKHOUSE_CLIENT --query "CREATE TABLE index_append_test_test (i Int64, a UInt32, b UInt64, CONSTRAINT c1 ASSUME i <= 2 * b AND i + 40 > a) ENGINE = MergeTree() ORDER BY i;" +$CLICKHOUSE_CLIENT --query "INSERT INTO index_append_test_test VALUES (1, 10, 1), (2, 20, 2);" + +function run_with_settings() +{ + query="$1 SETTINGS convert_query_to_cnf = 1\ + , optimize_using_constraints = 1\ + , optimize_move_to_prewhere = 1\ + , optimize_substitute_columns = 1\ + , optimize_append_index = 1" + + $CLICKHOUSE_CLIENT --query="$query" + +} + +run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a = 0" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a = 0" | grep -Fac "indexHint" +run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a < 0" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a < 0" | grep -Fac "indexHint" +run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a >= 0" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a >= 0" | grep -Fac "indexHint" +run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE 2 * b < 100" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE 2 * b < 100" | grep -Fac "indexHint" + +$CLICKHOUSE_CLIENT --query "DROP TABLE index_append_test_test;" diff --git a/tests/queries/0_stateless/01625_constraints_index_append.sql b/tests/queries/0_stateless/01625_constraints_index_append.sql deleted file mode 100644 index fbffc9c7f10..00000000000 --- a/tests/queries/0_stateless/01625_constraints_index_append.sql +++ /dev/null @@ -1,17 +0,0 @@ -SET convert_query_to_cnf = 1; -SET optimize_using_constraints = 1; -SET optimize_move_to_prewhere = 1; -SET optimize_substitute_columns = 1; -SET optimize_append_index = 1; - -DROP TABLE IF EXISTS index_append_test_test; - -CREATE TABLE index_append_test_test (i Int64, a UInt32, b UInt64, CONSTRAINT c1 ASSUME i <= 2 * b AND i + 40 > a) ENGINE = MergeTree() ORDER BY i; -INSERT INTO index_append_test_test VALUES (1, 10, 1), (2, 20, 2); - -EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a = 0; -EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a < 0; -EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a >= 0; -EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE 2 * b < 100; - -DROP TABLE index_append_test_test; From 0fb9f9ffe6018558cbbc26818b272dc191901d00 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Fri, 17 Mar 2023 13:38:01 +0000 Subject: [PATCH 054/377] Add support for substitute column --- src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 307 +++++++++++++++++- .../AddIndexConstraintsOptimizer.cpp | 2 +- src/Interpreters/ComparisonGraph.h | 2 +- .../SubstituteColumnOptimizer.cpp | 6 +- .../WhereConstraintsOptimizer.cpp | 12 +- .../tests/gtest_comparison_graph.cpp | 4 +- src/Storages/ConstraintsDescription.cpp | 8 +- src/Storages/ConstraintsDescription.h | 6 +- ...ergeTreeIndexHypothesisMergedCondition.cpp | 12 +- .../MergeTreeIndexHypothesisMergedCondition.h | 6 +- .../01623_constraints_column_swap.reference | 236 ++++++++++++++ .../01623_constraints_column_swap.sql | 16 +- 12 files changed, 580 insertions(+), 37 deletions(-) diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp index f807b096a78..591f2a1557c 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -9,7 +9,13 @@ #include #include +#include + #include +#include "Analyzer/HashUtils.h" +#include "Analyzer/IQueryTreeNode.h" +#include "Interpreters/ComparisonGraph.h" +#include "base/types.h" namespace DB { @@ -344,6 +350,286 @@ void addIndexConstraint(Analyzer::CNF & cnf, const QueryTreeNodes & table_expres } } +struct ColumnPrice +{ + Int64 compressed_size{0}; + Int64 uncompressed_size{0}; + + ColumnPrice(const Int64 compressed_size_, const Int64 uncompressed_size_) + : compressed_size(compressed_size_) + , uncompressed_size(uncompressed_size_) + { + } + + bool operator<(const ColumnPrice & that) const + { + return std::tie(compressed_size, uncompressed_size) < std::tie(that.compressed_size, that.uncompressed_size); + } + + ColumnPrice & operator+=(const ColumnPrice & that) + { + compressed_size += that.compressed_size; + uncompressed_size += that.uncompressed_size; + return *this; + } + + ColumnPrice & operator-=(const ColumnPrice & that) + { + compressed_size -= that.compressed_size; + uncompressed_size -= that.uncompressed_size; + return *this; + } +}; + +using ColumnPriceByName = std::unordered_map; +using ColumnPriceByQueryNode = QueryTreeNodePtrWithHashMap; + +class ComponentCollectorVisitor : public ConstInDepthQueryTreeVisitor +{ +public: + ComponentCollectorVisitor( + std::set & components_, + QueryTreeNodePtrWithHashMap & query_node_to_component_, + const ComparisonGraph & graph_) + : components(components_), query_node_to_component(query_node_to_component_), graph(graph_) + {} + + void visitImpl(const QueryTreeNodePtr & node) + { + if (auto id = graph.getComponentId(node)) + { + query_node_to_component.emplace(node, *id); + components.insert(*id); + } + } + +private: + std::set & components; + QueryTreeNodePtrWithHashMap & query_node_to_component; + + const ComparisonGraph & graph; +}; + +class ColumnNameCollectorVisitor : public ConstInDepthQueryTreeVisitor +{ +public: + ColumnNameCollectorVisitor( + std::unordered_set & column_names_, + const QueryTreeNodePtrWithHashMap & query_node_to_component_) + : column_names(column_names_), query_node_to_component(query_node_to_component_) + {} + + bool needChildVisit(const VisitQueryTreeNodeType & parent, const VisitQueryTreeNodeType &) + { + return !query_node_to_component.contains(parent); + } + + void visitImpl(const QueryTreeNodePtr & node) + { + if (query_node_to_component.contains(node)) + return; + + if (const auto * column_node = node->as()) + column_names.insert(column_node->getColumnName()); + } + +private: + std::unordered_set & column_names; + const QueryTreeNodePtrWithHashMap & query_node_to_component; +}; + +class SubstituteColumnVisitor : public InDepthQueryTreeVisitor +{ +public: + SubstituteColumnVisitor( + const QueryTreeNodePtrWithHashMap & query_node_to_component_, + const std::unordered_map & id_to_query_node_map_, + ContextPtr context_) + : query_node_to_component(query_node_to_component_), id_to_query_node_map(id_to_query_node_map_), context(std::move(context_)) + {} + + void visitImpl(QueryTreeNodePtr & node) + { + auto component_id_it = query_node_to_component.find(node); + if (component_id_it == query_node_to_component.end()) + return; + + const auto component_id = component_id_it->second; + auto new_node = id_to_query_node_map.at(component_id)->clone(); + + if (!node->getResultType()->equals(*new_node->getResultType())) + { + node = buildCastFunction(new_node, node->getResultType(), context); + return; + } + + node = std::move(new_node); + } + +private: + const QueryTreeNodePtrWithHashMap & query_node_to_component; + const std::unordered_map & id_to_query_node_map; + ContextPtr context; +}; + +ColumnPrice calculatePrice( + const ColumnPriceByName & column_prices, + const std::unordered_set & column_names) +{ + ColumnPrice result(0, 0); + + for (const auto & column : column_names) + { + if (auto it = column_prices.find(column); it != column_prices.end()) + result += it->second; + } + + return result; +} + + +void bruteForce( + const ComparisonGraph & graph, + const std::vector & components, + size_t current_component, + const ColumnPriceByName & column_prices, + ColumnPrice current_price, + std::vector & expressions_stack, + ColumnPrice & min_price, + std::vector & min_expressions) +{ + if (current_component == components.size()) + { + if (current_price < min_price) + { + min_price = current_price; + min_expressions = expressions_stack; + } + return; + } + + for (const auto & node : graph.getComponent(components[current_component])) + { + std::unordered_set column_names; + ColumnNameCollectorVisitor column_name_collector{column_names, {}}; + column_name_collector.visit(node); + + ColumnPrice expression_price = calculatePrice(column_prices, column_names); + + expressions_stack.push_back(node); + current_price += expression_price; + + ColumnPriceByName new_prices(column_prices); + for (const auto & column : column_names) + new_prices.insert_or_assign(column, ColumnPrice(0, 0)); + + bruteForce(graph, + components, + current_component + 1, + new_prices, + current_price, + expressions_stack, + min_price, + min_expressions); + + current_price -= expression_price; + expressions_stack.pop_back(); + } +} + +void substituteColumns(QueryNode & query_node, const QueryTreeNodes & table_expressions, const ContextPtr & context) +{ + static constexpr UInt64 COLUMN_PENALTY = 10 * 1024 * 1024; + static constexpr Int64 INDEX_PRICE = -1'000'000'000'000'000'000; + + for (const auto & table_expression : table_expressions) + { + auto snapshot = getStorageSnapshot(table_expression); + if (!snapshot || !snapshot->metadata) + continue; + + const auto column_sizes = snapshot->storage.getColumnSizes(); + if (column_sizes.empty()) + return; + + auto query_tree_constraint = snapshot->metadata->getConstraints().getQueryTreeData(context, table_expression); + const auto & graph = query_tree_constraint.getGraph(); + + auto run_for_all = [&](const auto function) + { + function(query_node.getProjectionNode()); + + if (query_node.hasWhere()) + function(query_node.getWhere()); + + if (query_node.hasPrewhere()) + function(query_node.getPrewhere()); + + if (query_node.hasHaving()) + function(query_node.getHaving()); + }; + + std::set components; + QueryTreeNodePtrWithHashMap query_node_to_component; + std::unordered_set column_names; + + run_for_all([&](QueryTreeNodePtr & node) + { + ComponentCollectorVisitor component_collector{components, query_node_to_component, graph}; + component_collector.visit(node); + ColumnNameCollectorVisitor column_name_collector{column_names, query_node_to_component}; + column_name_collector.visit(node); + }); + + ColumnPriceByName column_prices; + const auto primary_key = snapshot->metadata->getColumnsRequiredForPrimaryKey(); + + for (const auto & [column_name, column_size] : column_sizes) + column_prices.insert_or_assign(column_name, ColumnPrice(column_size.data_compressed + COLUMN_PENALTY, column_size.data_uncompressed)); + + for (const auto & column_name : primary_key) + column_prices.insert_or_assign(column_name, ColumnPrice(INDEX_PRICE, INDEX_PRICE)); + + for (const auto & column_name : column_names) + column_prices.insert_or_assign(column_name, ColumnPrice(0, 0)); + + std::unordered_map id_to_query_node_map; + std::vector components_list; + + for (const auto component_id : components) + { + auto component = graph.getComponent(component_id); + if (component.size() == 1) + id_to_query_node_map[component_id] = component.front(); + else + components_list.push_back(component_id); + } + + std::vector expressions_stack; + ColumnPrice min_price(std::numeric_limits::max(), std::numeric_limits::max()); + std::vector min_expressions; + + bruteForce(graph, + components_list, + 0, + column_prices, + ColumnPrice(0, 0), + expressions_stack, + min_price, + min_expressions); + + for (size_t i = 0; i < components_list.size(); ++i) + id_to_query_node_map[components_list[i]] = min_expressions[i]; + + SubstituteColumnVisitor substitute_column{query_node_to_component, id_to_query_node_map, context}; + + run_for_all([&](QueryTreeNodePtr & node) + { + substitute_column.visit(node); + }); + } +} + void optimizeWithConstraints(Analyzer::CNF & cnf, const QueryTreeNodes & table_expressions, const ContextPtr & context) { cnf.pullNotOutFunctions(context); @@ -410,11 +696,24 @@ public: auto table_expressions = extractTableExpressions(query_node->getJoinTree()); - if (query_node->hasWhere()) - optimizeNode(query_node->getWhere(), table_expressions, getContext()); + const auto & context = getContext(); + const auto & settings = context->getSettingsRef(); - if (query_node->hasPrewhere()) - optimizeNode(query_node->getPrewhere(), table_expressions, getContext()); + bool has_filter = false; + const auto optimize_filter = [&](QueryTreeNodePtr & filter_node) + { + if (filter_node == nullptr) + return; + + optimizeNode(query_node->getWhere(), table_expressions, context); + has_filter = true; + }; + + optimize_filter(query_node->getWhere()); + optimize_filter(query_node->getPrewhere()); + + if (has_filter && settings.optimize_substitute_columns) + substituteColumns(*query_node, table_expressions, context); } }; diff --git a/src/Interpreters/AddIndexConstraintsOptimizer.cpp b/src/Interpreters/AddIndexConstraintsOptimizer.cpp index 15adc737f6f..b9d56ec2ff8 100644 --- a/src/Interpreters/AddIndexConstraintsOptimizer.cpp +++ b/src/Interpreters/AddIndexConstraintsOptimizer.cpp @@ -104,7 +104,7 @@ namespace /// we can add to expression 'indexHint(I < A)' condition. CNFQuery::OrGroup createIndexHintGroup( const CNFQuery::OrGroup & group, - const ComparisonGraph<> & graph, + const ComparisonGraph & graph, const ASTs & primary_key_only_asts) { CNFQuery::OrGroup result; diff --git a/src/Interpreters/ComparisonGraph.h b/src/Interpreters/ComparisonGraph.h index 19b23917917..ecfc617ac8a 100644 --- a/src/Interpreters/ComparisonGraph.h +++ b/src/Interpreters/ComparisonGraph.h @@ -33,7 +33,7 @@ concept ComparisonGraphNodeType = std::same_as || std::same_as +template class ComparisonGraph { public: diff --git a/src/Interpreters/SubstituteColumnOptimizer.cpp b/src/Interpreters/SubstituteColumnOptimizer.cpp index 4cc9572749f..c4aef89fed2 100644 --- a/src/Interpreters/SubstituteColumnOptimizer.cpp +++ b/src/Interpreters/SubstituteColumnOptimizer.cpp @@ -32,13 +32,13 @@ public: struct Data { - const ComparisonGraph<> & graph; + const ComparisonGraph & graph; std::set & components; std::unordered_map & old_name; std::unordered_map & component; UInt64 & current_id; - Data(const ComparisonGraph<> & graph_, + Data(const ComparisonGraph & graph_, std::set & components_, std::unordered_map & old_name_, std::unordered_map & component_, @@ -165,7 +165,7 @@ ColumnPrice calculatePrice( /// price of all columns on which ast depends. /// TODO: branch-and-bound void bruteforce( - const ComparisonGraph<> & graph, + const ComparisonGraph & graph, const std::vector & components, size_t current_component, const ColumnPriceByName & column_prices, diff --git a/src/Interpreters/WhereConstraintsOptimizer.cpp b/src/Interpreters/WhereConstraintsOptimizer.cpp index e3934e8ea7f..91c19fa264e 100644 --- a/src/Interpreters/WhereConstraintsOptimizer.cpp +++ b/src/Interpreters/WhereConstraintsOptimizer.cpp @@ -74,7 +74,7 @@ bool checkIfGroupAlwaysTrueFullMatch(const CNFQuery::OrGroup & group, const Cons return false; } -bool checkIfGroupAlwaysTrueGraph(const CNFQuery::OrGroup & group, const ComparisonGraph<> & graph) +bool checkIfGroupAlwaysTrueGraph(const CNFQuery::OrGroup & group, const ComparisonGraph & graph) { /// We try to find at least one atom that is always true by using comparison graph. for (const auto & atom : group) @@ -82,7 +82,7 @@ bool checkIfGroupAlwaysTrueGraph(const CNFQuery::OrGroup & group, const Comparis const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2) { - const auto expected = ComparisonGraph<>::atomToCompareResult(atom); + const auto expected = ComparisonGraph::atomToCompareResult(atom); if (graph.isAlwaysCompare(expected, func->arguments->children[0], func->arguments->children[1])) return true; } @@ -108,20 +108,20 @@ bool checkIfAtomAlwaysFalseFullMatch(const CNFQuery::AtomicFormula & atom, const return false; } -bool checkIfAtomAlwaysFalseGraph(const CNFQuery::AtomicFormula & atom, const ComparisonGraph<> & graph) +bool checkIfAtomAlwaysFalseGraph(const CNFQuery::AtomicFormula & atom, const ComparisonGraph & graph) { const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2) { /// TODO: special support for != - const auto expected = ComparisonGraph<>::atomToCompareResult(atom); + const auto expected = ComparisonGraph::atomToCompareResult(atom); return !graph.isPossibleCompare(expected, func->arguments->children[0], func->arguments->children[1]); } return false; } -void replaceToConstants(ASTPtr & term, const ComparisonGraph<> & graph) +void replaceToConstants(ASTPtr & term, const ComparisonGraph & graph) { const auto equal_constant = graph.getEqualConst(term); if (equal_constant) @@ -135,7 +135,7 @@ void replaceToConstants(ASTPtr & term, const ComparisonGraph<> & graph) } } -CNFQuery::AtomicFormula replaceTermsToConstants(const CNFQuery::AtomicFormula & atom, const ComparisonGraph<> & graph) +CNFQuery::AtomicFormula replaceTermsToConstants(const CNFQuery::AtomicFormula & atom, const ComparisonGraph & graph) { CNFQuery::AtomicFormula result; result.negative = atom.negative; diff --git a/src/Interpreters/tests/gtest_comparison_graph.cpp b/src/Interpreters/tests/gtest_comparison_graph.cpp index 32dcb3de6b3..96a78241c8e 100644 --- a/src/Interpreters/tests/gtest_comparison_graph.cpp +++ b/src/Interpreters/tests/gtest_comparison_graph.cpp @@ -9,11 +9,11 @@ using namespace DB; -static ComparisonGraph<> getGraph(const String & query) +static ComparisonGraph getGraph(const String & query) { ParserExpressionList parser(false); ASTPtr ast = parseQuery(parser, query, 0, 0); - return ComparisonGraph<>(ast->children); + return ComparisonGraph(ast->children); } TEST(ComparisonGraph, Bounds) diff --git a/src/Storages/ConstraintsDescription.cpp b/src/Storages/ConstraintsDescription.cpp index 3f4d264b4a1..db37ac7c4c3 100644 --- a/src/Storages/ConstraintsDescription.cpp +++ b/src/Storages/ConstraintsDescription.cpp @@ -110,7 +110,7 @@ std::vector ConstraintsDescription::getAtomicConstraint return constraint_data; } -std::unique_ptr> ConstraintsDescription::buildGraph() const +std::unique_ptr> ConstraintsDescription::buildGraph() const { static const NameSet relations = { "equals", "less", "lessOrEquals", "greaterOrEquals", "greater" }; @@ -128,7 +128,7 @@ std::unique_ptr> ConstraintsDescription::buildGraph() const } } - return std::make_unique>(constraints_for_graph); + return std::make_unique>(constraints_for_graph); } ConstraintsExpressions ConstraintsDescription::getExpressions(const DB::ContextPtr context, @@ -150,7 +150,7 @@ ConstraintsExpressions ConstraintsDescription::getExpressions(const DB::ContextP return res; } -const ComparisonGraph<> & ConstraintsDescription::getGraph() const +const ComparisonGraph & ConstraintsDescription::getGraph() const { return *graph; } @@ -313,7 +313,7 @@ void ConstraintsDescription::update() { cnf_constraints.clear(); ast_to_atom_ids.clear(); - graph = std::make_unique>(ASTs()); + graph = std::make_unique>(ASTs()); return; } diff --git a/src/Storages/ConstraintsDescription.h b/src/Storages/ConstraintsDescription.h index 2c34ef1ef37..33bd8e1abf9 100644 --- a/src/Storages/ConstraintsDescription.h +++ b/src/Storages/ConstraintsDescription.h @@ -43,7 +43,7 @@ public: const std::vector> & getConstraintData() const; std::vector getAtomicConstraintData() const; - const ComparisonGraph<> & getGraph() const; + const ComparisonGraph & getGraph() const; ConstraintsExpressions getExpressions(ContextPtr context, const NamesAndTypesList & source_columns_) const; @@ -79,7 +79,7 @@ public: private: std::vector> buildConstraintData() const; - std::unique_ptr> buildGraph() const; + std::unique_ptr> buildGraph() const; void update(); ASTs constraints; @@ -87,7 +87,7 @@ private: std::vector> cnf_constraints; std::map ast_to_atom_ids; - std::unique_ptr> graph; + std::unique_ptr> graph; }; } diff --git a/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.cpp b/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.cpp index 2bb6857a855..4227ffc6873 100644 --- a/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.cpp +++ b/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.cpp @@ -108,7 +108,7 @@ bool MergeTreeIndexhypothesisMergedCondition::alwaysUnknownOrTrue() const func->name = "greaterOrEquals"; } - const auto weak_graph = std::make_unique>(active_atomic_formulas); + const auto weak_graph = std::make_unique>(active_atomic_formulas); bool useless = true; expression_cnf->iterateGroups( @@ -146,7 +146,7 @@ bool MergeTreeIndexhypothesisMergedCondition::mayBeTrueOnGranule(const MergeTree values.push_back(granule->met); } - const ComparisonGraph<> * graph = nullptr; + const ComparisonGraph * graph = nullptr; { std::lock_guard lock(cache_mutex); @@ -170,7 +170,7 @@ bool MergeTreeIndexhypothesisMergedCondition::mayBeTrueOnGranule(const MergeTree const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2) { - const auto expected = ComparisonGraph<>::atomToCompareResult(atom); + const auto expected = ComparisonGraph::atomToCompareResult(atom); if (graph->isPossibleCompare(expected, func->arguments->children[0], func->arguments->children[1])) { /// If graph failed use matching. @@ -188,7 +188,7 @@ bool MergeTreeIndexhypothesisMergedCondition::mayBeTrueOnGranule(const MergeTree return !always_false; } -std::unique_ptr> MergeTreeIndexhypothesisMergedCondition::buildGraph(const std::vector & values) const +std::unique_ptr> MergeTreeIndexhypothesisMergedCondition::buildGraph(const std::vector & values) const { ASTs active_atomic_formulas(atomic_constraints); for (size_t i = 0; i < values.size(); ++i) @@ -199,10 +199,10 @@ std::unique_ptr> MergeTreeIndexhypothesisMergedCondition::buil std::begin(index_to_compare_atomic_hypotheses[i]), std::end(index_to_compare_atomic_hypotheses[i])); } - return std::make_unique>(active_atomic_formulas); + return std::make_unique>(active_atomic_formulas); } -const ComparisonGraph<> * MergeTreeIndexhypothesisMergedCondition::getGraph(const std::vector & values) const +const ComparisonGraph * MergeTreeIndexhypothesisMergedCondition::getGraph(const std::vector & values) const { auto [it, inserted] = graph_cache.try_emplace(values); if (inserted) diff --git a/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.h b/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.h index f08cfba6ca0..3ab82f4d3ee 100644 --- a/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.h +++ b/src/Storages/MergeTree/MergeTreeIndexHypothesisMergedCondition.h @@ -20,8 +20,8 @@ public: private: void addConstraints(const ConstraintsDescription & constraints_description); - std::unique_ptr> buildGraph(const std::vector & values) const; - const ComparisonGraph<> * getGraph(const std::vector & values) const; + std::unique_ptr> buildGraph(const std::vector & values) const; + const ComparisonGraph * getGraph(const std::vector & values) const; ASTPtr expression_ast; std::unique_ptr expression_cnf; @@ -29,7 +29,7 @@ private: /// Part analysis can be done in parallel. /// So, we have shared answer and graph cache. mutable std::mutex cache_mutex; - mutable std::unordered_map, std::unique_ptr>> graph_cache; + mutable std::unordered_map, std::unique_ptr>> graph_cache; mutable std::unordered_map, bool> answer_cache; std::vector> index_to_compare_atomic_hypotheses; diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.reference b/tests/queries/0_stateless/01623_constraints_column_swap.reference index 7ae4516fe9e..520bd16ae25 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.reference +++ b/tests/queries/0_stateless/01623_constraints_column_swap.reference @@ -3,51 +3,287 @@ SELECT (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 1 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + COLUMN id: 4, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 6, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 7, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 8, nodes: 2 + COLUMN id: 9, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 10, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 5, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 11, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 12, nodes: 2 + COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 14, constant_value: UInt64_1, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 0 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + COLUMN id: 4, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 6, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 7, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 8, nodes: 2 + COLUMN id: 9, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 10, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 5, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 11, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 12, nodes: 2 + COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 14, constant_value: UInt64_0, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 0 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + COLUMN id: 4, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 6, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 7, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 8, nodes: 2 + COLUMN id: 9, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 10, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 5, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 11, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 12, nodes: 2 + COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 14, constant_value: UInt64_0, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` FROM column_swap_test_test WHERE b = 1 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + COLUMN id: 4, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 6, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 7, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 8, nodes: 2 + COLUMN id: 9, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 10, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 5, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 11, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 12, nodes: 2 + COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 14, constant_value: UInt64_1, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)` FROM column_swap_test_test WHERE b = 0 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + COLUMN id: 4, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 6, constant_value: UInt64_10, constant_value_type: UInt8 + JOIN TREE + TABLE id: 5, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 7, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 8, nodes: 2 + COLUMN id: 9, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 10, constant_value: UInt64_0, constant_value_type: UInt8 SELECT (cityHash64(a) AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, a AS a FROM column_swap_test_test WHERE cityHash64(a) = 0 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + a String + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 5, nodes: 1 + COLUMN id: 6, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 + COLUMN id: 9, column_name: a, result_type: String, source_id: 7 + JOIN TREE + TABLE id: 7, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 10, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 11, nodes: 2 + FUNCTION id: 12, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 13, nodes: 1 + COLUMN id: 14, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 15, constant_value: UInt64_0, constant_value_type: UInt8 SELECT (cityHash64(a) AS b) + 10 AS `plus(b, 10)`, a AS a FROM column_swap_test_test WHERE cityHash64(a) = 0 +QUERY id: 0 + PROJECTION COLUMNS + plus(b, 10) UInt64 + a String + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + FUNCTION id: 4, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 5, nodes: 1 + COLUMN id: 6, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 8, constant_value: UInt64_10, constant_value_type: UInt8 + COLUMN id: 9, column_name: a, result_type: String, source_id: 7 + JOIN TREE + TABLE id: 7, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 10, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 11, nodes: 2 + FUNCTION id: 12, function_name: cityHash64, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 13, nodes: 1 + COLUMN id: 14, column_name: a, result_type: String, source_id: 7 + CONSTANT id: 15, constant_value: UInt64_0, constant_value_type: UInt8 SELECT a AS `substring(reverse(b), 1, 1)`, a AS a FROM column_swap_test_test WHERE a = \'c\' +QUERY id: 0 + PROJECTION COLUMNS + substring(reverse(b), 1, 1) String + a String + PROJECTION + LIST id: 1, nodes: 2 + COLUMN id: 2, column_name: a, result_type: String, source_id: 3 + COLUMN id: 4, column_name: a, result_type: String, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 5, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 6, nodes: 2 + COLUMN id: 7, column_name: a, result_type: String, source_id: 3 + CONSTANT id: 8, constant_value: \'c\', constant_value_type: String SELECT a AS `substring(reverse(b), 1, 1)`, a AS a FROM column_swap_test_test WHERE a = \'c\' +QUERY id: 0 + PROJECTION COLUMNS + substring(reverse(b), 1, 1) String + a String + PROJECTION + LIST id: 1, nodes: 2 + COLUMN id: 2, column_name: a, result_type: String, source_id: 3 + COLUMN id: 4, column_name: a, result_type: String, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 5, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 6, nodes: 2 + COLUMN id: 7, column_name: a, result_type: String, source_id: 3 + CONSTANT id: 8, constant_value: \'c\', constant_value_type: String SELECT a AS t1, a AS t2 FROM column_swap_test_test WHERE a = \'c\' +QUERY id: 0 + PROJECTION COLUMNS + t1 String + t2 String + PROJECTION + LIST id: 1, nodes: 2 + COLUMN id: 2, column_name: a, result_type: String, source_id: 3 + COLUMN id: 4, column_name: a, result_type: String, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 5, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 6, nodes: 2 + COLUMN id: 7, column_name: a, result_type: String, source_id: 3 + CONSTANT id: 8, constant_value: \'c\', constant_value_type: String SELECT a AS `substring(reverse(b), 1, 1)` FROM column_swap_test_test WHERE a = \'c\' +QUERY id: 0 + PROJECTION COLUMNS + substring(reverse(b), 1, 1) String + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: a, result_type: String, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.column_swap_test_test + WHERE + FUNCTION id: 4, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: a, result_type: String, source_id: 3 + CONSTANT id: 7, constant_value: \'c\', constant_value_type: String SELECT a FROM t_bad_constraint +QUERY id: 0 + PROJECTION COLUMNS + a UInt32 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: a, result_type: UInt32, source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.t_bad_constraint diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.sql b/tests/queries/0_stateless/01623_constraints_column_swap.sql index 873ebbed729..97e014d9c25 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.sql +++ b/tests/queries/0_stateless/01623_constraints_column_swap.sql @@ -13,17 +13,20 @@ INSERT INTO column_swap_test_test VALUES (1, 'cat', 1), (2, 'dog', 2); INSERT INTO column_swap_test_test SELECT number AS i, format('test {} kek {}', toString(number), toString(number + 10)) AS a, 1 AS b FROM system.numbers LIMIT 1000000; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; ---EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; ---EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; ---EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; ---EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10 FROM column_swap_test_test WHERE cityHash64(a) = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10 FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, a FROM column_swap_test_test WHERE cityHash64(a) = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, a FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN SYNTAX SELECT b + 10, a FROM column_swap_test_test WHERE b = 0; +EXPLAIN QUERY TREE SELECT b + 10, a FROM column_swap_test_test WHERE b = 0; DROP TABLE column_swap_test_test; @@ -31,9 +34,13 @@ CREATE TABLE column_swap_test_test (i Int64, a String, b String, CONSTRAINT c1 A INSERT INTO column_swap_test_test SELECT number AS i, toString(number) AS a, format('test {} kek {}', toString(number), toString(number + 10)) b FROM system.numbers LIMIT 1000000; EXPLAIN SYNTAX SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE a = 'c'; +EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE a = 'c'; EXPLAIN SYNTAX SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; +EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; EXPLAIN SYNTAX SELECT substring(reverse(b), 1, 1) AS t1, a AS t2 FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; +EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1) AS t1, a AS t2 FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; EXPLAIN SYNTAX SELECT substring(reverse(b), 1, 1) FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; +EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1) FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; DROP TABLE column_swap_test_test; @@ -44,5 +51,6 @@ CREATE TABLE t_bad_constraint(a UInt32, s String, CONSTRAINT c1 ASSUME a = toUIn INSERT INTO t_bad_constraint SELECT number, randomPrintableASCII(100) FROM numbers(10000); EXPLAIN SYNTAX SELECT a FROM t_bad_constraint; +EXPLAIN QUERY TREE SELECT a FROM t_bad_constraint; DROP TABLE t_bad_constraint; From 7f8622752c3d34b64a1fb371d4fae6f8128469cf Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Fri, 17 Mar 2023 14:17:16 +0000 Subject: [PATCH 055/377] Update description --- src/Analyzer/Passes/ConvertQueryToCNFPass.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.h b/src/Analyzer/Passes/ConvertQueryToCNFPass.h index 71fb28bdf85..232af3b015e 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.h +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.h @@ -8,9 +8,9 @@ namespace DB class ConvertQueryToCNFPass final : public IQueryTreePass { public: - String getName() override { return "ConvertQueryToCnfPass"; } + String getName() override { return "ConvertQueryToCNFPass"; } - String getDescription() override { return "Convert query to CNF"; } + String getDescription() override { return "Convert query to CNF and apply optimizations using constraints"; } void run(QueryTreeNodePtr query_tree_node, ContextPtr context) override; }; From a2c0e2e4e7e2865ad1ac70fd4d38f8995dfa26b2 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Fri, 17 Mar 2023 14:55:17 +0000 Subject: [PATCH 056/377] Use pointer --- src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp index 591f2a1557c..d5d11a19ffe 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -415,18 +415,18 @@ class ColumnNameCollectorVisitor : public ConstInDepthQueryTreeVisitor & column_names_, - const QueryTreeNodePtrWithHashMap & query_node_to_component_) + const QueryTreeNodePtrWithHashMap * query_node_to_component_) : column_names(column_names_), query_node_to_component(query_node_to_component_) {} bool needChildVisit(const VisitQueryTreeNodeType & parent, const VisitQueryTreeNodeType &) { - return !query_node_to_component.contains(parent); + return !query_node_to_component || !query_node_to_component->contains(parent); } void visitImpl(const QueryTreeNodePtr & node) { - if (query_node_to_component.contains(node)) + if (query_node_to_component && query_node_to_component->contains(node)) return; if (const auto * column_node = node->as()) @@ -435,7 +435,7 @@ public: private: std::unordered_set & column_names; - const QueryTreeNodePtrWithHashMap & query_node_to_component; + const QueryTreeNodePtrWithHashMap * query_node_to_component; }; class SubstituteColumnVisitor : public InDepthQueryTreeVisitor @@ -511,7 +511,7 @@ void bruteForce( for (const auto & node : graph.getComponent(components[current_component])) { std::unordered_set column_names; - ColumnNameCollectorVisitor column_name_collector{column_names, {}}; + ColumnNameCollectorVisitor column_name_collector{column_names, nullptr}; column_name_collector.visit(node); ColumnPrice expression_price = calculatePrice(column_prices, column_names); @@ -577,7 +577,7 @@ void substituteColumns(QueryNode & query_node, const QueryTreeNodes & table_expr { ComponentCollectorVisitor component_collector{components, query_node_to_component, graph}; component_collector.visit(node); - ColumnNameCollectorVisitor column_name_collector{column_names, query_node_to_component}; + ColumnNameCollectorVisitor column_name_collector{column_names, &query_node_to_component}; column_name_collector.visit(node); }); From 2b3c42335a57533b4a06bbf57803dcbf202509ad Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 18 Mar 2023 03:12:04 +0100 Subject: [PATCH 057/377] Fix some strange code --- src/Functions/array/arrayIntersect.cpp | 45 ++++++++++++++------------ 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/Functions/array/arrayIntersect.cpp b/src/Functions/array/arrayIntersect.cpp index c6f0a5afa62..61fde13cff9 100644 --- a/src/Functions/array/arrayIntersect.cpp +++ b/src/Functions/array/arrayIntersect.cpp @@ -22,7 +22,6 @@ #include #include #include -#include namespace DB @@ -219,11 +218,12 @@ FunctionArrayIntersect::CastArgumentsResult FunctionArrayIntersect::castColumns( const auto & type_nested = type_array->getNestedType(); auto type_not_nullable_nested = removeNullable(type_nested); - const bool is_numeric_or_string = isNativeNumber(type_not_nullable_nested) - || isDate(type_not_nullable_nested) - || isDateTime(type_not_nullable_nested) - || isDateTime64(type_not_nullable_nested) - || isStringOrFixedString(type_not_nullable_nested); + const bool is_numeric_or_string = + isNumber(type_not_nullable_nested) + || isDate(type_not_nullable_nested) + || isDateTime(type_not_nullable_nested) + || isDateTime64(type_not_nullable_nested) + || isStringOrFixedString(type_not_nullable_nested); DataTypePtr nullable_return_type; @@ -303,7 +303,7 @@ FunctionArrayIntersect::UnpackedArrays FunctionArrayIntersect::prepareArrays( bool all_const = true; - for (auto i : collections::range(0, columns_number)) + for (size_t i = 0; i < columns_number; ++i) { auto & arg = arrays.args[i]; const auto * argument_column = columns[i].column.get(); @@ -313,7 +313,7 @@ FunctionArrayIntersect::UnpackedArrays FunctionArrayIntersect::prepareArrays( { arg.is_const = true; argument_column = argument_column_const->getDataColumnPtr().get(); - initial_column = typeid_cast(initial_column)->getDataColumnPtr().get(); + initial_column = typeid_cast(*initial_column).getDataColumnPtr().get(); } if (const auto * argument_column_array = typeid_cast(argument_column)) @@ -324,22 +324,28 @@ FunctionArrayIntersect::UnpackedArrays FunctionArrayIntersect::prepareArrays( arg.offsets = &argument_column_array->getOffsets(); arg.nested_column = &argument_column_array->getData(); - initial_column = &typeid_cast(initial_column)->getData(); + initial_column = &typeid_cast(*initial_column).getData(); if (const auto * column_nullable = typeid_cast(arg.nested_column)) { arg.null_map = &column_nullable->getNullMapData(); arg.nested_column = &column_nullable->getNestedColumn(); - initial_column = &typeid_cast(initial_column)->getNestedColumn(); + + if (initial_column->isNullable()) + initial_column = &typeid_cast(*initial_column).getNestedColumn(); } - /// In case column was casted need to create overflow mask for integer types. + /// In case the column was casted, we need to create an overflow mask for integer types. if (arg.nested_column != initial_column) { - const auto & nested_init_type = typeid_cast(removeNullable(initial_columns[i].type).get())->getNestedType(); - const auto & nested_cast_type = typeid_cast(removeNullable(columns[i].type).get())->getNestedType(); + const auto & nested_init_type = typeid_cast(*removeNullable(initial_columns[i].type)).getNestedType(); + const auto & nested_cast_type = typeid_cast(*removeNullable(columns[i].type)).getNestedType(); - if (isInteger(nested_init_type) || isDate(nested_init_type) || isDateTime(nested_init_type) || isDateTime64(nested_init_type)) + if (isInteger(nested_init_type) + || isDate(nested_init_type) + || isDateTime(nested_init_type) + || isDateTime64(nested_init_type) + || isDecimal(nested_init_type)) { /// Compare original and casted columns. It seem to be the easiest way. auto overflow_mask = callFunctionNotEquals( @@ -347,7 +353,7 @@ FunctionArrayIntersect::UnpackedArrays FunctionArrayIntersect::prepareArrays( {initial_column->getPtr(), nested_cast_type, ""}, context); - arg.overflow_mask = &typeid_cast(overflow_mask.get())->getData(); + arg.overflow_mask = &typeid_cast(*overflow_mask).getData(); arrays.column_holders.emplace_back(std::move(overflow_mask)); } } @@ -362,7 +368,7 @@ FunctionArrayIntersect::UnpackedArrays FunctionArrayIntersect::prepareArrays( } else { - for (auto i : collections::range(0, columns_number)) + for (size_t i = 0; i < columns_number; ++i) { if (arrays.args[i].is_const) continue; @@ -371,7 +377,7 @@ FunctionArrayIntersect::UnpackedArrays FunctionArrayIntersect::prepareArrays( if (arrays.base_rows == 0 && rows > 0) arrays.base_rows = rows; else if (arrays.base_rows != rows) - throw Exception(ErrorCodes::LOGICAL_ERROR, "Non-const array columns in function {}should have same rows", getName()); + throw Exception(ErrorCodes::LOGICAL_ERROR, "Non-const array columns in function {} should have the same number of rows", getName()); } } @@ -397,7 +403,6 @@ ColumnPtr FunctionArrayIntersect::executeImpl(const ColumnsWithTypeAndName & arg data_types.push_back(arguments[i].type); auto return_type_with_nulls = getMostSubtype(data_types, true, true); - auto casted_columns = castColumns(arguments, result_type, return_type_with_nulls); UnpackedArrays arrays = prepareArrays(casted_columns.casted, casted_columns.initial); @@ -501,13 +506,13 @@ ColumnPtr FunctionArrayIntersect::execute(const UnpackedArrays & arrays, Mutable Map map; std::vector prev_off(args, 0); size_t result_offset = 0; - for (auto row : collections::range(0, rows)) + for (size_t row = 0; row < rows; ++row) { map.clear(); bool all_has_nullable = all_nullable; - for (auto arg_num : collections::range(0, args)) + for (size_t arg_num = 0; arg_num < args; ++arg_num) { const auto & arg = arrays.args[arg_num]; bool current_has_nullable = false; From e805d75756fdb3650f4c2f31043286f44b3fbfef Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 20 Mar 2023 07:10:47 +0100 Subject: [PATCH 058/377] ClickHouse Obfuscator: add README --- programs/obfuscator/Obfuscator.cpp | 1 - programs/obfuscator/README.md | 354 +++++++++++++++++++++++++++++ 2 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 programs/obfuscator/README.md diff --git a/programs/obfuscator/Obfuscator.cpp b/programs/obfuscator/Obfuscator.cpp index 274ad29a174..add16ec5205 100644 --- a/programs/obfuscator/Obfuscator.cpp +++ b/programs/obfuscator/Obfuscator.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/programs/obfuscator/README.md b/programs/obfuscator/README.md new file mode 100644 index 00000000000..8c2aaafb3ac --- /dev/null +++ b/programs/obfuscator/README.md @@ -0,0 +1,354 @@ +## clickhouse-obfuscator — a tool for dataset anonymization + +### Installation And Usage + +``` +curl https://clickhouse.com/ | sh +./clickhouse obfuscator --help +``` + +### Example + +``` +./clickhouse obfuscator --seed 123 --input-format TSV --output-format TSV \ + --structure 'CounterID UInt32, URLDomain String, URL String, SearchPhrase String, Title String' \ + < source.tsv > result.tsv +``` + + +### A long, long time ago... + +ClickHouse users already know that its biggest advantage is its high-speed processing of analytical queries. But claims like this need to be confirmed with reliable performance testing. That's what we want to talk about today. + +![benchmarks.png](https://clickhouse.com/uploads/benchmarks_24f1904cc9.png) + +We started running tests in 2013, long before ClickHouse was available as open source. Back then, our main concern was data processing speed for a web analytics product. We started storing this data, which we would later store in ClickHouse, in January 2009. Part of the data had been written to a database starting in 2012, and part was converted from OLAPServer and Metrage (data structures previously used by the solution). For testing, we took the first subset at random from data for 1 billion pageviews. Our web analytics platform didn't have any queries at that point, so we came up with queries that interested us, using all the possible ways to filter, aggregate, and sort the data. + +ClickHouse performance was compared with similar systems like Vertica and MonetDB. To avoid bias, testing was performed by an employee who hadn't participated in ClickHouse development, and special cases in the code were not optimized until all the results were obtained. We used the same approach to get a data set for functional testing. + +After ClickHouse was released as open source in 2016, people began questioning these tests. + +## Shortcomings of tests on private data + +Our performance tests: + +- Couldn't be reproduced independently because they used private data that can't be published. Some of the functional tests are not available to external users for the same reason. +- Needed further development. The set of tests needed to be substantially expanded in order to isolate performance changes in individual parts of the system. +- Didn't run on a per-commit basis or for individual pull requests. External developers couldn't check their code for performance regressions. + +We could solve these problems by throwing out the old tests and writing new ones based on open data, like [flight data for the USA](https://clickhouse.com/docs/en/getting-started/example-datasets/ontime/) and [taxi rides in New York](https://clickhouse.com/docs/en/getting-started/example-datasets/nyc-taxi). Or we could use benchmarks like TPC-H, TPC-DS, and [Star Schema Benchmark](https://clickhouse.com/docs/en/getting-started/example-datasets/star-schema). The disadvantage is that this data was very different from web analytics data, and we would rather keep the test queries. + +### Why it's important to use real data + +Performance should only be tested on real data from a production environment. Let's look at some examples. + +### Example 1 + +Let's say you fill a database with evenly distributed pseudorandom numbers. Data compression isn't going to work in this case, although data compression is essential to analytical databases. There is no silver bullet solution to the challenge of choosing the right compression algorithm and the right way to integrate it into the system since data compression requires a compromise between the speed of compression and decompression and the potential compression efficiency. But systems that can't compress data are guaranteed losers. If your tests use evenly distributed pseudorandom numbers, this factor is ignored, and the results will be distorted. + +Bottom line: Test data must have a realistic compression ratio. + +### Example 2 + +Let's say we are interested in the execution speed of this SQL query: + +```sql +SELECT RegionID, uniq(UserID) AS visitors + FROM test.hits +GROUP BY RegionID +ORDER BY visitors DESC +LIMIT 10 +``` + +This was a typical query for web analytics product. What affects the processing speed? + +- How `GROUP BY` is executed. +- Which data structure is used for calculating the `uniq` aggregate function. +- How many different RegionIDs there are and how much RAM each state of the `uniq` function requires. + +But another important factor is that the amount of data is distributed unevenly between regions. (It probably follows a power law. I put the distribution on a log-log graph, but I can't say for sure.) If this is the case, the states of the `uniq` aggregate function with fewer values must use very little memory. When there are a lot of different aggregation keys, every single byte counts. How can we get generated data that has all these properties? The obvious solution is to use real data. + +Many DBMSs implement the HyperLogLog data structure for an approximation of COUNT(DISTINCT), but none of them work very well because this data structure uses a fixed amount of memory. ClickHouse has a function that uses [a combination of three different data structures](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/reference/uniqcombined), depending on the size of the data set. + +Bottom line: Test data must represent distribution properties of the real data well enough, meaning cardinality (number of distinct values per column) and cross-column cardinality (number of different values counted across several different columns). + +### Example 3 + +Instead of testing the performance of the ClickHouse DBMS, let's take something simpler, like hash tables. For hash tables, it's essential to choose the right hash function. This is not as important for `std::unordered_map`, because it's a hash table based on chaining, and a prime number is used as the array size. The standard library implementation in GCC and Clang uses a trivial hash function as the default hash function for numeric types. However, `std::unordered_map` is not the best choice when we are looking for maximum speed. With an open-addressing hash table, we can't just use a standard hash function. Choosing the right hash function becomes the deciding factor. + +It's easy to find hash table performance tests using random data that don't take the hash functions used into account. Many hash function tests also focus on the calculation speed and certain quality criteria, even though they ignore the data structures used. But the fact is that hash tables and HyperLogLog require different hash function quality criteria. + +![alexey_chat.png](https://clickhouse.com/uploads/alexey_chat_3f8db88301.png) + +## Challenge + +Our goal was to obtain data for testing performance that had the same structure as our web analytics data with all the properties that are important for benchmarks, but in such a way that there remain no traces of real website users in this data. In other words, the data must be anonymized and still preserve its: + +* Compression ratio. +* Cardinality (the number of distinct values). +* Mutual cardinality between several different columns. +* Properties of probability distributions that can be used for data modeling (for example, if we believe that regions are distributed according to a power law, then the exponent — the distribution parameter — should be approximately the same for artificial data and for real data). + +How can we get a similar compression ratio for the data? If LZ4 is used, substrings in binary data must be repeated at approximately the same distance, and the repetitions must be approximately the same length. For ZSTD, entropy per byte must also coincide. + +The ultimate goal was to create a publicly available tool that anyone can use to anonymize their data sets for publication. This would allow us to debug and test performance on other people's data similar to our production data. We would also like the generated data to be interesting. + +However, these are very loosely-defined requirements, and we aren't planning to write up a formal problem statement or specification for this task. + +## Possible solutions + +I don't want to make it sound like this problem was particularly important. It was never actually included in planning, and no one had intentions to work on it. I hoped that an idea would come up someday, and suddenly I would be in a good mood and be able to put everything else off until later. + +### Explicit probabilistic models + +- We want to preserve the continuity of time series data. This means that for some types of data, we need to model the difference between neighboring values rather than the value itself. +- To model "joint cardinality" of columns, we would also have to explicitly reflect dependencies between columns. For instance, there are usually very few IP addresses per user ID, so to generate an IP address, we would have to use a hash value of the user ID as a seed and add a small amount of other pseudorandom data. +- We weren't sure how to express the dependency that the same user frequently visits URLs with matching domains at approximately the same time. + +All this can be written in a C++ "script" with the distributions and dependencies hard coded. However, Markov models are obtained from a combination of statistics with smoothing and adding noise. I started writing a script like this, but after writing explicit models for ten columns, it became unbearably boring — and the "hits" table in the web analytics product had more than 100 columns way back in 2012. + +```c++ +EventTime.day(std::discrete_distribution<>({ + 0, 0, 13, 30, 0, 14, 42, 5, 6, 31, 17, 0, 0, 0, 0, 23, 10, ...})(random)); +EventTime.hour(std::discrete_distribution<>({ + 13, 7, 4, 3, 2, 3, 4, 6, 10, 16, 20, 23, 24, 23, 18, 19, 19, ...})(random)); +EventTime.minute(std::uniform_int_distribution(0, 59)(random)); +EventTime.second(std::uniform_int_distribution(0, 59)(random)); + +UInt64 UserID = hash(4, powerLaw(5000, 1.1)); +UserID = UserID / 10000000000ULL * 10000000000ULL + + static_cast(EventTime) + UserID % 1000000; + +random_with_seed.seed(powerLaw(5000, 1.1)); +auto get_random_with_seed = [&]{ return random_with_seed(); }; +``` + +Advantages: + +- Conceptual simplicity. + +Disadvantages: + +- A large amount of work is required. +- The solution only applies to one type of data. + +And I preferred a more general solution that can be used for obfuscating any dataset. + +In any case, this solution could be improved. Instead of manually selecting models, we could implement a catalog of models and choose the best among them (best fit plus some form of regularization). Or maybe we could use Markov models for all types of fields, not just for text. Dependencies between data could also be extracted automatically. This would require calculating the [relative entropy](https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence) (the relative amount of information) between columns. A simpler alternative is to calculate relative cardinalities for each pair of columns (something like "how many different values of A are there on average for a fixed value B"). For instance, this will make it clear that `URLDomain` fully depends on the `URL`, and not vice versa. + +But I also rejected this idea because there are too many factors to consider, and it would take too long to write. + +### Neural networks + +As I've already mentioned, this task wasn't high on the priority list — no one was even thinking about trying to solve it. But as luck would have it, our colleague Ivan Puzirevsky was teaching at the Higher School of Economics. He asked me if I had any interesting problems that would work as suitable thesis topics for his students. When I offered him this one, he assured me it had potential. So I handed this challenge off to a nice guy "off the street" Sharif (he did have to sign an NDA to access the data, though). + +I shared all my ideas with him but emphasized that there were no restrictions on how the problem could be solved, and a good option would be to try approaches that I know nothing about, like using LSTM to generate a text dump of data. This seemed promising after coming across the article [The Unreasonable Effectiveness of Recurrent Neural Networks](http://karpathy.github.io/2015/05/21/rnn-effectiveness/). + +The first challenge is that we need to generate structured data, not just text. But it wasn't clear whether a recurrent neural network could generate data with the desired structure. There are two ways to solve this. The first solution is to use separate models for generating the structure and the "filler", and only use the neural network for generating values. But this approach was postponed and then never completed. The second solution is to simply generate a TSV dump as text. Experience has shown that some of the rows in the text won't match the structure, but these rows can be thrown out when loading the data. + +The second challenge is that the recurrent neural network generates a sequence of data, and thus dependencies in data must follow in the order of the sequence. But in our data, the order of columns can potentially be in reverse to dependencies between them. We didn't do anything to resolve this problem. + +As summer approached, we had the first working Python script that generated data. The data quality seemed decent at first glance: + +![python_script.jpg](https://clickhouse.com/uploads/python_script_810d491dfb.jpg) + +However, we did run into some difficulties: + +1. The size of the model was about a gigabyte. We tried to create a model for data that was several gigabytes in size (for a start). The fact that the resulting model is so large raised concerns. Would it be possible to extract the real data that it was trained on? Unlikely. But I don't know much about machine learning and neural networks, and I haven't read this developer's Python code, so how can I be sure? There were several articles published at the time about how to compress neural networks without loss of quality, but it wasn't implemented. On the one hand, this doesn't seem to be a serious problem since we can opt out of publishing the model and just publish the generated data. On the other hand, if overfitting occurs, the generated data may contain some part of the source data. + +2. On a machine with a single CPU, the data generation speed is approximately 100 rows per second. Our goal was to generate at least a billion rows. Calculations showed that this wouldn't be completed before the date of the thesis defense. It didn't make sense to use additional hardware because the goal was to make a data generation tool that anyone could use. + +Sharif tried to analyze the quality of data by comparing statistics. Among other things, he calculated the frequency of different characters occurring in the source data and in the generated data. The result was stunning: the most frequent characters were Ð and Ñ. + +Don't worry about Sharif, though. He successfully defended his thesis, and we happily forgot about the whole thing. + +### Mutation of compressed data + +Let's assume that the problem statement has been reduced to a single point: we need to generate data that has the same compression ratio as the source data, and the data must decompress at the same speed. How can we achieve this? We need to edit compressed data bytes directly! This allows us to change the data without changing the size of the compressed data, plus everything will work fast. I wanted to try out this idea right away, despite the fact that the problem it solves is different from what we started with. But that's how it always is. + +So how do we edit a compressed file? Let's say we are only interested in LZ4. LZ4 compressed data is composed of sequences, which in turn are strings of not-compressed bytes (literals), followed by a match copy: + +1. Literals (copy the following N bytes as is). +2. Matches with a minimum repeat length of 4 (repeat N bytes in the file at a distance of M). + +Source data: + +`Hello world Hello.` + +Compressed data (arbitrary example): + +`literals 12 "Hello world " match 5 12.` + +In the compressed file, we leave "match" as-is and change the byte values in "literals". As a result, after decompressing, we get a file in which all repeating sequences at least 4 bytes long are also repeated at the same distance, but they consist of a different set of bytes (basically, the modified file doesn't contain a single byte that was taken from the source file). + +But how do we change the bytes? The answer isn't obvious because, in addition to the column types, the data also has its own internal, implicit structure that we would like to preserve. For example, text data is often stored in UTF-8 encoding, and we want the generated data also to be valid UTF-8. I developed a simple heuristic that involves meeting several criteria: + +- Null bytes and ASCII control characters are kept as-is. +- Some punctuation characters remain as-is. +- ASCII is converted to ASCII, and for everything else, the most significant bit is preserved (or an explicit set of "if" statements is written for different UTF-8 lengths). In one byte class, a new value is picked uniformly at random. +- Fragments like `https://` are preserved; otherwise, it looks a bit silly. + +The only caveat to this approach is that the data model is the source data itself, which means it cannot be published. The model is only fit for generating amounts of data no larger than the source. On the contrary, the previous approaches provide models allowing the generation of data of arbitrary size. + +``` +http://ljc.she/kdoqdqwpgafe/klwlpm&qw=962788775I0E7bs7OXeAyAx +http://ljc.she/kdoqdqwdffhant.am/wcpoyodjit/cbytjgeoocvdtclac +http://ljc.she/kdoqdqwpgafe/klwlpm&qw=962788775I0E7bs7OXe +http://ljc.she/kdoqdqwdffhant.am/wcpoyodjit/cbytjgeoocvdtclac +http://ljc.she/kdoqdqwdbknvj.s/hmqhpsavon.yf#aortxqdvjja +http://ljc.she/kdoqdqw-bknvj.s/hmqhpsavon.yf#aortxqdvjja +http://ljc.she/kdoqdqwpdtu-Unu-Rjanjna-bbcohu_qxht +http://ljc.she/kdoqdqw-bknvj.s/hmqhpsavon.yf#aortxqdvjja +http://ljc.she/kdoqdqwpdtu-Unu-Rjanjna-bbcohu_qxht +http://ljc.she/kdoqdqw-bknvj.s/hmqhpsavon.yf#aortxqdvjja +http://ljc.she/kdoqdqwpdtu-Unu-Rjanjna-bbcohu-702130 +``` + +The results were positive, and the data was interesting, but something wasn't quite right. The URLs kept the same structure, but in some of them, it was too easy to recognize the original terms, such as "avito" (a popular marketplace in Russia), so I created a heuristic that swapped some of the bytes around. + +There were other concerns as well. For example, sensitive information could possibly reside in a FixedString column in binary representation and potentially consist of ASCII control characters and punctuation, which I decided to preserve. However, I didn't take data types into consideration. + +Another problem is that if a column stores data in the "length, value" format (this is how String columns are stored), how do I ensure that the length remains correct after the mutation? When I tried to fix this, I immediately lost interest. + +### Random permutations + +Unfortunately, the problem wasn't solved. We performed a few experiments, and it just got worse. The only thing left was to sit around doing nothing and surf the web randomly since the magic was gone. Luckily, I came across a page that [explained the algorithm](http://fabiensanglard.net/fizzlefade/index.php) for rendering the death of the main character in the game Wolfenstein 3D. + +wolfenstein.gif + +
+ +The animation is really well done — the screen fills up with blood. The article explains that this is actually a pseudorandom permutation. A random permutation of a set of elements is a randomly picked bijective (one-to-one) transformation of the set. In other words, a mapping where each and every derived element corresponds to exactly one original element (and vice versa). In other words, it is a way to randomly iterate through all the elements of a data set. And that is exactly the process shown in the picture: each pixel is filled in random order, without any repetition. If we were to just choose a random pixel at each step, it would take a long time to get to the last one. + +The game uses a very simple algorithm for pseudorandom permutation called linear feedback shift register ([LFSR](https://en.wikipedia.org/wiki/Linear-feedback_shift_register)). Similar to pseudorandom number generators, random permutations, or rather their families, can be cryptographically strong when parametrized by a key. This is exactly what we needed for our data transformation. However, the details were trickier. For example, cryptographically strong encryption of N bytes to N bytes with a pre-determined key and initialization vector seems like it would work for a pseudorandom permutation of a set of N-byte strings. Indeed, this is a one-to-one transformation, and it appears to be random. But if we use the same transformation for all of our data, the result may be susceptible to cryptoanalysis because the same initialization vector and key value are used multiple times. This is similar to the [Electronic Codebook](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#ECB) mode of operation for a block cipher. + +For example, three multiplications and two xorshift operations are used for the [murmurhash](https://github.com/ClickHouse/ClickHouse/blob/master/dbms/src/Common/HashTable/Hash.h#L18) finalizer. This operation is a pseudorandom permutation. However, I should point out that hash functions don't have to be one-to-one (even hashes of N bits to N bits). + +Or here's another interesting [example from elementary number theory](https://preshing.com/20121224/how-to-generate-a-sequence-of-unique-random-integers/) from Jeff Preshing's website. + +How can we use pseudorandom permutations to solve our problem? We can use them to transform all numeric fields so we can preserve the cardinalities and mutual cardinalities of all combinations of fields. In other words, COUNT(DISTINCT) will return the same value as before the transformation and, furthermore, with any GROUP BY. + +It is worth noting that preserving all cardinalities somewhat contradicts our goal of data anonymization. Let's say someone knows that the source data for site sessions contains a user who visited sites from 10 different countries, and they want to find that user in the transformed data. The transformed data also shows that the user visited sites from 10 different countries, which makes it easy to narrow down the search. However, even if they find out what the user was transformed into, it won't be very useful; all of the other data has also been transformed, so they won't be able to figure out what sites the user visited or anything else. But these rules can be applied in a chain. For example, suppose someone knows that the most frequently occurring website in our data is Google, with Yahoo in second place. In that case, they can use the ranking to determine which transformed site identifiers actually mean Yahoo and Google. There's nothing surprising about this since we are working with an informal problem statement, and we are trying to find a balance between the anonymization of data (hiding information) and preserving data properties (disclosure of information). For information about how to approach the data anonymization issue more reliably, read this [article](https://medium.com/georgian-impact-blog/a-brief-introduction-to-differential-privacy-eacf8722283b). + +In addition to keeping the original cardinality of values, I also wanted to keep the order of magnitude of the values. What I mean is that if the source data contained numbers under 10, then I want the transformed numbers to also be small. How can we achieve this? + +For example, we can divide a set of possible values into size classes and perform permutations within each class separately (maintaining the size classes). The easiest way to do this is to take the nearest power of two or the position of the most significant bit in the number as the size class (these are the same thing). The numbers 0 and 1 will always remain as is. The numbers 2 and 3 will sometimes remain as is (with a probability of 1/2) and will sometimes be swapped (with a probability of 1/2). The set of numbers 1024..2047 will be mapped to one of 1024! (factorial) variants, and so on. For signed numbers, we will keep the sign. + +It's also doubtful whether we need a one-to-one function. We can probably just use a cryptographically strong hash function. The transformation won't be one-to-one, but the cardinality will be close to the same. + +However, we need a cryptographically strong random permutation so that when we define a key and derive a permutation with that key, restoring the original data from the rearranged data without knowing the key would be difficult. + +There is one problem: in addition to knowing nothing about neural networks and machine learning, I am also quite ignorant when it comes to cryptography. That leaves just my courage. I was still reading random web pages and found a link on [Hackers News](https://news.ycombinator.com/item?id=15122540) to a discussion on Fabien Sanglard's page. It had a link to a [blog post](http://antirez.com/news/113) by Redis developer Salvatore Sanfilippo that talked about using a wonderful generic way of getting random permutations, known as a [Feistel network](https://en.wikipedia.org/wiki/Feistel_cipher). + +The Feistel network is iterative, consisting of rounds. Each round is a remarkable transformation that allows you to get a one-to-one function from any function. Let's look at how it works. + +1. The argument's bits are divided into two halves: +``` + arg: xxxxyyyy + arg_l: xxxx + arg_r: yyyy +``` +2. The right half replaces the left. In its place, we put the result of XOR on the initial value of the left half and the result of the function applied to the initial value of the right half, like this: + + ``` + res: yyyyzzzz + res_l = yyyy = arg_r + res_r = zzzz = arg_l ^ F(arg_r) +``` + +There is also a claim that if we use a cryptographically strong pseudorandom function for F and apply a Feistel round at least four times, we'll get a cryptographically strong pseudorandom permutation. + +This is like a miracle: we take a function that produces random garbage based on data, insert it into the Feistel network, and we now have a function that produces random garbage based on data, but yet is invertible! + +The Feistel network is at the heart of several data encryption algorithms. What we're going to do is something like encryption, only it's really bad. There are two reasons for this: + +1. We are encrypting individual values independently and in the same way, similar to the Electronic Codebook mode of operation. +2. We are storing information about the order of magnitude (the nearest power of two) and the sign of the value, which means that some values do not change at all. + +This way, we can obfuscate numeric fields while preserving the properties we need. For example, after using LZ4, the compression ratio should remain approximately the same because the duplicate values in the source data will be repeated in the converted data and at the same distances from each other. + +### Markov models + +Text models are used for data compression, predictive input, speech recognition, and random string generation. A text model is a probability distribution of all possible strings. Let's say we have an imaginary probability distribution of the texts of all the books that humanity could ever write. To generate a string, we just take a random value with this distribution and return the resulting string (a random book that humanity could write). But how do we find out the probability distribution of all possible strings? + +First, this would require too much information. There are 256^10 possible strings that are 10 bytes in length, and it would take quite a lot of memory to explicitly write a table with the probability of each string. Second, we don't have enough statistics to accurately assess the distribution. + +This is why we use a probability distribution obtained from rough statistics as the text model. For example, we could calculate the probability of each letter occurring in the text and then generate strings by selecting each next letter with the same probability. This primitive model works, but the strings are still very unnatural. + +To improve the model slightly, we could also make use of the conditional probability of the letter's occurrence if it is preceded by N-specific letters. N is a pre-set constant. Let's say N = 5, and we are calculating the probability of the letter "e" occurring after the letters "compr". This text model is called an Order-N Markov model. + +``` +P(cata | cat) = 0.8 +P(catb | cat) = 0.05 +P(catc | cat) = 0.1 +... +``` + +Let's look at how Markov models work on the website [of Hay Kranen](https://projects.haykranen.nl/markov/demo/). Unlike LSTM neural networks, the models only have enough memory for a small context of fixed-length N, so they generate funny nonsensical texts. Markov models are also used in primitive methods for generating spam, and the generated texts can be easily distinguished from real ones by counting statistics that don't fit the model. There is one advantage: Markov models work much faster than neural networks, which is exactly what we need. + +Example for Title (our examples are in Turkish because of the data used): + +
+

Hyunday Butter'dan anket shluha — Politika head manşetleri | STALKER BOXER Çiftede book — Yanudistkarışmanlı Mı Kanal | League el Digitalika Haberler Haberleri — Haberlerisi — Hotels with Centry'ler Neden babah.com

+
+ +We can calculate statistics from the source data, create a Markov model, and generate new data. Note that the model needs smoothing to avoid disclosing information about rare combinations in the source data, but this is not a problem. We use a combination of models from 0 to N. If statistics are insufficient for order N, the N−1 model is used instead. + +But we still want to preserve the cardinality of data. In other words, if the source data had 123456 unique URL values, the result should have approximately the same number of unique values. We can use a deterministically initialized random number generator to achieve this. The easiest way is to use a hash function and apply it to the original value. In other words, we get a pseudorandom result that is explicitly determined by the original value. + +Another requirement is that the source data may have many different URLs that start with the same prefix but aren't identical. For example: `https://www.clickhouse.com/images/cats/?id=xxxxxx`. We want the result to also have URLs that all start with the same prefix, but a different one. For example: http://ftp.google.kz/cgi-bin/index.phtml?item=xxxxxx. As a random number generator for generating the next character using a Markov model, we'll take a hash function from a moving window of 8 bytes at the specified position (instead of taking it from the entire string). + +
+
+https://www.clickhouse.com/images/cats/?id=12345 + ^^^^^^^^ + +distribution: [aaaa][b][cc][dddd][e][ff][ggggg][h]... +hash("images/c") % total_count: ^ +
+
+ + It turns out to be exactly what we need. Here's the example of page titles: + +
+
+PhotoFunia - Haber7 - Hava mükemment.net Oynamak içinde şaşıracak haber, Oyunu Oynanılmaz • apród.hu kínálatában - RT Arabic +PhotoFunia - Kinobar.Net - apród: Ingyenes | Posti +PhotoFunia - Peg Perfeo - Castika, Sıradışı Deniz Lokoning Your Code, sire Eminema.tv/ +PhotoFunia - TUT.BY - Your Ayakkanın ve Son Dakika Spor, +PhotoFunia - big film izle, Del Meireles offilim, Samsung DealeXtreme Değerler NEWSru.com.tv, Smotri.com Mobile yapmak Okey +PhotoFunia 5 | Galaxy, gt, după ce anal bilgi yarak Ceza RE050A V-Stranç +PhotoFunia :: Miami olacaksını yerel Haberler Oyun Young video +PhotoFunia Monstelli'nin En İyi kisa.com.tr –Star Thunder Ekranı +PhotoFunia Seks - Politika,Ekonomi,Spor GTA SANAYİ VE +PhotoFunia Taker-Rating Star TV Resmi Söylenen Yatağa każdy dzież wierzchnie +PhotoFunia TourIndex.Marketime oyunu Oyna Geldolları Mynet Spor,Magazin,Haberler yerel Haberleri ve Solvia, korkusuz Ev SahneTv +PhotoFunia todo in the Gratis Perky Parti'nin yapıyı bu fotogram +PhotoFunian Dünyasın takımız halles en kulları - TEZ +
+
+ +## Results + +After trying four methods, I got so tired of this problem that it was time just to choose something, make it into a usable tool, and announce the solution. I chose the solution that uses random permutations and Markov models parametrized by a key. It is implemented as the clickhouse-obfuscator program, which is very easy to use. The input is a table dump in any supported format (such as CSV or JSONEachRow), and the command line parameters specify the table structure (column names and types) and the secret key (any string, which you can forget immediately after use). The output is the same number of rows of obfuscated data. + +The program is installed with `clickhouse-client`, has no dependencies, and works on almost any flavor of Linux. You can apply it to any database dump, not just ClickHouse. For instance, you can generate test data from MySQL or PostgreSQL databases or create development databases that are similar to your production databases. + +```bash +clickhouse-obfuscator \ + --seed "$(head -c16 /dev/urandom | base64)" \ + --input-format TSV --output-format TSV \ + --structure 'CounterID UInt32, URLDomain String, \ + URL String, SearchPhrase String, Title String' \ + < table.tsv > result.tsv +``` + +```bash + clickhouse-obfuscator --help +``` + +Of course, everything isn't so cut and dry because data transformed by this program is almost completely reversible. The question is whether it is possible to perform the reverse transformation without knowing the key. If the transformation used a cryptographic algorithm, this operation would be as difficult as a brute-force search. Although the transformation uses some cryptographic primitives, they are not used in the correct way, and the data is susceptible to certain methods of analysis. To avoid problems, these issues are covered in the documentation for the program (access it using --help). + +In the end, we transformed the data set we needed [for functional and performance testing](https://clickhouse.com/docs/en/getting-started/example-datasets/metrica/), and received approval from our data security team to publish. + +Our developers and members of our community use this data for real performance testing when optimizing algorithms inside ClickHouse. Third-party users can provide us with their obfuscated data so that we can make ClickHouse even faster for them. We also released an independent open benchmark for hardware and cloud providers on top of this data: [https://benchmark.clickhouse.com/](https://benchmark.clickhouse.com/) From 23644f655bd648010a414f5f9e096284408ec37f Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 20 Mar 2023 13:23:25 +0000 Subject: [PATCH 059/377] Add support for custom key in new analyzer --- src/Planner/Planner.cpp | 80 +++++++++++++++++++++++ src/Planner/PlannerExpressionAnalysis.cpp | 37 ++++++----- src/Planner/PlannerExpressionAnalysis.h | 6 ++ 3 files changed, 105 insertions(+), 18 deletions(-) diff --git a/src/Planner/Planner.cpp b/src/Planner/Planner.cpp index 2242bf92e6b..db7f3305565 100644 --- a/src/Planner/Planner.cpp +++ b/src/Planner/Planner.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -69,6 +70,10 @@ #include #include +#include + +#include + namespace DB { @@ -1188,6 +1193,69 @@ void Planner::buildPlanForQueryNode() collectTableExpressionData(query_tree, *planner_context); collectSets(query_tree, *planner_context); + const auto & settings = query_context->getSettingsRef(); + const auto & table_expression_data = planner_context->getTableExpressionNodeToData(); + + auto & mutable_context = planner_context->getMutableQueryContext(); + + QueryTreeNodePtr parallel_replicas_custom_filter_node{nullptr}; + if (table_expression_data.size() > 1 && (!settings.parallel_replicas_custom_key.value.empty() || settings.allow_experimental_parallel_reading_from_replicas)) + { + LOG_WARNING(&Poco::Logger::get("Planner"), "Joins are not supported with parallel replicas. Query will be executed without using them."); + mutable_context->setSetting("allow_experimental_parallel_reading_from_replicas", false); + mutable_context->setSetting("parallel_replicas_custom_key", String{""}); + } + else if (table_expression_data.size() == 1 && !settings.parallel_replicas_custom_key.value.empty()) + { + const auto & table_expression = (*table_expression_data.begin()).first; + + StoragePtr storage{nullptr}; + if (const auto * table_node = table_expression->as()) + storage = table_node->getStorage(); + else if (const auto * table_function_node = table_expression->as()) + storage = table_function_node->getStorage(); + + std::cout << "COUNT: " << settings.parallel_replicas_count << std::endl; + if (settings.parallel_replicas_count > 1) + { + if (auto custom_key_ast = parseCustomKeyForTable(settings.parallel_replicas_custom_key, *query_context)) + { + LOG_TRACE(&Poco::Logger::get("Planner"), "Processing query on a replica using custom_key '{}'", settings.parallel_replicas_custom_key.value); + if (!storage) + throw DB::Exception(ErrorCodes::BAD_ARGUMENTS, "Storage is unknown when trying to parse custom key for parallel replica"); + + auto parallel_replicas_custom_filter_ast = getCustomKeyFilterForParallelReplica( + settings.parallel_replicas_count, + settings.parallel_replica_offset, + std::move(custom_key_ast), + settings.parallel_replicas_custom_key_filter_type, + *storage, + query_context); + + parallel_replicas_custom_filter_node = buildQueryTree(parallel_replicas_custom_filter_ast, query_context); + QueryAnalysisPass pass(table_expression); + pass.run(parallel_replicas_custom_filter_node, query_context); + } + else if (settings.parallel_replica_offset > 0) + { + throw Exception( + ErrorCodes::BAD_ARGUMENTS, + "Parallel replicas processing with custom_key has been requested " + "(setting 'max_parallel_replicas') but the table does not have custom_key defined for it " + "or it's invalid (settings `parallel_replicas_custom_key`)"); + } + } + else if (storage) + { + if (auto * distributed = dynamic_cast(storage.get()); + distributed && canUseCustomKey(settings, *distributed->getCluster(), *query_context)) + { + select_query_info.use_custom_key = true; + mutable_context->setSetting("distributed_group_by_no_merge", 2); + } + } + } + auto top_level_identifiers = collectTopLevelColumnIdentifiers(query_tree, planner_context); auto join_tree_query_plan = buildJoinTreeQueryPlan(query_tree, select_query_info, @@ -1206,6 +1274,15 @@ void Planner::buildPlanForQueryNode() if (select_query_options.to_stage == QueryProcessingStage::FetchColumns) return; + std::optional parallel_replicas_custom_filter_info; + if (parallel_replicas_custom_filter_node) + { + ActionsChain chain; + parallel_replicas_custom_filter_info = analyzeFilter(parallel_replicas_custom_filter_node, query_plan.getCurrentDataStream().header.getColumnsWithTypeAndName(), planner_context, chain); + parallel_replicas_custom_filter_info->remove_filter_column = true; + select_query_info.filter_asts.push_back(parallel_replicas_custom_filter_node->toAST()); + } + PlannerQueryProcessingInfo query_processing_info(from_stage, select_query_options.to_stage); QueryAnalysisResult query_analysis_result(query_tree, query_processing_info, planner_context); auto expression_analysis_result = buildExpressionAnalysisResult(query_tree, @@ -1237,6 +1314,9 @@ void Planner::buildPlanForQueryNode() if (expression_analysis_result.hasWhere()) addFilterStep(query_plan, expression_analysis_result.getWhere(), "WHERE", result_actions_to_execute); + if (parallel_replicas_custom_filter_info) + addFilterStep(query_plan, *parallel_replicas_custom_filter_info, "Parallel replica custom key filter", result_actions_to_execute); + if (expression_analysis_result.hasAggregation()) { const auto & aggregation_analysis_result = expression_analysis_result.getAggregation(); diff --git a/src/Planner/PlannerExpressionAnalysis.cpp b/src/Planner/PlannerExpressionAnalysis.cpp index 11444503c5f..9cd542b4bc1 100644 --- a/src/Planner/PlannerExpressionAnalysis.cpp +++ b/src/Planner/PlannerExpressionAnalysis.cpp @@ -29,24 +29,6 @@ namespace ErrorCodes namespace { -/** Construct filter analysis result for filter expression node - * Actions before filter are added into into actions chain. - * It is client responsibility to update filter analysis result if filter column must be removed after chain is finalized. - */ -FilterAnalysisResult analyzeFilter(const QueryTreeNodePtr & filter_expression_node, - const ColumnsWithTypeAndName & input_columns, - const PlannerContextPtr & planner_context, - ActionsChain & actions_chain) -{ - FilterAnalysisResult result; - - result.filter_actions = buildActionsDAGFromExpressionNode(filter_expression_node, input_columns, planner_context); - result.filter_column_name = result.filter_actions->getOutputs().at(0)->result_name; - actions_chain.addStep(std::make_unique(result.filter_actions)); - - return result; -} - /** Construct aggregation analysis result if query tree has GROUP BY or aggregates. * Actions before aggregation are added into actions chain, if result is not null optional. */ @@ -458,6 +440,25 @@ LimitByAnalysisResult analyzeLimitBy(const QueryNode & query_node, } +/** Construct filter analysis result for filter expression node + * Actions before filter are added into into actions chain. + * It is client responsibility to update filter analysis result if filter column must be removed after chain is finalized. + */ +FilterAnalysisResult analyzeFilter(const QueryTreeNodePtr & filter_expression_node, + const ColumnsWithTypeAndName & input_columns, + const PlannerContextPtr & planner_context, + ActionsChain & actions_chain) +{ + FilterAnalysisResult result; + + result.filter_actions = buildActionsDAGFromExpressionNode(filter_expression_node, input_columns, planner_context); + result.filter_column_name = result.filter_actions->getOutputs().at(0)->result_name; + actions_chain.addStep(std::make_unique(result.filter_actions)); + + return result; +} + + PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNodePtr & query_tree, const ColumnsWithTypeAndName & join_tree_input_columns, const PlannerContextPtr & planner_context, diff --git a/src/Planner/PlannerExpressionAnalysis.h b/src/Planner/PlannerExpressionAnalysis.h index 792cfdec2ff..ddf9f120cdb 100644 --- a/src/Planner/PlannerExpressionAnalysis.h +++ b/src/Planner/PlannerExpressionAnalysis.h @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -169,6 +170,11 @@ private: LimitByAnalysisResult limit_by_analysis_result; }; +FilterAnalysisResult analyzeFilter(const QueryTreeNodePtr & filter_expression_node, + const ColumnsWithTypeAndName & input_columns, + const PlannerContextPtr & planner_context, + ActionsChain & actions_chain); + /// Build expression analysis result for query tree, join tree input columns and planner context PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNodePtr & query_tree, const ColumnsWithTypeAndName & join_tree_input_columns, From d8493780b2bfa2aec5781c87ea33544e97b43910 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 14 Mar 2023 11:57:51 +0000 Subject: [PATCH 060/377] Query cache: Enable compression and squashing of result blocks ClickHouse reads table data in blocks of 'max_block_size' rows. Due to filtering, aggregation, etc., result blocks are typically much smaller than 'max_block_size' but there are also cases where they are much bigger. Setting 'query_cache_squash_partial_results' (enabled by default) now controls if result blocks are squashed (if they are tiny) or split (if they are large) into blocks of 'max_block_size' size before insertion into the query result cache. This reduces performance of writes into the query cache but improves compressability of cache entries and provides more natural block granularity when query results are later served from the query cache. Entries in the query cache are now also compressed by default. This reduces the overall memory consumption at the cost of slower writes into / reads from the query cache. To disable compression, use setting 'query_cache_compress_entries'. --- docs/en/operations/query-cache.md | 14 + docs/en/operations/settings/settings.md | 22 ++ src/Columns/ColumnCompressed.cpp | 2 +- src/Core/Settings.h | 2 + src/Interpreters/Cache/QueryCache.cpp | 106 ++++++- src/Interpreters/Cache/QueryCache.h | 26 +- src/Interpreters/executeQuery.cpp | 14 +- src/Processors/Chunk.cpp | 33 ++- src/Processors/Chunk.h | 1 + .../StreamInQueryCacheTransform.cpp | 9 +- .../Transforms/StreamInQueryCacheTransform.h | 7 +- .../System/StorageSystemQueryCache.cpp | 6 +- ...query_cache_disabled_compression.reference | 2 + ...02494_query_cache_disabled_compression.sql | 12 + ...ery_cache_squash_partial_results.reference | 275 ++++++++++++++++++ ...494_query_cache_squash_partial_results.sql | 48 +++ 16 files changed, 538 insertions(+), 41 deletions(-) create mode 100644 tests/queries/0_stateless/02494_query_cache_disabled_compression.reference create mode 100644 tests/queries/0_stateless/02494_query_cache_disabled_compression.sql create mode 100644 tests/queries/0_stateless/02494_query_cache_squash_partial_results.reference create mode 100644 tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql diff --git a/docs/en/operations/query-cache.md b/docs/en/operations/query-cache.md index 1a486de7904..b50c4e5bbf4 100644 --- a/docs/en/operations/query-cache.md +++ b/docs/en/operations/query-cache.md @@ -103,6 +103,20 @@ cached - for that use setting [query_cache_min_query_runs](settings/settings.md# Entries in the query cache become stale after a certain time period (time-to-live). By default, this period is 60 seconds but a different value can be specified at session, profile or query level using setting [query_cache_ttl](settings/settings.md#query-cache-ttl). +Entries in the query cache are compressed by default. This reduces the overall memory consumption at the cost of slower writes into / reads +from the query cache. To disable compression, use setting [query_cache_compress_entries](settings/settings.md#query-cache-compress-entries). + +ClickHouse reads table data in blocks of [max_block_size](settings/settings.md#settings-max_block_size) rows. Due to filtering, aggregation, +etc., result blocks are typically much smaller than 'max_block_size' but there are also cases where they are much bigger. Setting +[query_cache_squash_partial_results](settings/settings.md#query-cache-squash-partial-results) (enabled by default) controls if result blocks +are squashed (if they are tiny) or split (if they are large) into blocks of 'max_block_size' size before insertion into the query result +cache. This reduces performance of writes into the query cache but improves compression rate of cache entries and provides more natural +block granularity when query results are later served from the query cache. + +As a result, the query cache stores for each query multiple (partial) +result blocks. While this behavior is a good default, it can be suppressed using setting +[query_cache_squash_partial_query_results](settings/settings.md#query-cache-squash-partial-query-results). + Also, results of queries with non-deterministic functions such as `rand()` and `now()` are not cached. This can be overruled using setting [query_cache_store_results_of_queries_with_nondeterministic_functions](settings/settings.md#query-cache-store-results-of-queries-with-nondeterministic-functions). diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index daaa79e90db..12c2ff8597e 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -1425,6 +1425,28 @@ Possible values: Default value: `0` +## query_cache_compress_entries {#query-cache-compress-entries} + +Compress entries in the [query cache](../query-cache.md). Lessens the memory consumption of the query cache at the cost of slower inserts into / reads from it. + +Possible values: + +- 0 - Disabled +- 1 - Enabled + +Default value: `1` + +## query_cache_squash_partial_results {#query-cache-squash-partial-results} + +Squash partial result blocks to blocks of size [max_block_size](#setting-max_block_size). Reduces performance of inserts into the [query cache](../query-cache.md) but improves the compressability of cache entries (see [query_cache_compress-entries](#query_cache_compress_entries)). + +Possible values: + +- 0 - Disabled +- 1 - Enabled + +Default value: `1` + ## query_cache_ttl {#query-cache-ttl} After this time in seconds entries in the [query cache](../query-cache.md) become stale. diff --git a/src/Columns/ColumnCompressed.cpp b/src/Columns/ColumnCompressed.cpp index cdf604d89f7..aead31da3e9 100644 --- a/src/Columns/ColumnCompressed.cpp +++ b/src/Columns/ColumnCompressed.cpp @@ -24,7 +24,7 @@ std::shared_ptr> ColumnCompressed::compressBuffer(const void * data, si Memory<> compressed(max_dest_size); - auto compressed_size = LZ4_compress_default( + int compressed_size = LZ4_compress_default( reinterpret_cast(data), compressed.data(), static_cast(data_size), diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 3770d7f73a0..ca4bc8f324a 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -557,6 +557,8 @@ class IColumn; M(Bool, query_cache_store_results_of_queries_with_nondeterministic_functions, false, "Store results of queries with non-deterministic functions (e.g. rand(), now()) in the query cache", 0) \ M(UInt64, query_cache_min_query_runs, 0, "Minimum number a SELECT query must run before its result is stored in the query cache", 0) \ M(Milliseconds, query_cache_min_query_duration, 0, "Minimum time in milliseconds for a query to run for its result to be stored in the query cache.", 0) \ + M(Bool, query_cache_compress_entries, true, "Compress cache entries.", 0) \ + M(Bool, query_cache_squash_partial_results, true, "Squash partial result blocks to blocks of size 'max_block_size'. Reduces performance of inserts into the query cache but improves the compressability of cache entries.", 0) \ M(Seconds, query_cache_ttl, 60, "After this time in seconds entries in the query cache become stale", 0) \ M(Bool, query_cache_share_between_users, false, "Allow other users to read entry in the query cache", 0) \ \ diff --git a/src/Interpreters/Cache/QueryCache.cpp b/src/Interpreters/Cache/QueryCache.cpp index ce2373a8af9..b37cfbb17f4 100644 --- a/src/Interpreters/Cache/QueryCache.cpp +++ b/src/Interpreters/Cache/QueryCache.cpp @@ -122,12 +122,15 @@ ASTPtr removeQueryCacheSettings(ASTPtr ast) QueryCache::Key::Key( ASTPtr ast_, - Block header_, const std::optional & username_, - std::chrono::time_point expires_at_) + Block header_, + const std::optional & username_, + std::chrono::time_point expires_at_, + bool is_compressed_) : ast(removeQueryCacheSettings(ast_)) , header(header_) , username(username_) , expires_at(expires_at_) + , is_compressed(is_compressed_) { } @@ -153,7 +156,7 @@ size_t QueryCache::KeyHasher::operator()(const Key & key) const return res; } -size_t QueryCache::QueryResultWeight::operator()(const QueryResult & chunks) const +size_t QueryCache::QueryResultWeight::operator()(const Chunks & chunks) const { size_t res = 0; for (const auto & chunk : chunks) @@ -168,12 +171,16 @@ bool QueryCache::IsStale::operator()(const Key & key) const QueryCache::Writer::Writer(Cache & cache_, const Key & key_, size_t max_entry_size_in_bytes_, size_t max_entry_size_in_rows_, - std::chrono::milliseconds min_query_runtime_) + std::chrono::milliseconds min_query_runtime_, + bool squash_partial_results_, + size_t max_block_size_) : cache(cache_) , key(key_) , max_entry_size_in_bytes(max_entry_size_in_bytes_) , max_entry_size_in_rows(max_entry_size_in_rows_) , min_query_runtime(min_query_runtime_) + , squash_partial_results(squash_partial_results_) + , max_block_size(max_block_size_) { if (auto entry = cache.getWithKey(key); entry.has_value() && !IsStale()(entry->key)) { @@ -211,6 +218,8 @@ void QueryCache::Writer::finalizeWrite() std::lock_guard lock(mutex); + chassert(!was_finalized); + if (std::chrono::duration_cast(std::chrono::system_clock::now() - query_start_time) < min_query_runtime) { LOG_TRACE(&Poco::Logger::get("QueryCache"), "Skipped insert (query not expensive enough), query: {}", key.queryStringFromAst()); @@ -224,7 +233,67 @@ void QueryCache::Writer::finalizeWrite() return; } + if (squash_partial_results) + { + // Squash partial result chunks to chunks of size 'max_block_size' each. This costs some performance but provides a more natural + // compression of neither too small nor big blocks. Also, it will look like 'max_block_size' is respected when the query result is + // served later on from the query cache. + + Chunks squashed_chunks; + size_t rows_remaining_in_squashed = 0; /// how many further rows can the last squashed chunk consume until it reaches max_block_size + + for (const auto & chunk : *query_result) + { + const size_t rows_chunk = chunk.getNumRows(); + size_t rows_chunk_processed = 0; + + if (rows_chunk == 0) + continue; + + while (true) + { + if (rows_remaining_in_squashed == 0) + { + Chunk empty_chunk = Chunk(chunk.cloneEmptyColumns(), 0); + squashed_chunks.push_back(std::move(empty_chunk)); + rows_remaining_in_squashed = max_block_size; + } + + const size_t rows_to_append = std::min(rows_chunk - rows_chunk_processed, rows_remaining_in_squashed); + squashed_chunks.back().append(chunk, rows_chunk_processed, rows_to_append); + rows_chunk_processed += rows_to_append; + rows_remaining_in_squashed += rows_to_append; + + if (rows_chunk_processed == rows_chunk) + break; + } + } + + *query_result = std::move(squashed_chunks); + } + + if (key.is_compressed) + { + Chunks compressed_chunks; + const Chunks & decompressed_chunks = *query_result; + for (const auto & decompressed_chunk : decompressed_chunks) + { + const Columns & decompressed_columns = decompressed_chunk.getColumns(); + Columns compressed_columns; + for (const auto & decompressed_column : decompressed_columns) + { + auto compressed_column = decompressed_column->compress(); + compressed_columns.push_back(compressed_column); + } + Chunk compressed_chunk(compressed_columns, decompressed_chunk.getNumRows()); + compressed_chunks.push_back(std::move(compressed_chunk)); + } + *query_result = std::move(compressed_chunks); + } + cache.set(key, query_result); + + was_finalized = true; } QueryCache::Reader::Reader(Cache & cache_, const Key & key, const std::lock_guard &) @@ -249,7 +318,28 @@ QueryCache::Reader::Reader(Cache & cache_, const Key & key, const std::lock_guar return; } - pipe = Pipe(std::make_shared(entry->key.header, entry->mapped)); + if (!entry->key.is_compressed) + pipe = Pipe(std::make_shared(entry->key.header, entry->mapped)); + else + { + auto decompressed_chunks = std::make_shared(); + const Chunks & compressed_chunks = *entry->mapped; + for (const auto & compressed_chunk : compressed_chunks) + { + const Columns & compressed_chunk_columns = compressed_chunk.getColumns(); + Columns decompressed_columns; + for (const auto & compressed_column : compressed_chunk_columns) + { + auto column = compressed_column->decompress(); + decompressed_columns.push_back(column); + } + Chunk decompressed_chunk(decompressed_columns, compressed_chunk.getNumRows()); + decompressed_chunks->push_back(std::move(decompressed_chunk)); + } + + pipe = Pipe(std::make_shared(entry->key.header, decompressed_chunks)); + } + LOG_TRACE(&Poco::Logger::get("QueryCache"), "Entry found for query {}", key.queryStringFromAst()); } @@ -277,10 +367,10 @@ QueryCache::Reader QueryCache::createReader(const Key & key) return Reader(cache, key, lock); } -QueryCache::Writer QueryCache::createWriter(const Key & key, std::chrono::milliseconds min_query_runtime) +QueryCache::Writer QueryCache::createWriter(const Key & key, std::chrono::milliseconds min_query_runtime, bool squash_partial_results, size_t max_block_size) { std::lock_guard lock(mutex); - return Writer(cache, key, max_entry_size_in_bytes, max_entry_size_in_rows, min_query_runtime); + return Writer(cache, key, max_entry_size_in_bytes, max_entry_size_in_rows, min_query_runtime, squash_partial_results, max_block_size); } void QueryCache::reset() @@ -308,7 +398,7 @@ std::vector QueryCache::dump() const } QueryCache::QueryCache() - : cache(std::make_unique>()) + : cache(std::make_unique>()) { } diff --git a/src/Interpreters/Cache/QueryCache.h b/src/Interpreters/Cache/QueryCache.h index 763e797ac07..316694ee406 100644 --- a/src/Interpreters/Cache/QueryCache.h +++ b/src/Interpreters/Cache/QueryCache.h @@ -50,16 +50,19 @@ public: /// When does the entry expire? const std::chrono::time_point expires_at; + /// Is the entry compressed? + const bool is_compressed; + Key(ASTPtr ast_, - Block header_, const std::optional & username_, - std::chrono::time_point expires_at_); + Block header_, + const std::optional & username_, + std::chrono::time_point expires_at_, + bool is_compressed); bool operator==(const Key & other) const; String queryStringFromAst() const; }; - using QueryResult = Chunks; - private: struct KeyHasher { @@ -68,7 +71,7 @@ private: struct QueryResultWeight { - size_t operator()(const QueryResult & chunks) const; + size_t operator()(const Chunks & chunks) const; }; struct IsStale @@ -77,7 +80,7 @@ private: }; /// query --> query result - using Cache = CacheBase; + using Cache = CacheBase; /// query --> query execution count using TimesExecuted = std::unordered_map; @@ -109,12 +112,17 @@ public: const size_t max_entry_size_in_rows; const std::chrono::time_point query_start_time = std::chrono::system_clock::now(); /// Writer construction and finalizeWrite() coincide with query start/end const std::chrono::milliseconds min_query_runtime; - std::shared_ptr query_result TSA_GUARDED_BY(mutex) = std::make_shared(); + const bool squash_partial_results; + const size_t max_block_size; + std::shared_ptr query_result TSA_GUARDED_BY(mutex) = std::make_shared(); std::atomic skip_insert = false; + bool was_finalized = false; Writer(Cache & cache_, const Key & key_, size_t max_entry_size_in_bytes_, size_t max_entry_size_in_rows_, - std::chrono::milliseconds min_query_runtime_); + std::chrono::milliseconds min_query_runtime_, + bool squash_partial_results_, + size_t max_block_size_); friend class QueryCache; /// for createWriter() }; @@ -136,7 +144,7 @@ public: void updateConfiguration(const Poco::Util::AbstractConfiguration & config); Reader createReader(const Key & key); - Writer createWriter(const Key & key, std::chrono::milliseconds min_query_runtime); + Writer createWriter(const Key & key, std::chrono::milliseconds min_query_runtime, bool squash_partial_results, size_t max_block_size); void reset(); diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index 74394879191..83e88f5b2e5 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -726,7 +726,8 @@ static std::tuple executeQueryImpl( QueryCache::Key key( ast, res.pipeline.getHeader(), std::make_optional(context->getUserName()), - std::chrono::system_clock::now() + std::chrono::seconds(settings.query_cache_ttl)); + /*dummy value for expires_at*/ std::chrono::system_clock::from_time_t(1), + /*dummy value for is_compressed*/ true); QueryCache::Reader reader = query_cache->createReader(key); if (reader.hasCacheEntryForKey()) { @@ -748,13 +749,18 @@ static std::tuple executeQueryImpl( QueryCache::Key key( ast, res.pipeline.getHeader(), settings.query_cache_share_between_users ? std::nullopt : std::make_optional(context->getUserName()), - std::chrono::system_clock::now() + std::chrono::seconds(settings.query_cache_ttl)); + std::chrono::system_clock::now() + std::chrono::seconds(settings.query_cache_ttl), + settings.query_cache_compress_entries); const size_t num_query_runs = query_cache->recordQueryRun(key); if (num_query_runs > settings.query_cache_min_query_runs) { - auto stream_in_query_cache_transform = std::make_shared(res.pipeline.getHeader(), query_cache, key, - std::chrono::milliseconds(context->getSettings().query_cache_min_query_duration.totalMilliseconds())); + auto stream_in_query_cache_transform = + std::make_shared( + res.pipeline.getHeader(), query_cache, key, + std::chrono::milliseconds(context->getSettings().query_cache_min_query_duration.totalMilliseconds()), + context->getSettings().query_cache_squash_partial_results, + context->getSettings().max_block_size); res.pipeline.streamIntoQueryCache(stream_in_query_cache_transform); } } diff --git a/src/Processors/Chunk.cpp b/src/Processors/Chunk.cpp index bbfa1683cf6..0a4b2413e4c 100644 --- a/src/Processors/Chunk.cpp +++ b/src/Processors/Chunk.cpp @@ -23,11 +23,11 @@ Chunk::Chunk(Columns columns_, UInt64 num_rows_, ChunkInfoPtr chunk_info_) checkNumRowsIsConsistent(); } -static Columns unmuteColumns(MutableColumns && mut_columns) +static Columns unmuteColumns(MutableColumns && mutable_columns) { Columns columns; - columns.reserve(mut_columns.size()); - for (auto & col : mut_columns) + columns.reserve(mutable_columns.size()); + for (auto & col : mutable_columns) columns.emplace_back(std::move(col)); return columns; @@ -78,23 +78,23 @@ void Chunk::checkNumRowsIsConsistent() MutableColumns Chunk::mutateColumns() { size_t num_columns = columns.size(); - MutableColumns mut_columns(num_columns); + MutableColumns mutable_columns(num_columns); for (size_t i = 0; i < num_columns; ++i) - mut_columns[i] = IColumn::mutate(std::move(columns[i])); + mutable_columns[i] = IColumn::mutate(std::move(columns[i])); columns.clear(); num_rows = 0; - return mut_columns; + return mutable_columns; } MutableColumns Chunk::cloneEmptyColumns() const { size_t num_columns = columns.size(); - MutableColumns mut_columns(num_columns); + MutableColumns mutable_columns(num_columns); for (size_t i = 0; i < num_columns; ++i) - mut_columns[i] = columns[i]->cloneEmpty(); - return mut_columns; + mutable_columns[i] = columns[i]->cloneEmpty(); + return mutable_columns; } Columns Chunk::detachColumns() @@ -171,14 +171,19 @@ std::string Chunk::dumpStructure() const void Chunk::append(const Chunk & chunk) { - MutableColumns mutation = mutateColumns(); - for (size_t position = 0; position < mutation.size(); ++position) + append(chunk, 0, chunk.getNumRows()); +} + +void Chunk::append(const Chunk & chunk, size_t from, size_t length) +{ + MutableColumns mutable_columns = mutateColumns(); + for (size_t position = 0; position < mutable_columns.size(); ++position) { auto column = chunk.getColumns()[position]; - mutation[position]->insertRangeFrom(*column, 0, column->size()); + mutable_columns[position]->insertRangeFrom(*column, from, length); } - size_t rows = mutation[0]->size(); - setColumns(std::move(mutation), rows); + size_t rows = mutable_columns[0]->size(); + setColumns(std::move(mutable_columns), rows); } void ChunkMissingValues::setBit(size_t column_idx, size_t row_idx) diff --git a/src/Processors/Chunk.h b/src/Processors/Chunk.h index 15d91431b68..6f2097b71f1 100644 --- a/src/Processors/Chunk.h +++ b/src/Processors/Chunk.h @@ -102,6 +102,7 @@ public: std::string dumpStructure() const; void append(const Chunk & chunk); + void append(const Chunk & chunk, size_t from, size_t length); // append rows [from, from+length) of chunk private: Columns columns; diff --git a/src/Processors/Transforms/StreamInQueryCacheTransform.cpp b/src/Processors/Transforms/StreamInQueryCacheTransform.cpp index 1ba57ea8ed2..6156123f9f8 100644 --- a/src/Processors/Transforms/StreamInQueryCacheTransform.cpp +++ b/src/Processors/Transforms/StreamInQueryCacheTransform.cpp @@ -4,9 +4,14 @@ namespace DB { StreamInQueryCacheTransform::StreamInQueryCacheTransform( - const Block & header_, QueryCachePtr cache, const QueryCache::Key & cache_key, std::chrono::milliseconds min_query_duration) + const Block & header_, + QueryCachePtr cache, + const QueryCache::Key & cache_key, + std::chrono::milliseconds min_query_duration, + bool squash_partial_results, + size_t max_block_size) : ISimpleTransform(header_, header_, false) - , cache_writer(cache->createWriter(cache_key, min_query_duration)) + , cache_writer(cache->createWriter(cache_key, min_query_duration, squash_partial_results, max_block_size)) { } diff --git a/src/Processors/Transforms/StreamInQueryCacheTransform.h b/src/Processors/Transforms/StreamInQueryCacheTransform.h index 15d977cd445..42472a708fe 100644 --- a/src/Processors/Transforms/StreamInQueryCacheTransform.h +++ b/src/Processors/Transforms/StreamInQueryCacheTransform.h @@ -10,7 +10,12 @@ class StreamInQueryCacheTransform : public ISimpleTransform { public: StreamInQueryCacheTransform( - const Block & header_, QueryCachePtr cache, const QueryCache::Key & cache_key, std::chrono::milliseconds min_query_duration); + const Block & header_, + QueryCachePtr cache, + const QueryCache::Key & cache_key, + std::chrono::milliseconds min_query_duration, + bool squash_partial_results, + size_t max_block_size); protected: void transform(Chunk & chunk) override; diff --git a/src/Storages/System/StorageSystemQueryCache.cpp b/src/Storages/System/StorageSystemQueryCache.cpp index 2cbcc773ad6..5e4a1e662e2 100644 --- a/src/Storages/System/StorageSystemQueryCache.cpp +++ b/src/Storages/System/StorageSystemQueryCache.cpp @@ -16,6 +16,7 @@ NamesAndTypesList StorageSystemQueryCache::getNamesAndTypes() {"key_hash", std::make_shared()}, {"expires_at", std::make_shared()}, {"stale", std::make_shared()}, + {"compressed", std::make_shared()}, {"shared", std::make_shared()}, {"result_size", std::make_shared()} }; @@ -47,8 +48,9 @@ void StorageSystemQueryCache::fillData(MutableColumns & res_columns, ContextPtr res_columns[1]->insert(key.ast->getTreeHash().first); res_columns[2]->insert(std::chrono::system_clock::to_time_t(key.expires_at)); res_columns[3]->insert(key.expires_at < std::chrono::system_clock::now()); - res_columns[4]->insert(!key.username.has_value()); - res_columns[5]->insert(QueryCache::QueryResultWeight()(*query_result)); + res_columns[4]->insert(key.is_compressed); + res_columns[5]->insert(!key.username.has_value()); + res_columns[6]->insert(QueryCache::QueryResultWeight()(*query_result)); } } diff --git a/tests/queries/0_stateless/02494_query_cache_disabled_compression.reference b/tests/queries/0_stateless/02494_query_cache_disabled_compression.reference new file mode 100644 index 00000000000..6ed281c757a --- /dev/null +++ b/tests/queries/0_stateless/02494_query_cache_disabled_compression.reference @@ -0,0 +1,2 @@ +1 +1 diff --git a/tests/queries/0_stateless/02494_query_cache_disabled_compression.sql b/tests/queries/0_stateless/02494_query_cache_disabled_compression.sql new file mode 100644 index 00000000000..ca95ffd918d --- /dev/null +++ b/tests/queries/0_stateless/02494_query_cache_disabled_compression.sql @@ -0,0 +1,12 @@ +-- Tags: no-parallel +-- Tag no-parallel: Messes with internal cache + +SET allow_experimental_query_cache = true; + +SYSTEM DROP QUERY CACHE; + +-- Run query and store result in query cache but without compression which is on by default +SELECT 1 SETTINGS use_query_cache = true, query_cache_compress_entries = false; + +-- Run again to check that no bad things happen and that the result is as expected +SELECT 1 SETTINGS use_query_cache = true; diff --git a/tests/queries/0_stateless/02494_query_cache_squash_partial_results.reference b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.reference new file mode 100644 index 00000000000..e3ffe57ae3e --- /dev/null +++ b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.reference @@ -0,0 +1,275 @@ +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +- +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +-------------------- +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +- +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +ghi +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl diff --git a/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql new file mode 100644 index 00000000000..eee633b747e --- /dev/null +++ b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql @@ -0,0 +1,48 @@ +-- Tags: no-parallel +-- Tag no-parallel: Messes with internal cache + +SET allow_experimental_query_cache = true; + +SYSTEM DROP QUERY CACHE; + +DROP TABLE IF EXISTS t; + +-- Create test table with "many" rows +CREATE TABLE t(c String) ENGINE=MergeTree ORDER BY c; +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); +INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); + +-- Run query which reads multiple chunks (small max_block_size), cache result in query cache, force squashing of partial results +SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true, query_cache_squash_partial_results = true; + +SELECT '-'; + +-- Run again to check that no bad things happen and that the result is as expected +SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true; + +SELECT '--------------------'; + +-- Run query which reads multiple chunks (small max_block_size), cache result in query cache, but **disable** squashing of partial results +SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true, query_cache_squash_partial_results = false; + +SELECT '-'; + +-- Run again to check that no bad things happen and that the result is as expected +SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true; + +DROP TABLE t; From 91a34fa6279e625ffbec6b162a0e6e60a2a61c70 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Tue, 21 Mar 2023 14:10:01 +0800 Subject: [PATCH 061/377] Set query id in case of exceptions during query execution --- src/Interpreters/executeQuery.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index 076b0c08d93..d96f78c034d 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -1273,18 +1273,24 @@ void executeQuery( end = begin + parse_buf.size(); } - ASTPtr ast; - BlockIO streams; - - std::tie(ast, streams) = executeQueryImpl(begin, end, context, false, QueryProcessingStage::Complete, &istr); - auto & pipeline = streams.pipeline; - QueryResultDetails result_details { .query_id = context->getClientInfo().current_query_id, .timezone = DateLUT::instance().getTimeZone(), }; + // Set the result details in case of any exception raised during query execution + SCOPE_EXIT({ + if (set_result_details) + set_result_details(result_details); + }); + + ASTPtr ast; + BlockIO streams; + + std::tie(ast, streams) = executeQueryImpl(begin, end, context, false, QueryProcessingStage::Complete, &istr); + auto & pipeline = streams.pipeline; + std::unique_ptr compressed_buffer; try { @@ -1353,9 +1359,13 @@ void executeQuery( pipeline.setProgressCallback(context->getProgressCallback()); } - if (set_result_details) + if (set_result_details) { set_result_details(result_details); + // Clear the callback so that result details won't be set twice in case of any exception raised after this point + set_result_details = nullptr; + } + if (pipeline.initialized()) { CompletedPipelineExecutor executor(pipeline); From ce44d8bc4298be6439993320dfeb83bb0c83cbf8 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Tue, 21 Mar 2023 14:10:28 +0800 Subject: [PATCH 062/377] Add test case --- tests/queries/0_stateless/02564_query_id_header.reference | 4 ++++ tests/queries/0_stateless/02564_query_id_header.sh | 1 + 2 files changed, 5 insertions(+) diff --git a/tests/queries/0_stateless/02564_query_id_header.reference b/tests/queries/0_stateless/02564_query_id_header.reference index 413e8929f36..fa56fc23e3e 100644 --- a/tests/queries/0_stateless/02564_query_id_header.reference +++ b/tests/queries/0_stateless/02564_query_id_header.reference @@ -20,3 +20,7 @@ DROP TABLE t_query_id_header < Content-Type: text/plain; charset=UTF-8 < X-ClickHouse-Query-Id: query_id < X-ClickHouse-Timezone: timezone +BAD SQL +< Content-Type: text/plain; charset=UTF-8 +< X-ClickHouse-Query-Id: query_id +< X-ClickHouse-Timezone: timezone diff --git a/tests/queries/0_stateless/02564_query_id_header.sh b/tests/queries/0_stateless/02564_query_id_header.sh index 67ddbcfcc46..7184422a030 100755 --- a/tests/queries/0_stateless/02564_query_id_header.sh +++ b/tests/queries/0_stateless/02564_query_id_header.sh @@ -28,3 +28,4 @@ run_and_check_headers "INSERT INTO t_query_id_header VALUES (1)" run_and_check_headers "EXISTS TABLE t_query_id_header" run_and_check_headers "SELECT * FROM t_query_id_header" run_and_check_headers "DROP TABLE t_query_id_header" +run_and_check_headers "BAD SQL" From 837638e1c116520330787122efb9366ff8343d36 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Tue, 21 Mar 2023 15:27:13 +0800 Subject: [PATCH 063/377] Fix style --- src/Interpreters/executeQuery.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index d96f78c034d..af9cae96155 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -1359,7 +1359,8 @@ void executeQuery( pipeline.setProgressCallback(context->getProgressCallback()); } - if (set_result_details) { + if (set_result_details) + { set_result_details(result_details); // Clear the callback so that result details won't be set twice in case of any exception raised after this point From 1bd6eef9c2afdf41f35ce901bbbb7ea6c8f8da5a Mon Sep 17 00:00:00 2001 From: zvonand Date: Tue, 21 Mar 2023 13:38:39 +0100 Subject: [PATCH 064/377] readded new funcs + basic tests, big decimals WiP --- src/Functions/FunctionFormatDecimal.cpp | 15 + src/Functions/FunctionFormatDecimal.h | 303 ++++++++++++++++++ src/IO/WriteHelpers.h | 47 ++- .../02676_format_decimal.reference | 6 + .../0_stateless/02676_format_decimal.sql | 6 + 5 files changed, 366 insertions(+), 11 deletions(-) create mode 100644 src/Functions/FunctionFormatDecimal.cpp create mode 100644 src/Functions/FunctionFormatDecimal.h create mode 100644 tests/queries/0_stateless/02676_format_decimal.reference create mode 100644 tests/queries/0_stateless/02676_format_decimal.sql diff --git a/src/Functions/FunctionFormatDecimal.cpp b/src/Functions/FunctionFormatDecimal.cpp new file mode 100644 index 00000000000..da0b05ca185 --- /dev/null +++ b/src/Functions/FunctionFormatDecimal.cpp @@ -0,0 +1,15 @@ +#include +#include +#include + +namespace DB +{ + +REGISTER_FUNCTION(FormatDecimal) +{ + factory.registerFunction(); +} + +} + + diff --git a/src/Functions/FunctionFormatDecimal.h b/src/Functions/FunctionFormatDecimal.h new file mode 100644 index 00000000000..6998444dc58 --- /dev/null +++ b/src/Functions/FunctionFormatDecimal.h @@ -0,0 +1,303 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int ILLEGAL_TYPE_OF_ARGUMENT; + extern const int ILLEGAL_COLUMN; +} + +struct Processor +{ + /// For operations with Integer/Float + template + void vectorConstant(const FromVectorType & vec_from, const UInt8 value_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_from.size(); + offsets_to.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + format(vec_from[i], buf_to, value_precision); + writeChar(0, buf_to); + offsets_to[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + template + void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_from.size(); + offsets_to.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + format(vec_from[i], buf_to, vec_precision[i]); + writeChar(0, buf_to); + offsets_to[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + template + void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_precision.size(); + offsets_to.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + format(value_from, buf_to, vec_precision[i]); + writeChar(0, buf_to); + offsets_to[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + /// For operations with Decimal + template + void vectorConstant(const FirstArgVectorType & vec_from, const UInt8 value_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to, const UInt8 from_scale) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_from.size(); + offsets_to.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(value_precision)); + offsets_to[i] = buf_to.count(); + } + buf_to.finalize(); + } + + template + void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to, const UInt8 from_scale) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_from.size(); + offsets_to.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(vec_precision[i])); + writeChar(0, buf_to); + offsets_to[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + template + void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to, const UInt8 from_scale) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_precision.size(); + offsets_to.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + writeText(value_from, from_scale, buf_to, true, true, static_cast(vec_precision[i])); + offsets_to[i] = buf_to.count(); + } + + buf_to.finalize(); + } +private: + + static void format(double value, DB::WriteBuffer & out, int precision) + { + DB::DoubleConverter::BufferType buffer; + double_conversion::StringBuilder builder{buffer, sizeof(buffer)}; + + const auto result = DB::DoubleConverter::instance().ToFixed(value, precision, &builder); + + if (!result) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, "Cannot print float or double number"); + + out.write(buffer, builder.position()); + } +}; + +class FunctionFormatDecimal : public IFunction +{ +public: + static constexpr auto name = "formatDecimal"; + static FunctionPtr create(ContextPtr) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override + { + return true; + } + + size_t getNumberOfArguments() const override { return 2; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isNumber(*arguments[0])) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal first argument for formatDecimal function: got {}, expected numeric type", + arguments[0]->getName()); + + if (!isUInt8(*arguments[1])) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal second argument for formatDecimal function: got {}, expected UInt8", + arguments[1]->getName()); + + return std::make_shared(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + WhichDataType which_from(arguments[0].type.get()); + + if (which_from.isUInt8()) + return executeType(arguments); + else if (which_from.isUInt16()) + return executeType(arguments); + else if (which_from.isUInt32()) + return executeType(arguments); + else if (which_from.isUInt64()) + return executeType(arguments); + else if (which_from.isUInt128()) + return executeType(arguments); + else if (which_from.isUInt256()) + return executeType(arguments); + else if (which_from.isInt8()) + return executeType(arguments); + else if (which_from.isInt16()) + return executeType(arguments); + else if (which_from.isInt32()) + return executeType(arguments); + else if (which_from.isInt64()) + return executeType(arguments); + else if (which_from.isInt128()) + return executeType(arguments); + else if (which_from.isInt256()) + return executeType(arguments); + else if (which_from.isFloat32()) + return executeType(arguments); + else if (which_from.isFloat64()) + return executeType(arguments); + else if (which_from.isDecimal32()) + return executeType(arguments); + else if (which_from.isDecimal64()) + return executeType(arguments); + else if (which_from.isDecimal128()) + return executeType(arguments); + else if (which_from.isDecimal256()) + return executeType(arguments); + + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", + arguments[0].column->getName(), getName()); + } + +private: + template || is_floating_point, bool> = true> + ColumnPtr executeType(const ColumnsWithTypeAndName & arguments) const + { + auto result_column_string = ColumnString::create(); + auto col_to = assert_cast(result_column_string.get()); + ColumnString::Chars & data_to = col_to->getChars(); + ColumnString::Offsets & offsets_to = col_to->getOffsets(); + + const auto * from_col = checkAndGetColumn>(arguments[0].column.get()); + const auto * precision_col = checkAndGetColumn>(arguments[1].column.get()); + const auto * from_col_const = typeid_cast(arguments[0].column.get()); + const auto * precision_col_const = typeid_cast(arguments[1].column.get()); + + Processor processor; + + if (from_col) + { + if (precision_col_const) + processor.vectorConstant(from_col->getData(), precision_col_const->template getValue(), data_to, offsets_to); + else + processor.vectorVector(from_col->getData(), precision_col->getData(), data_to, offsets_to); + } + else if (from_col_const) + { + processor.constantVector(from_col_const->template getValue(), precision_col->getData(), data_to, offsets_to); + } + else + { + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", + arguments[0].column->getName()); + } + + return result_column_string; + } + + template , bool> = true> + ColumnPtr executeType(const ColumnsWithTypeAndName & arguments) const + { + auto result_column_string = ColumnString::create(); + auto col_to = assert_cast(result_column_string.get()); + ColumnString::Chars & data_to = col_to->getChars(); + ColumnString::Offsets & offsets_to = col_to->getOffsets(); + + const auto * from_col = checkAndGetColumn>(arguments[0].column.get()); + const auto * precision_col = checkAndGetColumn>(arguments[1].column.get()); + const auto * from_col_const = typeid_cast(arguments[0].column.get()); + const auto * precision_col_const = typeid_cast(arguments[1].column.get()); + + UInt8 from_scale = from_col->getScale(); + Processor processor; + + if (from_col) + { + if (precision_col_const) + processor.vectorConstant(from_col->getData(), precision_col_const->template getValue(), data_to, offsets_to, from_scale); + else + processor.vectorVector(from_col->getData(), precision_col->getData(), data_to, offsets_to, from_scale); + } + else if (from_col_const) + { + processor.constantVector(from_col_const->template getValue(), precision_col->getData(), data_to, offsets_to, from_scale); + } + else + { + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", + arguments[0].column->getName()); + } + + return result_column_string; + } +}; + +} diff --git a/src/IO/WriteHelpers.h b/src/IO/WriteHelpers.h index 1c0b48c53c3..f0adeec6806 100644 --- a/src/IO/WriteHelpers.h +++ b/src/IO/WriteHelpers.h @@ -891,7 +891,8 @@ inline void writeText(const IPv4 & x, WriteBuffer & buf) { writeIPv4Text(x, buf) inline void writeText(const IPv6 & x, WriteBuffer & buf) { writeIPv6Text(x, buf); } template -void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool trailing_zeros) +void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool trailing_zeros, + bool exact_frac_digits_set, UInt32 frac_digits_num) { /// If it's big integer, but the number of digits is small, /// use the implementation for smaller integers for more efficient arithmetic. @@ -900,17 +901,17 @@ void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool { if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); return; } else if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); return; } else if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); return; } } @@ -918,27 +919,48 @@ void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool { if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); return; } else if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); return; } } constexpr size_t max_digits = std::numeric_limits::digits10; assert(scale <= max_digits); + char buf[max_digits]; memset(buf, '0', scale); T value = x; Int32 last_nonzero_pos = 0; - for (Int32 pos = scale - 1; pos >= 0; --pos) + + if (exact_frac_digits_set && frac_digits_num < scale) + { + T new_value = value / DecimalUtils::scaleMultiplier(frac_digits_num - 1); + auto round_carry = new_value % 10; + value = new_value / 10; + if (round_carry >= 5) + value += 1; + } + + for (Int32 pos = exact_frac_digits_set ? frac_digits_num - 1 : scale - 1; pos >= 0; --pos) { auto remainder = value % 10; value /= 10; +// if (unlikely(carry)) +// { +// if (remainder) +// { +// --remainder; +// carry = 0; +// } +// else +// remainder = 9; +// } if (remainder != 0 && last_nonzero_pos == 0) last_nonzero_pos = pos; @@ -946,12 +968,15 @@ void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool buf[pos] += static_cast(remainder); } - writeChar('.', ostr); - ostr.write(buf, trailing_zeros ? scale : last_nonzero_pos + 1); + + if (likely(!exact_frac_digits_set || frac_digits_num != 0)) + writeChar('.', ostr); + ostr.write(buf, exact_frac_digits_set ? frac_digits_num : trailing_zeros ? scale : last_nonzero_pos + 1); } template -void writeText(Decimal x, UInt32 scale, WriteBuffer & ostr, bool trailing_zeros) +void writeText(Decimal x, UInt32 scale, WriteBuffer & ostr, bool trailing_zeros, + bool exact_frac_digits_set = false, UInt32 frac_digits_num = 0) { T part = DecimalUtils::getWholePart(x, scale); @@ -970,7 +995,7 @@ void writeText(Decimal x, UInt32 scale, WriteBuffer & ostr, bool trailing_zer if (part < 0) part *= T(-1); - writeDecimalFractional(part, scale, ostr, trailing_zeros); + writeDecimalFractional(part, scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); } } } diff --git a/tests/queries/0_stateless/02676_format_decimal.reference b/tests/queries/0_stateless/02676_format_decimal.reference new file mode 100644 index 00000000000..70b7c95b26a --- /dev/null +++ b/tests/queries/0_stateless/02676_format_decimal.reference @@ -0,0 +1,6 @@ +2.00 +2.12 +2.15 +64.32 +64.32 +64.32 diff --git a/tests/queries/0_stateless/02676_format_decimal.sql b/tests/queries/0_stateless/02676_format_decimal.sql new file mode 100644 index 00000000000..c0b41726973 --- /dev/null +++ b/tests/queries/0_stateless/02676_format_decimal.sql @@ -0,0 +1,6 @@ +SELECT formatDecimal(2,2); -- 2.00 +SELECT formatDecimal(2.123456,2); -- 2.12 +SELECT formatDecimal(2.1456,2); -- 2.15 -- rounding! +SELECT formatDecimal(64.32::Float64, 2); -- 64.32 +SELECT formatDecimal(64.32::Decimal32(2), 2); -- 64.32 +SELECT formatDecimal(64.32::Decimal64(2), 2); -- 64.32 \ No newline at end of file From b5b200c4839482f0ed2190ad15b1c8586183b968 Mon Sep 17 00:00:00 2001 From: zvonand Date: Tue, 21 Mar 2023 14:28:47 +0100 Subject: [PATCH 065/377] add docs in docs section --- .../functions/type-conversion-functions.md | 35 +++++++++++++++++++ .../functions/type-conversion-functions.md | 35 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 5d96113fe50..83ed8efd1eb 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -737,6 +737,41 @@ Result: └────────────┴───────┘ ``` +## formatDecimal + +Represents a numeric value as String with exact number of fractional digits. + +**Syntax** + +``` sql +formatDecimal(number, num_digits) +``` + +**Parameters** + +- `number` — Value to be represented as String, of any numeric value. [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md), [Float](/docs/en/sql-reference/data-types/float.md), [Decimal](/docs/en/sql-reference/data-types/decimal.md), +- `num_digits` — Number of fractional digits [UInt8](/docs/en/sql-reference/data-types/int-uint.md). + +**Returned value** + +- Input value represented as [String](/docs/en/sql-reference/data-types/string.md) with given scale. + +**Example** + +Query: + +``` sql +SELECT formatDecimal(CAST('64.32', 'Float64'), 5); +``` + +Result: + +```response +┌─formatDecimal(CAST('64.32', 'Float64'), 5)──┐ +│ 64.32000 │ +└─────────────────────────────────────────────┘ +``` + ## reinterpretAsUInt(8\|16\|32\|64) ## reinterpretAsInt(8\|16\|32\|64) diff --git a/docs/ru/sql-reference/functions/type-conversion-functions.md b/docs/ru/sql-reference/functions/type-conversion-functions.md index c2beb55fee1..74a5b34e75d 100644 --- a/docs/ru/sql-reference/functions/type-conversion-functions.md +++ b/docs/ru/sql-reference/functions/type-conversion-functions.md @@ -553,6 +553,41 @@ SELECT toFixedString('foo\0bar', 8) AS s, toStringCutToZero(s) AS s_cut; └────────────┴───────┘ ``` +## formatDecimal + +Принимает любой численный тип первым аргументом, возвращает строковое десятичное представление числа с точностью, заданной вторым аргументом. + +**Синтаксис** + +``` sql +formatDecimal(number, num_digits) +``` + +**Параметры** + +- `number` — Число любого числового типа: [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md), [Float](/docs/en/sql-reference/data-types/float.md), [Decimal](/docs/en/sql-reference/data-types/decimal.md), +- `num_digits` — Требуемое количество десятичных знаков после запятой, [UInt8](/docs/en/sql-reference/data-types/int-uint.md). + +**Возвращаемое значение** + +- Строка ([String](/docs/en/sql-reference/data-types/string.md)), представляющая собой входное число с заданной точностью. + +**Пример использования** + +Запрос: + +``` sql +SELECT formatDecimal(CAST('64.32', 'Float64'), 5); +``` + +Результат: + +```response +┌─formatDecimal(CAST('64.32', 'Float64'), 5)──┐ +│ 64.32000 │ +└─────────────────────────────────────────────┘ +``` + ## reinterpretAsUInt(8\|16\|32\|64) {#reinterpretasuint8163264} ## reinterpretAsInt(8\|16\|32\|64) {#reinterpretasint8163264} From 83ee185fce0f220c85cf92acea09f92ee423e25d Mon Sep 17 00:00:00 2001 From: zvonand Date: Tue, 21 Mar 2023 16:33:19 +0100 Subject: [PATCH 066/377] upd --- src/Functions/FunctionFormatDecimal.cpp | 11 ++++++++++- src/IO/WriteHelpers.h | 14 ++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Functions/FunctionFormatDecimal.cpp b/src/Functions/FunctionFormatDecimal.cpp index da0b05ca185..f18aff060d5 100644 --- a/src/Functions/FunctionFormatDecimal.cpp +++ b/src/Functions/FunctionFormatDecimal.cpp @@ -7,7 +7,16 @@ namespace DB REGISTER_FUNCTION(FormatDecimal) { - factory.registerFunction(); + factory.registerFunction( + { + R"( +Returns string representation of a number. First argument is the number of any numeric type, +second argument is the desired number of digits in fractional part. Returns String. + + )", + Documentation::Examples{{"formatDecimal", "SELECT formatDecimal(2.1456,2)"}}, + Documentation::Categories{"String"} + }, FunctionFactory::CaseInsensitive); } } diff --git a/src/IO/WriteHelpers.h b/src/IO/WriteHelpers.h index f0adeec6806..df3c5bbc78c 100644 --- a/src/IO/WriteHelpers.h +++ b/src/IO/WriteHelpers.h @@ -940,27 +940,17 @@ void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool if (exact_frac_digits_set && frac_digits_num < scale) { - T new_value = value / DecimalUtils::scaleMultiplier(frac_digits_num - 1); + T new_value = value / DecimalUtils::scaleMultiplier(scale - frac_digits_num - 1); auto round_carry = new_value % 10; value = new_value / 10; if (round_carry >= 5) value += 1; } - for (Int32 pos = exact_frac_digits_set ? frac_digits_num - 1 : scale - 1; pos >= 0; --pos) + for (Int32 pos = scale - 1; pos >= 0; --pos) { auto remainder = value % 10; value /= 10; -// if (unlikely(carry)) -// { -// if (remainder) -// { -// --remainder; -// carry = 0; -// } -// else -// remainder = 9; -// } if (remainder != 0 && last_nonzero_pos == 0) last_nonzero_pos = pos; From d72c0def6e9c6dc1700c56d32e20d3004e942672 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Tue, 21 Mar 2023 16:30:13 +0000 Subject: [PATCH 067/377] Improve search of original node name in ActionsDAG --- src/Interpreters/ActionsDAG.cpp | 22 +++++++++---------- src/Interpreters/ActionsDAG.h | 13 ++++++++++- src/Processors/QueryPlan/ExpressionStep.cpp | 4 ++-- src/Processors/QueryPlan/FilterStep.cpp | 4 ++-- .../Optimizations/distinctReadInOrder.cpp | 3 ++- .../Optimizations/removeRedundantDistinct.cpp | 6 +++-- 6 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/Interpreters/ActionsDAG.cpp b/src/Interpreters/ActionsDAG.cpp index 66d4fc73f2b..fbddcf31aa0 100644 --- a/src/Interpreters/ActionsDAG.cpp +++ b/src/Interpreters/ActionsDAG.cpp @@ -2316,23 +2316,21 @@ ActionsDAGPtr ActionsDAG::buildFilterActionsDAG( return result_dag; } -const ActionsDAG::Node * getOriginalNodeForOutputAlias(const ActionsDAGPtr & actions, const String & output_name) +FindOriginalNodeForOutputName::FindOriginalNodeForOutputName(const ActionsDAGPtr & actions_) + :actions(actions_) { - /// find alias in output - const ActionsDAG::Node * output_alias = nullptr; for (const auto * node : actions->getOutputs()) - { - if (node->result_name == output_name) - { - output_alias = node; - break; - } - } - if (!output_alias) + index.emplace(node->result_name, node); +} + +const ActionsDAG::Node * FindOriginalNodeForOutputName::find(const String & output_name) +{ + const auto it = index.find(output_name); + if (it == index.end()) return nullptr; /// find original(non alias) node it refers to - const ActionsDAG::Node * node = output_alias; + const ActionsDAG::Node * node = it->second; while (node && node->type == ActionsDAG::ActionType::ALIAS) { chassert(!node->children.empty()); diff --git a/src/Interpreters/ActionsDAG.h b/src/Interpreters/ActionsDAG.h index 771fce673c8..13871c5d308 100644 --- a/src/Interpreters/ActionsDAG.h +++ b/src/Interpreters/ActionsDAG.h @@ -374,7 +374,18 @@ private: static ActionsDAGPtr cloneActionsForConjunction(NodeRawConstPtrs conjunction, const ColumnsWithTypeAndName & all_inputs); }; -const ActionsDAG::Node * getOriginalNodeForOutputAlias(const ActionsDAGPtr & actions, const String & output_name); +class FindOriginalNodeForOutputName +{ + using NameToNodeIndex = std::unordered_map; + +public: + explicit FindOriginalNodeForOutputName(const ActionsDAGPtr & actions); + const ActionsDAG::Node* find(const String& output_name); + +private: + ActionsDAGPtr actions; + NameToNodeIndex index; +}; /// This is an ugly way to bypass impossibility to forward declare ActionDAG::Node. struct ActionDAGNodes diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index 8a1e10f0643..a93ccb2e020 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -79,7 +79,7 @@ void ExpressionStep::updateOutputStream() if (!getDataStreamTraits().preserves_sorting) return; - const ActionsDAGPtr & actions = actions_dag; + FindOriginalNodeForOutputName original_node_finder(actions_dag); const auto & input_sort_description = getInputStreams().front().sort_description; for (size_t i = 0, s = input_sort_description.size(); i < s; ++i) { @@ -88,7 +88,7 @@ void ExpressionStep::updateOutputStream() const auto & origin_column = desc.column_name; for (const auto & column : output_stream->header) { - const auto * original_node = getOriginalNodeForOutputAlias(actions, column.name); + const auto * original_node = original_node_finder.find(column.name); if (original_node && original_node->result_name == origin_column) { alias = column.name; diff --git a/src/Processors/QueryPlan/FilterStep.cpp b/src/Processors/QueryPlan/FilterStep.cpp index d63fded918d..bab94b62f23 100644 --- a/src/Processors/QueryPlan/FilterStep.cpp +++ b/src/Processors/QueryPlan/FilterStep.cpp @@ -109,7 +109,7 @@ void FilterStep::updateOutputStream() if (!getDataStreamTraits().preserves_sorting) return; - const ActionsDAGPtr & actions = actions_dag; + FindOriginalNodeForOutputName original_node_finder(actions_dag); const auto & input_sort_description = getInputStreams().front().sort_description; for (size_t i = 0, s = input_sort_description.size(); i < s; ++i) { @@ -118,7 +118,7 @@ void FilterStep::updateOutputStream() const auto & origin_column = desc.column_name; for (const auto & column : output_stream->header) { - const auto * original_node = getOriginalNodeForOutputAlias(actions, column.name); + const auto * original_node = original_node_finder.find(column.name); if (original_node && original_node->result_name == origin_column) { alias = column.name; diff --git a/src/Processors/QueryPlan/Optimizations/distinctReadInOrder.cpp b/src/Processors/QueryPlan/Optimizations/distinctReadInOrder.cpp index 04f10cfa821..0a3a4094a66 100644 --- a/src/Processors/QueryPlan/Optimizations/distinctReadInOrder.cpp +++ b/src/Processors/QueryPlan/Optimizations/distinctReadInOrder.cpp @@ -30,6 +30,7 @@ static std::set getOriginalDistinctColumns(const ColumnsWithTypeAndName & distinct_columns, std::vector & dag_stack) { auto actions = buildActionsForPlanPath(dag_stack); + FindOriginalNodeForOutputName original_node_finder(actions); std::set original_distinct_columns; for (const auto & column : distinct_columns) { @@ -37,7 +38,7 @@ getOriginalDistinctColumns(const ColumnsWithTypeAndName & distinct_columns, std: if (isColumnConst(*column.column)) continue; - const auto * input_node = getOriginalNodeForOutputAlias(actions, column.name); + const auto * input_node = original_node_finder.find(column.name); if (!input_node) break; diff --git a/src/Processors/QueryPlan/Optimizations/removeRedundantDistinct.cpp b/src/Processors/QueryPlan/Optimizations/removeRedundantDistinct.cpp index 1ad90358acc..a6029d673e3 100644 --- a/src/Processors/QueryPlan/Optimizations/removeRedundantDistinct.cpp +++ b/src/Processors/QueryPlan/Optimizations/removeRedundantDistinct.cpp @@ -72,10 +72,11 @@ namespace logDebug("distinct_columns size", distinct_columns.size()); std::set original_distinct_columns; + FindOriginalNodeForOutputName original_node_finder(path_actions); for (const auto & column : distinct_columns) { logDebug("distinct column name", column); - const auto * alias_node = getOriginalNodeForOutputAlias(path_actions, String(column)); + const auto * alias_node = original_node_finder.find(String(column)); if (!alias_node) { logDebug("original name for alias is not found", column); @@ -242,9 +243,10 @@ namespace logActionsDAG("distinct pass: merged DAG", path_actions); /// compare columns of two DISTINCTs + FindOriginalNodeForOutputName original_node_finder(path_actions); for (const auto & column : distinct_columns) { - const auto * alias_node = getOriginalNodeForOutputAlias(path_actions, String(column)); + const auto * alias_node = original_node_finder.find(String(column)); if (!alias_node) return false; From 7504e107acb4fc48a793fb542f17693b058b98c7 Mon Sep 17 00:00:00 2001 From: serxa Date: Tue, 21 Mar 2023 22:11:19 +0000 Subject: [PATCH 068/377] Fix CPU usage counters in segmentator thread --- .../Formats/Impl/ParallelParsingInputFormat.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp b/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp index 5ba32251a71..5881854571c 100644 --- a/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp +++ b/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp @@ -18,6 +18,10 @@ void ParallelParsingInputFormat::segmentatorThreadFunction(ThreadGroupStatusPtr CurrentThread::attachToGroup(thread_group); setThreadName("Segmentator"); + + Stopwatch total_stopwatch {CLOCK_MONOTONIC_COARSE}; + UInt64 last_profile_events_update_time = 0; + try { while (!parsing_finished) @@ -50,6 +54,15 @@ void ParallelParsingInputFormat::segmentatorThreadFunction(ThreadGroupStatusPtr if (!have_more_data) break; + + // Segmentator thread can be long-living, so we have to manually update performance counters for CPU progress to be correct + constexpr UInt64 profile_events_update_period_microseconds = 10 * 1000; // 10 milliseconds + UInt64 total_elapsed_microseconds = total_stopwatch.elapsedMicroseconds(); + if (last_profile_events_update_time + profile_events_update_period_microseconds < total_elapsed_microseconds) + { + CurrentThread::updatePerformanceCounters(); + last_profile_events_update_time = total_elapsed_microseconds; + } } } catch (...) From 3de73d03cc32621e8ae663e932dceeee54499033 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Wed, 22 Mar 2023 14:31:16 +0800 Subject: [PATCH 069/377] Improve the robustness --- src/Interpreters/executeQuery.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index af9cae96155..bfcfba621ea 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -1279,10 +1279,22 @@ void executeQuery( .timezone = DateLUT::instance().getTimeZone(), }; - // Set the result details in case of any exception raised during query execution + /// Set the result details in case of any exception raised during query execution SCOPE_EXIT({ - if (set_result_details) + if (set_result_details == nullptr) + /// Either the result_details have been set in the flow below or the caller of this function does not provide this callback + return; + + try + { set_result_details(result_details); + } + catch (...) + { + /// This exception can be ignored because if the code goes here, + /// it means there's already exception occurred during query execution, + /// So there's no need to report the exception thrown here. + } }); ASTPtr ast; @@ -1361,10 +1373,13 @@ void executeQuery( if (set_result_details) { - set_result_details(result_details); - - // Clear the callback so that result details won't be set twice in case of any exception raised after this point + /// The call of set_result_details itself might throw exception, + /// in such case there's no need to call this function again in the SCOPE_EXIT defined above. + /// So the callback is cleared before its execution. + auto set_result_details_copy = set_result_details; set_result_details = nullptr; + + set_result_details_copy(result_details); } if (pipeline.initialized()) From 3e05f2c58a30e31be1e3651849fcd3c75535dbe0 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Wed, 22 Mar 2023 14:35:58 +0800 Subject: [PATCH 070/377] Improve comment --- src/Interpreters/executeQuery.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index bfcfba621ea..d70a47df533 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -1292,8 +1292,9 @@ void executeQuery( catch (...) { /// This exception can be ignored because if the code goes here, - /// it means there's already exception occurred during query execution, - /// So there's no need to report the exception thrown here. + /// it means there's already an exception raised during query execution, + /// and that exception will be propagated to outter caller, + /// there's no need to report the exception thrown here. } }); From c1927d8f3ee8fd2544832ee599ead6a799e245af Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Wed, 22 Mar 2023 16:39:14 +0800 Subject: [PATCH 071/377] Fix check style --- src/Interpreters/executeQuery.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index d70a47df533..4ce9b40da05 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -1291,8 +1291,8 @@ void executeQuery( } catch (...) { - /// This exception can be ignored because if the code goes here, - /// it means there's already an exception raised during query execution, + /// This exception can be ignored. + /// because if the code goes here, it means there's already an exception raised during query execution, /// and that exception will be propagated to outter caller, /// there's no need to report the exception thrown here. } From 532590773a310eae03f0f607f3c2f22efba1bb8d Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Wed, 22 Mar 2023 17:46:09 +0800 Subject: [PATCH 072/377] Fix typo check --- src/Interpreters/executeQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index 4ce9b40da05..05928f8ac47 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -1293,7 +1293,7 @@ void executeQuery( { /// This exception can be ignored. /// because if the code goes here, it means there's already an exception raised during query execution, - /// and that exception will be propagated to outter caller, + /// and that exception will be propagated to outer caller, /// there's no need to report the exception thrown here. } }); From c28b97881c4f16b0e8161746c674ede815572e1a Mon Sep 17 00:00:00 2001 From: Nikolay Degterinsky Date: Wed, 22 Mar 2023 13:42:35 +0000 Subject: [PATCH 073/377] Fix isIPv6String function --- src/Functions/FunctionsCodingIP.cpp | 4 +++- .../queries/0_stateless/02675_is_ipv6_function_fix.reference | 1 + tests/queries/0_stateless/02675_is_ipv6_function_fix.sql | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 tests/queries/0_stateless/02675_is_ipv6_function_fix.reference create mode 100644 tests/queries/0_stateless/02675_is_ipv6_function_fix.sql diff --git a/src/Functions/FunctionsCodingIP.cpp b/src/Functions/FunctionsCodingIP.cpp index 4784368db9b..7e54905efc5 100644 --- a/src/Functions/FunctionsCodingIP.cpp +++ b/src/Functions/FunctionsCodingIP.cpp @@ -1131,7 +1131,9 @@ public: for (size_t i = 0; i < vec_res.size(); ++i) { - vec_res[i] = DB::parseIPv6whole(reinterpret_cast(&vec_src[prev_offset]), reinterpret_cast(buffer)); + vec_res[i] = DB::parseIPv6whole(reinterpret_cast(&vec_src[prev_offset]), + reinterpret_cast(&vec_src[offsets_src[i] - 1]), + reinterpret_cast(buffer)); prev_offset = offsets_src[i]; } diff --git a/tests/queries/0_stateless/02675_is_ipv6_function_fix.reference b/tests/queries/0_stateless/02675_is_ipv6_function_fix.reference new file mode 100644 index 00000000000..573541ac970 --- /dev/null +++ b/tests/queries/0_stateless/02675_is_ipv6_function_fix.reference @@ -0,0 +1 @@ +0 diff --git a/tests/queries/0_stateless/02675_is_ipv6_function_fix.sql b/tests/queries/0_stateless/02675_is_ipv6_function_fix.sql new file mode 100644 index 00000000000..c28b4a5dc2d --- /dev/null +++ b/tests/queries/0_stateless/02675_is_ipv6_function_fix.sql @@ -0,0 +1 @@ +SELECT isIPv6String('1234::1234:'); \ No newline at end of file From f3c12b854386f9c57fe5f8a40193eaae3471a9f8 Mon Sep 17 00:00:00 2001 From: serxa Date: Wed, 22 Mar 2023 13:51:07 +0000 Subject: [PATCH 074/377] fix per-thread perf counters update --- src/Common/CurrentThread.cpp | 7 +++++++ src/Common/CurrentThread.h | 1 + src/Common/ThreadStatus.cpp | 11 +++++++++++ src/Common/ThreadStatus.h | 4 ++++ src/Common/mysqlxx/Pool.cpp | 14 +------------- .../Formats/Impl/ParallelParsingInputFormat.cpp | 11 +---------- src/QueryPipeline/ReadProgressCallback.cpp | 15 +++------------ src/QueryPipeline/ReadProgressCallback.h | 6 ++---- 8 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/Common/CurrentThread.cpp b/src/Common/CurrentThread.cpp index 7fd82426522..6ec46d6508c 100644 --- a/src/Common/CurrentThread.cpp +++ b/src/Common/CurrentThread.cpp @@ -25,6 +25,13 @@ void CurrentThread::updatePerformanceCounters() current_thread->updatePerformanceCounters(); } +void CurrentThread::updatePerformanceCountersIfNeeded() +{ + if (unlikely(!current_thread)) + return; + current_thread->updatePerformanceCountersIfNeeded(); +} + bool CurrentThread::isInitialized() { return current_thread; diff --git a/src/Common/CurrentThread.h b/src/Common/CurrentThread.h index 3b16163b1ba..373f7aa3e10 100644 --- a/src/Common/CurrentThread.h +++ b/src/Common/CurrentThread.h @@ -53,6 +53,7 @@ public: /// Makes system calls to update ProfileEvents that contain info from rusage and taskstats static void updatePerformanceCounters(); + static void updatePerformanceCountersIfNeeded(); static ProfileEvents::Counters & getProfileEvents(); inline ALWAYS_INLINE static MemoryTracker * getMemoryTracker() diff --git a/src/Common/ThreadStatus.cpp b/src/Common/ThreadStatus.cpp index aa1690890d8..91c695216a8 100644 --- a/src/Common/ThreadStatus.cpp +++ b/src/Common/ThreadStatus.cpp @@ -217,6 +217,17 @@ void ThreadStatus::updatePerformanceCounters() } } +void ThreadStatus::updatePerformanceCountersIfNeeded() +{ + constexpr UInt64 performance_counters_update_period_microseconds = 10 * 1000; // 10 milliseconds + UInt64 total_elapsed_microseconds = stopwatch.elapsedMicroseconds(); + if (last_performance_counters_update_time + performance_counters_update_period_microseconds < total_elapsed_microseconds) + { + CurrentThread::updatePerformanceCounters(); + last_performance_counters_update_time = total_elapsed_microseconds; + } +} + void ThreadStatus::onFatalError() { if (fatal_error_callback) diff --git a/src/Common/ThreadStatus.h b/src/Common/ThreadStatus.h index 77c924f9650..79474f292ec 100644 --- a/src/Common/ThreadStatus.h +++ b/src/Common/ThreadStatus.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -202,6 +203,8 @@ private: /// Use ptr not to add extra dependencies in the header std::unique_ptr last_rusage; std::unique_ptr taskstats; + Stopwatch stopwatch{CLOCK_MONOTONIC_COARSE}; + UInt64 last_performance_counters_update_time = 0; /// See setInternalThread() bool internal_thread = false; @@ -265,6 +268,7 @@ public: /// Update several ProfileEvents counters void updatePerformanceCounters(); + void updatePerformanceCountersIfNeeded(); /// Update ProfileEvents and dumps info to system.query_thread_log void finalizePerformanceCounters(); diff --git a/src/Common/mysqlxx/Pool.cpp b/src/Common/mysqlxx/Pool.cpp index 6cd1ae8b399..6438d76cc3a 100644 --- a/src/Common/mysqlxx/Pool.cpp +++ b/src/Common/mysqlxx/Pool.cpp @@ -10,22 +10,10 @@ #include #include #include +#include #include -namespace -{ - -inline uint64_t clock_gettime_ns(clockid_t clock_type = CLOCK_MONOTONIC) -{ - struct timespec ts; - clock_gettime(clock_type, &ts); - return uint64_t(ts.tv_sec * 1000000000LL + ts.tv_nsec); -} - -} - - namespace mysqlxx { diff --git a/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp b/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp index 5881854571c..a2e5074efb1 100644 --- a/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp +++ b/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp @@ -19,9 +19,6 @@ void ParallelParsingInputFormat::segmentatorThreadFunction(ThreadGroupStatusPtr setThreadName("Segmentator"); - Stopwatch total_stopwatch {CLOCK_MONOTONIC_COARSE}; - UInt64 last_profile_events_update_time = 0; - try { while (!parsing_finished) @@ -56,13 +53,7 @@ void ParallelParsingInputFormat::segmentatorThreadFunction(ThreadGroupStatusPtr break; // Segmentator thread can be long-living, so we have to manually update performance counters for CPU progress to be correct - constexpr UInt64 profile_events_update_period_microseconds = 10 * 1000; // 10 milliseconds - UInt64 total_elapsed_microseconds = total_stopwatch.elapsedMicroseconds(); - if (last_profile_events_update_time + profile_events_update_period_microseconds < total_elapsed_microseconds) - { - CurrentThread::updatePerformanceCounters(); - last_profile_events_update_time = total_elapsed_microseconds; - } + CurrentThread::updatePerformanceCountersIfNeeded(); } } catch (...) diff --git a/src/QueryPipeline/ReadProgressCallback.cpp b/src/QueryPipeline/ReadProgressCallback.cpp index 6692b0f96bd..0f50d56f1a5 100644 --- a/src/QueryPipeline/ReadProgressCallback.cpp +++ b/src/QueryPipeline/ReadProgressCallback.cpp @@ -112,22 +112,13 @@ bool ReadProgressCallback::onProgress(uint64_t read_rows, uint64_t read_bytes, c size_t total_rows = progress.total_rows_to_read; - constexpr UInt64 profile_events_update_period_microseconds = 10 * 1000; // 10 milliseconds - UInt64 total_elapsed_microseconds = total_stopwatch.elapsedMicroseconds(); + CurrentThread::updatePerformanceCountersIfNeeded(); - std::lock_guard lock(last_profile_events_update_time_mutex); - { - if (last_profile_events_update_time + profile_events_update_period_microseconds < total_elapsed_microseconds) - { - /// TODO: Should be done in PipelineExecutor. - CurrentThread::updatePerformanceCounters(); - last_profile_events_update_time = total_elapsed_microseconds; - } - } + std::lock_guard lock(limits_and_quotas_mutex); /// TODO: Should be done in PipelineExecutor. for (const auto & limits : storage_limits) - limits.local_limits.speed_limits.throttle(progress.read_rows, progress.read_bytes, total_rows, total_elapsed_microseconds); + limits.local_limits.speed_limits.throttle(progress.read_rows, progress.read_bytes, total_rows, total_stopwatch.elapsedMicroseconds()); if (quota) quota->used({QuotaType::READ_ROWS, value.read_rows}, {QuotaType::READ_BYTES, value.read_bytes}); diff --git a/src/QueryPipeline/ReadProgressCallback.h b/src/QueryPipeline/ReadProgressCallback.h index c8f0d4cf537..08f2f9fc99b 100644 --- a/src/QueryPipeline/ReadProgressCallback.h +++ b/src/QueryPipeline/ReadProgressCallback.h @@ -38,10 +38,8 @@ private: /// The approximate total number of rows to read. For progress bar. std::atomic_size_t total_rows_approx = 0; - Stopwatch total_stopwatch {CLOCK_MONOTONIC_COARSE}; /// Time with waiting time. - /// According to total_stopwatch in microseconds. - UInt64 last_profile_events_update_time = 0; - std::mutex last_profile_events_update_time_mutex; + std::mutex limits_and_quotas_mutex; + Stopwatch total_stopwatch{CLOCK_MONOTONIC_COARSE}; /// Including waiting time bool update_profile_events = true; }; From b9bbda3a6f1634e5471cccbe6bf3de631ca13454 Mon Sep 17 00:00:00 2001 From: zvonand Date: Thu, 23 Mar 2023 01:42:00 +0100 Subject: [PATCH 075/377] add test + fix --- src/Functions/FunctionFormatDecimal.h | 2 ++ src/IO/WriteHelpers.h | 35 +++++++++---------- .../02676_format_decimal.reference | 15 ++++++-- .../0_stateless/02676_format_decimal.sql | 26 ++++++++++---- 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/Functions/FunctionFormatDecimal.h b/src/Functions/FunctionFormatDecimal.h index 6998444dc58..b85fd0ba55b 100644 --- a/src/Functions/FunctionFormatDecimal.h +++ b/src/Functions/FunctionFormatDecimal.h @@ -92,6 +92,7 @@ struct Processor for (size_t i = 0; i < input_rows_count; ++i) { writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(value_precision)); + writeChar(0, buf_to); offsets_to[i] = buf_to.count(); } buf_to.finalize(); @@ -126,6 +127,7 @@ struct Processor for (size_t i = 0; i < input_rows_count; ++i) { writeText(value_from, from_scale, buf_to, true, true, static_cast(vec_precision[i])); + writeChar(0, buf_to); offsets_to[i] = buf_to.count(); } diff --git a/src/IO/WriteHelpers.h b/src/IO/WriteHelpers.h index df3c5bbc78c..c06aa7469e9 100644 --- a/src/IO/WriteHelpers.h +++ b/src/IO/WriteHelpers.h @@ -892,26 +892,25 @@ inline void writeText(const IPv6 & x, WriteBuffer & buf) { writeIPv6Text(x, buf) template void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool trailing_zeros, - bool exact_frac_digits_set, UInt32 frac_digits_num) + bool fixed_fractional_length, UInt32 fractional_length) { /// If it's big integer, but the number of digits is small, /// use the implementation for smaller integers for more efficient arithmetic. - if constexpr (std::is_same_v) { if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, fixed_fractional_length, fractional_length); return; } else if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, fixed_fractional_length, fractional_length); return; } else if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, fixed_fractional_length, fractional_length); return; } } @@ -919,35 +918,35 @@ void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool { if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, fixed_fractional_length, fractional_length); return; } else if (x <= std::numeric_limits::max()) { - writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); + writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros, fixed_fractional_length, fractional_length); return; } } constexpr size_t max_digits = std::numeric_limits::digits10; - assert(scale <= max_digits); + assert(scale <= max_digits && fractional_length <= max_digits); char buf[max_digits]; - memset(buf, '0', scale); + memset(buf, '0', std::max(scale, fractional_length)); T value = x; Int32 last_nonzero_pos = 0; - if (exact_frac_digits_set && frac_digits_num < scale) + if (fixed_fractional_length && fractional_length < scale) { - T new_value = value / DecimalUtils::scaleMultiplier(scale - frac_digits_num - 1); + T new_value = value / DecimalUtils::scaleMultiplier(scale - fractional_length - 1); auto round_carry = new_value % 10; value = new_value / 10; if (round_carry >= 5) value += 1; } - for (Int32 pos = scale - 1; pos >= 0; --pos) + for (Int32 pos = fixed_fractional_length ? std::min(scale - 1, fractional_length - 1) : scale - 1; pos >= 0; --pos) { auto remainder = value % 10; value /= 10; @@ -958,15 +957,13 @@ void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool buf[pos] += static_cast(remainder); } - - if (likely(!exact_frac_digits_set || frac_digits_num != 0)) - writeChar('.', ostr); - ostr.write(buf, exact_frac_digits_set ? frac_digits_num : trailing_zeros ? scale : last_nonzero_pos + 1); + writeChar('.', ostr); + ostr.write(buf, fixed_fractional_length ? fractional_length : (trailing_zeros ? scale : last_nonzero_pos + 1)); } template void writeText(Decimal x, UInt32 scale, WriteBuffer & ostr, bool trailing_zeros, - bool exact_frac_digits_set = false, UInt32 frac_digits_num = 0) + bool fixed_fractional_length = false, UInt32 fractional_length = 0) { T part = DecimalUtils::getWholePart(x, scale); @@ -977,7 +974,7 @@ void writeText(Decimal x, UInt32 scale, WriteBuffer & ostr, bool trailing_zer writeIntText(part, ostr); - if (scale) + if (scale || (fixed_fractional_length && fractional_length)) { part = DecimalUtils::getFractionalPart(x, scale); if (part || trailing_zeros) @@ -985,7 +982,7 @@ void writeText(Decimal x, UInt32 scale, WriteBuffer & ostr, bool trailing_zer if (part < 0) part *= T(-1); - writeDecimalFractional(part, scale, ostr, trailing_zeros, exact_frac_digits_set, frac_digits_num); + writeDecimalFractional(part, scale, ostr, trailing_zeros, fixed_fractional_length, fractional_length); } } } diff --git a/tests/queries/0_stateless/02676_format_decimal.reference b/tests/queries/0_stateless/02676_format_decimal.reference index 70b7c95b26a..d7b7dfb76d6 100644 --- a/tests/queries/0_stateless/02676_format_decimal.reference +++ b/tests/queries/0_stateless/02676_format_decimal.reference @@ -1,6 +1,15 @@ 2.00 2.12 2.15 -64.32 -64.32 -64.32 +2.987600 +64.12 +64.23 +32.345 +32.345000 +32.46 +64.5671232345 +128.78932312332132985464 +128.78932312332132985464 +128.78932312332132985464000000 +128.7893231233 +128.78932312332132985464123123789323123321329854600000 diff --git a/tests/queries/0_stateless/02676_format_decimal.sql b/tests/queries/0_stateless/02676_format_decimal.sql index c0b41726973..2c29bdc91f2 100644 --- a/tests/queries/0_stateless/02676_format_decimal.sql +++ b/tests/queries/0_stateless/02676_format_decimal.sql @@ -1,6 +1,20 @@ -SELECT formatDecimal(2,2); -- 2.00 -SELECT formatDecimal(2.123456,2); -- 2.12 -SELECT formatDecimal(2.1456,2); -- 2.15 -- rounding! -SELECT formatDecimal(64.32::Float64, 2); -- 64.32 -SELECT formatDecimal(64.32::Decimal32(2), 2); -- 64.32 -SELECT formatDecimal(64.32::Decimal64(2), 2); -- 64.32 \ No newline at end of file +-- Regular types +SELECT formatDecimal(2, 2); -- more digits required than exist +SELECT formatDecimal(2.123456, 2); -- rounding +SELECT formatDecimal(2.1456, 2); -- rounding +SELECT formatDecimal(2.9876, 6); -- more digits required than exist + +-- Float32 and Float64 tests +SELECT formatDecimal(64.123::Float32, 2); +SELECT formatDecimal(64.234::Float64, 2); + +-- Decimals +SELECT formatDecimal(32.345::Decimal32(3), 3); +SELECT formatDecimal(32.345::Decimal32(3), 6); -- more digits required than exist +SELECT formatDecimal(32.456::Decimal32(3), 2); -- rounding +SELECT formatDecimal('64.5671232345'::Decimal64(10), 10); +SELECT formatDecimal('128.78932312332132985464'::Decimal128(20), 20); +SELECT formatDecimal('128.78932312332132985464123123'::Decimal128(26), 20); -- rounding +SELECT formatDecimal('128.78932312332132985464'::Decimal128(20), 26); -- more digits required than exist +SELECT formatDecimal('128.789323123321329854641231237893231233213298546'::Decimal256(45), 10); -- rounding +SELECT formatDecimal('128.789323123321329854641231237893231233213298546'::Decimal256(45), 50); -- more digits required than exist \ No newline at end of file From a4d1cd514d3ffd6f109dab4d9184b47d01ea8fa9 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 23 Mar 2023 08:58:56 +0000 Subject: [PATCH 076/377] Refactor --- programs/copier/ClusterCopierApp.cpp | 9 +-- .../extract-from-config/ExtractFromConfig.cpp | 11 +--- programs/server/Server.cpp | 8 +-- src/Common/ZooKeeper/ZooKeeper.cpp | 24 +++++-- src/Common/ZooKeeper/ZooKeeper.h | 6 +- .../examples/zk_many_watches_reconnect.cpp | 66 ------------------- src/Interpreters/Context.cpp | 8 +-- ...get_abandonable_lock_in_all_partitions.cpp | 2 +- .../get_current_inserts_in_replicated.cpp | 2 +- 9 files changed, 37 insertions(+), 99 deletions(-) delete mode 100644 src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp diff --git a/programs/copier/ClusterCopierApp.cpp b/programs/copier/ClusterCopierApp.cpp index 297648280aa..822289dd89c 100644 --- a/programs/copier/ClusterCopierApp.cpp +++ b/programs/copier/ClusterCopierApp.cpp @@ -1,4 +1,5 @@ #include "ClusterCopierApp.h" +#include #include #include #include @@ -12,11 +13,6 @@ namespace fs = std::filesystem; namespace DB { -namespace ErrorCodes -{ - extern const int EXCESSIVE_ELEMENT_IN_CONFIG; -} - /// ClusterCopierApp void ClusterCopierApp::initialize(Poco::Util::Application & self) @@ -197,8 +193,7 @@ void ClusterCopierApp::mainImpl() if (!task_file.empty()) copier->uploadTaskDescription(task_path, task_file, config().getBool("task-upload-force", false)); - if (config().has("zookeeper") && config().has("keeper")) - throw Exception("Both ZooKeeper and Keeper are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); + zkutil::validateZooKeeperConfig(config()); copier->init(); copier->process(ConnectionTimeouts::getTCPTimeoutsWithoutFailover(context->getSettingsRef())); diff --git a/programs/extract-from-config/ExtractFromConfig.cpp b/programs/extract-from-config/ExtractFromConfig.cpp index 75b0d311fdb..5305c61b730 100644 --- a/programs/extract-from-config/ExtractFromConfig.cpp +++ b/programs/extract-from-config/ExtractFromConfig.cpp @@ -20,11 +20,6 @@ #include -namespace DB::ErrorCodes -{ - extern const int EXCESSIVE_ELEMENT_IN_CONFIG; -} - static void setupLogging(const std::string & log_level) { Poco::AutoPtr channel(new Poco::ConsoleChannel); @@ -95,11 +90,9 @@ static std::vector extractFromConfig( { DB::ConfigurationPtr bootstrap_configuration(new Poco::Util::XMLConfiguration(config_xml)); - if (bootstrap_configuration->has("zookeeper") && bootstrap_configuration->has("keeper")) - throw DB::Exception(DB::ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG, "Both ZooKeeper and Keeper are specified"); + zkutil::validateZooKeeperConfig(*bootstrap_configuration); - zkutil::ZooKeeperPtr zookeeper; - zookeeper = std::make_shared( + zkutil::ZooKeeperPtr zookeeper = std::make_shared( *bootstrap_configuration, bootstrap_configuration->has("zookeeper") ? "zookeeper" : "keeper", nullptr); zkutil::ZooKeeperNodeCache zk_node_cache([&] { return zookeeper; }); diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 79c14dac0a9..802bee7fad5 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -815,10 +815,8 @@ try } ); - if (config().has("zookeeper") && config().has("keeper")) - throw Exception(ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG, "Both ZooKeeper and Keeper are specified"); - - bool has_zookeeper = config().has("zookeeper") || config().has("keeper") || config().has("keeper_server"); + zkutil::validateZooKeeperConfig(config()); + bool has_zookeeper = zkutil::hasZooKeeperConfig(config()); zkutil::ZooKeeperNodeCache main_config_zk_node_cache([&] { return global_context->getZooKeeper(); }); zkutil::EventPtr main_config_zk_changed_event = std::make_shared(); @@ -1310,7 +1308,7 @@ try { /// We do not load ZooKeeper configuration on the first config loading /// because TestKeeper server is not started yet. - if (config->has("zookeeper") || config->has("keeper") || config->has("keeper_server")) + if (zkutil::hasZooKeeperConfig(config)) global_context->reloadZooKeeperIfChanged(config); global_context->reloadAuxiliaryZooKeepersConfigIfChanged(config); diff --git a/src/Common/ZooKeeper/ZooKeeper.cpp b/src/Common/ZooKeeper/ZooKeeper.cpp index bda1b168a14..f9d851f9697 100644 --- a/src/Common/ZooKeeper/ZooKeeper.cpp +++ b/src/Common/ZooKeeper/ZooKeeper.cpp @@ -30,6 +30,7 @@ namespace ErrorCodes extern const int NOT_IMPLEMENTED; extern const int BAD_ARGUMENTS; extern const int NO_ELEMENTS_IN_CONFIG; + extern const int EXCESSIVE_ELEMENT_IN_CONFIG; } } @@ -1335,16 +1336,29 @@ String getSequentialNodeName(const String & prefix, UInt64 number) return name; } -String getZookeeperConfigName(const Poco::Util::AbstractConfiguration & config) +void validateZooKeeperConfig(const Poco::Util::AbstractConfiguration & config) +{ + if (config.has("zookeeper") && config.has("keeper")) + throw DB::Exception(DB::ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG, "Both ZooKeeper and Keeper are specified"); +} + +bool hasZooKeeperConfig(const Poco::Util::AbstractConfiguration & config, bool allow_keeper_server) +{ + return config.has("zookeeper") || config.has("keeper") || (allow_keeper_server && config.has("keeper_server")); +} + +String getZooKeeperConfigName(const Poco::Util::AbstractConfiguration & config, bool allow_keeper_server) { if (config.has("zookeeper")) return "zookeeper"; - else if (config.has("keeper")) + + if (config.has("keeper")) return "keeper"; - else if (config.has("keeper_server")) + + if (allow_keeper_server && config.has("keeper_server")) return "keeper_server"; - else - throw DB::Exception("There is no Zookeeper configuration in server config", DB::ErrorCodes::NO_ELEMENTS_IN_CONFIG); + + throw DB::Exception(DB::ErrorCodes::NO_ELEMENTS_IN_CONFIG, "There is no Zookeeper configuration in server config"); } } diff --git a/src/Common/ZooKeeper/ZooKeeper.h b/src/Common/ZooKeeper/ZooKeeper.h index d02fbbedd86..8776497a41d 100644 --- a/src/Common/ZooKeeper/ZooKeeper.h +++ b/src/Common/ZooKeeper/ZooKeeper.h @@ -667,6 +667,10 @@ String extractZooKeeperPath(const String & path, bool check_starts_with_slash, P String getSequentialNodeName(const String & prefix, UInt64 number); -String getZookeeperConfigName(const Poco::Util::AbstractConfiguration & config); +void validateZooKeeperConfig(const Poco::Util::AbstractConfiguration & config); + +bool hasZooKeeperConfig(const Poco::Util::AbstractConfiguration & config, bool allow_keeper_server = true); + +String getZooKeeperConfigName(const Poco::Util::AbstractConfiguration & config, bool allow_keeper_server = true); } diff --git a/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp b/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp deleted file mode 100644 index aad8913ca8b..00000000000 --- a/src/Common/ZooKeeper/examples/zk_many_watches_reconnect.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include -#include - -/// A tool for reproducing https://issues.apache.org/jira/browse/ZOOKEEPER-706 -/// Original libzookeeper can't reconnect the session if the length of SET_WATCHES message -/// exceeds jute.maxbuffer (0xfffff by default). -/// This happens when the number of watches exceeds ~29000. -/// -/// Session reconnect can be caused by forbidding packets to the current zookeeper server, e.g. -/// sudo ip6tables -A OUTPUT -d mtzoo01it.haze.yandex.net -j REJECT - -const size_t N_THREADS = 100; - -int main(int argc, char ** argv) -{ - try - { - if (argc != 3) - { - std::cerr << "usage: " << argv[0] << " " << std::endl; - return 3; - } - - DB::ConfigProcessor processor(argv[1], false, true); - auto config = processor.loadConfig().configuration; - zkutil::ZooKeeper zk(*config, zkutil::getZookeeperConfigName(*config), nullptr); - zkutil::EventPtr watch = std::make_shared(); - - /// NOTE: setting watches in multiple threads because doing it in a single thread is too slow. - size_t watches_per_thread = std::stoull(argv[2]) / N_THREADS; - std::vector threads; - for (size_t i_thread = 0; i_thread < N_THREADS; ++i_thread) - { - threads.emplace_back([&, i_thread] - { - for (size_t i = 0; i < watches_per_thread; ++i) - zk.exists("/clickhouse/nonexistent_node" + std::to_string(i * N_THREADS + i_thread), nullptr, watch); - }); - } - for (size_t i_thread = 0; i_thread < N_THREADS; ++i_thread) - threads[i_thread].join(); - - while (true) - { - std::cerr << "WAITING..." << std::endl; - sleep(10); - } - } - catch (Poco::Exception & e) - { - std::cerr << "Exception: " << e.displayText() << std::endl; - return 1; - } - catch (std::exception & e) - { - std::cerr << "std::exception: " << e.what() << std::endl; - return 3; - } - catch (...) - { - std::cerr << "Some exception" << std::endl; - return 2; - } -} diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 9c133a60ea6..e51a831684f 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -2360,7 +2360,7 @@ zkutil::ZooKeeperPtr Context::getZooKeeper() const const auto & config = shared->zookeeper_config ? *shared->zookeeper_config : getConfigRef(); if (!shared->zookeeper) - shared->zookeeper = std::make_shared(config, zkutil::getZookeeperConfigName(config), getZooKeeperLog()); + shared->zookeeper = std::make_shared(config, zkutil::getZooKeeperConfigName(config), getZooKeeperLog()); else if (shared->zookeeper->expired()) { Stopwatch watch; @@ -2399,9 +2399,9 @@ bool Context::tryCheckClientConnectionToMyKeeperCluster() const { try { + const auto config_name = zkutil::getZooKeeperConfigName(getConfigRef()); /// If our server is part of main Keeper cluster - if (checkZooKeeperConfigIsLocal(getConfigRef(), "zookeeper") || checkZooKeeperConfigIsLocal(getConfigRef(), "keeper") - || (!getConfigRef().has("zookeeper") && !getConfigRef().has("keeper") && getConfigRef().has("keeper_server"))) + if (config_name == "keeper_server" || checkZooKeeperConfigIsLocal(getConfigRef(), config_name)) { LOG_DEBUG(shared->log, "Keeper server is participant of the main zookeeper cluster, will try to connect to it"); getZooKeeper(); @@ -2600,7 +2600,7 @@ void Context::reloadZooKeeperIfChanged(const ConfigurationPtr & config) const { std::lock_guard lock(shared->zookeeper_mutex); shared->zookeeper_config = config; - reloadZooKeeperIfChangedImpl(config, zkutil::getZookeeperConfigName(*config), shared->zookeeper, getZooKeeperLog()); + reloadZooKeeperIfChangedImpl(config, zkutil::getZooKeeperConfigName(*config), shared->zookeeper, getZooKeeperLog()); } void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & config) diff --git a/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp b/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp index e1faa67eb45..9e2b2a83b98 100644 --- a/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp +++ b/src/Storages/examples/get_abandonable_lock_in_all_partitions.cpp @@ -26,7 +26,7 @@ try auto config = processor.loadConfig().configuration; String root_path = argv[2]; - zkutil::ZooKeeper zk(*config, zkutil::getZookeeperConfigName(*config), nullptr); + zkutil::ZooKeeper zk(*config, zkutil::getZooKeeperConfigName(*config), nullptr); String temp_path = root_path + "/temp"; String blocks_path = root_path + "/block_numbers"; diff --git a/src/Storages/examples/get_current_inserts_in_replicated.cpp b/src/Storages/examples/get_current_inserts_in_replicated.cpp index 9ba75b81440..d77b0f5177d 100644 --- a/src/Storages/examples/get_current_inserts_in_replicated.cpp +++ b/src/Storages/examples/get_current_inserts_in_replicated.cpp @@ -29,7 +29,7 @@ try auto config = processor.loadConfig().configuration; String zookeeper_path = argv[2]; - auto zookeeper = std::make_shared(*config, zkutil::getZookeeperConfigName(*config), nullptr); + auto zookeeper = std::make_shared(*config, zkutil::getZooKeeperConfigName(*config), nullptr); std::unordered_map> current_inserts; From d6cbc5d05b34f630f64445105632ccb8b2429470 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 23 Mar 2023 12:58:39 +0000 Subject: [PATCH 077/377] Fix tests --- programs/server/Server.cpp | 2 +- src/Common/ZooKeeper/ZooKeeper.cpp | 8 +- src/Common/ZooKeeper/ZooKeeper.h | 4 +- src/Common/ZooKeeper/ZooKeeperArgs.cpp | 98 ++++++++++++------- src/Common/ZooKeeper/ZooKeeperArgs.h | 2 +- src/Interpreters/Context.cpp | 2 +- .../helpers/zookeeper_secure_config.xml | 4 +- .../enable_keeper1.xml | 8 ++ .../enable_keeper2.xml | 8 ++ .../configs/enable_keeper3.xml | 41 ++++++++ .../configs/keeper_config.xml | 12 +-- .../configs/remote_servers.xml | 6 +- .../configs/zookeeper_config.xml | 12 +-- .../configs_keeper_server/remote_servers.xml | 18 ---- .../configs_keeper_server/use_keeper.xml | 12 --- .../test_alternative_keeper_config/test.py | 31 ++++-- .../test_keeper_server.py | 61 ------------ .../configs/enable_keeper1.xml | 6 +- .../configs/enable_keeper2.xml | 6 +- .../configs/enable_keeper3.xml | 6 +- .../configs/keeper_config_with_allow_list.xml | 4 +- .../keeper_config_with_allow_list_all.xml | 4 +- .../keeper_config_without_allow_list.xml | 4 +- .../test_keeper_four_word_command/test.py | 9 +- .../configs/enable_keeper1.xml | 2 + .../configs/enable_keeper_three_nodes_1.xml | 2 + .../configs/enable_keeper_three_nodes_2.xml | 2 + .../configs/enable_keeper_three_nodes_3.xml | 2 + .../configs/enable_keeper_two_nodes_1.xml | 2 + .../configs/enable_keeper_two_nodes_2.xml | 2 + .../configs/keeper_config.xml | 2 + .../configs/zookeeper_config_root_a.xml | 4 +- 32 files changed, 204 insertions(+), 182 deletions(-) rename tests/integration/test_alternative_keeper_config/{configs_keeper_server => configs}/enable_keeper1.xml (79%) rename tests/integration/test_alternative_keeper_config/{configs_keeper_server => configs}/enable_keeper2.xml (79%) create mode 100644 tests/integration/test_alternative_keeper_config/configs/enable_keeper3.xml delete mode 100644 tests/integration/test_alternative_keeper_config/configs_keeper_server/remote_servers.xml delete mode 100644 tests/integration/test_alternative_keeper_config/configs_keeper_server/use_keeper.xml delete mode 100644 tests/integration/test_alternative_keeper_config/test_keeper_server.py diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 802bee7fad5..7e120e0fa17 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -1308,7 +1308,7 @@ try { /// We do not load ZooKeeper configuration on the first config loading /// because TestKeeper server is not started yet. - if (zkutil::hasZooKeeperConfig(config)) + if (zkutil::hasZooKeeperConfig(*config)) global_context->reloadZooKeeperIfChanged(config); global_context->reloadAuxiliaryZooKeepersConfigIfChanged(config); diff --git a/src/Common/ZooKeeper/ZooKeeper.cpp b/src/Common/ZooKeeper/ZooKeeper.cpp index f9d851f9697..c9062d9fd4c 100644 --- a/src/Common/ZooKeeper/ZooKeeper.cpp +++ b/src/Common/ZooKeeper/ZooKeeper.cpp @@ -1342,12 +1342,12 @@ void validateZooKeeperConfig(const Poco::Util::AbstractConfiguration & config) throw DB::Exception(DB::ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG, "Both ZooKeeper and Keeper are specified"); } -bool hasZooKeeperConfig(const Poco::Util::AbstractConfiguration & config, bool allow_keeper_server) +bool hasZooKeeperConfig(const Poco::Util::AbstractConfiguration & config) { - return config.has("zookeeper") || config.has("keeper") || (allow_keeper_server && config.has("keeper_server")); + return config.has("zookeeper") || config.has("keeper") || (config.has("keeper_server") && config.getBool("keeper_server.use_cluster", true)); } -String getZooKeeperConfigName(const Poco::Util::AbstractConfiguration & config, bool allow_keeper_server) +String getZooKeeperConfigName(const Poco::Util::AbstractConfiguration & config) { if (config.has("zookeeper")) return "zookeeper"; @@ -1355,7 +1355,7 @@ String getZooKeeperConfigName(const Poco::Util::AbstractConfiguration & config, if (config.has("keeper")) return "keeper"; - if (allow_keeper_server && config.has("keeper_server")) + if (config.has("keeper_server") && config.getBool("keeper_server.use_cluster", true)) return "keeper_server"; throw DB::Exception(DB::ErrorCodes::NO_ELEMENTS_IN_CONFIG, "There is no Zookeeper configuration in server config"); diff --git a/src/Common/ZooKeeper/ZooKeeper.h b/src/Common/ZooKeeper/ZooKeeper.h index 8776497a41d..41f8d110964 100644 --- a/src/Common/ZooKeeper/ZooKeeper.h +++ b/src/Common/ZooKeeper/ZooKeeper.h @@ -669,8 +669,8 @@ String getSequentialNodeName(const String & prefix, UInt64 number); void validateZooKeeperConfig(const Poco::Util::AbstractConfiguration & config); -bool hasZooKeeperConfig(const Poco::Util::AbstractConfiguration & config, bool allow_keeper_server = true); +bool hasZooKeeperConfig(const Poco::Util::AbstractConfiguration & config); -String getZooKeeperConfigName(const Poco::Util::AbstractConfiguration & config, bool allow_keeper_server = true); +String getZooKeeperConfigName(const Poco::Util::AbstractConfiguration & config); } diff --git a/src/Common/ZooKeeper/ZooKeeperArgs.cpp b/src/Common/ZooKeeper/ZooKeeperArgs.cpp index 3d6d75ba5d5..7cd85db931b 100644 --- a/src/Common/ZooKeeper/ZooKeeperArgs.cpp +++ b/src/Common/ZooKeeper/ZooKeeperArgs.cpp @@ -19,8 +19,8 @@ namespace zkutil ZooKeeperArgs::ZooKeeperArgs(const Poco::Util::AbstractConfiguration & config, const String & config_name) { - if (endsWith(config_name, "keeper_server")) - initFromKeeperServerSection(config, config_name); + if (config_name == "keeper_server") + initFromKeeperServerSection(config); else initFromKeeperSection(config, config_name); @@ -52,49 +52,79 @@ ZooKeeperArgs::ZooKeeperArgs(const String & hosts_string) splitInto<','>(hosts, hosts_string); } -void ZooKeeperArgs::initFromKeeperServerSection(const Poco::Util::AbstractConfiguration & config, const std::string & config_name) +void ZooKeeperArgs::initFromKeeperServerSection(const Poco::Util::AbstractConfiguration & config) { - Poco::Util::AbstractConfiguration::Keys keys; - config.keys(config_name, keys); + static constexpr std::string_view config_name = "keeper_server"; - bool secure = false; - String tcp_port; - String tcp_port_secure; - for (const auto & key : keys) + if (auto key = std::string{config_name} + ".tcp_port_secure"; + config.has(key)) { - if (key == "tcp_port_secure") - { - secure = true; - tcp_port_secure = config.getString(config_name + "." + key); - } - else if (key == "tcp_port") - { - tcp_port = config.getString(config_name + "." + key); - } - else if (key == "coordination_settings") - { - if (config.has(config_name + "." + key + ".operation_timeout_ms")) - operation_timeout_ms = config.getInt(config_name + "." + key + ".operation_timeout_ms"); - if (config.has(config_name + "." + key + ".session_timeout_ms")) - session_timeout_ms = config.getInt(config_name + "." + key + ".session_timeout_ms"); - } + auto tcp_port_secure = config.getString(key); + + if (tcp_port_secure.empty()) + throw KeeperException("Empty tcp_port_secure in config file", Coordination::Error::ZBADARGUMENTS); } - if (secure && tcp_port_secure.empty()) - throw KeeperException("No tcp_port_secure in config file", Coordination::Error::ZBADARGUMENTS); - if (!secure && tcp_port.empty()) - throw KeeperException("No tcp_port in config file", Coordination::Error::ZBADARGUMENTS); + bool secure{false}; + std::string tcp_port; + if (auto tcp_port_secure_key = std::string{config_name} + ".tcp_port_secure"; + config.has(tcp_port_secure_key)) + { + secure = true; + tcp_port = config.getString(tcp_port_secure_key); + } + else if (auto tcp_port_key = std::string{config_name} + ".tcp_port"; + config.has(tcp_port_key)) + { + tcp_port = config.getString(tcp_port_key); + } - config.keys(config_name + ".raft_configuration", keys); + if (tcp_port.empty()) + throw KeeperException("No tcp_port or tcp_port_secure in config file", Coordination::Error::ZBADARGUMENTS); + + if (auto coordination_key = std::string{config_name} + ".coordination_settings"; + config.has(coordination_key)) + { + if (auto operation_timeout_key = coordination_key + ".operation_timeout_ms"; + config.has(operation_timeout_key)) + operation_timeout_ms = config.getInt(operation_timeout_key); + + if (auto session_timeout_key = coordination_key + ".session_timeout_ms"; + config.has(session_timeout_key)) + session_timeout_key = config.getInt(session_timeout_key); + } + + Poco::Util::AbstractConfiguration::Keys keys; + std::string raft_configuration_key = std::string{config_name} + ".raft_configuration"; + config.keys(raft_configuration_key, keys); for (const auto & key : keys) { if (startsWith(key, "server")) - { hosts.push_back( - (secure ? "secure://" : "") + config.getString(config_name + ".raft_configuration." + key + ".hostname") + ":" - + (secure ? tcp_port_secure : tcp_port)); + (secure ? "secure://" : "") + config.getString(raft_configuration_key + "." + key + ".hostname") + ":" + tcp_port); + } + + static constexpr std::array load_balancing_keys + { + ".zookeeper_load_balancing", + ".keeper_load_balancing" + }; + + for (const auto * load_balancing_key : load_balancing_keys) + { + if (auto load_balancing_config = std::string{config_name} + load_balancing_key; + config.has(load_balancing_config)) + { + String load_balancing_str = config.getString(load_balancing_config); + /// Use magic_enum to avoid dependency from dbms (`SettingFieldLoadBalancingTraits::fromString(...)`) + auto load_balancing = magic_enum::enum_cast(Poco::toUpper(load_balancing_str)); + if (!load_balancing) + throw DB::Exception(DB::ErrorCodes::BAD_ARGUMENTS, "Unknown load balancing: {}", load_balancing_str); + get_priority_load_balancing.load_balancing = *load_balancing; + break; } } + } void ZooKeeperArgs::initFromKeeperSection(const Poco::Util::AbstractConfiguration & config, const std::string & config_name) @@ -144,7 +174,7 @@ void ZooKeeperArgs::initFromKeeperSection(const Poco::Util::AbstractConfiguratio { implementation = config.getString(config_name + "." + key); } - else if (key == "zookeeper_load_balancing") + else if (key == "zookeeper_load_balancing" || key == "keeper_load_balancing") { String load_balancing_str = config.getString(config_name + "." + key); /// Use magic_enum to avoid dependency from dbms (`SettingFieldLoadBalancingTraits::fromString(...)`) diff --git a/src/Common/ZooKeeper/ZooKeeperArgs.h b/src/Common/ZooKeeper/ZooKeeperArgs.h index fa97f8b860f..3f33721804f 100644 --- a/src/Common/ZooKeeper/ZooKeeperArgs.h +++ b/src/Common/ZooKeeper/ZooKeeperArgs.h @@ -34,7 +34,7 @@ struct ZooKeeperArgs DB::GetPriorityForLoadBalancing get_priority_load_balancing; private: - void initFromKeeperServerSection(const Poco::Util::AbstractConfiguration & config, const std::string & config_name); + void initFromKeeperServerSection(const Poco::Util::AbstractConfiguration & config); void initFromKeeperSection(const Poco::Util::AbstractConfiguration & config, const std::string & config_name); }; diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index e51a831684f..107adc3ac57 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -2624,7 +2624,7 @@ void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & bool Context::hasZooKeeper() const { - return getConfigRef().has("zookeeper") || getConfigRef().has("keeper") || getConfigRef().has("keeper_server"); + return zkutil::hasZooKeeperConfig(getConfigRef()); } bool Context::hasAuxiliaryZooKeeper(const String & name) const diff --git a/tests/integration/helpers/zookeeper_secure_config.xml b/tests/integration/helpers/zookeeper_secure_config.xml index 349bcbc5eba..3e3706988ab 100644 --- a/tests/integration/helpers/zookeeper_secure_config.xml +++ b/tests/integration/helpers/zookeeper_secure_config.xml @@ -1,5 +1,5 @@ - + zoo1 2281 @@ -13,5 +13,5 @@ 2281 15000 - + diff --git a/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper1.xml b/tests/integration/test_alternative_keeper_config/configs/enable_keeper1.xml similarity index 79% rename from tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper1.xml rename to tests/integration/test_alternative_keeper_config/configs/enable_keeper1.xml index 7c2e283e89f..fbdece06085 100644 --- a/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper1.xml +++ b/tests/integration/test_alternative_keeper_config/configs/enable_keeper1.xml @@ -28,6 +28,14 @@ true 2 + + 3 + node3 + 9234 + true + true + 3 + diff --git a/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper2.xml b/tests/integration/test_alternative_keeper_config/configs/enable_keeper2.xml similarity index 79% rename from tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper2.xml rename to tests/integration/test_alternative_keeper_config/configs/enable_keeper2.xml index 618e6a04aec..dc3ce6c30c4 100644 --- a/tests/integration/test_alternative_keeper_config/configs_keeper_server/enable_keeper2.xml +++ b/tests/integration/test_alternative_keeper_config/configs/enable_keeper2.xml @@ -28,6 +28,14 @@ true 2 + + 3 + node3 + 9234 + true + true + 3 + diff --git a/tests/integration/test_alternative_keeper_config/configs/enable_keeper3.xml b/tests/integration/test_alternative_keeper_config/configs/enable_keeper3.xml new file mode 100644 index 00000000000..af2566565e4 --- /dev/null +++ b/tests/integration/test_alternative_keeper_config/configs/enable_keeper3.xml @@ -0,0 +1,41 @@ + + + 9181 + 3 + /var/lib/clickhouse/coordination/log + /var/lib/clickhouse/coordination/snapshots + + + 5000 + 10000 + 75 + trace + + + + + 1 + node1 + 9234 + true + 3 + + + 2 + node2 + 9234 + true + true + 2 + + + 3 + node3 + 9234 + true + true + 3 + + + + diff --git a/tests/integration/test_alternative_keeper_config/configs/keeper_config.xml b/tests/integration/test_alternative_keeper_config/configs/keeper_config.xml index bd783b83853..b62e2728085 100644 --- a/tests/integration/test_alternative_keeper_config/configs/keeper_config.xml +++ b/tests/integration/test_alternative_keeper_config/configs/keeper_config.xml @@ -1,16 +1,16 @@ - zoo1 - 2181 + node1 + 9181 - zoo2 - 2181 + node2 + 9181 - zoo3 - 2181 + node3 + 9181 3000 diff --git a/tests/integration/test_alternative_keeper_config/configs/remote_servers.xml b/tests/integration/test_alternative_keeper_config/configs/remote_servers.xml index e77cc5c65e6..5b453fdeb67 100644 --- a/tests/integration/test_alternative_keeper_config/configs/remote_servers.xml +++ b/tests/integration/test_alternative_keeper_config/configs/remote_servers.xml @@ -6,12 +6,14 @@ node1 9000 - node2 9000 - + + node3 + 9000 + diff --git a/tests/integration/test_alternative_keeper_config/configs/zookeeper_config.xml b/tests/integration/test_alternative_keeper_config/configs/zookeeper_config.xml index 7a0d7c1de92..31913bb6e2c 100644 --- a/tests/integration/test_alternative_keeper_config/configs/zookeeper_config.xml +++ b/tests/integration/test_alternative_keeper_config/configs/zookeeper_config.xml @@ -1,16 +1,16 @@ - zoo1 - 2181 + node1 + 9181 - zoo2 - 2181 + node2 + 9181 - zoo3 - 2181 + node3 + 9181 3000 diff --git a/tests/integration/test_alternative_keeper_config/configs_keeper_server/remote_servers.xml b/tests/integration/test_alternative_keeper_config/configs_keeper_server/remote_servers.xml deleted file mode 100644 index e77cc5c65e6..00000000000 --- a/tests/integration/test_alternative_keeper_config/configs_keeper_server/remote_servers.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - node1 - 9000 - - - - node2 - 9000 - - - - - - diff --git a/tests/integration/test_alternative_keeper_config/configs_keeper_server/use_keeper.xml b/tests/integration/test_alternative_keeper_config/configs_keeper_server/use_keeper.xml deleted file mode 100644 index b250f06cf81..00000000000 --- a/tests/integration/test_alternative_keeper_config/configs_keeper_server/use_keeper.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - node1 - 9181 - - - node2 - 9181 - - - diff --git a/tests/integration/test_alternative_keeper_config/test.py b/tests/integration/test_alternative_keeper_config/test.py index 8784cb9be56..d2cfc4fe25e 100644 --- a/tests/integration/test_alternative_keeper_config/test.py +++ b/tests/integration/test_alternative_keeper_config/test.py @@ -8,18 +8,30 @@ cluster = ClickHouseCluster(__file__) node1 = cluster.add_instance( "node1", - with_zookeeper=True, - main_configs=["configs/remote_servers.xml", "configs/keeper_config.xml"], + main_configs=[ + "configs/remote_servers.xml", + "configs/keeper_config.xml", + "configs/enable_keeper1.xml", + ], macros={"replica": "node1"}, ) node2 = cluster.add_instance( "node2", - with_zookeeper=True, - main_configs=["configs/remote_servers.xml", "configs/zookeeper_config.xml"], + main_configs=[ + "configs/remote_servers.xml", + "configs/zookeeper_config.xml", + "configs/enable_keeper2.xml", + ], macros={"replica": "node2"}, ) +node3 = cluster.add_instance( + "node3", + main_configs=["configs/remote_servers.xml", "configs/enable_keeper3.xml"], + macros={"replica": "node3"}, +) + @pytest.fixture(scope="module", autouse=True) def started_cluster(): @@ -45,10 +57,9 @@ def test_create_insert(started_cluster): node1.query("INSERT INTO tbl VALUES (1, 'str1')") node2.query("INSERT INTO tbl VALUES (1, 'str1')") # Test deduplication - node2.query("INSERT INTO tbl VALUES (2, 'str2')") + node3.query("INSERT INTO tbl VALUES (2, 'str2')") - expected = [[1, "str1"], [2, "str2"]] - assert node1.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) - assert node2.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) - assert node1.query("CHECK TABLE tbl") == "1\n" - assert node2.query("CHECK TABLE tbl") == "1\n" + for node in [node1, node2, node3]: + expected = [[1, "str1"], [2, "str2"]] + assert node.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) + assert node.query("CHECK TABLE tbl") == "1\n" diff --git a/tests/integration/test_alternative_keeper_config/test_keeper_server.py b/tests/integration/test_alternative_keeper_config/test_keeper_server.py deleted file mode 100644 index 9c61e076671..00000000000 --- a/tests/integration/test_alternative_keeper_config/test_keeper_server.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 - -import pytest -from helpers.cluster import ClickHouseCluster -from helpers.test_tools import TSV - -cluster = ClickHouseCluster(__file__) - -node1 = cluster.add_instance( - "node1", - with_zookeeper=True, - main_configs=[ - "configs_keeper_server/remote_servers.xml", - "configs_keeper_server/enable_keeper1.xml", - "configs_keeper_server/use_keeper.xml", - ], - macros={"replica": "node1"}, -) - -node2 = cluster.add_instance( - "node2", - with_zookeeper=True, - main_configs=[ - "configs_keeper_server/remote_servers.xml", - "configs_keeper_server/enable_keeper2.xml", - ], - macros={"replica": "node2"}, -) - - -@pytest.fixture(scope="module", autouse=True) -def started_cluster(): - try: - cluster.start() - yield cluster - - finally: - cluster.shutdown() - - -def test_create_insert(started_cluster): - node1.query("DROP TABLE IF EXISTS tbl ON CLUSTER 'test_cluster' NO DELAY") - node1.query( - """ - CREATE TABLE tbl ON CLUSTER 'test_cluster' ( - id Int64, - str String - ) ENGINE=ReplicatedMergeTree('/clickhouse/tables/tbl/', '{replica}') - ORDER BY id - """ - ) - - node1.query("INSERT INTO tbl VALUES (1, 'str1')") - node2.query("INSERT INTO tbl VALUES (1, 'str1')") # Test deduplication - node2.query("INSERT INTO tbl VALUES (2, 'str2')") - - expected = [[1, "str1"], [2, "str2"]] - assert node1.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) - assert node2.query("SELECT * FROM tbl ORDER BY id") == TSV(expected) - assert node1.query("CHECK TABLE tbl") == "1\n" - assert node2.query("CHECK TABLE tbl") == "1\n" diff --git a/tests/integration/test_keeper_four_word_command/configs/enable_keeper1.xml b/tests/integration/test_keeper_four_word_command/configs/enable_keeper1.xml index a686c96e426..0ec413ac2ec 100644 --- a/tests/integration/test_keeper_four_word_command/configs/enable_keeper1.xml +++ b/tests/integration/test_keeper_four_word_command/configs/enable_keeper1.xml @@ -1,5 +1,7 @@ - + + false + 9181 1 /var/lib/clickhouse/coordination/log @@ -39,4 +41,4 @@ - + diff --git a/tests/integration/test_keeper_four_word_command/configs/enable_keeper2.xml b/tests/integration/test_keeper_four_word_command/configs/enable_keeper2.xml index 9818d32a74a..fde345f67b3 100644 --- a/tests/integration/test_keeper_four_word_command/configs/enable_keeper2.xml +++ b/tests/integration/test_keeper_four_word_command/configs/enable_keeper2.xml @@ -1,5 +1,7 @@ - + + false + 9181 2 /var/lib/clickhouse/coordination/log @@ -39,4 +41,4 @@ - + diff --git a/tests/integration/test_keeper_four_word_command/configs/enable_keeper3.xml b/tests/integration/test_keeper_four_word_command/configs/enable_keeper3.xml index 5a883fac3f6..84a8a402b46 100644 --- a/tests/integration/test_keeper_four_word_command/configs/enable_keeper3.xml +++ b/tests/integration/test_keeper_four_word_command/configs/enable_keeper3.xml @@ -1,5 +1,7 @@ - + + false + 9181 3 /var/lib/clickhouse/coordination/log @@ -39,4 +41,4 @@ - + diff --git a/tests/integration/test_keeper_four_word_command/configs/keeper_config_with_allow_list.xml b/tests/integration/test_keeper_four_word_command/configs/keeper_config_with_allow_list.xml index feafd3f6b44..b06f845ecf7 100644 --- a/tests/integration/test_keeper_four_word_command/configs/keeper_config_with_allow_list.xml +++ b/tests/integration/test_keeper_four_word_command/configs/keeper_config_with_allow_list.xml @@ -1,4 +1,4 @@ - + 9181 1 @@ -21,4 +21,4 @@ - + diff --git a/tests/integration/test_keeper_four_word_command/configs/keeper_config_with_allow_list_all.xml b/tests/integration/test_keeper_four_word_command/configs/keeper_config_with_allow_list_all.xml index 523e6b2fa27..46c2681c581 100644 --- a/tests/integration/test_keeper_four_word_command/configs/keeper_config_with_allow_list_all.xml +++ b/tests/integration/test_keeper_four_word_command/configs/keeper_config_with_allow_list_all.xml @@ -1,4 +1,4 @@ - + 9181 3 @@ -21,4 +21,4 @@ - + diff --git a/tests/integration/test_keeper_four_word_command/configs/keeper_config_without_allow_list.xml b/tests/integration/test_keeper_four_word_command/configs/keeper_config_without_allow_list.xml index 891f8a2ec12..cd5dea882af 100644 --- a/tests/integration/test_keeper_four_word_command/configs/keeper_config_without_allow_list.xml +++ b/tests/integration/test_keeper_four_word_command/configs/keeper_config_without_allow_list.xml @@ -1,4 +1,4 @@ - + 9181 2 @@ -20,4 +20,4 @@ - + diff --git a/tests/integration/test_keeper_four_word_command/test.py b/tests/integration/test_keeper_four_word_command/test.py index 412780c8f0f..df333ec479e 100644 --- a/tests/integration/test_keeper_four_word_command/test.py +++ b/tests/integration/test_keeper_four_word_command/test.py @@ -1,14 +1,7 @@ -import socket import pytest from helpers.cluster import ClickHouseCluster import helpers.keeper_utils as keeper_utils -import random -import string -import os import time -from multiprocessing.dummy import Pool -from helpers.test_tools import assert_eq_with_retry -from io import StringIO import csv import re @@ -23,7 +16,7 @@ node3 = cluster.add_instance( "node3", main_configs=["configs/enable_keeper3.xml"], stay_alive=True ) -from kazoo.client import KazooClient, KazooState +from kazoo.client import KazooClient def wait_nodes(): diff --git a/tests/integration/test_keeper_nodes_add/configs/enable_keeper1.xml b/tests/integration/test_keeper_nodes_add/configs/enable_keeper1.xml index c1d38a1de52..03307e912f6 100644 --- a/tests/integration/test_keeper_nodes_add/configs/enable_keeper1.xml +++ b/tests/integration/test_keeper_nodes_add/configs/enable_keeper1.xml @@ -1,5 +1,7 @@ + false + 9181 1 /var/lib/clickhouse/coordination/log diff --git a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_1.xml b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_1.xml index d2717283a8d..39a60afffec 100644 --- a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_1.xml +++ b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_1.xml @@ -1,5 +1,7 @@ + false + 9181 1 /var/lib/clickhouse/coordination/log diff --git a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_2.xml b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_2.xml index 5924ee1c2dc..0f2d5ff912b 100644 --- a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_2.xml +++ b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_2.xml @@ -1,5 +1,7 @@ + false + 9181 2 /var/lib/clickhouse/coordination/log diff --git a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_3.xml b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_3.xml index d261e4f67f3..f5061fe0f36 100644 --- a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_3.xml +++ b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_three_nodes_3.xml @@ -1,5 +1,7 @@ + false + 9181 3 /var/lib/clickhouse/coordination/log diff --git a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_two_nodes_1.xml b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_two_nodes_1.xml index 697986638d7..57585080e0f 100644 --- a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_two_nodes_1.xml +++ b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_two_nodes_1.xml @@ -1,5 +1,7 @@ + false + 9181 1 /var/lib/clickhouse/coordination/log diff --git a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_two_nodes_2.xml b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_two_nodes_2.xml index 967940e1e2b..35493e22270 100644 --- a/tests/integration/test_keeper_nodes_add/configs/enable_keeper_two_nodes_2.xml +++ b/tests/integration/test_keeper_nodes_add/configs/enable_keeper_two_nodes_2.xml @@ -1,5 +1,7 @@ + false + 9181 2 /var/lib/clickhouse/coordination/log diff --git a/tests/integration/test_keeper_zookeeper_converter/configs/keeper_config.xml b/tests/integration/test_keeper_zookeeper_converter/configs/keeper_config.xml index 9b50f2c6c41..38ef4295b01 100644 --- a/tests/integration/test_keeper_zookeeper_converter/configs/keeper_config.xml +++ b/tests/integration/test_keeper_zookeeper_converter/configs/keeper_config.xml @@ -1,5 +1,7 @@ + false + 9181 1 /var/lib/clickhouse/coordination/logs diff --git a/tests/integration/test_zookeeper_config/configs/zookeeper_config_root_a.xml b/tests/integration/test_zookeeper_config/configs/zookeeper_config_root_a.xml index d3c62862002..6c413378524 100644 --- a/tests/integration/test_zookeeper_config/configs/zookeeper_config_root_a.xml +++ b/tests/integration/test_zookeeper_config/configs/zookeeper_config_root_a.xml @@ -1,5 +1,5 @@ - + zoo1 2181 @@ -14,5 +14,5 @@ 3000 /root_a - + From f46970671d1586312e67d55a49ffc66e5955546f Mon Sep 17 00:00:00 2001 From: ltrk2 <107155950+ltrk2@users.noreply.github.com> Date: Thu, 9 Feb 2023 15:42:08 -0800 Subject: [PATCH 078/377] Implement tokenbf_v1 index utilization for hasTokenCaseInsensitive --- .../mergetree-family/mergetree.md | 52 +++++---- .../MergeTree/MergeTreeIndexFullText.cpp | 103 ++++++++---------- .../MergeTree/MergeTreeIndexFullText.h | 2 +- .../MergeTree/MergeTreeIndexInverted.cpp | 3 +- .../00990_hasToken_and_tokenbf.reference | 6 + .../00990_hasToken_and_tokenbf.sql | 25 ++++- 6 files changed, 104 insertions(+), 87 deletions(-) diff --git a/docs/en/engines/table-engines/mergetree-family/mergetree.md b/docs/en/engines/table-engines/mergetree-family/mergetree.md index 9fea158b100..004eaf64b0b 100644 --- a/docs/en/engines/table-engines/mergetree-family/mergetree.md +++ b/docs/en/engines/table-engines/mergetree-family/mergetree.md @@ -454,32 +454,36 @@ Conditions in the `WHERE` clause contains calls of the functions that operate wi Indexes of type `set` can be utilized by all functions. The other index types are supported as follows: -| Function (operator) / Index | primary key | minmax | ngrambf_v1 | tokenbf_v1 | bloom_filter | -|------------------------------------------------------------------------------------------------------------|-------------|--------|------------|------------|--------------| -| [equals (=, ==)](/docs/en/sql-reference/functions/comparison-functions.md/#function-equals) | ✔ | ✔ | ✔ | ✔ | ✔ | -| [notEquals(!=, <>)](/docs/en/sql-reference/functions/comparison-functions.md/#function-notequals) | ✔ | ✔ | ✔ | ✔ | ✔ | -| [like](/docs/en/sql-reference/functions/string-search-functions.md/#function-like) | ✔ | ✔ | ✔ | ✔ | ✗ | -| [notLike](/docs/en/sql-reference/functions/string-search-functions.md/#function-notlike) | ✔ | ✔ | ✔ | ✔ | ✗ | -| [startsWith](/docs/en/sql-reference/functions/string-functions.md/#startswith) | ✔ | ✔ | ✔ | ✔ | ✗ | -| [endsWith](/docs/en/sql-reference/functions/string-functions.md/#endswith) | ✗ | ✗ | ✔ | ✔ | ✗ | -| [multiSearchAny](/docs/en/sql-reference/functions/string-search-functions.md/#function-multisearchany) | ✗ | ✗ | ✔ | ✗ | ✗ | -| [in](/docs/en/sql-reference/functions/in-functions#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | -| [notIn](/docs/en/sql-reference/functions/in-functions#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | -| [less (<)](/docs/en/sql-reference/functions/comparison-functions.md/#function-less) | ✔ | ✔ | ✗ | ✗ | ✗ | -| [greater (>)](/docs/en/sql-reference/functions/comparison-functions.md/#function-greater) | ✔ | ✔ | ✗ | ✗ | ✗ | -| [lessOrEquals (<=)](/docs/en/sql-reference/functions/comparison-functions.md/#function-lessorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | -| [greaterOrEquals (>=)](/docs/en/sql-reference/functions/comparison-functions.md/#function-greaterorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | -| [empty](/docs/en/sql-reference/functions/array-functions#function-empty) | ✔ | ✔ | ✗ | ✗ | ✗ | -| [notEmpty](/docs/en/sql-reference/functions/array-functions#function-notempty) | ✔ | ✔ | ✗ | ✗ | ✗ | -| [has](/docs/en/sql-reference/functions/array-functions#function-has) | ✗ | ✗ | ✔ | ✔ | ✔ | -| [hasAny](/docs/en/sql-reference/functions/array-functions#function-hasAny) | ✗ | ✗ | ✗ | ✗ | ✔ | -| [hasAll](/docs/en/sql-reference/functions/array-functions#function-hasAll) | ✗ | ✗ | ✗ | ✗ | ✔ | -| hasToken | ✗ | ✗ | ✗ | ✔ | ✗ | -| hasTokenOrNull | ✗ | ✗ | ✗ | ✔ | ✗ | -| hasTokenCaseInsensitive | ✗ | ✗ | ✗ | ✔ | ✗ | -| hasTokenCaseInsensitiveOrNull | ✗ | ✗ | ✗ | ✔ | ✗ | +| Function (operator) / Index | primary key | minmax | ngrambf_v1 | tokenbf_v1 | bloom_filter | inverted | +|------------------------------------------------------------------------------------------------------------|-------------|--------|------------|------------|--------------|----------| +| [equals (=, ==)](/docs/en/sql-reference/functions/comparison-functions.md/#function-equals) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| [notEquals(!=, <>)](/docs/en/sql-reference/functions/comparison-functions.md/#function-notequals) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| [like](/docs/en/sql-reference/functions/string-search-functions.md/#function-like) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | +| [notLike](/docs/en/sql-reference/functions/string-search-functions.md/#function-notlike) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | +| [startsWith](/docs/en/sql-reference/functions/string-functions.md/#startswith) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | +| [endsWith](/docs/en/sql-reference/functions/string-functions.md/#endswith) | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | +| [multiSearchAny](/docs/en/sql-reference/functions/string-search-functions.md/#function-multisearchany) | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | +| [in](/docs/en/sql-reference/functions/in-functions#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| [notIn](/docs/en/sql-reference/functions/in-functions#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| [less (<)](/docs/en/sql-reference/functions/comparison-functions.md/#function-less) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [greater (>)](/docs/en/sql-reference/functions/comparison-functions.md/#function-greater) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [lessOrEquals (<=)](/docs/en/sql-reference/functions/comparison-functions.md/#function-lessorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [greaterOrEquals (>=)](/docs/en/sql-reference/functions/comparison-functions.md/#function-greaterorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [empty](/docs/en/sql-reference/functions/array-functions#function-empty) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [notEmpty](/docs/en/sql-reference/functions/array-functions#function-notempty) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [has](/docs/en/sql-reference/functions/array-functions#function-has) | ✗ | ✗ | ✔ | ✔ | ✔ | ✔ | +| [hasAny](/docs/en/sql-reference/functions/array-functions#function-hasAny) | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | +| [hasAll](/docs/en/sql-reference/functions/array-functions#function-hasAll) | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | +| hasToken | ✗ | ✗ | ✗ | ✔ | ✗ | ✔ | +| hasTokenOrNull | ✗ | ✗ | ✗ | ✔ | ✗ | ✔ | +| hasTokenCaseInsensitive (*) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | +| hasTokenCaseInsensitiveOrNull (*) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | Functions with a constant argument that is less than ngram size can’t be used by `ngrambf_v1` for query optimization. +(*) For `hasTokenCaseInsensitve` and `hasTokenCaseInsensitive` to be effective, the data skipping index of type `tokenbf_v1` must be created on lowercased data, for example: +``` +CREATE TABLE tab (id UInt64, s String, INDEX tok_bf_idx (s, lower(s)) TYPE tokenbf_v1(512, 3, 0) GRANULARITY 1) ... . ) ENGINE = MergeTree() +``` :::note Bloom filters can have false positive matches, so the `ngrambf_v1`, `tokenbf_v1`, and `bloom_filter` indexes can not be used for optimizing queries where the result of a function is expected to be false. diff --git a/src/Storages/MergeTree/MergeTreeIndexFullText.cpp b/src/Storages/MergeTree/MergeTreeIndexFullText.cpp index fa1bd36f863..06fddd51cb8 100644 --- a/src/Storages/MergeTree/MergeTreeIndexFullText.cpp +++ b/src/Storages/MergeTree/MergeTreeIndexFullText.cpp @@ -322,14 +322,10 @@ bool MergeTreeConditionFullText::mayBeTrueOnGranule(MergeTreeIndexGranulePtr idx return rpn_stack[0].can_be_true; } -bool MergeTreeConditionFullText::getKey(const std::string & key_column_name, size_t & key_column_num) +std::optional MergeTreeConditionFullText::getKeyIndex(const std::string & key_column_name) { - auto it = std::find(index_columns.begin(), index_columns.end(), key_column_name); - if (it == index_columns.end()) - return false; - - key_column_num = static_cast(it - index_columns.begin()); - return true; + const auto it = std::ranges::find(index_columns, key_column_name); + return it == index_columns.end() ? std::nullopt : std::make_optional(std::ranges::distance(index_columns.cbegin(), it)); } bool MergeTreeConditionFullText::extractAtomFromTree(const RPNBuilderTreeNode & node, RPNElement & out) @@ -389,7 +385,7 @@ bool MergeTreeConditionFullText::extractAtomFromTree(const RPNBuilderTreeNode & function_name == "mapContains" || function_name == "like" || function_name == "notLike" || - function_name == "hasToken" || + function_name.starts_with("hasToken") || function_name == "startsWith" || function_name == "endsWith" || function_name == "multiSearchAny") @@ -426,10 +422,9 @@ bool MergeTreeConditionFullText::traverseTreeEquals( Field const_value = value_field; - auto column_name = key_node.getColumnName(); - size_t key_column_num = 0; - bool key_exists = getKey(column_name, key_column_num); - bool map_key_exists = getKey(fmt::format("mapKeys({})", column_name), key_column_num); + const auto column_name = key_node.getColumnName(); + auto key_index = getKeyIndex(column_name); + const auto map_key_index = getKeyIndex(fmt::format("mapKeys({})", column_name)); if (key_node.isFunction()) { @@ -450,24 +445,14 @@ bool MergeTreeConditionFullText::traverseTreeEquals( auto first_argument = key_function_node.getArgumentAt(0); const auto map_column_name = first_argument.getColumnName(); - - size_t map_keys_key_column_num = 0; - auto map_keys_index_column_name = fmt::format("mapKeys({})", map_column_name); - bool map_keys_exists = getKey(map_keys_index_column_name, map_keys_key_column_num); - - size_t map_values_key_column_num = 0; - auto map_values_index_column_name = fmt::format("mapValues({})", map_column_name); - bool map_values_exists = getKey(map_values_index_column_name, map_values_key_column_num); - - if (map_keys_exists) + if (const auto map_keys_index = getKeyIndex(fmt::format("mapKeys({})", map_column_name))) { auto second_argument = key_function_node.getArgumentAt(1); DataTypePtr const_type; if (second_argument.tryGetConstant(const_value, const_type)) { - key_column_num = map_keys_key_column_num; - key_exists = true; + key_index = map_keys_index; auto const_data_type = WhichDataType(const_type); if (!const_data_type.isStringOrFixedString() && !const_data_type.isArray()) @@ -478,10 +463,9 @@ bool MergeTreeConditionFullText::traverseTreeEquals( return false; } } - else if (map_values_exists) + else if (const auto map_values_exists = getKeyIndex(fmt::format("mapValues({})", map_column_name))) { - key_column_num = map_values_key_column_num; - key_exists = true; + key_index = map_values_exists; } else { @@ -490,12 +474,29 @@ bool MergeTreeConditionFullText::traverseTreeEquals( } } - if (!key_exists && !map_key_exists) + const auto lowercase_key_index = getKeyIndex(fmt::format("lower({})", column_name)); + const auto is_has_token_case_insensitive = function_name.starts_with("hasTokenCaseInsensitive"); + if (const auto is_case_insensitive_scenario = is_has_token_case_insensitive && lowercase_key_index; + function_name.starts_with("hasToken") && ((!is_has_token_case_insensitive && key_index) || is_case_insensitive_scenario)) + { + out.key_column = is_case_insensitive_scenario ? *lowercase_key_index : *key_index; + out.function = RPNElement::FUNCTION_EQUALS; + out.bloom_filter = std::make_unique(params); + + auto value = const_value.get(); + if (is_case_insensitive_scenario) + std::ranges::transform(value, value.begin(), [](const auto & c) { return static_cast(std::tolower(c)); }); + + token_extractor->stringToBloomFilter(value.data(), value.size(), *out.bloom_filter); + return true; + } + + if (!key_index && !map_key_index) return false; - if (map_key_exists && (function_name == "has" || function_name == "mapContains")) + if (map_key_index && (function_name == "has" || function_name == "mapContains")) { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_HAS; out.bloom_filter = std::make_unique(params); auto & value = const_value.get(); @@ -504,7 +505,7 @@ bool MergeTreeConditionFullText::traverseTreeEquals( } else if (function_name == "has") { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_HAS; out.bloom_filter = std::make_unique(params); auto & value = const_value.get(); @@ -514,7 +515,7 @@ bool MergeTreeConditionFullText::traverseTreeEquals( if (function_name == "notEquals") { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_NOT_EQUALS; out.bloom_filter = std::make_unique(params); const auto & value = const_value.get(); @@ -523,7 +524,7 @@ bool MergeTreeConditionFullText::traverseTreeEquals( } else if (function_name == "equals") { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_EQUALS; out.bloom_filter = std::make_unique(params); const auto & value = const_value.get(); @@ -532,7 +533,7 @@ bool MergeTreeConditionFullText::traverseTreeEquals( } else if (function_name == "like") { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_EQUALS; out.bloom_filter = std::make_unique(params); const auto & value = const_value.get(); @@ -541,25 +542,16 @@ bool MergeTreeConditionFullText::traverseTreeEquals( } else if (function_name == "notLike") { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_NOT_EQUALS; out.bloom_filter = std::make_unique(params); const auto & value = const_value.get(); token_extractor->stringLikeToBloomFilter(value.data(), value.size(), *out.bloom_filter); return true; } - else if (function_name == "hasToken") - { - out.key_column = key_column_num; - out.function = RPNElement::FUNCTION_EQUALS; - out.bloom_filter = std::make_unique(params); - const auto & value = const_value.get(); - token_extractor->stringToBloomFilter(value.data(), value.size(), *out.bloom_filter); - return true; - } else if (function_name == "startsWith") { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_EQUALS; out.bloom_filter = std::make_unique(params); const auto & value = const_value.get(); @@ -568,7 +560,7 @@ bool MergeTreeConditionFullText::traverseTreeEquals( } else if (function_name == "endsWith") { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_EQUALS; out.bloom_filter = std::make_unique(params); const auto & value = const_value.get(); @@ -577,7 +569,7 @@ bool MergeTreeConditionFullText::traverseTreeEquals( } else if (function_name == "multiSearchAny") { - out.key_column = key_column_num; + out.key_column = *key_index; out.function = RPNElement::FUNCTION_MULTI_SEARCH; /// 2d vector is not needed here but is used because already exists for FUNCTION_IN @@ -616,22 +608,17 @@ bool MergeTreeConditionFullText::tryPrepareSetBloomFilter( for (size_t i = 0; i < left_argument_function_node_arguments_size; ++i) { - size_t key = 0; - if (getKey(left_argument_function_node.getArgumentAt(i).getColumnName(), key)) + if (const auto key = getKeyIndex(left_argument_function_node.getArgumentAt(i).getColumnName())) { - key_tuple_mapping.emplace_back(i, key); - data_types.push_back(index_data_types[key]); + key_tuple_mapping.emplace_back(i, *key); + data_types.push_back(index_data_types[*key]); } } } - else + else if (const auto key = getKeyIndex(left_argument.getColumnName())) { - size_t key = 0; - if (getKey(left_argument.getColumnName(), key)) - { - key_tuple_mapping.emplace_back(0, key); - data_types.push_back(index_data_types[key]); - } + key_tuple_mapping.emplace_back(0, *key); + data_types.push_back(index_data_types[*key]); } if (key_tuple_mapping.empty()) diff --git a/src/Storages/MergeTree/MergeTreeIndexFullText.h b/src/Storages/MergeTree/MergeTreeIndexFullText.h index ad487816aef..c9b24f7086b 100644 --- a/src/Storages/MergeTree/MergeTreeIndexFullText.h +++ b/src/Storages/MergeTree/MergeTreeIndexFullText.h @@ -131,7 +131,7 @@ private: const Field & value_field, RPNElement & out); - bool getKey(const std::string & key_column_name, size_t & key_column_num); + std::optional getKeyIndex(const std::string & key_column_name); bool tryPrepareSetBloomFilter(const RPNBuilderTreeNode & left_argument, const RPNBuilderTreeNode & right_argument, RPNElement & out); static bool createFunctionEqualsCondition( diff --git a/src/Storages/MergeTree/MergeTreeIndexInverted.cpp b/src/Storages/MergeTree/MergeTreeIndexInverted.cpp index 8e8409f3868..baa11368c8b 100644 --- a/src/Storages/MergeTree/MergeTreeIndexInverted.cpp +++ b/src/Storages/MergeTree/MergeTreeIndexInverted.cpp @@ -426,6 +426,7 @@ bool MergeTreeConditionInverted::traverseAtomAST(const RPNBuilderTreeNode & node function_name == "like" || function_name == "notLike" || function_name == "hasToken" || + function_name == "hasTokenOrNull" || function_name == "startsWith" || function_name == "endsWith" || function_name == "multiSearchAny") @@ -568,7 +569,7 @@ bool MergeTreeConditionInverted::traverseASTEquals( token_extractor->stringLikeToGinFilter(value.data(), value.size(), *out.gin_filter); return true; } - else if (function_name == "hasToken") + else if (function_name == "hasToken" || function_name == "hasTokenOrNull") { out.key_column = key_column_num; out.function = RPNElement::FUNCTION_EQUALS; diff --git a/tests/queries/0_stateless/00990_hasToken_and_tokenbf.reference b/tests/queries/0_stateless/00990_hasToken_and_tokenbf.reference index 4b3beccf5f1..c462030edbf 100644 --- a/tests/queries/0_stateless/00990_hasToken_and_tokenbf.reference +++ b/tests/queries/0_stateless/00990_hasToken_and_tokenbf.reference @@ -2,6 +2,12 @@ 0 2007 2007 +0 +2007 +2007 +2007 +2007 +2007 2007 0 2007 diff --git a/tests/queries/0_stateless/00990_hasToken_and_tokenbf.sql b/tests/queries/0_stateless/00990_hasToken_and_tokenbf.sql index 41676905771..1361ce0f3c3 100644 --- a/tests/queries/0_stateless/00990_hasToken_and_tokenbf.sql +++ b/tests/queries/0_stateless/00990_hasToken_and_tokenbf.sql @@ -12,21 +12,40 @@ insert into bloom_filter select number+2000, 'abc,def,zzz' from numbers(8); insert into bloom_filter select number+3000, 'yyy,uuu' from numbers(1024); insert into bloom_filter select number+3000, 'abcdefzzz' from numbers(1024); +drop table if exists bloom_filter2; +create table bloom_filter2 +( + id UInt64, + s String, + index tok_bf3 (s, lower(s)) type tokenbf_v1(512, 3, 0) GRANULARITY 1 +) engine = MergeTree() order by id settings index_granularity = 8; + +insert into bloom_filter2 select number, 'yyy,uuu' from numbers(1024); +insert into bloom_filter2 select number+2000, 'ABC,def,zzz' from numbers(8); +insert into bloom_filter2 select number+3000, 'yyy,uuu' from numbers(1024); +insert into bloom_filter2 select number+3000, 'abcdefzzz' from numbers(1024); + SELECT max(id) FROM bloom_filter WHERE hasToken(s, 'abc,def,zzz'); -- { serverError BAD_ARGUMENTS } SELECT max(id) FROM bloom_filter WHERE hasTokenCaseInsensitive(s, 'abc,def,zzz'); -- { serverError BAD_ARGUMENTS } SELECT max(id) FROM bloom_filter WHERE hasTokenOrNull(s, 'abc,def,zzz'); SELECT max(id) FROM bloom_filter WHERE hasTokenCaseInsensitiveOrNull(s, 'abc,def,zzz'); -select max(id) from bloom_filter where hasTokenCaseInsensitive(s, 'ABC'); -select max(id) from bloom_filter where hasTokenCaseInsensitive(s, 'zZz'); - set max_rows_to_read = 16; SELECT max(id) FROM bloom_filter WHERE hasToken(s, 'abc'); +SELECT max(id) FROM bloom_filter WHERE hasTokenOrNull(s, 'abc'); SELECT max(id) FROM bloom_filter WHERE hasToken(s, 'ABC'); +select max(id) from bloom_filter where hasTokenCaseInsensitive(s, 'ABC'); +select max(id) from bloom_filter where hasTokenCaseInsensitiveOrNull(s, 'ABC'); SELECT max(id) FROM bloom_filter WHERE hasToken(s, 'def'); SELECT max(id) FROM bloom_filter WHERE hasToken(s, 'zzz'); +select max(id) from bloom_filter where hasTokenCaseInsensitive(s, 'zZz'); + +select max(id) from bloom_filter2 where hasToken(s, 'ABC'); +select max(id) from bloom_filter2 where hasToken(s, 'abc'); +select max(id) from bloom_filter2 where hasTokenCaseInsensitive(s, 'abc'); +select max(id) from bloom_filter2 where hasTokenCaseInsensitive(s, 'ABC'); -- invert result -- this does not work as expected, reading more rows that it should From 3f2afd6f6e543ebf7d5a23ddccd1875efeb5e01f Mon Sep 17 00:00:00 2001 From: zvonand Date: Fri, 24 Mar 2023 13:35:09 +0100 Subject: [PATCH 079/377] fix --- src/Functions/FunctionFormatDecimal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Functions/FunctionFormatDecimal.h b/src/Functions/FunctionFormatDecimal.h index b85fd0ba55b..638019962f9 100644 --- a/src/Functions/FunctionFormatDecimal.h +++ b/src/Functions/FunctionFormatDecimal.h @@ -21,6 +21,7 @@ namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_COLUMN; + extern const int CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER; } struct Processor From 28ca2de86d3982b1b16aa292d5df6fe75ecda7d3 Mon Sep 17 00:00:00 2001 From: serxa Date: Fri, 24 Mar 2023 14:58:50 +0000 Subject: [PATCH 080/377] fix --- src/Common/ThreadStatus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/ThreadStatus.cpp b/src/Common/ThreadStatus.cpp index 91c695216a8..16ce73cda20 100644 --- a/src/Common/ThreadStatus.cpp +++ b/src/Common/ThreadStatus.cpp @@ -223,7 +223,7 @@ void ThreadStatus::updatePerformanceCountersIfNeeded() UInt64 total_elapsed_microseconds = stopwatch.elapsedMicroseconds(); if (last_performance_counters_update_time + performance_counters_update_period_microseconds < total_elapsed_microseconds) { - CurrentThread::updatePerformanceCounters(); + updatePerformanceCounters(); last_performance_counters_update_time = total_elapsed_microseconds; } } From 98c9b1f75cc9452bedc129c3e41fbe794f42a0b1 Mon Sep 17 00:00:00 2001 From: robot-clickhouse Date: Fri, 24 Mar 2023 15:09:27 +0000 Subject: [PATCH 081/377] Automatic style fix --- docker/test/performance-comparison/perf.py | 2 ++ docker/test/performance-comparison/report.py | 1 - tests/ci/clickhouse_helper.py | 1 - tests/ci/docker_images_check.py | 1 - tests/ci/get_previous_release_tag.py | 1 - tests/ci/report.py | 2 +- tests/integration/helpers/cluster.py | 3 +-- tests/integration/helpers/network.py | 2 -- .../pytest_xdist_logging_to_separate_files.py | 1 + .../test_detach_part_wrong_partition_id.py | 1 - .../test_cluster_copier/test_three_nodes.py | 1 - .../test_cluster_copier/test_two_nodes.py | 1 - tests/integration/test_composable_protocols/test.py | 1 - .../test_create_query_constraints/test.py | 2 -- .../common.py | 1 - tests/integration/test_disks_app_func/test.py | 1 - .../test_distributed_ddl_parallel/test.py | 1 + tests/integration/test_fetch_memory_usage/test.py | 1 - .../scripts/stress_test.py | 1 - tests/integration/test_jbod_balancer/test.py | 1 - .../test_keeper_and_access_storage/test.py | 1 + tests/integration/test_keeper_back_to_back/test.py | 2 +- tests/integration/test_keeper_persistent_log/test.py | 1 - .../test_keeper_zookeeper_converter/test.py | 1 - tests/integration/test_merge_tree_load_parts/test.py | 6 +++--- .../s3_endpoint/endpoint.py | 1 - .../test_merge_tree_settings_constraints/test.py | 1 - .../test_old_parts_finally_removed/test.py | 1 - tests/integration/test_partition/test.py | 4 +++- tests/integration/test_password_constraints/test.py | 1 - tests/integration/test_read_only_table/test.py | 1 - .../test_reload_auxiliary_zookeepers/test.py | 1 - .../s3_endpoint/endpoint.py | 1 + tests/integration/test_s3_with_proxy/test.py | 1 + .../integration/test_ssl_cert_authentication/test.py | 1 - tests/integration/test_storage_kafka/kafka_pb2.py | 1 - .../test_storage_kafka/message_with_repeated_pb2.py | 1 - tests/integration/test_storage_kafka/social_pb2.py | 1 - tests/integration/test_storage_kafka/test.py | 12 ++---------- tests/integration/test_storage_nats/nats_pb2.py | 1 - .../test_storage_postgresql_replica/test.py | 1 - .../test_storage_rabbitmq/rabbitmq_pb2.py | 1 - tests/integration/test_storage_rabbitmq/test.py | 3 --- tests/integration/test_storage_s3/test.py | 1 + .../test_storage_s3/test_invalid_env_credentials.py | 1 + tests/integration/test_system_merges/test.py | 1 - tests/integration/test_ttl_move/test.py | 2 +- tests/integration/test_zero_copy_fetch/test.py | 1 - utils/changelog-simple/format-changelog.py | 1 + utils/keeper-overload/keeper-overload.py | 2 +- 50 files changed, 23 insertions(+), 57 deletions(-) diff --git a/docker/test/performance-comparison/perf.py b/docker/test/performance-comparison/perf.py index 65bf49c2914..7a4e6386d0d 100755 --- a/docker/test/performance-comparison/perf.py +++ b/docker/test/performance-comparison/perf.py @@ -26,6 +26,7 @@ logging.basicConfig( total_start_seconds = time.perf_counter() stage_start_seconds = total_start_seconds + # Thread executor that does not hides exception that happens during function # execution, and rethrows it after join() class SafeThread(Thread): @@ -158,6 +159,7 @@ for e in subst_elems: available_parameters[name] = values + # Takes parallel lists of templates, substitutes them with all combos of # parameters. The set of parameters is determined based on the first list. # Note: keep the order of queries -- sometimes we have DROP IF EXISTS diff --git a/docker/test/performance-comparison/report.py b/docker/test/performance-comparison/report.py index 782cf29863c..214f2d550b4 100755 --- a/docker/test/performance-comparison/report.py +++ b/docker/test/performance-comparison/report.py @@ -670,7 +670,6 @@ if args.report == "main": ) elif args.report == "all-queries": - print((header_template.format())) add_tested_commits() diff --git a/tests/ci/clickhouse_helper.py b/tests/ci/clickhouse_helper.py index d60a9e6afd1..64b64896f66 100644 --- a/tests/ci/clickhouse_helper.py +++ b/tests/ci/clickhouse_helper.py @@ -141,7 +141,6 @@ def prepare_tests_results_for_clickhouse( report_url: str, check_name: str, ) -> List[dict]: - pull_request_url = "https://github.com/ClickHouse/ClickHouse/commits/master" base_ref = "master" head_ref = "master" diff --git a/tests/ci/docker_images_check.py b/tests/ci/docker_images_check.py index 192d216614e..f2b1105b3b0 100644 --- a/tests/ci/docker_images_check.py +++ b/tests/ci/docker_images_check.py @@ -96,7 +96,6 @@ def get_images_dict(repo_path: str, image_file_path: str) -> ImagesDict: def get_changed_docker_images( pr_info: PRInfo, images_dict: ImagesDict ) -> Set[DockerImage]: - if not images_dict: return set() diff --git a/tests/ci/get_previous_release_tag.py b/tests/ci/get_previous_release_tag.py index c6fe6cd5fb5..c2d279f7fec 100755 --- a/tests/ci/get_previous_release_tag.py +++ b/tests/ci/get_previous_release_tag.py @@ -51,7 +51,6 @@ def find_previous_release( for release in releases: if release.version < server_version: - # Check if the artifact exists on GitHub. # It can be not true for a short period of time # after creating a tag for a new release before uploading the packages. diff --git a/tests/ci/report.py b/tests/ci/report.py index 947fb33d905..ddee035d26f 100644 --- a/tests/ci/report.py +++ b/tests/ci/report.py @@ -473,7 +473,7 @@ def create_build_html_report( commit_url: str, ) -> str: rows = "" - for (build_result, build_log_url, artifact_urls) in zip( + for build_result, build_log_url, artifact_urls in zip( build_results, build_logs_urls, artifact_urls_list ): row = "" diff --git a/tests/integration/helpers/cluster.py b/tests/integration/helpers/cluster.py index dc5ada81995..a9a996e0a5f 100644 --- a/tests/integration/helpers/cluster.py +++ b/tests/integration/helpers/cluster.py @@ -63,6 +63,7 @@ DEFAULT_ENV_NAME = ".env" SANITIZER_SIGN = "==================" + # to create docker-compose env file def _create_env_file(path, variables): logging.debug(f"Env {variables} stored in {path}") @@ -1454,7 +1455,6 @@ class ClickHouseCluster: config_root_name="clickhouse", extra_configs=[], ) -> "ClickHouseInstance": - """Add an instance to the cluster. name - the name of the instance directory and the value of the 'instance' macro in ClickHouse. @@ -3089,7 +3089,6 @@ class ClickHouseInstance: config_root_name="clickhouse", extra_configs=[], ): - self.name = name self.base_cmd = cluster.base_cmd self.docker_id = cluster.get_instance_docker_id(self.name) diff --git a/tests/integration/helpers/network.py b/tests/integration/helpers/network.py index e408c9beec1..471aa2bdc2e 100644 --- a/tests/integration/helpers/network.py +++ b/tests/integration/helpers/network.py @@ -216,7 +216,6 @@ class _NetworkManager: container_exit_timeout=60, docker_api_version=os.environ.get("DOCKER_API_VERSION"), ): - self.container_expire_timeout = container_expire_timeout self.container_exit_timeout = container_exit_timeout @@ -232,7 +231,6 @@ class _NetworkManager: def _ensure_container(self): if self._container is None or self._container_expire_time <= time.time(): - for i in range(5): if self._container is not None: try: diff --git a/tests/integration/helpers/pytest_xdist_logging_to_separate_files.py b/tests/integration/helpers/pytest_xdist_logging_to_separate_files.py index d424ad58fa4..370aa23a014 100644 --- a/tests/integration/helpers/pytest_xdist_logging_to_separate_files.py +++ b/tests/integration/helpers/pytest_xdist_logging_to_separate_files.py @@ -1,6 +1,7 @@ import logging import os.path + # Makes the parallel workers of pytest-xdist to log to separate files. # Without this function all workers will log to the same log file # and mix everything together making it much more difficult for troubleshooting. diff --git a/tests/integration/test_backward_compatibility/test_detach_part_wrong_partition_id.py b/tests/integration/test_backward_compatibility/test_detach_part_wrong_partition_id.py index 02fccfae4e5..a6f7a8653da 100644 --- a/tests/integration/test_backward_compatibility/test_detach_part_wrong_partition_id.py +++ b/tests/integration/test_backward_compatibility/test_detach_part_wrong_partition_id.py @@ -24,7 +24,6 @@ def start_cluster(): def test_detach_part_wrong_partition_id(start_cluster): - # Here we create table with partition by UUID. node_21_6.query( "create table tab (id UUID, value UInt32) engine = MergeTree PARTITION BY (id) order by tuple()" diff --git a/tests/integration/test_cluster_copier/test_three_nodes.py b/tests/integration/test_cluster_copier/test_three_nodes.py index 31d6c0448f4..e7d07757adb 100644 --- a/tests/integration/test_cluster_copier/test_three_nodes.py +++ b/tests/integration/test_cluster_copier/test_three_nodes.py @@ -19,7 +19,6 @@ cluster = ClickHouseCluster(__file__) def started_cluster(): global cluster try: - for name in ["first", "second", "third"]: cluster.add_instance( name, diff --git a/tests/integration/test_cluster_copier/test_two_nodes.py b/tests/integration/test_cluster_copier/test_two_nodes.py index 10ab7d03b00..2b6fcf6cac2 100644 --- a/tests/integration/test_cluster_copier/test_two_nodes.py +++ b/tests/integration/test_cluster_copier/test_two_nodes.py @@ -19,7 +19,6 @@ cluster = ClickHouseCluster(__file__) def started_cluster(): global cluster try: - for name in ["first_of_two", "second_of_two"]: instance = cluster.add_instance( name, diff --git a/tests/integration/test_composable_protocols/test.py b/tests/integration/test_composable_protocols/test.py index bc87fea5296..df74cfffa54 100644 --- a/tests/integration/test_composable_protocols/test.py +++ b/tests/integration/test_composable_protocols/test.py @@ -63,7 +63,6 @@ def netcat(hostname, port, content): def test_connections(): - client = Client(server.ip_address, 9000, command=cluster.client_bin_path) assert client.query("SELECT 1") == "1\n" diff --git a/tests/integration/test_create_query_constraints/test.py b/tests/integration/test_create_query_constraints/test.py index 8df043fd24b..33c41b4f161 100644 --- a/tests/integration/test_create_query_constraints/test.py +++ b/tests/integration/test_create_query_constraints/test.py @@ -25,7 +25,6 @@ def start_cluster(): def test_create_query_const_constraints(): - instance.query("CREATE USER u_const SETTINGS max_threads = 1 CONST") instance.query("GRANT ALL ON *.* TO u_const") @@ -57,7 +56,6 @@ def test_create_query_const_constraints(): def test_create_query_minmax_constraints(): - instance.query("CREATE USER u_minmax SETTINGS max_threads = 4 MIN 2 MAX 6") instance.query("GRANT ALL ON *.* TO u_minmax") diff --git a/tests/integration/test_dictionaries_all_layouts_separate_sources/common.py b/tests/integration/test_dictionaries_all_layouts_separate_sources/common.py index b38e81b0227..01addae2542 100644 --- a/tests/integration/test_dictionaries_all_layouts_separate_sources/common.py +++ b/tests/integration/test_dictionaries_all_layouts_separate_sources/common.py @@ -348,7 +348,6 @@ class RangedLayoutTester(BaseLayoutTester): self.layouts = LAYOUTS_RANGED def execute(self, layout_name, node): - if layout_name not in self.layout_to_dictionary: raise RuntimeError("Source doesn't support layout: {}".format(layout_name)) diff --git a/tests/integration/test_disks_app_func/test.py b/tests/integration/test_disks_app_func/test.py index 027ef8feed0..2428c53854e 100644 --- a/tests/integration/test_disks_app_func/test.py +++ b/tests/integration/test_disks_app_func/test.py @@ -7,7 +7,6 @@ import pytest def started_cluster(): global cluster try: - cluster = ClickHouseCluster(__file__) cluster.add_instance( "disks_app_test", main_configs=["config.xml"], with_minio=True diff --git a/tests/integration/test_distributed_ddl_parallel/test.py b/tests/integration/test_distributed_ddl_parallel/test.py index 6ebfe472e09..eb98dd3e230 100644 --- a/tests/integration/test_distributed_ddl_parallel/test.py +++ b/tests/integration/test_distributed_ddl_parallel/test.py @@ -10,6 +10,7 @@ from helpers.cluster import ClickHouseCluster cluster = ClickHouseCluster(__file__) + # By default the exceptions that was throwed in threads will be ignored # (they will not mark the test as failed, only printed to stderr). # diff --git a/tests/integration/test_fetch_memory_usage/test.py b/tests/integration/test_fetch_memory_usage/test.py index a4371140150..7591cc0e8a9 100644 --- a/tests/integration/test_fetch_memory_usage/test.py +++ b/tests/integration/test_fetch_memory_usage/test.py @@ -18,7 +18,6 @@ def started_cluster(): def test_huge_column(started_cluster): - if ( node.is_built_with_thread_sanitizer() or node.is_built_with_memory_sanitizer() diff --git a/tests/integration/test_host_regexp_multiple_ptr_records_concurrent/scripts/stress_test.py b/tests/integration/test_host_regexp_multiple_ptr_records_concurrent/scripts/stress_test.py index b8bafb3d0c1..fe69d72c1c7 100644 --- a/tests/integration/test_host_regexp_multiple_ptr_records_concurrent/scripts/stress_test.py +++ b/tests/integration/test_host_regexp_multiple_ptr_records_concurrent/scripts/stress_test.py @@ -13,7 +13,6 @@ number_of_iterations = 100 def perform_request(): - buffer = BytesIO() crl = pycurl.Curl() crl.setopt(pycurl.INTERFACE, client_ip) diff --git a/tests/integration/test_jbod_balancer/test.py b/tests/integration/test_jbod_balancer/test.py index e746698611a..df34a075d5a 100644 --- a/tests/integration/test_jbod_balancer/test.py +++ b/tests/integration/test_jbod_balancer/test.py @@ -45,7 +45,6 @@ def start_cluster(): def check_balance(node, table): - partitions = node.query( """ WITH diff --git a/tests/integration/test_keeper_and_access_storage/test.py b/tests/integration/test_keeper_and_access_storage/test.py index 6ec307f7082..0314825b6b7 100644 --- a/tests/integration/test_keeper_and_access_storage/test.py +++ b/tests/integration/test_keeper_and_access_storage/test.py @@ -10,6 +10,7 @@ node1 = cluster.add_instance( "node1", main_configs=["configs/keeper.xml"], stay_alive=True ) + # test that server is able to start @pytest.fixture(scope="module") def started_cluster(): diff --git a/tests/integration/test_keeper_back_to_back/test.py b/tests/integration/test_keeper_back_to_back/test.py index 73fface02b4..b737ac284d2 100644 --- a/tests/integration/test_keeper_back_to_back/test.py +++ b/tests/integration/test_keeper_back_to_back/test.py @@ -546,7 +546,6 @@ def test_random_requests(started_cluster): def test_end_of_session(started_cluster): - fake_zk1 = None fake_zk2 = None genuine_zk1 = None @@ -685,6 +684,7 @@ def test_concurrent_watches(started_cluster): nonlocal watches_created nonlocal all_paths_created fake_zk.ensure_path(global_path + "/" + str(i)) + # new function each time def dumb_watch(event): nonlocal dumb_watch_triggered_counter diff --git a/tests/integration/test_keeper_persistent_log/test.py b/tests/integration/test_keeper_persistent_log/test.py index 70cc14fe26d..4164ffb33d3 100644 --- a/tests/integration/test_keeper_persistent_log/test.py +++ b/tests/integration/test_keeper_persistent_log/test.py @@ -163,7 +163,6 @@ def test_state_duplicate_restart(started_cluster): # http://zookeeper-user.578899.n2.nabble.com/Why-are-ephemeral-nodes-written-to-disk-tp7583403p7583418.html def test_ephemeral_after_restart(started_cluster): - try: node_zk = None node_zk2 = None diff --git a/tests/integration/test_keeper_zookeeper_converter/test.py b/tests/integration/test_keeper_zookeeper_converter/test.py index 063421bf922..de5a9416119 100644 --- a/tests/integration/test_keeper_zookeeper_converter/test.py +++ b/tests/integration/test_keeper_zookeeper_converter/test.py @@ -114,7 +114,6 @@ def start_clickhouse(): def copy_zookeeper_data(make_zk_snapshots): - if make_zk_snapshots: # force zookeeper to create snapshot generate_zk_snapshot() else: diff --git a/tests/integration/test_merge_tree_load_parts/test.py b/tests/integration/test_merge_tree_load_parts/test.py index 777b6f14fc6..dfbe00c8e28 100644 --- a/tests/integration/test_merge_tree_load_parts/test.py +++ b/tests/integration/test_merge_tree_load_parts/test.py @@ -148,17 +148,17 @@ def test_merge_tree_load_parts_corrupted(started_cluster): node1.query("SYSTEM WAIT LOADING PARTS mt_load_parts_2") def check_parts_loading(node, partition, loaded, failed, skipped): - for (min_block, max_block) in loaded: + for min_block, max_block in loaded: part_name = f"{partition}_{min_block}_{max_block}" assert node.contains_in_log(f"Loading Active part {part_name}") assert node.contains_in_log(f"Finished loading Active part {part_name}") - for (min_block, max_block) in failed: + for min_block, max_block in failed: part_name = f"{partition}_{min_block}_{max_block}" assert node.contains_in_log(f"Loading Active part {part_name}") assert not node.contains_in_log(f"Finished loading Active part {part_name}") - for (min_block, max_block) in skipped: + for min_block, max_block in skipped: part_name = f"{partition}_{min_block}_{max_block}" assert not node.contains_in_log(f"Loading Active part {part_name}") assert not node.contains_in_log(f"Finished loading Active part {part_name}") diff --git a/tests/integration/test_merge_tree_s3_failover/s3_endpoint/endpoint.py b/tests/integration/test_merge_tree_s3_failover/s3_endpoint/endpoint.py index b6567dfebc5..4613fdb850b 100644 --- a/tests/integration/test_merge_tree_s3_failover/s3_endpoint/endpoint.py +++ b/tests/integration/test_merge_tree_s3_failover/s3_endpoint/endpoint.py @@ -42,7 +42,6 @@ def delete(_bucket): @route("/<_bucket>/<_path:path>", ["GET", "POST", "PUT", "DELETE"]) def server(_bucket, _path): - # It's delete query for failed part if _path.endswith("delete"): response.set_header("Location", "http://minio1:9001/" + _bucket + "/" + _path) diff --git a/tests/integration/test_merge_tree_settings_constraints/test.py b/tests/integration/test_merge_tree_settings_constraints/test.py index 0bb0179108d..be6e2a31873 100644 --- a/tests/integration/test_merge_tree_settings_constraints/test.py +++ b/tests/integration/test_merge_tree_settings_constraints/test.py @@ -20,7 +20,6 @@ def start_cluster(): def test_merge_tree_settings_constraints(): - assert "Setting storage_policy should not be changed" in instance.query_and_get_error( f"CREATE TABLE wrong_table (number Int64) engine = MergeTree() ORDER BY number SETTINGS storage_policy = 'secret_policy'" ) diff --git a/tests/integration/test_old_parts_finally_removed/test.py b/tests/integration/test_old_parts_finally_removed/test.py index 108b72c5ccd..5347d433419 100644 --- a/tests/integration/test_old_parts_finally_removed/test.py +++ b/tests/integration/test_old_parts_finally_removed/test.py @@ -63,7 +63,6 @@ def test_part_finally_removed(started_cluster): ) for i in range(60): - if ( node1.query( "SELECT count() from system.parts WHERE table = 'drop_outdated_part'" diff --git a/tests/integration/test_partition/test.py b/tests/integration/test_partition/test.py index ae4393fc6f6..a34141c6189 100644 --- a/tests/integration/test_partition/test.py +++ b/tests/integration/test_partition/test.py @@ -528,7 +528,9 @@ def test_make_clone_in_detached(started_cluster): ["cp", "-r", path + "all_0_0_0", path + "detached/broken_all_0_0_0"] ) assert_eq_with_retry(instance, "select * from clone_in_detached", "\n") - assert ["broken_all_0_0_0",] == sorted( + assert [ + "broken_all_0_0_0", + ] == sorted( instance.exec_in_container(["ls", path + "detached/"]).strip().split("\n") ) diff --git a/tests/integration/test_password_constraints/test.py b/tests/integration/test_password_constraints/test.py index e3628861b28..9cdff51caa1 100644 --- a/tests/integration/test_password_constraints/test.py +++ b/tests/integration/test_password_constraints/test.py @@ -17,7 +17,6 @@ def start_cluster(): def test_complexity_rules(start_cluster): - error_message = "DB::Exception: Invalid password. The password should: be at least 12 characters long, contain at least 1 numeric character, contain at least 1 lowercase character, contain at least 1 uppercase character, contain at least 1 special character" assert error_message in node.query_and_get_error( "CREATE USER u_1 IDENTIFIED WITH plaintext_password BY ''" diff --git a/tests/integration/test_read_only_table/test.py b/tests/integration/test_read_only_table/test.py index 914c6a99508..df084f9dbbd 100644 --- a/tests/integration/test_read_only_table/test.py +++ b/tests/integration/test_read_only_table/test.py @@ -49,7 +49,6 @@ def start_cluster(): def test_restart_zookeeper(start_cluster): - for table_id in range(NUM_TABLES): node1.query( f"INSERT INTO test_table_{table_id} VALUES (1), (2), (3), (4), (5);" diff --git a/tests/integration/test_reload_auxiliary_zookeepers/test.py b/tests/integration/test_reload_auxiliary_zookeepers/test.py index bb1455333fc..476c5dee99e 100644 --- a/tests/integration/test_reload_auxiliary_zookeepers/test.py +++ b/tests/integration/test_reload_auxiliary_zookeepers/test.py @@ -20,7 +20,6 @@ def start_cluster(): def test_reload_auxiliary_zookeepers(start_cluster): - node.query( "CREATE TABLE simple (date Date, id UInt32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/0/simple', 'node') ORDER BY tuple() PARTITION BY date;" ) diff --git a/tests/integration/test_s3_aws_sdk_has_slightly_unreliable_behaviour/s3_endpoint/endpoint.py b/tests/integration/test_s3_aws_sdk_has_slightly_unreliable_behaviour/s3_endpoint/endpoint.py index d6a732cc681..1d33ca02f86 100644 --- a/tests/integration/test_s3_aws_sdk_has_slightly_unreliable_behaviour/s3_endpoint/endpoint.py +++ b/tests/integration/test_s3_aws_sdk_has_slightly_unreliable_behaviour/s3_endpoint/endpoint.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from bottle import request, route, run, response + # Handle for MultipleObjectsDelete. @route("/<_bucket>", ["POST"]) def delete(_bucket): diff --git a/tests/integration/test_s3_with_proxy/test.py b/tests/integration/test_s3_with_proxy/test.py index 1102d190a87..1af040c3c30 100644 --- a/tests/integration/test_s3_with_proxy/test.py +++ b/tests/integration/test_s3_with_proxy/test.py @@ -5,6 +5,7 @@ import time import pytest from helpers.cluster import ClickHouseCluster + # Runs simple proxy resolver in python env container. def run_resolver(cluster): container_id = cluster.get_container_id("resolver") diff --git a/tests/integration/test_ssl_cert_authentication/test.py b/tests/integration/test_ssl_cert_authentication/test.py index 7c62ca0d8b6..b3570b6e281 100644 --- a/tests/integration/test_ssl_cert_authentication/test.py +++ b/tests/integration/test_ssl_cert_authentication/test.py @@ -87,7 +87,6 @@ config = """ def execute_query_native(node, query, user, cert_name): - config_path = f"{SCRIPT_DIR}/configs/client.xml" formatted = config.format( diff --git a/tests/integration/test_storage_kafka/kafka_pb2.py b/tests/integration/test_storage_kafka/kafka_pb2.py index 7de1363bbf1..3e47af6c1e0 100644 --- a/tests/integration/test_storage_kafka/kafka_pb2.py +++ b/tests/integration/test_storage_kafka/kafka_pb2.py @@ -21,7 +21,6 @@ _builder.BuildTopDescriptorsAndMessages( DESCRIPTOR, "clickhouse_path.format_schemas.kafka_pb2", globals() ) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _KEYVALUEPAIR._serialized_start = 46 _KEYVALUEPAIR._serialized_end = 88 diff --git a/tests/integration/test_storage_kafka/message_with_repeated_pb2.py b/tests/integration/test_storage_kafka/message_with_repeated_pb2.py index 4d1a23c0b43..3715a9bea04 100644 --- a/tests/integration/test_storage_kafka/message_with_repeated_pb2.py +++ b/tests/integration/test_storage_kafka/message_with_repeated_pb2.py @@ -21,7 +21,6 @@ _builder.BuildTopDescriptorsAndMessages( DESCRIPTOR, "clickhouse_path.format_schemas.message_with_repeated_pb2", globals() ) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b"H\001" _MESSAGE._serialized_start = 62 diff --git a/tests/integration/test_storage_kafka/social_pb2.py b/tests/integration/test_storage_kafka/social_pb2.py index 830ade81d33..f91a7bd0539 100644 --- a/tests/integration/test_storage_kafka/social_pb2.py +++ b/tests/integration/test_storage_kafka/social_pb2.py @@ -21,7 +21,6 @@ _builder.BuildTopDescriptorsAndMessages( DESCRIPTOR, "clickhouse_path.format_schemas.social_pb2", globals() ) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _USER._serialized_start = 47 _USER._serialized_end = 90 diff --git a/tests/integration/test_storage_kafka/test.py b/tests/integration/test_storage_kafka/test.py index 51952ac1eb7..3a4fa6c6bfe 100644 --- a/tests/integration/test_storage_kafka/test.py +++ b/tests/integration/test_storage_kafka/test.py @@ -121,7 +121,7 @@ def kafka_create_topic( def kafka_delete_topic(admin_client, topic, max_retries=50): result = admin_client.delete_topics([topic]) - for (topic, e) in result.topic_error_codes: + for topic, e in result.topic_error_codes: if e == 0: logging.debug(f"Topic {topic} deleted") else: @@ -917,9 +917,7 @@ def describe_consumer_group(kafka_cluster, name): member_info["client_id"] = client_id member_info["client_host"] = client_host member_topics_assignment = [] - for (topic, partitions) in MemberAssignment.decode( - member_assignment - ).assignment: + for topic, partitions in MemberAssignment.decode(member_assignment).assignment: member_topics_assignment.append({"topic": topic, "partitions": partitions}) member_info["assignment"] = member_topics_assignment res.append(member_info) @@ -1537,7 +1535,6 @@ def test_kafka_protobuf_no_delimiter(kafka_cluster): def test_kafka_materialized_view(kafka_cluster): - instance.query( """ DROP TABLE IF EXISTS test.view; @@ -2315,7 +2312,6 @@ def test_kafka_virtual_columns2(kafka_cluster): def test_kafka_produce_key_timestamp(kafka_cluster): - admin_client = KafkaAdminClient( bootstrap_servers="localhost:{}".format(kafka_cluster.kafka_port) ) @@ -2444,7 +2440,6 @@ def test_kafka_insert_avro(kafka_cluster): def test_kafka_produce_consume_avro(kafka_cluster): - admin_client = KafkaAdminClient( bootstrap_servers="localhost:{}".format(kafka_cluster.kafka_port) ) @@ -4031,7 +4026,6 @@ def test_kafka_predefined_configuration(kafka_cluster): # https://github.com/ClickHouse/ClickHouse/issues/26643 def test_issue26643(kafka_cluster): - # for backporting: # admin_client = KafkaAdminClient(bootstrap_servers="localhost:9092") admin_client = KafkaAdminClient( @@ -4313,7 +4307,6 @@ def test_row_based_formats(kafka_cluster): "RowBinaryWithNamesAndTypes", "MsgPack", ]: - print(format_name) kafka_create_topic(admin_client, format_name) @@ -4438,7 +4431,6 @@ def test_block_based_formats_2(kafka_cluster): "ORC", "JSONCompactColumns", ]: - kafka_create_topic(admin_client, format_name) instance.query( diff --git a/tests/integration/test_storage_nats/nats_pb2.py b/tests/integration/test_storage_nats/nats_pb2.py index 4330ff57950..e9e5cb72363 100644 --- a/tests/integration/test_storage_nats/nats_pb2.py +++ b/tests/integration/test_storage_nats/nats_pb2.py @@ -31,7 +31,6 @@ ProtoKeyValue = _reflection.GeneratedProtocolMessageType( _sym_db.RegisterMessage(ProtoKeyValue) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _PROTOKEYVALUE._serialized_start = 45 _PROTOKEYVALUE._serialized_end = 88 diff --git a/tests/integration/test_storage_postgresql_replica/test.py b/tests/integration/test_storage_postgresql_replica/test.py index 5df8b9029e6..8666d7ae58c 100644 --- a/tests/integration/test_storage_postgresql_replica/test.py +++ b/tests/integration/test_storage_postgresql_replica/test.py @@ -706,7 +706,6 @@ def test_abrupt_connection_loss_while_heavy_replication(started_cluster): def test_abrupt_server_restart_while_heavy_replication(started_cluster): - # FIXME (kssenii) temporary disabled if instance.is_built_with_sanitizer(): pytest.skip("Temporary disabled (FIXME)") diff --git a/tests/integration/test_storage_rabbitmq/rabbitmq_pb2.py b/tests/integration/test_storage_rabbitmq/rabbitmq_pb2.py index e017b4e66c2..a5845652eef 100644 --- a/tests/integration/test_storage_rabbitmq/rabbitmq_pb2.py +++ b/tests/integration/test_storage_rabbitmq/rabbitmq_pb2.py @@ -21,7 +21,6 @@ _builder.BuildTopDescriptorsAndMessages( DESCRIPTOR, "clickhouse_path.format_schemas.rabbitmq_pb2", globals() ) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _KEYVALUEPROTO._serialized_start = 49 _KEYVALUEPROTO._serialized_end = 92 diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 2e54f21787a..53b6c4109ef 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -2864,7 +2864,6 @@ def test_rabbitmq_predefined_configuration(rabbitmq_cluster): def test_rabbitmq_msgpack(rabbitmq_cluster): - instance.query( """ drop table if exists rabbit_in; @@ -2908,7 +2907,6 @@ def test_rabbitmq_msgpack(rabbitmq_cluster): def test_rabbitmq_address(rabbitmq_cluster): - instance2.query( """ drop table if exists rabbit_in; @@ -3243,7 +3241,6 @@ def test_block_based_formats_2(rabbitmq_cluster): "ORC", "JSONCompactColumns", ]: - print(format_name) instance.query( diff --git a/tests/integration/test_storage_s3/test.py b/tests/integration/test_storage_s3/test.py index 8b20727a7b5..4d493d9526b 100644 --- a/tests/integration/test_storage_s3/test.py +++ b/tests/integration/test_storage_s3/test.py @@ -18,6 +18,7 @@ MINIO_INTERNAL_PORT = 9001 SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) + # Creates S3 bucket for tests and allows anonymous read-write access to it. def prepare_s3_bucket(started_cluster): # Allows read-write access for bucket without authorization. diff --git a/tests/integration/test_storage_s3/test_invalid_env_credentials.py b/tests/integration/test_storage_s3/test_invalid_env_credentials.py index 2f5d9349904..aa6479a2ed3 100644 --- a/tests/integration/test_storage_s3/test_invalid_env_credentials.py +++ b/tests/integration/test_storage_s3/test_invalid_env_credentials.py @@ -11,6 +11,7 @@ MINIO_INTERNAL_PORT = 9001 SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) + # Creates S3 bucket for tests and allows anonymous read-write access to it. def prepare_s3_bucket(started_cluster): # Allows read-write access for bucket without authorization. diff --git a/tests/integration/test_system_merges/test.py b/tests/integration/test_system_merges/test.py index 0a469bd7bbd..ff303afe19e 100644 --- a/tests/integration/test_system_merges/test.py +++ b/tests/integration/test_system_merges/test.py @@ -171,7 +171,6 @@ def test_mutation_simple(started_cluster, replicated): starting_block = 0 if replicated else 1 try: - for node in nodes: node.query( f"create table {name} (a Int64) engine={engine} order by tuple()" diff --git a/tests/integration/test_ttl_move/test.py b/tests/integration/test_ttl_move/test.py index 99978cbf6dc..89824293320 100644 --- a/tests/integration/test_ttl_move/test.py +++ b/tests/integration/test_ttl_move/test.py @@ -1863,7 +1863,7 @@ def test_ttl_move_if_exists(started_cluster, name, dest_type): ) ) - for (node, policy) in zip( + for node, policy in zip( [node1, node2], ["only_jbod_1", "small_jbod_with_external"] ): node.query( diff --git a/tests/integration/test_zero_copy_fetch/test.py b/tests/integration/test_zero_copy_fetch/test.py index b71752528d3..9b9aa5e0da7 100644 --- a/tests/integration/test_zero_copy_fetch/test.py +++ b/tests/integration/test_zero_copy_fetch/test.py @@ -16,7 +16,6 @@ cluster = ClickHouseCluster(__file__) @pytest.fixture(scope="module") def started_cluster(): try: - cluster.add_instance( "node1", main_configs=["configs/storage_conf.xml"], diff --git a/utils/changelog-simple/format-changelog.py b/utils/changelog-simple/format-changelog.py index d5e1518270e..01f2694dd0f 100755 --- a/utils/changelog-simple/format-changelog.py +++ b/utils/changelog-simple/format-changelog.py @@ -20,6 +20,7 @@ parser.add_argument( ) args = parser.parse_args() + # This function mirrors the PR description checks in ClickhousePullRequestTrigger. # Returns False if the PR should not be mentioned changelog. def parse_one_pull_request(item): diff --git a/utils/keeper-overload/keeper-overload.py b/utils/keeper-overload/keeper-overload.py index bdb4563c713..0a059b10588 100755 --- a/utils/keeper-overload/keeper-overload.py +++ b/utils/keeper-overload/keeper-overload.py @@ -166,7 +166,7 @@ def main(args): keeper_bench_path = args.keeper_bench_path keepers = [] - for (port, server_id) in zip(PORTS, SERVER_IDS): + for port, server_id in zip(PORTS, SERVER_IDS): keepers.append( Keeper( keeper_binary_path, server_id, port, workdir, args.with_thread_fuzzer From bf7b0c5cfe5bcdf61a0ce774bb1850daba7aa2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Fri, 24 Mar 2023 17:07:48 +0100 Subject: [PATCH 082/377] Different approach to inject timeouts --- src/Common/ZooKeeper/ZooKeeperArgs.cpp | 20 ++++++ src/Common/ZooKeeper/ZooKeeperArgs.h | 5 ++ src/Common/ZooKeeper/ZooKeeperImpl.cpp | 65 +++++++++++++++---- src/Common/ZooKeeper/ZooKeeperImpl.h | 7 ++ .../config.d/zookeeper_fault_injection.xml | 4 ++ 5 files changed, 87 insertions(+), 14 deletions(-) diff --git a/src/Common/ZooKeeper/ZooKeeperArgs.cpp b/src/Common/ZooKeeper/ZooKeeperArgs.cpp index fe2f6957490..5cf7e927434 100644 --- a/src/Common/ZooKeeper/ZooKeeperArgs.cpp +++ b/src/Common/ZooKeeper/ZooKeeperArgs.cpp @@ -42,6 +42,10 @@ ZooKeeperArgs::ZooKeeperArgs(const Poco::Util::AbstractConfiguration & config, c { connection_timeout_ms = config.getInt(config_name + "." + key); } + else if (key == "enable_fault_injections_during_startup") + { + enable_fault_injections_during_startup = config.getBool(config_name + "." + key); + } else if (key == "send_fault_probability") { send_fault_probability = config.getDouble(config_name + "." + key); @@ -50,6 +54,22 @@ ZooKeeperArgs::ZooKeeperArgs(const Poco::Util::AbstractConfiguration & config, c { recv_fault_probability = config.getDouble(config_name + "." + key); } + else if (key == "send_sleep_probability") + { + send_sleep_probability = config.getDouble(config_name + "." + key); + } + else if (key == "recv_sleep_probability") + { + recv_sleep_probability = config.getDouble(config_name + "." + key); + } + else if (key == "send_sleep_ms") + { + send_sleep_ms = config.getUInt64(config_name + "." + key); + } + else if (key == "recv_sleep_ms") + { + recv_sleep_ms = config.getUInt64(config_name + "." + key); + } else if (key == "identity") { identity = config.getString(config_name + "." + key); diff --git a/src/Common/ZooKeeper/ZooKeeperArgs.h b/src/Common/ZooKeeper/ZooKeeperArgs.h index 48c8e4386f8..e1f8fe16aee 100644 --- a/src/Common/ZooKeeper/ZooKeeperArgs.h +++ b/src/Common/ZooKeeper/ZooKeeperArgs.h @@ -28,8 +28,13 @@ struct ZooKeeperArgs int32_t connection_timeout_ms = Coordination::DEFAULT_CONNECTION_TIMEOUT_MS; int32_t session_timeout_ms = Coordination::DEFAULT_SESSION_TIMEOUT_MS; int32_t operation_timeout_ms = Coordination::DEFAULT_OPERATION_TIMEOUT_MS; + bool enable_fault_injections_during_startup = false; double send_fault_probability = 0.0; double recv_fault_probability = 0.0; + double send_sleep_probability = 0.0; + double recv_sleep_probability = 0.0; + UInt64 send_sleep_ms = 0; + UInt64 recv_sleep_ms = 0; DB::GetPriorityForLoadBalancing get_priority_load_balancing; }; diff --git a/src/Common/ZooKeeper/ZooKeeperImpl.cpp b/src/Common/ZooKeeper/ZooKeeperImpl.cpp index b637bdea835..99e1e0d3e7f 100644 --- a/src/Common/ZooKeeper/ZooKeeperImpl.cpp +++ b/src/Common/ZooKeeper/ZooKeeperImpl.cpp @@ -1,19 +1,21 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include #include -#include #include -#include -#include -#include -#include -#include -#include #include "Coordination/KeeperConstants.h" #include "config.h" @@ -352,6 +354,14 @@ ZooKeeper::ZooKeeper( { recv_inject_fault.emplace(args.recv_fault_probability); } + if (0 < args.send_sleep_probability && args.send_sleep_probability <= 1) + { + send_inject_sleep.emplace(args.send_sleep_probability); + } + if (0 < args.recv_sleep_probability && args.recv_sleep_probability <= 1) + { + recv_inject_sleep.emplace(args.recv_sleep_probability); + } connect(nodes, args.connection_timeout_ms * 1000); @@ -571,7 +581,6 @@ void ZooKeeper::sendAuth(const String & scheme, const String & data) static_cast(err), errorMessage(err)); } - void ZooKeeper::sendThread() { setThreadName("ZooKeeperSend"); @@ -587,6 +596,8 @@ void ZooKeeper::sendThread() auto now = clock::now(); auto next_heartbeat_time = prev_heartbeat_time + std::chrono::milliseconds(args.session_timeout_ms / 3); + maybeInjectSendSleep(); + if (next_heartbeat_time > now) { /// Wait for the next request in queue. No more than operation timeout. No more than until next heartbeat time. @@ -659,6 +670,7 @@ void ZooKeeper::receiveThread() Int64 waited_us = 0; while (!requests_queue.isFinished()) { + maybeInjectRecvSleep(); auto prev_bytes_received = in->count(); clock::time_point now = clock::now(); @@ -728,8 +740,7 @@ void ZooKeeper::receiveEvent() ZooKeeperResponsePtr response; UInt64 elapsed_ms = 0; - if (unlikely(recv_inject_fault) && recv_inject_fault.value()(thread_local_rng)) - throw Exception(Error::ZSESSIONEXPIRED, "Session expired (fault injected on recv)"); + maybeInjectRecvFault(); if (xid == PING_XID) { @@ -1078,8 +1089,7 @@ void ZooKeeper::pushRequest(RequestInfo && info) } } - if (unlikely(send_inject_fault) && send_inject_fault.value()(thread_local_rng)) - throw Exception(Error::ZSESSIONEXPIRED, "Session expired (fault injected on send)"); + maybeInjectSendFault(); if (!requests_queue.tryPush(std::move(info), args.operation_timeout_ms)) { @@ -1403,4 +1413,31 @@ void ZooKeeper::logOperationIfNeeded(const ZooKeeperRequestPtr &, const ZooKeepe {} #endif +void ZooKeeper::maybeInjectSendFault() +{ + if (unlikely(send_inject_fault) && send_inject_fault.value()(thread_local_rng) + && (args.enable_fault_injections_during_startup || Context::getGlobalContextInstance()->isServerCompletelyStarted())) + throw Exception(Error::ZSESSIONEXPIRED, "Session expired (fault injected on recv)"); +} + +void ZooKeeper::maybeInjectRecvFault() +{ + if (unlikely(recv_inject_fault) && recv_inject_fault.value()(thread_local_rng) + && (args.enable_fault_injections_during_startup || Context::getGlobalContextInstance()->isServerCompletelyStarted())) + throw Exception(Error::ZSESSIONEXPIRED, "Session expired (fault injected on recv)"); +} + +void ZooKeeper::maybeInjectSendSleep() +{ + if (unlikely(send_inject_sleep) && send_inject_sleep.value()(thread_local_rng) + && (args.enable_fault_injections_during_startup || Context::getGlobalContextInstance()->isServerCompletelyStarted())) + sleepForMilliseconds(args.send_sleep_ms); +} + +void ZooKeeper::maybeInjectRecvSleep() +{ + if (unlikely(recv_inject_sleep) && recv_inject_sleep.value()(thread_local_rng) + && (args.enable_fault_injections_during_startup || Context::getGlobalContextInstance()->isServerCompletelyStarted())) + sleepForMilliseconds(args.recv_sleep_ms); +} } diff --git a/src/Common/ZooKeeper/ZooKeeperImpl.h b/src/Common/ZooKeeper/ZooKeeperImpl.h index 6b70f8bc753..2939d7bfaf8 100644 --- a/src/Common/ZooKeeper/ZooKeeperImpl.h +++ b/src/Common/ZooKeeper/ZooKeeperImpl.h @@ -202,8 +202,15 @@ private: zkutil::ZooKeeperArgs args; + /// Fault injection + void maybeInjectSendFault(); + void maybeInjectRecvFault(); + void maybeInjectSendSleep(); + void maybeInjectRecvSleep(); std::optional send_inject_fault; std::optional recv_inject_fault; + std::optional send_inject_sleep; + std::optional recv_inject_sleep; Poco::Net::StreamSocket socket; /// To avoid excessive getpeername(2) calls. diff --git a/tests/config/config.d/zookeeper_fault_injection.xml b/tests/config/config.d/zookeeper_fault_injection.xml index 1f13155a130..a339e1f0fba 100644 --- a/tests/config/config.d/zookeeper_fault_injection.xml +++ b/tests/config/config.d/zookeeper_fault_injection.xml @@ -12,7 +12,11 @@ In other words, session will expire 4 times per 99996 successful requests or approximately each 25000 requests (on average). --> + 0 0.00002 0.00002 + + 0.00001 + 10000 From 08054b79a8f7cfe5d08facecd3786d8fa20d62ef Mon Sep 17 00:00:00 2001 From: kssenii Date: Fri, 24 Mar 2023 18:45:27 +0100 Subject: [PATCH 083/377] Try fix --- tests/integration/test_storage_rabbitmq/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 2e54f21787a..10e2aaf8355 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -2718,7 +2718,7 @@ def test_rabbitmq_drop_mv(rabbitmq_cluster): else: logging.debug(f"Number of rows in test.view: {res}") - instance.query("DROP VIEW test.consumer") + instance.query("DROP VIEW test.consumer SYNC") for i in range(20, 40): channel.basic_publish( exchange="mv", routing_key="", body=json.dumps({"key": i, "value": i}) From 9a71d265ae1b9ccb3a5e7a5c899cd0881d754896 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Sun, 26 Mar 2023 21:38:17 -0600 Subject: [PATCH 084/377] Create amazon-reviews.md --- .../example-datasets/amazon-reviews.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/en/getting-started/example-datasets/amazon-reviews.md diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md new file mode 100644 index 00000000000..7ce554f625c --- /dev/null +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -0,0 +1,61 @@ +--- +slug: /en/getting-started/example-datasets/amazon-reviews +sidebar_label: Amazon customer reviews +description: +--- + +# Amazon customer reviews dataset + +**Amazon Customer Reviews** (a.k.a. Product Reviews) is one of Amazon’s iconic products. In a period of over two decades since the first review in 1995, millions of Amazon customers have contributed over a hundred million reviews to express opinions and describe their experiences regarding products on the Amazon.com website. This makes Amazon Customer Reviews a rich source of information for academic researchers in the fields of Natural Language Processing (NLP), Information Retrieval (IR), and Machine Learning (ML), amongst others. + +The data is in a JSON format, and the files are up in AWS S3. Let's walk through the steps to insert it into ClickHouse. + +:::note +The queries below were executed on a **Production** instance of [ClickHouse Cloud](https://clickhouse.cloud). +::: + +1. Let's check out the format of the data in the files. Notice the `s3Cluster` table function returns a table where the data types are inferred - helpful when you are researching a new dataset: + +```sql + +``` + + +```response + +``` + +2. Let's define a new table named `amazon_reviews`. We'll optimize some of the inferred column data types - and choose a primary key (the `ORDER BY` clause): + +```sql +CREATE TABLE amazon_reviews +( + review_date Date, + marketplace LowCardinality(String), + customer_id UInt64, + review_id String, + product_id String, + product_parent UInt64, + product_title String, + product_category LowCardinality(String), + star_rating UInt8, + helpful_votes UInt32, + total_votes UInt32, + vine UInt8, + verified_purchase UInt8, + review_headline String, + review_body String +) +ENGINE = MergeTree +ORDER BY (marketplace, review_date, product_category); +``` + +3. The following `INSERT` query inserts all of the files in our S3 bucket into `amazon_reviews`. If you do not want all of the data, simply add a `LIMIT` clause. + +:::tip +In ClickHouse Cloud, there is a cluster named `default`. Change `default` to the name of your cluster...or use the `s3` table function (instead of `s3Cluster`) if you do not have a cluster. +::: + +```sql + +``` From f6de2160419b2f0111a59deb2dc535cee90dceff Mon Sep 17 00:00:00 2001 From: vdimir Date: Fri, 24 Mar 2023 16:57:32 +0000 Subject: [PATCH 085/377] PullingAsyncPipelineExecutor for Direct dictionary with ClickHouse source --- src/Core/Settings.h | 2 + src/Dictionaries/DirectDictionary.cpp | 69 ++++++++++++++++--- src/Dictionaries/DirectDictionary.h | 4 ++ .../01268_dictionary_direct_layout.sql | 2 +- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/Core/Settings.h b/src/Core/Settings.h index ca89106dc08..d80f26e86da 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -935,6 +935,8 @@ class IColumn; \ M(Bool, regexp_dict_allow_other_sources, false, "Allow regexp_tree dictionary to use sources other than yaml source.", 0) \ M(Bool, regexp_dict_allow_hyperscan, false, "Allow regexp_tree dictionary using Hyperscan library.", 0) \ + \ + M(Bool, dictionary_use_async_executor, false, "Execute a pipeline for reading from a dictionary with several threads. It's supported only by DIRECT dictionary with CLICKHOUSE source.", 0) \ // End of FORMAT_FACTORY_SETTINGS // Please add settings non-related to formats into the COMMON_SETTINGS above. diff --git a/src/Dictionaries/DirectDictionary.cpp b/src/Dictionaries/DirectDictionary.cpp index 189ea2a7bca..d84967fbae6 100644 --- a/src/Dictionaries/DirectDictionary.cpp +++ b/src/Dictionaries/DirectDictionary.cpp @@ -4,16 +4,22 @@ #include #include +#include #include +#include #include -#include -#include -#include #include +#include +#include + +#include +#include + namespace DB { + namespace ErrorCodes { extern const int UNSUPPORTED_METHOD; @@ -73,9 +79,17 @@ Columns DirectDictionary::getColumns( PullingPipelineExecutor executor(pipeline); + Stopwatch watch; Block block; + size_t block_num = 0; + size_t rows_num = 0; while (executor.pull(block)) { + if (!block) + continue; + + ++block_num; + rows_num += block.rows(); convertToFullIfSparse(block); /// Split into keys columns and attribute columns @@ -104,6 +118,9 @@ Columns DirectDictionary::getColumns( block_key_columns.clear(); } + LOG_DEBUG(&Poco::Logger::get("DirectDictionary"), "read {} blocks with {} rows from pipeline in {} ms", + block_num, rows_num, watch.elapsedMilliseconds()); + Field value_to_insert; size_t requested_keys_size = requested_keys.size(); @@ -263,6 +280,7 @@ ColumnUInt8::Ptr DirectDictionary::isInHierarchy( return nullptr; } +template class SourceFromQueryPipeline : public ISource { public: @@ -272,7 +290,10 @@ public: , executor(pipeline) {} - std::string getName() const override { return "SourceFromQueryPipeline"; } + std::string getName() const override + { + return std::is_same_v ? "SourceFromQueryPipelineAsync" : "SourceFromQueryPipeline"; + } Chunk generate() override { @@ -286,10 +307,9 @@ public: return {}; } - private: QueryPipeline pipeline; - PullingPipelineExecutor executor; + TExecutor executor; }; template @@ -297,6 +317,8 @@ Pipe DirectDictionary::getSourcePipe( const Columns & key_columns [[maybe_unused]], const PaddedPODArray & requested_keys [[maybe_unused]]) const { + Stopwatch watch; + size_t requested_keys_size = requested_keys.size(); Pipe pipe; @@ -309,7 +331,12 @@ Pipe DirectDictionary::getSourcePipe( for (auto key : requested_keys) ids.emplace_back(key); - pipe = Pipe(std::make_shared(source_ptr->loadIds(ids))); + auto pipeline = source_ptr->loadIds(ids); + + if (use_async_executor) + pipe = Pipe(std::make_shared>(std::move(pipeline))); + else + pipe = Pipe(std::make_shared>(std::move(pipeline))); } else { @@ -318,16 +345,31 @@ Pipe DirectDictionary::getSourcePipe( for (size_t i = 0; i < requested_keys_size; ++i) requested_rows.emplace_back(i); - pipe = Pipe(std::make_shared(source_ptr->loadKeys(key_columns, requested_rows))); + auto pipeline = source_ptr->loadKeys(key_columns, requested_rows); + if (use_async_executor) + pipe = Pipe(std::make_shared>(std::move(pipeline))); + else + pipe = Pipe(std::make_shared>(std::move(pipeline))); } + LOG_DEBUG(&Poco::Logger::get("DirectDictionary"), "building pipeline for loading keys done in {} ms", watch.elapsedMilliseconds()); return pipe; } template Pipe DirectDictionary::read(const Names & /* column_names */, size_t /* max_block_size */, size_t /* num_streams */) const { - return Pipe(std::make_shared(source_ptr->loadAll())); + return Pipe(std::make_shared>(source_ptr->loadAll())); +} + +template +void DirectDictionary::applySettings(const Settings & settings) +{ + if (dynamic_cast(source_ptr.get())) + { + /// Only applicable for CLICKHOUSE dictionary source. + use_async_executor = settings.dictionary_use_async_executor; + } } namespace @@ -339,7 +381,7 @@ namespace const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, DictionarySourcePtr source_ptr, - ContextPtr /* global_context */, + ContextPtr global_context, bool /* created_from_ddl */) { const auto * layout_name = dictionary_key_type == DictionaryKeyType::Simple ? "direct" : "complex_key_direct"; @@ -372,7 +414,12 @@ namespace "'lifetime' parameter is redundant for the dictionary' of layout '{}'", layout_name); - return std::make_unique>(dict_id, dict_struct, std::move(source_ptr)); + auto dictionary = std::make_unique>(dict_id, dict_struct, std::move(source_ptr)); + + auto context = copyContextAndApplySettingsFromDictionaryConfig(global_context, config, config_prefix); + dictionary->applySettings(context->getSettingsRef()); + + return dictionary; } } diff --git a/src/Dictionaries/DirectDictionary.h b/src/Dictionaries/DirectDictionary.h index 2b5662b8c1a..214c8ef8a13 100644 --- a/src/Dictionaries/DirectDictionary.h +++ b/src/Dictionaries/DirectDictionary.h @@ -95,6 +95,8 @@ public: Pipe read(const Names & column_names, size_t max_block_size, size_t num_streams) const override; + void applySettings(const Settings & settings); + private: Pipe getSourcePipe(const Columns & key_columns, const PaddedPODArray & requested_keys) const; @@ -102,6 +104,8 @@ private: const DictionarySourcePtr source_ptr; const DictionaryLifetime dict_lifetime; + bool use_async_executor = false; + mutable std::atomic query_count{0}; mutable std::atomic found_count{0}; }; diff --git a/tests/queries/0_stateless/01268_dictionary_direct_layout.sql b/tests/queries/0_stateless/01268_dictionary_direct_layout.sql index 914d24a740a..45b5c580561 100644 --- a/tests/queries/0_stateless/01268_dictionary_direct_layout.sql +++ b/tests/queries/0_stateless/01268_dictionary_direct_layout.sql @@ -75,7 +75,7 @@ CREATE DICTIONARY db_01268.dict2 ) PRIMARY KEY region_id SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() USER 'default' TABLE 'table_for_dict2' PASSWORD '' DB 'database_for_dict_01268')) -LAYOUT(DIRECT()); +LAYOUT(DIRECT()) SETTINGS(dictionary_use_async_executor=1, max_threads=8); CREATE DICTIONARY db_01268.dict3 ( From 86b40a1b792e493666a7ad7998bcd6a53e1569fe Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 27 Mar 2023 10:03:40 +0000 Subject: [PATCH 086/377] address PR review --- src/Analyzer/Passes/CNF.cpp | 25 ++++++++++++------- src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 3 ++- src/Analyzer/Passes/ConvertQueryToCNFPass.h | 6 ++--- src/Analyzer/QueryTreePassManager.cpp | 2 +- src/Interpreters/ComparisonGraph.h | 4 +-- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Analyzer/Passes/CNF.cpp b/src/Analyzer/Passes/CNF.cpp index 68a95a509f8..f53d833c107 100644 --- a/src/Analyzer/Passes/CNF.cpp +++ b/src/Analyzer/Passes/CNF.cpp @@ -110,8 +110,10 @@ private: class PushNotVisitor { public: - explicit PushNotVisitor(ContextPtr context) - : current_context(std::move(context)) + explicit PushNotVisitor(const ContextPtr & context) + : not_function_resolver(FunctionFactory::instance().get("not", context)) + , or_function_resolver(FunctionFactory::instance().get("or", context)) + , and_function_resolver(FunctionFactory::instance().get("and", context)) {} void visit(QueryTreeNodePtr & node, bool add_negation) @@ -123,7 +125,7 @@ public: if (!function_node || !isLogicalFunction(*function_node)) { if (add_negation) - node = createFunctionNode(FunctionFactory::instance().get("not", current_context), std::move(node)); + node = createFunctionNode(not_function_resolver, std::move(node)); return; } @@ -132,8 +134,10 @@ public: { if (add_negation) { - auto function_resolver = FunctionFactory::instance().get(function_name == "and" ? "or" : "and", current_context); - function_node->resolveAsFunction(function_resolver); + if (function_name == "and") + function_node->resolveAsFunction(or_function_resolver); + else + function_node->resolveAsFunction(and_function_resolver); } auto & arguments = function_node->getArguments().getNodes(); @@ -150,7 +154,9 @@ public: } private: - ContextPtr current_context; + const FunctionOverloadResolverPtr not_function_resolver; + const FunctionOverloadResolverPtr or_function_resolver; + const FunctionOverloadResolverPtr and_function_resolver; }; class PushOrVisitor @@ -303,9 +309,10 @@ bool CNF::AtomicFormula::operator==(const AtomicFormula & rhs) const bool CNF::AtomicFormula::operator<(const AtomicFormula & rhs) const { - return node_with_hash.hash == rhs.node_with_hash.hash - ? negative < rhs.negative - : node_with_hash.hash < rhs.node_with_hash.hash; + if (node_with_hash.hash > rhs.node_with_hash.hash) + return false; + + return node_with_hash.hash < rhs.node_with_hash.hash || negative < rhs.negative; } std::string CNF::dump() const diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp index d5d11a19ffe..5998237f3ce 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -711,6 +711,7 @@ public: optimize_filter(query_node->getWhere()); optimize_filter(query_node->getPrewhere()); + optimize_filter(query_node->getHaving()); if (has_filter && settings.optimize_substitute_columns) substituteColumns(*query_node, table_expressions, context); @@ -719,7 +720,7 @@ public: } -void ConvertQueryToCNFPass::run(QueryTreeNodePtr query_tree_node, ContextPtr context) +void ConvertLogicalExpressionToCNFPass::run(QueryTreeNodePtr query_tree_node, ContextPtr context) { const auto & settings = context->getSettingsRef(); if (!settings.convert_query_to_cnf) diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.h b/src/Analyzer/Passes/ConvertQueryToCNFPass.h index 232af3b015e..5ed874db006 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.h +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.h @@ -5,12 +5,12 @@ namespace DB { -class ConvertQueryToCNFPass final : public IQueryTreePass +class ConvertLogicalExpressionToCNFPass final : public IQueryTreePass { public: - String getName() override { return "ConvertQueryToCNFPass"; } + String getName() override { return "ConvertLogicalExpressionToCNFPass"; } - String getDescription() override { return "Convert query to CNF and apply optimizations using constraints"; } + String getDescription() override { return "Convert logical expression to CNF and apply optimizations using constraints"; } void run(QueryTreeNodePtr query_tree_node, ContextPtr context) override; }; diff --git a/src/Analyzer/QueryTreePassManager.cpp b/src/Analyzer/QueryTreePassManager.cpp index a9b812d71b4..52fce8abaf2 100644 --- a/src/Analyzer/QueryTreePassManager.cpp +++ b/src/Analyzer/QueryTreePassManager.cpp @@ -233,7 +233,7 @@ void addQueryTreePasses(QueryTreePassManager & manager) manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); - manager.addPass(std::make_unique()); + manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); manager.addPass(std::make_unique()); diff --git a/src/Interpreters/ComparisonGraph.h b/src/Interpreters/ComparisonGraph.h index ecfc617ac8a..70543227b58 100644 --- a/src/Interpreters/ComparisonGraph.h +++ b/src/Interpreters/ComparisonGraph.h @@ -4,8 +4,8 @@ #include #include -#include "Analyzer/HashUtils.h" -#include "Analyzer/IQueryTreeNode.h" +#include +#include #include #include From 714b54b322c9d7908d32c64f17f7d3e1cd0981b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Mon, 27 Mar 2023 15:06:46 +0200 Subject: [PATCH 087/377] Reimplement enable_fault_injections_during_startup --- src/Common/ZooKeeper/ZooKeeper.cpp | 6 +++ src/Common/ZooKeeper/ZooKeeper.h | 2 + src/Common/ZooKeeper/ZooKeeperImpl.cpp | 66 ++++++++++++++++---------- src/Common/ZooKeeper/ZooKeeperImpl.h | 4 ++ src/Interpreters/Context.cpp | 25 ++++++++-- 5 files changed, 73 insertions(+), 30 deletions(-) diff --git a/src/Common/ZooKeeper/ZooKeeper.cpp b/src/Common/ZooKeeper/ZooKeeper.cpp index aa6ed5b2090..67ad553bbd3 100644 --- a/src/Common/ZooKeeper/ZooKeeper.cpp +++ b/src/Common/ZooKeeper/ZooKeeper.cpp @@ -1163,6 +1163,12 @@ void ZooKeeper::setZooKeeperLog(std::shared_ptr zk_log_) zk->setZooKeeperLog(zk_log); } +void ZooKeeper::setServerCompletelyStarted() +{ + if (auto * zk = dynamic_cast(impl.get())) + zk->setServerCompletelyStarted(); +} + size_t getFailedOpIndex(Coordination::Error exception_code, const Coordination::Responses & responses) { diff --git a/src/Common/ZooKeeper/ZooKeeper.h b/src/Common/ZooKeeper/ZooKeeper.h index d20d036f04e..acd6750fced 100644 --- a/src/Common/ZooKeeper/ZooKeeper.h +++ b/src/Common/ZooKeeper/ZooKeeper.h @@ -520,6 +520,8 @@ public: UInt32 getSessionUptime() const { return static_cast(session_uptime.elapsedSeconds()); } + void setServerCompletelyStarted(); + private: friend class EphemeralNodeHolder; diff --git a/src/Common/ZooKeeper/ZooKeeperImpl.cpp b/src/Common/ZooKeeper/ZooKeeperImpl.cpp index 99e1e0d3e7f..8183569a718 100644 --- a/src/Common/ZooKeeper/ZooKeeperImpl.cpp +++ b/src/Common/ZooKeeper/ZooKeeperImpl.cpp @@ -344,24 +344,8 @@ ZooKeeper::ZooKeeper( default_acls.emplace_back(std::move(acl)); } - /// It makes sense (especially, for async requests) to inject a fault in two places: - /// pushRequest (before request is sent) and receiveEvent (after request was executed). - if (0 < args.send_fault_probability && args.send_fault_probability <= 1) - { - send_inject_fault.emplace(args.send_fault_probability); - } - if (0 < args.recv_fault_probability && args.recv_fault_probability <= 1) - { - recv_inject_fault.emplace(args.recv_fault_probability); - } - if (0 < args.send_sleep_probability && args.send_sleep_probability <= 1) - { - send_inject_sleep.emplace(args.send_sleep_probability); - } - if (0 < args.recv_sleep_probability && args.recv_sleep_probability <= 1) - { - recv_inject_sleep.emplace(args.recv_sleep_probability); - } + if (args.enable_fault_injections_during_startup) + setupFaultDistributions(); connect(nodes, args.connection_timeout_ms * 1000); @@ -1413,31 +1397,61 @@ void ZooKeeper::logOperationIfNeeded(const ZooKeeperRequestPtr &, const ZooKeepe {} #endif + +void ZooKeeper::setServerCompletelyStarted() +{ + if (!args.enable_fault_injections_during_startup) + setupFaultDistributions(); +} + +void ZooKeeper::setupFaultDistributions() +{ + /// It makes sense (especially, for async requests) to inject a fault in two places: + /// pushRequest (before request is sent) and receiveEvent (after request was executed). + if (0 < args.send_fault_probability && args.send_fault_probability <= 1) + { + LOG_INFO(log, "ZK send fault: {}%", args.send_fault_probability * 100); + send_inject_fault.emplace(args.send_fault_probability); + } + if (0 < args.recv_fault_probability && args.recv_fault_probability <= 1) + { + LOG_INFO(log, "ZK recv fault: {}%", args.recv_fault_probability * 100); + recv_inject_fault.emplace(args.recv_fault_probability); + } + if (0 < args.send_sleep_probability && args.send_sleep_probability <= 1) + { + LOG_INFO(log, "ZK send sleep: {}% -> {}ms", args.send_sleep_probability * 100, args.send_sleep_ms); + send_inject_sleep.emplace(args.send_sleep_probability); + } + if (0 < args.recv_sleep_probability && args.recv_sleep_probability <= 1) + { + LOG_INFO(log, "ZK recv sleep: {}% -> {}ms", args.recv_sleep_probability * 100, args.recv_sleep_ms); + recv_inject_sleep.emplace(args.recv_sleep_probability); + } + inject_setup.test_and_set(); +} + void ZooKeeper::maybeInjectSendFault() { - if (unlikely(send_inject_fault) && send_inject_fault.value()(thread_local_rng) - && (args.enable_fault_injections_during_startup || Context::getGlobalContextInstance()->isServerCompletelyStarted())) + if (unlikely(inject_setup.test() && send_inject_fault && send_inject_fault.value()(thread_local_rng))) throw Exception(Error::ZSESSIONEXPIRED, "Session expired (fault injected on recv)"); } void ZooKeeper::maybeInjectRecvFault() { - if (unlikely(recv_inject_fault) && recv_inject_fault.value()(thread_local_rng) - && (args.enable_fault_injections_during_startup || Context::getGlobalContextInstance()->isServerCompletelyStarted())) + if (unlikely(inject_setup.test() && recv_inject_fault && recv_inject_fault.value()(thread_local_rng))) throw Exception(Error::ZSESSIONEXPIRED, "Session expired (fault injected on recv)"); } void ZooKeeper::maybeInjectSendSleep() { - if (unlikely(send_inject_sleep) && send_inject_sleep.value()(thread_local_rng) - && (args.enable_fault_injections_during_startup || Context::getGlobalContextInstance()->isServerCompletelyStarted())) + if (unlikely(inject_setup.test() && send_inject_sleep && send_inject_sleep.value()(thread_local_rng))) sleepForMilliseconds(args.send_sleep_ms); } void ZooKeeper::maybeInjectRecvSleep() { - if (unlikely(recv_inject_sleep) && recv_inject_sleep.value()(thread_local_rng) - && (args.enable_fault_injections_during_startup || Context::getGlobalContextInstance()->isServerCompletelyStarted())) + if (unlikely(inject_setup.test() && recv_inject_sleep && recv_inject_sleep.value()(thread_local_rng))) sleepForMilliseconds(args.recv_sleep_ms); } } diff --git a/src/Common/ZooKeeper/ZooKeeperImpl.h b/src/Common/ZooKeeper/ZooKeeperImpl.h index 2939d7bfaf8..91c5083bda1 100644 --- a/src/Common/ZooKeeper/ZooKeeperImpl.h +++ b/src/Common/ZooKeeper/ZooKeeperImpl.h @@ -197,6 +197,8 @@ public: void setZooKeeperLog(std::shared_ptr zk_log_); + void setServerCompletelyStarted(); + private: ACLs default_acls; @@ -207,6 +209,8 @@ private: void maybeInjectRecvFault(); void maybeInjectSendSleep(); void maybeInjectRecvSleep(); + void setupFaultDistributions(); + std::atomic_flag inject_setup = ATOMIC_FLAG_INIT; std::optional send_inject_fault; std::optional recv_inject_fault; std::optional send_inject_sleep; diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 5b412416747..286e91f4a40 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -2588,8 +2588,12 @@ void Context::resetZooKeeper() const shared->zookeeper.reset(); } -static void reloadZooKeeperIfChangedImpl(const ConfigurationPtr & config, const std::string & config_name, zkutil::ZooKeeperPtr & zk, - std::shared_ptr zk_log) +static void reloadZooKeeperIfChangedImpl( + const ConfigurationPtr & config, + const std::string & config_name, + zkutil::ZooKeeperPtr & zk, + std::shared_ptr zk_log, + bool server_started) { if (!zk || zk->configChanged(*config, config_name)) { @@ -2597,18 +2601,22 @@ static void reloadZooKeeperIfChangedImpl(const ConfigurationPtr & config, const zk->finalize("Config changed"); zk = std::make_shared(*config, config_name, std::move(zk_log)); + if (server_started) + zk->setServerCompletelyStarted(); } } void Context::reloadZooKeeperIfChanged(const ConfigurationPtr & config) const { + bool server_started = isServerCompletelyStarted(); std::lock_guard lock(shared->zookeeper_mutex); shared->zookeeper_config = config; - reloadZooKeeperIfChangedImpl(config, "zookeeper", shared->zookeeper, getZooKeeperLog()); + reloadZooKeeperIfChangedImpl(config, "zookeeper", shared->zookeeper, getZooKeeperLog(), server_started); } void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & config) { + bool server_started = isServerCompletelyStarted(); std::lock_guard lock(shared->auxiliary_zookeepers_mutex); shared->auxiliary_zookeepers_config = config; @@ -2619,7 +2627,7 @@ void Context::reloadAuxiliaryZooKeepersConfigIfChanged(const ConfigurationPtr & it = shared->auxiliary_zookeepers.erase(it); else { - reloadZooKeeperIfChangedImpl(config, "auxiliary_zookeepers." + it->first, it->second, getZooKeeperLog()); + reloadZooKeeperIfChangedImpl(config, "auxiliary_zookeepers." + it->first, it->second, getZooKeeperLog(), server_started); ++it; } } @@ -3695,6 +3703,15 @@ bool Context::isServerCompletelyStarted() const void Context::setServerCompletelyStarted() { + { + std::lock_guard lock(shared->zookeeper_mutex); + if (shared->zookeeper) + shared->zookeeper->setServerCompletelyStarted(); + + for (auto & zk : shared->auxiliary_zookeepers) + zk.second->setServerCompletelyStarted(); + } + auto lock = getLock(); assert(global_context.lock().get() == this); assert(!shared->is_server_completely_started); From c47dcb0805b0787abc6db6134aed935f6a3e708f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Mon, 27 Mar 2023 15:07:59 +0200 Subject: [PATCH 088/377] Enable ZK fault injection on new servers (but not during startup) --- docker/test/upgrade/run.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/test/upgrade/run.sh b/docker/test/upgrade/run.sh index b9abe5b51fe..fce90ca2537 100644 --- a/docker/test/upgrade/run.sh +++ b/docker/test/upgrade/run.sh @@ -109,8 +109,7 @@ mv /var/log/clickhouse-server/clickhouse-server.log /var/log/clickhouse-server/c # Install and start new server install_packages package_folder -# Disable fault injections on start (we don't test them here, and it can lead to tons of requests in case of huge number of tables). -export ZOOKEEPER_FAULT_INJECTION=0 +export ZOOKEEPER_FAULT_INJECTION=1 configure start 500 clickhouse-client --query "SELECT 'Server successfully started', 'OK', NULL, ''" >> /test_output/test_results.tsv \ From fa8ea85f3d31dcc1a6cd5913af3ae103e00045f7 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 27 Mar 2023 13:07:24 +0000 Subject: [PATCH 089/377] Make custom key work with new analyzer --- src/Interpreters/InterpreterSelectQuery.cpp | 4 +- src/Planner/Planner.cpp | 74 +-------- src/Planner/PlannerExpressionAnalysis.cpp | 37 +++-- src/Planner/PlannerExpressionAnalysis.h | 6 - src/Planner/PlannerJoinTree.cpp | 164 ++++++++++++++------ 5 files changed, 143 insertions(+), 142 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 273d81ff9f9..e471f279a14 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -518,15 +518,13 @@ InterpreterSelectQuery::InterpreterSelectQuery( settings.additional_table_filters, joined_tables.tablesWithColumns().front().table, *context); ASTPtr parallel_replicas_custom_filter_ast = nullptr; - if (context->getParallelReplicasMode() == Context::ParallelReplicasMode::CUSTOM_KEY && !joined_tables.tablesWithColumns().empty()) + if (storage && context->getParallelReplicasMode() == Context::ParallelReplicasMode::CUSTOM_KEY && !joined_tables.tablesWithColumns().empty()) { if (settings.parallel_replicas_count > 1) { if (auto custom_key_ast = parseCustomKeyForTable(settings.parallel_replicas_custom_key, *context)) { LOG_TRACE(log, "Processing query on a replica using custom_key '{}'", settings.parallel_replicas_custom_key.value); - if (!storage) - throw DB::Exception(ErrorCodes::BAD_ARGUMENTS, "Storage is unknown when trying to parse custom key for parallel replica"); parallel_replicas_custom_filter_ast = getCustomKeyFilterForParallelReplica( settings.parallel_replicas_count, diff --git a/src/Planner/Planner.cpp b/src/Planner/Planner.cpp index c233941b6b5..e38f460e7c5 100644 --- a/src/Planner/Planner.cpp +++ b/src/Planner/Planner.cpp @@ -1136,67 +1136,17 @@ void Planner::buildPlanForQueryNode() collectTableExpressionData(query_tree, planner_context); const auto & settings = query_context->getSettingsRef(); - const auto & table_expression_data = planner_context->getTableExpressionNodeToData(); - auto & mutable_context = planner_context->getMutableQueryContext(); - - QueryTreeNodePtr parallel_replicas_custom_filter_node{nullptr}; - if (table_expression_data.size() > 1 && (!settings.parallel_replicas_custom_key.value.empty() || settings.allow_experimental_parallel_reading_from_replicas)) + if (planner_context->getTableExpressionNodeToData().size() > 1 + && (!settings.parallel_replicas_custom_key.value.empty() || settings.allow_experimental_parallel_reading_from_replicas)) { - LOG_WARNING(&Poco::Logger::get("Planner"), "Joins are not supported with parallel replicas. Query will be executed without using them."); + LOG_WARNING( + &Poco::Logger::get("Planner"), "Joins are not supported with parallel replicas. Query will be executed without using them."); + + auto & mutable_context = planner_context->getMutableQueryContext(); mutable_context->setSetting("allow_experimental_parallel_reading_from_replicas", false); mutable_context->setSetting("parallel_replicas_custom_key", String{""}); } - else if (table_expression_data.size() == 1 && !settings.parallel_replicas_custom_key.value.empty()) - { - const auto & table_expression = (*table_expression_data.begin()).first; - - StoragePtr storage{nullptr}; - if (const auto * table_node = table_expression->as()) - storage = table_node->getStorage(); - else if (const auto * table_function_node = table_expression->as()) - storage = table_function_node->getStorage(); - - std::cout << "COUNT: " << settings.parallel_replicas_count << std::endl; - if (settings.parallel_replicas_count > 1) - { - if (auto custom_key_ast = parseCustomKeyForTable(settings.parallel_replicas_custom_key, *query_context)) - { - LOG_TRACE(&Poco::Logger::get("Planner"), "Processing query on a replica using custom_key '{}'", settings.parallel_replicas_custom_key.value); - if (!storage) - throw DB::Exception(ErrorCodes::BAD_ARGUMENTS, "Storage is unknown when trying to parse custom key for parallel replica"); - - auto parallel_replicas_custom_filter_ast = getCustomKeyFilterForParallelReplica( - settings.parallel_replicas_count, - settings.parallel_replica_offset, - std::move(custom_key_ast), - settings.parallel_replicas_custom_key_filter_type, - *storage, - query_context); - - parallel_replicas_custom_filter_node = buildQueryTree(parallel_replicas_custom_filter_ast, query_context); - QueryAnalysisPass pass(table_expression); - pass.run(parallel_replicas_custom_filter_node, query_context); - } - else if (settings.parallel_replica_offset > 0) - { - throw Exception( - ErrorCodes::BAD_ARGUMENTS, - "Parallel replicas processing with custom_key has been requested " - "(setting 'max_parallel_replicas') but the table does not have custom_key defined for it " - "or it's invalid (settings `parallel_replicas_custom_key`)"); - } - } - else if (storage) - { - if (auto * distributed = dynamic_cast(storage.get()); - distributed && canUseCustomKey(settings, *distributed->getCluster(), *query_context)) - { - select_query_info.use_custom_key = true; - mutable_context->setSetting("distributed_group_by_no_merge", 2); - } - } - } auto top_level_identifiers = collectTopLevelColumnIdentifiers(query_tree, planner_context); auto join_tree_query_plan = buildJoinTreeQueryPlan(query_tree, @@ -1216,15 +1166,6 @@ void Planner::buildPlanForQueryNode() if (select_query_options.to_stage == QueryProcessingStage::FetchColumns) return; - std::optional parallel_replicas_custom_filter_info; - if (parallel_replicas_custom_filter_node) - { - ActionsChain chain; - parallel_replicas_custom_filter_info = analyzeFilter(parallel_replicas_custom_filter_node, query_plan.getCurrentDataStream().header.getColumnsWithTypeAndName(), planner_context, chain); - parallel_replicas_custom_filter_info->remove_filter_column = true; - select_query_info.filter_asts.push_back(parallel_replicas_custom_filter_node->toAST()); - } - PlannerQueryProcessingInfo query_processing_info(from_stage, select_query_options.to_stage); QueryAnalysisResult query_analysis_result(query_tree, query_processing_info, planner_context); auto expression_analysis_result = buildExpressionAnalysisResult(query_tree, @@ -1262,9 +1203,6 @@ void Planner::buildPlanForQueryNode() if (expression_analysis_result.hasWhere()) addFilterStep(query_plan, expression_analysis_result.getWhere(), "WHERE", result_actions_to_execute); - if (parallel_replicas_custom_filter_info) - addFilterStep(query_plan, *parallel_replicas_custom_filter_info, "Parallel replica custom key filter", result_actions_to_execute); - if (expression_analysis_result.hasAggregation()) { const auto & aggregation_analysis_result = expression_analysis_result.getAggregation(); diff --git a/src/Planner/PlannerExpressionAnalysis.cpp b/src/Planner/PlannerExpressionAnalysis.cpp index cd03346416e..b6b6b2e2d85 100644 --- a/src/Planner/PlannerExpressionAnalysis.cpp +++ b/src/Planner/PlannerExpressionAnalysis.cpp @@ -29,6 +29,24 @@ namespace ErrorCodes namespace { +/** Construct filter analysis result for filter expression node + * Actions before filter are added into into actions chain. + * It is client responsibility to update filter analysis result if filter column must be removed after chain is finalized. + */ +FilterAnalysisResult analyzeFilter(const QueryTreeNodePtr & filter_expression_node, + const ColumnsWithTypeAndName & input_columns, + const PlannerContextPtr & planner_context, + ActionsChain & actions_chain) +{ + FilterAnalysisResult result; + + result.filter_actions = buildActionsDAGFromExpressionNode(filter_expression_node, input_columns, planner_context); + result.filter_column_name = result.filter_actions->getOutputs().at(0)->result_name; + actions_chain.addStep(std::make_unique(result.filter_actions)); + + return result; +} + /** Construct aggregation analysis result if query tree has GROUP BY or aggregates. * Actions before aggregation are added into actions chain, if result is not null optional. */ @@ -450,25 +468,6 @@ LimitByAnalysisResult analyzeLimitBy(const QueryNode & query_node, } -/** Construct filter analysis result for filter expression node - * Actions before filter are added into into actions chain. - * It is client responsibility to update filter analysis result if filter column must be removed after chain is finalized. - */ -FilterAnalysisResult analyzeFilter(const QueryTreeNodePtr & filter_expression_node, - const ColumnsWithTypeAndName & input_columns, - const PlannerContextPtr & planner_context, - ActionsChain & actions_chain) -{ - FilterAnalysisResult result; - - result.filter_actions = buildActionsDAGFromExpressionNode(filter_expression_node, input_columns, planner_context); - result.filter_column_name = result.filter_actions->getOutputs().at(0)->result_name; - actions_chain.addStep(std::make_unique(result.filter_actions)); - - return result; -} - - PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNodePtr & query_tree, const ColumnsWithTypeAndName & join_tree_input_columns, const PlannerContextPtr & planner_context, diff --git a/src/Planner/PlannerExpressionAnalysis.h b/src/Planner/PlannerExpressionAnalysis.h index ddf9f120cdb..792cfdec2ff 100644 --- a/src/Planner/PlannerExpressionAnalysis.h +++ b/src/Planner/PlannerExpressionAnalysis.h @@ -7,7 +7,6 @@ #include -#include #include #include #include @@ -170,11 +169,6 @@ private: LimitByAnalysisResult limit_by_analysis_result; }; -FilterAnalysisResult analyzeFilter(const QueryTreeNodePtr & filter_expression_node, - const ColumnsWithTypeAndName & input_columns, - const PlannerContextPtr & planner_context, - ActionsChain & actions_chain); - /// Build expression analysis result for query tree, join tree input columns and planner context PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNodePtr & query_tree, const ColumnsWithTypeAndName & join_tree_input_columns, diff --git a/src/Planner/PlannerJoinTree.cpp b/src/Planner/PlannerJoinTree.cpp index 79005612b0a..a18ebbe8f9e 100644 --- a/src/Planner/PlannerJoinTree.cpp +++ b/src/Planner/PlannerJoinTree.cpp @@ -1,6 +1,7 @@ #include #include +#include "Storages/SelectQueryInfo.h" #include @@ -17,6 +18,7 @@ #include #include +#include #include #include @@ -47,6 +49,7 @@ #include #include #include +#include #include #include @@ -381,6 +384,46 @@ void updatePrewhereOutputsIfNeeded(SelectQueryInfo & table_expression_query_info prewhere_outputs.insert(prewhere_outputs.end(), required_output_nodes.begin(), required_output_nodes.end()); } +FilterDAGInfo buildFilterInfo(ASTPtr filter_expression, + SelectQueryInfo & table_expression_query_info, + PlannerContextPtr & planner_context) +{ + const auto & query_context = planner_context->getQueryContext(); + + auto filter_query_tree = buildQueryTree(filter_expression, query_context); + + QueryAnalysisPass query_analysis_pass(table_expression_query_info.table_expression); + query_analysis_pass.run(filter_query_tree, query_context); + + auto & table_expression_data = planner_context->getTableExpressionDataOrThrow(table_expression_query_info.table_expression); + const auto table_expression_names = table_expression_data.getColumnNames(); + NameSet table_expression_required_names_without_filter(table_expression_names.begin(), table_expression_names.end()); + + collectSourceColumns(filter_query_tree, planner_context); + collectSets(filter_query_tree, *planner_context); + + auto filter_actions_dag = std::make_shared(); + + PlannerActionsVisitor actions_visitor(planner_context, false /*use_column_identifier_as_action_node_name*/); + auto expression_nodes = actions_visitor.visit(filter_actions_dag, filter_query_tree); + if (expression_nodes.size() != 1) + throw Exception(ErrorCodes::BAD_ARGUMENTS, + "Filter actions must return single output node. Actual {}", + expression_nodes.size()); + + auto & filter_actions_outputs = filter_actions_dag->getOutputs(); + filter_actions_outputs = std::move(expression_nodes); + + std::string filter_node_name = filter_actions_outputs[0]->result_name; + bool remove_filter_column = true; + + for (const auto & filter_input_node : filter_actions_dag->getInputs()) + if (table_expression_required_names_without_filter.contains(filter_input_node->result_name)) + filter_actions_outputs.push_back(filter_input_node); + + return {std::move(filter_actions_dag), std::move(filter_node_name), remove_filter_column}; +} + FilterDAGInfo buildRowPolicyFilterIfNeeded(const StoragePtr & storage, SelectQueryInfo & table_expression_query_info, PlannerContextPtr & planner_context) @@ -392,38 +435,38 @@ FilterDAGInfo buildRowPolicyFilterIfNeeded(const StoragePtr & storage, if (!row_policy_filter) return {}; - auto row_policy_filter_query_tree = buildQueryTree(row_policy_filter->expression, query_context); + return buildFilterInfo(row_policy_filter->expression, table_expression_query_info, planner_context); +} - QueryAnalysisPass query_analysis_pass(table_expression_query_info.table_expression); - query_analysis_pass.run(row_policy_filter_query_tree, query_context); +FilterDAGInfo buildCustomKeyFilterIfNeeded(const StoragePtr & storage, + SelectQueryInfo & table_expression_query_info, + PlannerContextPtr & planner_context) +{ + const auto & query_context = planner_context->getQueryContext(); + const auto & settings = query_context->getSettingsRef(); - auto & table_expression_data = planner_context->getTableExpressionDataOrThrow(table_expression_query_info.table_expression); - const auto table_expression_names = table_expression_data.getColumnNames(); - NameSet table_expression_required_names_without_row_policy(table_expression_names.begin(), table_expression_names.end()); + if (settings.parallel_replicas_count <= 1 || settings.parallel_replicas_custom_key.value.empty()) + return {}; - collectSourceColumns(row_policy_filter_query_tree, planner_context); - collectSets(row_policy_filter_query_tree, *planner_context); + auto custom_key_ast = parseCustomKeyForTable(settings.parallel_replicas_custom_key, *query_context); + if (!custom_key_ast) + throw DB::Exception( + ErrorCodes::BAD_ARGUMENTS, + "Parallel replicas processing iwth custom_key has been requested " + "(setting 'max_parallel_replcias'), but the table does not have custom_key defined for it " + " or it's invalid (setting 'parallel_replicas_custom_key')"); - auto row_policy_actions_dag = std::make_shared(); + LOG_TRACE(&Poco::Logger::get("Planner"), "Processing query on a replica using custom_key '{}'", settings.parallel_replicas_custom_key.value); - PlannerActionsVisitor actions_visitor(planner_context, false /*use_column_identifier_as_action_node_name*/); - auto expression_nodes = actions_visitor.visit(row_policy_actions_dag, row_policy_filter_query_tree); - if (expression_nodes.size() != 1) - throw Exception(ErrorCodes::BAD_ARGUMENTS, - "Row policy filter actions must return single output node. Actual {}", - expression_nodes.size()); + auto parallel_replicas_custom_filter_ast = getCustomKeyFilterForParallelReplica( + settings.parallel_replicas_count, + settings.parallel_replica_offset, + std::move(custom_key_ast), + settings.parallel_replicas_custom_key_filter_type, + *storage, + query_context); - auto & row_policy_actions_outputs = row_policy_actions_dag->getOutputs(); - row_policy_actions_outputs = std::move(expression_nodes); - - std::string filter_node_name = row_policy_actions_outputs[0]->result_name; - bool remove_filter_column = true; - - for (const auto & row_policy_input_node : row_policy_actions_dag->getInputs()) - if (table_expression_required_names_without_row_policy.contains(row_policy_input_node->result_name)) - row_policy_actions_outputs.push_back(row_policy_input_node); - - return {std::move(row_policy_actions_dag), std::move(filter_node_name), remove_filter_column}; + return buildFilterInfo(parallel_replicas_custom_filter_ast, table_expression_query_info, planner_context); } JoinTreeQueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expression, @@ -596,11 +639,14 @@ JoinTreeQueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expres updatePrewhereOutputsIfNeeded(table_expression_query_info, table_expression_data.getColumnNames(), storage_snapshot); - auto row_policy_filter_info = buildRowPolicyFilterIfNeeded(storage, table_expression_query_info, planner_context); - bool moved_row_policy_to_prewhere = false; + const auto & columns_names = table_expression_data.getColumnNames(); - if (row_policy_filter_info.actions) + std::vector> where_filters; + const auto add_filter = [&](const FilterDAGInfo & filter_info, std::string description) { + if (!filter_info.actions) + return; + bool is_final = table_expression_query_info.table_expression_modifiers && table_expression_query_info.table_expression_modifiers->hasFinal(); bool optimize_move_to_prewhere = settings.optimize_move_to_prewhere && (!is_final || settings.optimize_move_to_prewhere_if_final); @@ -612,36 +658,62 @@ JoinTreeQueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expres if (!table_expression_query_info.prewhere_info->prewhere_actions) { - table_expression_query_info.prewhere_info->prewhere_actions = row_policy_filter_info.actions; - table_expression_query_info.prewhere_info->prewhere_column_name = row_policy_filter_info.column_name; - table_expression_query_info.prewhere_info->remove_prewhere_column = row_policy_filter_info.do_remove_column; + table_expression_query_info.prewhere_info->prewhere_actions = filter_info.actions; + table_expression_query_info.prewhere_info->prewhere_column_name = filter_info.column_name; + table_expression_query_info.prewhere_info->remove_prewhere_column = filter_info.do_remove_column; } else { - table_expression_query_info.prewhere_info->row_level_filter = row_policy_filter_info.actions; - table_expression_query_info.prewhere_info->row_level_column_name = row_policy_filter_info.column_name; + table_expression_query_info.prewhere_info->row_level_filter = filter_info.actions; + table_expression_query_info.prewhere_info->row_level_column_name = filter_info.column_name; } table_expression_query_info.prewhere_info->need_filter = true; - moved_row_policy_to_prewhere = true; + } + else + { + where_filters.emplace_back(filter_info, std::move(description)); + } + }; + + auto row_policy_filter_info = buildRowPolicyFilterIfNeeded(storage, table_expression_query_info, planner_context); + add_filter(row_policy_filter_info, "Row-level security filter"); + + if (query_context->getParallelReplicasMode() == Context::ParallelReplicasMode::CUSTOM_KEY) + { + if (settings.parallel_replicas_count > 1) + { + auto parallel_replicas_custom_key_filter_info = buildCustomKeyFilterIfNeeded(storage, table_expression_query_info, planner_context); + add_filter(parallel_replicas_custom_key_filter_info, "Parallel replicas custom key filter"); + } + else + { + if (auto * distributed = dynamic_cast(storage.get()); + distributed && canUseCustomKey(settings, *distributed->getCluster(), *query_context)) + { + table_expression_query_info.use_custom_key = true; + planner_context->getMutableQueryContext()->setSetting("distributed_group_by_no_merge", 2); + } } } - const auto & columns_names = table_expression_data.getColumnNames(); from_stage = storage->getQueryProcessingStage(query_context, select_query_options.to_stage, storage_snapshot, table_expression_query_info); storage->read(query_plan, columns_names, storage_snapshot, table_expression_query_info, query_context, from_stage, max_block_size, max_streams); - if (query_plan.isInitialized() && - from_stage == QueryProcessingStage::FetchColumns && - row_policy_filter_info.actions && - !moved_row_policy_to_prewhere) + for (const auto & filter_info_and_description : where_filters) { - auto row_level_filter_step = std::make_unique(query_plan.getCurrentDataStream(), - row_policy_filter_info.actions, - row_policy_filter_info.column_name, - row_policy_filter_info.do_remove_column); - row_level_filter_step->setStepDescription("Row-level security filter"); - query_plan.addStep(std::move(row_level_filter_step)); + const auto & [filter_info, description] = filter_info_and_description; + if (query_plan.isInitialized() && + from_stage == QueryProcessingStage::FetchColumns && + filter_info.actions) + { + auto filter_step = std::make_unique(query_plan.getCurrentDataStream(), + filter_info.actions, + filter_info.column_name, + filter_info.do_remove_column); + filter_step->setStepDescription(description); + query_plan.addStep(std::move(filter_step)); + } } if (query_context->hasQueryContext() && !select_query_options.is_internal) From 28182e9489632a09b10fb3dccae05ae0f508e625 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 27 Mar 2023 15:51:42 +0200 Subject: [PATCH 090/377] Fix typo --- src/Planner/PlannerJoinTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Planner/PlannerJoinTree.cpp b/src/Planner/PlannerJoinTree.cpp index a18ebbe8f9e..1741cca17c5 100644 --- a/src/Planner/PlannerJoinTree.cpp +++ b/src/Planner/PlannerJoinTree.cpp @@ -452,7 +452,7 @@ FilterDAGInfo buildCustomKeyFilterIfNeeded(const StoragePtr & storage, if (!custom_key_ast) throw DB::Exception( ErrorCodes::BAD_ARGUMENTS, - "Parallel replicas processing iwth custom_key has been requested " + "Parallel replicas processing with custom_key has been requested " "(setting 'max_parallel_replcias'), but the table does not have custom_key defined for it " " or it's invalid (setting 'parallel_replicas_custom_key')"); From 5a18e0d5a58794c0e344a2312972592601087d4f Mon Sep 17 00:00:00 2001 From: xiedeyantu Date: Tue, 21 Mar 2023 20:14:26 +0800 Subject: [PATCH 091/377] support undrop table --- src/Access/Common/AccessType.h | 2 + src/Access/tests/gtest_access_rights_ops.cpp | 2 +- src/Core/Settings.h | 1 + src/Databases/DatabaseAtomic.cpp | 10 ++ src/Databases/DatabaseAtomic.h | 2 + src/Databases/IDatabase.h | 5 + src/Interpreters/DatabaseCatalog.cpp | 123 +++++++++++++++++- src/Interpreters/DatabaseCatalog.h | 2 + src/Interpreters/InterpreterFactory.cpp | 6 + src/Interpreters/InterpreterUndropQuery.cpp | 77 +++++++++++ src/Interpreters/InterpreterUndropQuery.h | 29 +++++ src/Parsers/ASTUndropQuery.cpp | 42 ++++++ src/Parsers/ASTUndropQuery.h | 30 +++++ src/Parsers/IAST.h | 1 + src/Parsers/ParserQueryWithOutput.cpp | 3 + src/Parsers/ParserUndropQuery.cpp | 81 ++++++++++++ src/Parsers/ParserUndropQuery.h | 20 +++ .../integration/test_grant_and_revoke/test.py | 2 +- .../integration/test_undrop_query/__init__.py | 0 .../configs/with_delay_config.xml | 3 + tests/integration/test_undrop_query/test.py | 59 +++++++++ .../01271_show_privileges.reference | 1 + .../02117_show_create_table_system.reference | 8 +- .../0_stateless/02681_undrop_query.reference | 32 +++++ .../0_stateless/02681_undrop_query.sql | 90 +++++++++++++ .../02681_undrop_query_uuid.reference | 5 + .../0_stateless/02681_undrop_query_uuid.sh | 17 +++ 27 files changed, 646 insertions(+), 7 deletions(-) create mode 100644 src/Interpreters/InterpreterUndropQuery.cpp create mode 100644 src/Interpreters/InterpreterUndropQuery.h create mode 100644 src/Parsers/ASTUndropQuery.cpp create mode 100644 src/Parsers/ASTUndropQuery.h create mode 100644 src/Parsers/ParserUndropQuery.cpp create mode 100644 src/Parsers/ParserUndropQuery.h create mode 100644 tests/integration/test_undrop_query/__init__.py create mode 100644 tests/integration/test_undrop_query/configs/with_delay_config.xml create mode 100644 tests/integration/test_undrop_query/test.py create mode 100644 tests/queries/0_stateless/02681_undrop_query.reference create mode 100644 tests/queries/0_stateless/02681_undrop_query.sql create mode 100644 tests/queries/0_stateless/02681_undrop_query_uuid.reference create mode 100755 tests/queries/0_stateless/02681_undrop_query_uuid.sh diff --git a/src/Access/Common/AccessType.h b/src/Access/Common/AccessType.h index a7827ee7c59..57fa75dc67b 100644 --- a/src/Access/Common/AccessType.h +++ b/src/Access/Common/AccessType.h @@ -104,6 +104,8 @@ enum class AccessType M(DROP_NAMED_COLLECTION, "", NAMED_COLLECTION, NAMED_COLLECTION_CONTROL) /* allows to execute DROP NAMED COLLECTION */\ M(DROP, "", GROUP, ALL) /* allows to execute {DROP|DETACH} */\ \ + M(UNDROP_TABLE, "", TABLE, ALL) /* allows to execute {UNDROP} TABLE */\ + \ M(TRUNCATE, "TRUNCATE TABLE", TABLE, ALL) \ M(OPTIMIZE, "OPTIMIZE TABLE", TABLE, ALL) \ M(BACKUP, "", TABLE, ALL) /* allows to backup tables */\ diff --git a/src/Access/tests/gtest_access_rights_ops.cpp b/src/Access/tests/gtest_access_rights_ops.cpp index 025f70af587..5f1f13ca5a2 100644 --- a/src/Access/tests/gtest_access_rights_ops.cpp +++ b/src/Access/tests/gtest_access_rights_ops.cpp @@ -48,7 +48,7 @@ TEST(AccessRights, Union) ASSERT_EQ(lhs.toString(), "GRANT INSERT ON *.*, " "GRANT SHOW, SELECT, ALTER, CREATE DATABASE, CREATE TABLE, CREATE VIEW, " - "CREATE DICTIONARY, DROP DATABASE, DROP TABLE, DROP VIEW, DROP DICTIONARY, " + "CREATE DICTIONARY, DROP DATABASE, DROP TABLE, DROP VIEW, DROP DICTIONARY, UNDROP TABLE, " "TRUNCATE, OPTIMIZE, BACKUP, CREATE ROW POLICY, ALTER ROW POLICY, DROP ROW POLICY, " "SHOW ROW POLICIES, SYSTEM MERGES, SYSTEM TTL MERGES, SYSTEM FETCHES, " "SYSTEM MOVES, SYSTEM SENDS, SYSTEM REPLICATION QUEUES, " diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 9fa2ba0d32f..acc20c85165 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -720,6 +720,7 @@ class IColumn; M(UInt64, insert_keeper_fault_injection_seed, 0, "0 - random seed, otherwise the setting value", 0) \ M(Bool, force_aggregation_in_order, false, "Force use of aggregation in order on remote nodes during distributed aggregation. PLEASE, NEVER CHANGE THIS SETTING VALUE MANUALLY!", IMPORTANT) \ M(UInt64, http_max_request_param_data_size, 10_MiB, "Limit on size of request data used as a query parameter in predefined HTTP requests.", 0) \ + M(Bool, allow_experimental_undrop_table_query, false, "Allow to use undrop query to restore dropped table in a limited time", 0) \ // End of COMMON_SETTINGS // Please add settings related to formats into the FORMAT_FACTORY_SETTINGS and move obsolete settings to OBSOLETE_SETTINGS. diff --git a/src/Databases/DatabaseAtomic.cpp b/src/Databases/DatabaseAtomic.cpp index 7e20b6f6535..e5320dc6ff4 100644 --- a/src/Databases/DatabaseAtomic.cpp +++ b/src/Databases/DatabaseAtomic.cpp @@ -110,6 +110,16 @@ StoragePtr DatabaseAtomic::detachTable(ContextPtr /* context */, const String & return table; } +void DatabaseAtomic::undropTable(ContextPtr /* context_ */, const String & table_name, const StoragePtr & table, const String & relative_table_path) +{ + std::lock_guard lock(mutex); + String table_metadata_path = getObjectMetadataPath(table_name); + String table_metadata_path_drop = DatabaseCatalog::instance().getPathForDroppedMetadata(table->getStorageID()); + renameNoReplace(table_metadata_path_drop, table_metadata_path); + DatabaseOrdinary::attachTableUnlocked(table_name, table); + table_name_to_path.emplace(std::make_pair(table_name, relative_table_path)); +} + void DatabaseAtomic::dropTable(ContextPtr local_context, const String & table_name, bool sync) { auto table = tryGetTable(table_name, local_context); diff --git a/src/Databases/DatabaseAtomic.h b/src/Databases/DatabaseAtomic.h index cb275812098..b8ff719989b 100644 --- a/src/Databases/DatabaseAtomic.h +++ b/src/Databases/DatabaseAtomic.h @@ -41,6 +41,8 @@ public: void attachTable(ContextPtr context, const String & name, const StoragePtr & table, const String & relative_table_path) override; StoragePtr detachTable(ContextPtr context, const String & name) override; + void undropTable(ContextPtr context, const String & table_name, const StoragePtr & table, const String & relative_table_path) override; + String getTableDataPath(const String & table_name) const override; String getTableDataPath(const ASTCreateQuery & query) const override; diff --git a/src/Databases/IDatabase.h b/src/Databases/IDatabase.h index b8880c4c4cc..4dbf78cb5c4 100644 --- a/src/Databases/IDatabase.h +++ b/src/Databases/IDatabase.h @@ -216,6 +216,11 @@ public: throw Exception(ErrorCodes::NOT_IMPLEMENTED, "There is no DETACH TABLE query for Database{}", getEngineName()); } + virtual void undropTable(ContextPtr /* context */, const String & /*name*/, const StoragePtr & /*table*/, [[maybe_unused]] const String & relative_table_path = {}) /// NOLINT + { + throw Exception(ErrorCodes::NOT_IMPLEMENTED, "There is no UNDROP TABLE query for Database{}", getEngineName()); + } + /// Forget about the table without deleting it's data, but rename metadata file to prevent reloading it /// with next restart. The database may not support this method. virtual void detachTablePermanently(ContextPtr /*context*/, const String & /*name*/) diff --git a/src/Interpreters/DatabaseCatalog.cpp b/src/Interpreters/DatabaseCatalog.cpp index b11a973c7b7..4b4032f65a8 100644 --- a/src/Interpreters/DatabaseCatalog.cpp +++ b/src/Interpreters/DatabaseCatalog.cpp @@ -51,6 +51,7 @@ namespace ErrorCodes extern const int DATABASE_ACCESS_DENIED; extern const int LOGICAL_ERROR; extern const int HAVE_DEPENDENT_OBJECTS; + extern const int FS_METADATA_ERROR; } TemporaryTableHolder::TemporaryTableHolder(ContextPtr context_, const TemporaryTableHolder::Creator & creator, const ASTPtr & query) @@ -869,6 +870,13 @@ String DatabaseCatalog::getPathForDroppedMetadata(const StorageID & table_id) co toString(table_id.uuid) + ".sql"; } +String DatabaseCatalog::getPathForMetadata(const StorageID & table_id) const +{ + return getContext()->getPath() + "metadata/" + + escapeForFileName(table_id.getDatabaseName()) + "/" + + escapeForFileName(table_id.getTableName()) + ".sql"; +} + void DatabaseCatalog::enqueueDroppedTableCleanup(StorageID table_id, StoragePtr table, String dropped_metadata_path, bool ignore_delay) { assert(table_id.hasUUID()); @@ -936,6 +944,118 @@ void DatabaseCatalog::enqueueDroppedTableCleanup(StorageID table_id, StoragePtr (*drop_task)->schedule(); } +void DatabaseCatalog::dequeueDroppedTableCleanup(StorageID table_id) +{ + String latest_metadata_dropped_path; + StorageID dropped_table_id = table_id; + TableMarkedAsDropped dropped_table; + { + std::lock_guard lock(tables_marked_dropped_mutex); + time_t latest_drop_time = std::numeric_limits::min(); + auto it_table = tables_marked_dropped.end(); + for (auto it = tables_marked_dropped.begin(); it != tables_marked_dropped.end(); ++it) + { + auto storage_ptr = it->table; + if (it->table_id.uuid == table_id.uuid) + { + it_table = it; + dropped_table = *it; + break; + } + /// If table uuid exists, only find tables with equal uuid. + if (table_id.uuid != UUIDHelpers::Nil) + continue; + if (it->table_id.database_name == table_id.database_name && + it->table_id.table_name == table_id.table_name && + it->drop_time >= latest_drop_time) + { + latest_drop_time = it->drop_time; + it_table = it; + dropped_table = *it; + } + } + if (it_table == tables_marked_dropped.end()) + throw Exception(ErrorCodes::UNKNOWN_TABLE, + "The drop task of table {} is in progress, has been dropped or the database engine doesn't support it", + table_id.getNameForLogs()); + latest_metadata_dropped_path = it_table->metadata_path; + dropped_table_id = it_table->table_id; + tables_marked_dropped.erase(it_table); + [[maybe_unused]] auto removed = tables_marked_dropped_ids.erase(dropped_table_id.uuid); + assert(removed); + } + /// Remove the table from tables_marked_dropped and tables_marked_dropped_ids, + /// and the drop task for this table will no longer be scheduled. + LOG_INFO(log, "Trying Undrop table {} from {}", dropped_table_id.getNameForLogs(), latest_metadata_dropped_path); + String table_metadata_path = getPathForMetadata(dropped_table_id); + + auto enqueue = [&]() + { + /// In the dropTableDataTask method. + /// 1. We will first determine whether there are tables to be dropped in tables_marked_dropped. + /// 2. If one is exist, the table will be removed from tables_marked_dropped. + /// 3. And then execute dropTableFinally. + /// So undrop and drop do not cross-execute. + std::lock_guard lock(tables_marked_dropped_mutex); + tables_marked_dropped.emplace_back(dropped_table); + tables_marked_dropped_ids.insert(dropped_table_id.uuid); + CurrentMetrics::add(CurrentMetrics::TablesToDropQueueSize, 1); + }; + + ASTPtr ast = DatabaseOnDisk::parseQueryFromMetadata( + log, getContext(), latest_metadata_dropped_path, /*throw_on_error*/ true, /*remove_empty*/ false); + auto * create = typeid_cast(ast.get()); + if (!create) + { + enqueue(); + throw Exception( + ErrorCodes::FS_METADATA_ERROR, + "Cannot parse metadata of table {} from {}", + dropped_table_id.getNameForLogs(), + table_metadata_path); + } + + create->setDatabase(dropped_table_id.database_name); + create->setTable(dropped_table_id.table_name); + + try + { + auto wait_dropped_table_not_in_use = [&]() + { + while (true) + { + { + std::lock_guard lock(tables_marked_dropped_mutex); + if (dropped_table.table.unique()) + return; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }; + wait_dropped_table_not_in_use(); + auto database = getDatabase(dropped_table_id.database_name, getContext()); + String relative_table_path = fs::path(database->getDataPath()) / DatabaseCatalog::instance().getPathForUUID(dropped_table.table_id.uuid); + auto storage = createTableFromAST( + *create, + dropped_table.table_id.database_name, + relative_table_path, + getContext(), + /* force_restore */ true).second; + database->undropTable(getContext(), dropped_table_id.table_name, storage, relative_table_path); + storage->startup(); + CurrentMetrics::sub(CurrentMetrics::TablesToDropQueueSize, 1); + } + catch (...) + { + enqueue(); + throw Exception( + ErrorCodes::FS_METADATA_ERROR, + "Cannot undrop table {} from {}", + dropped_table_id.getNameForLogs(), + table_metadata_path); + } +} + void DatabaseCatalog::dropTableDataTask() { /// Background task that removes data of tables which were marked as dropped by Atomic databases. @@ -948,7 +1068,8 @@ void DatabaseCatalog::dropTableDataTask() try { std::lock_guard lock(tables_marked_dropped_mutex); - assert(!tables_marked_dropped.empty()); + if (tables_marked_dropped.empty()) + return; time_t current_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); time_t min_drop_time = std::numeric_limits::max(); size_t tables_in_use_count = 0; diff --git a/src/Interpreters/DatabaseCatalog.h b/src/Interpreters/DatabaseCatalog.h index 88645ff72af..51e9fbdb936 100644 --- a/src/Interpreters/DatabaseCatalog.h +++ b/src/Interpreters/DatabaseCatalog.h @@ -215,7 +215,9 @@ public: DatabaseAndTable tryGetByUUID(const UUID & uuid) const; String getPathForDroppedMetadata(const StorageID & table_id) const; + String getPathForMetadata(const StorageID & table_id) const; void enqueueDroppedTableCleanup(StorageID table_id, StoragePtr table, String dropped_metadata_path, bool ignore_delay = false); + void dequeueDroppedTableCleanup(StorageID table_id); void waitTableFinallyDropped(const UUID & uuid); diff --git a/src/Interpreters/InterpreterFactory.cpp b/src/Interpreters/InterpreterFactory.cpp index 502de459156..91291090e02 100644 --- a/src/Interpreters/InterpreterFactory.cpp +++ b/src/Interpreters/InterpreterFactory.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -161,6 +163,10 @@ std::unique_ptr InterpreterFactory::get(ASTPtr & query, ContextMut { return std::make_unique(query, context); } + else if (query->as()) + { + return std::make_unique(query, context); + } else if (query->as()) { return std::make_unique(query, context); diff --git a/src/Interpreters/InterpreterUndropQuery.cpp b/src/Interpreters/InterpreterUndropQuery.cpp new file mode 100644 index 00000000000..cc3b1b3ac24 --- /dev/null +++ b/src/Interpreters/InterpreterUndropQuery.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +#include "config.h" + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; + extern const int TABLE_ALREADY_EXISTS; + extern const int SUPPORT_IS_DISABLED; +} + +InterpreterUndropQuery::InterpreterUndropQuery(const ASTPtr & query_ptr_, ContextMutablePtr context_) : WithMutableContext(context_), query_ptr(query_ptr_) +{ +} + + +BlockIO InterpreterUndropQuery::execute() +{ + if (!getContext()->getSettingsRef().allow_experimental_undrop_table_query) + throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, + "Undrop table is experimental. " + "Set `allow_experimental_undrop_table_query` setting to enable it"); + + getContext()->checkAccess(AccessType::UNDROP_TABLE); + auto & undrop = query_ptr->as(); + if (!undrop.cluster.empty() && !maybeRemoveOnCluster(query_ptr, getContext())) + { + DDLQueryOnClusterParams params; + params.access_to_check = getRequiredAccessForDDLOnCluster(); + return executeDDLQueryOnCluster(query_ptr, getContext(), params); + } + + if (undrop.table) + return executeToTable(undrop); + else + throw Exception(ErrorCodes::LOGICAL_ERROR, "Nothing to undrop, both names are empty"); +} + +BlockIO InterpreterUndropQuery::executeToTable(ASTUndropQuery & query) +{ + auto table_id = StorageID(query); + auto guard = DatabaseCatalog::instance().getDDLGuard(table_id.database_name, table_id.table_name); + + auto context = getContext(); + if (table_id.database_name.empty()) + { + table_id.database_name = context->getCurrentDatabase(); + query.setDatabase(table_id.database_name); + } + + auto database = DatabaseCatalog::instance().getDatabase(table_id.database_name); + if (database->isTableExist(table_id.table_name, getContext())) + throw Exception( + ErrorCodes::TABLE_ALREADY_EXISTS, "Cannot Undrop table, {}.{} already exists", backQuote(table_id.database_name), backQuote(table_id.table_name)); + + database->checkMetadataFilenameAvailability(table_id.table_name); + + DatabaseCatalog::instance().dequeueDroppedTableCleanup(table_id); + return {}; +} + +AccessRightsElements InterpreterUndropQuery::getRequiredAccessForDDLOnCluster() const +{ + AccessRightsElements required_access; + const auto & undrop = query_ptr->as(); + + required_access.emplace_back(AccessType::UNDROP_TABLE, undrop.getDatabase(), undrop.getTable()); + return required_access; +} +} diff --git a/src/Interpreters/InterpreterUndropQuery.h b/src/Interpreters/InterpreterUndropQuery.h new file mode 100644 index 00000000000..a47617fd17f --- /dev/null +++ b/src/Interpreters/InterpreterUndropQuery.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +namespace DB +{ + +class Context; +using DatabaseAndTable = std::pair; +class AccessRightsElements; + + +class InterpreterUndropQuery : public IInterpreter, WithMutableContext +{ +public: + InterpreterUndropQuery(const ASTPtr & query_ptr_, ContextMutablePtr context_); + + /// Undrop table. + BlockIO execute() override; + +private: + AccessRightsElements getRequiredAccessForDDLOnCluster() const; + ASTPtr query_ptr; + + BlockIO executeToTable(ASTUndropQuery & query); +}; +} diff --git a/src/Parsers/ASTUndropQuery.cpp b/src/Parsers/ASTUndropQuery.cpp new file mode 100644 index 00000000000..0b8a18b12c9 --- /dev/null +++ b/src/Parsers/ASTUndropQuery.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + + +namespace DB +{ + +String ASTUndropQuery::getID(char delim) const +{ + return "UndropQuery" + (delim + getDatabase()) + delim + getTable(); +} + +ASTPtr ASTUndropQuery::clone() const +{ + auto res = std::make_shared(*this); + cloneOutputOptions(*res); + cloneTableOptions(*res); + return res; +} + +void ASTUndropQuery::formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const +{ + settings.ostr << (settings.hilite ? hilite_keyword : ""); + settings.ostr << "UNDROP "; + settings.ostr << "TABLE "; + settings.ostr << (settings.hilite ? hilite_none : ""); + + assert (table); + if (!database) + settings.ostr << backQuoteIfNeed(getTable()); + else + settings.ostr << backQuoteIfNeed(getDatabase()) + "." << backQuoteIfNeed(getTable()); + + if (uuid != UUIDHelpers::Nil) + settings.ostr << (settings.hilite ? hilite_keyword : "") << " UUID " << (settings.hilite ? hilite_none : "") + << quoteString(toString(uuid)); + + formatOnCluster(settings); +} + +} diff --git a/src/Parsers/ASTUndropQuery.h b/src/Parsers/ASTUndropQuery.h new file mode 100644 index 00000000000..7aac4c86c5b --- /dev/null +++ b/src/Parsers/ASTUndropQuery.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + + +namespace DB +{ + +/** UNDROP query + */ +class ASTUndropQuery : public ASTQueryWithTableAndOutput, public ASTQueryWithOnCluster +{ +public: + /** Get the text that identifies this element. */ + String getID(char) const override; + ASTPtr clone() const override; + + ASTPtr getRewrittenASTWithoutOnCluster(const WithoutOnClusterASTRewriteParams & params) const override + { + return removeOnCluster(clone(), params.default_database); + } + + QueryKind getQueryKind() const override { return QueryKind::Undrop; } + +protected: + void formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override; +}; + +} diff --git a/src/Parsers/IAST.h b/src/Parsers/IAST.h index 5928506aa5b..606a822ecee 100644 --- a/src/Parsers/IAST.h +++ b/src/Parsers/IAST.h @@ -268,6 +268,7 @@ public: Delete, Create, Drop, + Undrop, Rename, Optimize, Check, diff --git a/src/Parsers/ParserQueryWithOutput.cpp b/src/Parsers/ParserQueryWithOutput.cpp index 7024d8cbe11..d6cf9109be0 100644 --- a/src/Parsers/ParserQueryWithOutput.cpp +++ b/src/Parsers/ParserQueryWithOutput.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec ParserAlterQuery alter_p; ParserRenameQuery rename_p; ParserDropQuery drop_p; + ParserUndropQuery undrop_p; ParserCheckQuery check_p; ParserOptimizeQuery optimize_p; ParserKillQueryQuery kill_query_p; @@ -71,6 +73,7 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec || alter_p.parse(pos, query, expected) || rename_p.parse(pos, query, expected) || drop_p.parse(pos, query, expected) + || undrop_p.parse(pos, query, expected) || check_p.parse(pos, query, expected) || kill_query_p.parse(pos, query, expected) || optimize_p.parse(pos, query, expected) diff --git a/src/Parsers/ParserUndropQuery.cpp b/src/Parsers/ParserUndropQuery.cpp new file mode 100644 index 00000000000..3784ab0f353 --- /dev/null +++ b/src/Parsers/ParserUndropQuery.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include +#include "Parsers/ASTLiteral.h" + + +namespace DB +{ + +namespace +{ + +bool parseUndropQuery(IParser::Pos & pos, ASTPtr & node, Expected & expected) +{ + ParserKeyword s_table("TABLE"); + ParserToken s_dot(TokenType::Dot); + ParserIdentifier name_p(true); + + ASTPtr database; + ASTPtr table; + String cluster_str; + /// We can specify the table's uuid for exact undrop. + /// because the same name of a table can be created and deleted multiple times, + /// and can generate multiple different uuids. + UUID uuid = UUIDHelpers::Nil; + + if (!s_table.ignore(pos, expected)) + return false; + if (!name_p.parse(pos, table, expected)) + return false; + if (s_dot.ignore(pos, expected)) + { + database = table; + if (!name_p.parse(pos, table, expected)) + return false; + } + if (ParserKeyword("UUID").ignore(pos, expected)) + { + ParserStringLiteral uuid_p; + ASTPtr ast_uuid; + if (!uuid_p.parse(pos, ast_uuid, expected)) + return false; + uuid = parseFromString(ast_uuid->as()->value.get()); + } + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster_str, expected)) + return false; + } + auto query = std::make_shared(); + node = query; + + query->database = database; + query->table = table; + query->uuid = uuid; + + if (database) + query->children.push_back(database); + + assert (table); + query->children.push_back(table); + + query->cluster = cluster_str; + + return true; +} + +} + +bool ParserUndropQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) +{ + ParserKeyword s_undrop("UNDROP"); + + if (s_undrop.ignore(pos, expected)) + return parseUndropQuery(pos, node, expected); + else + return false; +} + +} diff --git a/src/Parsers/ParserUndropQuery.h b/src/Parsers/ParserUndropQuery.h new file mode 100644 index 00000000000..4b289600396 --- /dev/null +++ b/src/Parsers/ParserUndropQuery.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + + +namespace DB +{ + +/** Query like this: + * UNDROP TABLE [db.]name [UUID uuid] + */ +class ParserUndropQuery : public IParserBase +{ +protected: + const char * getName() const override{ return "UNDROP query"; } + bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; +}; + +} diff --git a/tests/integration/test_grant_and_revoke/test.py b/tests/integration/test_grant_and_revoke/test.py index 4d89e6255d3..4ad046fe5d2 100644 --- a/tests/integration/test_grant_and_revoke/test.py +++ b/tests/integration/test_grant_and_revoke/test.py @@ -184,7 +184,7 @@ def test_grant_all_on_table(): assert ( instance.query("SHOW GRANTS FOR B") == "GRANT SHOW TABLES, SHOW COLUMNS, SHOW DICTIONARIES, SELECT, INSERT, ALTER TABLE, ALTER VIEW, CREATE TABLE, CREATE VIEW, CREATE DICTIONARY, " - "DROP TABLE, DROP VIEW, DROP DICTIONARY, TRUNCATE, OPTIMIZE, BACKUP, CREATE ROW POLICY, ALTER ROW POLICY, DROP ROW POLICY, SHOW ROW POLICIES, " + "DROP TABLE, DROP VIEW, DROP DICTIONARY, UNDROP TABLE, TRUNCATE, OPTIMIZE, BACKUP, CREATE ROW POLICY, ALTER ROW POLICY, DROP ROW POLICY, SHOW ROW POLICIES, " "SYSTEM MERGES, SYSTEM TTL MERGES, SYSTEM FETCHES, SYSTEM MOVES, SYSTEM SENDS, SYSTEM REPLICATION QUEUES, SYSTEM DROP REPLICA, SYSTEM SYNC REPLICA, " "SYSTEM RESTART REPLICA, SYSTEM RESTORE REPLICA, SYSTEM WAIT LOADING PARTS, SYSTEM FLUSH DISTRIBUTED, dictGet ON test.table TO B\n" ) diff --git a/tests/integration/test_undrop_query/__init__.py b/tests/integration/test_undrop_query/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/integration/test_undrop_query/configs/with_delay_config.xml b/tests/integration/test_undrop_query/configs/with_delay_config.xml new file mode 100644 index 00000000000..c672b187b01 --- /dev/null +++ b/tests/integration/test_undrop_query/configs/with_delay_config.xml @@ -0,0 +1,3 @@ + + 5 + diff --git a/tests/integration/test_undrop_query/test.py b/tests/integration/test_undrop_query/test.py new file mode 100644 index 00000000000..63d92d84541 --- /dev/null +++ b/tests/integration/test_undrop_query/test.py @@ -0,0 +1,59 @@ +import pytest +import uuid +import random +import logging +import time + +from helpers.cluster import ClickHouseCluster + +cluster = ClickHouseCluster(__file__) + +node = cluster.add_instance("node", main_configs=["configs/with_delay_config.xml"]) + + +@pytest.fixture(scope="module") +def started_cluster(): + try: + cluster.start() + yield cluster + + finally: + cluster.shutdown() + + +def test_undrop_drop_and_undrop_loop(started_cluster): + count = 0 + node.query("set allow_experimental_undrop_table_query = 1;") + while count < 10: + random_sec = random.randint(0, 10) + table_uuid = uuid.uuid1().__str__() + logging.info( + "random_sec: " + random_sec.__str__() + ", table_uuid: " + table_uuid + ) + node.query( + "create table test_undrop_loop" + + count.__str__() + + " UUID '" + + table_uuid + + "' (id Int32) Engine=MergeTree() order by id;" + ) + node.query("drop table test_undrop_loop" + count.__str__() + ";") + time.sleep(random_sec) + if random_sec >= 5: + error = node.query_and_get_error( + "undrop table test_undrop_loop" + + count.__str__() + + " uuid '" + + table_uuid + + "' settings allow_experimental_undrop_table_query = 1;" + ) + assert "UNKNOWN_TABLE" in error + else: + node.query( + "undrop table test_undrop_loop" + + count.__str__() + + " uuid '" + + table_uuid + + "' settings allow_experimental_undrop_table_query = 1;" + ) + count = count + 1 diff --git a/tests/queries/0_stateless/01271_show_privileges.reference b/tests/queries/0_stateless/01271_show_privileges.reference index 94ab2d8b01d..553fee1f435 100644 --- a/tests/queries/0_stateless/01271_show_privileges.reference +++ b/tests/queries/0_stateless/01271_show_privileges.reference @@ -62,6 +62,7 @@ DROP DICTIONARY [] DICTIONARY DROP DROP FUNCTION [] GLOBAL DROP DROP NAMED COLLECTION [] NAMED_COLLECTION NAMED COLLECTION CONTROL DROP [] \N ALL +UNDROP TABLE [] TABLE ALL TRUNCATE ['TRUNCATE TABLE'] TABLE ALL OPTIMIZE ['OPTIMIZE TABLE'] TABLE ALL BACKUP [] TABLE ALL diff --git a/tests/queries/0_stateless/02117_show_create_table_system.reference b/tests/queries/0_stateless/02117_show_create_table_system.reference index 6fb59657305..5247dff64ad 100644 --- a/tests/queries/0_stateless/02117_show_create_table_system.reference +++ b/tests/queries/0_stateless/02117_show_create_table_system.reference @@ -289,7 +289,7 @@ CREATE TABLE system.grants ( `user_name` Nullable(String), `role_name` Nullable(String), - `access_type` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'ACCESS MANAGEMENT' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'NAMED COLLECTION CONTROL' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160), + `access_type` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'ACCESS MANAGEMENT' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160), `database` Nullable(String), `table` Nullable(String), `column` Nullable(String), @@ -570,10 +570,10 @@ ENGINE = SystemPartsColumns COMMENT 'SYSTEM TABLE is built on the fly.' CREATE TABLE system.privileges ( - `privilege` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'ACCESS MANAGEMENT' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'NAMED COLLECTION CONTROL' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160), + `privilege` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'ACCESS MANAGEMENT' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160), `aliases` Array(String), - `level` Nullable(Enum8('GLOBAL' = 0, 'DATABASE' = 1, 'TABLE' = 2, 'DICTIONARY' = 3, 'VIEW' = 4, 'COLUMN' = 5, 'NAMED_COLLECTION' = 6)), - `parent_group` Nullable(Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'ACCESS MANAGEMENT' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'NAMED COLLECTION CONTROL' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160)) + `level` Nullable(Enum8('GLOBAL' = 0, 'DATABASE' = 1, 'TABLE' = 2, 'DICTIONARY' = 3, 'VIEW' = 4, 'COLUMN' = 5)), + `parent_group` Nullable(Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'ACCESS MANAGEMENT' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160)) ) ENGINE = SystemPrivileges COMMENT 'SYSTEM TABLE is built on the fly.' diff --git a/tests/queries/0_stateless/02681_undrop_query.reference b/tests/queries/0_stateless/02681_undrop_query.reference new file mode 100644 index 00000000000..5df6c392eb9 --- /dev/null +++ b/tests/queries/0_stateless/02681_undrop_query.reference @@ -0,0 +1,32 @@ +test MergeTree undrop +02681_undrop_mergetree +1 +2 +3 +test detach +UPDATE num = 2 WHERE id = 1 +test MergeTree with cluster +02681_undrop_uuid_on_cluster +1 +2 +3 +test MergeTree without uuid on cluster +02681_undrop_no_uuid_on_cluster +1 +2 +3 +test ReplicatedMergeTree undrop +02681_undrop_replicatedmergetree +1 +2 +3 +test Log undrop +02681_undrop_log +1 +2 +3 +test Distributed undrop +02681_undrop_distributed +test MergeTree drop and undrop multiple times +02681_undrop_multiple +3 diff --git a/tests/queries/0_stateless/02681_undrop_query.sql b/tests/queries/0_stateless/02681_undrop_query.sql new file mode 100644 index 00000000000..5004eec77f1 --- /dev/null +++ b/tests/queries/0_stateless/02681_undrop_query.sql @@ -0,0 +1,90 @@ +-- Tags: no-ordinary-database, no-replicated-database, distributed, zookeeper + +set database_atomic_wait_for_drop_and_detach_synchronously = 0; +set allow_experimental_undrop_table_query = 1; + +select 'test MergeTree undrop'; +drop table if exists 02681_undrop_mergetree sync; +create table 02681_undrop_mergetree (id Int32) Engine=MergeTree() order by id; +insert into 02681_undrop_mergetree values (1),(2),(3); +drop table 02681_undrop_mergetree; +select table from system.marked_dropped_tables where table = '02681_undrop_mergetree' limit 1; +undrop table 02681_undrop_mergetree; +select * from 02681_undrop_mergetree order by id; +drop table 02681_undrop_mergetree sync; + +select 'test detach'; +drop table if exists 02681_undrop_detach sync; +create table 02681_undrop_detach (id Int32, num Int32) Engine=MergeTree() order by id; +insert into 02681_undrop_detach values (1, 1); +detach table 02681_undrop_detach; +undrop table 02681_undrop_detach; -- { serverError 57 } +attach table 02681_undrop_detach; +alter table 02681_undrop_detach update num = 2 where id = 1; +select command from system.mutations where table='02681_undrop_detach' limit 1; +drop table 02681_undrop_detach sync; + +select 'test MergeTree with cluster'; +drop table if exists 02681_undrop_uuid_on_cluster on cluster test_shard_localhost sync format Null; +create table 02681_undrop_uuid_on_cluster on cluster test_shard_localhost (id Int32) Engine=MergeTree() order by id format Null; +insert into 02681_undrop_uuid_on_cluster values (1),(2),(3); +drop table 02681_undrop_uuid_on_cluster on cluster test_shard_localhost format Null; +select table from system.marked_dropped_tables where table = '02681_undrop_uuid_on_cluster' limit 1; +undrop table 02681_undrop_uuid_on_cluster on cluster test_shard_localhost format Null; +select * from 02681_undrop_uuid_on_cluster order by id; +drop table 02681_undrop_uuid_on_cluster sync; + +select 'test MergeTree without uuid on cluster'; +drop table if exists 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost sync format Null; +create table 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost (id Int32) Engine=MergeTree() order by id format Null; +insert into 02681_undrop_no_uuid_on_cluster values (1),(2),(3); +drop table 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost format Null; +select table from system.marked_dropped_tables where table = '02681_undrop_no_uuid_on_cluster' limit 1; +undrop table 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost format Null; +select * from 02681_undrop_no_uuid_on_cluster order by id; +drop table 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost sync format Null; + +select 'test ReplicatedMergeTree undrop'; +drop table if exists 02681_undrop_replicatedmergetree sync; +create table 02681_undrop_replicatedmergetree (id Int32) Engine=ReplicatedMergeTree('/clickhouse/tables/{database}/02681_undrop_replicatedmergetree', 'test_undrop') order by id; +insert into 02681_undrop_replicatedmergetree values (1),(2),(3); +drop table 02681_undrop_replicatedmergetree; +select table from system.marked_dropped_tables where table = '02681_undrop_replicatedmergetree' limit 1; +undrop table 02681_undrop_replicatedmergetree; +select * from 02681_undrop_replicatedmergetree order by id; +drop table 02681_undrop_replicatedmergetree sync; + +select 'test Log undrop'; +drop table if exists 02681_undrop_log sync; +create table 02681_undrop_log (id Int32) Engine=Log(); +insert into 02681_undrop_log values (1),(2),(3); +drop table 02681_undrop_log; +select table from system.marked_dropped_tables where table = '02681_undrop_log' limit 1; +undrop table 02681_undrop_log; +select * from 02681_undrop_log order by id; +drop table 02681_undrop_log sync; + +select 'test Distributed undrop'; +drop table if exists 02681_undrop_distributed sync; +create table 02681_undrop_distributed (id Int32) Engine = Distributed(test_shard_localhost, currentDatabase(), 02681_undrop, rand()); +drop table 02681_undrop_distributed; +select table from system.marked_dropped_tables where table = '02681_undrop_distributed' limit 1; +undrop table 02681_undrop_distributed; +drop table 02681_undrop_distributed sync; + +select 'test MergeTree drop and undrop multiple times'; +drop table if exists 02681_undrop_multiple sync; +create table 02681_undrop_multiple (id Int32) Engine=MergeTree() order by id; +insert into 02681_undrop_multiple values (1); +drop table 02681_undrop_multiple; +create table 02681_undrop_multiple (id Int32) Engine=MergeTree() order by id; +insert into 02681_undrop_multiple values (2); +drop table 02681_undrop_multiple; +create table 02681_undrop_multiple (id Int32) Engine=MergeTree() order by id; +insert into 02681_undrop_multiple values (3); +drop table 02681_undrop_multiple; +select table from system.marked_dropped_tables where table = '02681_undrop_multiple' limit 1; +undrop table 02681_undrop_multiple; +select * from 02681_undrop_multiple order by id; +undrop table 02681_undrop_multiple; -- { serverError 57 } +drop table 02681_undrop_multiple sync; diff --git a/tests/queries/0_stateless/02681_undrop_query_uuid.reference b/tests/queries/0_stateless/02681_undrop_query_uuid.reference new file mode 100644 index 00000000000..9d36a21dbda --- /dev/null +++ b/tests/queries/0_stateless/02681_undrop_query_uuid.reference @@ -0,0 +1,5 @@ +test MergeTree with uuid +02681_undrop_uuid +1 +2 +3 diff --git a/tests/queries/0_stateless/02681_undrop_query_uuid.sh b/tests/queries/0_stateless/02681_undrop_query_uuid.sh new file mode 100755 index 00000000000..33ff0fa908e --- /dev/null +++ b/tests/queries/0_stateless/02681_undrop_query_uuid.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Tags: no-ordinary-database, distributed, zookeeper + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +echo 'test MergeTree with uuid' +${CLICKHOUSE_CLIENT} -q "drop table if exists 02681_undrop_uuid sync;" +uuid=$(${CLICKHOUSE_CLIENT} --query "SELECT reinterpretAsUUID(currentDatabase())") +${CLICKHOUSE_CLIENT} -q "create table 02681_undrop_uuid UUID '$uuid' (id Int32) Engine=MergeTree() order by id;" +${CLICKHOUSE_CLIENT} -q "insert into 02681_undrop_uuid values (1),(2),(3);" +${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid settings database_atomic_wait_for_drop_and_detach_synchronously = 0;" +${CLICKHOUSE_CLIENT} -q "select table from system.marked_dropped_tables where table = '02681_undrop_uuid' limit 1;" +${CLICKHOUSE_CLIENT} -q "undrop table 02681_undrop_uuid UUID '$uuid' settings allow_experimental_undrop_table_query = 1;" +${CLICKHOUSE_CLIENT} -q "select * from 02681_undrop_uuid order by id;" +${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid sync;" From d1c3b1bd270853eb856daed2a0d59a2092324680 Mon Sep 17 00:00:00 2001 From: xiedeyantu Date: Tue, 21 Mar 2023 20:34:17 +0800 Subject: [PATCH 092/377] fix test --- .../0_stateless/02117_show_create_table_system.reference | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/queries/0_stateless/02117_show_create_table_system.reference b/tests/queries/0_stateless/02117_show_create_table_system.reference index 5247dff64ad..b07d6e01161 100644 --- a/tests/queries/0_stateless/02117_show_create_table_system.reference +++ b/tests/queries/0_stateless/02117_show_create_table_system.reference @@ -289,7 +289,7 @@ CREATE TABLE system.grants ( `user_name` Nullable(String), `role_name` Nullable(String), - `access_type` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'ACCESS MANAGEMENT' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160), + `access_type` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'ACCESS MANAGEMENT' = 93, 'SHOW NAMED COLLECTIONS' = 94, 'SHOW NAMED COLLECTIONS SECRETS' = 95, 'NAMED COLLECTION CONTROL' = 96, 'SYSTEM SHUTDOWN' = 97, 'SYSTEM DROP DNS CACHE' = 98, 'SYSTEM DROP MARK CACHE' = 99, 'SYSTEM DROP UNCOMPRESSED CACHE' = 100, 'SYSTEM DROP MMAP CACHE' = 101, 'SYSTEM DROP QUERY CACHE' = 102, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 103, 'SYSTEM DROP FILESYSTEM CACHE' = 104, 'SYSTEM DROP SCHEMA CACHE' = 105, 'SYSTEM DROP S3 CLIENT CACHE' = 106, 'SYSTEM DROP CACHE' = 107, 'SYSTEM RELOAD CONFIG' = 108, 'SYSTEM RELOAD USERS' = 109, 'SYSTEM RELOAD SYMBOLS' = 110, 'SYSTEM RELOAD DICTIONARY' = 111, 'SYSTEM RELOAD MODEL' = 112, 'SYSTEM RELOAD FUNCTION' = 113, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 114, 'SYSTEM RELOAD' = 115, 'SYSTEM RESTART DISK' = 116, 'SYSTEM MERGES' = 117, 'SYSTEM TTL MERGES' = 118, 'SYSTEM FETCHES' = 119, 'SYSTEM MOVES' = 120, 'SYSTEM DISTRIBUTED SENDS' = 121, 'SYSTEM REPLICATED SENDS' = 122, 'SYSTEM SENDS' = 123, 'SYSTEM REPLICATION QUEUES' = 124, 'SYSTEM DROP REPLICA' = 125, 'SYSTEM SYNC REPLICA' = 126, 'SYSTEM RESTART REPLICA' = 127, 'SYSTEM RESTORE REPLICA' = 128, 'SYSTEM WAIT LOADING PARTS' = 129, 'SYSTEM SYNC DATABASE REPLICA' = 130, 'SYSTEM SYNC TRANSACTION LOG' = 131, 'SYSTEM SYNC FILE CACHE' = 132, 'SYSTEM FLUSH DISTRIBUTED' = 133, 'SYSTEM FLUSH LOGS' = 134, 'SYSTEM FLUSH' = 135, 'SYSTEM THREAD FUZZER' = 136, 'SYSTEM UNFREEZE' = 137, 'SYSTEM' = 138, 'dictGet' = 139, 'addressToLine' = 140, 'addressToLineWithInlines' = 141, 'addressToSymbol' = 142, 'demangle' = 143, 'INTROSPECTION' = 144, 'FILE' = 145, 'URL' = 146, 'REMOTE' = 147, 'MONGO' = 148, 'MEILISEARCH' = 149, 'MYSQL' = 150, 'POSTGRES' = 151, 'SQLITE' = 152, 'ODBC' = 153, 'JDBC' = 154, 'HDFS' = 155, 'S3' = 156, 'HIVE' = 157, 'SOURCES' = 158, 'CLUSTER' = 159, 'ALL' = 160, 'NONE' = 161), `database` Nullable(String), `table` Nullable(String), `column` Nullable(String), @@ -570,10 +570,10 @@ ENGINE = SystemPartsColumns COMMENT 'SYSTEM TABLE is built on the fly.' CREATE TABLE system.privileges ( - `privilege` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'ACCESS MANAGEMENT' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160), + `privilege` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'ACCESS MANAGEMENT' = 93, 'SHOW NAMED COLLECTIONS' = 94, 'SHOW NAMED COLLECTIONS SECRETS' = 95, 'NAMED COLLECTION CONTROL' = 96, 'SYSTEM SHUTDOWN' = 97, 'SYSTEM DROP DNS CACHE' = 98, 'SYSTEM DROP MARK CACHE' = 99, 'SYSTEM DROP UNCOMPRESSED CACHE' = 100, 'SYSTEM DROP MMAP CACHE' = 101, 'SYSTEM DROP QUERY CACHE' = 102, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 103, 'SYSTEM DROP FILESYSTEM CACHE' = 104, 'SYSTEM DROP SCHEMA CACHE' = 105, 'SYSTEM DROP S3 CLIENT CACHE' = 106, 'SYSTEM DROP CACHE' = 107, 'SYSTEM RELOAD CONFIG' = 108, 'SYSTEM RELOAD USERS' = 109, 'SYSTEM RELOAD SYMBOLS' = 110, 'SYSTEM RELOAD DICTIONARY' = 111, 'SYSTEM RELOAD MODEL' = 112, 'SYSTEM RELOAD FUNCTION' = 113, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 114, 'SYSTEM RELOAD' = 115, 'SYSTEM RESTART DISK' = 116, 'SYSTEM MERGES' = 117, 'SYSTEM TTL MERGES' = 118, 'SYSTEM FETCHES' = 119, 'SYSTEM MOVES' = 120, 'SYSTEM DISTRIBUTED SENDS' = 121, 'SYSTEM REPLICATED SENDS' = 122, 'SYSTEM SENDS' = 123, 'SYSTEM REPLICATION QUEUES' = 124, 'SYSTEM DROP REPLICA' = 125, 'SYSTEM SYNC REPLICA' = 126, 'SYSTEM RESTART REPLICA' = 127, 'SYSTEM RESTORE REPLICA' = 128, 'SYSTEM WAIT LOADING PARTS' = 129, 'SYSTEM SYNC DATABASE REPLICA' = 130, 'SYSTEM SYNC TRANSACTION LOG' = 131, 'SYSTEM SYNC FILE CACHE' = 132, 'SYSTEM FLUSH DISTRIBUTED' = 133, 'SYSTEM FLUSH LOGS' = 134, 'SYSTEM FLUSH' = 135, 'SYSTEM THREAD FUZZER' = 136, 'SYSTEM UNFREEZE' = 137, 'SYSTEM' = 138, 'dictGet' = 139, 'addressToLine' = 140, 'addressToLineWithInlines' = 141, 'addressToSymbol' = 142, 'demangle' = 143, 'INTROSPECTION' = 144, 'FILE' = 145, 'URL' = 146, 'REMOTE' = 147, 'MONGO' = 148, 'MEILISEARCH' = 149, 'MYSQL' = 150, 'POSTGRES' = 151, 'SQLITE' = 152, 'ODBC' = 153, 'JDBC' = 154, 'HDFS' = 155, 'S3' = 156, 'HIVE' = 157, 'SOURCES' = 158, 'CLUSTER' = 159, 'ALL' = 160, 'NONE' = 161), `aliases` Array(String), - `level` Nullable(Enum8('GLOBAL' = 0, 'DATABASE' = 1, 'TABLE' = 2, 'DICTIONARY' = 3, 'VIEW' = 4, 'COLUMN' = 5)), - `parent_group` Nullable(Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'ACCESS MANAGEMENT' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160)) + `level` Nullable(Enum8('GLOBAL' = 0, 'DATABASE' = 1, 'TABLE' = 2, 'DICTIONARY' = 3, 'VIEW' = 4, 'COLUMN' = 5, 'NAMED_COLLECTION' = 6)), + `parent_group` Nullable(Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'UNDROP TABLE' = 64, 'TRUNCATE' = 65, 'OPTIMIZE' = 66, 'BACKUP' = 67, 'KILL QUERY' = 68, 'KILL TRANSACTION' = 69, 'MOVE PARTITION BETWEEN SHARDS' = 70, 'CREATE USER' = 71, 'ALTER USER' = 72, 'DROP USER' = 73, 'CREATE ROLE' = 74, 'ALTER ROLE' = 75, 'DROP ROLE' = 76, 'ROLE ADMIN' = 77, 'CREATE ROW POLICY' = 78, 'ALTER ROW POLICY' = 79, 'DROP ROW POLICY' = 80, 'CREATE QUOTA' = 81, 'ALTER QUOTA' = 82, 'DROP QUOTA' = 83, 'CREATE SETTINGS PROFILE' = 84, 'ALTER SETTINGS PROFILE' = 85, 'DROP SETTINGS PROFILE' = 86, 'SHOW USERS' = 87, 'SHOW ROLES' = 88, 'SHOW ROW POLICIES' = 89, 'SHOW QUOTAS' = 90, 'SHOW SETTINGS PROFILES' = 91, 'SHOW ACCESS' = 92, 'ACCESS MANAGEMENT' = 93, 'SHOW NAMED COLLECTIONS' = 94, 'SHOW NAMED COLLECTIONS SECRETS' = 95, 'NAMED COLLECTION CONTROL' = 96, 'SYSTEM SHUTDOWN' = 97, 'SYSTEM DROP DNS CACHE' = 98, 'SYSTEM DROP MARK CACHE' = 99, 'SYSTEM DROP UNCOMPRESSED CACHE' = 100, 'SYSTEM DROP MMAP CACHE' = 101, 'SYSTEM DROP QUERY CACHE' = 102, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 103, 'SYSTEM DROP FILESYSTEM CACHE' = 104, 'SYSTEM DROP SCHEMA CACHE' = 105, 'SYSTEM DROP S3 CLIENT CACHE' = 106, 'SYSTEM DROP CACHE' = 107, 'SYSTEM RELOAD CONFIG' = 108, 'SYSTEM RELOAD USERS' = 109, 'SYSTEM RELOAD SYMBOLS' = 110, 'SYSTEM RELOAD DICTIONARY' = 111, 'SYSTEM RELOAD MODEL' = 112, 'SYSTEM RELOAD FUNCTION' = 113, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 114, 'SYSTEM RELOAD' = 115, 'SYSTEM RESTART DISK' = 116, 'SYSTEM MERGES' = 117, 'SYSTEM TTL MERGES' = 118, 'SYSTEM FETCHES' = 119, 'SYSTEM MOVES' = 120, 'SYSTEM DISTRIBUTED SENDS' = 121, 'SYSTEM REPLICATED SENDS' = 122, 'SYSTEM SENDS' = 123, 'SYSTEM REPLICATION QUEUES' = 124, 'SYSTEM DROP REPLICA' = 125, 'SYSTEM SYNC REPLICA' = 126, 'SYSTEM RESTART REPLICA' = 127, 'SYSTEM RESTORE REPLICA' = 128, 'SYSTEM WAIT LOADING PARTS' = 129, 'SYSTEM SYNC DATABASE REPLICA' = 130, 'SYSTEM SYNC TRANSACTION LOG' = 131, 'SYSTEM SYNC FILE CACHE' = 132, 'SYSTEM FLUSH DISTRIBUTED' = 133, 'SYSTEM FLUSH LOGS' = 134, 'SYSTEM FLUSH' = 135, 'SYSTEM THREAD FUZZER' = 136, 'SYSTEM UNFREEZE' = 137, 'SYSTEM' = 138, 'dictGet' = 139, 'addressToLine' = 140, 'addressToLineWithInlines' = 141, 'addressToSymbol' = 142, 'demangle' = 143, 'INTROSPECTION' = 144, 'FILE' = 145, 'URL' = 146, 'REMOTE' = 147, 'MONGO' = 148, 'MEILISEARCH' = 149, 'MYSQL' = 150, 'POSTGRES' = 151, 'SQLITE' = 152, 'ODBC' = 153, 'JDBC' = 154, 'HDFS' = 155, 'S3' = 156, 'HIVE' = 157, 'SOURCES' = 158, 'CLUSTER' = 159, 'ALL' = 160, 'NONE' = 161)) ) ENGINE = SystemPrivileges COMMENT 'SYSTEM TABLE is built on the fly.' From 6b59836b3ab7849d5e9055893e81b625356ef86d Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 22 Mar 2023 14:27:56 +0800 Subject: [PATCH 093/377] Update 02681_undrop_query_uuid.sh --- tests/queries/0_stateless/02681_undrop_query_uuid.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02681_undrop_query_uuid.sh b/tests/queries/0_stateless/02681_undrop_query_uuid.sh index 33ff0fa908e..42f2b2c2f6c 100755 --- a/tests/queries/0_stateless/02681_undrop_query_uuid.sh +++ b/tests/queries/0_stateless/02681_undrop_query_uuid.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Tags: no-ordinary-database, distributed, zookeeper +# Tags: no-ordinary-database, no-replicated-database CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh From 5036d19af3fc8a6771f32cd1cb730affb164c5ab Mon Sep 17 00:00:00 2001 From: xiedeyantu Date: Sun, 26 Mar 2023 13:15:23 +0800 Subject: [PATCH 094/377] better --- src/Interpreters/Context.h | 4 + src/Interpreters/DatabaseCatalog.cpp | 98 ++++++++----------- src/Interpreters/DatabaseCatalog.h | 2 + src/Interpreters/InterpreterCreateQuery.cpp | 5 +- src/Interpreters/InterpreterUndropQuery.cpp | 5 +- .../System/StorageSystemDroppedTables.cpp | 3 + .../25400_marked_dropped_tables.reference | 1 + 7 files changed, 58 insertions(+), 60 deletions(-) diff --git a/src/Interpreters/Context.h b/src/Interpreters/Context.h index bbfbd4defdc..b9ef5e88a2a 100644 --- a/src/Interpreters/Context.h +++ b/src/Interpreters/Context.h @@ -402,6 +402,7 @@ private: /// Temporary data for query execution accounting. TemporaryDataOnDiskScopePtr temp_data_on_disk; + bool in_ddl_guard = false; public: /// Some counters for current query execution. /// Most of them are workarounds and should be removed in the future. @@ -1014,6 +1015,9 @@ public: bool isInternalQuery() const { return is_internal_query; } void setInternalQuery(bool internal) { is_internal_query = internal; } + bool isInDDLGuard() const { return in_ddl_guard; } + void setInDDLGuard(bool ddl_guard) { in_ddl_guard = ddl_guard; } + ActionLocksManagerPtr getActionLocksManager() const; enum class ApplicationType diff --git a/src/Interpreters/DatabaseCatalog.cpp b/src/Interpreters/DatabaseCatalog.cpp index 4b4032f65a8..27a8743089c 100644 --- a/src/Interpreters/DatabaseCatalog.cpp +++ b/src/Interpreters/DatabaseCatalog.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -947,18 +948,17 @@ void DatabaseCatalog::enqueueDroppedTableCleanup(StorageID table_id, StoragePtr void DatabaseCatalog::dequeueDroppedTableCleanup(StorageID table_id) { String latest_metadata_dropped_path; - StorageID dropped_table_id = table_id; TableMarkedAsDropped dropped_table; { std::lock_guard lock(tables_marked_dropped_mutex); time_t latest_drop_time = std::numeric_limits::min(); - auto it_table = tables_marked_dropped.end(); + auto it_dropped_table = tables_marked_dropped.end(); for (auto it = tables_marked_dropped.begin(); it != tables_marked_dropped.end(); ++it) { auto storage_ptr = it->table; if (it->table_id.uuid == table_id.uuid) { - it_table = it; + it_dropped_table = it; dropped_table = *it; break; } @@ -970,53 +970,43 @@ void DatabaseCatalog::dequeueDroppedTableCleanup(StorageID table_id) it->drop_time >= latest_drop_time) { latest_drop_time = it->drop_time; - it_table = it; + it_dropped_table = it; dropped_table = *it; } } - if (it_table == tables_marked_dropped.end()) + if (it_dropped_table == tables_marked_dropped.end()) throw Exception(ErrorCodes::UNKNOWN_TABLE, "The drop task of table {} is in progress, has been dropped or the database engine doesn't support it", table_id.getNameForLogs()); - latest_metadata_dropped_path = it_table->metadata_path; - dropped_table_id = it_table->table_id; - tables_marked_dropped.erase(it_table); - [[maybe_unused]] auto removed = tables_marked_dropped_ids.erase(dropped_table_id.uuid); + latest_metadata_dropped_path = it_dropped_table->metadata_path; + /// If is_being_undropped of table is true, table drop task will skip this table. + it_dropped_table->is_being_undropped = true; + String table_metadata_path = getPathForMetadata(it_dropped_table->table_id); + + try + { + /// a table is successfully marked undropped, + /// if and only if its metadata file was moved to a database. + renameNoReplace(latest_metadata_dropped_path, table_metadata_path); + } + catch (...) + { + it_dropped_table->is_being_undropped = false; + throw Exception( + ErrorCodes::FS_METADATA_ERROR, + "Cannot undrop table {}, failed to rename {} to {}", + dropped_table.table_id.getNameForLogs(), + latest_metadata_dropped_path, + table_metadata_path); + } + + tables_marked_dropped.erase(it_dropped_table); + [[maybe_unused]] auto removed = tables_marked_dropped_ids.erase(dropped_table.table_id.uuid); assert(removed); - } - /// Remove the table from tables_marked_dropped and tables_marked_dropped_ids, - /// and the drop task for this table will no longer be scheduled. - LOG_INFO(log, "Trying Undrop table {} from {}", dropped_table_id.getNameForLogs(), latest_metadata_dropped_path); - String table_metadata_path = getPathForMetadata(dropped_table_id); - - auto enqueue = [&]() - { - /// In the dropTableDataTask method. - /// 1. We will first determine whether there are tables to be dropped in tables_marked_dropped. - /// 2. If one is exist, the table will be removed from tables_marked_dropped. - /// 3. And then execute dropTableFinally. - /// So undrop and drop do not cross-execute. - std::lock_guard lock(tables_marked_dropped_mutex); - tables_marked_dropped.emplace_back(dropped_table); - tables_marked_dropped_ids.insert(dropped_table_id.uuid); - CurrentMetrics::add(CurrentMetrics::TablesToDropQueueSize, 1); - }; - - ASTPtr ast = DatabaseOnDisk::parseQueryFromMetadata( - log, getContext(), latest_metadata_dropped_path, /*throw_on_error*/ true, /*remove_empty*/ false); - auto * create = typeid_cast(ast.get()); - if (!create) - { - enqueue(); - throw Exception( - ErrorCodes::FS_METADATA_ERROR, - "Cannot parse metadata of table {} from {}", - dropped_table_id.getNameForLogs(), - table_metadata_path); + CurrentMetrics::sub(CurrentMetrics::TablesToDropQueueSize, 1); } - create->setDatabase(dropped_table_id.database_name); - create->setTable(dropped_table_id.table_name); + LOG_INFO(log, "Trying Undrop table {} from {}", dropped_table.table_id.getNameForLogs(), latest_metadata_dropped_path); try { @@ -1033,26 +1023,22 @@ void DatabaseCatalog::dequeueDroppedTableCleanup(StorageID table_id) } }; wait_dropped_table_not_in_use(); - auto database = getDatabase(dropped_table_id.database_name, getContext()); - String relative_table_path = fs::path(database->getDataPath()) / DatabaseCatalog::instance().getPathForUUID(dropped_table.table_id.uuid); - auto storage = createTableFromAST( - *create, - dropped_table.table_id.database_name, - relative_table_path, - getContext(), - /* force_restore */ true).second; - database->undropTable(getContext(), dropped_table_id.table_name, storage, relative_table_path); - storage->startup(); - CurrentMetrics::sub(CurrentMetrics::TablesToDropQueueSize, 1); + + String query = fmt::format("ATTACH TABLE {}", dropped_table.table_id.getFullTableName()); + auto query_context = Context::createCopy(getContext()); + /// Attach table needs to acquire ddl guard, that has already been acquired in undrop table, + /// and cannot be acquired in the attach table again. + query_context->setInDDLGuard(true); + executeQuery(query, query_context, true); + LOG_INFO(log, "Table {} was successfully Undropped.", dropped_table.table_id.getNameForLogs()); } catch (...) { - enqueue(); throw Exception( ErrorCodes::FS_METADATA_ERROR, "Cannot undrop table {} from {}", - dropped_table_id.getNameForLogs(), - table_metadata_path); + dropped_table.table_id.getNameForLogs(), + latest_metadata_dropped_path); } } @@ -1079,7 +1065,7 @@ void DatabaseCatalog::dropTableDataTask() bool old_enough = elem.drop_time <= current_time; min_drop_time = std::min(min_drop_time, elem.drop_time); tables_in_use_count += !not_in_use; - return not_in_use && old_enough; + return not_in_use && old_enough && !elem.is_being_undropped; }); if (it != tables_marked_dropped.end()) { diff --git a/src/Interpreters/DatabaseCatalog.h b/src/Interpreters/DatabaseCatalog.h index 51e9fbdb936..516bb15911c 100644 --- a/src/Interpreters/DatabaseCatalog.h +++ b/src/Interpreters/DatabaseCatalog.h @@ -244,6 +244,8 @@ public: StoragePtr table; String metadata_path; time_t drop_time{}; + /// If is_being_undropped marks true, the drop task for table will no longer be scheduled. + bool is_being_undropped = false; }; using TablesMarkedAsDropped = std::list; diff --git a/src/Interpreters/InterpreterCreateQuery.cpp b/src/Interpreters/InterpreterCreateQuery.cpp index 7a4d65a4d57..0ba8b0cd40b 100644 --- a/src/Interpreters/InterpreterCreateQuery.cpp +++ b/src/Interpreters/InterpreterCreateQuery.cpp @@ -1107,7 +1107,8 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create) /// For short syntax of ATTACH query we have to lock table name here, before reading metadata /// and hold it until table is attached - ddl_guard = DatabaseCatalog::instance().getDDLGuard(database_name, create.getTable()); + if (!getContext()->isInDDLGuard()) + ddl_guard = DatabaseCatalog::instance().getDDLGuard(database_name, create.getTable()); bool if_not_exists = create.if_not_exists; @@ -1312,7 +1313,7 @@ bool InterpreterCreateQuery::doCreateTable(ASTCreateQuery & create, return true; } - if (!ddl_guard) + if (!ddl_guard && !getContext()->isInDDLGuard()) ddl_guard = DatabaseCatalog::instance().getDDLGuard(create.getDatabase(), create.getTable()); String data_path; diff --git a/src/Interpreters/InterpreterUndropQuery.cpp b/src/Interpreters/InterpreterUndropQuery.cpp index cc3b1b3ac24..c4c214e75ca 100644 --- a/src/Interpreters/InterpreterUndropQuery.cpp +++ b/src/Interpreters/InterpreterUndropQuery.cpp @@ -46,7 +46,6 @@ BlockIO InterpreterUndropQuery::execute() BlockIO InterpreterUndropQuery::executeToTable(ASTUndropQuery & query) { auto table_id = StorageID(query); - auto guard = DatabaseCatalog::instance().getDDLGuard(table_id.database_name, table_id.table_name); auto context = getContext(); if (table_id.database_name.empty()) @@ -55,10 +54,12 @@ BlockIO InterpreterUndropQuery::executeToTable(ASTUndropQuery & query) query.setDatabase(table_id.database_name); } + auto guard = DatabaseCatalog::instance().getDDLGuard(table_id.database_name, table_id.table_name); + auto database = DatabaseCatalog::instance().getDatabase(table_id.database_name); if (database->isTableExist(table_id.table_name, getContext())) throw Exception( - ErrorCodes::TABLE_ALREADY_EXISTS, "Cannot Undrop table, {}.{} already exists", backQuote(table_id.database_name), backQuote(table_id.table_name)); + ErrorCodes::TABLE_ALREADY_EXISTS, "Cannot Undrop table, {} already exists", table_id); database->checkMetadataFilenameAvailability(table_id.table_name); diff --git a/src/Storages/System/StorageSystemDroppedTables.cpp b/src/Storages/System/StorageSystemDroppedTables.cpp index 1d6c8824c76..14caec38f41 100644 --- a/src/Storages/System/StorageSystemDroppedTables.cpp +++ b/src/Storages/System/StorageSystemDroppedTables.cpp @@ -21,6 +21,7 @@ NamesAndTypesList StorageSystemDroppedTables::getNamesAndTypes() {"table", std::make_shared()}, {"uuid", std::make_shared()}, {"engine", std::make_shared()}, + {"is_being_undropped", std::make_shared()}, {"metadata_dropped_path", std::make_shared()}, {"table_dropped_time", std::make_shared()}, }; @@ -39,6 +40,7 @@ void StorageSystemDroppedTables::fillData(MutableColumns & res_columns, ContextP auto & column_table = assert_cast(*res_columns[index++]); auto & column_uuid = assert_cast(*res_columns[index++]).getData(); auto & column_engine = assert_cast(*res_columns[index++]); + auto & column_is_being_undropped = assert_cast(*res_columns[index++]); auto & column_metadata_dropped_path = assert_cast(*res_columns[index++]); auto & column_table_dropped_time = assert_cast(*res_columns[index++]); @@ -52,6 +54,7 @@ void StorageSystemDroppedTables::fillData(MutableColumns & res_columns, ContextP column_engine.insertData(table_mark_dropped.table->getName().data(), table_mark_dropped.table->getName().size()); else column_engine.insertData({}, 0); + column_is_being_undropped.insertValue(static_cast(table_mark_dropped.is_being_undropped)); column_metadata_dropped_path.insertData(table_mark_dropped.metadata_path.data(), table_mark_dropped.metadata_path.size()); column_table_dropped_time.insertValue(static_cast(table_mark_dropped.drop_time)); }; diff --git a/tests/queries/0_stateless/25400_marked_dropped_tables.reference b/tests/queries/0_stateless/25400_marked_dropped_tables.reference index 44906da9527..778a54222b3 100644 --- a/tests/queries/0_stateless/25400_marked_dropped_tables.reference +++ b/tests/queries/0_stateless/25400_marked_dropped_tables.reference @@ -4,5 +4,6 @@ database String table String uuid UUID engine String +is_being_undropped UInt8 metadata_dropped_path String table_dropped_time DateTime From 61d41e8152cc79166de54c11e10ccf50b1418fe4 Mon Sep 17 00:00:00 2001 From: avogar Date: Mon, 27 Mar 2023 14:41:36 +0000 Subject: [PATCH 095/377] Use uniq names for Records in Avro to avoid reusing its schema --- src/Processors/Formats/Impl/AvroRowOutputFormat.cpp | 4 +++- .../02592_avro_records_with_same_names.reference | 1 + .../0_stateless/02592_avro_records_with_same_names.sh | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/queries/0_stateless/02592_avro_records_with_same_names.reference create mode 100755 tests/queries/0_stateless/02592_avro_records_with_same_names.sh diff --git a/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp b/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp index f1b42147cd6..8a851b511da 100644 --- a/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp +++ b/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp @@ -386,7 +386,9 @@ AvroSerializer::SchemaWithSerializeFn AvroSerializer::createSchemaWithSerializeF const auto & nested_names = tuple_type.getElementNames(); std::vector nested_serializers; nested_serializers.reserve(nested_types.size()); - auto schema = avro::RecordSchema(column_name); + /// We should use unique names for records. Otherwise avro will reuse schema of this record later + /// for all records with the same name. + auto schema = avro::RecordSchema(column_name + "_" + std::to_string(type_name_increment)); for (size_t i = 0; i != nested_types.size(); ++i) { auto nested_mapping = createSchemaWithSerializeFn(nested_types[i], type_name_increment, nested_names[i]); diff --git a/tests/queries/0_stateless/02592_avro_records_with_same_names.reference b/tests/queries/0_stateless/02592_avro_records_with_same_names.reference new file mode 100644 index 00000000000..7237be8884e --- /dev/null +++ b/tests/queries/0_stateless/02592_avro_records_with_same_names.reference @@ -0,0 +1 @@ +((1,2)) ((3,4,5)) diff --git a/tests/queries/0_stateless/02592_avro_records_with_same_names.sh b/tests/queries/0_stateless/02592_avro_records_with_same_names.sh new file mode 100755 index 00000000000..d13d36230e3 --- /dev/null +++ b/tests/queries/0_stateless/02592_avro_records_with_same_names.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Tags: no-fasttest + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +SCHEMADIR=$CURDIR/format_schemas +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +$CLICKHOUSE_LOCAL -q "select tuple(tuple(1, 2))::Tuple(x Tuple(a UInt32, b UInt32)) as c1, tuple(tuple(3, 4, 5))::Tuple(x Tuple(c UInt32, d UInt32, e UInt32)) as c2 format Avro" | $CLICKHOUSE_LOCAL --input-format Avro --structure 'c1 Tuple(x Tuple(a UInt32, b UInt32)), c2 Tuple(x Tuple(c UInt32, d UInt32, e UInt32))' -q "select * from table" From ceefe77ce2395f4522498a4012486efba8b960f4 Mon Sep 17 00:00:00 2001 From: Kruglov Pavel <48961922+Avogar@users.noreply.github.com> Date: Mon, 27 Mar 2023 17:40:17 +0200 Subject: [PATCH 096/377] Fix style --- tests/queries/0_stateless/02592_avro_records_with_same_names.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/queries/0_stateless/02592_avro_records_with_same_names.sh b/tests/queries/0_stateless/02592_avro_records_with_same_names.sh index d13d36230e3..92a7846d3bd 100755 --- a/tests/queries/0_stateless/02592_avro_records_with_same_names.sh +++ b/tests/queries/0_stateless/02592_avro_records_with_same_names.sh @@ -2,7 +2,6 @@ # Tags: no-fasttest CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -SCHEMADIR=$CURDIR/format_schemas # shellcheck source=../shell_config.sh . "$CURDIR"/../shell_config.sh From d1a6c1991abc073b9320c3d6b529b596d416b2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Mon, 27 Mar 2023 17:38:52 +0200 Subject: [PATCH 097/377] Only check MV on ALTER when necessary --- src/Storages/MergeTree/MergeTreeData.cpp | 7 +++++-- src/Storages/StorageBuffer.cpp | 6 ++++-- src/Storages/StorageDistributed.cpp | 6 ++++-- src/Storages/StorageMerge.cpp | 6 ++++-- src/Storages/StorageNull.cpp | 6 ++++-- .../02697_alter_dependencies.reference | 1 + .../0_stateless/02697_alter_dependencies.sql | 16 ++++++++++++++++ 7 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 tests/queries/0_stateless/02697_alter_dependencies.reference create mode 100644 tests/queries/0_stateless/02697_alter_dependencies.sql diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index acb5ed248c8..52da00ca4a1 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -2930,7 +2930,8 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context old_types.emplace(column.name, column.type.get()); NamesAndTypesList columns_to_check_conversion; - auto name_deps = getDependentViewsByColumn(local_context); + + std::optional name_deps{}; for (const AlterCommand & command : commands) { /// Just validate partition expression @@ -3012,7 +3013,9 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context if (!command.clear) { - const auto & deps_mv = name_deps[command.column_name]; + if (!name_deps) + name_deps = getDependentViewsByColumn(local_context); + const auto & deps_mv = name_deps.value()[command.column_name]; if (!deps_mv.empty()) { throw Exception(ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN, diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index a69f26203e9..a4cb15d5711 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -1016,7 +1016,7 @@ void StorageBuffer::reschedule() void StorageBuffer::checkAlterIsPossible(const AlterCommands & commands, ContextPtr local_context) const { - auto name_deps = getDependentViewsByColumn(local_context); + std::optional name_deps{}; for (const auto & command : commands) { if (command.type != AlterCommand::Type::ADD_COLUMN && command.type != AlterCommand::Type::MODIFY_COLUMN @@ -1027,7 +1027,9 @@ void StorageBuffer::checkAlterIsPossible(const AlterCommands & commands, Context if (command.type == AlterCommand::Type::DROP_COLUMN && !command.clear) { - const auto & deps_mv = name_deps[command.column_name]; + if (!name_deps) + name_deps = getDependentViewsByColumn(local_context); + const auto & deps_mv = name_deps.value()[command.column_name]; if (!deps_mv.empty()) { throw Exception(ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN, diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 0be4ae3a79f..ec2e8fed8ae 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -1394,7 +1394,7 @@ std::optional StorageDistributed::distributedWrite(const ASTInser void StorageDistributed::checkAlterIsPossible(const AlterCommands & commands, ContextPtr local_context) const { - auto name_deps = getDependentViewsByColumn(local_context); + std::optional name_deps{}; for (const auto & command : commands) { if (command.type != AlterCommand::Type::ADD_COLUMN && command.type != AlterCommand::Type::MODIFY_COLUMN @@ -1406,7 +1406,9 @@ void StorageDistributed::checkAlterIsPossible(const AlterCommands & commands, Co if (command.type == AlterCommand::DROP_COLUMN && !command.clear) { - const auto & deps_mv = name_deps[command.column_name]; + if (!name_deps) + name_deps = getDependentViewsByColumn(local_context); + const auto & deps_mv = name_deps.value()[command.column_name]; if (!deps_mv.empty()) { throw Exception(ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN, diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 0f7b47255f6..96c40fc28d4 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -919,7 +919,7 @@ StorageMerge::DatabaseTablesIterators StorageMerge::getDatabaseIterators(Context void StorageMerge::checkAlterIsPossible(const AlterCommands & commands, ContextPtr local_context) const { - auto name_deps = getDependentViewsByColumn(local_context); + std::optional name_deps{}; for (const auto & command : commands) { if (command.type != AlterCommand::Type::ADD_COLUMN && command.type != AlterCommand::Type::MODIFY_COLUMN @@ -930,7 +930,9 @@ void StorageMerge::checkAlterIsPossible(const AlterCommands & commands, ContextP if (command.type == AlterCommand::Type::DROP_COLUMN && !command.clear) { - const auto & deps_mv = name_deps[command.column_name]; + if (!name_deps) + name_deps = getDependentViewsByColumn(local_context); + const auto & deps_mv = name_deps.value()[command.column_name]; if (!deps_mv.empty()) { throw Exception(ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN, diff --git a/src/Storages/StorageNull.cpp b/src/Storages/StorageNull.cpp index aa462e1a40c..0ced128c8ef 100644 --- a/src/Storages/StorageNull.cpp +++ b/src/Storages/StorageNull.cpp @@ -37,7 +37,7 @@ void registerStorageNull(StorageFactory & factory) void StorageNull::checkAlterIsPossible(const AlterCommands & commands, ContextPtr context) const { - auto name_deps = getDependentViewsByColumn(context); + std::optional name_deps{}; for (const auto & command : commands) { if (command.type != AlterCommand::Type::ADD_COLUMN @@ -50,7 +50,9 @@ void StorageNull::checkAlterIsPossible(const AlterCommands & commands, ContextPt if (command.type == AlterCommand::DROP_COLUMN && !command.clear) { - const auto & deps_mv = name_deps[command.column_name]; + if (!name_deps) + name_deps = getDependentViewsByColumn(context); + const auto & deps_mv = name_deps.value()[command.column_name]; if (!deps_mv.empty()) { throw Exception(ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN, diff --git a/tests/queries/0_stateless/02697_alter_dependencies.reference b/tests/queries/0_stateless/02697_alter_dependencies.reference new file mode 100644 index 00000000000..d05b1f927f4 --- /dev/null +++ b/tests/queries/0_stateless/02697_alter_dependencies.reference @@ -0,0 +1 @@ +0 0 diff --git a/tests/queries/0_stateless/02697_alter_dependencies.sql b/tests/queries/0_stateless/02697_alter_dependencies.sql new file mode 100644 index 00000000000..cf9b7551b5f --- /dev/null +++ b/tests/queries/0_stateless/02697_alter_dependencies.sql @@ -0,0 +1,16 @@ +CREATE TABLE mv_source (a Int64, insert_time DateTime) ENGINE = MergeTree() ORDER BY insert_time; +CREATE TABLE mv_target (a Int64, insert_time DateTime) ENGINE = MergeTree() ORDER BY insert_time; +CREATE MATERIALIZED VIEW source_to_target to mv_target as Select * from mv_source where a not in (Select sleepEachRow(0.1) from numbers(50)); + +ALTER TABLE mv_source MODIFY TTL insert_time + toIntervalDay(1); +SYSTEM FLUSH LOGS; +-- This is a fancy way to check that the MV hasn't been called (no functions executed by ALTER) +SELECT + ProfileEvents['FunctionExecute'], + ProfileEvents['TableFunctionExecute'] +FROM system.query_log +WHERE + type = 'QueryFinish' AND + query like '%ALTER TABLE mv_source%' AND + current_database = currentDatabase() AND + event_time > now() - INTERVAL 10 minute; From 9382c6ffdcc77bd1105fe0cc7565089bd5bb32e4 Mon Sep 17 00:00:00 2001 From: xiedeyantu Date: Mon, 27 Mar 2023 23:51:35 +0800 Subject: [PATCH 098/377] better --- src/Interpreters/DatabaseCatalog.cpp | 36 ++++++++----------- src/Interpreters/DatabaseCatalog.h | 2 -- .../System/StorageSystemDroppedTables.cpp | 3 -- .../0_stateless/02681_undrop_query.sql | 14 ++++---- .../0_stateless/02681_undrop_query_uuid.sh | 2 +- .../25400_marked_dropped_tables.reference | 1 - 6 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/Interpreters/DatabaseCatalog.cpp b/src/Interpreters/DatabaseCatalog.cpp index 27a8743089c..ed6568a0901 100644 --- a/src/Interpreters/DatabaseCatalog.cpp +++ b/src/Interpreters/DatabaseCatalog.cpp @@ -979,26 +979,12 @@ void DatabaseCatalog::dequeueDroppedTableCleanup(StorageID table_id) "The drop task of table {} is in progress, has been dropped or the database engine doesn't support it", table_id.getNameForLogs()); latest_metadata_dropped_path = it_dropped_table->metadata_path; - /// If is_being_undropped of table is true, table drop task will skip this table. - it_dropped_table->is_being_undropped = true; String table_metadata_path = getPathForMetadata(it_dropped_table->table_id); - try - { - /// a table is successfully marked undropped, - /// if and only if its metadata file was moved to a database. - renameNoReplace(latest_metadata_dropped_path, table_metadata_path); - } - catch (...) - { - it_dropped_table->is_being_undropped = false; - throw Exception( - ErrorCodes::FS_METADATA_ERROR, - "Cannot undrop table {}, failed to rename {} to {}", - dropped_table.table_id.getNameForLogs(), - latest_metadata_dropped_path, - table_metadata_path); - } + /// a table is successfully marked undropped, + /// if and only if its metadata file was moved to a database. + /// This maybe throw exception. + renameNoReplace(latest_metadata_dropped_path, table_metadata_path); tables_marked_dropped.erase(it_dropped_table); [[maybe_unused]] auto removed = tables_marked_dropped_ids.erase(dropped_table.table_id.uuid); @@ -1024,12 +1010,20 @@ void DatabaseCatalog::dequeueDroppedTableCleanup(StorageID table_id) }; wait_dropped_table_not_in_use(); - String query = fmt::format("ATTACH TABLE {}", dropped_table.table_id.getFullTableName()); + auto ast_attach = std::make_shared(); + ast_attach->attach = true; + ast_attach->setDatabase(dropped_table.table_id.database_name); + ast_attach->setTable(dropped_table.table_id.table_name); + auto query_context = Context::createCopy(getContext()); /// Attach table needs to acquire ddl guard, that has already been acquired in undrop table, /// and cannot be acquired in the attach table again. query_context->setInDDLGuard(true); - executeQuery(query, query_context, true); + InterpreterCreateQuery interpreter(ast_attach, query_context); + interpreter.setForceAttach(true); + interpreter.setForceRestoreData(true); + interpreter.execute(); + LOG_INFO(log, "Table {} was successfully Undropped.", dropped_table.table_id.getNameForLogs()); } catch (...) @@ -1065,7 +1059,7 @@ void DatabaseCatalog::dropTableDataTask() bool old_enough = elem.drop_time <= current_time; min_drop_time = std::min(min_drop_time, elem.drop_time); tables_in_use_count += !not_in_use; - return not_in_use && old_enough && !elem.is_being_undropped; + return not_in_use && old_enough; }); if (it != tables_marked_dropped.end()) { diff --git a/src/Interpreters/DatabaseCatalog.h b/src/Interpreters/DatabaseCatalog.h index 516bb15911c..51e9fbdb936 100644 --- a/src/Interpreters/DatabaseCatalog.h +++ b/src/Interpreters/DatabaseCatalog.h @@ -244,8 +244,6 @@ public: StoragePtr table; String metadata_path; time_t drop_time{}; - /// If is_being_undropped marks true, the drop task for table will no longer be scheduled. - bool is_being_undropped = false; }; using TablesMarkedAsDropped = std::list; diff --git a/src/Storages/System/StorageSystemDroppedTables.cpp b/src/Storages/System/StorageSystemDroppedTables.cpp index 14caec38f41..1d6c8824c76 100644 --- a/src/Storages/System/StorageSystemDroppedTables.cpp +++ b/src/Storages/System/StorageSystemDroppedTables.cpp @@ -21,7 +21,6 @@ NamesAndTypesList StorageSystemDroppedTables::getNamesAndTypes() {"table", std::make_shared()}, {"uuid", std::make_shared()}, {"engine", std::make_shared()}, - {"is_being_undropped", std::make_shared()}, {"metadata_dropped_path", std::make_shared()}, {"table_dropped_time", std::make_shared()}, }; @@ -40,7 +39,6 @@ void StorageSystemDroppedTables::fillData(MutableColumns & res_columns, ContextP auto & column_table = assert_cast(*res_columns[index++]); auto & column_uuid = assert_cast(*res_columns[index++]).getData(); auto & column_engine = assert_cast(*res_columns[index++]); - auto & column_is_being_undropped = assert_cast(*res_columns[index++]); auto & column_metadata_dropped_path = assert_cast(*res_columns[index++]); auto & column_table_dropped_time = assert_cast(*res_columns[index++]); @@ -54,7 +52,6 @@ void StorageSystemDroppedTables::fillData(MutableColumns & res_columns, ContextP column_engine.insertData(table_mark_dropped.table->getName().data(), table_mark_dropped.table->getName().size()); else column_engine.insertData({}, 0); - column_is_being_undropped.insertValue(static_cast(table_mark_dropped.is_being_undropped)); column_metadata_dropped_path.insertData(table_mark_dropped.metadata_path.data(), table_mark_dropped.metadata_path.size()); column_table_dropped_time.insertValue(static_cast(table_mark_dropped.drop_time)); }; diff --git a/tests/queries/0_stateless/02681_undrop_query.sql b/tests/queries/0_stateless/02681_undrop_query.sql index 5004eec77f1..ead1a8bb305 100644 --- a/tests/queries/0_stateless/02681_undrop_query.sql +++ b/tests/queries/0_stateless/02681_undrop_query.sql @@ -8,7 +8,7 @@ drop table if exists 02681_undrop_mergetree sync; create table 02681_undrop_mergetree (id Int32) Engine=MergeTree() order by id; insert into 02681_undrop_mergetree values (1),(2),(3); drop table 02681_undrop_mergetree; -select table from system.marked_dropped_tables where table = '02681_undrop_mergetree' limit 1; +select table from system.dropped_tables where table = '02681_undrop_mergetree' limit 1; undrop table 02681_undrop_mergetree; select * from 02681_undrop_mergetree order by id; drop table 02681_undrop_mergetree sync; @@ -29,7 +29,7 @@ drop table if exists 02681_undrop_uuid_on_cluster on cluster test_shard_localhos create table 02681_undrop_uuid_on_cluster on cluster test_shard_localhost (id Int32) Engine=MergeTree() order by id format Null; insert into 02681_undrop_uuid_on_cluster values (1),(2),(3); drop table 02681_undrop_uuid_on_cluster on cluster test_shard_localhost format Null; -select table from system.marked_dropped_tables where table = '02681_undrop_uuid_on_cluster' limit 1; +select table from system.dropped_tables where table = '02681_undrop_uuid_on_cluster' limit 1; undrop table 02681_undrop_uuid_on_cluster on cluster test_shard_localhost format Null; select * from 02681_undrop_uuid_on_cluster order by id; drop table 02681_undrop_uuid_on_cluster sync; @@ -39,7 +39,7 @@ drop table if exists 02681_undrop_no_uuid_on_cluster on cluster test_shard_local create table 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost (id Int32) Engine=MergeTree() order by id format Null; insert into 02681_undrop_no_uuid_on_cluster values (1),(2),(3); drop table 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost format Null; -select table from system.marked_dropped_tables where table = '02681_undrop_no_uuid_on_cluster' limit 1; +select table from system.dropped_tables where table = '02681_undrop_no_uuid_on_cluster' limit 1; undrop table 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost format Null; select * from 02681_undrop_no_uuid_on_cluster order by id; drop table 02681_undrop_no_uuid_on_cluster on cluster test_shard_localhost sync format Null; @@ -49,7 +49,7 @@ drop table if exists 02681_undrop_replicatedmergetree sync; create table 02681_undrop_replicatedmergetree (id Int32) Engine=ReplicatedMergeTree('/clickhouse/tables/{database}/02681_undrop_replicatedmergetree', 'test_undrop') order by id; insert into 02681_undrop_replicatedmergetree values (1),(2),(3); drop table 02681_undrop_replicatedmergetree; -select table from system.marked_dropped_tables where table = '02681_undrop_replicatedmergetree' limit 1; +select table from system.dropped_tables where table = '02681_undrop_replicatedmergetree' limit 1; undrop table 02681_undrop_replicatedmergetree; select * from 02681_undrop_replicatedmergetree order by id; drop table 02681_undrop_replicatedmergetree sync; @@ -59,7 +59,7 @@ drop table if exists 02681_undrop_log sync; create table 02681_undrop_log (id Int32) Engine=Log(); insert into 02681_undrop_log values (1),(2),(3); drop table 02681_undrop_log; -select table from system.marked_dropped_tables where table = '02681_undrop_log' limit 1; +select table from system.dropped_tables where table = '02681_undrop_log' limit 1; undrop table 02681_undrop_log; select * from 02681_undrop_log order by id; drop table 02681_undrop_log sync; @@ -68,7 +68,7 @@ select 'test Distributed undrop'; drop table if exists 02681_undrop_distributed sync; create table 02681_undrop_distributed (id Int32) Engine = Distributed(test_shard_localhost, currentDatabase(), 02681_undrop, rand()); drop table 02681_undrop_distributed; -select table from system.marked_dropped_tables where table = '02681_undrop_distributed' limit 1; +select table from system.dropped_tables where table = '02681_undrop_distributed' limit 1; undrop table 02681_undrop_distributed; drop table 02681_undrop_distributed sync; @@ -83,7 +83,7 @@ drop table 02681_undrop_multiple; create table 02681_undrop_multiple (id Int32) Engine=MergeTree() order by id; insert into 02681_undrop_multiple values (3); drop table 02681_undrop_multiple; -select table from system.marked_dropped_tables where table = '02681_undrop_multiple' limit 1; +select table from system.dropped_tables where table = '02681_undrop_multiple' limit 1; undrop table 02681_undrop_multiple; select * from 02681_undrop_multiple order by id; undrop table 02681_undrop_multiple; -- { serverError 57 } diff --git a/tests/queries/0_stateless/02681_undrop_query_uuid.sh b/tests/queries/0_stateless/02681_undrop_query_uuid.sh index 42f2b2c2f6c..c7d87bad3d4 100755 --- a/tests/queries/0_stateless/02681_undrop_query_uuid.sh +++ b/tests/queries/0_stateless/02681_undrop_query_uuid.sh @@ -11,7 +11,7 @@ uuid=$(${CLICKHOUSE_CLIENT} --query "SELECT reinterpretAsUUID(currentDatabase()) ${CLICKHOUSE_CLIENT} -q "create table 02681_undrop_uuid UUID '$uuid' (id Int32) Engine=MergeTree() order by id;" ${CLICKHOUSE_CLIENT} -q "insert into 02681_undrop_uuid values (1),(2),(3);" ${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid settings database_atomic_wait_for_drop_and_detach_synchronously = 0;" -${CLICKHOUSE_CLIENT} -q "select table from system.marked_dropped_tables where table = '02681_undrop_uuid' limit 1;" +${CLICKHOUSE_CLIENT} -q "select table from system.dropped_tables where table = '02681_undrop_uuid' limit 1;" ${CLICKHOUSE_CLIENT} -q "undrop table 02681_undrop_uuid UUID '$uuid' settings allow_experimental_undrop_table_query = 1;" ${CLICKHOUSE_CLIENT} -q "select * from 02681_undrop_uuid order by id;" ${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid sync;" diff --git a/tests/queries/0_stateless/25400_marked_dropped_tables.reference b/tests/queries/0_stateless/25400_marked_dropped_tables.reference index 778a54222b3..44906da9527 100644 --- a/tests/queries/0_stateless/25400_marked_dropped_tables.reference +++ b/tests/queries/0_stateless/25400_marked_dropped_tables.reference @@ -4,6 +4,5 @@ database String table String uuid UUID engine String -is_being_undropped UInt8 metadata_dropped_path String table_dropped_time DateTime From f8b45a00ec89037f2ae71d63231f8b06195596bb Mon Sep 17 00:00:00 2001 From: kssenii Date: Mon, 27 Mar 2023 19:20:48 +0200 Subject: [PATCH 099/377] Fix --- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 37 ++++++++++--------- .../integration/test_storage_rabbitmq/test.py | 20 ++++++---- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index b7fb2c6df64..da9bd8a482f 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -954,8 +954,10 @@ bool StorageRabbitMQ::checkDependencies(const StorageID & table_id) { // Check if all dependencies are attached auto view_ids = DatabaseCatalog::instance().getDependentViews(table_id); + LOG_TEST(log, "Number of attached views {} for {}", view_ids.size(), table_id.getNameForLogs()); + if (view_ids.empty()) - return true; + return false; // Check the dependencies are ready? for (const auto & view_id : view_ids) @@ -968,10 +970,6 @@ bool StorageRabbitMQ::checkDependencies(const StorageID & table_id) auto * materialized_view = dynamic_cast(view.get()); if (materialized_view && !materialized_view->tryGetTargetTable()) return false; - - // Check all its dependencies - if (!checkDependencies(view_id)) - return false; } return true; @@ -1017,27 +1015,19 @@ void StorageRabbitMQ::streamingToViewsFunc() LOG_DEBUG(log, "Started streaming to {} attached views", num_views); - if (streamToViews()) - { - /// Reschedule with backoff. - if (milliseconds_to_wait < rabbitmq_settings->rabbitmq_empty_queue_backoff_end_ms) - milliseconds_to_wait += rabbitmq_settings->rabbitmq_empty_queue_backoff_step_ms; - stopLoopIfNoReaders(); + bool continue_reading = !streamToViews(); + if (!continue_reading) break; - } - else - { - milliseconds_to_wait = rabbitmq_settings->rabbitmq_empty_queue_backoff_start_ms; - } auto end_time = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(end_time - start_time); if (duration.count() > MAX_THREAD_WORK_DURATION_MS) { - stopLoopIfNoReaders(); LOG_TRACE(log, "Reschedule streaming. Thread work duration limit exceeded."); break; } + + milliseconds_to_wait = rabbitmq_settings->rabbitmq_empty_queue_backoff_start_ms; } } } @@ -1055,7 +1045,13 @@ void StorageRabbitMQ::streamingToViewsFunc() stopLoopIfNoReaders(); if (!shutdown_called) + { + /// Reschedule with backoff. + if (milliseconds_to_wait < rabbitmq_settings->rabbitmq_empty_queue_backoff_end_ms) + milliseconds_to_wait += rabbitmq_settings->rabbitmq_empty_queue_backoff_step_ms; + streaming_task->scheduleAfter(milliseconds_to_wait); + } } @@ -1117,6 +1113,13 @@ bool StorageRabbitMQ::streamToViews() deactivateTask(looping_task, false, true); size_t queue_empty = 0; + if (!checkDependencies(getStorageID())) + { + /// Do not commit to rabbitmq if the dependency was removed. + LOG_TRACE(log, "No dependencies, reschedule"); + return true; + } + if (!connection->isConnected()) { if (shutdown_called) diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 10e2aaf8355..aec98e1cb73 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -2674,7 +2674,7 @@ def test_rabbitmq_issue_30691(rabbitmq_cluster): def test_rabbitmq_drop_mv(rabbitmq_cluster): instance.query( """ - CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + CREATE TABLE test.drop_mv (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', rabbitmq_exchange_name = 'mv', @@ -2693,7 +2693,7 @@ def test_rabbitmq_drop_mv(rabbitmq_cluster): instance.query( """ CREATE MATERIALIZED VIEW test.consumer TO test.view AS - SELECT * FROM test.rabbitmq; + SELECT * FROM test.drop_mv; """ ) @@ -2711,9 +2711,10 @@ def test_rabbitmq_drop_mv(rabbitmq_cluster): ) start = time.time() - while time.time() - start < 30: + while True: res = instance.query("SELECT COUNT(*) FROM test.view") - if "20" == res: + print(f"Current count (1): {res}") + if int(res) == 20: break else: logging.debug(f"Number of rows in test.view: {res}") @@ -2727,7 +2728,7 @@ def test_rabbitmq_drop_mv(rabbitmq_cluster): instance.query( """ CREATE MATERIALIZED VIEW test.consumer TO test.view AS - SELECT * FROM test.rabbitmq; + SELECT * FROM test.drop_mv; """ ) for i in range(40, 50): @@ -2736,11 +2737,13 @@ def test_rabbitmq_drop_mv(rabbitmq_cluster): ) while True: - result = instance.query("SELECT * FROM test.view ORDER BY key") - if rabbitmq_check_result(result): + result = instance.query("SELECT count() FROM test.view") + print(f"Current count (2): {result}") + if int(result) == 50: break time.sleep(1) + result = instance.query("SELECT * FROM test.view ORDER BY key") rabbitmq_check_result(result, True) instance.query("DROP VIEW test.consumer NO DELAY") @@ -2754,10 +2757,11 @@ def test_rabbitmq_drop_mv(rabbitmq_cluster): count = 0 start = time.time() while time.time() - start < 30: - count = int(instance.query("SELECT count() FROM test.rabbitmq")) + count = int(instance.query("SELECT count() FROM test.drop_mv")) if count: break + instance.query("DROP TABLE test.drop_mv") assert count > 0 From b7bb09d362a41ecc26eadd0d98bb16218a246374 Mon Sep 17 00:00:00 2001 From: zvonand Date: Mon, 27 Mar 2023 20:19:52 +0200 Subject: [PATCH 100/377] upload --- .../functions/type-conversion-functions.md | 17 +- .../functions/type-conversion-functions.md | 15 +- src/Functions/FunctionFormatDecimal.h | 306 ----------------- ...ecimal.cpp => FunctionToDecimalString.cpp} | 10 +- src/Functions/FunctionToDecimalString.h | 309 ++++++++++++++++++ src/IO/WriteHelpers.h | 5 +- .../0_stateless/02676_format_decimal.sql | 20 -- ...ence => 02676_to_decimal_string.reference} | 0 .../0_stateless/02676_to_decimal_string.sql | 35 ++ 9 files changed, 369 insertions(+), 348 deletions(-) delete mode 100644 src/Functions/FunctionFormatDecimal.h rename src/Functions/{FunctionFormatDecimal.cpp => FunctionToDecimalString.cpp} (63%) create mode 100644 src/Functions/FunctionToDecimalString.h delete mode 100644 tests/queries/0_stateless/02676_format_decimal.sql rename tests/queries/0_stateless/{02676_format_decimal.reference => 02676_to_decimal_string.reference} (100%) create mode 100644 tests/queries/0_stateless/02676_to_decimal_string.sql diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 83ed8efd1eb..9e13df60d14 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -737,37 +737,38 @@ Result: └────────────┴───────┘ ``` -## formatDecimal +## toDecimalString -Represents a numeric value as String with exact number of fractional digits. +Converts a numeric value to String with the number of fractional digits in the output specified by the user. **Syntax** ``` sql -formatDecimal(number, num_digits) +toDecimalString(number, scale) ``` **Parameters** -- `number` — Value to be represented as String, of any numeric value. [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md), [Float](/docs/en/sql-reference/data-types/float.md), [Decimal](/docs/en/sql-reference/data-types/decimal.md), -- `num_digits` — Number of fractional digits [UInt8](/docs/en/sql-reference/data-types/int-uint.md). +- `number` — Value to be represented as String, [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md), [Float](/docs/en/sql-reference/data-types/float.md), [Decimal](/docs/en/sql-reference/data-types/decimal.md), +- `scale` — Number of fractional digits, [UInt8](/docs/en/sql-reference/data-types/int-uint.md). **Returned value** -- Input value represented as [String](/docs/en/sql-reference/data-types/string.md) with given scale. +- Input value represented as [String](/docs/en/sql-reference/data-types/string.md) with given number of fractional digits (scale). + The number is rounded up or down according to common arithmetics in case requested scale is smaller than original number's scale. **Example** Query: ``` sql -SELECT formatDecimal(CAST('64.32', 'Float64'), 5); +SELECT toDecimalString(CAST('64.32', 'Float64'), 5); ``` Result: ```response -┌─formatDecimal(CAST('64.32', 'Float64'), 5)──┐ +┌toDecimalString(CAST('64.32', 'Float64'), 5)─┐ │ 64.32000 │ └─────────────────────────────────────────────┘ ``` diff --git a/docs/ru/sql-reference/functions/type-conversion-functions.md b/docs/ru/sql-reference/functions/type-conversion-functions.md index 74a5b34e75d..df047b1856f 100644 --- a/docs/ru/sql-reference/functions/type-conversion-functions.md +++ b/docs/ru/sql-reference/functions/type-conversion-functions.md @@ -553,37 +553,38 @@ SELECT toFixedString('foo\0bar', 8) AS s, toStringCutToZero(s) AS s_cut; └────────────┴───────┘ ``` -## formatDecimal +## toDecimalString Принимает любой численный тип первым аргументом, возвращает строковое десятичное представление числа с точностью, заданной вторым аргументом. **Синтаксис** ``` sql -formatDecimal(number, num_digits) +toDecimalString(number, scale) ``` **Параметры** -- `number` — Число любого числового типа: [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md), [Float](/docs/en/sql-reference/data-types/float.md), [Decimal](/docs/en/sql-reference/data-types/decimal.md), -- `num_digits` — Требуемое количество десятичных знаков после запятой, [UInt8](/docs/en/sql-reference/data-types/int-uint.md). +- `number` — Значение любого числового типа: [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md), [Float](/docs/en/sql-reference/data-types/float.md), [Decimal](/docs/en/sql-reference/data-types/decimal.md), +- `scale` — Требуемое количество десятичных знаков после запятой, [UInt8](/docs/en/sql-reference/data-types/int-uint.md). **Возвращаемое значение** -- Строка ([String](/docs/en/sql-reference/data-types/string.md)), представляющая собой входное число с заданной точностью. +- Строка ([String](/docs/en/sql-reference/data-types/string.md)), представляющая собой десятичное представление входного числа с заданной длиной дробной части. + При необходимости число округляется по стандартным правилам арифметики. **Пример использования** Запрос: ``` sql -SELECT formatDecimal(CAST('64.32', 'Float64'), 5); +SELECT toDecimalString(CAST('64.32', 'Float64'), 5); ``` Результат: ```response -┌─formatDecimal(CAST('64.32', 'Float64'), 5)──┐ +┌─toDecimalString(CAST('64.32', 'Float64'), 5)┐ │ 64.32000 │ └─────────────────────────────────────────────┘ ``` diff --git a/src/Functions/FunctionFormatDecimal.h b/src/Functions/FunctionFormatDecimal.h deleted file mode 100644 index 638019962f9..00000000000 --- a/src/Functions/FunctionFormatDecimal.h +++ /dev/null @@ -1,306 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int ILLEGAL_TYPE_OF_ARGUMENT; - extern const int ILLEGAL_COLUMN; - extern const int CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER; -} - -struct Processor -{ - /// For operations with Integer/Float - template - void vectorConstant(const FromVectorType & vec_from, const UInt8 value_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to) const - { - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_from.size(); - offsets_to.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - format(vec_from[i], buf_to, value_precision); - writeChar(0, buf_to); - offsets_to[i] = buf_to.count(); - } - - buf_to.finalize(); - } - - template - void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to) const - { - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_from.size(); - offsets_to.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - format(vec_from[i], buf_to, vec_precision[i]); - writeChar(0, buf_to); - offsets_to[i] = buf_to.count(); - } - - buf_to.finalize(); - } - - template - void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to) const - { - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_precision.size(); - offsets_to.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - format(value_from, buf_to, vec_precision[i]); - writeChar(0, buf_to); - offsets_to[i] = buf_to.count(); - } - - buf_to.finalize(); - } - - /// For operations with Decimal - template - void vectorConstant(const FirstArgVectorType & vec_from, const UInt8 value_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to, const UInt8 from_scale) const - { - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_from.size(); - offsets_to.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(value_precision)); - writeChar(0, buf_to); - offsets_to[i] = buf_to.count(); - } - buf_to.finalize(); - } - - template - void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to, const UInt8 from_scale) const - { - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_from.size(); - offsets_to.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(vec_precision[i])); - writeChar(0, buf_to); - offsets_to[i] = buf_to.count(); - } - - buf_to.finalize(); - } - - template - void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & offsets_to, const UInt8 from_scale) const - { - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_precision.size(); - offsets_to.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - writeText(value_from, from_scale, buf_to, true, true, static_cast(vec_precision[i])); - writeChar(0, buf_to); - offsets_to[i] = buf_to.count(); - } - - buf_to.finalize(); - } -private: - - static void format(double value, DB::WriteBuffer & out, int precision) - { - DB::DoubleConverter::BufferType buffer; - double_conversion::StringBuilder builder{buffer, sizeof(buffer)}; - - const auto result = DB::DoubleConverter::instance().ToFixed(value, precision, &builder); - - if (!result) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, "Cannot print float or double number"); - - out.write(buffer, builder.position()); - } -}; - -class FunctionFormatDecimal : public IFunction -{ -public: - static constexpr auto name = "formatDecimal"; - static FunctionPtr create(ContextPtr) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override - { - return true; - } - - size_t getNumberOfArguments() const override { return 2; } - - DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override - { - if (!isNumber(*arguments[0])) - throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Illegal first argument for formatDecimal function: got {}, expected numeric type", - arguments[0]->getName()); - - if (!isUInt8(*arguments[1])) - throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Illegal second argument for formatDecimal function: got {}, expected UInt8", - arguments[1]->getName()); - - return std::make_shared(); - } - - bool useDefaultImplementationForConstants() const override { return true; } - - ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override - { - WhichDataType which_from(arguments[0].type.get()); - - if (which_from.isUInt8()) - return executeType(arguments); - else if (which_from.isUInt16()) - return executeType(arguments); - else if (which_from.isUInt32()) - return executeType(arguments); - else if (which_from.isUInt64()) - return executeType(arguments); - else if (which_from.isUInt128()) - return executeType(arguments); - else if (which_from.isUInt256()) - return executeType(arguments); - else if (which_from.isInt8()) - return executeType(arguments); - else if (which_from.isInt16()) - return executeType(arguments); - else if (which_from.isInt32()) - return executeType(arguments); - else if (which_from.isInt64()) - return executeType(arguments); - else if (which_from.isInt128()) - return executeType(arguments); - else if (which_from.isInt256()) - return executeType(arguments); - else if (which_from.isFloat32()) - return executeType(arguments); - else if (which_from.isFloat64()) - return executeType(arguments); - else if (which_from.isDecimal32()) - return executeType(arguments); - else if (which_from.isDecimal64()) - return executeType(arguments); - else if (which_from.isDecimal128()) - return executeType(arguments); - else if (which_from.isDecimal256()) - return executeType(arguments); - - throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", - arguments[0].column->getName(), getName()); - } - -private: - template || is_floating_point, bool> = true> - ColumnPtr executeType(const ColumnsWithTypeAndName & arguments) const - { - auto result_column_string = ColumnString::create(); - auto col_to = assert_cast(result_column_string.get()); - ColumnString::Chars & data_to = col_to->getChars(); - ColumnString::Offsets & offsets_to = col_to->getOffsets(); - - const auto * from_col = checkAndGetColumn>(arguments[0].column.get()); - const auto * precision_col = checkAndGetColumn>(arguments[1].column.get()); - const auto * from_col_const = typeid_cast(arguments[0].column.get()); - const auto * precision_col_const = typeid_cast(arguments[1].column.get()); - - Processor processor; - - if (from_col) - { - if (precision_col_const) - processor.vectorConstant(from_col->getData(), precision_col_const->template getValue(), data_to, offsets_to); - else - processor.vectorVector(from_col->getData(), precision_col->getData(), data_to, offsets_to); - } - else if (from_col_const) - { - processor.constantVector(from_col_const->template getValue(), precision_col->getData(), data_to, offsets_to); - } - else - { - throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", - arguments[0].column->getName()); - } - - return result_column_string; - } - - template , bool> = true> - ColumnPtr executeType(const ColumnsWithTypeAndName & arguments) const - { - auto result_column_string = ColumnString::create(); - auto col_to = assert_cast(result_column_string.get()); - ColumnString::Chars & data_to = col_to->getChars(); - ColumnString::Offsets & offsets_to = col_to->getOffsets(); - - const auto * from_col = checkAndGetColumn>(arguments[0].column.get()); - const auto * precision_col = checkAndGetColumn>(arguments[1].column.get()); - const auto * from_col_const = typeid_cast(arguments[0].column.get()); - const auto * precision_col_const = typeid_cast(arguments[1].column.get()); - - UInt8 from_scale = from_col->getScale(); - Processor processor; - - if (from_col) - { - if (precision_col_const) - processor.vectorConstant(from_col->getData(), precision_col_const->template getValue(), data_to, offsets_to, from_scale); - else - processor.vectorVector(from_col->getData(), precision_col->getData(), data_to, offsets_to, from_scale); - } - else if (from_col_const) - { - processor.constantVector(from_col_const->template getValue(), precision_col->getData(), data_to, offsets_to, from_scale); - } - else - { - throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", - arguments[0].column->getName()); - } - - return result_column_string; - } -}; - -} diff --git a/src/Functions/FunctionFormatDecimal.cpp b/src/Functions/FunctionToDecimalString.cpp similarity index 63% rename from src/Functions/FunctionFormatDecimal.cpp rename to src/Functions/FunctionToDecimalString.cpp index f18aff060d5..e9687d80177 100644 --- a/src/Functions/FunctionFormatDecimal.cpp +++ b/src/Functions/FunctionToDecimalString.cpp @@ -1,20 +1,20 @@ -#include #include -#include +#include +#include namespace DB { -REGISTER_FUNCTION(FormatDecimal) +REGISTER_FUNCTION(ToDecimalString) { - factory.registerFunction( + factory.registerFunction( { R"( Returns string representation of a number. First argument is the number of any numeric type, second argument is the desired number of digits in fractional part. Returns String. )", - Documentation::Examples{{"formatDecimal", "SELECT formatDecimal(2.1456,2)"}}, + Documentation::Examples{{"toDecimalString", "SELECT toDecimalString(2.1456,2)"}}, Documentation::Categories{"String"} }, FunctionFactory::CaseInsensitive); } diff --git a/src/Functions/FunctionToDecimalString.h b/src/Functions/FunctionToDecimalString.h new file mode 100644 index 00000000000..eb34511015b --- /dev/null +++ b/src/Functions/FunctionToDecimalString.h @@ -0,0 +1,309 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int ILLEGAL_TYPE_OF_ARGUMENT; + extern const int ILLEGAL_COLUMN; + extern const int CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER; +} + +struct Processor +{ + /// For operations with Integer/Float + template + void vectorConstant(const FromVectorType & vec_from, const UInt8 value_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const + { + size_t input_rows_count = vec_from.size(); + result_offsets.resize(input_rows_count); + + /// Buffer is used here and in functions below because resulting size cannot be precisely anticipated, + /// and buffer resizes on-the-go. Also, .count() provided by buffer is convenient in this case. + WriteBufferFromVector buf_to(vec_to); + + for (size_t i = 0; i < input_rows_count; ++i) + { + format(vec_from[i], buf_to, value_precision); + result_offsets[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + template + void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const + { + constexpr size_t max_digits = std::numeric_limits::digits10; + + size_t input_rows_count = vec_from.size(); + result_offsets.resize(input_rows_count); + WriteBufferFromVector buf_to(vec_to); + + for (size_t i = 0; i < input_rows_count; ++i) + { + if (vec_precision[i] > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional symbols requested, shall not be more than {}", max_digits); + format(vec_from[i], buf_to, vec_precision[i]); + result_offsets[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + template + void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const + { + constexpr size_t max_digits = std::numeric_limits::digits10; + + size_t input_rows_count = vec_precision.size(); + result_offsets.resize(input_rows_count); + WriteBufferFromVector buf_to(vec_to); + + for (size_t i = 0; i < input_rows_count; ++i) + { + if (vec_precision[i] > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional symbols requested, shall not be more than {}", max_digits); + format(value_from, buf_to, vec_precision[i]); + result_offsets[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + /// For operations with Decimal + template + void vectorConstant(const FirstArgVectorType & vec_from, const UInt8 value_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_from.size(); + result_offsets.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(value_precision)); + writeChar(0, buf_to); + result_offsets[i] = buf_to.count(); + } + buf_to.finalize(); + } + + template + void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_from.size(); + result_offsets.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(vec_precision[i])); + writeChar(0, buf_to); + result_offsets[i] = buf_to.count(); + } + buf_to.finalize(); + } + + template + void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const + { + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_precision.size(); + result_offsets.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + writeText(value_from, from_scale, buf_to, true, true, static_cast(vec_precision[i])); + writeChar(0, buf_to); + result_offsets[i] = buf_to.count(); + } + buf_to.finalize(); + } +private: + + template + static void format(T value, DB::WriteBuffer & out, UInt8 precision) + { + /// Maximum is hard-coded in 'contrib/double-conversion' for floating point values, + /// Catch this here to give user a more reasonable error. + if (precision > double_conversion::DoubleToStringConverter::kMaxFixedDigitsAfterPoint) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional symbols requested for Float, must be 60 or less"); + + DB::DoubleConverter::BufferType buffer; + double_conversion::StringBuilder builder{buffer, sizeof(buffer)}; + + const auto result = DB::DoubleConverter::instance().ToFixed(value, precision, &builder); + + if (!result) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, "Error processing number: {}", value); + + out.write(buffer, builder.position()); + writeChar(0, out); + } + + template + static void format(T value, DB::WriteBuffer & out, UInt8 precision) + { + /// Fractional part for Integer is just trailing zeros. Let's limit it with 77 (like with Decimals). + constexpr size_t max_digits = std::numeric_limits::digits10; + if (precision > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional symbols requested, shall not be more than {}", max_digits); + writeText(value, out); + if (likely(precision > 0)){ + writeChar('.', out); + for (int i = 0; i < precision; ++i) + writeChar('0', out); + writeChar(0, out); + } + } +}; + +class FunctionToDecimalString : public IFunction +{ +public: + static constexpr auto name = "toDecimalString"; + static FunctionPtr create(ContextPtr) { return std::make_shared(); } + + String getName() const override { return name; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; } + + size_t getNumberOfArguments() const override { return 2; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isNumber(*arguments[0])) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal first argument for formatDecimal function: got {}, expected numeric type", + arguments[0]->getName()); + + if (!isUInt8(*arguments[1])) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal second argument for formatDecimal function: got {}, expected UInt8", + arguments[1]->getName()); + + return std::make_shared(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + switch (arguments[0].type->getTypeId()) + { + case TypeIndex::UInt8: return executeType(arguments); + case TypeIndex::UInt16: return executeType(arguments); + case TypeIndex::UInt32: return executeType(arguments); + case TypeIndex::UInt64: return executeType(arguments); + case TypeIndex::UInt128: return executeType(arguments); + case TypeIndex::UInt256: return executeType(arguments); + case TypeIndex::Int8: return executeType(arguments); + case TypeIndex::Int16: return executeType(arguments); + case TypeIndex::Int32: return executeType(arguments); + case TypeIndex::Int64: return executeType(arguments); + case TypeIndex::Int128: return executeType(arguments); + case TypeIndex::Int256: return executeType(arguments); + case TypeIndex::Float32: return executeType(arguments); + case TypeIndex::Float64: return executeType(arguments); + case TypeIndex::Decimal32: return executeType(arguments); + case TypeIndex::Decimal64: return executeType(arguments); + case TypeIndex::Decimal128: return executeType(arguments); + case TypeIndex::Decimal256: return executeType(arguments); + + default: + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", + arguments[0].column->getName(), getName()); + + } + } + +private: + template + ColumnPtr executeType(const ColumnsWithTypeAndName & arguments) const + { + auto result_col = ColumnString::create(); + auto *result_col_string = assert_cast(result_col.get()); + ColumnString::Chars & result_chars = result_col_string->getChars(); + ColumnString::Offsets & result_offsets = result_col_string->getOffsets(); + + const auto * precision_col = checkAndGetColumn>(arguments[1].column.get()); + const auto * from_col_const = typeid_cast(arguments[0].column.get()); + const auto * precision_col_const = typeid_cast(arguments[1].column.get()); + + Processor processor; + + if constexpr (is_decimal) + { + const auto * from_col = checkAndGetColumn>(arguments[0].column.get()); + UInt8 from_scale = from_col->getScale(); + + if (from_col) + { + if (precision_col_const) + processor.vectorConstant(from_col->getData(), precision_col_const->template getValue(), result_chars, result_offsets, from_scale); + else + processor.vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets, from_scale); + } + else if (from_col_const) + { + processor.constantVector(from_col_const->template getValue(), precision_col->getData(), result_chars, result_offsets, from_scale); + } + else + { + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", + arguments[0].column->getName()); + } + } + else + { + const auto * from_col = checkAndGetColumn>(arguments[0].column.get()); + if (from_col) + { + if (precision_col_const) + processor.vectorConstant(from_col->getData(), precision_col_const->template getValue(), result_chars, result_offsets); + else + processor.vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets); + } + else if (from_col_const) + { + processor.constantVector(from_col_const->template getValue(), precision_col->getData(), result_chars, result_offsets); + } + else + { + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of first argument of function formatDecimal", + arguments[0].column->getName()); + } + } + + return result_col; + } +}; + +} diff --git a/src/IO/WriteHelpers.h b/src/IO/WriteHelpers.h index c06aa7469e9..e08e451e0a7 100644 --- a/src/IO/WriteHelpers.h +++ b/src/IO/WriteHelpers.h @@ -929,7 +929,8 @@ void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool } constexpr size_t max_digits = std::numeric_limits::digits10; - assert(scale <= max_digits && fractional_length <= max_digits); + assert(scale <= max_digits); + assert(fractional_length <= max_digits); char buf[max_digits]; memset(buf, '0', std::max(scale, fractional_length)); @@ -974,7 +975,7 @@ void writeText(Decimal x, UInt32 scale, WriteBuffer & ostr, bool trailing_zer writeIntText(part, ostr); - if (scale || (fixed_fractional_length && fractional_length)) + if (scale || (fixed_fractional_length && fractional_length > 0)) { part = DecimalUtils::getFractionalPart(x, scale); if (part || trailing_zeros) diff --git a/tests/queries/0_stateless/02676_format_decimal.sql b/tests/queries/0_stateless/02676_format_decimal.sql deleted file mode 100644 index 2c29bdc91f2..00000000000 --- a/tests/queries/0_stateless/02676_format_decimal.sql +++ /dev/null @@ -1,20 +0,0 @@ --- Regular types -SELECT formatDecimal(2, 2); -- more digits required than exist -SELECT formatDecimal(2.123456, 2); -- rounding -SELECT formatDecimal(2.1456, 2); -- rounding -SELECT formatDecimal(2.9876, 6); -- more digits required than exist - --- Float32 and Float64 tests -SELECT formatDecimal(64.123::Float32, 2); -SELECT formatDecimal(64.234::Float64, 2); - --- Decimals -SELECT formatDecimal(32.345::Decimal32(3), 3); -SELECT formatDecimal(32.345::Decimal32(3), 6); -- more digits required than exist -SELECT formatDecimal(32.456::Decimal32(3), 2); -- rounding -SELECT formatDecimal('64.5671232345'::Decimal64(10), 10); -SELECT formatDecimal('128.78932312332132985464'::Decimal128(20), 20); -SELECT formatDecimal('128.78932312332132985464123123'::Decimal128(26), 20); -- rounding -SELECT formatDecimal('128.78932312332132985464'::Decimal128(20), 26); -- more digits required than exist -SELECT formatDecimal('128.789323123321329854641231237893231233213298546'::Decimal256(45), 10); -- rounding -SELECT formatDecimal('128.789323123321329854641231237893231233213298546'::Decimal256(45), 50); -- more digits required than exist \ No newline at end of file diff --git a/tests/queries/0_stateless/02676_format_decimal.reference b/tests/queries/0_stateless/02676_to_decimal_string.reference similarity index 100% rename from tests/queries/0_stateless/02676_format_decimal.reference rename to tests/queries/0_stateless/02676_to_decimal_string.reference diff --git a/tests/queries/0_stateless/02676_to_decimal_string.sql b/tests/queries/0_stateless/02676_to_decimal_string.sql new file mode 100644 index 00000000000..0931d25fc71 --- /dev/null +++ b/tests/queries/0_stateless/02676_to_decimal_string.sql @@ -0,0 +1,35 @@ +-- Regular types +SELECT toDecimalString(2, 77); -- more digits required than exist +SELECT toDecimalString(2.123456, 2); -- rounding +SELECT toDecimalString(-2, 77); -- more digits required than exist +SELECT toDecimalString(-2.123456, 2); -- rounding + +SELECT toDecimalString(2.9876, 60); -- more digits required than exist (took 60 as it is float by default) +SELECT toDecimalString(2.1456, 2); -- rounding +SELECT toDecimalString(-2.9876, 60); -- more digits required than exist +SELECT toDecimalString(-2.1456, 2); -- rounding + +-- Float32 and Float64 tests. No sense to test big float precision -- the result will be a mess anyway. +SELECT toDecimalString(64.123::Float32, 10); +SELECT toDecimalString(64.234::Float64, 10); +SELECT toDecimalString(-64.123::Float32, 10); +SELECT toDecimalString(-64.234::Float64, 10); + +-- Decimals +SELECT toDecimalString(-32.345::Decimal32(3), 3); +SELECT toDecimalString(32.345::Decimal32(3), 77); -- more digits required than exist +SELECT toDecimalString(32.456::Decimal32(3), 2); -- rounding +SELECT toDecimalString('-64.5671232345'::Decimal64(10), 10); +SELECT toDecimalString('128.78932312332132985464'::Decimal128(20), 20); +SELECT toDecimalString('-128.78932312332132985464123123'::Decimal128(26), 20); -- rounding +SELECT toDecimalString('128.78932312332132985464'::Decimal128(20), 77); -- more digits required than exist +SELECT toDecimalString('128.789323123321329854641231237893231233213298546'::Decimal256(45), 10); -- rounding +SELECT toDecimalString('-128.789323123321329854641231237893231233213298546'::Decimal256(45), 77); -- more digits required than exist + +-- Max number of decimal fractional digits is defined as 77 for Int/UInt/Decimal and 60 for Float. +-- These values shall work OK. +SELECT toDecimalString('32.32'::Float32, 61); -- {serverError 28} +SELECT toDecimalString('64.64'::Float64, 61); -- {serverError 28} +SELECT toDecimalString('88'::UInt8, 78); -- {serverError 28} +SELECT toDecimalString('646464'::Int256, 78); -- {serverError 28} +SELECT toDecimalString('-128.789323123321329854641231237893231233213298546'::Decimal256(45), 78); -- {serverError 28} \ No newline at end of file From b6c598cace65a78b3bfb28fb981737238c047e3d Mon Sep 17 00:00:00 2001 From: zvonand Date: Mon, 27 Mar 2023 22:38:42 +0200 Subject: [PATCH 101/377] fixed big ints + added tests --- src/Functions/FunctionToDecimalString.h | 24 ++++++++++++++--- .../02676_to_decimal_string.reference | 26 ++++++++++++------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/Functions/FunctionToDecimalString.h b/src/Functions/FunctionToDecimalString.h index eb34511015b..16fcd9e687f 100644 --- a/src/Functions/FunctionToDecimalString.h +++ b/src/Functions/FunctionToDecimalString.h @@ -1,5 +1,9 @@ #pragma once + +#include +#include + #include #include #include @@ -96,13 +100,19 @@ struct Processor void vectorConstant(const FirstArgVectorType & vec_from, const UInt8 value_precision, ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const { + /// There are no more than 77 meaning digits (as it is the max length of UInt256). So we can limit it with 77. + constexpr size_t max_digits = std::numeric_limits::digits10; + if (value_precision > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional symbols requested for Decimal, must be 77 or less"); + WriteBufferFromVector buf_to(vec_to); size_t input_rows_count = vec_from.size(); result_offsets.resize(input_rows_count); for (size_t i = 0; i < input_rows_count; ++i) { - writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(value_precision)); + writeText(vec_from[i], from_scale, buf_to, true, true, value_precision); writeChar(0, buf_to); result_offsets[i] = buf_to.count(); } @@ -113,13 +123,17 @@ struct Processor void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const { + constexpr size_t max_digits = std::numeric_limits::digits10; WriteBufferFromVector buf_to(vec_to); size_t input_rows_count = vec_from.size(); result_offsets.resize(input_rows_count); for (size_t i = 0; i < input_rows_count; ++i) { - writeText(vec_from[i], from_scale, buf_to, true, true, static_cast(vec_precision[i])); + if (vec_precision[i] > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional symbols requested for Decimal, must be 77 or less"); + writeText(vec_from[i], from_scale, buf_to, true, true, vec_precision[i]); writeChar(0, buf_to); result_offsets[i] = buf_to.count(); } @@ -130,13 +144,17 @@ struct Processor void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const { + constexpr size_t max_digits = std::numeric_limits::digits10; WriteBufferFromVector buf_to(vec_to); size_t input_rows_count = vec_precision.size(); result_offsets.resize(input_rows_count); for (size_t i = 0; i < input_rows_count; ++i) { - writeText(value_from, from_scale, buf_to, true, true, static_cast(vec_precision[i])); + if (vec_precision[i] > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional symbols requested for Decimal, must be 77 or less"); + writeText(value_from, from_scale, buf_to, true, true, vec_precision[i]); writeChar(0, buf_to); result_offsets[i] = buf_to.count(); } diff --git a/tests/queries/0_stateless/02676_to_decimal_string.reference b/tests/queries/0_stateless/02676_to_decimal_string.reference index d7b7dfb76d6..4c27ee5b528 100644 --- a/tests/queries/0_stateless/02676_to_decimal_string.reference +++ b/tests/queries/0_stateless/02676_to_decimal_string.reference @@ -1,15 +1,21 @@ -2.00 +2.00000000000000000000000000000000000000000000000000000000000000000000000000000 2.12 +-2.00000000000000000000000000000000000000000000000000000000000000000000000000000 +-2.12 +2.987600000000000033395508580724708735942840576171875000000000 2.15 -2.987600 -64.12 -64.23 -32.345 -32.345000 +-2.987600000000000033395508580724708735942840576171875000000000 +-2.15 +64.1230010986 +64.2340000000 +-64.1230010986 +-64.2340000000 +-32.345 +32.34500000000000000000000000000000000000000000000000000000000000000000000000000 32.46 -64.5671232345 +-64.5671232345 128.78932312332132985464 -128.78932312332132985464 -128.78932312332132985464000000 +-128.78932312332132985464 +128.78932312332132985464000000000000000000000000000000000000000000000000000000000 128.7893231233 -128.78932312332132985464123123789323123321329854600000 +-128.78932312332132985464123123789323123321329854600000000000000000000000000000000 From c7c9d8a92adc44636eb0ba9c5b041bcfa39a8fb7 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 28 Mar 2023 01:05:10 +0300 Subject: [PATCH 102/377] Update README.md --- programs/obfuscator/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/obfuscator/README.md b/programs/obfuscator/README.md index 8c2aaafb3ac..371af6344ef 100644 --- a/programs/obfuscator/README.md +++ b/programs/obfuscator/README.md @@ -312,7 +312,7 @@ hash("images/c") % total_count: ^
 
-PhotoFunia - Haber7 - Hava mükemment.net Oynamak içinde şaşıracak haber, Oyunu Oynanılmaz • apród.hu kínálatában - RT Arabic +PhotoFunia - Haber7 - Have mükemment.net Oynamak içinde şaşıracak haber, Oyunu Oynanılmaz • apród.hu kínálatában - RT Arabic PhotoFunia - Kinobar.Net - apród: Ingyenes | Posti PhotoFunia - Peg Perfeo - Castika, Sıradışı Deniz Lokoning Your Code, sire Eminema.tv/ PhotoFunia - TUT.BY - Your Ayakkanın ve Son Dakika Spor, @@ -323,7 +323,7 @@ PhotoFunia Monstelli'nin En İyi kisa.com.tr –Star Thunder Ekranı PhotoFunia Seks - Politika,Ekonomi,Spor GTA SANAYİ VE PhotoFunia Taker-Rating Star TV Resmi Söylenen Yatağa każdy dzież wierzchnie PhotoFunia TourIndex.Marketime oyunu Oyna Geldolları Mynet Spor,Magazin,Haberler yerel Haberleri ve Solvia, korkusuz Ev SahneTv -PhotoFunia todo in the Gratis Perky Parti'nin yapıyı bu fotogram +PhotoFunia todo in the Gratis Perky Parti'nin yapıyı by fotogram PhotoFunian Dünyasın takımız halles en kulları - TEZ
From 62087c3f09641b41360ac5e4a5d96599baf6a5ed Mon Sep 17 00:00:00 2001 From: xiedeyantu Date: Tue, 28 Mar 2023 07:27:13 +0800 Subject: [PATCH 103/377] fix --- src/Interpreters/DatabaseCatalog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interpreters/DatabaseCatalog.cpp b/src/Interpreters/DatabaseCatalog.cpp index ed6568a0901..436bf244e4e 100644 --- a/src/Interpreters/DatabaseCatalog.cpp +++ b/src/Interpreters/DatabaseCatalog.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include From b5fe1ada0be41d70d6b0a823af605616cae6aa6d Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Tue, 28 Mar 2023 01:39:36 +0200 Subject: [PATCH 104/377] more options for sync replica --- docs/en/sql-reference/statements/system.md | 8 ++- docs/ru/sql-reference/statements/system.md | 8 ++- docs/zh/sql-reference/statements/system.md | 2 +- src/Interpreters/InterpreterSystemQuery.cpp | 2 +- src/Parsers/ASTSystemQuery.cpp | 5 +- src/Parsers/ASTSystemQuery.h | 3 +- src/Parsers/ParserSystemQuery.cpp | 11 +++- src/Parsers/SyncReplicaMode.h | 12 ++++ .../MergeTree/ReplicatedMergeTreeQueue.cpp | 49 +++++++++++------ .../MergeTree/ReplicatedMergeTreeQueue.h | 10 +++- src/Storages/StorageReplicatedMergeTree.cpp | 55 +++++++++++-------- src/Storages/StorageReplicatedMergeTree.h | 3 +- ...ed_storage_definition_syntax_zookeeper.sql | 2 +- ...ptimize_on_nonleader_replica_zookeeper.sql | 2 +- .../02370_lost_part_intersecting_merges.sh | 4 +- .../02438_sync_replica_lightweight.reference | 15 +++++ .../02438_sync_replica_lightweight.sql | 39 +++++++++++++ tests/queries/0_stateless/replication.lib | 4 +- 18 files changed, 173 insertions(+), 61 deletions(-) create mode 100644 src/Parsers/SyncReplicaMode.h create mode 100644 tests/queries/0_stateless/02438_sync_replica_lightweight.reference create mode 100644 tests/queries/0_stateless/02438_sync_replica_lightweight.sql diff --git a/docs/en/sql-reference/statements/system.md b/docs/en/sql-reference/statements/system.md index d069ae8413a..c2be8aa4485 100644 --- a/docs/en/sql-reference/statements/system.md +++ b/docs/en/sql-reference/statements/system.md @@ -283,10 +283,14 @@ SYSTEM START REPLICATION QUEUES [[db.]replicated_merge_tree_family_table_name] Wait until a `ReplicatedMergeTree` table will be synced with other replicas in a cluster, but no more than `receive_timeout` seconds. ``` sql -SYSTEM SYNC REPLICA [ON CLUSTER cluster_name] [db.]replicated_merge_tree_family_table_name [STRICT] +SYSTEM SYNC REPLICA [ON CLUSTER cluster_name] [db.]replicated_merge_tree_family_table_name [STRICT | LIGHTWEIGHT | PULL] ``` -After running this statement the `[db.]replicated_merge_tree_family_table_name` fetches commands from the common replicated log into its own replication queue, and then the query waits till the replica processes all of the fetched commands. If a `STRICT` modifier was specified then the query waits for the replication queue to become empty. The `STRICT` version may never succeed if new entries constantly appear in the replication queue. +After running this statement the `[db.]replicated_merge_tree_family_table_name` fetches commands from the common replicated log into its own replication queue, and then the query waits till the replica processes all of the fetched commands. The following modifiers are supported: + + - If a `STRICT` modifier was specified then the query waits for the replication queue to become empty. The `STRICT` version may never succeed if new entries constantly appear in the replication queue. + - If a `LIGHTWEIGHT` modifier was specified then the query waits only for `GET_PART`, `ATTACH_PART`, `DROP_RANGE`, `REPLACE_RANGE` and `DROP_PART` entries to be processed. + - If a `PULL` modifier was specified then the query pulls new replication queue entries from ZooKeeper, but does not wait for anything to be processed. ### RESTART REPLICA diff --git a/docs/ru/sql-reference/statements/system.md b/docs/ru/sql-reference/statements/system.md index 847f60ca35c..22a74648eab 100644 --- a/docs/ru/sql-reference/statements/system.md +++ b/docs/ru/sql-reference/statements/system.md @@ -272,10 +272,14 @@ SYSTEM START REPLICATION QUEUES [[db.]replicated_merge_tree_family_table_name] Ждет когда таблица семейства `ReplicatedMergeTree` будет синхронизирована с другими репликами в кластере, но не более `receive_timeout` секунд: ``` sql -SYSTEM SYNC REPLICA [db.]replicated_merge_tree_family_table_name [STRICT] +SYSTEM SYNC REPLICA [db.]replicated_merge_tree_family_table_name [STRICT | LIGHTWEIGHT | PULL] ``` -После выполнения этого запроса таблица `[db.]replicated_merge_tree_family_table_name` загружает команды из общего реплицированного лога в свою собственную очередь репликации. Затем запрос ждет, пока реплика не обработает все загруженные команды. Если указан модификатор `STRICT`, то запрос ждёт когда очередь репликации станет пустой. Строгий вариант запроса может никогда не завершиться успешно, если в очереди репликации постоянно появляются новые записи. +После выполнения этого запроса таблица `[db.]replicated_merge_tree_family_table_name` загружает команды из общего реплицированного лога в свою собственную очередь репликации. Затем запрос ждет, пока реплика не обработает все загруженные команды. Поддерживаются следующие модификаторы: + + - Если указан модификатор `STRICT`, то запрос ждёт когда очередь репликации станет пустой. Строгий вариант запроса может никогда не завершиться успешно, если в очереди репликации постоянно появляются новые записи. + - Если указан модификатор `LIGHTWEIGHT`, то запрос ждёт когда будут обработаны записи `GET_PART`, `ATTACH_PART`, `DROP_RANGE`, `REPLACE_RANGE` и `DROP_PART`. + - Если указан модификатор `PULL`, то запрос только загружает записи очереди репликации из ZooKeeper и не ждёт выполнения чего-либо. ### RESTART REPLICA {#query_language-system-restart-replica} diff --git a/docs/zh/sql-reference/statements/system.md b/docs/zh/sql-reference/statements/system.md index 1942d6fb79a..8fd2dd74d26 100644 --- a/docs/zh/sql-reference/statements/system.md +++ b/docs/zh/sql-reference/statements/system.md @@ -240,7 +240,7 @@ SYSTEM START REPLICATION QUEUES [[db.]replicated_merge_tree_family_table_name] ``` sql -SYSTEM SYNC REPLICA [db.]replicated_merge_tree_family_table_name +SYSTEM SYNC REPLICA [db.]replicated_merge_tree_family_table_name [STRICT | LIGHTWEIGHT | PULL] ``` ### RESTART REPLICA {#query_language-system-restart-replica} diff --git a/src/Interpreters/InterpreterSystemQuery.cpp b/src/Interpreters/InterpreterSystemQuery.cpp index fb6b1635f28..90fd184948d 100644 --- a/src/Interpreters/InterpreterSystemQuery.cpp +++ b/src/Interpreters/InterpreterSystemQuery.cpp @@ -888,7 +888,7 @@ void InterpreterSystemQuery::syncReplica(ASTSystemQuery & query) { LOG_TRACE(log, "Synchronizing entries in replica's queue with table's log and waiting for current last entry to be processed"); auto sync_timeout = getContext()->getSettingsRef().receive_timeout.totalMilliseconds(); - if (!storage_replicated->waitForProcessingQueue(sync_timeout, query.strict_sync)) + if (!storage_replicated->waitForProcessingQueue(sync_timeout, query.sync_replica_mode)) { LOG_ERROR(log, "SYNC REPLICA {}: Timed out!", table_id.getNameForLogs()); throw Exception(ErrorCodes::TIMEOUT_EXCEEDED, "SYNC REPLICA {}: command timed out. " \ diff --git a/src/Parsers/ASTSystemQuery.cpp b/src/Parsers/ASTSystemQuery.cpp index c38604690ca..4bd5cdb5ebe 100644 --- a/src/Parsers/ASTSystemQuery.cpp +++ b/src/Parsers/ASTSystemQuery.cpp @@ -182,8 +182,9 @@ void ASTSystemQuery::formatImpl(const FormatSettings & settings, FormatState &, else if (!disk.empty()) print_identifier(disk); - if (strict_sync) - settings.ostr << (settings.hilite ? hilite_keyword : "") << " STRICT" << (settings.hilite ? hilite_none : ""); + if (sync_replica_mode != SyncReplicaMode::DEFAULT) + settings.ostr << ' ' << (settings.hilite ? hilite_keyword : "") << magic_enum::enum_name(sync_replica_mode) + << (settings.hilite ? hilite_none : ""); } else if (type == Type::SYNC_DATABASE_REPLICA) { diff --git a/src/Parsers/ASTSystemQuery.h b/src/Parsers/ASTSystemQuery.h index 2c7d42edebe..e5824911645 100644 --- a/src/Parsers/ASTSystemQuery.h +++ b/src/Parsers/ASTSystemQuery.h @@ -2,6 +2,7 @@ #include #include +#include #include "config.h" @@ -108,7 +109,7 @@ public: String schema_cache_storage; - bool strict_sync = false; + SyncReplicaMode sync_replica_mode = SyncReplicaMode::DEFAULT; String getID(char) const override { return "SYSTEM query"; } diff --git a/src/Parsers/ParserSystemQuery.cpp b/src/Parsers/ParserSystemQuery.cpp index 7c8d42b6bce..26819f0ee6c 100644 --- a/src/Parsers/ParserSystemQuery.cpp +++ b/src/Parsers/ParserSystemQuery.cpp @@ -259,8 +259,15 @@ bool ParserSystemQuery::parseImpl(IParser::Pos & pos, ASTPtr & node, Expected & return false; if (!parseDatabaseAndTableAsAST(pos, expected, res->database, res->table)) return false; - if (res->type == Type::SYNC_REPLICA && ParserKeyword{"STRICT"}.ignore(pos, expected)) - res->strict_sync = true; + if (res->type == Type::SYNC_REPLICA) + { + if (ParserKeyword{"STRICT"}.ignore(pos, expected)) + res->sync_replica_mode = SyncReplicaMode::STRICT; + else if (ParserKeyword{"LIGHTWEIGHT"}.ignore(pos, expected)) + res->sync_replica_mode = SyncReplicaMode::LIGHTWEIGHT; + else if (ParserKeyword{"PULL"}.ignore(pos, expected)) + res->sync_replica_mode = SyncReplicaMode::PULL; + } break; } diff --git a/src/Parsers/SyncReplicaMode.h b/src/Parsers/SyncReplicaMode.h new file mode 100644 index 00000000000..a98e1cace50 --- /dev/null +++ b/src/Parsers/SyncReplicaMode.h @@ -0,0 +1,12 @@ +#pragma once + +namespace DB +{ +enum class SyncReplicaMode +{ + DEFAULT, + STRICT, + LIGHTWEIGHT, + PULL, +}; +} diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp index c0aac96dd31..7cc26c00098 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp @@ -544,7 +544,7 @@ void ReplicatedMergeTreeQueue::removeProcessedEntry(zkutil::ZooKeeperPtr zookeep if (!found && need_remove_from_zk) throw Exception(ErrorCodes::LOGICAL_ERROR, "Can't find {} in the memory queue. It is a bug. Entry: {}", entry->znode_name, entry->toString()); - notifySubscribers(queue_size, entry->znode_name); + notifySubscribers(queue_size, &(entry->znode_name)); if (!need_remove_from_zk) return; @@ -2465,38 +2465,55 @@ String ReplicatedMergeTreeMergePredicate::getCoveringVirtualPart(const String & ReplicatedMergeTreeQueue::SubscriberHandler ReplicatedMergeTreeQueue::addSubscriber(ReplicatedMergeTreeQueue::SubscriberCallBack && callback) { - std::lock_guard lock(state_mutex); - std::unordered_set result; - result.reserve(queue.size()); - for (const auto & entry : queue) - result.insert(entry->znode_name); - + std::lock_guard lock(state_mutex); std::lock_guard lock_subscribers(subscribers_mutex); auto it = subscribers.emplace(subscribers.end(), std::move(callback)); - /// Notify queue size & log entry ids to avoid waiting for removed entries - (*it)(result.size(), result, std::nullopt); + /// Atomically notify about current size + (*it)(queue.size(), nullptr); return SubscriberHandler(it, *this); } +std::unordered_set ReplicatedMergeTreeQueue::getEntryNamesSet(bool lightweight_entries_only) +{ + std::lock_guard lock(state_mutex); + std::unordered_set result; + result.reserve(queue.size()); + for (const auto & entry : queue) + { + bool is_lightweight = entry->type == LogEntry::GET_PART || entry->type == LogEntry::ATTACH_PART + || entry->type == LogEntry::DROP_RANGE || entry->type == LogEntry::REPLACE_RANGE || entry->type == LogEntry::DROP_PART; + if (!lightweight_entries_only || is_lightweight) + result.insert(entry->znode_name); + } + return result; +} + +void ReplicatedMergeTreeQueue::notifySubscribersOnPartialShutdown() +{ + size_t queue_size; + { + std::lock_guard lock(state_mutex); + queue_size = queue.size(); + } + std::lock_guard lock_subscribers(subscribers_mutex); + for (auto & subscriber_callback : subscribers) + subscriber_callback(queue_size, nullptr); +} + ReplicatedMergeTreeQueue::SubscriberHandler::~SubscriberHandler() { std::lock_guard lock(queue.subscribers_mutex); queue.subscribers.erase(it); } -void ReplicatedMergeTreeQueue::notifySubscribers(size_t new_queue_size, std::optional removed_log_entry_id) +void ReplicatedMergeTreeQueue::notifySubscribers(size_t new_queue_size, const String * removed_log_entry_id) { std::lock_guard lock_subscribers(subscribers_mutex); for (auto & subscriber_callback : subscribers) - subscriber_callback(new_queue_size, {}, removed_log_entry_id); -} - -ReplicatedMergeTreeQueue::~ReplicatedMergeTreeQueue() -{ - notifySubscribers(0, std::nullopt); + subscriber_callback(new_queue_size, removed_log_entry_id); } String padIndex(Int64 index) diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h index 36552129690..9b29c6cef5b 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h +++ b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h @@ -164,7 +164,7 @@ private: /// A subscriber callback is called when an entry queue is deleted mutable std::mutex subscribers_mutex; - using SubscriberCallBack = std::function /*wait_for_ids*/, std::optional /* removed_log_entry_id */)>; + using SubscriberCallBack = std::function; using Subscribers = std::list; using SubscriberIterator = Subscribers::iterator; @@ -182,7 +182,7 @@ private: Subscribers subscribers; /// Notify subscribers about queue change (new queue size and entry that was removed) - void notifySubscribers(size_t new_queue_size, std::optional removed_log_entry_id); + void notifySubscribers(size_t new_queue_size, const String * removed_log_entry_id); /// Check that entry_ptr is REPLACE_RANGE entry and can be removed from queue because current entry covers it bool checkReplaceRangeCanBeRemoved( @@ -287,7 +287,7 @@ private: public: ReplicatedMergeTreeQueue(StorageReplicatedMergeTree & storage_, ReplicatedMergeTreeMergeStrategyPicker & merge_strategy_picker_); - ~ReplicatedMergeTreeQueue(); + ~ReplicatedMergeTreeQueue() = default; /// Clears queue state void clear(); @@ -427,6 +427,10 @@ public: /// Adds a subscriber SubscriberHandler addSubscriber(SubscriberCallBack && callback); + std::unordered_set getEntryNamesSet(bool lightweight_entries_only); + + void notifySubscribersOnPartialShutdown(); + struct Status { /// TODO: consider using UInt64 here diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 76170032ca5..1450b08a4fb 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -4415,6 +4415,7 @@ void StorageReplicatedMergeTree::partialShutdown() partial_shutdown_called = true; partial_shutdown_event.set(); + queue.notifySubscribersOnPartialShutdown(); replica_is_active_node = nullptr; LOG_TRACE(log, "Waiting for threads to finish"); @@ -7578,53 +7579,59 @@ void StorageReplicatedMergeTree::onActionLockRemove(StorageActionBlockType actio background_moves_assignee.trigger(); } -bool StorageReplicatedMergeTree::waitForProcessingQueue(UInt64 max_wait_milliseconds, bool strict) +bool StorageReplicatedMergeTree::waitForProcessingQueue(UInt64 max_wait_milliseconds, SyncReplicaMode sync_mode) { - Stopwatch watch; - /// Let's fetch new log entries firstly queue.pullLogsToQueue(getZooKeeperAndAssertNotReadonly(), {}, ReplicatedMergeTreeQueue::SYNC); + + if (sync_mode == SyncReplicaMode::PULL) + return true; + /// This is significant, because the execution of this task could be delayed at BackgroundPool. /// And we force it to be executed. background_operations_assignee.trigger(); std::unordered_set wait_for_ids; - bool set_ids_to_wait = true; + bool was_interrupted = false; + if (sync_mode == SyncReplicaMode::DEFAULT) + wait_for_ids = queue.getEntryNamesSet(/* lightweight_entries_only */ false); + else if (sync_mode == SyncReplicaMode::LIGHTWEIGHT) + wait_for_ids = queue.getEntryNamesSet(/* lightweight_entries_only */ true); Poco::Event target_entry_event; - auto callback = [&target_entry_event, &wait_for_ids, &set_ids_to_wait, strict] - (size_t new_queue_size, std::unordered_set log_entry_ids, std::optional removed_log_entry_id) + auto callback = [this, &target_entry_event, &wait_for_ids, &was_interrupted, sync_mode] + (size_t new_queue_size, const String * removed_log_entry_id) { - if (strict) + if (partial_shutdown_called) { - /// In strict mode we wait for queue to become empty + was_interrupted = true; + target_entry_event.set(); + return; + } + + if (sync_mode == SyncReplicaMode::STRICT) + { + /// Wait for queue to become empty if (new_queue_size == 0) target_entry_event.set(); return; } - if (set_ids_to_wait) - { - wait_for_ids = log_entry_ids; - set_ids_to_wait = false; - } + if (removed_log_entry_id) + wait_for_ids.erase(*removed_log_entry_id); - if (removed_log_entry_id.has_value()) - wait_for_ids.erase(removed_log_entry_id.value()); - - if (wait_for_ids.empty() || new_queue_size == 0) + chassert(new_queue_size || wait_for_ids.empty()); + if (wait_for_ids.empty()) target_entry_event.set(); }; const auto handler = queue.addSubscriber(std::move(callback)); - while (!target_entry_event.tryWait(50)) - { - if (max_wait_milliseconds && watch.elapsedMilliseconds() > max_wait_milliseconds) - return false; + if (!target_entry_event.tryWait(max_wait_milliseconds)) + return false; + + if (was_interrupted) + throw Exception(ErrorCodes::ABORTED, "Shutdown is called for table"); - if (partial_shutdown_called) - throw Exception(ErrorCodes::ABORTED, "Shutdown is called for table"); - } return true; } diff --git a/src/Storages/StorageReplicatedMergeTree.h b/src/Storages/StorageReplicatedMergeTree.h index 270d4eb68b2..d410350af31 100644 --- a/src/Storages/StorageReplicatedMergeTree.h +++ b/src/Storages/StorageReplicatedMergeTree.h @@ -38,6 +38,7 @@ #include #include #include +#include namespace DB @@ -181,7 +182,7 @@ public: /// Wait till replication queue's current last entry is processed or till size becomes 0 /// If timeout is exceeded returns false - bool waitForProcessingQueue(UInt64 max_wait_milliseconds, bool strict); + bool waitForProcessingQueue(UInt64 max_wait_milliseconds, SyncReplicaMode sync_mode); /// Get the status of the table. If with_zk_fields = false - do not fill in the fields that require queries to ZK. void getStatus(ReplicatedTableStatus & res, bool with_zk_fields = true); diff --git a/tests/queries/0_stateless/00509_extended_storage_definition_syntax_zookeeper.sql b/tests/queries/0_stateless/00509_extended_storage_definition_syntax_zookeeper.sql index 0aa692d9667..06fada1ccdb 100644 --- a/tests/queries/0_stateless/00509_extended_storage_definition_syntax_zookeeper.sql +++ b/tests/queries/0_stateless/00509_extended_storage_definition_syntax_zookeeper.sql @@ -44,7 +44,7 @@ CREATE TABLE replicated_collapsing(d Date, x UInt32, sign Int8) INSERT INTO replicated_collapsing VALUES ('2017-10-23', 1, 1); INSERT INTO replicated_collapsing VALUES ('2017-10-23', 1, -1), ('2017-10-23', 2, 1); -SYSTEM SYNC REPLICA replicated_collapsing; +SYSTEM SYNC REPLICA replicated_collapsing PULL; OPTIMIZE TABLE replicated_collapsing PARTITION 201710 FINAL; SELECT * FROM replicated_collapsing; diff --git a/tests/queries/0_stateless/00620_optimize_on_nonleader_replica_zookeeper.sql b/tests/queries/0_stateless/00620_optimize_on_nonleader_replica_zookeeper.sql index f6c4287b9fc..6c5eb6556ea 100644 --- a/tests/queries/0_stateless/00620_optimize_on_nonleader_replica_zookeeper.sql +++ b/tests/queries/0_stateless/00620_optimize_on_nonleader_replica_zookeeper.sql @@ -12,7 +12,7 @@ CREATE TABLE rename2 (p Int64, i Int64, v UInt64) ENGINE = ReplicatedReplacingMe INSERT INTO rename1 VALUES (0, 1, 0); INSERT INTO rename1 VALUES (0, 1, 1); -SYSTEM SYNC REPLICA rename1; -- Avoid "Cannot select parts for optimization: Entry for part 0_1_1_0 hasn't been read from the replication log yet" +SYSTEM SYNC REPLICA rename1 PULL; -- Avoid "Cannot select parts for optimization: Entry for part 0_1_1_0 hasn't been read from the replication log yet" SYSTEM SYNC REPLICA rename2; OPTIMIZE TABLE rename1 FINAL; OPTIMIZE TABLE rename2 FINAL; diff --git a/tests/queries/0_stateless/02370_lost_part_intersecting_merges.sh b/tests/queries/0_stateless/02370_lost_part_intersecting_merges.sh index db9bd2dd430..bc297cbb963 100755 --- a/tests/queries/0_stateless/02370_lost_part_intersecting_merges.sh +++ b/tests/queries/0_stateless/02370_lost_part_intersecting_merges.sh @@ -18,7 +18,7 @@ $CLICKHOUSE_CLIENT --insert_keeper_fault_injection_probability=0 -q "insert into $CLICKHOUSE_CLIENT --insert_keeper_fault_injection_probability=0 -q "insert into rmt1 values (1);" $CLICKHOUSE_CLIENT --insert_keeper_fault_injection_probability=0 -q "insert into rmt1 values (2);" -$CLICKHOUSE_CLIENT --receive_timeout=3 -q "system sync replica rmt1;" 2>/dev/null 1>/dev/null +$CLICKHOUSE_CLIENT -q "system sync replica rmt1 pull;" # There's a stupid effect from "zero copy replication": # MERGE_PARTS all_1_2_1 can be executed by rmt2 even if it was assigned by rmt1 @@ -46,7 +46,7 @@ $CLICKHOUSE_CLIENT -q "detach table rmt1;" $CLICKHOUSE_CLIENT -q "attach table rmt1;" $CLICKHOUSE_CLIENT --insert_keeper_fault_injection_probability=0 -q "insert into rmt1 values (3);" -$CLICKHOUSE_CLIENT -q "system sync replica rmt1;" +$CLICKHOUSE_CLIENT -q "system sync replica rmt1 pull;" $CLICKHOUSE_CLIENT -q "optimize table rmt1 final;" $CLICKHOUSE_CLIENT -q "system sync replica rmt1;" diff --git a/tests/queries/0_stateless/02438_sync_replica_lightweight.reference b/tests/queries/0_stateless/02438_sync_replica_lightweight.reference new file mode 100644 index 00000000000..25abaad13e2 --- /dev/null +++ b/tests/queries/0_stateless/02438_sync_replica_lightweight.reference @@ -0,0 +1,15 @@ +GET_PART all_0_0_0 +GET_PART all_1_1_0 +1 1 all_0_0_0 +1 2 all_1_1_0 +MERGE_PARTS all_0_1_1 +3 1 all_0_1_1 +3 2 all_0_1_1 +4 1 all_0_1_1 +4 2 all_0_1_1 +5 1 all_0_2_2 +5 2 all_0_2_2 +5 3 all_0_2_2 +6 1 all_0_2_2 +6 2 all_0_2_2 +6 3 all_0_2_2 diff --git a/tests/queries/0_stateless/02438_sync_replica_lightweight.sql b/tests/queries/0_stateless/02438_sync_replica_lightweight.sql new file mode 100644 index 00000000000..207571da147 --- /dev/null +++ b/tests/queries/0_stateless/02438_sync_replica_lightweight.sql @@ -0,0 +1,39 @@ +-- Tags: no-replicated-database +-- Tag no-replicated-database: different number of replicas + +create table rmt1 (n int) engine=ReplicatedMergeTree('/test/{database}/02438/', '1') order by tuple(); +create table rmt2 (n int) engine=ReplicatedMergeTree('/test/{database}/02438/', '2') order by tuple(); + +system stop replicated sends rmt1; +system stop merges rmt2; + +insert into rmt1 values (1); +insert into rmt1 values (2); +system sync replica rmt2 pull; -- does not wait +select type, new_part_name from system.replication_queue where database=currentDatabase() and table='rmt2'; +select 1, n, _part from rmt1 order by n; +select 2, n, _part from rmt2 order by n; + +set optimize_throw_if_noop = 1; +system sync replica rmt1 pull; +optimize table rmt1 final; + +system start replicated sends rmt1; +system sync replica rmt2 lightweight; -- waits for fetches, not merges +select type, new_part_name from system.replication_queue where database=currentDatabase() and table='rmt2'; +select 3, n, _part from rmt1 order by n; +select 4, n, _part from rmt2 order by n; + +system start merges rmt2; +system sync replica rmt2; + +insert into rmt2 values (3); +optimize table rmt2 final; + +system sync replica rmt1 strict; + +select 5, n, _part from rmt1 order by n; +select 6, n, _part from rmt2 order by n; + +drop table rmt1; +drop table rmt2; diff --git a/tests/queries/0_stateless/replication.lib b/tests/queries/0_stateless/replication.lib index 6784fee6395..1805b56f8dc 100755 --- a/tests/queries/0_stateless/replication.lib +++ b/tests/queries/0_stateless/replication.lib @@ -81,9 +81,9 @@ function check_replication_consistency() # Trigger pullLogsToQueue(...) and updateMutations(...) on some replica to make it pull all mutations, so it will be possible to kill them some_table=$($CLICKHOUSE_CLIENT -q "SELECT name FROM system.tables WHERE database=currentDatabase() AND name like '$table_name_prefix%' ORDER BY rand() LIMIT 1") - $CLICKHOUSE_CLIENT --receive_timeout 3 -q "SYSTEM SYNC REPLICA $some_table" 1>/dev/null 2>/dev/null ||: + $CLICKHOUSE_CLIENT -q "SYSTEM SYNC REPLICA $some_table PULL" 1>/dev/null 2>/dev/null ||: some_table=$($CLICKHOUSE_CLIENT -q "SELECT name FROM system.tables WHERE database=currentDatabase() AND name like '$table_name_prefix%' ORDER BY rand() LIMIT 1") - $CLICKHOUSE_CLIENT --receive_timeout 3 -q "SYSTEM SYNC REPLICA $some_table" 1>/dev/null 2>/dev/null ||: + $CLICKHOUSE_CLIENT -q "SYSTEM SYNC REPLICA $some_table PULL" 1>/dev/null 2>/dev/null ||: # Forcefully cancel mutations to avoid waiting for them to finish ${CLICKHOUSE_CLIENT} -q "KILL MUTATION WHERE database=currentDatabase() AND table like '$table_name_prefix%'" > /dev/null From 71b498061b9540d82ec586770645722da26ce245 Mon Sep 17 00:00:00 2001 From: Nikolay Degterinsky Date: Tue, 28 Mar 2023 01:48:25 +0000 Subject: [PATCH 105/377] Fix a bug, review suggestions --- docs/en/sql-reference/functions/ulid-functions.md | 2 +- src/Functions/FunctionsCodingULID.cpp | 13 ++++++++++--- .../0_stateless/02668_ulid_decoding.reference | 2 ++ tests/queries/0_stateless/02668_ulid_decoding.sql | 7 +++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/en/sql-reference/functions/ulid-functions.md b/docs/en/sql-reference/functions/ulid-functions.md index 2bff5da1c1a..3d32a851df2 100644 --- a/docs/en/sql-reference/functions/ulid-functions.md +++ b/docs/en/sql-reference/functions/ulid-functions.md @@ -60,7 +60,7 @@ ULIDStringToDateTime(ulid[, timezone]) **Arguments** -- `ulid` — Input UUID. [String](/docs/en/sql-reference/data-types/string.md) or [FixedString(26)](/docs/en/sql-reference/data-types/fixedstring.md). +- `ulid` — Input ULID. [String](/docs/en/sql-reference/data-types/string.md) or [FixedString(26)](/docs/en/sql-reference/data-types/fixedstring.md). - `timezone` — [Timezone name](../../operations/server-configuration-parameters/settings.md#server_configuration_parameters-timezone) for the returned value (optional). [String](../../sql-reference/data-types/string.md). **Returned value** diff --git a/src/Functions/FunctionsCodingULID.cpp b/src/Functions/FunctionsCodingULID.cpp index 07be6f57cac..bc62b2d9aca 100644 --- a/src/Functions/FunctionsCodingULID.cpp +++ b/src/Functions/FunctionsCodingULID.cpp @@ -68,7 +68,8 @@ public: String timezone; if (arguments.size() == 2) { - timezone = extractTimeZoneNameFromColumn(*arguments[1].column); + if (arguments[1].column) + timezone = extractTimeZoneNameFromColumn(*arguments[1].column); if (timezone.empty()) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, @@ -120,8 +121,14 @@ public: DateTime64 time = 0; size_t string_size = offsets_src[i] - src_offset; - if (string_size == ULID_LENGTH + 1) - time = decode(vec_src.data() + src_offset); + if (string_size != ULID_LENGTH + 1) + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of argument of function {}, ULID must be {} characters long", + arguments[0].name, getName(), ULID_LENGTH + ); + + time = decode(vec_src.data() + src_offset); src_offset += string_size; vec_res[i] = time; diff --git a/tests/queries/0_stateless/02668_ulid_decoding.reference b/tests/queries/0_stateless/02668_ulid_decoding.reference index d00491fd7e5..57af48199b4 100644 --- a/tests/queries/0_stateless/02668_ulid_decoding.reference +++ b/tests/queries/0_stateless/02668_ulid_decoding.reference @@ -1 +1,3 @@ 1 +2023-03-28 01:16:44.000 +2023-03-27 19:16:44.000 diff --git a/tests/queries/0_stateless/02668_ulid_decoding.sql b/tests/queries/0_stateless/02668_ulid_decoding.sql index 042c8e52b36..62c4a7d4dbe 100644 --- a/tests/queries/0_stateless/02668_ulid_decoding.sql +++ b/tests/queries/0_stateless/02668_ulid_decoding.sql @@ -1,3 +1,10 @@ -- Tags: no-fasttest SELECT dateDiff('s', ULIDStringToDateTime(generateULID()), now()) = 0; +SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E'); +SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E', 'America/Costa_Rica'); +SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9', 'America/Costa_Rica'); -- { serverError ILLEGAL_COLUMN } +SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E', 'America/Costa_Ric'); -- { serverError POCO_EXCEPTION } +SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E0'); -- { serverError ILLEGAL_COLUMN } +SELECT ULIDStringToDateTime(1); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT ULIDStringToDateTime(1, 2); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } From 160aa186bbfb066f1bf3beefdce4131578f50b7f Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 27 Mar 2023 14:44:34 +0000 Subject: [PATCH 106/377] Add support for NOSIGN keyword and no_sign_request config --- src/Backups/BackupIO_S3.cpp | 17 +++-- src/Coordination/KeeperSnapshotManagerS3.cpp | 10 ++- src/Disks/ObjectStorages/S3/diskSettings.cpp | 10 ++- src/IO/S3/Client.cpp | 8 +- src/IO/S3/Client.h | 4 +- src/IO/S3/Credentials.cpp | 14 ++-- src/IO/S3/Credentials.h | 12 ++- src/IO/S3Common.cpp | 10 ++- src/IO/S3Common.h | 1 + src/Storages/StorageS3.cpp | 72 ++++++++++++++---- src/TableFunctions/TableFunctionS3.cpp | 76 +++++++++++++++---- .../test_invalid_env_credentials.py | 34 +++++---- 12 files changed, 191 insertions(+), 77 deletions(-) diff --git a/src/Backups/BackupIO_S3.cpp b/src/Backups/BackupIO_S3.cpp index 1ebc7cb3bb9..d1f4a15b552 100644 --- a/src/Backups/BackupIO_S3.cpp +++ b/src/Backups/BackupIO_S3.cpp @@ -66,12 +66,17 @@ namespace credentials.GetAWSSecretKey(), settings.auth_settings.server_side_encryption_customer_key_base64, std::move(headers), - settings.auth_settings.use_environment_credentials.value_or( - context->getConfigRef().getBool("s3.use_environment_credentials", false)), - settings.auth_settings.use_insecure_imds_request.value_or( - context->getConfigRef().getBool("s3.use_insecure_imds_request", false)), - settings.auth_settings.expiration_window_seconds.value_or( - context->getConfigRef().getUInt64("s3.expiration_window_seconds", S3::DEFAULT_EXPIRATION_WINDOW_SECONDS))); + S3::CredentialsConfiguration + { + settings.auth_settings.use_environment_credentials.value_or( + context->getConfigRef().getBool("s3.use_environment_credentials", false)), + settings.auth_settings.use_insecure_imds_request.value_or( + context->getConfigRef().getBool("s3.use_insecure_imds_request", false)), + settings.auth_settings.expiration_window_seconds.value_or( + context->getConfigRef().getUInt64("s3.expiration_window_seconds", S3::DEFAULT_EXPIRATION_WINDOW_SECONDS)), + settings.auth_settings.no_sign_request.value_or( + context->getConfigRef().getBool("s3.no_sign_request", false)), + }); } Aws::Vector listObjects(S3::Client & client, const S3::URI & s3_uri, const String & file_name) diff --git a/src/Coordination/KeeperSnapshotManagerS3.cpp b/src/Coordination/KeeperSnapshotManagerS3.cpp index cabeb13e2f8..db3ff235b67 100644 --- a/src/Coordination/KeeperSnapshotManagerS3.cpp +++ b/src/Coordination/KeeperSnapshotManagerS3.cpp @@ -103,9 +103,13 @@ void KeeperSnapshotManagerS3::updateS3Configuration(const Poco::Util::AbstractCo credentials.GetAWSSecretKey(), auth_settings.server_side_encryption_customer_key_base64, std::move(headers), - auth_settings.use_environment_credentials.value_or(false), - auth_settings.use_insecure_imds_request.value_or(false), - auth_settings.expiration_window_seconds.value_or(S3::DEFAULT_EXPIRATION_WINDOW_SECONDS)); + S3::CredentialsConfiguration + { + auth_settings.use_environment_credentials.value_or(false), + auth_settings.use_insecure_imds_request.value_or(false), + auth_settings.expiration_window_seconds.value_or(S3::DEFAULT_EXPIRATION_WINDOW_SECONDS), + auth_settings.no_sign_request.value_or(false), + }); auto new_client = std::make_shared(std::move(new_uri), std::move(auth_settings), std::move(client)); diff --git a/src/Disks/ObjectStorages/S3/diskSettings.cpp b/src/Disks/ObjectStorages/S3/diskSettings.cpp index 3abeb7c70c4..09c7185b007 100644 --- a/src/Disks/ObjectStorages/S3/diskSettings.cpp +++ b/src/Disks/ObjectStorages/S3/diskSettings.cpp @@ -152,9 +152,13 @@ std::unique_ptr getClient( config.getString(config_prefix + ".secret_access_key", ""), config.getString(config_prefix + ".server_side_encryption_customer_key_base64", ""), {}, - config.getBool(config_prefix + ".use_environment_credentials", config.getBool("s3.use_environment_credentials", false)), - config.getBool(config_prefix + ".use_insecure_imds_request", config.getBool("s3.use_insecure_imds_request", false)), - config.getUInt64(config_prefix + ".expiration_window_seconds", config.getUInt64("s3.expiration_window_seconds", S3::DEFAULT_EXPIRATION_WINDOW_SECONDS))); + S3::CredentialsConfiguration + { + config.getBool(config_prefix + ".use_environment_credentials", config.getBool("s3.use_environment_credentials", false)), + config.getBool(config_prefix + ".use_insecure_imds_request", config.getBool("s3.use_insecure_imds_request", false)), + config.getUInt64(config_prefix + ".expiration_window_seconds", config.getUInt64("s3.expiration_window_seconds", S3::DEFAULT_EXPIRATION_WINDOW_SECONDS)), + config.getBool(config_prefix + ".no_sign_request", config.getBool("s3.no_sign_request", false)) + }); } } diff --git a/src/IO/S3/Client.cpp b/src/IO/S3/Client.cpp index e80e58314c7..500af4c58b7 100644 --- a/src/IO/S3/Client.cpp +++ b/src/IO/S3/Client.cpp @@ -563,9 +563,7 @@ std::unique_ptr ClientFactory::create( // NOLINT const String & secret_access_key, const String & server_side_encryption_customer_key_base64, HTTPHeaderEntries headers, - bool use_environment_credentials, - bool use_insecure_imds_request, - uint64_t expiration_window_seconds) + CredentialsConfiguration credentials_configuration) { PocoHTTPClientConfiguration client_configuration = cfg_; client_configuration.updateSchemeAndRegion(); @@ -592,9 +590,7 @@ std::unique_ptr ClientFactory::create( // NOLINT auto credentials_provider = std::make_shared( client_configuration, std::move(credentials), - use_environment_credentials, - use_insecure_imds_request, - expiration_window_seconds); + credentials_configuration); client_configuration.retryStrategy = std::make_shared(std::move(client_configuration.retryStrategy)); return Client::create( diff --git a/src/IO/S3/Client.h b/src/IO/S3/Client.h index 0e102a1859d..5c68fca6f04 100644 --- a/src/IO/S3/Client.h +++ b/src/IO/S3/Client.h @@ -228,9 +228,7 @@ public: const String & secret_access_key, const String & server_side_encryption_customer_key_base64, HTTPHeaderEntries headers, - bool use_environment_credentials, - bool use_insecure_imds_request, - uint64_t expiration_window_seconds = DEFAULT_EXPIRATION_WINDOW_SECONDS); + CredentialsConfiguration credentials_configuration); PocoHTTPClientConfiguration createClientConfiguration( const String & force_region, diff --git a/src/IO/S3/Credentials.cpp b/src/IO/S3/Credentials.cpp index f6675961ddc..b6b264e274e 100644 --- a/src/IO/S3/Credentials.cpp +++ b/src/IO/S3/Credentials.cpp @@ -418,12 +418,14 @@ void AwsAuthSTSAssumeRoleWebIdentityCredentialsProvider::refreshIfExpired() S3CredentialsProviderChain::S3CredentialsProviderChain( const DB::S3::PocoHTTPClientConfiguration & configuration, const Aws::Auth::AWSCredentials & credentials, - bool use_environment_credentials, - bool use_insecure_imds_request, - uint64_t expiration_window_seconds) + CredentialsConfiguration credentials_configuration) { auto * logger = &Poco::Logger::get("S3CredentialsProviderChain"); + /// we don't provide any credentials to avoid signing + if (credentials_configuration.no_sign_request) + return; + /// add explicit credentials to the front of the chain /// because it's manually defined by the user if (!credentials.IsEmpty()) @@ -432,7 +434,7 @@ S3CredentialsProviderChain::S3CredentialsProviderChain( return; } - if (use_environment_credentials) + if (credentials_configuration.use_environment_credentials) { static const char AWS_ECS_CONTAINER_CREDENTIALS_RELATIVE_URI[] = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; static const char AWS_ECS_CONTAINER_CREDENTIALS_FULL_URI[] = "AWS_CONTAINER_CREDENTIALS_FULL_URI"; @@ -453,7 +455,7 @@ S3CredentialsProviderChain::S3CredentialsProviderChain( configuration.for_disk_s3, configuration.get_request_throttler, configuration.put_request_throttler); - AddProvider(std::make_shared(aws_client_configuration, expiration_window_seconds)); + AddProvider(std::make_shared(aws_client_configuration, credentials_configuration.expiration_window_seconds)); } AddProvider(std::make_shared()); @@ -519,7 +521,7 @@ S3CredentialsProviderChain::S3CredentialsProviderChain( aws_client_configuration.retryStrategy = std::make_shared(1, 1000); auto ec2_metadata_client = InitEC2MetadataClient(aws_client_configuration); - auto config_loader = std::make_shared(ec2_metadata_client, !use_insecure_imds_request); + auto config_loader = std::make_shared(ec2_metadata_client, !credentials_configuration.use_insecure_imds_request); AddProvider(std::make_shared(config_loader)); LOG_INFO(logger, "Added EC2 metadata service credentials provider to the provider chain."); diff --git a/src/IO/S3/Credentials.h b/src/IO/S3/Credentials.h index d6214c5e2fa..cd9072f9765 100644 --- a/src/IO/S3/Credentials.h +++ b/src/IO/S3/Credentials.h @@ -121,15 +121,21 @@ private: uint64_t expiration_window_seconds; }; +struct CredentialsConfiguration +{ + bool use_environment_credentials = false; + bool use_insecure_imds_request = false; + uint64_t expiration_window_seconds = DEFAULT_EXPIRATION_WINDOW_SECONDS; + bool no_sign_request = false; +}; + class S3CredentialsProviderChain : public Aws::Auth::AWSCredentialsProviderChain { public: S3CredentialsProviderChain( const DB::S3::PocoHTTPClientConfiguration & configuration, const Aws::Auth::AWSCredentials & credentials, - bool use_environment_credentials, - bool use_insecure_imds_request, - uint64_t expiration_window_seconds); + CredentialsConfiguration credentials_configuration); }; } diff --git a/src/IO/S3Common.cpp b/src/IO/S3Common.cpp index 4acc31ca472..20984b69463 100644 --- a/src/IO/S3Common.cpp +++ b/src/IO/S3Common.cpp @@ -89,6 +89,10 @@ AuthSettings AuthSettings::loadFromConfig(const std::string & config_elem, const if (config.has(config_elem + ".expiration_window_seconds")) expiration_window_seconds = config.getUInt64(config_elem + ".expiration_window_seconds"); + std::optional no_sign_request; + if (config.has(config_elem + ".no_sign_request")) + no_sign_request = config.getBool(config_elem + ".no_sign_request"); + HTTPHeaderEntries headers; Poco::Util::AbstractConfiguration::Keys subconfig_keys; config.keys(config_elem, subconfig_keys); @@ -112,7 +116,8 @@ AuthSettings AuthSettings::loadFromConfig(const std::string & config_elem, const std::move(headers), use_environment_credentials, use_insecure_imds_request, - expiration_window_seconds + expiration_window_seconds, + no_sign_request }; } @@ -133,6 +138,9 @@ void AuthSettings::updateFrom(const AuthSettings & from) use_environment_credentials = from.use_environment_credentials; use_insecure_imds_request = from.use_insecure_imds_request; expiration_window_seconds = from.expiration_window_seconds; + + if (from.no_sign_request.has_value()) + no_sign_request = *from.no_sign_request; } } diff --git a/src/IO/S3Common.h b/src/IO/S3Common.h index ff948c065f8..0b3ab97b2d8 100644 --- a/src/IO/S3Common.h +++ b/src/IO/S3Common.h @@ -85,6 +85,7 @@ struct AuthSettings std::optional use_environment_credentials; std::optional use_insecure_imds_request; std::optional expiration_window_seconds; + std::optional no_sign_request; bool operator==(const AuthSettings & other) const = default; diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index e17860af288..f2a9b3a3955 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -62,6 +62,8 @@ #include #include +#include + namespace fs = std::filesystem; @@ -1265,9 +1267,13 @@ void StorageS3::updateConfiguration(ContextPtr ctx, StorageS3::Configuration & u credentials.GetAWSSecretKey(), upd.auth_settings.server_side_encryption_customer_key_base64, std::move(headers), - upd.auth_settings.use_environment_credentials.value_or(ctx->getConfigRef().getBool("s3.use_environment_credentials", false)), - upd.auth_settings.use_insecure_imds_request.value_or(ctx->getConfigRef().getBool("s3.use_insecure_imds_request", false)), - upd.auth_settings.expiration_window_seconds.value_or(ctx->getConfigRef().getUInt64("s3.expiration_window_seconds", S3::DEFAULT_EXPIRATION_WINDOW_SECONDS))); + S3::CredentialsConfiguration{ + upd.auth_settings.use_environment_credentials.value_or(ctx->getConfigRef().getBool("s3.use_environment_credentials", false)), + upd.auth_settings.use_insecure_imds_request.value_or(ctx->getConfigRef().getBool("s3.use_insecure_imds_request", false)), + upd.auth_settings.expiration_window_seconds.value_or( + ctx->getConfigRef().getUInt64("s3.expiration_window_seconds", S3::DEFAULT_EXPIRATION_WINDOW_SECONDS)), + upd.auth_settings.no_sign_request.value_or(ctx->getConfigRef().getBool("s3.no_sign_request", false)), + }); } void StorageS3::processNamedCollectionResult(StorageS3::Configuration & configuration, const NamedCollection & collection) @@ -1306,6 +1312,9 @@ StorageS3::Configuration StorageS3::getConfiguration(ASTs & engine_args, Context /// S3('url') /// S3('url', 'format') /// S3('url', 'format', 'compression') + /// S3('url', NOSIGN) + /// S3('url', NOSIGN, 'format') + /// S3('url', NOSIGN, 'format', 'compression') /// S3('url', 'aws_access_key_id', 'aws_secret_access_key') /// S3('url', 'aws_access_key_id', 'aws_secret_access_key', 'format') /// S3('url', 'aws_access_key_id', 'aws_secret_access_key', 'format', 'compression') @@ -1314,7 +1323,7 @@ StorageS3::Configuration StorageS3::getConfiguration(ASTs & engine_args, Context if (engine_args.empty() || engine_args.size() > 5) throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Storage S3 requires 1 to 5 arguments: " - "url, [access_key_id, secret_access_key], name of used format and [compression_method]"); + "url, [NOSIGN | access_key_id, secret_access_key], name of used format and [compression_method]"); auto * header_it = StorageURL::collectHeaders(engine_args, configuration.headers_from_ast, local_context); if (header_it != engine_args.end()) @@ -1327,24 +1336,57 @@ StorageS3::Configuration StorageS3::getConfiguration(ASTs & engine_args, Context static std::unordered_map> size_to_engine_args { {1, {{}}}, - {2, {{"format", 1}}}, - {4, {{"access_key_id", 1}, {"secret_access_key", 2}, {"format", 3}}}, {5, {{"access_key_id", 1}, {"secret_access_key", 2}, {"format", 3}, {"compression_method", 4}}} }; std::unordered_map engine_args_to_idx; - /// For 3 arguments we support 2 possible variants: - /// s3(source, format, compression_method) and s3(source, access_key_id, access_key_id) - /// We can distinguish them by looking at the 2-nd argument: check if it's a format name or not. - if (engine_args.size() == 3) - { - auto second_arg = checkAndGetLiteralArgument(engine_args[1], "format/access_key_id"); - if (second_arg == "auto" || FormatFactory::instance().getAllFormats().contains(second_arg)) - engine_args_to_idx = {{"format", 1}, {"compression_method", 2}}; + bool no_sign_request = false; + /// For 2 arguments we support 2 possible variants: + /// - s3(source, format) + /// - s3(source, NOSIGN) + /// We can distinguish them by looking at the 2-nd argument: check if it's NOSIGN or not. + if (engine_args.size() == 2) + { + auto second_arg = checkAndGetLiteralArgument(engine_args[1], "format/NOSIGN"); + if (boost::iequals(second_arg, "NOSIGN")) + no_sign_request = true; + else + engine_args_to_idx = {{"format", 1}}; + } + /// For 3 arguments we support 2 possible variants: + /// - s3(source, format, compression_method) + /// - s3(source, access_key_id, access_key_id) + /// - s3(source, NOSIGN, format) + /// We can distinguish them by looking at the 2-nd argument: check if it's NOSIGN or format name. + else if (engine_args.size() == 3) + { + auto second_arg = checkAndGetLiteralArgument(engine_args[1], "format/access_key_id/NOSIGN"); + if (boost::iequals(second_arg, "NOSIGN")) + { + no_sign_request = true; + engine_args_to_idx = {{"format", 2}}; + } + else if (second_arg == "auto" || FormatFactory::instance().getAllFormats().contains(second_arg)) + engine_args_to_idx = {{"format", 1}, {"compression_method", 2}}; else engine_args_to_idx = {{"access_key_id", 1}, {"secret_access_key", 2}}; } + /// For 4 arguments we support 2 possible variants: + /// - s3(source, access_key_id, secret_access_key, format) + /// - s3(source, NOSIGN, format, compression_method) + /// We can distinguish them by looking at the 2-nd argument: check if it's a NOSIGN or not. + else if (engine_args.size() == 4) + { + auto second_arg = checkAndGetLiteralArgument(engine_args[1], "access_key_id/NOSIGN"); + if (boost::iequals(second_arg, "NOSIGN")) + { + no_sign_request = true; + engine_args_to_idx = {{"format", 2}, {"compression_method", 3}}; + } + else + engine_args_to_idx = {{"access_key_id", 1}, {"secret_access_key", 2}, {"format", 3}}; + } else { engine_args_to_idx = size_to_engine_args[engine_args.size()]; @@ -1364,6 +1406,8 @@ StorageS3::Configuration StorageS3::getConfiguration(ASTs & engine_args, Context if (engine_args_to_idx.contains("secret_access_key")) configuration.auth_settings.secret_access_key = checkAndGetLiteralArgument(engine_args[engine_args_to_idx["secret_access_key"]], "secret_access_key"); + + configuration.auth_settings.no_sign_request = no_sign_request; } configuration.static_configuration = !configuration.auth_settings.access_key_id.empty(); diff --git a/src/TableFunctions/TableFunctionS3.cpp b/src/TableFunctions/TableFunctionS3.cpp index f082b192ee0..9f5d992e4c9 100644 --- a/src/TableFunctions/TableFunctionS3.cpp +++ b/src/TableFunctions/TableFunctionS3.cpp @@ -18,6 +18,8 @@ #include "registerTableFunctions.h" #include +#include + namespace DB { @@ -52,36 +54,76 @@ void TableFunctionS3::parseArgumentsImpl( static std::unordered_map> size_to_args { {1, {{}}}, - {2, {{"format", 1}}}, - {5, {{"access_key_id", 1}, {"secret_access_key", 2}, {"format", 3}, {"structure", 4}}}, {6, {{"access_key_id", 1}, {"secret_access_key", 2}, {"format", 3}, {"structure", 4}, {"compression_method", 5}}} }; std::unordered_map args_to_idx; - /// For 4 arguments we support 2 possible variants: - /// s3(source, format, structure, compression_method) and s3(source, access_key_id, access_key_id, format) - /// We can distinguish them by looking at the 2-nd argument: check if it's a format name or not. - if (args.size() == 4) - { - auto second_arg = checkAndGetLiteralArgument(args[1], "format/access_key_id"); - if (second_arg == "auto" || FormatFactory::instance().getAllFormats().contains(second_arg)) - args_to_idx = {{"format", 1}, {"structure", 2}, {"compression_method", 3}}; + bool no_sign_request = false; + + /// For 2 arguments we support 2 possible variants: + /// - s3(source, format) + /// - s3(source, NOSIGN) + /// We can distinguish them by looking at the 2-nd argument: check if it's NOSIGN or not. + if (args.size() == 2) + { + auto second_arg = checkAndGetLiteralArgument(args[1], "format/NOSIGN"); + if (boost::iequals(second_arg, "NOSIGN")) + no_sign_request = true; else - args_to_idx = {{"access_key_id", 1}, {"secret_access_key", 2}, {"format", 3}}; + args_to_idx = {{"format", 1}}; } - /// For 3 arguments we support 2 possible variants: - /// s3(source, format, structure) and s3(source, access_key_id, access_key_id) + /// For 3 arguments we support 3 possible variants: + /// - s3(source, format, structure) + /// - s3(source, access_key_id, access_key_id) + /// - s3(source, NOSIGN, format) /// We can distinguish them by looking at the 2-nd argument: check if it's a format name or not. else if (args.size() == 3) { - - auto second_arg = checkAndGetLiteralArgument(args[1], "format/access_key_id"); - if (second_arg == "auto" || FormatFactory::instance().getAllFormats().contains(second_arg)) + auto second_arg = checkAndGetLiteralArgument(args[1], "format/access_key_id/NOSIGN"); + if (boost::iequals(second_arg, "NOSIGN")) + { + no_sign_request = true; + args_to_idx = {{"format", 2}}; + } + else if (second_arg == "auto" || FormatFactory::instance().getAllFormats().contains(second_arg)) args_to_idx = {{"format", 1}, {"structure", 2}}; else args_to_idx = {{"access_key_id", 1}, {"secret_access_key", 2}}; } + /// For 4 arguments we support 3 possible variants: + /// - s3(source, format, structure, compression_method), + /// - s3(source, access_key_id, access_key_id, format) + /// - s3(source, NOSIGN, format, structure) + /// We can distinguish them by looking at the 2-nd argument: check if it's a format name or not. + else if (args.size() == 4) + { + auto second_arg = checkAndGetLiteralArgument(args[1], "format/access_key_id/NOSIGN"); + if (boost::iequals(second_arg, "NOSIGN")) + { + no_sign_request = true; + args_to_idx = {{"format", 2}, {"structure", 3}}; + } + else if (second_arg == "auto" || FormatFactory::instance().getAllFormats().contains(second_arg)) + args_to_idx = {{"format", 1}, {"structure", 2}, {"compression_method", 3}}; + else + args_to_idx = {{"access_key_id", 1}, {"secret_access_key", 2}, {"format", 3}}; + } + /// For 5 arguments we support 2 possible variants: + /// - s3(source, access_key_id, access_key_id, format, structure) + /// - s3(source, NOSIGN, format, structure, compression_method) + /// We can distinguish them by looking at the 2-nd argument: check if it's a format name or not. + else if (args.size() == 5) + { + auto second_arg = checkAndGetLiteralArgument(args[1], "NOSIGN/access_key_id"); + if (boost::iequals(second_arg, "NOSIGN")) + { + no_sign_request = true; + args_to_idx = {{"format", 2}, {"structure", 3}, {"compression_method", 4}}; + } + else + args_to_idx = {{"access_key_id", 1}, {"secret_access_key", 2}, {"format", 3}, {"structure", 4}}; + } else { args_to_idx = size_to_args[args.size()]; @@ -104,6 +146,8 @@ void TableFunctionS3::parseArgumentsImpl( if (args_to_idx.contains("secret_access_key")) s3_configuration.auth_settings.secret_access_key = checkAndGetLiteralArgument(args[args_to_idx["secret_access_key"]], "secret_access_key"); + + s3_configuration.auth_settings.no_sign_request = no_sign_request; } /// For DataLake table functions, we should specify default format. diff --git a/tests/integration/test_storage_s3/test_invalid_env_credentials.py b/tests/integration/test_storage_s3/test_invalid_env_credentials.py index aa6479a2ed3..a599855cae1 100644 --- a/tests/integration/test_storage_s3/test_invalid_env_credentials.py +++ b/tests/integration/test_storage_s3/test_invalid_env_credentials.py @@ -105,25 +105,27 @@ def started_cluster(): def test_with_invalid_environment_credentials(started_cluster): - auth = "'minio','minio123'" - bucket = started_cluster.minio_restricted_bucket - instance = started_cluster.instances["s3_with_invalid_environment_credentials"] - instance.query( - f"insert into function s3('http://{started_cluster.minio_host}:{started_cluster.minio_port}/{bucket}/test_cache4.jsonl', {auth}) select * from numbers(100) settings s3_truncate_on_insert=1" - ) - with pytest.raises(helpers.client.QueryRuntimeException) as ei: + for (bucket, auth) in [ + (started_cluster.minio_restricted_bucket, "'minio', 'minio123'"), + (started_cluster.minio_bucket, "NOSIGN"), + ]: instance.query( - f"select count() from s3('http://{started_cluster.minio_host}:{started_cluster.minio_port}/{bucket}/test_cache4.jsonl')" + f"insert into function s3('http://{started_cluster.minio_host}:{started_cluster.minio_port}/{bucket}/test_cache4.jsonl', {auth}) select * from numbers(100) settings s3_truncate_on_insert=1" ) - assert ei.value.returncode == 243 - assert "HTTP response code: 403" in ei.value.stderr + with pytest.raises(helpers.client.QueryRuntimeException) as ei: + instance.query( + f"select count() from s3('http://{started_cluster.minio_host}:{started_cluster.minio_port}/{bucket}/test_cache4.jsonl')" + ) - assert ( - "100" - == instance.query( - f"select count() from s3('http://{started_cluster.minio_host}:{started_cluster.minio_port}/{bucket}/test_cache4.jsonl', {auth})" - ).strip() - ) + assert ei.value.returncode == 243 + assert "HTTP response code: 403" in ei.value.stderr + + assert ( + "100" + == instance.query( + f"select count() from s3('http://{started_cluster.minio_host}:{started_cluster.minio_port}/{bucket}/test_cache4.jsonl', {auth})" + ).strip() + ) From 4c61ec2faddb6b0e5ada9b1fa19ee2bc7469a0d5 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Tue, 28 Mar 2023 07:19:08 +0000 Subject: [PATCH 107/377] update docs --- .../engines/table-engines/integrations/s3.md | 21 ++++++++++++++++++- docs/en/sql-reference/table-functions/s3.md | 18 +++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/docs/en/engines/table-engines/integrations/s3.md b/docs/en/engines/table-engines/integrations/s3.md index dd843945e10..c539340d332 100644 --- a/docs/en/engines/table-engines/integrations/s3.md +++ b/docs/en/engines/table-engines/integrations/s3.md @@ -12,7 +12,7 @@ This engine provides integration with [Amazon S3](https://aws.amazon.com/s3/) ec ``` sql CREATE TABLE s3_engine_table (name String, value UInt32) - ENGINE = S3(path, [aws_access_key_id, aws_secret_access_key,] format, [compression]) + ENGINE = S3(path, [, NOSIGN | aws_access_key_id, aws_secret_access_key,] format, [compression]) [PARTITION BY expr] [SETTINGS ...] ``` @@ -20,6 +20,7 @@ CREATE TABLE s3_engine_table (name String, value UInt32) **Engine parameters** - `path` — Bucket url with path to file. Supports following wildcards in readonly mode: `*`, `?`, `{abc,def}` and `{N..M}` where `N`, `M` — numbers, `'abc'`, `'def'` — strings. For more information see [below](#wildcards-in-path). +- `NOSIGN` - If this keyword is provided in place of credentials, all the requests will not be signed. - `format` — The [format](../../../interfaces/formats.md#formats) of the file. - `aws_access_key_id`, `aws_secret_access_key` - Long-term credentials for the [AWS](https://aws.amazon.com/) account user. You can use these to authenticate your requests. Parameter is optional. If credentials are not specified, they are used from the configuration file. For more information see [Using S3 for Data Storage](../mergetree-family/mergetree.md#table_engine-mergetree-s3). - `compression` — Compression type. Supported values: `none`, `gzip/gz`, `brotli/br`, `xz/LZMA`, `zstd/zst`. Parameter is optional. By default, it will autodetect compression by file extension. @@ -151,6 +152,7 @@ The following settings can be specified in configuration file for given endpoint - `region` — Specifies S3 region name. Optional. - `use_insecure_imds_request` — If set to `true`, S3 client will use insecure IMDS request while obtaining credentials from Amazon EC2 metadata. Optional, default value is `false`. - `expiration_window_seconds` — Grace period for checking if expiration-based credentials have expired. Optional, default value is `120`. +- `no_sign_request` - Ignore all the credentials so requests are not signed. Useful for accessing public buckets. - `header` — Adds specified HTTP header to a request to given endpoint. Optional, can be specified multiple times. - `server_side_encryption_customer_key_base64` — If specified, required headers for accessing S3 objects with SSE-C encryption will be set. Optional. - `max_single_read_retries` — The maximum number of attempts during single read. Default value is `4`. Optional. @@ -168,6 +170,7 @@ The following settings can be specified in configuration file for given endpoint + @@ -175,6 +178,22 @@ The following settings can be specified in configuration file for given endpoint ``` +## Accessing public buckets + +ClickHouse tries to fetch credentials from many different types of sources. +Sometimes, it can produce problems when accessing some buckets that are public causing the client to return `403` error code. +This issue can be avoided by using `NOSIGN` keyword, forcing the client to ignore all the credentials, and not sign the requests. + +``` sql +SELECT * +FROM s3( + 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/aapl_stock.csv', + NOSIGN, + 'CSVWithNames' +) +LIMIT 5; +``` + ## See also - [s3 table function](../../../sql-reference/table-functions/s3.md) diff --git a/docs/en/sql-reference/table-functions/s3.md b/docs/en/sql-reference/table-functions/s3.md index 99b7832394d..023d5229a66 100644 --- a/docs/en/sql-reference/table-functions/s3.md +++ b/docs/en/sql-reference/table-functions/s3.md @@ -12,7 +12,7 @@ Provides a table-like interface to select/insert files in [Amazon S3](https://aw **Syntax** ``` sql -s3(path [,aws_access_key_id, aws_secret_access_key] [,format] [,structure] [,compression]) +s3(path [, NOSIGN | aws_access_key_id, aws_secret_access_key] [,format] [,structure] [,compression]) ``` :::tip GCS @@ -33,6 +33,7 @@ For GCS, substitute your HMAC key and HMAC secret where you see `aws_access_key_ and not ~~https://storage.cloud.google.com~~. ::: +- `NOSIGN` - If this keyword is provided in place of credentials, all the requests will not be signed. - `format` — The [format](../../interfaces/formats.md#formats) of the file. - `structure` — Structure of the table. Format `'column1_name column1_type, column2_name column2_type, ...'`. - `compression` — Parameter is optional. Supported values: `none`, `gzip/gz`, `brotli/br`, `xz/LZMA`, `zstd/zst`. By default, it will autodetect compression by file extension. @@ -185,6 +186,21 @@ INSERT INTO TABLE FUNCTION ``` As a result, the data is written into three files in different buckets: `my_bucket_1/file.csv`, `my_bucket_10/file.csv`, and `my_bucket_20/file.csv`. +## Accessing public buckets + +ClickHouse tries to fetch credentials from many different types of sources. +Sometimes, it can produce problems when accessing some buckets that are public causing the client to return `403` error code. +This issue can be avoided by using `NOSIGN` keyword, forcing the client to ignore all the credentials, and not sign the requests. + +``` sql +SELECT * +FROM s3( + 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/aapl_stock.csv', + NOSIGN, + 'CSVWithNames' +) +LIMIT 5; +``` **See Also** From a0b6cd63bba2cf1cdee72b79b641f6a1a4880b25 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Tue, 28 Mar 2023 07:22:31 +0000 Subject: [PATCH 108/377] fix build --- src/Common/ZooKeeper/ZooKeeperArgs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/ZooKeeper/ZooKeeperArgs.cpp b/src/Common/ZooKeeper/ZooKeeperArgs.cpp index 7cd85db931b..dc00d9a66f1 100644 --- a/src/Common/ZooKeeper/ZooKeeperArgs.cpp +++ b/src/Common/ZooKeeper/ZooKeeperArgs.cpp @@ -91,7 +91,7 @@ void ZooKeeperArgs::initFromKeeperServerSection(const Poco::Util::AbstractConfig if (auto session_timeout_key = coordination_key + ".session_timeout_ms"; config.has(session_timeout_key)) - session_timeout_key = config.getInt(session_timeout_key); + session_timeout_ms = config.getInt(session_timeout_key); } Poco::Util::AbstractConfiguration::Keys keys; From 6bf48796a9e25fb26555b89f3fee1b942d49bf0e Mon Sep 17 00:00:00 2001 From: robot-clickhouse Date: Tue, 28 Mar 2023 07:29:36 +0000 Subject: [PATCH 109/377] Automatic style fix --- .../integration/test_storage_s3/test_invalid_env_credentials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_storage_s3/test_invalid_env_credentials.py b/tests/integration/test_storage_s3/test_invalid_env_credentials.py index a599855cae1..06d49d8e828 100644 --- a/tests/integration/test_storage_s3/test_invalid_env_credentials.py +++ b/tests/integration/test_storage_s3/test_invalid_env_credentials.py @@ -107,7 +107,7 @@ def started_cluster(): def test_with_invalid_environment_credentials(started_cluster): instance = started_cluster.instances["s3_with_invalid_environment_credentials"] - for (bucket, auth) in [ + for bucket, auth in [ (started_cluster.minio_restricted_bucket, "'minio', 'minio123'"), (started_cluster.minio_bucket, "NOSIGN"), ]: From ac8dd527dcfd3028fb440d98ad5eebbb7eced5cc Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Tue, 28 Mar 2023 07:47:37 +0000 Subject: [PATCH 110/377] Address PR comments --- src/Planner/Planner.cpp | 5 ----- src/Planner/PlannerJoinTree.cpp | 7 +++---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Planner/Planner.cpp b/src/Planner/Planner.cpp index e38f460e7c5..a8e0d80ce8c 100644 --- a/src/Planner/Planner.cpp +++ b/src/Planner/Planner.cpp @@ -38,7 +38,6 @@ #include #include -#include #include #include #include @@ -70,10 +69,6 @@ #include #include -#include - -#include - namespace DB { diff --git a/src/Planner/PlannerJoinTree.cpp b/src/Planner/PlannerJoinTree.cpp index 1741cca17c5..0479170eba1 100644 --- a/src/Planner/PlannerJoinTree.cpp +++ b/src/Planner/PlannerJoinTree.cpp @@ -1,7 +1,6 @@ #include #include -#include "Storages/SelectQueryInfo.h" #include @@ -439,8 +438,8 @@ FilterDAGInfo buildRowPolicyFilterIfNeeded(const StoragePtr & storage, } FilterDAGInfo buildCustomKeyFilterIfNeeded(const StoragePtr & storage, - SelectQueryInfo & table_expression_query_info, - PlannerContextPtr & planner_context) + SelectQueryInfo & table_expression_query_info, + PlannerContextPtr & planner_context) { const auto & query_context = planner_context->getQueryContext(); const auto & settings = query_context->getSettingsRef(); @@ -688,7 +687,7 @@ JoinTreeQueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expres } else { - if (auto * distributed = dynamic_cast(storage.get()); + if (auto * distributed = typeid_cast(storage.get()); distributed && canUseCustomKey(settings, *distributed->getCluster(), *query_context)) { table_expression_query_info.use_custom_key = true; From 30e4ee705a32d74e990ea3f03892508c39fcc700 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Tue, 28 Mar 2023 07:54:23 +0000 Subject: [PATCH 111/377] Better --- src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index afaedf05816..e6428868d0c 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -388,7 +388,14 @@ SelectPartsDecision MergeTreeDataMergerMutator::selectPartsToMerge( if (static_cast(best_partition_it->second.min_age) >= data_settings->min_age_to_force_merge_seconds) return selectAllPartsToMergeWithinPartition( - future_part, can_merge_callback, best_partition_it->first, true, metadata_snapshot, txn, out_disable_reason, true); + future_part, + can_merge_callback, + best_partition_it->first, + /*final=*/true, + metadata_snapshot, + txn, + out_disable_reason, + /*optimize_skip_merged_partitions=*/true); } if (out_disable_reason) From 1605afe64788529cb9ab39d59adf0c251ba47a21 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Tue, 28 Mar 2023 08:12:21 +0000 Subject: [PATCH 112/377] Fix test --- src/Analyzer/Passes/ConvertQueryToCNFPass.cpp | 2 +- ...2_constraints_where_optimization.reference | 17 +++++++++++ .../01622_constraints_where_optimization.sql | 2 ++ .../01623_constraints_column_swap.reference | 29 +++++++++++++++++++ .../01623_constraints_column_swap.sql | 2 ++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp index 5998237f3ce..4d32c96b845 100644 --- a/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp +++ b/src/Analyzer/Passes/ConvertQueryToCNFPass.cpp @@ -705,7 +705,7 @@ public: if (filter_node == nullptr) return; - optimizeNode(query_node->getWhere(), table_expressions, context); + optimizeNode(filter_node, table_expressions, context); has_filter = true; }; diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.reference b/tests/queries/0_stateless/01622_constraints_where_optimization.reference index 52aca371a6a..9bb42ed1c27 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.reference @@ -56,6 +56,23 @@ QUERY id: 0 CONSTANT id: 7, constant_value: UInt64_8, constant_value_type: UInt8 SELECT count() FROM t_constraints_where +PREWHERE (b > 20) OR (b < 8) +QUERY id: 0 + PROJECTION COLUMNS + count() UInt64 + PROJECTION + LIST id: 1, nodes: 1 + FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 + JOIN TREE + TABLE id: 3, table_name: default.t_constraints_where + PREWHERE + FUNCTION id: 4, function_name: less, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 5, nodes: 2 + COLUMN id: 6, column_name: b, result_type: UInt32, source_id: 3 + CONSTANT id: 7, constant_value: UInt64_8, constant_value_type: UInt8 +SELECT count() +FROM t_constraints_where QUERY id: 0 PROJECTION COLUMNS count() UInt64 diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.sql b/tests/queries/0_stateless/01622_constraints_where_optimization.sql index 33fa62368b0..562c8705a51 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.sql @@ -15,6 +15,8 @@ EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b < 2; -- assumptio EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) +EXPLAIN SYNTAX SELECT count() FROM t_constraints_where PREWHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where PREWHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) DROP TABLE t_constraints_where; diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.reference b/tests/queries/0_stateless/01623_constraints_column_swap.reference index 520bd16ae25..124b5d06bed 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.reference +++ b/tests/queries/0_stateless/01623_constraints_column_swap.reference @@ -27,6 +27,35 @@ QUERY id: 0 LIST id: 12, nodes: 2 COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 CONSTANT id: 14, constant_value: UInt64_1, constant_value_type: UInt8 +SELECT + cityHash64(a) + 10, + b + 3 +FROM column_swap_test_test +PREWHERE cityHash64(a) = 1 +QUERY id: 0 + PROJECTION COLUMNS + plus(cityHash64(a), 10) UInt64 + plus(b, 3) UInt64 + PROJECTION + LIST id: 1, nodes: 2 + FUNCTION id: 2, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 3, nodes: 2 + COLUMN id: 4, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 6, constant_value: UInt64_10, constant_value_type: UInt8 + FUNCTION id: 7, function_name: plus, function_type: ordinary, result_type: UInt64 + ARGUMENTS + LIST id: 8, nodes: 2 + COLUMN id: 9, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 10, constant_value: UInt64_3, constant_value_type: UInt8 + JOIN TREE + TABLE id: 5, table_name: default.column_swap_test_test + PREWHERE + FUNCTION id: 11, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 12, nodes: 2 + COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 + CONSTANT id: 14, constant_value: UInt64_1, constant_value_type: UInt8 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.sql b/tests/queries/0_stateless/01623_constraints_column_swap.sql index 97e014d9c25..6d70b78194d 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.sql +++ b/tests/queries/0_stateless/01623_constraints_column_swap.sql @@ -14,6 +14,8 @@ INSERT INTO column_swap_test_test SELECT number AS i, format('test {} kek {}', t EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; +EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test PREWHERE cityHash64(a) = 1; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test PREWHERE cityHash64(a) = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; From c64e9bd233cfae3c0def138ffb9107c64b084feb Mon Sep 17 00:00:00 2001 From: zvonand Date: Tue, 28 Mar 2023 10:31:52 +0200 Subject: [PATCH 113/377] fix style --- src/Functions/FunctionToDecimalString.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Functions/FunctionToDecimalString.h b/src/Functions/FunctionToDecimalString.h index 16fcd9e687f..1e244743d51 100644 --- a/src/Functions/FunctionToDecimalString.h +++ b/src/Functions/FunctionToDecimalString.h @@ -192,7 +192,8 @@ private: throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, "Too many fractional symbols requested, shall not be more than {}", max_digits); writeText(value, out); - if (likely(precision > 0)){ + if (likely(precision > 0)) + { writeChar('.', out); for (int i = 0; i < precision; ++i) writeChar('0', out); From 30444376b078095168d80fee5b4e98010c8f4766 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 28 Mar 2023 08:45:52 +0000 Subject: [PATCH 114/377] Follow-up to #47838 --- src/Functions/FunctionToDecimalString.cpp | 2 - src/Functions/FunctionToDecimalString.h | 427 +++++++++--------- .../0_stateless/02676_to_decimal_string.sql | 10 +- 3 files changed, 212 insertions(+), 227 deletions(-) diff --git a/src/Functions/FunctionToDecimalString.cpp b/src/Functions/FunctionToDecimalString.cpp index e9687d80177..fb076d03c6b 100644 --- a/src/Functions/FunctionToDecimalString.cpp +++ b/src/Functions/FunctionToDecimalString.cpp @@ -20,5 +20,3 @@ second argument is the desired number of digits in fractional part. Returns Stri } } - - diff --git a/src/Functions/FunctionToDecimalString.h b/src/Functions/FunctionToDecimalString.h index 1e244743d51..e33c9f84f00 100644 --- a/src/Functions/FunctionToDecimalString.h +++ b/src/Functions/FunctionToDecimalString.h @@ -1,6 +1,5 @@ #pragma once - #include #include @@ -28,180 +27,6 @@ namespace ErrorCodes extern const int CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER; } -struct Processor -{ - /// For operations with Integer/Float - template - void vectorConstant(const FromVectorType & vec_from, const UInt8 value_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const - { - size_t input_rows_count = vec_from.size(); - result_offsets.resize(input_rows_count); - - /// Buffer is used here and in functions below because resulting size cannot be precisely anticipated, - /// and buffer resizes on-the-go. Also, .count() provided by buffer is convenient in this case. - WriteBufferFromVector buf_to(vec_to); - - for (size_t i = 0; i < input_rows_count; ++i) - { - format(vec_from[i], buf_to, value_precision); - result_offsets[i] = buf_to.count(); - } - - buf_to.finalize(); - } - - template - void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const - { - constexpr size_t max_digits = std::numeric_limits::digits10; - - size_t input_rows_count = vec_from.size(); - result_offsets.resize(input_rows_count); - WriteBufferFromVector buf_to(vec_to); - - for (size_t i = 0; i < input_rows_count; ++i) - { - if (vec_precision[i] > max_digits) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, - "Too many fractional symbols requested, shall not be more than {}", max_digits); - format(vec_from[i], buf_to, vec_precision[i]); - result_offsets[i] = buf_to.count(); - } - - buf_to.finalize(); - } - - template - void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const - { - constexpr size_t max_digits = std::numeric_limits::digits10; - - size_t input_rows_count = vec_precision.size(); - result_offsets.resize(input_rows_count); - WriteBufferFromVector buf_to(vec_to); - - for (size_t i = 0; i < input_rows_count; ++i) - { - if (vec_precision[i] > max_digits) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, - "Too many fractional symbols requested, shall not be more than {}", max_digits); - format(value_from, buf_to, vec_precision[i]); - result_offsets[i] = buf_to.count(); - } - - buf_to.finalize(); - } - - /// For operations with Decimal - template - void vectorConstant(const FirstArgVectorType & vec_from, const UInt8 value_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const - { - /// There are no more than 77 meaning digits (as it is the max length of UInt256). So we can limit it with 77. - constexpr size_t max_digits = std::numeric_limits::digits10; - if (value_precision > max_digits) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, - "Too many fractional symbols requested for Decimal, must be 77 or less"); - - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_from.size(); - result_offsets.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - writeText(vec_from[i], from_scale, buf_to, true, true, value_precision); - writeChar(0, buf_to); - result_offsets[i] = buf_to.count(); - } - buf_to.finalize(); - } - - template - void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const - { - constexpr size_t max_digits = std::numeric_limits::digits10; - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_from.size(); - result_offsets.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - if (vec_precision[i] > max_digits) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, - "Too many fractional symbols requested for Decimal, must be 77 or less"); - writeText(vec_from[i], from_scale, buf_to, true, true, vec_precision[i]); - writeChar(0, buf_to); - result_offsets[i] = buf_to.count(); - } - buf_to.finalize(); - } - - template - void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, - ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, const UInt8 from_scale) const - { - constexpr size_t max_digits = std::numeric_limits::digits10; - WriteBufferFromVector buf_to(vec_to); - size_t input_rows_count = vec_precision.size(); - result_offsets.resize(input_rows_count); - - for (size_t i = 0; i < input_rows_count; ++i) - { - if (vec_precision[i] > max_digits) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, - "Too many fractional symbols requested for Decimal, must be 77 or less"); - writeText(value_from, from_scale, buf_to, true, true, vec_precision[i]); - writeChar(0, buf_to); - result_offsets[i] = buf_to.count(); - } - buf_to.finalize(); - } -private: - - template - static void format(T value, DB::WriteBuffer & out, UInt8 precision) - { - /// Maximum is hard-coded in 'contrib/double-conversion' for floating point values, - /// Catch this here to give user a more reasonable error. - if (precision > double_conversion::DoubleToStringConverter::kMaxFixedDigitsAfterPoint) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, - "Too many fractional symbols requested for Float, must be 60 or less"); - - DB::DoubleConverter::BufferType buffer; - double_conversion::StringBuilder builder{buffer, sizeof(buffer)}; - - const auto result = DB::DoubleConverter::instance().ToFixed(value, precision, &builder); - - if (!result) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, "Error processing number: {}", value); - - out.write(buffer, builder.position()); - writeChar(0, out); - } - - template - static void format(T value, DB::WriteBuffer & out, UInt8 precision) - { - /// Fractional part for Integer is just trailing zeros. Let's limit it with 77 (like with Decimals). - constexpr size_t max_digits = std::numeric_limits::digits10; - if (precision > max_digits) - throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, - "Too many fractional symbols requested, shall not be more than {}", max_digits); - writeText(value, out); - if (likely(precision > 0)) - { - writeChar('.', out); - for (int i = 0; i < precision; ++i) - writeChar('0', out); - writeChar(0, out); - } - } -}; - class FunctionToDecimalString : public IFunction { public: @@ -231,33 +56,209 @@ public: bool useDefaultImplementationForConstants() const override { return true; } +private: + /// For operations with Integer/Float + template + void vectorConstant(const FromVectorType & vec_from, UInt8 precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const + { + size_t input_rows_count = vec_from.size(); + result_offsets.resize(input_rows_count); + + /// Buffer is used here and in functions below because resulting size cannot be precisely anticipated, + /// and buffer resizes on-the-go. Also, .count() provided by buffer is convenient in this case. + WriteBufferFromVector buf_to(vec_to); + + for (size_t i = 0; i < input_rows_count; ++i) + { + format(vec_from[i], buf_to, precision); + result_offsets[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + template + void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const + { + size_t input_rows_count = vec_from.size(); + result_offsets.resize(input_rows_count); + + WriteBufferFromVector buf_to(vec_to); + + constexpr size_t max_digits = std::numeric_limits::digits10; + + for (size_t i = 0; i < input_rows_count; ++i) + { + if (vec_precision[i] > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional digits requested, shall not be more than {}", max_digits); + format(vec_from[i], buf_to, vec_precision[i]); + result_offsets[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + template + void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const + { + size_t input_rows_count = vec_precision.size(); + result_offsets.resize(input_rows_count); + + WriteBufferFromVector buf_to(vec_to); + + constexpr size_t max_digits = std::numeric_limits::digits10; + + for (size_t i = 0; i < input_rows_count; ++i) + { + if (vec_precision[i] > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional digits requested, shall not be more than {}", max_digits); + format(value_from, buf_to, vec_precision[i]); + result_offsets[i] = buf_to.count(); + } + + buf_to.finalize(); + } + + /// For operations with Decimal + template + void vectorConstant(const FirstArgVectorType & vec_from, UInt8 precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, UInt8 from_scale) const + { + /// There are no more than 77 meaning digits (as it is the max length of UInt256). So we can limit it with 77. + constexpr size_t max_digits = std::numeric_limits::digits10; + if (precision > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional digits requested for Decimal, must not be more than {}", max_digits); + + WriteBufferFromVector buf_to(vec_to); + size_t input_rows_count = vec_from.size(); + result_offsets.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + writeText(vec_from[i], from_scale, buf_to, true, true, precision); + writeChar(0, buf_to); + result_offsets[i] = buf_to.count(); + } + buf_to.finalize(); + } + + template + void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, UInt8 from_scale) const + { + size_t input_rows_count = vec_from.size(); + result_offsets.resize(input_rows_count); + + WriteBufferFromVector buf_to(vec_to); + + constexpr size_t max_digits = std::numeric_limits::digits10; + + for (size_t i = 0; i < input_rows_count; ++i) + { + if (vec_precision[i] > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional digits requested for Decimal, must not be more than {}", max_digits); + writeText(vec_from[i], from_scale, buf_to, true, true, vec_precision[i]); + writeChar(0, buf_to); + result_offsets[i] = buf_to.count(); + } + buf_to.finalize(); + } + + template + void constantVector(const FirstArgType & value_from, const ColumnVector::Container & vec_precision, + ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, UInt8 from_scale) const + { + size_t input_rows_count = vec_precision.size(); + result_offsets.resize(input_rows_count); + + WriteBufferFromVector buf_to(vec_to); + + constexpr size_t max_digits = std::numeric_limits::digits10; + + for (size_t i = 0; i < input_rows_count; ++i) + { + if (vec_precision[i] > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional digits requested for Decimal, must not be more than {}", max_digits); + writeText(value_from, from_scale, buf_to, true, true, vec_precision[i]); + writeChar(0, buf_to); + result_offsets[i] = buf_to.count(); + } + buf_to.finalize(); + } + + template + static void format(T value, DB::WriteBuffer & out, UInt8 precision) + { + /// Maximum is hard-coded in 'contrib/double-conversion' for floating point values, + /// Catch this here to give user a more reasonable error. + if (precision > double_conversion::DoubleToStringConverter::kMaxFixedDigitsAfterPoint) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional digits requested for Float, must not be more than {}", double_conversion::DoubleToStringConverter::kMaxFixedDigitsAfterPoint); + + DB::DoubleConverter::BufferType buffer; + double_conversion::StringBuilder builder{buffer, sizeof(buffer)}; + + const auto result = DB::DoubleConverter::instance().ToFixed(value, precision, &builder); + + if (!result) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, "Error processing number: {}", value); + + out.write(buffer, builder.position()); + writeChar(0, out); + } + + template + static void format(T value, DB::WriteBuffer & out, UInt8 precision) + { + /// Fractional part for Integer is just trailing zeros. Let's limit it with 77 (like with Decimals). + constexpr size_t max_digits = std::numeric_limits::digits10; + if (precision > max_digits) + throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, + "Too many fractional digits requested, shall not be more than {}", max_digits); + writeText(value, out); + if (precision > 0) [[likely]] + { + writeChar('.', out); + for (int i = 0; i < precision; ++i) + writeChar('0', out); + writeChar(0, out); + } + } + +public: ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override { switch (arguments[0].type->getTypeId()) { - case TypeIndex::UInt8: return executeType(arguments); - case TypeIndex::UInt16: return executeType(arguments); - case TypeIndex::UInt32: return executeType(arguments); - case TypeIndex::UInt64: return executeType(arguments); - case TypeIndex::UInt128: return executeType(arguments); - case TypeIndex::UInt256: return executeType(arguments); - case TypeIndex::Int8: return executeType(arguments); - case TypeIndex::Int16: return executeType(arguments); - case TypeIndex::Int32: return executeType(arguments); - case TypeIndex::Int64: return executeType(arguments); - case TypeIndex::Int128: return executeType(arguments); - case TypeIndex::Int256: return executeType(arguments); - case TypeIndex::Float32: return executeType(arguments); - case TypeIndex::Float64: return executeType(arguments); - case TypeIndex::Decimal32: return executeType(arguments); - case TypeIndex::Decimal64: return executeType(arguments); + case TypeIndex::UInt8: return executeType(arguments); + case TypeIndex::UInt16: return executeType(arguments); + case TypeIndex::UInt32: return executeType(arguments); + case TypeIndex::UInt64: return executeType(arguments); + case TypeIndex::UInt128: return executeType(arguments); + case TypeIndex::UInt256: return executeType(arguments); + case TypeIndex::Int8: return executeType(arguments); + case TypeIndex::Int16: return executeType(arguments); + case TypeIndex::Int32: return executeType(arguments); + case TypeIndex::Int64: return executeType(arguments); + case TypeIndex::Int128: return executeType(arguments); + case TypeIndex::Int256: return executeType(arguments); + case TypeIndex::Float32: return executeType(arguments); + case TypeIndex::Float64: return executeType(arguments); + case TypeIndex::Decimal32: return executeType(arguments); + case TypeIndex::Decimal64: return executeType(arguments); case TypeIndex::Decimal128: return executeType(arguments); case TypeIndex::Decimal256: return executeType(arguments); - default: throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", arguments[0].column->getName(), getName()); - } } @@ -265,16 +266,14 @@ private: template ColumnPtr executeType(const ColumnsWithTypeAndName & arguments) const { - auto result_col = ColumnString::create(); - auto *result_col_string = assert_cast(result_col.get()); - ColumnString::Chars & result_chars = result_col_string->getChars(); - ColumnString::Offsets & result_offsets = result_col_string->getOffsets(); - - const auto * precision_col = checkAndGetColumn>(arguments[1].column.get()); const auto * from_col_const = typeid_cast(arguments[0].column.get()); + const auto * precision_col = checkAndGetColumn>(arguments[1].column.get()); const auto * precision_col_const = typeid_cast(arguments[1].column.get()); - Processor processor; + auto result_col = ColumnString::create(); + auto * result_col_string = assert_cast(result_col.get()); + ColumnString::Chars & result_chars = result_col_string->getChars(); + ColumnString::Offsets & result_offsets = result_col_string->getOffsets(); if constexpr (is_decimal) { @@ -284,19 +283,14 @@ private: if (from_col) { if (precision_col_const) - processor.vectorConstant(from_col->getData(), precision_col_const->template getValue(), result_chars, result_offsets, from_scale); + vectorConstant(from_col->getData(), precision_col_const->template getValue(), result_chars, result_offsets, from_scale); else - processor.vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets, from_scale); + vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets, from_scale); } else if (from_col_const) - { - processor.constantVector(from_col_const->template getValue(), precision_col->getData(), result_chars, result_offsets, from_scale); - } + constantVector(from_col_const->template getValue(), precision_col->getData(), result_chars, result_offsets, from_scale); else - { - throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", - arguments[0].column->getName()); - } + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", arguments[0].column->getName()); } else { @@ -304,21 +298,14 @@ private: if (from_col) { if (precision_col_const) - processor.vectorConstant(from_col->getData(), precision_col_const->template getValue(), result_chars, result_offsets); + vectorConstant(from_col->getData(), precision_col_const->template getValue(), result_chars, result_offsets); else - processor.vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets); + vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets); } else if (from_col_const) - { - processor.constantVector(from_col_const->template getValue(), precision_col->getData(), result_chars, result_offsets); - } + constantVector(from_col_const->template getValue(), precision_col->getData(), result_chars, result_offsets); else - { - throw Exception( - ErrorCodes::ILLEGAL_COLUMN, - "Illegal column {} of first argument of function formatDecimal", - arguments[0].column->getName()); - } + throw Exception( ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", arguments[0].column->getName()); } return result_col; diff --git a/tests/queries/0_stateless/02676_to_decimal_string.sql b/tests/queries/0_stateless/02676_to_decimal_string.sql index 0931d25fc71..563d60c62c7 100644 --- a/tests/queries/0_stateless/02676_to_decimal_string.sql +++ b/tests/queries/0_stateless/02676_to_decimal_string.sql @@ -28,8 +28,8 @@ SELECT toDecimalString('-128.789323123321329854641231237893231233213298546'::Dec -- Max number of decimal fractional digits is defined as 77 for Int/UInt/Decimal and 60 for Float. -- These values shall work OK. -SELECT toDecimalString('32.32'::Float32, 61); -- {serverError 28} -SELECT toDecimalString('64.64'::Float64, 61); -- {serverError 28} -SELECT toDecimalString('88'::UInt8, 78); -- {serverError 28} -SELECT toDecimalString('646464'::Int256, 78); -- {serverError 28} -SELECT toDecimalString('-128.789323123321329854641231237893231233213298546'::Decimal256(45), 78); -- {serverError 28} \ No newline at end of file +SELECT toDecimalString('32.32'::Float32, 61); -- {serverError CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER} +SELECT toDecimalString('64.64'::Float64, 61); -- {serverError CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER} +SELECT toDecimalString('88'::UInt8, 78); -- {serverError CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER} +SELECT toDecimalString('646464'::Int256, 78); -- {serverError CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER} +SELECT toDecimalString('-128.789323123321329854641231237893231233213298546'::Decimal256(45), 78); -- {serverError CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER} From 406e654b002bfe8a226e36b16fb9e9719fc175f5 Mon Sep 17 00:00:00 2001 From: zvonand Date: Tue, 28 Mar 2023 10:56:04 +0200 Subject: [PATCH 115/377] removed unnecessary imports --- src/Functions/FunctionToDecimalString.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Functions/FunctionToDecimalString.h b/src/Functions/FunctionToDecimalString.h index e33c9f84f00..ef9c2783039 100644 --- a/src/Functions/FunctionToDecimalString.h +++ b/src/Functions/FunctionToDecimalString.h @@ -1,8 +1,5 @@ #pragma once -#include -#include - #include #include #include From 6f1e50598a472388d8267f38bcb860633bd44f19 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Tue, 28 Mar 2023 09:32:23 +0000 Subject: [PATCH 116/377] fix test --- src/IO/S3/tests/gtest_aws_s3_client.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/IO/S3/tests/gtest_aws_s3_client.cpp b/src/IO/S3/tests/gtest_aws_s3_client.cpp index bbbf6a430ab..3e0ed21d01c 100644 --- a/src/IO/S3/tests/gtest_aws_s3_client.cpp +++ b/src/IO/S3/tests/gtest_aws_s3_client.cpp @@ -1,5 +1,6 @@ #include +#include "IO/S3/Credentials.h" #include "config.h" @@ -109,8 +110,11 @@ TEST(IOTestAwsS3Client, AppendExtraSSECHeaders) secret_access_key, server_side_encryption_customer_key_base64, headers, - use_environment_credentials, - use_insecure_imds_request + DB::S3::CredentialsConfiguration + { + .use_environment_credentials = use_environment_credentials, + .use_insecure_imds_request = use_insecure_imds_request + } ); ASSERT_TRUE(client); From 57226fcb25d002a087ff0fbd2bf60b982dc83558 Mon Sep 17 00:00:00 2001 From: kssenii Date: Tue, 28 Mar 2023 11:43:15 +0200 Subject: [PATCH 117/377] Review fixes --- src/Storages/RabbitMQ/RabbitMQSettings.h | 2 +- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 20 +++++++++---------- src/Storages/RabbitMQ/StorageRabbitMQ.h | 5 +++-- .../integration/test_storage_rabbitmq/test.py | 1 - 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.h b/src/Storages/RabbitMQ/RabbitMQSettings.h index d6938a67256..58d80c6ec9d 100644 --- a/src/Storages/RabbitMQ/RabbitMQSettings.h +++ b/src/Storages/RabbitMQ/RabbitMQSettings.h @@ -29,7 +29,7 @@ namespace DB M(String, rabbitmq_queue_settings_list, "", "A list of rabbitmq queue settings", 0) \ M(UInt64, rabbitmq_empty_queue_backoff_start_ms, 10, "A minimum backoff point to reschedule read if the rabbitmq queue is empty", 0) \ M(UInt64, rabbitmq_empty_queue_backoff_end_ms, 10000, "A maximum backoff point to reschedule read if the rabbitmq queue is empty", 0) \ - M(UInt64, rabbitmq_empty_queue_backoff_step_ms, 100, "A maximum backoff point to reschedule read if the rabbitmq queue is empty", 0) \ + M(UInt64, rabbitmq_empty_queue_backoff_step_ms, 100, "A backoff step to reschedule read if the rabbitmq queue is empty", 0) \ M(Bool, rabbitmq_queue_consume, false, "Use user-defined queues and do not make any RabbitMQ setup: declaring exchanges, queues, bindings", 0) \ M(String, rabbitmq_username, "", "RabbitMQ username", 0) \ M(String, rabbitmq_password, "", "RabbitMQ password", 0) \ diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index da9bd8a482f..c17b7d829d9 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -950,7 +950,7 @@ RabbitMQConsumerPtr StorageRabbitMQ::createConsumer() unique_strbase, log, queue_size, shutdown_called); } -bool StorageRabbitMQ::checkDependencies(const StorageID & table_id) +bool StorageRabbitMQ::hasDependencies(const StorageID & table_id) { // Check if all dependencies are attached auto view_ids = DatabaseCatalog::instance().getDependentViews(table_id); @@ -1010,12 +1010,12 @@ void StorageRabbitMQ::streamingToViewsFunc() // Keep streaming as long as there are attached views and streaming is not cancelled while (!shutdown_called && num_created_consumers > 0) { - if (!checkDependencies(table_id)) + if (!hasDependencies(table_id)) break; LOG_DEBUG(log, "Started streaming to {} attached views", num_views); - bool continue_reading = !streamToViews(); + bool continue_reading = tryStreamToViews(); if (!continue_reading) break; @@ -1055,7 +1055,7 @@ void StorageRabbitMQ::streamingToViewsFunc() } -bool StorageRabbitMQ::streamToViews() +bool StorageRabbitMQ::tryStreamToViews() { auto table_id = getStorageID(); auto table = DatabaseCatalog::instance().getTable(table_id, getContext()); @@ -1113,17 +1113,17 @@ bool StorageRabbitMQ::streamToViews() deactivateTask(looping_task, false, true); size_t queue_empty = 0; - if (!checkDependencies(getStorageID())) + if (!hasDependencies(getStorageID())) { /// Do not commit to rabbitmq if the dependency was removed. LOG_TRACE(log, "No dependencies, reschedule"); - return true; + return false; } if (!connection->isConnected()) { if (shutdown_called) - return true; + return false; if (connection->reconnect()) { @@ -1134,7 +1134,7 @@ bool StorageRabbitMQ::streamToViews() else { LOG_TRACE(log, "Reschedule streaming. Unable to restore connection."); - return true; + return false; } } else @@ -1180,7 +1180,7 @@ bool StorageRabbitMQ::streamToViews() connection->heartbeat(); read_attempts = 0; LOG_TRACE(log, "Reschedule streaming. Queues are empty."); - return true; + return false; } else { @@ -1188,7 +1188,7 @@ bool StorageRabbitMQ::streamToViews() } /// Do not reschedule, do not stop event loop. - return false; + return true; } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index a3f51e43baa..19ec5cc206e 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -191,8 +191,9 @@ private: void bindExchange(AMQP::TcpChannel & rabbit_channel); void bindQueue(size_t queue_id, AMQP::TcpChannel & rabbit_channel); - bool streamToViews(); - bool checkDependencies(const StorageID & table_id); + /// Return true on successful stream attempt. + bool tryStreamToViews(); + bool hasDependencies(const StorageID & table_id); static String getRandomName() { diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index aec98e1cb73..7b6e79f0809 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -2710,7 +2710,6 @@ def test_rabbitmq_drop_mv(rabbitmq_cluster): exchange="mv", routing_key="", body=json.dumps({"key": i, "value": i}) ) - start = time.time() while True: res = instance.query("SELECT COUNT(*) FROM test.view") print(f"Current count (1): {res}") From 943a4f75f85006a9e6c0c07132318b30d992e1c2 Mon Sep 17 00:00:00 2001 From: kssenii Date: Tue, 28 Mar 2023 12:00:56 +0200 Subject: [PATCH 118/377] Catch all exceptions to avoid replication stuck --- .../PostgreSQL/PostgreSQLReplicationHandler.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp b/src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp index f9bfe1d174a..322ad3c78c0 100644 --- a/src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp +++ b/src/Storages/PostgreSQL/PostgreSQLReplicationHandler.cpp @@ -417,7 +417,15 @@ void PostgreSQLReplicationHandler::consumerFunc() { assertInitialized(); - bool schedule_now = getConsumer()->consume(); + bool schedule_now = true; + try + { + schedule_now = getConsumer()->consume(); + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } if (stop_synchronization) { From a2df2b73fa78100be9de783ffc7eb25c02deb61e Mon Sep 17 00:00:00 2001 From: Nikolay Degterinsky Date: Tue, 28 Mar 2023 10:51:27 +0000 Subject: [PATCH 119/377] Update SQLite to 3.41.2 --- .gitmodules | 2 +- contrib/sqlite-amalgamation | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index ca55281e643..00b470bc547 100644 --- a/.gitmodules +++ b/.gitmodules @@ -218,7 +218,7 @@ url = https://github.com/ClickHouse/libpqxx [submodule "contrib/sqlite-amalgamation"] path = contrib/sqlite-amalgamation - url = https://github.com/ClickHouse/sqlite-amalgamation + url = https://github.com/evillique/sqlite-amalgamation [submodule "contrib/s2geometry"] path = contrib/s2geometry url = https://github.com/ClickHouse/s2geometry diff --git a/contrib/sqlite-amalgamation b/contrib/sqlite-amalgamation index 400ad7152a0..20598079891 160000 --- a/contrib/sqlite-amalgamation +++ b/contrib/sqlite-amalgamation @@ -1 +1 @@ -Subproject commit 400ad7152a0c7ee07756d96ab4f6a8f6d1080916 +Subproject commit 20598079891d27ef1a3ad3f66bbfa3f983c25268 From 7a85974c356fca2b941c988ebf63de9ccf837c73 Mon Sep 17 00:00:00 2001 From: vdimir Date: Tue, 28 Mar 2023 11:04:07 +0000 Subject: [PATCH 120/377] Fix crash in explain graph with StorageMerge --- src/Storages/StorageMerge.cpp | 2 +- src/Storages/StorageMerge.h | 2 ++ .../25402_storage_merge_explain_graph_crash.reference | 0 .../25402_storage_merge_explain_graph_crash.sql | 9 +++++++++ 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.reference create mode 100644 tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.sql diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 0f7b47255f6..40ad4ab0f26 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -655,7 +655,7 @@ QueryPipelineBuilderPtr ReadFromMerge::createSources( if (real_column_names.empty()) real_column_names.push_back(ExpressionActions::getSmallestColumn(storage_snapshot->metadata->getColumns().getAllPhysical()).name); - QueryPlan plan; + QueryPlan & plan = child_plans.emplace_back(); StorageView * view = dynamic_cast(storage.get()); if (!view || allow_experimental_analyzer) diff --git a/src/Storages/StorageMerge.h b/src/Storages/StorageMerge.h index 4bc47375047..602da045dfb 100644 --- a/src/Storages/StorageMerge.h +++ b/src/Storages/StorageMerge.h @@ -159,6 +159,8 @@ private: StoragePtr storage_merge; StorageSnapshotPtr merge_storage_snapshot; + std::vector child_plans; + SelectQueryInfo query_info; ContextMutablePtr context; QueryProcessingStage::Enum common_processed_stage; diff --git a/tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.reference b/tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.sql b/tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.sql new file mode 100644 index 00000000000..b1725da6e82 --- /dev/null +++ b/tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.sql @@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS foo; +DROP TABLE IF EXISTS merge1; + +CREATE TABLE foo (`Id` Int32, `Val` Int32) ENGINE = MergeTree ORDER BY Id; +INSERT INTO foo SELECT number, number FROM numbers(100); + +CREATE TABLE merge1 AS foo ENGINE = Merge(currentDatabase(), '^foo'); + +EXPLAIN PIPELINE graph = 1, compact = 1 SELECT * FROM merge1 FORMAT Null; From edc80fbd94399bb35b9a3db875ceda6f1ffd649f Mon Sep 17 00:00:00 2001 From: zvonand Date: Tue, 28 Mar 2023 13:25:18 +0200 Subject: [PATCH 121/377] updated docs + fix linker isssue --- .../en/sql-reference/functions/type-conversion-functions.md | 2 ++ .../ru/sql-reference/functions/type-conversion-functions.md | 6 ++++-- src/Functions/FunctionToDecimalString.h | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index ee44b6e55fa..73156898d4d 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -751,6 +751,8 @@ toDecimalString(number, scale) - `number` — Value to be represented as String, [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md), [Float](/docs/en/sql-reference/data-types/float.md), [Decimal](/docs/en/sql-reference/data-types/decimal.md), - `scale` — Number of fractional digits, [UInt8](/docs/en/sql-reference/data-types/int-uint.md). + * Maximum scale for [Decimal](/docs/en/sql-reference/data-types/decimal.md) and [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md) types is 77 (it is the maximum possible number of significant digits for Decimal), + * Maximum scale for [Float](/docs/en/sql-reference/data-types/float.md) is 60. **Returned value** diff --git a/docs/ru/sql-reference/functions/type-conversion-functions.md b/docs/ru/sql-reference/functions/type-conversion-functions.md index df047b1856f..298b7bbc93e 100644 --- a/docs/ru/sql-reference/functions/type-conversion-functions.md +++ b/docs/ru/sql-reference/functions/type-conversion-functions.md @@ -565,8 +565,10 @@ toDecimalString(number, scale) **Параметры** -- `number` — Значение любого числового типа: [Int, UInt](/docs/en/sql-reference/data-types/int-uint.md), [Float](/docs/en/sql-reference/data-types/float.md), [Decimal](/docs/en/sql-reference/data-types/decimal.md), -- `scale` — Требуемое количество десятичных знаков после запятой, [UInt8](/docs/en/sql-reference/data-types/int-uint.md). +- `number` — Значение любого числового типа: [Int, UInt](/docs/ru/sql-reference/data-types/int-uint.md), [Float](/docs/ru/sql-reference/data-types/float.md), [Decimal](/docs/ru/sql-reference/data-types/decimal.md), +- `scale` — Требуемое количество десятичных знаков после запятой, [UInt8](/docs/ru/sql-reference/data-types/int-uint.md). + * Значение `scale` для типов [Decimal](/docs/ru/sql-reference/data-types/decimal.md) и [Int, UInt](/docs/ru/sql-reference/data-types/int-uint.md) должно не превышать 77 (так как это наибольшее количество значимых символов для этих типов), + * Значение `scale` для типа [Float](/docs/ru/sql-reference/data-types/float.md) не должно превышать 60. **Возвращаемое значение** diff --git a/src/Functions/FunctionToDecimalString.h b/src/Functions/FunctionToDecimalString.h index ef9c2783039..097c0593fa4 100644 --- a/src/Functions/FunctionToDecimalString.h +++ b/src/Functions/FunctionToDecimalString.h @@ -194,11 +194,11 @@ private: template static void format(T value, DB::WriteBuffer & out, UInt8 precision) { - /// Maximum is hard-coded in 'contrib/double-conversion' for floating point values, + /// Maximum of 60 is hard-coded in 'double-conversion/double-conversion.h' for floating point values, /// Catch this here to give user a more reasonable error. - if (precision > double_conversion::DoubleToStringConverter::kMaxFixedDigitsAfterPoint) + if (precision > 60) throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, - "Too many fractional digits requested for Float, must not be more than {}", double_conversion::DoubleToStringConverter::kMaxFixedDigitsAfterPoint); + "Too high precision requested for Float, must not be more than 60, got {}", Int8(precision)); DB::DoubleConverter::BufferType buffer; double_conversion::StringBuilder builder{buffer, sizeof(buffer)}; From e01743a5cdd996d96f711ad5a9056b89ec474c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Tue, 28 Mar 2023 13:34:23 +0200 Subject: [PATCH 122/377] Try to fix unrelated flakiness --- tests/integration/test_version_update_after_mutation/test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_version_update_after_mutation/test.py b/tests/integration/test_version_update_after_mutation/test.py index 6b27c69462a..67f7ce47451 100644 --- a/tests/integration/test_version_update_after_mutation/test.py +++ b/tests/integration/test_version_update_after_mutation/test.py @@ -91,8 +91,8 @@ def test_mutate_and_upgrade(start_cluster): node2.query("OPTIMIZE TABLE mt FINAL") - assert node1.query("SELECT id FROM mt") == "1\n4\n" - assert node2.query("SELECT id FROM mt") == "1\n4\n" + assert node1.query("SELECT id FROM mt ORDER BY id") == "1\n4\n" + assert node2.query("SELECT id FROM mt ORDER BY id") == "1\n4\n" for node in [node1, node2]: node.query("DROP TABLE mt") From 79f06ddfef0513b70af9f975b3d858701003fdfe Mon Sep 17 00:00:00 2001 From: zvonand Date: Tue, 28 Mar 2023 13:58:37 +0200 Subject: [PATCH 123/377] style fix --- src/Functions/FunctionToDecimalString.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/FunctionToDecimalString.h b/src/Functions/FunctionToDecimalString.h index 097c0593fa4..6ae007e6b66 100644 --- a/src/Functions/FunctionToDecimalString.h +++ b/src/Functions/FunctionToDecimalString.h @@ -302,7 +302,7 @@ private: else if (from_col_const) constantVector(from_col_const->template getValue(), precision_col->getData(), result_chars, result_offsets); else - throw Exception( ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", arguments[0].column->getName()); + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", arguments[0].column->getName()); } return result_col; From d1e874e95bb9062d6ca8be5190d061efa39c76ba Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 3 Mar 2023 18:22:55 +0100 Subject: [PATCH 124/377] Add a test for ClientInfo initial_query_start_time in inter-server mode Signed-off-by: Azat Khuzhin --- ...nt_info_initial_query_start_time.reference | 8 +++ ...de_client_info_initial_query_start_time.sh | 67 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 tests/queries/0_stateless/02590_interserver_mode_client_info_initial_query_start_time.reference create mode 100755 tests/queries/0_stateless/02590_interserver_mode_client_info_initial_query_start_time.sh diff --git a/tests/queries/0_stateless/02590_interserver_mode_client_info_initial_query_start_time.reference b/tests/queries/0_stateless/02590_interserver_mode_client_info_initial_query_start_time.reference new file mode 100644 index 00000000000..fbce8ae2026 --- /dev/null +++ b/tests/queries/0_stateless/02590_interserver_mode_client_info_initial_query_start_time.reference @@ -0,0 +1,8 @@ +SELECT +3 0 0 +3 0 0 +INSERT +CHECK +1 +2 +6 0 2 diff --git a/tests/queries/0_stateless/02590_interserver_mode_client_info_initial_query_start_time.sh b/tests/queries/0_stateless/02590_interserver_mode_client_info_initial_query_start_time.sh new file mode 100755 index 00000000000..5da643bd17b --- /dev/null +++ b/tests/queries/0_stateless/02590_interserver_mode_client_info_initial_query_start_time.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# Tags: no-fasttest +# Tag no-fasttest: interserver mode requires SSL +# +# Test that checks that some of ClientInfo correctly passed in inter-server mode. +# NOTE: we need .sh test (.sql is not enough) because queries on remote nodes does not have current_database = currentDatabase() +# +# Check-style suppression: select * from system.query_log where current_database = currentDatabase(); + +CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CUR_DIR"/../shell_config.sh + +function get_query_id() { random_str 10; } + +$CLICKHOUSE_CLIENT -nm -q " + drop table if exists buf; + drop table if exists dist; + drop table if exists data; + + create table data (key Int) engine=Memory(); + create table dist as data engine=Distributed(test_cluster_interserver_secret, currentDatabase(), data, key); + create table dist_dist as data engine=Distributed(test_cluster_interserver_secret, currentDatabase(), dist, key); + system stop distributed sends dist; +" + +echo "SELECT" +query_id="$(get_query_id)" +# initialize connection, but actually if there are other tables that uses this +# cluster then, it will be created long time ago, but this is OK for this +# test, since we care about the difference between NOW() and there should +# not be any significant difference. +$CLICKHOUSE_CLIENT --prefer_localhost_replica=0 --query_id "$query_id" -q "select * from dist" +$CLICKHOUSE_CLIENT -nm --param_query_id "$query_id" -q " + system flush logs; + select count(), countIf(initial_query_start_time_microseconds != query_start_time_microseconds), countIf(event_time - initial_query_start_time > 3) from system.query_log where type = 'QueryFinish' and initial_query_id = {query_id:String}; +" + +sleep 6 + +query_id="$(get_query_id)" +# this query (and all subsequent) should reuse the previous connection (at least most of the time) +$CLICKHOUSE_CLIENT --prefer_localhost_replica=0 --query_id "$query_id" -q "select * from dist" + +$CLICKHOUSE_CLIENT -nm --param_query_id "$query_id" -q " + system flush logs; + select count(), countIf(initial_query_start_time_microseconds != query_start_time_microseconds), countIf(event_time - initial_query_start_time > 3) from system.query_log where type = 'QueryFinish' and initial_query_id = {query_id:String}; +" + +echo "INSERT" +query_id="$(get_query_id)" +$CLICKHOUSE_CLIENT --prefer_localhost_replica=0 --query_id "$query_id" -nm -q " + insert into dist_dist values (1),(2); + select * from data; +" + +sleep 3 +$CLICKHOUSE_CLIENT -nm --param_query_id "$query_id" -q "system flush distributed dist_dist" +sleep 1 +$CLICKHOUSE_CLIENT -nm --param_query_id "$query_id" -q "system flush distributed dist" + +echo "CHECK" +$CLICKHOUSE_CLIENT -nm --param_query_id "$query_id" -q " + select * from data order by key; + system flush logs; + select count(), countIf(initial_query_start_time_microseconds != query_start_time_microseconds), countIf(event_time - initial_query_start_time > 3) from system.query_log where type = 'QueryFinish' and initial_query_id = {query_id:String}; +" From 3b73f94eb881297ff3b8064512de05fe741be397 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Tue, 28 Mar 2023 12:15:28 +0000 Subject: [PATCH 125/377] Correctly set socket timeouts --- programs/keeper/Keeper.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/programs/keeper/Keeper.cpp b/programs/keeper/Keeper.cpp index ed3297ed7cb..b1e988befbd 100644 --- a/programs/keeper/Keeper.cpp +++ b/programs/keeper/Keeper.cpp @@ -315,12 +315,12 @@ struct Keeper::KeeperHTTPContext : public IHTTPContext Poco::Timespan getReceiveTimeout() const override { - return context->getConfigRef().getUInt64("keeper_server.http_receive_timeout", DEFAULT_HTTP_READ_BUFFER_TIMEOUT); + return {context->getConfigRef().getInt64("keeper_server.http_receive_timeout", DBMS_DEFAULT_RECEIVE_TIMEOUT_SEC), 0}; } Poco::Timespan getSendTimeout() const override { - return context->getConfigRef().getUInt64("keeper_server.http_send_timeout", DEFAULT_HTTP_READ_BUFFER_TIMEOUT); + return {context->getConfigRef().getInt64("keeper_server.http_send_timeout", DBMS_DEFAULT_SEND_TIMEOUT_SEC), 0}; } TinyContextPtr context; @@ -445,6 +445,9 @@ try return tiny_context->getConfigRef(); }; + auto tcp_receive_timeout = config().getInt64("keeper_server.socket_receive_timeout_sec", DBMS_DEFAULT_RECEIVE_TIMEOUT_SEC); + auto tcp_send_timeout = config().getInt64("keeper_server.socket_send_timeout_sec", DBMS_DEFAULT_SEND_TIMEOUT_SEC); + for (const auto & listen_host : listen_hosts) { /// TCP Keeper @@ -453,8 +456,8 @@ try { Poco::Net::ServerSocket socket; auto address = socketBindListen(socket, listen_host, port); - socket.setReceiveTimeout(config().getUInt64("keeper_server.socket_receive_timeout_sec", DBMS_DEFAULT_RECEIVE_TIMEOUT_SEC)); - socket.setSendTimeout(config().getUInt64("keeper_server.socket_send_timeout_sec", DBMS_DEFAULT_SEND_TIMEOUT_SEC)); + socket.setReceiveTimeout(Poco::Timespan{tcp_receive_timeout, 0}); + socket.setSendTimeout(Poco::Timespan{tcp_send_timeout, 0}); servers->emplace_back( listen_host, port_name, @@ -462,8 +465,7 @@ try std::make_unique( new KeeperTCPHandlerFactory( config_getter, tiny_context->getKeeperDispatcher(), - config().getUInt64("keeper_server.socket_receive_timeout_sec", DBMS_DEFAULT_RECEIVE_TIMEOUT_SEC), - config().getUInt64("keeper_server.socket_send_timeout_sec", DBMS_DEFAULT_SEND_TIMEOUT_SEC), false), server_pool, socket)); + tcp_receive_timeout, tcp_send_timeout, false), server_pool, socket)); }); const char * secure_port_name = "keeper_server.tcp_port_secure"; @@ -472,8 +474,8 @@ try #if USE_SSL Poco::Net::SecureServerSocket socket; auto address = socketBindListen(socket, listen_host, port, /* secure = */ true); - socket.setReceiveTimeout(config().getUInt64("keeper_server.socket_receive_timeout_sec", DBMS_DEFAULT_RECEIVE_TIMEOUT_SEC)); - socket.setSendTimeout(config().getUInt64("keeper_server.socket_send_timeout_sec", DBMS_DEFAULT_SEND_TIMEOUT_SEC)); + socket.setReceiveTimeout(Poco::Timespan{tcp_receive_timeout, 0}); + socket.setSendTimeout(Poco::Timespan{tcp_send_timeout, 0}); servers->emplace_back( listen_host, secure_port_name, @@ -481,8 +483,7 @@ try std::make_unique( new KeeperTCPHandlerFactory( config_getter, tiny_context->getKeeperDispatcher(), - config().getUInt64("keeper_server.socket_receive_timeout_sec", DBMS_DEFAULT_RECEIVE_TIMEOUT_SEC), - config().getUInt64("keeper_server.socket_send_timeout_sec", DBMS_DEFAULT_SEND_TIMEOUT_SEC), true), server_pool, socket)); + tcp_receive_timeout, tcp_send_timeout, true), server_pool, socket)); #else UNUSED(port); throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "SSL support for TCP protocol is disabled because Poco library was built without NetSSL support."); @@ -490,18 +491,18 @@ try }); const auto & config = config_getter(); + auto http_context = httpContext(); Poco::Timespan keep_alive_timeout(config.getUInt("keep_alive_timeout", 10), 0); Poco::Net::HTTPServerParams::Ptr http_params = new Poco::Net::HTTPServerParams; - http_params->setTimeout(DBMS_DEFAULT_RECEIVE_TIMEOUT_SEC); + http_params->setTimeout(http_context->getReceiveTimeout()); http_params->setKeepAliveTimeout(keep_alive_timeout); /// Prometheus (if defined and not setup yet with http_port) port_name = "prometheus.port"; - createServer(listen_host, port_name, listen_try, [&](UInt16 port) + createServer(listen_host, port_name, listen_try, [&, http_context = std::move(http_context)](UInt16 port) { Poco::Net::ServerSocket socket; auto address = socketBindListen(socket, listen_host, port); - auto http_context = httpContext(); socket.setReceiveTimeout(http_context->getReceiveTimeout()); socket.setSendTimeout(http_context->getSendTimeout()); servers->emplace_back( From 29d640aa48296c7fb5d79285d977fd45105ee525 Mon Sep 17 00:00:00 2001 From: Suzy Wang Date: Tue, 28 Mar 2023 07:11:21 -0700 Subject: [PATCH 126/377] s390x reinterpret as float64 --- src/Functions/reinterpretAs.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Functions/reinterpretAs.cpp b/src/Functions/reinterpretAs.cpp index 76afedb4f06..1481f73fbbf 100644 --- a/src/Functions/reinterpretAs.cpp +++ b/src/Functions/reinterpretAs.cpp @@ -180,9 +180,14 @@ public: size_t offset = 0; for (size_t i = 0; i < size; ++i) { - memcpy(&vec_res[i], - &data_from[offset], - std::min(static_cast(sizeof(ToFieldType)), offsets_from[i] - offset - 1)); + if constexpr (std::endian::native == std::endian::little) + memcpy(&vec_res[i], + &data_from[offset], + std::min(static_cast(sizeof(ToFieldType)), offsets_from[i] - offset - 1)); + else + reverseMemcpy(&vec_res[i], + &data_from[offset], + std::min(static_cast(sizeof(ToFieldType)), offsets_from[i] - offset - 1)); offset = offsets_from[i]; } From bc143b35ceb1206c1040eb3b1dd9a36eead229a7 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 28 Mar 2023 16:44:44 +0200 Subject: [PATCH 127/377] Remove slow outdated test --- ...ition_with_different_granularity.reference | 3 -- ...e_partition_with_different_granularity.sql | 41 ------------------- 2 files changed, 44 deletions(-) delete mode 100644 tests/queries/1_stateful/00151_replace_partition_with_different_granularity.reference delete mode 100644 tests/queries/1_stateful/00151_replace_partition_with_different_granularity.sql diff --git a/tests/queries/1_stateful/00151_replace_partition_with_different_granularity.reference b/tests/queries/1_stateful/00151_replace_partition_with_different_granularity.reference deleted file mode 100644 index 31d3e6d14da..00000000000 --- a/tests/queries/1_stateful/00151_replace_partition_with_different_granularity.reference +++ /dev/null @@ -1,3 +0,0 @@ -8873898 -8873899 -8873899 diff --git a/tests/queries/1_stateful/00151_replace_partition_with_different_granularity.sql b/tests/queries/1_stateful/00151_replace_partition_with_different_granularity.sql deleted file mode 100644 index c1a2001e2a5..00000000000 --- a/tests/queries/1_stateful/00151_replace_partition_with_different_granularity.sql +++ /dev/null @@ -1,41 +0,0 @@ --- Tags: no-tsan - -DROP TABLE IF EXISTS mixed_granularity_table; - -CREATE TABLE mixed_granularity_table (`WatchID` UInt64, `JavaEnable` UInt8, `Title` String, `GoodEvent` Int16, `EventTime` DateTime, `EventDate` Date, `CounterID` UInt32, `ClientIP` UInt32, `ClientIP6` FixedString(16), `RegionID` UInt32, `UserID` UInt64, `CounterClass` Int8, `OS` UInt8, `UserAgent` UInt8, `URL` String, `Referer` String, `URLDomain` String, `RefererDomain` String, `Refresh` UInt8, `IsRobot` UInt8, `RefererCategories` Array(UInt16), `URLCategories` Array(UInt16), `URLRegions` Array(UInt32), `RefererRegions` Array(UInt32), `ResolutionWidth` UInt16, `ResolutionHeight` UInt16, `ResolutionDepth` UInt8, `FlashMajor` UInt8, `FlashMinor` UInt8, `FlashMinor2` String, `NetMajor` UInt8, `NetMinor` UInt8, `UserAgentMajor` UInt16, `UserAgentMinor` FixedString(2), `CookieEnable` UInt8, `JavascriptEnable` UInt8, `IsMobile` UInt8, `MobilePhone` UInt8, `MobilePhoneModel` String, `Params` String, `IPNetworkID` UInt32, `TraficSourceID` Int8, `SearchEngineID` UInt16, `SearchPhrase` String, `AdvEngineID` UInt8, `IsArtifical` UInt8, `WindowClientWidth` UInt16, `WindowClientHeight` UInt16, `ClientTimeZone` Int16, `ClientEventTime` DateTime, `SilverlightVersion1` UInt8, `SilverlightVersion2` UInt8, `SilverlightVersion3` UInt32, `SilverlightVersion4` UInt16, `PageCharset` String, `CodeVersion` UInt32, `IsLink` UInt8, `IsDownload` UInt8, `IsNotBounce` UInt8, `FUniqID` UInt64, `HID` UInt32, `IsOldCounter` UInt8, `IsEvent` UInt8, `IsParameter` UInt8, `DontCountHits` UInt8, `WithHash` UInt8, `HitColor` FixedString(1), `UTCEventTime` DateTime, `Age` UInt8, `Sex` UInt8, `Income` UInt8, `Interests` UInt16, `Robotness` UInt8, `GeneralInterests` Array(UInt16), `RemoteIP` UInt32, `RemoteIP6` FixedString(16), `WindowName` Int32, `OpenerName` Int32, `HistoryLength` Int16, `BrowserLanguage` FixedString(2), `BrowserCountry` FixedString(2), `SocialNetwork` String, `SocialAction` String, `HTTPError` UInt16, `SendTiming` Int32, `DNSTiming` Int32, `ConnectTiming` Int32, `ResponseStartTiming` Int32, `ResponseEndTiming` Int32, `FetchTiming` Int32, `RedirectTiming` Int32, `DOMInteractiveTiming` Int32, `DOMContentLoadedTiming` Int32, `DOMCompleteTiming` Int32, `LoadEventStartTiming` Int32, `LoadEventEndTiming` Int32, `NSToDOMContentLoadedTiming` Int32, `FirstPaintTiming` Int32, `RedirectCount` Int8, `SocialSourceNetworkID` UInt8, `SocialSourcePage` String, `ParamPrice` Int64, `ParamOrderID` String, `ParamCurrency` FixedString(3), `ParamCurrencyID` UInt16, `GoalsReached` Array(UInt32), `OpenstatServiceName` String, `OpenstatCampaignID` String, `OpenstatAdID` String, `OpenstatSourceID` String, `UTMSource` String, `UTMMedium` String, `UTMCampaign` String, `UTMContent` String, `UTMTerm` String, `FromTag` String, `HasGCLID` UInt8, `RefererHash` UInt64, `URLHash` UInt64, `CLID` UInt32, `YCLID` UInt64, `ShareService` String, `ShareURL` String, `ShareTitle` String, `ParsedParams.Key1` Array(String), `ParsedParams.Key2` Array(String), `ParsedParams.Key3` Array(String), `ParsedParams.Key4` Array(String), `ParsedParams.Key5` Array(String), `ParsedParams.ValueDouble` Array(Float64), `IslandID` FixedString(16), `RequestNum` UInt32, `RequestTry` UInt8) ENGINE = MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192, enable_mixed_granularity_parts=1; -- same with hits, but enabled mixed granularity - -INSERT INTO mixed_granularity_table SELECT * FROM test.hits LIMIT 10; - -ALTER TABLE mixed_granularity_table REPLACE PARTITION 201403 FROM test.hits; - -SELECT COUNT() FROM mixed_granularity_table; - -INSERT INTO mixed_granularity_table SELECT * FROM test.hits LIMIT 1; - -SELECT COUNT() FROM mixed_granularity_table; - -OPTIMIZE TABLE mixed_granularity_table FINAL; - -SELECT COUNT() FROM mixed_granularity_table; - -CREATE TABLE non_mixed_granularity_non_adaptive_table (`WatchID` UInt64, `JavaEnable` UInt8, `Title` String, `GoodEvent` Int16, `EventTime` DateTime, `EventDate` Date, `CounterID` UInt32, `ClientIP` UInt32, `ClientIP6` FixedString(16), `RegionID` UInt32, `UserID` UInt64, `CounterClass` Int8, `OS` UInt8, `UserAgent` UInt8, `URL` String, `Referer` String, `URLDomain` String, `RefererDomain` String, `Refresh` UInt8, `IsRobot` UInt8, `RefererCategories` Array(UInt16), `URLCategories` Array(UInt16), `URLRegions` Array(UInt32), `RefererRegions` Array(UInt32), `ResolutionWidth` UInt16, `ResolutionHeight` UInt16, `ResolutionDepth` UInt8, `FlashMajor` UInt8, `FlashMinor` UInt8, `FlashMinor2` String, `NetMajor` UInt8, `NetMinor` UInt8, `UserAgentMajor` UInt16, `UserAgentMinor` FixedString(2), `CookieEnable` UInt8, `JavascriptEnable` UInt8, `IsMobile` UInt8, `MobilePhone` UInt8, `MobilePhoneModel` String, `Params` String, `IPNetworkID` UInt32, `TraficSourceID` Int8, `SearchEngineID` UInt16, `SearchPhrase` String, `AdvEngineID` UInt8, `IsArtifical` UInt8, `WindowClientWidth` UInt16, `WindowClientHeight` UInt16, `ClientTimeZone` Int16, `ClientEventTime` DateTime, `SilverlightVersion1` UInt8, `SilverlightVersion2` UInt8, `SilverlightVersion3` UInt32, `SilverlightVersion4` UInt16, `PageCharset` String, `CodeVersion` UInt32, `IsLink` UInt8, `IsDownload` UInt8, `IsNotBounce` UInt8, `FUniqID` UInt64, `HID` UInt32, `IsOldCounter` UInt8, `IsEvent` UInt8, `IsParameter` UInt8, `DontCountHits` UInt8, `WithHash` UInt8, `HitColor` FixedString(1), `UTCEventTime` DateTime, `Age` UInt8, `Sex` UInt8, `Income` UInt8, `Interests` UInt16, `Robotness` UInt8, `GeneralInterests` Array(UInt16), `RemoteIP` UInt32, `RemoteIP6` FixedString(16), `WindowName` Int32, `OpenerName` Int32, `HistoryLength` Int16, `BrowserLanguage` FixedString(2), `BrowserCountry` FixedString(2), `SocialNetwork` String, `SocialAction` String, `HTTPError` UInt16, `SendTiming` Int32, `DNSTiming` Int32, `ConnectTiming` Int32, `ResponseStartTiming` Int32, `ResponseEndTiming` Int32, `FetchTiming` Int32, `RedirectTiming` Int32, `DOMInteractiveTiming` Int32, `DOMContentLoadedTiming` Int32, `DOMCompleteTiming` Int32, `LoadEventStartTiming` Int32, `LoadEventEndTiming` Int32, `NSToDOMContentLoadedTiming` Int32, `FirstPaintTiming` Int32, `RedirectCount` Int8, `SocialSourceNetworkID` UInt8, `SocialSourcePage` String, `ParamPrice` Int64, `ParamOrderID` String, `ParamCurrency` FixedString(3), `ParamCurrencyID` UInt16, `GoalsReached` Array(UInt32), `OpenstatServiceName` String, `OpenstatCampaignID` String, `OpenstatAdID` String, `OpenstatSourceID` String, `UTMSource` String, `UTMMedium` String, `UTMCampaign` String, `UTMContent` String, `UTMTerm` String, `FromTag` String, `HasGCLID` UInt8, `RefererHash` UInt64, `URLHash` UInt64, `CLID` UInt32, `YCLID` UInt64, `ShareService` String, `ShareURL` String, `ShareTitle` String, `ParsedParams.Key1` Array(String), `ParsedParams.Key2` Array(String), `ParsedParams.Key3` Array(String), `ParsedParams.Key4` Array(String), `ParsedParams.Key5` Array(String), `ParsedParams.ValueDouble` Array(Float64), `IslandID` FixedString(16), `RequestNum` UInt32, `RequestTry` UInt8) ENGINE = MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192, index_granularity_bytes=0; -- same with hits, but enabled mixed granularity and fixed_granularity - -INSERT INTO non_mixed_granularity_non_adaptive_table SELECT * FROM test.hits LIMIT 10; - --- after optimize mixed_granularity_table will have .mrk2 parts -ALTER TABLE non_mixed_granularity_non_adaptive_table REPLACE PARTITION 201403 FROM mixed_granularity_table; -- { serverError 36 } - -DROP TABLE IF EXISTS non_mixed_granularity_non_adaptive_table; - - -DROP TABLE IF EXISTS mixed_granularity_strictly_non_adaptive_table; - -CREATE TABLE mixed_granularity_strictly_non_adaptive_table (`WatchID` UInt64, `JavaEnable` UInt8, `Title` String, `GoodEvent` Int16, `EventTime` DateTime, `EventDate` Date, `CounterID` UInt32, `ClientIP` UInt32, `ClientIP6` FixedString(16), `RegionID` UInt32, `UserID` UInt64, `CounterClass` Int8, `OS` UInt8, `UserAgent` UInt8, `URL` String, `Referer` String, `URLDomain` String, `RefererDomain` String, `Refresh` UInt8, `IsRobot` UInt8, `RefererCategories` Array(UInt16), `URLCategories` Array(UInt16), `URLRegions` Array(UInt32), `RefererRegions` Array(UInt32), `ResolutionWidth` UInt16, `ResolutionHeight` UInt16, `ResolutionDepth` UInt8, `FlashMajor` UInt8, `FlashMinor` UInt8, `FlashMinor2` String, `NetMajor` UInt8, `NetMinor` UInt8, `UserAgentMajor` UInt16, `UserAgentMinor` FixedString(2), `CookieEnable` UInt8, `JavascriptEnable` UInt8, `IsMobile` UInt8, `MobilePhone` UInt8, `MobilePhoneModel` String, `Params` String, `IPNetworkID` UInt32, `TraficSourceID` Int8, `SearchEngineID` UInt16, `SearchPhrase` String, `AdvEngineID` UInt8, `IsArtifical` UInt8, `WindowClientWidth` UInt16, `WindowClientHeight` UInt16, `ClientTimeZone` Int16, `ClientEventTime` DateTime, `SilverlightVersion1` UInt8, `SilverlightVersion2` UInt8, `SilverlightVersion3` UInt32, `SilverlightVersion4` UInt16, `PageCharset` String, `CodeVersion` UInt32, `IsLink` UInt8, `IsDownload` UInt8, `IsNotBounce` UInt8, `FUniqID` UInt64, `HID` UInt32, `IsOldCounter` UInt8, `IsEvent` UInt8, `IsParameter` UInt8, `DontCountHits` UInt8, `WithHash` UInt8, `HitColor` FixedString(1), `UTCEventTime` DateTime, `Age` UInt8, `Sex` UInt8, `Income` UInt8, `Interests` UInt16, `Robotness` UInt8, `GeneralInterests` Array(UInt16), `RemoteIP` UInt32, `RemoteIP6` FixedString(16), `WindowName` Int32, `OpenerName` Int32, `HistoryLength` Int16, `BrowserLanguage` FixedString(2), `BrowserCountry` FixedString(2), `SocialNetwork` String, `SocialAction` String, `HTTPError` UInt16, `SendTiming` Int32, `DNSTiming` Int32, `ConnectTiming` Int32, `ResponseStartTiming` Int32, `ResponseEndTiming` Int32, `FetchTiming` Int32, `RedirectTiming` Int32, `DOMInteractiveTiming` Int32, `DOMContentLoadedTiming` Int32, `DOMCompleteTiming` Int32, `LoadEventStartTiming` Int32, `LoadEventEndTiming` Int32, `NSToDOMContentLoadedTiming` Int32, `FirstPaintTiming` Int32, `RedirectCount` Int8, `SocialSourceNetworkID` UInt8, `SocialSourcePage` String, `ParamPrice` Int64, `ParamOrderID` String, `ParamCurrency` FixedString(3), `ParamCurrencyID` UInt16, `GoalsReached` Array(UInt32), `OpenstatServiceName` String, `OpenstatCampaignID` String, `OpenstatAdID` String, `OpenstatSourceID` String, `UTMSource` String, `UTMMedium` String, `UTMCampaign` String, `UTMContent` String, `UTMTerm` String, `FromTag` String, `HasGCLID` UInt8, `RefererHash` UInt64, `URLHash` UInt64, `CLID` UInt32, `YCLID` UInt64, `ShareService` String, `ShareURL` String, `ShareTitle` String, `ParsedParams.Key1` Array(String), `ParsedParams.Key2` Array(String), `ParsedParams.Key3` Array(String), `ParsedParams.Key4` Array(String), `ParsedParams.Key5` Array(String), `ParsedParams.ValueDouble` Array(Float64), `IslandID` FixedString(16), `RequestNum` UInt32, `RequestTry` UInt8) ENGINE = MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192, enable_mixed_granularity_parts=1, index_granularity_bytes=0; -- same with hits, but enabled mixed granularity and fixed_granularity - -INSERT INTO mixed_granularity_strictly_non_adaptive_table SELECT * FROM test.hits LIMIT 10; - -ALTER TABLE mixed_granularity_strictly_non_adaptive_table REPLACE PARTITION 201403 FROM mixed_granularity_table; -- { serverError 36 } - -DROP TABLE IF EXISTS mixed_granularity_table; - -DROP TABLE IF EXISTS mixed_granularity_strictly_non_adaptive_table; From cea631a4c267e96a90c97baa8710a18232b66fa6 Mon Sep 17 00:00:00 2001 From: save-my-heart Date: Tue, 28 Mar 2023 23:24:07 +0800 Subject: [PATCH 128/377] throw exception while non-parametric functions having parameters --- .../UserDefined/UserDefinedSQLFunctionVisitor.cpp | 7 +++++++ src/Interpreters/ActionsVisitor.cpp | 7 +++++++ .../0_stateless/25403_non_parametric_function.reference | 0 .../queries/0_stateless/25403_non_parametric_function.sql | 5 +++++ 4 files changed, 19 insertions(+) create mode 100644 tests/queries/0_stateless/25403_non_parametric_function.reference create mode 100644 tests/queries/0_stateless/25403_non_parametric_function.sql diff --git a/src/Functions/UserDefined/UserDefinedSQLFunctionVisitor.cpp b/src/Functions/UserDefined/UserDefinedSQLFunctionVisitor.cpp index d78a8623a18..57cc45cc75d 100644 --- a/src/Functions/UserDefined/UserDefinedSQLFunctionVisitor.cpp +++ b/src/Functions/UserDefined/UserDefinedSQLFunctionVisitor.cpp @@ -20,6 +20,7 @@ namespace DB namespace ErrorCodes { extern const int UNSUPPORTED_METHOD; + extern const int FUNCTION_CANNOT_HAVE_PARAMETERS; } void UserDefinedSQLFunctionVisitor::visit(ASTPtr & ast) @@ -132,6 +133,12 @@ ASTPtr UserDefinedSQLFunctionVisitor::tryToReplaceFunction(const ASTFunction & f if (!user_defined_function) return nullptr; + /// All UDFs are not parametric for now. + if (function.parameters) + { + throw Exception(ErrorCodes::FUNCTION_CANNOT_HAVE_PARAMETERS, "Function {} is not parametric", function.name); + } + const auto & function_arguments_list = function.children.at(0)->as(); auto & function_arguments = function_arguments_list->children; diff --git a/src/Interpreters/ActionsVisitor.cpp b/src/Interpreters/ActionsVisitor.cpp index 8a5ea1205e7..1f4969a7f9a 100644 --- a/src/Interpreters/ActionsVisitor.cpp +++ b/src/Interpreters/ActionsVisitor.cpp @@ -75,6 +75,7 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION; extern const int TOO_MANY_ARGUMENTS_FOR_FUNCTION; + extern const int FUNCTION_CANNOT_HAVE_PARAMETERS; } static NamesAndTypesList::iterator findColumn(const String & name, NamesAndTypesList & cols) @@ -1109,6 +1110,12 @@ void ActionsMatcher::visit(const ASTFunction & node, const ASTPtr & ast, Data & } } + /// Normal functions are not parametric for now. + if (node.parameters) + { + throw Exception(ErrorCodes::FUNCTION_CANNOT_HAVE_PARAMETERS, "Function {} is not parametric", node.name); + } + Names argument_names; DataTypes argument_types; bool arguments_present = true; diff --git a/tests/queries/0_stateless/25403_non_parametric_function.reference b/tests/queries/0_stateless/25403_non_parametric_function.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/25403_non_parametric_function.sql b/tests/queries/0_stateless/25403_non_parametric_function.sql new file mode 100644 index 00000000000..9b7e2ae7a04 --- /dev/null +++ b/tests/queries/0_stateless/25403_non_parametric_function.sql @@ -0,0 +1,5 @@ +SELECT * FROM system.numbers WHERE number > toUInt64(10)(number) LIMIT 10; -- { serverError 309 } + +CREATE FUNCTION sum_udf as (x, y) -> (x + y); + +SELECT sum_udf(1)(1, 2); -- { serverError 309 } \ No newline at end of file From 6d05968a0f7433f3d6dd0fde5d720f5159e6a7d1 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Tue, 28 Mar 2023 16:23:32 +0000 Subject: [PATCH 129/377] optimize function mapUpdate --- src/Functions/map.cpp | 136 ++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/src/Functions/map.cpp b/src/Functions/map.cpp index 549de200bea..f4f2e1fbfea 100644 --- a/src/Functions/map.cpp +++ b/src/Functions/map.cpp @@ -18,6 +18,7 @@ #include "array/arrayIndex.h" #include "Functions/like.h" #include "Functions/FunctionsStringSearch.h" +#include namespace DB @@ -616,103 +617,110 @@ public: "Number of arguments for function {} doesn't match: passed {}, should be 2", getName(), arguments.size()); - const DataTypeMap * left = checkAndGetDataType(arguments[0].type.get()); - const DataTypeMap * right = checkAndGetDataType(arguments[1].type.get()); + const auto * left = checkAndGetDataType(arguments[0].type.get()); + const auto * right = checkAndGetDataType(arguments[1].type.get()); if (!left || !right) - throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "The two arguments for function {} must be both Map type", - getName()); + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "The two arguments for function {} must be both Map type", getName()); + if (!left->getKeyType()->equals(*right->getKeyType()) || !left->getValueType()->equals(*right->getValueType())) - throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "The Key And Value type of Map for function {} must be the same", - getName()); + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "The Key And Value type of Map for function {} must be the same", getName()); return std::make_shared(left->getKeyType(), left->getValueType()); } bool useDefaultImplementationForConstants() const override { return true; } - ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override { - const ColumnMap * col_map_left = typeid_cast(arguments[0].column.get()); - const auto * col_const_map_left = checkAndGetColumnConst(arguments[0].column.get()); - bool col_const_map_left_flag = false; - if (col_const_map_left) - { - col_const_map_left_flag = true; - col_map_left = typeid_cast(&col_const_map_left->getDataColumn()); - } - if (!col_map_left) - return nullptr; + bool is_left_const = isColumnConst(*arguments[0].column); + bool is_right_const = isColumnConst(*arguments[1].column); - const ColumnMap * col_map_right = typeid_cast(arguments[1].column.get()); - const auto * col_const_map_right = checkAndGetColumnConst(arguments[1].column.get()); - bool col_const_map_right_flag = false; - if (col_const_map_right) - { - col_const_map_right_flag = true; - col_map_right = typeid_cast(&col_const_map_right->getDataColumn()); - } - if (!col_map_right) - return nullptr; + const auto * map_column_left = is_left_const + ? checkAndGetColumnConstData(arguments[0].column.get()) + : checkAndGetColumn(arguments[0].column.get()); - const auto & nested_column_left = col_map_left->getNestedColumn(); - const auto & keys_data_left = col_map_left->getNestedData().getColumn(0); - const auto & values_data_left = col_map_left->getNestedData().getColumn(1); + const auto * map_column_right = is_right_const + ? checkAndGetColumnConstData(arguments[1].column.get()) + : checkAndGetColumn(arguments[1].column.get()); + + if (!map_column_left || !map_column_right) + throw Exception(ErrorCodes::ILLEGAL_COLUMN, + "Arguments for function {} must be maps, got {} and {} instead", + getName(), arguments[0].column->getName(), arguments[1].column->getName()); + + const auto & nested_column_left = map_column_left->getNestedColumn(); + const auto & keys_data_left = map_column_left->getNestedData().getColumn(0); + const auto & values_data_left = map_column_left->getNestedData().getColumn(1); const auto & offsets_left = nested_column_left.getOffsets(); - const auto & nested_column_right = col_map_right->getNestedColumn(); - const auto & keys_data_right = col_map_right->getNestedData().getColumn(0); - const auto & values_data_right = col_map_right->getNestedData().getColumn(1); + const auto & nested_column_right = map_column_right->getNestedColumn(); + const auto & keys_data_right = map_column_right->getNestedData().getColumn(0); + const auto & values_data_right = map_column_right->getNestedData().getColumn(1); const auto & offsets_right = nested_column_right.getOffsets(); - const auto & result_type_map = static_cast(*result_type); - const DataTypePtr & key_type = result_type_map.getKeyType(); - const DataTypePtr & value_type = result_type_map.getValueType(); - MutableColumnPtr keys_data = key_type->createColumn(); - MutableColumnPtr values_data = value_type->createColumn(); - MutableColumnPtr offsets = DataTypeNumber().createColumn(); + auto result_keys = keys_data_left.cloneEmpty(); + auto result_values = values_data_left.cloneEmpty(); + auto result_offsets = ColumnVector::create(input_rows_count); + auto & result_offsets_data = result_offsets->getData(); + + using Set = HashSetWithStackMemory; + + Set right_keys_const; + if (is_right_const) + { + for (size_t i = 0; i < keys_data_right.size(); ++i) + right_keys_const.insert(keys_data_right.getDataAt(i)); + } IColumn::Offset current_offset = 0; for (size_t row_idx = 0; row_idx < input_rows_count; ++row_idx) { - size_t left_it_begin = col_const_map_left_flag ? 0 : offsets_left[row_idx - 1]; - size_t left_it_end = col_const_map_left_flag ? offsets_left.size() : offsets_left[row_idx]; - size_t right_it_begin = col_const_map_right_flag ? 0 : offsets_right[row_idx - 1]; - size_t right_it_end = col_const_map_right_flag ? offsets_right.size() : offsets_right[row_idx]; + size_t left_from = is_left_const ? 0 : offsets_left[row_idx - 1]; + size_t left_to = is_left_const ? offsets_left[0] : offsets_left[row_idx]; - for (size_t i = left_it_begin; i < left_it_end; ++i) + size_t right_from = is_right_const ? 0 : offsets_right[row_idx - 1]; + size_t right_to = is_right_const ? offsets_right[0] : offsets_right[row_idx]; + + auto execute_row = [&](const auto & set) { - bool matched = false; - auto key = keys_data_left.getDataAt(i); - for (size_t j = right_it_begin; j < right_it_end; ++j) + for (size_t i = left_from; i < left_to; ++i) { - if (keys_data_right.getDataAt(j).toString() == key.toString()) + if (!set.find(keys_data_left.getDataAt(i))) { - matched = true; - break; + result_keys->insertFrom(keys_data_left, i); + result_values->insertFrom(values_data_left, i); + ++current_offset; } } - if (!matched) - { - keys_data->insertFrom(keys_data_left, i); - values_data->insertFrom(values_data_left, i); - ++current_offset; - } - } + }; - for (size_t j = right_it_begin; j < right_it_end; ++j) + if (is_right_const) { - keys_data->insertFrom(keys_data_right, j); - values_data->insertFrom(values_data_right, j); - ++current_offset; + execute_row(right_keys_const); + } + else + { + Set right_keys; + for (size_t i = right_from; i < right_to; ++i) + right_keys.insert(keys_data_right.getDataAt(i)); + + execute_row(right_keys); } - offsets->insert(current_offset); + size_t right_map_size = right_to - right_from; + result_keys->insertRangeFrom(keys_data_right, right_from, right_map_size); + result_values->insertRangeFrom(values_data_right, right_from, right_map_size); + + current_offset += right_map_size; + result_offsets_data[row_idx] = current_offset; } auto nested_column = ColumnArray::create( - ColumnTuple::create(Columns{std::move(keys_data), std::move(values_data)}), - std::move(offsets)); + ColumnTuple::create(Columns{std::move(result_keys), std::move(result_values)}), + std::move(result_offsets)); return ColumnMap::create(nested_column); } From 1e79245b942bf21419daa35719008299bdeccf81 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Tue, 28 Mar 2023 17:17:42 +0000 Subject: [PATCH 130/377] add tests --- src/Functions/map.cpp | 6 +++++ tests/performance/map_update.xml | 22 +++++++++++++++++++ .../0_stateless/02169_map_functions.reference | 4 ++++ .../0_stateless/02169_map_functions.sql | 5 +++++ 4 files changed, 37 insertions(+) create mode 100644 tests/performance/map_update.xml diff --git a/src/Functions/map.cpp b/src/Functions/map.cpp index f4f2e1fbfea..c7125d85c58 100644 --- a/src/Functions/map.cpp +++ b/src/Functions/map.cpp @@ -663,6 +663,12 @@ public: auto result_keys = keys_data_left.cloneEmpty(); auto result_values = values_data_left.cloneEmpty(); + + size_t size_to_reserve = keys_data_right.size() + (keys_data_left.size() - keys_data_right.size()); + + result_keys->reserve(size_to_reserve); + result_values->reserve(size_to_reserve); + auto result_offsets = ColumnVector::create(input_rows_count); auto & result_offsets_data = result_offsets->getData(); diff --git a/tests/performance/map_update.xml b/tests/performance/map_update.xml new file mode 100644 index 00000000000..2c5417ffe28 --- /dev/null +++ b/tests/performance/map_update.xml @@ -0,0 +1,22 @@ + + + WITH (range(10), range(10))::Map(String, UInt64) AS m1, (range(3), range(3))::Map(String, UInt64) AS m2 + SELECT count() FROM numbers(500000) + WHERE NOT ignore(mapUpdate(materialize(m1), materialize(m2))) + + + WITH (range(10), range(10))::Map(String, UInt64) AS m1, (range(3), range(3))::Map(String, UInt64) AS m2 + SELECT count() FROM numbers(500000) + WHERE NOT ignore(mapUpdate(materialize(m1), m2)) + + + WITH (range(100), range(100))::Map(String, UInt64) AS m1, (range(30), range(30))::Map(String, UInt64) AS m2 + SELECT count() FROM numbers(50000) + WHERE NOT ignore(mapUpdate(materialize(m1), materialize(m2))) + + + WITH (range(100), range(100))::Map(String, UInt64) AS m1, (range(30), range(30))::Map(String, UInt64) AS m2 + SELECT count() FROM numbers(50000) + WHERE NOT ignore(mapUpdate(materialize(m1), m2)) + + diff --git a/tests/queries/0_stateless/02169_map_functions.reference b/tests/queries/0_stateless/02169_map_functions.reference index 160aebbc852..fc7cd1a3c4b 100644 --- a/tests/queries/0_stateless/02169_map_functions.reference +++ b/tests/queries/0_stateless/02169_map_functions.reference @@ -31,3 +31,7 @@ {1:2,2:3} {'x':'y','x':'y'} {'x':'y','x':'y'} +{'k1':11,'k2':22} +{'k1':11,'k2':22} +{'k1':11,'k2':22} +{'k1':11,'k2':22} diff --git a/tests/queries/0_stateless/02169_map_functions.sql b/tests/queries/0_stateless/02169_map_functions.sql index 4cccaa56722..31112e18b58 100644 --- a/tests/queries/0_stateless/02169_map_functions.sql +++ b/tests/queries/0_stateless/02169_map_functions.sql @@ -15,6 +15,11 @@ SELECT mapApply((x, y) -> (x, x + 1), materialize(map(1, 0, 2, 0))); SELECT mapApply((x, y) -> ('x', 'y'), map(1, 0, 2, 0)); SELECT mapApply((x, y) -> ('x', 'y'), materialize(map(1, 0, 2, 0))); +SELECT mapUpdate(map('k1', 1, 'k2', 2), map('k1', 11, 'k2', 22)); +SELECT mapUpdate(materialize(map('k1', 1, 'k2', 2)), map('k1', 11, 'k2', 22)); +SELECT mapUpdate(map('k1', 1, 'k2', 2), materialize(map('k1', 11, 'k2', 22))); +SELECT mapUpdate(materialize(map('k1', 1, 'k2', 2)), materialize(map('k1', 11, 'k2', 22))); + SELECT mapApply(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } SELECT mapApply((x, y) -> (x), map(1, 0, 2, 0)); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } SELECT mapApply((x, y) -> ('x'), map(1, 0, 2, 0)); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } From 06a6f5cf76dd3508bb630103c2cfe797b1a99b34 Mon Sep 17 00:00:00 2001 From: avogar Date: Tue, 28 Mar 2023 17:36:20 +0000 Subject: [PATCH 131/377] Support more ClickHouse types in Avro format --- docs/en/interfaces/formats.md | 8 ++- .../Formats/Impl/AvroRowInputFormat.cpp | 53 +++++++++++++------ .../Formats/Impl/AvroRowInputFormat.h | 4 +- .../Formats/Impl/AvroRowOutputFormat.cpp | 44 +++++++++++---- .../Formats/Impl/AvroRowOutputFormat.h | 3 +- src/Storages/StorageIceberg.cpp | 9 ++-- .../02592_avro_more_types.reference | 7 +++ .../0_stateless/02592_avro_more_types.sh | 13 +++++ 8 files changed, 106 insertions(+), 35 deletions(-) create mode 100644 tests/queries/0_stateless/02592_avro_more_types.reference create mode 100755 tests/queries/0_stateless/02592_avro_more_types.sh diff --git a/docs/en/interfaces/formats.md b/docs/en/interfaces/formats.md index 3debea0087e..781c6519b0d 100644 --- a/docs/en/interfaces/formats.md +++ b/docs/en/interfaces/formats.md @@ -1818,15 +1818,19 @@ The table below shows supported data types and how they match ClickHouse [data t | `bytes`, `string`, `fixed` | [FixedString(N)](/docs/en/sql-reference/data-types/fixedstring.md) | `fixed(N)` | | `enum` | [Enum(8\16)](/docs/en/sql-reference/data-types/enum.md) | `enum` | | `array(T)` | [Array(T)](/docs/en/sql-reference/data-types/array.md) | `array(T)` | +| `map(V, K)` | [Map(V, K)](/docs/en/sql-reference/data-types/map.md) | `map(string, K)` | | `union(null, T)`, `union(T, null)` | [Nullable(T)](/docs/en/sql-reference/data-types/date.md) | `union(null, T)` | | `null` | [Nullable(Nothing)](/docs/en/sql-reference/data-types/special-data-types/nothing.md) | `null` | | `int (date)` \** | [Date](/docs/en/sql-reference/data-types/date.md), [Date32](docs/en/sql-reference/data-types/date32.md) | `int (date)` \** | | `long (timestamp-millis)` \** | [DateTime64(3)](/docs/en/sql-reference/data-types/datetime.md) | `long (timestamp-millis)` \** | | `long (timestamp-micros)` \** | [DateTime64(6)](/docs/en/sql-reference/data-types/datetime.md) | `long (timestamp-micros)` \** | +| `bytes (decimal)` \** | [DateTime64(N)](/docs/en/sql-reference/data-types/datetime.md) | `bytes (decimal)` \** | | `int` | [IPv4](/docs/en/sql-reference/data-types/domains/ipv4.md) | `int` | | `fixed(16)` | [IPv6](/docs/en/sql-reference/data-types/domains/ipv6.md) | `fixed(16)` | -| `bytes (decimal)` \** | [Decimal(P, S)](/docs/en/sql-reference/data-types/decimal.md) | `bytes (decimal)` \** | -| `string (uuid)` \** | [UUID](/docs/en/sql-reference/data-types/uuid.md) | `string (uuid)` \** | +| `bytes (decimal)` \** | [Decimal(P, S)](/docs/en/sql-reference/data-types/decimal.md) | `bytes (decimal)` \** | +| `string (uuid)` \** | [UUID](/docs/en/sql-reference/data-types/uuid.md) | `string (uuid)` \** | +| `fixed(16)` | [Int128/UInt128](/docs/en/sql-reference/data-types/int-uint.md) | `fixed(16)` | +| `fixed(32)` | [Int256/UInt256](/docs/en/sql-reference/data-types/int-uint.md) | `fixed(32)` | \* `bytes` is default, controlled by [output_format_avro_string_column_pattern](/docs/en/operations/settings/settings-formats.md/#output_format_avro_string_column_pattern) diff --git a/src/Processors/Formats/Impl/AvroRowInputFormat.cpp b/src/Processors/Formats/Impl/AvroRowInputFormat.cpp index 383b1b08a73..7b978de1a4c 100644 --- a/src/Processors/Formats/Impl/AvroRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/AvroRowInputFormat.cpp @@ -40,6 +40,8 @@ #include #include +#include + #include #include #include @@ -165,9 +167,10 @@ static AvroDeserializer::DeserializeFn createDecimalDeserializeFn(const avro::No if (decimal_type.getScale() != static_cast(logical_type.scale()) || decimal_type.getPrecision() != static_cast(logical_type.precision())) throw Exception( ErrorCodes::BAD_ARGUMENTS, - "Cannot insert Avro decimal with scale {} and precision {} to ClickHouse Decimal with scale {} and precision {}", + "Cannot insert Avro decimal with scale {} and precision {} to ClickHouse type {} with scale {} and precision {}", logical_type.scale(), logical_type.precision(), + target_type->getName(), decimal_type.getScale(), decimal_type.getPrecision()); @@ -207,6 +210,24 @@ static std::string nodeName(avro::NodePtr node) return avro::toString(node->type()); } +static bool canBeDeserializedFromFixed( const DataTypePtr & target_type, size_t fixed_size) +{ + switch (target_type->getTypeId()) + { + case TypeIndex::String: + return true; + case TypeIndex::FixedString: [[fallthrough]]; + case TypeIndex::IPv6: [[fallthrough]]; + case TypeIndex::Int128: [[fallthrough]]; + case TypeIndex::UInt128: [[fallthrough]]; + case TypeIndex::Int256: [[fallthrough]]; + case TypeIndex::UInt256: + return target_type->getSizeOfValueInMemory() == fixed_size; + default: + return false; + } +} + AvroDeserializer::DeserializeFn AvroDeserializer::createDeserializeFn(const avro::NodePtr & root_node, const DataTypePtr & target_type) { if (target_type->lowCardinality()) @@ -260,6 +281,8 @@ AvroDeserializer::DeserializeFn AvroDeserializer::createDeserializeFn(const avro return createDecimalDeserializeFn(root_node, target_type); if (target.isDecimal256()) return createDecimalDeserializeFn(root_node, target_type); + if (target.isDateTime64()) + return createDecimalDeserializeFn(root_node, target_type); break; case avro::AVRO_INT: if (target_type->isValueRepresentedByNumber()) @@ -464,16 +487,7 @@ AvroDeserializer::DeserializeFn AvroDeserializer::createDeserializeFn(const avro case avro::AVRO_FIXED: { size_t fixed_size = root_node->fixedSize(); - if ((target.isFixedString() && target_type->getSizeOfValueInMemory() == fixed_size) || target.isString()) - { - return [tmp_fixed = std::vector(fixed_size)](IColumn & column, avro::Decoder & decoder) mutable - { - decoder.decodeFixed(tmp_fixed.size(), tmp_fixed); - column.insertData(reinterpret_cast(tmp_fixed.data()), tmp_fixed.size()); - return true; - }; - } - else if (target.isIPv6() && fixed_size == sizeof(IPv6)) + if (canBeDeserializedFromFixed(target_type, fixed_size)) { return [tmp_fixed = std::vector(fixed_size)](IColumn & column, avro::Decoder & decoder) mutable { @@ -525,7 +539,14 @@ AvroDeserializer::DeserializeFn AvroDeserializer::createDeserializeFn(const avro const auto & values_type = map_type.getValueType(); auto keys_source_type = root_node->leafAt(0); auto values_source_type = root_node->leafAt(1); - auto keys_deserializer = createDeserializeFn(keys_source_type, keys_type); + auto keys_deserializer = [keys_type, this](IColumn & column, avro::Decoder & decoder) + { + String key = decoder.decodeString(); + ReadBufferFromString buf(key); + keys_type->getDefaultSerialization()->deserializeWholeText(column, buf, settings); + return true; + }; + auto values_deserializer = createDeserializeFn(values_source_type, values_type); return [keys_deserializer, values_deserializer](IColumn & column, avro::Decoder & decoder) { @@ -810,8 +831,8 @@ AvroDeserializer::Action AvroDeserializer::createAction(const Block & header, co } } -AvroDeserializer::AvroDeserializer(const Block & header, avro::ValidSchema schema, bool allow_missing_fields, bool null_as_default_) - : null_as_default(null_as_default_) +AvroDeserializer::AvroDeserializer(const Block & header, avro::ValidSchema schema, bool allow_missing_fields, bool null_as_default_, const FormatSettings & settings_) + : null_as_default(null_as_default_), settings(settings_) { const auto & schema_root = schema.root(); if (schema_root->type() != avro::AVRO_RECORD) @@ -857,7 +878,7 @@ void AvroRowInputFormat::readPrefix() { file_reader_ptr = std::make_unique(std::make_unique(*in)); deserializer_ptr = std::make_unique( - output.getHeader(), file_reader_ptr->dataSchema(), format_settings.avro.allow_missing_fields, format_settings.null_as_default); + output.getHeader(), file_reader_ptr->dataSchema(), format_settings.avro.allow_missing_fields, format_settings.null_as_default, format_settings); file_reader_ptr->init(); } @@ -1048,7 +1069,7 @@ const AvroDeserializer & AvroConfluentRowInputFormat::getOrCreateDeserializer(Sc { auto schema = schema_registry->getSchema(schema_id); AvroDeserializer deserializer( - output.getHeader(), schema, format_settings.avro.allow_missing_fields, format_settings.null_as_default); + output.getHeader(), schema, format_settings.avro.allow_missing_fields, format_settings.null_as_default, format_settings); it = deserializer_cache.emplace(schema_id, deserializer).first; } return it->second; diff --git a/src/Processors/Formats/Impl/AvroRowInputFormat.h b/src/Processors/Formats/Impl/AvroRowInputFormat.h index ccadb431fa2..341b430205f 100644 --- a/src/Processors/Formats/Impl/AvroRowInputFormat.h +++ b/src/Processors/Formats/Impl/AvroRowInputFormat.h @@ -48,7 +48,7 @@ private: class AvroDeserializer { public: - AvroDeserializer(const Block & header, avro::ValidSchema schema, bool allow_missing_fields, bool null_as_default_); + AvroDeserializer(const Block & header, avro::ValidSchema schema, bool allow_missing_fields, bool null_as_default_, const FormatSettings & settings_); void deserializeRow(MutableColumns & columns, avro::Decoder & decoder, RowReadExtension & ext) const; using DeserializeFn = std::function; @@ -145,6 +145,8 @@ private: std::map symbolic_skip_fn_map; bool null_as_default = false; + + const FormatSettings & settings; }; class AvroRowInputFormat final : public IRowInputFormat diff --git a/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp b/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp index f1b42147cd6..cd1c1b096aa 100644 --- a/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp +++ b/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp @@ -33,6 +33,7 @@ #include #include +#include namespace DB { @@ -110,6 +111,19 @@ AvroSerializer::SchemaWithSerializeFn createDecimalSchemaWithSerializeFn(const D }}; } +template +AvroSerializer::SchemaWithSerializeFn createBigIntegerSchemaWithSerializeFn(const DataTypePtr & data_type, size_t type_name_increment) +{ + auto schema = avro::FixedSchema(sizeof(BigIntegerType), boost::algorithm::to_lower_copy(data_type->getName()) + std::to_string(type_name_increment)); + return {schema, [](const IColumn & column, size_t row_num, avro::Encoder & encoder) + { + const auto & col = assert_cast &>(column); + WriteBufferFromOwnString buf; + writeBinary(col.getElement(row_num), buf); + encoder.encodeFixed(reinterpret_cast(buf.str().data()), buf.str().size()); + }}; +} + } AvroSerializer::SchemaWithSerializeFn AvroSerializer::createSchemaWithSerializeFn(const DataTypePtr & data_type, size_t & type_name_increment, const String & column_name) @@ -180,6 +194,14 @@ AvroSerializer::SchemaWithSerializeFn AvroSerializer::createSchemaWithSerializeF { encoder.encodeDouble(assert_cast(column).getElement(row_num)); }}; + case TypeIndex::Int128: + return createBigIntegerSchemaWithSerializeFn(data_type, type_name_increment); + case TypeIndex::UInt128: + return createBigIntegerSchemaWithSerializeFn(data_type, type_name_increment); + case TypeIndex::Int256: + return createBigIntegerSchemaWithSerializeFn(data_type, type_name_increment); + case TypeIndex::UInt256: + return createBigIntegerSchemaWithSerializeFn(data_type, type_name_increment); case TypeIndex::Date: { auto schema = avro::IntSchema(); @@ -210,7 +232,7 @@ AvroSerializer::SchemaWithSerializeFn AvroSerializer::createSchemaWithSerializeF else if (provided_type.getScale() == 6) schema.root()->setLogicalType(avro::LogicalType(avro::LogicalType::TIMESTAMP_MICROS)); else - break; + return createDecimalSchemaWithSerializeFn(data_type); return {schema, [](const IColumn & column, size_t row_num, avro::Encoder & encoder) { @@ -386,7 +408,9 @@ AvroSerializer::SchemaWithSerializeFn AvroSerializer::createSchemaWithSerializeF const auto & nested_names = tuple_type.getElementNames(); std::vector nested_serializers; nested_serializers.reserve(nested_types.size()); - auto schema = avro::RecordSchema(column_name); + /// We should use unique names for records. Otherwise avro will reuse schema of this record later + /// for all records with the same name. + auto schema = avro::RecordSchema(column_name + "_" + std::to_string(type_name_increment)); for (size_t i = 0; i != nested_types.size(); ++i) { auto nested_mapping = createSchemaWithSerializeFn(nested_types[i], type_name_increment, nested_names[i]); @@ -406,13 +430,13 @@ AvroSerializer::SchemaWithSerializeFn AvroSerializer::createSchemaWithSerializeF { const auto & map_type = assert_cast(*data_type); const auto & keys_type = map_type.getKeyType(); - if (!isStringOrFixedString(keys_type)) - throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Avro Maps support only keys with type String, got {}", keys_type->getName()); + auto keys_serialization = keys_type->getDefaultSerialization(); - auto keys_serializer = [](const IColumn & column, size_t row_num, avro::Encoder & encoder) + auto keys_serializer = [keys_serialization, this](const IColumn & column, size_t row_num, avro::Encoder & encoder) { - const std::string_view & s = column.getDataAt(row_num).toView(); - encoder.encodeString(std::string(s)); + WriteBufferFromOwnString buf; + keys_serialization->serializeText(column, row_num, buf, settings); + encoder.encodeString(buf.str()); }; const auto & values_type = map_type.getValueType(); @@ -450,8 +474,8 @@ AvroSerializer::SchemaWithSerializeFn AvroSerializer::createSchemaWithSerializeF } -AvroSerializer::AvroSerializer(const ColumnsWithTypeAndName & columns, std::unique_ptr traits_) - : traits(std::move(traits_)) +AvroSerializer::AvroSerializer(const ColumnsWithTypeAndName & columns, std::unique_ptr traits_, const FormatSettings & settings_) + : traits(std::move(traits_)), settings(settings_) { avro::RecordSchema record_schema("row"); @@ -507,7 +531,7 @@ AvroRowOutputFormat::AvroRowOutputFormat( WriteBuffer & out_, const Block & header_, const FormatSettings & settings_) : IRowOutputFormat(header_, out_) , settings(settings_) - , serializer(header_.getColumnsWithTypeAndName(), std::make_unique(settings)) + , serializer(header_.getColumnsWithTypeAndName(), std::make_unique(settings), settings) { } diff --git a/src/Processors/Formats/Impl/AvroRowOutputFormat.h b/src/Processors/Formats/Impl/AvroRowOutputFormat.h index fdcf97e14ba..c526936b383 100644 --- a/src/Processors/Formats/Impl/AvroRowOutputFormat.h +++ b/src/Processors/Formats/Impl/AvroRowOutputFormat.h @@ -23,7 +23,7 @@ class AvroSerializerTraits; class AvroSerializer { public: - AvroSerializer(const ColumnsWithTypeAndName & columns, std::unique_ptr); + AvroSerializer(const ColumnsWithTypeAndName & columns, std::unique_ptr, const FormatSettings & settings_); const avro::ValidSchema & getSchema() const { return valid_schema; } void serializeRow(const Columns & columns, size_t row_num, avro::Encoder & encoder); @@ -41,6 +41,7 @@ private: std::vector serialize_fns; avro::ValidSchema valid_schema; std::unique_ptr traits; + const FormatSettings & settings; }; class AvroRowOutputFormat final : public IRowOutputFormat diff --git a/src/Storages/StorageIceberg.cpp b/src/Storages/StorageIceberg.cpp index dd7923fa76a..9b612152f8f 100644 --- a/src/Storages/StorageIceberg.cpp +++ b/src/Storages/StorageIceberg.cpp @@ -100,11 +100,10 @@ String IcebergMetadataParser::getManifestList return {}; } -static MutableColumns -parseAvro(const std::unique_ptr & file_reader, const DataTypePtr & data_type, const String & field_name) +static MutableColumns parseAvro(const std::unique_ptr & file_reader, const DataTypePtr & data_type, const String & field_name, const FormatSettings & settings) { auto deserializer = std::make_unique( - Block{{data_type->createColumn(), data_type, field_name}}, file_reader->dataSchema(), true, true); + Block{{data_type->createColumn(), data_type, field_name}}, file_reader->dataSchema(), true, true, settings); file_reader->init(); MutableColumns columns; columns.emplace_back(data_type->createColumn()); @@ -131,7 +130,7 @@ std::vector IcebergMetadataParser::ge /// And its have String data type /// {'manifest_path': 'xxx', ...} auto data_type = AvroSchemaReader::avroNodeToDataType(file_reader->dataSchema().root()->leafAt(0)); - auto columns = parseAvro(file_reader, data_type, manifest_path); + auto columns = parseAvro(file_reader, data_type, manifest_path, getFormatSettings(context)); auto & col = columns.at(0); std::vector res; @@ -171,7 +170,7 @@ std::vector IcebergMetadataParser::ge /// {'status': xx, 'snapshot_id': xx, 'data_file': {'file_path': 'xxx', ...}, ...} /// and it's also a nested record, so its result type is a nested Tuple auto data_type = AvroSchemaReader::avroNodeToDataType(file_reader->dataSchema().root()->leafAt(2)); - auto columns = parseAvro(file_reader, data_type, manifest_path); + auto columns = parseAvro(file_reader, data_type, manifest_path, getFormatSettings(context)); auto & col = columns.at(0); if (col->getDataType() == TypeIndex::Tuple) diff --git a/tests/queries/0_stateless/02592_avro_more_types.reference b/tests/queries/0_stateless/02592_avro_more_types.reference new file mode 100644 index 00000000000..5de415be619 --- /dev/null +++ b/tests/queries/0_stateless/02592_avro_more_types.reference @@ -0,0 +1,7 @@ +c1 FixedString(16) +c2 FixedString(16) +c3 FixedString(32) +c4 FixedString(32) +c5 Map(String, Int32) +c6 Decimal(18, 2) +42 42 42 42 {42:42} 2020-01-01 00:00:00.00 diff --git a/tests/queries/0_stateless/02592_avro_more_types.sh b/tests/queries/0_stateless/02592_avro_more_types.sh new file mode 100755 index 00000000000..7b87acd5f96 --- /dev/null +++ b/tests/queries/0_stateless/02592_avro_more_types.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Tags: no-fasttest + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +$CLICKHOUSE_LOCAL -q "select 42::Int128 as c1, 42::UInt128 as c2, 42::Int256 as c3, 42::UInt256 as c4, map(42, 42) as c5, toDateTime64('2020-01-01', 2) as c6 format Avro" | $CLICKHOUSE_LOCAL --input-format Avro --table test -q "desc test" + +$CLICKHOUSE_LOCAL -q "select 42::Int128 as c1, 42::UInt128 as c2, 42::Int256 as c3, 42::UInt256 as c4, map(42, 42) as c5, toDateTime64('2020-01-01', 2) as c6 format Avro" | $CLICKHOUSE_LOCAL --structure "c1 Int128, c2 UInt128, c3 Int256, c4 UInt256, c5 Map(UInt32, UInt32), c6 DateTime64(2)" --input-format Avro --table test -q "select * from test" + + + From 0fad5bdc7d6debb2e0da7ca890449022c50d713e Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Tue, 28 Mar 2023 19:52:51 +0200 Subject: [PATCH 132/377] Add a script to clone or update clickhouse-docs --- docs/.gitignore | 1 + docs/README.md | 2 ++ docs/get-clickhouse-docs.sh | 31 +++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 docs/get-clickhouse-docs.sh diff --git a/docs/.gitignore b/docs/.gitignore index 378eac25d31..509538d9051 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1,2 @@ build +clickhouse-docs diff --git a/docs/README.md b/docs/README.md index 9bfd3d2b897..0cd35a4e3ec 100644 --- a/docs/README.md +++ b/docs/README.md @@ -40,6 +40,8 @@ The documentation contains information about all the aspects of the ClickHouse l At the moment, [documentation](https://clickhouse.com/docs) exists in English, Russian, and Chinese. We store the reference documentation besides the ClickHouse source code in the [GitHub repository](https://github.com/ClickHouse/ClickHouse/tree/master/docs), and user guides in a separate repo [Clickhouse/clickhouse-docs](https://github.com/ClickHouse/clickhouse-docs). +To get the latter launch the `get-clickhouse-docs.sh` script. + Each language lies in the corresponding folder. Files that are not translated from English are symbolic links to the English ones. diff --git a/docs/get-clickhouse-docs.sh b/docs/get-clickhouse-docs.sh new file mode 100644 index 00000000000..1ba0dae9844 --- /dev/null +++ b/docs/get-clickhouse-docs.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -e +# The script to clone or update the user-guides documentation repo +# https://github.com/ClickHouse/clickhouse-docs + +WORKDIR=$(dirname "$0") +WORKDIR=$(readlink -f "${WORKDIR}") +cd "$WORKDIR" + +if [ -d "clickhouse-docs" ]; then + git -C clickhouse-docs pull +else + if [ -n "$1" ]; then + url_type="$1" + else + read -rp "Enter the URL type (ssh | https): " url_type + fi + case "$url_type" in + ssh) + git_url=git@github.com:ClickHouse/clickhouse-docs.git + ;; + https) + git_url=https://github.com/ClickHouse/clickhouse-docs.git + ;; + *) + echo "Url type must be 'ssh' or 'https'" + exit 1 + ;; + esac + git clone "$git_url" "clickhouse-docs" +fi From 202f52dc3aa8a7f08905cfbd1ce8688f51fb44a8 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Sun, 26 Mar 2023 14:36:38 +0000 Subject: [PATCH 133/377] Implement SHOW COLUMNS Modeled after (*) Fixes #46437 (*) https://dev.mysql.com/doc/refman/8.0/en/show-columns.html --- docs/en/sql-reference/statements/show.md | 89 +++++++++++---- src/Interpreters/InterpreterFactory.cpp | 6 + .../InterpreterShowColumnsQuery.cpp | 108 ++++++++++++++++++ .../InterpreterShowColumnsQuery.h | 32 ++++++ .../InterpreterShowTablesQuery.cpp | 5 +- src/Interpreters/InterpreterShowTablesQuery.h | 3 +- src/Parsers/ASTIdentifier_fwd.h | 4 + src/Parsers/ASTShowColumnsQuery.cpp | 53 +++++++++ src/Parsers/ASTShowColumnsQuery.h | 34 ++++++ src/Parsers/ASTShowTablesQuery.h | 25 ++-- src/Parsers/ParserQueryWithOutput.cpp | 3 + src/Parsers/ParserShowColumnsQuery.cpp | 80 +++++++++++++ src/Parsers/ParserShowColumnsQuery.h | 19 +++ src/Parsers/ParserShowTablesQuery.cpp | 6 - .../0_stateless/25402_show_columns.reference | 38 ++++++ .../0_stateless/25402_show_columns.sql | 80 +++++++++++++ 16 files changed, 541 insertions(+), 44 deletions(-) create mode 100644 src/Interpreters/InterpreterShowColumnsQuery.cpp create mode 100644 src/Interpreters/InterpreterShowColumnsQuery.h create mode 100644 src/Parsers/ASTShowColumnsQuery.cpp create mode 100644 src/Parsers/ASTShowColumnsQuery.h create mode 100644 src/Parsers/ParserShowColumnsQuery.cpp create mode 100644 src/Parsers/ParserShowColumnsQuery.h create mode 100644 tests/queries/0_stateless/25402_show_columns.reference create mode 100644 tests/queries/0_stateless/25402_show_columns.sql diff --git a/docs/en/sql-reference/statements/show.md b/docs/en/sql-reference/statements/show.md index 544c556d4b3..c0ab51ae444 100644 --- a/docs/en/sql-reference/statements/show.md +++ b/docs/en/sql-reference/statements/show.md @@ -30,7 +30,7 @@ This statement is identical to the query: SELECT name FROM system.databases [WHERE name [NOT] LIKE | ILIKE ''] [LIMIT ] [INTO OUTFILE filename] [FORMAT format] ``` -### Examples +**Examples** Getting database names, containing the symbols sequence 'de' in their names: @@ -92,7 +92,7 @@ Result: └────────────────────────────────┘ ``` -### See Also +**See also** - [CREATE DATABASE](https://clickhouse.com/docs/en/sql-reference/statements/create/database/#query-language-create-database) @@ -128,7 +128,7 @@ This statement is identical to the query: SELECT name FROM system.tables [WHERE name [NOT] LIKE | ILIKE ''] [LIMIT ] [INTO OUTFILE ] [FORMAT ] ``` -### Examples +**Examples** Getting table names, containing the symbols sequence 'user' in their names: @@ -191,11 +191,59 @@ Result: └────────────────────────────────┘ ``` -### See Also +**See also** - [Create Tables](https://clickhouse.com/docs/en/getting-started/tutorial/#create-tables) - [SHOW CREATE TABLE](https://clickhouse.com/docs/en/sql-reference/statements/show/#show-create-table) +## SHOW COLUMNS + +Displays a list of columns + +```sql +SHOW [EXTENDED] [FULL] COLUMNS {FROM | IN} [{FROM | IN} ] [{[NOT] {LIKE | ILIKE} '' | WHERE }] [LIMIT ] [INTO +OUTFILE ] [FORMAT ] +``` + +The database and table name can be specified in abbreviated form as `.
`, i.e. `FROM tab FROM db` and `FROM db.tab` are +equivalent. If no database is specified, the query returns the list of columns from the current database. + +The optional keyword `EXTENDED` currently has no effect, it only exists for MySQL compatibility. + +The optional keyword `FULL` causes the output to include the collation, comment and privilege columns. + +`SHOW COLUMNS` produces a result table with the following structure: +- field - The name of the column (String) +- type - The column data type (String) +- null - If the column data type is Nullable (UInt8) +- key - `PRI` if the column is part of the primary key, `SOR` if the column is part of the sorting key, empty otherwise (String) +- default - Default expression of the column if it is of type `ALIAS`, `DEFAULT`, or `MATERIALIZED`, otherwise `NULL`. (Nullable(String)) +- extra - Additional information, currently unused (String) +- collation - Collation of the column, always `NULL` because ClickHouse has no per-column collations, only if `FULL` keyword was specified (Nullable(String)) +- comment - Comment on the column, only if `FULL` keyword was specified (String) +- privilege - The privilege you have on this column, currently not available, only if `FULL` keyword was specified (String) + +**Examples** + +Getting information about all columns in table 'order' starting with 'delivery_': + +```sql +SHOW COLUMNS FROM 'orders' LIKE 'delivery_%' +``` + +Result: + +``` text +┌─field───────────┬─type─────┬─null─┬─key─────┬─default─┬─extra─┐ +│ delivery_date │ DateTime │ 0 │ PRI SOR │ ᴺᵁᴸᴸ │ │ +│ delivery_status │ Bool │ 0 │ │ ᴺᵁᴸᴸ │ │ +└─────────────────┴──────────┴──────┴─────────┴─────────┴───────┘ +``` + +**See also** + +- [system.columns](https://clickhouse.com/docs/en/operations/system-tables/columns) + ## SHOW DICTIONARIES Displays a list of [Dictionaries](../../sql-reference/dictionaries/index.md). @@ -212,7 +260,7 @@ You can get the same results as the `SHOW DICTIONARIES` query in the following w SELECT name FROM system.dictionaries WHERE database = [AND name LIKE ] [LIMIT ] [INTO OUTFILE ] [FORMAT ] ``` -**Example** +**Examples** The following query selects the first two rows from the list of tables in the `system` database, whose names contain `reg`. @@ -231,7 +279,7 @@ SHOW DICTIONARIES FROM db LIKE '%reg%' LIMIT 2 Shows privileges for a user. -### Syntax +**Syntax** ``` sql SHOW GRANTS [FOR user1 [, user2 ...]] @@ -245,7 +293,7 @@ Shows parameters that were used at a [user creation](../../sql-reference/stateme `SHOW CREATE USER` does not output user passwords. -### Syntax +**Syntax** ``` sql SHOW CREATE USER [name1 [, name2 ...] | CURRENT_USER] @@ -255,7 +303,7 @@ SHOW CREATE USER [name1 [, name2 ...] | CURRENT_USER] Shows parameters that were used at a [role creation](../../sql-reference/statements/create/role.md). -### Syntax +**Syntax** ``` sql SHOW CREATE ROLE name1 [, name2 ...] @@ -265,7 +313,7 @@ SHOW CREATE ROLE name1 [, name2 ...] Shows parameters that were used at a [row policy creation](../../sql-reference/statements/create/row-policy.md). -### Syntax +**Syntax** ``` sql SHOW CREATE [ROW] POLICY name ON [database1.]table1 [, [database2.]table2 ...] @@ -275,7 +323,7 @@ SHOW CREATE [ROW] POLICY name ON [database1.]table1 [, [database2.]table2 ...] Shows parameters that were used at a [quota creation](../../sql-reference/statements/create/quota.md). -### Syntax +**Syntax** ``` sql SHOW CREATE QUOTA [name1 [, name2 ...] | CURRENT] @@ -285,7 +333,7 @@ SHOW CREATE QUOTA [name1 [, name2 ...] | CURRENT] Shows parameters that were used at a [settings profile creation](../../sql-reference/statements/create/settings-profile.md). -### Syntax +**Syntax** ``` sql SHOW CREATE [SETTINGS] PROFILE name1 [, name2 ...] @@ -295,7 +343,7 @@ SHOW CREATE [SETTINGS] PROFILE name1 [, name2 ...] Returns a list of [user account](../../guides/sre/user-management/index.md#user-account-management) names. To view user accounts parameters, see the system table [system.users](../../operations/system-tables/users.md#system_tables-users). -### Syntax +**Syntax** ``` sql SHOW USERS @@ -305,7 +353,7 @@ SHOW USERS Returns a list of [roles](../../guides/sre/user-management/index.md#role-management). To view another parameters, see system tables [system.roles](../../operations/system-tables/roles.md#system_tables-roles) and [system.role_grants](../../operations/system-tables/role-grants.md#system_tables-role_grants). -### Syntax +**Syntax** ``` sql SHOW [CURRENT|ENABLED] ROLES @@ -314,7 +362,7 @@ SHOW [CURRENT|ENABLED] ROLES Returns a list of [setting profiles](../../guides/sre/user-management/index.md#settings-profiles-management). To view user accounts parameters, see the system table [settings_profiles](../../operations/system-tables/settings_profiles.md#system_tables-settings_profiles). -### Syntax +**Syntax** ``` sql SHOW [SETTINGS] PROFILES @@ -324,7 +372,7 @@ SHOW [SETTINGS] PROFILES Returns a list of [row policies](../../guides/sre/user-management/index.md#row-policy-management) for the specified table. To view user accounts parameters, see the system table [system.row_policies](../../operations/system-tables/row_policies.md#system_tables-row_policies). -### Syntax +**Syntax** ``` sql SHOW [ROW] POLICIES [ON [db.]table] @@ -334,7 +382,7 @@ SHOW [ROW] POLICIES [ON [db.]table] Returns a list of [quotas](../../guides/sre/user-management/index.md#quotas-management). To view quotas parameters, see the system table [system.quotas](../../operations/system-tables/quotas.md#system_tables-quotas). -### Syntax +**Syntax** ``` sql SHOW QUOTAS @@ -344,7 +392,7 @@ SHOW QUOTAS Returns a [quota](../../operations/quotas.md) consumption for all users or for current user. To view another parameters, see system tables [system.quotas_usage](../../operations/system-tables/quotas_usage.md#system_tables-quotas_usage) and [system.quota_usage](../../operations/system-tables/quota_usage.md#system_tables-quota_usage). -### Syntax +**Syntax** ``` sql SHOW [CURRENT] QUOTA @@ -353,7 +401,7 @@ SHOW [CURRENT] QUOTA Shows all [users](../../guides/sre/user-management/index.md#user-account-management), [roles](../../guides/sre/user-management/index.md#role-management), [profiles](../../guides/sre/user-management/index.md#settings-profiles-management), etc. and all their [grants](../../sql-reference/statements/grant.md#grant-privileges). -### Syntax +**Syntax** ``` sql SHOW ACCESS @@ -366,13 +414,14 @@ Returns a list of clusters. All available clusters are listed in the [system.clu `SHOW CLUSTER name` query displays the contents of system.clusters table for this cluster. ::: -### Syntax +**Syntax** ``` sql SHOW CLUSTER '' SHOW CLUSTERS [[NOT] LIKE|ILIKE ''] [LIMIT ] ``` -### Examples + +**Examples** Query: diff --git a/src/Interpreters/InterpreterFactory.cpp b/src/Interpreters/InterpreterFactory.cpp index 502de459156..fb013ce4569 100644 --- a/src/Interpreters/InterpreterFactory.cpp +++ b/src/Interpreters/InterpreterFactory.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +171,10 @@ std::unique_ptr InterpreterFactory::get(ASTPtr & query, ContextMut { return std::make_unique(query, context); } + else if (query->as()) + { + return std::make_unique(query, context); + } else if (query->as()) { return std::make_unique(query, context); diff --git a/src/Interpreters/InterpreterShowColumnsQuery.cpp b/src/Interpreters/InterpreterShowColumnsQuery.cpp new file mode 100644 index 00000000000..95530ad7c53 --- /dev/null +++ b/src/Interpreters/InterpreterShowColumnsQuery.cpp @@ -0,0 +1,108 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int SYNTAX_ERROR; +} + + +InterpreterShowColumnsQuery::InterpreterShowColumnsQuery(const ASTPtr & query_ptr_, ContextMutablePtr context_) + : WithMutableContext(context_) + , query_ptr(query_ptr_) +{ +} + + +String InterpreterShowColumnsQuery::getRewrittenQuery() +{ + const auto & query = query_ptr->as(); + + WriteBufferFromOwnString rewritten_query; + + rewritten_query << "SELECT name AS field, type AS type, startsWith(type, 'Nullable') AS null, trim(concatWithSeparator(' ', if(is_in_primary_key, 'PRI', ''), if (is_in_sorting_key, 'SOR', ''))) AS key, if(default_kind IN ('ALIAS', 'DEFAULT', 'MATERIALIZED'), default_expression, NULL) AS default, '' AS extra "; + + // TODO Interpret query.extended. It is supposed to show internal/virtual columns. Need to fetch virtual column names, see + // IStorage::getVirtuals(). We can't easily do that via SQL. + + if (query.full) + { + /// "Full" mode is mostly for MySQL compat + /// - collation: no such thing in ClickHouse + /// - comment + /// - privileges: + rewritten_query << ", NULL AS collation, comment, '' AS privileges "; + } + + rewritten_query << "FROM system.columns WHERE "; + + String database; + String table; + if (query.from_table.contains(".")) + { + /// FROM .
(abbreviated form) + chassert(query.from_database.empty()); + std::vector splitted; + boost::split(splitted, query.from_table, boost::is_any_of(".")); + chassert(splitted.size() == 2); + database = splitted[0]; + table = splitted[1]; + } + else if (query.from_database.empty()) + { + /// FROM
+ chassert(!query.from_table.empty()); + database = getContext()->getCurrentDatabase(); + table = query.from_table; + } + else + { + /// FROM FROM
+ chassert(!query.from_database.empty()); + chassert(!query.from_table.empty()); + database = query.from_database; + table = query.from_table; + } + rewritten_query << "database = " << DB::quote << database; + rewritten_query << " AND table = " << DB::quote << table; + + if (!query.like.empty()) + rewritten_query + << " AND name " + << (query.not_like ? "NOT " : "") + << (query.case_insensitive_like ? "ILIKE " : "LIKE ") + << DB::quote << query.like; + else if (query.where_expression) + rewritten_query << " AND (" << query.where_expression << ")"; + + /// Sorting is strictly speaking not necessary but 1. it is convenient for users, 2. SQL currently does not allow to + /// sort the output of SHOW COLUMNS otherwise (SELECT * FROM (SHOW COLUMNS ...) ORDER BY ...) is rejected) and 3. some + /// SQL tests can take advantage of this. + rewritten_query << " ORDER BY field, type, null, key, default, extra"; + + if (query.limit_length) + rewritten_query << " LIMIT " << query.limit_length; + + return rewritten_query.str(); + +} + + +BlockIO InterpreterShowColumnsQuery::execute() +{ + return executeQuery(getRewrittenQuery(), getContext(), true); +} + + +} diff --git a/src/Interpreters/InterpreterShowColumnsQuery.h b/src/Interpreters/InterpreterShowColumnsQuery.h new file mode 100644 index 00000000000..ee6dcabd97b --- /dev/null +++ b/src/Interpreters/InterpreterShowColumnsQuery.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + + +namespace DB +{ + +class Context; + + +/// Returns a list of columns which meet some conditions. +class InterpreterShowColumnsQuery : public IInterpreter, WithMutableContext +{ +public: + InterpreterShowColumnsQuery(const ASTPtr & query_ptr_, ContextMutablePtr context_); + + BlockIO execute() override; + + /// Ignore quota and limits here because execute() produces a SELECT query which checks quotas/limits by itself. + bool ignoreQuota() const override { return true; } + bool ignoreLimits() const override { return true; } + +private: + ASTPtr query_ptr; + + String getRewrittenQuery(); +}; + + +} diff --git a/src/Interpreters/InterpreterShowTablesQuery.cpp b/src/Interpreters/InterpreterShowTablesQuery.cpp index 4e0dfdc9236..026057a8309 100644 --- a/src/Interpreters/InterpreterShowTablesQuery.cpp +++ b/src/Interpreters/InterpreterShowTablesQuery.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -24,7 +24,8 @@ namespace ErrorCodes InterpreterShowTablesQuery::InterpreterShowTablesQuery(const ASTPtr & query_ptr_, ContextMutablePtr context_) - : WithMutableContext(context_), query_ptr(query_ptr_) + : WithMutableContext(context_) + , query_ptr(query_ptr_) { } diff --git a/src/Interpreters/InterpreterShowTablesQuery.h b/src/Interpreters/InterpreterShowTablesQuery.h index 16fc9ef2cf4..2693e5b08ed 100644 --- a/src/Interpreters/InterpreterShowTablesQuery.h +++ b/src/Interpreters/InterpreterShowTablesQuery.h @@ -20,8 +20,7 @@ public: BlockIO execute() override; - /// We ignore the quota and limits here because execute() will rewrite a show query as a SELECT query and then - /// the SELECT query will checks the quota and limits. + /// Ignore quota and limits here because execute() produces a SELECT query which checks quotas/limits by itself. bool ignoreQuota() const override { return true; } bool ignoreLimits() const override { return true; } diff --git a/src/Parsers/ASTIdentifier_fwd.h b/src/Parsers/ASTIdentifier_fwd.h index c4434477fdb..01cfbb8fec7 100644 --- a/src/Parsers/ASTIdentifier_fwd.h +++ b/src/Parsers/ASTIdentifier_fwd.h @@ -17,17 +17,21 @@ class ASTTableIdentifier; void setIdentifierSpecial(ASTPtr & ast); String getIdentifierName(const IAST * ast); + std::optional tryGetIdentifierName(const IAST * ast); + bool tryGetIdentifierNameInto(const IAST * ast, String & name); inline String getIdentifierName(const ASTPtr & ast) { return getIdentifierName(ast.get()); } + inline std::optional tryGetIdentifierName(const ASTPtr & ast) { return tryGetIdentifierName(ast.get()); } + inline bool tryGetIdentifierNameInto(const ASTPtr & ast, String & name) { return tryGetIdentifierNameInto(ast.get(), name); diff --git a/src/Parsers/ASTShowColumnsQuery.cpp b/src/Parsers/ASTShowColumnsQuery.cpp new file mode 100644 index 00000000000..113029aacd7 --- /dev/null +++ b/src/Parsers/ASTShowColumnsQuery.cpp @@ -0,0 +1,53 @@ +#include + +#include +#include +#include + +namespace DB +{ + +ASTPtr ASTShowColumnsQuery::clone() const +{ + auto res = std::make_shared(*this); + res->children.clear(); + cloneOutputOptions(*res); + return res; +} + +void ASTShowColumnsQuery::formatQueryImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const +{ + settings.ostr << (settings.hilite ? hilite_keyword : "") + << "SHOW " + << (extended ? "EXTENDED " : "") + << (full ? "FULL " : "") + << "COLUMNS" + << (settings.hilite ? hilite_none : ""); + + if (from_database.empty()) + settings.ostr << (settings.hilite ? hilite_keyword : "") << " FROM " << (settings.hilite ? hilite_none : "") << backQuoteIfNeed(from_table); + else + settings.ostr << (settings.hilite ? hilite_keyword : "") << " FROM " << (settings.hilite ? hilite_none : "") << backQuoteIfNeed(from_database) << "." << backQuoteIfNeed(from_table); + + + if (!like.empty()) + settings.ostr << (settings.hilite ? hilite_keyword : "") + << (not_like ? " NOT " : "") + << (case_insensitive_like ? " ILIKE " : " LIKE" ) + << (settings.hilite ? hilite_none : "") + << DB::quote << like; + + if (where_expression) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " WHERE " << (settings.hilite ? hilite_none : ""); + where_expression->formatImpl(settings, state, frame); + } + + if (limit_length) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " LIMIT " << (settings.hilite ? hilite_none : ""); + limit_length->formatImpl(settings, state, frame); + } +} + +} diff --git a/src/Parsers/ASTShowColumnsQuery.h b/src/Parsers/ASTShowColumnsQuery.h new file mode 100644 index 00000000000..79e8a67b558 --- /dev/null +++ b/src/Parsers/ASTShowColumnsQuery.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace DB +{ + +/// Query SHOW COLUMNS +class ASTShowColumnsQuery : public ASTQueryWithOutput +{ +public: + bool extended = false; + bool full = false; + bool not_like = false; + bool case_insensitive_like = false; + + ASTPtr where_expression; + ASTPtr limit_length; + + String from_database; + String from_table; + + String like; + + String getID(char) const override { return "ShowColumns"; } + ASTPtr clone() const override; + QueryKind getQueryKind() const override { return QueryKind::Show; } + +protected: + void formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override; +}; + +} diff --git a/src/Parsers/ASTShowTablesQuery.h b/src/Parsers/ASTShowTablesQuery.h index b58d65e37ab..2878df54bcc 100644 --- a/src/Parsers/ASTShowTablesQuery.h +++ b/src/Parsers/ASTShowTablesQuery.h @@ -14,31 +14,28 @@ namespace DB class ASTShowTablesQuery : public ASTQueryWithOutput { public: - bool databases{false}; - bool clusters{false}; - bool cluster{false}; - bool dictionaries{false}; - bool m_settings{false}; - bool changed{false}; - bool temporary{false}; - bool caches{false}; - bool full{false}; + bool databases = false; + bool clusters = false; + bool cluster = false; + bool dictionaries = false; + bool m_settings = false; + bool changed = false; + bool temporary = false; + bool caches = false; + bool full = false; String cluster_str; String from; String like; - bool not_like{false}; - bool case_insensitive_like{false}; + bool not_like = false; + bool case_insensitive_like = false; ASTPtr where_expression; ASTPtr limit_length; - /** Get the text that identifies this element. */ String getID(char) const override { return "ShowTables"; } - ASTPtr clone() const override; - QueryKind getQueryKind() const override { return QueryKind::Show; } protected: diff --git a/src/Parsers/ParserQueryWithOutput.cpp b/src/Parsers/ParserQueryWithOutput.cpp index 7024d8cbe11..518f0e7e50e 100644 --- a/src/Parsers/ParserQueryWithOutput.cpp +++ b/src/Parsers/ParserQueryWithOutput.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ namespace DB bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) { ParserShowTablesQuery show_tables_p; + ParserShowColumnsQuery show_columns_p; ParserShowEnginesQuery show_engine_p; ParserSelectWithUnionQuery select_p; ParserTablePropertiesQuery table_p; @@ -62,6 +64,7 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec || select_p.parse(pos, query, expected) || show_create_access_entity_p.parse(pos, query, expected) /// should be before `show_tables_p` || show_tables_p.parse(pos, query, expected) + || show_columns_p.parse(pos, query, expected) || show_engine_p.parse(pos, query, expected) || table_p.parse(pos, query, expected) || describe_cache_p.parse(pos, query, expected) diff --git a/src/Parsers/ParserShowColumnsQuery.cpp b/src/Parsers/ParserShowColumnsQuery.cpp new file mode 100644 index 00000000000..1db31601437 --- /dev/null +++ b/src/Parsers/ParserShowColumnsQuery.cpp @@ -0,0 +1,80 @@ +#include + +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +bool ParserShowColumnsQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) +{ + ASTPtr like; + ASTPtr from_db; + ASTPtr from_table; + + auto query = std::make_shared(); + + if (!ParserKeyword("SHOW").ignore(pos, expected)) + return false; + + if (ParserKeyword("EXTENDED").ignore(pos, expected)) + query->extended = true; + + if (ParserKeyword("FULL").ignore(pos, expected)) + query->full = true; + + if (!ParserKeyword("COLUMNS").ignore(pos, expected) || ParserKeyword("FIELDS").ignore(pos, expected)) + return false; + + if (ParserKeyword("FROM").ignore(pos, expected) || ParserKeyword("IN").ignore(pos, expected)) + { + if (!ParserCompoundIdentifier().parse(pos, from_table, expected)) + return false; + } + else + return false; + + tryGetIdentifierNameInto(from_table, query->from_table); + bool abbreviated_form = query->from_table.contains("."); /// FROM .
+ + if (!abbreviated_form) + if (ParserKeyword("FROM").ignore(pos, expected) || ParserKeyword("IN").ignore(pos, expected)) + if (!ParserIdentifier().parse(pos, from_db, expected)) + return false; + + tryGetIdentifierNameInto(from_db, query->from_database); + + if (ParserKeyword("NOT").ignore(pos, expected)) + query->not_like = true; + + if (bool insensitive = ParserKeyword("ILIKE").ignore(pos, expected); insensitive || ParserKeyword("LIKE").ignore(pos, expected)) + { + if (insensitive) + query->case_insensitive_like = true; + + if (!ParserStringLiteral().parse(pos, like, expected)) + return false; + } + else if (query->not_like) + return false; + else if (ParserKeyword("WHERE").ignore(pos, expected)) + if (!ParserExpressionWithOptionalAlias(false).parse(pos, query->where_expression, expected)) + return false; + + if (ParserKeyword("LIMIT").ignore(pos, expected)) + if (!ParserExpressionWithOptionalAlias(false).parse(pos, query->limit_length, expected)) + return false; + + if (like) + query->like = like->as().value.safeGet(); + + node = query; + + return true; +} + +} diff --git a/src/Parsers/ParserShowColumnsQuery.h b/src/Parsers/ParserShowColumnsQuery.h new file mode 100644 index 00000000000..999acf722af --- /dev/null +++ b/src/Parsers/ParserShowColumnsQuery.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace DB +{ + +/** Parses queries of the form + * SHOW [EXTENDED] [FULL] COLUMNS (FROM|IN) tbl [(FROM|IN) db] [(([NOT] (LIKE|ILIKE) expr) | (WHERE expr))] [LIMIT n] + */ +class ParserShowColumnsQuery : public IParserBase +{ +protected: + const char * getName() const override { return "SHOW COLUMNS query"; } + + bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; +}; + +} diff --git a/src/Parsers/ParserShowTablesQuery.cpp b/src/Parsers/ParserShowTablesQuery.cpp index 1647dd9a5b4..3540a6d3fc8 100644 --- a/src/Parsers/ParserShowTablesQuery.cpp +++ b/src/Parsers/ParserShowTablesQuery.cpp @@ -149,10 +149,8 @@ bool ParserShowTablesQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec } if (s_from.ignore(pos, expected) || s_in.ignore(pos, expected)) - { if (!name_p.parse(pos, database, expected)) return false; - } if (s_not.ignore(pos, expected)) query->not_like = true; @@ -168,16 +166,12 @@ bool ParserShowTablesQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec else if (query->not_like) return false; else if (s_where.ignore(pos, expected)) - { if (!exp_elem.parse(pos, query->where_expression, expected)) return false; - } if (s_limit.ignore(pos, expected)) - { if (!exp_elem.parse(pos, query->limit_length, expected)) return false; - } } tryGetIdentifierNameInto(database, query->from); diff --git a/tests/queries/0_stateless/25402_show_columns.reference b/tests/queries/0_stateless/25402_show_columns.reference new file mode 100644 index 00000000000..98438bd6595 --- /dev/null +++ b/tests/queries/0_stateless/25402_show_columns.reference @@ -0,0 +1,38 @@ +int32 Nullable(Int32) 1 \N +str String 0 SOR \N +uint64 UInt64 0 PRI SOR \N +--- +int32 Nullable(Int32) 1 \N +str String 0 SOR \N +uint64 UInt64 0 PRI SOR \N +--- +int32 Nullable(Int32) 1 \N \N example comment +str String 0 SOR \N \N +uint64 UInt64 0 PRI SOR \N \N +--- +int32 Nullable(Int32) 1 \N +uint64 UInt64 0 PRI SOR \N +--- +str String 0 SOR \N +--- +int32 Nullable(Int32) 1 \N +uint64 UInt64 0 PRI SOR \N +--- +str String 0 SOR \N +--- +int32 Nullable(Int32) 1 \N +uint64 UInt64 0 PRI SOR \N +--- +int32 Nullable(Int32) 1 \N +--- +int32 Nullable(Int32) 1 \N +str String 0 SOR \N +uint64 UInt64 0 PRI SOR \N +--- +int32 Int32 0 \N +str String 0 \N +uint64 UInt64 0 PRI SOR \N +--- +int32 Int32 0 \N +str String 0 \N +uint64 UInt64 0 PRI SOR \N diff --git a/tests/queries/0_stateless/25402_show_columns.sql b/tests/queries/0_stateless/25402_show_columns.sql new file mode 100644 index 00000000000..baafecd7f68 --- /dev/null +++ b/tests/queries/0_stateless/25402_show_columns.sql @@ -0,0 +1,80 @@ +-- Tags: no-parallel +-- no-parallel: creates a custom database schema and expects to use it exclusively + +-- Create a test table and verify that the output of SHOW COLUMNS is sane. +-- The matching of actual/expected results relies on the fact that the output of SHOW COLUMNS is sorted. +CREATE OR REPLACE TABLE tab +( + `uint64` UInt64, + `int32` Nullable(Int32) COMMENT 'example comment', + `str` String, + INDEX idx str TYPE set(1000) +) +ENGINE = MergeTree +PRIMARY KEY (uint64) +ORDER BY (uint64, str); + +SHOW COLUMNS FROM tab; + +SELECT '---'; + +SHOW EXTENDED COLUMNS FROM tab; + +SELECT '---'; + +SHOW FULL COLUMNS FROM tab; + +SELECT '---'; + +SHOW COLUMNS FROM tab LIKE '%int%'; + +SELECT '---'; + +SHOW COLUMNS FROM tab NOT LIKE '%int%'; + +SELECT '---'; + +SHOW COLUMNS FROM tab ILIKE '%INT%'; + +SELECT '---'; + +SHOW COLUMNS FROM tab NOT ILIKE '%INT%'; + +SELECT '---'; + +SHOW COLUMNS FROM tab WHERE field LIKE '%int%'; + +SELECT '---'; + +SHOW COLUMNS FROM tab LIMIT 1; + +SELECT '---'; + + +-- Create a table in a different database. Intentionally useing the same table/column names as above so +-- we notice if something is buggy in the implementation of SHOW COLUMNS. +DROP DATABASE database_123456789abcde; +CREATE DATABASE database_123456789abcde; -- pseudo-random database name + +CREATE OR REPLACE TABLE database_123456789abcde.tab +( + `uint64` UInt64, + `int32` Int32, + `str` String +) +ENGINE = MergeTree +ORDER BY uint64; + +SHOW COLUMNS FROM tab; + +SELECT '---'; + +SHOW COLUMNS FROM tab FROM database_123456789abcde; + +SELECT '---'; + +SHOW COLUMNS FROM database_123456789abcde.tab; + +DROP DATABASE database_123456789abc; + +DROP TABLE tab; From a0fcf81abfed85bf10caa4eae28fe36a2f164a4d Mon Sep 17 00:00:00 2001 From: avogar Date: Tue, 28 Mar 2023 18:25:52 +0000 Subject: [PATCH 134/377] Support more ClickHouse types in MsgPack format --- docs/en/interfaces/formats.md | 38 +++--- .../Formats/Impl/MsgPackRowInputFormat.cpp | 128 +++++++++++++++--- .../Formats/Impl/MsgPackRowInputFormat.h | 4 +- .../Formats/Impl/MsgPackRowOutputFormat.cpp | 64 ++++++++- .../02594_msgpack_more_types.reference | 2 + .../0_stateless/02594_msgpack_more_types.sh | 11 ++ 6 files changed, 208 insertions(+), 39 deletions(-) create mode 100644 tests/queries/0_stateless/02594_msgpack_more_types.reference create mode 100755 tests/queries/0_stateless/02594_msgpack_more_types.sh diff --git a/docs/en/interfaces/formats.md b/docs/en/interfaces/formats.md index 8430946a6c6..d82f7c4ea3f 100644 --- a/docs/en/interfaces/formats.md +++ b/docs/en/interfaces/formats.md @@ -2281,22 +2281,28 @@ ClickHouse supports reading and writing [MessagePack](https://msgpack.org/) data ### Data Types Matching {#data-types-matching-msgpack} -| MessagePack data type (`INSERT`) | ClickHouse data type | MessagePack data type (`SELECT`) | -|--------------------------------------------------------------------|-----------------------------------------------------------------|------------------------------------| -| `uint N`, `positive fixint` | [UIntN](/docs/en/sql-reference/data-types/int-uint.md) | `uint N` | -| `int N`, `negative fixint` | [IntN](/docs/en/sql-reference/data-types/int-uint.md) | `int N` | -| `bool` | [UInt8](/docs/en/sql-reference/data-types/int-uint.md) | `uint 8` | -| `fixstr`, `str 8`, `str 16`, `str 32`, `bin 8`, `bin 16`, `bin 32` | [String](/docs/en/sql-reference/data-types/string.md) | `bin 8`, `bin 16`, `bin 32` | -| `fixstr`, `str 8`, `str 16`, `str 32`, `bin 8`, `bin 16`, `bin 32` | [FixedString](/docs/en/sql-reference/data-types/fixedstring.md) | `bin 8`, `bin 16`, `bin 32` | -| `float 32` | [Float32](/docs/en/sql-reference/data-types/float.md) | `float 32` | -| `float 64` | [Float64](/docs/en/sql-reference/data-types/float.md) | `float 64` | -| `uint 16` | [Date](/docs/en/sql-reference/data-types/date.md) | `uint 16` | -| `uint 32` | [DateTime](/docs/en/sql-reference/data-types/datetime.md) | `uint 32` | -| `uint 64` | [DateTime64](/docs/en/sql-reference/data-types/datetime.md) | `uint 64` | -| `fixarray`, `array 16`, `array 32` | [Array](/docs/en/sql-reference/data-types/array.md) | `fixarray`, `array 16`, `array 32` | -| `fixmap`, `map 16`, `map 32` | [Map](/docs/en/sql-reference/data-types/map.md) | `fixmap`, `map 16`, `map 32` | -| `uint 32` | [IPv4](/docs/en/sql-reference/data-types/domains/ipv4.md) | `uint 32` | -| `bin 8` | [String](/docs/en/sql-reference/data-types/string.md) | `bin 8` | +| MessagePack data type (`INSERT`) | ClickHouse data type | MessagePack data type (`SELECT`) | +|--------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|----------------------------------| +| `uint N`, `positive fixint` | [UIntN](/docs/en/sql-reference/data-types/int-uint.md) | `uint N` | +| `int N`, `negative fixint` | [IntN](/docs/en/sql-reference/data-types/int-uint.md) | `int N` | +| `bool` | [UInt8](/docs/en/sql-reference/data-types/int-uint.md) | `uint 8` | +| `fixstr`, `str 8`, `str 16`, `str 32`, `bin 8`, `bin 16`, `bin 32` | [String](/docs/en/sql-reference/data-types/string.md) | `bin 8`, `bin 16`, `bin 32` | +| `fixstr`, `str 8`, `str 16`, `str 32`, `bin 8`, `bin 16`, `bin 32` | [FixedString](/docs/en/sql-reference/data-types/fixedstring.md) | `bin 8`, `bin 16`, `bin 32` | +| `float 32` | [Float32](/docs/en/sql-reference/data-types/float.md) | `float 32` | +| `float 64` | [Float64](/docs/en/sql-reference/data-types/float.md) | `float 64` | +| `uint 16` | [Date](/docs/en/sql-reference/data-types/date.md) | `uint 16` | +| `int 32` | [Date32](/docs/en/sql-reference/data-types/date32.md) | `int 32` | +| `uint 32` | [DateTime](/docs/en/sql-reference/data-types/datetime.md) | `uint 32` | +| `uint 64` | [DateTime64](/docs/en/sql-reference/data-types/datetime.md) | `uint 64` | +| `fixarray`, `array 16`, `array 32` | [Array](/docs/en/sql-reference/data-types/array.md)/[Tuple](/docs/en/sql-reference/data-types/tuple.md) | `fixarray`, `array 16`, `array 32` | +| `fixmap`, `map 16`, `map 32` | [Map](/docs/en/sql-reference/data-types/map.md) | `fixmap`, `map 16`, `map 32` | +| `uint 32` | [IPv4](/docs/en/sql-reference/data-types/domains/ipv4.md) | `uint 32` | +| `bin 8` | [String](/docs/en/sql-reference/data-types/string.md) | `bin 8` | +| `int 8` | [Enum8](/docs/en/sql-reference/data-types/enum.md) | `int 8` | +| `bin 8` | [(U)Int128/(U)Int256](/docs/en/sql-reference/data-types/int-uint.md) | `bin 8` | +| `int 32` | [Decimal32](/docs/en/sql-reference/data-types/decimal.md) | `int 32` | +| `int 64` | [Decimal64](/docs/en/sql-reference/data-types/decimal.md) | `int 64` | +| `bin 8` | [Decimal128/Decimal256](/docs/en/sql-reference/data-types/decimal.md) | `bin 8 ` | Example: diff --git a/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp b/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp index bc41b512f79..7ce58b9991d 100644 --- a/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include +#include #include #include #include @@ -64,7 +66,7 @@ void MsgPackVisitor::set_info(IColumn & column, DataTypePtr type, UInt8 & read) { info_stack.pop(); } - info_stack.push(Info{column, type, &read}); + info_stack.push(Info{column, type, false, std::nullopt, &read}); } void MsgPackVisitor::reset() @@ -137,16 +139,19 @@ static void insertInteger(IColumn & column, DataTypePtr type, UInt64 value) assert_cast(column).insertValue(value); break; } + case TypeIndex::Enum8: [[fallthrough]]; case TypeIndex::Int8: { assert_cast(column).insertValue(value); break; } + case TypeIndex::Enum16: [[fallthrough]]; case TypeIndex::Int16: { assert_cast(column).insertValue(value); break; } + case TypeIndex::Date32: [[fallthrough]]; case TypeIndex::Int32: { assert_cast(column).insertValue(static_cast(value)); @@ -167,11 +172,30 @@ static void insertInteger(IColumn & column, DataTypePtr type, UInt64 value) assert_cast(column).insertValue(IPv4(static_cast(value))); break; } + case TypeIndex::Decimal32: + { + assert_cast &>(column).insertValue(static_cast(value)); + break; + } + case TypeIndex::Decimal64: + { + assert_cast &>(column).insertValue(value); + break; + } default: throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Cannot insert MessagePack integer into column with type {}.", type->getName()); } } +template +static void insertFromBinaryRepresentation(IColumn & column, DataTypePtr type, const char * value, size_t size) +{ + if (size != sizeof(typename ColumnType::ValueType)) + throw Exception(ErrorCodes::INCORRECT_DATA, "Unexpected size of {} value: {}", type->getName(), size); + + assert_cast(column).insertData(value, size); +} + static void insertString(IColumn & column, DataTypePtr type, const char * value, size_t size, bool bin) { auto insert_func = [&](IColumn & column_, DataTypePtr type_) @@ -195,10 +219,33 @@ static void insertString(IColumn & column, DataTypePtr type, const char * value, return; } - if (isIPv6(type) && bin) + if (bin) { - assert_cast(column).insertData(value, size); - return; + switch (type->getTypeId()) + { + case TypeIndex::IPv6: + insertFromBinaryRepresentation(column, type, value, size); + return; + case TypeIndex::Int128: + insertFromBinaryRepresentation(column, type, value, size); + return; + case TypeIndex::UInt128: + insertFromBinaryRepresentation(column, type, value, size); + return; + case TypeIndex::Int256: + insertFromBinaryRepresentation(column, type, value, size); + return; + case TypeIndex::UInt256: + insertFromBinaryRepresentation(column, type, value, size); + return; + case TypeIndex::Decimal128: + insertFromBinaryRepresentation>(column, type, value, size); + return; + case TypeIndex::Decimal256: + insertFromBinaryRepresentation>(column, type, value, size); + return; + default:; + } } if (!isStringOrFixedString(type)) @@ -328,21 +375,47 @@ bool MsgPackVisitor::visit_boolean(bool value) bool MsgPackVisitor::start_array(size_t size) // NOLINT { - if (!isArray(info_stack.top().type)) - throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Cannot insert MessagePack array into column with type {}.", info_stack.top().type->getName()); + if (isArray(info_stack.top().type)) + { + auto nested_type = assert_cast(*info_stack.top().type).getNestedType(); + ColumnArray & column_array = assert_cast(info_stack.top().column); + ColumnArray::Offsets & offsets = column_array.getOffsets(); + IColumn & nested_column = column_array.getData(); + offsets.push_back(offsets.back() + size); + if (size > 0) + info_stack.push(Info{nested_column, nested_type, false, size, nullptr}); + } + else if (isTuple(info_stack.top().type)) + { + const auto & tuple_type = assert_cast(*info_stack.top().type); + const auto & nested_types = tuple_type.getElements(); + if (size != nested_types.size()) + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Cannot insert MessagePack array with size {} into Tuple column with {} elements", size, nested_types.size()); + + ColumnTuple & column_tuple = assert_cast(info_stack.top().column); + /// Push nested columns into stack in reverse order. + for (ssize_t i = nested_types.size() - 1; i >= 0; --i) + info_stack.push(Info{column_tuple.getColumn(i), nested_types[i], true, std::nullopt, nullptr}); + } + else + { + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Cannot insert MessagePack array into column with type {}", info_stack.top().type->getName()); + } - auto nested_type = assert_cast(*info_stack.top().type).getNestedType(); - ColumnArray & column_array = assert_cast(info_stack.top().column); - ColumnArray::Offsets & offsets = column_array.getOffsets(); - IColumn & nested_column = column_array.getData(); - offsets.push_back(offsets.back() + size); - info_stack.push(Info{nested_column, nested_type, nullptr}); return true; } -bool MsgPackVisitor::end_array() // NOLINT + +bool MsgPackVisitor::end_array_item() // NOLINT { - info_stack.pop(); + if (info_stack.top().is_tuple_element) + info_stack.pop(); + else + { + --(*info_stack.top().array_size); + if (*info_stack.top().array_size == 0) + info_stack.pop(); + } return true; } @@ -360,7 +433,7 @@ bool MsgPackVisitor::start_map_key() // NOLINT { auto key_column = assert_cast(info_stack.top().column).getNestedData().getColumns()[0]; auto key_type = assert_cast(*info_stack.top().type).getKeyType(); - info_stack.push(Info{*key_column, key_type, nullptr}); + info_stack.push(Info{*key_column, key_type, false, std::nullopt, nullptr}); return true; } @@ -374,7 +447,7 @@ bool MsgPackVisitor::start_map_value() // NOLINT { auto value_column = assert_cast(info_stack.top().column).getNestedData().getColumns()[1]; auto value_type = assert_cast(*info_stack.top().type).getValueType(); - info_stack.push(Info{*value_column, value_type, nullptr}); + info_stack.push(Info{*value_column, value_type, false, std::nullopt, nullptr}); return true; } @@ -513,13 +586,26 @@ DataTypePtr MsgPackSchemaReader::getDataType(const msgpack::object & object) case msgpack::type::object_type::ARRAY: { msgpack::object_array object_array = object.via.array; - if (object_array.size) + if (!object_array.size) + return nullptr; + + DataTypes nested_types; + nested_types.reserve(object_array.size); + bool nested_types_are_equal = true; + for (size_t i = 0; i != object_array.size; ++i) { - auto nested_type = getDataType(object_array.ptr[0]); - if (nested_type) - return std::make_shared(getDataType(object_array.ptr[0])); + auto nested_type = getDataType(object_array.ptr[i]); + if (!nested_type) + return nullptr; + + nested_types.push_back(nested_type); + nested_types_are_equal &= nested_type->equals(*nested_types[0]); } - return nullptr; + + if (nested_types_are_equal) + return std::make_shared(nested_types[0]); + + return std::make_shared(std::move(nested_types)); } case msgpack::type::object_type::MAP: { diff --git a/src/Processors/Formats/Impl/MsgPackRowInputFormat.h b/src/Processors/Formats/Impl/MsgPackRowInputFormat.h index 5eaa3719d0c..0b485d3b97c 100644 --- a/src/Processors/Formats/Impl/MsgPackRowInputFormat.h +++ b/src/Processors/Formats/Impl/MsgPackRowInputFormat.h @@ -25,6 +25,8 @@ public: { IColumn & column; DataTypePtr type; + bool is_tuple_element; + std::optional array_size; UInt8 * read; }; @@ -37,7 +39,7 @@ public: bool visit_bin(const char * value, size_t size); bool visit_boolean(bool value); bool start_array(size_t size); - bool end_array(); + bool end_array_item(); bool visit_nil(); bool start_map(uint32_t size); bool start_map_key(); diff --git a/src/Processors/Formats/Impl/MsgPackRowOutputFormat.cpp b/src/Processors/Formats/Impl/MsgPackRowOutputFormat.cpp index 07951d42bc6..9c601492217 100644 --- a/src/Processors/Formats/Impl/MsgPackRowOutputFormat.cpp +++ b/src/Processors/Formats/Impl/MsgPackRowOutputFormat.cpp @@ -9,12 +9,14 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -66,16 +68,19 @@ void MsgPackRowOutputFormat::serializeField(const IColumn & column, DataTypePtr packer.pack_uint64(assert_cast(column).getElement(row_num)); return; } + case TypeIndex::Enum8: [[fallthrough]]; case TypeIndex::Int8: { packer.pack_int8(assert_cast(column).getElement(row_num)); return; } + case TypeIndex::Enum16: [[fallthrough]]; case TypeIndex::Int16: { packer.pack_int16(assert_cast(column).getElement(row_num)); return; } + case TypeIndex::Date32: [[fallthrough]]; case TypeIndex::Int32: { packer.pack_int32(assert_cast(column).getElement(row_num)); @@ -86,6 +91,30 @@ void MsgPackRowOutputFormat::serializeField(const IColumn & column, DataTypePtr packer.pack_int64(assert_cast(column).getElement(row_num)); return; } + case TypeIndex::Int128: + { + packer.pack_bin(static_cast(sizeof(Int128))); + packer.pack_bin_body(column.getDataAt(row_num).data, sizeof(Int128)); + return; + } + case TypeIndex::UInt128: + { + packer.pack_bin(static_cast(sizeof(UInt128))); + packer.pack_bin_body(column.getDataAt(row_num).data, sizeof(UInt128)); + return; + } + case TypeIndex::Int256: + { + packer.pack_bin(static_cast(sizeof(Int256))); + packer.pack_bin_body(column.getDataAt(row_num).data, sizeof(Int256)); + return; + } + case TypeIndex::UInt256: + { + packer.pack_bin(static_cast(sizeof(UInt256))); + packer.pack_bin_body(column.getDataAt(row_num).data, sizeof(UInt256)); + return; + } case TypeIndex::Float32: { packer.pack_float(assert_cast(column).getElement(row_num)); @@ -101,6 +130,28 @@ void MsgPackRowOutputFormat::serializeField(const IColumn & column, DataTypePtr packer.pack_uint64(assert_cast(column).getElement(row_num)); return; } + case TypeIndex::Decimal32: + { + packer.pack_int32(assert_cast &>(column).getElement(row_num)); + return; + } + case TypeIndex::Decimal64: + { + packer.pack_int64(assert_cast &>(column).getElement(row_num)); + return; + } + case TypeIndex::Decimal128: + { + packer.pack_bin(static_cast(sizeof(Decimal128))); + packer.pack_bin_body(column.getDataAt(row_num).data, sizeof(Decimal128)); + return; + } + case TypeIndex::Decimal256: + { + packer.pack_bin(static_cast(sizeof(Decimal256))); + packer.pack_bin_body(column.getDataAt(row_num).data, sizeof(Decimal256)); + return; + } case TypeIndex::String: { const std::string_view & string = assert_cast(column).getDataAt(row_num).toView(); @@ -136,7 +187,18 @@ void MsgPackRowOutputFormat::serializeField(const IColumn & column, DataTypePtr serializeField(nested_column, nested_type, offset + i); } return; - } + } + case TypeIndex::Tuple: + { + const auto & tuple_type = assert_cast(*data_type); + const auto & nested_types = tuple_type.getElements(); + const ColumnTuple & column_tuple = assert_cast(column); + const auto & nested_columns = column_tuple.getColumns(); + packer.pack_array(static_cast(nested_types.size())); + for (size_t i = 0; i < nested_types.size(); ++i) + serializeField(*nested_columns[i], nested_types[i], row_num); + return; + } case TypeIndex::Nullable: { auto nested_type = removeNullable(data_type); diff --git a/tests/queries/0_stateless/02594_msgpack_more_types.reference b/tests/queries/0_stateless/02594_msgpack_more_types.reference new file mode 100644 index 00000000000..8ccf11ccdb4 --- /dev/null +++ b/tests/queries/0_stateless/02594_msgpack_more_types.reference @@ -0,0 +1,2 @@ +a b 2020-01-01 42 42 42 42 42.42 42.42 42.42 42.42 +(42,'Hello') ({42:[1,2,3]},[([(1,2),(1,2)],'Hello',[1,2,3]),([],'World',[1])]) diff --git a/tests/queries/0_stateless/02594_msgpack_more_types.sh b/tests/queries/0_stateless/02594_msgpack_more_types.sh new file mode 100755 index 00000000000..bddfb5ad829 --- /dev/null +++ b/tests/queries/0_stateless/02594_msgpack_more_types.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Tags: no-fasttest + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +$CLICKHOUSE_LOCAL -q "select 'a'::Enum8('a' = 1) as c1, 'b'::Enum16('b' = 1) as c2, '2020-01-01'::Date32 as c3, 42::Int128 as c4, 42::UInt128 as c5, 42::Int256 as c6, 42::UInt256 as c7, 42.42::Decimal32(2) as c8, 42.42::Decimal64(2) as c9, 42.42::Decimal128(2) as c10, 42.42::Decimal256(2) as c11 format MsgPack" | $CLICKHOUSE_LOCAL --input-format MsgPack --structure="c1 Enum8('a' = 1), c2 Enum16('b' = 1), c3 Date32, c4 Int128, c5 UInt128, c6 Int256, c7 UInt256, c8 Decimal32(2), c9 Decimal64(2), c10 Decimal128(2), c11 Decimal256(2)" -q "select * from table" + +$CLICKHOUSE_LOCAL -q "select tuple(42, 'Hello') as c1, tuple(map(42, [1, 2, 3]), [tuple([tuple(1, 2), tuple(1, 2)], 'Hello', [1, 2, 3]), tuple([], 'World', [1])]) as c2 format MsgPack" | $CLICKHOUSE_LOCAL --input-format MsgPack --structure="c1 Tuple(UInt32, String), c2 Tuple(Map(UInt32, Array(UInt32)), Array(Tuple(Array(Tuple(UInt32, UInt32)), String, Array(UInt32))))" -q "select * from table" + From 3fb1a10a169964cdd7804f8eb595c7b5487d8b39 Mon Sep 17 00:00:00 2001 From: Kruglov Pavel <48961922+Avogar@users.noreply.github.com> Date: Tue, 28 Mar 2023 20:49:10 +0200 Subject: [PATCH 135/377] Fix style --- src/Processors/Formats/Impl/AvroRowInputFormat.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Processors/Formats/Impl/AvroRowInputFormat.cpp b/src/Processors/Formats/Impl/AvroRowInputFormat.cpp index 7b978de1a4c..c2602a4d1d5 100644 --- a/src/Processors/Formats/Impl/AvroRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/AvroRowInputFormat.cpp @@ -40,8 +40,6 @@ #include #include -#include - #include #include #include @@ -210,7 +208,7 @@ static std::string nodeName(avro::NodePtr node) return avro::toString(node->type()); } -static bool canBeDeserializedFromFixed( const DataTypePtr & target_type, size_t fixed_size) +static bool canBeDeserializedFromFixed(const DataTypePtr & target_type, size_t fixed_size) { switch (target_type->getTypeId()) { From d33ead5f4f3744b8bad097fd3f46ca7aad9cedee Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 12:58:57 -0600 Subject: [PATCH 136/377] Update amazon-reviews.md --- .../example-datasets/amazon-reviews.md | 91 +++++++++++++++++-- 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md index 7ce554f625c..17aa7d38442 100644 --- a/docs/en/getting-started/example-datasets/amazon-reviews.md +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -8,24 +8,58 @@ description: **Amazon Customer Reviews** (a.k.a. Product Reviews) is one of Amazon’s iconic products. In a period of over two decades since the first review in 1995, millions of Amazon customers have contributed over a hundred million reviews to express opinions and describe their experiences regarding products on the Amazon.com website. This makes Amazon Customer Reviews a rich source of information for academic researchers in the fields of Natural Language Processing (NLP), Information Retrieval (IR), and Machine Learning (ML), amongst others. -The data is in a JSON format, and the files are up in AWS S3. Let's walk through the steps to insert it into ClickHouse. +The data is in a tab-separated format in gzipped files are up in AWS S3. Let's walk through the steps to insert it into ClickHouse. :::note The queries below were executed on a **Production** instance of [ClickHouse Cloud](https://clickhouse.cloud). ::: -1. Let's check out the format of the data in the files. Notice the `s3Cluster` table function returns a table where the data types are inferred - helpful when you are researching a new dataset: + +1. Without inserting the data into ClickHouse, we can query it in place. Let's grab some rows so we can see what they look like: ```sql - +SELECT * FROM s3('https://s3.amazonaws.com/amazon-reviews-pds/tsv/amazon_reviews_us_Wireless_v1_00.tsv.gz', + 'TabSeparatedWithNames', + 'marketplace String, + customer_id Int64, + review_id String, + product_id String, + product_parent Int64, + product_title String, + product_category String, + star_rating Int64, + helpful_votes Int64, + total_votes Int64, + vine Bool, + verified_purchase Bool, + review_headline String, + review_body String, + review_date Date') + LIMIT 10; ``` +The rows look like: ```response - +┌─marketplace─┬─customer_id─┬─review_id──────┬─product_id─┬─product_parent─┬─product_title──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─product_category─┬─star_rating─┬─helpful_votes─┬─total_votes─┬─vine──┬─verified_purchase─┬─review_headline───────────┬─review_body────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─review_date─┐ +│ US │ 16414143 │ R3W4P9UBGNGH1U │ B00YL0EKWE │ 852431543 │ LG G4 Case Hard Transparent Slim Clear Cover for LG G4 │ Wireless │ 2 │ 1 │ 3 │ false │ true │ Looks good, functions meh │ 2 issues - Once I turned on the circle apps and installed this case, my battery drained twice as fast as usual. I ended up turning off the circle apps, which kind of makes the case just a case... with a hole in it. Second, the wireless charging doesn't work. I have a Motorola 360 watch and a Qi charging pad. The watch charges fine but this case doesn't. But hey, it looks nice. │ 2015-08-31 │ +│ US │ 50800750 │ R15V54KBMTQWAY │ B00XK95RPQ │ 516894650 │ Selfie Stick Fiblastiq™ Extendable Wireless Bluetooth Selfie Stick with built-in Bluetooth Adjustable Phone Holder │ Wireless │ 4 │ 0 │ 0 │ false │ false │ A fun little gadget │ I’m embarrassed to admit that until recently, I have had a very negative opinion about “selfie sticks” aka “monopods” aka “narcissticks.” But having reviewed a number of them recently, they’re growing on me. This one is pretty nice and simple to set up and with easy instructions illustrated on the back of the box (not sure why some reviewers have stated that there are no instructions when they are clearly printed on the box unless they received different packaging than I did). Once assembled, the pairing via bluetooth and use of the stick are easy and intuitive. Nothing to it.

The stick comes with a USB charging cable but arrived with a charge so you can use it immediately, though it’s probably a good idea to charge it right away so that you have no interruption of use out of the box. Make sure the stick is switched to on (it will light up) and extend your stick to the length you desire up to about a yard’s length and snap away.

The phone clamp held the phone sturdily so I wasn’t worried about it slipping out. But the longer you extend the stick, the harder it is to maneuver. But that will happen with any stick and is not specific to this one in particular.

Two things that could improve this: 1) add the option to clamp this in portrait orientation instead of having to try and hold the stick at the portrait angle, which makes it feel unstable; 2) add the opening for a tripod so that this can be used to sit upright on a table for skyping and facetime eliminating the need to hold the phone up with your hand, causing fatigue.

But other than that, this is a nice quality monopod for a variety of picture taking opportunities.

I received a sample in exchange for my honest opinion. │ 2015-08-31 │ +│ US │ 15184378 │ RY8I449HNXSVF │ B00SXRXUKO │ 984297154 │ Tribe AB40 Water Resistant Sports Armband with Key Holder for 4.7-Inch iPhone 6S/6/5/5S/5C, Galaxy S4 + Screen Protector - Dark Pink │ Wireless │ 5 │ 0 │ 0 │ false │ true │ Five Stars │ Fits iPhone 6 well │ 2015-08-31 │ +│ US │ 10203548 │ R18TLJYCKJFLSR │ B009V5X1CE │ 279912704 │ RAVPower® Element 10400mAh External Battery USB Portable Charger (Dual USB Outputs, Ultra Compact Design), Travel Charger for iPhone 6,iPhone 6 plus,iPhone 5, 5S, 5C, 4S, 4, iPad Air, 4, 3, 2, Mini 2 (Apple adapters not included); Samsung Galaxy S5, S4, S3, S2, Note 3, Note 2; HTC One, EVO, Thunderbolt, Incredible, Droid DNA, Motorola ATRIX, Droid, Moto X, Google Glass, Nexus 4, Nexus 5, Nexus 7, │ Wireless │ 5 │ 0 │ 0 │ false │ true │ Great charger │ Great charger. I easily get 3+ charges on a Samsung Galaxy 3. Works perfectly for camping trips or long days on the boat. │ 2015-08-31 │ +│ US │ 488280 │ R1NK26SWS53B8Q │ B00D93OVF0 │ 662791300 │ Fosmon Micro USB Value Pack Bundle for Samsung Galaxy Exhilarate - Includes Home / Travel Charger, Car / Vehicle Charger and USB Cable │ Wireless │ 5 │ 0 │ 0 │ false │ true │ Five Stars │ Great for the price :-) │ 2015-08-31 │ +│ US │ 13334021 │ R11LOHEDYJALTN │ B00XVGJMDQ │ 421688488 │ iPhone 6 Case, Vofolen Impact Resistant Protective Shell iPhone 6S Wallet Cover Shockproof Rubber Bumper Case Anti-scratches Hard Cover Skin Card Slot Holder for iPhone 6 6S │ Wireless │ 5 │ 0 │ 0 │ false │ true │ Five Stars │ Great Case, better customer service! │ 2015-08-31 │ +│ US │ 27520697 │ R3ALQVQB2P9LA7 │ B00KQW1X1C │ 554285554 │ Nokia Lumia 630 RM-978 White Factory Unlocked - International Version No Warranty │ Wireless │ 4 │ 0 │ 0 │ false │ true │ Four Stars │ Easy to set up and use. Great functions for the price │ 2015-08-31 │ +│ US │ 48086021 │ R3MWLXLNO21PDQ │ B00IP1MQNK │ 488006702 │ Lumsing 10400mah external battery │ Wireless │ 5 │ 0 │ 0 │ false │ true │ Five Stars │ Works great │ 2015-08-31 │ +│ US │ 12738196 │ R2L15IS24CX0LI │ B00HVORET8 │ 389677711 │ iPhone 5S Battery Case - iPhone 5 Battery Case , Maxboost Atomic S [MFI Certified] External Protective Battery Charging Case Power Bank Charger All Versions of Apple iPhone 5/5S [Juice Battery Pack] │ Wireless │ 5 │ 0 │ 0 │ false │ true │ So far so good │ So far so good. It is essentially identical to the one it replaced from another company. That one stopped working after 7 months so I am a bit apprehensive about this one. │ 2015-08-31 │ +│ US │ 15867807 │ R1DJ8976WPWVZU │ B00HX3G6J6 │ 299654876 │ HTC One M8 Screen Protector, Skinomi TechSkin Full Coverage Screen Protector for HTC One M8 Clear HD Anti-Bubble Film │ Wireless │ 3 │ 0 │ 0 │ false │ true │ seems durable but these are always harder to get on ... │ seems durable but these are always harder to get on right than people make them out to be. also send to curl up at the edges after a while. with today's smartphones, you hardly need screen protectors anyway. │ 2015-08-31 │ +└─────────────┴─────────────┴────────────────┴────────────┴────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────┴─────────────┴───────────────┴─────────────┴───────┴───────────────────┴─────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴─────────────┘ ``` -2. Let's define a new table named `amazon_reviews`. We'll optimize some of the inferred column data types - and choose a primary key (the `ORDER BY` clause): +:::note +Normally you would not need to pass in the schema into the `s3` table function - ClickHouse can infer the names and data types of the columns. However, this particular dataset uses a non-standard tab-separated format, but the `s3` function seems to work fine with this non-standard format if you include the schema. +::: + +2. Let's define a new table named `amazon_reviews`. We'll optimize some of the column data types - and choose a primary key (the `ORDER BY` clause): ```sql CREATE TABLE amazon_reviews @@ -41,8 +75,8 @@ CREATE TABLE amazon_reviews star_rating UInt8, helpful_votes UInt32, total_votes UInt32, - vine UInt8, - verified_purchase UInt8, + vine FixedString(1), + verified_purchase FixedString(1), review_headline String, review_body String ) @@ -50,12 +84,51 @@ ENGINE = MergeTree ORDER BY (marketplace, review_date, product_category); ``` -3. The following `INSERT` query inserts all of the files in our S3 bucket into `amazon_reviews`. If you do not want all of the data, simply add a `LIMIT` clause. +3. We are now ready to insert the data into ClickHouse. Before we do, check out the [list of files in the dataset](https://s3.amazonaws.com/amazon-reviews-pds/tsv/index.txt) and decide which ones you want to include. + +4. We will insert all of the US reviews - which is about 151M rows. The following `INSERT` command uses the `s3Cluster` table function, which allows the processing of mulitple S3 files in parallel using all the nodes of your cluster. We also use a wildcard to insert any file that starts with the name `https://s3.amazonaws.com/amazon-reviews-pds/tsv/amazon_reviews_us_`: + +```sql +INSERT INTO amazon_reviews +SELECT + * REPLACE(vine = 'Y' AS vine, verified_purchase = 'Y' AS verified_purchase) +FROM s3Cluster( + 'default', + 'https://s3.amazonaws.com/amazon-reviews-pds/tsv/amazon_reviews_us_*.tsv.gz', + 'TSVWithNames', + 'review_date Date, + marketplace LowCardinality(String), + customer_id UInt64, + review_id String, + product_id String, + product_parent UInt64, + product_title String, + product_category LowCardinality(String), + star_rating UInt8, + helpful_votes UInt32, + total_votes UInt32, + vine FixedString(1), + verified_purchase FixedString(1), + review_headline String, + review_body String' + ); +``` :::tip In ClickHouse Cloud, there is a cluster named `default`. Change `default` to the name of your cluster...or use the `s3` table function (instead of `s3Cluster`) if you do not have a cluster. ::: -```sql +5. That query doesn't take long - within 5 minutes or so you should see all the rows inserted: +```sql +SELECT formatReadableQuantity(count()) +FROM amazon_reviews ``` + +```response +┌─formatReadableQuantity(count())─┐ +│ 150.96 million │ +└─────────────────────────────────┘ +``` + +6. Let's run some queries... From 0f241f71a3b761fa2d85bd821c9ff4d8b2ca050e Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 28 Mar 2023 20:03:55 +0000 Subject: [PATCH 137/377] Fix fasttest --- tests/queries/0_stateless/25402_show_columns.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/25402_show_columns.sql b/tests/queries/0_stateless/25402_show_columns.sql index baafecd7f68..6cf2a599f2c 100644 --- a/tests/queries/0_stateless/25402_show_columns.sql +++ b/tests/queries/0_stateless/25402_show_columns.sql @@ -53,7 +53,7 @@ SELECT '---'; -- Create a table in a different database. Intentionally useing the same table/column names as above so -- we notice if something is buggy in the implementation of SHOW COLUMNS. -DROP DATABASE database_123456789abcde; +DROP DATABASE IF EXISTS database_123456789abcde; CREATE DATABASE database_123456789abcde; -- pseudo-random database name CREATE OR REPLACE TABLE database_123456789abcde.tab @@ -75,6 +75,6 @@ SELECT '---'; SHOW COLUMNS FROM database_123456789abcde.tab; -DROP DATABASE database_123456789abc; +DROP DATABASE database_123456789abcde; DROP TABLE tab; From e743f840f02b5d0a210ab95de3fe2f7e0f225100 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 28 Mar 2023 20:05:17 +0000 Subject: [PATCH 138/377] Fix taipos --- src/Interpreters/InterpreterShowColumnsQuery.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Interpreters/InterpreterShowColumnsQuery.cpp b/src/Interpreters/InterpreterShowColumnsQuery.cpp index 95530ad7c53..c5392ccdb15 100644 --- a/src/Interpreters/InterpreterShowColumnsQuery.cpp +++ b/src/Interpreters/InterpreterShowColumnsQuery.cpp @@ -53,11 +53,11 @@ String InterpreterShowColumnsQuery::getRewrittenQuery() { /// FROM .
(abbreviated form) chassert(query.from_database.empty()); - std::vector splitted; - boost::split(splitted, query.from_table, boost::is_any_of(".")); - chassert(splitted.size() == 2); - database = splitted[0]; - table = splitted[1]; + std::vector split; + boost::split(split, query.from_table, boost::is_any_of(".")); + chassert(split.size() == 2); + database = split[0]; + table = split[1]; } else if (query.from_database.empty()) { From 6c84eabb79f0813be0c333ecb0effaa89f3600c7 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 28 Mar 2023 20:07:26 +0000 Subject: [PATCH 139/377] Fix stylecheck --- src/Interpreters/InterpreterShowColumnsQuery.cpp | 5 ----- src/Parsers/ASTShowColumnsQuery.cpp | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Interpreters/InterpreterShowColumnsQuery.cpp b/src/Interpreters/InterpreterShowColumnsQuery.cpp index c5392ccdb15..4474be21d8b 100644 --- a/src/Interpreters/InterpreterShowColumnsQuery.cpp +++ b/src/Interpreters/InterpreterShowColumnsQuery.cpp @@ -12,11 +12,6 @@ namespace DB { -namespace ErrorCodes -{ - extern const int SYNTAX_ERROR; -} - InterpreterShowColumnsQuery::InterpreterShowColumnsQuery(const ASTPtr & query_ptr_, ContextMutablePtr context_) : WithMutableContext(context_) diff --git a/src/Parsers/ASTShowColumnsQuery.cpp b/src/Parsers/ASTShowColumnsQuery.cpp index 113029aacd7..2b3e1a58c71 100644 --- a/src/Parsers/ASTShowColumnsQuery.cpp +++ b/src/Parsers/ASTShowColumnsQuery.cpp @@ -32,7 +32,7 @@ void ASTShowColumnsQuery::formatQueryImpl(const FormatSettings & settings, Forma if (!like.empty()) settings.ostr << (settings.hilite ? hilite_keyword : "") - << (not_like ? " NOT " : "") + << (not_like ? " NOT " : "") << (case_insensitive_like ? " ILIKE " : " LIKE" ) << (settings.hilite ? hilite_none : "") << DB::quote << like; From 6733a85d25cb6a0ab1278a733918b859b86e9b86 Mon Sep 17 00:00:00 2001 From: Igor Nikonov Date: Tue, 28 Mar 2023 20:24:21 +0000 Subject: [PATCH 140/377] Fixes --- src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp | 1 - src/AggregateFunctions/AggregateFunctionStatisticsSimple.h | 5 ----- src/DataTypes/getLeastSupertype.cpp | 4 ++-- src/Functions/array/arrayIntersect.cpp | 3 +-- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp b/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp index 14ba4d27cec..e2acccce516 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp +++ b/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.cpp @@ -21,7 +21,6 @@ namespace ErrorCodes namespace { -/// TODO Proper support for Decimal256. template struct MovingSum { diff --git a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h index a0bf423cb0a..f9a60c0c0b4 100644 --- a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h +++ b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h @@ -33,11 +33,6 @@ namespace DB { -namespace ErrorCodes -{ - extern const int LOGICAL_ERROR; -} - struct Settings; enum class StatisticsFunctionKind diff --git a/src/DataTypes/getLeastSupertype.cpp b/src/DataTypes/getLeastSupertype.cpp index 9f9d7fea428..3c33289c304 100644 --- a/src/DataTypes/getLeastSupertype.cpp +++ b/src/DataTypes/getLeastSupertype.cpp @@ -532,8 +532,8 @@ DataTypePtr getLeastSupertype(const DataTypes & types) { size_t num_supported = have_decimal32 + have_decimal64 + have_decimal128 + have_decimal256; - std::vector int_ids = {TypeIndex::Int8, TypeIndex::UInt8, TypeIndex::Int16, TypeIndex::UInt16, - TypeIndex::Int32, TypeIndex::UInt32, TypeIndex::Int64, TypeIndex::UInt64}; + std::array int_ids = {TypeIndex::Int8, TypeIndex::UInt8, TypeIndex::Int16, TypeIndex::UInt16, + TypeIndex::Int32, TypeIndex::UInt32, TypeIndex::Int64, TypeIndex::UInt64}; TypeIndex max_int = TypeIndex::Nothing; for (auto int_id : int_ids) diff --git a/src/Functions/array/arrayIntersect.cpp b/src/Functions/array/arrayIntersect.cpp index 61fde13cff9..d1bbd169513 100644 --- a/src/Functions/array/arrayIntersect.cpp +++ b/src/Functions/array/arrayIntersect.cpp @@ -344,8 +344,7 @@ FunctionArrayIntersect::UnpackedArrays FunctionArrayIntersect::prepareArrays( if (isInteger(nested_init_type) || isDate(nested_init_type) || isDateTime(nested_init_type) - || isDateTime64(nested_init_type) - || isDecimal(nested_init_type)) + || isDateTime64(nested_init_type)) { /// Compare original and casted columns. It seem to be the easiest way. auto overflow_mask = callFunctionNotEquals( From 6f8a450805f3e84611fe53f0623b00264db81834 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 28 Mar 2023 20:55:28 +0000 Subject: [PATCH 141/377] Sort output of SHOW statements Sorting is strictly speaking not necessary, but 1. sorted output is more user-friendly, 2. SQL currently does not allow to sort the output of SHOW otherwise, e.g. SELECT * FROM (SHOW ...) ORDER BY is rejected. 3. SQL tests can take advantage of that. --- .../InterpreterShowEngineQuery.cpp | 2 +- .../InterpreterShowTablesQuery.cpp | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Interpreters/InterpreterShowEngineQuery.cpp b/src/Interpreters/InterpreterShowEngineQuery.cpp index 5aae6ad5d28..8fd829f39ec 100644 --- a/src/Interpreters/InterpreterShowEngineQuery.cpp +++ b/src/Interpreters/InterpreterShowEngineQuery.cpp @@ -12,7 +12,7 @@ namespace DB BlockIO InterpreterShowEnginesQuery::execute() { - return executeQuery("SELECT * FROM system.table_engines", getContext(), true); + return executeQuery("SELECT * FROM system.table_engines ORDER BY name", getContext(), true); } } diff --git a/src/Interpreters/InterpreterShowTablesQuery.cpp b/src/Interpreters/InterpreterShowTablesQuery.cpp index 4e0dfdc9236..bd29a933bcf 100644 --- a/src/Interpreters/InterpreterShowTablesQuery.cpp +++ b/src/Interpreters/InterpreterShowTablesQuery.cpp @@ -28,7 +28,6 @@ InterpreterShowTablesQuery::InterpreterShowTablesQuery(const ASTPtr & query_ptr_ { } - String InterpreterShowTablesQuery::getRewrittenQuery() { const auto & query = query_ptr->as(); @@ -51,6 +50,9 @@ String InterpreterShowTablesQuery::getRewrittenQuery() if (query.limit_length) rewritten_query << " LIMIT " << query.limit_length; + /// (*) + rewritten_query << " ORDER BY name"; + return rewritten_query.str(); } @@ -72,6 +74,9 @@ String InterpreterShowTablesQuery::getRewrittenQuery() if (query.limit_length) rewritten_query << " LIMIT " << query.limit_length; + /// (*) + rewritten_query << " ORDER BY cluster"; + return rewritten_query.str(); } else if (query.cluster) @@ -81,6 +86,9 @@ String InterpreterShowTablesQuery::getRewrittenQuery() rewritten_query << " WHERE cluster = " << DB::quote << query.cluster_str; + /// (*) + rewritten_query << " ORDER BY cluster"; + return rewritten_query.str(); } @@ -101,6 +109,9 @@ String InterpreterShowTablesQuery::getRewrittenQuery() << DB::quote << query.like; } + /// (*) + rewritten_query << " ORDER BY name, type, value "; + return rewritten_query.str(); } @@ -146,6 +157,9 @@ String InterpreterShowTablesQuery::getRewrittenQuery() else if (query.where_expression) rewritten_query << " AND (" << query.where_expression << ")"; + /// (*) + rewritten_query << " ORDER BY name "; + if (query.limit_length) rewritten_query << " LIMIT " << query.limit_length; @@ -176,5 +190,8 @@ BlockIO InterpreterShowTablesQuery::execute() return executeQuery(getRewrittenQuery(), getContext(), true); } +/// (*) Sorting is strictly speaking not necessary but 1. it is convenient for users, 2. SQL currently does not allow to +/// sort the output of SHOW otherwise (SELECT * FROM (SHOW ...) ORDER BY ...) is rejected) and 3. some +/// SQL tests can take advantage of this. } From 12559236eeb3de493ddda1da035064aa9591e158 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 28 Mar 2023 21:01:17 +0000 Subject: [PATCH 142/377] Another style fix --- src/Parsers/ASTShowColumnsQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parsers/ASTShowColumnsQuery.cpp b/src/Parsers/ASTShowColumnsQuery.cpp index 2b3e1a58c71..d14cbdc9b84 100644 --- a/src/Parsers/ASTShowColumnsQuery.cpp +++ b/src/Parsers/ASTShowColumnsQuery.cpp @@ -33,7 +33,7 @@ void ASTShowColumnsQuery::formatQueryImpl(const FormatSettings & settings, Forma if (!like.empty()) settings.ostr << (settings.hilite ? hilite_keyword : "") << (not_like ? " NOT " : "") - << (case_insensitive_like ? " ILIKE " : " LIKE" ) + << (case_insensitive_like ? " ILIKE " : " LIKE") << (settings.hilite ? hilite_none : "") << DB::quote << like; From 44d0a8075de482c72c76d3624e43882bb5408127 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 28 Mar 2023 20:38:43 +0000 Subject: [PATCH 143/377] Small follow-up to #46252 --- .../mergetree-family/mergetree.md | 50 +++++++++---------- .../00990_hasToken_and_tokenbf.sql | 13 ++--- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/docs/en/engines/table-engines/mergetree-family/mergetree.md b/docs/en/engines/table-engines/mergetree-family/mergetree.md index 4f3c9c0cb69..1c16ffe9db5 100644 --- a/docs/en/engines/table-engines/mergetree-family/mergetree.md +++ b/docs/en/engines/table-engines/mergetree-family/mergetree.md @@ -458,34 +458,32 @@ Indexes of type `set` can be utilized by all functions. The other index types ar | Function (operator) / Index | primary key | minmax | ngrambf_v1 | tokenbf_v1 | bloom_filter | inverted | |------------------------------------------------------------------------------------------------------------|-------------|--------|------------|------------|--------------|----------| -| [equals (=, ==)](/docs/en/sql-reference/functions/comparison-functions.md/#function-equals) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| [notEquals(!=, <>)](/docs/en/sql-reference/functions/comparison-functions.md/#function-notequals) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| [like](/docs/en/sql-reference/functions/string-search-functions.md/#function-like) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | -| [notLike](/docs/en/sql-reference/functions/string-search-functions.md/#function-notlike) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | -| [startsWith](/docs/en/sql-reference/functions/string-functions.md/#startswith) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | -| [endsWith](/docs/en/sql-reference/functions/string-functions.md/#endswith) | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | -| [multiSearchAny](/docs/en/sql-reference/functions/string-search-functions.md/#function-multisearchany) | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | -| [in](/docs/en/sql-reference/functions/in-functions#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| [notIn](/docs/en/sql-reference/functions/in-functions#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| [less (<)](/docs/en/sql-reference/functions/comparison-functions.md/#function-less) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | -| [greater (>)](/docs/en/sql-reference/functions/comparison-functions.md/#function-greater) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | -| [lessOrEquals (<=)](/docs/en/sql-reference/functions/comparison-functions.md/#function-lessorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | -| [greaterOrEquals (>=)](/docs/en/sql-reference/functions/comparison-functions.md/#function-greaterorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | -| [empty](/docs/en/sql-reference/functions/array-functions#function-empty) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | -| [notEmpty](/docs/en/sql-reference/functions/array-functions#function-notempty) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | -| [has](/docs/en/sql-reference/functions/array-functions#function-has) | ✗ | ✗ | ✔ | ✔ | ✔ | ✔ | -| [hasAny](/docs/en/sql-reference/functions/array-functions#function-hasAny) | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | -| [hasAll](/docs/en/sql-reference/functions/array-functions#function-hasAll) | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | -| hasToken | ✗ | ✗ | ✗ | ✔ | ✗ | ✔ | -| hasTokenOrNull | ✗ | ✗ | ✗ | ✔ | ✗ | ✔ | -| hasTokenCaseInsensitive (*) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | -| hasTokenCaseInsensitiveOrNull (*) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | +| [equals (=, ==)](/docs/en/sql-reference/functions/comparison-functions.md/#function-equals) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| [notEquals(!=, <>)](/docs/en/sql-reference/functions/comparison-functions.md/#function-notequals) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| [like](/docs/en/sql-reference/functions/string-search-functions.md/#function-like) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | +| [notLike](/docs/en/sql-reference/functions/string-search-functions.md/#function-notlike) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | +| [startsWith](/docs/en/sql-reference/functions/string-functions.md/#startswith) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | +| [endsWith](/docs/en/sql-reference/functions/string-functions.md/#endswith) | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | +| [multiSearchAny](/docs/en/sql-reference/functions/string-search-functions.md/#function-multisearchany) | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | +| [in](/docs/en/sql-reference/functions/in-functions#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| [notIn](/docs/en/sql-reference/functions/in-functions#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| [less (<)](/docs/en/sql-reference/functions/comparison-functions.md/#function-less) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [greater (>)](/docs/en/sql-reference/functions/comparison-functions.md/#function-greater) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [lessOrEquals (<=)](/docs/en/sql-reference/functions/comparison-functions.md/#function-lessorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [greaterOrEquals (>=)](/docs/en/sql-reference/functions/comparison-functions.md/#function-greaterorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [empty](/docs/en/sql-reference/functions/array-functions#function-empty) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [notEmpty](/docs/en/sql-reference/functions/array-functions#function-notempty) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | +| [has](/docs/en/sql-reference/functions/array-functions#function-has) | ✗ | ✗ | ✔ | ✔ | ✔ | ✔ | +| [hasAny](/docs/en/sql-reference/functions/array-functions#function-hasAny) | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | +| [hasAll](/docs/en/sql-reference/functions/array-functions#function-hasAll) | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | +| hasToken | ✗ | ✗ | ✗ | ✔ | ✗ | ✔ | +| hasTokenOrNull | ✗ | ✗ | ✗ | ✔ | ✗ | ✔ | +| hasTokenCaseInsensitive (*) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | +| hasTokenCaseInsensitiveOrNull (*) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | Functions with a constant argument that is less than ngram size can’t be used by `ngrambf_v1` for query optimization. -(*) For `hasTokenCaseInsensitve` and `hasTokenCaseInsensitive` to be effective, the data skipping index of type `tokenbf_v1` must be created on lowercased data, for example: -``` -CREATE TABLE tab (id UInt64, s String, INDEX tok_bf_idx (s, lower(s)) TYPE tokenbf_v1(512, 3, 0) GRANULARITY 1) ... . ) ENGINE = MergeTree() -``` + +(*) For `hasTokenCaseInsensitve` and `hasTokenCaseInsensitive` to be effective, the `tokenbf_v1` index must be created on lowercased data, for example `INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0)`. :::note Bloom filters can have false positive matches, so the `ngrambf_v1`, `tokenbf_v1`, and `bloom_filter` indexes can not be used for optimizing queries where the result of a function is expected to be false. diff --git a/tests/queries/0_stateless/00990_hasToken_and_tokenbf.sql b/tests/queries/0_stateless/00990_hasToken_and_tokenbf.sql index 1361ce0f3c3..8e88af40046 100644 --- a/tests/queries/0_stateless/00990_hasToken_and_tokenbf.sql +++ b/tests/queries/0_stateless/00990_hasToken_and_tokenbf.sql @@ -12,6 +12,13 @@ insert into bloom_filter select number+2000, 'abc,def,zzz' from numbers(8); insert into bloom_filter select number+3000, 'yyy,uuu' from numbers(1024); insert into bloom_filter select number+3000, 'abcdefzzz' from numbers(1024); +SELECT max(id) FROM bloom_filter WHERE hasToken(s, 'abc,def,zzz'); -- { serverError BAD_ARGUMENTS } +SELECT max(id) FROM bloom_filter WHERE hasTokenCaseInsensitive(s, 'abc,def,zzz'); -- { serverError BAD_ARGUMENTS } + +SELECT max(id) FROM bloom_filter WHERE hasTokenOrNull(s, 'abc,def,zzz'); +SELECT max(id) FROM bloom_filter WHERE hasTokenCaseInsensitiveOrNull(s, 'abc,def,zzz'); + +-- as table "bloom_filter" but w/o index_granularity_bytes drop table if exists bloom_filter2; create table bloom_filter2 ( @@ -25,12 +32,6 @@ insert into bloom_filter2 select number+2000, 'ABC,def,zzz' from numbers(8); insert into bloom_filter2 select number+3000, 'yyy,uuu' from numbers(1024); insert into bloom_filter2 select number+3000, 'abcdefzzz' from numbers(1024); -SELECT max(id) FROM bloom_filter WHERE hasToken(s, 'abc,def,zzz'); -- { serverError BAD_ARGUMENTS } -SELECT max(id) FROM bloom_filter WHERE hasTokenCaseInsensitive(s, 'abc,def,zzz'); -- { serverError BAD_ARGUMENTS } - -SELECT max(id) FROM bloom_filter WHERE hasTokenOrNull(s, 'abc,def,zzz'); -SELECT max(id) FROM bloom_filter WHERE hasTokenCaseInsensitiveOrNull(s, 'abc,def,zzz'); - set max_rows_to_read = 16; SELECT max(id) FROM bloom_filter WHERE hasToken(s, 'abc'); From 3d0b94e21029a62fcb8f8478806ecfe13b2b46c4 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 15:26:34 -0600 Subject: [PATCH 144/377] Update amazon-reviews.md --- .../example-datasets/amazon-reviews.md | 278 ++++++++++++++++-- 1 file changed, 258 insertions(+), 20 deletions(-) diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md index 17aa7d38442..05a295f314d 100644 --- a/docs/en/getting-started/example-datasets/amazon-reviews.md +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -1,7 +1,6 @@ --- slug: /en/getting-started/example-datasets/amazon-reviews sidebar_label: Amazon customer reviews -description: --- # Amazon customer reviews dataset @@ -18,24 +17,26 @@ The queries below were executed on a **Production** instance of [ClickHouse Clou 1. Without inserting the data into ClickHouse, we can query it in place. Let's grab some rows so we can see what they look like: ```sql -SELECT * FROM s3('https://s3.amazonaws.com/amazon-reviews-pds/tsv/amazon_reviews_us_Wireless_v1_00.tsv.gz', - 'TabSeparatedWithNames', - 'marketplace String, - customer_id Int64, - review_id String, - product_id String, - product_parent Int64, - product_title String, - product_category String, - star_rating Int64, - helpful_votes Int64, - total_votes Int64, - vine Bool, - verified_purchase Bool, - review_headline String, - review_body String, - review_date Date') - LIMIT 10; +SELECT * +FROM s3('https://s3.amazonaws.com/amazon-reviews-pds/tsv/amazon_reviews_us_Wireless_v1_00.tsv.gz', + 'TabSeparatedWithNames', + 'marketplace String, + customer_id Int64, + review_id String, + product_id String, + product_parent Int64, + product_title String, + product_category String, + star_rating Int64, + helpful_votes Int64, + total_votes Int64, + vine Bool, + verified_purchase Bool, + review_headline String, + review_body String, + review_date Date' +) +LIMIT 10; ``` The rows look like: @@ -131,4 +132,241 @@ FROM amazon_reviews └─────────────────────────────────┘ ``` -6. Let's run some queries... +6. Let's see how much space our data is using: + +```sql +SELECT + disk_name, + formatReadableSize(sum(data_compressed_bytes) AS size) AS compressed, + formatReadableSize(sum(data_uncompressed_bytes) AS usize) AS uncompressed, + round(usize / size, 2) AS compr_rate, + sum(rows) AS rows, + count() AS part_count +FROM system.parts +WHERE (active = 1) AND (table = 'amazon_reviews') +GROUP BY disk_name +ORDER BY size DESC; +``` +The original data was about 70G, but compressed in ClickHouse it takes up about 30G: + +```response +┌─disk_name─┬─compressed─┬─uncompressed─┬─compr_rate─┬──────rows─┬─part_count─┐ +│ s3disk │ 30.00 GiB │ 70.61 GiB │ 2.35 │ 150957260 │ 9 │ +└───────────┴────────────┴──────────────┴────────────┴───────────┴────────────┘ +``` + +7. Let's run some queries...here are the top 10 most-helpful reviews on Amazon: + +```sql +SELECT + product_title, + review_headline +FROM amazon_reviews +ORDER BY helpful_votes DESC +LIMIT 10; +``` + +Notice the query has to process all 151M rows, and it takes about 17 seconds: + +```response +┌─product_title────────────────────────────────────────────────────────────────────────────┬─review_headline───────────────────────────────────────────────────────┐ +│ Kindle: Amazon's Original Wireless Reading Device (1st generation) │ Why and how the Kindle changes everything │ +│ BIC Cristal For Her Ball Pen, 1.0mm, Black, 16ct (MSLP16-Blk) │ FINALLY! │ +│ The Mountain Kids 100% Cotton Three Wolf Moon T-Shirt │ Dual Function Design │ +│ Kindle Keyboard 3G, Free 3G + Wi-Fi, 6" E Ink Display │ Kindle vs. Nook (updated) │ +│ Kindle Fire HD 7", Dolby Audio, Dual-Band Wi-Fi │ You Get What You Pay For │ +│ Kindle Fire (Previous Generation - 1st) │ A great device WHEN you consider price and function, with a few flaws │ +│ Fifty Shades of Grey: Book One of the Fifty Shades Trilogy (Fifty Shades of Grey Series) │ Did a teenager write this??? │ +│ Wheelmate Laptop Steering Wheel Desk │ Perfect for an Starfleet Helmsman │ +│ Kindle Wireless Reading Device (6" Display, U.S. Wireless) │ BEWARE of the SIGNIFICANT DIFFERENCES between Kindle 1 and Kindle 2! │ +│ Tuscan Dairy Whole Vitamin D Milk, Gallon, 128 oz │ Make this your only stock and store │ +└──────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────┘ + +10 rows in set. Elapsed: 17.595 sec. Processed 150.96 million rows, 15.36 GB (8.58 million rows/s., 872.89 MB/s.) +``` + +8. Here are the top 10 products in Amazon with the most reviews: + +```sql +SELECT + any(product_title), + count() +FROM amazon_reviews +GROUP BY product_id +ORDER BY 2 DESC +LIMIT 10; +``` + +```response +┌─any(product_title)────────────────────────────┬─count()─┐ +│ Candy Crush Saga │ 50051 │ +│ The Secret Society® - Hidden Mystery │ 41255 │ +│ Google Chromecast HDMI Streaming Media Player │ 35977 │ +│ Minecraft │ 35129 │ +│ Bosch Season 1 │ 33610 │ +│ Gone Girl: A Novel │ 33240 │ +│ Subway Surfers │ 32328 │ +│ The Fault in Our Stars │ 30149 │ +│ Amazon.com eGift Cards │ 28879 │ +│ Crossy Road │ 28111 │ +└───────────────────────────────────────────────┴─────────┘ + +10 rows in set. Elapsed: 16.684 sec. Processed 195.05 million rows, 20.86 GB (11.69 million rows/s., 1.25 GB/s.) +``` + +9. Here are the average review ratings per month for each product (an actual [Amazon job interview question](https://datalemur.com/questions/sql-avg-review-ratings)!): + +```sql +SELECT + toYYYYMM(review_date) AS month, + any(product_title), + avg(star_rating) AS avg_stars +FROM amazon_reviews +GROUP BY + month, + product_id +ORDER BY + month DESC, + product_id ASC +LIMIT 20; +``` + +It calculates all the monthly averages for each product, but we only returned 20 rows: + +```response +┌──month─┬─any(product_title)──────────────────────────────────────────────────────────────────────┬─avg_stars─┐ +│ 201508 │ Mystiqueshapes Girls Ballet Tutu Neon Lime Green │ 4 │ +│ 201508 │ Adult Ballet Tutu Yellow │ 5 │ +│ 201508 │ The Way Things Work: An Illustrated Encyclopedia of Technology │ 5 │ +│ 201508 │ Hilda Boswell's Treasury of Poetry │ 5 │ +│ 201508 │ Treasury of Poetry │ 5 │ +│ 201508 │ Uncle Remus Stories │ 5 │ +│ 201508 │ The Book of Daniel │ 5 │ +│ 201508 │ Berenstains' B Book │ 5 │ +│ 201508 │ The High Hills (Brambly Hedge) │ 4.5 │ +│ 201508 │ Fuzzypeg Goes to School (The Little Grey Rabbit library) │ 5 │ +│ 201508 │ Dictionary in French: The Cat in the Hat (Beginner Series) │ 5 │ +│ 201508 │ Windfallen │ 5 │ +│ 201508 │ The Monk Who Sold His Ferrari: A Remarkable Story About Living Your Dreams │ 5 │ +│ 201508 │ Illustrissimi: The Letters of Pope John Paul I │ 5 │ +│ 201508 │ Social Contract: A Personal Inquiry into the Evolutionary Sources of Order and Disorder │ 5 │ +│ 201508 │ Mexico The Beautiful Cookbook: Authentic Recipes from the Regions of Mexico │ 4.5 │ +│ 201508 │ Alanbrooke │ 5 │ +│ 201508 │ Back to Cape Horn │ 4 │ +│ 201508 │ Ovett: An Autobiography (Willow books) │ 5 │ +│ 201508 │ The Birds of West Africa (Collins Field Guides) │ 4 │ +└────────┴─────────────────────────────────────────────────────────────────────────────────────────┴───────────┘ + +20 rows in set. Elapsed: 55.529 sec. Processed 252.02 million rows, 35.58 GB (4.54 million rows/s., 640.79 MB/s.) +``` + +10. Here are the total number of votes per product category. This query is fast because `product_category` is in the primary key: + +```sql +SELECT + sum(total_votes), + product_category +FROM amazon_reviews +GROUP BY product_category +ORDER BY 1 DESC; +``` + +```response +┌─sum(total_votes)─┬─product_category─────────┐ +│ 103877874 │ Books │ +│ 25330411 │ Digital_Ebook_Purchase │ +│ 23065953 │ Video DVD │ +│ 18048069 │ Music │ +│ 17292294 │ Mobile_Apps │ +│ 15977124 │ Health & Personal Care │ +│ 13554090 │ PC │ +│ 13065746 │ Kitchen │ +│ 12537926 │ Home │ +│ 11067538 │ Beauty │ +│ 10418643 │ Wireless │ +│ 9089085 │ Toys │ +│ 9071484 │ Sports │ +│ 7335647 │ Electronics │ +│ 6885504 │ Apparel │ +│ 6710085 │ Video Games │ +│ 6556319 │ Camera │ +│ 6305478 │ Lawn and Garden │ +│ 5954422 │ Office Products │ +│ 5339437 │ Home Improvement │ +│ 5284343 │ Outdoors │ +│ 5125199 │ Pet Products │ +│ 4733251 │ Grocery │ +│ 4697750 │ Shoes │ +│ 4666487 │ Automotive │ +│ 4361518 │ Digital_Video_Download │ +│ 4033550 │ Tools │ +│ 3559010 │ Baby │ +│ 3317662 │ Home Entertainment │ +│ 2559501 │ Video │ +│ 2204328 │ Furniture │ +│ 2157587 │ Musical Instruments │ +│ 1881662 │ Software │ +│ 1676081 │ Jewelry │ +│ 1499945 │ Watches │ +│ 1224071 │ Digital_Music_Purchase │ +│ 847918 │ Luggage │ +│ 503939 │ Major Appliances │ +│ 392001 │ Digital_Video_Games │ +│ 348990 │ Personal_Care_Appliances │ +│ 321372 │ Digital_Software │ +│ 169585 │ Mobile_Electronics │ +│ 72970 │ Gift Card │ +└──────────────────┴──────────────────────────┘ + +43 rows in set. Elapsed: 0.423 sec. Processed 150.96 million rows, 756.20 MB (356.70 million rows/s., 1.79 GB/s.) +``` + +11. Let's find the products with the word **"awful"** occurring most frequently in the review. This is a big task - over 151M strings have to be parsed looking for a single word: + +```sql +SELECT + product_id, + any(product_title), + avg(star_rating), + count() AS count +FROM amazon_reviews +WHERE position(review_body, 'awful') > 0 +GROUP BY product_id +ORDER BY count DESC +LIMIT 20; +``` + +The query takes over minutes: + +```response +┌─product_id─┬─any(product_title)───────────────────────────────────────────────────────────────────────┬───avg(star_rating)─┬─count─┐ +│ 0345803485 │ Fifty Shades of Grey: Book One of the Fifty Shades Trilogy (Fifty Shades of Grey Series) │ 1.3870967741935485 │ 248 │ +│ B007J4T2G8 │ Fifty Shades of Grey (Fifty Shades, Book 1) │ 1.4439834024896265 │ 241 │ +│ B006LSZECO │ Gone Girl: A Novel │ 2.2986425339366514 │ 221 │ +│ B00008OWZG │ St. Anger │ 1.6565656565656566 │ 198 │ +│ B00BD99JMW │ Allegiant (Divergent Trilogy, Book 3) │ 1.8342541436464088 │ 181 │ +│ B0000YUXI0 │ Mavala Switzerland Mavala Stop Nail Biting │ 4.473684210526316 │ 171 │ +│ B004S8F7QM │ Cards Against Humanity │ 4.753012048192771 │ 166 │ +│ 031606792X │ Breaking Dawn (The Twilight Saga, Book 4) │ 1.796875 │ 128 │ +│ 006202406X │ Allegiant (Divergent Series) │ 1.4242424242424243 │ 99 │ +│ B0051VVOB2 │ Kindle Fire (Previous Generation - 1st) │ 2.7448979591836733 │ 98 │ +│ B00I3MP3SG │ Pilot │ 1.8762886597938144 │ 97 │ +│ 030758836X │ Gone Girl │ 2.15625 │ 96 │ +│ B0009X29WK │ Precious Cat Ultra Premium Clumping Cat Litter │ 3.0759493670886076 │ 79 │ +│ B00JB3MVCW │ Noah │ 1.2027027027027026 │ 74 │ +│ B00BAXFECK │ The Goldfinch: A Novel (Pulitzer Prize for Fiction) │ 2.643835616438356 │ 73 │ +│ B00N28818A │ Amazon Prime Video │ 1.4305555555555556 │ 72 │ +│ B007FTE2VW │ SimCity - Limited Edition │ 1.2794117647058822 │ 68 │ +│ 0439023513 │ Mockingjay (The Hunger Games) │ 2.6417910447761193 │ 67 │ +│ B00178630A │ Diablo III - PC/Mac │ 1.671875 │ 64 │ +│ B000OCEWGW │ Liquid Ass │ 4.8125 │ 64 │ +└────────────┴──────────────────────────────────────────────────────────────────────────────────────────┴────────────────────┴───────┘ + +20 rows in set. Elapsed: 139.580 sec. Processed 150.96 million rows, 68.93 GB (1.08 million rows/s., 493.86 MB/s.) +``` + +12. Let's try and improve that string search by adding an inverted index to `amazon_reviews`. (It's an experimental setting so it won't work in a Production instance of ClickHouse Cloud). We create the index on the `review_body` column: + +```sql + +``` \ No newline at end of file From 813a1ef8ac3ab3d15fa1a731acdf3f70c9de9a98 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 15:37:22 -0600 Subject: [PATCH 145/377] Update amazon-reviews.md --- .../example-datasets/amazon-reviews.md | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md index 05a295f314d..cd06774ca73 100644 --- a/docs/en/getting-started/example-datasets/amazon-reviews.md +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -333,12 +333,13 @@ FROM amazon_reviews WHERE position(review_body, 'awful') > 0 GROUP BY product_id ORDER BY count DESC -LIMIT 20; +LIMIT 50; ``` -The query takes over minutes: +The query takes a couple of minutes, but the results are a fun read: ```response + ┌─product_id─┬─any(product_title)───────────────────────────────────────────────────────────────────────┬───avg(star_rating)─┬─count─┐ │ 0345803485 │ Fifty Shades of Grey: Book One of the Fifty Shades Trilogy (Fifty Shades of Grey Series) │ 1.3870967741935485 │ 248 │ │ B007J4T2G8 │ Fifty Shades of Grey (Fifty Shades, Book 1) │ 1.4439834024896265 │ 241 │ @@ -360,13 +361,38 @@ The query takes over minutes: │ 0439023513 │ Mockingjay (The Hunger Games) │ 2.6417910447761193 │ 67 │ │ B00178630A │ Diablo III - PC/Mac │ 1.671875 │ 64 │ │ B000OCEWGW │ Liquid Ass │ 4.8125 │ 64 │ +│ B005ZOBNOI │ The Fault in Our Stars │ 4.316666666666666 │ 60 │ +│ B00L9B7IKE │ The Girl on the Train: A Novel │ 2.0677966101694913 │ 59 │ +│ B007S6Y6VS │ Garden of Life Raw Organic Meal │ 2.8793103448275863 │ 58 │ +│ B0064X7B4A │ Words With Friends │ 2.2413793103448274 │ 58 │ +│ B003WUYPPG │ Unbroken: A World War II Story of Survival, Resilience, and Redemption │ 4.620689655172414 │ 58 │ +│ B00006HBUJ │ Star Wars: Episode II - Attack of the Clones (Widescreen Edition) │ 2.2982456140350878 │ 57 │ +│ B000XUBFE2 │ The Book Thief │ 4.526315789473684 │ 57 │ +│ B0006399FS │ How to Dismantle an Atomic Bomb │ 1.9821428571428572 │ 56 │ +│ B003ZSJ212 │ Star Wars: The Complete Saga (Episodes I-VI) (Packaging May Vary) [Blu-ray] │ 2.309090909090909 │ 55 │ +│ 193700788X │ Dead Ever After (Sookie Stackhouse/True Blood) │ 1.5185185185185186 │ 54 │ +│ B004FYEZMQ │ Mass Effect 3 │ 2.056603773584906 │ 53 │ +│ B000CFYAMC │ The Room │ 3.9615384615384617 │ 52 │ +│ B0031JK95S │ Garden of Life Raw Organic Meal │ 3.3137254901960786 │ 51 │ +│ B0012JY4G4 │ Color Oops Hair Color Remover Extra Strength 1 Each │ 3.9019607843137254 │ 51 │ +│ B007VTVRFA │ SimCity - Limited Edition │ 1.2040816326530612 │ 49 │ +│ B00CE18P0K │ Pilot │ 1.7142857142857142 │ 49 │ +│ 0316015849 │ Twilight (The Twilight Saga, Book 1) │ 1.8979591836734695 │ 49 │ +│ B00DR0PDNE │ Google Chromecast HDMI Streaming Media Player │ 2.5416666666666665 │ 48 │ +│ B000056OWC │ The First Years: 4-Stage Bath System │ 1.2127659574468086 │ 47 │ +│ B007IXWKUK │ Fifty Shades Darker (Fifty Shades, Book 2) │ 1.6304347826086956 │ 46 │ +│ 1892112000 │ To Train Up a Child │ 1.4130434782608696 │ 46 │ +│ 043935806X │ Harry Potter and the Order of the Phoenix (Book 5) │ 3.977272727272727 │ 44 │ +│ B00BGO0Q9O │ Fitbit Flex Wireless Wristband with Sleep Function, Black │ 1.9318181818181819 │ 44 │ +│ B003XF1XOQ │ Mockingjay (Hunger Games Trilogy, Book 3) │ 2.772727272727273 │ 44 │ +│ B00DD2B52Y │ Spring Breakers │ 1.2093023255813953 │ 43 │ +│ B0064X7FVE │ The Weather Channel: Forecast, Radar & Alerts │ 1.5116279069767442 │ 43 │ +│ B0083PWAPW │ Kindle Fire HD 7", Dolby Audio, Dual-Band Wi-Fi │ 2.627906976744186 │ 43 │ +│ B00192KCQ0 │ Death Magnetic │ 3.5714285714285716 │ 42 │ +│ B007S6Y74O │ Garden of Life Raw Organic Meal │ 3.292682926829268 │ 41 │ +│ B0052QYLUM │ Infant Optics DXR-5 Portable Video Baby Monitor │ 2.1463414634146343 │ 41 │ └────────────┴──────────────────────────────────────────────────────────────────────────────────────────┴────────────────────┴───────┘ -20 rows in set. Elapsed: 139.580 sec. Processed 150.96 million rows, 68.93 GB (1.08 million rows/s., 493.86 MB/s.) +50 rows in set. Elapsed: 60.052 sec. Processed 150.96 million rows, 68.93 GB (2.51 million rows/s., 1.15 GB/s.) ``` -12. Let's try and improve that string search by adding an inverted index to `amazon_reviews`. (It's an experimental setting so it won't work in a Production instance of ClickHouse Cloud). We create the index on the `review_body` column: - -```sql - -``` \ No newline at end of file From 95abfca14785fe99bdef027495630e5670e361fa Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 15:40:54 -0600 Subject: [PATCH 146/377] Update amazon-reviews.md --- .../example-datasets/amazon-reviews.md | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md index cd06774ca73..d3fb9538444 100644 --- a/docs/en/getting-started/example-datasets/amazon-reviews.md +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -396,3 +396,77 @@ The query takes a couple of minutes, but the results are a fun read: 50 rows in set. Elapsed: 60.052 sec. Processed 150.96 million rows, 68.93 GB (2.51 million rows/s., 1.15 GB/s.) ``` +12. We can run the same query again, except this time we search for **awesome** in the reviews: + +```sql +SELECT + product_id, + any(product_title), + avg(star_rating), + count() AS count +FROM amazon_reviews +WHERE position(review_body, 'awesome') > 0 +GROUP BY product_id +ORDER BY count DESC +LIMIT 50; +``` + +It runs quite a bit faster - which means the cache is helping us out here: + +```response + +┌─product_id─┬─any(product_title)────────────────────────────────────────────────────┬───avg(star_rating)─┬─count─┐ +│ B00992CF6W │ Minecraft │ 4.848130353039482 │ 4787 │ +│ B009UX2YAC │ Subway Surfers │ 4.866720955483171 │ 3684 │ +│ B00QW8TYWO │ Crossy Road │ 4.935217903415784 │ 2547 │ +│ B00DJFIMW6 │ Minion Rush: Despicable Me Official Game │ 4.850450450450451 │ 2220 │ +│ B00AREIAI8 │ My Horse │ 4.865313653136531 │ 2168 │ +│ B00I8Q77Y0 │ Flappy Wings (not Flappy Bird) │ 4.8246561886051085 │ 2036 │ +│ B0054JZC6E │ 101-in-1 Games │ 4.792542016806722 │ 1904 │ +│ B00G5LQ5MU │ Escape The Titanic │ 4.724673710379117 │ 1609 │ +│ B0086700CM │ Temple Run │ 4.87636130685458 │ 1561 │ +│ B009HKL4B8 │ The Sims Freeplay │ 4.763942931258106 │ 1542 │ +│ B00I6IKSZ0 │ Pixel Gun 3D (Pocket Edition) - multiplayer shooter with skin creator │ 4.849894291754757 │ 1419 │ +│ B006OC2ANS │ BLOOD & GLORY │ 4.8561538461538465 │ 1300 │ +│ B00FATEJYE │ Injustice: Gods Among Us (Kindle Tablet Edition) │ 4.789265982636149 │ 1267 │ +│ B00B2V66VS │ Temple Run 2 │ 4.764705882352941 │ 1173 │ +│ B00JOT3HQ2 │ Geometry Dash Lite │ 4.909747292418772 │ 1108 │ +│ B00DUGCLY4 │ Guess The Emoji │ 4.813606710158434 │ 1073 │ +│ B00DR0PDNE │ Google Chromecast HDMI Streaming Media Player │ 4.607276119402985 │ 1072 │ +│ B00FAPF5U0 │ Candy Crush Saga │ 4.825757575757576 │ 1056 │ +│ B0051VVOB2 │ Kindle Fire (Previous Generation - 1st) │ 4.600407747196738 │ 981 │ +│ B007JPG04E │ FRONTLINE COMMANDO │ 4.8125 │ 912 │ +│ B00PTB7B34 │ Call of Duty®: Heroes │ 4.876404494382022 │ 890 │ +│ B00846GKTW │ Style Me Girl - Free 3D Fashion Dressup │ 4.785714285714286 │ 882 │ +│ B004S8F7QM │ Cards Against Humanity │ 4.931034482758621 │ 754 │ +│ B00FAX6XQC │ DEER HUNTER CLASSIC │ 4.700272479564033 │ 734 │ +│ B00PSGW79I │ Buddyman: Kick │ 4.888736263736264 │ 728 │ +│ B00CTQ6SIG │ The Simpsons: Tapped Out │ 4.793948126801153 │ 694 │ +│ B008JK6W5K │ Logo Quiz │ 4.782106782106782 │ 693 │ +│ B00EDTSKLU │ Geometry Dash │ 4.942028985507246 │ 690 │ +│ B00CSR2J9I │ Hill Climb Racing │ 4.880059970014993 │ 667 │ +│ B005ZXWMUS │ Netflix │ 4.722306525037936 │ 659 │ +│ B00CRFAAYC │ Fab Tattoo Artist FREE │ 4.907435508345979 │ 659 │ +│ B00DHQHQCE │ Battle Beach │ 4.863287250384024 │ 651 │ +│ B00BGA9WK2 │ PlayStation 4 500GB Console [Old Model] │ 4.688751926040061 │ 649 │ +│ B008Y7SMQU │ Logo Quiz - Fun Plus Free │ 4.7888 │ 625 │ +│ B0083PWAPW │ Kindle Fire HD 7", Dolby Audio, Dual-Band Wi-Fi │ 4.593900481540931 │ 623 │ +│ B008XG1X18 │ Pinterest │ 4.8148760330578515 │ 605 │ +│ B007SYWFRM │ Ice Age Village │ 4.8566666666666665 │ 600 │ +│ B00K7WGUKA │ Don't Tap The White Tile (Piano Tiles) │ 4.922689075630252 │ 595 │ +│ B00BWYQ9YE │ Kindle Fire HDX 7", HDX Display (Previous Generation - 3rd) │ 4.649913344887349 │ 577 │ +│ B00IZLM8MY │ High School Story │ 4.840425531914893 │ 564 │ +│ B004MC8CA2 │ Bible │ 4.884476534296029 │ 554 │ +│ B00KNWYDU8 │ Dragon City │ 4.861111111111111 │ 540 │ +│ B009ZKSPDK │ Survivalcraft │ 4.738317757009346 │ 535 │ +│ B00A4O6NMG │ My Singing Monsters │ 4.845559845559846 │ 518 │ +│ B002MQYOFW │ The Hunger Games (Hunger Games Trilogy, Book 1) │ 4.846899224806202 │ 516 │ +│ B005ZFOOE8 │ iHeartRadio – Free Music & Internet Radio │ 4.837301587301587 │ 504 │ +│ B00AIUUXHC │ Hungry Shark Evolution │ 4.846311475409836 │ 488 │ +│ B00E8KLWB4 │ The Secret Society® - Hidden Mystery │ 4.669438669438669 │ 481 │ +│ B006D1ONE4 │ Where's My Water? │ 4.916317991631799 │ 478 │ +│ B00G6ZTM3Y │ Terraria │ 4.728421052631579 │ 475 │ +└────────────┴───────────────────────────────────────────────────────────────────────┴────────────────────┴───────┘ + +50 rows in set. Elapsed: 33.954 sec. Processed 150.96 million rows, 68.95 GB (4.45 million rows/s., 2.03 GB/s.) +``` \ No newline at end of file From 53725bdea135d10e2538a0b5b00fd0be62f6ccf7 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Tue, 28 Mar 2023 23:47:45 +0200 Subject: [PATCH 147/377] monor improvements --- src/Databases/DatabaseAtomic.cpp | 10 ---- src/Databases/DatabaseAtomic.h | 2 - src/Databases/IDatabase.h | 5 -- src/Interpreters/Context.h | 4 -- src/Interpreters/DatabaseCatalog.cpp | 58 +++++++------------ src/Interpreters/InterpreterCreateQuery.cpp | 4 +- src/Interpreters/InterpreterCreateQuery.h | 6 ++ src/Interpreters/InterpreterUndropQuery.cpp | 4 +- .../02681_undrop_query_uuid.reference | 1 + .../0_stateless/02681_undrop_query_uuid.sh | 10 ++-- 10 files changed, 38 insertions(+), 66 deletions(-) diff --git a/src/Databases/DatabaseAtomic.cpp b/src/Databases/DatabaseAtomic.cpp index e5320dc6ff4..7e20b6f6535 100644 --- a/src/Databases/DatabaseAtomic.cpp +++ b/src/Databases/DatabaseAtomic.cpp @@ -110,16 +110,6 @@ StoragePtr DatabaseAtomic::detachTable(ContextPtr /* context */, const String & return table; } -void DatabaseAtomic::undropTable(ContextPtr /* context_ */, const String & table_name, const StoragePtr & table, const String & relative_table_path) -{ - std::lock_guard lock(mutex); - String table_metadata_path = getObjectMetadataPath(table_name); - String table_metadata_path_drop = DatabaseCatalog::instance().getPathForDroppedMetadata(table->getStorageID()); - renameNoReplace(table_metadata_path_drop, table_metadata_path); - DatabaseOrdinary::attachTableUnlocked(table_name, table); - table_name_to_path.emplace(std::make_pair(table_name, relative_table_path)); -} - void DatabaseAtomic::dropTable(ContextPtr local_context, const String & table_name, bool sync) { auto table = tryGetTable(table_name, local_context); diff --git a/src/Databases/DatabaseAtomic.h b/src/Databases/DatabaseAtomic.h index b8ff719989b..cb275812098 100644 --- a/src/Databases/DatabaseAtomic.h +++ b/src/Databases/DatabaseAtomic.h @@ -41,8 +41,6 @@ public: void attachTable(ContextPtr context, const String & name, const StoragePtr & table, const String & relative_table_path) override; StoragePtr detachTable(ContextPtr context, const String & name) override; - void undropTable(ContextPtr context, const String & table_name, const StoragePtr & table, const String & relative_table_path) override; - String getTableDataPath(const String & table_name) const override; String getTableDataPath(const ASTCreateQuery & query) const override; diff --git a/src/Databases/IDatabase.h b/src/Databases/IDatabase.h index 4dbf78cb5c4..b8880c4c4cc 100644 --- a/src/Databases/IDatabase.h +++ b/src/Databases/IDatabase.h @@ -216,11 +216,6 @@ public: throw Exception(ErrorCodes::NOT_IMPLEMENTED, "There is no DETACH TABLE query for Database{}", getEngineName()); } - virtual void undropTable(ContextPtr /* context */, const String & /*name*/, const StoragePtr & /*table*/, [[maybe_unused]] const String & relative_table_path = {}) /// NOLINT - { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "There is no UNDROP TABLE query for Database{}", getEngineName()); - } - /// Forget about the table without deleting it's data, but rename metadata file to prevent reloading it /// with next restart. The database may not support this method. virtual void detachTablePermanently(ContextPtr /*context*/, const String & /*name*/) diff --git a/src/Interpreters/Context.h b/src/Interpreters/Context.h index b9ef5e88a2a..bbfbd4defdc 100644 --- a/src/Interpreters/Context.h +++ b/src/Interpreters/Context.h @@ -402,7 +402,6 @@ private: /// Temporary data for query execution accounting. TemporaryDataOnDiskScopePtr temp_data_on_disk; - bool in_ddl_guard = false; public: /// Some counters for current query execution. /// Most of them are workarounds and should be removed in the future. @@ -1015,9 +1014,6 @@ public: bool isInternalQuery() const { return is_internal_query; } void setInternalQuery(bool internal) { is_internal_query = internal; } - bool isInDDLGuard() const { return in_ddl_guard; } - void setInDDLGuard(bool ddl_guard) { in_ddl_guard = ddl_guard; } - ActionLocksManagerPtr getActionLocksManager() const; enum class ApplicationType diff --git a/src/Interpreters/DatabaseCatalog.cpp b/src/Interpreters/DatabaseCatalog.cpp index 436bf244e4e..f57bb92d310 100644 --- a/src/Interpreters/DatabaseCatalog.cpp +++ b/src/Interpreters/DatabaseCatalog.cpp @@ -993,48 +993,30 @@ void DatabaseCatalog::dequeueDroppedTableCleanup(StorageID table_id) CurrentMetrics::sub(CurrentMetrics::TablesToDropQueueSize, 1); } - LOG_INFO(log, "Trying Undrop table {} from {}", dropped_table.table_id.getNameForLogs(), latest_metadata_dropped_path); + LOG_INFO(log, "Attaching undropped table {} (metadata moved from {})", + dropped_table.table_id.getNameForLogs(), latest_metadata_dropped_path); - try - { - auto wait_dropped_table_not_in_use = [&]() - { - while (true) - { - { - std::lock_guard lock(tables_marked_dropped_mutex); - if (dropped_table.table.unique()) - return; - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - }; - wait_dropped_table_not_in_use(); + /// It's unsafe to create another instance while the old one exists + /// We cannot wait on shared_ptr's refcount, so it's busy wait + while (!dropped_table.table.unique()) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + dropped_table.table.reset(); - auto ast_attach = std::make_shared(); - ast_attach->attach = true; - ast_attach->setDatabase(dropped_table.table_id.database_name); - ast_attach->setTable(dropped_table.table_id.table_name); + auto ast_attach = std::make_shared(); + ast_attach->attach = true; + ast_attach->setDatabase(dropped_table.table_id.database_name); + ast_attach->setTable(dropped_table.table_id.table_name); - auto query_context = Context::createCopy(getContext()); - /// Attach table needs to acquire ddl guard, that has already been acquired in undrop table, - /// and cannot be acquired in the attach table again. - query_context->setInDDLGuard(true); - InterpreterCreateQuery interpreter(ast_attach, query_context); - interpreter.setForceAttach(true); - interpreter.setForceRestoreData(true); - interpreter.execute(); + auto query_context = Context::createCopy(getContext()); + /// Attach table needs to acquire ddl guard, that has already been acquired in undrop table, + /// and cannot be acquired in the attach table again. + InterpreterCreateQuery interpreter(ast_attach, query_context); + interpreter.setForceAttach(true); + interpreter.setForceRestoreData(true); + interpreter.setDontNeedDDLGuard(); /// It's already locked by caller + interpreter.execute(); - LOG_INFO(log, "Table {} was successfully Undropped.", dropped_table.table_id.getNameForLogs()); - } - catch (...) - { - throw Exception( - ErrorCodes::FS_METADATA_ERROR, - "Cannot undrop table {} from {}", - dropped_table.table_id.getNameForLogs(), - latest_metadata_dropped_path); - } + LOG_INFO(log, "Table {} was successfully undropped.", dropped_table.table_id.getNameForLogs()); } void DatabaseCatalog::dropTableDataTask() diff --git a/src/Interpreters/InterpreterCreateQuery.cpp b/src/Interpreters/InterpreterCreateQuery.cpp index 66ef2dd0143..77179ad2f32 100644 --- a/src/Interpreters/InterpreterCreateQuery.cpp +++ b/src/Interpreters/InterpreterCreateQuery.cpp @@ -1107,7 +1107,7 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create) /// For short syntax of ATTACH query we have to lock table name here, before reading metadata /// and hold it until table is attached - if (!getContext()->isInDDLGuard()) + if (likely(need_ddl_guard)) ddl_guard = DatabaseCatalog::instance().getDDLGuard(database_name, create.getTable()); bool if_not_exists = create.if_not_exists; @@ -1313,7 +1313,7 @@ bool InterpreterCreateQuery::doCreateTable(ASTCreateQuery & create, return true; } - if (!ddl_guard && !getContext()->isInDDLGuard()) + if (!ddl_guard && likely(need_ddl_guard)) ddl_guard = DatabaseCatalog::instance().getDDLGuard(create.getDatabase(), create.getTable()); String data_path; diff --git a/src/Interpreters/InterpreterCreateQuery.h b/src/Interpreters/InterpreterCreateQuery.h index 4d11387f44c..a5fa6576091 100644 --- a/src/Interpreters/InterpreterCreateQuery.h +++ b/src/Interpreters/InterpreterCreateQuery.h @@ -61,6 +61,11 @@ public: load_database_without_tables = load_database_without_tables_; } + void setDontNeedDDLGuard() + { + need_ddl_guard = false; + } + /// Obtain information about columns, their types, default values and column comments, /// for case when columns in CREATE query is specified explicitly. static ColumnsDescription getColumnsDescription(const ASTExpressionList & columns, ContextPtr context, bool attach); @@ -112,6 +117,7 @@ private: bool internal = false; bool force_attach = false; bool load_database_without_tables = false; + bool need_ddl_guard = true; mutable String as_database_saved; mutable String as_table_saved; diff --git a/src/Interpreters/InterpreterUndropQuery.cpp b/src/Interpreters/InterpreterUndropQuery.cpp index c4c214e75ca..72b8e7eba4f 100644 --- a/src/Interpreters/InterpreterUndropQuery.cpp +++ b/src/Interpreters/InterpreterUndropQuery.cpp @@ -57,9 +57,11 @@ BlockIO InterpreterUndropQuery::executeToTable(ASTUndropQuery & query) auto guard = DatabaseCatalog::instance().getDDLGuard(table_id.database_name, table_id.table_name); auto database = DatabaseCatalog::instance().getDatabase(table_id.database_name); + if (database->getEngineName() == "Replicated") + throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "Replicated database does not support UNDROP query"); if (database->isTableExist(table_id.table_name, getContext())) throw Exception( - ErrorCodes::TABLE_ALREADY_EXISTS, "Cannot Undrop table, {} already exists", table_id); + ErrorCodes::TABLE_ALREADY_EXISTS, "Cannot undrop table, {} already exists", table_id); database->checkMetadataFilenameAvailability(table_id.table_name); diff --git a/tests/queries/0_stateless/02681_undrop_query_uuid.reference b/tests/queries/0_stateless/02681_undrop_query_uuid.reference index 9d36a21dbda..beae016401b 100644 --- a/tests/queries/0_stateless/02681_undrop_query_uuid.reference +++ b/tests/queries/0_stateless/02681_undrop_query_uuid.reference @@ -1,5 +1,6 @@ test MergeTree with uuid 02681_undrop_uuid +OK 1 2 3 diff --git a/tests/queries/0_stateless/02681_undrop_query_uuid.sh b/tests/queries/0_stateless/02681_undrop_query_uuid.sh index c7d87bad3d4..05e0f9c0cd0 100755 --- a/tests/queries/0_stateless/02681_undrop_query_uuid.sh +++ b/tests/queries/0_stateless/02681_undrop_query_uuid.sh @@ -7,11 +7,13 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) echo 'test MergeTree with uuid' ${CLICKHOUSE_CLIENT} -q "drop table if exists 02681_undrop_uuid sync;" -uuid=$(${CLICKHOUSE_CLIENT} --query "SELECT reinterpretAsUUID(currentDatabase())") -${CLICKHOUSE_CLIENT} -q "create table 02681_undrop_uuid UUID '$uuid' (id Int32) Engine=MergeTree() order by id;" +uuid=$(${CLICKHOUSE_CLIENT} --query "SELECT generateUUIDv4()") +uuid2=$(${CLICKHOUSE_CLIENT} --query "SELECT generateUUIDv4()") +${CLICKHOUSE_CLIENT} -q "create table 02681_undrop_uuid UUID '$uuid' on cluster test_shard_localhost (id Int32) Engine=MergeTree() order by id;" ${CLICKHOUSE_CLIENT} -q "insert into 02681_undrop_uuid values (1),(2),(3);" -${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid settings database_atomic_wait_for_drop_and_detach_synchronously = 0;" +${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid on cluster test_shard_localhost settings database_atomic_wait_for_drop_and_detach_synchronously = 0;" ${CLICKHOUSE_CLIENT} -q "select table from system.dropped_tables where table = '02681_undrop_uuid' limit 1;" -${CLICKHOUSE_CLIENT} -q "undrop table 02681_undrop_uuid UUID '$uuid' settings allow_experimental_undrop_table_query = 1;" +${CLICKHOUSE_CLIENT} -q "undrop table 02681_undrop_uuid UUID '$uuid2' settings allow_experimental_undrop_table_query = 1;" 2>&1| grep -Faq "UNKNOWN_TABLE" && echo OK +${CLICKHOUSE_CLIENT} -q "undrop table 02681_undrop_uuid UUID '$uuid' on cluster test_shard_localhost settings allow_experimental_undrop_table_query = 1;" ${CLICKHOUSE_CLIENT} -q "select * from 02681_undrop_uuid order by id;" ${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid sync;" From b8edcd90ccfee98411248c442baf3589cf8a85dd Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 15:49:58 -0600 Subject: [PATCH 148/377] Update stack_trace.md --- docs/en/operations/system-tables/stack_trace.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/operations/system-tables/stack_trace.md b/docs/en/operations/system-tables/stack_trace.md index c64cf067220..d6963160399 100644 --- a/docs/en/operations/system-tables/stack_trace.md +++ b/docs/en/operations/system-tables/stack_trace.md @@ -14,6 +14,10 @@ Columns: - `query_id` ([String](../../sql-reference/data-types/string.md)) — Query identifier that can be used to get details about a query that was running from the [query_log](../system-tables/query_log.md) system table. - `trace` ([Array(UInt64)](../../sql-reference/data-types/array.md)) — A [stack trace](https://en.wikipedia.org/wiki/Stack_trace) which represents a list of physical addresses where the called methods are stored. +:::tip +Check out the Knowledge Base for some handy queries, including [how to see what threads are currently running](https://clickhouse.com/docs/knowledgebase/find-expensive-queries) and [useful queries for troubleshooting](https://clickhouse.com/docs/knowledgebase/useful-queries-for-troubleshooting). +::: + **Example** Enabling introspection functions: From 42335c5f1e56feb5cca0a2e5bad57fa12479a2ea Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 29 Mar 2023 00:15:20 +0200 Subject: [PATCH 149/377] Fix double whitespace in exception message --- src/DataTypes/EnumValues.cpp | 2 +- tests/queries/0_stateless/02701_fail_on_invalid_having.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataTypes/EnumValues.cpp b/src/DataTypes/EnumValues.cpp index e82a8e96047..b0f51a54ccb 100644 --- a/src/DataTypes/EnumValues.cpp +++ b/src/DataTypes/EnumValues.cpp @@ -69,7 +69,7 @@ T EnumValues::getValue(StringRef field_name, bool try_treat_as_id) const } auto hints = this->getHints(field_name.toString()); auto hints_string = !hints.empty() ? ", maybe you meant: " + toString(hints) : ""; - throw Exception(ErrorCodes::BAD_ARGUMENTS, "Unknown element '{}' for enum {}", field_name.toString(), hints_string); + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Unknown element '{}' for enum{}", field_name.toString(), hints_string); } return it->getMapped(); } diff --git a/tests/queries/0_stateless/02701_fail_on_invalid_having.sql b/tests/queries/0_stateless/02701_fail_on_invalid_having.sql index 2d46a335dc1..092bda23164 100644 --- a/tests/queries/0_stateless/02701_fail_on_invalid_having.sql +++ b/tests/queries/0_stateless/02701_fail_on_invalid_having.sql @@ -1 +1 @@ -SELECT a, sum(b) FROM (SELECT 1 AS a, 1 AS b, 0 AS c) GROUP BY a HAVING c SETTINGS allow_experimental_analyzer=1 -- {{ serverError NOT_AN_AGGREGATE}} \ No newline at end of file +SELECT a, sum(b) FROM (SELECT 1 AS a, 1 AS b, 0 AS c) GROUP BY a HAVING c SETTINGS allow_experimental_analyzer=1 -- { serverError NOT_AN_AGGREGATE } From e35cf47b06fe840aeed783e85fc51ebb827ed93e Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 29 Mar 2023 00:31:38 +0200 Subject: [PATCH 150/377] Allow skipping errors related to unknown enum values in row input formats --- .../02702_allow_skip_errors_enum.reference | 2 ++ .../0_stateless/02702_allow_skip_errors_enum.sh | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/queries/0_stateless/02702_allow_skip_errors_enum.reference create mode 100755 tests/queries/0_stateless/02702_allow_skip_errors_enum.sh diff --git a/tests/queries/0_stateless/02702_allow_skip_errors_enum.reference b/tests/queries/0_stateless/02702_allow_skip_errors_enum.reference new file mode 100644 index 00000000000..f9264f7fbd3 --- /dev/null +++ b/tests/queries/0_stateless/02702_allow_skip_errors_enum.reference @@ -0,0 +1,2 @@ +Hello +World diff --git a/tests/queries/0_stateless/02702_allow_skip_errors_enum.sh b/tests/queries/0_stateless/02702_allow_skip_errors_enum.sh new file mode 100755 index 00000000000..de4a4bced65 --- /dev/null +++ b/tests/queries/0_stateless/02702_allow_skip_errors_enum.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +$CLICKHOUSE_CLIENT --query "CREATE OR REPLACE TABLE t (x Enum('Hello' = 1, 'World' = 2)) ENGINE = Memory;" +$CLICKHOUSE_CLIENT --input_format_allow_errors_num 1 --query "INSERT INTO t FORMAT CSV" < Date: Wed, 29 Mar 2023 00:31:53 +0200 Subject: [PATCH 151/377] Allow skipping errors related to unknown enum values in row input formats --- src/Common/ErrorCodes.cpp | 1 + src/DataTypes/EnumValues.cpp | 4 ++-- src/Processors/Formats/IRowInputFormat.cpp | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Common/ErrorCodes.cpp b/src/Common/ErrorCodes.cpp index 549112f8c84..9abf3bba8ff 100644 --- a/src/Common/ErrorCodes.cpp +++ b/src/Common/ErrorCodes.cpp @@ -649,6 +649,7 @@ M(678, IO_URING_INIT_FAILED) \ M(679, IO_URING_SUBMIT_ERROR) \ M(690, MIXED_ACCESS_PARAMETER_TYPES) \ + M(691, UNKNOWN_ELEMENT_OF_ENUM) \ \ M(999, KEEPER_EXCEPTION) \ M(1000, POCO_EXCEPTION) \ diff --git a/src/DataTypes/EnumValues.cpp b/src/DataTypes/EnumValues.cpp index b0f51a54ccb..9df49e765a7 100644 --- a/src/DataTypes/EnumValues.cpp +++ b/src/DataTypes/EnumValues.cpp @@ -10,7 +10,7 @@ namespace ErrorCodes { extern const int SYNTAX_ERROR; extern const int EMPTY_DATA_PASSED; - extern const int BAD_ARGUMENTS; + extern const int UNKNOWN_ELEMENT_OF_ENUM; } template @@ -69,7 +69,7 @@ T EnumValues::getValue(StringRef field_name, bool try_treat_as_id) const } auto hints = this->getHints(field_name.toString()); auto hints_string = !hints.empty() ? ", maybe you meant: " + toString(hints) : ""; - throw Exception(ErrorCodes::BAD_ARGUMENTS, "Unknown element '{}' for enum{}", field_name.toString(), hints_string); + throw Exception(ErrorCodes::UNKNOWN_ELEMENT_OF_ENUM, "Unknown element '{}' for enum{}", field_name.toString(), hints_string); } return it->getMapped(); } diff --git a/src/Processors/Formats/IRowInputFormat.cpp b/src/Processors/Formats/IRowInputFormat.cpp index 81c818e3334..948e6f4cb82 100644 --- a/src/Processors/Formats/IRowInputFormat.cpp +++ b/src/Processors/Formats/IRowInputFormat.cpp @@ -28,6 +28,7 @@ namespace ErrorCodes extern const int CANNOT_PARSE_DOMAIN_VALUE_FROM_STRING; extern const int CANNOT_PARSE_IPV4; extern const int CANNOT_PARSE_IPV6; + extern const int UNKNOWN_ELEMENT_OF_ENUM; } @@ -48,7 +49,8 @@ bool isParseError(int code) || code == ErrorCodes::INCORRECT_DATA /// For some ReadHelpers || code == ErrorCodes::CANNOT_PARSE_DOMAIN_VALUE_FROM_STRING || code == ErrorCodes::CANNOT_PARSE_IPV4 - || code == ErrorCodes::CANNOT_PARSE_IPV6; + || code == ErrorCodes::CANNOT_PARSE_IPV6 + || code == ErrorCodes::UNKNOWN_ELEMENT_OF_ENUM; } IRowInputFormat::IRowInputFormat(Block header, ReadBuffer & in_, Params params_) From 4b0b5301beac23a22bb20e192a286700b0b93a8c Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Wed, 29 Mar 2023 00:55:03 +0200 Subject: [PATCH 152/377] improve script for updating clickhouse-docs --- docs/get-clickhouse-docs.sh | 22 ++++++++++++++++++++++ docs/pull-clickhouse-docs-hook.sh | 27 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) mode change 100644 => 100755 docs/get-clickhouse-docs.sh create mode 100755 docs/pull-clickhouse-docs-hook.sh diff --git a/docs/get-clickhouse-docs.sh b/docs/get-clickhouse-docs.sh old mode 100644 new mode 100755 index 1ba0dae9844..7868f281bd0 --- a/docs/get-clickhouse-docs.sh +++ b/docs/get-clickhouse-docs.sh @@ -27,5 +27,27 @@ else exit 1 ;; esac + + if [ -n "$2" ]; then + set_git_hook="$2" + elif [ ! -n "$1" ]; then + read -rp "Would you like to setup git hook for automatic update? (y|n): " set_git_hook + fi + + if [ "$set_git_hook" = "y" ]; then + hook_command="$(pwd)/pull-clickhouse-docs-hook.sh 24" + hook_file=$(realpath "$(pwd)/../.git/hooks/post-checkout") + already_have=$(grep -Faq "pull-clickhouse-docs-hook.sh" "$hook_file" 2>/dev/null && echo 1 || echo 0) + if [ $already_have -eq 0 ]; then + echo "Appending '$hook_command' to $hook_file" + echo "$hook_command" >> "$hook_file" + chmod u+x "$hook_file" # Just in case it did not exist before append + else + echo "Looks like the update hook already exists, will not add another one" + fi + elif [ ! "$set_git_hook" = "n" ]; then + echo "Expected 'y' or 'n', got '$set_git_hook', will not setup git hook" + fi + git clone "$git_url" "clickhouse-docs" fi diff --git a/docs/pull-clickhouse-docs-hook.sh b/docs/pull-clickhouse-docs-hook.sh new file mode 100755 index 00000000000..bd93a1d3997 --- /dev/null +++ b/docs/pull-clickhouse-docs-hook.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -e +# The script to update user-guides documentation repo +# https://github.com/ClickHouse/clickhouse-docs + +WORKDIR=$(dirname "$0") +WORKDIR=$(readlink -f "${WORKDIR}") +cd "$WORKDIR" + +UPDATE_PERIOD_HOURS="${1:-24}" # By default update once per 24 hours; 0 means "always update" + +if [ ! -d "clickhouse-docs" ]; then + echo "There's no clickhouse-docs/ dir, run get-clickhouse-docs.sh first to clone the repo" + exit 1 +fi + +# Do not update it too often +LAST_FETCH_TS=$(stat -c %Y clickhouse-docs/.git/FETCH_HEAD 2>/dev/null || echo 0) +CURRENT_TS=$(date +%s) +HOURS_SINCE_LAST_FETCH=$(( (CURRENT_TS - LAST_FETCH_TS) / 60 / 60 )) + +if [ "$HOURS_SINCE_LAST_FETCH" -lt "$UPDATE_PERIOD_HOURS" ]; then + exit 0; +fi + +echo "Updating clickhouse-docs..." +git -C clickhouse-docs pull From 989c9d77e0867b5829bf9091a7b4fcb6f45f3276 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 29 Mar 2023 02:13:27 +0300 Subject: [PATCH 153/377] Update .gitmodules --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 00b470bc547..ca55281e643 100644 --- a/.gitmodules +++ b/.gitmodules @@ -218,7 +218,7 @@ url = https://github.com/ClickHouse/libpqxx [submodule "contrib/sqlite-amalgamation"] path = contrib/sqlite-amalgamation - url = https://github.com/evillique/sqlite-amalgamation + url = https://github.com/ClickHouse/sqlite-amalgamation [submodule "contrib/s2geometry"] path = contrib/s2geometry url = https://github.com/ClickHouse/s2geometry From 61916cfd96eb37c55d3def2eb97d5637f79ec397 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 17:45:27 -0600 Subject: [PATCH 154/377] Update amazon-reviews.md --- docs/en/getting-started/example-datasets/amazon-reviews.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md index d3fb9538444..ec642525cc9 100644 --- a/docs/en/getting-started/example-datasets/amazon-reviews.md +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -5,7 +5,7 @@ sidebar_label: Amazon customer reviews # Amazon customer reviews dataset -**Amazon Customer Reviews** (a.k.a. Product Reviews) is one of Amazon’s iconic products. In a period of over two decades since the first review in 1995, millions of Amazon customers have contributed over a hundred million reviews to express opinions and describe their experiences regarding products on the Amazon.com website. This makes Amazon Customer Reviews a rich source of information for academic researchers in the fields of Natural Language Processing (NLP), Information Retrieval (IR), and Machine Learning (ML), amongst others. +[**Amazon Customer Reviews**](https://s3.amazonaws.com/amazon-reviews-pds/readme.html) (a.k.a. Product Reviews) is one of Amazon’s iconic products. In a period of over two decades since the first review in 1995, millions of Amazon customers have contributed over a hundred million reviews to express opinions and describe their experiences regarding products on the Amazon.com website. This makes Amazon Customer Reviews a rich source of information for academic researchers in the fields of Natural Language Processing (NLP), Information Retrieval (IR), and Machine Learning (ML), amongst others. By accessing the dataset, you agree to the [license terms](https://s3.amazonaws.com/amazon-reviews-pds/license.txt). The data is in a tab-separated format in gzipped files are up in AWS S3. Let's walk through the steps to insert it into ClickHouse. From 81af0b6deb0b4e477c61dab704368d5ac619258d Mon Sep 17 00:00:00 2001 From: serxa Date: Wed, 29 Mar 2023 00:29:21 +0000 Subject: [PATCH 155/377] avoid counters updates if not initialized --- src/Common/ThreadStatus.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Common/ThreadStatus.cpp b/src/Common/ThreadStatus.cpp index 16ce73cda20..1b783aa9ec4 100644 --- a/src/Common/ThreadStatus.cpp +++ b/src/Common/ThreadStatus.cpp @@ -219,6 +219,9 @@ void ThreadStatus::updatePerformanceCounters() void ThreadStatus::updatePerformanceCountersIfNeeded() { + if (last_rusage->thread_id == 0) + return; // Performance counters are not initialized, so there is no need to update them + constexpr UInt64 performance_counters_update_period_microseconds = 10 * 1000; // 10 milliseconds UInt64 total_elapsed_microseconds = stopwatch.elapsedMicroseconds(); if (last_performance_counters_update_time + performance_counters_update_period_microseconds < total_elapsed_microseconds) From cfe0fb65718f1a2fc097029ba8f1304e5c6193da Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Wed, 29 Mar 2023 03:31:39 +0200 Subject: [PATCH 156/377] fix --- src/Interpreters/DatabaseCatalog.cpp | 1 - tests/queries/0_stateless/02681_undrop_query_uuid.sh | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Interpreters/DatabaseCatalog.cpp b/src/Interpreters/DatabaseCatalog.cpp index f57bb92d310..ac9b8615fe5 100644 --- a/src/Interpreters/DatabaseCatalog.cpp +++ b/src/Interpreters/DatabaseCatalog.cpp @@ -53,7 +53,6 @@ namespace ErrorCodes extern const int DATABASE_ACCESS_DENIED; extern const int LOGICAL_ERROR; extern const int HAVE_DEPENDENT_OBJECTS; - extern const int FS_METADATA_ERROR; } TemporaryTableHolder::TemporaryTableHolder(ContextPtr context_, const TemporaryTableHolder::Creator & creator, const ASTPtr & query) diff --git a/tests/queries/0_stateless/02681_undrop_query_uuid.sh b/tests/queries/0_stateless/02681_undrop_query_uuid.sh index 05e0f9c0cd0..a93f30ef459 100755 --- a/tests/queries/0_stateless/02681_undrop_query_uuid.sh +++ b/tests/queries/0_stateless/02681_undrop_query_uuid.sh @@ -9,11 +9,11 @@ echo 'test MergeTree with uuid' ${CLICKHOUSE_CLIENT} -q "drop table if exists 02681_undrop_uuid sync;" uuid=$(${CLICKHOUSE_CLIENT} --query "SELECT generateUUIDv4()") uuid2=$(${CLICKHOUSE_CLIENT} --query "SELECT generateUUIDv4()") -${CLICKHOUSE_CLIENT} -q "create table 02681_undrop_uuid UUID '$uuid' on cluster test_shard_localhost (id Int32) Engine=MergeTree() order by id;" +${CLICKHOUSE_CLIENT} --distributed_ddl_output_mode=none -q "create table 02681_undrop_uuid UUID '$uuid' on cluster test_shard_localhost (id Int32) Engine=MergeTree() order by id;" ${CLICKHOUSE_CLIENT} -q "insert into 02681_undrop_uuid values (1),(2),(3);" -${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid on cluster test_shard_localhost settings database_atomic_wait_for_drop_and_detach_synchronously = 0;" +${CLICKHOUSE_CLIENT} --distributed_ddl_output_mode=none -q "drop table 02681_undrop_uuid on cluster test_shard_localhost settings database_atomic_wait_for_drop_and_detach_synchronously = 0;" ${CLICKHOUSE_CLIENT} -q "select table from system.dropped_tables where table = '02681_undrop_uuid' limit 1;" ${CLICKHOUSE_CLIENT} -q "undrop table 02681_undrop_uuid UUID '$uuid2' settings allow_experimental_undrop_table_query = 1;" 2>&1| grep -Faq "UNKNOWN_TABLE" && echo OK -${CLICKHOUSE_CLIENT} -q "undrop table 02681_undrop_uuid UUID '$uuid' on cluster test_shard_localhost settings allow_experimental_undrop_table_query = 1;" +${CLICKHOUSE_CLIENT} --distributed_ddl_output_mode=none -q "undrop table 02681_undrop_uuid UUID '$uuid' on cluster test_shard_localhost settings allow_experimental_undrop_table_query = 1;" ${CLICKHOUSE_CLIENT} -q "select * from 02681_undrop_uuid order by id;" ${CLICKHOUSE_CLIENT} -q "drop table 02681_undrop_uuid sync;" From 2f09b43d984b5b07f09411a47211ff21c1297ad7 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 19:46:29 -0600 Subject: [PATCH 157/377] Create environmental-sensors.md --- .../example-datasets/environmental-sensors.md | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 docs/en/getting-started/example-datasets/environmental-sensors.md diff --git a/docs/en/getting-started/example-datasets/environmental-sensors.md b/docs/en/getting-started/example-datasets/environmental-sensors.md new file mode 100644 index 00000000000..82b7615e812 --- /dev/null +++ b/docs/en/getting-started/example-datasets/environmental-sensors.md @@ -0,0 +1,150 @@ +--- +slug: /en/getting-started/example-datasets/environmental-sensors +sidebar_label: Environmental Sensors Data +--- + +[Sensor.Community](https://sensor.community/en/) is a contributors-driven global sensor network that creates Open Environmental Data. The data is collected from sensors all over the globe. Anyone can purchase a sensor and place it wherever they like. The APIs to download the data is in [GitHub](https://github.com/opendata-stuttgart/meta/wiki/APIs) and the data is freely available under the [Database Contents License (DbCL)](https://opendatacommons.org/licenses/dbcl/1-0/). + +:::important +The dataset has over 20 billion records, so be careful just copying-and-pasting the commands below unless your resources can handle that type of volume. The commands below were executed on a **Production** instance of [ClickHouse Cloud](https://clickhouse.cloud). +::: + +1. The data is in S3, so we can use the `s3` table function to create a table from the files. We can also query the data in place. Let's look at a few rows before attempting to insert it into ClickHouse: + +```sql +SELECT * +FROM s3( + 'https://clickhouse-public-datasets.s3.eu-central-1.amazonaws.com/sensors/monthly/2019-06_bmp180.csv.zst', + 'CSVWithNames' + ) +LIMIT 10 +SETTINGS format_csv_delimiter = ';'; +``` + +The data is in CSV files but uses a semi-colon for the delimiter. The rows look like: + +```response +┌─sensor_id─┬─sensor_type─┬─location─┬────lat─┬────lon─┬─timestamp───────────┬──pressure─┬─altitude─┬─pressure_sealevel─┬─temperature─┐ +│ 9119 │ BMP180 │ 4594 │ 50.994 │ 7.126 │ 2019-06-01T00:00:00 │ 101471 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 19.9 │ +│ 21210 │ BMP180 │ 10762 │ 42.206 │ 25.326 │ 2019-06-01T00:00:00 │ 99525 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 19.3 │ +│ 19660 │ BMP180 │ 9978 │ 52.434 │ 17.056 │ 2019-06-01T00:00:04 │ 101570 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 15.3 │ +│ 12126 │ BMP180 │ 6126 │ 57.908 │ 16.49 │ 2019-06-01T00:00:05 │ 101802.56 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 8.07 │ +│ 15845 │ BMP180 │ 8022 │ 52.498 │ 13.466 │ 2019-06-01T00:00:05 │ 101878 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 23 │ +│ 16415 │ BMP180 │ 8316 │ 49.312 │ 6.744 │ 2019-06-01T00:00:06 │ 100176 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 14.7 │ +│ 7389 │ BMP180 │ 3735 │ 50.136 │ 11.062 │ 2019-06-01T00:00:06 │ 98905 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 12.1 │ +│ 13199 │ BMP180 │ 6664 │ 52.514 │ 13.44 │ 2019-06-01T00:00:07 │ 101855.54 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 19.74 │ +│ 12753 │ BMP180 │ 6440 │ 44.616 │ 2.032 │ 2019-06-01T00:00:07 │ 99475 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 17 │ +│ 16956 │ BMP180 │ 8594 │ 52.052 │ 8.354 │ 2019-06-01T00:00:08 │ 101322 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 17.2 │ +└───────────┴─────────────┴──────────┴────────┴───────┴─────────────────────┴──────────┴──────────┴───────────────────┴─────────────┘ +``` + +2. We will use the following `MergeTree` table to store the data in ClickHouse: + +```sql +CREATE TABLE sensors +( + sensor_id UInt16, + sensor_type Enum('BME280', 'BMP180', 'BMP280', 'DHT22', 'DS18B20', 'HPM', 'HTU21D', 'PMS1003', 'PMS3003', 'PMS5003', 'PMS6003', 'PMS7003', 'PPD42NS', 'SDS011'), + location UInt32, + lat Float32, + lon Float32, + timestamp DateTime, + P1 Float32, + P2 Float32, + P0 Float32, + durP1 Float32, + ratioP1 Float32, + durP2 Float32, + ratioP2 Float32, + pressure Float32, + altitude Float32, + pressure_sealevel Float32, + temperature Float32, + humidity Float32, + date Date MATERIALIZED toDate(timestamp) +) +ENGINE = MergeTree +ORDER BY (timestamp, sensor_id); +``` + +3. ClickHouse Cloud services have a cluster named `default`. We will use the `s3Cluster` table function, which reads S3 files in parallel from the nodes in your cluster. (If you do not have a cluster, just use the `s3` function and remove the cluster name.) + +This query will take a while - it's about 1.67T of data uncompressed: + +```sql +INSERT INTO sensors + SELECT * + FROM s3Cluster( + 'default', + 'https://clickhouse-public-datasets.s3.amazonaws.com/sensors/monthly/*.csv.zst', + 'CSVWithNames', + $$ sensor_id UInt16, + sensor_type String, + location UInt32, + lat Float32, + lon Float32, + timestamp DateTime, + P1 Float32, + P2 Float32, + P0 Float32, + durP1 Float32, + ratioP1 Float32, + durP2 Float32, + ratioP2 Float32, + pressure Float32, + altitude Float32, + pressure_sealevel Float32, + temperature Float32, + humidity Float32 $$ + ) +SETTINGS + format_csv_delimiter = ';', + input_format_allow_errors_ratio = '0.5', + input_format_allow_errors_num = 10000, + input_format_parallel_parsing = 0, + date_time_input_format = 'best_effort', + max_insert_threads = 32, + parallel_distributed_insert_select = 1; +``` + +Here is the response - showing the number of rows and the speed of processing. It is input at a rate of about 6.5M rows per second! + +```response + +``` + +4. Let's see how much storage disk is needed for the `sensors` table: + +```sql +SELECT + disk_name, + formatReadableSize(sum(data_compressed_bytes) AS size) AS compressed, + formatReadableSize(sum(data_uncompressed_bytes) AS usize) AS uncompressed, + round(usize / size, 2) AS compr_rate, + sum(rows) AS rows, + count() AS part_count +FROM system.parts +WHERE (active = 1) AND (table = 'sensors') +GROUP BY + disk_name +ORDER BY size DESC; +``` + +5. Let's analyze the data now that it's in ClickHouse. Notice the quantity of data increases over time as more sensors are deployed: + +```sql +SELECT + date, + count() +FROM sensors +GROUP BY date +ORDER BY date ASC; +``` + +We can create a chart in the SQL Console to visualize the results: + +// ![Number of events per day](@site/docs/en/getting-started/images/sensors_01.png) + +6. + +7. \ No newline at end of file From 87b75269fb253c9adeff5cf99461186384a73a00 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 20:32:10 -0600 Subject: [PATCH 158/377] Added image --- .../example-datasets/environmental-sensors.md | 14 +++++++++++--- .../example-datasets/images/sensors_01.png | Bin 0 -> 428464 bytes 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 docs/en/getting-started/example-datasets/images/sensors_01.png diff --git a/docs/en/getting-started/example-datasets/environmental-sensors.md b/docs/en/getting-started/example-datasets/environmental-sensors.md index 82b7615e812..110c3262697 100644 --- a/docs/en/getting-started/example-datasets/environmental-sensors.md +++ b/docs/en/getting-started/example-datasets/environmental-sensors.md @@ -107,10 +107,10 @@ SETTINGS parallel_distributed_insert_select = 1; ``` -Here is the response - showing the number of rows and the speed of processing. It is input at a rate of about 6.5M rows per second! +Here is the response - showing the number of rows and the speed of processing. It is input at a rate of over 6M rows per second! ```response - +0 rows in set. Elapsed: 3419.330 sec. Processed 20.69 billion rows, 1.67 TB (6.05 million rows/s., 488.52 MB/s.) ``` 4. Let's see how much storage disk is needed for the `sensors` table: @@ -130,6 +130,14 @@ GROUP BY ORDER BY size DESC; ``` +The 1.67T is compressed down to 1.30T, and there are 20.69 billion rows: + +```response +┌─disk_name─┬─compressed─┬─uncompressed─┬─compr_rate─┬────────rows─┬─part_count─┐ +│ s3disk │ 310.21 GiB │ 1.30 TiB │ 4.29 │ 20693971809 │ 472 │ +└───────────┴────────────┴──────────────┴────────────┴─────────────┴────────────┘ +``` + 5. Let's analyze the data now that it's in ClickHouse. Notice the quantity of data increases over time as more sensors are deployed: ```sql @@ -143,7 +151,7 @@ ORDER BY date ASC; We can create a chart in the SQL Console to visualize the results: -// ![Number of events per day](@site/docs/en/getting-started/images/sensors_01.png) +![Number of events per day](./images/sensors_01.png) 6. diff --git a/docs/en/getting-started/example-datasets/images/sensors_01.png b/docs/en/getting-started/example-datasets/images/sensors_01.png new file mode 100644 index 0000000000000000000000000000000000000000..1804bda6d1b227c471dcb8cf797c94752e5cbf53 GIT binary patch literal 428464 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�+_{f!UkJVW-_eW+oqWS~GG;vaL8VqCc-Ng1 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 KO7VW$Ikxi%y@-*9 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%0Ap{^JCuIkbH`@|sg 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%0DX-R14 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%0Ap(V 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 literal 0 HcmV?d00001 From 258c1a9bc0dfa34427874517cfc9353d31bd8ed1 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 21:11:28 -0600 Subject: [PATCH 159/377] Add query --- .../example-datasets/environmental-sensors.md | 16 +++++++++++++++- .../example-datasets/images/sensors_02.png | Bin 0 -> 208417 bytes 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/en/getting-started/example-datasets/images/sensors_02.png diff --git a/docs/en/getting-started/example-datasets/environmental-sensors.md b/docs/en/getting-started/example-datasets/environmental-sensors.md index 110c3262697..28d8b6755d8 100644 --- a/docs/en/getting-started/example-datasets/environmental-sensors.md +++ b/docs/en/getting-started/example-datasets/environmental-sensors.md @@ -153,6 +153,20 @@ We can create a chart in the SQL Console to visualize the results: ![Number of events per day](./images/sensors_01.png) -6. +6. This query counts the number of overly hot and humid days: + +```sql +WITH + toYYYYMMDD(timestamp) AS day +SELECT day, count() FROM sensors +WHERE temperature >= 40 and temperature <= 50 +and humidity >= 90 +GROUP BY day +ORDER BY day asc; +``` + +Here's a visualization of the result: + +![Hot and humid days](./images/sensors_02.png) 7. \ No newline at end of file diff --git a/docs/en/getting-started/example-datasets/images/sensors_02.png b/docs/en/getting-started/example-datasets/images/sensors_02.png new file mode 100644 index 0000000000000000000000000000000000000000..8226f4578d0fc9d8523a8345c48f3cafaa5715c8 GIT binary patch literal 208417 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�LYb-!tW!Lo$e>=-!36}VB{AGH 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

?4vL!6hAN>F%R(|xr#>{z;oGGtu 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 F{|iTz&Q$;a literal 0 HcmV?d00001 From 2b84fb3fffe2c409407506bab719ff03620f6c91 Mon Sep 17 00:00:00 2001 From: save-my-heart Date: Wed, 29 Mar 2023 11:21:30 +0800 Subject: [PATCH 160/377] fix tests --- ...function.reference => 02701_non_parametric_function.reference} | 0 ..._parametric_function.sql => 02701_non_parametric_function.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/queries/0_stateless/{25403_non_parametric_function.reference => 02701_non_parametric_function.reference} (100%) rename tests/queries/0_stateless/{25403_non_parametric_function.sql => 02701_non_parametric_function.sql} (100%) diff --git a/tests/queries/0_stateless/25403_non_parametric_function.reference b/tests/queries/0_stateless/02701_non_parametric_function.reference similarity index 100% rename from tests/queries/0_stateless/25403_non_parametric_function.reference rename to tests/queries/0_stateless/02701_non_parametric_function.reference diff --git a/tests/queries/0_stateless/25403_non_parametric_function.sql b/tests/queries/0_stateless/02701_non_parametric_function.sql similarity index 100% rename from tests/queries/0_stateless/25403_non_parametric_function.sql rename to tests/queries/0_stateless/02701_non_parametric_function.sql From 954d486c20a55564b9f47a68221397336c2e3649 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 21:27:23 -0600 Subject: [PATCH 161/377] Update environmental-sensors.md --- .../getting-started/example-datasets/environmental-sensors.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/en/getting-started/example-datasets/environmental-sensors.md b/docs/en/getting-started/example-datasets/environmental-sensors.md index 28d8b6755d8..c10f0728bba 100644 --- a/docs/en/getting-started/example-datasets/environmental-sensors.md +++ b/docs/en/getting-started/example-datasets/environmental-sensors.md @@ -159,8 +159,7 @@ We can create a chart in the SQL Console to visualize the results: WITH toYYYYMMDD(timestamp) AS day SELECT day, count() FROM sensors -WHERE temperature >= 40 and temperature <= 50 -and humidity >= 90 +WHERE temperature >= 40 AND temperature <= 50 AND humidity >= 90 GROUP BY day ORDER BY day asc; ``` @@ -169,4 +168,3 @@ Here's a visualization of the result: ![Hot and humid days](./images/sensors_02.png) -7. \ No newline at end of file From 0ca26b09ebe8f96bb344b66adbb38c29b9f64c0e Mon Sep 17 00:00:00 2001 From: rfraposa Date: Tue, 28 Mar 2023 21:51:19 -0600 Subject: [PATCH 162/377] Update environmental-sensors.md --- .../getting-started/example-datasets/environmental-sensors.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/en/getting-started/example-datasets/environmental-sensors.md b/docs/en/getting-started/example-datasets/environmental-sensors.md index c10f0728bba..d6c9552030f 100644 --- a/docs/en/getting-started/example-datasets/environmental-sensors.md +++ b/docs/en/getting-started/example-datasets/environmental-sensors.md @@ -3,6 +3,8 @@ slug: /en/getting-started/example-datasets/environmental-sensors sidebar_label: Environmental Sensors Data --- +# Environmental Sensors Data + [Sensor.Community](https://sensor.community/en/) is a contributors-driven global sensor network that creates Open Environmental Data. The data is collected from sensors all over the globe. Anyone can purchase a sensor and place it wherever they like. The APIs to download the data is in [GitHub](https://github.com/opendata-stuttgart/meta/wiki/APIs) and the data is freely available under the [Database Contents License (DbCL)](https://opendatacommons.org/licenses/dbcl/1-0/). :::important From 7078576ee52f6834228be3d1bdb4e67d92afd745 Mon Sep 17 00:00:00 2001 From: save-my-heart Date: Wed, 29 Mar 2023 12:58:35 +0800 Subject: [PATCH 163/377] fix tests --- tests/queries/0_stateless/02701_non_parametric_function.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02701_non_parametric_function.sql b/tests/queries/0_stateless/02701_non_parametric_function.sql index 9b7e2ae7a04..9946b50a8c8 100644 --- a/tests/queries/0_stateless/02701_non_parametric_function.sql +++ b/tests/queries/0_stateless/02701_non_parametric_function.sql @@ -2,4 +2,6 @@ SELECT * FROM system.numbers WHERE number > toUInt64(10)(number) LIMIT 10; -- { CREATE FUNCTION sum_udf as (x, y) -> (x + y); -SELECT sum_udf(1)(1, 2); -- { serverError 309 } \ No newline at end of file +SELECT sum_udf(1)(1, 2); -- { serverError 309 } + +DROP FUNCTION sum_udf; \ No newline at end of file From 3507c4998e3a0c2eef6c88c89e06cf66202de1fd Mon Sep 17 00:00:00 2001 From: save-my-heart Date: Wed, 29 Mar 2023 14:07:45 +0800 Subject: [PATCH 164/377] fix tests --- tests/queries/0_stateless/02701_non_parametric_function.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02701_non_parametric_function.sql b/tests/queries/0_stateless/02701_non_parametric_function.sql index 9946b50a8c8..1164a060323 100644 --- a/tests/queries/0_stateless/02701_non_parametric_function.sql +++ b/tests/queries/0_stateless/02701_non_parametric_function.sql @@ -1,6 +1,6 @@ SELECT * FROM system.numbers WHERE number > toUInt64(10)(number) LIMIT 10; -- { serverError 309 } -CREATE FUNCTION sum_udf as (x, y) -> (x + y); +CREATE FUNCTION IF NOT EXISTS sum_udf as (x, y) -> (x + y); SELECT sum_udf(1)(1, 2); -- { serverError 309 } From 32cfc983b524088b78154a65200f7f220ca576bd Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Wed, 29 Mar 2023 07:38:37 +0000 Subject: [PATCH 165/377] Fix clang-tidy build --- programs/keeper/Keeper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/keeper/Keeper.cpp b/programs/keeper/Keeper.cpp index b1e988befbd..266b363eb47 100644 --- a/programs/keeper/Keeper.cpp +++ b/programs/keeper/Keeper.cpp @@ -499,7 +499,7 @@ try /// Prometheus (if defined and not setup yet with http_port) port_name = "prometheus.port"; - createServer(listen_host, port_name, listen_try, [&, http_context = std::move(http_context)](UInt16 port) + createServer(listen_host, port_name, listen_try, [&, http_context = std::move(http_context)](UInt16 port) mutable { Poco::Net::ServerSocket socket; auto address = socketBindListen(socket, listen_host, port); From 6738be00298db5095a6b0837b58e4c0337dd1888 Mon Sep 17 00:00:00 2001 From: save-my-heart Date: Wed, 29 Mar 2023 16:14:51 +0800 Subject: [PATCH 166/377] fix tests --- tests/queries/0_stateless/02701_non_parametric_function.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02701_non_parametric_function.sql b/tests/queries/0_stateless/02701_non_parametric_function.sql index 1164a060323..a757343a624 100644 --- a/tests/queries/0_stateless/02701_non_parametric_function.sql +++ b/tests/queries/0_stateless/02701_non_parametric_function.sql @@ -4,4 +4,4 @@ CREATE FUNCTION IF NOT EXISTS sum_udf as (x, y) -> (x + y); SELECT sum_udf(1)(1, 2); -- { serverError 309 } -DROP FUNCTION sum_udf; \ No newline at end of file +DROP FUNCTION IF EXISTS sum_udf; \ No newline at end of file From 086cd4bb7ea1a8eb71d9b554051e54f52e271c80 Mon Sep 17 00:00:00 2001 From: Sema Checherinda Date: Wed, 29 Mar 2023 10:27:39 +0200 Subject: [PATCH 167/377] fix fast tests --- tests/queries/0_stateless/00453_cast_enum.sql | 2 +- tests/queries/0_stateless/01310_enum_comparison.sql | 2 +- .../0_stateless/01402_cast_nullable_string_to_enum.sql | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/queries/0_stateless/00453_cast_enum.sql b/tests/queries/0_stateless/00453_cast_enum.sql index 384db50c7c4..023e7233acf 100644 --- a/tests/queries/0_stateless/00453_cast_enum.sql +++ b/tests/queries/0_stateless/00453_cast_enum.sql @@ -12,6 +12,6 @@ INSERT INTO cast_enums SELECT 2 AS type, toDate('2017-01-01') AS date, number AS SELECT type, date, id FROM cast_enums ORDER BY type, id; -INSERT INTO cast_enums VALUES ('wrong_value', '2017-01-02', 7); -- { clientError 36 } +INSERT INTO cast_enums VALUES ('wrong_value', '2017-01-02', 7); -- { clientError 691 } DROP TABLE IF EXISTS cast_enums; diff --git a/tests/queries/0_stateless/01310_enum_comparison.sql b/tests/queries/0_stateless/01310_enum_comparison.sql index 26901a61b2b..ed63911e698 100644 --- a/tests/queries/0_stateless/01310_enum_comparison.sql +++ b/tests/queries/0_stateless/01310_enum_comparison.sql @@ -3,4 +3,4 @@ INSERT INTO enum VALUES ('hello'); SELECT count() FROM enum WHERE x = 'hello'; SELECT count() FROM enum WHERE x = 'world'; -SELECT count() FROM enum WHERE x = 'xyz'; -- { serverError 36 } +SELECT count() FROM enum WHERE x = 'xyz'; -- { serverError 691 } diff --git a/tests/queries/0_stateless/01402_cast_nullable_string_to_enum.sql b/tests/queries/0_stateless/01402_cast_nullable_string_to_enum.sql index b8b5370515a..1d445412381 100644 --- a/tests/queries/0_stateless/01402_cast_nullable_string_to_enum.sql +++ b/tests/queries/0_stateless/01402_cast_nullable_string_to_enum.sql @@ -5,8 +5,8 @@ SELECT CAST(CAST(NULL AS Nullable(String)) AS Nullable(Enum8('Hello' = 1))); SELECT CAST(CAST(NULL AS Nullable(FixedString(1))) AS Nullable(Enum8('Hello' = 1))); -- empty string still not acceptable -SELECT CAST(CAST('' AS Nullable(String)) AS Nullable(Enum8('Hello' = 1))); -- { serverError 36 } -SELECT CAST(CAST('' AS Nullable(FixedString(1))) AS Nullable(Enum8('Hello' = 1))); -- { serverError 36 } +SELECT CAST(CAST('' AS Nullable(String)) AS Nullable(Enum8('Hello' = 1))); -- { serverError 691 } +SELECT CAST(CAST('' AS Nullable(FixedString(1))) AS Nullable(Enum8('Hello' = 1))); -- { serverError 691 } -- non-Nullable Enum() still not acceptable SELECT CAST(CAST(NULL AS Nullable(String)) AS Enum8('Hello' = 1)); -- { serverError 349 } From f38a7aeabe83cff19fba7d727081da75c06912c7 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 22 Mar 2023 09:49:22 +0200 Subject: [PATCH 168/377] ThreadPool metrics introspection There are lots of thread pools and simple local-vs-global is not enough already, it is good to know which one in particular uses threads. Signed-off-by: Azat Khuzhin --- programs/benchmark/Benchmark.cpp | 9 ++- programs/copier/ClusterCopier.cpp | 9 ++- src/Backups/BackupsWorker.cpp | 13 ++++- src/Common/CurrentMetrics.cpp | 58 +++++++++++++++++++ src/Common/CurrentMetrics.h | 6 +- src/Common/ThreadPool.cpp | 47 +++++++++++---- src/Common/ThreadPool.h | 31 +++++++--- src/Common/examples/parallel_aggregation.cpp | 9 ++- src/Common/examples/parallel_aggregation2.cpp | 9 ++- .../examples/thread_creation_latency.cpp | 13 ++++- .../gtest_thread_pool_concurrent_wait.cpp | 11 +++- .../tests/gtest_thread_pool_global_full.cpp | 13 ++++- src/Common/tests/gtest_thread_pool_loop.cpp | 9 ++- .../gtest_thread_pool_schedule_exception.cpp | 9 ++- src/Databases/DatabaseOnDisk.cpp | 13 ++++- src/Databases/DatabaseOrdinary.cpp | 9 ++- src/Databases/TablesLoader.cpp | 20 +++++-- .../CacheDictionaryUpdateQueue.cpp | 9 ++- src/Dictionaries/HashedDictionary.cpp | 10 +++- src/Disks/IO/ThreadPoolReader.cpp | 4 +- src/Disks/IO/ThreadPoolRemoteFSReader.cpp | 4 +- .../ObjectStorages/DiskObjectStorage.cpp | 11 +++- ...ThreadPool.cpp => BackupsIOThreadPool.cpp} | 17 +++++- src/IO/IOThreadPool.cpp | 17 +++++- src/Interpreters/Aggregator.cpp | 9 ++- src/Interpreters/AsynchronousInsertQueue.cpp | 4 +- src/Interpreters/DDLWorker.cpp | 12 +++- src/Interpreters/DDLWorker.h | 1 + src/Interpreters/DatabaseCatalog.cpp | 4 +- src/Interpreters/InterpreterSystemQuery.cpp | 10 +++- src/Interpreters/loadMetadata.cpp | 16 +++-- .../Impl/ParallelFormattingOutputFormat.h | 11 +++- .../Formats/Impl/ParallelParsingInputFormat.h | 11 +++- .../Transforms/AggregatingTransform.h | 12 +++- src/Storages/Hive/StorageHive.cpp | 8 ++- .../MergeTree/MergeTreeBackgroundExecutor.h | 7 +++ src/Storages/MergeTree/MergeTreeData.cpp | 9 ++- .../MergeTree/MergeTreeDataSelectExecutor.cpp | 12 +++- src/Storages/StorageDistributed.cpp | 9 ++- src/Storages/StorageS3.cpp | 13 ++++- src/Storages/System/StorageSystemReplicas.cpp | 11 +++- utils/keeper-bench/Runner.h | 9 ++- 42 files changed, 441 insertions(+), 87 deletions(-) rename src/IO/{BackupIOThreadPool.cpp => BackupsIOThreadPool.cpp} (59%) diff --git a/programs/benchmark/Benchmark.cpp b/programs/benchmark/Benchmark.cpp index 994f9b7ac4d..466a0c194f7 100644 --- a/programs/benchmark/Benchmark.cpp +++ b/programs/benchmark/Benchmark.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -43,6 +44,12 @@ namespace fs = std::filesystem; * The tool emulates a case with fixed amount of simultaneously executing queries. */ +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + namespace DB { @@ -103,7 +110,7 @@ public: settings(settings_), shared_context(Context::createShared()), global_context(Context::createGlobal(shared_context.get())), - pool(concurrency) + pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, concurrency) { const auto secure = secure_ ? Protocol::Secure::Enable : Protocol::Secure::Disable; size_t connections_cnt = std::max(ports_.size(), hosts_.size()); diff --git a/programs/copier/ClusterCopier.cpp b/programs/copier/ClusterCopier.cpp index 3a6261974d1..079c70596a6 100644 --- a/programs/copier/ClusterCopier.cpp +++ b/programs/copier/ClusterCopier.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,12 @@ #include #include +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + namespace DB { @@ -192,7 +199,7 @@ void ClusterCopier::discoverTablePartitions(const ConnectionTimeouts & timeouts, { /// Fetch partitions list from a shard { - ThreadPool thread_pool(num_threads ? num_threads : 2 * getNumberOfPhysicalCPUCores()); + ThreadPool thread_pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, num_threads ? num_threads : 2 * getNumberOfPhysicalCPUCores()); for (const TaskShardPtr & task_shard : task_table.all_shards) thread_pool.scheduleOrThrowOnError([this, timeouts, task_shard]() diff --git a/src/Backups/BackupsWorker.cpp b/src/Backups/BackupsWorker.cpp index b6cf74efe22..5ac51a81eca 100644 --- a/src/Backups/BackupsWorker.cpp +++ b/src/Backups/BackupsWorker.cpp @@ -20,10 +20,19 @@ #include #include #include +#include #include #include +namespace CurrentMetrics +{ + extern const Metric BackupsThreads; + extern const Metric BackupsThreadsActive; + extern const Metric RestoreThreads; + extern const Metric RestoreThreadsActive; +} + namespace DB { @@ -152,8 +161,8 @@ namespace BackupsWorker::BackupsWorker(size_t num_backup_threads, size_t num_restore_threads, bool allow_concurrent_backups_, bool allow_concurrent_restores_) - : backups_thread_pool(num_backup_threads, /* max_free_threads = */ 0, num_backup_threads) - , restores_thread_pool(num_restore_threads, /* max_free_threads = */ 0, num_restore_threads) + : backups_thread_pool(CurrentMetrics::BackupsThreads, CurrentMetrics::BackupsThreadsActive, num_backup_threads, /* max_free_threads = */ 0, num_backup_threads) + , restores_thread_pool(CurrentMetrics::RestoreThreads, CurrentMetrics::RestoreThreadsActive, num_restore_threads, /* max_free_threads = */ 0, num_restore_threads) , log(&Poco::Logger::get("BackupsWorker")) , allow_concurrent_backups(allow_concurrent_backups_) , allow_concurrent_restores(allow_concurrent_restores_) diff --git a/src/Common/CurrentMetrics.cpp b/src/Common/CurrentMetrics.cpp index 5b20d98aa01..4c773048597 100644 --- a/src/Common/CurrentMetrics.cpp +++ b/src/Common/CurrentMetrics.cpp @@ -72,6 +72,64 @@ M(GlobalThreadActive, "Number of threads in global thread pool running a task.") \ M(LocalThread, "Number of threads in local thread pools. The threads in local thread pools are taken from the global thread pool.") \ M(LocalThreadActive, "Number of threads in local thread pools running a task.") \ + M(MergeTreeDataSelectExecutorThreads, "Number of threads in the MergeTreeDataSelectExecutor thread pool.") \ + M(MergeTreeDataSelectExecutorThreadsActive, "Number of threads in the MergeTreeDataSelectExecutor thread pool running a task.") \ + M(BackupsThreads, "Number of threads in the thread pool for BACKUP.") \ + M(BackupsThreadsActive, "Number of threads in thread pool for BACKUP running a task.") \ + M(RestoreThreads, "Number of threads in the thread pool for RESTORE.") \ + M(RestoreThreadsActive, "Number of threads in the thread pool for RESTORE running a task.") \ + M(IOThreads, "Number of threads in the IO thread pool.") \ + M(IOThreadsActive, "Number of threads in the IO thread pool running a task.") \ + M(ThreadPoolRemoteFSReaderThreads, "Number of threads in the thread pool for remote_filesystem_read_method=threadpool.") \ + M(ThreadPoolRemoteFSReaderThreadsActive, "Number of threads in the thread pool for remote_filesystem_read_method=threadpool running a task.") \ + M(ThreadPoolFSReaderThreads, "Number of threads in the thread pool for local_filesystem_read_method=threadpool.") \ + M(ThreadPoolFSReaderThreadsActive, "Number of threads in the thread pool for local_filesystem_read_method=threadpool running a task.") \ + M(BackupsIOThreads, "Number of threads in the BackupsIO thread pool.") \ + M(BackupsIOThreadsActive, "Number of threads in the BackupsIO thread pool running a task.") \ + M(DiskObjectStorageAsyncThreads, "Number of threads in the async thread pool for DiskObjectStorage.") \ + M(DiskObjectStorageAsyncThreadsActive, "Number of threads in the async thread pool for DiskObjectStorage running a task.") \ + M(StorageHiveThreads, "Number of threads in the StorageHive thread pool.") \ + M(StorageHiveThreadsActive, "Number of threads in the StorageHive thread pool running a task.") \ + M(TablesLoaderThreads, "Number of threads in the tables loader thread pool.") \ + M(TablesLoaderThreadsActive, "Number of threads in the tables loader thread pool running a task.") \ + M(DatabaseOrdinaryThreads, "Number of threads in the Ordinary database thread pool.") \ + M(DatabaseOrdinaryThreadsActive, "Number of threads in the Ordinary database thread pool running a task.") \ + M(DatabaseOnDiskThreads, "Number of threads in the DatabaseOnDisk thread pool.") \ + M(DatabaseOnDiskThreadsActive, "Number of threads in the DatabaseOnDisk thread pool running a task.") \ + M(DatabaseCatalogThreads, "Number of threads in the DatabaseCatalog thread pool.") \ + M(DatabaseCatalogThreadsActive, "Number of threads in the DatabaseCatalog thread pool running a task.") \ + M(DestroyAggregatesThreads, "Number of threads in the thread pool for destroy aggregate states.") \ + M(DestroyAggregatesThreadsActive, "Number of threads in the thread pool for destroy aggregate states running a task.") \ + M(HashedDictionaryThreads, "Number of threads in the HashedDictionary thread pool.") \ + M(HashedDictionaryThreadsActive, "Number of threads in the HashedDictionary thread pool running a task.") \ + M(CacheDictionaryThreads, "Number of threads in the CacheDictionary thread pool.") \ + M(CacheDictionaryThreadsActive, "Number of threads in the CacheDictionary thread pool running a task.") \ + M(ParallelFormattingOutputFormatThreads, "Number of threads in the ParallelFormattingOutputFormatThreads thread pool.") \ + M(ParallelFormattingOutputFormatThreadsActive, "Number of threads in the ParallelFormattingOutputFormatThreads thread pool running a task.") \ + M(ParallelParsingInputFormatThreads, "Number of threads in the ParallelParsingInputFormat thread pool.") \ + M(ParallelParsingInputFormatThreadsActive, "Number of threads in the ParallelParsingInputFormat thread pool running a task.") \ + M(MergeTreeBackgroundExecutorThreads, "Number of threads in the MergeTreeBackgroundExecutor thread pool.") \ + M(MergeTreeBackgroundExecutorThreadsActive, "Number of threads in the MergeTreeBackgroundExecutor thread pool running a task.") \ + M(AsynchronousInsertThreads, "Number of threads in the AsynchronousInsert thread pool.") \ + M(AsynchronousInsertThreadsActive, "Number of threads in the AsynchronousInsert thread pool running a task.") \ + M(StartupSystemTablesThreads, "Number of threads in the StartupSystemTables thread pool.") \ + M(StartupSystemTablesThreadsActive, "Number of threads in the StartupSystemTables thread pool running a task.") \ + M(AggregatorThreads, "Number of threads in the Aggregator thread pool.") \ + M(AggregatorThreadsActive, "Number of threads in the Aggregator thread pool running a task.") \ + M(DDLWorkerThreads, "Number of threads in the DDLWorker thread pool for ON CLUSTER queries.") \ + M(DDLWorkerThreadsActive, "Number of threads in the DDLWORKER thread pool for ON CLUSTER queries running a task.") \ + M(StorageDistributedThreads, "Number of threads in the StorageDistributed thread pool.") \ + M(StorageDistributedThreadsActive, "Number of threads in the StorageDistributed thread pool running a task.") \ + M(StorageS3Threads, "Number of threads in the StorageS3 thread pool.") \ + M(StorageS3ThreadsActive, "Number of threads in the StorageS3 thread pool running a task.") \ + M(MergeTreePartsLoaderThreads, "Number of threads in the MergeTree parts loader thread pool.") \ + M(MergeTreePartsLoaderThreadsActive, "Number of threads in the MergeTree parts loader thread pool running a task.") \ + M(MergeTreePartsCleanerThreads, "Number of threads in the MergeTree parts cleaner thread pool.") \ + M(MergeTreePartsCleanerThreadsActive, "Number of threads in the MergeTree parts cleaner thread pool running a task.") \ + M(SystemReplicasThreads, "Number of threads in the system.replicas thread pool.") \ + M(SystemReplicasThreadsActive, "Number of threads in the system.replicas thread pool running a task.") \ + M(RestartReplicaThreads, "Number of threads in the RESTART REPLICA thread pool.") \ + M(RestartReplicaThreadsActive, "Number of threads in the RESTART REPLICA thread pool running a task.") \ M(DistributedFilesToInsert, "Number of pending files to process for asynchronous insertion into Distributed tables. Number of files for every shard is summed.") \ M(BrokenDistributedFilesToInsert, "Number of files for asynchronous insertion into Distributed tables that has been marked as broken. This metric will starts from 0 on start. Number of files for every shard is summed.") \ M(TablesToDropQueueSize, "Number of dropped tables, that are waiting for background data removal.") \ diff --git a/src/Common/CurrentMetrics.h b/src/Common/CurrentMetrics.h index c184ee1e7f2..0ae16e2d08d 100644 --- a/src/Common/CurrentMetrics.h +++ b/src/Common/CurrentMetrics.h @@ -4,6 +4,7 @@ #include #include #include +#include #include /** Allows to count number of simultaneously happening processes or current value of some metric. @@ -73,7 +74,10 @@ namespace CurrentMetrics public: explicit Increment(Metric metric, Value amount_ = 1) - : Increment(&values[metric], amount_) {} + : Increment(&values[metric], amount_) + { + assert(metric < CurrentMetrics::end()); + } ~Increment() { diff --git a/src/Common/ThreadPool.cpp b/src/Common/ThreadPool.cpp index caa32b61c65..11b115c9234 100644 --- a/src/Common/ThreadPool.cpp +++ b/src/Common/ThreadPool.cpp @@ -25,27 +25,36 @@ namespace CurrentMetrics { extern const Metric GlobalThread; extern const Metric GlobalThreadActive; - extern const Metric LocalThread; - extern const Metric LocalThreadActive; } template -ThreadPoolImpl::ThreadPoolImpl() - : ThreadPoolImpl(getNumberOfPhysicalCPUCores()) +ThreadPoolImpl::ThreadPoolImpl(Metric metric_threads_, Metric metric_active_threads_) + : ThreadPoolImpl(metric_threads_, metric_active_threads_, getNumberOfPhysicalCPUCores()) { } template -ThreadPoolImpl::ThreadPoolImpl(size_t max_threads_) - : ThreadPoolImpl(max_threads_, max_threads_, max_threads_) +ThreadPoolImpl::ThreadPoolImpl( + Metric metric_threads_, + Metric metric_active_threads_, + size_t max_threads_) + : ThreadPoolImpl(metric_threads_, metric_active_threads_, max_threads_, max_threads_, max_threads_) { } template -ThreadPoolImpl::ThreadPoolImpl(size_t max_threads_, size_t max_free_threads_, size_t queue_size_, bool shutdown_on_exception_) - : max_threads(max_threads_) +ThreadPoolImpl::ThreadPoolImpl( + Metric metric_threads_, + Metric metric_active_threads_, + size_t max_threads_, + size_t max_free_threads_, + size_t queue_size_, + bool shutdown_on_exception_) + : metric_threads(metric_threads_) + , metric_active_threads(metric_active_threads_) + , max_threads(max_threads_) , max_free_threads(std::min(max_free_threads_, max_threads)) , queue_size(queue_size_ ? std::max(queue_size_, max_threads) : 0 /* zero means the queue is unlimited */) , shutdown_on_exception(shutdown_on_exception_) @@ -322,8 +331,7 @@ template void ThreadPoolImpl::worker(typename std::list::iterator thread_it) { DENY_ALLOCATIONS_IN_SCOPE; - CurrentMetrics::Increment metric_all_threads( - std::is_same_v ? CurrentMetrics::GlobalThread : CurrentMetrics::LocalThread); + CurrentMetrics::Increment metric_pool_threads(metric_threads); /// Remove this thread from `threads` and detach it, that must be done before exiting from this worker. /// We can't wrap the following lambda function into `SCOPE_EXIT` because it requires `mutex` to be locked. @@ -381,8 +389,7 @@ void ThreadPoolImpl::worker(typename std::list::iterator thread_ try { - CurrentMetrics::Increment metric_active_threads( - std::is_same_v ? CurrentMetrics::GlobalThreadActive : CurrentMetrics::LocalThreadActive); + CurrentMetrics::Increment metric_active_pool_threads(metric_active_threads); job(); @@ -449,6 +456,22 @@ template class ThreadFromGlobalPoolImpl; std::unique_ptr GlobalThreadPool::the_instance; + +GlobalThreadPool::GlobalThreadPool( + size_t max_threads_, + size_t max_free_threads_, + size_t queue_size_, + const bool shutdown_on_exception_) + : FreeThreadPool( + CurrentMetrics::GlobalThread, + CurrentMetrics::GlobalThreadActive, + max_threads_, + max_free_threads_, + queue_size_, + shutdown_on_exception_) +{ +} + void GlobalThreadPool::initialize(size_t max_threads, size_t max_free_threads, size_t queue_size) { if (the_instance) diff --git a/src/Common/ThreadPool.h b/src/Common/ThreadPool.h index a1ca79a1e4b..b2f77f9693c 100644 --- a/src/Common/ThreadPool.h +++ b/src/Common/ThreadPool.h @@ -16,6 +16,7 @@ #include #include #include +#include #include /** Very simple thread pool similar to boost::threadpool. @@ -33,15 +34,25 @@ class ThreadPoolImpl { public: using Job = std::function; + using Metric = CurrentMetrics::Metric; /// Maximum number of threads is based on the number of physical cores. - ThreadPoolImpl(); + ThreadPoolImpl(Metric metric_threads_, Metric metric_active_threads_); /// Size is constant. Up to num_threads are created on demand and then run until shutdown. - explicit ThreadPoolImpl(size_t max_threads_); + explicit ThreadPoolImpl( + Metric metric_threads_, + Metric metric_active_threads_, + size_t max_threads_); /// queue_size - maximum number of running plus scheduled jobs. It can be greater than max_threads. Zero means unlimited. - ThreadPoolImpl(size_t max_threads_, size_t max_free_threads_, size_t queue_size_, bool shutdown_on_exception_ = true); + ThreadPoolImpl( + Metric metric_threads_, + Metric metric_active_threads_, + size_t max_threads_, + size_t max_free_threads_, + size_t queue_size_, + bool shutdown_on_exception_ = true); /// Add new job. Locks until number of scheduled jobs is less than maximum or exception in one of threads was thrown. /// If any thread was throw an exception, first exception will be rethrown from this method, @@ -96,6 +107,9 @@ private: std::condition_variable job_finished; std::condition_variable new_job_or_shutdown; + Metric metric_threads; + Metric metric_active_threads; + size_t max_threads; size_t max_free_threads; size_t queue_size; @@ -159,12 +173,11 @@ class GlobalThreadPool : public FreeThreadPool, private boost::noncopyable { static std::unique_ptr the_instance; - GlobalThreadPool(size_t max_threads_, size_t max_free_threads_, - size_t queue_size_, const bool shutdown_on_exception_) - : FreeThreadPool(max_threads_, max_free_threads_, queue_size_, - shutdown_on_exception_) - { - } + GlobalThreadPool( + size_t max_threads_, + size_t max_free_threads_, + size_t queue_size_, + bool shutdown_on_exception_); public: static void initialize(size_t max_threads = 10000, size_t max_free_threads = 1000, size_t queue_size = 10000); diff --git a/src/Common/examples/parallel_aggregation.cpp b/src/Common/examples/parallel_aggregation.cpp index bd252b330f3..cf7a3197fef 100644 --- a/src/Common/examples/parallel_aggregation.cpp +++ b/src/Common/examples/parallel_aggregation.cpp @@ -17,6 +17,7 @@ #include #include +#include using Key = UInt64; @@ -28,6 +29,12 @@ using Map = HashMap; using MapTwoLevel = TwoLevelHashMap; +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + struct SmallLock { std::atomic locked {false}; @@ -247,7 +254,7 @@ int main(int argc, char ** argv) std::cerr << std::fixed << std::setprecision(2); - ThreadPool pool(num_threads); + ThreadPool pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, num_threads); Source data(n); diff --git a/src/Common/examples/parallel_aggregation2.cpp b/src/Common/examples/parallel_aggregation2.cpp index 6c20f46ab0e..1b0ad760490 100644 --- a/src/Common/examples/parallel_aggregation2.cpp +++ b/src/Common/examples/parallel_aggregation2.cpp @@ -17,6 +17,7 @@ #include #include +#include using Key = UInt64; @@ -24,6 +25,12 @@ using Value = UInt64; using Source = std::vector; +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + template struct AggregateIndependent { @@ -274,7 +281,7 @@ int main(int argc, char ** argv) std::cerr << std::fixed << std::setprecision(2); - ThreadPool pool(num_threads); + ThreadPool pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, num_threads); Source data(n); diff --git a/src/Common/examples/thread_creation_latency.cpp b/src/Common/examples/thread_creation_latency.cpp index 351f709013a..2434759c968 100644 --- a/src/Common/examples/thread_creation_latency.cpp +++ b/src/Common/examples/thread_creation_latency.cpp @@ -6,6 +6,7 @@ #include #include #include +#include int value = 0; @@ -14,6 +15,12 @@ static void f() { ++value; } static void * g(void *) { f(); return {}; } +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + namespace DB { namespace ErrorCodes @@ -65,7 +72,7 @@ int main(int argc, char ** argv) test(n, "Create and destroy ThreadPool each iteration", [] { - ThreadPool tp(1); + ThreadPool tp(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, 1); tp.scheduleOrThrowOnError(f); tp.wait(); }); @@ -86,7 +93,7 @@ int main(int argc, char ** argv) }); { - ThreadPool tp(1); + ThreadPool tp(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, 1); test(n, "Schedule job for Threadpool each iteration", [&tp] { @@ -96,7 +103,7 @@ int main(int argc, char ** argv) } { - ThreadPool tp(128); + ThreadPool tp(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, 128); test(n, "Schedule job for Threadpool with 128 threads each iteration", [&tp] { diff --git a/src/Common/tests/gtest_thread_pool_concurrent_wait.cpp b/src/Common/tests/gtest_thread_pool_concurrent_wait.cpp index f5f14739e39..f93017129dd 100644 --- a/src/Common/tests/gtest_thread_pool_concurrent_wait.cpp +++ b/src/Common/tests/gtest_thread_pool_concurrent_wait.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -7,6 +8,12 @@ */ +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + TEST(ThreadPool, ConcurrentWait) { auto worker = [] @@ -18,14 +25,14 @@ TEST(ThreadPool, ConcurrentWait) constexpr size_t num_threads = 4; constexpr size_t num_jobs = 4; - ThreadPool pool(num_threads); + ThreadPool pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, num_threads); for (size_t i = 0; i < num_jobs; ++i) pool.scheduleOrThrowOnError(worker); constexpr size_t num_waiting_threads = 4; - ThreadPool waiting_pool(num_waiting_threads); + ThreadPool waiting_pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, num_waiting_threads); for (size_t i = 0; i < num_waiting_threads; ++i) waiting_pool.scheduleOrThrowOnError([&pool] { pool.wait(); }); diff --git a/src/Common/tests/gtest_thread_pool_global_full.cpp b/src/Common/tests/gtest_thread_pool_global_full.cpp index 583d43be1bb..1b2ded9c7e1 100644 --- a/src/Common/tests/gtest_thread_pool_global_full.cpp +++ b/src/Common/tests/gtest_thread_pool_global_full.cpp @@ -2,10 +2,17 @@ #include #include +#include #include +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + /// Test what happens if local ThreadPool cannot create a ThreadFromGlobalPool. /// There was a bug: if local ThreadPool cannot allocate even a single thread, /// the job will be scheduled but never get executed. @@ -27,7 +34,7 @@ TEST(ThreadPool, GlobalFull1) auto func = [&] { ++counter; while (counter != num_jobs) {} }; - ThreadPool pool(num_jobs); + ThreadPool pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, num_jobs); for (size_t i = 0; i < capacity; ++i) pool.scheduleOrThrowOnError(func); @@ -65,11 +72,11 @@ TEST(ThreadPool, GlobalFull2) std::atomic counter = 0; auto func = [&] { ++counter; while (counter != capacity + 1) {} }; - ThreadPool pool(capacity, 0, capacity); + ThreadPool pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, capacity, 0, capacity); for (size_t i = 0; i < capacity; ++i) pool.scheduleOrThrowOnError(func); - ThreadPool another_pool(1); + ThreadPool another_pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, 1); EXPECT_THROW(another_pool.scheduleOrThrowOnError(func), DB::Exception); ++counter; diff --git a/src/Common/tests/gtest_thread_pool_loop.cpp b/src/Common/tests/gtest_thread_pool_loop.cpp index 15915044652..556c39df949 100644 --- a/src/Common/tests/gtest_thread_pool_loop.cpp +++ b/src/Common/tests/gtest_thread_pool_loop.cpp @@ -1,10 +1,17 @@ #include #include #include +#include #include +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + TEST(ThreadPool, Loop) { std::atomic res{0}; @@ -12,7 +19,7 @@ TEST(ThreadPool, Loop) for (size_t i = 0; i < 1000; ++i) { size_t threads = 16; - ThreadPool pool(threads); + ThreadPool pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, threads); for (size_t j = 0; j < threads; ++j) pool.scheduleOrThrowOnError([&] { ++res; }); pool.wait(); diff --git a/src/Common/tests/gtest_thread_pool_schedule_exception.cpp b/src/Common/tests/gtest_thread_pool_schedule_exception.cpp index 69362c34cd2..176e469d5ef 100644 --- a/src/Common/tests/gtest_thread_pool_schedule_exception.cpp +++ b/src/Common/tests/gtest_thread_pool_schedule_exception.cpp @@ -1,13 +1,20 @@ #include #include #include +#include #include +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + static bool check() { - ThreadPool pool(10); + ThreadPool pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, 10); /// The throwing thread. pool.scheduleOrThrowOnError([] { throw std::runtime_error("Hello, world!"); }); diff --git a/src/Databases/DatabaseOnDisk.cpp b/src/Databases/DatabaseOnDisk.cpp index 4d9e22bd15d..01afbdcaa57 100644 --- a/src/Databases/DatabaseOnDisk.cpp +++ b/src/Databases/DatabaseOnDisk.cpp @@ -17,14 +17,21 @@ #include #include #include +#include +#include +#include #include #include -#include #include -#include namespace fs = std::filesystem; +namespace CurrentMetrics +{ + extern const Metric DatabaseOnDiskThreads; + extern const Metric DatabaseOnDiskThreadsActive; +} + namespace DB { @@ -620,7 +627,7 @@ void DatabaseOnDisk::iterateMetadataFiles(ContextPtr local_context, const Iterat } /// Read and parse metadata in parallel - ThreadPool pool; + ThreadPool pool(CurrentMetrics::DatabaseOnDiskThreads, CurrentMetrics::DatabaseOnDiskThreadsActive); for (const auto & file : metadata_files) { pool.scheduleOrThrowOnError([&]() diff --git a/src/Databases/DatabaseOrdinary.cpp b/src/Databases/DatabaseOrdinary.cpp index 49250602132..0db16f80656 100644 --- a/src/Databases/DatabaseOrdinary.cpp +++ b/src/Databases/DatabaseOrdinary.cpp @@ -25,9 +25,16 @@ #include #include #include +#include namespace fs = std::filesystem; +namespace CurrentMetrics +{ + extern const Metric DatabaseOrdinaryThreads; + extern const Metric DatabaseOrdinaryThreadsActive; +} + namespace DB { @@ -99,7 +106,7 @@ void DatabaseOrdinary::loadStoredObjects( std::atomic dictionaries_processed{0}; std::atomic tables_processed{0}; - ThreadPool pool; + ThreadPool pool(CurrentMetrics::DatabaseOrdinaryThreads, CurrentMetrics::DatabaseOrdinaryThreadsActive); /// We must attach dictionaries before attaching tables /// because while we're attaching tables we may need to have some dictionaries attached diff --git a/src/Databases/TablesLoader.cpp b/src/Databases/TablesLoader.cpp index 5d66f49554d..177b32daa2e 100644 --- a/src/Databases/TablesLoader.cpp +++ b/src/Databases/TablesLoader.cpp @@ -8,8 +8,15 @@ #include #include #include +#include #include +namespace CurrentMetrics +{ + extern const Metric TablesLoaderThreads; + extern const Metric TablesLoaderThreadsActive; +} + namespace DB { @@ -31,12 +38,13 @@ void logAboutProgress(Poco::Logger * log, size_t processed, size_t total, Atomic } TablesLoader::TablesLoader(ContextMutablePtr global_context_, Databases databases_, LoadingStrictnessLevel strictness_mode_) -: global_context(global_context_) -, databases(std::move(databases_)) -, strictness_mode(strictness_mode_) -, referential_dependencies("ReferentialDeps") -, loading_dependencies("LoadingDeps") -, all_loading_dependencies("LoadingDeps") + : global_context(global_context_) + , databases(std::move(databases_)) + , strictness_mode(strictness_mode_) + , referential_dependencies("ReferentialDeps") + , loading_dependencies("LoadingDeps") + , all_loading_dependencies("LoadingDeps") + , pool(CurrentMetrics::TablesLoaderThreads, CurrentMetrics::TablesLoaderThreadsActive) { metadata.default_database = global_context->getCurrentDatabase(); log = &Poco::Logger::get("TablesLoader"); diff --git a/src/Dictionaries/CacheDictionaryUpdateQueue.cpp b/src/Dictionaries/CacheDictionaryUpdateQueue.cpp index 1fdaf10c57c..09d5bed18b8 100644 --- a/src/Dictionaries/CacheDictionaryUpdateQueue.cpp +++ b/src/Dictionaries/CacheDictionaryUpdateQueue.cpp @@ -2,8 +2,15 @@ #include +#include #include +namespace CurrentMetrics +{ + extern const Metric CacheDictionaryThreads; + extern const Metric CacheDictionaryThreadsActive; +} + namespace DB { @@ -26,7 +33,7 @@ CacheDictionaryUpdateQueue::CacheDictionaryUpdateQueue( , configuration(configuration_) , update_func(std::move(update_func_)) , update_queue(configuration.max_update_queue_size) - , update_pool(configuration.max_threads_for_updates) + , update_pool(CurrentMetrics::CacheDictionaryThreads, CurrentMetrics::CacheDictionaryThreadsActive, configuration.max_threads_for_updates) { for (size_t i = 0; i < configuration.max_threads_for_updates; ++i) update_pool.scheduleOrThrowOnError([this] { updateThreadFunction(); }); diff --git a/src/Dictionaries/HashedDictionary.cpp b/src/Dictionaries/HashedDictionary.cpp index d6c9ac50dbe..0e5d18363e9 100644 --- a/src/Dictionaries/HashedDictionary.cpp +++ b/src/Dictionaries/HashedDictionary.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -21,6 +22,11 @@ #include #include +namespace CurrentMetrics +{ + extern const Metric HashedDictionaryThreads; + extern const Metric HashedDictionaryThreadsActive; +} namespace { @@ -60,7 +66,7 @@ public: explicit ParallelDictionaryLoader(HashedDictionary & dictionary_) : dictionary(dictionary_) , shards(dictionary.configuration.shards) - , pool(shards) + , pool(CurrentMetrics::HashedDictionaryThreads, CurrentMetrics::HashedDictionaryThreadsActive, shards) , shards_queues(shards) { UInt64 backlog = dictionary.configuration.shard_load_queue_backlog; @@ -213,7 +219,7 @@ HashedDictionary::~HashedDictionary() return; size_t shards = std::max(configuration.shards, 1); - ThreadPool pool(shards); + ThreadPool pool(CurrentMetrics::HashedDictionaryThreads, CurrentMetrics::HashedDictionaryThreadsActive, shards); size_t hash_tables_count = 0; auto schedule_destroy = [&hash_tables_count, &pool](auto & container) diff --git a/src/Disks/IO/ThreadPoolReader.cpp b/src/Disks/IO/ThreadPoolReader.cpp index 18b283b0ff3..3a071d13122 100644 --- a/src/Disks/IO/ThreadPoolReader.cpp +++ b/src/Disks/IO/ThreadPoolReader.cpp @@ -61,6 +61,8 @@ namespace ProfileEvents namespace CurrentMetrics { extern const Metric Read; + extern const Metric ThreadPoolFSReaderThreads; + extern const Metric ThreadPoolFSReaderThreadsActive; } @@ -85,7 +87,7 @@ static bool hasBugInPreadV2() #endif ThreadPoolReader::ThreadPoolReader(size_t pool_size, size_t queue_size_) - : pool(pool_size, pool_size, queue_size_) + : pool(CurrentMetrics::ThreadPoolFSReaderThreads, CurrentMetrics::ThreadPoolFSReaderThreadsActive, pool_size, pool_size, queue_size_) { } diff --git a/src/Disks/IO/ThreadPoolRemoteFSReader.cpp b/src/Disks/IO/ThreadPoolRemoteFSReader.cpp index c2d3ee8b53d..1980f57c876 100644 --- a/src/Disks/IO/ThreadPoolRemoteFSReader.cpp +++ b/src/Disks/IO/ThreadPoolRemoteFSReader.cpp @@ -26,6 +26,8 @@ namespace ProfileEvents namespace CurrentMetrics { extern const Metric RemoteRead; + extern const Metric ThreadPoolRemoteFSReaderThreads; + extern const Metric ThreadPoolRemoteFSReaderThreadsActive; } namespace DB @@ -60,7 +62,7 @@ IAsynchronousReader::Result RemoteFSFileDescriptor::readInto(char * data, size_t ThreadPoolRemoteFSReader::ThreadPoolRemoteFSReader(size_t pool_size, size_t queue_size_) - : pool(pool_size, pool_size, queue_size_) + : pool(CurrentMetrics::ThreadPoolRemoteFSReaderThreads, CurrentMetrics::ThreadPoolRemoteFSReaderThreadsActive, pool_size, pool_size, queue_size_) { } diff --git a/src/Disks/ObjectStorages/DiskObjectStorage.cpp b/src/Disks/ObjectStorages/DiskObjectStorage.cpp index 44cb80558af..6143f0620b8 100644 --- a/src/Disks/ObjectStorages/DiskObjectStorage.cpp +++ b/src/Disks/ObjectStorages/DiskObjectStorage.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,13 @@ #include #include +namespace CurrentMetrics +{ + extern const Metric DiskObjectStorageAsyncThreads; + extern const Metric DiskObjectStorageAsyncThreadsActive; +} + + namespace DB { @@ -38,7 +46,8 @@ class AsyncThreadPoolExecutor : public Executor public: AsyncThreadPoolExecutor(const String & name_, int thread_pool_size) : name(name_) - , pool(ThreadPool(thread_pool_size)) {} + , pool(CurrentMetrics::DiskObjectStorageAsyncThreads, CurrentMetrics::DiskObjectStorageAsyncThreadsActive, thread_pool_size) + {} std::future execute(std::function task) override { diff --git a/src/IO/BackupIOThreadPool.cpp b/src/IO/BackupsIOThreadPool.cpp similarity index 59% rename from src/IO/BackupIOThreadPool.cpp rename to src/IO/BackupsIOThreadPool.cpp index 067fc54b1ae..0829553945a 100644 --- a/src/IO/BackupIOThreadPool.cpp +++ b/src/IO/BackupsIOThreadPool.cpp @@ -1,5 +1,12 @@ #include -#include "Core/Field.h" +#include +#include + +namespace CurrentMetrics +{ + extern const Metric BackupsIOThreads; + extern const Metric BackupsIOThreadsActive; +} namespace DB { @@ -18,7 +25,13 @@ void BackupsIOThreadPool::initialize(size_t max_threads, size_t max_free_threads throw Exception(ErrorCodes::LOGICAL_ERROR, "The BackupsIO thread pool is initialized twice"); } - instance = std::make_unique(max_threads, max_free_threads, queue_size, false /*shutdown_on_exception*/); + instance = std::make_unique( + CurrentMetrics::BackupsIOThreads, + CurrentMetrics::BackupsIOThreadsActive, + max_threads, + max_free_threads, + queue_size, + /* shutdown_on_exception= */ false); } ThreadPool & BackupsIOThreadPool::get() diff --git a/src/IO/IOThreadPool.cpp b/src/IO/IOThreadPool.cpp index 4014d00d8b8..98bb6ffe6a7 100644 --- a/src/IO/IOThreadPool.cpp +++ b/src/IO/IOThreadPool.cpp @@ -1,5 +1,12 @@ #include -#include "Core/Field.h" +#include +#include + +namespace CurrentMetrics +{ + extern const Metric IOThreads; + extern const Metric IOThreadsActive; +} namespace DB { @@ -18,7 +25,13 @@ void IOThreadPool::initialize(size_t max_threads, size_t max_free_threads, size_ throw Exception(ErrorCodes::LOGICAL_ERROR, "The IO thread pool is initialized twice"); } - instance = std::make_unique(max_threads, max_free_threads, queue_size, false /*shutdown_on_exception*/); + instance = std::make_unique( + CurrentMetrics::IOThreads, + CurrentMetrics::IOThreadsActive, + max_threads, + max_free_threads, + queue_size, + /* shutdown_on_exception= */ false); } ThreadPool & IOThreadPool::get() diff --git a/src/Interpreters/Aggregator.cpp b/src/Interpreters/Aggregator.cpp index b0bcea23449..d6fbf072d05 100644 --- a/src/Interpreters/Aggregator.cpp +++ b/src/Interpreters/Aggregator.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,8 @@ namespace ProfileEvents namespace CurrentMetrics { extern const Metric TemporaryFilesForAggregation; + extern const Metric AggregatorThreads; + extern const Metric AggregatorThreadsActive; } namespace DB @@ -2397,7 +2400,7 @@ BlocksList Aggregator::convertToBlocks(AggregatedDataVariants & data_variants, b std::unique_ptr thread_pool; if (max_threads > 1 && data_variants.sizeWithoutOverflowRow() > 100000 /// TODO Make a custom threshold. && data_variants.isTwoLevel()) /// TODO Use the shared thread pool with the `merge` function. - thread_pool = std::make_unique(max_threads); + thread_pool = std::make_unique(CurrentMetrics::AggregatorThreads, CurrentMetrics::AggregatorThreadsActive, max_threads); if (data_variants.without_key) blocks.emplace_back(prepareBlockAndFillWithoutKey( @@ -2592,7 +2595,7 @@ void NO_INLINE Aggregator::mergeDataOnlyExistingKeysImpl( void NO_INLINE Aggregator::mergeWithoutKeyDataImpl( ManyAggregatedDataVariants & non_empty_data) const { - ThreadPool thread_pool{params.max_threads}; + ThreadPool thread_pool{CurrentMetrics::AggregatorThreads, CurrentMetrics::AggregatorThreadsActive, params.max_threads}; AggregatedDataVariantsPtr & res = non_empty_data[0]; @@ -3065,7 +3068,7 @@ void Aggregator::mergeBlocks(BucketToBlocks bucket_to_blocks, AggregatedDataVari std::unique_ptr thread_pool; if (max_threads > 1 && total_input_rows > 100000) /// TODO Make a custom threshold. - thread_pool = std::make_unique(max_threads); + thread_pool = std::make_unique(CurrentMetrics::AggregatorThreads, CurrentMetrics::AggregatorThreadsActive, max_threads); for (const auto & bucket_blocks : bucket_to_blocks) { diff --git a/src/Interpreters/AsynchronousInsertQueue.cpp b/src/Interpreters/AsynchronousInsertQueue.cpp index 590cbc9ba83..2dd2409442e 100644 --- a/src/Interpreters/AsynchronousInsertQueue.cpp +++ b/src/Interpreters/AsynchronousInsertQueue.cpp @@ -32,6 +32,8 @@ namespace CurrentMetrics { extern const Metric PendingAsyncInsert; + extern const Metric AsynchronousInsertThreads; + extern const Metric AsynchronousInsertThreadsActive; } namespace ProfileEvents @@ -130,7 +132,7 @@ AsynchronousInsertQueue::AsynchronousInsertQueue(ContextPtr context_, size_t poo : WithContext(context_) , pool_size(pool_size_) , queue_shards(pool_size) - , pool(pool_size) + , pool(CurrentMetrics::AsynchronousInsertThreads, CurrentMetrics::AsynchronousInsertThreadsActive, pool_size) { if (!pool_size) throw Exception(ErrorCodes::BAD_ARGUMENTS, "pool_size cannot be zero"); diff --git a/src/Interpreters/DDLWorker.cpp b/src/Interpreters/DDLWorker.cpp index f70c0010585..22bece0ef04 100644 --- a/src/Interpreters/DDLWorker.cpp +++ b/src/Interpreters/DDLWorker.cpp @@ -40,6 +40,12 @@ namespace fs = std::filesystem; +namespace CurrentMetrics +{ + extern const Metric DDLWorkerThreads; + extern const Metric DDLWorkerThreadsActive; +} + namespace DB { @@ -85,7 +91,7 @@ DDLWorker::DDLWorker( { LOG_WARNING(log, "DDLWorker is configured to use multiple threads. " "It's not recommended because queries can be reordered. Also it may cause some unknown issues to appear."); - worker_pool = std::make_unique(pool_size); + worker_pool = std::make_unique(CurrentMetrics::DDLWorkerThreads, CurrentMetrics::DDLWorkerThreadsActive, pool_size); } queue_dir = zk_root_dir; @@ -1084,7 +1090,7 @@ void DDLWorker::runMainThread() /// It will wait for all threads in pool to finish and will not rethrow exceptions (if any). /// We create new thread pool to forget previous exceptions. if (1 < pool_size) - worker_pool = std::make_unique(pool_size); + worker_pool = std::make_unique(CurrentMetrics::DDLWorkerThreads, CurrentMetrics::DDLWorkerThreadsActive, pool_size); /// Clear other in-memory state, like server just started. current_tasks.clear(); last_skipped_entry_name.reset(); @@ -1123,7 +1129,7 @@ void DDLWorker::runMainThread() initialized = false; /// Wait for pending async tasks if (1 < pool_size) - worker_pool = std::make_unique(pool_size); + worker_pool = std::make_unique(CurrentMetrics::DDLWorkerThreads, CurrentMetrics::DDLWorkerThreadsActive, pool_size); LOG_INFO(log, "Lost ZooKeeper connection, will try to connect again: {}", getCurrentExceptionMessage(true)); } else diff --git a/src/Interpreters/DDLWorker.h b/src/Interpreters/DDLWorker.h index 65ef4b440a1..6cf034edae8 100644 --- a/src/Interpreters/DDLWorker.h +++ b/src/Interpreters/DDLWorker.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include diff --git a/src/Interpreters/DatabaseCatalog.cpp b/src/Interpreters/DatabaseCatalog.cpp index b11a973c7b7..6af09972f1c 100644 --- a/src/Interpreters/DatabaseCatalog.cpp +++ b/src/Interpreters/DatabaseCatalog.cpp @@ -36,6 +36,8 @@ namespace CurrentMetrics { extern const Metric TablesToDropQueueSize; + extern const Metric DatabaseCatalogThreads; + extern const Metric DatabaseCatalogThreadsActive; } namespace DB @@ -850,7 +852,7 @@ void DatabaseCatalog::loadMarkedAsDroppedTables() LOG_INFO(log, "Found {} partially dropped tables. Will load them and retry removal.", dropped_metadata.size()); - ThreadPool pool; + ThreadPool pool(CurrentMetrics::DatabaseCatalogThreads, CurrentMetrics::DatabaseCatalogThreadsActive); for (const auto & elem : dropped_metadata) { pool.scheduleOrThrowOnError([&]() diff --git a/src/Interpreters/InterpreterSystemQuery.cpp b/src/Interpreters/InterpreterSystemQuery.cpp index fb6b1635f28..cd3e5579e12 100644 --- a/src/Interpreters/InterpreterSystemQuery.cpp +++ b/src/Interpreters/InterpreterSystemQuery.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,12 @@ #include "config.h" +namespace CurrentMetrics +{ + extern const Metric RestartReplicaThreads; + extern const Metric RestartReplicaThreadsActive; +} + namespace DB { @@ -685,7 +692,8 @@ void InterpreterSystemQuery::restartReplicas(ContextMutablePtr system_context) for (auto & guard : guards) guard.second = catalog.getDDLGuard(guard.first.database_name, guard.first.table_name); - ThreadPool pool(std::min(static_cast(getNumberOfPhysicalCPUCores()), replica_names.size())); + size_t threads = std::min(static_cast(getNumberOfPhysicalCPUCores()), replica_names.size()); + ThreadPool pool(CurrentMetrics::RestartReplicaThreads, CurrentMetrics::RestartReplicaThreadsActive, threads); for (auto & replica : replica_names) { diff --git a/src/Interpreters/loadMetadata.cpp b/src/Interpreters/loadMetadata.cpp index 47ccff57419..83af2684322 100644 --- a/src/Interpreters/loadMetadata.cpp +++ b/src/Interpreters/loadMetadata.cpp @@ -16,16 +16,24 @@ #include #include -#include +#include #include -#include #include +#include + +#include #define ORDINARY_TO_ATOMIC_PREFIX ".tmp_convert." namespace fs = std::filesystem; +namespace CurrentMetrics +{ + extern const Metric StartupSystemTablesThreads; + extern const Metric StartupSystemTablesThreadsActive; +} + namespace DB { @@ -366,7 +374,7 @@ static void maybeConvertOrdinaryDatabaseToAtomic(ContextMutablePtr context, cons if (!tables_started) { /// It's not quite correct to run DDL queries while database is not started up. - ThreadPool pool; + ThreadPool pool(CurrentMetrics::StartupSystemTablesThreads, CurrentMetrics::StartupSystemTablesThreadsActive); DatabaseCatalog::instance().getSystemDatabase()->startupTables(pool, LoadingStrictnessLevel::FORCE_RESTORE); } @@ -461,7 +469,7 @@ void convertDatabasesEnginesIfNeed(ContextMutablePtr context) void startupSystemTables() { - ThreadPool pool; + ThreadPool pool(CurrentMetrics::StartupSystemTablesThreads, CurrentMetrics::StartupSystemTablesThreadsActive); DatabaseCatalog::instance().getSystemDatabase()->startupTables(pool, LoadingStrictnessLevel::FORCE_RESTORE); } diff --git a/src/Processors/Formats/Impl/ParallelFormattingOutputFormat.h b/src/Processors/Formats/Impl/ParallelFormattingOutputFormat.h index 7ea19f01e01..790d05e83dd 100644 --- a/src/Processors/Formats/Impl/ParallelFormattingOutputFormat.h +++ b/src/Processors/Formats/Impl/ParallelFormattingOutputFormat.h @@ -7,7 +7,8 @@ #include #include #include -#include "IO/WriteBufferFromString.h" +#include +#include #include #include #include @@ -17,6 +18,12 @@ #include #include +namespace CurrentMetrics +{ + extern const Metric ParallelFormattingOutputFormatThreads; + extern const Metric ParallelFormattingOutputFormatThreadsActive; +} + namespace DB { @@ -74,7 +81,7 @@ public: explicit ParallelFormattingOutputFormat(Params params) : IOutputFormat(params.header, params.out) , internal_formatter_creator(params.internal_formatter_creator) - , pool(params.max_threads_for_parallel_formatting) + , pool(CurrentMetrics::ParallelFormattingOutputFormatThreads, CurrentMetrics::ParallelFormattingOutputFormatThreadsActive, params.max_threads_for_parallel_formatting) { LOG_TEST(&Poco::Logger::get("ParallelFormattingOutputFormat"), "Parallel formatting is being used"); diff --git a/src/Processors/Formats/Impl/ParallelParsingInputFormat.h b/src/Processors/Formats/Impl/ParallelParsingInputFormat.h index 03fb2d650dc..97df9308dbf 100644 --- a/src/Processors/Formats/Impl/ParallelParsingInputFormat.h +++ b/src/Processors/Formats/Impl/ParallelParsingInputFormat.h @@ -5,14 +5,21 @@ #include #include #include +#include +#include #include #include #include #include -#include #include +namespace CurrentMetrics +{ + extern const Metric ParallelParsingInputFormatThreads; + extern const Metric ParallelParsingInputFormatThreadsActive; +} + namespace DB { @@ -94,7 +101,7 @@ public: , min_chunk_bytes(params.min_chunk_bytes) , max_block_size(params.max_block_size) , is_server(params.is_server) - , pool(params.max_threads) + , pool(CurrentMetrics::ParallelParsingInputFormatThreads, CurrentMetrics::ParallelParsingInputFormatThreadsActive, params.max_threads) { // One unit for each thread, including segmentator and reader, plus a // couple more units so that the segmentation thread doesn't spuriously diff --git a/src/Processors/Transforms/AggregatingTransform.h b/src/Processors/Transforms/AggregatingTransform.h index 3abd2ac3346..048b69adae6 100644 --- a/src/Processors/Transforms/AggregatingTransform.h +++ b/src/Processors/Transforms/AggregatingTransform.h @@ -6,6 +6,13 @@ #include #include #include +#include + +namespace CurrentMetrics +{ + extern const Metric DestroyAggregatesThreads; + extern const Metric DestroyAggregatesThreadsActive; +} namespace DB { @@ -84,7 +91,10 @@ struct ManyAggregatedData // Aggregation states destruction may be very time-consuming. // In the case of a query with LIMIT, most states won't be destroyed during conversion to blocks. // Without the following code, they would be destroyed in the destructor of AggregatedDataVariants in the current thread (i.e. sequentially). - const auto pool = std::make_unique(variants.size()); + const auto pool = std::make_unique( + CurrentMetrics::DestroyAggregatesThreads, + CurrentMetrics::DestroyAggregatesThreadsActive, + variants.size()); for (auto && variant : variants) { diff --git a/src/Storages/Hive/StorageHive.cpp b/src/Storages/Hive/StorageHive.cpp index 85e6341eb5a..71830e8bc86 100644 --- a/src/Storages/Hive/StorageHive.cpp +++ b/src/Storages/Hive/StorageHive.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,11 @@ #include #include +namespace CurrentMetrics +{ + extern const Metric StorageHiveThreads; + extern const Metric StorageHiveThreadsActive; +} namespace DB { @@ -844,7 +850,7 @@ HiveFiles StorageHive::collectHiveFiles( Int64 hive_max_query_partitions = context_->getSettings().max_partitions_to_read; /// Mutext to protect hive_files, which maybe appended in multiple threads std::mutex hive_files_mutex; - ThreadPool pool{max_threads}; + ThreadPool pool{CurrentMetrics::StorageHiveThreads, CurrentMetrics::StorageHiveThreadsActive, max_threads}; if (!partitions.empty()) { for (const auto & partition : partitions) diff --git a/src/Storages/MergeTree/MergeTreeBackgroundExecutor.h b/src/Storages/MergeTree/MergeTreeBackgroundExecutor.h index 5c47d20865b..a27fb18c0fe 100644 --- a/src/Storages/MergeTree/MergeTreeBackgroundExecutor.h +++ b/src/Storages/MergeTree/MergeTreeBackgroundExecutor.h @@ -21,6 +21,12 @@ #include +namespace CurrentMetrics +{ + extern const Metric MergeTreeBackgroundExecutorThreads; + extern const Metric MergeTreeBackgroundExecutorThreadsActive; +} + namespace DB { namespace ErrorCodes @@ -255,6 +261,7 @@ public: , max_tasks_count(max_tasks_count_) , metric(metric_) , max_tasks_metric(max_tasks_metric_, 2 * max_tasks_count) // active + pending + , pool(CurrentMetrics::MergeTreeBackgroundExecutorThreads, CurrentMetrics::MergeTreeBackgroundExecutorThreadsActive) { if (max_tasks_count == 0) throw Exception(ErrorCodes::INVALID_CONFIG_PARAMETER, "Task count for MergeTreeBackgroundExecutor must not be zero"); diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index fef6ccbdaeb..f9848b572f9 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,10 @@ namespace ProfileEvents namespace CurrentMetrics { extern const Metric DelayedInserts; + extern const Metric MergeTreePartsLoaderThreads; + extern const Metric MergeTreePartsLoaderThreadsActive; + extern const Metric MergeTreePartsCleanerThreads; + extern const Metric MergeTreePartsCleanerThreadsActive; } @@ -1567,7 +1572,7 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks) } } - ThreadPool pool(disks.size()); + ThreadPool pool(CurrentMetrics::MergeTreePartsLoaderThreads, CurrentMetrics::MergeTreePartsLoaderThreadsActive, disks.size()); std::vector parts_to_load_by_disk(disks.size()); for (size_t i = 0; i < disks.size(); ++i) @@ -2299,7 +2304,7 @@ void MergeTreeData::clearPartsFromFilesystemImpl(const DataPartsVector & parts_t /// Parallel parts removal. size_t num_threads = std::min(settings->max_part_removal_threads, parts_to_remove.size()); std::mutex part_names_mutex; - ThreadPool pool(num_threads); + ThreadPool pool(CurrentMetrics::MergeTreePartsCleanerThreads, CurrentMetrics::MergeTreePartsCleanerThreadsActive, num_threads); bool has_zero_copy_parts = false; if (settings->allow_remote_fs_zero_copy_replication && dynamic_cast(this) != nullptr) diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index ff8862f0f36..2039912106c 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,12 @@ #include +namespace CurrentMetrics +{ + extern const Metric MergeTreeDataSelectExecutorThreads; + extern const Metric MergeTreeDataSelectExecutorThreadsActive; +} + namespace DB { @@ -1077,7 +1084,10 @@ RangesInDataParts MergeTreeDataSelectExecutor::filterPartsByPrimaryKeyAndSkipInd else { /// Parallel loading of data parts. - ThreadPool pool(num_threads); + ThreadPool pool( + CurrentMetrics::MergeTreeDataSelectExecutorThreads, + CurrentMetrics::MergeTreeDataSelectExecutorThreadsActive, + num_threads); for (size_t part_index = 0; part_index < parts.size(); ++part_index) pool.scheduleOrThrowOnError([&, part_index, thread_group = CurrentThread::getGroup()] diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 0be4ae3a79f..4dddd22fae5 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -126,6 +127,12 @@ namespace ProfileEvents extern const Event DistributedDelayedInsertsMilliseconds; } +namespace CurrentMetrics +{ + extern const Metric StorageDistributedThreads; + extern const Metric StorageDistributedThreadsActive; +} + namespace DB { @@ -1436,7 +1443,7 @@ void StorageDistributed::initializeFromDisk() const auto & disks = data_volume->getDisks(); /// Make initialization for large number of disks parallel. - ThreadPool pool(disks.size()); + ThreadPool pool(CurrentMetrics::StorageDistributedThreads, CurrentMetrics::StorageDistributedThreadsActive, disks.size()); for (const DiskPtr & disk : disks) { diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index e17860af288..c7d2915f32f 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -53,8 +52,10 @@ #include +#include #include #include +#include #include #include @@ -65,6 +66,12 @@ namespace fs = std::filesystem; +namespace CurrentMetrics +{ + extern const Metric StorageS3Threads; + extern const Metric StorageS3ThreadsActive; +} + namespace ProfileEvents { extern const Event S3DeleteObjects; @@ -150,7 +157,7 @@ public: , object_infos(object_infos_) , read_keys(read_keys_) , request_settings(request_settings_) - , list_objects_pool(1) + , list_objects_pool(CurrentMetrics::StorageS3Threads, CurrentMetrics::StorageS3ThreadsActive, 1) , list_objects_scheduler(threadPoolCallbackRunner(list_objects_pool, "ListObjects")) { if (globbed_uri.bucket.find_first_of("*?{") != globbed_uri.bucket.npos) @@ -574,7 +581,7 @@ StorageS3Source::StorageS3Source( , requested_virtual_columns(requested_virtual_columns_) , file_iterator(file_iterator_) , download_thread_num(download_thread_num_) - , create_reader_pool(1) + , create_reader_pool(CurrentMetrics::StorageS3Threads, CurrentMetrics::StorageS3ThreadsActive, 1) , create_reader_scheduler(threadPoolCallbackRunner(create_reader_pool, "CreateS3Reader")) { reader = createReader(); diff --git a/src/Storages/System/StorageSystemReplicas.cpp b/src/Storages/System/StorageSystemReplicas.cpp index 65878d356f4..240d452fe29 100644 --- a/src/Storages/System/StorageSystemReplicas.cpp +++ b/src/Storages/System/StorageSystemReplicas.cpp @@ -7,12 +7,19 @@ #include #include #include -#include #include #include +#include +#include #include +namespace CurrentMetrics +{ + extern const Metric SystemReplicasThreads; + extern const Metric SystemReplicasThreadsActive; +} + namespace DB { @@ -160,7 +167,7 @@ Pipe StorageSystemReplicas::read( if (settings.max_threads != 0) thread_pool_size = std::min(thread_pool_size, static_cast(settings.max_threads)); - ThreadPool thread_pool(thread_pool_size); + ThreadPool thread_pool(CurrentMetrics::SystemReplicasThreads, CurrentMetrics::SystemReplicasThreadsActive, thread_pool_size); for (size_t i = 0; i < tables_size; ++i) { diff --git a/utils/keeper-bench/Runner.h b/utils/keeper-bench/Runner.h index 3976ac720eb..a00b7b43eff 100644 --- a/utils/keeper-bench/Runner.h +++ b/utils/keeper-bench/Runner.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "Stats.h" @@ -15,6 +16,12 @@ using Ports = std::vector; using Strings = std::vector; +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + class Runner { public: @@ -27,7 +34,7 @@ public: bool continue_on_error_, size_t max_iterations_) : concurrency(concurrency_) - , pool(concurrency) + , pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, concurrency) , hosts_strings(hosts_strings_) , generator(getGenerator(generator_name)) , max_time(max_time_) From 36f6408ada36c03968a5b86f8a47da7d43f32cb4 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 15 Mar 2023 08:33:45 +0100 Subject: [PATCH 169/377] Add sanity checks for writing number in variable length format And just to double check: # var_uint 9223372036854775807 ffffffffffffffff7f ffffffffffffffff7f ffffffffffffffff7f x: 9223372036854775807, y: 9223372036854775807 # var_uint 9223372036854775808 808080808080808080 808080808080808080 808080808080808080 x: 9223372036854775808, y: 0 Signed-off-by: Azat Khuzhin --- src/IO/VarInt.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/IO/VarInt.h b/src/IO/VarInt.h index 0869051034a..d026192cb7d 100644 --- a/src/IO/VarInt.h +++ b/src/IO/VarInt.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -14,7 +15,19 @@ namespace ErrorCodes } -/** Write UInt64 in variable length format (base128) NOTE Only up to 2^63 - 1 are supported. */ +/** Variable-Length Quantity (VLQ) Base-128 compression + * + * NOTE: Due to historical reasons, only up to 1<<63-1 are supported, which + * cannot be changed without breaking the backward compatibility. + * Also some drivers may support full 1<<64 range (i.e. python - + * clickhouse-driver), while others has the same limitations as ClickHouse + * (i.e. Rust - clickhouse-rs). + * So implementing VLQ for the whole 1<<64 range will require different set of + * helpers. + */ +constexpr size_t VAR_UINT_MAX = (1ULL<<63) - 1; + +/** Write UInt64 in variable length format (base128) */ void writeVarUInt(UInt64 x, std::ostream & ostr); void writeVarUInt(UInt64 x, WriteBuffer & ostr); char * writeVarUInt(UInt64 x, char * ostr); @@ -186,6 +199,7 @@ inline const char * readVarUInt(UInt64 & x, const char * istr, size_t size) inline void writeVarUInt(UInt64 x, WriteBuffer & ostr) { + chassert(x <= VAR_UINT_MAX); for (size_t i = 0; i < 9; ++i) { uint8_t byte = x & 0x7F; @@ -205,6 +219,7 @@ inline void writeVarUInt(UInt64 x, WriteBuffer & ostr) inline void writeVarUInt(UInt64 x, std::ostream & ostr) { + chassert(x <= VAR_UINT_MAX); for (size_t i = 0; i < 9; ++i) { uint8_t byte = x & 0x7F; @@ -222,6 +237,7 @@ inline void writeVarUInt(UInt64 x, std::ostream & ostr) inline char * writeVarUInt(UInt64 x, char * ostr) { + chassert(x <= VAR_UINT_MAX); for (size_t i = 0; i < 9; ++i) { uint8_t byte = x & 0x7F; From 9457b1cc46f08aca385eea3c86578e561b80ba38 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 15 Mar 2023 13:10:35 +0100 Subject: [PATCH 170/377] Use VAR_UINT_MAX for unknown_packet_in_send_data Signed-off-by: Azat Khuzhin --- src/Server/TCPHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/TCPHandler.cpp b/src/Server/TCPHandler.cpp index 85cdb75977b..941bbe4fffe 100644 --- a/src/Server/TCPHandler.cpp +++ b/src/Server/TCPHandler.cpp @@ -1895,7 +1895,7 @@ void TCPHandler::sendData(const Block & block) { --unknown_packet_in_send_data; if (unknown_packet_in_send_data == 0) - writeVarUInt(UInt64(-1), *out); + writeVarUInt(VAR_UINT_MAX, *out); } writeVarUInt(Protocol::Server::Data, *out); From 71a6a02e8853fd9f1707985fe587a13c76d48309 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Wed, 29 Mar 2023 12:20:27 +0300 Subject: [PATCH 171/377] fix stdlib compatibility issues --- src/Client/ReplxxLineReader.cpp | 1 + src/Parsers/IParser.h | 1 + src/Processors/QueryPlan/FilterStep.cpp | 2 +- src/Storages/MeiliSearch/StorageMeiliSearch.cpp | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Client/ReplxxLineReader.cpp b/src/Client/ReplxxLineReader.cpp index fa5ba8b8ba9..1979b37a94b 100644 --- a/src/Client/ReplxxLineReader.cpp +++ b/src/Client/ReplxxLineReader.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/src/Parsers/IParser.h b/src/Parsers/IParser.h index dbd292bcd9f..d53b58baa7c 100644 --- a/src/Parsers/IParser.h +++ b/src/Parsers/IParser.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include diff --git a/src/Processors/QueryPlan/FilterStep.cpp b/src/Processors/QueryPlan/FilterStep.cpp index dc837446a96..9137b2d1998 100644 --- a/src/Processors/QueryPlan/FilterStep.cpp +++ b/src/Processors/QueryPlan/FilterStep.cpp @@ -14,7 +14,7 @@ static ITransformingStep::Traits getTraits(const ActionsDAGPtr & expression, con bool preserves_sorting = expression->isSortingPreserved(header, sort_description, remove_filter_column ? filter_column_name : ""); if (remove_filter_column) { - preserves_sorting &= find_if( + preserves_sorting &= std::find_if( begin(sort_description), end(sort_description), [&](const auto & column_desc) { return column_desc.column_name == filter_column_name; }) diff --git a/src/Storages/MeiliSearch/StorageMeiliSearch.cpp b/src/Storages/MeiliSearch/StorageMeiliSearch.cpp index 62a6c471070..fe8cc74f577 100644 --- a/src/Storages/MeiliSearch/StorageMeiliSearch.cpp +++ b/src/Storages/MeiliSearch/StorageMeiliSearch.cpp @@ -99,7 +99,7 @@ Pipe StorageMeiliSearch::read( for (const auto & el : query_params->children) { auto str = el->getColumnName(); - auto it = find(str.begin(), str.end(), '='); + auto it = std::find(str.begin(), str.end(), '='); if (it == str.end()) throw Exception(ErrorCodes::BAD_QUERY_PARAMETER, "meiliMatch function must have parameters of the form \'key=value\'"); From fc5406d9e2ad2fc4806afc77aa7e5a9fb54e4155 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Wed, 29 Mar 2023 09:39:58 +0000 Subject: [PATCH 172/377] Fix 25402_show_columns (hopefully) --- tests/queries/0_stateless/25402_show_columns.sql | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/25402_show_columns.sql b/tests/queries/0_stateless/25402_show_columns.sql index 6cf2a599f2c..28ac54bd193 100644 --- a/tests/queries/0_stateless/25402_show_columns.sql +++ b/tests/queries/0_stateless/25402_show_columns.sql @@ -3,7 +3,8 @@ -- Create a test table and verify that the output of SHOW COLUMNS is sane. -- The matching of actual/expected results relies on the fact that the output of SHOW COLUMNS is sorted. -CREATE OR REPLACE TABLE tab +DROP TABLE IF EXISTS tab; +CREATE TABLE tab ( `uint64` UInt64, `int32` Nullable(Int32) COMMENT 'example comment', @@ -56,7 +57,8 @@ SELECT '---'; DROP DATABASE IF EXISTS database_123456789abcde; CREATE DATABASE database_123456789abcde; -- pseudo-random database name -CREATE OR REPLACE TABLE database_123456789abcde.tab +DROP TABLE IF EXISTS database_123456789abcde.tab; +CREATE TABLE database_123456789abcde.tab ( `uint64` UInt64, `int32` Int32, From 588fdcffdf4f1883063db4f896d6a536291c072b Mon Sep 17 00:00:00 2001 From: vdimir Date: Wed, 29 Mar 2023 09:42:32 +0000 Subject: [PATCH 173/377] comment child_plans in ReadFromMerge --- src/Storages/StorageMerge.cpp | 2 ++ src/Storages/StorageMerge.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 40ad4ab0f26..00ccfebff25 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -655,6 +655,8 @@ QueryPipelineBuilderPtr ReadFromMerge::createSources( if (real_column_names.empty()) real_column_names.push_back(ExpressionActions::getSmallestColumn(storage_snapshot->metadata->getColumns().getAllPhysical()).name); + /// Steps for reading from child tables should have the same lifetime as the current step + /// because `builder` can have references to them (mainly for EXPLAIN PIPELINE). QueryPlan & plan = child_plans.emplace_back(); StorageView * view = dynamic_cast(storage.get()); diff --git a/src/Storages/StorageMerge.h b/src/Storages/StorageMerge.h index 602da045dfb..d53dcd34f5f 100644 --- a/src/Storages/StorageMerge.h +++ b/src/Storages/StorageMerge.h @@ -159,6 +159,8 @@ private: StoragePtr storage_merge; StorageSnapshotPtr merge_storage_snapshot; + /// Store read plan for each child table. + /// It's needed to guarantee lifetime for child steps to be the same as for this step. std::vector child_plans; SelectQueryInfo query_info; From 7f2a43e0c708490579f166b78208319e57f1ec02 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Wed, 29 Mar 2023 04:23:43 -0600 Subject: [PATCH 174/377] Update v22.4.1.2305-prestable.md --- docs/changelogs/v22.4.1.2305-prestable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelogs/v22.4.1.2305-prestable.md b/docs/changelogs/v22.4.1.2305-prestable.md index 41f57454d0d..b277137ca7e 100644 --- a/docs/changelogs/v22.4.1.2305-prestable.md +++ b/docs/changelogs/v22.4.1.2305-prestable.md @@ -9,7 +9,7 @@ sidebar_label: 2022 #### Backward Incompatible Change * Function `yandexConsistentHash` (consistent hashing algorithm by Konstantin "kostik" Oblakov) is renamed to `kostikConsistentHash`. The old name is left as an alias for compatibility. Although this change is backward compatible, we may remove the alias in subsequent releases, that's why it's recommended to update the usages of this function in your apps. [#35553](https://github.com/ClickHouse/ClickHouse/pull/35553) ([Alexey Milovidov](https://github.com/alexey-milovidov)). -* Do not allow SETTINGS after FORMAT for INSERT queries (there is compatibility setting `parser_settings_after_format_compact` to accept such queries, but it is turned OFF by default). [#35883](https://github.com/ClickHouse/ClickHouse/pull/35883) ([Azat Khuzhin](https://github.com/azat)). +* Do not allow SETTINGS after FORMAT for INSERT queries (there is compatibility setting `allow_settings_after_format_in_insert` to accept such queries, but it is turned OFF by default). [#35883](https://github.com/ClickHouse/ClickHouse/pull/35883) ([Azat Khuzhin](https://github.com/azat)). * Changed hashed path for cache files. [#36079](https://github.com/ClickHouse/ClickHouse/pull/36079) ([Kseniia Sumarokova](https://github.com/kssenii)). #### New Feature From d0f4c63d27d272b492d55379f0751b52ae5557a7 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Wed, 29 Mar 2023 10:29:17 +0000 Subject: [PATCH 175/377] Fix tests --- src/Interpreters/InterpreterShowTablesQuery.cpp | 6 +++--- tests/queries/0_stateless/01293_show_settings.reference | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Interpreters/InterpreterShowTablesQuery.cpp b/src/Interpreters/InterpreterShowTablesQuery.cpp index bd29a933bcf..a631cb72722 100644 --- a/src/Interpreters/InterpreterShowTablesQuery.cpp +++ b/src/Interpreters/InterpreterShowTablesQuery.cpp @@ -71,12 +71,12 @@ String InterpreterShowTablesQuery::getRewrittenQuery() << DB::quote << query.like; } - if (query.limit_length) - rewritten_query << " LIMIT " << query.limit_length; - /// (*) rewritten_query << " ORDER BY cluster"; + if (query.limit_length) + rewritten_query << " LIMIT " << query.limit_length; + return rewritten_query.str(); } else if (query.cluster) diff --git a/tests/queries/0_stateless/01293_show_settings.reference b/tests/queries/0_stateless/01293_show_settings.reference index f11956e1893..f053387d1c5 100644 --- a/tests/queries/0_stateless/01293_show_settings.reference +++ b/tests/queries/0_stateless/01293_show_settings.reference @@ -3,6 +3,6 @@ connect_timeout Seconds 10 connect_timeout_with_failover_ms Milliseconds 2000 connect_timeout_with_failover_secure_ms Milliseconds 3000 external_storage_connect_timeout_sec UInt64 10 +filesystem_prefetch_max_memory_usage UInt64 1073741824 max_untracked_memory UInt64 1048576 memory_profiler_step UInt64 1048576 -filesystem_prefetch_max_memory_usage UInt64 1073741824 From d7d9f0cb6b3b21d5116242c1f58ce950dae7bf31 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 29 Mar 2023 11:11:42 +0200 Subject: [PATCH 176/377] Fix overflow of VarUInt format in Progress packets Otherwise query like this, can trigger sanity check: WITH x AS (SELECT [], number AS a FROM numbers(9223372036854775807)), y AS (SELECT arrayLastOrNull(x -> (x >= -inf), []), arrayLastOrNull(x -> (x >= NULL), [1]), number AS a FROM numbers(1.)) SELECT [1023], * FROM x WHERE a IN (SELECT a FROM y) ORDER BY arrayLastOrNull(x -> (x >= 1025), [1048577, 1048576]) DESC NULLS LAST, '0.0000000002' ASC NULLS LAST, a DESC NULLS FIRST CI: https://s3.amazonaws.com/clickhouse-test-reports/0/a9bcd022d5f4a5be530595dbfae3ed177b5c1972/fuzzer_astfuzzermsan/report.html Signed-off-by: Azat Khuzhin --- src/IO/Progress.cpp | 10 +++++----- src/IO/VarInt.h | 8 ++++++++ src/IO/examples/var_uint.cpp | 14 ++++++++++---- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/IO/Progress.cpp b/src/IO/Progress.cpp index 1069803633c..1dd530b4c81 100644 --- a/src/IO/Progress.cpp +++ b/src/IO/Progress.cpp @@ -28,13 +28,13 @@ void ProgressValues::read(ReadBuffer & in, UInt64 server_revision) void ProgressValues::write(WriteBuffer & out, UInt64 client_revision) const { - writeVarUInt(read_rows, out); - writeVarUInt(read_bytes, out); - writeVarUInt(total_rows_to_read, out); + writeVarUIntOverflow(read_rows, out); + writeVarUIntOverflow(read_bytes, out); + writeVarUIntOverflow(total_rows_to_read, out); if (client_revision >= DBMS_MIN_REVISION_WITH_CLIENT_WRITE_INFO) { - writeVarUInt(written_rows, out); - writeVarUInt(written_bytes, out); + writeVarUIntOverflow(written_rows, out); + writeVarUIntOverflow(written_bytes, out); } if (client_revision >= DBMS_MIN_PROTOCOL_VERSION_WITH_SERVER_QUERY_TIME_IN_PROGRESS) { diff --git a/src/IO/VarInt.h b/src/IO/VarInt.h index d026192cb7d..f95b479df11 100644 --- a/src/IO/VarInt.h +++ b/src/IO/VarInt.h @@ -33,6 +33,14 @@ void writeVarUInt(UInt64 x, WriteBuffer & ostr); char * writeVarUInt(UInt64 x, char * ostr); +/** Write UInt64 in variable length format, wrap the value to VAR_UINT_MAX if it exceed VAR_UINT_MAX (to bypass sanity check) */ +template +auto writeVarUIntOverflow(UInt64 x, Args && ... args) +{ + return writeVarUInt(std::min(x, VAR_UINT_MAX), std::forward(args)...); +} + + /** Read UInt64, written in variable length format (base128) */ void readVarUInt(UInt64 & x, std::istream & istr); void readVarUInt(UInt64 & x, ReadBuffer & istr); diff --git a/src/IO/examples/var_uint.cpp b/src/IO/examples/var_uint.cpp index 65e1f0495d3..06c707b4a0a 100644 --- a/src/IO/examples/var_uint.cpp +++ b/src/IO/examples/var_uint.cpp @@ -18,26 +18,32 @@ int main(int argc, char ** argv) } DB::UInt64 x = DB::parse(argv[1]); + + std::cout << std::hex << std::showbase << "Input: " << x << std::endl; + Poco::HexBinaryEncoder hex(std::cout); - DB::writeVarUInt(x, hex); + std::cout << "writeVarUIntOverflow(std::ostream): 0x"; + DB::writeVarUIntOverflow(x, hex); std::cout << std::endl; std::string s; { DB::WriteBufferFromString wb(s); - DB::writeVarUInt(x, wb); + DB::writeVarUIntOverflow(x, wb); wb.next(); } + std::cout << "writeVarUIntOverflow(WriteBuffer): 0x"; hex << s; std::cout << std::endl; s.clear(); s.resize(9); - s.resize(DB::writeVarUInt(x, s.data()) - s.data()); + s.resize(DB::writeVarUIntOverflow(x, s.data()) - s.data()); + std::cout << "writeVarUIntOverflow(char *): 0x"; hex << s; std::cout << std::endl; @@ -46,7 +52,7 @@ int main(int argc, char ** argv) DB::ReadBufferFromString rb(s); DB::readVarUInt(y, rb); - std::cerr << "x: " << x << ", y: " << y << std::endl; + std::cerr << "Input: " << x << ", readVarUInt(writeVarUIntOverflow()): " << y << std::endl; return 0; } From bdc8a47bcfbebb8ba4fc32d5a1c37efe11657515 Mon Sep 17 00:00:00 2001 From: vdimir Date: Wed, 29 Mar 2023 11:02:50 +0000 Subject: [PATCH 177/377] Remove unused mockSystemDatabase from gtest_transform_query_for_external_database --- .../gtest_transform_query_for_external_database.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Storages/tests/gtest_transform_query_for_external_database.cpp b/src/Storages/tests/gtest_transform_query_for_external_database.cpp index 3eff0a8ba70..5c1442ece11 100644 --- a/src/Storages/tests/gtest_transform_query_for_external_database.cpp +++ b/src/Storages/tests/gtest_transform_query_for_external_database.cpp @@ -106,22 +106,9 @@ private: StorageID(db_name, table_name), ColumnsDescription{tab.columns}, ConstraintsDescription{}, String{})); } DatabaseCatalog::instance().attachDatabase(database->getDatabaseName(), database); - // DatabaseCatalog::instance().attachDatabase("system", mockSystemDatabase()); context->setCurrentDatabase("test"); } - - DatabasePtr mockSystemDatabase() - { - DatabasePtr database = std::make_shared("system", context); - auto tab = TableWithColumnNamesAndTypes(createDBAndTable("one", "system"), { {"dummy", std::make_shared()} }); - database->attachTable(context, tab.table.table, - std::make_shared( - StorageID(tab.table.database, tab.table.table), - ColumnsDescription{tab.columns}, ConstraintsDescription{}, String{})); - - return database; - } }; static void checkOld( From 7b1ad221b259abf9ceec9d0e1e48840f456bf4c0 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Wed, 29 Mar 2023 11:08:44 +0000 Subject: [PATCH 178/377] Address PR comments --- .../engines/table-engines/integrations/s3.md | 11 +++----- src/IO/S3Common.cpp | 12 ++++++--- src/Storages/StorageS3.cpp | 4 +++ src/TableFunctions/TableFunctionS3.cpp | 2 +- .../configs/named_collections.xml | 4 +++ .../test_invalid_env_credentials.py | 25 ++++++++++++++++++- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/docs/en/engines/table-engines/integrations/s3.md b/docs/en/engines/table-engines/integrations/s3.md index c539340d332..7b2df1b98fb 100644 --- a/docs/en/engines/table-engines/integrations/s3.md +++ b/docs/en/engines/table-engines/integrations/s3.md @@ -12,7 +12,7 @@ This engine provides integration with [Amazon S3](https://aws.amazon.com/s3/) ec ``` sql CREATE TABLE s3_engine_table (name String, value UInt32) - ENGINE = S3(path, [, NOSIGN | aws_access_key_id, aws_secret_access_key,] format, [compression]) + ENGINE = S3(path [, NOSIGN | aws_access_key_id, aws_secret_access_key,] format, [compression]) [PARTITION BY expr] [SETTINGS ...] ``` @@ -185,13 +185,8 @@ Sometimes, it can produce problems when accessing some buckets that are public c This issue can be avoided by using `NOSIGN` keyword, forcing the client to ignore all the credentials, and not sign the requests. ``` sql -SELECT * -FROM s3( - 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/aapl_stock.csv', - NOSIGN, - 'CSVWithNames' -) -LIMIT 5; +CREATE TABLE big_table (name String, value UInt32) + ENGINE = S3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/aapl_stock.csv', NOSIGN, 'CSVWithNames'); ``` ## See also diff --git a/src/IO/S3Common.cpp b/src/IO/S3Common.cpp index 20984b69463..2f99c523429 100644 --- a/src/IO/S3Common.cpp +++ b/src/IO/S3Common.cpp @@ -135,9 +135,15 @@ void AuthSettings::updateFrom(const AuthSettings & from) headers = from.headers; region = from.region; server_side_encryption_customer_key_base64 = from.server_side_encryption_customer_key_base64; - use_environment_credentials = from.use_environment_credentials; - use_insecure_imds_request = from.use_insecure_imds_request; - expiration_window_seconds = from.expiration_window_seconds; + + if (from.use_environment_credentials) + use_environment_credentials = from.use_environment_credentials; + + if (from.use_insecure_imds_request) + use_insecure_imds_request = from.use_insecure_imds_request; + + if (from.expiration_window_seconds) + expiration_window_seconds = from.expiration_window_seconds; if (from.no_sign_request.has_value()) no_sign_request = *from.no_sign_request; diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index f2a9b3a3955..b735e090f59 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -96,6 +96,8 @@ static const std::unordered_set optional_configuration_keys = "upload_part_size_multiply_parts_count_threshold", "max_single_part_upload_size", "max_connections", + "expiration_window_seconds", + "no_sign_request" }; namespace ErrorCodes @@ -1289,6 +1291,8 @@ void StorageS3::processNamedCollectionResult(StorageS3::Configuration & configur configuration.auth_settings.access_key_id = collection.getOrDefault("access_key_id", ""); configuration.auth_settings.secret_access_key = collection.getOrDefault("secret_access_key", ""); configuration.auth_settings.use_environment_credentials = collection.getOrDefault("use_environment_credentials", 0); + configuration.auth_settings.no_sign_request = collection.getOrDefault("no_sign_request", 0); + configuration.auth_settings.expiration_window_seconds = collection.getOrDefault("expiration_window_seconds", S3::DEFAULT_EXPIRATION_WINDOW_SECONDS); configuration.format = collection.getOrDefault("format", "auto"); configuration.compression_method = collection.getOrDefault("compression_method", collection.getOrDefault("compression", "auto")); diff --git a/src/TableFunctions/TableFunctionS3.cpp b/src/TableFunctions/TableFunctionS3.cpp index 9f5d992e4c9..9ce1ac439af 100644 --- a/src/TableFunctions/TableFunctionS3.cpp +++ b/src/TableFunctions/TableFunctionS3.cpp @@ -112,7 +112,7 @@ void TableFunctionS3::parseArgumentsImpl( /// For 5 arguments we support 2 possible variants: /// - s3(source, access_key_id, access_key_id, format, structure) /// - s3(source, NOSIGN, format, structure, compression_method) - /// We can distinguish them by looking at the 2-nd argument: check if it's a format name or not. + /// We can distinguish them by looking at the 2-nd argument: check if it's a NOSIGN keyword name or not. else if (args.size() == 5) { auto second_arg = checkAndGetLiteralArgument(args[1], "NOSIGN/access_key_id"); diff --git a/tests/integration/test_storage_s3/configs/named_collections.xml b/tests/integration/test_storage_s3/configs/named_collections.xml index fcc8bcac555..64674e2a3e3 100644 --- a/tests/integration/test_storage_s3/configs/named_collections.xml +++ b/tests/integration/test_storage_s3/configs/named_collections.xml @@ -35,5 +35,9 @@ minio minio123 + + http://minio1:9001/root/test_cache4.jsonl + true + diff --git a/tests/integration/test_storage_s3/test_invalid_env_credentials.py b/tests/integration/test_storage_s3/test_invalid_env_credentials.py index 06d49d8e828..0ee679014b1 100644 --- a/tests/integration/test_storage_s3/test_invalid_env_credentials.py +++ b/tests/integration/test_storage_s3/test_invalid_env_credentials.py @@ -88,7 +88,10 @@ def started_cluster(): "AWS_ACCESS_KEY_ID": "aws", "AWS_SECRET_ACCESS_KEY": "aws123", }, - main_configs=["configs/use_environment_credentials.xml"], + main_configs=[ + "configs/use_environment_credentials.xml", + "configs/named_collections.xml", + ], ) logging.info("Starting cluster...") @@ -129,3 +132,23 @@ def test_with_invalid_environment_credentials(started_cluster): f"select count() from s3('http://{started_cluster.minio_host}:{started_cluster.minio_port}/{bucket}/test_cache4.jsonl', {auth})" ).strip() ) + + +def test_no_sign_named_collections(started_cluster): + instance = started_cluster.instances["s3_with_invalid_environment_credentials"] + + bucket = started_cluster.minio_bucket + + instance.query( + f"insert into function s3(s3_json_no_sign) select * from numbers(100) settings s3_truncate_on_insert=1" + ) + + with pytest.raises(helpers.client.QueryRuntimeException) as ei: + instance.query( + f"select count() from s3('http://{started_cluster.minio_host}:{started_cluster.minio_port}/{bucket}/test_cache4.jsonl')" + ) + + assert ei.value.returncode == 243 + assert "HTTP response code: 403" in ei.value.stderr + + assert "100" == instance.query(f"select count() from s3(s3_json_no_sign)").strip() From f4b884a5a82de0c0164f8ad53b2f47fd766a3c93 Mon Sep 17 00:00:00 2001 From: Kruglov Pavel <48961922+Avogar@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:55:29 +0200 Subject: [PATCH 179/377] Fix test flakiness --- tests/queries/0_stateless/02701_non_parametric_function.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02701_non_parametric_function.sql b/tests/queries/0_stateless/02701_non_parametric_function.sql index a757343a624..5261fa7b082 100644 --- a/tests/queries/0_stateless/02701_non_parametric_function.sql +++ b/tests/queries/0_stateless/02701_non_parametric_function.sql @@ -1,7 +1,9 @@ +-- Tags: no-parallel + SELECT * FROM system.numbers WHERE number > toUInt64(10)(number) LIMIT 10; -- { serverError 309 } CREATE FUNCTION IF NOT EXISTS sum_udf as (x, y) -> (x + y); SELECT sum_udf(1)(1, 2); -- { serverError 309 } -DROP FUNCTION IF EXISTS sum_udf; \ No newline at end of file +DROP FUNCTION IF EXISTS sum_udf; From a495cd4c725137957b7516c5e8df540eaee0d356 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 29 Mar 2023 14:59:43 +0300 Subject: [PATCH 180/377] Update environmental-sensors.md --- .../getting-started/example-datasets/environmental-sensors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/getting-started/example-datasets/environmental-sensors.md b/docs/en/getting-started/example-datasets/environmental-sensors.md index d6c9552030f..309a6dc6c0f 100644 --- a/docs/en/getting-started/example-datasets/environmental-sensors.md +++ b/docs/en/getting-started/example-datasets/environmental-sensors.md @@ -132,7 +132,7 @@ GROUP BY ORDER BY size DESC; ``` -The 1.67T is compressed down to 1.30T, and there are 20.69 billion rows: +The 1.67T is compressed down to 310 GiB, and there are 20.69 billion rows: ```response ┌─disk_name─┬─compressed─┬─uncompressed─┬─compr_rate─┬────────rows─┬─part_count─┐ From 8b8159d58f868213b99d2669b266a5c717e17ee9 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Wed, 29 Mar 2023 14:04:25 +0200 Subject: [PATCH 181/377] fix --- docs/get-clickhouse-docs.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/get-clickhouse-docs.sh b/docs/get-clickhouse-docs.sh index 7868f281bd0..e3b70dee665 100755 --- a/docs/get-clickhouse-docs.sh +++ b/docs/get-clickhouse-docs.sh @@ -7,6 +7,8 @@ WORKDIR=$(dirname "$0") WORKDIR=$(readlink -f "${WORKDIR}") cd "$WORKDIR" +UPDATE_PERIOD_HOURS=${UPDATE_PERIOD_HOURS:=24} + if [ -d "clickhouse-docs" ]; then git -C clickhouse-docs pull else @@ -30,20 +32,20 @@ else if [ -n "$2" ]; then set_git_hook="$2" - elif [ ! -n "$1" ]; then + elif [ -z "$1" ]; then read -rp "Would you like to setup git hook for automatic update? (y|n): " set_git_hook fi if [ "$set_git_hook" = "y" ]; then - hook_command="$(pwd)/pull-clickhouse-docs-hook.sh 24" + hook_command="$(pwd)/pull-clickhouse-docs-hook.sh $UPDATE_PERIOD_HOURS" hook_file=$(realpath "$(pwd)/../.git/hooks/post-checkout") already_have=$(grep -Faq "pull-clickhouse-docs-hook.sh" "$hook_file" 2>/dev/null && echo 1 || echo 0) - if [ $already_have -eq 0 ]; then + if grep -Faq "pull-clickhouse-docs-hook.sh" "$hook_file" 2>/dev/null; then + echo "Looks like the update hook already exists, will not add another one" + else echo "Appending '$hook_command' to $hook_file" echo "$hook_command" >> "$hook_file" chmod u+x "$hook_file" # Just in case it did not exist before append - else - echo "Looks like the update hook already exists, will not add another one" fi elif [ ! "$set_git_hook" = "n" ]; then echo "Expected 'y' or 'n', got '$set_git_hook', will not setup git hook" From 611e7c6382682ee012995bb85ad5f1fc56721825 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Wed, 29 Mar 2023 12:37:43 +0000 Subject: [PATCH 182/377] Ignore null constants in logical optimizer --- .../Passes/LogicalExpressionOptimizerPass.cpp | 6 +- ...702_logical_optimizer_with_nulls.reference | 58 +++++++++++++++++++ .../02702_logical_optimizer_with_nulls.sql | 17 ++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/queries/0_stateless/02702_logical_optimizer_with_nulls.reference create mode 100644 tests/queries/0_stateless/02702_logical_optimizer_with_nulls.sql diff --git a/src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp b/src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp index 3d65035f9fd..97669f3924f 100644 --- a/src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp +++ b/src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp @@ -153,9 +153,11 @@ private: } }; - if (const auto * lhs_literal = lhs->as()) + if (const auto * lhs_literal = lhs->as(); + lhs_literal && !lhs_literal->getValue().isNull()) add_equals_function_if_not_present(rhs, lhs_literal); - else if (const auto * rhs_literal = rhs->as()) + else if (const auto * rhs_literal = rhs->as(); + rhs_literal && !rhs_literal->getValue().isNull()) add_equals_function_if_not_present(lhs, rhs_literal); else or_operands.push_back(argument); diff --git a/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.reference b/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.reference new file mode 100644 index 00000000000..263329e47be --- /dev/null +++ b/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.reference @@ -0,0 +1,58 @@ +1 test +3 another +QUERY id: 0 + PROJECTION COLUMNS + a Int32 + b LowCardinality(String) + PROJECTION + LIST id: 1, nodes: 2 + COLUMN id: 2, column_name: a, result_type: Int32, source_id: 3 + COLUMN id: 4, column_name: b, result_type: LowCardinality(String), source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.02702_logical_optimizer + WHERE + FUNCTION id: 5, function_name: or, function_type: ordinary, result_type: Nullable(UInt8) + ARGUMENTS + LIST id: 6, nodes: 3 + FUNCTION id: 7, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 8, nodes: 2 + COLUMN id: 9, column_name: a, result_type: Int32, source_id: 3 + CONSTANT id: 10, constant_value: UInt64_1, constant_value_type: UInt8 + FUNCTION id: 11, function_name: equals, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 12, nodes: 2 + CONSTANT id: 13, constant_value: UInt64_3, constant_value_type: UInt8 + COLUMN id: 9, column_name: a, result_type: Int32, source_id: 3 + FUNCTION id: 14, function_name: equals, function_type: ordinary, result_type: Nullable(Nothing) + ARGUMENTS + LIST id: 15, nodes: 2 + CONSTANT id: 16, constant_value: NULL, constant_value_type: Nullable(Nothing) + COLUMN id: 9, column_name: a, result_type: Int32, source_id: 3 +1 test +2 test2 +3 another +QUERY id: 0 + PROJECTION COLUMNS + a Int32 + b LowCardinality(String) + PROJECTION + LIST id: 1, nodes: 2 + COLUMN id: 2, column_name: a, result_type: Int32, source_id: 3 + COLUMN id: 4, column_name: b, result_type: LowCardinality(String), source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.02702_logical_optimizer + WHERE + FUNCTION id: 5, function_name: or, function_type: ordinary, result_type: Nullable(UInt8) + ARGUMENTS + LIST id: 6, nodes: 2 + FUNCTION id: 7, function_name: equals, function_type: ordinary, result_type: Nullable(Nothing) + ARGUMENTS + LIST id: 8, nodes: 2 + COLUMN id: 9, column_name: a, result_type: Int32, source_id: 3 + CONSTANT id: 10, constant_value: NULL, constant_value_type: Nullable(Nothing) + FUNCTION id: 11, function_name: in, function_type: ordinary, result_type: UInt8 + ARGUMENTS + LIST id: 12, nodes: 2 + COLUMN id: 9, column_name: a, result_type: Int32, source_id: 3 + CONSTANT id: 13, constant_value: Tuple_(UInt64_1, UInt64_3, UInt64_2), constant_value_type: Tuple(UInt8, UInt8, UInt8) diff --git a/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.sql b/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.sql new file mode 100644 index 00000000000..9a49e31fe81 --- /dev/null +++ b/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.sql @@ -0,0 +1,17 @@ +SET allow_experimental_analyzer = 1; + +DROP TABLE IF EXISTS 02702_logical_optimizer; + +CREATE TABLE 02702_logical_optimizer +(a Int32, b LowCardinality(String)) +ENGINE=Memory; + +INSERT INTO 02702_logical_optimizer VALUES (1, 'test'), (2, 'test2'), (3, 'another'); + +SET optimize_min_equality_disjunction_chain_length = 3; + +SELECT * FROM 02702_logical_optimizer WHERE a = 1 OR 3 = a OR NULL = a; +EXPLAIN QUERY TREE SELECT * FROM 02702_logical_optimizer WHERE a = 1 OR 3 = a OR NULL = a; + +SELECT * FROM 02702_logical_optimizer WHERE a = 1 OR 3 = a OR 2 = a OR a = NULL; +EXPLAIN QUERY TREE SELECT * FROM 02702_logical_optimizer WHERE a = 1 OR 3 = a OR 2 = a OR a = NULL; From 4ea9f96b1bde2e62d834b2e8a436433414fbc53a Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Wed, 29 Mar 2023 12:46:49 +0000 Subject: [PATCH 183/377] Lower number of processes in KeeperMap test --- tests/integration/test_keeper_map/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_keeper_map/test.py b/tests/integration/test_keeper_map/test.py index 3809f046d55..4b940fbf1d1 100644 --- a/tests/integration/test_keeper_map/test.py +++ b/tests/integration/test_keeper_map/test.py @@ -123,7 +123,7 @@ def test_create_drop_keeper_map_concurrent(started_cluster): manager = multiprocessing.Manager() stop_event = manager.Event() results = [] - for i in range(multiprocessing.cpu_count()): + for i in range(8): sleep(0.2) results.append( pool.apply_async( From 80f0251ff209915fe5fb8996d6f5803cf65b841e Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Wed, 29 Mar 2023 12:54:57 +0000 Subject: [PATCH 184/377] better --- src/IO/S3Common.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IO/S3Common.cpp b/src/IO/S3Common.cpp index 2f99c523429..5c946d59274 100644 --- a/src/IO/S3Common.cpp +++ b/src/IO/S3Common.cpp @@ -136,13 +136,13 @@ void AuthSettings::updateFrom(const AuthSettings & from) region = from.region; server_side_encryption_customer_key_base64 = from.server_side_encryption_customer_key_base64; - if (from.use_environment_credentials) + if (from.use_environment_credentials.has_value()) use_environment_credentials = from.use_environment_credentials; - if (from.use_insecure_imds_request) + if (from.use_insecure_imds_request.has_value()) use_insecure_imds_request = from.use_insecure_imds_request; - if (from.expiration_window_seconds) + if (from.expiration_window_seconds.has_value()) expiration_window_seconds = from.expiration_window_seconds; if (from.no_sign_request.has_value()) From 481a7a76ace898652e7f67da1d8465de99050d43 Mon Sep 17 00:00:00 2001 From: Vitaly Baranov Date: Wed, 29 Mar 2023 15:19:40 +0200 Subject: [PATCH 185/377] Simplify backup coordination for file infos (#48095) * Remove obsolete code for archive suffixes. * Simplify backup coordination, stop using it for restoring. * Build all file infos before writing to backup. Decrease number of znodes. * Split long values before writing to ZooKeeper. * Use separate mutexes for unrelated activities. * Make test test_disallow_concurrency less flaky. * Add comments and test for backup_keeper_value_max_size. --- src/Backups/BackupCoordinationFileInfos.cpp | 146 +++++ src/Backups/BackupCoordinationFileInfos.h | 56 ++ src/Backups/BackupCoordinationLocal.cpp | 146 +---- src/Backups/BackupCoordinationLocal.h | 39 +- src/Backups/BackupCoordinationRemote.cpp | 521 ++++++------------ src/Backups/BackupCoordinationRemote.h | 47 +- src/Backups/BackupCoordinationStage.h | 3 + src/Backups/BackupEntriesCollector.cpp | 1 - src/Backups/BackupFileInfo.cpp | 284 ++++++++++ src/Backups/BackupFileInfo.h | 70 +++ src/Backups/BackupImpl.cpp | 519 +++++------------ src/Backups/BackupImpl.h | 35 +- src/Backups/BackupsWorker.cpp | 43 +- src/Backups/BackupsWorker.h | 5 +- src/Backups/IBackup.h | 8 +- src/Backups/IBackupCoordination.h | 71 +-- src/Core/Settings.h | 1 + .../test_backup_restore_on_cluster/test.py | 33 ++ .../test_disallow_concurrency.py | 52 +- 19 files changed, 1104 insertions(+), 976 deletions(-) create mode 100644 src/Backups/BackupCoordinationFileInfos.cpp create mode 100644 src/Backups/BackupCoordinationFileInfos.h create mode 100644 src/Backups/BackupFileInfo.cpp create mode 100644 src/Backups/BackupFileInfo.h diff --git a/src/Backups/BackupCoordinationFileInfos.cpp b/src/Backups/BackupCoordinationFileInfos.cpp new file mode 100644 index 00000000000..44f00f6c543 --- /dev/null +++ b/src/Backups/BackupCoordinationFileInfos.cpp @@ -0,0 +1,146 @@ +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int BACKUP_ENTRY_ALREADY_EXISTS; + extern const int BAD_ARGUMENTS; + extern const int LOGICAL_ERROR; +} + +using SizeAndChecksum = std::pair; + + +void BackupCoordinationFileInfos::addFileInfos(BackupFileInfos && file_infos_, const String & host_id_) +{ + if (prepared) + throw Exception(ErrorCodes::LOGICAL_ERROR, "addFileInfos() must not be called after preparing"); + file_infos.emplace(host_id_, std::move(file_infos_)); +} + +BackupFileInfos BackupCoordinationFileInfos::getFileInfos(const String & host_id_) const +{ + prepare(); + auto it = file_infos.find(host_id_); + if (it == file_infos.end()) + return {}; + return it->second; +} + +BackupFileInfos BackupCoordinationFileInfos::getFileInfosForAllHosts() const +{ + prepare(); + BackupFileInfos res; + res.reserve(file_infos_for_all_hosts.size()); + for (const auto * file_info : file_infos_for_all_hosts) + res.emplace_back(*file_info); + return res; +} + +BackupFileInfo BackupCoordinationFileInfos::getFileInfoByDataFileIndex(size_t data_file_index) const +{ + prepare(); + if (data_file_index >= file_infos_for_all_hosts.size()) + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid data file index: {}", data_file_index); + return *(file_infos_for_all_hosts[data_file_index]); +} + +void BackupCoordinationFileInfos::prepare() const +{ + if (prepared) + return; + + /// Make a list of all file infos from all hosts. + size_t total_num_infos = 0; + for (const auto & [_, infos] : file_infos) + total_num_infos += infos.size(); + + file_infos_for_all_hosts.reserve(total_num_infos); + for (auto & [_, infos] : file_infos) + for (auto & info : infos) + file_infos_for_all_hosts.emplace_back(&info); + + /// Sort the list of all file infos by file name (file names must be unique). + std::sort(file_infos_for_all_hosts.begin(), file_infos_for_all_hosts.end(), BackupFileInfo::LessByFileName{}); + + auto adjacent_it = std::adjacent_find(file_infos_for_all_hosts.begin(), file_infos_for_all_hosts.end(), BackupFileInfo::EqualByFileName{}); + if (adjacent_it != file_infos_for_all_hosts.end()) + { + throw Exception( + ErrorCodes::BACKUP_ENTRY_ALREADY_EXISTS, "Entry {} added multiple times to backup", quoteString((*adjacent_it)->file_name)); + } + + num_files = 0; + total_size_of_files = 0; + + if (plain_backup) + { + /// For plain backup all file infos are stored as is, without checking for duplicates or skipping empty files. + for (size_t i = 0; i != file_infos_for_all_hosts.size(); ++i) + { + auto & info = *(file_infos_for_all_hosts[i]); + info.data_file_name = info.file_name; + info.data_file_index = i; + info.base_size = 0; /// Base backup must not be used while creating a plain backup. + info.base_checksum = 0; + total_size_of_files += info.size; + } + num_files = file_infos_for_all_hosts.size(); + } + else + { + /// For non-plain backups files with the same size and checksum are stored only once, + /// in order to find those files we'll use this map. + std::map data_file_index_by_checksum; + + for (size_t i = 0; i != file_infos_for_all_hosts.size(); ++i) + { + auto & info = *(file_infos_for_all_hosts[i]); + if (info.size == info.base_size) + { + /// A file is either empty or can be get from the base backup as a whole. + info.data_file_name.clear(); + info.data_file_index = static_cast(-1); + } + else + { + SizeAndChecksum size_and_checksum{info.size, info.checksum}; + auto [it, inserted] = data_file_index_by_checksum.emplace(size_and_checksum, i); + if (inserted) + { + /// Found a new file. + info.data_file_name = info.file_name; + info.data_file_index = i; + ++num_files; + total_size_of_files += info.size - info.base_size; + } + else + { + /// Found a file with the same size and checksum as some file before, reuse old `data_file_index` and `data_file_name`. + info.data_file_index = it->second; + info.data_file_name = file_infos_for_all_hosts[it->second]->data_file_name; + } + } + } + } + + prepared = true; +} + +size_t BackupCoordinationFileInfos::getNumFiles() const +{ + prepare(); + return num_files; +} + +size_t BackupCoordinationFileInfos::getTotalSizeOfFiles() const +{ + prepare(); + return total_size_of_files; +} + +} diff --git a/src/Backups/BackupCoordinationFileInfos.h b/src/Backups/BackupCoordinationFileInfos.h new file mode 100644 index 00000000000..b0d302b1767 --- /dev/null +++ b/src/Backups/BackupCoordinationFileInfos.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include + + +namespace DB +{ + +/// Hosts use this class to coordinate lists of files they are going to write to a backup. +/// Because different hosts shouldn't write the same file twice and or even files with different names but with the same checksum. +/// Also the initiator of the BACKUP query uses this class to get a whole list of files written by all hosts to write that list +/// as a part of the contents of the .backup file (the backup metadata file). +class BackupCoordinationFileInfos +{ +public: + /// plain_backup sets that we're writing a plain backup, which means all duplicates are written as is, and empty files are written as is. + /// (For normal backups only the first file amongst duplicates is actually stored, and empty files are not stored). + BackupCoordinationFileInfos(bool plain_backup_) : plain_backup(plain_backup_) {} + + /// Adds file infos for the specified host. + void addFileInfos(BackupFileInfos && file_infos, const String & host_id); + + /// Returns file infos for the specified host after preparation. + BackupFileInfos getFileInfos(const String & host_id) const; + + /// Returns file infos for all hosts after preparation. + BackupFileInfos getFileInfosForAllHosts() const; + + /// Returns a file info by data file index (see BackupFileInfo::data_file_index). + BackupFileInfo getFileInfoByDataFileIndex(size_t data_file_index) const; + + /// Returns the number of files after deduplication and excluding empty files. + size_t getNumFiles() const; + + /// Returns the total size of files after deduplication and excluding empty files. + size_t getTotalSizeOfFiles() const; + +private: + void prepare() const; + + /// before preparation + const bool plain_backup; + mutable std::unordered_map file_infos; + + /// after preparation + mutable bool prepared = false; + mutable std::vector file_infos_for_all_hosts; + mutable size_t num_files; + mutable size_t total_size_of_files; +}; + +} diff --git a/src/Backups/BackupCoordinationLocal.cpp b/src/Backups/BackupCoordinationLocal.cpp index 90f64f15d97..a96ed443b9c 100644 --- a/src/Backups/BackupCoordinationLocal.cpp +++ b/src/Backups/BackupCoordinationLocal.cpp @@ -1,16 +1,17 @@ #include #include #include +#include #include namespace DB { -using SizeAndChecksum = IBackupCoordination::SizeAndChecksum; -using FileInfo = IBackupCoordination::FileInfo; +BackupCoordinationLocal::BackupCoordinationLocal(bool plain_backup_) : file_infos(plain_backup_) +{ +} -BackupCoordinationLocal::BackupCoordinationLocal() = default; BackupCoordinationLocal::~BackupCoordinationLocal() = default; void BackupCoordinationLocal::setStage(const String &, const String &) @@ -33,187 +34,94 @@ Strings BackupCoordinationLocal::waitForStage(const String &, std::chrono::milli void BackupCoordinationLocal::addReplicatedPartNames(const String & table_shared_id, const String & table_name_for_logs, const String & replica_name, const std::vector & part_names_and_checksums) { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; replicated_tables.addPartNames(table_shared_id, table_name_for_logs, replica_name, part_names_and_checksums); } Strings BackupCoordinationLocal::getReplicatedPartNames(const String & table_shared_id, const String & replica_name) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; return replicated_tables.getPartNames(table_shared_id, replica_name); } void BackupCoordinationLocal::addReplicatedMutations(const String & table_shared_id, const String & table_name_for_logs, const String & replica_name, const std::vector & mutations) { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; replicated_tables.addMutations(table_shared_id, table_name_for_logs, replica_name, mutations); } std::vector BackupCoordinationLocal::getReplicatedMutations(const String & table_shared_id, const String & replica_name) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; return replicated_tables.getMutations(table_shared_id, replica_name); } void BackupCoordinationLocal::addReplicatedDataPath(const String & table_shared_id, const String & data_path) { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; replicated_tables.addDataPath(table_shared_id, data_path); } Strings BackupCoordinationLocal::getReplicatedDataPaths(const String & table_shared_id) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; return replicated_tables.getDataPaths(table_shared_id); } void BackupCoordinationLocal::addReplicatedAccessFilePath(const String & access_zk_path, AccessEntityType access_entity_type, const String & file_path) { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_access_mutex}; replicated_access.addFilePath(access_zk_path, access_entity_type, "", file_path); } Strings BackupCoordinationLocal::getReplicatedAccessFilePaths(const String & access_zk_path, AccessEntityType access_entity_type) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_access_mutex}; return replicated_access.getFilePaths(access_zk_path, access_entity_type, ""); } void BackupCoordinationLocal::addReplicatedSQLObjectsDir(const String & loader_zk_path, UserDefinedSQLObjectType object_type, const String & dir_path) { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_sql_objects_mutex}; replicated_sql_objects.addDirectory(loader_zk_path, object_type, "", dir_path); } Strings BackupCoordinationLocal::getReplicatedSQLObjectsDirs(const String & loader_zk_path, UserDefinedSQLObjectType object_type) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_sql_objects_mutex}; return replicated_sql_objects.getDirectories(loader_zk_path, object_type, ""); } -void BackupCoordinationLocal::addFileInfo(const FileInfo & file_info, bool & is_data_file_required) +void BackupCoordinationLocal::addFileInfos(BackupFileInfos && file_infos_) { - std::lock_guard lock{mutex}; - file_names.emplace(file_info.file_name, std::pair{file_info.size, file_info.checksum}); - if (!file_info.size) - { - is_data_file_required = false; - return; - } - bool inserted_file_info = file_infos.try_emplace(std::pair{file_info.size, file_info.checksum}, file_info).second; - is_data_file_required = inserted_file_info && (file_info.size > file_info.base_size); + std::lock_guard lock{file_infos_mutex}; + file_infos.addFileInfos(std::move(file_infos_), ""); } -void BackupCoordinationLocal::updateFileInfo(const FileInfo & file_info) +BackupFileInfos BackupCoordinationLocal::getFileInfos() const { - if (!file_info.size) - return; /// we don't keep FileInfos for empty files, nothing to update - - std::lock_guard lock{mutex}; - auto & dest = file_infos.at(std::pair{file_info.size, file_info.checksum}); - dest.archive_suffix = file_info.archive_suffix; + std::lock_guard lock{file_infos_mutex}; + return file_infos.getFileInfos(""); } -std::vector BackupCoordinationLocal::getAllFileInfos() const +BackupFileInfos BackupCoordinationLocal::getFileInfosForAllHosts() const { - std::lock_guard lock{mutex}; - std::vector res; - for (const auto & [file_name, size_and_checksum] : file_names) - { - FileInfo info; - UInt64 size = size_and_checksum.first; - if (size) /// we don't keep FileInfos for empty files - info = file_infos.at(size_and_checksum); - info.file_name = file_name; - res.push_back(std::move(info)); - } - return res; + std::lock_guard lock{file_infos_mutex}; + return file_infos.getFileInfosForAllHosts(); } -Strings BackupCoordinationLocal::listFiles(const String & directory, bool recursive) const +bool BackupCoordinationLocal::startWritingFile(size_t data_file_index) { - std::lock_guard lock{mutex}; - String prefix = directory; - if (!prefix.empty() && !prefix.ends_with('/')) - prefix += '/'; - String terminator = recursive ? "" : "/"; - - Strings elements; - for (auto it = file_names.lower_bound(prefix); it != file_names.end(); ++it) - { - const String & name = it->first; - if (!name.starts_with(prefix)) - break; - size_t start_pos = prefix.length(); - size_t end_pos = String::npos; - if (!terminator.empty()) - end_pos = name.find(terminator, start_pos); - std::string_view new_element = std::string_view{name}.substr(start_pos, end_pos - start_pos); - if (!elements.empty() && (elements.back() == new_element)) - continue; - elements.push_back(String{new_element}); - } - - return elements; + std::lock_guard lock{writing_files_mutex}; + /// Return false if this function was already called with this `data_file_index`. + return writing_files.emplace(data_file_index).second; } -bool BackupCoordinationLocal::hasFiles(const String & directory) const -{ - std::lock_guard lock{mutex}; - String prefix = directory; - if (!prefix.empty() && !prefix.ends_with('/')) - prefix += '/'; - - auto it = file_names.lower_bound(prefix); - if (it == file_names.end()) - return false; - - const String & name = it->first; - return name.starts_with(prefix); -} - -std::optional BackupCoordinationLocal::getFileInfo(const String & file_name) const -{ - std::lock_guard lock{mutex}; - auto it = file_names.find(file_name); - if (it == file_names.end()) - return std::nullopt; - const auto & size_and_checksum = it->second; - UInt64 size = size_and_checksum.first; - FileInfo info; - if (size) /// we don't keep FileInfos for empty files - info = file_infos.at(size_and_checksum); - info.file_name = file_name; - return info; -} - -std::optional BackupCoordinationLocal::getFileInfo(const SizeAndChecksum & size_and_checksum) const -{ - std::lock_guard lock{mutex}; - auto it = file_infos.find(size_and_checksum); - if (it == file_infos.end()) - return std::nullopt; - return it->second; -} - -String BackupCoordinationLocal::getNextArchiveSuffix() -{ - std::lock_guard lock{mutex}; - String new_archive_suffix = fmt::format("{:03}", ++current_archive_suffix); /// Outputs 001, 002, 003, ... - archive_suffixes.push_back(new_archive_suffix); - return new_archive_suffix; -} - -Strings BackupCoordinationLocal::getAllArchiveSuffixes() const -{ - std::lock_guard lock{mutex}; - return archive_suffixes; -} bool BackupCoordinationLocal::hasConcurrentBackups(const std::atomic & num_active_backups) const { diff --git a/src/Backups/BackupCoordinationLocal.h b/src/Backups/BackupCoordinationLocal.h index 21db165be67..db2070fa891 100644 --- a/src/Backups/BackupCoordinationLocal.h +++ b/src/Backups/BackupCoordinationLocal.h @@ -1,12 +1,13 @@ #pragma once #include +#include #include #include #include #include -#include #include +#include namespace Poco { class Logger; } @@ -18,7 +19,7 @@ namespace DB class BackupCoordinationLocal : public IBackupCoordination { public: - BackupCoordinationLocal(); + BackupCoordinationLocal(bool plain_backup_); ~BackupCoordinationLocal() override; void setStage(const String & new_stage, const String & message) override; @@ -43,31 +44,25 @@ public: void addReplicatedSQLObjectsDir(const String & loader_zk_path, UserDefinedSQLObjectType object_type, const String & dir_path) override; Strings getReplicatedSQLObjectsDirs(const String & loader_zk_path, UserDefinedSQLObjectType object_type) const override; - void addFileInfo(const FileInfo & file_info, bool & is_data_file_required) override; - void updateFileInfo(const FileInfo & file_info) override; - - std::vector getAllFileInfos() const override; - Strings listFiles(const String & directory, bool recursive) const override; - bool hasFiles(const String & directory) const override; - - std::optional getFileInfo(const String & file_name) const override; - std::optional getFileInfo(const SizeAndChecksum & size_and_checksum) const override; - - String getNextArchiveSuffix() override; - Strings getAllArchiveSuffixes() const override; + void addFileInfos(BackupFileInfos && file_infos) override; + BackupFileInfos getFileInfos() const override; + BackupFileInfos getFileInfosForAllHosts() const override; + bool startWritingFile(size_t data_file_index) override; bool hasConcurrentBackups(const std::atomic & num_active_backups) const override; private: - mutable std::mutex mutex; - BackupCoordinationReplicatedTables replicated_tables TSA_GUARDED_BY(mutex); - BackupCoordinationReplicatedAccess replicated_access TSA_GUARDED_BY(mutex); - BackupCoordinationReplicatedSQLObjects replicated_sql_objects TSA_GUARDED_BY(mutex); + BackupCoordinationReplicatedTables TSA_GUARDED_BY(replicated_tables_mutex) replicated_tables; + BackupCoordinationReplicatedAccess TSA_GUARDED_BY(replicated_access_mutex) replicated_access; + BackupCoordinationReplicatedSQLObjects TSA_GUARDED_BY(replicated_sql_objects_mutex) replicated_sql_objects; + BackupCoordinationFileInfos TSA_GUARDED_BY(file_infos_mutex) file_infos; + std::unordered_set TSA_GUARDED_BY(writing_files_mutex) writing_files; - std::map file_names TSA_GUARDED_BY(mutex); /// Should be ordered alphabetically, see listFiles(). For empty files we assume checksum = 0. - std::map file_infos TSA_GUARDED_BY(mutex); /// Information about files. Without empty files. - Strings archive_suffixes TSA_GUARDED_BY(mutex); - size_t current_archive_suffix TSA_GUARDED_BY(mutex) = 0; + mutable std::mutex replicated_tables_mutex; + mutable std::mutex replicated_access_mutex; + mutable std::mutex replicated_sql_objects_mutex; + mutable std::mutex file_infos_mutex; + mutable std::mutex writing_files_mutex; }; } diff --git a/src/Backups/BackupCoordinationRemote.cpp b/src/Backups/BackupCoordinationRemote.cpp index 5ad95490c95..9b4343a1d3b 100644 --- a/src/Backups/BackupCoordinationRemote.cpp +++ b/src/Backups/BackupCoordinationRemote.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include @@ -16,21 +15,13 @@ namespace DB namespace ErrorCodes { - extern const int UNEXPECTED_NODE_IN_ZOOKEEPER; extern const int LOGICAL_ERROR; } namespace Stage = BackupCoordinationStage; -/// zookeeper_path/file_names/file_name->checksum_and_size -/// zookeeper_path/file_infos/checksum_and_size->info -/// zookeeper_path/archive_suffixes -/// zookeeper_path/current_archive_suffix - namespace { - using SizeAndChecksum = IBackupCoordination::SizeAndChecksum; - using FileInfo = IBackupCoordination::FileInfo; using PartNameAndChecksum = IBackupCoordination::PartNameAndChecksum; using MutationInfo = IBackupCoordination::MutationInfo; @@ -104,66 +95,46 @@ namespace } }; - String serializeFileInfo(const FileInfo & info) + struct FileInfos { - WriteBufferFromOwnString out; - writeBinary(info.file_name, out); - writeBinary(info.size, out); - writeBinary(info.checksum, out); - writeBinary(info.base_size, out); - writeBinary(info.base_checksum, out); - writeBinary(info.data_file_name, out); - writeBinary(info.archive_suffix, out); - writeBinary(info.pos_in_archive, out); - return out.str(); - } + BackupFileInfos file_infos; - FileInfo deserializeFileInfo(const String & str) - { - FileInfo info; - ReadBufferFromString in{str}; - readBinary(info.file_name, in); - readBinary(info.size, in); - readBinary(info.checksum, in); - readBinary(info.base_size, in); - readBinary(info.base_checksum, in); - readBinary(info.data_file_name, in); - readBinary(info.archive_suffix, in); - readBinary(info.pos_in_archive, in); - return info; - } + static String serialize(const BackupFileInfos & file_infos_) + { + WriteBufferFromOwnString out; + writeBinary(file_infos_.size(), out); + for (const auto & info : file_infos_) + { + writeBinary(info.file_name, out); + writeBinary(info.size, out); + writeBinary(info.checksum, out); + writeBinary(info.base_size, out); + writeBinary(info.base_checksum, out); + /// We don't store `info.data_file_name` and `info.data_file_index` because they're determined automalically + /// after reading file infos for all the hosts (see the class BackupCoordinationFileInfos). + } + return out.str(); + } - String serializeSizeAndChecksum(const SizeAndChecksum & size_and_checksum) - { - return getHexUIntLowercase(size_and_checksum.second) + '_' + std::to_string(size_and_checksum.first); - } - - SizeAndChecksum deserializeSizeAndChecksum(const String & str) - { - constexpr size_t num_chars_in_checksum = sizeof(UInt128) * 2; - if (str.size() <= num_chars_in_checksum) - throw Exception( - ErrorCodes::UNEXPECTED_NODE_IN_ZOOKEEPER, - "Unexpected size of checksum: {}, must be {}", - str.size(), - num_chars_in_checksum); - UInt128 checksum = unhexUInt(str.data()); - UInt64 size = parseFromString(str.substr(num_chars_in_checksum + 1)); - return std::pair{size, checksum}; - } - - size_t extractCounterFromSequentialNodeName(const String & node_name) - { - size_t pos_before_counter = node_name.find_last_not_of("0123456789"); - size_t counter_length = node_name.length() - 1 - pos_before_counter; - auto counter = std::string_view{node_name}.substr(node_name.length() - counter_length); - return parseFromString(counter); - } - - String formatArchiveSuffix(size_t counter) - { - return fmt::format("{:03}", counter); /// Outputs 001, 002, 003, ... - } + static FileInfos deserialize(const String & str) + { + ReadBufferFromString in{str}; + FileInfos res; + size_t num; + readBinary(num, in); + res.file_infos.resize(num); + for (size_t i = 0; i != num; ++i) + { + auto & info = res.file_infos[i]; + readBinary(info.file_name, in); + readBinary(info.size, in); + readBinary(info.checksum, in); + readBinary(info.base_size, in); + readBinary(info.base_checksum, in); + } + return res; + } + }; } size_t BackupCoordinationRemote::findCurrentHostIndex(const Strings & all_hosts, const String & current_host) @@ -181,6 +152,7 @@ BackupCoordinationRemote::BackupCoordinationRemote( const String & backup_uuid_, const Strings & all_hosts_, const String & current_host_, + bool plain_backup_, bool is_internal_) : get_zookeeper(get_zookeeper_) , root_zookeeper_path(root_zookeeper_path_) @@ -190,6 +162,7 @@ BackupCoordinationRemote::BackupCoordinationRemote( , all_hosts(all_hosts_) , current_host(current_host_) , current_host_index(findCurrentHostIndex(all_hosts, current_host)) + , plain_backup(plain_backup_) , is_internal(is_internal_) { zookeeper_retries_info = ZooKeeperRetriesInfo( @@ -219,12 +192,7 @@ BackupCoordinationRemote::~BackupCoordinationRemote() zkutil::ZooKeeperPtr BackupCoordinationRemote::getZooKeeper() const { - std::lock_guard lock{mutex}; - return getZooKeeperNoLock(); -} - -zkutil::ZooKeeperPtr BackupCoordinationRemote::getZooKeeperNoLock() const -{ + std::lock_guard lock{zookeeper_mutex}; if (!zookeeper || zookeeper->expired()) { zookeeper = get_zookeeper(); @@ -246,9 +214,8 @@ void BackupCoordinationRemote::createRootNodes() zk->createIfNotExists(zookeeper_path + "/repl_data_paths", ""); zk->createIfNotExists(zookeeper_path + "/repl_access", ""); zk->createIfNotExists(zookeeper_path + "/repl_sql_objects", ""); - zk->createIfNotExists(zookeeper_path + "/file_names", ""); zk->createIfNotExists(zookeeper_path + "/file_infos", ""); - zk->createIfNotExists(zookeeper_path + "/archive_suffixes", ""); + zk->createIfNotExists(zookeeper_path + "/writing_files", ""); } void BackupCoordinationRemote::removeAllNodes() @@ -285,6 +252,72 @@ Strings BackupCoordinationRemote::waitForStage(const String & stage_to_wait, std } +void BackupCoordinationRemote::serializeToMultipleZooKeeperNodes(const String & path, const String & value, const String & logging_name) +{ + { + ZooKeeperRetriesControl retries_ctl(logging_name + "::create", zookeeper_retries_info); + retries_ctl.retryLoop([&] + { + auto zk = getZooKeeper(); + zk->createIfNotExists(path, ""); + }); + } + + if (value.empty()) + return; + + size_t max_part_size = keeper_settings.keeper_value_max_size; + if (!max_part_size) + max_part_size = value.size(); + + size_t num_parts = (value.size() + max_part_size - 1) / max_part_size; /// round up + + for (size_t i = 0; i != num_parts; ++i) + { + size_t begin = i * max_part_size; + size_t end = std::min(begin + max_part_size, value.size()); + String part = value.substr(begin, end - begin); + String part_path = fmt::format("{}/{:06}", path, i); + + ZooKeeperRetriesControl retries_ctl(logging_name + "::createPart", zookeeper_retries_info); + retries_ctl.retryLoop([&] + { + auto zk = getZooKeeper(); + zk->createIfNotExists(part_path, part); + }); + } +} + +String BackupCoordinationRemote::deserializeFromMultipleZooKeeperNodes(const String & path, const String & logging_name) const +{ + Strings part_names; + + { + ZooKeeperRetriesControl retries_ctl(logging_name + "::getChildren", zookeeper_retries_info); + retries_ctl.retryLoop([&]{ + auto zk = getZooKeeper(); + part_names = zk->getChildren(path); + std::sort(part_names.begin(), part_names.end()); + }); + } + + String res; + for (const String & part_name : part_names) + { + String part; + String part_path = path + "/" + part_name; + ZooKeeperRetriesControl retries_ctl(logging_name + "::get", zookeeper_retries_info); + retries_ctl.retryLoop([&] + { + auto zk = getZooKeeper(); + part = zk->get(part_path); + }); + res += part; + } + return res; +} + + void BackupCoordinationRemote::addReplicatedPartNames( const String & table_shared_id, const String & table_name_for_logs, @@ -292,7 +325,7 @@ void BackupCoordinationRemote::addReplicatedPartNames( const std::vector & part_names_and_checksums) { { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; if (replicated_tables) throw Exception(ErrorCodes::LOGICAL_ERROR, "addReplicatedPartNames() must not be called after preparing"); } @@ -306,7 +339,7 @@ void BackupCoordinationRemote::addReplicatedPartNames( Strings BackupCoordinationRemote::getReplicatedPartNames(const String & table_shared_id, const String & replica_name) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; prepareReplicatedTables(); return replicated_tables->getPartNames(table_shared_id, replica_name); } @@ -318,7 +351,7 @@ void BackupCoordinationRemote::addReplicatedMutations( const std::vector & mutations) { { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; if (replicated_tables) throw Exception(ErrorCodes::LOGICAL_ERROR, "addReplicatedMutations() must not be called after preparing"); } @@ -332,7 +365,7 @@ void BackupCoordinationRemote::addReplicatedMutations( std::vector BackupCoordinationRemote::getReplicatedMutations(const String & table_shared_id, const String & replica_name) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; prepareReplicatedTables(); return replicated_tables->getMutations(table_shared_id, replica_name); } @@ -342,7 +375,7 @@ void BackupCoordinationRemote::addReplicatedDataPath( const String & table_shared_id, const String & data_path) { { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; if (replicated_tables) throw Exception(ErrorCodes::LOGICAL_ERROR, "addReplicatedDataPath() must not be called after preparing"); } @@ -356,7 +389,7 @@ void BackupCoordinationRemote::addReplicatedDataPath( Strings BackupCoordinationRemote::getReplicatedDataPaths(const String & table_shared_id) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_tables_mutex}; prepareReplicatedTables(); return replicated_tables->getDataPaths(table_shared_id); } @@ -368,7 +401,7 @@ void BackupCoordinationRemote::prepareReplicatedTables() const return; replicated_tables.emplace(); - auto zk = getZooKeeperNoLock(); + auto zk = getZooKeeper(); { String path = zookeeper_path + "/repl_part_names"; @@ -419,7 +452,7 @@ void BackupCoordinationRemote::prepareReplicatedTables() const void BackupCoordinationRemote::addReplicatedAccessFilePath(const String & access_zk_path, AccessEntityType access_entity_type, const String & file_path) { { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_access_mutex}; if (replicated_access) throw Exception(ErrorCodes::LOGICAL_ERROR, "addReplicatedAccessFilePath() must not be called after preparing"); } @@ -435,7 +468,7 @@ void BackupCoordinationRemote::addReplicatedAccessFilePath(const String & access Strings BackupCoordinationRemote::getReplicatedAccessFilePaths(const String & access_zk_path, AccessEntityType access_entity_type) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_access_mutex}; prepareReplicatedAccess(); return replicated_access->getFilePaths(access_zk_path, access_entity_type, current_host); } @@ -446,7 +479,7 @@ void BackupCoordinationRemote::prepareReplicatedAccess() const return; replicated_access.emplace(); - auto zk = getZooKeeperNoLock(); + auto zk = getZooKeeper(); String path = zookeeper_path + "/repl_access"; for (const String & escaped_access_zk_path : zk->getChildren(path)) @@ -469,7 +502,7 @@ void BackupCoordinationRemote::prepareReplicatedAccess() const void BackupCoordinationRemote::addReplicatedSQLObjectsDir(const String & loader_zk_path, UserDefinedSQLObjectType object_type, const String & dir_path) { { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_sql_objects_mutex}; if (replicated_sql_objects) throw Exception(ErrorCodes::LOGICAL_ERROR, "addReplicatedSQLObjectsDir() must not be called after preparing"); } @@ -493,7 +526,7 @@ void BackupCoordinationRemote::addReplicatedSQLObjectsDir(const String & loader_ Strings BackupCoordinationRemote::getReplicatedSQLObjectsDirs(const String & loader_zk_path, UserDefinedSQLObjectType object_type) const { - std::lock_guard lock{mutex}; + std::lock_guard lock{replicated_sql_objects_mutex}; prepareReplicatedSQLObjects(); return replicated_sql_objects->getDirectories(loader_zk_path, object_type, current_host); } @@ -504,7 +537,7 @@ void BackupCoordinationRemote::prepareReplicatedSQLObjects() const return; replicated_sql_objects.emplace(); - auto zk = getZooKeeperNoLock(); + auto zk = getZooKeeper(); String path = zookeeper_path + "/repl_sql_objects"; for (const String & escaped_loader_zk_path : zk->getChildren(path)) @@ -525,274 +558,80 @@ void BackupCoordinationRemote::prepareReplicatedSQLObjects() const } -void BackupCoordinationRemote::addFileInfo(const FileInfo & file_info, bool & is_data_file_required) +void BackupCoordinationRemote::addFileInfos(BackupFileInfos && file_infos_) { - auto zk = getZooKeeper(); - - String full_path = zookeeper_path + "/file_names/" + escapeForFileName(file_info.file_name); - String size_and_checksum = serializeSizeAndChecksum(std::pair{file_info.size, file_info.checksum}); - zk->create(full_path, size_and_checksum, zkutil::CreateMode::Persistent); - - if (!file_info.size) { - is_data_file_required = false; + std::lock_guard lock{file_infos_mutex}; + if (file_infos) + throw Exception(ErrorCodes::LOGICAL_ERROR, "addFileInfos() must not be called after preparing"); + } + + /// Serialize `file_infos_` and write it to ZooKeeper's nodes. + String file_infos_str = FileInfos::serialize(file_infos_); + serializeToMultipleZooKeeperNodes(zookeeper_path + "/file_infos/" + current_host, file_infos_str, "addFileInfos"); +} + +BackupFileInfos BackupCoordinationRemote::getFileInfos() const +{ + std::lock_guard lock{file_infos_mutex}; + prepareFileInfos(); + return file_infos->getFileInfos(current_host); +} + +BackupFileInfos BackupCoordinationRemote::getFileInfosForAllHosts() const +{ + std::lock_guard lock{file_infos_mutex}; + prepareFileInfos(); + return file_infos->getFileInfosForAllHosts(); +} + +void BackupCoordinationRemote::prepareFileInfos() const +{ + if (file_infos) return; + + file_infos.emplace(plain_backup); + + Strings hosts_with_file_infos; + { + ZooKeeperRetriesControl retries_ctl("prepareFileInfos::get_hosts", zookeeper_retries_info); + retries_ctl.retryLoop([&]{ + auto zk = getZooKeeper(); + hosts_with_file_infos = zk->getChildren(zookeeper_path + "/file_infos"); + }); } - full_path = zookeeper_path + "/file_infos/" + size_and_checksum; - auto code = zk->tryCreate(full_path, serializeFileInfo(file_info), zkutil::CreateMode::Persistent); - if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS)) - throw zkutil::KeeperException(code, full_path); - - is_data_file_required = (code == Coordination::Error::ZOK) && (file_info.size > file_info.base_size); + for (const String & host : hosts_with_file_infos) + { + String file_infos_str = deserializeFromMultipleZooKeeperNodes(zookeeper_path + "/file_infos/" + host, "prepareFileInfos"); + auto deserialized_file_infos = FileInfos::deserialize(file_infos_str).file_infos; + file_infos->addFileInfos(std::move(deserialized_file_infos), host); + } } -void BackupCoordinationRemote::updateFileInfo(const FileInfo & file_info) +bool BackupCoordinationRemote::startWritingFile(size_t data_file_index) { - if (!file_info.size) - return; /// we don't keep FileInfos for empty files, nothing to update + bool acquired_writing = false; + String full_path = zookeeper_path + "/writing_files/" + std::to_string(data_file_index); + String host_index_str = std::to_string(current_host_index); - auto zk = getZooKeeper(); - String size_and_checksum = serializeSizeAndChecksum(std::pair{file_info.size, file_info.checksum}); - String full_path = zookeeper_path + "/file_infos/" + size_and_checksum; - for (size_t attempt = 0; attempt < MAX_ZOOKEEPER_ATTEMPTS; ++attempt) + ZooKeeperRetriesControl retries_ctl("startWritingFile", zookeeper_retries_info); + retries_ctl.retryLoop([&] { - Coordination::Stat stat; - auto new_info = deserializeFileInfo(zk->get(full_path, &stat)); - new_info.archive_suffix = file_info.archive_suffix; - auto code = zk->trySet(full_path, serializeFileInfo(new_info), stat.version); + auto zk = getZooKeeper(); + auto code = zk->tryCreate(full_path, host_index_str, zkutil::CreateMode::Persistent); + if (code == Coordination::Error::ZOK) - return; - bool is_last_attempt = (attempt == MAX_ZOOKEEPER_ATTEMPTS - 1); - if ((code != Coordination::Error::ZBADVERSION) || is_last_attempt) + acquired_writing = true; /// If we've just created this ZooKeeper's node, the writing is acquired, i.e. we should write this data file. + else if (code == Coordination::Error::ZNODEEXISTS) + acquired_writing = (zk->get(full_path) == host_index_str); /// The previous retry could write this ZooKeeper's node and then fail. + else throw zkutil::KeeperException(code, full_path); - } + }); + + return acquired_writing; } -std::vector BackupCoordinationRemote::getAllFileInfos() const -{ - /// There could be tons of files inside /file_names or /file_infos - /// Thus we use MultiRead requests for processing them - /// We also use [Zoo]Keeper retries and it should be safe, because - /// this function is called at the end after the actual copying is finished. - - auto split_vector = [](Strings && vec, size_t max_batch_size) -> std::vector - { - std::vector result; - size_t left_border = 0; - - auto move_to_result = [&](auto && begin, auto && end) - { - auto batch = Strings(); - batch.reserve(max_batch_size); - std::move(begin, end, std::back_inserter(batch)); - result.push_back(std::move(batch)); - }; - - if (max_batch_size == 0) - { - move_to_result(vec.begin(), vec.end()); - return result; - } - - for (size_t pos = 0; pos < vec.size(); ++pos) - { - if (pos >= left_border + max_batch_size) - { - move_to_result(vec.begin() + left_border, vec.begin() + pos); - left_border = pos; - } - } - - if (vec.begin() + left_border != vec.end()) - move_to_result(vec.begin() + left_border, vec.end()); - - return result; - }; - - std::vector batched_escaped_names; - { - ZooKeeperRetriesControl retries_ctl("getAllFileInfos::getChildren", zookeeper_retries_info); - retries_ctl.retryLoop([&]() - { - auto zk = getZooKeeper(); - batched_escaped_names = split_vector(zk->getChildren(zookeeper_path + "/file_names"), keeper_settings.batch_size_for_keeper_multiread); - }); - } - - std::vector file_infos; - file_infos.reserve(batched_escaped_names.size()); - - for (auto & batch : batched_escaped_names) - { - zkutil::ZooKeeper::MultiGetResponse sizes_and_checksums; - { - Strings file_names_paths; - file_names_paths.reserve(batch.size()); - for (const String & escaped_name : batch) - file_names_paths.emplace_back(zookeeper_path + "/file_names/" + escaped_name); - - - ZooKeeperRetriesControl retries_ctl("getAllFileInfos::getSizesAndChecksums", zookeeper_retries_info); - retries_ctl.retryLoop([&] - { - auto zk = getZooKeeper(); - sizes_and_checksums = zk->get(file_names_paths); - }); - } - - Strings non_empty_file_names; - Strings non_empty_file_infos_paths; - std::vector non_empty_files_infos; - - /// Process all files and understand whether there are some empty files - /// Save non empty file names for further batch processing - { - std::vector empty_files_infos; - for (size_t i = 0; i < batch.size(); ++i) - { - auto file_name = batch[i]; - if (sizes_and_checksums[i].error != Coordination::Error::ZOK) - throw zkutil::KeeperException(sizes_and_checksums[i].error); - const auto & size_and_checksum = sizes_and_checksums[i].data; - auto size = deserializeSizeAndChecksum(size_and_checksum).first; - - if (size) - { - /// Save it later for batch processing - non_empty_file_names.emplace_back(file_name); - non_empty_file_infos_paths.emplace_back(zookeeper_path + "/file_infos/" + size_and_checksum); - continue; - } - - /// File is empty - FileInfo empty_file_info; - empty_file_info.file_name = unescapeForFileName(file_name); - empty_files_infos.emplace_back(std::move(empty_file_info)); - } - - std::move(empty_files_infos.begin(), empty_files_infos.end(), std::back_inserter(file_infos)); - } - - zkutil::ZooKeeper::MultiGetResponse non_empty_file_infos_serialized; - ZooKeeperRetriesControl retries_ctl("getAllFileInfos::getFileInfos", zookeeper_retries_info); - retries_ctl.retryLoop([&]() - { - auto zk = getZooKeeper(); - non_empty_file_infos_serialized = zk->get(non_empty_file_infos_paths); - }); - - /// Process non empty files - for (size_t i = 0; i < non_empty_file_names.size(); ++i) - { - FileInfo file_info; - if (non_empty_file_infos_serialized[i].error != Coordination::Error::ZOK) - throw zkutil::KeeperException(non_empty_file_infos_serialized[i].error); - file_info = deserializeFileInfo(non_empty_file_infos_serialized[i].data); - file_info.file_name = unescapeForFileName(non_empty_file_names[i]); - non_empty_files_infos.emplace_back(std::move(file_info)); - } - - std::move(non_empty_files_infos.begin(), non_empty_files_infos.end(), std::back_inserter(file_infos)); - } - - return file_infos; -} - -Strings BackupCoordinationRemote::listFiles(const String & directory, bool recursive) const -{ - auto zk = getZooKeeper(); - Strings escaped_names = zk->getChildren(zookeeper_path + "/file_names"); - - String prefix = directory; - if (!prefix.empty() && !prefix.ends_with('/')) - prefix += '/'; - String terminator = recursive ? "" : "/"; - - Strings elements; - std::unordered_set unique_elements; - - for (const String & escaped_name : escaped_names) - { - String name = unescapeForFileName(escaped_name); - if (!name.starts_with(prefix)) - continue; - size_t start_pos = prefix.length(); - size_t end_pos = String::npos; - if (!terminator.empty()) - end_pos = name.find(terminator, start_pos); - std::string_view new_element = std::string_view{name}.substr(start_pos, end_pos - start_pos); - if (unique_elements.contains(new_element)) - continue; - elements.push_back(String{new_element}); - unique_elements.emplace(new_element); - } - - ::sort(elements.begin(), elements.end()); - return elements; -} - -bool BackupCoordinationRemote::hasFiles(const String & directory) const -{ - auto zk = getZooKeeper(); - Strings escaped_names = zk->getChildren(zookeeper_path + "/file_names"); - - String prefix = directory; - if (!prefix.empty() && !prefix.ends_with('/')) - prefix += '/'; - - for (const String & escaped_name : escaped_names) - { - String name = unescapeForFileName(escaped_name); - if (name.starts_with(prefix)) - return true; - } - - return false; -} - -std::optional BackupCoordinationRemote::getFileInfo(const String & file_name) const -{ - auto zk = getZooKeeper(); - String size_and_checksum; - if (!zk->tryGet(zookeeper_path + "/file_names/" + escapeForFileName(file_name), size_and_checksum)) - return std::nullopt; - UInt64 size = deserializeSizeAndChecksum(size_and_checksum).first; - FileInfo file_info; - if (size) /// we don't keep FileInfos for empty files - file_info = deserializeFileInfo(zk->get(zookeeper_path + "/file_infos/" + size_and_checksum)); - file_info.file_name = file_name; - return file_info; -} - -std::optional BackupCoordinationRemote::getFileInfo(const SizeAndChecksum & size_and_checksum) const -{ - auto zk = getZooKeeper(); - String file_info_str; - if (!zk->tryGet(zookeeper_path + "/file_infos/" + serializeSizeAndChecksum(size_and_checksum), file_info_str)) - return std::nullopt; - return deserializeFileInfo(file_info_str); -} - -String BackupCoordinationRemote::getNextArchiveSuffix() -{ - auto zk = getZooKeeper(); - String path = zookeeper_path + "/archive_suffixes/a"; - String path_created; - auto code = zk->tryCreate(path, "", zkutil::CreateMode::PersistentSequential, path_created); - if (code != Coordination::Error::ZOK) - throw zkutil::KeeperException(code, path); - return formatArchiveSuffix(extractCounterFromSequentialNodeName(path_created)); -} - -Strings BackupCoordinationRemote::getAllArchiveSuffixes() const -{ - auto zk = getZooKeeper(); - Strings node_names = zk->getChildren(zookeeper_path + "/archive_suffixes"); - for (auto & node_name : node_names) - node_name = formatArchiveSuffix(extractCounterFromSequentialNodeName(node_name)); - return node_names; -} bool BackupCoordinationRemote::hasConcurrentBackups(const std::atomic &) const { diff --git a/src/Backups/BackupCoordinationRemote.h b/src/Backups/BackupCoordinationRemote.h index 268f20b9e39..e7f5ff3a211 100644 --- a/src/Backups/BackupCoordinationRemote.h +++ b/src/Backups/BackupCoordinationRemote.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -23,7 +24,7 @@ public: UInt64 keeper_max_retries; UInt64 keeper_retry_initial_backoff_ms; UInt64 keeper_retry_max_backoff_ms; - UInt64 batch_size_for_keeper_multiread; + UInt64 keeper_value_max_size; }; BackupCoordinationRemote( @@ -33,6 +34,7 @@ public: const String & backup_uuid_, const Strings & all_hosts_, const String & current_host_, + bool plain_backup_, bool is_internal_); ~BackupCoordinationRemote() override; @@ -67,17 +69,10 @@ public: void addReplicatedSQLObjectsDir(const String & loader_zk_path, UserDefinedSQLObjectType object_type, const String & dir_path) override; Strings getReplicatedSQLObjectsDirs(const String & loader_zk_path, UserDefinedSQLObjectType object_type) const override; - void addFileInfo(const FileInfo & file_info, bool & is_data_file_required) override; - void updateFileInfo(const FileInfo & file_info) override; - - std::vector getAllFileInfos() const override; - Strings listFiles(const String & directory, bool recursive) const override; - bool hasFiles(const String & directory) const override; - std::optional getFileInfo(const String & file_name) const override; - std::optional getFileInfo(const SizeAndChecksum & size_and_checksum) const override; - - String getNextArchiveSuffix() override; - Strings getAllArchiveSuffixes() const override; + void addFileInfos(BackupFileInfos && file_infos) override; + BackupFileInfos getFileInfos() const override; + BackupFileInfos getFileInfosForAllHosts() const override; + bool startWritingFile(size_t data_file_index) override; bool hasConcurrentBackups(const std::atomic & num_active_backups) const override; @@ -85,16 +80,19 @@ public: private: zkutil::ZooKeeperPtr getZooKeeper() const; - zkutil::ZooKeeperPtr getZooKeeperNoLock() const; void createRootNodes(); void removeAllNodes(); + void serializeToMultipleZooKeeperNodes(const String & path, const String & value, const String & logging_name); + String deserializeFromMultipleZooKeeperNodes(const String & path, const String & logging_name) const; + /// Reads data of all objects from ZooKeeper that replicas have added to backup and add it to the corresponding /// BackupCoordinationReplicated* objects. /// After that, calling addReplicated* functions is not allowed and throws an exception. - void prepareReplicatedTables() const; - void prepareReplicatedAccess() const; - void prepareReplicatedSQLObjects() const; + void prepareReplicatedTables() const TSA_REQUIRES(replicated_tables_mutex); + void prepareReplicatedAccess() const TSA_REQUIRES(replicated_access_mutex); + void prepareReplicatedSQLObjects() const TSA_REQUIRES(replicated_sql_objects_mutex); + void prepareFileInfos() const TSA_REQUIRES(file_infos_mutex); const zkutil::GetZooKeeper get_zookeeper; const String root_zookeeper_path; @@ -104,16 +102,23 @@ private: const Strings all_hosts; const String current_host; const size_t current_host_index; + const bool plain_backup; const bool is_internal; mutable ZooKeeperRetriesInfo zookeeper_retries_info; std::optional stage_sync; - mutable std::mutex mutex; - mutable zkutil::ZooKeeperPtr zookeeper; - mutable std::optional replicated_tables; - mutable std::optional replicated_access; - mutable std::optional replicated_sql_objects; + mutable zkutil::ZooKeeperPtr TSA_GUARDED_BY(zookeeper_mutex) zookeeper; + mutable std::optional TSA_GUARDED_BY(replicated_tables_mutex) replicated_tables; + mutable std::optional TSA_GUARDED_BY(replicated_access_mutex) replicated_access; + mutable std::optional TSA_GUARDED_BY(replicated_sql_objects_mutex) replicated_sql_objects; + mutable std::optional TSA_GUARDED_BY(file_infos_mutex) file_infos; + + mutable std::mutex zookeeper_mutex; + mutable std::mutex replicated_tables_mutex; + mutable std::mutex replicated_access_mutex; + mutable std::mutex replicated_sql_objects_mutex; + mutable std::mutex file_infos_mutex; }; } diff --git a/src/Backups/BackupCoordinationStage.h b/src/Backups/BackupCoordinationStage.h index 2c02b651851..40a4b262caa 100644 --- a/src/Backups/BackupCoordinationStage.h +++ b/src/Backups/BackupCoordinationStage.h @@ -23,6 +23,9 @@ namespace BackupCoordinationStage /// Running special tasks for replicated tables which can also prepare some backup entries. constexpr const char * RUNNING_POST_TASKS = "running post-tasks"; + /// Building information about all files which will be written to a backup. + constexpr const char * BUILDING_FILE_INFOS = "building file infos"; + /// Writing backup entries to the backup and removing temporary hard links. constexpr const char * WRITING_BACKUP = "writing backup"; diff --git a/src/Backups/BackupEntriesCollector.cpp b/src/Backups/BackupEntriesCollector.cpp index fc3eab5b8f0..4d117f54342 100644 --- a/src/Backups/BackupEntriesCollector.cpp +++ b/src/Backups/BackupEntriesCollector.cpp @@ -123,7 +123,6 @@ BackupEntries BackupEntriesCollector::run() runPostTasks(); /// No more backup entries or tasks are allowed after this point. - setStage(Stage::WRITING_BACKUP); return std::move(backup_entries); } diff --git a/src/Backups/BackupFileInfo.cpp b/src/Backups/BackupFileInfo.cpp new file mode 100644 index 00000000000..24548ca05fe --- /dev/null +++ b/src/Backups/BackupFileInfo.cpp @@ -0,0 +1,284 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace +{ + using SizeAndChecksum = std::pair; + + std::optional getInfoAboutFileFromBaseBackupIfExists(const BackupPtr & base_backup, const std::string & file_path) + { + if (base_backup && base_backup->fileExists(file_path)) + return base_backup->getFileSizeAndChecksum(file_path); + + return std::nullopt; + } + + enum class CheckBackupResult + { + HasPrefix, + HasFull, + HasNothing, + }; + + CheckBackupResult checkBaseBackupForFile(const SizeAndChecksum & base_backup_info, const BackupFileInfo & new_entry_info) + { + /// We cannot reuse base backup because our file is smaller + /// than file stored in previous backup + if (new_entry_info.size < base_backup_info.first) + return CheckBackupResult::HasNothing; + + if (base_backup_info.first == new_entry_info.size) + return CheckBackupResult::HasFull; + + return CheckBackupResult::HasPrefix; + + } + + struct ChecksumsForNewEntry + { + UInt128 full_checksum; + UInt128 prefix_checksum; + }; + + /// Calculate checksum for backup entry if it's empty. + /// Also able to calculate additional checksum of some prefix. + ChecksumsForNewEntry calculateNewEntryChecksumsIfNeeded(const BackupEntryPtr & entry, size_t prefix_size) + { + if (prefix_size > 0) + { + auto read_buffer = entry->getReadBuffer(); + HashingReadBuffer hashing_read_buffer(*read_buffer); + hashing_read_buffer.ignore(prefix_size); + auto prefix_checksum = hashing_read_buffer.getHash(); + if (entry->getChecksum() == std::nullopt) + { + hashing_read_buffer.ignoreAll(); + auto full_checksum = hashing_read_buffer.getHash(); + return ChecksumsForNewEntry{full_checksum, prefix_checksum}; + } + else + { + return ChecksumsForNewEntry{*(entry->getChecksum()), prefix_checksum}; + } + } + else + { + if (entry->getChecksum() == std::nullopt) + { + auto read_buffer = entry->getReadBuffer(); + HashingReadBuffer hashing_read_buffer(*read_buffer); + hashing_read_buffer.ignoreAll(); + return ChecksumsForNewEntry{hashing_read_buffer.getHash(), 0}; + } + else + { + return ChecksumsForNewEntry{*(entry->getChecksum()), 0}; + } + } + } + + /// We store entries' file names in the backup without leading slashes. + String removeLeadingSlash(const String & path) + { + if (path.starts_with('/')) + return path.substr(1); + return path; + } +} + + +/// Note: this format doesn't allow to parse data back +/// It is useful only for debugging purposes +String BackupFileInfo::describe() const +{ + String result; + result += fmt::format("file_name: {};\n", file_name); + result += fmt::format("size: {};\n", size); + result += fmt::format("checksum: {};\n", getHexUIntLowercase(checksum)); + result += fmt::format("base_size: {};\n", base_size); + result += fmt::format("base_checksum: {};\n", getHexUIntLowercase(checksum)); + result += fmt::format("data_file_name: {};\n", data_file_name); + result += fmt::format("data_file_index: {};\n", data_file_index); + return result; +} + + +BackupFileInfo buildFileInfoForBackupEntry(const String & file_name, const BackupEntryPtr & backup_entry, const BackupPtr & base_backup, Poco::Logger * log) +{ + auto adjusted_path = removeLeadingSlash(file_name); + + BackupFileInfo info; + info.file_name = adjusted_path; + info.size = backup_entry->getSize(); + + /// We don't set `info.data_file_name` and `info.data_file_index` in this function because they're set during backup coordination + /// (see the class BackupCoordinationFileInfos). + + if (!info.size) + { + /// Empty file. + return info; + } + + if (!log) + log = &Poco::Logger::get("FileInfoFromBackupEntry"); + + std::optional base_backup_file_info = getInfoAboutFileFromBaseBackupIfExists(base_backup, adjusted_path); + + /// We have info about this file in base backup + /// If file has no checksum -- calculate and fill it. + if (base_backup_file_info.has_value()) + { + LOG_TRACE(log, "File {} found in base backup, checking for equality", adjusted_path); + CheckBackupResult check_base = checkBaseBackupForFile(*base_backup_file_info, info); + + /// File with the same name but smaller size exist in previous backup + if (check_base == CheckBackupResult::HasPrefix) + { + auto checksums = calculateNewEntryChecksumsIfNeeded(backup_entry, base_backup_file_info->first); + info.checksum = checksums.full_checksum; + + /// We have prefix of this file in backup with the same checksum. + /// In ClickHouse this can happen for StorageLog for example. + if (checksums.prefix_checksum == base_backup_file_info->second) + { + LOG_TRACE(log, "Found prefix of file {} in the base backup, will write rest of the file to current backup", adjusted_path); + info.base_size = base_backup_file_info->first; + info.base_checksum = base_backup_file_info->second; + } + else + { + LOG_TRACE(log, "Prefix of file {} doesn't match the file in the base backup", adjusted_path); + } + } + else + { + /// We have full file or have nothing, first of all let's get checksum + /// of current file + auto checksums = calculateNewEntryChecksumsIfNeeded(backup_entry, 0); + info.checksum = checksums.full_checksum; + + if (info.checksum == base_backup_file_info->second) + { + LOG_TRACE(log, "Found whole file {} in base backup", adjusted_path); + assert(check_base == CheckBackupResult::HasFull); + assert(info.size == base_backup_file_info->first); + + info.base_size = base_backup_file_info->first; + info.base_checksum = base_backup_file_info->second; + /// Actually we can add this info to coordination and exist, + /// but we intentionally don't do it, otherwise control flow + /// of this function will be very complex. + } + else + { + LOG_TRACE(log, "Whole file {} in base backup doesn't match by checksum", adjusted_path); + } + } + } + else + { + auto checksums = calculateNewEntryChecksumsIfNeeded(backup_entry, 0); + info.checksum = checksums.full_checksum; + } + + /// We don't have info about this file_name (sic!) in base backup, + /// however file could be renamed, so we will check one more time using size and checksum + if (base_backup && base_backup->fileExists(std::pair{info.size, info.checksum})) + { + LOG_TRACE(log, "Found a file in the base backup with the same size and checksum as {}", adjusted_path); + info.base_size = info.size; + info.base_checksum = info.checksum; + } + + if (base_backup && !info.base_size) + LOG_TRACE(log, "Nothing found for file {} in base backup", adjusted_path); + + return info; +} + +BackupFileInfos buildFileInfosForBackupEntries(const BackupEntries & backup_entries, const BackupPtr & base_backup, ThreadPool & thread_pool) +{ + BackupFileInfos infos; + infos.resize(backup_entries.size()); + + size_t num_active_jobs = 0; + std::mutex mutex; + std::condition_variable event; + std::exception_ptr exception; + + auto thread_group = CurrentThread::getGroup(); + Poco::Logger * log = &Poco::Logger::get("FileInfosFromBackupEntries"); + + for (size_t i = 0; i != backup_entries.size(); ++i) + { + { + std::lock_guard lock{mutex}; + if (exception) + break; + ++num_active_jobs; + } + + auto job = [&mutex, &num_active_jobs, &event, &exception, &infos, &backup_entries, &base_backup, &thread_group, i, log](bool async) + { + SCOPE_EXIT_SAFE({ + std::lock_guard lock{mutex}; + if (!--num_active_jobs) + event.notify_all(); + if (async) + CurrentThread::detachFromGroupIfNotDetached(); + }); + + try + { + const auto & name = backup_entries[i].first; + const auto & entry = backup_entries[i].second; + + if (async && thread_group) + CurrentThread::attachToGroup(thread_group); + + if (async) + setThreadName("BackupWorker"); + + { + std::lock_guard lock{mutex}; + if (exception) + return; + } + + infos[i] = buildFileInfoForBackupEntry(name, entry, base_backup, log); + } + catch (...) + { + std::lock_guard lock{mutex}; + if (!exception) + exception = std::current_exception(); + } + }; + + if (!thread_pool.trySchedule([job] { job(true); })) + job(false); + } + + { + std::unique_lock lock{mutex}; + event.wait(lock, [&] { return !num_active_jobs; }); + if (exception) + std::rethrow_exception(exception); + } + + return infos; +} + +} diff --git a/src/Backups/BackupFileInfo.h b/src/Backups/BackupFileInfo.h new file mode 100644 index 00000000000..96df8ab2e0b --- /dev/null +++ b/src/Backups/BackupFileInfo.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + + +namespace DB +{ + +class IBackup; +class IBackupEntry; +using BackupPtr = std::shared_ptr; +using BackupEntryPtr = std::shared_ptr; +using BackupEntries = std::vector>; + + +/// Information about a file stored in a backup. +struct BackupFileInfo +{ + String file_name; + + UInt64 size = 0; + UInt128 checksum{0}; + + /// for incremental backups + UInt64 base_size = 0; + UInt128 base_checksum{0}; + + /// Name of the data file. An empty string means there is no data file (that can happen if the file is empty or was taken from the base backup as a whole). + /// This field is set during backup coordination (see the class BackupCoordinationFileInfos). + String data_file_name; + + /// Index of the data file. -1 means there is no data file. + /// This field is set during backup coordination (see the class BackupCoordinationFileInfos). + size_t data_file_index = static_cast(-1); + + struct LessByFileName + { + bool operator()(const BackupFileInfo & lhs, const BackupFileInfo & rhs) const { return (lhs.file_name < rhs.file_name); } + bool operator()(const BackupFileInfo * lhs, const BackupFileInfo * rhs) const { return (lhs->file_name < rhs->file_name); } + }; + + struct EqualByFileName + { + bool operator()(const BackupFileInfo & lhs, const BackupFileInfo & rhs) const { return (lhs.file_name == rhs.file_name); } + bool operator()(const BackupFileInfo * lhs, const BackupFileInfo * rhs) const { return (lhs->file_name == rhs->file_name); } + }; + + struct LessBySizeOrChecksum + { + bool operator()(const BackupFileInfo & lhs, const BackupFileInfo & rhs) const + { + return (lhs.size < rhs.size) || (lhs.size == rhs.size && lhs.checksum < rhs.checksum); + } + }; + + /// Note: this format doesn't allow to parse data back. + /// Must be used only for debugging purposes. + String describe() const; +}; + +using BackupFileInfos = std::vector; + +/// Builds a BackupFileInfo for a specified backup entry. +BackupFileInfo buildFileInfoForBackupEntry(const String & file_name, const BackupEntryPtr & backup_entry, const BackupPtr & base_backup, Poco::Logger * log); + +/// Builds a vector of BackupFileInfos for specified backup entries. +BackupFileInfos buildFileInfosForBackupEntries(const BackupEntries & backup_entries, const BackupPtr & base_backup, ThreadPool & thread_pool); + +} diff --git a/src/Backups/BackupImpl.cpp b/src/Backups/BackupImpl.cpp index a89dc2ff2b3..0ab1bf7f997 100644 --- a/src/Backups/BackupImpl.cpp +++ b/src/Backups/BackupImpl.cpp @@ -1,12 +1,11 @@ #include #include -#include +#include #include #include -#include -#include #include #include +#include #include #include #include @@ -15,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -36,7 +34,6 @@ namespace ErrorCodes extern const int BACKUP_DAMAGED; extern const int NO_BASE_BACKUP; extern const int WRONG_BASE_BACKUP; - extern const int BACKUP_ENTRY_ALREADY_EXISTS; extern const int BACKUP_ENTRY_NOT_FOUND; extern const int BACKUP_IS_EMPTY; extern const int FAILED_TO_SYNC_BACKUP_OR_RESTORE; @@ -49,7 +46,6 @@ namespace const int CURRENT_BACKUP_VERSION = 1; using SizeAndChecksum = IBackup::SizeAndChecksum; - using FileInfo = IBackupCoordination::FileInfo; String hexChecksum(UInt128 checksum) { @@ -86,12 +82,11 @@ BackupImpl::BackupImpl( std::shared_ptr reader_, const ContextPtr & context_) : backup_name_for_logging(backup_name_for_logging_) + , use_archive(!archive_params_.archive_name.empty()) , archive_params(archive_params_) - , use_archives(!archive_params.archive_name.empty()) , open_mode(OpenMode::READ) , reader(std::move(reader_)) , is_internal_backup(false) - , coordination(std::make_shared()) , version(INITIAL_BACKUP_VERSION) , base_backup_info(base_backup_info_) { @@ -110,8 +105,8 @@ BackupImpl::BackupImpl( const std::optional & backup_uuid_, bool deduplicate_files_) : backup_name_for_logging(backup_name_for_logging_) + , use_archive(!archive_params_.archive_name.empty()) , archive_params(archive_params_) - , use_archives(!archive_params.archive_name.empty()) , open_mode(OpenMode::WRITE) , writer(std::move(writer_)) , is_internal_backup(is_internal_backup_) @@ -147,7 +142,7 @@ void BackupImpl::open(const ContextPtr & context) timestamp = std::time(nullptr); if (!uuid) uuid = UUIDHelpers::generateV4(); - lock_file_name = use_archives ? (archive_params.archive_name + ".lock") : ".lock"; + lock_file_name = use_archive ? (archive_params.archive_name + ".lock") : ".lock"; writing_finalized = false; /// Check that we can write a backup there and create the lock file to own this destination. @@ -157,6 +152,9 @@ void BackupImpl::open(const ContextPtr & context) checkLockFile(true); } + if (use_archive) + openArchive(); + if (open_mode == OpenMode::READ) readBackupMetadata(); @@ -188,7 +186,7 @@ void BackupImpl::open(const ContextPtr & context) void BackupImpl::close() { std::lock_guard lock{mutex}; - closeArchives(); + closeArchive(); if (!is_internal_backup && writer && !writing_finalized) removeAllFilesAfterFailure(); @@ -198,11 +196,33 @@ void BackupImpl::close() coordination.reset(); } -void BackupImpl::closeArchives() +void BackupImpl::openArchive() { - archive_readers.clear(); - for (auto & archive_writer : archive_writers) - archive_writer = {"", nullptr}; + if (!use_archive) + return; + + const String & archive_name = archive_params.archive_name; + + if (open_mode == OpenMode::READ) + { + if (!reader->fileExists(archive_name)) + throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name_for_logging); + size_t archive_size = reader->getFileSize(archive_name); + archive_reader = createArchiveReader(archive_name, [reader=reader, archive_name]{ return reader->readFile(archive_name); }, archive_size); + archive_reader->setPassword(archive_params.password); + } + else + { + archive_writer = createArchiveWriter(archive_name, writer->writeFile(archive_name)); + archive_writer->setPassword(archive_params.password); + archive_writer->setCompression(archive_params.compression_method, archive_params.compression_level); + } +} + +void BackupImpl::closeArchive() +{ + archive_reader.reset(); + archive_writer.reset(); } size_t BackupImpl::getNumFiles() const @@ -260,8 +280,8 @@ void BackupImpl::writeBackupMetadata() checkLockFile(true); std::unique_ptr out; - if (use_archives) - out = getArchiveWriter("")->writeFile(".backup"); + if (use_archive) + out = archive_writer->writeFile(".backup"); else out = writer->writeFile(".backup"); @@ -271,7 +291,10 @@ void BackupImpl::writeBackupMetadata() *out << "" << toString(LocalDateTime{timestamp}) << ""; *out << "" << toString(*uuid) << ""; - auto all_file_infos = coordination->getAllFileInfos(); + auto all_file_infos = coordination->getFileInfosForAllHosts(); + + if (all_file_infos.empty()) + throw Exception(ErrorCodes::BACKUP_IS_EMPTY, "Backup must not be empty"); if (base_backup_info) { @@ -316,10 +339,6 @@ void BackupImpl::writeBackupMetadata() } if (!info.data_file_name.empty() && (info.data_file_name != info.file_name)) *out << "" << xml << info.data_file_name << ""; - if (!info.archive_suffix.empty()) - *out << "" << xml << info.archive_suffix << ""; - if (info.pos_in_archive != static_cast(-1)) - *out << "" << info.pos_in_archive << ""; } total_size += info.size; @@ -347,12 +366,12 @@ void BackupImpl::readBackupMetadata() using namespace XMLUtils; std::unique_ptr in; - if (use_archives) + if (use_archive) { - if (!reader->fileExists(archive_params.archive_name)) - throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name_for_logging); + if (!archive_reader->fileExists(".backup")) + throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Archive {} is not a backup", backup_name_for_logging); setCompressedSize(); - in = getArchiveReader("")->readFile(".backup"); + in = archive_reader->readFile(".backup"); } else { @@ -392,7 +411,7 @@ void BackupImpl::readBackupMetadata() if (child->nodeName() == "file") { const Poco::XML::Node * file_config = child; - FileInfo info; + BackupFileInfo info; info.file_name = getString(file_config, "name"); info.size = getUInt64(file_config, "size"); if (info.size) @@ -424,12 +443,12 @@ void BackupImpl::readBackupMetadata() if (info.size > info.base_size) { info.data_file_name = getString(file_config, "data_file", info.file_name); - info.archive_suffix = getString(file_config, "archive_suffix", ""); - info.pos_in_archive = getUInt64(file_config, "pos_in_archive", static_cast(-1)); } } - coordination->addFileInfo(info); + file_names.emplace(info.file_name, std::pair{info.size, info.checksum}); + if (info.size) + file_infos.try_emplace(std::pair{info.size, info.checksum}, info); ++num_files; total_size += info.size; @@ -444,14 +463,14 @@ void BackupImpl::readBackupMetadata() uncompressed_size = size_of_entries + str.size(); compressed_size = uncompressed_size; - if (!use_archives) + if (!use_archive) setCompressedSize(); } void BackupImpl::checkBackupDoesntExist() const { String file_name_to_check_existence; - if (use_archives) + if (use_archive) file_name_to_check_existence = archive_params.archive_name; else file_name_to_check_existence = ".backup"; @@ -512,69 +531,91 @@ void BackupImpl::removeLockFile() Strings BackupImpl::listFiles(const String & directory, bool recursive) const { + if (open_mode != OpenMode::READ) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for reading"); + + String prefix = removeLeadingSlash(directory); + if (!prefix.empty() && !prefix.ends_with('/')) + prefix += '/'; + String terminator = recursive ? "" : "/"; + Strings elements; + std::lock_guard lock{mutex}; - auto adjusted_dir = removeLeadingSlash(directory); - return coordination->listFiles(adjusted_dir, recursive); + for (auto it = file_names.lower_bound(prefix); it != file_names.end(); ++it) + { + const String & name = it->first; + if (!name.starts_with(prefix)) + break; + size_t start_pos = prefix.length(); + size_t end_pos = String::npos; + if (!terminator.empty()) + end_pos = name.find(terminator, start_pos); + std::string_view new_element = std::string_view{name}.substr(start_pos, end_pos - start_pos); + if (!elements.empty() && (elements.back() == new_element)) + continue; + elements.push_back(String{new_element}); + } + + return elements; } bool BackupImpl::hasFiles(const String & directory) const { + if (open_mode != OpenMode::READ) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for reading"); + + String prefix = removeLeadingSlash(directory); + if (!prefix.empty() && !prefix.ends_with('/')) + prefix += '/'; + std::lock_guard lock{mutex}; - auto adjusted_dir = removeLeadingSlash(directory); - return coordination->hasFiles(adjusted_dir); + auto it = file_names.lower_bound(prefix); + if (it == file_names.end()) + return false; + + const String & name = it->first; + return name.starts_with(prefix); } bool BackupImpl::fileExists(const String & file_name) const { - std::lock_guard lock{mutex}; + if (open_mode != OpenMode::READ) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for reading"); + auto adjusted_path = removeLeadingSlash(file_name); - return coordination->getFileInfo(adjusted_path).has_value(); + std::lock_guard lock{mutex}; + return file_names.contains(adjusted_path); } bool BackupImpl::fileExists(const SizeAndChecksum & size_and_checksum) const { + if (open_mode != OpenMode::READ) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for reading"); + std::lock_guard lock{mutex}; - return coordination->getFileInfo(size_and_checksum).has_value(); + return file_infos.contains(size_and_checksum); } UInt64 BackupImpl::getFileSize(const String & file_name) const { - std::lock_guard lock{mutex}; - auto adjusted_path = removeLeadingSlash(file_name); - auto info = coordination->getFileInfo(adjusted_path); - if (!info) - { - throw Exception( - ErrorCodes::BACKUP_ENTRY_NOT_FOUND, - "Backup {}: Entry {} not found in the backup", - backup_name_for_logging, - quoteString(file_name)); - } - return info->size; + return getFileSizeAndChecksum(file_name).first; } UInt128 BackupImpl::getFileChecksum(const String & file_name) const { - std::lock_guard lock{mutex}; - auto adjusted_path = removeLeadingSlash(file_name); - auto info = coordination->getFileInfo(adjusted_path); - if (!info) - { - throw Exception( - ErrorCodes::BACKUP_ENTRY_NOT_FOUND, - "Backup {}: Entry {} not found in the backup", - backup_name_for_logging, - quoteString(file_name)); - } - return info->checksum; + return getFileSizeAndChecksum(file_name).second; } SizeAndChecksum BackupImpl::getFileSizeAndChecksum(const String & file_name) const { - std::lock_guard lock{mutex}; + if (open_mode != OpenMode::READ) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for reading"); + auto adjusted_path = removeLeadingSlash(file_name); - auto info = coordination->getFileInfo(adjusted_path); - if (!info) + + std::lock_guard lock{mutex}; + auto it = file_names.find(adjusted_path); + if (it == file_names.end()) { throw Exception( ErrorCodes::BACKUP_ENTRY_NOT_FOUND, @@ -582,7 +623,8 @@ SizeAndChecksum BackupImpl::getFileSizeAndChecksum(const String & file_name) con backup_name_for_logging, quoteString(file_name)); } - return {info->size, info->checksum}; + + return it->second; } std::unique_ptr BackupImpl::readFile(const String & file_name) const @@ -603,37 +645,31 @@ std::unique_ptr BackupImpl::readFile(const SizeAndChecksum & return std::make_unique(static_cast(nullptr), 0); } - auto info_opt = coordination->getFileInfo(size_and_checksum); - if (!info_opt) + BackupFileInfo info; { - throw Exception( - ErrorCodes::BACKUP_ENTRY_NOT_FOUND, - "Backup {}: Entry {} not found in the backup", - backup_name_for_logging, - formatSizeAndChecksum(size_and_checksum)); + std::lock_guard lock{mutex}; + auto it = file_infos.find(size_and_checksum); + if (it == file_infos.end()) + { + throw Exception( + ErrorCodes::BACKUP_ENTRY_NOT_FOUND, + "Backup {}: Entry {} not found in the backup", + backup_name_for_logging, + formatSizeAndChecksum(size_and_checksum)); + } + info = it->second; } - const auto & info = *info_opt; - std::unique_ptr read_buffer; std::unique_ptr base_read_buffer; if (info.size > info.base_size) { /// Make `read_buffer` if there is data for this backup entry in this backup. - if (use_archives) - { - std::shared_ptr archive_reader; - { - std::lock_guard lock{mutex}; - archive_reader = getArchiveReader(info.archive_suffix); - } + if (use_archive) read_buffer = archive_reader->readFile(info.data_file_name); - } else - { read_buffer = reader->readFile(info.data_file_name); - } } if (info.base_size) @@ -709,21 +745,24 @@ size_t BackupImpl::copyFileToDisk(const SizeAndChecksum & size_and_checksum, Dis return 0; } - auto info_opt = coordination->getFileInfo(size_and_checksum); - if (!info_opt) + BackupFileInfo info; { - throw Exception( - ErrorCodes::BACKUP_ENTRY_NOT_FOUND, - "Backup {}: Entry {} not found in the backup", - backup_name_for_logging, - formatSizeAndChecksum(size_and_checksum)); + std::lock_guard lock{mutex}; + auto it = file_infos.find(size_and_checksum); + if (it == file_infos.end()) + { + throw Exception( + ErrorCodes::BACKUP_ENTRY_NOT_FOUND, + "Backup {}: Entry {} not found in the backup", + backup_name_for_logging, + formatSizeAndChecksum(size_and_checksum)); + } + info = it->second; } - const auto & info = *info_opt; - bool file_copied = false; - if (info.size && !info.base_size && !use_archives) + if (info.size && !info.base_size && !use_archive) { /// Data comes completely from this backup. reader->copyFileToDisk(info.data_file_name, info.size, destination_disk, destination_path, write_mode, write_settings); @@ -758,84 +797,7 @@ size_t BackupImpl::copyFileToDisk(const SizeAndChecksum & size_and_checksum, Dis } -namespace -{ - -std::optional getInfoAboutFileFromBaseBackupIfExists(std::shared_ptr base_backup, const std::string & file_path) -{ - if (base_backup && base_backup->fileExists(file_path)) - return std::pair{base_backup->getFileSize(file_path), base_backup->getFileChecksum(file_path)}; - - return std::nullopt; -} - -enum class CheckBackupResult -{ - HasPrefix, - HasFull, - HasNothing, -}; - -CheckBackupResult checkBaseBackupForFile(const SizeAndChecksum & base_backup_info, const FileInfo & new_entry_info) -{ - /// We cannot reuse base backup because our file is smaller - /// than file stored in previous backup - if (new_entry_info.size < base_backup_info.first) - return CheckBackupResult::HasNothing; - - if (base_backup_info.first == new_entry_info.size) - return CheckBackupResult::HasFull; - - return CheckBackupResult::HasPrefix; - -} - -struct ChecksumsForNewEntry -{ - UInt128 full_checksum; - UInt128 prefix_checksum; -}; - -/// Calculate checksum for backup entry if it's empty. -/// Also able to calculate additional checksum of some prefix. -ChecksumsForNewEntry calculateNewEntryChecksumsIfNeeded(BackupEntryPtr entry, size_t prefix_size) -{ - if (prefix_size > 0) - { - auto read_buffer = entry->getReadBuffer(); - HashingReadBuffer hashing_read_buffer(*read_buffer); - hashing_read_buffer.ignore(prefix_size); - auto prefix_checksum = hashing_read_buffer.getHash(); - if (entry->getChecksum() == std::nullopt) - { - hashing_read_buffer.ignoreAll(); - auto full_checksum = hashing_read_buffer.getHash(); - return ChecksumsForNewEntry{full_checksum, prefix_checksum}; - } - else - { - return ChecksumsForNewEntry{*(entry->getChecksum()), prefix_checksum}; - } - } - else - { - if (entry->getChecksum() == std::nullopt) - { - auto read_buffer = entry->getReadBuffer(); - HashingReadBuffer hashing_read_buffer(*read_buffer); - hashing_read_buffer.ignoreAll(); - return ChecksumsForNewEntry{hashing_read_buffer.getHash(), 0}; - } - else - { - return ChecksumsForNewEntry{*(entry->getChecksum()), 0}; - } - } -} - -} - -void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry) +void BackupImpl::writeFile(const BackupFileInfo & info, BackupEntryPtr entry) { if (open_mode != OpenMode::WRITE) throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for writing"); @@ -846,24 +808,6 @@ void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry) std::string from_file_name = "memory buffer"; if (auto fname = entry->getFilePath(); !fname.empty()) from_file_name = "file " + fname; - LOG_TRACE(log, "Writing backup for file {} from {}", file_name, from_file_name); - - auto adjusted_path = removeLeadingSlash(file_name); - - if (coordination->getFileInfo(adjusted_path)) - { - throw Exception( - ErrorCodes::BACKUP_ENTRY_ALREADY_EXISTS, "Backup {}: Entry {} already exists", - backup_name_for_logging, quoteString(file_name)); - } - - FileInfo info - { - .file_name = adjusted_path, - .size = entry->getSize(), - .base_size = 0, - .base_checksum = 0, - }; { std::lock_guard lock{mutex}; @@ -871,123 +815,29 @@ void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry) total_size += info.size; } - /// Empty file, nothing to backup - if (info.size == 0 && deduplicate_files) + if (info.data_file_name.empty()) { - coordination->addFileInfo(info); + LOG_TRACE(log, "Writing backup for file {} from {}: skipped, {}", info.data_file_name, from_file_name, !info.size ? "empty" : "base backup has it"); return; } - std::optional base_backup_file_info = getInfoAboutFileFromBaseBackupIfExists(base_backup, adjusted_path); - - /// We have info about this file in base backup - /// If file has no checksum -- calculate and fill it. - if (base_backup_file_info.has_value()) + if (!coordination->startWritingFile(info.data_file_index)) { - LOG_TRACE(log, "File {} found in base backup, checking for equality", adjusted_path); - CheckBackupResult check_base = checkBaseBackupForFile(*base_backup_file_info, info); - - /// File with the same name but smaller size exist in previous backup - if (check_base == CheckBackupResult::HasPrefix) - { - auto checksums = calculateNewEntryChecksumsIfNeeded(entry, base_backup_file_info->first); - info.checksum = checksums.full_checksum; - - /// We have prefix of this file in backup with the same checksum. - /// In ClickHouse this can happen for StorageLog for example. - if (checksums.prefix_checksum == base_backup_file_info->second) - { - LOG_TRACE(log, "File prefix of {} in base backup, will write rest part of file to current backup", adjusted_path); - info.base_size = base_backup_file_info->first; - info.base_checksum = base_backup_file_info->second; - } - else - { - LOG_TRACE(log, "Prefix checksum of file {} doesn't match with checksum in base backup", adjusted_path); - } - } - else - { - /// We have full file or have nothing, first of all let's get checksum - /// of current file - auto checksums = calculateNewEntryChecksumsIfNeeded(entry, 0); - info.checksum = checksums.full_checksum; - - if (info.checksum == base_backup_file_info->second) - { - LOG_TRACE(log, "Found whole file {} in base backup", adjusted_path); - assert(check_base == CheckBackupResult::HasFull); - assert(info.size == base_backup_file_info->first); - - info.base_size = base_backup_file_info->first; - info.base_checksum = base_backup_file_info->second; - /// Actually we can add this info to coordination and exist, - /// but we intentionally don't do it, otherwise control flow - /// of this function will be very complex. - } - else - { - LOG_TRACE(log, "Whole file {} in base backup doesn't match by checksum", adjusted_path); - } - } - } - else /// We don't have info about this file_name (sic!) in base backup, - /// however file could be renamed, so we will check one more time using size and checksum - { - - LOG_TRACE(log, "Nothing found for file {} in base backup", adjusted_path); - auto checksums = calculateNewEntryChecksumsIfNeeded(entry, 0); - info.checksum = checksums.full_checksum; - } - - /// Maybe we have a copy of this file in the backup already. - if (coordination->getFileInfo(std::pair{info.size, info.checksum}) && deduplicate_files) - { - LOG_TRACE(log, "File {} already exist in current backup, adding reference", adjusted_path); - coordination->addFileInfo(info); + LOG_TRACE(log, "Writing backup for file {} from {}: skipped, data file #{} is already being written", info.data_file_name, from_file_name, info.data_file_index); return; } - /// On the previous lines we checked that backup for file with adjusted_name exist in previous backup. - /// However file can be renamed, but has the same size and checksums, let's check for this case. - if (base_backup && base_backup->fileExists(std::pair{info.size, info.checksum})) - { + LOG_TRACE(log, "Writing backup for file {} from {}: data file #{}", info.data_file_name, from_file_name, info.data_file_index); - LOG_TRACE(log, "File {} doesn't exist in current backup, but we have file with same size and checksum", adjusted_path); - info.base_size = info.size; - info.base_checksum = info.checksum; - - coordination->addFileInfo(info); - return; - } - - /// All "short paths" failed. We don't have this file in previous or existing backup - /// or have only prefix of it in previous backup. Let's go long path. - - info.data_file_name = info.file_name; - - if (use_archives) - { - std::lock_guard lock{mutex}; - info.archive_suffix = current_archive_suffix; - } - - bool is_data_file_required; - coordination->addFileInfo(info, is_data_file_required); - if (!is_data_file_required && deduplicate_files) - { - LOG_TRACE(log, "File {} doesn't exist in current backup, but we have file with same size and checksum", adjusted_path); - return; /// We copy data only if it's a new combination of size & checksum. - } auto writer_description = writer->getDataSourceDescription(); auto reader_description = entry->getDataSourceDescription(); /// We need to copy whole file without archive, we can do it faster /// if source and destination are compatible - if (!use_archives && writer->supportNativeCopy(reader_description)) + if (!use_archive && writer->supportNativeCopy(reader_description)) { /// Should be much faster than writing data through server. - LOG_TRACE(log, "Will copy file {} using native copy", adjusted_path); + LOG_TRACE(log, "Will copy file {} using native copy", info.data_file_name); /// NOTE: `mutex` must be unlocked here otherwise writing will be in one thread maximum and hence slow. @@ -995,8 +845,6 @@ void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry) } else { - LOG_TRACE(log, "Will copy file {}", adjusted_path); - bool has_entries = false; { std::lock_guard lock{mutex}; @@ -1005,30 +853,10 @@ void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry) if (!has_entries) checkLockFile(true); - if (use_archives) + if (use_archive) { - LOG_TRACE(log, "Adding file {} to archive", adjusted_path); - - /// An archive must be written strictly in one thread, so it's correct to lock the mutex for all the time we're writing the file - /// to the archive. - std::lock_guard lock{mutex}; - - String archive_suffix = current_archive_suffix; - bool next_suffix = false; - if (current_archive_suffix.empty() && is_internal_backup) - next_suffix = true; - /*if (archive_params.max_volume_size && current_archive_writer - && (current_archive_writer->getTotalSize() + size - base_size > archive_params.max_volume_size)) - next_suffix = true;*/ - if (next_suffix) - current_archive_suffix = coordination->getNextArchiveSuffix(); - - if (info.archive_suffix != current_archive_suffix) - { - info.archive_suffix = current_archive_suffix; - coordination->updateFileInfo(info); - } - auto out = getArchiveWriter(current_archive_suffix)->writeFile(info.data_file_name); + LOG_TRACE(log, "Adding file {} to archive", info.data_file_name); + auto out = archive_writer->writeFile(info.data_file_name); auto read_buffer = entry->getReadBuffer(); if (info.base_size != 0) read_buffer->seek(info.base_size, SEEK_SET); @@ -1037,6 +865,7 @@ void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry) } else { + LOG_TRACE(log, "Will copy file {}", info.data_file_name); auto create_read_buffer = [entry] { return entry->getReadBuffer(); }; /// NOTE: `mutex` must be unlocked here otherwise writing will be in one thread maximum and hence slow. @@ -1062,14 +891,11 @@ void BackupImpl::finalizeWriting() if (writing_finalized) throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is already finalized"); - if (!coordination->hasFiles("")) - throw Exception(ErrorCodes::BACKUP_IS_EMPTY, "Backup must not be empty"); - if (!is_internal_backup) { LOG_TRACE(log, "Finalizing backup {}", backup_name_for_logging); writeBackupMetadata(); - closeArchives(); + closeArchive(); setCompressedSize(); removeLockFile(); LOG_TRACE(log, "Finalized backup {}", backup_name_for_logging); @@ -1081,51 +907,13 @@ void BackupImpl::finalizeWriting() void BackupImpl::setCompressedSize() { - if (use_archives) + if (use_archive) compressed_size = writer ? writer->getFileSize(archive_params.archive_name) : reader->getFileSize(archive_params.archive_name); else compressed_size = uncompressed_size; } -String BackupImpl::getArchiveNameWithSuffix(const String & suffix) const -{ - return archive_params.archive_name + (suffix.empty() ? "" : ".") + suffix; -} - -std::shared_ptr BackupImpl::getArchiveReader(const String & suffix) const -{ - auto it = archive_readers.find(suffix); - if (it != archive_readers.end()) - return it->second; - String archive_name_with_suffix = getArchiveNameWithSuffix(suffix); - size_t archive_size = reader->getFileSize(archive_name_with_suffix); - auto new_archive_reader = createArchiveReader(archive_params.archive_name, [reader=reader, archive_name_with_suffix]{ return reader->readFile(archive_name_with_suffix); }, - archive_size); - new_archive_reader->setPassword(archive_params.password); - archive_readers.emplace(suffix, new_archive_reader); - return new_archive_reader; -} - -std::shared_ptr BackupImpl::getArchiveWriter(const String & suffix) -{ - for (const auto & archive_writer : archive_writers) - { - if ((suffix == archive_writer.first) && archive_writer.second) - return archive_writer.second; - } - - String archive_name_with_suffix = getArchiveNameWithSuffix(suffix); - auto new_archive_writer = createArchiveWriter(archive_params.archive_name, writer->writeFile(archive_name_with_suffix)); - new_archive_writer->setPassword(archive_params.password); - new_archive_writer->setCompression(archive_params.compression_method, archive_params.compression_level); - size_t pos = suffix.empty() ? 0 : 1; - archive_writers[pos] = {suffix, new_archive_writer}; - - return new_archive_writer; -} - - void BackupImpl::removeAllFilesAfterFailure() { if (is_internal_backup) @@ -1136,19 +924,14 @@ void BackupImpl::removeAllFilesAfterFailure() LOG_INFO(log, "Removing all files of backup {} after failure", backup_name_for_logging); Strings files_to_remove; - if (use_archives) + if (use_archive) { files_to_remove.push_back(archive_params.archive_name); - for (const auto & suffix : coordination->getAllArchiveSuffixes()) - { - String archive_name_with_suffix = getArchiveNameWithSuffix(suffix); - files_to_remove.push_back(std::move(archive_name_with_suffix)); - } } else { files_to_remove.push_back(".backup"); - for (const auto & file_info : coordination->getAllFileInfos()) + for (const auto & file_info : coordination->getFileInfosForAllHosts()) files_to_remove.push_back(file_info.data_file_name); } diff --git a/src/Backups/BackupImpl.h b/src/Backups/BackupImpl.h index c33ca7c94ad..bf94926c46c 100644 --- a/src/Backups/BackupImpl.h +++ b/src/Backups/BackupImpl.h @@ -3,8 +3,8 @@ #include #include #include +#include #include -#include namespace DB @@ -58,6 +58,7 @@ public: OpenMode getOpenMode() const override { return open_mode; } time_t getTimestamp() const override { return timestamp; } UUID getUUID() const override { return *uuid; } + BackupPtr getBaseBackup() const override { return base_backup; } size_t getNumFiles() const override; UInt64 getTotalSize() const override; size_t getNumEntries() const override; @@ -79,21 +80,20 @@ public: WriteMode write_mode, const WriteSettings & write_settings) const override; size_t copyFileToDisk(const SizeAndChecksum & size_and_checksum, DiskPtr destination_disk, const String & destination_path, WriteMode write_mode, const WriteSettings & write_settings) const override; - void writeFile(const String & file_name, BackupEntryPtr entry) override; + void writeFile(const BackupFileInfo & info, BackupEntryPtr entry) override; void finalizeWriting() override; - bool supportsWritingInMultipleThreads() const override { return !use_archives; } + bool supportsWritingInMultipleThreads() const override { return !use_archive; } private: - using FileInfo = IBackupCoordination::FileInfo; - class BackupEntryFromBackupImpl; - void open(const ContextPtr & context); void close(); - void closeArchives(); + + void openArchive(); + void closeArchive(); /// Writes the file ".backup" containing backup's metadata. - void writeBackupMetadata(); - void readBackupMetadata(); + void writeBackupMetadata() TSA_REQUIRES(mutex); + void readBackupMetadata() TSA_REQUIRES(mutex); /// Checks that a new backup doesn't exist yet. void checkBackupDoesntExist() const; @@ -106,16 +106,12 @@ private: void removeAllFilesAfterFailure(); - String getArchiveNameWithSuffix(const String & suffix) const; - std::shared_ptr getArchiveReader(const String & suffix) const; - std::shared_ptr getArchiveWriter(const String & suffix); - /// Calculates and sets `compressed_size`. void setCompressedSize(); const String backup_name_for_logging; + const bool use_archive; const ArchiveParams archive_params; - const bool use_archives; const OpenMode open_mode; std::shared_ptr writer; std::shared_ptr reader; @@ -123,6 +119,11 @@ private: std::shared_ptr coordination; mutable std::mutex mutex; + + using SizeAndChecksum = std::pair; + std::map file_names TSA_GUARDED_BY(mutex); /// Should be ordered alphabetically, see listFiles(). For empty files we assume checksum = 0. + std::map file_infos TSA_GUARDED_BY(mutex); /// Information about files. Without empty files. + std::optional uuid; time_t timestamp = 0; size_t num_files = 0; @@ -137,10 +138,10 @@ private: std::optional base_backup_info; std::shared_ptr base_backup; std::optional base_backup_uuid; - mutable std::unordered_map> archive_readers; - std::pair> archive_writers[2]; - String current_archive_suffix; + std::shared_ptr archive_reader; + std::shared_ptr archive_writer; String lock_file_name; + bool writing_finalized = false; bool deduplicate_files = true; const Poco::Logger * log; diff --git a/src/Backups/BackupsWorker.cpp b/src/Backups/BackupsWorker.cpp index b6cf74efe22..b82629eadf7 100644 --- a/src/Backups/BackupsWorker.cpp +++ b/src/Backups/BackupsWorker.cpp @@ -52,7 +52,7 @@ namespace .keeper_max_retries = context->getSettingsRef().backup_keeper_max_retries, .keeper_retry_initial_backoff_ms = context->getSettingsRef().backup_keeper_retry_initial_backoff_ms, .keeper_retry_max_backoff_ms = context->getSettingsRef().backup_keeper_retry_max_backoff_ms, - .batch_size_for_keeper_multiread = context->getSettingsRef().backup_batch_size_for_keeper_multiread, + .keeper_value_max_size = context->getSettingsRef().backup_keeper_value_max_size, }; auto all_hosts = BackupSettings::Util::filterHostIDs( @@ -65,11 +65,12 @@ namespace toString(*backup_settings.backup_uuid), all_hosts, backup_settings.host_id, + !backup_settings.deduplicate_files, backup_settings.internal); } else { - return std::make_shared(); + return std::make_shared(!backup_settings.deduplicate_files); } } @@ -350,7 +351,8 @@ void BackupsWorker::doBackup( } /// Write the backup entries to the backup. - writeBackupEntries(backup_id, backup, std::move(backup_entries), backups_thread_pool, backup_settings.internal); + buildFileInfosForBackupEntries(backup, backup_entries, backup_coordination); + writeBackupEntries(backup, std::move(backup_entries), backup_id, backup_coordination, backup_settings.internal); /// We have written our backup entries, we need to tell other hosts (they could be waiting for it). backup_coordination->setStage(Stage::COMPLETED, ""); @@ -399,8 +401,31 @@ void BackupsWorker::doBackup( } -void BackupsWorker::writeBackupEntries(const OperationID & backup_id, BackupMutablePtr backup, BackupEntries && backup_entries, ThreadPool & thread_pool, bool internal) +void BackupsWorker::buildFileInfosForBackupEntries(const BackupPtr & backup, const BackupEntries & backup_entries, std::shared_ptr backup_coordination) { + LOG_TRACE(log, "{}", Stage::BUILDING_FILE_INFOS); + backup_coordination->setStage(Stage::BUILDING_FILE_INFOS, ""); + backup_coordination->waitForStage(Stage::BUILDING_FILE_INFOS); + backup_coordination->addFileInfos(::DB::buildFileInfosForBackupEntries(backup_entries, backup->getBaseBackup(), backups_thread_pool)); +} + + +void BackupsWorker::writeBackupEntries(BackupMutablePtr backup, BackupEntries && backup_entries, const OperationID & backup_id, std::shared_ptr backup_coordination, bool internal) +{ + LOG_TRACE(log, "{}, num backup entries={}", Stage::WRITING_BACKUP, backup_entries.size()); + backup_coordination->setStage(Stage::WRITING_BACKUP, ""); + backup_coordination->waitForStage(Stage::WRITING_BACKUP); + + auto file_infos = backup_coordination->getFileInfos(); + if (file_infos.size() != backup_entries.size()) + { + throw Exception( + ErrorCodes::LOGICAL_ERROR, + "Number of file infos ({}) doesn't match the number of backup entries ({})", + file_infos.size(), + backup_entries.size()); + } + size_t num_active_jobs = 0; std::mutex mutex; std::condition_variable event; @@ -409,10 +434,10 @@ void BackupsWorker::writeBackupEntries(const OperationID & backup_id, BackupMuta bool always_single_threaded = !backup->supportsWritingInMultipleThreads(); auto thread_group = CurrentThread::getGroup(); - for (auto & name_and_entry : backup_entries) + for (size_t i = 0; i != backup_entries.size(); ++i) { - auto & name = name_and_entry.first; - auto & entry = name_and_entry.second; + auto & entry = backup_entries[i].second; + const auto & file_info = file_infos[i]; { std::unique_lock lock{mutex}; @@ -445,7 +470,7 @@ void BackupsWorker::writeBackupEntries(const OperationID & backup_id, BackupMuta return; } - backup->writeFile(name, std::move(entry)); + backup->writeFile(file_info, std::move(entry)); // Update metadata if (!internal) { @@ -468,7 +493,7 @@ void BackupsWorker::writeBackupEntries(const OperationID & backup_id, BackupMuta } }; - if (always_single_threaded || !thread_pool.trySchedule([job] { job(true); })) + if (always_single_threaded || !backups_thread_pool.trySchedule([job] { job(true); })) job(false); } diff --git a/src/Backups/BackupsWorker.h b/src/Backups/BackupsWorker.h index c36b58da14f..d319daf42bd 100644 --- a/src/Backups/BackupsWorker.h +++ b/src/Backups/BackupsWorker.h @@ -105,8 +105,11 @@ private: ContextMutablePtr mutable_context, bool called_async); + /// Builds file infos for specified backup entries. + void buildFileInfosForBackupEntries(const BackupPtr & backup, const BackupEntries & backup_entries, std::shared_ptr backup_coordination); + /// Write backup entries to an opened backup. - void writeBackupEntries(const OperationID & backup_id, BackupMutablePtr backup, BackupEntries && backup_entries, ThreadPool & thread_pool, bool internal); + void writeBackupEntries(BackupMutablePtr backup, BackupEntries && backup_entries, const OperationID & backup_id, std::shared_ptr backup_coordination, bool internal); OperationID startRestoring(const ASTPtr & query, ContextMutablePtr context); diff --git a/src/Backups/IBackup.h b/src/Backups/IBackup.h index 03fab6a25d6..031130fa3b4 100644 --- a/src/Backups/IBackup.h +++ b/src/Backups/IBackup.h @@ -10,8 +10,9 @@ namespace DB { class IBackupEntry; -class IDisk; using BackupEntryPtr = std::shared_ptr; +struct BackupFileInfo; +class IDisk; using DiskPtr = std::shared_ptr; class SeekableReadBuffer; @@ -42,6 +43,9 @@ public: /// Returns UUID of the backup. virtual UUID getUUID() const = 0; + /// Returns the base backup (can be null). + virtual std::shared_ptr getBaseBackup() const = 0; + /// Returns the number of files stored in the backup. Compare with getNumEntries(). virtual size_t getNumFiles() const = 0; @@ -111,7 +115,7 @@ public: WriteMode write_mode = WriteMode::Rewrite, const WriteSettings & write_settings = {}) const = 0; /// Puts a new entry to the backup. - virtual void writeFile(const String & file_name, BackupEntryPtr entry) = 0; + virtual void writeFile(const BackupFileInfo & file_info, BackupEntryPtr entry) = 0; /// Finalizes writing the backup, should be called after all entries have been successfully written. virtual void finalizeWriting() = 0; diff --git a/src/Backups/IBackupCoordination.h b/src/Backups/IBackupCoordination.h index 26f101f29e5..75d9202374b 100644 --- a/src/Backups/IBackupCoordination.h +++ b/src/Backups/IBackupCoordination.h @@ -1,14 +1,13 @@ #pragma once -#include -#include -#include #include namespace DB { class Exception; +struct BackupFileInfo; +using BackupFileInfos = std::vector; enum class AccessEntityType; enum class UserDefinedSQLObjectType; @@ -73,70 +72,14 @@ public: virtual void addReplicatedSQLObjectsDir(const String & loader_zk_path, UserDefinedSQLObjectType object_type, const String & dir_path) = 0; virtual Strings getReplicatedSQLObjectsDirs(const String & loader_zk_path, UserDefinedSQLObjectType object_type) const = 0; - struct FileInfo - { - String file_name; - - UInt64 size = 0; - UInt128 checksum{0}; - - /// for incremental backups - UInt64 base_size = 0; - UInt128 base_checksum{0}; - - /// Name of the data file. - String data_file_name; - - /// Suffix of an archive if the backup is stored as a series of archives. - String archive_suffix; - - /// Position in the archive. - UInt64 pos_in_archive = static_cast(-1); - - /// Note: this format doesn't allow to parse data back - /// It is useful only for debugging purposes - [[ maybe_unused ]] String describe() - { - String result; - result += fmt::format("file_name: {};\n", file_name); - result += fmt::format("size: {};\n", size); - result += fmt::format("checksum: {};\n", getHexUIntLowercase(checksum)); - result += fmt::format("base_size: {};\n", base_size); - result += fmt::format("base_checksum: {};\n", getHexUIntLowercase(checksum)); - result += fmt::format("data_file_name: {};\n", data_file_name); - result += fmt::format("archive_suffix: {};\n", archive_suffix); - result += fmt::format("pos_in_archive: {};\n", pos_in_archive); - return result; - } - }; - /// Adds file information. /// If specified checksum+size are new for this IBackupContentsInfo the function sets `is_data_file_required`. - virtual void addFileInfo(const FileInfo & file_info, bool & is_data_file_required) = 0; + virtual void addFileInfos(BackupFileInfos && file_infos) = 0; + virtual BackupFileInfos getFileInfos() const = 0; + virtual BackupFileInfos getFileInfosForAllHosts() const = 0; - void addFileInfo(const FileInfo & file_info) - { - bool is_data_file_required; - addFileInfo(file_info, is_data_file_required); - } - - /// Updates some fields (currently only `archive_suffix`) of a stored file's information. - virtual void updateFileInfo(const FileInfo & file_info) = 0; - - virtual std::vector getAllFileInfos() const = 0; - virtual Strings listFiles(const String & directory, bool recursive) const = 0; - virtual bool hasFiles(const String & directory) const = 0; - - using SizeAndChecksum = std::pair; - - virtual std::optional getFileInfo(const String & file_name) const = 0; - virtual std::optional getFileInfo(const SizeAndChecksum & size_and_checksum) const = 0; - - /// Generates a new archive suffix, e.g. "001", "002", "003", ... - virtual String getNextArchiveSuffix() = 0; - - /// Returns the list of all the archive suffixes which were generated. - virtual Strings getAllArchiveSuffixes() const = 0; + /// Starts writing a specified file, the function returns false if that file is already being written concurrently. + virtual bool startWritingFile(size_t data_file_index) = 0; /// This function is used to check if concurrent backups are running /// other than the backup passed to the function diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 874a6050743..27596cf1fc8 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -418,6 +418,7 @@ class IColumn; M(UInt64, backup_keeper_max_retries, 20, "Max retries for keeper operations during backup", 0) \ M(UInt64, backup_keeper_retry_initial_backoff_ms, 100, "Initial backoff timeout for [Zoo]Keeper operations during backup", 0) \ M(UInt64, backup_keeper_retry_max_backoff_ms, 5000, "Max backoff timeout for [Zoo]Keeper operations during backup", 0) \ + M(UInt64, backup_keeper_value_max_size, 1048576, "Maximum size of data of a [Zoo]Keeper's node during backup", 0) \ M(UInt64, backup_batch_size_for_keeper_multiread, 10000, "Maximum size of batch for multiread request to [Zoo]Keeper during backup", 0) \ \ M(Bool, log_profile_events, true, "Log query performance statistics into the query_log, query_thread_log and query_views_log.", 0) \ diff --git a/tests/integration/test_backup_restore_on_cluster/test.py b/tests/integration/test_backup_restore_on_cluster/test.py index 2a3d34c559c..b95d49a2e7c 100644 --- a/tests/integration/test_backup_restore_on_cluster/test.py +++ b/tests/integration/test_backup_restore_on_cluster/test.py @@ -429,6 +429,39 @@ def test_replicated_database_async(): assert node2.query("SELECT * FROM mydb.tbl2 ORDER BY y") == TSV(["a", "bb"]) +# By default `backup_keeper_value_max_size` is 1 MB, but in this test we'll set it to 50 bytes just to check it works. +def test_keeper_value_max_size(): + node1.query( + "CREATE TABLE tbl ON CLUSTER 'cluster' (" + "x UInt32" + ") ENGINE=ReplicatedMergeTree('/clickhouse/tables/tbl/', '{replica}')" + "ORDER BY x" + ) + + node1.query("INSERT INTO tbl VALUES (111)") + node2.query("INSERT INTO tbl VALUES (222)") + + node1.query("SYSTEM SYNC REPLICA ON CLUSTER 'cluster' tbl") + node1.query("SYSTEM STOP REPLICATED SENDS ON CLUSTER 'cluster' tbl") + + node1.query("INSERT INTO tbl VALUES (333)") + node2.query("INSERT INTO tbl VALUES (444)") + + backup_name = new_backup_name() + node1.query( + f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name}", + settings={"backup_keeper_value_max_size": 50}, + ) + + node1.query(f"DROP TABLE tbl ON CLUSTER 'cluster' NO DELAY") + + node1.query(f"RESTORE TABLE tbl ON CLUSTER 'cluster' FROM {backup_name}") + node1.query("SYSTEM SYNC REPLICA ON CLUSTER 'cluster' tbl") + + assert node1.query("SELECT * FROM tbl ORDER BY x") == TSV([111, 222, 333, 444]) + assert node2.query("SELECT * FROM tbl ORDER BY x") == TSV([111, 222, 333, 444]) + + @pytest.mark.parametrize( "interface, on_cluster", [("native", True), ("http", True), ("http", False)] ) diff --git a/tests/integration/test_backup_restore_on_cluster/test_disallow_concurrency.py b/tests/integration/test_backup_restore_on_cluster/test_disallow_concurrency.py index 9dcb036cdf9..b04eaed4bb8 100644 --- a/tests/integration/test_backup_restore_on_cluster/test_disallow_concurrency.py +++ b/tests/integration/test_backup_restore_on_cluster/test_disallow_concurrency.py @@ -204,18 +204,36 @@ def test_concurrent_restores_on_same_node(): "distributed_ddl_task_timeout": 360, }, ) + restore_id = ( nodes[0] .query(f"RESTORE TABLE tbl ON CLUSTER 'cluster' FROM {backup_name} ASYNC") .split("\t")[0] ) + + status = ( + nodes[0] + .query(f"SELECT status FROM system.backups WHERE id == '{restore_id}'") + .rstrip("\n") + ) + assert status in ["RESTORING", "RESTORED"] + + concurrent_error = nodes[0].query_and_get_error( + f"RESTORE TABLE tbl ON CLUSTER 'cluster' FROM {backup_name}" + ) + + expected_errors = [ + "Concurrent restores not supported", + "Cannot restore the table default.tbl because it already contains some data", + ] + assert any( + [expected_error in concurrent_error for expected_error in expected_errors] + ) + assert_eq_with_retry( nodes[0], - f"SELECT status FROM system.backups WHERE status == 'RESTORING' AND id == '{restore_id}'", - "RESTORING", - ) - assert "Concurrent restores not supported" in nodes[0].query_and_get_error( - f"RESTORE TABLE tbl ON CLUSTER 'cluster' FROM {backup_name}" + f"SELECT status FROM system.backups WHERE id == '{restore_id}'", + "RESTORED", ) @@ -247,22 +265,34 @@ def test_concurrent_restores_on_different_node(): "distributed_ddl_task_timeout": 360, }, ) + restore_id = ( nodes[0] .query(f"RESTORE TABLE tbl ON CLUSTER 'cluster' FROM {backup_name} ASYNC") .split("\t")[0] ) - assert_eq_with_retry( - nodes[0], - f"SELECT status FROM system.backups WHERE status == 'RESTORING'", - "RESTORING", + + status = ( + nodes[0] + .query(f"SELECT status FROM system.backups WHERE id == '{restore_id}'") + .rstrip("\n") ) - assert "Concurrent restores not supported" in nodes[1].query_and_get_error( + assert status in ["RESTORING", "RESTORED"] + + concurrent_error = nodes[1].query_and_get_error( f"RESTORE TABLE tbl ON CLUSTER 'cluster' FROM {backup_name}" ) + expected_errors = [ + "Concurrent restores not supported", + "Cannot restore the table default.tbl because it already contains some data", + ] + assert any( + [expected_error in concurrent_error for expected_error in expected_errors] + ) + assert_eq_with_retry( nodes[0], - f"SELECT status FROM system.backups WHERE status == 'RESTORED' AND id == '{restore_id}'", + f"SELECT status FROM system.backups WHERE id == '{restore_id}'", "RESTORED", ) From d8260405badcc3bd04fd3534198746cf3968b0f1 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Wed, 29 Mar 2023 08:23:52 -0600 Subject: [PATCH 186/377] Update amazon-reviews.md --- .../example-datasets/amazon-reviews.md | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md index ec642525cc9..7935cb6dba0 100644 --- a/docs/en/getting-started/example-datasets/amazon-reviews.md +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -92,7 +92,7 @@ ORDER BY (marketplace, review_date, product_category); ```sql INSERT INTO amazon_reviews SELECT - * REPLACE(vine = 'Y' AS vine, verified_purchase = 'Y' AS verified_purchase) + * FROM s3Cluster( 'default', 'https://s3.amazonaws.com/amazon-reviews-pds/tsv/amazon_reviews_us_*.tsv.gz', @@ -218,7 +218,7 @@ LIMIT 10; ```sql SELECT - toYYYYMM(review_date) AS month, + toStartOfMonth(review_date) AS month, any(product_title), avg(star_rating) AS avg_stars FROM amazon_reviews @@ -234,30 +234,30 @@ LIMIT 20; It calculates all the monthly averages for each product, but we only returned 20 rows: ```response -┌──month─┬─any(product_title)──────────────────────────────────────────────────────────────────────┬─avg_stars─┐ -│ 201508 │ Mystiqueshapes Girls Ballet Tutu Neon Lime Green │ 4 │ -│ 201508 │ Adult Ballet Tutu Yellow │ 5 │ -│ 201508 │ The Way Things Work: An Illustrated Encyclopedia of Technology │ 5 │ -│ 201508 │ Hilda Boswell's Treasury of Poetry │ 5 │ -│ 201508 │ Treasury of Poetry │ 5 │ -│ 201508 │ Uncle Remus Stories │ 5 │ -│ 201508 │ The Book of Daniel │ 5 │ -│ 201508 │ Berenstains' B Book │ 5 │ -│ 201508 │ The High Hills (Brambly Hedge) │ 4.5 │ -│ 201508 │ Fuzzypeg Goes to School (The Little Grey Rabbit library) │ 5 │ -│ 201508 │ Dictionary in French: The Cat in the Hat (Beginner Series) │ 5 │ -│ 201508 │ Windfallen │ 5 │ -│ 201508 │ The Monk Who Sold His Ferrari: A Remarkable Story About Living Your Dreams │ 5 │ -│ 201508 │ Illustrissimi: The Letters of Pope John Paul I │ 5 │ -│ 201508 │ Social Contract: A Personal Inquiry into the Evolutionary Sources of Order and Disorder │ 5 │ -│ 201508 │ Mexico The Beautiful Cookbook: Authentic Recipes from the Regions of Mexico │ 4.5 │ -│ 201508 │ Alanbrooke │ 5 │ -│ 201508 │ Back to Cape Horn │ 4 │ -│ 201508 │ Ovett: An Autobiography (Willow books) │ 5 │ -│ 201508 │ The Birds of West Africa (Collins Field Guides) │ 4 │ -└────────┴─────────────────────────────────────────────────────────────────────────────────────────┴───────────┘ +┌──────month─┬─any(product_title)──────────────────────────────────────────────────────────────────────┬─avg_stars─┐ +│ 2015-08-01 │ Mystiqueshapes Girls Ballet Tutu Neon Lime Green │ 4 │ +│ 2015-08-01 │ Adult Ballet Tutu Yellow │ 5 │ +│ 2015-08-01 │ The Way Things Work: An Illustrated Encyclopedia of Technology │ 5 │ +│ 2015-08-01 │ Hilda Boswell's Treasury of Poetry │ 5 │ +│ 2015-08-01 │ Treasury of Poetry │ 5 │ +│ 2015-08-01 │ Uncle Remus Stories │ 5 │ +│ 2015-08-01 │ The Book of Daniel │ 5 │ +│ 2015-08-01 │ Berenstains' B Book │ 5 │ +│ 2015-08-01 │ The High Hills (Brambly Hedge) │ 4.5 │ +│ 2015-08-01 │ Fuzzypeg Goes to School (The Little Grey Rabbit library) │ 5 │ +│ 2015-08-01 │ Dictionary in French: The Cat in the Hat (Beginner Series) │ 5 │ +│ 2015-08-01 │ Windfallen │ 5 │ +│ 2015-08-01 │ The Monk Who Sold His Ferrari: A Remarkable Story About Living Your Dreams │ 5 │ +│ 2015-08-01 │ Illustrissimi: The Letters of Pope John Paul I │ 5 │ +│ 2015-08-01 │ Social Contract: A Personal Inquiry into the Evolutionary Sources of Order and Disorder │ 5 │ +│ 2015-08-01 │ Mexico The Beautiful Cookbook: Authentic Recipes from the Regions of Mexico │ 4.5 │ +│ 2015-08-01 │ Alanbrooke │ 5 │ +│ 2015-08-01 │ Back to Cape Horn │ 4 │ +│ 2015-08-01 │ Ovett: An Autobiography (Willow books) │ 5 │ +│ 2015-08-01 │ The Birds of West Africa (Collins Field Guides) │ 4 │ +└────────────┴─────────────────────────────────────────────────────────────────────────────────────────┴───────────┘ -20 rows in set. Elapsed: 55.529 sec. Processed 252.02 million rows, 35.58 GB (4.54 million rows/s., 640.79 MB/s.) +20 rows in set. Elapsed: 52.827 sec. Processed 251.46 million rows, 35.26 GB (4.76 million rows/s., 667.55 MB/s.) ``` 10. Here are the total number of votes per product category. This query is fast because `product_category` is in the primary key: From 87d235e842b032f7edf6d2d15472d9e4d12f3620 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Wed, 29 Mar 2023 22:28:36 +0800 Subject: [PATCH 187/377] Some improvements about names of span logs (#47667) --- src/Common/OpenTelemetryTraceContext.cpp | 12 +++++++++++- src/Common/OpenTelemetryTraceContext.h | 2 ++ src/Common/ThreadPool.cpp | 15 ++++++++++++--- .../Executors/ExecutionThreadContext.cpp | 2 +- src/Processors/Executors/PipelineExecutor.cpp | 7 +++++++ src/Server/HTTPHandler.cpp | 8 +++----- 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Common/OpenTelemetryTraceContext.cpp b/src/Common/OpenTelemetryTraceContext.cpp index 014034c6ba8..0d89c581318 100644 --- a/src/Common/OpenTelemetryTraceContext.cpp +++ b/src/Common/OpenTelemetryTraceContext.cpp @@ -68,7 +68,8 @@ bool Span::addAttribute(const Exception & e) noexcept if (!this->isTraceEnabled()) return false; - return addAttributeImpl("clickhouse.exception", getExceptionMessage(e, false)); + return addAttributeImpl("clickhouse.exception", getExceptionMessage(e, false)) + && addAttributeImpl("clickhouse.exception_code", toString(e.code())); } bool Span::addAttribute(std::exception_ptr e) noexcept @@ -79,6 +80,15 @@ bool Span::addAttribute(std::exception_ptr e) noexcept return addAttributeImpl("clickhouse.exception", getExceptionMessage(e, false)); } +bool Span::addAttribute(const ExecutionStatus & e) noexcept +{ + if (!this->isTraceEnabled()) + return false; + + return addAttributeImpl("clickhouse.exception", e.message) + && addAttributeImpl("clickhouse.exception_code", toString(e.code)); +} + bool Span::addAttributeImpl(std::string_view name, std::string_view value) noexcept { try diff --git a/src/Common/OpenTelemetryTraceContext.h b/src/Common/OpenTelemetryTraceContext.h index bfd091e2902..ab30e3ff07d 100644 --- a/src/Common/OpenTelemetryTraceContext.h +++ b/src/Common/OpenTelemetryTraceContext.h @@ -9,6 +9,7 @@ struct Settings; class OpenTelemetrySpanLog; class WriteBuffer; class ReadBuffer; +struct ExecutionStatus; namespace OpenTelemetry { @@ -57,6 +58,7 @@ struct Span bool addAttribute(std::string_view name, std::function value_supplier) noexcept; bool addAttribute(const Exception & e) noexcept; bool addAttribute(std::exception_ptr e) noexcept; + bool addAttribute(const ExecutionStatus & e) noexcept; bool isTraceEnabled() const { diff --git a/src/Common/ThreadPool.cpp b/src/Common/ThreadPool.cpp index caa32b61c65..b742e2df06e 100644 --- a/src/Common/ThreadPool.cpp +++ b/src/Common/ThreadPool.cpp @@ -11,6 +11,7 @@ #include #include +#include namespace DB { @@ -29,6 +30,7 @@ namespace CurrentMetrics extern const Metric LocalThreadActive; } +static constexpr auto DEFAULT_THREAD_NAME = "ThreadPool"; template ThreadPoolImpl::ThreadPoolImpl() @@ -342,7 +344,7 @@ void ThreadPoolImpl::worker(typename std::list::iterator thread_ while (true) { /// This is inside the loop to also reset previous thread names set inside the jobs. - setThreadName("ThreadPool"); + setThreadName(DEFAULT_THREAD_NAME); /// A copy of parent trace context DB::OpenTelemetry::TracingContextOnThread parent_thead_trace_context; @@ -389,10 +391,17 @@ void ThreadPoolImpl::worker(typename std::list::iterator thread_ if (thread_trace_context.root_span.isTraceEnabled()) { /// Use the thread name as operation name so that the tracing log will be more clear. - /// The thread name is usually set in the jobs, we can only get the name after the job finishes + /// The thread name is usually set in jobs, we can only get the name after the job finishes std::string thread_name = getThreadName(); - if (!thread_name.empty()) + if (!thread_name.empty() && thread_name != DEFAULT_THREAD_NAME) + { thread_trace_context.root_span.operation_name = thread_name; + } + else + { + /// If the thread name is not set, use the type name of the job instead + thread_trace_context.root_span.operation_name = demangle(job.target_type().name()); + } } /// job should be reset before decrementing scheduled_jobs to diff --git a/src/Processors/Executors/ExecutionThreadContext.cpp b/src/Processors/Executors/ExecutionThreadContext.cpp index eddc1b76d8a..794f478b272 100644 --- a/src/Processors/Executors/ExecutionThreadContext.cpp +++ b/src/Processors/Executors/ExecutionThreadContext.cpp @@ -75,7 +75,7 @@ bool ExecutionThreadContext::executeTask() if (trace_processors) { - span = std::make_unique("ExecutionThreadContext::executeTask() " + node->processor->getName()); + span = std::make_unique(node->processor->getName()); span->addAttribute("thread_number", thread_number); } std::optional execution_time_watch; diff --git a/src/Processors/Executors/PipelineExecutor.cpp b/src/Processors/Executors/PipelineExecutor.cpp index b293524b86c..994051fd697 100644 --- a/src/Processors/Executors/PipelineExecutor.cpp +++ b/src/Processors/Executors/PipelineExecutor.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #ifndef NDEBUG #include @@ -94,6 +96,9 @@ void PipelineExecutor::execute(size_t num_threads) if (num_threads < 1) num_threads = 1; + OpenTelemetry::SpanHolder span("PipelineExecutor::execute()"); + span.addAttribute("clickhouse.thread_num", num_threads); + try { executeImpl(num_threads); @@ -108,6 +113,8 @@ void PipelineExecutor::execute(size_t num_threads) } catch (...) { + span.addAttribute(ExecutionStatus::fromCurrentException()); + #ifndef NDEBUG LOG_TRACE(log, "Exception while executing query. Current state:\n{}", dumpPipeline()); #endif diff --git a/src/Server/HTTPHandler.cpp b/src/Server/HTTPHandler.cpp index 665b7bb7695..bfdc067f733 100644 --- a/src/Server/HTTPHandler.cpp +++ b/src/Server/HTTPHandler.cpp @@ -1059,13 +1059,11 @@ void HTTPHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse /** If exception is received from remote server, then stack trace is embedded in message. * If exception is thrown on local server, then stack trace is in separate field. */ - std::string exception_message = getCurrentExceptionMessage(with_stacktrace, true); - int exception_code = getCurrentExceptionCode(); - - trySendExceptionToClient(exception_message, exception_code, request, response, used_output); + ExecutionStatus status = ExecutionStatus::fromCurrentException("", with_stacktrace); + trySendExceptionToClient(status.message, status.code, request, response, used_output); if (thread_trace_context) - thread_trace_context->root_span.addAttribute("clickhouse.exception_code", exception_code); + thread_trace_context->root_span.addAttribute(status); } used_output.finalize(); From 26e031169ef68670f2f9f1a79658928a6670e4bf Mon Sep 17 00:00:00 2001 From: Dale Mcdiarmid Date: Wed, 29 Mar 2023 15:29:22 +0100 Subject: [PATCH 188/377] youtube questions --- .../example-datasets/youtube-dislikes.md | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) diff --git a/docs/en/getting-started/example-datasets/youtube-dislikes.md b/docs/en/getting-started/example-datasets/youtube-dislikes.md index 2eb2071d5f2..8b8217ed5be 100644 --- a/docs/en/getting-started/example-datasets/youtube-dislikes.md +++ b/docs/en/getting-started/example-datasets/youtube-dislikes.md @@ -216,4 +216,276 @@ The results look like: │ 1919 │ 63 │ 1 │ https://youtu.be/b9MeoOtAivQ │ ClickHouse v21.10 Release Webinar │ │ 8710 │ 62 │ 4 │ https://youtu.be/PeV1mC2z--M │ What is JDBC DriverManager? | JDBC │ │ 3534 │ 62 │ 1 │ https://youtu.be/8nWRhK9gw10 │ CLICKHOUSE - Arquitetura Modular │ +``` + +## Questions + +### If someone disables comments does it lower the chance someone will actually click like or dislike? + +When commenting is disabled, are people more likely to like or dislike to express their feelings about a video? + + +```sql +SELECT + concat('< ', formatReadableQuantity(view_range)) AS views, + is_comments_enabled, + total_clicks / num_views AS prob_like_dislike +FROM +( + SELECT + is_comments_enabled, + power(10, CEILING(log10(view_count + 1))) AS view_range, + sum(like_count + dislike_count) AS total_clicks, + sum(view_count) AS num_views + FROM youtube + GROUP BY + view_range, + is_comments_enabled +) WHERE view_range > 1 +ORDER BY + is_comments_enabled ASC, + num_views ASC +``` + +```response + +┌─views─────────────┬─is_comments_enabled─┬────prob_like_dislike─┐ +│ < 10.00 │ false │ 0.08224180712685371 │ +│ < 100.00 │ false │ 0.06346337759167248 │ +│ < 1.00 thousand │ false │ 0.03201883652987105 │ +│ < 10.00 thousand │ false │ 0.01716073540410903 │ +│ < 10.00 billion │ false │ 0.004555639481829971 │ +│ < 100.00 thousand │ false │ 0.01293351460515323 │ +│ < 1.00 billion │ false │ 0.004761811192464957 │ +│ < 1.00 million │ false │ 0.010472604018980551 │ +│ < 10.00 million │ false │ 0.00788902538420125 │ +│ < 100.00 million │ false │ 0.00579152804250582 │ +│ < 10.00 │ true │ 0.09819517478134059 │ +│ < 100.00 │ true │ 0.07403784478585775 │ +│ < 1.00 thousand │ true │ 0.03846294910067627 │ +│ < 10.00 billion │ true │ 0.005615217329358215 │ +│ < 10.00 thousand │ true │ 0.02505881391701455 │ +│ < 1.00 billion │ true │ 0.007434998802482997 │ +│ < 100.00 thousand │ true │ 0.022694648130822004 │ +│ < 100.00 million │ true │ 0.011761563746575625 │ +│ < 1.00 million │ true │ 0.020776022304589435 │ +│ < 10.00 million │ true │ 0.016917095718089584 │ +└───────────────────┴─────────────────────┴──────────────────────┘ + +22 rows in set. Elapsed: 8.460 sec. Processed 4.56 billion rows, 77.48 GB (538.73 million rows/s., 9.16 GB/s.) + +``` + +Enabling comments seems to be correlated with a higher rate of engagement. + + +### How does the number of videos change over time - notable events? + +```sql +SELECT + toStartOfMonth(toDateTime(upload_date)) AS month, + uniq(uploader_id) AS uploaders, + count() as num_videos, + sum(view_count) as view_count +FROM youtube +WHERE (month >= '2005-01-01') AND (month < '2021-12-01') +GROUP BY month +ORDER BY month ASC +``` + +```response +┌──────month─┬─uploaders─┬─num_videos─┬───view_count─┐ +│ 2005-04-01 │ 5 │ 6 │ 213597737 │ +│ 2005-05-01 │ 6 │ 9 │ 2944005 │ +│ 2005-06-01 │ 165 │ 351 │ 18624981 │ +│ 2005-07-01 │ 395 │ 1168 │ 94164872 │ +│ 2005-08-01 │ 1171 │ 3128 │ 124540774 │ +│ 2005-09-01 │ 2418 │ 5206 │ 475536249 │ +│ 2005-10-01 │ 6750 │ 13747 │ 737593613 │ +│ 2005-11-01 │ 13706 │ 28078 │ 1896116976 │ +│ 2005-12-01 │ 24756 │ 49885 │ 2478418930 │ +│ 2006-01-01 │ 49992 │ 100447 │ 4532656581 │ +│ 2006-02-01 │ 67882 │ 138485 │ 5677516317 │ +│ 2006-03-01 │ 103358 │ 212237 │ 8430301366 │ +│ 2006-04-01 │ 114615 │ 234174 │ 9980760440 │ +│ 2006-05-01 │ 152682 │ 332076 │ 14129117212 │ +│ 2006-06-01 │ 193962 │ 429538 │ 17014143263 │ +│ 2006-07-01 │ 234401 │ 530311 │ 18721143410 │ +│ 2006-08-01 │ 281280 │ 614128 │ 20473502342 │ +│ 2006-09-01 │ 312434 │ 679906 │ 23158422265 │ +│ 2006-10-01 │ 404873 │ 897590 │ 27357846117 │ +``` + +A spike of uploaders [around covid is noticeable](https://www.theverge.com/2020/3/27/21197642/youtube-with-me-style-videos-views-coronavirus-cook-workout-study-home-beauty). + + +### More subtitiles over time and when + +With advances in speech recognition, it’s easier than ever to create subtitles for video with youtube adding auto-captioning in late 2009 - was the jump then? + +```sql +SELECT + toStartOfMonth(upload_date) AS month, + countIf(has_subtitles) / count() AS percent_subtitles, + percent_subtitles - any(percent_subtitles) OVER (ORDER BY month ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS previous +FROM youtube +WHERE (month >= '2015-01-01') AND (month < '2021-12-02') +GROUP BY month +ORDER BY month ASC +``` + +```response +┌──────month─┬───percent_subtitles─┬────────────────previous─┐ +│ 2015-01-01 │ 0.2652653881082824 │ 0.2652653881082824 │ +│ 2015-02-01 │ 0.3147556050309162 │ 0.049490216922633834 │ +│ 2015-03-01 │ 0.32460464492371877 │ 0.009849039892802558 │ +│ 2015-04-01 │ 0.33471963051468445 │ 0.010114985590965686 │ +│ 2015-05-01 │ 0.3168087575501062 │ -0.017910872964578273 │ +│ 2015-06-01 │ 0.3162609788438222 │ -0.0005477787062839745 │ +│ 2015-07-01 │ 0.31828767677518033 │ 0.0020266979313581235 │ +│ 2015-08-01 │ 0.3045551564286859 │ -0.013732520346494415 │ +│ 2015-09-01 │ 0.311221133995152 │ 0.006665977566466086 │ +│ 2015-10-01 │ 0.30574870926812175 │ -0.005472424727030245 │ +│ 2015-11-01 │ 0.31125409712077234 │ 0.0055053878526505895 │ +│ 2015-12-01 │ 0.3190967954651779 │ 0.007842698344405541 │ +│ 2016-01-01 │ 0.32636021432496176 │ 0.007263418859783877 │ + +``` + +The data results show a spike in 2009. Apparently at that, time YouTube was removing their community captions feature, which allowed you to upload captions for other people's video. +This prompted a very successful campaign to have creators add captions to their videos for hard of hearing and deaf viewers. + + +### Top uploaders over time + +```sql +WITH uploaders AS + ( + SELECT uploader + FROM youtube + GROUP BY uploader + ORDER BY sum(view_count) DESC + LIMIT 10 + ) +SELECT + month, + uploader, + sum(view_count) AS total_views, + avg(dislike_count / like_count) AS like_to_dislike_ratio +FROM youtube +WHERE uploader IN (uploaders) +GROUP BY + toStartOfMonth(upload_date) AS month, + uploader +ORDER BY + month ASC, + total_views DESC + +1001 rows in set. Elapsed: 34.917 sec. Processed 4.58 billion rows, 69.08 GB (131.15 million rows/s., 1.98 GB/s.) +``` + +```response +┌──────month─┬─uploader───────────────────┬─total_views─┬─like_to_dislike_ratio─┐ +│ 1970-01-01 │ T-Series │ 10957099 │ 0.022784656361208206 │ +│ 1970-01-01 │ Ryan's World │ 0 │ 0.003035559410234172 │ +│ 1970-01-01 │ SET India │ 0 │ nan │ +│ 2006-09-01 │ Cocomelon - Nursery Rhymes │ 256406497 │ 0.7005566715978622 │ +│ 2007-06-01 │ Cocomelon - Nursery Rhymes │ 33641320 │ 0.7088650914344298 │ +│ 2008-02-01 │ WWE │ 43733469 │ 0.07198856488734842 │ +│ 2008-03-01 │ WWE │ 16514541 │ 0.1230603715431997 │ +│ 2008-04-01 │ WWE │ 5907295 │ 0.2089399470159618 │ +│ 2008-05-01 │ WWE │ 7779627 │ 0.09101676560436774 │ +│ 2008-06-01 │ WWE │ 7018780 │ 0.0974184753155297 │ +│ 2008-07-01 │ WWE │ 4686447 │ 0.1263845422065158 │ +│ 2008-08-01 │ WWE │ 4514312 │ 0.08384574274791441 │ +│ 2008-09-01 │ WWE │ 3717092 │ 0.07872802579349912 │ +``` + +### How do like ratio changes as views go up? + +```sql +SELECT + concat('< ', formatReadableQuantity(view_range)) AS view_range, + is_comments_enabled, + round(like_ratio, 2) AS like_ratio +FROM +( +SELECT + power(10, CEILING(log10(view_count + 1))) as view_range, + is_comments_enabled, + avg(like_count / dislike_count) as like_ratio +FROM youtube WHERE dislike_count > 0 +GROUP BY + view_range, + is_comments_enabled HAVING view_range > 1 +ORDER BY + view_range ASC, + is_comments_enabled ASC +) + +20 rows in set. Elapsed: 9.043 sec. Processed 4.56 billion rows, 77.48 GB (503.99 million rows/s., 8.57 GB/s.) +``` + +```response + +┌─view_range────────┬─is_comments_enabled─┬─like_ratio─┐ +│ < 10.00 │ false │ 0.66 │ +│ < 10.00 │ true │ 0.66 │ +│ < 100.00 │ false │ 3 │ +│ < 100.00 │ true │ 3.95 │ +│ < 1.00 thousand │ false │ 8.45 │ +│ < 1.00 thousand │ true │ 13.07 │ +│ < 10.00 thousand │ false │ 18.57 │ +│ < 10.00 thousand │ true │ 30.92 │ +│ < 100.00 thousand │ false │ 23.55 │ +│ < 100.00 thousand │ true │ 42.13 │ +│ < 1.00 million │ false │ 19.23 │ +│ < 1.00 million │ true │ 37.86 │ +│ < 10.00 million │ false │ 12.13 │ +│ < 10.00 million │ true │ 30.72 │ +│ < 100.00 million │ false │ 6.67 │ +│ < 100.00 million │ true │ 23.32 │ +│ < 1.00 billion │ false │ 3.08 │ +│ < 1.00 billion │ true │ 20.69 │ +│ < 10.00 billion │ false │ 1.77 │ +│ < 10.00 billion │ true │ 19.5 │ +└───────────────────┴─────────────────────┴────────────┘ + +``` + +### How are views distributed? + +```sql +SELECT + labels AS percentile, + round(quantiles) AS views +FROM +( + SELECT + quantiles(0.999, 0.99, 0.95, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1)(view_count) AS quantiles, + ['99.9th', '99th', '95th', '90th', '80th', '70th','60th', '50th', '40th', '30th', '20th', '10th'] AS labels + FROM youtube +) +ARRAY JOIN + quantiles, + labels + +12 rows in set. Elapsed: 1.864 sec. Processed 4.56 billion rows, 36.46 GB (2.45 billion rows/s., 19.56 GB/s.) +``` + +```response +┌─percentile─┬───views─┐ +│ 99.9th │ 1216624 │ +│ 99th │ 143519 │ +│ 95th │ 13542 │ +│ 90th │ 4054 │ +│ 80th │ 950 │ +│ 70th │ 363 │ +│ 60th │ 177 │ +│ 50th │ 97 │ +│ 40th │ 57 │ +│ 30th │ 32 │ +│ 20th │ 16 │ +│ 10th │ 6 │ +└────────────┴─────────┘ ``` \ No newline at end of file From 70717c9dc1521b41ca962567f8db6c0d7bca3be8 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Wed, 29 Mar 2023 08:43:28 -0600 Subject: [PATCH 189/377] Change fields to Bool --- docs/en/getting-started/example-datasets/amazon-reviews.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md index 7935cb6dba0..5a41da117d9 100644 --- a/docs/en/getting-started/example-datasets/amazon-reviews.md +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -91,6 +91,9 @@ ORDER BY (marketplace, review_date, product_category); ```sql INSERT INTO amazon_reviews +WITH + transform(vine, ['Y','N'],[true, false]) AS vine, + transform(verified_purchase, ['Y','N'],[true, false]) AS verified_purchase SELECT * FROM s3Cluster( @@ -108,8 +111,8 @@ FROM s3Cluster( star_rating UInt8, helpful_votes UInt32, total_votes UInt32, - vine FixedString(1), - verified_purchase FixedString(1), + vine Bool, + verified_purchase Bool, review_headline String, review_body String' ); From e688b27ccfca20a5f3550fdaf70ff9063eb92ea2 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 29 Mar 2023 17:44:05 +0300 Subject: [PATCH 190/377] Update 02702_allow_skip_errors_enum.sh --- tests/queries/0_stateless/02702_allow_skip_errors_enum.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02702_allow_skip_errors_enum.sh b/tests/queries/0_stateless/02702_allow_skip_errors_enum.sh index de4a4bced65..e68f5517d52 100755 --- a/tests/queries/0_stateless/02702_allow_skip_errors_enum.sh +++ b/tests/queries/0_stateless/02702_allow_skip_errors_enum.sh @@ -4,7 +4,7 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh . "$CURDIR"/../shell_config.sh -$CLICKHOUSE_CLIENT --query "CREATE OR REPLACE TABLE t (x Enum('Hello' = 1, 'World' = 2)) ENGINE = Memory;" +$CLICKHOUSE_CLIENT --multiquery --query "DROP TABLE IF EXISTS t; CREATE TABLE t (x Enum('Hello' = 1, 'World' = 2)) ENGINE = Memory;" $CLICKHOUSE_CLIENT --input_format_allow_errors_num 1 --query "INSERT INTO t FORMAT CSV" < Date: Wed, 29 Mar 2023 08:47:23 -0600 Subject: [PATCH 191/377] Update amazon-reviews.md --- .../example-datasets/amazon-reviews.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/en/getting-started/example-datasets/amazon-reviews.md b/docs/en/getting-started/example-datasets/amazon-reviews.md index 5a41da117d9..55014d9b5f4 100644 --- a/docs/en/getting-started/example-datasets/amazon-reviews.md +++ b/docs/en/getting-started/example-datasets/amazon-reviews.md @@ -76,8 +76,8 @@ CREATE TABLE amazon_reviews star_rating UInt8, helpful_votes UInt32, total_votes UInt32, - vine FixedString(1), - verified_purchase FixedString(1), + vine Bool, + verified_purchase Bool, review_headline String, review_body String ) @@ -111,11 +111,12 @@ FROM s3Cluster( star_rating UInt8, helpful_votes UInt32, total_votes UInt32, - vine Bool, - verified_purchase Bool, + vine FixedString(1), + verified_purchase FixedString(1), review_headline String, review_body String' - ); + ) +SETTINGS input_format_allow_errors_num = 1000000; ``` :::tip From 85795965c0a0c09b7a561a61577a4d06e8b64444 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 29 Mar 2023 14:59:01 +0000 Subject: [PATCH 192/377] Deprecate EXPLAIN QUERY TREE with disabled analyzer. --- src/Interpreters/InterpreterExplainQuery.cpp | 5 +++++ ...plain_query_tree_is_forbidden_with_old_analyzer.reference | 0 ...402_explain_query_tree_is_forbidden_with_old_analyzer.sql | 2 ++ 3 files changed, 7 insertions(+) create mode 100644 tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.reference create mode 100644 tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.sql diff --git a/src/Interpreters/InterpreterExplainQuery.cpp b/src/Interpreters/InterpreterExplainQuery.cpp index 3c225522cc4..3a381cd8dab 100644 --- a/src/Interpreters/InterpreterExplainQuery.cpp +++ b/src/Interpreters/InterpreterExplainQuery.cpp @@ -41,6 +41,7 @@ namespace ErrorCodes extern const int INVALID_SETTING_VALUE; extern const int UNKNOWN_SETTING; extern const int LOGICAL_ERROR; + extern const int NOT_IMPLEMENTED; } namespace @@ -386,6 +387,10 @@ QueryPipeline InterpreterExplainQuery::executeImpl() } case ASTExplainQuery::QueryTree: { + if (!getContext()->getSettingsRef().allow_experimental_analyzer) + throw Exception(ErrorCodes::NOT_IMPLEMENTED, + "EXPLAIN QUERY TREE is only supported with a new analyzer. Set allow_experimental_analyzer = 1."); + if (ast.getExplainedQuery()->as() == nullptr) throw Exception(ErrorCodes::INCORRECT_QUERY, "Only SELECT is supported for EXPLAIN QUERY TREE query"); diff --git a/tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.reference b/tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.sql b/tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.sql new file mode 100644 index 00000000000..d351bfe402c --- /dev/null +++ b/tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.sql @@ -0,0 +1,2 @@ +set allow_experimental_analyzer=0; +EXPLAIN QUERY TREE run_passes = true, dump_passes = true SELECT 1; -- { serverError NOT_IMPLEMENTED } From 482a9690fcf1d95137c8b8cf94e687c277b6dfd7 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Wed, 29 Mar 2023 17:00:34 +0200 Subject: [PATCH 193/377] fix --- docs/get-clickhouse-docs.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/get-clickhouse-docs.sh b/docs/get-clickhouse-docs.sh index e3b70dee665..9f0bd52362a 100755 --- a/docs/get-clickhouse-docs.sh +++ b/docs/get-clickhouse-docs.sh @@ -39,7 +39,6 @@ else if [ "$set_git_hook" = "y" ]; then hook_command="$(pwd)/pull-clickhouse-docs-hook.sh $UPDATE_PERIOD_HOURS" hook_file=$(realpath "$(pwd)/../.git/hooks/post-checkout") - already_have=$(grep -Faq "pull-clickhouse-docs-hook.sh" "$hook_file" 2>/dev/null && echo 1 || echo 0) if grep -Faq "pull-clickhouse-docs-hook.sh" "$hook_file" 2>/dev/null; then echo "Looks like the update hook already exists, will not add another one" else From 8b965ba895df4edecfeec74a7a5056bc0c464ac0 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Wed, 29 Mar 2023 18:15:46 +0300 Subject: [PATCH 194/377] Update get-clickhouse-docs.sh --- docs/get-clickhouse-docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/get-clickhouse-docs.sh b/docs/get-clickhouse-docs.sh index 9f0bd52362a..34db4e74cee 100755 --- a/docs/get-clickhouse-docs.sh +++ b/docs/get-clickhouse-docs.sh @@ -37,7 +37,7 @@ else fi if [ "$set_git_hook" = "y" ]; then - hook_command="$(pwd)/pull-clickhouse-docs-hook.sh $UPDATE_PERIOD_HOURS" + hook_command="$(pwd)/pull-clickhouse-docs-hook.sh $UPDATE_PERIOD_HOURS ||:" hook_file=$(realpath "$(pwd)/../.git/hooks/post-checkout") if grep -Faq "pull-clickhouse-docs-hook.sh" "$hook_file" 2>/dev/null; then echo "Looks like the update hook already exists, will not add another one" From 0ac9dcd723a6c3f4ed081a6999b8499f61d6a48d Mon Sep 17 00:00:00 2001 From: Aleksei Filatov Date: Wed, 29 Mar 2023 16:37:58 +0300 Subject: [PATCH 195/377] Add allow_distributed_ddl_queries option to the cluster config --- .../en/engines/table-engines/special/distributed.md | 4 ++++ src/Interpreters/Cluster.cpp | 3 +++ src/Interpreters/Cluster.h | 5 +++++ src/Interpreters/executeDDLQueryOnCluster.cpp | 3 +++ .../configs/config.d/clusters.xml | 13 ++++++++++++- .../configs_secure/config.d/clusters.xml | 11 +++++++++++ tests/integration/test_distributed_ddl/test.py | 11 +++++++++++ 7 files changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/en/engines/table-engines/special/distributed.md b/docs/en/engines/table-engines/special/distributed.md index 52d82483a46..d13453bc08f 100644 --- a/docs/en/engines/table-engines/special/distributed.md +++ b/docs/en/engines/table-engines/special/distributed.md @@ -141,6 +141,10 @@ Clusters are configured in the [server configuration file](../../../operations/c be used as current user for the query. --> + + + + 1 diff --git a/src/Interpreters/Cluster.cpp b/src/Interpreters/Cluster.cpp index 0add0e427f9..445c227dd29 100644 --- a/src/Interpreters/Cluster.cpp +++ b/src/Interpreters/Cluster.cpp @@ -396,6 +396,9 @@ Cluster::Cluster(const Poco::Util::AbstractConfiguration & config, secret = config.getString(config_prefix + "secret", ""); boost::range::remove_erase(config_keys, "secret"); + allow_distributed_ddl_queries = config.getBool(config_prefix + "allow_distributed_ddl_queries", true); + boost::range::remove_erase(config_keys, "allow_distributed_ddl_queries"); + if (config_keys.empty()) throw Exception(ErrorCodes::SHARD_HAS_NO_CONNECTIONS, "No cluster elements (shard, node) specified in config at path {}", config_prefix); diff --git a/src/Interpreters/Cluster.h b/src/Interpreters/Cluster.h index 02c450c3c6b..d74f75c941e 100644 --- a/src/Interpreters/Cluster.h +++ b/src/Interpreters/Cluster.h @@ -256,6 +256,9 @@ public: /// NOTE: true does not mean, that it's actually a cross-replication cluster. bool maybeCrossReplication() const; + /// Are distributed DDL Queries (ON CLUSTER Clause) allowed for this cluster + bool areDistributedDDLQueriesAllowed() const { return allow_distributed_ddl_queries; } + private: SlotToShard slot_to_shard; @@ -287,6 +290,8 @@ private: /// An array of shards. For each shard, an array of replica addresses (servers that are considered identical). AddressesWithFailover addresses_with_failover; + bool allow_distributed_ddl_queries = true; + size_t remote_shard_count = 0; size_t local_shard_count = 0; diff --git a/src/Interpreters/executeDDLQueryOnCluster.cpp b/src/Interpreters/executeDDLQueryOnCluster.cpp index c1063e25587..c40b9c779c9 100644 --- a/src/Interpreters/executeDDLQueryOnCluster.cpp +++ b/src/Interpreters/executeDDLQueryOnCluster.cpp @@ -92,6 +92,9 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, ContextPtr context, span.addAttribute("clickhouse.cluster", query->cluster); + if (!cluster->areDistributedDDLQueriesAllowed()) + throw Exception(ErrorCodes::QUERY_IS_PROHIBITED, "Distributed DDL queries are prohibited for the cluster"); + /// TODO: support per-cluster grant context->checkAccess(AccessType::CLUSTER); diff --git a/tests/integration/test_distributed_ddl/configs/config.d/clusters.xml b/tests/integration/test_distributed_ddl/configs/config.d/clusters.xml index b353f88f4cd..159d578bb9d 100644 --- a/tests/integration/test_distributed_ddl/configs/config.d/clusters.xml +++ b/tests/integration/test_distributed_ddl/configs/config.d/clusters.xml @@ -89,5 +89,16 @@ + + + false + + + ch1 + 9000 + + + + - \ No newline at end of file + diff --git a/tests/integration/test_distributed_ddl/configs_secure/config.d/clusters.xml b/tests/integration/test_distributed_ddl/configs_secure/config.d/clusters.xml index 6c462ea6087..e5ec8161e00 100644 --- a/tests/integration/test_distributed_ddl/configs_secure/config.d/clusters.xml +++ b/tests/integration/test_distributed_ddl/configs_secure/config.d/clusters.xml @@ -102,5 +102,16 @@ + + + false + + + ch1 + 9000 + + + + diff --git a/tests/integration/test_distributed_ddl/test.py b/tests/integration/test_distributed_ddl/test.py index 8c2caf59a35..7cee60a7f35 100755 --- a/tests/integration/test_distributed_ddl/test.py +++ b/tests/integration/test_distributed_ddl/test.py @@ -585,6 +585,17 @@ def test_replicated_without_arguments(test_cluster): test_cluster.pm_random_drops.push_rules(rules) +def test_disabled_distributed_ddl(test_cluster): + instance = test_cluster.instances["ch1"] + + assert ( + "Distributed DDL queries are prohibited for the cluster" + in instance.query_and_get_error( + "CREATE DATABASE IF NOT EXISTS test ON CLUSTER 'cluster_disabled_ddl'" + ) + ) + + if __name__ == "__main__": with contextmanager(test_cluster)() as ctx_cluster: for name, instance in list(ctx_cluster.instances.items()): From 3174c07a8667b0e33eefef42db5d60d4da5f2135 Mon Sep 17 00:00:00 2001 From: Nikita Taranov Date: Wed, 29 Mar 2023 17:43:26 +0200 Subject: [PATCH 196/377] try fix test --- tests/queries/0_stateless/02151_hash_table_sizes_stats.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02151_hash_table_sizes_stats.sh b/tests/queries/0_stateless/02151_hash_table_sizes_stats.sh index ccd6f89e752..4a1eea0a238 100755 --- a/tests/queries/0_stateless/02151_hash_table_sizes_stats.sh +++ b/tests/queries/0_stateless/02151_hash_table_sizes_stats.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Tags: long +# Tags: long, no-tsan # shellcheck disable=SC2154 From 538a4acbcd8fe35e19ae6775dd295cf3f29cdfc4 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 29 Mar 2023 16:57:11 +0000 Subject: [PATCH 197/377] Updating tests. --- .../00849_multiple_comma_join_2.sql | 30 +++++++++---------- .../01300_group_by_other_keys_having.sql | 2 +- ..._redundant_functions_in_order_by.reference | 8 +++++ .../01323_redundant_functions_in_order_by.sql | 16 +++++----- ...1_analyzer_optimize_grouping_sets_keys.sql | 2 ++ 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/tests/queries/0_stateless/00849_multiple_comma_join_2.sql b/tests/queries/0_stateless/00849_multiple_comma_join_2.sql index db8b27c4d4d..51bf5a2ede1 100644 --- a/tests/queries/0_stateless/00849_multiple_comma_join_2.sql +++ b/tests/queries/0_stateless/00849_multiple_comma_join_2.sql @@ -62,49 +62,49 @@ SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explai --- EXPLAIN QUERY TREE SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2 WHERE t1.a = t2.a); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2 WHERE t1.a = t2.a SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2 WHERE t1.b = t2.b); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2 WHERE t1.b = t2.b SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3 WHERE t1.a = t2.a AND t1.a = t3.a); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3 WHERE t1.a = t2.a AND t1.a = t3.a SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3 WHERE t1.b = t2.b AND t1.b = t3.b); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3 WHERE t1.b = t2.b AND t1.b = t3.b SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t1.a = t3.a AND t1.a = t4.a); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t1.a = t3.a AND t1.a = t4.a SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.b = t2.b AND t1.b = t3.b AND t1.b = t4.b); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.b = t2.b AND t1.b = t3.b AND t1.b = t4.b SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t2.a = t1.a AND t2.a = t3.a AND t2.a = t4.a); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t2.a = t1.a AND t2.a = t3.a AND t2.a = t4.a SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t3.a = t1.a AND t3.a = t2.a AND t3.a = t4.a); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t3.a = t1.a AND t3.a = t2.a AND t3.a = t4.a SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t4.a = t1.a AND t4.a = t2.a AND t4.a = t3.a); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t4.a = t1.a AND t4.a = t2.a AND t4.a = t3.a SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t2.a = t3.a AND t3.a = t4.a); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t2.a = t3.a AND t3.a = t4.a SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2, t3, t4 SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1 CROSS JOIN t2 CROSS JOIN t3 CROSS JOIN t4); + EXPLAIN QUERY TREE SELECT t1.a FROM t1 CROSS JOIN t2 CROSS JOIN t3 CROSS JOIN t4 SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2 CROSS JOIN t3); + EXPLAIN QUERY TREE SELECT t1.a FROM t1, t2 CROSS JOIN t3 SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1 JOIN t2 USING a CROSS JOIN t3); + EXPLAIN QUERY TREE SELECT t1.a FROM t1 JOIN t2 USING a CROSS JOIN t3 SETTINGS allow_experimental_analyzer = 1); SELECT countIf(explain like '%COMMA%' OR explain like '%CROSS%'), countIf(explain like '%INNER%') FROM ( - EXPLAIN QUERY TREE SELECT t1.a FROM t1 JOIN t2 ON t1.a = t2.a CROSS JOIN t3); + EXPLAIN QUERY TREE SELECT t1.a FROM t1 JOIN t2 ON t1.a = t2.a CROSS JOIN t3 SETTINGS allow_experimental_analyzer = 1); INSERT INTO t1 values (1,1), (2,2), (3,3), (4,4); INSERT INTO t2 values (1,1), (1, Null); diff --git a/tests/queries/0_stateless/01300_group_by_other_keys_having.sql b/tests/queries/0_stateless/01300_group_by_other_keys_having.sql index d209c5f24e3..911f61a62e2 100644 --- a/tests/queries/0_stateless/01300_group_by_other_keys_having.sql +++ b/tests/queries/0_stateless/01300_group_by_other_keys_having.sql @@ -1,5 +1,5 @@ set optimize_group_by_function_keys = 1; - +set allow_experimental_analyzer = 1; -- { echoOn } SELECT round(avg(log(2) * number), 6) AS k FROM numbers(10000000) GROUP BY (number % 2) * (number % 3), number % 3, number % 2 HAVING avg(log(2) * number) > 3465735.3 ORDER BY k; diff --git a/tests/queries/0_stateless/01323_redundant_functions_in_order_by.reference b/tests/queries/0_stateless/01323_redundant_functions_in_order_by.reference index 91a96eb68a3..bf184d142ec 100644 --- a/tests/queries/0_stateless/01323_redundant_functions_in_order_by.reference +++ b/tests/queries/0_stateless/01323_redundant_functions_in_order_by.reference @@ -65,6 +65,7 @@ QUERY id: 0 SORT id: 12, sort_direction: ASCENDING, with_fill: 0 EXPRESSION COLUMN id: 7, column_name: number, result_type: UInt64, source_id: 8 + SETTINGS allow_experimental_analyzer=1 SELECT groupArray(x) FROM ( @@ -98,6 +99,7 @@ QUERY id: 0 SORT id: 12, sort_direction: ASCENDING, with_fill: 0 EXPRESSION COLUMN id: 7, column_name: number, result_type: UInt64, source_id: 8 + SETTINGS allow_experimental_analyzer=1 SELECT groupArray(x) FROM ( @@ -139,6 +141,7 @@ QUERY id: 0 SORT id: 15, sort_direction: ASCENDING, with_fill: 0 EXPRESSION COLUMN id: 7, column_name: number, result_type: UInt64, source_id: 8 + SETTINGS allow_experimental_analyzer=1 SELECT key, a, @@ -200,6 +203,7 @@ QUERY id: 0 SORT id: 25, sort_direction: ASCENDING, with_fill: 0 EXPRESSION COLUMN id: 26, column_name: key, result_type: UInt64, source_id: 5 + SETTINGS allow_experimental_analyzer=1 SELECT key, a @@ -225,6 +229,7 @@ QUERY id: 0 SORT id: 7, sort_direction: ASCENDING, with_fill: 0 EXPRESSION COLUMN id: 4, column_name: a, result_type: UInt8, source_id: 3 + SETTINGS allow_experimental_analyzer=1 SELECT key, a @@ -257,6 +262,7 @@ QUERY id: 0 LIST id: 11, nodes: 2 COLUMN id: 2, column_name: key, result_type: UInt64, source_id: 3 COLUMN id: 4, column_name: a, result_type: UInt8, source_id: 3 + SETTINGS allow_experimental_analyzer=1 QUERY id: 0 PROJECTION COLUMNS key UInt64 @@ -279,6 +285,7 @@ QUERY id: 0 SORT id: 10, sort_direction: ASCENDING, with_fill: 0 EXPRESSION COLUMN id: 2, column_name: key, result_type: UInt64, source_id: 3 + SETTINGS allow_experimental_analyzer=1 QUERY id: 0 PROJECTION COLUMNS t1.id UInt64 @@ -307,6 +314,7 @@ QUERY id: 0 SORT id: 14, sort_direction: ASCENDING, with_fill: 0 EXPRESSION COLUMN id: 15, column_name: id, result_type: UInt64, source_id: 5 + SETTINGS allow_experimental_analyzer=1 [0,1,2] [0,1,2] [0,1,2] diff --git a/tests/queries/0_stateless/01323_redundant_functions_in_order_by.sql b/tests/queries/0_stateless/01323_redundant_functions_in_order_by.sql index 338c1345052..738ad581e3d 100644 --- a/tests/queries/0_stateless/01323_redundant_functions_in_order_by.sql +++ b/tests/queries/0_stateless/01323_redundant_functions_in_order_by.sql @@ -20,25 +20,25 @@ SELECT key, a FROM test ORDER BY key, a, exp(key + a) SETTINGS allow_experimenta SELECT key, a FROM test ORDER BY key, exp(key + a); SELECT key, a FROM test ORDER BY key, exp(key + a) SETTINGS allow_experimental_analyzer=1; EXPLAIN SYNTAX SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY x, exp(x)); -EXPLAIN QUERY TREE run_passes=1 SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY x, exp(x)); +EXPLAIN QUERY TREE run_passes=1 SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY x, exp(x)) settings allow_experimental_analyzer=1; EXPLAIN SYNTAX SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY x, exp(exp(x))); -EXPLAIN QUERY TREE run_passes=1 SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY x, exp(exp(x))); +EXPLAIN QUERY TREE run_passes=1 SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY x, exp(exp(x))) settings allow_experimental_analyzer=1; EXPLAIN SYNTAX SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY exp(x), x); -EXPLAIN QUERY TREE run_passes=1 SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY exp(x), x); +EXPLAIN QUERY TREE run_passes=1 SELECT groupArray(x) from (SELECT number as x FROM numbers(3) ORDER BY exp(x), x) settings allow_experimental_analyzer=1; EXPLAIN SYNTAX SELECT * FROM (SELECT number + 2 AS key FROM numbers(4)) s FULL JOIN test t USING(key) ORDER BY s.key, t.key; -EXPLAIN QUERY TREE run_passes=1 SELECT * FROM (SELECT number + 2 AS key FROM numbers(4)) s FULL JOIN test t USING(key) ORDER BY s.key, t.key; +EXPLAIN QUERY TREE run_passes=1 SELECT * FROM (SELECT number + 2 AS key FROM numbers(4)) s FULL JOIN test t USING(key) ORDER BY s.key, t.key settings allow_experimental_analyzer=1; EXPLAIN SYNTAX SELECT key, a FROM test ORDER BY key, a, exp(key + a); -EXPLAIN QUERY TREE run_passes=1 SELECT key, a FROM test ORDER BY key, a, exp(key + a); +EXPLAIN QUERY TREE run_passes=1 SELECT key, a FROM test ORDER BY key, a, exp(key + a) settings allow_experimental_analyzer=1; EXPLAIN SYNTAX SELECT key, a FROM test ORDER BY key, exp(key + a); -EXPLAIN QUERY TREE run_passes=1 SELECT key, a FROM test ORDER BY key, exp(key + a); -EXPLAIN QUERY TREE run_passes=1 SELECT key FROM test GROUP BY key ORDER BY avg(a), key; +EXPLAIN QUERY TREE run_passes=1 SELECT key, a FROM test ORDER BY key, exp(key + a) settings allow_experimental_analyzer=1; +EXPLAIN QUERY TREE run_passes=1 SELECT key FROM test GROUP BY key ORDER BY avg(a), key settings allow_experimental_analyzer=1; DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; CREATE TABLE t1 (id UInt64) ENGINE = MergeTree() ORDER BY id; CREATE TABLE t2 (id UInt64) ENGINE = MergeTree() ORDER BY id; -EXPLAIN QUERY TREE run_passes=1 SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.id ORDER BY t1.id, t2.id; +EXPLAIN QUERY TREE run_passes=1 SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.id ORDER BY t1.id, t2.id settings allow_experimental_analyzer=1; set optimize_redundant_functions_in_order_by = 0; diff --git a/tests/queries/0_stateless/02481_analyzer_optimize_grouping_sets_keys.sql b/tests/queries/0_stateless/02481_analyzer_optimize_grouping_sets_keys.sql index b51233f734c..de9208ef009 100644 --- a/tests/queries/0_stateless/02481_analyzer_optimize_grouping_sets_keys.sql +++ b/tests/queries/0_stateless/02481_analyzer_optimize_grouping_sets_keys.sql @@ -1,3 +1,5 @@ +set allow_experimental_analyzer = 1; + EXPLAIN QUERY TREE run_passes=1 SELECT avg(log(2) * number) AS k FROM numbers(10000000) GROUP BY GROUPING SETS (((number % 2) * (number % 3)), number % 3, number % 2) From db83dd4e5205b3b02b7fc611e98e27f6cdee4e1a Mon Sep 17 00:00:00 2001 From: avogar Date: Wed, 29 Mar 2023 17:33:22 +0000 Subject: [PATCH 198/377] Fix possible member call on null pointer in Avro format --- src/Processors/Formats/Impl/AvroRowOutputFormat.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp b/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp index cd1c1b096aa..c743b2c1766 100644 --- a/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp +++ b/src/Processors/Formats/Impl/AvroRowOutputFormat.cpp @@ -565,6 +565,11 @@ void AvroRowOutputFormat::write(const Columns & columns, size_t row_num) void AvroRowOutputFormat::finalizeImpl() { + /// If file writer weren't created, we should create it here to write file prefix/suffix + /// even without actual data so the file will be valid Avro file + if (!file_writer_ptr) + createFileWriter(); + file_writer_ptr->close(); } From 4478c6b60ef7224e4977c6e3bf10e9cd30c09283 Mon Sep 17 00:00:00 2001 From: Vitaly Baranov Date: Wed, 29 Mar 2023 00:06:46 +0200 Subject: [PATCH 199/377] Make test test_disallow_concurrency less flaky. --- .../test_disallow_concurrency.py | 97 ++++++++----------- 1 file changed, 43 insertions(+), 54 deletions(-) diff --git a/tests/integration/test_backup_restore_on_cluster/test_disallow_concurrency.py b/tests/integration/test_backup_restore_on_cluster/test_disallow_concurrency.py index b04eaed4bb8..1fb1f807245 100644 --- a/tests/integration/test_backup_restore_on_cluster/test_disallow_concurrency.py +++ b/tests/integration/test_backup_restore_on_cluster/test_disallow_concurrency.py @@ -125,19 +125,29 @@ def test_concurrent_backups_on_same_node(): .query(f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name} ASYNC") .split("\t")[0] ) - assert_eq_with_retry( - nodes[0], - f"SELECT status FROM system.backups WHERE status == 'CREATING_BACKUP' AND id = '{id}'", - "CREATING_BACKUP", + + status = ( + nodes[0] + .query(f"SELECT status FROM system.backups WHERE id == '{id}'") + .rstrip("\n") ) - assert "Concurrent backups not supported" in nodes[0].query_and_get_error( + assert status in ["CREATING_BACKUP", "BACKUP_CREATED"] + + error = nodes[0].query_and_get_error( f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name}" ) + expected_errors = [ + "Concurrent backups not supported", + f"Backup {backup_name} already exists", + ] + assert any([expected_error in error for expected_error in expected_errors]) assert_eq_with_retry( nodes[0], - f"SELECT status FROM system.backups WHERE status == 'BACKUP_CREATED' AND id = '{id}'", + f"SELECT status FROM system.backups WHERE id = '{id}'", "BACKUP_CREATED", + sleep_time=2, + retry_count=50, ) # This restore part is added to confirm creating an internal backup & restore work @@ -161,18 +171,29 @@ def test_concurrent_backups_on_different_nodes(): .query(f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name} ASYNC") .split("\t")[0] ) - assert_eq_with_retry( - nodes[1], - f"SELECT status FROM system.backups WHERE status == 'CREATING_BACKUP' AND id = '{id}'", - "CREATING_BACKUP", + + status = ( + nodes[1] + .query(f"SELECT status FROM system.backups WHERE id == '{id}'") + .rstrip("\n") ) - assert "Concurrent backups not supported" in nodes[0].query_and_get_error( + assert status in ["CREATING_BACKUP", "BACKUP_CREATED"] + + error = nodes[0].query_and_get_error( f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name}" ) + expected_errors = [ + "Concurrent backups not supported", + f"Backup {backup_name} already exists", + ] + assert any([expected_error in error for expected_error in expected_errors]) + assert_eq_with_retry( nodes[1], - f"SELECT status FROM system.backups WHERE status == 'BACKUP_CREATED' AND id = '{id}'", + f"SELECT status FROM system.backups WHERE id = '{id}'", "BACKUP_CREATED", + sleep_time=2, + retry_count=50, ) @@ -181,22 +202,7 @@ def test_concurrent_restores_on_same_node(): backup_name = new_backup_name() - id = ( - nodes[0] - .query(f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name} ASYNC") - .split("\t")[0] - ) - assert_eq_with_retry( - nodes[0], - f"SELECT status FROM system.backups WHERE status == 'CREATING_BACKUP' AND id = '{id}'", - "CREATING_BACKUP", - ) - - assert_eq_with_retry( - nodes[0], - f"SELECT status FROM system.backups WHERE status == 'BACKUP_CREATED' AND id = '{id}'", - "BACKUP_CREATED", - ) + nodes[0].query(f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name}") nodes[0].query( f"DROP TABLE tbl ON CLUSTER 'cluster' NO DELAY", @@ -218,22 +224,21 @@ def test_concurrent_restores_on_same_node(): ) assert status in ["RESTORING", "RESTORED"] - concurrent_error = nodes[0].query_and_get_error( + error = nodes[0].query_and_get_error( f"RESTORE TABLE tbl ON CLUSTER 'cluster' FROM {backup_name}" ) - expected_errors = [ "Concurrent restores not supported", "Cannot restore the table default.tbl because it already contains some data", ] - assert any( - [expected_error in concurrent_error for expected_error in expected_errors] - ) + assert any([expected_error in error for expected_error in expected_errors]) assert_eq_with_retry( nodes[0], f"SELECT status FROM system.backups WHERE id == '{restore_id}'", "RESTORED", + sleep_time=2, + retry_count=50, ) @@ -242,22 +247,7 @@ def test_concurrent_restores_on_different_node(): backup_name = new_backup_name() - id = ( - nodes[0] - .query(f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name} ASYNC") - .split("\t")[0] - ) - assert_eq_with_retry( - nodes[0], - f"SELECT status FROM system.backups WHERE status == 'CREATING_BACKUP' AND id = '{id}'", - "CREATING_BACKUP", - ) - - assert_eq_with_retry( - nodes[0], - f"SELECT status FROM system.backups WHERE status == 'BACKUP_CREATED' AND id = '{id}'", - "BACKUP_CREATED", - ) + nodes[0].query(f"BACKUP TABLE tbl ON CLUSTER 'cluster' TO {backup_name}") nodes[0].query( f"DROP TABLE tbl ON CLUSTER 'cluster' NO DELAY", @@ -279,20 +269,19 @@ def test_concurrent_restores_on_different_node(): ) assert status in ["RESTORING", "RESTORED"] - concurrent_error = nodes[1].query_and_get_error( + error = nodes[1].query_and_get_error( f"RESTORE TABLE tbl ON CLUSTER 'cluster' FROM {backup_name}" ) - expected_errors = [ "Concurrent restores not supported", "Cannot restore the table default.tbl because it already contains some data", ] - assert any( - [expected_error in concurrent_error for expected_error in expected_errors] - ) + assert any([expected_error in error for expected_error in expected_errors]) assert_eq_with_retry( nodes[0], f"SELECT status FROM system.backups WHERE id == '{restore_id}'", "RESTORED", + sleep_time=2, + retry_count=50, ) From 46a7b73608e9af8081115fe238ded2711b12c5ae Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Wed, 29 Mar 2023 15:25:39 -0300 Subject: [PATCH 200/377] Update type-conversion-functions.md --- .../functions/type-conversion-functions.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 73156898d4d..07e06cb43a9 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -13,6 +13,21 @@ incompatible datatypes (for example from `String` to `Int`). Make sure to check ClickHouse generally uses the [same behavior as C++ programs](https://en.cppreference.com/w/cpp/language/implicit_conversion). +`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. + +Example: + +```sql +SELECT + toTypeName(toLowCardinality('') AS val) AS source_type, + toTypeName(toString(val)) AS to_type_result_type, + toTypeName(CAST(val, 'String')) AS cast_result_type + +┌─source_type────────────┬─to_type_result_type────┬─cast_result_type─┐ +│ LowCardinality(String) │ LowCardinality(String) │ String │ +└────────────────────────┴────────────────────────┴──────────────────┘ +``` + ## toInt(8\|16\|32\|64\|128\|256) Converts an input value to a value the [Int](/docs/en/sql-reference/data-types/int-uint.md) data type. This function family includes: From 12801f736bdd73dff0c2161825caf51138af7304 Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Wed, 29 Mar 2023 15:33:07 -0300 Subject: [PATCH 201/377] Update type-conversion-functions.md --- .../functions/type-conversion-functions.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 07e06cb43a9..0f1f6976cc8 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -13,7 +13,7 @@ incompatible datatypes (for example from `String` to `Int`). Make sure to check ClickHouse generally uses the [same behavior as C++ programs](https://en.cppreference.com/w/cpp/language/implicit_conversion). -`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. +`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable). Example: @@ -26,6 +26,15 @@ SELECT ┌─source_type────────────┬─to_type_result_type────┬─cast_result_type─┐ │ LowCardinality(String) │ LowCardinality(String) │ String │ └────────────────────────┴────────────────────────┴──────────────────┘ + +SELECT + toTypeName(toNullable('') AS val) AS source_type, + toTypeName(toString(val)) AS to_type_result_type, + toTypeName(CAST(val, 'String')) AS cast_result_type + +┌─source_type──────┬─to_type_result_type─┬─cast_result_type─┐ +│ Nullable(String) │ Nullable(String) │ String │ +└──────────────────┴─────────────────────┴──────────────────┘ ``` ## toInt(8\|16\|32\|64\|128\|256) From 2349fadbe7cb35c3c28a00048212d9f8994fee43 Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Wed, 29 Mar 2023 15:33:53 -0300 Subject: [PATCH 202/377] Update type-conversion-functions.md --- docs/en/sql-reference/functions/type-conversion-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 0f1f6976cc8..0c25d25be37 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -13,7 +13,7 @@ incompatible datatypes (for example from `String` to `Int`). Make sure to check ClickHouse generally uses the [same behavior as C++ programs](https://en.cppreference.com/w/cpp/language/implicit_conversion). -`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable). +`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md). Example: From 4240bb8527142bf630f3435552241c30dada7c85 Mon Sep 17 00:00:00 2001 From: Nikolay Degterinsky Date: Wed, 29 Mar 2023 19:41:09 +0000 Subject: [PATCH 203/377] Fix test --- tests/queries/0_stateless/02668_ulid_decoding.reference | 2 +- tests/queries/0_stateless/02668_ulid_decoding.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/02668_ulid_decoding.reference b/tests/queries/0_stateless/02668_ulid_decoding.reference index 57af48199b4..b48580d60bb 100644 --- a/tests/queries/0_stateless/02668_ulid_decoding.reference +++ b/tests/queries/0_stateless/02668_ulid_decoding.reference @@ -1,3 +1,3 @@ 1 -2023-03-28 01:16:44.000 +2023-03-27 19:16:44.000 2023-03-27 19:16:44.000 diff --git a/tests/queries/0_stateless/02668_ulid_decoding.sql b/tests/queries/0_stateless/02668_ulid_decoding.sql index 62c4a7d4dbe..296d2b7ce32 100644 --- a/tests/queries/0_stateless/02668_ulid_decoding.sql +++ b/tests/queries/0_stateless/02668_ulid_decoding.sql @@ -1,7 +1,7 @@ -- Tags: no-fasttest SELECT dateDiff('s', ULIDStringToDateTime(generateULID()), now()) = 0; -SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E'); +SELECT toTimezone(ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E'), 'America/Costa_Rica'); SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E', 'America/Costa_Rica'); SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9', 'America/Costa_Rica'); -- { serverError ILLEGAL_COLUMN } SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E', 'America/Costa_Ric'); -- { serverError POCO_EXCEPTION } From 98d098618b88206a9ee5842acc7bd613b398b9a9 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Wed, 29 Mar 2023 14:34:48 -0600 Subject: [PATCH 204/377] Add cross_to_inner_join_rewrite setting --- docs/en/sql-reference/statements/select/join.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/en/sql-reference/statements/select/join.md b/docs/en/sql-reference/statements/select/join.md index 49bd2672874..ece60961aaf 100644 --- a/docs/en/sql-reference/statements/select/join.md +++ b/docs/en/sql-reference/statements/select/join.md @@ -47,6 +47,7 @@ The default join type can be overridden using [join_default_strictness](../../.. The behavior of ClickHouse server for `ANY JOIN` operations depends on the [any_join_distinct_right_table_keys](../../../operations/settings/settings.md#any_join_distinct_right_table_keys) setting. + **See also** - [join_algorithm](../../../operations/settings/settings.md#settings-join_algorithm) @@ -57,6 +58,8 @@ The behavior of ClickHouse server for `ANY JOIN` operations depends on the [any_ - [join_on_disk_max_files_to_merge](../../../operations/settings/settings.md#join_on_disk_max_files_to_merge) - [any_join_distinct_right_table_keys](../../../operations/settings/settings.md#any_join_distinct_right_table_keys) +Use the `cross_to_inner_join_rewrite` setting to define the behavior when ClickHouse fails to rewrite a `CROSS JOIN` as an `INNER JOIN`. The default value is `1`, which allows the join to continue but it will be slower. Set `cross_to_inner_join_rewrite` to `0` if you want an error to be thrown, and set it to `2` to not run the cross joins but instead force a rewrite of all comma/cross joins. If the rewriting fails when the value is `2`, you will receive an error message stating "Please, try to simplify `WHERE` section". + ## ON Section Conditions An `ON` section can contain several conditions combined using the `AND` and `OR` operators. Conditions specifying join keys must refer both left and right tables and must use the equality operator. Other conditions may use other logical operators but they must refer either the left or the right table of a query. From cc02b1232d92fb5af3334ef0e92dc858895ac4cb Mon Sep 17 00:00:00 2001 From: avogar Date: Wed, 29 Mar 2023 20:34:55 +0000 Subject: [PATCH 205/377] Use uniq file names in 02149_* tests to avoid SIGBUS in stress tests --- tests/queries/0_stateless/02149_external_schema_inference.sh | 2 +- tests/queries/0_stateless/02149_schema_inference.sh | 2 +- .../0_stateless/02149_schema_inference_formats_with_schema.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/queries/0_stateless/02149_external_schema_inference.sh b/tests/queries/0_stateless/02149_external_schema_inference.sh index df2b9a43565..5e03120c80f 100755 --- a/tests/queries/0_stateless/02149_external_schema_inference.sh +++ b/tests/queries/0_stateless/02149_external_schema_inference.sh @@ -7,7 +7,7 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) USER_FILES_PATH=$(clickhouse-client --query "select _path,_file from file('nonexist.txt', 'CSV', 'val1 char')" 2>&1 | grep Exception | awk '{gsub("/nonexist.txt","",$9); print $9}') -FILE_NAME=test_02149.data +FILE_NAME=test_$CLICKHOUSE_TEST_UNIQUE_NAME.data DATA_FILE=$USER_FILES_PATH/$FILE_NAME touch $DATA_FILE diff --git a/tests/queries/0_stateless/02149_schema_inference.sh b/tests/queries/0_stateless/02149_schema_inference.sh index 1ccec240627..79b26f5b3f2 100755 --- a/tests/queries/0_stateless/02149_schema_inference.sh +++ b/tests/queries/0_stateless/02149_schema_inference.sh @@ -7,7 +7,7 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) USER_FILES_PATH=$(clickhouse-client --query "select _path,_file from file('nonexist.txt', 'CSV', 'val1 char')" 2>&1 | grep Exception | awk '{gsub("/nonexist.txt","",$9); print $9}') -FILE_NAME=test_02149.data +FILE_NAME=test_$CLICKHOUSE_TEST_UNIQUE_NAME.data DATA_FILE=${USER_FILES_PATH:?}/$FILE_NAME touch $DATA_FILE diff --git a/tests/queries/0_stateless/02149_schema_inference_formats_with_schema.sh b/tests/queries/0_stateless/02149_schema_inference_formats_with_schema.sh index d263ef63681..cf5a086fb5e 100755 --- a/tests/queries/0_stateless/02149_schema_inference_formats_with_schema.sh +++ b/tests/queries/0_stateless/02149_schema_inference_formats_with_schema.sh @@ -7,7 +7,7 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) USER_FILES_PATH=$(clickhouse-client --query "select _path,_file from file('nonexist.txt', 'CSV', 'val1 char')" 2>&1 | grep Exception | awk '{gsub("/nonexist.txt","",$9); print $9}') -FILE_NAME=test_02149.data +FILE_NAME=test_$CLICKHOUSE_TEST_UNIQUE_NAME.data DATA_FILE=$USER_FILES_PATH/$FILE_NAME for format in Arrow ArrowStream Parquet ORC Native TSVWithNamesAndTypes TSVRawWithNamesAndTypes CSVWithNamesAndTypes JSONCompactEachRowWithNamesAndTypes JSONCompactStringsEachRowWithNamesAndTypes RowBinaryWithNamesAndTypes CustomSeparatedWithNamesAndTypes From cd5852f11a7611fb2199bbfc6adcbdbecd9a6287 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 9 Feb 2023 13:11:33 +0100 Subject: [PATCH 206/377] Add typing and rewrite packager to pathlib.Path --- docker/packager/packager | 47 +++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/docker/packager/packager b/docker/packager/packager index 58dd299fd6d..6a5b6cabce1 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -5,13 +5,14 @@ import os import argparse import logging import sys +from pathlib import Path from typing import List -SCRIPT_PATH = os.path.realpath(__file__) +SCRIPT_PATH = Path(__file__).absolute() IMAGE_TYPE = "binary" -def check_image_exists_locally(image_name): +def check_image_exists_locally(image_name: str) -> bool: try: output = subprocess.check_output( f"docker images -q {image_name} 2> /dev/null", shell=True @@ -21,7 +22,7 @@ def check_image_exists_locally(image_name): return False -def pull_image(image_name): +def pull_image(image_name: str) -> bool: try: subprocess.check_call(f"docker pull {image_name}", shell=True) return True @@ -30,8 +31,8 @@ def pull_image(image_name): return False -def build_image(image_name, filepath): - context = os.path.dirname(filepath) +def build_image(image_name: str, filepath: Path) -> None: + context = filepath.parent build_cmd = f"docker build --network=host -t {image_name} -f {filepath} {context}" logging.info("Will build image with cmd: '%s'", build_cmd) subprocess.check_call( @@ -40,7 +41,7 @@ def build_image(image_name, filepath): ) -def pre_build(repo_path: str, env_variables: List[str]): +def pre_build(repo_path: Path, env_variables: List[str]): if "WITH_PERFORMANCE=1" in env_variables: current_branch = subprocess.check_output( "git branch --show-current", shell=True, encoding="utf-8" @@ -67,14 +68,15 @@ def pre_build(repo_path: str, env_variables: List[str]): def run_docker_image_with_env( - image_name, - as_root, - output, - env_variables, - ch_root, - ccache_dir, - docker_image_version, + image_name: str, + as_root: bool, + output_dir: Path, + env_variables: List[str], + ch_root: Path, + ccache_dir: Path, + docker_image_version: str, ): + output_dir.mkdir(parents=True, exist_ok=True) env_part = " -e ".join(env_variables) if env_part: env_part = " -e " + env_part @@ -90,7 +92,7 @@ def run_docker_image_with_env( user = f"{os.geteuid()}:{os.getegid()}" cmd = ( - f"docker run --network=host --user={user} --rm --volume={output}:/output " + f"docker run --network=host --user={user} --rm --volume={output_dir}:/output " f"--volume={ch_root}:/build --volume={ccache_dir}:/ccache {env_part} " f"{interactive} {image_name}:{docker_image_version}" ) @@ -100,7 +102,7 @@ def run_docker_image_with_env( subprocess.check_call(cmd, shell=True) -def is_release_build(build_type, package_type, sanitizer): +def is_release_build(build_type: str, package_type: str, sanitizer: str) -> bool: return build_type == "" and package_type == "deb" and sanitizer == "" @@ -312,10 +314,11 @@ def parse_env_variables( return result -def dir_name(name: str) -> str: - if not os.path.isabs(name): - name = os.path.abspath(os.path.join(os.getcwd(), name)) - return name +def dir_name(name: str) -> Path: + path = Path(name) + if not path.is_absolute(): + path = Path.cwd() / name + return path if __name__ == "__main__": @@ -331,7 +334,7 @@ if __name__ == "__main__": ) parser.add_argument( "--clickhouse-repo-path", - default=os.path.join(os.path.dirname(SCRIPT_PATH), os.pardir, os.pardir), + default=SCRIPT_PATH.parents[2], type=dir_name, help="ClickHouse git repository", ) @@ -364,7 +367,7 @@ if __name__ == "__main__": parser.add_argument("--cache", choices=("ccache", "distcc", ""), default="") parser.add_argument( "--ccache_dir", - default=os.getenv("HOME", "") + "/.ccache", + default=Path.home() / ".ccache", type=dir_name, help="a directory with ccache", ) @@ -400,7 +403,7 @@ if __name__ == "__main__": if args.with_binaries != "" and args.package_type == "deb": logging.info("Should place %s to output", args.with_binaries) - dockerfile = os.path.join(ch_root, "docker/packager", IMAGE_TYPE, "Dockerfile") + dockerfile = ch_root / "docker/packager" / IMAGE_TYPE / "Dockerfile" image_with_version = image_name + ":" + args.docker_image_version if not check_image_exists_locally(image_name) or args.force_build_image: if not pull_image(image_with_version) or args.force_build_image: From 648697f085a6260713a7f86e375aa5f559bf680b Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 9 Feb 2023 13:25:56 +0100 Subject: [PATCH 207/377] Add main function, use specific exception in packager --- docker/packager/packager | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/docker/packager/packager b/docker/packager/packager index 6a5b6cabce1..a11673759e7 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -12,6 +12,10 @@ SCRIPT_PATH = Path(__file__).absolute() IMAGE_TYPE = "binary" +class BuildException(Exception): + pass + + def check_image_exists_locally(image_name: str) -> bool: try: output = subprocess.check_output( @@ -57,7 +61,9 @@ def pre_build(repo_path: Path, env_variables: List[str]): # conclusion is: in the current state the easiest way to go is to force # unshallow repository for performance artifacts. # To change it we need to rework our performance tests docker image - raise Exception("shallow repository is not suitable for performance builds") + raise BuildException( + "shallow repository is not suitable for performance builds" + ) if current_branch != "master": cmd = ( f"git -C {repo_path} fetch --no-recurse-submodules " @@ -321,7 +327,7 @@ def dir_name(name: str) -> Path: return path -if __name__ == "__main__": +def main(): logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s") parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, @@ -395,12 +401,11 @@ if __name__ == "__main__": ch_root = args.clickhouse_repo_path if args.additional_pkgs and args.package_type != "deb": - raise Exception("Can build additional packages only in deb build") + raise BuildException("Can build additional packages only in deb build") - if args.with_binaries != "" and args.package_type != "deb": - raise Exception("Can add additional binaries only in deb build") - - if args.with_binaries != "" and args.package_type == "deb": + if args.with_binaries != "": + if args.package_type != "deb": + raise BuildException("Can add additional binaries only in deb build") logging.info("Should place %s to output", args.with_binaries) dockerfile = ch_root / "docker/packager" / IMAGE_TYPE / "Dockerfile" @@ -435,3 +440,7 @@ if __name__ == "__main__": args.docker_image_version, ) logging.info("Output placed into %s", args.output_dir) + + +if __name__ == "__main__": + main() From c994c41e744f4cdf254e61df6b59f8e72c0e2a60 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 9 Feb 2023 13:44:38 +0100 Subject: [PATCH 208/377] Fix logging formatting --- docker/packager/packager | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/packager/packager b/docker/packager/packager index a11673759e7..d59aeea50af 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -31,7 +31,7 @@ def pull_image(image_name: str) -> bool: subprocess.check_call(f"docker pull {image_name}", shell=True) return True except subprocess.CalledProcessError: - logging.info(f"Cannot pull image {image_name}".format()) + logging.info("Cannot pull image %s", image_name) return False From 612d1d88762ebecd1f8269db8ba15a73b86d1c13 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 9 Feb 2023 16:43:24 +0100 Subject: [PATCH 209/377] Install sccache in binary-builder and fasttests --- docker/packager/binary/Dockerfile | 1 + docker/packager/binary/build.sh | 1 + docker/test/fasttest/Dockerfile | 6 ------ docker/test/fasttest/run.sh | 11 +++++++++-- docker/test/util/Dockerfile | 13 +++++++++++++ 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/docker/packager/binary/Dockerfile b/docker/packager/binary/Dockerfile index 62e6d47c183..bc2327b9d12 100644 --- a/docker/packager/binary/Dockerfile +++ b/docker/packager/binary/Dockerfile @@ -69,6 +69,7 @@ RUN add-apt-repository ppa:ubuntu-toolchain-r/test --yes \ libc6 \ libc6-dev \ libc6-dev-arm64-cross \ + python3-boto3 \ yasm \ zstd \ && apt-get clean \ diff --git a/docker/packager/binary/build.sh b/docker/packager/binary/build.sh index 24dca72e946..2cd0a011013 100755 --- a/docker/packager/binary/build.sh +++ b/docker/packager/binary/build.sh @@ -6,6 +6,7 @@ exec &> >(ts) ccache_status () { ccache --show-config ||: ccache --show-stats ||: + SCCACHE_NO_DAEMON=1 sccache --show-stats ||: } [ -O /build ] || git config --global --add safe.directory /build diff --git a/docker/test/fasttest/Dockerfile b/docker/test/fasttest/Dockerfile index 32546b71eb8..ffb13fc774d 100644 --- a/docker/test/fasttest/Dockerfile +++ b/docker/test/fasttest/Dockerfile @@ -20,12 +20,6 @@ RUN apt-get update \ zstd \ --yes --no-install-recommends -# Install CMake 3.20+ for Rust compilation -RUN apt purge cmake --yes -RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null -RUN apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' -RUN apt update && apt install cmake --yes - RUN pip3 install numpy scipy pandas Jinja2 ARG odbc_driver_url="https://github.com/ClickHouse/clickhouse-odbc/releases/download/v1.1.4.20200302/clickhouse-odbc-1.1.4-Linux.tar.gz" diff --git a/docker/test/fasttest/run.sh b/docker/test/fasttest/run.sh index 086276bed55..ac2eb5b403e 100755 --- a/docker/test/fasttest/run.sh +++ b/docker/test/fasttest/run.sh @@ -37,6 +37,13 @@ export FASTTEST_DATA export FASTTEST_OUT export PATH +function ccache_status +{ + ccache --show-config ||: + ccache --show-stats ||: + SCCACHE_NO_DAEMON=1 sccache --show-stats ||: +} + function start_server { set -m # Spawn server in its own process groups @@ -171,7 +178,7 @@ function run_cmake export CCACHE_COMPILERCHECK=content export CCACHE_MAXSIZE=15G - ccache --show-stats ||: + ccache_status ccache --zero-stats ||: mkdir "$FASTTEST_BUILD" ||: @@ -193,7 +200,7 @@ function build strip programs/clickhouse -o "$FASTTEST_OUTPUT/clickhouse-stripped" zstd --threads=0 "$FASTTEST_OUTPUT/clickhouse-stripped" fi - ccache --show-stats ||: + ccache_status ccache --evict-older-than 1d ||: ) } diff --git a/docker/test/util/Dockerfile b/docker/test/util/Dockerfile index 0ee426f4e4d..911cadc3c58 100644 --- a/docker/test/util/Dockerfile +++ b/docker/test/util/Dockerfile @@ -92,4 +92,17 @@ RUN mkdir /tmp/ccache \ && cd / \ && rm -rf /tmp/ccache +ARG TARGETARCH +ARG SCCACHE_VERSION=v0.4.1 +RUN arch=${TARGETARCH:-amd64} \ + && case $arch in \ + amd64) rarch=x86_64 ;; \ + arm64) rarch=aarch64 ;; \ + esac \ + && curl -Ls "https://github.com/mozilla/sccache/releases/download/$SCCACHE_VERSION/sccache-$SCCACHE_VERSION-$rarch-unknown-linux-musl.tar.gz" | \ + tar xz -C /tmp \ + && mv "/tmp/sccache-$SCCACHE_VERSION-$rarch-unknown-linux-musl/sccache" /usr/bin \ + && rm "/tmp/sccache-$SCCACHE_VERSION-$rarch-unknown-linux-musl" -r + + COPY process_functional_tests_result.py / From e1531688bc049c654c5e59eeec0ebbc00ddfaab7 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 9 Feb 2023 16:45:57 +0100 Subject: [PATCH 210/377] Add sccache to cmake configuration --- cmake/ccache.cmake | 64 +++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/cmake/ccache.cmake b/cmake/ccache.cmake index f0769f337d0..6103a5212e3 100644 --- a/cmake/ccache.cmake +++ b/cmake/ccache.cmake @@ -8,45 +8,57 @@ if (CMAKE_CXX_COMPILER_LAUNCHER MATCHES "ccache" OR CMAKE_C_COMPILER_LAUNCHER MA return() endif() -option(ENABLE_CCACHE "Speedup re-compilations using ccache (external tool)" ON) +option(ENABLE_CCACHE "Speedup re-compilations using ccache (default) or sccache (external tools)" ON) +option(FORCE_SCCACHE "Use sccache by default" OFF) -if (NOT ENABLE_CCACHE) - message(STATUS "Using ccache: no (disabled via configuration)") +if (NOT ENABLE_CCACHE AND NOT FORCE_SCCACHE) + message(STATUS "Using *ccache: no (disabled via configuration)") return() endif() -find_program (CCACHE_EXECUTABLE ccache) +# We support ccache and sccache +if (FORCE_SCCACHE) + find_program (CCACHE_EXECUTABLE sccache) +else() + find_program (CCACHE_EXECUTABLE ccache sccache) +endif() + if (NOT CCACHE_EXECUTABLE) - message(${RECONFIGURE_MESSAGE_LEVEL} "Using ccache: no (Could not find find ccache. To significantly reduce compile times for the 2nd, 3rd, etc. build, it is highly recommended to install ccache. To suppress this message, run cmake with -DENABLE_CCACHE=0)") + message(${RECONFIGURE_MESSAGE_LEVEL} "Using *ccache: no (Could not find find ccache or sccache. To significantly reduce compile times for the 2nd, 3rd, etc. build, it is highly recommended to install one of them. To suppress this message, run cmake with -DENABLE_CCACHE=0)") return() endif() -execute_process(COMMAND ${CCACHE_EXECUTABLE} "-V" OUTPUT_VARIABLE CCACHE_VERSION) -string(REGEX REPLACE "ccache version ([0-9\\.]+).*" "\\1" CCACHE_VERSION ${CCACHE_VERSION}) +if (CCACHE_EXECUTABLE MATCHES "/ccache$") + execute_process(COMMAND ${CCACHE_EXECUTABLE} "-V" OUTPUT_VARIABLE CCACHE_VERSION) + string(REGEX REPLACE "ccache version ([0-9\\.]+).*" "\\1" CCACHE_VERSION ${CCACHE_VERSION}) -set (CCACHE_MINIMUM_VERSION 3.3) + set (CCACHE_MINIMUM_VERSION 3.3) -if (CCACHE_VERSION VERSION_LESS_EQUAL ${CCACHE_MINIMUM_VERSION}) - message(${RECONFIGURE_MESSAGE_LEVEL} "Using ccache: no (found ${CCACHE_EXECUTABLE} (version ${CCACHE_VERSION}), the minimum required version is ${CCACHE_MINIMUM_VERSION}") - return() -endif() + if (CCACHE_VERSION VERSION_LESS_EQUAL ${CCACHE_MINIMUM_VERSION}) + message(${RECONFIGURE_MESSAGE_LEVEL} "Using ccache: no (found ${CCACHE_EXECUTABLE} (version ${CCACHE_VERSION}), the minimum required version is ${CCACHE_MINIMUM_VERSION}") + return() + endif() -message(STATUS "Using ccache: ${CCACHE_EXECUTABLE} (version ${CCACHE_VERSION})") -set(LAUNCHER ${CCACHE_EXECUTABLE}) + message(STATUS "Using ccache: ${CCACHE_EXECUTABLE} (version ${CCACHE_VERSION})") + set(LAUNCHER ${CCACHE_EXECUTABLE}) -# Work around a well-intended but unfortunate behavior of ccache 4.0 & 4.1 with -# environment variable SOURCE_DATE_EPOCH. This variable provides an alternative -# to source-code embedded timestamps (__DATE__/__TIME__) and therefore helps with -# reproducible builds (*). SOURCE_DATE_EPOCH is set automatically by the -# distribution, e.g. Debian. Ccache 4.0 & 4.1 incorporate SOURCE_DATE_EPOCH into -# the hash calculation regardless they contain timestamps or not. This invalidates -# the cache whenever SOURCE_DATE_EPOCH changes. As a fix, ignore SOURCE_DATE_EPOCH. -# -# (*) https://reproducible-builds.org/specs/source-date-epoch/ -if (CCACHE_VERSION VERSION_GREATER_EQUAL "4.0" AND CCACHE_VERSION VERSION_LESS "4.2") - message(STATUS "Ignore SOURCE_DATE_EPOCH for ccache 4.0 / 4.1") - set(LAUNCHER env -u SOURCE_DATE_EPOCH ${CCACHE_EXECUTABLE}) + # Work around a well-intended but unfortunate behavior of ccache 4.0 & 4.1 with + # environment variable SOURCE_DATE_EPOCH. This variable provides an alternative + # to source-code embedded timestamps (__DATE__/__TIME__) and therefore helps with + # reproducible builds (*). SOURCE_DATE_EPOCH is set automatically by the + # distribution, e.g. Debian. Ccache 4.0 & 4.1 incorporate SOURCE_DATE_EPOCH into + # the hash calculation regardless they contain timestamps or not. This invalidates + # the cache whenever SOURCE_DATE_EPOCH changes. As a fix, ignore SOURCE_DATE_EPOCH. + # + # (*) https://reproducible-builds.org/specs/source-date-epoch/ + if (CCACHE_VERSION VERSION_GREATER_EQUAL "4.0" AND CCACHE_VERSION VERSION_LESS "4.2") + message(STATUS "Ignore SOURCE_DATE_EPOCH for ccache 4.0 / 4.1") + set(LAUNCHER env -u SOURCE_DATE_EPOCH ${CCACHE_EXECUTABLE}) + endif() +elseif(CCACHE_EXECUTABLE MATCHES "/sccache$") + message(STATUS "Using sccache: ${CCACHE_EXECUTABLE}") + set(LAUNCHER ${CCACHE_EXECUTABLE}) endif() set (CMAKE_CXX_COMPILER_LAUNCHER ${LAUNCHER} ${CMAKE_CXX_COMPILER_LAUNCHER}) From 3b1291eb0ebf8740b715f572c15811b8ae61a7a9 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 9 Feb 2023 16:45:57 +0100 Subject: [PATCH 211/377] Replace distcc with sccache in packager --- docker/packager/packager | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/docker/packager/packager b/docker/packager/packager index d59aeea50af..41d093f4596 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -118,10 +118,8 @@ def parse_env_variables( sanitizer, package_type, cache, - distcc_hosts, clang_tidy, version, - author, official, additional_pkgs, with_coverage, @@ -251,10 +249,7 @@ def parse_env_variables( else: result.append("BUILD_TYPE=None") - if cache == "distcc": - result.append(f"CCACHE_PREFIX={cache}") - - if cache: + if cache == "ccache": result.append("CCACHE_DIR=/ccache") result.append("CCACHE_COMPRESSLEVEL=5") result.append("CCACHE_BASEDIR=/build") @@ -270,14 +265,6 @@ def parse_env_variables( result.append("CTCACHE_DIR=/ccache/clang-tidy-cache") result.append(f"CCACHE_MAXSIZE={cache_maxsize}") - if distcc_hosts: - hosts_with_params = [f"{host}/24,lzo" for host in distcc_hosts] + [ - "localhost/`nproc`" - ] - result.append('DISTCC_HOSTS="' + " ".join(hosts_with_params) + '"') - elif cache == "distcc": - result.append('DISTCC_HOSTS="localhost/`nproc`"') - if additional_pkgs: # NOTE: This are the env for packages/build script result.append("MAKE_APK=true") @@ -308,9 +295,6 @@ def parse_env_variables( if version: result.append(f"VERSION_STRING='{version}'") - if author: - result.append(f"AUTHOR='{author}'") - if official: cmake_flags.append("-DCLICKHOUSE_OFFICIAL_BUILD=1") @@ -370,17 +354,19 @@ def main(): ) parser.add_argument("--clang-tidy", action="store_true") - parser.add_argument("--cache", choices=("ccache", "distcc", ""), default="") + parser.add_argument("--cache", choices=("ccache", "sccache", ""), default="") parser.add_argument( "--ccache_dir", default=Path.home() / ".ccache", type=dir_name, help="a directory with ccache", ) - parser.add_argument("--distcc-hosts", nargs="+") + parser.add_argument( + "--s3-bucket", + help="a bucket with cache for clang-tidy-cache and sscache", + ) parser.add_argument("--force-build-image", action="store_true") parser.add_argument("--version") - parser.add_argument("--author", default="clickhouse", help="a package author") parser.add_argument("--official", action="store_true") parser.add_argument("--additional-pkgs", action="store_true") parser.add_argument("--with-coverage", action="store_true") @@ -419,10 +405,8 @@ def main(): args.sanitizer, args.package_type, args.cache, - args.distcc_hosts, args.clang_tidy, args.version, - args.author, args.official, args.additional_pkgs, args.with_coverage, From 419f234294d62eee6b282a5a47a13c41310525a6 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Mon, 13 Feb 2023 15:25:23 +0100 Subject: [PATCH 212/377] Fix logic for --force-build-image --- docker/packager/packager | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docker/packager/packager b/docker/packager/packager index 41d093f4596..d92a949a956 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -396,9 +396,11 @@ def main(): dockerfile = ch_root / "docker/packager" / IMAGE_TYPE / "Dockerfile" image_with_version = image_name + ":" + args.docker_image_version - if not check_image_exists_locally(image_name) or args.force_build_image: - if not pull_image(image_with_version) or args.force_build_image: - build_image(image_with_version, dockerfile) + if args.force_build_image: + build_image(image_with_version, dockerfile) + elif not (check_image_exists_locally(image_name) or pull_image(image_with_version)): + build_image(image_with_version, dockerfile) + env_prepared = parse_env_variables( args.build_type, args.compiler, From 2d872ff23b9c0952f738e1a90b96122d19f03721 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Tue, 28 Feb 2023 20:18:50 +0100 Subject: [PATCH 213/377] Add a function to parse arguments --- docker/packager/packager | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/docker/packager/packager b/docker/packager/packager index d92a949a956..2a259cde874 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -10,6 +10,7 @@ from typing import List SCRIPT_PATH = Path(__file__).absolute() IMAGE_TYPE = "binary" +IMAGE_NAME = f"clickhouse/{IMAGE_TYPE}-builder" class BuildException(Exception): @@ -80,7 +81,6 @@ def run_docker_image_with_env( env_variables: List[str], ch_root: Path, ccache_dir: Path, - docker_image_version: str, ): output_dir.mkdir(parents=True, exist_ok=True) env_part = " -e ".join(env_variables) @@ -100,7 +100,7 @@ def run_docker_image_with_env( cmd = ( f"docker run --network=host --user={user} --rm --volume={output_dir}:/output " f"--volume={ch_root}:/build --volume={ccache_dir}:/ccache {env_part} " - f"{interactive} {image_name}:{docker_image_version}" + f"{interactive} {image_name}" ) logging.info("Will build ClickHouse pkg with cmd: '%s'", cmd) @@ -311,8 +311,7 @@ def dir_name(name: str) -> Path: return path -def main(): - logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s") +def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description="ClickHouse building script using prebuilt Docker image", @@ -382,23 +381,34 @@ def main(): args = parser.parse_args() - image_name = f"clickhouse/{IMAGE_TYPE}-builder" - - ch_root = args.clickhouse_repo_path - if args.additional_pkgs and args.package_type != "deb": - raise BuildException("Can build additional packages only in deb build") + raise argparse.ArgumentTypeError( + "Can build additional packages only in deb build" + ) if args.with_binaries != "": if args.package_type != "deb": - raise BuildException("Can add additional binaries only in deb build") + raise argparse.ArgumentTypeError( + "Can add additional binaries only in deb build" + ) logging.info("Should place %s to output", args.with_binaries) + return args + + +def main(): + logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s") + args = parse_args() + + ch_root = args.clickhouse_repo_path + dockerfile = ch_root / "docker/packager" / IMAGE_TYPE / "Dockerfile" - image_with_version = image_name + ":" + args.docker_image_version + image_with_version = IMAGE_NAME + ":" + args.docker_image_version if args.force_build_image: build_image(image_with_version, dockerfile) - elif not (check_image_exists_locally(image_name) or pull_image(image_with_version)): + elif not ( + check_image_exists_locally(image_with_version) or pull_image(image_with_version) + ): build_image(image_with_version, dockerfile) env_prepared = parse_env_variables( @@ -417,13 +427,12 @@ def main(): pre_build(args.clickhouse_repo_path, env_prepared) run_docker_image_with_env( - image_name, + image_with_version, args.as_root, args.output_dir, env_prepared, ch_root, args.ccache_dir, - args.docker_image_version, ) logging.info("Output placed into %s", args.output_dir) From df17a354103113e17be7f94971a7e5b5d09acf88 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Tue, 21 Mar 2023 16:39:21 +0100 Subject: [PATCH 214/377] Get rid of useless cache_maxsize parameter --- docker/packager/packager | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docker/packager/packager b/docker/packager/packager index 2a259cde874..fdde5ae8bc3 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -255,15 +255,11 @@ def parse_env_variables( result.append("CCACHE_BASEDIR=/build") result.append("CCACHE_NOHASHDIR=true") result.append("CCACHE_COMPILERCHECK=content") - cache_maxsize = "15G" + result.append("CCACHE_MAXSIZE=15G") if clang_tidy: - # 15G is not enough for tidy build - cache_maxsize = "25G" - # `CTCACHE_DIR` has the same purpose as the `CCACHE_DIR` above. # It's there to have the clang-tidy cache embedded into our standard `CCACHE_DIR` result.append("CTCACHE_DIR=/ccache/clang-tidy-cache") - result.append(f"CCACHE_MAXSIZE={cache_maxsize}") if additional_pkgs: # NOTE: This are the env for packages/build script From 6d8da8f88bad64bb3437b2cbb0570719ba3a8c8e Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 23 Mar 2023 12:43:05 +0100 Subject: [PATCH 215/377] Add sccache and ctcache S3 parameters, don't use --- docker/packager/binary/Dockerfile | 4 +- docker/packager/packager | 79 ++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/docker/packager/binary/Dockerfile b/docker/packager/binary/Dockerfile index bc2327b9d12..fa860b2207f 100644 --- a/docker/packager/binary/Dockerfile +++ b/docker/packager/binary/Dockerfile @@ -76,7 +76,7 @@ RUN add-apt-repository ppa:ubuntu-toolchain-r/test --yes \ && rm -rf /var/lib/apt/lists # Download toolchain and SDK for Darwin -RUN wget -nv https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX11.0.sdk.tar.xz +RUN curl -sL -O https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX11.0.sdk.tar.xz # Architecture of the image when BuildKit/buildx is used ARG TARGETARCH @@ -98,7 +98,7 @@ ENV PATH="$PATH:/usr/local/go/bin" ENV GOPATH=/workdir/go ENV GOCACHE=/workdir/ -ARG CLANG_TIDY_SHA1=03644275e794b0587849bfc2ec6123d5ae0bdb1c +ARG CLANG_TIDY_SHA1=c191254ea00d47ade11d7170ef82fe038c213774 RUN curl -Lo /usr/bin/clang-tidy-cache \ "https://raw.githubusercontent.com/matus-chochlik/ctcache/$CLANG_TIDY_SHA1/clang-tidy-cache" \ && chmod +x /usr/bin/clang-tidy-cache diff --git a/docker/packager/packager b/docker/packager/packager index fdde5ae8bc3..7df9ebe04ec 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -113,17 +113,20 @@ def is_release_build(build_type: str, package_type: str, sanitizer: str) -> bool def parse_env_variables( - build_type, - compiler, - sanitizer, - package_type, - cache, - clang_tidy, - version, - official, - additional_pkgs, - with_coverage, - with_binaries, + build_type: str, + compiler: str, + sanitizer: str, + package_type: str, + cache: str, + s3_bucket: str, + s3_directory: str, + s3_rw_access: bool, + clang_tidy: bool, + version: str, + official: bool, + additional_pkgs: bool, + with_coverage: bool, + with_binaries: str, ): DARWIN_SUFFIX = "-darwin" DARWIN_ARM_SUFFIX = "-darwin-aarch64" @@ -256,10 +259,30 @@ def parse_env_variables( result.append("CCACHE_NOHASHDIR=true") result.append("CCACHE_COMPILERCHECK=content") result.append("CCACHE_MAXSIZE=15G") - if clang_tidy: - # `CTCACHE_DIR` has the same purpose as the `CCACHE_DIR` above. - # It's there to have the clang-tidy cache embedded into our standard `CCACHE_DIR` - result.append("CTCACHE_DIR=/ccache/clang-tidy-cache") + + if cache == "sccache": + # see https://github.com/mozilla/sccache/blob/main/docs/S3.md + result.append(f"SCCACHE_BUCKET={s3_bucket}") + sccache_dir = "sccache" + if s3_directory: + sccache_dir = f"{s3_directory}/{sccache_dir}" + result.append(f"SCCACHE_S3_KEY_PREFIX={sccache_dir}") + if not s3_rw_access: + result.append("SCCACHE_S3_NO_CREDENTIALS=true") + + if clang_tidy: + # `CTCACHE_DIR` has the same purpose as the `CCACHE_DIR` above. + # It's there to have the clang-tidy cache embedded into our standard `CCACHE_DIR` + result.append("CTCACHE_DIR=/ccache/clang-tidy-cache") + if s3_bucket: + # see https://github.com/matus-chochlik/ctcache#environment-variables + ctcache_dir = "clang-tidy-cache" + if s3_directory: + ctcache_dir = f"{s3_directory}/{ctcache_dir}" + result.append(f"CTCACHE_S3_BUCKET={s3_bucket}") + result.append(f"CTCACHE_S3_FOLDER={ctcache_dir}") + if not s3_rw_access: + result.append("CTCACHE_S3_NO_CREDENTIALS=true") if additional_pkgs: # NOTE: This are the env for packages/build script @@ -349,7 +372,12 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument("--clang-tidy", action="store_true") - parser.add_argument("--cache", choices=("ccache", "sccache", ""), default="") + parser.add_argument( + "--cache", + choices=("ccache", "sccache", ""), + default="", + help="ccache or sccache for objects caching; sccache uses only S3 buckets", + ) parser.add_argument( "--ccache_dir", default=Path.home() / ".ccache", @@ -358,7 +386,17 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument( "--s3-bucket", - help="a bucket with cache for clang-tidy-cache and sscache", + help="an S3 bucket used for sscache and clang-tidy-cache", + ) + parser.add_argument( + "--s3-directory", + default="ccache", + help="an S3 directory prefix used for sscache and clang-tidy-cache", + ) + parser.add_argument( + "--s3-rw-access", + action="store_true", + help="if set, the build fails on errors writing cache to S3", ) parser.add_argument("--force-build-image", action="store_true") parser.add_argument("--version") @@ -389,6 +427,10 @@ def parse_args() -> argparse.Namespace: ) logging.info("Should place %s to output", args.with_binaries) + if args.cache == "sccache": + if not args.s3_bucket: + raise argparse.ArgumentTypeError("sccache must have --s3-bucket set") + return args @@ -413,6 +455,9 @@ def main(): args.sanitizer, args.package_type, args.cache, + args.s3_bucket, + args.s3_directory, + args.s3_rw_access, args.clang_tidy, args.version, args.official, From fcc14ae72429d9ed8b004f2d6e2edd8aa6cac642 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Fri, 24 Mar 2023 17:24:21 +0100 Subject: [PATCH 216/377] Enable s3 caching --- docker/packager/packager | 23 +++++++++++++++++++---- tests/ci/build_check.py | 31 +++---------------------------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/docker/packager/packager b/docker/packager/packager index 7df9ebe04ec..8acbf0669b5 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -6,7 +6,7 @@ import argparse import logging import sys from pathlib import Path -from typing import List +from typing import List, Optional SCRIPT_PATH = Path(__file__).absolute() IMAGE_TYPE = "binary" @@ -80,7 +80,7 @@ def run_docker_image_with_env( output_dir: Path, env_variables: List[str], ch_root: Path, - ccache_dir: Path, + ccache_dir: Optional[Path], ): output_dir.mkdir(parents=True, exist_ok=True) env_part = " -e ".join(env_variables) @@ -97,9 +97,13 @@ def run_docker_image_with_env( else: user = f"{os.geteuid()}:{os.getegid()}" + ccache_mount = f"--volume={ccache_dir}:/ccache" + if ccache_dir is None: + ccache_mount = "" + cmd = ( - f"docker run --network=host --user={user} --rm --volume={output_dir}:/output " - f"--volume={ch_root}:/build --volume={ccache_dir}:/ccache {env_part} " + f"docker run --network=host --user={user} --rm {ccache_mount}" + f"--volume={output_dir}:/output --volume={ch_root}:/build {env_part} " f"{interactive} {image_name}" ) @@ -252,7 +256,13 @@ def parse_env_variables( else: result.append("BUILD_TYPE=None") + if not cache: + cmake_flags.append("-DENABLE_CCACHE=OFF") + cmake_flags.append("-DFORCE_SCCACHE=OFF") + if cache == "ccache": + cmake_flags.append("-DENABLE_CCACHE=ON") + cmake_flags.append("-DFORCE_SCCACHE=OFF") result.append("CCACHE_DIR=/ccache") result.append("CCACHE_COMPRESSLEVEL=5") result.append("CCACHE_BASEDIR=/build") @@ -261,6 +271,8 @@ def parse_env_variables( result.append("CCACHE_MAXSIZE=15G") if cache == "sccache": + cmake_flags.append("-DENABLE_CCACHE=OFF") + cmake_flags.append("-DFORCE_SCCACHE=ON") # see https://github.com/mozilla/sccache/blob/main/docs/S3.md result.append(f"SCCACHE_BUCKET={s3_bucket}") sccache_dir = "sccache" @@ -420,6 +432,9 @@ def parse_args() -> argparse.Namespace: "Can build additional packages only in deb build" ) + if args.cache != "ccache": + args.ccache_dir = None + if args.with_binaries != "": if args.package_type != "deb": raise argparse.ArgumentTypeError( diff --git a/tests/ci/build_check.py b/tests/ci/build_check.py index 4db601be803..a829069985d 100644 --- a/tests/ci/build_check.py +++ b/tests/ci/build_check.py @@ -6,15 +6,12 @@ import json import os import sys import time -from shutil import rmtree from typing import List, Tuple -from ccache_utils import get_ccache_if_not_exists, upload_ccache from ci_config import CI_CONFIG, BuildConfig from commit_status_helper import get_commit_filtered_statuses, get_commit from docker_pull_helper import get_image_with_version from env_helper import ( - CACHES_PATH, GITHUB_JOB, IMAGES_PATH, REPO_COPY, @@ -54,7 +51,6 @@ def get_packager_cmd( output_path: str, build_version: str, image_version: str, - ccache_path: str, official: bool, ) -> str: package_type = build_config["package_type"] @@ -72,8 +68,9 @@ def get_packager_cmd( if build_config["tidy"] == "enable": cmd += " --clang-tidy" - cmd += " --cache=ccache" - cmd += f" --ccache_dir={ccache_path}" + cmd += " --cache=sccache" + cmd += " --s3-rw-access" + cmd += f" --s3-bucket={S3_BUILDS_BUCKET}" if "additional_pkgs" in build_config and build_config["additional_pkgs"]: cmd += " --additional-pkgs" @@ -314,29 +311,12 @@ def main(): if not os.path.exists(build_output_path): os.makedirs(build_output_path) - ccache_path = os.path.join(CACHES_PATH, build_name + "_ccache") - - logging.info("Will try to fetch cache for our build") - try: - get_ccache_if_not_exists( - ccache_path, s3_helper, pr_info.number, TEMP_PATH, pr_info.release_pr - ) - except Exception as e: - # In case there are issues with ccache, remove the path and do not fail a build - logging.info("Failed to get ccache, building without it. Error: %s", e) - rmtree(ccache_path, ignore_errors=True) - - if not os.path.exists(ccache_path): - logging.info("cache was not fetched, will create empty dir") - os.makedirs(ccache_path) - packager_cmd = get_packager_cmd( build_config, os.path.join(REPO_COPY, "docker/packager"), build_output_path, version.string, image_version, - ccache_path, official_flag, ) @@ -352,13 +332,8 @@ def main(): subprocess.check_call( f"sudo chown -R ubuntu:ubuntu {build_output_path}", shell=True ) - subprocess.check_call(f"sudo chown -R ubuntu:ubuntu {ccache_path}", shell=True) logging.info("Build finished with %s, log path %s", success, log_path) - # Upload the ccache first to have the least build time in case of problems - logging.info("Will upload cache") - upload_ccache(ccache_path, s3_helper, pr_info.number, TEMP_PATH) - # FIXME performance performance_urls = [] performance_path = os.path.join(build_output_path, "performance.tar.zst") From f1b1f307645a7399a61db560bde855649b175c30 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Mon, 27 Mar 2023 09:14:27 +0200 Subject: [PATCH 217/377] Make --ccache-dir equal to --ccache_dir --- docker/packager/packager | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/packager/packager b/docker/packager/packager index 8acbf0669b5..2a64721d5de 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -391,7 +391,7 @@ def parse_args() -> argparse.Namespace: help="ccache or sccache for objects caching; sccache uses only S3 buckets", ) parser.add_argument( - "--ccache_dir", + "--ccache-dir", default=Path.home() / ".ccache", type=dir_name, help="a directory with ccache", From 79177e198872343b8fbd4324971529d951f7f623 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Mon, 27 Mar 2023 15:35:51 +0200 Subject: [PATCH 218/377] Enable sccache in fasttests --- docker/test/fasttest/run.sh | 5 +++-- tests/ci/fast_test_check.py | 40 +++++++------------------------------ 2 files changed, 10 insertions(+), 35 deletions(-) diff --git a/docker/test/fasttest/run.sh b/docker/test/fasttest/run.sh index ac2eb5b403e..e0e30a63bb4 100755 --- a/docker/test/fasttest/run.sh +++ b/docker/test/fasttest/run.sh @@ -16,7 +16,8 @@ export LLVM_VERSION=${LLVM_VERSION:-13} # it being undefined. Also read it as array so that we can pass an empty list # of additional variable to cmake properly, and it doesn't generate an extra # empty parameter. -read -ra FASTTEST_CMAKE_FLAGS <<< "${FASTTEST_CMAKE_FLAGS:-}" +# Read it as CMAKE_FLAGS to not lose exported FASTTEST_CMAKE_FLAGS on subsequential launch +read -ra CMAKE_FLAGS <<< "${FASTTEST_CMAKE_FLAGS:-}" # Run only matching tests. FASTTEST_FOCUS=${FASTTEST_FOCUS:-""} @@ -185,7 +186,7 @@ function run_cmake ( cd "$FASTTEST_BUILD" - cmake "$FASTTEST_SOURCE" -DCMAKE_CXX_COMPILER="clang++-${LLVM_VERSION}" -DCMAKE_C_COMPILER="clang-${LLVM_VERSION}" "${CMAKE_LIBS_CONFIG[@]}" "${FASTTEST_CMAKE_FLAGS[@]}" 2>&1 | ts '%Y-%m-%d %H:%M:%S' | tee "$FASTTEST_OUTPUT/cmake_log.txt" + cmake "$FASTTEST_SOURCE" -DCMAKE_CXX_COMPILER="clang++-${LLVM_VERSION}" -DCMAKE_C_COMPILER="clang-${LLVM_VERSION}" "${CMAKE_LIBS_CONFIG[@]}" "${CMAKE_FLAGS[@]}" 2>&1 | ts '%Y-%m-%d %H:%M:%S' | tee "$FASTTEST_OUTPUT/cmake_log.txt" ) } diff --git a/tests/ci/fast_test_check.py b/tests/ci/fast_test_check.py index a5bb64889d1..9981234f87c 100644 --- a/tests/ci/fast_test_check.py +++ b/tests/ci/fast_test_check.py @@ -11,7 +11,6 @@ from typing import List, Tuple from github import Github -from ccache_utils import get_ccache_if_not_exists, upload_ccache from clickhouse_helper import ( ClickHouseHelper, mark_flaky_tests, @@ -22,7 +21,7 @@ from commit_status_helper import ( update_mergeable_check, ) from docker_pull_helper import get_image_with_version -from env_helper import CACHES_PATH, TEMP_PATH +from env_helper import S3_BUILDS_BUCKET, TEMP_PATH from get_robot_token import get_best_robot_token from pr_info import FORCE_TESTS_LABEL, PRInfo from report import TestResults, read_test_results @@ -38,24 +37,22 @@ NAME = "Fast test" csv.field_size_limit(sys.maxsize) -def get_fasttest_cmd( - workspace, output_path, ccache_path, repo_path, pr_number, commit_sha, image -): +def get_fasttest_cmd(workspace, output_path, repo_path, pr_number, commit_sha, image): return ( f"docker run --cap-add=SYS_PTRACE " + "--network=host " # required to get access to IAM credentials f"-e FASTTEST_WORKSPACE=/fasttest-workspace -e FASTTEST_OUTPUT=/test_output " f"-e FASTTEST_SOURCE=/ClickHouse --cap-add=SYS_PTRACE " + f"-e FASTTEST_CMAKE_FLAGS='-DENABLE_CCACHE=OFF -DFORCE_SCCACHE=ON' " f"-e PULL_REQUEST_NUMBER={pr_number} -e COMMIT_SHA={commit_sha} " f"-e COPY_CLICKHOUSE_BINARY_TO_OUTPUT=1 " + f"-e SCCACHE_BUCKET={S3_BUILDS_BUCKET} -e SCCACHE_S3_KEY_PREFIX=ccache/sccache " f"--volume={workspace}:/fasttest-workspace --volume={repo_path}:/ClickHouse " - f"--volume={output_path}:/test_output " - f"--volume={ccache_path}:/fasttest-workspace/ccache {image}" + f"--volume={output_path}:/test_output {image}" ) -def process_results( - result_folder: str, -) -> Tuple[str, str, TestResults, List[str]]: +def process_results(result_folder: str) -> Tuple[str, str, TestResults, List[str]]: test_results = [] # type: TestResults additional_files = [] # Just upload all files from result_folder. @@ -129,21 +126,6 @@ def main(): if not os.path.exists(output_path): os.makedirs(output_path) - if not os.path.exists(CACHES_PATH): - os.makedirs(CACHES_PATH) - subprocess.check_call(f"sudo chown -R ubuntu:ubuntu {CACHES_PATH}", shell=True) - cache_path = os.path.join(CACHES_PATH, "fasttest") - - logging.info("Will try to fetch cache for our build") - ccache_for_pr = get_ccache_if_not_exists( - cache_path, s3_helper, pr_info.number, temp_path, pr_info.release_pr - ) - upload_master_ccache = ccache_for_pr in (-1, 0) - - if not os.path.exists(cache_path): - logging.info("cache was not fetched, will create empty dir") - os.makedirs(cache_path) - repo_path = os.path.join(temp_path, "fasttest-repo") if not os.path.exists(repo_path): os.makedirs(repo_path) @@ -151,7 +133,6 @@ def main(): run_cmd = get_fasttest_cmd( workspace, output_path, - cache_path, repo_path, pr_info.number, pr_info.sha, @@ -172,7 +153,6 @@ def main(): logging.info("Run failed") subprocess.check_call(f"sudo chown -R ubuntu:ubuntu {temp_path}", shell=True) - subprocess.check_call(f"sudo chown -R ubuntu:ubuntu {cache_path}", shell=True) test_output_files = os.listdir(output_path) additional_logs = [] @@ -202,12 +182,6 @@ def main(): else: state, description, test_results, additional_logs = process_results(output_path) - logging.info("Will upload cache") - upload_ccache(cache_path, s3_helper, pr_info.number, temp_path) - if upload_master_ccache: - logging.info("Will upload a fallback cache for master") - upload_ccache(cache_path, s3_helper, 0, temp_path) - ch_helper = ClickHouseHelper() mark_flaky_tests(ch_helper, NAME, test_results) From b140915b0a3d83dd31f58d20118f522c413793d1 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Mon, 27 Mar 2023 20:26:40 +0200 Subject: [PATCH 219/377] Use a network-independent cluster in 01293_show_clusters --- tests/queries/0_stateless/01293_show_clusters.reference | 3 ++- tests/queries/0_stateless/01293_show_clusters.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/01293_show_clusters.reference b/tests/queries/0_stateless/01293_show_clusters.reference index c62f8cdfa2d..9f8a44ebd0a 100644 --- a/tests/queries/0_stateless/01293_show_clusters.reference +++ b/tests/queries/0_stateless/01293_show_clusters.reference @@ -1,2 +1,3 @@ test_shard_localhost -test_shard_localhost 1 1 1 localhost ::1 9000 1 default +test_cluster_one_shard_two_replicas 1 1 1 127.0.0.1 127.0.0.1 9000 1 default +test_cluster_one_shard_two_replicas 1 1 2 127.0.0.2 127.0.0.2 9000 0 default diff --git a/tests/queries/0_stateless/01293_show_clusters.sh b/tests/queries/0_stateless/01293_show_clusters.sh index 2fdf17ec25e..ae027210383 100755 --- a/tests/queries/0_stateless/01293_show_clusters.sh +++ b/tests/queries/0_stateless/01293_show_clusters.sh @@ -6,4 +6,5 @@ CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) $CLICKHOUSE_CLIENT -q "show clusters like 'test_shard%' limit 1" # cluster,shard_num,shard_weight,replica_num,host_name,host_address,port,is_local,user,default_database[,errors_count,slowdowns_count,estimated_recovery_time] -$CLICKHOUSE_CLIENT -q "show cluster 'test_shard_localhost'" | cut -f-10 +# use a cluster with static IPv4 +$CLICKHOUSE_CLIENT -q "show cluster 'test_cluster_one_shard_two_replicas'" | cut -f-10 From def5ae851e681ae3e0147141df5dd95c765f6807 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Tue, 28 Mar 2023 17:36:05 +0200 Subject: [PATCH 220/377] Use CTCACHE_DIR only with ccache --- docker/packager/packager | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/packager/packager b/docker/packager/packager index 2a64721d5de..e4b17a3a1da 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -285,7 +285,8 @@ def parse_env_variables( if clang_tidy: # `CTCACHE_DIR` has the same purpose as the `CCACHE_DIR` above. # It's there to have the clang-tidy cache embedded into our standard `CCACHE_DIR` - result.append("CTCACHE_DIR=/ccache/clang-tidy-cache") + if cache == "ccache": + result.append("CTCACHE_DIR=/ccache/clang-tidy-cache") if s3_bucket: # see https://github.com/matus-chochlik/ctcache#environment-variables ctcache_dir = "clang-tidy-cache" From 7ff582b963bdfca74f942704fd0c1deb96c27e3e Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Wed, 29 Mar 2023 16:52:04 +0200 Subject: [PATCH 221/377] Deprecate ENABLE_CCACHE and replace it by COMPILER_CACHE --- CMakeLists.txt | 2 +- cmake/ccache.cmake | 29 +++++++++++++++++++---------- docker/packager/packager | 9 +++------ tests/ci/fast_test_check.py | 2 +- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd7de46474b..ce615c11f2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -568,7 +568,7 @@ if (NATIVE_BUILD_TARGETS COMMAND ${CMAKE_COMMAND} "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" - "-DENABLE_CCACHE=${ENABLE_CCACHE}" + "-DCOMPILER_CACHE=${COMPILER_CACHE}" # Avoid overriding .cargo/config.toml with native toolchain. "-DENABLE_RUST=OFF" "-DENABLE_CLICKHOUSE_SELF_EXTRACTING=${ENABLE_CLICKHOUSE_SELF_EXTRACTING}" diff --git a/cmake/ccache.cmake b/cmake/ccache.cmake index 6103a5212e3..285c8efb9e1 100644 --- a/cmake/ccache.cmake +++ b/cmake/ccache.cmake @@ -1,5 +1,6 @@ # Setup integration with ccache to speed up builds, see https://ccache.dev/ +# Matches both ccache and sccache if (CMAKE_CXX_COMPILER_LAUNCHER MATCHES "ccache" OR CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache") # custom compiler launcher already defined, most likely because cmake was invoked with like "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache" or # via environment variable --> respect setting and trust that the launcher was specified correctly @@ -8,24 +9,32 @@ if (CMAKE_CXX_COMPILER_LAUNCHER MATCHES "ccache" OR CMAKE_C_COMPILER_LAUNCHER MA return() endif() -option(ENABLE_CCACHE "Speedup re-compilations using ccache (default) or sccache (external tools)" ON) -option(FORCE_SCCACHE "Use sccache by default" OFF) - -if (NOT ENABLE_CCACHE AND NOT FORCE_SCCACHE) - message(STATUS "Using *ccache: no (disabled via configuration)") - return() +set(ENABLE_CCACHE "default" CACHE STRING "Deprecated, use COMPILER_CACHE=(auto|ccache|sccache|disabled)") +if (NOT ENABLE_CCACHE STREQUAL "default") + message(WARNING "The -DENABLE_CCACHE is deprecated in favor of -DCOMPILER_CACHE") endif() -# We support ccache and sccache -if (FORCE_SCCACHE) +set(COMPILER_CACHE "auto" CACHE STRING "Speedup re-compilations using the caching tools; valid options are 'auto' (ccache, then sccache), 'ccache', 'sccache', or 'disabled'") + +# It has pretty complex logic, because the ENABLE_CCACHE is deprecated, but still should +# control the COMPILER_CACHE +# After it will be completely removed, the following block will be much simpler +if (COMPILER_CACHE STREQUAL "ccache" OR (ENABLE_CCACHE AND NOT ENABLE_CCACHE STREQUAL "default")) + find_program (CCACHE_EXECUTABLE ccache) +elseif(COMPILER_CACHE STREQUAL "disabled" OR NOT ENABLE_CCACHE STREQUAL "default") + message(STATUS "Using *ccache: no (disabled via configuration)") + return() +elseif(COMPILER_CACHE STREQUAL "auto") + find_program (CCACHE_EXECUTABLE ccache sccache) +elseif(COMPILER_CACHE STREQUAL "sccache") find_program (CCACHE_EXECUTABLE sccache) else() - find_program (CCACHE_EXECUTABLE ccache sccache) + message(${RECONFIGURE_MESSAGE_LEVEL} "The COMPILER_CACHE must be one of (auto|ccache|sccache|disabled), given '${COMPILER_CACHE}'") endif() if (NOT CCACHE_EXECUTABLE) - message(${RECONFIGURE_MESSAGE_LEVEL} "Using *ccache: no (Could not find find ccache or sccache. To significantly reduce compile times for the 2nd, 3rd, etc. build, it is highly recommended to install one of them. To suppress this message, run cmake with -DENABLE_CCACHE=0)") + message(${RECONFIGURE_MESSAGE_LEVEL} "Using *ccache: no (Could not find find ccache or sccache. To significantly reduce compile times for the 2nd, 3rd, etc. build, it is highly recommended to install one of them. To suppress this message, run cmake with -DCOMPILER_CACHE=disabled)") return() endif() diff --git a/docker/packager/packager b/docker/packager/packager index e4b17a3a1da..7d022df52e6 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -257,12 +257,10 @@ def parse_env_variables( result.append("BUILD_TYPE=None") if not cache: - cmake_flags.append("-DENABLE_CCACHE=OFF") - cmake_flags.append("-DFORCE_SCCACHE=OFF") + cmake_flags.append("-DCOMPILER_CACHE=disabled") if cache == "ccache": - cmake_flags.append("-DENABLE_CCACHE=ON") - cmake_flags.append("-DFORCE_SCCACHE=OFF") + cmake_flags.append("-DCOMPILER_CACHE=ccache") result.append("CCACHE_DIR=/ccache") result.append("CCACHE_COMPRESSLEVEL=5") result.append("CCACHE_BASEDIR=/build") @@ -271,8 +269,7 @@ def parse_env_variables( result.append("CCACHE_MAXSIZE=15G") if cache == "sccache": - cmake_flags.append("-DENABLE_CCACHE=OFF") - cmake_flags.append("-DFORCE_SCCACHE=ON") + cmake_flags.append("-DCOMPILER_CACHE=sccache") # see https://github.com/mozilla/sccache/blob/main/docs/S3.md result.append(f"SCCACHE_BUCKET={s3_bucket}") sccache_dir = "sccache" diff --git a/tests/ci/fast_test_check.py b/tests/ci/fast_test_check.py index 9981234f87c..54367f70b3f 100644 --- a/tests/ci/fast_test_check.py +++ b/tests/ci/fast_test_check.py @@ -43,7 +43,7 @@ def get_fasttest_cmd(workspace, output_path, repo_path, pr_number, commit_sha, i "--network=host " # required to get access to IAM credentials f"-e FASTTEST_WORKSPACE=/fasttest-workspace -e FASTTEST_OUTPUT=/test_output " f"-e FASTTEST_SOURCE=/ClickHouse --cap-add=SYS_PTRACE " - f"-e FASTTEST_CMAKE_FLAGS='-DENABLE_CCACHE=OFF -DFORCE_SCCACHE=ON' " + f"-e FASTTEST_CMAKE_FLAGS='-DCOMPILER_CACHE=sccache' " f"-e PULL_REQUEST_NUMBER={pr_number} -e COMMIT_SHA={commit_sha} " f"-e COPY_CLICKHOUSE_BINARY_TO_OUTPUT=1 " f"-e SCCACHE_BUCKET={S3_BUILDS_BUCKET} -e SCCACHE_S3_KEY_PREFIX=ccache/sccache " From 460a83feb6af8f45aced7c3ff86e07020b04fbd1 Mon Sep 17 00:00:00 2001 From: Nikita Taranov Date: Wed, 29 Mar 2023 23:59:39 +0200 Subject: [PATCH 222/377] Fix aggregation by partitions (#47634) --- .../Optimizations/useDataParallelAggregation.cpp | 6 +++++- .../02681_aggregation_by_partitions_bug.reference | 1 + .../02681_aggregation_by_partitions_bug.sql | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/queries/0_stateless/02681_aggregation_by_partitions_bug.reference create mode 100644 tests/queries/0_stateless/02681_aggregation_by_partitions_bug.sql diff --git a/src/Processors/QueryPlan/Optimizations/useDataParallelAggregation.cpp b/src/Processors/QueryPlan/Optimizations/useDataParallelAggregation.cpp index 13a749bd0b6..dc801d10514 100644 --- a/src/Processors/QueryPlan/Optimizations/useDataParallelAggregation.cpp +++ b/src/Processors/QueryPlan/Optimizations/useDataParallelAggregation.cpp @@ -153,6 +153,10 @@ bool isPartitionKeySuitsGroupByKey( if (group_by_actions->hasArrayJoin() || group_by_actions->hasStatefulFunctions() || group_by_actions->hasNonDeterministic()) return false; + + /// We are interested only in calculations required to obtain group by keys (and not aggregate function arguments for example). + group_by_actions->removeUnusedActions(aggregating.getParams().keys); + const auto & gb_key_required_columns = group_by_actions->getRequiredColumnsNames(); const auto & partition_actions = reading.getStorageMetadata()->getPartitionKey().expression->getActionsDAG(); @@ -202,7 +206,7 @@ size_t tryAggregatePartitionsIndependently(QueryPlan::Node * node, QueryPlan::No return 0; if (!reading->willOutputEachPartitionThroughSeparatePort() - && isPartitionKeySuitsGroupByKey(*reading, expression_step->getExpression(), *aggregating_step)) + && isPartitionKeySuitsGroupByKey(*reading, expression_step->getExpression()->clone(), *aggregating_step)) { if (reading->requestOutputEachPartitionThroughSeparatePort()) aggregating_step->skipMerging(); diff --git a/tests/queries/0_stateless/02681_aggregation_by_partitions_bug.reference b/tests/queries/0_stateless/02681_aggregation_by_partitions_bug.reference new file mode 100644 index 00000000000..749fce669df --- /dev/null +++ b/tests/queries/0_stateless/02681_aggregation_by_partitions_bug.reference @@ -0,0 +1 @@ +1000000 diff --git a/tests/queries/0_stateless/02681_aggregation_by_partitions_bug.sql b/tests/queries/0_stateless/02681_aggregation_by_partitions_bug.sql new file mode 100644 index 00000000000..32b4b55076b --- /dev/null +++ b/tests/queries/0_stateless/02681_aggregation_by_partitions_bug.sql @@ -0,0 +1,10 @@ +-- Tags: no-random-merge-tree-settings + +set max_threads = 16; + +create table t(a UInt32) engine=MergeTree order by tuple() partition by a % 16; + +insert into t select * from numbers_mt(1e6); + +set allow_aggregate_partitions_independently=1, force_aggregate_partitions_independently=1; +select count(distinct a) from t; From 42c2ccb7cc39e8c2812b1dc03368c55180c47266 Mon Sep 17 00:00:00 2001 From: Vitaly Baranov Date: Thu, 30 Mar 2023 00:11:13 +0200 Subject: [PATCH 223/377] Support BACKUP ALL command. --- src/Parsers/ParserBackupQuery.cpp | 13 ++--- .../test_backup_restore_new/test.py | 47 +++++++++++++++++++ 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/Parsers/ParserBackupQuery.cpp b/src/Parsers/ParserBackupQuery.cpp index cead1a98c1a..cbe4567ee90 100644 --- a/src/Parsers/ParserBackupQuery.cpp +++ b/src/Parsers/ParserBackupQuery.cpp @@ -103,7 +103,7 @@ namespace }); } - bool parseElement(IParser::Pos & pos, Expected & expected, bool allow_all, Element & element) + bool parseElement(IParser::Pos & pos, Expected & expected, Element & element) { return IParserBase::wrapParseImpl(pos, [&] { @@ -169,7 +169,7 @@ namespace return true; } - if (allow_all && ParserKeyword{"ALL"}.ignore(pos, expected)) + if (ParserKeyword{"ALL"}.ignore(pos, expected)) { element.type = ElementType::ALL; parseExceptDatabases(pos, expected, element.except_databases); @@ -181,7 +181,7 @@ namespace }); } - bool parseElements(IParser::Pos & pos, Expected & expected, bool allow_all, std::vector & elements) + bool parseElements(IParser::Pos & pos, Expected & expected, std::vector & elements) { return IParserBase::wrapParseImpl(pos, [&] { @@ -190,7 +190,7 @@ namespace auto parse_element = [&] { Element element; - if (parseElement(pos, expected, allow_all, element)) + if (parseElement(pos, expected, element)) { result.emplace_back(std::move(element)); return true; @@ -334,11 +334,8 @@ bool ParserBackupQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) else return false; - /// Disable "ALL" if this is a RESTORE command. - bool allow_all = (kind == Kind::RESTORE); - std::vector elements; - if (!parseElements(pos, expected, allow_all, elements)) + if (!parseElements(pos, expected, elements)) return false; String cluster; diff --git a/tests/integration/test_backup_restore_new/test.py b/tests/integration/test_backup_restore_new/test.py index 3f67fe8e5f7..7c3374ffe7d 100644 --- a/tests/integration/test_backup_restore_new/test.py +++ b/tests/integration/test_backup_restore_new/test.py @@ -1184,6 +1184,53 @@ def test_restore_partition(): ) +def test_backup_all(): + create_and_fill_table() + + session_id = new_session_id() + instance.http_query( + "CREATE TEMPORARY TABLE temp_tbl(s String)", params={"session_id": session_id} + ) + instance.http_query( + "INSERT INTO temp_tbl VALUES ('q'), ('w'), ('e')", + params={"session_id": session_id}, + ) + + instance.query("CREATE FUNCTION two_and_half AS (x) -> x * 2.5") + + instance.query("CREATE USER u1 IDENTIFIED BY 'qwe123' SETTINGS custom_a = 1") + + backup_name = new_backup_name() + instance.http_query( + f"BACKUP ALL TO {backup_name}", + params={"session_id": session_id}, + ) + + instance.query("DROP TABLE test.table") + instance.query("DROP FUNCTION two_and_half") + instance.query("DROP USER u1") + + session_id = new_session_id() + instance.http_query( + f"RESTORE ALL FROM {backup_name}", + params={"session_id": session_id}, + method="POST", + ) + + assert instance.query("SELECT count(), sum(x) FROM test.table") == "100\t4950\n" + + assert instance.http_query( + "SELECT * FROM temp_tbl ORDER BY s", params={"session_id": session_id} + ) == TSV([["e"], ["q"], ["w"]]) + + assert instance.query("SELECT two_and_half(6)") == "15\n" + + assert ( + instance.query("SHOW CREATE USER u1") + == "CREATE USER u1 IDENTIFIED WITH sha256_password SETTINGS custom_a = 1\n" + ) + + def test_operation_id(): create_and_fill_table(n=30) From ed29c141fb2f6f9d6cbad1695468018be747dc64 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Wed, 29 Mar 2023 22:09:17 +0000 Subject: [PATCH 224/377] fix race in StorageS3 --- src/Storages/IStorageDataLake.h | 2 +- src/Storages/StorageS3.cpp | 122 +++++++----------- src/Storages/StorageS3.h | 28 ++-- .../02703_storage_s3_race.reference | 1 + .../0_stateless/02703_storage_s3_race.sh | 15 +++ 5 files changed, 74 insertions(+), 94 deletions(-) create mode 100644 tests/queries/0_stateless/02703_storage_s3_race.reference create mode 100755 tests/queries/0_stateless/02703_storage_s3_race.sh diff --git a/src/Storages/IStorageDataLake.h b/src/Storages/IStorageDataLake.h index 9e322377fbd..37776294491 100644 --- a/src/Storages/IStorageDataLake.h +++ b/src/Storages/IStorageDataLake.h @@ -59,7 +59,7 @@ public: auto new_configuration = getAdjustedConfiguration(ctx, configuration, &Poco::Logger::get("Storage" + String(name))); - return Storage::getTableStructureFromData(new_configuration, format_settings, ctx, /*object_infos*/ nullptr); + return Storage::getTableStructureFromData(new_configuration, format_settings, ctx); } static Configuration diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index e17860af288..0753095874c 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -139,15 +139,13 @@ public: ASTPtr & query_, const Block & virtual_header_, ContextPtr context_, - ObjectInfos * object_infos_, - Strings * read_keys_, + KeysWithInfo * read_keys_, const S3Settings::RequestSettings & request_settings_) : WithContext(context_) , client(S3::Client::create(client_)) , globbed_uri(globbed_uri_) , query(query_) , virtual_header(virtual_header_) - , object_infos(object_infos_) , read_keys(read_keys_) , request_settings(request_settings_) , list_objects_pool(1) @@ -278,9 +276,6 @@ private: .last_modification_time = row.GetLastModified().Millis() / 1000, }; - if (object_infos) - (*object_infos)[fs::path(globbed_uri.bucket) / key] = info; - temp_buffer.emplace_back(std::move(key), std::move(info)); } } @@ -324,11 +319,7 @@ private: buffer_iter = buffer.begin(); if (read_keys) - { - read_keys->reserve(read_keys->size() + buffer.size()); - for (const auto & [key, _] : buffer) - read_keys->push_back(key); - } + read_keys->insert(read_keys->end(), buffer.begin(), buffer.end()); } void createFilterAST(const String & any_key) @@ -374,8 +365,7 @@ private: std::unique_ptr matcher; bool recursive{false}; bool is_finished{false}; - ObjectInfos * object_infos; - Strings * read_keys; + KeysWithInfo * read_keys; S3::ListObjectsV2Request request; S3Settings::RequestSettings request_settings; @@ -392,10 +382,9 @@ StorageS3Source::DisclosedGlobIterator::DisclosedGlobIterator( ASTPtr query, const Block & virtual_header, ContextPtr context, - ObjectInfos * object_infos_, - Strings * read_keys_, + KeysWithInfo * read_keys_, const S3Settings::RequestSettings & request_settings_) - : pimpl(std::make_shared(client_, globbed_uri_, query, virtual_header, context, object_infos_, read_keys_, request_settings_)) + : pimpl(std::make_shared(client_, globbed_uri_, query, virtual_header, context, read_keys_, request_settings_)) { } @@ -421,8 +410,7 @@ public: ASTPtr query_, const Block & virtual_header_, ContextPtr context_, - ObjectInfos * object_infos_, - Strings * read_keys_) + KeysWithInfo * read_keys_) : WithContext(context_) , bucket(bucket_) , query(query_) @@ -460,26 +448,15 @@ public: } } - if (read_keys_) - *read_keys_ = all_keys; - for (auto && key : all_keys) { - std::optional info; - - /// To avoid extra requests update total_size only if object_infos != nullptr - /// (which means we eventually need this info anyway, so it should be ok to do it now) - if (object_infos_) - { - info = S3::getObjectInfo(client_, bucket, key, version_id_, request_settings_); - total_size += info->size; - - String path = fs::path(bucket) / key; - (*object_infos_)[std::move(path)] = *info; - } - + auto info = S3::getObjectInfo(client_, bucket, key, version_id_, request_settings_); + total_size += info.size; keys.emplace_back(std::move(key), std::move(info)); } + + if (read_keys_) + *read_keys_ = keys; } KeyWithInfo next() @@ -516,11 +493,10 @@ StorageS3Source::KeysIterator::KeysIterator( ASTPtr query, const Block & virtual_header, ContextPtr context, - ObjectInfos * object_infos, - Strings * read_keys) + KeysWithInfo * read_keys) : pimpl(std::make_shared( client_, version_id_, keys_, bucket_, request_settings_, - query, virtual_header, context, object_infos, read_keys)) + query, virtual_header, context, read_keys)) { } @@ -963,8 +939,7 @@ StorageS3::StorageS3( compression_method, is_key_with_globs, format_settings, - context_, - &object_infos); + context_); storage_metadata.setColumns(columns); } @@ -993,8 +968,7 @@ std::shared_ptr StorageS3::createFileIterator( ContextPtr local_context, ASTPtr query, const Block & virtual_block, - ObjectInfos * object_infos, - Strings * read_keys) + KeysWithInfo * read_keys) { if (distributed_processing) { @@ -1005,14 +979,14 @@ std::shared_ptr StorageS3::createFileIterator( /// Iterate through disclosed globs and make a source for each file return std::make_shared( *s3_configuration.client, s3_configuration.url, query, virtual_block, - local_context, object_infos, read_keys, s3_configuration.request_settings); + local_context, read_keys, s3_configuration.request_settings); } else { return std::make_shared( *s3_configuration.client, s3_configuration.url.version_id, keys, - s3_configuration.url.bucket, s3_configuration.request_settings, query, virtual_block, local_context, - object_infos, read_keys); + s3_configuration.url.bucket, s3_configuration.request_settings, query, + virtual_block, local_context, read_keys); } } @@ -1060,8 +1034,7 @@ Pipe StorageS3::read( distributed_processing, local_context, query_info.query, - virtual_block, - &object_infos); + virtual_block); ColumnsDescription columns_description; Block block_for_format; @@ -1377,13 +1350,12 @@ StorageS3::Configuration StorageS3::getConfiguration(ASTs & engine_args, Context ColumnsDescription StorageS3::getTableStructureFromData( StorageS3::Configuration & configuration, const std::optional & format_settings, - ContextPtr ctx, - ObjectInfos * object_infos) + ContextPtr ctx) { updateConfiguration(ctx, configuration); return getTableStructureFromDataImpl( configuration.format, configuration, configuration.compression_method, - configuration.url.key.find_first_of("*?{") != std::string::npos, format_settings, ctx, object_infos); + configuration.url.key.find_first_of("*?{") != std::string::npos, format_settings, ctx); } ColumnsDescription StorageS3::getTableStructureFromDataImpl( @@ -1392,10 +1364,9 @@ ColumnsDescription StorageS3::getTableStructureFromDataImpl( const String & compression_method, bool is_key_with_globs, const std::optional & format_settings, - ContextPtr ctx, - ObjectInfos * object_infos) + ContextPtr ctx) { - std::vector read_keys; + KeysWithInfo read_keys; auto file_iterator = createFileIterator( s3_configuration, @@ -1403,12 +1374,12 @@ ColumnsDescription StorageS3::getTableStructureFromDataImpl( is_key_with_globs, false, ctx, nullptr, - {}, object_infos, &read_keys); + {}, &read_keys); std::optional columns_from_cache; size_t prev_read_keys_size = read_keys.size(); if (ctx->getSettingsRef().schema_inference_use_cache_for_s3) - columns_from_cache = tryGetColumnsFromCache(read_keys.begin(), read_keys.end(), s3_configuration, object_infos, format, format_settings, ctx); + columns_from_cache = tryGetColumnsFromCache(read_keys.begin(), read_keys.end(), s3_configuration, format, format_settings, ctx); ReadBufferIterator read_buffer_iterator = [&, first = true](ColumnsDescription & cached_columns) mutable -> std::unique_ptr { @@ -1428,7 +1399,7 @@ ColumnsDescription StorageS3::getTableStructureFromDataImpl( /// S3 file iterator could get new keys after new iteration, check them in schema cache. if (ctx->getSettingsRef().schema_inference_use_cache_for_s3 && read_keys.size() > prev_read_keys_size) { - columns_from_cache = tryGetColumnsFromCache(read_keys.begin() + prev_read_keys_size, read_keys.end(), s3_configuration, object_infos, format, format_settings, ctx); + columns_from_cache = tryGetColumnsFromCache(read_keys.begin() + prev_read_keys_size, read_keys.end(), s3_configuration, format, format_settings, ctx); prev_read_keys_size = read_keys.size(); if (columns_from_cache) { @@ -1549,10 +1520,9 @@ SchemaCache & StorageS3::getSchemaCache(const ContextPtr & ctx) } std::optional StorageS3::tryGetColumnsFromCache( - const Strings::const_iterator & begin, - const Strings::const_iterator & end, + const KeysWithInfo::const_iterator & begin, + const KeysWithInfo::const_iterator & end, const Configuration & s3_configuration, - ObjectInfos * object_infos, const String & format_name, const std::optional & format_settings, const ContextPtr & ctx) @@ -1560,31 +1530,33 @@ std::optional StorageS3::tryGetColumnsFromCache( auto & schema_cache = getSchemaCache(ctx); for (auto it = begin; it < end; ++it) { - String path = fs::path(s3_configuration.url.bucket) / *it; - auto get_last_mod_time = [&]() -> std::optional + auto get_last_mod_time = [&] { - S3::ObjectInfo info; - /// Check if we already have information about this object. - /// If no, request it and remember for possible future usage. - if (object_infos && object_infos->contains(path)) - info = (*object_infos)[path]; + time_t last_modification_time = 0; + if (it->info) + { + last_modification_time = it->info->last_modification_time; + } else { /// Note that in case of exception in getObjectInfo returned info will be empty, /// but schema cache will handle this case and won't return columns from cache /// because we can't say that it's valid without last modification time. - info = S3::getObjectInfo(*s3_configuration.client, s3_configuration.url.bucket, *it, s3_configuration.url.version_id, s3_configuration.request_settings, - {}, {}, /* throw_on_error= */ false); - if (object_infos) - (*object_infos)[path] = info; + last_modification_time = S3::getObjectInfo( + *s3_configuration.client, + s3_configuration.url.bucket, + it->key, + s3_configuration.url.version_id, + s3_configuration.request_settings, + /*with_metadata=*/ false, + /*for_disk_s3=*/ false, + /*throw_on_error= */ false).last_modification_time; } - if (info.last_modification_time) - return info.last_modification_time; - - return std::nullopt; + return last_modification_time ? std::make_optional(last_modification_time) : std::nullopt; }; + String path = fs::path(s3_configuration.url.bucket) / it->key; String source = fs::path(s3_configuration.url.uri.getHost() + std::to_string(s3_configuration.url.uri.getPort())) / path; auto cache_key = getKeyForSchemaCache(source, format_name, format_settings, ctx); auto columns = schema_cache.tryGet(cache_key, get_last_mod_time); @@ -1596,7 +1568,7 @@ std::optional StorageS3::tryGetColumnsFromCache( } void StorageS3::addColumnsToCache( - const Strings & keys, + const KeysWithInfo & keys, const Configuration & s3_configuration, const ColumnsDescription & columns, const String & format_name, @@ -1606,7 +1578,7 @@ void StorageS3::addColumnsToCache( auto host_and_bucket = fs::path(s3_configuration.url.uri.getHost() + std::to_string(s3_configuration.url.uri.getPort())) / s3_configuration.url.bucket; Strings sources; sources.reserve(keys.size()); - std::transform(keys.begin(), keys.end(), std::back_inserter(sources), [&](const String & key){ return host_and_bucket / key; }); + std::transform(keys.begin(), keys.end(), std::back_inserter(sources), [&](const auto & elem){ return host_and_bucket / elem.key; }); auto cache_keys = getKeysForSchemaCache(sources, format_name, format_settings, ctx); auto & schema_cache = getSchemaCache(ctx); schema_cache.addMany(cache_keys, columns); diff --git a/src/Storages/StorageS3.h b/src/Storages/StorageS3.h index b4f95d8d10d..a34113b8bae 100644 --- a/src/Storages/StorageS3.h +++ b/src/Storages/StorageS3.h @@ -51,7 +51,7 @@ public: }; using KeysWithInfo = std::vector; - using ObjectInfos = std::unordered_map; + class IIterator { public: @@ -71,8 +71,7 @@ public: ASTPtr query, const Block & virtual_header, ContextPtr context, - ObjectInfos * object_infos = nullptr, - Strings * read_keys_ = nullptr, + KeysWithInfo * read_keys_ = nullptr, const S3Settings::RequestSettings & request_settings_ = {}); KeyWithInfo next() override; @@ -96,8 +95,7 @@ public: ASTPtr query, const Block & virtual_header, ContextPtr context, - ObjectInfos * object_infos = nullptr, - Strings * read_keys = nullptr); + KeysWithInfo * read_keys = nullptr); KeyWithInfo next() override; size_t getTotalSize() const override; @@ -288,8 +286,6 @@ public: bool supportsPartitionBy() const override; - using ObjectInfos = StorageS3Source::ObjectInfos; - static void processNamedCollectionResult(StorageS3::Configuration & configuration, const NamedCollection & collection); static SchemaCache & getSchemaCache(const ContextPtr & ctx); @@ -299,8 +295,7 @@ public: static ColumnsDescription getTableStructureFromData( StorageS3::Configuration & configuration, const std::optional & format_settings, - ContextPtr ctx, - ObjectInfos * object_infos = nullptr); + ContextPtr ctx); protected: static StorageS3::Configuration updateConfiguration(ContextPtr local_context, const Configuration & configuration); @@ -323,7 +318,7 @@ private: ASTPtr partition_by; bool is_key_with_globs = false; - ObjectInfos object_infos; + using KeysWithInfo = StorageS3Source::KeysWithInfo; static std::shared_ptr createFileIterator( const Configuration & s3_configuration, @@ -333,8 +328,7 @@ private: ContextPtr local_context, ASTPtr query, const Block & virtual_block, - ObjectInfos * object_infos = nullptr, - Strings * read_keys = nullptr); + KeysWithInfo * read_keys = nullptr); static ColumnsDescription getTableStructureFromDataImpl( const String & format, @@ -342,24 +336,22 @@ private: const String & compression_method, bool is_key_with_globs, const std::optional & format_settings, - ContextPtr ctx, - ObjectInfos * object_infos = nullptr); + ContextPtr ctx); bool supportsSubcolumns() const override; bool supportsSubsetOfColumns() const override; static std::optional tryGetColumnsFromCache( - const Strings::const_iterator & begin, - const Strings::const_iterator & end, + const KeysWithInfo::const_iterator & begin, + const KeysWithInfo::const_iterator & end, const Configuration & s3_configuration, - ObjectInfos * object_infos, const String & format_name, const std::optional & format_settings, const ContextPtr & ctx); static void addColumnsToCache( - const Strings & keys, + const KeysWithInfo & keys, const Configuration & s3_configuration, const ColumnsDescription & columns, const String & format_name, diff --git a/tests/queries/0_stateless/02703_storage_s3_race.reference b/tests/queries/0_stateless/02703_storage_s3_race.reference new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/tests/queries/0_stateless/02703_storage_s3_race.reference @@ -0,0 +1 @@ +OK diff --git a/tests/queries/0_stateless/02703_storage_s3_race.sh b/tests/queries/0_stateless/02703_storage_s3_race.sh new file mode 100755 index 00000000000..f4ef8c2426f --- /dev/null +++ b/tests/queries/0_stateless/02703_storage_s3_race.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +filename="test_${CLICKHOUSE_DATABASE}_${RANDOM}" + +$CLICKHOUSE_CLIENT --query "DROP TABLE IF EXISTS test_s3_race" +$CLICKHOUSE_CLIENT --query "CREATE TABLE test_s3_race (u UInt64) ENGINE = S3(s3_conn, filename='$filename', format='CSV')" +$CLICKHOUSE_CLIENT --s3_truncate_on_insert 1 --query "INSERT INTO test_s3_race VALUES (1)" + +$CLICKHOUSE_BENCHMARK -i 100 -c 4 <<< "SELECT * FROM test_s3_race" >/dev/null 2>&1 +$CLICKHOUSE_CLIENT --query "DROP TABLE IF EXISTS test_s3_race" +echo "OK" From 21bfdefebfed51a6f9e63513ea26a5983c9b95b3 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 01:34:13 +0200 Subject: [PATCH 225/377] Add changelog for 23.3 --- CHANGELOG.md | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01bbafe2f16..95b18856928 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,194 @@ ### Table of Contents +**[ClickHouse release v23.3, 2023-03-30](#233)**
**[ClickHouse release v23.2, 2023-02-23](#232)**
**[ClickHouse release v23.1, 2023-01-25](#231)**
**[Changelog for 2022](https://clickhouse.com/docs/en/whats-new/changelog/2022/)**
# 2023 Changelog +###
ClickHouse release 23.3 LTS, 2023-03-30 + +#### Upgrade Notes +* The behavior of `*domain*RFC` and `netloc` functions is slightly changed: relaxed the set of symbols that are allowed in the URL authority for better conformance. [#46841](https://github.com/ClickHouse/ClickHouse/pull/46841) ([Azat Khuzhin](https://github.com/azat)). +* Prohibited creating tables based on KafkaEngine with DEFAULT/EPHEMERAL/ALIAS/MATERIALIZED statements for columns. [#47138](https://github.com/ClickHouse/ClickHouse/pull/47138) ([Aleksandr Musorin](https://github.com/AVMusorin)). +* An "asynchronous connection drain" feature is removed. Related settings and metrics are removed as well. It was an internal feature, so the removal should not affect users who had never heard about that feature. [#47486](https://github.com/ClickHouse/ClickHouse/pull/47486) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Support 256-bit Decimal data type (more than 38 digits) in `arraySum`/`Min`/`Max`/`Avg`/`Product`, `arrayCumSum`/`CumSumNonNegative`, `arrayDifference`, array construction, IN operator, query parameters, `groupArrayMovingSum`, statistical functions, `min`/`max`/`any`/`argMin`/`argMax`, PostgreSQL wire protocol, MySQL table engine and function, `sumMap`, `mapAdd`, `mapSubtract`, `arrayIntersect`. Add support for big integers in `arrayIntersect`. Statistical aggregate functions involving moments (such as `corr` or various `TTest`s) will use `Float64` as their internal representation (they were using `Decimal128` before this change, but it was pointless), and these functions can return `nan` instead of `inf` in case of infinite variance. Some functions were allowed on `Decimal256` data types but returned `Decimal128` in previous versions - now it is fixed. This closes [#47569](https://github.com/ClickHouse/ClickHouse/issues/47569). This closes [#44864](https://github.com/ClickHouse/ClickHouse/issues/44864). This closes [#28335](https://github.com/ClickHouse/ClickHouse/issues/28335). [#47594](https://github.com/ClickHouse/ClickHouse/pull/47594) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Make backup_threads/restore_threads server settings (instead of user settings). [#47881](https://github.com/ClickHouse/ClickHouse/pull/47881) ([Azat Khuzhin](https://github.com/azat)). +* Do not allow const and non-deterministic secondary indices [#46839](https://github.com/ClickHouse/ClickHouse/pull/46839) ([Anton Popov](https://github.com/CurtizJ)). + +#### New Feature +* Add new mode for splitting the work on replicas using settings `parallel_replicas_custom_key` and `parallel_replicas_custom_key_filter_type`. If the cluster consists of a single shard with multiple replicas, up to `max_parallel_replicas` will be randomly picked and turned into shards. For each shard, a corresponding filter is added to the query on the initiator before being sent to the shard. If the cluster consists of multiple shards, it will behave the same as `sample_key` but with the possibility to define an arbitrary key. [#45108](https://github.com/ClickHouse/ClickHouse/pull/45108) ([Antonio Andelic](https://github.com/antonio2368)). +* An option to display partial result on cancel: Added query setting `stop_reading_on_first_cancel` allowing the canceled query (e.g. due to Ctrl-C) to return a partial result. [#45689](https://github.com/ClickHouse/ClickHouse/pull/45689) ([Alexey Perevyshin](https://github.com/alexX512)). +* Added support of arbitrary tables engines for temporary tables (except for Replicated and KeeperMap engines). Close [#31497](https://github.com/ClickHouse/ClickHouse/issues/31497). [#46071](https://github.com/ClickHouse/ClickHouse/pull/46071) ([Roman Vasin](https://github.com/rvasin)). +* Add support for replication of user-defined SQL functions using a centralized storage in Keeper. [#46085](https://github.com/ClickHouse/ClickHouse/pull/46085) ([Aleksei Filatov](https://github.com/aalexfvk)). +* Implement `system.server_settings` (similar to `system.settings`), which will contain server configurations. [#46550](https://github.com/ClickHouse/ClickHouse/pull/46550) ([pufit](https://github.com/pufit)). +* Support for `UNDROP TABLE` query. Closes [#46811](https://github.com/ClickHouse/ClickHouse/issues/46811). [#47241](https://github.com/ClickHouse/ClickHouse/pull/47241) ([chen](https://github.com/xiedeyantu)). +* Allow separate grants for named collections (e.g. to be able to give `SHOW/CREATE/ALTER/DROP named collection` access only to certain collections, instead of all at once). Closes [#40894](https://github.com/ClickHouse/ClickHouse/issues/40894). Add new access type `NAMED_COLLECTION_CONTROL` which is not given to default user unless explicitly added to user config (is required to be able to do `GRANT ALL`), also `show_named_collections` is no longer obligatory to be manually specified for default user to be able to have full access rights as was in 23.2. [#46241](https://github.com/ClickHouse/ClickHouse/pull/46241) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Allow nested custom disks. Previously custom disks supported only flat disk structure. [#47106](https://github.com/ClickHouse/ClickHouse/pull/47106) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Intruduce a function `widthBucket` (with a `WIDTH_BUCKET` alias for compatibility). [#42974](https://github.com/ClickHouse/ClickHouse/issues/42974). [#46790](https://github.com/ClickHouse/ClickHouse/pull/46790) ([avoiderboi](https://github.com/avoiderboi)). +* Add new function `parseDateTime`/`parseDateTimeInJodaSyntax` according to specified format string. parseDateTime parses string to datetime in MySQL syntax, parseDateTimeInJodaSyntax parses in Joda syntax. [#46815](https://github.com/ClickHouse/ClickHouse/pull/46815) ([李扬](https://github.com/taiyang-li)). +* Use `dummy UInt8` for default structure of table function `null`. Closes [#46930](https://github.com/ClickHouse/ClickHouse/issues/46930). [#47006](https://github.com/ClickHouse/ClickHouse/pull/47006) ([flynn](https://github.com/ucasfl)). +* Support for date format with a comma, like `Dec 15, 2021` in the `parseDateTimeBestEffort` function. Closes [#46816](https://github.com/ClickHouse/ClickHouse/issues/46816). [#47071](https://github.com/ClickHouse/ClickHouse/pull/47071) ([chen](https://github.com/xiedeyantu)). +* Add settings `http_wait_end_of_query` and `http_response_buffer_size` that corresponds to URL params `wait_end_of_query` and `buffer_size` for HTTP interface. This allows to change these settings in the profiles. [#47108](https://github.com/ClickHouse/ClickHouse/pull/47108) ([Vladimir C](https://github.com/vdimir)). +* Add `system.marked_dropped_tables` table that shows tables that were dropped from `Atomic` databases but were not completely removed yet. [#47364](https://github.com/ClickHouse/ClickHouse/pull/47364) ([chen](https://github.com/xiedeyantu)). +* Add `INSTR` as alias of `positionCaseInsensitive` for MySQL compatibility. Closes [#47529](https://github.com/ClickHouse/ClickHouse/issues/47529). [#47535](https://github.com/ClickHouse/ClickHouse/pull/47535) ([flynn](https://github.com/ucasfl)). +* Added `toDecimalString` function allowing to convert numbers to string with fixed precision. [#47838](https://github.com/ClickHouse/ClickHouse/pull/47838) ([Andrey Zvonov](https://github.com/zvonand)). +* Add a merge tree setting `max_number_of_mutations_for_replica`. It limits the number of part mutations per replica to the specified amount. Zero means no limit on the number of mutations per replica (the execution can still be constrained by other settings). [#48047](https://github.com/ClickHouse/ClickHouse/pull/48047) ([Vladimir C](https://github.com/vdimir)). +* Add Map-related function `mapFromArrays`, which allows us to create map from a pair of arrays. [#31125](https://github.com/ClickHouse/ClickHouse/pull/31125) ([李扬](https://github.com/taiyang-li)). +* Allow control compression in Parquet/ORC/Arrow output formats, support more compression for input formats. This closes [#13541](https://github.com/ClickHouse/ClickHouse/issues/13541). [#47114](https://github.com/ClickHouse/ClickHouse/pull/47114) ([Kruglov Pavel](https://github.com/Avogar)). +* Add SSL User Certificate authentication to the native protocol. Closes [#47077](https://github.com/ClickHouse/ClickHouse/issues/47077). [#47596](https://github.com/ClickHouse/ClickHouse/pull/47596) ([Nikolay Degterinsky](https://github.com/evillique)). +* Add *OrNull() and *OrZero() variants for `parseDateTime`, add alias `str_to_date` for MySQL parity. [#48000](https://github.com/ClickHouse/ClickHouse/pull/48000) ([Robert Schulze](https://github.com/rschu1ze)). +* Added operator `REGEXP` (similar to operators "LIKE", "IN", "MOD" etc.) for better compatibility with MySQL [#47869](https://github.com/ClickHouse/ClickHouse/pull/47869) ([Robert Schulze](https://github.com/rschu1ze)). + +#### Performance Improvement +* Marks in memory are now compressed, using 3-6x less memory. [#47290](https://github.com/ClickHouse/ClickHouse/pull/47290) ([Michael Kolupaev](https://github.com/al13n321)). +* Backups for large numbers of files were unbelievably slow in previous versions. Not anymore. Now they are unbelievably fast. [#47251](https://github.com/ClickHouse/ClickHouse/pull/47251) ([Alexey Milovidov](https://github.com/alexey-milovidov)). Introduced a separate thread pool for backup's IO operations. This will allow to scale it independently of other pools and increase performance. [#47174](https://github.com/ClickHouse/ClickHouse/pull/47174) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). Use MultiRead request and retries for collecting metadata at final stage of backup processing. [#47243](https://github.com/ClickHouse/ClickHouse/pull/47243) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). If a backup and restoring data are both in S3 then server-side copy should be used from now on. [#47546](https://github.com/ClickHouse/ClickHouse/pull/47546) ([Vitaly Baranov](https://github.com/vitlibar)). +* Fixed excessive reading in queries with `FINAL`. [#47801](https://github.com/ClickHouse/ClickHouse/pull/47801) ([Nikita Taranov](https://github.com/nickitat)). +* Setting `max_final_threads` would be set to number of cores at server startup (by the same algorithm as we use for `max_threads`). This improves concurrency of `final` execution on servers with high number of CPUs. [#47915](https://github.com/ClickHouse/ClickHouse/pull/47915) ([Nikita Taranov](https://github.com/nickitat)). +* Allow executing reading pipeline for DIRECT dictionary with CLICKHOUSE source in multiple threads. To enable set `dictionary_use_async_executor=1` in `SETTINGS` section for source in `CREATE DICTIONARY` statement. [#47986](https://github.com/ClickHouse/ClickHouse/pull/47986) ([Vladimir C](https://github.com/vdimir)). +* Optimize one nullable key aggregate performance. [#45772](https://github.com/ClickHouse/ClickHouse/pull/45772) ([LiuNeng](https://github.com/liuneng1994)). +* Implemented lowercase `tokenbf_v1` index utilization for `hasTokenOrNull`, `hasTokenCaseInsensitive` and `hasTokenCaseInsensitiveOrNull`. [#46252](https://github.com/ClickHouse/ClickHouse/pull/46252) ([ltrk2](https://github.com/ltrk2)). +* Optimize functions `position` and `LIKE` by searching the first two chars using SIMD. [#46289](https://github.com/ClickHouse/ClickHouse/pull/46289) ([Jiebin Sun](https://github.com/jiebinn)). +* Optimize queries from the `system.detached_parts`, which could be significantly large. Added several sources with respect to the block size limitation; in each block an IO thread pool is used to calculate the part size, i.e. to make syscalls in parallel. [#46624](https://github.com/ClickHouse/ClickHouse/pull/46624) ([Sema Checherinda](https://github.com/CheSema)). +* Increase the default value of `max_replicated_merges_in_queue` for ReplicatedMergeTree tables from 16 to 1000. It allows faster background merge operation on clusters with a very large number of replicas, such as clusters with shared storage in ClickHouse Cloud. [#47050](https://github.com/ClickHouse/ClickHouse/pull/47050) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Updated `clickhouse-copier` to use `GROUP BY` instead of `DISTINCT` to get list of partitions. For large tables this reduced the select time from over 500s to under 1s. [#47386](https://github.com/ClickHouse/ClickHouse/pull/47386) ([Clayton McClure](https://github.com/cmcclure-twilio)). +* Fix performance degradation in `ASOF JOIN`. [#47544](https://github.com/ClickHouse/ClickHouse/pull/47544) ([Ongkong](https://github.com/ongkong)). +* Even more batching in Keeper. Avoid breaking batches on read requests to improve performance. [#47978](https://github.com/ClickHouse/ClickHouse/pull/47978) ([Antonio Andelic](https://github.com/antonio2368)). +* Allow PREWHERE for Merge with different DEFAULT expression for column. [#46831](https://github.com/ClickHouse/ClickHouse/pull/46831) ([Azat Khuzhin](https://github.com/azat)). + +#### Experimental Feature +* Parallel replicas: Improved the overall performance by better utilizing local replica. And forbid reading with parallel replicas from non-replicated MergeTree by default. [#47858](https://github.com/ClickHouse/ClickHouse/pull/47858) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). +* Support filter push down to left table for JOIN with `Join`, `Dictionary` and `EmbeddedRocksDB` tables if the experimental Analyzer is enabled. [#47280](https://github.com/ClickHouse/ClickHouse/pull/47280) ([Maksim Kita](https://github.com/kitaisreal)). +* Now ReplicatedMergeTree with zero copy replication has less load to Keeper. [#47676](https://github.com/ClickHouse/ClickHouse/pull/47676) ([alesapin](https://github.com/alesapin)). +* Fix create materialized view with MaterializedPostgreSQL [#40807](https://github.com/ClickHouse/ClickHouse/pull/40807) ([Maksim Buren](https://github.com/maks-buren630501)). + +#### Improvement +* Enable `input_format_json_ignore_unknown_keys_in_named_tuple` by default. [#46742](https://github.com/ClickHouse/ClickHouse/pull/46742) ([Kruglov Pavel](https://github.com/Avogar)). +* Allow to ignore errors while pushing to MATERIALIZED VIEW (add new setting `materialized_views_ignore_errors`, by default to `false`, but it is set to `true` for flushing logs to `system.*_log` tables unconditionally). [#46658](https://github.com/ClickHouse/ClickHouse/pull/46658) ([Azat Khuzhin](https://github.com/azat)). +* Track the file queue of distributed sends in memory. [#45491](https://github.com/ClickHouse/ClickHouse/pull/45491) ([Azat Khuzhin](https://github.com/azat)). +* Now `X-ClickHouse-Query-Id` and `X-ClickHouse-Timezone` headers are added to response in all queries via http protocol. Previously it was done only for `SELECT` queries. [#46364](https://github.com/ClickHouse/ClickHouse/pull/46364) ([Anton Popov](https://github.com/CurtizJ)). +* External tables from `MongoDB`: support for connection to a replica set via a URI with a host:port enum and support for the readPreference option in MongoDB dictionaries. Example URI: mongodb://db0.example.com:27017,db1.example.com:27017,db2.example.com:27017/?replicaSet=myRepl&readPreference=primary. [#46524](https://github.com/ClickHouse/ClickHouse/pull/46524) ([artem-yadr](https://github.com/artem-yadr)). +* This improvement should be invisible for users. Re-implement projection analysis on top of query plan. Added setting `query_plan_optimize_projection=1` to switch between old and new version. Fixes [#44963](https://github.com/ClickHouse/ClickHouse/issues/44963). [#46537](https://github.com/ClickHouse/ClickHouse/pull/46537) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Use parquet format v2 instead of v1 in output format by default. Add setting `output_format_parquet_version` to control parquet version, possible values `v1_0`, `v2_4`, `v2_6`, `v2_latest` (default). [#46617](https://github.com/ClickHouse/ClickHouse/pull/46617) ([Kruglov Pavel](https://github.com/Avogar)). +* It is now possible using new configuration syntax to configure Kafka topics with periods (`.`) in their name. [#46752](https://github.com/ClickHouse/ClickHouse/pull/46752) ([Robert Schulze](https://github.com/rschu1ze)). +* Fix heuristics that check hyperscan patterns for problematic repeats. [#46819](https://github.com/ClickHouse/ClickHouse/pull/46819) ([Robert Schulze](https://github.com/rschu1ze)). +* Don't report ZK node exists to system.errors when a block was created concurrently by a different replica. [#46820](https://github.com/ClickHouse/ClickHouse/pull/46820) ([Raúl Marín](https://github.com/Algunenano)). +* Increase the limit for opened files in `clickhouse-local`. It will be able to read from `web` tables on servers with a huge number of CPU cores. Do not back off reading from the URL table engine in case of too many opened files. This closes [#46852](https://github.com/ClickHouse/ClickHouse/issues/46852). [#46853](https://github.com/ClickHouse/ClickHouse/pull/46853) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Exceptions thrown when numbers cannot be parsed now have an easier-to-read exception message. [#46917](https://github.com/ClickHouse/ClickHouse/pull/46917) ([Robert Schulze](https://github.com/rschu1ze)). +* Added update `system.backups` after every processed task to track the progress of backups. [#46989](https://github.com/ClickHouse/ClickHouse/pull/46989) ([Aleksandr Musorin](https://github.com/AVMusorin)). +* Allow types conversion in Native input format. Add settings `input_format_native_allow_types_conversion` that controls it (enabled by default). [#46990](https://github.com/ClickHouse/ClickHouse/pull/46990) ([Kruglov Pavel](https://github.com/Avogar)). +* Allow IPv4 in the `range` function to generate IP ranges. [#46995](https://github.com/ClickHouse/ClickHouse/pull/46995) ([Yakov Olkhovskiy](https://github.com/yakov-olkhovskiy)). +* Improve exception message when it's impossible to make part move from one volume/disk to another. [#47032](https://github.com/ClickHouse/ClickHouse/pull/47032) ([alesapin](https://github.com/alesapin)). +* Support `Bool` type in `JSONType` function. Previously `Null` type was mistakenly returned for bool values. [#47046](https://github.com/ClickHouse/ClickHouse/pull/47046) ([Anton Popov](https://github.com/CurtizJ)). +* Use `_request_body` parameter to configure predefined HTTP queries. [#47086](https://github.com/ClickHouse/ClickHouse/pull/47086) ([Constantine Peresypkin](https://github.com/pkit)). +* Automatic indentation in the built-in UI SQL editor when Enter is pressed. [#47113](https://github.com/ClickHouse/ClickHouse/pull/47113) ([Alexey Korepanov](https://github.com/alexkorep)). +* Self-extraction with 'sudo' will attempt to set uid and gid of extracted files to running user. [#47116](https://github.com/ClickHouse/ClickHouse/pull/47116) ([Yakov Olkhovskiy](https://github.com/yakov-olkhovskiy)). +* Previously, the `repeat` function's second argument only accepted an unsigned integer type, which meant it could not accept values such as -1. This behavior differed from that of the Spark function. In this update, the repeat function has been modified to match the behavior of the Spark function. It now accepts the same types of inputs, including negative integers. Extensive testing has been performed to verify the correctness of the updated implementation. [#47134](https://github.com/ClickHouse/ClickHouse/pull/47134) ([KevinyhZou](https://github.com/KevinyhZou)). Note: the changelog entry was rewritten by ChatGPT. +* Remove `::__1` part from stacktraces. Display `std::basic_string ClickHouse release 23.2, 2023-02-23 #### Backward Incompatible Change From 1f251b173c209b706eb7ad8a4a120d963ec85773 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 01:35:02 +0200 Subject: [PATCH 226/377] Add changelog for 23.3 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95b18856928..122164bc9e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ ### Table of Contents -**[ClickHouse release v23.3, 2023-03-30](#233)**
+**[ClickHouse release v23.3 LTS, 2023-03-30](#233)**
**[ClickHouse release v23.2, 2023-02-23](#232)**
**[ClickHouse release v23.1, 2023-01-25](#231)**
**[Changelog for 2022](https://clickhouse.com/docs/en/whats-new/changelog/2022/)**
From 8aebdd79f615bda1b4433ea2d745b519a7d68378 Mon Sep 17 00:00:00 2001 From: Nikolay Degterinsky <43110995+evillique@users.noreply.github.com> Date: Thu, 30 Mar 2023 01:37:01 +0200 Subject: [PATCH 227/377] Update 02668_ulid_decoding.sql --- tests/queries/0_stateless/02668_ulid_decoding.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02668_ulid_decoding.sql b/tests/queries/0_stateless/02668_ulid_decoding.sql index 296d2b7ce32..df94025b7b5 100644 --- a/tests/queries/0_stateless/02668_ulid_decoding.sql +++ b/tests/queries/0_stateless/02668_ulid_decoding.sql @@ -1,6 +1,6 @@ -- Tags: no-fasttest -SELECT dateDiff('s', ULIDStringToDateTime(generateULID()), now()) = 0; +SELECT dateDiff('minute', ULIDStringToDateTime(generateULID()), now()) = 0; SELECT toTimezone(ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E'), 'America/Costa_Rica'); SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9E', 'America/Costa_Rica'); SELECT ULIDStringToDateTime('01GWJWKW30MFPQJRYEAF4XFZ9', 'America/Costa_Rica'); -- { serverError ILLEGAL_COLUMN } From 553c5aa6227f2f1fe48af8224e9b22a6210905ad Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Wed, 29 Mar 2023 20:46:34 -0300 Subject: [PATCH 228/377] Update type-conversion-functions.md --- .../functions/type-conversion-functions.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 0c25d25be37..c49ec92067c 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -13,7 +13,7 @@ incompatible datatypes (for example from `String` to `Int`). Make sure to check ClickHouse generally uses the [same behavior as C++ programs](https://en.cppreference.com/w/cpp/language/implicit_conversion). -`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md). +`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md), this behaviour is not compatible with SQL standart, and it can changed using [cast_keep_nullable](/docs/en/operations/settings/settings.md/#cast_keep_nullable) setting. Example: @@ -35,6 +35,16 @@ SELECT ┌─source_type──────┬─to_type_result_type─┬─cast_result_type─┐ │ Nullable(String) │ Nullable(String) │ String │ └──────────────────┴─────────────────────┴──────────────────┘ + +SELECT + toTypeName(toNullable('') AS val) AS source_type, + toTypeName(toString(val)) AS to_type_result_type, + toTypeName(CAST(val, 'String')) AS cast_result_type +SETTINGS cast_keep_nullable = 1 + +┌─source_type──────┬─to_type_result_type─┬─cast_result_type─┐ +│ Nullable(String) │ Nullable(String) │ Nullable(String) │ +└──────────────────┴─────────────────────┴──────────────────┘ ``` ## toInt(8\|16\|32\|64\|128\|256) From 2c231a6ac4d38c5e9715c982d71b4ddd307a848b Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Wed, 29 Mar 2023 20:49:44 -0300 Subject: [PATCH 229/377] Update settings.md --- docs/en/operations/settings/settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index 9f5b8dbda58..4d638964c6f 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -2769,7 +2769,7 @@ Default value: `120` seconds. ## cast_keep_nullable {#cast_keep_nullable} -Enables or disables keeping of the `Nullable` data type in [CAST](../../sql-reference/functions/type-conversion-functions.md/#type_conversion_function-cast) operations. +Enables or disables keeping of the `Nullable` data type in [CAST](../../sql-reference/functions/type-conversion-functions.md/#castx-t) operations. When the setting is enabled and the argument of `CAST` function is `Nullable`, the result is also transformed to `Nullable` type. When the setting is disabled, the result always has the destination type exactly. From e43780326ec2319ea343fd68bbba1a2bbb1925e2 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Thu, 30 Mar 2023 11:09:07 +0800 Subject: [PATCH 230/377] wip support map args for map_from_arrays --- src/Functions/map.cpp | 56 +++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/Functions/map.cpp b/src/Functions/map.cpp index 549de200bea..e1dc58eb077 100644 --- a/src/Functions/map.cpp +++ b/src/Functions/map.cpp @@ -173,23 +173,31 @@ public: getName(), arguments.size()); - const auto * keys_type = checkAndGetDataType(arguments[0].get()); - if (!keys_type) - throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "First argument for function {} must be an Array", getName()); + /// The first argument should always be Array. + /// Because key type can not be nested type of Map, which is Tuple + DataTypePtr key_type; + if (const auto * keys_type = checkAndGetDataType(arguments[0].get())) + key_type = keys_type->getNestedType(); + else + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "First argument for function {} must be Array or Map", getName()); - const auto * values_type = checkAndGetDataType(arguments[1].get()); - if (!values_type) - throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Second argument for function {} must be an Array", getName()); + DataTypePtr value_type; + if (const auto * value_array_type = checkAndGetDataType(arguments[1].get())) + value_type = value_array_type->getNestedType(); + else if (const auto * value_map_type = checkAndGetDataType(arguments[1].get())) + value_type = value_map_type->getValueType(); + else + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Second argument for function {} must be Array or Map", getName()); - DataTypes key_value_types{keys_type->getNestedType(), values_type->getNestedType()}; + DataTypes key_value_types{key_type, value_type}; return std::make_shared(key_value_types); } ColumnPtr executeImpl( const ColumnsWithTypeAndName & arguments, const DataTypePtr & /* result_type */, size_t /* input_rows_count */) const override { - ColumnPtr holder_keys; bool is_keys_const = isColumnConst(*arguments[0].column); + ColumnPtr holder_keys; const ColumnArray * col_keys; if (is_keys_const) { @@ -201,24 +209,26 @@ public: col_keys = checkAndGetColumn(arguments[0].column.get()); } - ColumnPtr holder_values; - bool is_values_const = isColumnConst(*arguments[1].column); - const ColumnArray * col_values; - if (is_values_const) - { - holder_values = arguments[1].column->convertToFullColumnIfConst(); - col_values = checkAndGetColumn(holder_values.get()); - } - else - { - col_values = checkAndGetColumn(arguments[1].column.get()); - } + if (!col_keys) + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "The first argument of function {} must be Array", getName()); - if (!col_keys || !col_values) - throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Arguments of function {} must be array", getName()); + bool is_values_const = isColumnConst(*arguments[1].column); + ColumnPtr holder_values; + if (is_values_const) + holder_values = arguments[1].column->convertToFullColumnIfConst(); + else + holder_values = arguments[1].column; + + const ColumnArray * col_values; + if (const auto * col_values_array = checkAndGetColumn(holder_values.get())) + col_values = col_values_array; + else if (const auto * col_values_map = checkAndGetColumn(holder_values.get())) + col_values = &col_values_map->getNestedColumn(); + else + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "The second arguments of function {} must be Array or Map", getName()); if (!col_keys->hasEqualOffsets(*col_values)) - throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Array arguments for function {} must have equal sizes", getName()); + throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH, "Two arguments for function {} must have equal sizes", getName()); const auto & data_keys = col_keys->getDataPtr(); const auto & data_values = col_values->getDataPtr(); From 5ca488d70e17e46fc1904abb52a2ad002e2b9bec Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 09:14:52 +0300 Subject: [PATCH 231/377] Update src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp Co-authored-by: Antonio Andelic --- src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp b/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp index 7ce58b9991d..eeca14176cc 100644 --- a/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp @@ -412,8 +412,10 @@ bool MsgPackVisitor::end_array_item() // NOLINT info_stack.pop(); else { - --(*info_stack.top().array_size); - if (*info_stack.top().array_size == 0) + assert(info_stack.top().array_size.has_value()); + auto & current_array_size = *info_stack.top().array_size; + --current_array_size; + if (current_array_size == 0) info_stack.pop(); } return true; From 9db58532f451f91bac7da76709709f0b4f42c623 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 30 Mar 2023 08:41:14 +0200 Subject: [PATCH 232/377] Clang-tidy fix --- src/Storages/StorageS3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index b735e090f59..6cdb950b202 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -1291,7 +1291,7 @@ void StorageS3::processNamedCollectionResult(StorageS3::Configuration & configur configuration.auth_settings.access_key_id = collection.getOrDefault("access_key_id", ""); configuration.auth_settings.secret_access_key = collection.getOrDefault("secret_access_key", ""); configuration.auth_settings.use_environment_credentials = collection.getOrDefault("use_environment_credentials", 0); - configuration.auth_settings.no_sign_request = collection.getOrDefault("no_sign_request", 0); + configuration.auth_settings.no_sign_request = collection.getOrDefault("no_sign_request", false); configuration.auth_settings.expiration_window_seconds = collection.getOrDefault("expiration_window_seconds", S3::DEFAULT_EXPIRATION_WINDOW_SECONDS); configuration.format = collection.getOrDefault("format", "auto"); From 8c8c7464514834135a37131567b66257fae84046 Mon Sep 17 00:00:00 2001 From: Nikolay Degterinsky <43110995+evillique@users.noreply.github.com> Date: Thu, 30 Mar 2023 09:04:36 +0200 Subject: [PATCH 233/377] Update FunctionsCodingULID.cpp --- src/Functions/FunctionsCodingULID.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/FunctionsCodingULID.cpp b/src/Functions/FunctionsCodingULID.cpp index bc62b2d9aca..3201578791a 100644 --- a/src/Functions/FunctionsCodingULID.cpp +++ b/src/Functions/FunctionsCodingULID.cpp @@ -49,7 +49,7 @@ public: DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { - if (arguments.size() < 1 || arguments.size() > 2) + if (arguments.empty() || arguments.size() > 2) throw Exception( ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Wrong number of arguments for function {}: should be 1 or 2", From 202dc90045497212f3e8b95381144e468b041cee Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 10:01:52 +0200 Subject: [PATCH 234/377] Randomize JIT settings in tests --- tests/clickhouse-test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/clickhouse-test b/tests/clickhouse-test index a355c2f8e73..4d16fead44f 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -558,6 +558,9 @@ class SettingsRandomizer: "enable_memory_bound_merging_of_aggregation_results": lambda: random.randint( 0, 1 ), + "min_count_to_compile_expression": lambda: random.randint(0, 3), + "min_count_to_compile_aggregate_expression": lambda: random.randint(0, 3), + "min_count_to_compile_sort_description": lambda: random.randint(0, 3), } @staticmethod From 990ef56443b270dc11b50a0ab20e5661b9d3aa31 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 10:55:29 +0200 Subject: [PATCH 235/377] Randomize JIT settings in tests --- tests/clickhouse-test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/clickhouse-test b/tests/clickhouse-test index 4d16fead44f..fa88bc19efd 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -558,9 +558,9 @@ class SettingsRandomizer: "enable_memory_bound_merging_of_aggregation_results": lambda: random.randint( 0, 1 ), - "min_count_to_compile_expression": lambda: random.randint(0, 3), - "min_count_to_compile_aggregate_expression": lambda: random.randint(0, 3), - "min_count_to_compile_sort_description": lambda: random.randint(0, 3), + "min_count_to_compile_expression": lambda: random.choice([0, 3]), + "min_count_to_compile_aggregate_expression": lambda: random.choice([0, 3]), + "min_count_to_compile_sort_description": lambda: random.choice([0, 3]), } @staticmethod From baabc49f339373f77395d0c8078208cf5f2616ea Mon Sep 17 00:00:00 2001 From: Ilya Yatsishin <2159081+qoega@users.noreply.github.com> Date: Thu, 30 Mar 2023 11:01:51 +0200 Subject: [PATCH 236/377] Update ParserKQLSort.cpp Style --- src/Parsers/Kusto/ParserKQLSort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parsers/Kusto/ParserKQLSort.cpp b/src/Parsers/Kusto/ParserKQLSort.cpp index f7540d729fd..ef4b84b17c7 100644 --- a/src/Parsers/Kusto/ParserKQLSort.cpp +++ b/src/Parsers/Kusto/ParserKQLSort.cpp @@ -27,7 +27,7 @@ bool ParserKQLSort :: parseImpl(Pos & pos, ASTPtr & node, Expected & expected) while (!new_pos->isEnd() && new_pos->type != TokenType::PipeMark && new_pos->type != TokenType::Semicolon) { String tmp(new_pos->begin, new_pos->end); - if (tmp == "desc" or tmp == "asc") + if (tmp == "desc" || tmp == "asc") has_dir = true; if (new_pos->type == TokenType::Comma) From 15a5372eb1e8d1c0e2f0644685f497d3f410de61 Mon Sep 17 00:00:00 2001 From: mateng915 Date: Thu, 30 Mar 2023 17:07:36 +0800 Subject: [PATCH 237/377] Update build.md Latest master branch and stable tag version using Clang-14 can not compile successfully. Need using Clang-15 --- docs/en/development/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/development/build.md b/docs/en/development/build.md index 804aa8a3dc5..00a8a54f80a 100644 --- a/docs/en/development/build.md +++ b/docs/en/development/build.md @@ -85,7 +85,7 @@ The build requires the following components: - Git (is used only to checkout the sources, it’s not needed for the build) - CMake 3.15 or newer - Ninja -- C++ compiler: clang-14 or newer +- C++ compiler: clang-15 or newer - Linker: lld - Yasm - Gawk From e464ef1f7af0313f4c74741852185639a5387a7c Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 30 Mar 2023 09:44:23 +0000 Subject: [PATCH 238/377] Remove support for std::unary/binary_function (removed in C++17) --- contrib/boost | 2 +- contrib/llvm-project | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/boost b/contrib/boost index 03d9ec9cd15..8fe7b3326ef 160000 --- a/contrib/boost +++ b/contrib/boost @@ -1 +1 @@ -Subproject commit 03d9ec9cd159d14bd0b17c05138098451a1ea606 +Subproject commit 8fe7b3326ef482ee6ecdf5a4f698f2b8c2780f98 diff --git a/contrib/llvm-project b/contrib/llvm-project index 4bfaeb31dd0..e0accd51793 160000 --- a/contrib/llvm-project +++ b/contrib/llvm-project @@ -1 +1 @@ -Subproject commit 4bfaeb31dd0ef13f025221f93c138974a3e0a22a +Subproject commit e0accd517933ebb44aff84bc8db448ffd8ef1929 From e3f4089f3b6923d0281c0fe66cf186d759b07a02 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Thu, 30 Mar 2023 18:00:38 +0800 Subject: [PATCH 239/377] fix bugs --- src/Functions/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/map.cpp b/src/Functions/map.cpp index e1dc58eb077..14453de0646 100644 --- a/src/Functions/map.cpp +++ b/src/Functions/map.cpp @@ -185,7 +185,7 @@ public: if (const auto * value_array_type = checkAndGetDataType(arguments[1].get())) value_type = value_array_type->getNestedType(); else if (const auto * value_map_type = checkAndGetDataType(arguments[1].get())) - value_type = value_map_type->getValueType(); + value_type = std::make_shared(value_map_type->getKeyValueTypes()); else throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Second argument for function {} must be Array or Map", getName()); From 7d6cd2241296fbaed23adda0de217cf8b8f64c29 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 12:07:28 +0200 Subject: [PATCH 240/377] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 122164bc9e3..70f663b8a1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ * Use `dummy UInt8` for default structure of table function `null`. Closes [#46930](https://github.com/ClickHouse/ClickHouse/issues/46930). [#47006](https://github.com/ClickHouse/ClickHouse/pull/47006) ([flynn](https://github.com/ucasfl)). * Support for date format with a comma, like `Dec 15, 2021` in the `parseDateTimeBestEffort` function. Closes [#46816](https://github.com/ClickHouse/ClickHouse/issues/46816). [#47071](https://github.com/ClickHouse/ClickHouse/pull/47071) ([chen](https://github.com/xiedeyantu)). * Add settings `http_wait_end_of_query` and `http_response_buffer_size` that corresponds to URL params `wait_end_of_query` and `buffer_size` for HTTP interface. This allows to change these settings in the profiles. [#47108](https://github.com/ClickHouse/ClickHouse/pull/47108) ([Vladimir C](https://github.com/vdimir)). -* Add `system.marked_dropped_tables` table that shows tables that were dropped from `Atomic` databases but were not completely removed yet. [#47364](https://github.com/ClickHouse/ClickHouse/pull/47364) ([chen](https://github.com/xiedeyantu)). +* Add `system.dropped_tables` table that shows tables that were dropped from `Atomic` databases but were not completely removed yet. [#47364](https://github.com/ClickHouse/ClickHouse/pull/47364) ([chen](https://github.com/xiedeyantu)). * Add `INSTR` as alias of `positionCaseInsensitive` for MySQL compatibility. Closes [#47529](https://github.com/ClickHouse/ClickHouse/issues/47529). [#47535](https://github.com/ClickHouse/ClickHouse/pull/47535) ([flynn](https://github.com/ucasfl)). * Added `toDecimalString` function allowing to convert numbers to string with fixed precision. [#47838](https://github.com/ClickHouse/ClickHouse/pull/47838) ([Andrey Zvonov](https://github.com/zvonand)). * Add a merge tree setting `max_number_of_mutations_for_replica`. It limits the number of part mutations per replica to the specified amount. Zero means no limit on the number of mutations per replica (the execution can still be constrained by other settings). [#48047](https://github.com/ClickHouse/ClickHouse/pull/48047) ([Vladimir C](https://github.com/vdimir)). From 103972497b661ec9b2f7066d198d6db10bc57f1b Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 12:07:56 +0200 Subject: [PATCH 241/377] Remove unused setting --- src/Core/ServerSettings.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Core/ServerSettings.h b/src/Core/ServerSettings.h index e780424507c..efa41a5ec13 100644 --- a/src/Core/ServerSettings.h +++ b/src/Core/ServerSettings.h @@ -29,7 +29,6 @@ namespace DB M(Int32, max_connections, 1024, "Max server connections.", 0) \ M(UInt32, asynchronous_metrics_update_period_s, 1, "Period in seconds for updating asynchronous metrics.", 0) \ M(UInt32, asynchronous_heavy_metrics_update_period_s, 120, "Period in seconds for updating asynchronous metrics.", 0) \ - M(UInt32, max_threads_for_connection_collector, 10, "The maximum number of threads that will be used for draining connections asynchronously in a background upon finishing executing distributed queries.", 0) \ M(String, default_database, "default", "Default database name.", 0) \ M(String, tmp_policy, "", "Policy for storage with temporary data.", 0) \ M(UInt64, max_temporary_data_on_disk_size, 0, "The maximum amount of storage that could be used for external aggregation, joins or sorting., ", 0) \ From 2a35c189732fb36261dcf63b3885ab364600de79 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Thu, 30 Mar 2023 18:18:21 +0800 Subject: [PATCH 242/377] add docs and uts --- .../functions/tuple-map-functions.md | 20 +++++++++++-------- .../0_stateless/01651_map_functions.reference | 3 +++ .../0_stateless/01651_map_functions.sql | 5 +++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/en/sql-reference/functions/tuple-map-functions.md b/docs/en/sql-reference/functions/tuple-map-functions.md index 34c865e7752..cdecbbcc2e9 100644 --- a/docs/en/sql-reference/functions/tuple-map-functions.md +++ b/docs/en/sql-reference/functions/tuple-map-functions.md @@ -68,9 +68,9 @@ Result: ## mapFromArrays -Merges an [Array](../../sql-reference/data-types/array.md) of keys and an [Array](../../sql-reference/data-types/array.md) of values into a [Map(key, value)](../../sql-reference/data-types/map.md). +Merges an [Array](../../sql-reference/data-types/array.md) of keys and an [Array](../../sql-reference/data-types/array.md) of values into a [Map(key, value)](../../sql-reference/data-types/map.md). Notice that the second argument could also be a [Map](../../sql-reference/data-types/map.md), thus it is casted to an Array when executing. -The function is a more convenient alternative to `CAST((key_array, value_array), 'Map(key_type, value_type)')`. For example, instead of writing `CAST((['aa', 'bb'], [4, 5]), 'Map(String, UInt32)')`, you can write `mapFromArrays(['aa', 'bb'], [4, 5])`. +The function is a more convenient alternative to `CAST((key_array, value_array_or_map), 'Map(key_type, value_type)')`. For example, instead of writing `CAST((['aa', 'bb'], [4, 5]), 'Map(String, UInt32)')`, you can write `mapFromArrays(['aa', 'bb'], [4, 5])`. **Syntax** @@ -82,11 +82,11 @@ Alias: `MAP_FROM_ARRAYS(keys, values)` **Arguments** - `keys` — Given key array to create a map from. The nested type of array must be: [String](../../sql-reference/data-types/string.md), [Integer](../../sql-reference/data-types/int-uint.md), [LowCardinality](../../sql-reference/data-types/lowcardinality.md), [FixedString](../../sql-reference/data-types/fixedstring.md), [UUID](../../sql-reference/data-types/uuid.md), [Date](../../sql-reference/data-types/date.md), [DateTime](../../sql-reference/data-types/datetime.md), [Date32](../../sql-reference/data-types/date32.md), [Enum](../../sql-reference/data-types/enum.md) -- `values` - Given value array to create a map from. +- `values` - Given value array or map to create a map from. **Returned value** -- A map whose keys and values are constructed from the key and value arrays +- A map whose keys and values are constructed from the key array and value array/map. **Example** @@ -94,13 +94,17 @@ Query: ```sql select mapFromArrays(['a', 'b', 'c'], [1, 2, 3]) -``` - -```text + ┌─mapFromArrays(['a', 'b', 'c'], [1, 2, 3])─┐ │ {'a':1,'b':2,'c':3} │ └───────────────────────────────────────────┘ -``` + +SELECT mapFromArrays([1, 2, 3], map('a', 1, 'b', 2, 'c', 3)) + +┌─mapFromArrays([1, 2, 3], map('a', 1, 'b', 2, 'c', 3))─┐ +│ {1:('a',1),2:('b',2),3:('c',3)} │ +└───────────────────────────────────────────────────────┘ +``` ## mapAdd diff --git a/tests/queries/0_stateless/01651_map_functions.reference b/tests/queries/0_stateless/01651_map_functions.reference index f7fd3503327..60f1b6e0d0c 100644 --- a/tests/queries/0_stateless/01651_map_functions.reference +++ b/tests/queries/0_stateless/01651_map_functions.reference @@ -33,3 +33,6 @@ {'aa':4,'bb':5} {'aa':4,'bb':5} {'aa':4,'bb':5} +{'aa':('a',4),'bb':('b',5)} +{'aa':('a',4),'bb':('b',5)} +{'aa':('a',4),'bb':('b',5)} diff --git a/tests/queries/0_stateless/01651_map_functions.sql b/tests/queries/0_stateless/01651_map_functions.sql index 848b6932fe0..5942bf8b2c2 100644 --- a/tests/queries/0_stateless/01651_map_functions.sql +++ b/tests/queries/0_stateless/01651_map_functions.sql @@ -39,3 +39,8 @@ select mapFromArrays(['aa', 'bb'], 5); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT select mapFromArrays(['aa', 'bb'], [4, 5], [6, 7]); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } select mapFromArrays(['aa', 'bb'], [4, 5, 6]); -- { serverError SIZES_OF_ARRAYS_DONT_MATCH } select mapFromArrays([[1,2], [3,4]], [4, 5, 6]); -- { serverError BAD_ARGUMENTS } + +select mapFromArrays(['aa', 'bb'], map('a', 4, 'b', 5)); +select mapFromArrays(['aa', 'bb'], materialize(map('a', 4, 'b', 5))) from numbers(2); +select mapFromArrays(map('a', 4, 'b', 4), ['aa', 'bb']) from numbers(2); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +select mapFromArrays(['aa', 'bb'], map('a', 4)); -- { serverError SIZES_OF_ARRAYS_DONT_MATCH } From b22d3e913620a7bd8dc805d520a94f7824910610 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 30 Mar 2023 09:55:01 +0000 Subject: [PATCH 243/377] Remove wrong assert --- .../Passes/LogicalExpressionOptimizerPass.cpp | 1 - ...702_logical_optimizer_with_nulls.reference | 19 +++++++++++++++++++ .../02702_logical_optimizer_with_nulls.sql | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp b/src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp index 97669f3924f..13f8025f5ea 100644 --- a/src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp +++ b/src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp @@ -219,7 +219,6 @@ private: /// we can replace OR with the operand if (or_operands[0]->getResultType()->equals(*function_node.getResultType())) { - assert(!function_node.getResultType()->isNullable()); node = std::move(or_operands[0]); return; } diff --git a/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.reference b/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.reference index 263329e47be..eb79bbc842a 100644 --- a/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.reference +++ b/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.reference @@ -56,3 +56,22 @@ QUERY id: 0 LIST id: 12, nodes: 2 COLUMN id: 9, column_name: a, result_type: Int32, source_id: 3 CONSTANT id: 13, constant_value: Tuple_(UInt64_1, UInt64_3, UInt64_2), constant_value_type: Tuple(UInt8, UInt8, UInt8) +1 test +2 test2 +3 another +QUERY id: 0 + PROJECTION COLUMNS + a Nullable(Int32) + b LowCardinality(String) + PROJECTION + LIST id: 1, nodes: 2 + COLUMN id: 2, column_name: a, result_type: Nullable(Int32), source_id: 3 + COLUMN id: 4, column_name: b, result_type: LowCardinality(String), source_id: 3 + JOIN TREE + TABLE id: 3, table_name: default.02702_logical_optimizer_with_null_column + WHERE + FUNCTION id: 5, function_name: in, function_type: ordinary, result_type: Nullable(UInt8) + ARGUMENTS + LIST id: 6, nodes: 2 + COLUMN id: 7, column_name: a, result_type: Nullable(Int32), source_id: 3 + CONSTANT id: 8, constant_value: Tuple_(UInt64_1, UInt64_3, UInt64_2), constant_value_type: Tuple(UInt8, UInt8, UInt8) diff --git a/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.sql b/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.sql index 9a49e31fe81..07d0b170a02 100644 --- a/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.sql +++ b/tests/queries/0_stateless/02702_logical_optimizer_with_nulls.sql @@ -15,3 +15,18 @@ EXPLAIN QUERY TREE SELECT * FROM 02702_logical_optimizer WHERE a = 1 OR 3 = a OR SELECT * FROM 02702_logical_optimizer WHERE a = 1 OR 3 = a OR 2 = a OR a = NULL; EXPLAIN QUERY TREE SELECT * FROM 02702_logical_optimizer WHERE a = 1 OR 3 = a OR 2 = a OR a = NULL; + +DROP TABLE 02702_logical_optimizer; + +DROP TABLE IF EXISTS 02702_logical_optimizer_with_null_column; + +CREATE TABLE 02702_logical_optimizer_with_null_column +(a Nullable(Int32), b LowCardinality(String)) +ENGINE=Memory; + +INSERT INTO 02702_logical_optimizer_with_null_column VALUES (1, 'test'), (2, 'test2'), (3, 'another'); + +SELECT * FROM 02702_logical_optimizer_with_null_column WHERE a = 1 OR 3 = a OR 2 = a; +EXPLAIN QUERY TREE SELECT * FROM 02702_logical_optimizer_with_null_column WHERE a = 1 OR 3 = a OR 2 = a; + +DROP TABLE 02702_logical_optimizer_with_null_column; From 2df32324af0a3dba04fe688b5158ec6bff59cf2e Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 30 Mar 2023 12:03:52 +0000 Subject: [PATCH 244/377] MySQL compatibility: Make str_to_date alias case-insensitive MySQL doesn't care about the case --- src/Functions/parseDateTime.cpp | 2 +- tests/queries/0_stateless/02668_parse_datetime.reference | 2 ++ tests/queries/0_stateless/02668_parse_datetime.sql | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Functions/parseDateTime.cpp b/src/Functions/parseDateTime.cpp index 6a7a6010d4b..553e993a806 100644 --- a/src/Functions/parseDateTime.cpp +++ b/src/Functions/parseDateTime.cpp @@ -1856,7 +1856,7 @@ REGISTER_FUNCTION(ParseDateTime) factory.registerAlias("TO_UNIXTIME", FunctionParseDateTime::name); factory.registerFunction(); factory.registerFunction(); - factory.registerAlias("str_to_date", FunctionParseDateTimeOrNull::name); + factory.registerAlias("str_to_date", FunctionParseDateTimeOrNull::name, FunctionFactory::CaseInsensitive); factory.registerFunction(); factory.registerFunction(); diff --git a/tests/queries/0_stateless/02668_parse_datetime.reference b/tests/queries/0_stateless/02668_parse_datetime.reference index f39655c6a41..afa3d0eb962 100644 --- a/tests/queries/0_stateless/02668_parse_datetime.reference +++ b/tests/queries/0_stateless/02668_parse_datetime.reference @@ -208,5 +208,7 @@ select parseDateTimeOrNull('10:04:11 invalid 03-07-2019', '%s:%i:%H %d-%m-%Y', ' 1 select str_to_date('10:04:11 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') = toDateTime('2019-07-03 11:04:10', 'UTC'); 1 +select sTr_To_DaTe('10:04:11 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') = toDateTime('2019-07-03 11:04:10', 'UTC'); +1 select str_to_date('10:04:11 invalid 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') IS NULL; 1 diff --git a/tests/queries/0_stateless/02668_parse_datetime.sql b/tests/queries/0_stateless/02668_parse_datetime.sql index 757c4fe2efe..51c2fda8428 100644 --- a/tests/queries/0_stateless/02668_parse_datetime.sql +++ b/tests/queries/0_stateless/02668_parse_datetime.sql @@ -138,6 +138,7 @@ select parseDateTimeOrZero('10:04:11 invalid 03-07-2019', '%s:%i:%H %d-%m-%Y', ' select parseDateTimeOrNull('10:04:11 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') = toDateTime('2019-07-03 11:04:10', 'UTC'); select parseDateTimeOrNull('10:04:11 invalid 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') IS NULL; select str_to_date('10:04:11 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') = toDateTime('2019-07-03 11:04:10', 'UTC'); +select sTr_To_DaTe('10:04:11 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') = toDateTime('2019-07-03 11:04:10', 'UTC'); select str_to_date('10:04:11 invalid 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') IS NULL; -- { echoOff } From ad246d669e85755bb6bc88169479bd6e50379afb Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 30 Mar 2023 12:08:52 +0000 Subject: [PATCH 245/377] Disable AST optimizations for projection analysis. --- .../Optimizations/optimizeUseAggregateProjection.cpp | 6 +++++- .../25402_projection_and_ast_optimizations_bug.reference | 1 + .../25402_projection_and_ast_optimizations_bug.sql | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.reference create mode 100644 tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.sql diff --git a/src/Processors/QueryPlan/Optimizations/optimizeUseAggregateProjection.cpp b/src/Processors/QueryPlan/Optimizations/optimizeUseAggregateProjection.cpp index 77b5547207c..21cb112cb14 100644 --- a/src/Processors/QueryPlan/Optimizations/optimizeUseAggregateProjection.cpp +++ b/src/Processors/QueryPlan/Optimizations/optimizeUseAggregateProjection.cpp @@ -61,11 +61,15 @@ static AggregateProjectionInfo getAggregatingProjectionInfo( /// This is a bad approach. /// We'd better have a separate interpreter for projections. /// Now it's not obvious we didn't miss anything here. + /// + /// Setting ignoreASTOptimizations is used because some of them are invalid for projections. + /// Example: 'SELECT min(c0), max(c0), count() GROUP BY -c0' for minmax_count projection can be rewritten to + /// 'SELECT min(c0), max(c0), count() GROUP BY c0' which is incorrect cause we store a column '-c0' in projection. InterpreterSelectQuery interpreter( projection.query_ast, context, Pipe(std::make_shared(metadata_snapshot->getSampleBlock())), - SelectQueryOptions{QueryProcessingStage::WithMergeableState}); + SelectQueryOptions{QueryProcessingStage::WithMergeableState}.ignoreASTOptimizations()); const auto & analysis_result = interpreter.getAnalysisResult(); const auto & query_analyzer = interpreter.getQueryAnalyzer(); diff --git a/tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.reference b/tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.reference new file mode 100644 index 00000000000..9049324c392 --- /dev/null +++ b/tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.reference @@ -0,0 +1 @@ +-2.5574077246549023 0.6663667453928805 1 diff --git a/tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.sql b/tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.sql new file mode 100644 index 00000000000..5589fbeeb9e --- /dev/null +++ b/tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.sql @@ -0,0 +1,6 @@ +drop table if exists t1; +CREATE TABLE t1 (c0 Int32) ENGINE = MergeTree() ORDER BY c0 PARTITION BY (- (c0)); +insert into t1 values(1); +SELECT (- ((((tan (t1.c0)))+(t1.c0)))), (cos ((sin (pow(t1.c0,t1.c0))))), ((gcd((- (t1.c0)),((t1.c0)+(t1.c0))))*((- ((- (t1.c0)))))) FROM t1 GROUP BY (sqrt ((- (t1.c0)))), t1.c0, pow((erf ((- (t1.c0)))),t1.c0); +drop table t1; + From 8b2fc8a40a0d7072965714083908dd25e81fe18d Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 30 Mar 2023 12:24:52 +0000 Subject: [PATCH 246/377] parseDateTime[InJodaSyntax](): Require format argument Function parseDateTime[InJodaSyntax]() previously accepted an (undocumented) default format string. The problem was that 1. there is no "natural" default format 2. MySQL str_to_date() (= the motivation for parseDateTime[InJodaSyntax]()) also provides no default format. --> get rid of the default format --- src/Functions/parseDateTime.cpp | 16 ++++------------ .../0_stateless/02668_parse_datetime.reference | 3 +++ .../queries/0_stateless/02668_parse_datetime.sql | 4 ++++ ...02668_parse_datetime_in_joda_syntax.reference | 3 +++ .../02668_parse_datetime_in_joda_syntax.sql | 3 +++ 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Functions/parseDateTime.cpp b/src/Functions/parseDateTime.cpp index 6a7a6010d4b..785c423f3ea 100644 --- a/src/Functions/parseDateTime.cpp +++ b/src/Functions/parseDateTime.cpp @@ -480,10 +480,10 @@ namespace DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { - if (arguments.size() != 1 && arguments.size() != 2 && arguments.size() != 3) + if (arguments.size() != 2 && arguments.size() != 3) throw Exception( ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, - "Number of arguments for function {} doesn't match: passed {}, should be 1, 2 or 3", + "Number of arguments for function {} doesn't match: passed {}, should be 2 or 3", getName(), arguments.size()); @@ -494,14 +494,14 @@ namespace arguments[0].type->getName(), getName()); - if (arguments.size() > 1 && !isString(arguments[1].type)) + if (!isString(arguments[1].type)) throw Exception( ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of second argument of function {}. Should be String", arguments[0].type->getName(), getName()); - if (arguments.size() > 2 && !isString(arguments[2].type)) + if (arguments.size() == 3 && !isString(arguments[2].type)) throw Exception( ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of third argument of function {}. Should be String", @@ -1776,14 +1776,6 @@ namespace String getFormat(const ColumnsWithTypeAndName & arguments) const { - if (arguments.size() < 2) - { - if constexpr (parse_syntax == ParseSyntax::Joda) - return "yyyy-MM-dd HH:mm:ss"; - else - return "%Y-%m-%d %H:%M:%S"; - } - const auto * format_column = checkAndGetColumnConst(arguments[1].column.get()); if (!format_column) throw Exception( diff --git a/tests/queries/0_stateless/02668_parse_datetime.reference b/tests/queries/0_stateless/02668_parse_datetime.reference index f39655c6a41..c60f6e6467d 100644 --- a/tests/queries/0_stateless/02668_parse_datetime.reference +++ b/tests/queries/0_stateless/02668_parse_datetime.reference @@ -210,3 +210,6 @@ select str_to_date('10:04:11 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') = toDateTi 1 select str_to_date('10:04:11 invalid 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') IS NULL; 1 +-- Error handling +select parseDateTime('12 AM'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +select parseDateTime('12 AM', '%h %p', 'UTC', 'a fourth argument'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } diff --git a/tests/queries/0_stateless/02668_parse_datetime.sql b/tests/queries/0_stateless/02668_parse_datetime.sql index 757c4fe2efe..d6ee6cb617c 100644 --- a/tests/queries/0_stateless/02668_parse_datetime.sql +++ b/tests/queries/0_stateless/02668_parse_datetime.sql @@ -140,4 +140,8 @@ select parseDateTimeOrNull('10:04:11 invalid 03-07-2019', '%s:%i:%H %d-%m-%Y', ' select str_to_date('10:04:11 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') = toDateTime('2019-07-03 11:04:10', 'UTC'); select str_to_date('10:04:11 invalid 03-07-2019', '%s:%i:%H %d-%m-%Y', 'UTC') IS NULL; +-- Error handling +select parseDateTime('12 AM'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +select parseDateTime('12 AM', '%h %p', 'UTC', 'a fourth argument'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } + -- { echoOff } diff --git a/tests/queries/0_stateless/02668_parse_datetime_in_joda_syntax.reference b/tests/queries/0_stateless/02668_parse_datetime_in_joda_syntax.reference index 124836d6118..9fbf105dc41 100644 --- a/tests/queries/0_stateless/02668_parse_datetime_in_joda_syntax.reference +++ b/tests/queries/0_stateless/02668_parse_datetime_in_joda_syntax.reference @@ -353,3 +353,6 @@ select parseDateTimeInJodaSyntaxOrNull('2001 366 2000', 'yyyy D yyyy', 'UTC') = 1 select parseDateTimeInJodaSyntaxOrNull('2001 invalid 366 2000', 'yyyy D yyyy', 'UTC') IS NULL; 1 +-- Error handling +select parseDateTimeInJodaSyntax('12 AM'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +select parseDateTimeInJodaSyntax('12 AM', 'h a', 'UTC', 'a fourth argument'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } diff --git a/tests/queries/0_stateless/02668_parse_datetime_in_joda_syntax.sql b/tests/queries/0_stateless/02668_parse_datetime_in_joda_syntax.sql index b2c781432d1..f5810d3d4c3 100644 --- a/tests/queries/0_stateless/02668_parse_datetime_in_joda_syntax.sql +++ b/tests/queries/0_stateless/02668_parse_datetime_in_joda_syntax.sql @@ -238,4 +238,7 @@ select parseDateTimeInJodaSyntaxOrZero('2001 invalid 366 2000', 'yyyy D yyyy', ' select parseDateTimeInJodaSyntaxOrNull('2001 366 2000', 'yyyy D yyyy', 'UTC') = toDateTime('2000-12-31', 'UTC'); select parseDateTimeInJodaSyntaxOrNull('2001 invalid 366 2000', 'yyyy D yyyy', 'UTC') IS NULL; +-- Error handling +select parseDateTimeInJodaSyntax('12 AM'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +select parseDateTimeInJodaSyntax('12 AM', 'h a', 'UTC', 'a fourth argument'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } -- { echoOff } From 372948fb4c745d92fefdfe3fd56570350d804a7c Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Thu, 30 Mar 2023 09:30:45 -0300 Subject: [PATCH 247/377] Update type-conversion-functions.md --- docs/en/sql-reference/functions/type-conversion-functions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index c49ec92067c..71613ae25c4 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -13,7 +13,7 @@ incompatible datatypes (for example from `String` to `Int`). Make sure to check ClickHouse generally uses the [same behavior as C++ programs](https://en.cppreference.com/w/cpp/language/implicit_conversion). -`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md), this behaviour is not compatible with SQL standart, and it can changed using [cast_keep_nullable](/docs/en/operations/settings/settings.md/#cast_keep_nullable) setting. +`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md), this behaviour is not compatible with SQL standart, and it can changed using [cast_keep_nullable](../../../operations/settings/settings.md/#cast_keep_nullable) setting. Example: @@ -1028,7 +1028,7 @@ Result: **See also** -- [cast_keep_nullable](/docs/en/operations/settings/settings.md/#cast_keep_nullable) setting +- [cast_keep_nullable](../../../operations/settings/settings.md/#cast_keep_nullable) setting ## accurateCast(x, T) From 0e113ad829ae6bc7e2b34cf680b95c37b246d6fa Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 14:31:56 +0200 Subject: [PATCH 248/377] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70f663b8a1d..a09e4d4fe24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ * Now `X-ClickHouse-Query-Id` and `X-ClickHouse-Timezone` headers are added to response in all queries via http protocol. Previously it was done only for `SELECT` queries. [#46364](https://github.com/ClickHouse/ClickHouse/pull/46364) ([Anton Popov](https://github.com/CurtizJ)). * External tables from `MongoDB`: support for connection to a replica set via a URI with a host:port enum and support for the readPreference option in MongoDB dictionaries. Example URI: mongodb://db0.example.com:27017,db1.example.com:27017,db2.example.com:27017/?replicaSet=myRepl&readPreference=primary. [#46524](https://github.com/ClickHouse/ClickHouse/pull/46524) ([artem-yadr](https://github.com/artem-yadr)). * This improvement should be invisible for users. Re-implement projection analysis on top of query plan. Added setting `query_plan_optimize_projection=1` to switch between old and new version. Fixes [#44963](https://github.com/ClickHouse/ClickHouse/issues/44963). [#46537](https://github.com/ClickHouse/ClickHouse/pull/46537) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). -* Use parquet format v2 instead of v1 in output format by default. Add setting `output_format_parquet_version` to control parquet version, possible values `v1_0`, `v2_4`, `v2_6`, `v2_latest` (default). [#46617](https://github.com/ClickHouse/ClickHouse/pull/46617) ([Kruglov Pavel](https://github.com/Avogar)). +* Use parquet format v2 instead of v1 in output format by default. Add setting `output_format_parquet_version` to control parquet version, possible values `1.0`, `2.4`, `2.6`, `2.latest` (default). [#46617](https://github.com/ClickHouse/ClickHouse/pull/46617) ([Kruglov Pavel](https://github.com/Avogar)). * It is now possible using new configuration syntax to configure Kafka topics with periods (`.`) in their name. [#46752](https://github.com/ClickHouse/ClickHouse/pull/46752) ([Robert Schulze](https://github.com/rschu1ze)). * Fix heuristics that check hyperscan patterns for problematic repeats. [#46819](https://github.com/ClickHouse/ClickHouse/pull/46819) ([Robert Schulze](https://github.com/rschu1ze)). * Don't report ZK node exists to system.errors when a block was created concurrently by a different replica. [#46820](https://github.com/ClickHouse/ClickHouse/pull/46820) ([Raúl Marín](https://github.com/Algunenano)). From a44d2bf7245c9f816bfe70d15aba7d1a32addaab Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Thu, 30 Mar 2023 09:32:32 -0300 Subject: [PATCH 249/377] Update type-conversion-functions.md --- docs/en/sql-reference/functions/type-conversion-functions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 71613ae25c4..16d3787c5ce 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -13,7 +13,7 @@ incompatible datatypes (for example from `String` to `Int`). Make sure to check ClickHouse generally uses the [same behavior as C++ programs](https://en.cppreference.com/w/cpp/language/implicit_conversion). -`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md), this behaviour is not compatible with SQL standart, and it can changed using [cast_keep_nullable](../../../operations/settings/settings.md/#cast_keep_nullable) setting. +`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md), this behaviour is not compatible with SQL standart, and it can changed using [cast_keep_nullable](../../operations/settings/settings.md/#cast_keep_nullable) setting. Example: @@ -1028,7 +1028,7 @@ Result: **See also** -- [cast_keep_nullable](../../../operations/settings/settings.md/#cast_keep_nullable) setting +- [cast_keep_nullable](../../operations/settings/settings.md/#cast_keep_nullable) setting ## accurateCast(x, T) From 014db1fbaf787474a4629abfc84f38b30ad3e93a Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Thu, 30 Mar 2023 15:45:33 +0300 Subject: [PATCH 250/377] Update docs/get-clickhouse-docs.sh Co-authored-by: Mikhail f. Shiryaev --- docs/get-clickhouse-docs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/get-clickhouse-docs.sh b/docs/get-clickhouse-docs.sh index 34db4e74cee..92ba8058dcc 100755 --- a/docs/get-clickhouse-docs.sh +++ b/docs/get-clickhouse-docs.sh @@ -36,6 +36,8 @@ else read -rp "Would you like to setup git hook for automatic update? (y|n): " set_git_hook fi + git clone "$git_url" "clickhouse-docs" + if [ "$set_git_hook" = "y" ]; then hook_command="$(pwd)/pull-clickhouse-docs-hook.sh $UPDATE_PERIOD_HOURS ||:" hook_file=$(realpath "$(pwd)/../.git/hooks/post-checkout") @@ -49,6 +51,4 @@ else elif [ ! "$set_git_hook" = "n" ]; then echo "Expected 'y' or 'n', got '$set_git_hook', will not setup git hook" fi - - git clone "$git_url" "clickhouse-docs" fi From 07f11b6732a91a91b50413f7fb2a27a60152bc11 Mon Sep 17 00:00:00 2001 From: vdimir Date: Thu, 30 Mar 2023 13:37:49 +0000 Subject: [PATCH 251/377] Fix Too big of a difference between test numbers --- ...3_explain_query_tree_is_forbidden_with_old_analyzer.reference} | 0 ...> 02703_explain_query_tree_is_forbidden_with_old_analyzer.sql} | 0 ...eference => 02704_storage_merge_explain_graph_crash.reference} | 0 ...raph_crash.sql => 02704_storage_merge_explain_graph_crash.sql} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename tests/queries/0_stateless/{25402_explain_query_tree_is_forbidden_with_old_analyzer.reference => 02703_explain_query_tree_is_forbidden_with_old_analyzer.reference} (100%) rename tests/queries/0_stateless/{25402_explain_query_tree_is_forbidden_with_old_analyzer.sql => 02703_explain_query_tree_is_forbidden_with_old_analyzer.sql} (100%) rename tests/queries/0_stateless/{25402_storage_merge_explain_graph_crash.reference => 02704_storage_merge_explain_graph_crash.reference} (100%) rename tests/queries/0_stateless/{25402_storage_merge_explain_graph_crash.sql => 02704_storage_merge_explain_graph_crash.sql} (100%) diff --git a/tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.reference b/tests/queries/0_stateless/02703_explain_query_tree_is_forbidden_with_old_analyzer.reference similarity index 100% rename from tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.reference rename to tests/queries/0_stateless/02703_explain_query_tree_is_forbidden_with_old_analyzer.reference diff --git a/tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.sql b/tests/queries/0_stateless/02703_explain_query_tree_is_forbidden_with_old_analyzer.sql similarity index 100% rename from tests/queries/0_stateless/25402_explain_query_tree_is_forbidden_with_old_analyzer.sql rename to tests/queries/0_stateless/02703_explain_query_tree_is_forbidden_with_old_analyzer.sql diff --git a/tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.reference b/tests/queries/0_stateless/02704_storage_merge_explain_graph_crash.reference similarity index 100% rename from tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.reference rename to tests/queries/0_stateless/02704_storage_merge_explain_graph_crash.reference diff --git a/tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.sql b/tests/queries/0_stateless/02704_storage_merge_explain_graph_crash.sql similarity index 100% rename from tests/queries/0_stateless/25402_storage_merge_explain_graph_crash.sql rename to tests/queries/0_stateless/02704_storage_merge_explain_graph_crash.sql From d6c71533fae14635efdbda83ebe20d62226a9497 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Thu, 30 Mar 2023 08:20:20 +0000 Subject: [PATCH 252/377] Move keeper map tests to stateless --- tests/integration/test_keeper_map/test.py | 110 ------------------ ...eeper_map_concurrent_create_drop.reference | 1 + ...02703_keeper_map_concurrent_create_drop.sh | 53 +++++++++ .../02704_keeper_map_zk_nodes.reference | 0 .../0_stateless/02704_keeper_map_zk_nodes.sh | 77 ++++++++++++ 5 files changed, 131 insertions(+), 110 deletions(-) create mode 100644 tests/queries/0_stateless/02703_keeper_map_concurrent_create_drop.reference create mode 100755 tests/queries/0_stateless/02703_keeper_map_concurrent_create_drop.sh create mode 100644 tests/queries/0_stateless/02704_keeper_map_zk_nodes.reference create mode 100755 tests/queries/0_stateless/02704_keeper_map_zk_nodes.sh diff --git a/tests/integration/test_keeper_map/test.py b/tests/integration/test_keeper_map/test.py index 4b940fbf1d1..c6ec7103056 100644 --- a/tests/integration/test_keeper_map/test.py +++ b/tests/integration/test_keeper_map/test.py @@ -1,14 +1,6 @@ -import multiprocessing import pytest -from time import sleep -import random -from itertools import count -from sys import stdout - -from multiprocessing.dummy import Pool from helpers.cluster import ClickHouseCluster -from helpers.test_tools import assert_eq_with_retry, assert_logs_contain from helpers.network import PartitionManager test_recover_staled_replica_run = 1 @@ -46,108 +38,6 @@ def remove_children(client, path): client.delete(child_path) -def test_create_keeper_map(started_cluster): - node.query( - "CREATE TABLE test_keeper_map (key UInt64, value UInt64) ENGINE = KeeperMap('/test1') PRIMARY KEY(key);" - ) - zk_client = get_genuine_zk() - - def assert_children_size(path, expected_size): - children_size = 0 - # 4 secs should be more than enough for replica to sync - for _ in range(10): - children_size = len(zk_client.get_children(path)) - if children_size == expected_size: - return - sleep(0.4) - assert ( - False - ), f"Invalid number of children for '{path}': actual {children_size}, expected {expected_size}" - - def assert_root_children_size(expected_size): - assert_children_size("/test_keeper_map/test1", expected_size) - - def assert_data_children_size(expected_size): - assert_children_size("/test_keeper_map/test1/data", expected_size) - - assert_root_children_size(2) - assert_data_children_size(0) - - node.query("INSERT INTO test_keeper_map VALUES (1, 11)") - assert_data_children_size(1) - - node.query( - "CREATE TABLE test_keeper_map_another (key UInt64, value UInt64) ENGINE = KeeperMap('/test1') PRIMARY KEY(key);" - ) - assert_root_children_size(2) - assert_data_children_size(1) - - node.query("INSERT INTO test_keeper_map_another VALUES (1, 11)") - assert_root_children_size(2) - assert_data_children_size(1) - - node.query("INSERT INTO test_keeper_map_another VALUES (2, 22)") - assert_root_children_size(2) - assert_data_children_size(2) - - node.query("DROP TABLE test_keeper_map SYNC") - assert_root_children_size(2) - assert_data_children_size(2) - - node.query("DROP TABLE test_keeper_map_another SYNC") - assert_root_children_size(0) - - zk_client.stop() - - -def create_drop_loop(index, stop_event): - table_name = f"test_keeper_map_{index}" - - for i in count(0, 1): - if stop_event.is_set(): - return - - node.query_with_retry( - f"CREATE TABLE IF NOT EXISTS {table_name} (key UInt64, value UInt64) ENGINE = KeeperMap('/test') PRIMARY KEY(key);" - ) - node.query_with_retry(f"INSERT INTO {table_name} VALUES ({index}, {i})") - result = node.query_with_retry( - f"SELECT value FROM {table_name} WHERE key = {index}" - ) - assert result.strip() == str(i) - node.query_with_retry(f"DROP TABLE IF EXISTS {table_name} SYNC") - - -def test_create_drop_keeper_map_concurrent(started_cluster): - pool = Pool() - manager = multiprocessing.Manager() - stop_event = manager.Event() - results = [] - for i in range(8): - sleep(0.2) - results.append( - pool.apply_async( - create_drop_loop, - args=( - i, - stop_event, - ), - ) - ) - - sleep(60) - stop_event.set() - - for result in results: - result.get() - - pool.close() - - client = get_genuine_zk() - assert len(client.get_children("/test_keeper_map/test")) == 0 - client.stop() - - def test_keeper_map_without_zk(started_cluster): def assert_keeper_exception_after_partition(query): with PartitionManager() as pm: diff --git a/tests/queries/0_stateless/02703_keeper_map_concurrent_create_drop.reference b/tests/queries/0_stateless/02703_keeper_map_concurrent_create_drop.reference new file mode 100644 index 00000000000..573541ac970 --- /dev/null +++ b/tests/queries/0_stateless/02703_keeper_map_concurrent_create_drop.reference @@ -0,0 +1 @@ +0 diff --git a/tests/queries/0_stateless/02703_keeper_map_concurrent_create_drop.sh b/tests/queries/0_stateless/02703_keeper_map_concurrent_create_drop.sh new file mode 100755 index 00000000000..3964427895c --- /dev/null +++ b/tests/queries/0_stateless/02703_keeper_map_concurrent_create_drop.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Tags: no-ordinary-database, zookeeper, no-fasttest, no-parallel + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +function create_drop_loop() +{ + table_name="02703_keeper_map_concurrent_$1" + $CLICKHOUSE_CLIENT --query="DROP TABLE IF EXISTS $table_name" + for _ in `seq $1` + do + sleep 0.3 + done + + i=0 + while true; + do + $CLICKHOUSE_CLIENT --query="CREATE TABLE IF NOT EXISTS $table_name (key UInt64, value UInt64) ENGINE = KeeperMap('/02703_keeper_map/$CLICKHOUSE_DATABASE') PRIMARY KEY(key)" + $CLICKHOUSE_CLIENT --query="INSERT INTO $table_name VALUES ($1, $i)" + result=$($CLICKHOUSE_CLIENT --query="SELECT value FROM $table_name WHERE key = $1") + + if [ $result != $i ] + then + echo "Got invalid result $result" + exit 1 + fi + + $CLICKHOUSE_CLIENT --query="DROP TABLE $table_name" + + ((++i)) + done +} + +export -f create_drop_loop; + +THREADS=10 +TIMEOUT=30 + +for i in `seq $THREADS` +do + timeout $TIMEOUT bash -c "create_drop_loop $i" 2> /dev/null & +done + +wait + +for i in `seq $THREADS` +do + $CLICKHOUSE_CLIENT --query="DROP TABLE IF EXISTS 02703_keeper_map_concurrent_$i" +done + +$CLICKHOUSE_CLIENT --query="SELECT count() FROM system.zookeeper WHERE path = '/test_keeper_map/02703_keeper_map/$CLICKHOUSE_DATABASE'" diff --git a/tests/queries/0_stateless/02704_keeper_map_zk_nodes.reference b/tests/queries/0_stateless/02704_keeper_map_zk_nodes.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/02704_keeper_map_zk_nodes.sh b/tests/queries/0_stateless/02704_keeper_map_zk_nodes.sh new file mode 100755 index 00000000000..9689d4f5a50 --- /dev/null +++ b/tests/queries/0_stateless/02704_keeper_map_zk_nodes.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# Tags: no-ordinary-database, zookeeper, no-fasttest + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +table_name="02704_keeper_map_zk_nodes" +table_name_another="02704_keeper_map_zk_nodes_new_table" + +$CLICKHOUSE_CLIENT --multiquery --query=" +DROP TABLE IF EXISTS $table_name; +DROP TABLE IF EXISTS $table_name_another; +CREATE TABLE $table_name (key UInt64, value UInt64) +ENGINE = KeeperMap('/$table_name/$CLICKHOUSE_DATABASE') +PRIMARY KEY(key)" + +function assert_children_size() +{ + for _ in `seq 10` + do + children_size=$($CLICKHOUSE_CLIENT --query="SELECT count() FROM system.zookeeper WHERE path = '$1'") + if [ $children_size == $2 ] + then + return + fi + + sleep 0.4 + done + + echo "Invalid number of children for path '$1': actual $children_size, expected $2" + exit 1 +} + +function assert_root_children_size() +{ + assert_children_size "/test_keeper_map/02704_keeper_map_zk_nodes/$CLICKHOUSE_DATABASE" $1 +} + +function assert_data_children_size() +{ + assert_children_size "/test_keeper_map/02704_keeper_map_zk_nodes/$CLICKHOUSE_DATABASE/data" $1 +} + +assert_root_children_size 2 +assert_data_children_size 0 + +$CLICKHOUSE_CLIENT --query="INSERT INTO $table_name VALUES (1, 11)" + +assert_data_children_size 1 + +$CLICKHOUSE_CLIENT --query=" +CREATE TABLE $table_name_another (key UInt64, value UInt64) +ENGINE = KeeperMap('/$table_name/$CLICKHOUSE_DATABASE') +PRIMARY KEY(key)" + +assert_root_children_size 2 +assert_data_children_size 1 + +$CLICKHOUSE_CLIENT --query="INSERT INTO $table_name_another VALUES (1, 11)" + +assert_root_children_size 2 +assert_data_children_size 1 + +$CLICKHOUSE_CLIENT --query="INSERT INTO $table_name_another VALUES (2, 22)" + +assert_root_children_size 2 +assert_data_children_size 2 + +$CLICKHOUSE_CLIENT --query="DROP TABLE $table_name" + +assert_root_children_size 2 +assert_data_children_size 2 + +$CLICKHOUSE_CLIENT --query="DROP TABLE $table_name_another" + +assert_root_children_size 0 From ee5e1ece1f8d729cc45256fa7709b13c4adfc2fc Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 30 Mar 2023 13:43:11 +0000 Subject: [PATCH 253/377] Stabilize 02477_age --- tests/queries/0_stateless/02477_age.reference | 2 +- tests/queries/0_stateless/02477_age.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/02477_age.reference b/tests/queries/0_stateless/02477_age.reference index 249c413d923..4afe3df8ad8 100644 --- a/tests/queries/0_stateless/02477_age.reference +++ b/tests/queries/0_stateless/02477_age.reference @@ -37,7 +37,7 @@ Constant and non-constant arguments -524160 1440 Case insensitive --10 +-1 Dependance of timezones 0 0 diff --git a/tests/queries/0_stateless/02477_age.sql b/tests/queries/0_stateless/02477_age.sql index 9b612276b01..72a692f61d0 100644 --- a/tests/queries/0_stateless/02477_age.sql +++ b/tests/queries/0_stateless/02477_age.sql @@ -45,7 +45,7 @@ SELECT age('minute', materialize(toDate('2017-12-31')), materialize(toDate('2018 SELECT 'Case insensitive'; -SELECT age('year', today(), today() - INTERVAL 10 YEAR); +SELECT age('YeAr', toDate('2017-12-31'), toDate('2016-01-01')); SELECT 'Dependance of timezones'; From 886c3636ec826c67f5a2e6ac2263e352a24e18f3 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Thu, 30 Mar 2023 15:56:49 +0200 Subject: [PATCH 254/377] Update 02703_storage_s3_race.sh --- tests/queries/0_stateless/02703_storage_s3_race.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/queries/0_stateless/02703_storage_s3_race.sh b/tests/queries/0_stateless/02703_storage_s3_race.sh index f4ef8c2426f..65a38e600f7 100755 --- a/tests/queries/0_stateless/02703_storage_s3_race.sh +++ b/tests/queries/0_stateless/02703_storage_s3_race.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# Tags: no-fasttest CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh From f870e6f7a928ec9a926c10d9398677c86f44e74a Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 30 Mar 2023 14:00:44 +0000 Subject: [PATCH 255/377] Rename setting stop_reading_on_first_cancel to partial_result_on_first_cancel. --- docs/en/operations/settings/settings.md | 4 ++-- docs/ru/operations/settings/settings.md | 4 ++-- src/Client/ClientBase.cpp | 10 +++++----- src/Client/ClientBase.h | 2 +- src/Core/Settings.h | 2 +- src/Server/TCPHandler.cpp | 6 +++--- .../0_stateless/25341_stop_reading_on_first_cancel.sh | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index f3c0f20f3a6..1f068285fd7 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -4050,7 +4050,7 @@ Possible values: Default value: `0`. -## stop_reading_on_first_cancel {#stop_reading_on_first_cancel} +## partial_result_on_first_cancel {#partial_result_on_first_cancel} When set to `true` and the user wants to interrupt a query (for example using `Ctrl+C` on the client), then the query continues execution only on data that was already read from the table. Afterward, it will return a partial result of the query for the part of the table that was read. To fully stop the execution of a query without a partial result, the user should send 2 cancel requests. **Example without setting on Ctrl+C** @@ -4066,7 +4066,7 @@ Query was cancelled. **Example with setting on Ctrl+C** ```sql -SELECT sum(number) FROM numbers(10000000000) SETTINGS stop_reading_on_first_cancel=true +SELECT sum(number) FROM numbers(10000000000) SETTINGS partial_result_on_first_cancel=true ┌──────sum(number)─┐ │ 1355411451286266 │ diff --git a/docs/ru/operations/settings/settings.md b/docs/ru/operations/settings/settings.md index d77f7ba6a46..171e9918163 100644 --- a/docs/ru/operations/settings/settings.md +++ b/docs/ru/operations/settings/settings.md @@ -4085,7 +4085,7 @@ ALTER TABLE test FREEZE SETTINGS alter_partition_verbose_result = 1; Значение по умолчанию: `''`. -## stop_reading_on_first_cancel {#stop_reading_on_first_cancel} +## partial_result_on_first_cancel {#partial_result_on_first_cancel} Если установлено значение `true` и пользователь хочет прервать запрос (например, с помощью `Ctrl+C` на клиенте), то запрос продолжает выполнение только для данных, которые уже были считаны из таблицы. После этого он вернет частичный результат запроса для той части таблицы, которая была прочитана. Чтобы полностью остановить выполнение запроса без частичного результата, пользователь должен отправить 2 запроса отмены. **Пример с выключенной настройкой при нажатии Ctrl+C** @@ -4101,7 +4101,7 @@ Query was cancelled. **Пример с включенной настройкой при нажатии Ctrl+C** ```sql -SELECT sum(number) FROM numbers(10000000000) SETTINGS stop_reading_on_first_cancel=true +SELECT sum(number) FROM numbers(10000000000) SETTINGS partial_result_on_first_cancel=true ┌──────sum(number)─┐ │ 1355411451286266 │ diff --git a/src/Client/ClientBase.cpp b/src/Client/ClientBase.cpp index d3ba1d7e84c..120d273aa62 100644 --- a/src/Client/ClientBase.cpp +++ b/src/Client/ClientBase.cpp @@ -861,7 +861,7 @@ void ClientBase::processOrdinaryQuery(const String & query_to_execute, ASTPtr pa } const auto & settings = global_context->getSettingsRef(); - const Int32 signals_before_stop = settings.stop_reading_on_first_cancel ? 2 : 1; + const Int32 signals_before_stop = settings.partial_result_on_first_cancel ? 2 : 1; int retries_left = 10; while (retries_left) @@ -885,7 +885,7 @@ void ClientBase::processOrdinaryQuery(const String & query_to_execute, ASTPtr pa if (send_external_tables) sendExternalTables(parsed_query); - receiveResult(parsed_query, signals_before_stop, settings.stop_reading_on_first_cancel); + receiveResult(parsed_query, signals_before_stop, settings.partial_result_on_first_cancel); break; } @@ -910,7 +910,7 @@ void ClientBase::processOrdinaryQuery(const String & query_to_execute, ASTPtr pa /// Receives and processes packets coming from server. /// Also checks if query execution should be cancelled. -void ClientBase::receiveResult(ASTPtr parsed_query, Int32 signals_before_stop, bool stop_reading_on_first_cancel) +void ClientBase::receiveResult(ASTPtr parsed_query, Int32 signals_before_stop, bool partial_result_on_first_cancel) { // TODO: get the poll_interval from commandline. const auto receive_timeout = connection_parameters.timeouts.receive_timeout; @@ -934,11 +934,11 @@ void ClientBase::receiveResult(ASTPtr parsed_query, Int32 signals_before_stop, b /// to avoid losing sync. if (!cancelled) { - if (stop_reading_on_first_cancel && QueryInterruptHandler::cancelled_status() == signals_before_stop - 1) + if (partial_result_on_first_cancel && QueryInterruptHandler::cancelled_status() == signals_before_stop - 1) { connection->sendCancel(); /// First cancel reading request was sent. Next requests will only be with a full cancel - stop_reading_on_first_cancel = false; + partial_result_on_first_cancel = false; } else if (QueryInterruptHandler::cancelled()) { diff --git a/src/Client/ClientBase.h b/src/Client/ClientBase.h index faf3fa8653a..5926f73f51a 100644 --- a/src/Client/ClientBase.h +++ b/src/Client/ClientBase.h @@ -131,7 +131,7 @@ protected: private: - void receiveResult(ASTPtr parsed_query, Int32 signals_before_stop, bool stop_reading_on_first_cancel); + void receiveResult(ASTPtr parsed_query, Int32 signals_before_stop, bool partial_result_on_first_cancel); bool receiveAndProcessPacket(ASTPtr parsed_query, bool cancelled_); void receiveLogsAndProfileEvents(ASTPtr parsed_query); bool receiveSampleBlock(Block & out, ColumnsDescription & columns_description, ASTPtr parsed_query); diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 9fa2ba0d32f..89c18165ea2 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -281,7 +281,7 @@ class IColumn; \ M(Bool, final, false, "Query with the FINAL modifier by default. If the engine does not support final, it does not have any effect. On queries with multiple tables final is applied only on those that support it. It also works on distributed tables", 0) \ \ - M(Bool, stop_reading_on_first_cancel, false, "Allows query to return a partial result after cancel.", 0) \ + M(Bool, partial_result_on_first_cancel, false, "Allows query to return a partial result after cancel.", 0) \ /** Settings for testing hedged requests */ \ M(Milliseconds, sleep_in_send_tables_status_ms, 0, "Time to sleep in sending tables status response in TCPHandler", 0) \ M(Milliseconds, sleep_in_send_data_ms, 0, "Time to sleep in sending data in TCPHandler", 0) \ diff --git a/src/Server/TCPHandler.cpp b/src/Server/TCPHandler.cpp index 85cdb75977b..7977605db3b 100644 --- a/src/Server/TCPHandler.cpp +++ b/src/Server/TCPHandler.cpp @@ -1818,14 +1818,14 @@ void TCPHandler::decreaseCancellationStatus(const std::string & log_message) { auto prev_status = magic_enum::enum_name(state.cancellation_status); - bool stop_reading_on_first_cancel = false; + bool partial_result_on_first_cancel = false; if (query_context) { const auto & settings = query_context->getSettingsRef(); - stop_reading_on_first_cancel = settings.stop_reading_on_first_cancel; + partial_result_on_first_cancel = settings.partial_result_on_first_cancel; } - if (stop_reading_on_first_cancel && state.cancellation_status == CancellationStatus::NOT_CANCELLED) + if (partial_result_on_first_cancel && state.cancellation_status == CancellationStatus::NOT_CANCELLED) { state.cancellation_status = CancellationStatus::READ_CANCELLED; } diff --git a/tests/queries/0_stateless/25341_stop_reading_on_first_cancel.sh b/tests/queries/0_stateless/25341_stop_reading_on_first_cancel.sh index 9694907d679..09837bff808 100755 --- a/tests/queries/0_stateless/25341_stop_reading_on_first_cancel.sh +++ b/tests/queries/0_stateless/25341_stop_reading_on_first_cancel.sh @@ -4,7 +4,7 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh . "$CURDIR"/../shell_config.sh -$CLICKHOUSE_CLIENT -n --query="SELECT sum(number * 0) FROM numbers(10000000000) SETTINGS stop_reading_on_first_cancel=true;" & +$CLICKHOUSE_CLIENT -n --query="SELECT sum(number * 0) FROM numbers(10000000000) SETTINGS partial_result_on_first_cancel=true;" & pid=$! sleep 2 kill -SIGINT $pid From e25872a333fad9370cee9562d7bd18ccca908f74 Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Thu, 30 Mar 2023 11:15:11 -0300 Subject: [PATCH 256/377] Update type-conversion-functions.md --- docs/en/sql-reference/functions/type-conversion-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 16d3787c5ce..213ed187f15 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -13,7 +13,7 @@ incompatible datatypes (for example from `String` to `Int`). Make sure to check ClickHouse generally uses the [same behavior as C++ programs](https://en.cppreference.com/w/cpp/language/implicit_conversion). -`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md), this behaviour is not compatible with SQL standart, and it can changed using [cast_keep_nullable](../../operations/settings/settings.md/#cast_keep_nullable) setting. +`to` functions and [cast](#castx-t) have different behaviour in some cases, for example in case of [LowCardinality](../data-types/lowcardinality.md): [cast](#castx-t) removes [LowCardinality](../data-types/lowcardinality.md) trait `to` functions don't. The same with [Nullable](../data-types/nullable.md), this behaviour is not compatible with SQL standard, and it can be changed using [cast_keep_nullable](../../operations/settings/settings.md/#cast_keep_nullable) setting. Example: From 7b3754102f4eacc54830027bf0b85062beb2a987 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 30 Mar 2023 14:14:46 +0000 Subject: [PATCH 257/377] Address flaky 02346_full_text_search --- .../02346_full_text_search.reference | 14 -- .../0_stateless/02346_full_text_search.sql | 203 +++++++++--------- 2 files changed, 103 insertions(+), 114 deletions(-) diff --git a/tests/queries/0_stateless/02346_full_text_search.reference b/tests/queries/0_stateless/02346_full_text_search.reference index 9cd09110608..d6e510b9375 100644 --- a/tests/queries/0_stateless/02346_full_text_search.reference +++ b/tests/queries/0_stateless/02346_full_text_search.reference @@ -46,17 +46,3 @@ Test inverted(2) on UTF-8 data af inverted 102 clickhouse你好 1 -Test max_digestion_size_per_segment -af inverted -BC614E,05397FB1,6969696969898240,CF3304 -1 -Test density==1 -af inverted -1 -1 -Test density==0.1 -af inverted -1 -1 -1 -1 diff --git a/tests/queries/0_stateless/02346_full_text_search.sql b/tests/queries/0_stateless/02346_full_text_search.sql index ed086861f1f..18d1ce0fd96 100644 --- a/tests/queries/0_stateless/02346_full_text_search.sql +++ b/tests/queries/0_stateless/02346_full_text_search.sql @@ -234,105 +234,108 @@ SELECT read_rows==2 from system.query_log AND result_rows==1 LIMIT 1; ----------------------------------------------------- -SELECT 'Test max_digestion_size_per_segment'; -DROP TABLE IF EXISTS tab; +-- Tests with parameter max_digestion_size_per_segment are flaky in CI, not clear why --> comment out for the time being: -CREATE TABLE tab(k UInt64, s String, INDEX af(s) TYPE inverted(0)) - Engine=MergeTree - ORDER BY (k) - SETTINGS max_digestion_size_per_segment = 1024, index_granularity = 256 - AS - SELECT - number, - format('{},{},{},{}', hex(12345678), hex(87654321), hex(number/17 + 5), hex(13579012)) as s - FROM numbers(10240); - --- check inverted index was created -SELECT name, type FROM system.data_skipping_indices WHERE table == 'tab' AND database = currentDatabase() LIMIT 1; - --- search inverted index -SELECT s FROM tab WHERE hasToken(s, '6969696969898240'); - --- check the query only read 1 granule (1 row total; each granule has 256 rows) -SYSTEM FLUSH LOGS; -SELECT read_rows==256 from system.query_log - WHERE query_kind ='Select' - AND current_database = currentDatabase() - AND endsWith(trimRight(query), 'SELECT s FROM tab WHERE hasToken(s, \'6969696969898240\');') - AND type='QueryFinish' - AND result_rows==1 - LIMIT 1; - ----------------------------------------------------- -SELECT 'Test density==1'; - -DROP TABLE IF EXISTS tab; - -CREATE TABLE tab(k UInt64, s String, INDEX af(s) TYPE inverted(0, 1.0)) - Engine=MergeTree - ORDER BY (k) - SETTINGS max_digestion_size_per_segment = 1, index_granularity = 512 - AS - SELECT number, if(number%2, format('happy {}', hex(number)), format('birthday {}', hex(number))) - FROM numbers(1024); - --- check inverted index was created -SELECT name, type FROM system.data_skipping_indices WHERE table == 'tab' AND database = currentDatabase() LIMIT 1; - --- search inverted index, no row has 'happy birthday' -SELECT count() == 0 FROM tab WHERE s =='happy birthday'; - --- check the query only skip all granules (0 row total; each granule has 512 rows) -SYSTEM FLUSH LOGS; -SELECT read_rows==0 from system.query_log - WHERE query_kind ='Select' - AND current_database = currentDatabase() - AND endsWith(trimRight(query), 'SELECT count() == 0 FROM tab WHERE s ==\'happy birthday\';') - AND type='QueryFinish' - AND result_rows==1 - LIMIT 1; - ----------------------------------------------------- -SELECT 'Test density==0.1'; - -DROP TABLE IF EXISTS tab; - -CREATE TABLE tab(k UInt64, s String, INDEX af(s) TYPE inverted(0, 0.1)) - Engine=MergeTree - ORDER BY (k) - SETTINGS max_digestion_size_per_segment = 1, index_granularity = 512 - AS - SELECT number, if(number==1023, 'happy new year', if(number%2, format('happy {}', hex(number)), format('birthday {}', hex(number)))) - FROM numbers(1024); - --- check inverted index was created - -SELECT name, type FROM system.data_skipping_indices WHERE table == 'tab' AND database = currentDatabase() LIMIT 1; - --- search inverted index, no row has 'happy birthday' -SELECT count() == 0 FROM tab WHERE s == 'happy birthday'; - --- check the query does not skip any of the 2 granules(1024 rows total; each granule has 512 rows) -SYSTEM FLUSH LOGS; -SELECT read_rows==1024 from system.query_log - WHERE query_kind ='Select' - AND current_database = currentDatabase() - AND endsWith(trimRight(query), 'SELECT count() == 0 FROM tab WHERE s == \'happy birthday\';') - AND type='QueryFinish' - AND result_rows==1 - LIMIT 1; - --- search inverted index, no row has 'happy new year' -SELECT count() == 1 FROM tab WHERE s == 'happy new year'; - --- check the query only read 1 granule because of density (1024 rows total; each granule has 512 rows) -SYSTEM FLUSH LOGS; -SELECT read_rows==512 from system.query_log - WHERE query_kind ='Select' - AND current_database = currentDatabase() - AND endsWith(trimRight(query), 'SELECT count() == 1 FROM tab WHERE s == \'happy new year\';') - AND type='QueryFinish' - AND result_rows==1 - LIMIT 1; +-- ---------------------------------------------------- +-- SELECT 'Test max_digestion_size_per_segment'; +-- +-- DROP TABLE IF EXISTS tab; +-- +-- CREATE TABLE tab(k UInt64, s String, INDEX af(s) TYPE inverted(0)) +-- Engine=MergeTree +-- ORDER BY (k) +-- SETTINGS max_digestion_size_per_segment = 1024, index_granularity = 256 +-- AS +-- SELECT +-- number, +-- format('{},{},{},{}', hex(12345678), hex(87654321), hex(number/17 + 5), hex(13579012)) as s +-- FROM numbers(10240); +-- +-- -- check inverted index was created +-- SELECT name, type FROM system.data_skipping_indices WHERE table == 'tab' AND database = currentDatabase() LIMIT 1; +-- +-- -- search inverted index +-- SELECT s FROM tab WHERE hasToken(s, '6969696969898240'); +-- +-- -- check the query only read 1 granule (1 row total; each granule has 256 rows) +-- SYSTEM FLUSH LOGS; +-- SELECT read_rows==256 from system.query_log +-- WHERE query_kind ='Select' +-- AND current_database = currentDatabase() +-- AND endsWith(trimRight(query), 'SELECT s FROM tab WHERE hasToken(s, \'6969696969898240\');') +-- AND type='QueryFinish' +-- AND result_rows==1 +-- LIMIT 1; +-- +-- ---------------------------------------------------- +-- SELECT 'Test density==1'; +-- +-- DROP TABLE IF EXISTS tab; +-- +-- CREATE TABLE tab(k UInt64, s String, INDEX af(s) TYPE inverted(0, 1.0)) +-- Engine=MergeTree +-- ORDER BY (k) +-- SETTINGS max_digestion_size_per_segment = 1, index_granularity = 512 +-- AS +-- SELECT number, if(number%2, format('happy {}', hex(number)), format('birthday {}', hex(number))) +-- FROM numbers(1024); +-- +-- -- check inverted index was created +-- SELECT name, type FROM system.data_skipping_indices WHERE table == 'tab' AND database = currentDatabase() LIMIT 1; +-- +-- -- search inverted index, no row has 'happy birthday' +-- SELECT count() == 0 FROM tab WHERE s =='happy birthday'; +-- +-- -- check the query only skip all granules (0 row total; each granule has 512 rows) +-- SYSTEM FLUSH LOGS; +-- SELECT read_rows==0 from system.query_log +-- WHERE query_kind ='Select' +-- AND current_database = currentDatabase() +-- AND endsWith(trimRight(query), 'SELECT count() == 0 FROM tab WHERE s ==\'happy birthday\';') +-- AND type='QueryFinish' +-- AND result_rows==1 +-- LIMIT 1; +-- +-- ---------------------------------------------------- +-- SELECT 'Test density==0.1'; +-- +-- DROP TABLE IF EXISTS tab; +-- +-- CREATE TABLE tab(k UInt64, s String, INDEX af(s) TYPE inverted(0, 0.1)) +-- Engine=MergeTree +-- ORDER BY (k) +-- SETTINGS max_digestion_size_per_segment = 1, index_granularity = 512 +-- AS +-- SELECT number, if(number==1023, 'happy new year', if(number%2, format('happy {}', hex(number)), format('birthday {}', hex(number)))) +-- FROM numbers(1024); +-- +-- -- check inverted index was created +-- +-- SELECT name, type FROM system.data_skipping_indices WHERE table == 'tab' AND database = currentDatabase() LIMIT 1; +-- +-- -- search inverted index, no row has 'happy birthday' +-- SELECT count() == 0 FROM tab WHERE s == 'happy birthday'; +-- +-- -- check the query does not skip any of the 2 granules(1024 rows total; each granule has 512 rows) +-- SYSTEM FLUSH LOGS; +-- SELECT read_rows==1024 from system.query_log +-- WHERE query_kind ='Select' +-- AND current_database = currentDatabase() +-- AND endsWith(trimRight(query), 'SELECT count() == 0 FROM tab WHERE s == \'happy birthday\';') +-- AND type='QueryFinish' +-- AND result_rows==1 +-- LIMIT 1; +-- +-- -- search inverted index, no row has 'happy new year' +-- SELECT count() == 1 FROM tab WHERE s == 'happy new year'; +-- +-- -- check the query only read 1 granule because of density (1024 rows total; each granule has 512 rows) +-- SYSTEM FLUSH LOGS; +-- SELECT read_rows==512 from system.query_log +-- WHERE query_kind ='Select' +-- AND current_database = currentDatabase() +-- AND endsWith(trimRight(query), 'SELECT count() == 1 FROM tab WHERE s == \'happy new year\';') +-- AND type='QueryFinish' +-- AND result_rows==1 +-- LIMIT 1; From fbb22348ea8467ce653f069e7be70c7e750a9002 Mon Sep 17 00:00:00 2001 From: filimonov <1549571+filimonov@users.noreply.github.com> Date: Thu, 30 Mar 2023 16:44:11 +0200 Subject: [PATCH 258/377] Refactor reading the pool setting & from server config. (#48055) After #36425 there was a lot of confusions/problems with configuring pools - when the message was confusing, and settings need to be ajusted in several places. See some examples in #44251, #43351, #47900, #46515. The commit includes the following changes: 1) Introduced a unified mechanism for reading pool sizes from the configuration file(s). Previously, pool sizes were read from the Context.cpp with fallbacks to profiles, whereas main_config_reloader in Server.cpp read them directly without fallbacks. 2) Corrected the data type for background_merges_mutations_concurrency_ratio. It should be float instead of int. 3) Refactored the default values for settings. Previously, they were defined in multiple places throughout the codebase, but they are now defined in one place (or two, to be exact: Settings.h and ServerSettings.h). 4) Improved documentation, including the correct message in system.settings. Additionally make the code more conform with #46550. --- .../table-engines/integrations/kafka.md | 2 +- .../mergetree-family/mergetree.md | 2 +- .../mergetree-family/replication.md | 6 +- .../settings.md | 25 +++++- docs/en/operations/settings/settings.md | 55 ++---------- .../table-engines/integrations/kafka.md | 2 +- .../mergetree-family/mergetree.md | 2 +- .../mergetree-family/replication.md | 8 +- .../settings.md | 67 +++++++++++++++ docs/ru/operations/settings/settings.md | 55 ++---------- .../table-engines/integrations/kafka.md | 2 +- .../mergetree-family/mergetree.md | 2 +- .../mergetree-family/replication.md | 6 +- programs/server/Server.cpp | 2 +- src/Core/ServerSettings.cpp | 16 ++++ src/Core/ServerSettings.h | 4 +- src/Core/Settings.h | 23 +++-- src/Interpreters/Context.cpp | 84 ++++++------------- 18 files changed, 179 insertions(+), 184 deletions(-) diff --git a/docs/en/engines/table-engines/integrations/kafka.md b/docs/en/engines/table-engines/integrations/kafka.md index e2a7304dc59..0b1717978b7 100644 --- a/docs/en/engines/table-engines/integrations/kafka.md +++ b/docs/en/engines/table-engines/integrations/kafka.md @@ -259,4 +259,4 @@ The number of rows in one Kafka message depends on whether the format is row-bas **See Also** - [Virtual columns](../../../engines/table-engines/index.md#table_engines-virtual_columns) -- [background_message_broker_schedule_pool_size](../../../operations/settings/settings.md#background_message_broker_schedule_pool_size) +- [background_message_broker_schedule_pool_size](../../../operations/server-configuration-parameters/settings.md#background_message_broker_schedule_pool_size) diff --git a/docs/en/engines/table-engines/mergetree-family/mergetree.md b/docs/en/engines/table-engines/mergetree-family/mergetree.md index 1c16ffe9db5..95efe42c757 100644 --- a/docs/en/engines/table-engines/mergetree-family/mergetree.md +++ b/docs/en/engines/table-engines/mergetree-family/mergetree.md @@ -874,7 +874,7 @@ SETTINGS storage_policy = 'moving_from_ssd_to_hdd' The `default` storage policy implies using only one volume, which consists of only one disk given in ``. You could change storage policy after table creation with [ALTER TABLE ... MODIFY SETTING] query, new policy should include all old disks and volumes with same names. -The number of threads performing background moves of data parts can be changed by [background_move_pool_size](/docs/en/operations/settings/settings.md/#background_move_pool_size) setting. +The number of threads performing background moves of data parts can be changed by [background_move_pool_size](/docs/en/operations/server-configuration-parameters/settings.md/#background_move_pool_size) setting. ### Details {#details} diff --git a/docs/en/engines/table-engines/mergetree-family/replication.md b/docs/en/engines/table-engines/mergetree-family/replication.md index b2b967c685a..e9ca87916a0 100644 --- a/docs/en/engines/table-engines/mergetree-family/replication.md +++ b/docs/en/engines/table-engines/mergetree-family/replication.md @@ -112,7 +112,7 @@ For each `INSERT` query, approximately ten entries are added to ZooKeeper throug For very large clusters, you can use different ZooKeeper clusters for different shards. However, from our experience this has not proven necessary based on production clusters with approximately 300 servers. -Replication is asynchronous and multi-master. `INSERT` queries (as well as `ALTER`) can be sent to any available server. Data is inserted on the server where the query is run, and then it is copied to the other servers. Because it is asynchronous, recently inserted data appears on the other replicas with some latency. If part of the replicas are not available, the data is written when they become available. If a replica is available, the latency is the amount of time it takes to transfer the block of compressed data over the network. The number of threads performing background tasks for replicated tables can be set by [background_schedule_pool_size](/docs/en/operations/settings/settings.md/#background_schedule_pool_size) setting. +Replication is asynchronous and multi-master. `INSERT` queries (as well as `ALTER`) can be sent to any available server. Data is inserted on the server where the query is run, and then it is copied to the other servers. Because it is asynchronous, recently inserted data appears on the other replicas with some latency. If part of the replicas are not available, the data is written when they become available. If a replica is available, the latency is the amount of time it takes to transfer the block of compressed data over the network. The number of threads performing background tasks for replicated tables can be set by [background_schedule_pool_size](/docs/en/operations/server-configuration-parameters/settings.md/#background_schedule_pool_size) setting. `ReplicatedMergeTree` engine uses a separate thread pool for replicated fetches. Size of the pool is limited by the [background_fetches_pool_size](/docs/en/operations/settings/settings.md/#background_fetches_pool_size) setting which can be tuned with a server restart. @@ -320,8 +320,8 @@ If the data in ClickHouse Keeper was lost or damaged, you can save data by movin **See Also** -- [background_schedule_pool_size](/docs/en/operations/settings/settings.md/#background_schedule_pool_size) -- [background_fetches_pool_size](/docs/en/operations/settings/settings.md/#background_fetches_pool_size) +- [background_schedule_pool_size](/docs/en/operations/server-configuration-parameters/settings.md/#background_schedule_pool_size) +- [background_fetches_pool_size](/docs/en/operations/server-configuration-parameters/settings.md/#background_fetches_pool_size) - [execute_merges_on_single_replica_time_threshold](/docs/en/operations/settings/settings.md/#execute-merges-on-single-replica-time-threshold) - [max_replicated_fetches_network_bandwidth](/docs/en/operations/settings/merge-tree-settings.md/#max_replicated_fetches_network_bandwidth) - [max_replicated_sends_network_bandwidth](/docs/en/operations/settings/merge-tree-settings.md/#max_replicated_sends_network_bandwidth) diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index c2c577d3d79..138d5780423 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -257,6 +257,7 @@ The path to the table in ZooKeeper. ``` xml /clickhouse/tables/{uuid}/{shard} ``` + ## default_replica_name {#default_replica_name} The replica name in ZooKeeper. @@ -418,6 +419,7 @@ Opens `https://tabix.io/` when accessing `http://localhost: http_port`.

]]> ``` + ## hsts_max_age {#hsts-max-age} Expired time for HSTS in seconds. The default value is 0 means clickhouse disabled HSTS. If you set a positive number, the HSTS will be enabled and the max-age is the number you set. @@ -1113,7 +1115,7 @@ Default value: 8. ## background_fetches_pool_size {#background_fetches_pool_size} -Sets the number of threads performing background fetches for tables with ReplicatedMergeTree engines. Could be increased at runtime and could be applied at server startup from the `default` profile for backward compatibility. +Sets the number of threads performing background fetches for tables with ReplicatedMergeTree engines. Could be increased at runtime. Possible values: @@ -1129,7 +1131,7 @@ Default value: 8. ## background_common_pool_size {#background_common_pool_size} -Sets the number of threads performing background non-specialized operations like cleaning the filesystem etc. for tables with MergeTree engines. Could be increased at runtime and could be applied at server startup from the `default` profile for backward compatibility. +Sets the number of threads performing background non-specialized operations like cleaning the filesystem etc. for tables with MergeTree engines. Could be increased at runtime. Possible values: @@ -1143,6 +1145,25 @@ Default value: 8. 36 ``` +## background_buffer_flush_schedule_pool_size {#background_buffer_flush_schedule_pool_size} + +Sets the number of threads performing background flush in [Buffer](../../engines/table-engines/special/buffer.md)-engine tables. + +Possible values: + +- Any positive integer. + +Default value: 16. + +## background_schedule_pool_size {#background_schedule_pool_size} + +Sets the number of threads performing background tasks for [replicated](../../engines/table-engines/mergetree-family/replication.md) tables, [Kafka](../../engines/table-engines/integrations/kafka.md) streaming, [DNS cache updates](../../operations/server-configuration-parameters/settings.md/#server-settings-dns-cache-update-period). + +Possible values: + +- Any positive integer. + +Default value: 128. ## merge_tree {#server_configuration_parameters-merge_tree} diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index 4d638964c6f..489e6a6b15b 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -1552,6 +1552,7 @@ For the replicated tables by default the only 100 of the most recent blocks for For not replicated tables see [non_replicated_deduplication_window](merge-tree-settings.md/#non-replicated-deduplication-window). ## Asynchronous Insert settings + ### async_insert {#async-insert} Enables or disables asynchronous inserts. This makes sense only for insertion over HTTP protocol. Note that deduplication isn't working for such inserts. @@ -1645,6 +1646,7 @@ Possible values: - 0 — Timeout disabled. Default value: `0`. + ### async_insert_deduplicate {#settings-async-insert-deduplicate} Enables or disables insert deduplication of `ASYNC INSERT` (for Replicated\* tables). @@ -2449,43 +2451,19 @@ Default value: `1`. ## background_buffer_flush_schedule_pool_size {#background_buffer_flush_schedule_pool_size} -Sets the number of threads performing background flush in [Buffer](../../engines/table-engines/special/buffer.md)-engine tables. This setting is applied at the ClickHouse server start and can’t be changed in a user session. - -Possible values: - -- Any positive integer. - -Default value: 16. +That setting was moved to the [server configuration parameters](../../operations/server-configuration-parameters/settings.md/#background_buffer_flush_schedule_pool_size). ## background_move_pool_size {#background_move_pool_size} -Sets the number of threads performing background moves of data parts for [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md/#table_engine-mergetree-multiple-volumes)-engine tables. This setting is applied at the ClickHouse server start and can’t be changed in a user session. - -Possible values: - -- Any positive integer. - -Default value: 8. +That setting was moved to the [server configuration parameters](../../operations/server-configuration-parameters/settings.md/#background_move_pool_size). ## background_schedule_pool_size {#background_schedule_pool_size} -Sets the number of threads performing background tasks for [replicated](../../engines/table-engines/mergetree-family/replication.md) tables, [Kafka](../../engines/table-engines/integrations/kafka.md) streaming, [DNS cache updates](../../operations/server-configuration-parameters/settings.md/#server-settings-dns-cache-update-period). This setting is applied at ClickHouse server start and can’t be changed in a user session. - -Possible values: - -- Any positive integer. - -Default value: 128. +That setting was moved to the [server configuration parameters](../../operations/server-configuration-parameters/settings.md/#background_schedule_pool_size). ## background_fetches_pool_size {#background_fetches_pool_size} -Sets the number of threads performing background fetches for [replicated](../../engines/table-engines/mergetree-family/replication.md) tables. This setting is applied at the ClickHouse server start and can’t be changed in a user session. For production usage with frequent small insertions or slow ZooKeeper cluster it is recommended to use default value. - -Possible values: - -- Any positive integer. - -Default value: 8. +That setting was moved to the [server configuration parameters](../../operations/server-configuration-parameters/settings.md/#background_fetches_pool_size). ## always_fetch_merged_part {#always_fetch_merged_part} @@ -2506,28 +2484,11 @@ Default value: 0. ## background_distributed_schedule_pool_size {#background_distributed_schedule_pool_size} -Sets the number of threads performing background tasks for [distributed](../../engines/table-engines/special/distributed.md) sends. This setting is applied at the ClickHouse server start and can’t be changed in a user session. - -Possible values: - -- Any positive integer. - -Default value: 16. +That setting was moved to the [server configuration parameters](../../operations/server-configuration-parameters/settings.md/#background_distributed_schedule_pool_size). ## background_message_broker_schedule_pool_size {#background_message_broker_schedule_pool_size} -Sets the number of threads performing background tasks for message streaming. This setting is applied at the ClickHouse server start and can’t be changed in a user session. - -Possible values: - -- Any positive integer. - -Default value: 16. - -**See Also** - -- [Kafka](../../engines/table-engines/integrations/kafka.md/#kafka) engine. -- [RabbitMQ](../../engines/table-engines/integrations/rabbitmq.md/#rabbitmq-engine) engine. +That setting was moved to the [server configuration parameters](../../operations/server-configuration-parameters/settings.md/#background_message_broker_schedule_pool_size). ## validate_polygons {#validate_polygons} diff --git a/docs/ru/engines/table-engines/integrations/kafka.md b/docs/ru/engines/table-engines/integrations/kafka.md index 58e03ba30cc..832486c038a 100644 --- a/docs/ru/engines/table-engines/integrations/kafka.md +++ b/docs/ru/engines/table-engines/integrations/kafka.md @@ -211,4 +211,4 @@ ClickHouse может поддерживать учетные данные Kerbe **Смотрите также** - [Виртуальные столбцы](index.md#table_engines-virtual_columns) -- [background_message_broker_schedule_pool_size](../../../operations/settings/settings.md#background_message_broker_schedule_pool_size) +- [background_message_broker_schedule_pool_size](../../../operations/server-configuration-parameters/settings.md#background_message_broker_schedule_pool_size) diff --git a/docs/ru/engines/table-engines/mergetree-family/mergetree.md b/docs/ru/engines/table-engines/mergetree-family/mergetree.md index ef17a370dc6..6182ab20203 100644 --- a/docs/ru/engines/table-engines/mergetree-family/mergetree.md +++ b/docs/ru/engines/table-engines/mergetree-family/mergetree.md @@ -752,7 +752,7 @@ SETTINGS storage_policy = 'moving_from_ssd_to_hdd' Изменить политику хранения после создания таблицы можно при помощи запроса [ALTER TABLE ... MODIFY SETTING]. При этом необходимо учесть, что новая политика должна содержать все тома и диски предыдущей политики с теми же именами. -Количество потоков для фоновых перемещений кусков между дисками можно изменить с помощью настройки [background_move_pool_size](../../../operations/settings/settings.md#background_move_pool_size) +Количество потоков для фоновых перемещений кусков между дисками можно изменить с помощью настройки [background_move_pool_size](../../../operations/server-configuration-parameters/settings.md#background_move_pool_size) ### Особенности работы {#details} diff --git a/docs/ru/engines/table-engines/mergetree-family/replication.md b/docs/ru/engines/table-engines/mergetree-family/replication.md index 22cb2196ef1..2b4d89dbe0a 100644 --- a/docs/ru/engines/table-engines/mergetree-family/replication.md +++ b/docs/ru/engines/table-engines/mergetree-family/replication.md @@ -64,9 +64,9 @@ ClickHouse хранит метаинформацию о репликах в [Apa Для очень больших кластеров, можно использовать разные кластеры ZooKeeper для разных шардов. Впрочем, на кластере Яндекс.Метрики (примерно 300 серверов) такой необходимости не возникает. -Репликация асинхронная, мульти-мастер. Запросы `INSERT` и `ALTER` можно направлять на любой доступный сервер. Данные вставятся на сервер, где выполнен запрос, а затем скопируются на остальные серверы. В связи с асинхронностью, только что вставленные данные появляются на остальных репликах с небольшой задержкой. Если часть реплик недоступна, данные на них запишутся тогда, когда они станут доступны. Если реплика доступна, то задержка составляет столько времени, сколько требуется для передачи блока сжатых данных по сети. Количество потоков для выполнения фоновых задач можно задать с помощью настройки [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size). +Репликация асинхронная, мульти-мастер. Запросы `INSERT` и `ALTER` можно направлять на любой доступный сервер. Данные вставятся на сервер, где выполнен запрос, а затем скопируются на остальные серверы. В связи с асинхронностью, только что вставленные данные появляются на остальных репликах с небольшой задержкой. Если часть реплик недоступна, данные на них запишутся тогда, когда они станут доступны. Если реплика доступна, то задержка составляет столько времени, сколько требуется для передачи блока сжатых данных по сети. Количество потоков для выполнения фоновых задач можно задать с помощью настройки [background_schedule_pool_size](../../../operations/server-configuration-parameters/settings.md#background_schedule_pool_size). -Движок `ReplicatedMergeTree` использует отдельный пул потоков для скачивания кусков данных. Размер пула ограничен настройкой [background_fetches_pool_size](../../../operations/settings/settings.md#background_fetches_pool_size), которую можно указать при перезапуске сервера. +Движок `ReplicatedMergeTree` использует отдельный пул потоков для скачивания кусков данных. Размер пула ограничен настройкой [background_fetches_pool_size](../../../operations/server-configuration-parameters/settings.md#background_fetches_pool_size), которую можно указать при перезапуске сервера. По умолчанию, запрос INSERT ждёт подтверждения записи только от одной реплики. Если данные были успешно записаны только на одну реплику, и сервер с этой репликой перестал существовать, то записанные данные будут потеряны. Вы можете включить подтверждение записи от нескольких реплик, используя настройку `insert_quorum`. @@ -251,8 +251,8 @@ $ sudo -u clickhouse touch /var/lib/clickhouse/flags/force_restore_data **Смотрите также** -- [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size) -- [background_fetches_pool_size](../../../operations/settings/settings.md#background_fetches_pool_size) +- [background_schedule_pool_size](../../../operations/server-configuration-parameters/settings.md#background_schedule_pool_size) +- [background_fetches_pool_size](../../../operations/server-configuration-parameters/settings.md#background_fetches_pool_size) - [execute_merges_on_single_replica_time_threshold](../../../operations/settings/settings.md#execute-merges-on-single-replica-time-threshold) - [max_replicated_fetches_network_bandwidth](../../../operations/settings/merge-tree-settings.md#max_replicated_fetches_network_bandwidth) - [max_replicated_sends_network_bandwidth](../../../operations/settings/merge-tree-settings.md#max_replicated_sends_network_bandwidth) diff --git a/docs/ru/operations/server-configuration-parameters/settings.md b/docs/ru/operations/server-configuration-parameters/settings.md index 4b1d8ce717f..787153d4d19 100644 --- a/docs/ru/operations/server-configuration-parameters/settings.md +++ b/docs/ru/operations/server-configuration-parameters/settings.md @@ -225,6 +225,7 @@ ClickHouse проверяет условия для `min_part_size` и `min_part ``` xml /clickhouse/tables/{uuid}/{shard} ``` + ## default_replica_name {#default_replica_name} Имя реплики в ZooKeeper. @@ -916,6 +917,72 @@ ClickHouse использует потоки из глобального пул 12000 ``` +## background_buffer_flush_schedule_pool_size {#background_buffer_flush_schedule_pool_size} + +Задает количество потоков для выполнения фонового сброса данных в таблицах с движком [Buffer](../../engines/table-engines/special/buffer.md). + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 16. + +## background_move_pool_size {#background_move_pool_size} + +Задает количество потоков для фоновых перемещений кусков между дисками. Работает для таблиц с движком [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md#table_engine-mergetree-multiple-volumes). + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 8. + +## background_schedule_pool_size {#background_schedule_pool_size} + +Задает количество потоков для выполнения фоновых задач. Работает для [реплицируемых](../../engines/table-engines/mergetree-family/replication.md) таблиц, стримов в [Kafka](../../engines/table-engines/integrations/kafka.md) и обновления IP адресов у записей во внутреннем [DNS кеше](../server-configuration-parameters/settings.md#server-settings-dns-cache-update-period). + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 128. + +## background_fetches_pool_size {#background_fetches_pool_size} + +Задает количество потоков для скачивания кусков данных для [реплицируемых](../../engines/table-engines/mergetree-family/replication.md) таблиц. Для использования в продакшене с частыми небольшими вставками или медленным кластером ZooKeeper рекомендуется использовать значение по умолчанию. + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 8. + +## background_distributed_schedule_pool_size {#background_distributed_schedule_pool_size} + +Задает количество потоков для выполнения фоновых задач. Работает для таблиц с движком [Distributed](../../engines/table-engines/special/distributed.md). + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 16. + +## background_message_broker_schedule_pool_size {#background_message_broker_schedule_pool_size} + +Задает количество потоков для фонового потокового вывода сообщений. + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 16. + +**Смотрите также** + +- Движок [Kafka](../../engines/table-engines/integrations/kafka.md#kafka). +- Движок [RabbitMQ](../../engines/table-engines/integrations/rabbitmq.md#rabbitmq-engine). + + ## merge_tree {#server_configuration_parameters-merge_tree} Тонкая настройка таблиц семейства [MergeTree](../../operations/server-configuration-parameters/settings.md). diff --git a/docs/ru/operations/settings/settings.md b/docs/ru/operations/settings/settings.md index d77f7ba6a46..ca2748688a2 100644 --- a/docs/ru/operations/settings/settings.md +++ b/docs/ru/operations/settings/settings.md @@ -1122,6 +1122,7 @@ SELECT type, query FROM system.query_log WHERE log_comment = 'log_comment test' :::note "Предупреждение" Эта настройка экспертного уровня, не используйте ее, если вы только начинаете работать с Clickhouse. ::: + ## max_query_size {#settings-max_query_size} Максимальный кусок запроса, который будет считан в оперативку для разбора парсером языка SQL. @@ -2517,68 +2518,27 @@ SELECT idx, i FROM null_in WHERE i IN (1, NULL) SETTINGS transform_null_in = 1; ## background_buffer_flush_schedule_pool_size {#background_buffer_flush_schedule_pool_size} -Задает количество потоков для выполнения фонового сброса данных в таблицах с движком [Buffer](../../engines/table-engines/special/buffer.md). Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. - -Допустимые значения: - -- Положительное целое число. - -Значение по умолчанию: 16. +Параметр перенесен в [серверную конфигурацию](../../operations/server-configuration-parameters/settings.md/#background_buffer_flush_schedule_pool_size). ## background_move_pool_size {#background_move_pool_size} -Задает количество потоков для фоновых перемещений кусков между дисками. Работает для таблиц с движком [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md#table_engine-mergetree-multiple-volumes). Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. - -Допустимые значения: - -- Положительное целое число. - -Значение по умолчанию: 8. +Параметр перенесен в [серверную конфигурацию](../../operations/server-configuration-parameters/settings.md/#background_move_pool_size). ## background_schedule_pool_size {#background_schedule_pool_size} -Задает количество потоков для выполнения фоновых задач. Работает для [реплицируемых](../../engines/table-engines/mergetree-family/replication.md) таблиц, стримов в [Kafka](../../engines/table-engines/integrations/kafka.md) и обновления IP адресов у записей во внутреннем [DNS кеше](../server-configuration-parameters/settings.md#server-settings-dns-cache-update-period). Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. - -Допустимые значения: - -- Положительное целое число. - -Значение по умолчанию: 128. +Параметр перенесен в [серверную конфигурацию](../../operations/server-configuration-parameters/settings.md/#background_schedule_pool_size). ## background_fetches_pool_size {#background_fetches_pool_size} -Задает количество потоков для скачивания кусков данных для [реплицируемых](../../engines/table-engines/mergetree-family/replication.md) таблиц. Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. Для использования в продакшене с частыми небольшими вставками или медленным кластером ZooKeeper рекомендуется использовать значение по умолчанию. - -Допустимые значения: - -- Положительное целое число. - -Значение по умолчанию: 8. +Параметр перенесен в [серверную конфигурацию](../../operations/server-configuration-parameters/settings.md/#background_fetches_pool_size). ## background_distributed_schedule_pool_size {#background_distributed_schedule_pool_size} -Задает количество потоков для выполнения фоновых задач. Работает для таблиц с движком [Distributed](../../engines/table-engines/special/distributed.md). Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. - -Допустимые значения: - -- Положительное целое число. - -Значение по умолчанию: 16. +Параметр перенесен в [серверную конфигурацию](../../operations/server-configuration-parameters/settings.md/#background_distributed_schedule_pool_size). ## background_message_broker_schedule_pool_size {#background_message_broker_schedule_pool_size} -Задает количество потоков для фонового потокового вывода сообщений. Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. - -Допустимые значения: - -- Положительное целое число. - -Значение по умолчанию: 16. - -**Смотрите также** - -- Движок [Kafka](../../engines/table-engines/integrations/kafka.md#kafka). -- Движок [RabbitMQ](../../engines/table-engines/integrations/rabbitmq.md#rabbitmq-engine). +Параметр перенесен в [серверную конфигурацию](../../operations/server-configuration-parameters/settings.md/#background_message_broker_schedule_pool_size). ## format_avro_schema_registry_url {#format_avro_schema_registry_url} @@ -3388,6 +3348,7 @@ SELECT * FROM test LIMIT 10 OFFSET 100; │ 109 │ └─────┘ ``` + ## http_connection_timeout {#http_connection_timeout} Тайм-аут для HTTP-соединения (в секундах). diff --git a/docs/zh/engines/table-engines/integrations/kafka.md b/docs/zh/engines/table-engines/integrations/kafka.md index 5ab1e0573af..fd4e5e9c10a 100644 --- a/docs/zh/engines/table-engines/integrations/kafka.md +++ b/docs/zh/engines/table-engines/integrations/kafka.md @@ -163,4 +163,4 @@ clickhouse也支持自己使用keyfile的方式来维护kerbros的凭证。配 **另请参阅** - [虚拟列](../../../engines/table-engines/index.md#table_engines-virtual_columns) -- [后台消息代理调度池大小](../../../operations/settings/settings.md#background_message_broker_schedule_pool_size) +- [后台消息代理调度池大小](../../../operations/server-configuration-parameters/settings.md#background_message_broker_schedule_pool_size) diff --git a/docs/zh/engines/table-engines/mergetree-family/mergetree.md b/docs/zh/engines/table-engines/mergetree-family/mergetree.md index 54524388650..6775662d555 100644 --- a/docs/zh/engines/table-engines/mergetree-family/mergetree.md +++ b/docs/zh/engines/table-engines/mergetree-family/mergetree.md @@ -689,7 +689,7 @@ SETTINGS storage_policy = 'moving_from_ssd_to_hdd' `default` 存储策略意味着只使用一个卷,这个卷只包含一个在 `` 中定义的磁盘。您可以使用[ALTER TABLE ... MODIFY SETTING]来修改存储策略,新的存储策略应该包含所有以前的磁盘和卷,并使用相同的名称。 -可以通过 [background_move_pool_size](../../../operations/settings/settings.md#background_move_pool_size) 设置调整执行后台任务的线程数。 +可以通过 [background_move_pool_size](../../../operations/server-configuration-parameters/settings.md#background_move_pool_size) 设置调整执行后台任务的线程数。 ### 详细说明 {#details} diff --git a/docs/zh/engines/table-engines/mergetree-family/replication.md b/docs/zh/engines/table-engines/mergetree-family/replication.md index 791ea448212..04d5e7d467c 100644 --- a/docs/zh/engines/table-engines/mergetree-family/replication.md +++ b/docs/zh/engines/table-engines/mergetree-family/replication.md @@ -98,7 +98,7 @@ CREATE TABLE table_name ( ... ) ENGINE = ReplicatedMergeTree('zookeeper_name_con 对于非常大的集群,你可以把不同的 ZooKeeper 集群用于不同的分片。然而,即使 Yandex.Metrica 集群(大约300台服务器)也证明还不需要这么做。 -复制是多主异步。 `INSERT` 语句(以及 `ALTER` )可以发给任意可用的服务器。数据会先插入到执行该语句的服务器上,然后被复制到其他服务器。由于它是异步的,在其他副本上最近插入的数据会有一些延迟。如果部分副本不可用,则数据在其可用时再写入。副本可用的情况下,则延迟时长是通过网络传输压缩数据块所需的时间。为复制表执行后台任务的线程数量,可以通过 [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size) 进行设置。 +复制是多主异步。 `INSERT` 语句(以及 `ALTER` )可以发给任意可用的服务器。数据会先插入到执行该语句的服务器上,然后被复制到其他服务器。由于它是异步的,在其他副本上最近插入的数据会有一些延迟。如果部分副本不可用,则数据在其可用时再写入。副本可用的情况下,则延迟时长是通过网络传输压缩数据块所需的时间。为复制表执行后台任务的线程数量,可以通过 [background_schedule_pool_size](../../../operations/server-configuration-parameters/settings.md#background_schedule_pool_size) 进行设置。 `ReplicatedMergeTree` 引擎采用一个独立的线程池进行复制拉取。线程池的大小通过 [background_fetches_pool_size](../../../operations/settings/settings.md#background_fetches_pool_size) 进行限定,它可以在重启服务器时进行调整。 @@ -282,8 +282,8 @@ sudo -u clickhouse touch /var/lib/clickhouse/flags/force_restore_data **参考** -- [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size) -- [background_fetches_pool_size](../../../operations/settings/settings.md#background_fetches_pool_size) +- [background_schedule_pool_size](../../../operations/server-configuration-parameters/settings.md#background_schedule_pool_size) +- [background_fetches_pool_size](../../../operations/server-configuration-parameters/settings.md#background_fetches_pool_size) - [execute_merges_on_single_replica_time_threshold](../../../operations/settings/settings.md#execute-merges-on-single-replica-time-threshold) - [max_replicated_fetches_network_bandwidth](../../../operations/settings/merge-tree-settings.mdx#max_replicated_fetches_network_bandwidth) - [max_replicated_sends_network_bandwidth](../../../operations/settings/merge-tree-settings.mdx#max_replicated_sends_network_bandwidth) diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 5d172aa4f82..430540807c8 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -1271,7 +1271,7 @@ try { auto new_pool_size = server_settings.background_pool_size; auto new_ratio = server_settings.background_merges_mutations_concurrency_ratio; - global_context->getMergeMutateExecutor()->increaseThreadsAndMaxTasksCount(new_pool_size, new_pool_size * new_ratio); + global_context->getMergeMutateExecutor()->increaseThreadsAndMaxTasksCount(new_pool_size, static_cast(new_pool_size * new_ratio)); global_context->getMergeMutateExecutor()->updateSchedulingPolicy(server_settings.background_merges_mutations_scheduling_policy.toString()); } diff --git a/src/Core/ServerSettings.cpp b/src/Core/ServerSettings.cpp index c50a67b04c9..0a94b0dffcc 100644 --- a/src/Core/ServerSettings.cpp +++ b/src/Core/ServerSettings.cpp @@ -8,11 +8,27 @@ IMPLEMENT_SETTINGS_TRAITS(ServerSettingsTraits, SERVER_SETTINGS) void ServerSettings::loadSettingsFromConfig(const Poco::Util::AbstractConfiguration & config) { + // settings which can be loaded from the the default profile, see also MAKE_DEPRECATED_BY_SERVER_CONFIG in src/Core/Settings.h + std::unordered_set settings_from_profile_allowlist = { + "background_pool_size", + "background_merges_mutations_concurrency_ratio", + "background_merges_mutations_scheduling_policy", + "background_move_pool_size", + "background_fetches_pool_size", + "background_common_pool_size", + "background_buffer_flush_schedule_pool_size", + "background_schedule_pool_size", + "background_message_broker_schedule_pool_size", + "background_distributed_schedule_pool_size" + }; + for (auto setting : all()) { const auto & name = setting.getName(); if (config.has(name)) set(name, config.getString(name)); + else if (settings_from_profile_allowlist.contains(name) && config.has("profiles.default." + name)) + set(name, config.getString("profiles.default." + name)); } } diff --git a/src/Core/ServerSettings.h b/src/Core/ServerSettings.h index efa41a5ec13..753a70a7c25 100644 --- a/src/Core/ServerSettings.h +++ b/src/Core/ServerSettings.h @@ -60,13 +60,13 @@ namespace DB M(UInt64, concurrent_threads_soft_limit_ratio_to_cores, 0, "Same as concurrent_threads_soft_limit_num, but with ratio to cores.", 0) \ \ M(UInt64, background_pool_size, 16, "The maximum number of threads what will be used for merging or mutating data parts for *MergeTree-engine tables in a background.", 0) \ - M(UInt64, background_merges_mutations_concurrency_ratio, 2, "The number of part mutation tasks that can be executed concurrently by each thread in background pool.", 0) \ + M(Float, background_merges_mutations_concurrency_ratio, 2, "The number of part mutation tasks that can be executed concurrently by each thread in background pool.", 0) \ M(String, background_merges_mutations_scheduling_policy, "round_robin", "The policy on how to perform a scheduling for background merges and mutations. Possible values are: `round_robin` and `shortest_task_first`. ", 0) \ M(UInt64, background_move_pool_size, 8, "The maximum number of threads that will be used for moving data parts to another disk or volume for *MergeTree-engine tables in a background.", 0) \ M(UInt64, background_fetches_pool_size, 8, "The maximum number of threads that will be used for fetching data parts from another replica for *MergeTree-engine tables in a background.", 0) \ M(UInt64, background_common_pool_size, 8, "The maximum number of threads that will be used for performing a variety of operations (mostly garbage collection) for *MergeTree-engine tables in a background.", 0) \ M(UInt64, background_buffer_flush_schedule_pool_size, 16, "The maximum number of threads that will be used for performing flush operations for Buffer-engine tables in a background.", 0) \ - M(UInt64, background_schedule_pool_size, 16, "The maximum number of threads that will be used for constantly executing some lightweight periodic operations.", 0) \ + M(UInt64, background_schedule_pool_size, 128, "The maximum number of threads that will be used for constantly executing some lightweight periodic operations.", 0) \ M(UInt64, background_message_broker_schedule_pool_size, 16, "The maximum number of threads that will be used for executing background operations for message streaming.", 0) \ M(UInt64, background_distributed_schedule_pool_size, 16, "The maximum number of threads that will be used for executing distributed sends.", 0) \ diff --git a/src/Core/Settings.h b/src/Core/Settings.h index fe8e9d4dc7d..841abc42543 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -728,6 +728,9 @@ class IColumn; #define MAKE_OBSOLETE(M, TYPE, NAME, DEFAULT) \ M(TYPE, NAME, DEFAULT, "Obsolete setting, does nothing.", BaseSettingsHelpers::Flags::OBSOLETE) +#define MAKE_DEPRECATED_BY_SERVER_CONFIG(M, TYPE, NAME, DEFAULT) \ + M(TYPE, NAME, DEFAULT, "User-level setting is deprecated, and it must be defined in the server configuration instead.", BaseSettingsHelpers::Flags::OBSOLETE) + #define OBSOLETE_SETTINGS(M, ALIAS) \ /** Obsolete settings that do nothing but left for compatibility reasons. Remove each one after half a year of obsolescence. */ \ MAKE_OBSOLETE(M, UInt64, max_memory_usage_for_all_queries, 0) \ @@ -748,15 +751,17 @@ class IColumn; MAKE_OBSOLETE(M, UInt64, partial_merge_join_optimizations, 0) \ MAKE_OBSOLETE(M, MaxThreads, max_alter_threads, 0) \ MAKE_OBSOLETE(M, Bool, allow_experimental_projection_optimization, true) \ - MAKE_OBSOLETE(M, UInt64, background_buffer_flush_schedule_pool_size, 16) \ - MAKE_OBSOLETE(M, UInt64, background_pool_size, 16) \ - MAKE_OBSOLETE(M, Float, background_merges_mutations_concurrency_ratio, 2) \ - MAKE_OBSOLETE(M, UInt64, background_move_pool_size, 8) \ - MAKE_OBSOLETE(M, UInt64, background_fetches_pool_size, 8) \ - MAKE_OBSOLETE(M, UInt64, background_common_pool_size, 8) \ - MAKE_OBSOLETE(M, UInt64, background_schedule_pool_size, 128) \ - MAKE_OBSOLETE(M, UInt64, background_message_broker_schedule_pool_size, 16) \ - MAKE_OBSOLETE(M, UInt64, background_distributed_schedule_pool_size, 16) \ + /* moved to config.xml: see also src/Core/ServerSettings.h */ \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, UInt64, background_buffer_flush_schedule_pool_size, 16) \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, UInt64, background_pool_size, 16) \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, Float, background_merges_mutations_concurrency_ratio, 2) \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, UInt64, background_move_pool_size, 8) \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, UInt64, background_fetches_pool_size, 8) \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, UInt64, background_common_pool_size, 8) \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, UInt64, background_schedule_pool_size, 128) \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, UInt64, background_message_broker_schedule_pool_size, 16) \ + MAKE_DEPRECATED_BY_SERVER_CONFIG(M, UInt64, background_distributed_schedule_pool_size, 16) \ + /* ---- */ \ MAKE_OBSOLETE(M, DefaultDatabaseEngine, default_database_engine, DefaultDatabaseEngine::Atomic) \ MAKE_OBSOLETE(M, UInt64, max_pipeline_depth, 0) \ MAKE_OBSOLETE(M, Seconds, temporary_live_view_timeout, 1) \ diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 286e91f4a40..82a8e43e8e2 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -2179,14 +2180,11 @@ BackgroundSchedulePool & Context::getBufferFlushSchedulePool() const auto lock = getLock(); if (!shared->buffer_flush_schedule_pool) { - size_t background_buffer_flush_schedule_pool_size = 16; - if (getConfigRef().has("background_buffer_flush_schedule_pool_size")) - background_buffer_flush_schedule_pool_size = getConfigRef().getUInt64("background_buffer_flush_schedule_pool_size"); - else if (getConfigRef().has("profiles.default.background_buffer_flush_schedule_pool_size")) - background_buffer_flush_schedule_pool_size = getConfigRef().getUInt64("profiles.default.background_buffer_flush_schedule_pool_size"); + ServerSettings server_settings; + server_settings.loadSettingsFromConfig(getConfigRef()); shared->buffer_flush_schedule_pool = std::make_unique( - background_buffer_flush_schedule_pool_size, + server_settings.background_buffer_flush_schedule_pool_size, CurrentMetrics::BackgroundBufferFlushSchedulePoolTask, CurrentMetrics::BackgroundBufferFlushSchedulePoolSize, "BgBufSchPool"); @@ -2231,14 +2229,11 @@ BackgroundSchedulePool & Context::getSchedulePool() const auto lock = getLock(); if (!shared->schedule_pool) { - size_t background_schedule_pool_size = 128; - if (getConfigRef().has("background_schedule_pool_size")) - background_schedule_pool_size = getConfigRef().getUInt64("background_schedule_pool_size"); - else if (getConfigRef().has("profiles.default.background_schedule_pool_size")) - background_schedule_pool_size = getConfigRef().getUInt64("profiles.default.background_schedule_pool_size"); + ServerSettings server_settings; + server_settings.loadSettingsFromConfig(getConfigRef()); shared->schedule_pool = std::make_unique( - background_schedule_pool_size, + server_settings.background_schedule_pool_size, CurrentMetrics::BackgroundSchedulePoolTask, CurrentMetrics::BackgroundSchedulePoolSize, "BgSchPool"); @@ -2252,14 +2247,11 @@ BackgroundSchedulePool & Context::getDistributedSchedulePool() const auto lock = getLock(); if (!shared->distributed_schedule_pool) { - size_t background_distributed_schedule_pool_size = 16; - if (getConfigRef().has("background_distributed_schedule_pool_size")) - background_distributed_schedule_pool_size = getConfigRef().getUInt64("background_distributed_schedule_pool_size"); - else if (getConfigRef().has("profiles.default.background_distributed_schedule_pool_size")) - background_distributed_schedule_pool_size = getConfigRef().getUInt64("profiles.default.background_distributed_schedule_pool_size"); + ServerSettings server_settings; + server_settings.loadSettingsFromConfig(getConfigRef()); shared->distributed_schedule_pool = std::make_unique( - background_distributed_schedule_pool_size, + server_settings.background_distributed_schedule_pool_size, CurrentMetrics::BackgroundDistributedSchedulePoolTask, CurrentMetrics::BackgroundDistributedSchedulePoolSize, "BgDistSchPool"); @@ -2273,14 +2265,11 @@ BackgroundSchedulePool & Context::getMessageBrokerSchedulePool() const auto lock = getLock(); if (!shared->message_broker_schedule_pool) { - size_t background_message_broker_schedule_pool_size = 16; - if (getConfigRef().has("background_message_broker_schedule_pool_size")) - background_message_broker_schedule_pool_size = getConfigRef().getUInt64("background_message_broker_schedule_pool_size"); - else if (getConfigRef().has("profiles.default.background_message_broker_schedule_pool_size")) - background_message_broker_schedule_pool_size = getConfigRef().getUInt64("profiles.default.background_message_broker_schedule_pool_size"); + ServerSettings server_settings; + server_settings.loadSettingsFromConfig(getConfigRef()); shared->message_broker_schedule_pool = std::make_unique( - background_message_broker_schedule_pool_size, + server_settings.background_message_broker_schedule_pool_size, CurrentMetrics::BackgroundMessageBrokerSchedulePoolTask, CurrentMetrics::BackgroundMessageBrokerSchedulePoolSize, "BgMBSchPool"); @@ -3817,54 +3806,29 @@ void Context::initializeBackgroundExecutorsIfNeeded() const auto & config = getConfigRef(); - size_t background_pool_size = 16; - if (config.has("background_pool_size")) - background_pool_size = config.getUInt64("background_pool_size"); - else if (config.has("profiles.default.background_pool_size")) - background_pool_size = config.getUInt64("profiles.default.background_pool_size"); + ServerSettings server_settings; + server_settings.loadSettingsFromConfig(config); - size_t background_merges_mutations_concurrency_ratio = 2; - if (config.has("background_merges_mutations_concurrency_ratio")) - background_merges_mutations_concurrency_ratio = config.getUInt64("background_merges_mutations_concurrency_ratio"); - else if (config.has("profiles.default.background_merges_mutations_concurrency_ratio")) - background_merges_mutations_concurrency_ratio = config.getUInt64("profiles.default.background_merges_mutations_concurrency_ratio"); - - String background_merges_mutations_scheduling_policy = "round_robin"; - if (config.has("background_merges_mutations_scheduling_policy")) - background_merges_mutations_scheduling_policy = config.getString("background_merges_mutations_scheduling_policy"); - else if (config.has("profiles.default.background_merges_mutations_scheduling_policy")) - background_merges_mutations_scheduling_policy = config.getString("profiles.default.background_merges_mutations_scheduling_policy"); - - size_t background_move_pool_size = 8; - if (config.has("background_move_pool_size")) - background_move_pool_size = config.getUInt64("background_move_pool_size"); - else if (config.has("profiles.default.background_move_pool_size")) - background_move_pool_size = config.getUInt64("profiles.default.background_move_pool_size"); - - size_t background_fetches_pool_size = 8; - if (config.has("background_fetches_pool_size")) - background_fetches_pool_size = config.getUInt64("background_fetches_pool_size"); - else if (config.has("profiles.default.background_fetches_pool_size")) - background_fetches_pool_size = config.getUInt64("profiles.default.background_fetches_pool_size"); - - size_t background_common_pool_size = 8; - if (config.has("background_common_pool_size")) - background_common_pool_size = config.getUInt64("background_common_pool_size"); - else if (config.has("profiles.default.background_common_pool_size")) - background_common_pool_size = config.getUInt64("profiles.default.background_common_pool_size"); + size_t background_pool_size = server_settings.background_pool_size; + auto background_merges_mutations_concurrency_ratio = server_settings.background_merges_mutations_concurrency_ratio; + size_t background_pool_max_tasks_count = static_cast(background_pool_size * background_merges_mutations_concurrency_ratio); + String background_merges_mutations_scheduling_policy = server_settings.background_merges_mutations_scheduling_policy; + size_t background_move_pool_size = server_settings.background_move_pool_size; + size_t background_fetches_pool_size = server_settings.background_fetches_pool_size; + size_t background_common_pool_size = server_settings.background_common_pool_size; /// With this executor we can execute more tasks than threads we have shared->merge_mutate_executor = std::make_shared ( "MergeMutate", /*max_threads_count*/background_pool_size, - /*max_tasks_count*/background_pool_size * background_merges_mutations_concurrency_ratio, + /*max_tasks_count*/background_pool_max_tasks_count, CurrentMetrics::BackgroundMergesAndMutationsPoolTask, CurrentMetrics::BackgroundMergesAndMutationsPoolSize, background_merges_mutations_scheduling_policy ); LOG_INFO(shared->log, "Initialized background executor for merges and mutations with num_threads={}, num_tasks={}, scheduling_policy={}", - background_pool_size, background_pool_size * background_merges_mutations_concurrency_ratio, background_merges_mutations_scheduling_policy); + background_pool_size, background_pool_max_tasks_count, background_merges_mutations_scheduling_policy); shared->moves_executor = std::make_shared ( From 7f841454d1b20bd9017f508382b0924f64fb4406 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Thu, 30 Mar 2023 14:57:00 +0000 Subject: [PATCH 259/377] add more test cases --- .../0_stateless/02169_map_functions.reference | 18 ++++++++++++++++++ .../0_stateless/02169_map_functions.sql | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/tests/queries/0_stateless/02169_map_functions.reference b/tests/queries/0_stateless/02169_map_functions.reference index fc7cd1a3c4b..ed5394726c0 100644 --- a/tests/queries/0_stateless/02169_map_functions.reference +++ b/tests/queries/0_stateless/02169_map_functions.reference @@ -35,3 +35,21 @@ {'k1':11,'k2':22} {'k1':11,'k2':22} {'k1':11,'k2':22} +{'k1':1,'k2':22,'k3':33,'k4':44} +{'k1':1,'k2':22,'k3':33,'k4':44} +{'k1':1,'k2':22,'k3':33,'k4':44} +{'k1':1,'k2':22,'k3':33,'k4':44} +{'k1':1,'k2':2,'k3':33,'k4':44} +{'k1':1,'k2':2,'k3':33,'k4':44} +{'k1':1,'k2':2,'k3':33,'k4':44} +{'k1':1,'k2':2,'k3':33,'k4':44} +{} +{0:0} +{1:1,0:0} +{1:1,0:0,2:4} +{1:1,3:3,0:0,2:4} +{1:1,3:3,0:0,2:4,4:16} +{1:1,3:3,5:5,0:0,2:4,4:16} +{1:1,3:3,5:5,0:0,2:4,4:16,6:36} +{1:1,3:3,5:5,7:7,0:0,2:4,4:16,6:36} +{1:1,3:3,5:5,7:7,0:0,2:4,4:16,6:36,8:64} diff --git a/tests/queries/0_stateless/02169_map_functions.sql b/tests/queries/0_stateless/02169_map_functions.sql index 31112e18b58..1f43647c509 100644 --- a/tests/queries/0_stateless/02169_map_functions.sql +++ b/tests/queries/0_stateless/02169_map_functions.sql @@ -20,6 +20,20 @@ SELECT mapUpdate(materialize(map('k1', 1, 'k2', 2)), map('k1', 11, 'k2', 22)); SELECT mapUpdate(map('k1', 1, 'k2', 2), materialize(map('k1', 11, 'k2', 22))); SELECT mapUpdate(materialize(map('k1', 1, 'k2', 2)), materialize(map('k1', 11, 'k2', 22))); +SELECT mapUpdate(map('k1', 1, 'k2', 2, 'k3', 3), map('k2', 22, 'k3', 33, 'k4', 44)); +SELECT mapUpdate(materialize(map('k1', 1, 'k2', 2, 'k3', 3)), map('k2', 22, 'k3', 33, 'k4', 44)); +SELECT mapUpdate(map('k1', 1, 'k2', 2, 'k3', 3), materialize(map('k2', 22, 'k3', 33, 'k4', 44))); +SELECT mapUpdate(materialize(map('k1', 1, 'k2', 2, 'k3', 3)), materialize(map('k2', 22, 'k3', 33, 'k4', 44))); + +SELECT mapUpdate(map('k1', 1, 'k2', 2), map('k3', 33, 'k4', 44)); +SELECT mapUpdate(materialize(map('k1', 1, 'k2', 2)), map('k3', 33, 'k4', 44)); +SELECT mapUpdate(map('k1', 1, 'k2', 2), materialize(map('k3', 33, 'k4', 44))); +SELECT mapUpdate(materialize(map('k1', 1, 'k2', 2)), materialize(map('k3', 33, 'k4', 44))); + +WITH (range(0, number % 10), range(0, number % 10))::Map(UInt64, UInt64) AS m1, + (range(0, number % 10, 2), arrayMap(x -> x * x, range(0, number % 10, 2)))::Map(UInt64, UInt64) AS m2 +SELECT DISTINCT mapUpdate(m1, m2) FROM numbers (100000); + SELECT mapApply(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } SELECT mapApply((x, y) -> (x), map(1, 0, 2, 0)); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } SELECT mapApply((x, y) -> ('x'), map(1, 0, 2, 0)); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } From c6056d993daf9f9ebabb867aa7dda8f3256c0e1f Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 18:00:52 +0300 Subject: [PATCH 260/377] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a09e4d4fe24..bb85d8e33ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ #### New Feature * Add new mode for splitting the work on replicas using settings `parallel_replicas_custom_key` and `parallel_replicas_custom_key_filter_type`. If the cluster consists of a single shard with multiple replicas, up to `max_parallel_replicas` will be randomly picked and turned into shards. For each shard, a corresponding filter is added to the query on the initiator before being sent to the shard. If the cluster consists of multiple shards, it will behave the same as `sample_key` but with the possibility to define an arbitrary key. [#45108](https://github.com/ClickHouse/ClickHouse/pull/45108) ([Antonio Andelic](https://github.com/antonio2368)). -* An option to display partial result on cancel: Added query setting `stop_reading_on_first_cancel` allowing the canceled query (e.g. due to Ctrl-C) to return a partial result. [#45689](https://github.com/ClickHouse/ClickHouse/pull/45689) ([Alexey Perevyshin](https://github.com/alexX512)). +* An option to display partial result on cancel: Added query setting `partial_result_on_first_cancel` allowing the canceled query (e.g. due to Ctrl-C) to return a partial result. [#45689](https://github.com/ClickHouse/ClickHouse/pull/45689) ([Alexey Perevyshin](https://github.com/alexX512)). * Added support of arbitrary tables engines for temporary tables (except for Replicated and KeeperMap engines). Close [#31497](https://github.com/ClickHouse/ClickHouse/issues/31497). [#46071](https://github.com/ClickHouse/ClickHouse/pull/46071) ([Roman Vasin](https://github.com/rvasin)). * Add support for replication of user-defined SQL functions using a centralized storage in Keeper. [#46085](https://github.com/ClickHouse/ClickHouse/pull/46085) ([Aleksei Filatov](https://github.com/aalexfvk)). * Implement `system.server_settings` (similar to `system.settings`), which will contain server configurations. [#46550](https://github.com/ClickHouse/ClickHouse/pull/46550) ([pufit](https://github.com/pufit)). From d186d356a6fc3415fe7f3190e92361bed57fab47 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 30 Mar 2023 18:01:52 +0300 Subject: [PATCH 261/377] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb85d8e33ae..5decadf8f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### ClickHouse release 23.3 LTS, 2023-03-30 #### Upgrade Notes +* Lightweight DELETEs are production ready and enabled by default. The `DELETE` query for MergeTree tables is now available by default. * The behavior of `*domain*RFC` and `netloc` functions is slightly changed: relaxed the set of symbols that are allowed in the URL authority for better conformance. [#46841](https://github.com/ClickHouse/ClickHouse/pull/46841) ([Azat Khuzhin](https://github.com/azat)). * Prohibited creating tables based on KafkaEngine with DEFAULT/EPHEMERAL/ALIAS/MATERIALIZED statements for columns. [#47138](https://github.com/ClickHouse/ClickHouse/pull/47138) ([Aleksandr Musorin](https://github.com/AVMusorin)). * An "asynchronous connection drain" feature is removed. Related settings and metrics are removed as well. It was an internal feature, so the removal should not affect users who had never heard about that feature. [#47486](https://github.com/ClickHouse/ClickHouse/pull/47486) ([Alexander Tokmakov](https://github.com/tavplubix)). From 06bf500c3062449f48b566f6a7b46e301366b534 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 30 Mar 2023 17:36:56 +0200 Subject: [PATCH 262/377] Update settings.md --- docs/en/operations/settings/settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index 533072a2372..43797f593d2 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -4061,7 +4061,7 @@ Possible values: Default value: `0`. ## partial_result_on_first_cancel {#partial_result_on_first_cancel} -When set to `true` and the user wants to interrupt a query (for example using `Ctrl+C` on the client), then the query continues execution only on data that was already read from the table. Afterward, it will return a partial result of the query for the part of the table that was read. To fully stop the execution of a query without a partial result, the user should send 2 cancel requests. +When set to `true` and the user wants to interrupt a query (for example using `Ctrl+C` on the client), then the query continues execution only on data that was already read from the table. Afterwards, it will return a partial result of the query for the part of the table that was read. To fully stop the execution of a query without a partial result, the user should send 2 cancel requests. **Example without setting on Ctrl+C** ```sql From 9593ffc7cb928d56eb03d8a53047cd04716cfdcd Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 30 Mar 2023 17:37:31 +0200 Subject: [PATCH 263/377] Determine the hosts order in `SHOW CLUSTER` query --- src/Interpreters/InterpreterShowTablesQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterShowTablesQuery.cpp b/src/Interpreters/InterpreterShowTablesQuery.cpp index a631cb72722..158c74d7dc6 100644 --- a/src/Interpreters/InterpreterShowTablesQuery.cpp +++ b/src/Interpreters/InterpreterShowTablesQuery.cpp @@ -87,7 +87,7 @@ String InterpreterShowTablesQuery::getRewrittenQuery() rewritten_query << " WHERE cluster = " << DB::quote << query.cluster_str; /// (*) - rewritten_query << " ORDER BY cluster"; + rewritten_query << " ORDER BY cluster, shard_num, replica_num, host_name, host_address, port"; return rewritten_query.str(); } From 1c58a911eb77eee5abcc1cdd78d71de9690d3323 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 30 Mar 2023 18:07:40 +0200 Subject: [PATCH 264/377] Push clickhouse-keeper as both w/ and w/o suffix `-alpine` --- .github/workflows/backport_branches.yml | 2 +- .github/workflows/master.yml | 2 +- .github/workflows/pull_request.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/release_branches.yml | 2 +- docker/keeper/Dockerfile | 3 +++ docker/keeper/Dockerfile.ubuntu | 1 + 7 files changed, 9 insertions(+), 5 deletions(-) create mode 120000 docker/keeper/Dockerfile.ubuntu diff --git a/.github/workflows/backport_branches.yml b/.github/workflows/backport_branches.yml index 867cca9d037..0d81a7b303c 100644 --- a/.github/workflows/backport_branches.yml +++ b/.github/workflows/backport_branches.yml @@ -470,7 +470,7 @@ jobs: cd "$GITHUB_WORKSPACE/tests/ci" python3 docker_server.py --release-type head --no-push \ --image-repo clickhouse/clickhouse-server --image-path docker/server - python3 docker_server.py --release-type head --no-push --no-ubuntu \ + python3 docker_server.py --release-type head --no-push \ --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - name: Cleanup if: always() diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 7c5e477ab60..b1ea1641a02 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -862,7 +862,7 @@ jobs: cd "$GITHUB_WORKSPACE/tests/ci" python3 docker_server.py --release-type head \ --image-repo clickhouse/clickhouse-server --image-path docker/server - python3 docker_server.py --release-type head --no-ubuntu \ + python3 docker_server.py --release-type head \ --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - name: Cleanup if: always() diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 2f2c263df37..ab0cbbb7ec1 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -918,7 +918,7 @@ jobs: cd "$GITHUB_WORKSPACE/tests/ci" python3 docker_server.py --release-type head --no-push \ --image-repo clickhouse/clickhouse-server --image-path docker/server - python3 docker_server.py --release-type head --no-push --no-ubuntu \ + python3 docker_server.py --release-type head --no-push \ --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - name: Cleanup if: always() diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73246af6dfc..0742ebfd449 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,7 +55,7 @@ jobs: cd "$GITHUB_WORKSPACE/tests/ci" python3 docker_server.py --release-type auto --version "$GITHUB_TAG" \ --image-repo clickhouse/clickhouse-server --image-path docker/server - python3 docker_server.py --release-type auto --version "$GITHUB_TAG" --no-ubuntu \ + python3 docker_server.py --release-type auto --version "$GITHUB_TAG" \ --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - name: Cleanup if: always() diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index e56a1fb58fc..1282dbef50b 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -527,7 +527,7 @@ jobs: cd "$GITHUB_WORKSPACE/tests/ci" python3 docker_server.py --release-type head --no-push \ --image-repo clickhouse/clickhouse-server --image-path docker/server - python3 docker_server.py --release-type head --no-push --no-ubuntu \ + python3 docker_server.py --release-type head --no-push \ --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - name: Cleanup if: always() diff --git a/docker/keeper/Dockerfile b/docker/keeper/Dockerfile index 34c1406b687..6496a2b2a12 100644 --- a/docker/keeper/Dockerfile +++ b/docker/keeper/Dockerfile @@ -1,3 +1,6 @@ +# The Dockerfile.ubuntu exists for the tests/ci/docker_server.py script +# If the image is built from Dockerfile.alpine, then the `-alpine` suffix is added automatically, +# so the only purpose of Dockerfile.ubuntu is to push `latest`, `head` and so on w/o suffixes FROM ubuntu:20.04 AS glibc-donor ARG TARGETARCH diff --git a/docker/keeper/Dockerfile.ubuntu b/docker/keeper/Dockerfile.ubuntu new file mode 120000 index 00000000000..1d1fe94df49 --- /dev/null +++ b/docker/keeper/Dockerfile.ubuntu @@ -0,0 +1 @@ +Dockerfile \ No newline at end of file From 3a3438ebf970cd4eae6de275e04bb4699a9266d5 Mon Sep 17 00:00:00 2001 From: Rich Raposa Date: Thu, 30 Mar 2023 10:26:33 -0600 Subject: [PATCH 265/377] Update settings-formats.md Default compression method for Parquet changed from snappy to lz4 in 23.3 --- docs/en/operations/settings/settings-formats.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/operations/settings/settings-formats.md b/docs/en/operations/settings/settings-formats.md index 40ca914b738..91b67ee8238 100644 --- a/docs/en/operations/settings/settings-formats.md +++ b/docs/en/operations/settings/settings-formats.md @@ -1128,7 +1128,7 @@ Default value: `2.latest`. Compression method used in output Parquet format. Supported codecs: `snappy`, `lz4`, `brotli`, `zstd`, `gzip`, `none` (uncompressed) -Default value: `snappy`. +Default value: `lz4`. ## Hive format settings {#hive-format-settings} From 4675fa82a33dad335a782116e53bc9a76eb1dfd1 Mon Sep 17 00:00:00 2001 From: Rich Raposa Date: Thu, 30 Mar 2023 10:29:08 -0600 Subject: [PATCH 266/377] Update delete.md Lightweight deletes are no longer experimental in 23.3 --- docs/en/sql-reference/statements/delete.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/en/sql-reference/statements/delete.md b/docs/en/sql-reference/statements/delete.md index 7d7b8855d51..149e7ab371f 100644 --- a/docs/en/sql-reference/statements/delete.md +++ b/docs/en/sql-reference/statements/delete.md @@ -21,15 +21,6 @@ DELETE FROM hits WHERE Title LIKE '%hello%'; Lightweight deletes are asynchronous by default. Set `mutations_sync` equal to 1 to wait for one replica to process the statement, and set `mutations_sync` to 2 to wait for all replicas. -:::note -This feature is experimental and requires you to set `allow_experimental_lightweight_delete` to true: - -```sql -SET allow_experimental_lightweight_delete = true; -``` - -::: - :::note `DELETE FROM` requires the `ALTER DELETE` privilege: ```sql @@ -64,6 +55,3 @@ With the described implementation now we can see what can negatively affect 'DEL - Table having a very large number of data parts - Having a lot of data in Compact parts—in a Compact part, all columns are stored in one file. -:::note -This implementation might change in the future. -::: From 191f78022321786d2dd6fa9f3369b2849be4107e Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 30 Mar 2023 17:22:20 +0000 Subject: [PATCH 267/377] Sort output of SHOW PROCESSLIST Follow-up to #48127 --- src/Interpreters/InterpreterShowProcesslistQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterShowProcesslistQuery.cpp b/src/Interpreters/InterpreterShowProcesslistQuery.cpp index 780ba688a89..1522af4bcbe 100644 --- a/src/Interpreters/InterpreterShowProcesslistQuery.cpp +++ b/src/Interpreters/InterpreterShowProcesslistQuery.cpp @@ -12,7 +12,7 @@ namespace DB BlockIO InterpreterShowProcesslistQuery::execute() { - return executeQuery("SELECT * FROM system.processes", getContext(), true); + return executeQuery("SELECT * FROM system.processes ORDER BY query_id", getContext(), true); } } From 4938681f87a68cc382d6817048308d5d259b3669 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 30 Mar 2023 17:46:11 +0000 Subject: [PATCH 268/377] Fix macos build --- docs/en/development/build-cross-osx.md | 8 ++++---- src/IO/VarInt.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en/development/build-cross-osx.md b/docs/en/development/build-cross-osx.md index 1df88dbb235..e6e5bd6ca4d 100644 --- a/docs/en/development/build-cross-osx.md +++ b/docs/en/development/build-cross-osx.md @@ -11,14 +11,14 @@ This is intended for continuous integration checks that run on Linux servers. If The cross-build for macOS is based on the [Build instructions](../development/build.md), follow them first. -## Install Clang-14 +## Install Clang-15 Follow the instructions from https://apt.llvm.org/ for your Ubuntu or Debian setup. For example the commands for Bionic are like: ``` bash -sudo echo "deb [trusted=yes] http://apt.llvm.org/bionic/ llvm-toolchain-bionic-14 main" >> /etc/apt/sources.list -sudo apt-get install clang-14 +sudo echo "deb [trusted=yes] http://apt.llvm.org/bionic/ llvm-toolchain-bionic-15 main" >> /etc/apt/sources.list +sudo apt-get install clang-15 ``` ## Install Cross-Compilation Toolset {#install-cross-compilation-toolset} @@ -55,7 +55,7 @@ curl -L 'https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX1 cd ClickHouse mkdir build-darwin cd build-darwin -CC=clang-14 CXX=clang++-14 cmake -DCMAKE_AR:FILEPATH=${CCTOOLS}/bin/x86_64-apple-darwin-ar -DCMAKE_INSTALL_NAME_TOOL=${CCTOOLS}/bin/x86_64-apple-darwin-install_name_tool -DCMAKE_RANLIB:FILEPATH=${CCTOOLS}/bin/x86_64-apple-darwin-ranlib -DLINKER_NAME=${CCTOOLS}/bin/x86_64-apple-darwin-ld -DCMAKE_TOOLCHAIN_FILE=cmake/darwin/toolchain-x86_64.cmake .. +CC=clang-15 CXX=clang++-15 cmake -DCMAKE_AR:FILEPATH=${CCTOOLS}/bin/x86_64-apple-darwin-ar -DCMAKE_INSTALL_NAME_TOOL=${CCTOOLS}/bin/x86_64-apple-darwin-install_name_tool -DCMAKE_RANLIB:FILEPATH=${CCTOOLS}/bin/x86_64-apple-darwin-ranlib -DLINKER_NAME=${CCTOOLS}/bin/x86_64-apple-darwin-ld -DCMAKE_TOOLCHAIN_FILE=cmake/darwin/toolchain-x86_64.cmake .. ninja ``` diff --git a/src/IO/VarInt.h b/src/IO/VarInt.h index f95b479df11..fa1908cf615 100644 --- a/src/IO/VarInt.h +++ b/src/IO/VarInt.h @@ -25,7 +25,7 @@ namespace ErrorCodes * So implementing VLQ for the whole 1<<64 range will require different set of * helpers. */ -constexpr size_t VAR_UINT_MAX = (1ULL<<63) - 1; +constexpr UInt64 VAR_UINT_MAX = (1ULL<<63) - 1; /** Write UInt64 in variable length format (base128) */ void writeVarUInt(UInt64 x, std::ostream & ostr); From edeeac84cf0792503c2f9efc65390ce0d735648e Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 30 Mar 2023 20:08:38 +0200 Subject: [PATCH 269/377] Fix incorrect ThreadPool usage after ThreadPool introspection ``` $ gg 'ThreadPool[^()]([A-Za-z_]\+,' src/ src/Interpreters/Context.cpp: shared->load_marks_threadpool = std::make_unique(pool_size, pool_size, queue_size); src/Interpreters/Context.cpp: shared->prefetch_threadpool = std::make_unique(pool_size, pool_size, queue_size); src/Interpreters/Context.cpp: shared->threadpool_writer = std::make_unique(pool_size, pool_size, queue_size); ``` Fixes: #47880 Signed-off-by: Azat Khuzhin --- src/Common/CurrentMetrics.cpp | 6 ++++++ src/Interpreters/Context.cpp | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Common/CurrentMetrics.cpp b/src/Common/CurrentMetrics.cpp index 4c773048597..542c48148c8 100644 --- a/src/Common/CurrentMetrics.cpp +++ b/src/Common/CurrentMetrics.cpp @@ -78,6 +78,12 @@ M(BackupsThreadsActive, "Number of threads in thread pool for BACKUP running a task.") \ M(RestoreThreads, "Number of threads in the thread pool for RESTORE.") \ M(RestoreThreadsActive, "Number of threads in the thread pool for RESTORE running a task.") \ + M(MarksLoaderThreads, "Number of threads in thread pool for loading marks.") \ + M(MarksLoaderThreadsActive, "Number of threads in the thread pool for loading marks running a task.") \ + M(IOPrefetchThreads, "Number of threads in the IO prefertch thread pool.") \ + M(IOPrefetchThreadsActive, "Number of threads in the IO prefetch thread pool running a task.") \ + M(IOWriterThreads, "Number of threads in the IO writer thread pool.") \ + M(IOWriterThreadsActive, "Number of threads in the IO writer thread pool running a task.") \ M(IOThreads, "Number of threads in the IO thread pool.") \ M(IOThreadsActive, "Number of threads in the IO thread pool running a task.") \ M(ThreadPoolRemoteFSReaderThreads, "Number of threads in the thread pool for remote_filesystem_read_method=threadpool.") \ diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 82a8e43e8e2..522107dccc9 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -145,6 +145,12 @@ namespace CurrentMetrics extern const Metric BackgroundFetchesPoolSize; extern const Metric BackgroundCommonPoolTask; extern const Metric BackgroundCommonPoolSize; + extern const Metric MarksLoaderThreads; + extern const Metric MarksLoaderThreadsActive; + extern const Metric IOPrefetchThreads; + extern const Metric IOPrefetchThreadsActive; + extern const Metric IOWriterThreads; + extern const Metric IOWriterThreadsActive; } namespace DB @@ -2018,7 +2024,8 @@ ThreadPool & Context::getLoadMarksThreadpool() const { auto pool_size = config.getUInt(".load_marks_threadpool_pool_size", 50); auto queue_size = config.getUInt(".load_marks_threadpool_queue_size", 1000000); - shared->load_marks_threadpool = std::make_unique(pool_size, pool_size, queue_size); + shared->load_marks_threadpool = std::make_unique( + CurrentMetrics::MarksLoaderThreads, CurrentMetrics::MarksLoaderThreadsActive, pool_size, pool_size, queue_size); } return *shared->load_marks_threadpool; } @@ -2043,7 +2050,8 @@ ThreadPool & Context::getPrefetchThreadpool() const { auto pool_size = getPrefetchThreadpoolSize(); auto queue_size = config.getUInt(".prefetch_threadpool_queue_size", 1000000); - shared->prefetch_threadpool = std::make_unique(pool_size, pool_size, queue_size); + shared->prefetch_threadpool = std::make_unique( + CurrentMetrics::IOPrefetchThreads, CurrentMetrics::IOPrefetchThreadsActive, pool_size, pool_size, queue_size); } return *shared->prefetch_threadpool; } @@ -3967,7 +3975,8 @@ ThreadPool & Context::getThreadPoolWriter() const auto pool_size = config.getUInt(".threadpool_writer_pool_size", 100); auto queue_size = config.getUInt(".threadpool_writer_queue_size", 1000000); - shared->threadpool_writer = std::make_unique(pool_size, pool_size, queue_size); + shared->threadpool_writer = std::make_unique( + CurrentMetrics::IOWriterThreads, CurrentMetrics::IOWriterThreadsActive, pool_size, pool_size, queue_size); } return *shared->threadpool_writer; From 37213aa6b49b773a81e4810f8d65d7a9210f4e1f Mon Sep 17 00:00:00 2001 From: DanRoscigno Date: Thu, 30 Mar 2023 14:45:01 -0400 Subject: [PATCH 270/377] edits --- CHANGELOG.md | 72 ++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5decadf8f8c..47320208f02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,60 +18,60 @@ * Do not allow const and non-deterministic secondary indices [#46839](https://github.com/ClickHouse/ClickHouse/pull/46839) ([Anton Popov](https://github.com/CurtizJ)). #### New Feature -* Add new mode for splitting the work on replicas using settings `parallel_replicas_custom_key` and `parallel_replicas_custom_key_filter_type`. If the cluster consists of a single shard with multiple replicas, up to `max_parallel_replicas` will be randomly picked and turned into shards. For each shard, a corresponding filter is added to the query on the initiator before being sent to the shard. If the cluster consists of multiple shards, it will behave the same as `sample_key` but with the possibility to define an arbitrary key. [#45108](https://github.com/ClickHouse/ClickHouse/pull/45108) ([Antonio Andelic](https://github.com/antonio2368)). +* Add a new mode for splitting the work on replicas using settings `parallel_replicas_custom_key` and `parallel_replicas_custom_key_filter_type`. If the cluster consists of a single shard with multiple replicas, up to `max_parallel_replicas` will be randomly picked and turned into shards. For each shard, a corresponding filter is added to the query on the initiator before being sent to the shard. If the cluster consists of multiple shards, it will behave the same as `sample_key` but with the possibility to define an arbitrary key. [#45108](https://github.com/ClickHouse/ClickHouse/pull/45108) ([Antonio Andelic](https://github.com/antonio2368)). * An option to display partial result on cancel: Added query setting `partial_result_on_first_cancel` allowing the canceled query (e.g. due to Ctrl-C) to return a partial result. [#45689](https://github.com/ClickHouse/ClickHouse/pull/45689) ([Alexey Perevyshin](https://github.com/alexX512)). * Added support of arbitrary tables engines for temporary tables (except for Replicated and KeeperMap engines). Close [#31497](https://github.com/ClickHouse/ClickHouse/issues/31497). [#46071](https://github.com/ClickHouse/ClickHouse/pull/46071) ([Roman Vasin](https://github.com/rvasin)). -* Add support for replication of user-defined SQL functions using a centralized storage in Keeper. [#46085](https://github.com/ClickHouse/ClickHouse/pull/46085) ([Aleksei Filatov](https://github.com/aalexfvk)). +* Add support for replication of user-defined SQL functions using centralized storage in Keeper. [#46085](https://github.com/ClickHouse/ClickHouse/pull/46085) ([Aleksei Filatov](https://github.com/aalexfvk)). * Implement `system.server_settings` (similar to `system.settings`), which will contain server configurations. [#46550](https://github.com/ClickHouse/ClickHouse/pull/46550) ([pufit](https://github.com/pufit)). * Support for `UNDROP TABLE` query. Closes [#46811](https://github.com/ClickHouse/ClickHouse/issues/46811). [#47241](https://github.com/ClickHouse/ClickHouse/pull/47241) ([chen](https://github.com/xiedeyantu)). -* Allow separate grants for named collections (e.g. to be able to give `SHOW/CREATE/ALTER/DROP named collection` access only to certain collections, instead of all at once). Closes [#40894](https://github.com/ClickHouse/ClickHouse/issues/40894). Add new access type `NAMED_COLLECTION_CONTROL` which is not given to default user unless explicitly added to user config (is required to be able to do `GRANT ALL`), also `show_named_collections` is no longer obligatory to be manually specified for default user to be able to have full access rights as was in 23.2. [#46241](https://github.com/ClickHouse/ClickHouse/pull/46241) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Allow separate grants for named collections (e.g. to be able to give `SHOW/CREATE/ALTER/DROP named collection` access only to certain collections, instead of all at once). Closes [#40894](https://github.com/ClickHouse/ClickHouse/issues/40894). Add new access type `NAMED_COLLECTION_CONTROL` which is not given to user default unless explicitly added to the user config (is required to be able to do `GRANT ALL`), also `show_named_collections` is no longer obligatory to be manually specified for user default to be able to have full access rights as was in 23.2. [#46241](https://github.com/ClickHouse/ClickHouse/pull/46241) ([Kseniia Sumarokova](https://github.com/kssenii)). * Allow nested custom disks. Previously custom disks supported only flat disk structure. [#47106](https://github.com/ClickHouse/ClickHouse/pull/47106) ([Kseniia Sumarokova](https://github.com/kssenii)). -* Intruduce a function `widthBucket` (with a `WIDTH_BUCKET` alias for compatibility). [#42974](https://github.com/ClickHouse/ClickHouse/issues/42974). [#46790](https://github.com/ClickHouse/ClickHouse/pull/46790) ([avoiderboi](https://github.com/avoiderboi)). -* Add new function `parseDateTime`/`parseDateTimeInJodaSyntax` according to specified format string. parseDateTime parses string to datetime in MySQL syntax, parseDateTimeInJodaSyntax parses in Joda syntax. [#46815](https://github.com/ClickHouse/ClickHouse/pull/46815) ([李扬](https://github.com/taiyang-li)). -* Use `dummy UInt8` for default structure of table function `null`. Closes [#46930](https://github.com/ClickHouse/ClickHouse/issues/46930). [#47006](https://github.com/ClickHouse/ClickHouse/pull/47006) ([flynn](https://github.com/ucasfl)). +* Introduce a function `widthBucket` (with a `WIDTH_BUCKET` alias for compatibility). [#42974](https://github.com/ClickHouse/ClickHouse/issues/42974). [#46790](https://github.com/ClickHouse/ClickHouse/pull/46790) ([avoiderboi](https://github.com/avoiderboi)). +* Add new function `parseDateTime`/`parseDateTimeInJodaSyntax` according to the specified format string. parseDateTime parses String to DateTime in MySQL syntax, parseDateTimeInJodaSyntax parses in Joda syntax. [#46815](https://github.com/ClickHouse/ClickHouse/pull/46815) ([李扬](https://github.com/taiyang-li)). +* Use `dummy UInt8` for the default structure of table function `null`. Closes [#46930](https://github.com/ClickHouse/ClickHouse/issues/46930). [#47006](https://github.com/ClickHouse/ClickHouse/pull/47006) ([flynn](https://github.com/ucasfl)). * Support for date format with a comma, like `Dec 15, 2021` in the `parseDateTimeBestEffort` function. Closes [#46816](https://github.com/ClickHouse/ClickHouse/issues/46816). [#47071](https://github.com/ClickHouse/ClickHouse/pull/47071) ([chen](https://github.com/xiedeyantu)). -* Add settings `http_wait_end_of_query` and `http_response_buffer_size` that corresponds to URL params `wait_end_of_query` and `buffer_size` for HTTP interface. This allows to change these settings in the profiles. [#47108](https://github.com/ClickHouse/ClickHouse/pull/47108) ([Vladimir C](https://github.com/vdimir)). +* Add settings `http_wait_end_of_query` and `http_response_buffer_size` that corresponds to URL params `wait_end_of_query` and `buffer_size` for the HTTP interface. This allows changing these settings in the profiles. [#47108](https://github.com/ClickHouse/ClickHouse/pull/47108) ([Vladimir C](https://github.com/vdimir)). * Add `system.dropped_tables` table that shows tables that were dropped from `Atomic` databases but were not completely removed yet. [#47364](https://github.com/ClickHouse/ClickHouse/pull/47364) ([chen](https://github.com/xiedeyantu)). * Add `INSTR` as alias of `positionCaseInsensitive` for MySQL compatibility. Closes [#47529](https://github.com/ClickHouse/ClickHouse/issues/47529). [#47535](https://github.com/ClickHouse/ClickHouse/pull/47535) ([flynn](https://github.com/ucasfl)). * Added `toDecimalString` function allowing to convert numbers to string with fixed precision. [#47838](https://github.com/ClickHouse/ClickHouse/pull/47838) ([Andrey Zvonov](https://github.com/zvonand)). * Add a merge tree setting `max_number_of_mutations_for_replica`. It limits the number of part mutations per replica to the specified amount. Zero means no limit on the number of mutations per replica (the execution can still be constrained by other settings). [#48047](https://github.com/ClickHouse/ClickHouse/pull/48047) ([Vladimir C](https://github.com/vdimir)). -* Add Map-related function `mapFromArrays`, which allows us to create map from a pair of arrays. [#31125](https://github.com/ClickHouse/ClickHouse/pull/31125) ([李扬](https://github.com/taiyang-li)). -* Allow control compression in Parquet/ORC/Arrow output formats, support more compression for input formats. This closes [#13541](https://github.com/ClickHouse/ClickHouse/issues/13541). [#47114](https://github.com/ClickHouse/ClickHouse/pull/47114) ([Kruglov Pavel](https://github.com/Avogar)). +* Add the Map-related function `mapFromArrays`, which allows the creation of a map from a pair of arrays. [#31125](https://github.com/ClickHouse/ClickHouse/pull/31125) ([李扬](https://github.com/taiyang-li)). +* Allow control of compression in Parquet/ORC/Arrow output formats, adds support for more compression input formats. This closes [#13541](https://github.com/ClickHouse/ClickHouse/issues/13541). [#47114](https://github.com/ClickHouse/ClickHouse/pull/47114) ([Kruglov Pavel](https://github.com/Avogar)). * Add SSL User Certificate authentication to the native protocol. Closes [#47077](https://github.com/ClickHouse/ClickHouse/issues/47077). [#47596](https://github.com/ClickHouse/ClickHouse/pull/47596) ([Nikolay Degterinsky](https://github.com/evillique)). * Add *OrNull() and *OrZero() variants for `parseDateTime`, add alias `str_to_date` for MySQL parity. [#48000](https://github.com/ClickHouse/ClickHouse/pull/48000) ([Robert Schulze](https://github.com/rschu1ze)). * Added operator `REGEXP` (similar to operators "LIKE", "IN", "MOD" etc.) for better compatibility with MySQL [#47869](https://github.com/ClickHouse/ClickHouse/pull/47869) ([Robert Schulze](https://github.com/rschu1ze)). #### Performance Improvement * Marks in memory are now compressed, using 3-6x less memory. [#47290](https://github.com/ClickHouse/ClickHouse/pull/47290) ([Michael Kolupaev](https://github.com/al13n321)). -* Backups for large numbers of files were unbelievably slow in previous versions. Not anymore. Now they are unbelievably fast. [#47251](https://github.com/ClickHouse/ClickHouse/pull/47251) ([Alexey Milovidov](https://github.com/alexey-milovidov)). Introduced a separate thread pool for backup's IO operations. This will allow to scale it independently of other pools and increase performance. [#47174](https://github.com/ClickHouse/ClickHouse/pull/47174) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). Use MultiRead request and retries for collecting metadata at final stage of backup processing. [#47243](https://github.com/ClickHouse/ClickHouse/pull/47243) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). If a backup and restoring data are both in S3 then server-side copy should be used from now on. [#47546](https://github.com/ClickHouse/ClickHouse/pull/47546) ([Vitaly Baranov](https://github.com/vitlibar)). +* Backups for large numbers of files were unbelievably slow in previous versions. Not anymore. Now they are unbelievably fast. [#47251](https://github.com/ClickHouse/ClickHouse/pull/47251) ([Alexey Milovidov](https://github.com/alexey-milovidov)). Introduced a separate thread pool for backup's IO operations. This will allow scaling it independently of other pools and increase performance. [#47174](https://github.com/ClickHouse/ClickHouse/pull/47174) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). Use MultiRead request and retries for collecting metadata at the final stage of backup processing. [#47243](https://github.com/ClickHouse/ClickHouse/pull/47243) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). If a backup and restoring data are both in S3 then server-side copy should be used from now on. [#47546](https://github.com/ClickHouse/ClickHouse/pull/47546) ([Vitaly Baranov](https://github.com/vitlibar)). * Fixed excessive reading in queries with `FINAL`. [#47801](https://github.com/ClickHouse/ClickHouse/pull/47801) ([Nikita Taranov](https://github.com/nickitat)). -* Setting `max_final_threads` would be set to number of cores at server startup (by the same algorithm as we use for `max_threads`). This improves concurrency of `final` execution on servers with high number of CPUs. [#47915](https://github.com/ClickHouse/ClickHouse/pull/47915) ([Nikita Taranov](https://github.com/nickitat)). +* Setting `max_final_threads` would be set to the number of cores at server startup (by the same algorithm as used for `max_threads`). This improves the concurrency of `final` execution on servers with high number of CPUs. [#47915](https://github.com/ClickHouse/ClickHouse/pull/47915) ([Nikita Taranov](https://github.com/nickitat)). * Allow executing reading pipeline for DIRECT dictionary with CLICKHOUSE source in multiple threads. To enable set `dictionary_use_async_executor=1` in `SETTINGS` section for source in `CREATE DICTIONARY` statement. [#47986](https://github.com/ClickHouse/ClickHouse/pull/47986) ([Vladimir C](https://github.com/vdimir)). * Optimize one nullable key aggregate performance. [#45772](https://github.com/ClickHouse/ClickHouse/pull/45772) ([LiuNeng](https://github.com/liuneng1994)). * Implemented lowercase `tokenbf_v1` index utilization for `hasTokenOrNull`, `hasTokenCaseInsensitive` and `hasTokenCaseInsensitiveOrNull`. [#46252](https://github.com/ClickHouse/ClickHouse/pull/46252) ([ltrk2](https://github.com/ltrk2)). * Optimize functions `position` and `LIKE` by searching the first two chars using SIMD. [#46289](https://github.com/ClickHouse/ClickHouse/pull/46289) ([Jiebin Sun](https://github.com/jiebinn)). -* Optimize queries from the `system.detached_parts`, which could be significantly large. Added several sources with respect to the block size limitation; in each block an IO thread pool is used to calculate the part size, i.e. to make syscalls in parallel. [#46624](https://github.com/ClickHouse/ClickHouse/pull/46624) ([Sema Checherinda](https://github.com/CheSema)). +* Optimize queries from the `system.detached_parts`, which could be significantly large. Added several sources with respect to the block size limitation; in each block, an IO thread pool is used to calculate the part size, i.e. to make syscalls in parallel. [#46624](https://github.com/ClickHouse/ClickHouse/pull/46624) ([Sema Checherinda](https://github.com/CheSema)). * Increase the default value of `max_replicated_merges_in_queue` for ReplicatedMergeTree tables from 16 to 1000. It allows faster background merge operation on clusters with a very large number of replicas, such as clusters with shared storage in ClickHouse Cloud. [#47050](https://github.com/ClickHouse/ClickHouse/pull/47050) ([Alexey Milovidov](https://github.com/alexey-milovidov)). -* Updated `clickhouse-copier` to use `GROUP BY` instead of `DISTINCT` to get list of partitions. For large tables this reduced the select time from over 500s to under 1s. [#47386](https://github.com/ClickHouse/ClickHouse/pull/47386) ([Clayton McClure](https://github.com/cmcclure-twilio)). +* Updated `clickhouse-copier` to use `GROUP BY` instead of `DISTINCT` to get the list of partitions. For large tables, this reduced the select time from over 500s to under 1s. [#47386](https://github.com/ClickHouse/ClickHouse/pull/47386) ([Clayton McClure](https://github.com/cmcclure-twilio)). * Fix performance degradation in `ASOF JOIN`. [#47544](https://github.com/ClickHouse/ClickHouse/pull/47544) ([Ongkong](https://github.com/ongkong)). -* Even more batching in Keeper. Avoid breaking batches on read requests to improve performance. [#47978](https://github.com/ClickHouse/ClickHouse/pull/47978) ([Antonio Andelic](https://github.com/antonio2368)). -* Allow PREWHERE for Merge with different DEFAULT expression for column. [#46831](https://github.com/ClickHouse/ClickHouse/pull/46831) ([Azat Khuzhin](https://github.com/azat)). +* Even more batching in Keeper. Improve performance by avoiding breaking batches on read requests. [#47978](https://github.com/ClickHouse/ClickHouse/pull/47978) ([Antonio Andelic](https://github.com/antonio2368)). +* Allow PREWHERE for Merge with different DEFAULT expressions for columns. [#46831](https://github.com/ClickHouse/ClickHouse/pull/46831) ([Azat Khuzhin](https://github.com/azat)). #### Experimental Feature -* Parallel replicas: Improved the overall performance by better utilizing local replica. And forbid reading with parallel replicas from non-replicated MergeTree by default. [#47858](https://github.com/ClickHouse/ClickHouse/pull/47858) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). +* Parallel replicas: Improved the overall performance by better utilizing the local replica, and forbid the reading with parallel replicas from non-replicated MergeTree by default. [#47858](https://github.com/ClickHouse/ClickHouse/pull/47858) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). * Support filter push down to left table for JOIN with `Join`, `Dictionary` and `EmbeddedRocksDB` tables if the experimental Analyzer is enabled. [#47280](https://github.com/ClickHouse/ClickHouse/pull/47280) ([Maksim Kita](https://github.com/kitaisreal)). * Now ReplicatedMergeTree with zero copy replication has less load to Keeper. [#47676](https://github.com/ClickHouse/ClickHouse/pull/47676) ([alesapin](https://github.com/alesapin)). * Fix create materialized view with MaterializedPostgreSQL [#40807](https://github.com/ClickHouse/ClickHouse/pull/40807) ([Maksim Buren](https://github.com/maks-buren630501)). #### Improvement * Enable `input_format_json_ignore_unknown_keys_in_named_tuple` by default. [#46742](https://github.com/ClickHouse/ClickHouse/pull/46742) ([Kruglov Pavel](https://github.com/Avogar)). -* Allow to ignore errors while pushing to MATERIALIZED VIEW (add new setting `materialized_views_ignore_errors`, by default to `false`, but it is set to `true` for flushing logs to `system.*_log` tables unconditionally). [#46658](https://github.com/ClickHouse/ClickHouse/pull/46658) ([Azat Khuzhin](https://github.com/azat)). +* Allow errors to be ignored while pushing to MATERIALIZED VIEW (add new setting `materialized_views_ignore_errors`, by default to `false`, but it is set to `true` for flushing logs to `system.*_log` tables unconditionally). [#46658](https://github.com/ClickHouse/ClickHouse/pull/46658) ([Azat Khuzhin](https://github.com/azat)). * Track the file queue of distributed sends in memory. [#45491](https://github.com/ClickHouse/ClickHouse/pull/45491) ([Azat Khuzhin](https://github.com/azat)). -* Now `X-ClickHouse-Query-Id` and `X-ClickHouse-Timezone` headers are added to response in all queries via http protocol. Previously it was done only for `SELECT` queries. [#46364](https://github.com/ClickHouse/ClickHouse/pull/46364) ([Anton Popov](https://github.com/CurtizJ)). +* Now `X-ClickHouse-Query-Id` and `X-ClickHouse-Timezone` headers are added to responses in all queries via HTTP protocol. Previously it was done only for `SELECT` queries. [#46364](https://github.com/ClickHouse/ClickHouse/pull/46364) ([Anton Popov](https://github.com/CurtizJ)). * External tables from `MongoDB`: support for connection to a replica set via a URI with a host:port enum and support for the readPreference option in MongoDB dictionaries. Example URI: mongodb://db0.example.com:27017,db1.example.com:27017,db2.example.com:27017/?replicaSet=myRepl&readPreference=primary. [#46524](https://github.com/ClickHouse/ClickHouse/pull/46524) ([artem-yadr](https://github.com/artem-yadr)). * This improvement should be invisible for users. Re-implement projection analysis on top of query plan. Added setting `query_plan_optimize_projection=1` to switch between old and new version. Fixes [#44963](https://github.com/ClickHouse/ClickHouse/issues/44963). [#46537](https://github.com/ClickHouse/ClickHouse/pull/46537) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). -* Use parquet format v2 instead of v1 in output format by default. Add setting `output_format_parquet_version` to control parquet version, possible values `1.0`, `2.4`, `2.6`, `2.latest` (default). [#46617](https://github.com/ClickHouse/ClickHouse/pull/46617) ([Kruglov Pavel](https://github.com/Avogar)). -* It is now possible using new configuration syntax to configure Kafka topics with periods (`.`) in their name. [#46752](https://github.com/ClickHouse/ClickHouse/pull/46752) ([Robert Schulze](https://github.com/rschu1ze)). +* Use Parquet format v2 instead of v1 in output format by default. Add setting `output_format_parquet_version` to control parquet version, possible values `1.0`, `2.4`, `2.6`, `2.latest` (default). [#46617](https://github.com/ClickHouse/ClickHouse/pull/46617) ([Kruglov Pavel](https://github.com/Avogar)). +* It is now possible to use the new configuration syntax to configure Kafka topics with periods (`.`) in their name. [#46752](https://github.com/ClickHouse/ClickHouse/pull/46752) ([Robert Schulze](https://github.com/rschu1ze)). * Fix heuristics that check hyperscan patterns for problematic repeats. [#46819](https://github.com/ClickHouse/ClickHouse/pull/46819) ([Robert Schulze](https://github.com/rschu1ze)). * Don't report ZK node exists to system.errors when a block was created concurrently by a different replica. [#46820](https://github.com/ClickHouse/ClickHouse/pull/46820) ([Raúl Marín](https://github.com/Algunenano)). * Increase the limit for opened files in `clickhouse-local`. It will be able to read from `web` tables on servers with a huge number of CPU cores. Do not back off reading from the URL table engine in case of too many opened files. This closes [#46852](https://github.com/ClickHouse/ClickHouse/issues/46852). [#46853](https://github.com/ClickHouse/ClickHouse/pull/46853) ([Alexey Milovidov](https://github.com/alexey-milovidov)). @@ -79,7 +79,7 @@ * Added update `system.backups` after every processed task to track the progress of backups. [#46989](https://github.com/ClickHouse/ClickHouse/pull/46989) ([Aleksandr Musorin](https://github.com/AVMusorin)). * Allow types conversion in Native input format. Add settings `input_format_native_allow_types_conversion` that controls it (enabled by default). [#46990](https://github.com/ClickHouse/ClickHouse/pull/46990) ([Kruglov Pavel](https://github.com/Avogar)). * Allow IPv4 in the `range` function to generate IP ranges. [#46995](https://github.com/ClickHouse/ClickHouse/pull/46995) ([Yakov Olkhovskiy](https://github.com/yakov-olkhovskiy)). -* Improve exception message when it's impossible to make part move from one volume/disk to another. [#47032](https://github.com/ClickHouse/ClickHouse/pull/47032) ([alesapin](https://github.com/alesapin)). +* Improve exception message when it's impossible to move a part from one volume/disk to another. [#47032](https://github.com/ClickHouse/ClickHouse/pull/47032) ([alesapin](https://github.com/alesapin)). * Support `Bool` type in `JSONType` function. Previously `Null` type was mistakenly returned for bool values. [#47046](https://github.com/ClickHouse/ClickHouse/pull/47046) ([Anton Popov](https://github.com/CurtizJ)). * Use `_request_body` parameter to configure predefined HTTP queries. [#47086](https://github.com/ClickHouse/ClickHouse/pull/47086) ([Constantine Peresypkin](https://github.com/pkit)). * Automatic indentation in the built-in UI SQL editor when Enter is pressed. [#47113](https://github.com/ClickHouse/ClickHouse/pull/47113) ([Alexey Korepanov](https://github.com/alexkorep)). @@ -87,27 +87,27 @@ * Previously, the `repeat` function's second argument only accepted an unsigned integer type, which meant it could not accept values such as -1. This behavior differed from that of the Spark function. In this update, the repeat function has been modified to match the behavior of the Spark function. It now accepts the same types of inputs, including negative integers. Extensive testing has been performed to verify the correctness of the updated implementation. [#47134](https://github.com/ClickHouse/ClickHouse/pull/47134) ([KevinyhZou](https://github.com/KevinyhZou)). Note: the changelog entry was rewritten by ChatGPT. * Remove `::__1` part from stacktraces. Display `std::basic_string Date: Thu, 30 Mar 2023 20:42:06 +0200 Subject: [PATCH 271/377] add zk retries for distributed ddl --- src/Interpreters/DDLWorker.cpp | 1 + src/Interpreters/DDLWorker.h | 1 + src/Interpreters/executeDDLQueryOnCluster.cpp | 52 ++++++++++++++++--- src/Interpreters/executeDDLQueryOnCluster.h | 3 +- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/Interpreters/DDLWorker.cpp b/src/Interpreters/DDLWorker.cpp index 22bece0ef04..5389d11a2ce 100644 --- a/src/Interpreters/DDLWorker.cpp +++ b/src/Interpreters/DDLWorker.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Interpreters/DDLWorker.h b/src/Interpreters/DDLWorker.h index 6cf034edae8..62ca6cba5e8 100644 --- a/src/Interpreters/DDLWorker.h +++ b/src/Interpreters/DDLWorker.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/src/Interpreters/executeDDLQueryOnCluster.cpp b/src/Interpreters/executeDDLQueryOnCluster.cpp index c40b9c779c9..108d9bccf33 100644 --- a/src/Interpreters/executeDDLQueryOnCluster.cpp +++ b/src/Interpreters/executeDDLQueryOnCluster.cpp @@ -37,6 +37,27 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } +struct RetriesForDDL +{ + ZooKeeperRetriesInfo info; + ZooKeeperRetriesControl ctl; +}; + +static RetriesForDDL getRetriesForDistributedDDL() +{ + const auto & config_ref = Context::getGlobalContextInstance()->getConfigRef(); + auto info = ZooKeeperRetriesInfo( + "DistributedDDL", + &Poco::Logger::get("DDLQueryStatusSource"), + config_ref.getInt("distributed_ddl_keeper_max_retries", 5), + config_ref.getInt("distributed_ddl_keeper_initial_backoff_ms", 100), + config_ref.getInt("distributed_ddl_keeper_max_backoff_ms", 5000) + ); + + auto ctl = ZooKeeperRetriesControl("executeDDLQueryOnCluster", info); + return {info, ctl}; +} + bool isSupportedAlterType(int type) { assert(type != ASTAlterCommand::NO_TYPE); @@ -174,7 +195,7 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, ContextPtr context, entry.tracing_context = OpenTelemetry::CurrentContext(); String node_path = ddl_worker.enqueueQuery(entry); - return getDistributedDDLStatus(node_path, entry, context); + return getDistributedDDLStatus(node_path, entry, context, /* hosts_to_wait */ std::nullopt); } @@ -182,7 +203,7 @@ class DDLQueryStatusSource final : public ISource { public: DDLQueryStatusSource( - const String & zk_node_path, const DDLLogEntry & entry, ContextPtr context_, const std::optional & hosts_to_wait = {}); + const String & zk_node_path, const DDLLogEntry & entry, ContextPtr context_, const std::optional & hosts_to_wait); String getName() const override { return "DDLQueryStatus"; } Chunk generate() override; @@ -230,7 +251,8 @@ private: }; -BlockIO getDistributedDDLStatus(const String & node_path, const DDLLogEntry & entry, ContextPtr context, const std::optional & hosts_to_wait) +BlockIO getDistributedDDLStatus(const String & node_path, const DDLLogEntry & entry, ContextPtr context, + const std::optional & hosts_to_wait) { BlockIO io; if (context->getSettingsRef().distributed_ddl_task_timeout == 0) @@ -380,7 +402,6 @@ Chunk DDLQueryStatusSource::generate() if (is_replicated_database && context->getSettingsRef().database_replicated_enforce_synchronous_settings) node_to_wait = "synced"; - auto zookeeper = context->getZooKeeper(); size_t try_number = 0; while (true) @@ -420,7 +441,18 @@ Chunk DDLQueryStatusSource::generate() sleepForMilliseconds(std::min(1000, 50 * (try_number + 1))); } - if (!zookeeper->exists(node_path)) + bool node_exists = false; + Strings tmp_hosts; + Strings tmp_active_hosts; + + getRetriesForDistributedDDL().ctl.retryLoop([&](){ + auto zookeeper = context->getZooKeeper(); + node_exists = zookeeper->exists(node_path); + tmp_hosts = getChildrenAllowNoNode(zookeeper, fs::path(node_path) / node_to_wait); + tmp_active_hosts = getChildrenAllowNoNode(zookeeper, fs::path(node_path) / "active"); + }); + + if (!node_exists) { /// Paradoxically, this exception will be throw even in case of "never_throw" mode. @@ -432,12 +464,12 @@ Chunk DDLQueryStatusSource::generate() return {}; } - Strings new_hosts = getNewAndUpdate(getChildrenAllowNoNode(zookeeper, fs::path(node_path) / node_to_wait)); + Strings new_hosts = getNewAndUpdate(tmp_hosts); ++try_number; if (new_hosts.empty()) continue; - current_active_hosts = getChildrenAllowNoNode(zookeeper, fs::path(node_path) / "active"); + current_active_hosts = std::move(tmp_active_hosts); MutableColumns columns = output.getHeader().cloneEmptyColumns(); for (const String & host_id : new_hosts) @@ -447,7 +479,11 @@ Chunk DDLQueryStatusSource::generate() if (node_to_wait == "finished") { String status_data; - if (zookeeper->tryGet(fs::path(node_path) / "finished" / host_id, status_data)) + bool finished_exists = false; + getRetriesForDistributedDDL().ctl.retryLoop([&](){ + finished_exists = context->getZooKeeper()->tryGet(fs::path(node_path) / "finished" / host_id, status_data); + }); + if (finished_exists) status.tryDeserializeText(status_data); } else diff --git a/src/Interpreters/executeDDLQueryOnCluster.h b/src/Interpreters/executeDDLQueryOnCluster.h index 8df199f0ede..19fb3fdb5a6 100644 --- a/src/Interpreters/executeDDLQueryOnCluster.h +++ b/src/Interpreters/executeDDLQueryOnCluster.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace zkutil @@ -43,7 +44,7 @@ struct DDLQueryOnClusterParams BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, ContextPtr context, const DDLQueryOnClusterParams & params = {}); BlockIO getDistributedDDLStatus( - const String & node_path, const DDLLogEntry & entry, ContextPtr context, const std::optional & hosts_to_wait = {}); + const String & node_path, const DDLLogEntry & entry, ContextPtr context, const std::optional & hosts_to_wait); bool maybeRemoveOnCluster(const ASTPtr & query_ptr, ContextPtr context); From 429975b3455dcebf7563eebcea3cf4a7035eb685 Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Thu, 30 Mar 2023 17:01:16 -0300 Subject: [PATCH 272/377] Update index.md --- docs/en/sql-reference/functions/index.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/en/sql-reference/functions/index.md b/docs/en/sql-reference/functions/index.md index 22e79ec6623..01da5f01679 100644 --- a/docs/en/sql-reference/functions/index.md +++ b/docs/en/sql-reference/functions/index.md @@ -91,7 +91,7 @@ The command must read arguments from `STDIN` and must output the result to `STDO **Example** Creating `test_function` using XML configuration. -File test_function.xml. +File `test_function.xml` (`/etc/clickhouse-server/test_function.xml` with default path settings). ```xml @@ -108,7 +108,7 @@ File test_function.xml. ``` -Script file inside `user_scripts` folder `test_function.py`. +Script file inside `user_scripts` folder `test_function.py` (`/var/lib/clickhouse/user_scripts/test_function.py` with default path settings). ```python #!/usr/bin/python3 @@ -136,7 +136,7 @@ Result: ``` Creating `test_function_sum` manually specifying `execute_direct` to `0` using XML configuration. -File test_function.xml. +File `test_function.xml` (`/etc/clickhouse-server/test_function.xml` with default path settings). ```xml @@ -173,7 +173,7 @@ Result: ``` Creating `test_function_sum_json` with named arguments and format [JSONEachRow](../../interfaces/formats.md#jsoneachrow) using XML configuration. -File test_function.xml. +File `test_function.xml` (`/etc/clickhouse-server/test_function.xml` with default path settings). ```xml @@ -195,7 +195,7 @@ File test_function.xml. ``` -Script file inside `user_scripts` folder `test_function_sum_json.py`. +Script file inside `user_scripts` folder `test_function_sum_json.py` (`/var/lib/clickhouse/user_scripts/test_function_sum_json.py` with default path settings). ```python #!/usr/bin/python3 @@ -228,7 +228,7 @@ Result: ``` Executable user defined functions can take constant parameters configured in `command` setting (works only for user defined functions with `executable` type). -File test_function_parameter_python.xml. +File `test_function_parameter_python.xml` (`/etc/clickhouse-server/test_function_parameter_python.xml` with default path settings). ```xml @@ -244,7 +244,7 @@ File test_function_parameter_python.xml. ``` -Script file inside `user_scripts` folder `test_function_parameter_python.py`. +Script file inside `user_scripts` folder `test_function_parameter_python.py` (`/var/lib/clickhouse/user_scripts/test_function_parameter_python.py` with default path settings). ```python #!/usr/bin/python3 From 38389d878c1b166179207079d19a64d6fd866ce5 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Thu, 30 Mar 2023 21:06:53 +0000 Subject: [PATCH 273/377] fix one more race in StorageS3 --- src/Storages/IStorageDataLake.h | 2 +- src/Storages/StorageS3.cpp | 56 +++++++++++++++++---------------- src/Storages/StorageS3.h | 6 ++-- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Storages/IStorageDataLake.h b/src/Storages/IStorageDataLake.h index 37776294491..a54544c8228 100644 --- a/src/Storages/IStorageDataLake.h +++ b/src/Storages/IStorageDataLake.h @@ -39,7 +39,7 @@ public: std::optional format_settings_) : Storage( getAdjustedConfiguration( - context_, Storage::updateConfiguration(context_, configuration_), &Poco::Logger::get("Storage" + String(name))), + context_, Storage::copyAndUpdateConfiguration(context_, configuration_), &Poco::Logger::get("Storage" + String(name))), table_id_, columns_, constraints_, diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index 344ac56df78..8ecdb01e93c 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -1016,13 +1016,15 @@ Pipe StorageS3::read( size_t max_block_size, size_t num_streams) { - bool has_wildcards = s3_configuration.url.bucket.find(PARTITION_ID_WILDCARD) != String::npos + auto query_s3_configuration = copyAndUpdateConfiguration(local_context, s3_configuration); + + bool has_wildcards = + query_s3_configuration.url.bucket.find(PARTITION_ID_WILDCARD) != String::npos || keys.back().find(PARTITION_ID_WILDCARD) != String::npos; + if (partition_by && has_wildcards) throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Reading from a partitioned S3 storage is not implemented yet"); - updateConfiguration(local_context, s3_configuration); - Pipes pipes; std::unordered_set column_names_set(column_names.begin(), column_names.end()); @@ -1035,7 +1037,7 @@ Pipe StorageS3::read( } std::shared_ptr iterator_wrapper = createFileIterator( - s3_configuration, + query_s3_configuration, keys, is_key_with_globs, distributed_processing, @@ -1078,11 +1080,11 @@ Pipe StorageS3::read( format_settings, columns_description, max_block_size, - s3_configuration.request_settings, + query_s3_configuration.request_settings, compression_method, - s3_configuration.client, - s3_configuration.url.bucket, - s3_configuration.url.version_id, + query_s3_configuration.client, + query_s3_configuration.url.bucket, + query_s3_configuration.url.version_id, iterator_wrapper, max_download_threads)); } @@ -1095,11 +1097,11 @@ Pipe StorageS3::read( SinkToStoragePtr StorageS3::write(const ASTPtr & query, const StorageMetadataPtr & metadata_snapshot, ContextPtr local_context) { - updateConfiguration(local_context, s3_configuration); + auto query_s3_configuration = copyAndUpdateConfiguration(local_context, s3_configuration); auto sample_block = metadata_snapshot->getSampleBlock(); auto chosen_compression_method = chooseCompressionMethod(keys.back(), compression_method); - bool has_wildcards = s3_configuration.url.bucket.find(PARTITION_ID_WILDCARD) != String::npos || keys.back().find(PARTITION_ID_WILDCARD) != String::npos; + bool has_wildcards = query_s3_configuration.url.bucket.find(PARTITION_ID_WILDCARD) != String::npos || keys.back().find(PARTITION_ID_WILDCARD) != String::npos; auto insert_query = std::dynamic_pointer_cast(query); auto partition_by_ast = insert_query ? (insert_query->partition_by ? insert_query->partition_by : partition_by) : nullptr; @@ -1114,19 +1116,19 @@ SinkToStoragePtr StorageS3::write(const ASTPtr & query, const StorageMetadataPtr local_context, format_settings, chosen_compression_method, - s3_configuration, - s3_configuration.url.bucket, + query_s3_configuration, + query_s3_configuration.url.bucket, keys.back()); } else { if (is_key_with_globs) throw Exception(ErrorCodes::DATABASE_ACCESS_DENIED, - "S3 key '{}' contains globs, so the table is in readonly mode", s3_configuration.url.key); + "S3 key '{}' contains globs, so the table is in readonly mode", query_s3_configuration.url.key); bool truncate_in_insert = local_context->getSettingsRef().s3_truncate_on_insert; - if (!truncate_in_insert && S3::objectExists(*s3_configuration.client, s3_configuration.url.bucket, keys.back(), s3_configuration.url.version_id, s3_configuration.request_settings)) + if (!truncate_in_insert && S3::objectExists(*query_s3_configuration.client, query_s3_configuration.url.bucket, keys.back(), query_s3_configuration.url.version_id, query_s3_configuration.request_settings)) { if (local_context->getSettingsRef().s3_create_new_file_on_insert) { @@ -1138,7 +1140,7 @@ SinkToStoragePtr StorageS3::write(const ASTPtr & query, const StorageMetadataPtr new_key = keys[0].substr(0, pos) + "." + std::to_string(index) + (pos == std::string::npos ? "" : keys[0].substr(pos)); ++index; } - while (S3::objectExists(*s3_configuration.client, s3_configuration.url.bucket, new_key, s3_configuration.url.version_id, s3_configuration.request_settings)); + while (S3::objectExists(*query_s3_configuration.client, query_s3_configuration.url.bucket, new_key, query_s3_configuration.url.version_id, query_s3_configuration.request_settings)); keys.push_back(new_key); } else @@ -1147,7 +1149,7 @@ SinkToStoragePtr StorageS3::write(const ASTPtr & query, const StorageMetadataPtr "Object in bucket {} with key {} already exists. " "If you want to overwrite it, enable setting s3_truncate_on_insert, if you " "want to create a new file on each insert, enable setting s3_create_new_file_on_insert", - s3_configuration.url.bucket, + query_s3_configuration.url.bucket, keys.back()); } @@ -1157,19 +1159,19 @@ SinkToStoragePtr StorageS3::write(const ASTPtr & query, const StorageMetadataPtr local_context, format_settings, chosen_compression_method, - s3_configuration, - s3_configuration.url.bucket, + query_s3_configuration, + query_s3_configuration.url.bucket, keys.back()); } } void StorageS3::truncate(const ASTPtr & /* query */, const StorageMetadataPtr &, ContextPtr local_context, TableExclusiveLockHolder &) { - updateConfiguration(local_context, s3_configuration); + auto query_s3_configuration = copyAndUpdateConfiguration(local_context, s3_configuration); if (is_key_with_globs) throw Exception(ErrorCodes::DATABASE_ACCESS_DENIED, - "S3 key '{}' contains globs, so the table is in readonly mode", s3_configuration.url.key); + "S3 key '{}' contains globs, so the table is in readonly mode", query_s3_configuration.url.key); Aws::S3::Model::Delete delkeys; @@ -1182,10 +1184,10 @@ void StorageS3::truncate(const ASTPtr & /* query */, const StorageMetadataPtr &, ProfileEvents::increment(ProfileEvents::S3DeleteObjects); S3::DeleteObjectsRequest request; - request.SetBucket(s3_configuration.url.bucket); + request.SetBucket(query_s3_configuration.url.bucket); request.SetDelete(delkeys); - auto response = s3_configuration.client->DeleteObjects(request); + auto response = query_s3_configuration.client->DeleteObjects(request); if (!response.IsSuccess()) { const auto & err = response.GetError(); @@ -1197,7 +1199,7 @@ void StorageS3::truncate(const ASTPtr & /* query */, const StorageMetadataPtr &, } -StorageS3::Configuration StorageS3::updateConfiguration(ContextPtr local_context, const StorageS3::Configuration & configuration) +StorageS3::Configuration StorageS3::copyAndUpdateConfiguration(ContextPtr local_context, const StorageS3::Configuration & configuration) { StorageS3::Configuration new_configuration(configuration); updateConfiguration(local_context, new_configuration); @@ -1355,14 +1357,14 @@ StorageS3::Configuration StorageS3::getConfiguration(ASTs & engine_args, Context } ColumnsDescription StorageS3::getTableStructureFromData( - StorageS3::Configuration & configuration, + const StorageS3::Configuration & configuration, const std::optional & format_settings, ContextPtr ctx) { - updateConfiguration(ctx, configuration); + auto query_s3_configuration = copyAndUpdateConfiguration(ctx, configuration); return getTableStructureFromDataImpl( - configuration.format, configuration, configuration.compression_method, - configuration.url.key.find_first_of("*?{") != std::string::npos, format_settings, ctx); + query_s3_configuration.format, query_s3_configuration, query_s3_configuration.compression_method, + query_s3_configuration.url.key.find_first_of("*?{") != std::string::npos, format_settings, ctx); } ColumnsDescription StorageS3::getTableStructureFromDataImpl( diff --git a/src/Storages/StorageS3.h b/src/Storages/StorageS3.h index a34113b8bae..0bccc4a419f 100644 --- a/src/Storages/StorageS3.h +++ b/src/Storages/StorageS3.h @@ -293,13 +293,13 @@ public: static StorageS3::Configuration getConfiguration(ASTs & engine_args, ContextPtr local_context, bool get_format_from_file = true); static ColumnsDescription getTableStructureFromData( - StorageS3::Configuration & configuration, + const StorageS3::Configuration & configuration, const std::optional & format_settings, ContextPtr ctx); protected: - static StorageS3::Configuration updateConfiguration(ContextPtr local_context, const Configuration & configuration); - static void updateConfiguration(ContextPtr, Configuration &); + static StorageS3::Configuration copyAndUpdateConfiguration(ContextPtr local_context, const Configuration & configuration); + static void updateConfiguration(ContextPtr local_context, Configuration & configuration); private: friend class StorageS3Cluster; From b7f411443d9c79be817202d186c35baa17f3f151 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 00:36:34 +0200 Subject: [PATCH 274/377] fix test numbers again --- ...rence => 02705_projection_and_ast_optimizations_bug.reference} | 0 ...ons_bug.sql => 02705_projection_and_ast_optimizations_bug.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/queries/0_stateless/{25402_projection_and_ast_optimizations_bug.reference => 02705_projection_and_ast_optimizations_bug.reference} (100%) rename tests/queries/0_stateless/{25402_projection_and_ast_optimizations_bug.sql => 02705_projection_and_ast_optimizations_bug.sql} (100%) diff --git a/tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.reference b/tests/queries/0_stateless/02705_projection_and_ast_optimizations_bug.reference similarity index 100% rename from tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.reference rename to tests/queries/0_stateless/02705_projection_and_ast_optimizations_bug.reference diff --git a/tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.sql b/tests/queries/0_stateless/02705_projection_and_ast_optimizations_bug.sql similarity index 100% rename from tests/queries/0_stateless/25402_projection_and_ast_optimizations_bug.sql rename to tests/queries/0_stateless/02705_projection_and_ast_optimizations_bug.sql From 98833bcc82218cf0ec60582bfa65f4392345339d Mon Sep 17 00:00:00 2001 From: rfraposa Date: Thu, 30 Mar 2023 16:41:29 -0600 Subject: [PATCH 275/377] Update youtube-dislikes.md --- .../example-datasets/youtube-dislikes.md | 139 +----------------- 1 file changed, 6 insertions(+), 133 deletions(-) diff --git a/docs/en/getting-started/example-datasets/youtube-dislikes.md b/docs/en/getting-started/example-datasets/youtube-dislikes.md index 8b8217ed5be..e3b162a8dbf 100644 --- a/docs/en/getting-started/example-datasets/youtube-dislikes.md +++ b/docs/en/getting-started/example-datasets/youtube-dislikes.md @@ -244,11 +244,10 @@ FROM ) WHERE view_range > 1 ORDER BY is_comments_enabled ASC, - num_views ASC + num_views ASC; ``` ```response - ┌─views─────────────┬─is_comments_enabled─┬────prob_like_dislike─┐ │ < 10.00 │ false │ 0.08224180712685371 │ │ < 100.00 │ false │ 0.06346337759167248 │ @@ -273,134 +272,10 @@ ORDER BY └───────────────────┴─────────────────────┴──────────────────────┘ 22 rows in set. Elapsed: 8.460 sec. Processed 4.56 billion rows, 77.48 GB (538.73 million rows/s., 9.16 GB/s.) - ``` Enabling comments seems to be correlated with a higher rate of engagement. - -### How does the number of videos change over time - notable events? - -```sql -SELECT - toStartOfMonth(toDateTime(upload_date)) AS month, - uniq(uploader_id) AS uploaders, - count() as num_videos, - sum(view_count) as view_count -FROM youtube -WHERE (month >= '2005-01-01') AND (month < '2021-12-01') -GROUP BY month -ORDER BY month ASC -``` - -```response -┌──────month─┬─uploaders─┬─num_videos─┬───view_count─┐ -│ 2005-04-01 │ 5 │ 6 │ 213597737 │ -│ 2005-05-01 │ 6 │ 9 │ 2944005 │ -│ 2005-06-01 │ 165 │ 351 │ 18624981 │ -│ 2005-07-01 │ 395 │ 1168 │ 94164872 │ -│ 2005-08-01 │ 1171 │ 3128 │ 124540774 │ -│ 2005-09-01 │ 2418 │ 5206 │ 475536249 │ -│ 2005-10-01 │ 6750 │ 13747 │ 737593613 │ -│ 2005-11-01 │ 13706 │ 28078 │ 1896116976 │ -│ 2005-12-01 │ 24756 │ 49885 │ 2478418930 │ -│ 2006-01-01 │ 49992 │ 100447 │ 4532656581 │ -│ 2006-02-01 │ 67882 │ 138485 │ 5677516317 │ -│ 2006-03-01 │ 103358 │ 212237 │ 8430301366 │ -│ 2006-04-01 │ 114615 │ 234174 │ 9980760440 │ -│ 2006-05-01 │ 152682 │ 332076 │ 14129117212 │ -│ 2006-06-01 │ 193962 │ 429538 │ 17014143263 │ -│ 2006-07-01 │ 234401 │ 530311 │ 18721143410 │ -│ 2006-08-01 │ 281280 │ 614128 │ 20473502342 │ -│ 2006-09-01 │ 312434 │ 679906 │ 23158422265 │ -│ 2006-10-01 │ 404873 │ 897590 │ 27357846117 │ -``` - -A spike of uploaders [around covid is noticeable](https://www.theverge.com/2020/3/27/21197642/youtube-with-me-style-videos-views-coronavirus-cook-workout-study-home-beauty). - - -### More subtitiles over time and when - -With advances in speech recognition, it’s easier than ever to create subtitles for video with youtube adding auto-captioning in late 2009 - was the jump then? - -```sql -SELECT - toStartOfMonth(upload_date) AS month, - countIf(has_subtitles) / count() AS percent_subtitles, - percent_subtitles - any(percent_subtitles) OVER (ORDER BY month ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS previous -FROM youtube -WHERE (month >= '2015-01-01') AND (month < '2021-12-02') -GROUP BY month -ORDER BY month ASC -``` - -```response -┌──────month─┬───percent_subtitles─┬────────────────previous─┐ -│ 2015-01-01 │ 0.2652653881082824 │ 0.2652653881082824 │ -│ 2015-02-01 │ 0.3147556050309162 │ 0.049490216922633834 │ -│ 2015-03-01 │ 0.32460464492371877 │ 0.009849039892802558 │ -│ 2015-04-01 │ 0.33471963051468445 │ 0.010114985590965686 │ -│ 2015-05-01 │ 0.3168087575501062 │ -0.017910872964578273 │ -│ 2015-06-01 │ 0.3162609788438222 │ -0.0005477787062839745 │ -│ 2015-07-01 │ 0.31828767677518033 │ 0.0020266979313581235 │ -│ 2015-08-01 │ 0.3045551564286859 │ -0.013732520346494415 │ -│ 2015-09-01 │ 0.311221133995152 │ 0.006665977566466086 │ -│ 2015-10-01 │ 0.30574870926812175 │ -0.005472424727030245 │ -│ 2015-11-01 │ 0.31125409712077234 │ 0.0055053878526505895 │ -│ 2015-12-01 │ 0.3190967954651779 │ 0.007842698344405541 │ -│ 2016-01-01 │ 0.32636021432496176 │ 0.007263418859783877 │ - -``` - -The data results show a spike in 2009. Apparently at that, time YouTube was removing their community captions feature, which allowed you to upload captions for other people's video. -This prompted a very successful campaign to have creators add captions to their videos for hard of hearing and deaf viewers. - - -### Top uploaders over time - -```sql -WITH uploaders AS - ( - SELECT uploader - FROM youtube - GROUP BY uploader - ORDER BY sum(view_count) DESC - LIMIT 10 - ) -SELECT - month, - uploader, - sum(view_count) AS total_views, - avg(dislike_count / like_count) AS like_to_dislike_ratio -FROM youtube -WHERE uploader IN (uploaders) -GROUP BY - toStartOfMonth(upload_date) AS month, - uploader -ORDER BY - month ASC, - total_views DESC - -1001 rows in set. Elapsed: 34.917 sec. Processed 4.58 billion rows, 69.08 GB (131.15 million rows/s., 1.98 GB/s.) -``` - -```response -┌──────month─┬─uploader───────────────────┬─total_views─┬─like_to_dislike_ratio─┐ -│ 1970-01-01 │ T-Series │ 10957099 │ 0.022784656361208206 │ -│ 1970-01-01 │ Ryan's World │ 0 │ 0.003035559410234172 │ -│ 1970-01-01 │ SET India │ 0 │ nan │ -│ 2006-09-01 │ Cocomelon - Nursery Rhymes │ 256406497 │ 0.7005566715978622 │ -│ 2007-06-01 │ Cocomelon - Nursery Rhymes │ 33641320 │ 0.7088650914344298 │ -│ 2008-02-01 │ WWE │ 43733469 │ 0.07198856488734842 │ -│ 2008-03-01 │ WWE │ 16514541 │ 0.1230603715431997 │ -│ 2008-04-01 │ WWE │ 5907295 │ 0.2089399470159618 │ -│ 2008-05-01 │ WWE │ 7779627 │ 0.09101676560436774 │ -│ 2008-06-01 │ WWE │ 7018780 │ 0.0974184753155297 │ -│ 2008-07-01 │ WWE │ 4686447 │ 0.1263845422065158 │ -│ 2008-08-01 │ WWE │ 4514312 │ 0.08384574274791441 │ -│ 2008-09-01 │ WWE │ 3717092 │ 0.07872802579349912 │ -``` - ### How do like ratio changes as views go up? ```sql @@ -421,13 +296,10 @@ GROUP BY ORDER BY view_range ASC, is_comments_enabled ASC -) - -20 rows in set. Elapsed: 9.043 sec. Processed 4.56 billion rows, 77.48 GB (503.99 million rows/s., 8.57 GB/s.) +); ``` ```response - ┌─view_range────────┬─is_comments_enabled─┬─like_ratio─┐ │ < 10.00 │ false │ 0.66 │ │ < 10.00 │ true │ 0.66 │ @@ -451,6 +323,7 @@ ORDER BY │ < 10.00 billion │ true │ 19.5 │ └───────────────────┴─────────────────────┴────────────┘ +20 rows in set. Elapsed: 63.664 sec. Processed 4.56 billion rows, 113.93 GB (71.59 million rows/s., 1.79 GB/s.) ``` ### How are views distributed? @@ -468,9 +341,7 @@ FROM ) ARRAY JOIN quantiles, - labels - -12 rows in set. Elapsed: 1.864 sec. Processed 4.56 billion rows, 36.46 GB (2.45 billion rows/s., 19.56 GB/s.) + labels; ``` ```response @@ -488,4 +359,6 @@ ARRAY JOIN │ 20th │ 16 │ │ 10th │ 6 │ └────────────┴─────────┘ + +12 rows in set. Elapsed: 1.864 sec. Processed 4.56 billion rows, 36.46 GB (2.45 billion rows/s., 19.56 GB/s.) ``` \ No newline at end of file From 81375431d99ea81de4f4e58931e75d97e590fada Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 00:42:16 +0200 Subject: [PATCH 276/377] fix some tests --- tests/integration/helpers/client.py | 13 ++++++- tests/integration/test_drop_replica/test.py | 35 ++++++++++++------- .../test_system_replicated_fetches/test.py | 11 +++--- ...tem_parts_race_condition_drop_zookeeper.sh | 2 +- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/tests/integration/helpers/client.py b/tests/integration/helpers/client.py index ab1cc65e9a9..66e0f0bc907 100644 --- a/tests/integration/helpers/client.py +++ b/tests/integration/helpers/client.py @@ -202,6 +202,15 @@ class CommandRequest: self.timer = Timer(timeout, kill_process) self.timer.start() + def remove_trash_from_stderr(self, stderr): + if not stderr: + return stderr + lines = stderr.split("\n") + lines = [ + x for x in lines if ("completion_queue" not in x and "Kick failed" not in x) + ] + return "\n".join(lines) + def get_answer(self): self.process.wait(timeout=DEFAULT_QUERY_TIMEOUT) self.stdout_file.seek(0) @@ -218,7 +227,9 @@ class CommandRequest: logging.debug(f"Timed out. Last stdout:{stdout}, stderr:{stderr}") raise QueryTimeoutExceedException("Client timed out!") - if (self.process.returncode != 0 or stderr) and not self.ignore_error: + if ( + self.process.returncode != 0 or self.remove_trash_from_stderr(stderr) + ) and not self.ignore_error: raise QueryRuntimeException( "Client failed! Return code: {}, stderr: {}".format( self.process.returncode, stderr diff --git a/tests/integration/test_drop_replica/test.py b/tests/integration/test_drop_replica/test.py index f16a5b729f6..e87edb0a578 100644 --- a/tests/integration/test_drop_replica/test.py +++ b/tests/integration/test_drop_replica/test.py @@ -90,6 +90,11 @@ def start_cluster(): cluster.shutdown() +def check_exists(zk, path): + zk.sync(path) + return zk.exists(path) + + def test_drop_replica(start_cluster): node_1_1.query( "INSERT INTO test.test_table SELECT number, toString(number) FROM numbers(100)" @@ -158,10 +163,11 @@ def test_drop_replica(start_cluster): ) node_1_3.query("SYSTEM DROP REPLICA 'node_1_1'") - exists_replica_1_1 = zk.exists( + exists_replica_1_1 = check_exists( + zk, "/clickhouse/tables/test3/{shard}/replicated/test_table/replicas/{replica}".format( shard=1, replica="node_1_1" - ) + ), ) assert exists_replica_1_1 != None @@ -171,26 +177,29 @@ def test_drop_replica(start_cluster): shard=1 ) ) - exists_replica_1_1 = zk.exists( + exists_replica_1_1 = check_exists( + zk, "/clickhouse/tables/test2/{shard}/replicated/test_table/replicas/{replica}".format( shard=1, replica="node_1_1" - ) + ), ) assert exists_replica_1_1 == None node_1_2.query("SYSTEM DROP REPLICA 'node_1_1' FROM TABLE test.test_table") - exists_replica_1_1 = zk.exists( + exists_replica_1_1 = check_exists( + zk, "/clickhouse/tables/test/{shard}/replicated/test_table/replicas/{replica}".format( shard=1, replica="node_1_1" - ) + ), ) assert exists_replica_1_1 == None node_1_2.query("SYSTEM DROP REPLICA 'node_1_1' FROM DATABASE test1") - exists_replica_1_1 = zk.exists( + exists_replica_1_1 = check_exists( + zk, "/clickhouse/tables/test1/{shard}/replicated/test_table/replicas/{replica}".format( shard=1, replica="node_1_1" - ) + ), ) assert exists_replica_1_1 == None @@ -199,17 +208,19 @@ def test_drop_replica(start_cluster): shard=1 ) ) - exists_replica_1_1 = zk.exists( + exists_replica_1_1 = check_exists( + zk, "/clickhouse/tables/test3/{shard}/replicated/test_table/replicas/{replica}".format( shard=1, replica="node_1_1" - ) + ), ) assert exists_replica_1_1 == None node_1_2.query("SYSTEM DROP REPLICA 'node_1_1'") - exists_replica_1_1 = zk.exists( + exists_replica_1_1 = check_exists( + zk, "/clickhouse/tables/test4/{shard}/replicated/test_table/replicas/{replica}".format( shard=1, replica="node_1_1" - ) + ), ) assert exists_replica_1_1 == None diff --git a/tests/integration/test_system_replicated_fetches/test.py b/tests/integration/test_system_replicated_fetches/test.py index 2b516ebf69b..bcf9b38d3d4 100644 --- a/tests/integration/test_system_replicated_fetches/test.py +++ b/tests/integration/test_system_replicated_fetches/test.py @@ -90,11 +90,12 @@ def test_system_replicated_fetches(started_cluster): ) for elem in fetches_result: - assert ( - elem["bytes_read_compressed"] <= elem["total_size_bytes_compressed"] - ), "Bytes read ({}) more than total bytes ({}). It's a bug".format( - elem["bytes_read_compressed"], elem["total_size_bytes_compressed"] - ) + # FIXME https://github.com/ClickHouse/ClickHouse/issues/45435 + # assert ( + # elem["bytes_read_compressed"] <= elem["total_size_bytes_compressed"] + # ), "Bytes read ({}) more than total bytes ({}). It's a bug".format( + # elem["bytes_read_compressed"], elem["total_size_bytes_compressed"] + # ) assert ( 0.0 <= elem["progress"] <= 1.0 ), "Progress shouldn't less than 0 and bigger than 1, got {}".format( diff --git a/tests/queries/0_stateless/00993_system_parts_race_condition_drop_zookeeper.sh b/tests/queries/0_stateless/00993_system_parts_race_condition_drop_zookeeper.sh index f143c97bdf4..ff6a4b8fc42 100755 --- a/tests/queries/0_stateless/00993_system_parts_race_condition_drop_zookeeper.sh +++ b/tests/queries/0_stateless/00993_system_parts_race_condition_drop_zookeeper.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Tags: race, zookeeper, no-parallel, no-upgrade-check +# Tags: race, zookeeper, no-parallel, no-upgrade-check, no-replicated-database CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh From 2850d2822d5931f62ea93d241a2684c4d51586a2 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 01:38:23 +0200 Subject: [PATCH 277/377] more consistent workflows --- .github/workflows/master.yml | 763 +++++++++++++++++++++++++++++++++-- 1 file changed, 740 insertions(+), 23 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 7c5e477ab60..fc2c4def2ea 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -1131,7 +1131,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_database_replicated/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=2 + RUN_BY_HASH_TOTAL=4 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1167,6 +1167,114 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_database_replicated/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=1 + RUN_BY_HASH_TOTAL=4 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestReleaseDatabaseReplicated2: + needs: [BuilderDebRelease] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_database_replicated + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (release, DatabaseReplicated) + REPO_COPY=${{runner.temp}}/stateless_database_replicated/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=2 + RUN_BY_HASH_TOTAL=4 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestReleaseDatabaseReplicated3: + needs: [BuilderDebRelease] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_database_replicated + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (release, DatabaseReplicated) + REPO_COPY=${{runner.temp}}/stateless_database_replicated/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=3 + RUN_BY_HASH_TOTAL=4 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestReleaseS3_0: + needs: [BuilderDebRelease] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_s3_storage + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (release, s3 storage) + REPO_COPY=${{runner.temp}}/stateless_s3_storage/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=0 RUN_BY_HASH_TOTAL=2 EOF - name: Download json reports @@ -1190,7 +1298,7 @@ jobs: docker ps --quiet | xargs --no-run-if-empty docker kill ||: docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestReleaseS3: + FunctionalStatelessTestReleaseS3_1: needs: [BuilderDebRelease] runs-on: [self-hosted, func-tester] steps: @@ -1202,6 +1310,8 @@ jobs: CHECK_NAME=Stateless tests (release, s3 storage) REPO_COPY=${{runner.temp}}/stateless_s3_storage/ClickHouse KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=1 + RUN_BY_HASH_TOTAL=2 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1271,7 +1381,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=2 + RUN_BY_HASH_TOTAL=4 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1307,7 +1417,79 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=2 + RUN_BY_HASH_TOTAL=4 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestAsan2: + needs: [BuilderDebAsan] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_debug + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (asan) + REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=2 + RUN_BY_HASH_TOTAL=4 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestAsan3: + needs: [BuilderDebAsan] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_debug + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (asan) + REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=3 + RUN_BY_HASH_TOTAL=4 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1343,7 +1525,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=5 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1379,7 +1561,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=5 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1415,7 +1597,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=5 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1438,7 +1620,79 @@ jobs: docker ps --quiet | xargs --no-run-if-empty docker kill ||: docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestUBsan: + FunctionalStatelessTestTsan3: + needs: [BuilderDebTsan] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_tsan + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (tsan) + REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=3 + RUN_BY_HASH_TOTAL=5 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestTsan4: + needs: [BuilderDebTsan] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_tsan + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (tsan) + REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=4 + RUN_BY_HASH_TOTAL=5 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestUBsan0: needs: [BuilderDebUBsan] runs-on: [self-hosted, func-tester] steps: @@ -1450,6 +1704,44 @@ jobs: CHECK_NAME=Stateless tests (ubsan) REPO_COPY=${{runner.temp}}/stateless_ubsan/ClickHouse KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=0 + RUN_BY_HASH_TOTAL=2 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestUBsan1: + needs: [BuilderDebUBsan] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_ubsan + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (ubsan) + REPO_COPY=${{runner.temp}}/stateless_ubsan/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=1 + RUN_BY_HASH_TOTAL=2 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1485,7 +1777,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1521,7 +1813,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1557,7 +1849,115 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=6 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestMsan3: + needs: [BuilderDebMsan] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_memory + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (msan) + REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=3 + RUN_BY_HASH_TOTAL=6 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestMsan4: + needs: [BuilderDebMsan] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_memory + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (msan) + REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=4 + RUN_BY_HASH_TOTAL=6 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestMsan5: + needs: [BuilderDebMsan] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_memory + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (msan) + REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=5 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1593,7 +1993,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=5 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1629,7 +2029,7 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=5 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -1665,7 +2065,79 @@ jobs: REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse KILL_TIMEOUT=10800 RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=5 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestDebug3: + needs: [BuilderDebDebug] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_debug + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (debug) + REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=3 + RUN_BY_HASH_TOTAL=5 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Functional test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + FunctionalStatelessTestDebug4: + needs: [BuilderDebDebug] + runs-on: [self-hosted, func-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/stateless_debug + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (debug) + REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse + KILL_TIMEOUT=10800 + RUN_BY_HASH_NUM=4 + RUN_BY_HASH_TOTAL=5 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2116,7 +2588,7 @@ jobs: CHECK_NAME=Integration tests (asan) REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2151,7 +2623,7 @@ jobs: CHECK_NAME=Integration tests (asan) REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2186,7 +2658,112 @@ jobs: CHECK_NAME=Integration tests (asan) REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 + RUN_BY_HASH_TOTAL=6 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Integration test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 integration_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + IntegrationTestsAsan3: + needs: [BuilderDebAsan] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/integration_tests_asan + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Integration tests (asan) + REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse + RUN_BY_HASH_NUM=3 + RUN_BY_HASH_TOTAL=6 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Integration test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 integration_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + IntegrationTestsAsan4: + needs: [BuilderDebAsan] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/integration_tests_asan + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Integration tests (asan) + REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse + RUN_BY_HASH_NUM=4 + RUN_BY_HASH_TOTAL=6 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Integration test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 integration_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + IntegrationTestsAsan5: + needs: [BuilderDebAsan] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/integration_tests_asan + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Integration tests (asan) + REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse + RUN_BY_HASH_NUM=5 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2221,7 +2798,7 @@ jobs: CHECK_NAME=Integration tests (tsan) REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=4 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2256,7 +2833,7 @@ jobs: CHECK_NAME=Integration tests (tsan) REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=4 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2291,7 +2868,7 @@ jobs: CHECK_NAME=Integration tests (tsan) REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=4 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2326,7 +2903,77 @@ jobs: CHECK_NAME=Integration tests (tsan) REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse RUN_BY_HASH_NUM=3 - RUN_BY_HASH_TOTAL=4 + RUN_BY_HASH_TOTAL=6 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Integration test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 integration_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + IntegrationTestsTsan4: + needs: [BuilderDebTsan] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/integration_tests_tsan + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Integration tests (tsan) + REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse + RUN_BY_HASH_NUM=4 + RUN_BY_HASH_TOTAL=6 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Integration test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 integration_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + IntegrationTestsTsan5: + needs: [BuilderDebTsan] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/integration_tests_tsan + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Integration tests (tsan) + REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse + RUN_BY_HASH_NUM=5 + RUN_BY_HASH_TOTAL=6 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2361,7 +3008,7 @@ jobs: CHECK_NAME=Integration tests (release) REPO_COPY=${{runner.temp}}/integration_tests_release/ClickHouse RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=2 + RUN_BY_HASH_TOTAL=4 EOF - name: Download json reports uses: actions/download-artifact@v3 @@ -2396,7 +3043,77 @@ jobs: CHECK_NAME=Integration tests (release) REPO_COPY=${{runner.temp}}/integration_tests_release/ClickHouse RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=2 + RUN_BY_HASH_TOTAL=4 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Integration test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 integration_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + IntegrationTestsRelease2: + needs: [BuilderDebRelease] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/integration_tests_release + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Integration tests (release) + REPO_COPY=${{runner.temp}}/integration_tests_release/ClickHouse + RUN_BY_HASH_NUM=2 + RUN_BY_HASH_TOTAL=4 + EOF + - name: Download json reports + uses: actions/download-artifact@v3 + with: + path: ${{ env.REPORTS_PATH }} + - name: Check out repository code + uses: ClickHouse/checkout@v1 + with: + clear-repository: true + - name: Integration test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 integration_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + docker ps --quiet | xargs --no-run-if-empty docker kill ||: + docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||: + sudo rm -fr "$TEMP_PATH" + IntegrationTestsRelease3: + needs: [BuilderDebRelease] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/integration_tests_release + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Integration tests (release) + REPO_COPY=${{runner.temp}}/integration_tests_release/ClickHouse + RUN_BY_HASH_NUM=3 + RUN_BY_HASH_TOTAL=4 EOF - name: Download json reports uses: actions/download-artifact@v3 From 64b1f74a6074a1a0b458a5cd011b6de98a5688d5 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 01:39:49 +0200 Subject: [PATCH 278/377] fix --- src/Interpreters/executeDDLQueryOnCluster.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Interpreters/executeDDLQueryOnCluster.cpp b/src/Interpreters/executeDDLQueryOnCluster.cpp index 108d9bccf33..a93cc2f06ba 100644 --- a/src/Interpreters/executeDDLQueryOnCluster.cpp +++ b/src/Interpreters/executeDDLQueryOnCluster.cpp @@ -445,7 +445,8 @@ Chunk DDLQueryStatusSource::generate() Strings tmp_hosts; Strings tmp_active_hosts; - getRetriesForDistributedDDL().ctl.retryLoop([&](){ + getRetriesForDistributedDDL().ctl.retryLoop([&]() + { auto zookeeper = context->getZooKeeper(); node_exists = zookeeper->exists(node_path); tmp_hosts = getChildrenAllowNoNode(zookeeper, fs::path(node_path) / node_to_wait); @@ -480,7 +481,8 @@ Chunk DDLQueryStatusSource::generate() { String status_data; bool finished_exists = false; - getRetriesForDistributedDDL().ctl.retryLoop([&](){ + getRetriesForDistributedDDL().ctl.retryLoop([&]() + { finished_exists = context->getZooKeeper()->tryGet(fs::path(node_path) / "finished" / host_id, status_data); }); if (finished_exists) From 356716f0a1d510d7331e9cb7cbba3eed763050ef Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 03:26:16 +0300 Subject: [PATCH 279/377] Update master.yml --- .github/workflows/master.yml | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index fc2c4def2ea..a0b2e9d60fa 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -3833,23 +3833,36 @@ jobs: - FunctionalStatelessTestDebug0 - FunctionalStatelessTestDebug1 - FunctionalStatelessTestDebug2 + - FunctionalStatelessTestDebug3 + - FunctionalStatelessTestDebug4 - FunctionalStatelessTestRelease - FunctionalStatelessTestReleaseDatabaseOrdinary - FunctionalStatelessTestReleaseDatabaseReplicated0 - FunctionalStatelessTestReleaseDatabaseReplicated1 + - FunctionalStatelessTestReleaseDatabaseReplicated2 + - FunctionalStatelessTestReleaseDatabaseReplicated3 - FunctionalStatelessTestAarch64 - FunctionalStatelessTestAsan0 - FunctionalStatelessTestAsan1 + - FunctionalStatelessTestAsan2 + - FunctionalStatelessTestAsan3 - FunctionalStatelessTestTsan0 - FunctionalStatelessTestTsan1 - FunctionalStatelessTestTsan2 + - FunctionalStatelessTestTsan3 + - FunctionalStatelessTestTsan4 - FunctionalStatelessTestMsan0 - FunctionalStatelessTestMsan1 - FunctionalStatelessTestMsan2 - - FunctionalStatelessTestUBsan + - FunctionalStatelessTestMsan3 + - FunctionalStatelessTestMsan4 + - FunctionalStatelessTestMsan5 + - FunctionalStatelessTestUBsan0 + - FunctionalStatelessTestUBsan1 - FunctionalStatefulTestDebug - FunctionalStatefulTestRelease - - FunctionalStatelessTestReleaseS3 + - FunctionalStatelessTestReleaseS3_0 + - FunctionalStatelessTestReleaseS3_1 - FunctionalStatefulTestAarch64 - FunctionalStatefulTestAsan - FunctionalStatefulTestTsan @@ -3863,12 +3876,19 @@ jobs: - IntegrationTestsAsan0 - IntegrationTestsAsan1 - IntegrationTestsAsan2 + - IntegrationTestsAsan3 + - IntegrationTestsAsan4 + - IntegrationTestsAsan5 - IntegrationTestsRelease0 - IntegrationTestsRelease1 + - IntegrationTestsRelease2 + - IntegrationTestsRelease3 - IntegrationTestsTsan0 - IntegrationTestsTsan1 - IntegrationTestsTsan2 - IntegrationTestsTsan3 + - IntegrationTestsTsan4 + - IntegrationTestsTsan5 - PerformanceComparisonX86-0 - PerformanceComparisonX86-1 - PerformanceComparisonX86-2 From e16b43463114d076702c3cedd432c6066b8aeaa3 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 31 Mar 2023 06:03:05 +0200 Subject: [PATCH 280/377] Fix compiling examples without Hive Skip compiling comma_separated_streams since it requires Hive. Signed-off-by: Azat Khuzhin --- src/Processors/examples/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Processors/examples/CMakeLists.txt b/src/Processors/examples/CMakeLists.txt index 72c3a16d32f..5d43a0d7d08 100644 --- a/src/Processors/examples/CMakeLists.txt +++ b/src/Processors/examples/CMakeLists.txt @@ -1,2 +1,4 @@ -clickhouse_add_executable (comma_separated_streams comma_separated_streams.cpp) -target_link_libraries (comma_separated_streams PRIVATE dbms) +if (TARGET ch_contrib::hivemetastore) + clickhouse_add_executable (comma_separated_streams comma_separated_streams.cpp) + target_link_libraries (comma_separated_streams PRIVATE dbms) +endif() From 0f4c8144a65facf5e79502eeb9a2f2efe64fcb8e Mon Sep 17 00:00:00 2001 From: Vadym Chekan Date: Thu, 30 Mar 2023 22:46:18 -0700 Subject: [PATCH 281/377] In messages, put values into quotes Configuration values, such as disk names, backup engine names, etc, may give error message unintended sense, for example, if trying to backup to `disk` instead of `Disk`, the error message will be "Not found backup engine disk", which can be interpreted as "disk of backup engine not found". It might be not clear that the word "disk" comes from the query and is not part of the error message. --- src/Backups/BackupFactory.cpp | 4 ++-- src/Backups/registerBackupEnginesFileAndDisk.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Backups/BackupFactory.cpp b/src/Backups/BackupFactory.cpp index 7c870737b1d..898ac7bc490 100644 --- a/src/Backups/BackupFactory.cpp +++ b/src/Backups/BackupFactory.cpp @@ -20,14 +20,14 @@ BackupMutablePtr BackupFactory::createBackup(const CreateParams & params) const const String & engine_name = params.backup_info.backup_engine_name; auto it = creators.find(engine_name); if (it == creators.end()) - throw Exception(ErrorCodes::BACKUP_ENGINE_NOT_FOUND, "Not found backup engine {}", engine_name); + throw Exception(ErrorCodes::BACKUP_ENGINE_NOT_FOUND, "Not found backup engine '{}'", engine_name); return (it->second)(params); } void BackupFactory::registerBackupEngine(const String & engine_name, const CreatorFn & creator_fn) { if (creators.contains(engine_name)) - throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup engine {} was registered twice", engine_name); + throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup engine '{}' was registered twice", engine_name); creators[engine_name] = creator_fn; } diff --git a/src/Backups/registerBackupEnginesFileAndDisk.cpp b/src/Backups/registerBackupEnginesFileAndDisk.cpp index 020da13d6e1..51b14fbc1d8 100644 --- a/src/Backups/registerBackupEnginesFileAndDisk.cpp +++ b/src/Backups/registerBackupEnginesFileAndDisk.cpp @@ -41,7 +41,7 @@ namespace key = "backups.allowed_disk[" + std::to_string(++counter) + "]"; if (!config.has(key)) throw Exception(ErrorCodes::BAD_ARGUMENTS, - "Disk {} is not allowed for backups, see the 'backups.allowed_disk' configuration parameter", quoteString(disk_name)); + "Disk '{}' is not allowed for backups, see the 'backups.allowed_disk' configuration parameter", quoteString(disk_name)); } } @@ -54,7 +54,7 @@ namespace bool path_ok = path.empty() || (path.is_relative() && (*path.begin() != "..")); if (!path_ok) - throw Exception(ErrorCodes::BAD_ARGUMENTS, "Path {} to backup must be inside the specified disk {}", + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Path '{}' to backup must be inside the specified disk '{}'", quoteString(path.c_str()), quoteString(disk_name)); } From f31f11dd67d8dbce08ba98c5ae85a61e9fb7ddaa Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Fri, 31 Mar 2023 09:12:41 +0000 Subject: [PATCH 282/377] Disable env credentials for stateless tests --- tests/config/config.d/disable_s3_env_credentials.xml | 5 +++++ tests/config/install.sh | 1 + 2 files changed, 6 insertions(+) create mode 100644 tests/config/config.d/disable_s3_env_credentials.xml diff --git a/tests/config/config.d/disable_s3_env_credentials.xml b/tests/config/config.d/disable_s3_env_credentials.xml new file mode 100644 index 00000000000..24a7e0f2f35 --- /dev/null +++ b/tests/config/config.d/disable_s3_env_credentials.xml @@ -0,0 +1,5 @@ + + + 0 + + diff --git a/tests/config/install.sh b/tests/config/install.sh index a6391f6f43f..44eab0e4db0 100755 --- a/tests/config/install.sh +++ b/tests/config/install.sh @@ -55,6 +55,7 @@ ln -sf $SRC_PATH/config.d/custom_disks_base_path.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/display_name.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/reverse_dns_query_function.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/compressed_marks_and_index.xml $DEST_SERVER_PATH/config.d/ +ln -sf $SRC_PATH/config.d/disable_s3_env_credentials.xml $DEST_SERVER_PATH/config.d/ # Not supported with fasttest. if [ "${DEST_SERVER_PATH}" = "/etc/clickhouse-server" ] From 42abb843252bbb60c60f43006864a28cd47f18bc Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 31 Mar 2023 12:16:31 +0300 Subject: [PATCH 283/377] Fix 01710_projection_optimize_materialize flakiness Fails with different index granularity CI: https://s3.amazonaws.com/clickhouse-test-reports/48242/672dbf7cd894be6f5c0ac685d493371f2996229d/stateless_tests__asan__[3/4].html Signed-off-by: Azat Khuzhin --- .../0_stateless/01710_projection_optimize_materialize.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/queries/0_stateless/01710_projection_optimize_materialize.sql b/tests/queries/0_stateless/01710_projection_optimize_materialize.sql index 92d3ead828c..e704c3e5610 100644 --- a/tests/queries/0_stateless/01710_projection_optimize_materialize.sql +++ b/tests/queries/0_stateless/01710_projection_optimize_materialize.sql @@ -1,3 +1,4 @@ +-- Tags: no-random-merge-tree-settings drop table if exists z; create table z (pk Int64, d Date, id UInt64, c UInt64) Engine MergeTree partition by d order by pk settings ratio_of_defaults_for_sparse_serialization = 1.0; From 792979a5275d5f8a112f6b59e320ded050fb7e03 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 12:24:47 +0300 Subject: [PATCH 284/377] Revert "Randomize JIT settings in tests" --- tests/clickhouse-test | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/clickhouse-test b/tests/clickhouse-test index fa88bc19efd..a355c2f8e73 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -558,9 +558,6 @@ class SettingsRandomizer: "enable_memory_bound_merging_of_aggregation_results": lambda: random.randint( 0, 1 ), - "min_count_to_compile_expression": lambda: random.choice([0, 3]), - "min_count_to_compile_aggregate_expression": lambda: random.choice([0, 3]), - "min_count_to_compile_sort_description": lambda: random.choice([0, 3]), } @staticmethod From bac7def719603c9fae013113694cc0b7fb69244a Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 31 Mar 2023 12:26:45 +0300 Subject: [PATCH 285/377] Fix UB (signed integer overflow) in StorageMergeTree::backupData() UBsan report: /build/src/Storages/StorageMergeTree.cpp:2088:80: runtime error: signed integer overflow: 9223372036854775807 + 1 cannot be represented in type 'long' 0 0x30c2ae7b in DB::StorageMergeTree::backupData(DB::BackupEntriesCollector&, std::__1::basic_string, std::__1::allocator> const&, std::__1::optional, 7ul, std::__1::allocator>>> const&) build_docker/./src/Storages/StorageMergeTree.cpp:2088:80 1 0x2dd641b5 in DB::BackupEntriesCollector::makeBackupEntriesForTableData(DB::QualifiedTableName const&) build_docker/./src/Backups/BackupEntriesCollector.cpp:703:18 2 0x2dd5c2a7 in DB::BackupEntriesCollector::makeBackupEntriesForTablesData() build_docker/./src/Backups/BackupEntriesCollector.cpp:676:9 3 0x2dd5848d in DB::BackupEntriesCollector::run() build_docker/./src/Backups/BackupEntriesCollector.cpp:119:5 4 0x2dd84da3 in DB::BackupsWorker::doBackup(std::__1::shared_ptr const&, std::__1::basic_string, std::__1::allocator> const&, std::__1::basic_string, std::__1::allocator> const&, DB::BackupInfo const&, DB::BackupSettings, std::__1::shared_ptr, std::__1::shared_ptr const&, std::__1::shared_ptr, bool) build_docker/./src/Backups/BackupsWorker.cpp:359:59 5 0x2dd82405 in DB::BackupsWorker::startMakingBackup(std::__1::shared_ptr const&, std::__1::shared_ptr const&) build_docker/./src/Backups/BackupsWorker.cpp:248:13 6 0x2dd81d0a in DB::BackupsWorker::start(std::__1::shared_ptr const&, std::__1::shared_ptr) build_docker/./src/Backups/BackupsWorker.cpp:179:16 7 0x2f4d0d5f in DB::InterpreterBackupQuery::execute() build_docker/./src/Interpreters/InterpreterBackupQuery.cpp:39:30 SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /build/src/Storages/StorageMergeTree.cpp:2088:80 in Signed-off-by: Azat Khuzhin --- src/Storages/StorageMergeTree.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index abab2b2dc68..71a826fbc22 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -2082,10 +2082,10 @@ void StorageMergeTree::backupData(BackupEntriesCollector & backup_entries_collec Int64 min_data_version = std::numeric_limits::max(); for (const auto & data_part : data_parts) - min_data_version = std::min(min_data_version, data_part->info.getDataVersion()); + min_data_version = std::min(min_data_version, data_part->info.getDataVersion() + 1); backup_entries_collector.addBackupEntries(backupParts(data_parts, data_path_in_backup, local_context)); - backup_entries_collector.addBackupEntries(backupMutations(min_data_version + 1, data_path_in_backup)); + backup_entries_collector.addBackupEntries(backupMutations(min_data_version, data_path_in_backup)); } From 8d91a9f4c2c83cf0c82442f0ad309a1b0f1c1f2b Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Fri, 31 Mar 2023 09:36:03 +0000 Subject: [PATCH 286/377] Sort descendingly by elapsed time --- src/Interpreters/InterpreterShowProcesslistQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterShowProcesslistQuery.cpp b/src/Interpreters/InterpreterShowProcesslistQuery.cpp index 1522af4bcbe..f9241368a8f 100644 --- a/src/Interpreters/InterpreterShowProcesslistQuery.cpp +++ b/src/Interpreters/InterpreterShowProcesslistQuery.cpp @@ -12,7 +12,7 @@ namespace DB BlockIO InterpreterShowProcesslistQuery::execute() { - return executeQuery("SELECT * FROM system.processes ORDER BY query_id", getContext(), true); + return executeQuery("SELECT * FROM system.processes ORDER BY elapsed DESC", getContext(), true); } } From 9806a831c096724d23faba4c1af1fbaa1d41121a Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Fri, 31 Mar 2023 09:41:59 +0000 Subject: [PATCH 287/377] Small test refactoring --- .../0_stateless/25402_show_columns.reference | 22 ++++++------ .../0_stateless/25402_show_columns.sql | 34 ++++++------------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/tests/queries/0_stateless/25402_show_columns.reference b/tests/queries/0_stateless/25402_show_columns.reference index 98438bd6595..da967d59cda 100644 --- a/tests/queries/0_stateless/25402_show_columns.reference +++ b/tests/queries/0_stateless/25402_show_columns.reference @@ -1,38 +1,38 @@ int32 Nullable(Int32) 1 \N str String 0 SOR \N uint64 UInt64 0 PRI SOR \N ---- +--- EXTENDED int32 Nullable(Int32) 1 \N str String 0 SOR \N uint64 UInt64 0 PRI SOR \N ---- +--- FULL int32 Nullable(Int32) 1 \N \N example comment str String 0 SOR \N \N uint64 UInt64 0 PRI SOR \N \N ---- +--- LIKE int32 Nullable(Int32) 1 \N uint64 UInt64 0 PRI SOR \N ---- +--- NOT LIKE str String 0 SOR \N ---- +--- ILIKE int32 Nullable(Int32) 1 \N uint64 UInt64 0 PRI SOR \N ---- +--- NOT ILIKE str String 0 SOR \N ---- +--- WHERE int32 Nullable(Int32) 1 \N uint64 UInt64 0 PRI SOR \N ---- +--- LIMIT int32 Nullable(Int32) 1 \N ---- +--- Original table int32 Nullable(Int32) 1 \N str String 0 SOR \N uint64 UInt64 0 PRI SOR \N ---- +--- Equally named table in other database int32 Int32 0 \N str String 0 \N uint64 UInt64 0 PRI SOR \N ---- +--- Short form int32 Int32 0 \N str String 0 \N uint64 UInt64 0 PRI SOR \N diff --git a/tests/queries/0_stateless/25402_show_columns.sql b/tests/queries/0_stateless/25402_show_columns.sql index 28ac54bd193..ce206fedee4 100644 --- a/tests/queries/0_stateless/25402_show_columns.sql +++ b/tests/queries/0_stateless/25402_show_columns.sql @@ -17,41 +17,30 @@ ORDER BY (uint64, str); SHOW COLUMNS FROM tab; -SELECT '---'; - +SELECT '--- EXTENDED'; SHOW EXTENDED COLUMNS FROM tab; -SELECT '---'; - +SELECT '--- FULL'; SHOW FULL COLUMNS FROM tab; -SELECT '---'; - +SELECT '--- LIKE'; SHOW COLUMNS FROM tab LIKE '%int%'; -SELECT '---'; - +SELECT '--- NOT LIKE'; SHOW COLUMNS FROM tab NOT LIKE '%int%'; -SELECT '---'; - +SELECT '--- ILIKE'; SHOW COLUMNS FROM tab ILIKE '%INT%'; -SELECT '---'; - +SELECT '--- NOT ILIKE'; SHOW COLUMNS FROM tab NOT ILIKE '%INT%'; -SELECT '---'; - +SELECT '--- WHERE'; SHOW COLUMNS FROM tab WHERE field LIKE '%int%'; -SELECT '---'; - +SELECT '--- LIMIT'; SHOW COLUMNS FROM tab LIMIT 1; -SELECT '---'; - - -- Create a table in a different database. Intentionally useing the same table/column names as above so -- we notice if something is buggy in the implementation of SHOW COLUMNS. DROP DATABASE IF EXISTS database_123456789abcde; @@ -67,14 +56,13 @@ CREATE TABLE database_123456789abcde.tab ENGINE = MergeTree ORDER BY uint64; +SELECT '--- Original table'; SHOW COLUMNS FROM tab; -SELECT '---'; - +SELECT '--- Equally named table in other database'; SHOW COLUMNS FROM tab FROM database_123456789abcde; -SELECT '---'; - +SELECT '--- Short form'; SHOW COLUMNS FROM database_123456789abcde.tab; DROP DATABASE database_123456789abcde; From dd79bf0a03753bebc9d22af11771cce6830d8015 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 31 Mar 2023 12:20:50 +0200 Subject: [PATCH 288/377] Update autogenerated version to 23.4.1.1 and contributors --- cmake/autogenerated_versions.txt | 10 +++--- .../StorageSystemContributors.generated.cpp | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index b52b2eda992..9bb148c12a9 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -2,11 +2,11 @@ # NOTE: has nothing common with DBMS_TCP_PROTOCOL_VERSION, # only DBMS_TCP_PROTOCOL_VERSION should be incremented on protocol changes. -SET(VERSION_REVISION 54472) +SET(VERSION_REVISION 54473) SET(VERSION_MAJOR 23) -SET(VERSION_MINOR 3) +SET(VERSION_MINOR 4) SET(VERSION_PATCH 1) -SET(VERSION_GITHASH 52bf836e03a6ba7cf2d654eaaf73231701abc3a2) -SET(VERSION_DESCRIBE v23.3.1.2537-testing) -SET(VERSION_STRING 23.3.1.2537) +SET(VERSION_GITHASH 46e85357ce2da2a99f56ee83a079e892d7ec3726) +SET(VERSION_DESCRIBE v23.4.1.1-testing) +SET(VERSION_STRING 23.4.1.1) # end of autochange diff --git a/src/Storages/System/StorageSystemContributors.generated.cpp b/src/Storages/System/StorageSystemContributors.generated.cpp index ca19687918c..a6704144fde 100644 --- a/src/Storages/System/StorageSystemContributors.generated.cpp +++ b/src/Storages/System/StorageSystemContributors.generated.cpp @@ -35,6 +35,7 @@ const char * auto_contributors[] { "Aleksei Filatov", "Aleksei Levushkin", "Aleksei Semiglazov", + "Aleksei Tikhomirov", "Aleksey", "Aleksey Akulovich", "Alex", @@ -84,6 +85,7 @@ const char * auto_contributors[] { "Alexey Gusev", "Alexey Ilyukhov", "Alexey Ivanov", + "Alexey Korepanov", "Alexey Milovidov", "Alexey Perevyshin", "Alexey Tronov", @@ -135,6 +137,7 @@ const char * auto_contributors[] { "Andrii R", "Andy Liang", "Andy Yang", + "AndyB", "Anish Bhanwala", "Anmol Arora", "Anna", @@ -155,6 +158,7 @@ const char * auto_contributors[] { "Anton Yuzhaninov", "Anton Zhabolenko", "Antonio Andelic", + "Antonio Bonuccelli", "Ariel Robaldo", "Arsen Hakobyan", "Arslan G", @@ -227,6 +231,7 @@ const char * auto_contributors[] { "Christoph Wurm", "Chun-Sheng, Li", "Ciprian Hacman", + "Clayton McClure", "Clement Rodriguez", "ClickHouse Admin", "Clément Rodriguez", @@ -256,6 +261,7 @@ const char * auto_contributors[] { "Dario", "DarkWanderer", "Darío", + "Dave Lahn", "Denis Burlaka", "Denis Glazachev", "Denis Krivak", @@ -391,6 +397,7 @@ const char * auto_contributors[] { "HeenaBansal2009", "Hiroaki Nakamura", "Hongbin", + "Hosun Lee", "HuFuwang", "Hui Wang", "ILya Limarenko", @@ -457,12 +464,15 @@ const char * auto_contributors[] { "Jiebin Sun", "Joanna Hulboj", "Jochen Schalanda", + "Joey", + "Johannes Visintini", "John", "John Hummel", "John Skopis", "Jonatas Freitas", "Jonathan-Ackerman", "Jordi Villar", + "Joris Giovannangeli", "Jose", "Josh Taylor", "João Figueiredo", @@ -481,6 +491,7 @@ const char * auto_contributors[] { "Kevin Chiang", "Kevin Michel", "Kevin Zhang", + "KevinyhZou", "KinderRiven", "Kiran", "Kirill Danshin", @@ -525,6 +536,7 @@ const char * auto_contributors[] { "Li Yin", "Liu Cong", "LiuCong", + "LiuNeng", "LiuYangkuan", "Lloyd-Pottiger", "Lopatin Konstantin", @@ -544,6 +556,7 @@ const char * auto_contributors[] { "Maksim Buren", "Maksim Fedotov", "Maksim Kita", + "Maksym Sobolyev", "Mallik Hassan", "Malte", "Manuel de la Peña", @@ -625,6 +638,7 @@ const char * auto_contributors[] { "Mikhail Salosin", "Mikhail Surin", "Mikhail f. Shiryaev", + "MikhailBurdukov", "MikuSugar", "Milad Arabi", "Mingliang Pan", @@ -694,6 +708,7 @@ const char * auto_contributors[] { "OmarBazaraa", "OnePiece", "Onehr7", + "Ongkong", "Orivej Desh", "Orkhan Zeynalli", "Oskar Wojciski", @@ -701,6 +716,7 @@ const char * auto_contributors[] { "PHO", "Pablo Alegre", "Pablo Marcos", + "Palash Goel", "Paramtamtam", "Patrick Zippenfenig", "Paul Loyd", @@ -795,6 +811,7 @@ const char * auto_contributors[] { "Sergei Bocharov", "Sergei Semin", "Sergei Shtykov", + "Sergei Solomatov", "Sergei Trifonov", "Sergei Tsetlin (rekub)", "Sergey Demurin", @@ -844,6 +861,7 @@ const char * auto_contributors[] { "Stupnikov Andrey", "SuperBot", "SuperDJY", + "SupunKavinda", "Suzy Wang", "SuzyWangIBMer", "Sébastien", @@ -865,6 +883,7 @@ const char * auto_contributors[] { "The-Alchemist", "Thom O'Connor", "Thomas Berdy", + "Thomas Casteleyn", "Tian Xinhui", "Tiaonmmn", "Tigran Khudaverdyan", @@ -996,6 +1015,7 @@ const char * auto_contributors[] { "Zhipeng", "Zijie Lu", "Zoran Pandovski", + "[데이터플랫폼팀] 이호선", "a.palagashvili", "aaapetrenko", "abdrakhmanov", @@ -1011,6 +1031,7 @@ const char * auto_contributors[] { "akuzm", "alekseik1", "alesapin", + "alex filatov", "alex-zaitsev", "alex.lvxin", "alexX512", @@ -1035,12 +1056,14 @@ const char * auto_contributors[] { "anton", "ap11", "aprudaev", + "artem-yadr", "artpaul", "asiana21", "atereh", "attack204", "avasiliev", "avogar", + "avoiderboi", "avsharapov", "awakeljw", "awesomeleo", @@ -1079,6 +1102,7 @@ const char * auto_contributors[] { "chou.fan", "christophe.kalenzaga", "clarkcaoliu", + "clickhouse-adrianfraguela", "clickhouse-robot-curie", "cms", "cmsxbc", @@ -1111,6 +1135,7 @@ const char * auto_contributors[] { "dmitriy", "dmitry kuzmin", "dongyifeng", + "ducle.canh", "eaxdev", "eejoin", "egatov", @@ -1123,6 +1148,8 @@ const char * auto_contributors[] { "erikbaan", "ermaotech", "evtan", + "exX512", + "exmy", "exprmntr", "ezhaka", "f1yegor", @@ -1152,6 +1179,7 @@ const char * auto_contributors[] { "fuwhu", "fuzhe1989", "fuzzERot", + "fyu", "g-arslan", "ggerogery", "giordyb", @@ -1178,9 +1206,11 @@ const char * auto_contributors[] { "hhell", "homeward", "hotid", + "houbaron", "huangzhaowei", "hustnn", "huzhichengdd", + "iammagicc", "ianton-ru", "ice1x", "idfer", @@ -1212,6 +1242,7 @@ const char * auto_contributors[] { "jinjunzh", "jkuklis", "jthmath", + "jun won", "jus1096", "jyz0309", "karnevil13", @@ -1223,6 +1254,7 @@ const char * auto_contributors[] { "kigerzhang", "kirillikoff", "kmeaw", + "kolechenkov", "koloshmet", "kolsys", "konnectr", @@ -1252,6 +1284,7 @@ const char * auto_contributors[] { "liangqian", "libenwang", "lichengxiang", + "liding1992", "linceyou", "lincion", "lingo-xp", @@ -1330,6 +1363,7 @@ const char * auto_contributors[] { "nauta", "nautaa", "ndchikin", + "nellicus", "neng.liu", "never lee", "ni1l", @@ -1545,6 +1579,7 @@ const char * auto_contributors[] { "Дмитрий Канатников", "Иванов Евгений", "Илья Исаев", + "Коренберг Марк", "Коренберг ☢️ Марк", "Павел Литвиненко", "Смитюх Вячеслав", From 0c9d7f73ce1a6cd1fad8ce87e2c82cd6c118b064 Mon Sep 17 00:00:00 2001 From: robot-clickhouse Date: Fri, 31 Mar 2023 10:35:58 +0000 Subject: [PATCH 289/377] Update version_date.tsv and changelogs after v23.3.1.2823-lts --- SECURITY.md | 5 +- docker/keeper/Dockerfile | 2 +- docker/server/Dockerfile.alpine | 2 +- docker/server/Dockerfile.ubuntu | 2 +- docs/changelogs/v23.3.1.2823-lts.md | 545 +++++++++++++++++++++++++++ utils/list-versions/version_date.tsv | 1 + 6 files changed, 552 insertions(+), 5 deletions(-) create mode 100644 docs/changelogs/v23.3.1.2823-lts.md diff --git a/SECURITY.md b/SECURITY.md index 7c6648c70eb..566a1820834 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -13,9 +13,10 @@ The following versions of ClickHouse server are currently being supported with s | Version | Supported | |:-|:-| +| 23.3 | ✔️ | | 23.2 | ✔️ | | 23.1 | ✔️ | -| 22.12 | ✔️ | +| 22.12 | ❌ | | 22.11 | ❌ | | 22.10 | ❌ | | 22.9 | ❌ | @@ -24,7 +25,7 @@ The following versions of ClickHouse server are currently being supported with s | 22.6 | ❌ | | 22.5 | ❌ | | 22.4 | ❌ | -| 22.3 | ✔️ | +| 22.3 | ❌ | | 22.2 | ❌ | | 22.1 | ❌ | | 21.* | ❌ | diff --git a/docker/keeper/Dockerfile b/docker/keeper/Dockerfile index 6496a2b2a12..37b58758b9e 100644 --- a/docker/keeper/Dockerfile +++ b/docker/keeper/Dockerfile @@ -32,7 +32,7 @@ RUN arch=${TARGETARCH:-amd64} \ esac ARG REPOSITORY="https://s3.amazonaws.com/clickhouse-builds/22.4/31c367d3cd3aefd316778601ff6565119fe36682/package_release" -ARG VERSION="23.2.4.12" +ARG VERSION="23.3.1.2823" ARG PACKAGES="clickhouse-keeper" # user/group precreated explicitly with fixed uid/gid on purpose. diff --git a/docker/server/Dockerfile.alpine b/docker/server/Dockerfile.alpine index f4ca498a7e7..822aa752655 100644 --- a/docker/server/Dockerfile.alpine +++ b/docker/server/Dockerfile.alpine @@ -33,7 +33,7 @@ RUN arch=${TARGETARCH:-amd64} \ # lts / testing / prestable / etc ARG REPO_CHANNEL="stable" ARG REPOSITORY="https://packages.clickhouse.com/tgz/${REPO_CHANNEL}" -ARG VERSION="23.2.4.12" +ARG VERSION="23.3.1.2823" ARG PACKAGES="clickhouse-client clickhouse-server clickhouse-common-static" # user/group precreated explicitly with fixed uid/gid on purpose. diff --git a/docker/server/Dockerfile.ubuntu b/docker/server/Dockerfile.ubuntu index 13b3ebdb01c..ec7e164e51f 100644 --- a/docker/server/Dockerfile.ubuntu +++ b/docker/server/Dockerfile.ubuntu @@ -22,7 +22,7 @@ RUN sed -i "s|http://archive.ubuntu.com|${apt_archive}|g" /etc/apt/sources.list ARG REPO_CHANNEL="stable" ARG REPOSITORY="deb https://packages.clickhouse.com/deb ${REPO_CHANNEL} main" -ARG VERSION="23.2.4.12" +ARG VERSION="23.3.1.2823" ARG PACKAGES="clickhouse-client clickhouse-server clickhouse-common-static" # set non-empty deb_location_url url to create a docker image diff --git a/docs/changelogs/v23.3.1.2823-lts.md b/docs/changelogs/v23.3.1.2823-lts.md new file mode 100644 index 00000000000..0c9be3601da --- /dev/null +++ b/docs/changelogs/v23.3.1.2823-lts.md @@ -0,0 +1,545 @@ +--- +sidebar_position: 1 +sidebar_label: 2023 +--- + +# 2023 Changelog + +### ClickHouse release v23.3.1.2823-lts (46e85357ce2) FIXME as compared to v23.2.1.2537-stable (52bf836e03a) + +#### Backward Incompatible Change +* Relax symbols that are allowed in URL authority in *domain*RFC()/netloc(). [#46841](https://github.com/ClickHouse/ClickHouse/pull/46841) ([Azat Khuzhin](https://github.com/azat)). +* Prohibit create tables based on KafkaEngine with DEFAULT/EPHEMERAL/ALIAS/MATERIALIZED statements for columns. [#47138](https://github.com/ClickHouse/ClickHouse/pull/47138) ([Aleksandr Musorin](https://github.com/AVMusorin)). +* An "asynchronous connection drain" feature is removed. Related settings and metrics are removed as well. It was an internal feature, so the removal should not affect users who had never heard about that feature. [#47486](https://github.com/ClickHouse/ClickHouse/pull/47486) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Support 256-bit Decimal data type (more than 38 digits) in `arraySum`/`Min`/`Max`/`Avg`/`Product`, `arrayCumSum`/`CumSumNonNegative`, `arrayDifference`, array construction, IN operator, query parameters, `groupArrayMovingSum`, statistical functions, `min`/`max`/`any`/`argMin`/`argMax`, PostgreSQL wire protocol, MySQL table engine and function, `sumMap`, `mapAdd`, `mapSubtract`, `arrayIntersect`. Add support for big integers in `arrayIntersect`. Statistical aggregate functions involving moments (such as `corr` or various `TTest`s) will use `Float64` as their internal representation (they were using `Decimal128` before this change, but it was pointless), and these functions can return `nan` instead of `inf` in case of infinite variance. Some functions were allowed on `Decimal256` data types but returned `Decimal128` in previous versions - now it is fixed. This closes [#47569](https://github.com/ClickHouse/ClickHouse/issues/47569). This closes [#44864](https://github.com/ClickHouse/ClickHouse/issues/44864). This closes [#28335](https://github.com/ClickHouse/ClickHouse/issues/28335). [#47594](https://github.com/ClickHouse/ClickHouse/pull/47594) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Make backup_threads/restore_threads server settings. [#47881](https://github.com/ClickHouse/ClickHouse/pull/47881) ([Azat Khuzhin](https://github.com/azat)). +* Fix the isIPv6String function which could have outputted a false positive result in the case of an incorrect IPv6 address. For example `1234::1234:` was considered a valid IPv6 address. [#47895](https://github.com/ClickHouse/ClickHouse/pull/47895) ([Nikolay Degterinsky](https://github.com/evillique)). + +#### New Feature +* Add new mode for splitting the work on replicas using settings `parallel_replicas_custom_key` and `parallel_replicas_custom_key_filter_type`. If the cluster consists of a single shard with multiple replicas, up to `max_parallel_replicas` will be randomly picked and turned into shards. For each shard, a corresponding filter is added to the query on the initiator before being sent to the shard. If the cluster consists of multiple shards, it will behave the same as `sample_key` but with the possibility to define an arbitrary key. [#45108](https://github.com/ClickHouse/ClickHouse/pull/45108) ([Antonio Andelic](https://github.com/antonio2368)). +* Added query setting `partial_result_on_first_cancel` allowing the canceled query (e.g. due to Ctrl-C) to return a partial result. [#45689](https://github.com/ClickHouse/ClickHouse/pull/45689) ([Alexey Perevyshin](https://github.com/alexX512)). +* Added support of arbitrary tables engines for temporary tables except for Replicated and KeeperMap engines. Partially close [#31497](https://github.com/ClickHouse/ClickHouse/issues/31497). [#46071](https://github.com/ClickHouse/ClickHouse/pull/46071) ([Roman Vasin](https://github.com/rvasin)). +* Add replication of user-defined SQL functions using ZooKeeper. [#46085](https://github.com/ClickHouse/ClickHouse/pull/46085) ([Aleksei Filatov](https://github.com/aalexfvk)). +* Implement `system.server_settings` (similar to `system.settings`), which will contain server configurations. [#46550](https://github.com/ClickHouse/ClickHouse/pull/46550) ([pufit](https://github.com/pufit)). +* Intruduce a function `WIDTH_BUCKET`. [#42974](https://github.com/ClickHouse/ClickHouse/issues/42974). [#46790](https://github.com/ClickHouse/ClickHouse/pull/46790) ([avoiderboi](https://github.com/avoiderboi)). +* Add new function parseDateTime/parseDateTimeInJodaSyntax according to specified format string. parseDateTime parses string to datetime in MySQL syntax, parseDateTimeInJodaSyntax parses in Joda syntax. [#46815](https://github.com/ClickHouse/ClickHouse/pull/46815) ([李扬](https://github.com/taiyang-li)). +* Use `dummy UInt8` for default structure of table function `null`. Closes [#46930](https://github.com/ClickHouse/ClickHouse/issues/46930). [#47006](https://github.com/ClickHouse/ClickHouse/pull/47006) ([flynn](https://github.com/ucasfl)). +* Dec 15, 2021 support for parseDateTimeBestEffort function. closes [#46816](https://github.com/ClickHouse/ClickHouse/issues/46816). [#47071](https://github.com/ClickHouse/ClickHouse/pull/47071) ([chen](https://github.com/xiedeyantu)). +* Add function ULIDStringToDateTime(). Closes [#46945](https://github.com/ClickHouse/ClickHouse/issues/46945). [#47087](https://github.com/ClickHouse/ClickHouse/pull/47087) ([Nikolay Degterinsky](https://github.com/evillique)). +* Add settings `http_wait_end_of_query` and `http_response_buffer_size` that corresponds to URL params `wait_end_of_query` and `buffer_size` for HTTP interface. [#47108](https://github.com/ClickHouse/ClickHouse/pull/47108) ([Vladimir C](https://github.com/vdimir)). +* Support for `UNDROP TABLE` query. Closes [#46811](https://github.com/ClickHouse/ClickHouse/issues/46811). [#47241](https://github.com/ClickHouse/ClickHouse/pull/47241) ([chen](https://github.com/xiedeyantu)). +* Add `system.marked_dropped_tables` table that shows tables that were dropped from `Atomic` databases but were not completely removed yet. [#47364](https://github.com/ClickHouse/ClickHouse/pull/47364) ([chen](https://github.com/xiedeyantu)). +* Add `INSTR` as alias of `positionCaseInsensitive` for MySQL compatibility. Closes [#47529](https://github.com/ClickHouse/ClickHouse/issues/47529). [#47535](https://github.com/ClickHouse/ClickHouse/pull/47535) ([flynn](https://github.com/ucasfl)). +* Added `toDecimalString` function allowing to convert numbers to string with fixed precision. [#47838](https://github.com/ClickHouse/ClickHouse/pull/47838) ([Andrey Zvonov](https://github.com/zvonand)). +* Added operator "REGEXP" (similar to operators "LIKE", "IN", "MOD" etc.) for better compatibility with MySQL. [#47869](https://github.com/ClickHouse/ClickHouse/pull/47869) ([Robert Schulze](https://github.com/rschu1ze)). +* Allow executing reading pipeline for DIRECT dictionary with CLICKHOUSE source in multiple threads. To enable set `dictionary_use_async_executor=1` in `SETTINGS` section for source in `CREATE DICTIONARY` statement. [#47986](https://github.com/ClickHouse/ClickHouse/pull/47986) ([Vladimir C](https://github.com/vdimir)). +* Add merge tree setting `max_number_of_mutatuins_for_replica`. It limit the number of part mutations per replica to the specified amount. Zero means no limit on the number of mutations per replica (the execution can still be constrained by other settings). [#48047](https://github.com/ClickHouse/ClickHouse/pull/48047) ([Vladimir C](https://github.com/vdimir)). + +#### Performance Improvement +* Optimize one nullable key aggregate performance. [#45772](https://github.com/ClickHouse/ClickHouse/pull/45772) ([LiuNeng](https://github.com/liuneng1994)). +* Implemented lowercase tokenbf_v1 index utilization for hasTokenOrNull, hasTokenCaseInsensitive and hasTokenCaseInsensitiveOrNull. [#46252](https://github.com/ClickHouse/ClickHouse/pull/46252) ([ltrk2](https://github.com/ltrk2)). +* Optimize the generic SIMD StringSearcher by searching first two chars. [#46289](https://github.com/ClickHouse/ClickHouse/pull/46289) ([Jiebin Sun](https://github.com/jiebinn)). +* System.detached_parts could be significant large. - added several sources with respects block size limitation - in each block iothread pool is used to calculate part size, ie to make syscalls in parallel. [#46624](https://github.com/ClickHouse/ClickHouse/pull/46624) ([Sema Checherinda](https://github.com/CheSema)). +* Increase the default value of `max_replicated_merges_in_queue` for ReplicatedMergeTree tables from 16 to 1000. It allows faster background merge operation on clusters with a very large number of replicas, such as clusters with shared storage in ClickHouse Cloud. [#47050](https://github.com/ClickHouse/ClickHouse/pull/47050) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Backups for large numbers of files were unbelievably slow in previous versions. [#47251](https://github.com/ClickHouse/ClickHouse/pull/47251) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Support filter push down to left table for JOIN with StorageJoin, StorageDictionary, StorageEmbeddedRocksDB. [#47280](https://github.com/ClickHouse/ClickHouse/pull/47280) ([Maksim Kita](https://github.com/kitaisreal)). +* Marks in memory are now compressed, using 3-6x less memory. [#47290](https://github.com/ClickHouse/ClickHouse/pull/47290) ([Michael Kolupaev](https://github.com/al13n321)). +* Updated copier to use group by instead of distinct to get list of partitions. For large tables this reduced the select time from over 500s to under 1s. [#47386](https://github.com/ClickHouse/ClickHouse/pull/47386) ([Clayton McClure](https://github.com/cmcclure-twilio)). +* Address https://github.com/clickhouse/clickhouse/issues/46453. bisect marked https://github.com/clickhouse/clickhouse/pull/35525 as the bad changed. this pr looks to reverse the changes in that pr. [#47544](https://github.com/ClickHouse/ClickHouse/pull/47544) ([Ongkong](https://github.com/ongkong)). +* Fixed excessive reading in queries with `FINAL`. [#47801](https://github.com/ClickHouse/ClickHouse/pull/47801) ([Nikita Taranov](https://github.com/nickitat)). +* Setting `max_final_threads` would be set to number of cores at server startup (by the same algorithm as we use for `max_threads`). This improves concurrency of `final` execution on servers with high number of CPUs. [#47915](https://github.com/ClickHouse/ClickHouse/pull/47915) ([Nikita Taranov](https://github.com/nickitat)). +* Avoid breaking batches on read requests to improve performance. [#47978](https://github.com/ClickHouse/ClickHouse/pull/47978) ([Antonio Andelic](https://github.com/antonio2368)). + +#### Improvement +* Add map related functions: mapFromArrays, which allows us to create map from a pair of arrays. [#31125](https://github.com/ClickHouse/ClickHouse/pull/31125) ([李扬](https://github.com/taiyang-li)). +* Rewrite distributed sends to avoid using filesystem as a queue, use in-memory queue instead. [#45491](https://github.com/ClickHouse/ClickHouse/pull/45491) ([Azat Khuzhin](https://github.com/azat)). +* Allow separate grants for named collections (e.g. to be able to give `SHOW/CREATE/ALTER/DROP named collection` access only to certain collections, instead of all at once). Closes [#40894](https://github.com/ClickHouse/ClickHouse/issues/40894). Add new access type `NAMED_COLLECTION_CONTROL` which is not given to default user unless explicitly added to user config (is required to be able to do `GRANT ALL`), also `show_named_collections` is no longer obligatory to be manually specified for default user to be able to have full access rights as was in 23.2. [#46241](https://github.com/ClickHouse/ClickHouse/pull/46241) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Now `X-ClickHouse-Query-Id` and `X-ClickHouse-Timezone` headers are added to response in all queries via http protocol. Previously it was done only for `SELECT` queries. [#46364](https://github.com/ClickHouse/ClickHouse/pull/46364) ([Anton Popov](https://github.com/CurtizJ)). +* Support for connection to a replica set via a URI with a host:port enum and support for the readPreference option in MongoDB dictionaries. Example URI: mongodb://db0.example.com:27017,db1.example.com:27017,db2.example.com:27017/?replicaSet=myRepl&readPreference=primary. [#46524](https://github.com/ClickHouse/ClickHouse/pull/46524) ([artem-yadr](https://github.com/artem-yadr)). +* Re-implement projection analysis on top of query plan. Added setting `query_plan_optimize_projection=1` to switch between old and new version. Fixes [#44963](https://github.com/ClickHouse/ClickHouse/issues/44963). [#46537](https://github.com/ClickHouse/ClickHouse/pull/46537) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Use parquet format v2 instead of v1 in output format by default. Add setting `output_format_parquet_version` to control parquet version, possible values `v1_0`, `v2_4`, `v2_6`, `v2_latest` (default). [#46617](https://github.com/ClickHouse/ClickHouse/pull/46617) ([Kruglov Pavel](https://github.com/Avogar)). +* Not for changelog - part of [#42648](https://github.com/ClickHouse/ClickHouse/issues/42648). [#46632](https://github.com/ClickHouse/ClickHouse/pull/46632) ([Yakov Olkhovskiy](https://github.com/yakov-olkhovskiy)). +* Allow to ignore errors while pushing to MATERILIZED VIEW (add new setting `materialized_views_ignore_errors`, by default to `false`, but it is set to `true` for flushing logs to `system.*_log` tables unconditionally). [#46658](https://github.com/ClickHouse/ClickHouse/pull/46658) ([Azat Khuzhin](https://github.com/azat)). +* Enable input_format_json_ignore_unknown_keys_in_named_tuple by default. [#46742](https://github.com/ClickHouse/ClickHouse/pull/46742) ([Kruglov Pavel](https://github.com/Avogar)). +* It is now possible using new configuration syntax to configure Kafka topics with periods in their name. [#46752](https://github.com/ClickHouse/ClickHouse/pull/46752) ([Robert Schulze](https://github.com/rschu1ze)). +* Fix heuristics that check hyperscan patterns for problematic repeats. [#46819](https://github.com/ClickHouse/ClickHouse/pull/46819) ([Robert Schulze](https://github.com/rschu1ze)). +* Don't report ZK node exists to system.errors when a block was created concurrently by a different replica. [#46820](https://github.com/ClickHouse/ClickHouse/pull/46820) ([Raúl Marín](https://github.com/Algunenano)). +* Allow PREWHERE for Merge with different DEFAULT expression for column. [#46831](https://github.com/ClickHouse/ClickHouse/pull/46831) ([Azat Khuzhin](https://github.com/azat)). +* Increase the limit for opened files in `clickhouse-local`. It will be able to read from `web` tables on servers with a huge number of CPU cores. Do not back off reading from the URL table engine in case of too many opened files. This closes [#46852](https://github.com/ClickHouse/ClickHouse/issues/46852). [#46853](https://github.com/ClickHouse/ClickHouse/pull/46853) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Exceptions thrown when numbers cannot be parsed now have an easier-to-read exception message. [#46917](https://github.com/ClickHouse/ClickHouse/pull/46917) ([Robert Schulze](https://github.com/rschu1ze)). +* Added update `system.backups` after every processed task. [#46989](https://github.com/ClickHouse/ClickHouse/pull/46989) ([Aleksandr Musorin](https://github.com/AVMusorin)). +* Allow types conversion in Native input format. Add settings `input_format_native_allow_types_conversion` that controls it (enabled by default). [#46990](https://github.com/ClickHouse/ClickHouse/pull/46990) ([Kruglov Pavel](https://github.com/Avogar)). +* Allow IPv4 in the `range` function to generate IP ranges. [#46995](https://github.com/ClickHouse/ClickHouse/pull/46995) ([Yakov Olkhovskiy](https://github.com/yakov-olkhovskiy)). +* Role change was not promoted sometimes before https://github.com/ClickHouse/ClickHouse/pull/46772 This PR just adds tests. [#47002](https://github.com/ClickHouse/ClickHouse/pull/47002) ([Ilya Golshtein](https://github.com/ilejn)). +* Improve exception message when it's impossible to make part move from one volume/disk to another. [#47032](https://github.com/ClickHouse/ClickHouse/pull/47032) ([alesapin](https://github.com/alesapin)). +* Support `Bool` type in `JSONType` function. Previously `Null` type was mistakenly returned for bool values. [#47046](https://github.com/ClickHouse/ClickHouse/pull/47046) ([Anton Popov](https://github.com/CurtizJ)). +* Use _request_body parameter to configure predefined http queries. [#47086](https://github.com/ClickHouse/ClickHouse/pull/47086) ([Constantine Peresypkin](https://github.com/pkit)). +* Removing logging of custom disk structure. [#47103](https://github.com/ClickHouse/ClickHouse/pull/47103) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Allow nested custom disks. Previously custom disks supported only flat disk structure. [#47106](https://github.com/ClickHouse/ClickHouse/pull/47106) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Automatic indentation in the built-in UI SQL editor when Enter is pressed. [#47113](https://github.com/ClickHouse/ClickHouse/pull/47113) ([Alexey Korepanov](https://github.com/alexkorep)). +* Allow control compression in Parquet/ORC/Arrow output formats, support more compression for input formats. This closes [#13541](https://github.com/ClickHouse/ClickHouse/issues/13541). [#47114](https://github.com/ClickHouse/ClickHouse/pull/47114) ([Kruglov Pavel](https://github.com/Avogar)). +* Self-extraction with 'sudo' will attempt to set uid and gid of extracted files to running user. [#47116](https://github.com/ClickHouse/ClickHouse/pull/47116) ([Yakov Olkhovskiy](https://github.com/yakov-olkhovskiy)). +* Currently the funtion repeat's second argument must be unsigned integer type, which can not accept a integer value like -1. And this is different from the spark function, so I fix this here to make it same as spark. And it tested as below. [#47134](https://github.com/ClickHouse/ClickHouse/pull/47134) ([KevinyhZou](https://github.com/KevinyhZou)). +* Remove `::__1` part from stacktraces. Display `std::basic_string 1 trailing % [#46869](https://github.com/ClickHouse/ClickHouse/pull/46869) ([Robert Schulze](https://github.com/rschu1ze)). +* Add new metrics to system.asynchronous_metrics [#46886](https://github.com/ClickHouse/ClickHouse/pull/46886) ([Azat Khuzhin](https://github.com/azat)). +* Fix flaky `test_concurrent_queries_restriction_by_query_kind` [#46887](https://github.com/ClickHouse/ClickHouse/pull/46887) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Fix test test_async_backups_to_same_destination. [#46888](https://github.com/ClickHouse/ClickHouse/pull/46888) ([Vitaly Baranov](https://github.com/vitlibar)). +* Make ASTSelectQuery::formatImpl() more robust [#46889](https://github.com/ClickHouse/ClickHouse/pull/46889) ([Robert Schulze](https://github.com/rschu1ze)). +* tests: fix 02116_interactive_hello for "official build" [#46911](https://github.com/ClickHouse/ClickHouse/pull/46911) ([Azat Khuzhin](https://github.com/azat)). +* Fix some expect tests leftovers and enable them in fasttest [#46915](https://github.com/ClickHouse/ClickHouse/pull/46915) ([Azat Khuzhin](https://github.com/azat)). +* Increase ddl timeout for DROP statement in backup restore tests [#46920](https://github.com/ClickHouse/ClickHouse/pull/46920) ([SmitaRKulkarni](https://github.com/SmitaRKulkarni)). +* A better alternative to [#46344](https://github.com/ClickHouse/ClickHouse/issues/46344) [#46921](https://github.com/ClickHouse/ClickHouse/pull/46921) ([Robert Schulze](https://github.com/rschu1ze)). +* Code review from @tavplubix [#46922](https://github.com/ClickHouse/ClickHouse/pull/46922) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Planner: trivial count optimization [#46923](https://github.com/ClickHouse/ClickHouse/pull/46923) ([Igor Nikonov](https://github.com/devcrafter)). +* Typo: SIZES_OF_ARRAYS_DOESNT_MATCH --> SIZES_OF_ARRAYS_DONT_MATCH [#46940](https://github.com/ClickHouse/ClickHouse/pull/46940) ([Robert Schulze](https://github.com/rschu1ze)). +* Another fix for clone() for ASTColumnMatchers [#46947](https://github.com/ClickHouse/ClickHouse/pull/46947) ([Nikolay Degterinsky](https://github.com/evillique)). +* Un-inline likePatternToRegexp() [#46950](https://github.com/ClickHouse/ClickHouse/pull/46950) ([Robert Schulze](https://github.com/rschu1ze)). +* Fix missing format_description [#46959](https://github.com/ClickHouse/ClickHouse/pull/46959) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* ARM: Activate LDAPR with -march flag instead via -XClang [#46960](https://github.com/ClickHouse/ClickHouse/pull/46960) ([Robert Schulze](https://github.com/rschu1ze)). +* Preset description on the tweak reset [#46963](https://github.com/ClickHouse/ClickHouse/pull/46963) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Update version_date.tsv and changelogs after v22.3.19.6-lts [#46964](https://github.com/ClickHouse/ClickHouse/pull/46964) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Update version_date.tsv and changelogs after v22.8.14.53-lts [#46969](https://github.com/ClickHouse/ClickHouse/pull/46969) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Better exception messages when schema_inference_hints is ill-formatted [#46971](https://github.com/ClickHouse/ClickHouse/pull/46971) ([Kruglov Pavel](https://github.com/Avogar)). +* Decrease log level in "disks" [#46976](https://github.com/ClickHouse/ClickHouse/pull/46976) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Change the cherry-pick PR body [#46977](https://github.com/ClickHouse/ClickHouse/pull/46977) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Rename recent stateless tests to fix order [#46991](https://github.com/ClickHouse/ClickHouse/pull/46991) ([Kruglov Pavel](https://github.com/Avogar)). +* Pass headers from StorageURL to WriteBufferFromHTTP [#46996](https://github.com/ClickHouse/ClickHouse/pull/46996) ([Konstantin Bogdanov](https://github.com/thevar1able)). +* Change level log in executeQuery [#46997](https://github.com/ClickHouse/ClickHouse/pull/46997) ([Andrey Bystrov](https://github.com/AndyBys)). +* Add thevar1able to trusted contributors [#46998](https://github.com/ClickHouse/ClickHouse/pull/46998) ([Konstantin Bogdanov](https://github.com/thevar1able)). +* Use /etc/default/clickhouse in systemd too [#47003](https://github.com/ClickHouse/ClickHouse/pull/47003) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix tmp_path_template in HTTPHandler::processQuery [#47007](https://github.com/ClickHouse/ClickHouse/pull/47007) ([Vladimir C](https://github.com/vdimir)). +* Fix flaky azure test [#47011](https://github.com/ClickHouse/ClickHouse/pull/47011) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Temporary enable force_sync for keeper in CI [#47024](https://github.com/ClickHouse/ClickHouse/pull/47024) ([alesapin](https://github.com/alesapin)). +* ActionsDAG: do not change result of and() during optimization - part 2 [#47028](https://github.com/ClickHouse/ClickHouse/pull/47028) ([Salvatore Mesoraca](https://github.com/aiven-sal)). +* Add upgrade check to stateful dependent field [#47031](https://github.com/ClickHouse/ClickHouse/pull/47031) ([Kruglov Pavel](https://github.com/Avogar)). +* Disable path check in SQLite storage for clickhouse-local [#47052](https://github.com/ClickHouse/ClickHouse/pull/47052) ([Nikolay Degterinsky](https://github.com/evillique)). +* Terminate long-running offline non-busy runners in EC2 [#47064](https://github.com/ClickHouse/ClickHouse/pull/47064) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix Keeper with `force_sync = false` [#47065](https://github.com/ClickHouse/ClickHouse/pull/47065) ([Antonio Andelic](https://github.com/antonio2368)). +* Update version_date.tsv and changelogs after v23.2.2.20-stable [#47069](https://github.com/ClickHouse/ClickHouse/pull/47069) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Update version_date.tsv and changelogs after v23.1.4.58-stable [#47070](https://github.com/ClickHouse/ClickHouse/pull/47070) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Update version_date.tsv and changelogs after v22.12.4.76-stable [#47074](https://github.com/ClickHouse/ClickHouse/pull/47074) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Fix empty result when selection from only one side of join in analyzer [#47093](https://github.com/ClickHouse/ClickHouse/pull/47093) ([Vladimir C](https://github.com/vdimir)). +* Suppress "Cannot flush" for Distributed tables in upgrade check [#47095](https://github.com/ClickHouse/ClickHouse/pull/47095) ([Azat Khuzhin](https://github.com/azat)). +* Make stacktraces in hung check more readable [#47096](https://github.com/ClickHouse/ClickHouse/pull/47096) ([Alexander Tokmakov](https://github.com/tavplubix)). +* release lambda resources before detaching thread group [#47098](https://github.com/ClickHouse/ClickHouse/pull/47098) ([Sema Checherinda](https://github.com/CheSema)). +* Analyzer Planner fixes before enable by default [#47101](https://github.com/ClickHouse/ClickHouse/pull/47101) ([Maksim Kita](https://github.com/kitaisreal)). +* do flushUntrackedMemory when context switches [#47102](https://github.com/ClickHouse/ClickHouse/pull/47102) ([Sema Checherinda](https://github.com/CheSema)). +* fix: keeper systemd service file include invalid inline comment [#47105](https://github.com/ClickHouse/ClickHouse/pull/47105) ([SuperDJY](https://github.com/cmsxbc)). +* Add code for autoscaling lambda [#47107](https://github.com/ClickHouse/ClickHouse/pull/47107) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Enable lightweight delete support by default [#47109](https://github.com/ClickHouse/ClickHouse/pull/47109) ([Alexander Gololobov](https://github.com/davenger)). +* Update typing for a new PyGithub version [#47123](https://github.com/ClickHouse/ClickHouse/pull/47123) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Shorten some code with CTAD [#47139](https://github.com/ClickHouse/ClickHouse/pull/47139) ([Robert Schulze](https://github.com/rschu1ze)). +* Make 01710_projections more stable. [#47145](https://github.com/ClickHouse/ClickHouse/pull/47145) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* fix_JSON_searchField [#47147](https://github.com/ClickHouse/ClickHouse/pull/47147) ([Aleksei Tikhomirov](https://github.com/aletik256)). +* Mark 01771_bloom_filter_not_has as no-parallel and long [#47148](https://github.com/ClickHouse/ClickHouse/pull/47148) ([Azat Khuzhin](https://github.com/azat)). +* Use unique names and paths in `test_replicated_database` [#47152](https://github.com/ClickHouse/ClickHouse/pull/47152) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Add stupid retries in clickhouse-test health check. [#47158](https://github.com/ClickHouse/ClickHouse/pull/47158) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* 02346_full_text_search.sql: Add result separators to simplify analysis [#47166](https://github.com/ClickHouse/ClickHouse/pull/47166) ([Robert Schulze](https://github.com/rschu1ze)). +* More correct handling of fatal errors [#47175](https://github.com/ClickHouse/ClickHouse/pull/47175) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Update read in StorageMemory [#47180](https://github.com/ClickHouse/ClickHouse/pull/47180) ([Konstantin Morozov](https://github.com/k-morozov)). +* Doc update for mapFromArrays() [#47183](https://github.com/ClickHouse/ClickHouse/pull/47183) ([Robert Schulze](https://github.com/rschu1ze)). +* Fix failure context for Upgrade check [#47191](https://github.com/ClickHouse/ClickHouse/pull/47191) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Add support for different expected errors [#47196](https://github.com/ClickHouse/ClickHouse/pull/47196) ([Raúl Marín](https://github.com/Algunenano)). +* Fix ip coding on s390x [#47208](https://github.com/ClickHouse/ClickHouse/pull/47208) ([Suzy Wang](https://github.com/SuzyWangIBMer)). +* Add real client (initiator server) address into the logs for interserver mode [#47214](https://github.com/ClickHouse/ClickHouse/pull/47214) ([Azat Khuzhin](https://github.com/azat)). +* Fix 01019_alter_materialized_view_consistent [#47215](https://github.com/ClickHouse/ClickHouse/pull/47215) ([Vladimir C](https://github.com/vdimir)). +* Fix RewriteArrayExistsToHasPass [#47225](https://github.com/ClickHouse/ClickHouse/pull/47225) ([Maksim Kita](https://github.com/kitaisreal)). +* Release shared ptrs after finishing a transaction [#47245](https://github.com/ClickHouse/ClickHouse/pull/47245) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Add default constructor for `MultiReadResponse` [#47254](https://github.com/ClickHouse/ClickHouse/pull/47254) ([Antonio Andelic](https://github.com/antonio2368)). +* Join threads if exception happened in `ZooKeeperImpl` constructor [#47261](https://github.com/ClickHouse/ClickHouse/pull/47261) ([Antonio Andelic](https://github.com/antonio2368)). +* use std::lerp, constexpr hex.h [#47268](https://github.com/ClickHouse/ClickHouse/pull/47268) ([Mike Kot](https://github.com/myrrc)). +* Update version_date.tsv and changelogs after v23.2.3.17-stable [#47269](https://github.com/ClickHouse/ClickHouse/pull/47269) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Fix bug in zero copy replica which can lead to dataloss [#47274](https://github.com/ClickHouse/ClickHouse/pull/47274) ([alesapin](https://github.com/alesapin)). +* Fix typo [#47282](https://github.com/ClickHouse/ClickHouse/pull/47282) ([Nikolay Degterinsky](https://github.com/evillique)). +* Follow-up to [#46681](https://github.com/ClickHouse/ClickHouse/issues/46681) [#47284](https://github.com/ClickHouse/ClickHouse/pull/47284) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix test 02566_ipv4_ipv6_binary_formats [#47295](https://github.com/ClickHouse/ClickHouse/pull/47295) ([Kruglov Pavel](https://github.com/Avogar)). +* Set fixed index_granularity for test 00636 [#47298](https://github.com/ClickHouse/ClickHouse/pull/47298) ([Sema Checherinda](https://github.com/CheSema)). +* Add a manual trigger for release workflow [#47302](https://github.com/ClickHouse/ClickHouse/pull/47302) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix 02570_fallback_from_async_insert [#47308](https://github.com/ClickHouse/ClickHouse/pull/47308) ([Vladimir C](https://github.com/vdimir)). +* Catch exceptions in LiveViewPeriodicRefreshTask [#47309](https://github.com/ClickHouse/ClickHouse/pull/47309) ([Vladimir C](https://github.com/vdimir)). +* Fix MergeTreeTransaction::isReadOnly [#47310](https://github.com/ClickHouse/ClickHouse/pull/47310) ([Vladimir C](https://github.com/vdimir)). +* Fix an assertion with implicit transactions in interserver mode [#47312](https://github.com/ClickHouse/ClickHouse/pull/47312) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix `File exists` error in Upgrade check [#47314](https://github.com/ClickHouse/ClickHouse/pull/47314) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Support transformQueryForExternalDatabase for analyzer [#47316](https://github.com/ClickHouse/ClickHouse/pull/47316) ([Vladimir C](https://github.com/vdimir)). +* Disable parallel format in health check [#47318](https://github.com/ClickHouse/ClickHouse/pull/47318) ([Ilya Yatsishin](https://github.com/qoega)). +* Analyzer - fix combine logic for limit expression and limit setting [#47324](https://github.com/ClickHouse/ClickHouse/pull/47324) ([Yakov Olkhovskiy](https://github.com/yakov-olkhovskiy)). +* Suppress expected errors from test 01111 in Upgrade check [#47365](https://github.com/ClickHouse/ClickHouse/pull/47365) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix GROUPING function initialization for grouping sets [#47370](https://github.com/ClickHouse/ClickHouse/pull/47370) ([Dmitry Novik](https://github.com/novikd)). +* Add join_algorithm='grace_hash' to stress tests [#47372](https://github.com/ClickHouse/ClickHouse/pull/47372) ([Pradeep Chhetri](https://github.com/chhetripradeep)). +* Fix 02343_group_by_use_nulls test in new analyzer [#47373](https://github.com/ClickHouse/ClickHouse/pull/47373) ([Dmitry Novik](https://github.com/novikd)). +* Disable 02368_cancel_write_into_hdfs in stress tests [#47382](https://github.com/ClickHouse/ClickHouse/pull/47382) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Analyzer planner fixes before enable by default [#47383](https://github.com/ClickHouse/ClickHouse/pull/47383) ([Maksim Kita](https://github.com/kitaisreal)). +* Fix `ALTER CLEAR COLUMN` with sparse columns [#47384](https://github.com/ClickHouse/ClickHouse/pull/47384) ([Anton Popov](https://github.com/CurtizJ)). +* Fix: apply reading in order for distinct [#47385](https://github.com/ClickHouse/ClickHouse/pull/47385) ([Igor Nikonov](https://github.com/devcrafter)). +* add checks for ptr [#47398](https://github.com/ClickHouse/ClickHouse/pull/47398) ([Sema Checherinda](https://github.com/CheSema)). +* Remove distinct on top of MergingAggregatedStep [#47399](https://github.com/ClickHouse/ClickHouse/pull/47399) ([Igor Nikonov](https://github.com/devcrafter)). +* Update LRUFileCachePriority.cpp [#47411](https://github.com/ClickHouse/ClickHouse/pull/47411) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Make test 02473_optimize_old_parts less flaky [#47416](https://github.com/ClickHouse/ClickHouse/pull/47416) ([Michael Kolupaev](https://github.com/al13n321)). +* Add test to prevent regressions when using bitmapHasAny [#47419](https://github.com/ClickHouse/ClickHouse/pull/47419) ([Jordi Villar](https://github.com/jrdi)). +* Update README.md [#47421](https://github.com/ClickHouse/ClickHouse/pull/47421) ([Tyler Hannan](https://github.com/tylerhannan)). +* Refactor query cache (make use of CacheBase) [#47428](https://github.com/ClickHouse/ClickHouse/pull/47428) ([Robert Schulze](https://github.com/rschu1ze)). +* Suppress Hung Check with UBsan [#47429](https://github.com/ClickHouse/ClickHouse/pull/47429) ([Alexander Tokmakov](https://github.com/tavplubix)). +* [docs] Document add async_insert_max_query_number [#47431](https://github.com/ClickHouse/ClickHouse/pull/47431) ([Antonio Bonuccelli](https://github.com/nellicus)). +* Apply settings for EXPLAIN earlier (in the same way we do for SELECT). [#47433](https://github.com/ClickHouse/ClickHouse/pull/47433) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Update version_date.tsv and changelogs after v23.2.4.12-stable [#47448](https://github.com/ClickHouse/ClickHouse/pull/47448) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Fix aggregation-in-order with aliases. [#47449](https://github.com/ClickHouse/ClickHouse/pull/47449) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Fix 01429_join_on_error_messages [#47450](https://github.com/ClickHouse/ClickHouse/pull/47450) ([Vladimir C](https://github.com/vdimir)). +* Update version_date.tsv and changelogs after v23.1.5.24-stable [#47452](https://github.com/ClickHouse/ClickHouse/pull/47452) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Update version_date.tsv and changelogs after v22.12.5.34-stable [#47453](https://github.com/ClickHouse/ClickHouse/pull/47453) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Better error messages in ReplicatedMergeTreeAttachThread [#47454](https://github.com/ClickHouse/ClickHouse/pull/47454) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Update version_date.tsv and changelogs after v22.8.15.23-lts [#47455](https://github.com/ClickHouse/ClickHouse/pull/47455) ([robot-clickhouse](https://github.com/robot-clickhouse)). +* Disable grace hash join in upgrade check [#47474](https://github.com/ClickHouse/ClickHouse/pull/47474) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Revert [#46622](https://github.com/ClickHouse/ClickHouse/issues/46622) (test_async_insert_memory) [#47476](https://github.com/ClickHouse/ClickHouse/pull/47476) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix `00933_test_fix_extra_seek_on_compressed_cache` in releases. [#47490](https://github.com/ClickHouse/ClickHouse/pull/47490) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Fix long test `02371_select_projection_normal_agg.sql` [#47491](https://github.com/ClickHouse/ClickHouse/pull/47491) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Revert [#45878](https://github.com/ClickHouse/ClickHouse/issues/45878) and add a test [#47492](https://github.com/ClickHouse/ClickHouse/pull/47492) ([Kruglov Pavel](https://github.com/Avogar)). +* Planner JOIN TREE build fix [#47498](https://github.com/ClickHouse/ClickHouse/pull/47498) ([Maksim Kita](https://github.com/kitaisreal)). +* Better support of identifiers from compound expressions in analyzer [#47506](https://github.com/ClickHouse/ClickHouse/pull/47506) ([Anton Popov](https://github.com/CurtizJ)). +* Adapt some tests to pass with and without the analyzer [#47525](https://github.com/ClickHouse/ClickHouse/pull/47525) ([Raúl Marín](https://github.com/Algunenano)). +* Small enhancements [#47534](https://github.com/ClickHouse/ClickHouse/pull/47534) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). +* Support constants in INTERPOLATE clause (new analyzer) [#47539](https://github.com/ClickHouse/ClickHouse/pull/47539) ([Dmitry Novik](https://github.com/novikd)). +* Remove TOTALS handling in FillingTransform [#47542](https://github.com/ClickHouse/ClickHouse/pull/47542) ([Igor Nikonov](https://github.com/devcrafter)). +* Hide too noisy log messages, fix some tests [#47547](https://github.com/ClickHouse/ClickHouse/pull/47547) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix some flaky tests [#47553](https://github.com/ClickHouse/ClickHouse/pull/47553) ([Azat Khuzhin](https://github.com/azat)). +* remove counters for threads, fix negative counters [#47564](https://github.com/ClickHouse/ClickHouse/pull/47564) ([Sema Checherinda](https://github.com/CheSema)). +* Fix typo [#47565](https://github.com/ClickHouse/ClickHouse/pull/47565) ([hq1](https://github.com/aerosol)). +* Fixes for upgrade check [#47570](https://github.com/ClickHouse/ClickHouse/pull/47570) ([Azat Khuzhin](https://github.com/azat)). +* Change error code in case of columns definitions was empty in ODBC [#47573](https://github.com/ClickHouse/ClickHouse/pull/47573) ([Azat Khuzhin](https://github.com/azat)). +* Add missing SYSTEM FLUSH LOGS for log messages statistics [#47575](https://github.com/ClickHouse/ClickHouse/pull/47575) ([Azat Khuzhin](https://github.com/azat)). +* Fix performance regression in aggregation [#47582](https://github.com/ClickHouse/ClickHouse/pull/47582) ([Anton Popov](https://github.com/CurtizJ)). +* ReadFromMergeTree explain prewhere and row policy actions [#47583](https://github.com/ClickHouse/ClickHouse/pull/47583) ([Maksim Kita](https://github.com/kitaisreal)). +* Fix possible failures of 01300_client_save_history_when_terminated_long [#47606](https://github.com/ClickHouse/ClickHouse/pull/47606) ([Azat Khuzhin](https://github.com/azat)). +* checksum: do not check inverted index files [#47607](https://github.com/ClickHouse/ClickHouse/pull/47607) ([save-my-heart](https://github.com/save-my-heart)). +* Add sanity checks for writing number in variable length format [#47608](https://github.com/ClickHouse/ClickHouse/pull/47608) ([Azat Khuzhin](https://github.com/azat)). +* Analyzer planner fixes before enable by default [#47622](https://github.com/ClickHouse/ClickHouse/pull/47622) ([Maksim Kita](https://github.com/kitaisreal)). +* Fix exception message in clickhouse-test [#47625](https://github.com/ClickHouse/ClickHouse/pull/47625) ([Nikolay Degterinsky](https://github.com/evillique)). +* FillingTransform: remove unnecessary indirection when accessing columns [#47632](https://github.com/ClickHouse/ClickHouse/pull/47632) ([Igor Nikonov](https://github.com/devcrafter)). +* fix typo in HashJoin insertion that enables debug code in release build [#46726](https://github.com/ClickHouse/ClickHouse/issues/46726) [#47647](https://github.com/ClickHouse/ClickHouse/pull/47647) ([jorisgio](https://github.com/jorisgio)). +* clang-tidy >= 15: write CheckOptions in dictionary format [#47648](https://github.com/ClickHouse/ClickHouse/pull/47648) ([Robert Schulze](https://github.com/rschu1ze)). +* CMake: Build ClickHouse w/o GNU extensions [#47651](https://github.com/ClickHouse/ClickHouse/pull/47651) ([Robert Schulze](https://github.com/rschu1ze)). +* Faster fasttest [#47654](https://github.com/ClickHouse/ClickHouse/pull/47654) ([Robert Schulze](https://github.com/rschu1ze)). +* Add background pools size metrics [#47656](https://github.com/ClickHouse/ClickHouse/pull/47656) ([Sergei Trifonov](https://github.com/serxa)). +* Improve ThreadPool [#47657](https://github.com/ClickHouse/ClickHouse/pull/47657) ([Vitaly Baranov](https://github.com/vitlibar)). +* cmake: remove support for gold linker [#47660](https://github.com/ClickHouse/ClickHouse/pull/47660) ([Robert Schulze](https://github.com/rschu1ze)). +* Updated events and recordings [#47668](https://github.com/ClickHouse/ClickHouse/pull/47668) ([clickhouse-adrianfraguela](https://github.com/clickhouse-adrianfraguela)). +* Follow-up to [#47660](https://github.com/ClickHouse/ClickHouse/issues/47660): Further removal of gold linker support [#47669](https://github.com/ClickHouse/ClickHouse/pull/47669) ([Robert Schulze](https://github.com/rschu1ze)). +* Enable parallel execution for two tests [#47670](https://github.com/ClickHouse/ClickHouse/pull/47670) ([Robert Schulze](https://github.com/rschu1ze)). +* Restore native macos build [#47673](https://github.com/ClickHouse/ClickHouse/pull/47673) ([Robert Schulze](https://github.com/rschu1ze)). +* CMake: Remove further cruft from build [#47680](https://github.com/ClickHouse/ClickHouse/pull/47680) ([Robert Schulze](https://github.com/rschu1ze)). +* fix test / remove hardcoded database [#47682](https://github.com/ClickHouse/ClickHouse/pull/47682) ([Denny Crane](https://github.com/den-crane)). +* Apply log_queries_cut_to_length in MergeTreeWhereOptimizer [#47684](https://github.com/ClickHouse/ClickHouse/pull/47684) ([Vladimir C](https://github.com/vdimir)). +* Fix logical error in evaluate constant expression [#47685](https://github.com/ClickHouse/ClickHouse/pull/47685) ([Vladimir C](https://github.com/vdimir)). +* Try making `test_keeper_mntr_data_size` less flaky [#47687](https://github.com/ClickHouse/ClickHouse/pull/47687) ([Antonio Andelic](https://github.com/antonio2368)). +* Fix limit offset [#47688](https://github.com/ClickHouse/ClickHouse/pull/47688) ([flynn](https://github.com/ucasfl)). +* Fix startup on older systemd versions [#47689](https://github.com/ClickHouse/ClickHouse/pull/47689) ([Thomas Casteleyn](https://github.com/Hipska)). +* More random query id in tests [#47700](https://github.com/ClickHouse/ClickHouse/pull/47700) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Add a style check for unsafe code [#47703](https://github.com/ClickHouse/ClickHouse/pull/47703) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Make the code in Join less disgusting [#47712](https://github.com/ClickHouse/ClickHouse/pull/47712) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Fixup git reference to LLVM [#47719](https://github.com/ClickHouse/ClickHouse/pull/47719) ([Robert Schulze](https://github.com/rschu1ze)). +* Preparation for libcxx(abi), llvm, clang-tidy 16 [#47722](https://github.com/ClickHouse/ClickHouse/pull/47722) ([Robert Schulze](https://github.com/rschu1ze)). +* Rename cfg parameter query_cache.size to query_cache.max_size [#47724](https://github.com/ClickHouse/ClickHouse/pull/47724) ([Robert Schulze](https://github.com/rschu1ze)). +* Add optimization for MemoryStorageStep [#47726](https://github.com/ClickHouse/ClickHouse/pull/47726) ([Konstantin Morozov](https://github.com/k-morozov)). +* Fix aggregation with constant key in planner [#47727](https://github.com/ClickHouse/ClickHouse/pull/47727) ([Dmitry Novik](https://github.com/novikd)). +* Disable setting in 02343_group_by_use_nulls_distributed (for new analyzer) [#47728](https://github.com/ClickHouse/ClickHouse/pull/47728) ([Dmitry Novik](https://github.com/novikd)). +* Add a test for [#21469](https://github.com/ClickHouse/ClickHouse/issues/21469) [#47736](https://github.com/ClickHouse/ClickHouse/pull/47736) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Add a test for [#23804](https://github.com/ClickHouse/ClickHouse/issues/23804) [#47737](https://github.com/ClickHouse/ClickHouse/pull/47737) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Add a test for [#18937](https://github.com/ClickHouse/ClickHouse/issues/18937) [#47738](https://github.com/ClickHouse/ClickHouse/pull/47738) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Add a test for [#17756](https://github.com/ClickHouse/ClickHouse/issues/17756) [#47739](https://github.com/ClickHouse/ClickHouse/pull/47739) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Add a test for [#23162](https://github.com/ClickHouse/ClickHouse/issues/23162) [#47740](https://github.com/ClickHouse/ClickHouse/pull/47740) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* remove unused code [#47743](https://github.com/ClickHouse/ClickHouse/pull/47743) ([flynn](https://github.com/ucasfl)). +* Fix broken cross-compiled macos builds [#47744](https://github.com/ClickHouse/ClickHouse/pull/47744) ([Robert Schulze](https://github.com/rschu1ze)). +* Randomize query cache settings [#47749](https://github.com/ClickHouse/ClickHouse/pull/47749) ([Robert Schulze](https://github.com/rschu1ze)). +* Clarify steps for reopened cherry-pick PRs [#47755](https://github.com/ClickHouse/ClickHouse/pull/47755) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix ZK exception error message [#47757](https://github.com/ClickHouse/ClickHouse/pull/47757) ([Raúl Marín](https://github.com/Algunenano)). +* Add ComparisonTupleEliminationVisitor [#47758](https://github.com/ClickHouse/ClickHouse/pull/47758) ([Vladimir C](https://github.com/vdimir)). +* Add a fuse for backport branches w/o a created PR [#47760](https://github.com/ClickHouse/ClickHouse/pull/47760) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix partition ID byte order for s390x [#47769](https://github.com/ClickHouse/ClickHouse/pull/47769) ([Harry Lee](https://github.com/HarryLeeIBM)). +* Stop `wait for quorum` retries on shutdown [#47770](https://github.com/ClickHouse/ClickHouse/pull/47770) ([Igor Nikonov](https://github.com/devcrafter)). +* More preparation for upgrade to libcxx(abi), llvm, clang-tidy 16 [#47771](https://github.com/ClickHouse/ClickHouse/pull/47771) ([Robert Schulze](https://github.com/rschu1ze)). +* Only valid Reviews.STATES overwrite existing reviews [#47789](https://github.com/ClickHouse/ClickHouse/pull/47789) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Apply black formatter to all python scripts [#47790](https://github.com/ClickHouse/ClickHouse/pull/47790) ([Anton Popov](https://github.com/CurtizJ)). +* Try fix window view test [#47791](https://github.com/ClickHouse/ClickHouse/pull/47791) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Update test for nested lambdas [#47795](https://github.com/ClickHouse/ClickHouse/pull/47795) ([Dmitry Novik](https://github.com/novikd)). +* Decrease scale_down ratio for faster deflation [#47798](https://github.com/ClickHouse/ClickHouse/pull/47798) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix 993 and two other tests [#47802](https://github.com/ClickHouse/ClickHouse/pull/47802) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix flaky test 02417_opentelemetry_insert_on_distributed_table [#47811](https://github.com/ClickHouse/ClickHouse/pull/47811) ([Azat Khuzhin](https://github.com/azat)). +* Make 01086_odbc_roundtrip less flaky [#47820](https://github.com/ClickHouse/ClickHouse/pull/47820) ([Antonio Andelic](https://github.com/antonio2368)). +* Place short return before big block, improve logging [#47822](https://github.com/ClickHouse/ClickHouse/pull/47822) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* [FixTests] Remove wrong chassert() in UserDefinedSQLObjectsLoaderFromZooKeeper.cpp [#47839](https://github.com/ClickHouse/ClickHouse/pull/47839) ([Vitaly Baranov](https://github.com/vitlibar)). +* Fix test test_replicated_merge_tree_encryption_codec [#47851](https://github.com/ClickHouse/ClickHouse/pull/47851) ([Vitaly Baranov](https://github.com/vitlibar)). +* Allow injecting timeout errors on Keeper [#47856](https://github.com/ClickHouse/ClickHouse/pull/47856) ([Raúl Marín](https://github.com/Algunenano)). +* Comment stale cherry-pick PRs once a day to remind for resolving conflicts [#47857](https://github.com/ClickHouse/ClickHouse/pull/47857) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Followup to [#47802](https://github.com/ClickHouse/ClickHouse/issues/47802) [#47864](https://github.com/ClickHouse/ClickHouse/pull/47864) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Slightly better error message [#47868](https://github.com/ClickHouse/ClickHouse/pull/47868) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Make test_server_reload non-parallel [#47871](https://github.com/ClickHouse/ClickHouse/pull/47871) ([Alexander Tokmakov](https://github.com/tavplubix)). +* aspell-dict.txt: keep sorted things sorted [#47878](https://github.com/ClickHouse/ClickHouse/pull/47878) ([Robert Schulze](https://github.com/rschu1ze)). +* throw exception when all retries exhausted [#47902](https://github.com/ClickHouse/ClickHouse/pull/47902) ([Sema Checherinda](https://github.com/CheSema)). +* Fix GRANT query formatting [#47908](https://github.com/ClickHouse/ClickHouse/pull/47908) ([Nikolay Degterinsky](https://github.com/evillique)). +* Fix exception type in arrayElement function [#47909](https://github.com/ClickHouse/ClickHouse/pull/47909) ([Nikolay Degterinsky](https://github.com/evillique)). +* Fix logical error in DistributedSink [#47916](https://github.com/ClickHouse/ClickHouse/pull/47916) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix terminate in parts check thread [#47917](https://github.com/ClickHouse/ClickHouse/pull/47917) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Limit keeper request batching by size in bytes [#47918](https://github.com/ClickHouse/ClickHouse/pull/47918) ([Alexander Gololobov](https://github.com/davenger)). +* Improve replicated user defined functions [#47919](https://github.com/ClickHouse/ClickHouse/pull/47919) ([Vitaly Baranov](https://github.com/vitlibar)). +* Update 01072_window_view_multiple_columns_groupby.sh [#47928](https://github.com/ClickHouse/ClickHouse/pull/47928) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Added test. Closes [#12264](https://github.com/ClickHouse/ClickHouse/issues/12264) [#47931](https://github.com/ClickHouse/ClickHouse/pull/47931) ([Ilya Yatsishin](https://github.com/qoega)). +* Disallow concurrent backup restore test - removed SYSTEM SYNC [#47944](https://github.com/ClickHouse/ClickHouse/pull/47944) ([SmitaRKulkarni](https://github.com/SmitaRKulkarni)). +* Artifacts s3 prefix [#47945](https://github.com/ClickHouse/ClickHouse/pull/47945) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Set content-length for empty POST requests [#47950](https://github.com/ClickHouse/ClickHouse/pull/47950) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix test `02050_client_profile_events` [#47951](https://github.com/ClickHouse/ClickHouse/pull/47951) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)). +* Fix tsan error lock-order-inversion [#47953](https://github.com/ClickHouse/ClickHouse/pull/47953) ([Kruglov Pavel](https://github.com/Avogar)). +* Update docs for parseDateTime() (follow-up to [#46815](https://github.com/ClickHouse/ClickHouse/issues/46815)) [#47959](https://github.com/ClickHouse/ClickHouse/pull/47959) ([Robert Schulze](https://github.com/rschu1ze)). +* Docs: Update secondary index example [#47961](https://github.com/ClickHouse/ClickHouse/pull/47961) ([Robert Schulze](https://github.com/rschu1ze)). +* Fix compilation on MacOS [#47967](https://github.com/ClickHouse/ClickHouse/pull/47967) ([Jordi Villar](https://github.com/jrdi)). +* [Refactoring] Move information about current hosts and list of all hosts to BackupCoordination [#47971](https://github.com/ClickHouse/ClickHouse/pull/47971) ([Vitaly Baranov](https://github.com/vitlibar)). +* Stabilize tests for new function parseDateTimeInJodaSyntax [#47974](https://github.com/ClickHouse/ClickHouse/pull/47974) ([Robert Schulze](https://github.com/rschu1ze)). +* Docs: Fix links [#47976](https://github.com/ClickHouse/ClickHouse/pull/47976) ([Robert Schulze](https://github.com/rschu1ze)). +* Try fix rabbitmq test [#47987](https://github.com/ClickHouse/ClickHouse/pull/47987) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Better type check in arrayElement function [#47989](https://github.com/ClickHouse/ClickHouse/pull/47989) ([Nikolay Degterinsky](https://github.com/evillique)). +* Fix incorrect code indentation [#48011](https://github.com/ClickHouse/ClickHouse/pull/48011) ([exmy](https://github.com/exmy)). +* CMake: Remove configuration of CMAKE_SHARED_LINKER_FLAGS [#48018](https://github.com/ClickHouse/ClickHouse/pull/48018) ([Robert Schulze](https://github.com/rschu1ze)). +* Remove the old changelog script [#48042](https://github.com/ClickHouse/ClickHouse/pull/48042) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Fix automatic indentation in the built-in UI SQL editor [#48045](https://github.com/ClickHouse/ClickHouse/pull/48045) ([Nikolay Degterinsky](https://github.com/evillique)). +* Rename `system.marked_dropped_tables` to `dropped_tables` [#48048](https://github.com/ClickHouse/ClickHouse/pull/48048) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Automatically correct some mistakes in the changelog [#48052](https://github.com/ClickHouse/ClickHouse/pull/48052) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Docs: Document [FULL] keyword in SHOW TABLES [#48061](https://github.com/ClickHouse/ClickHouse/pull/48061) ([Robert Schulze](https://github.com/rschu1ze)). +* Fix stateless tests numbers [#48063](https://github.com/ClickHouse/ClickHouse/pull/48063) ([Raúl Marín](https://github.com/Algunenano)). +* Docs: Update syntax of some SHOW queries [#48064](https://github.com/ClickHouse/ClickHouse/pull/48064) ([Robert Schulze](https://github.com/rschu1ze)). +* Simplify backup coordination for file infos [#48095](https://github.com/ClickHouse/ClickHouse/pull/48095) ([Vitaly Baranov](https://github.com/vitlibar)). +* materialized pg small fix [#48098](https://github.com/ClickHouse/ClickHouse/pull/48098) ([Kseniia Sumarokova](https://github.com/kssenii)). +* Update SQLite to 3.41.2 [#48101](https://github.com/ClickHouse/ClickHouse/pull/48101) ([Nikolay Degterinsky](https://github.com/evillique)). +* Fix test numbers again and enforce it with style [#48106](https://github.com/ClickHouse/ClickHouse/pull/48106) ([Raúl Marín](https://github.com/Algunenano)). +* s390x reinterpret as float64 [#48112](https://github.com/ClickHouse/ClickHouse/pull/48112) ([Suzy Wang](https://github.com/SuzyWangIBMer)). +* Remove slow outdated test [#48114](https://github.com/ClickHouse/ClickHouse/pull/48114) ([alesapin](https://github.com/alesapin)). +* Cosmetic follow-up to [#46252](https://github.com/ClickHouse/ClickHouse/issues/46252) [#48128](https://github.com/ClickHouse/ClickHouse/pull/48128) ([Robert Schulze](https://github.com/rschu1ze)). +* Merging "Support undrop table" [#48130](https://github.com/ClickHouse/ClickHouse/pull/48130) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix double whitespace in exception message [#48132](https://github.com/ClickHouse/ClickHouse/pull/48132) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Improve script for updating clickhouse-docs [#48135](https://github.com/ClickHouse/ClickHouse/pull/48135) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix stdlib compatibility issues [#48150](https://github.com/ClickHouse/ClickHouse/pull/48150) ([DimasKovas](https://github.com/DimasKovas)). +* Make test test_disallow_concurrency less flaky [#48152](https://github.com/ClickHouse/ClickHouse/pull/48152) ([Vitaly Baranov](https://github.com/vitlibar)). +* Remove unused mockSystemDatabase from gtest_transform_query_for_exter… [#48162](https://github.com/ClickHouse/ClickHouse/pull/48162) ([Vladimir C](https://github.com/vdimir)). +* Update environmental-sensors.md [#48166](https://github.com/ClickHouse/ClickHouse/pull/48166) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Correctly handle NULL constants in logical optimizer for new analyzer [#48168](https://github.com/ClickHouse/ClickHouse/pull/48168) ([Antonio Andelic](https://github.com/antonio2368)). +* Try making KeeperMap test more stable [#48170](https://github.com/ClickHouse/ClickHouse/pull/48170) ([Antonio Andelic](https://github.com/antonio2368)). +* Deprecate EXPLAIN QUERY TREE with disabled analyzer. [#48177](https://github.com/ClickHouse/ClickHouse/pull/48177) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Use uniq file names in 02149_* tests to avoid SIGBUS in stress tests [#48187](https://github.com/ClickHouse/ClickHouse/pull/48187) ([Kruglov Pavel](https://github.com/Avogar)). +* Update style in ParserKQLSort.cpp [#48199](https://github.com/ClickHouse/ClickHouse/pull/48199) ([Ilya Yatsishin](https://github.com/qoega)). +* Remove support for std::unary/binary_function (removed in C++17) [#48204](https://github.com/ClickHouse/ClickHouse/pull/48204) ([Robert Schulze](https://github.com/rschu1ze)). +* Remove unused setting [#48208](https://github.com/ClickHouse/ClickHouse/pull/48208) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Remove wrong assert from LogicalExpressionOptimizerPass [#48214](https://github.com/ClickHouse/ClickHouse/pull/48214) ([Antonio Andelic](https://github.com/antonio2368)). +* MySQL compatibility: Make str_to_date alias case-insensitive [#48220](https://github.com/ClickHouse/ClickHouse/pull/48220) ([Robert Schulze](https://github.com/rschu1ze)). +* Disable AST optimizations for projection analysis. [#48221](https://github.com/ClickHouse/ClickHouse/pull/48221) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Fix Too big of a difference between test numbers [#48224](https://github.com/ClickHouse/ClickHouse/pull/48224) ([Vladimir C](https://github.com/vdimir)). +* Stabilize 02477_age [#48225](https://github.com/ClickHouse/ClickHouse/pull/48225) ([Robert Schulze](https://github.com/rschu1ze)). +* Rename setting stop_reading_on_first_cancel [#48226](https://github.com/ClickHouse/ClickHouse/pull/48226) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Address flaky 02346_full_text_search [#48227](https://github.com/ClickHouse/ClickHouse/pull/48227) ([Robert Schulze](https://github.com/rschu1ze)). +* Fix incorrect ThreadPool usage after ThreadPool introspection [#48244](https://github.com/ClickHouse/ClickHouse/pull/48244) ([Azat Khuzhin](https://github.com/azat)). +* fix test numbers again [#48264](https://github.com/ClickHouse/ClickHouse/pull/48264) ([Alexander Tokmakov](https://github.com/tavplubix)). + +#### Testing Improvement + +* Fixed functional test 02534_keyed_siphash and 02552_siphash128_reference for s390x. [#47615](https://github.com/ClickHouse/ClickHouse/pull/47615) ([Harry Lee](https://github.com/HarryLeeIBM)). + diff --git a/utils/list-versions/version_date.tsv b/utils/list-versions/version_date.tsv index c2d9781177d..f46a422446e 100644 --- a/utils/list-versions/version_date.tsv +++ b/utils/list-versions/version_date.tsv @@ -1,3 +1,4 @@ +v23.3.1.2823-lts 2023-03-31 v23.2.4.12-stable 2023-03-10 v23.2.3.17-stable 2023-03-06 v23.2.2.20-stable 2023-03-01 From cc61d35bc5eb61f267ca67a30e2ef159228bb5fb Mon Sep 17 00:00:00 2001 From: vdimir Date: Tue, 28 Mar 2023 17:57:58 +0000 Subject: [PATCH 290/377] Fix overflow in sparkbar function --- .../AggregateFunctionSparkbar.h | 30 ++++++++++++++++--- .../02016_aggregation_spark_bar.sql | 8 ++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionSparkbar.h b/src/AggregateFunctions/AggregateFunctionSparkbar.h index 919b59448a1..0927ff8994d 100644 --- a/src/AggregateFunctions/AggregateFunctionSparkbar.h +++ b/src/AggregateFunctions/AggregateFunctionSparkbar.h @@ -43,7 +43,16 @@ struct AggregateFunctionSparkbarData auto [it, inserted] = points.insert({x, y}); if (!inserted) - it->getMapped() += y; + { + if (std::numeric_limits::max() - it->getMapped() > y) + { + it->getMapped() += y; + } + else + { + it->getMapped() = std::numeric_limits::max(); + } + } return it->getMapped(); } @@ -117,6 +126,7 @@ class AggregateFunctionSparkbar final { private: + static constexpr size_t BAR_LEVELS = 8; const size_t width = 0; /// Range for x specified in parameters. @@ -126,8 +136,8 @@ private: size_t updateFrame(ColumnString::Chars & frame, Y value) const { - static constexpr std::array bars{" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}; - const auto & bar = (isNaN(value) || value < 1 || 8 < value) ? bars[0] : bars[static_cast(value)]; + static constexpr std::array bars{" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}; + const auto & bar = (isNaN(value) || value < 1 || static_cast(BAR_LEVELS) < value) ? bars[0] : bars[static_cast(value)]; frame.insert(bar.begin(), bar.end()); return bar.size(); } @@ -211,10 +221,22 @@ private: for (auto & y : histogram) { + constexpr auto bucket_num = static_cast(BAR_LEVELS - 1); + if (isNaN(y) || y <= 0) + { y = 0; + continue; + } + + /// handle potential overflow + if (y_max > bucket_num && y >= std::numeric_limits::max() / bucket_num) + y = y / (y_max / bucket_num); else - y = y * 7 / y_max + 1; + y = y * bucket_num / y_max; + + if (y < std::numeric_limits::max()) + y += 1; } size_t sz = 0; diff --git a/tests/queries/0_stateless/02016_aggregation_spark_bar.sql b/tests/queries/0_stateless/02016_aggregation_spark_bar.sql index 2100a3dd4a6..4d2de566eda 100644 --- a/tests/queries/0_stateless/02016_aggregation_spark_bar.sql +++ b/tests/queries/0_stateless/02016_aggregation_spark_bar.sql @@ -33,7 +33,7 @@ SELECT sparkbar(4,toDate('2020-01-01'),toDate('2020-01-08'))(event_date,cnt) FRO SELECT sparkbar(5,toDate('2020-01-01'),toDate('2020-01-10'))(event_date,cnt) FROM spark_bar_test; SELECT sparkbar(9,toDate('2020-01-01'),toDate('2020-01-10'))(event_date,cnt) FROM spark_bar_test; -WITH number DIV 50 AS k, number % 50 AS value SELECT k, sparkbar(50, 0, 99)(number, value) FROM numbers(100) GROUP BY k ORDER BY k; +WITH number DIV 50 AS k, toUInt32(number % 50) AS value SELECT k, sparkbar(50, 0, 99)(number, value) FROM numbers(100) GROUP BY k ORDER BY k; SELECT sparkbar(128, 0, 9223372036854775806)(toUInt64(9223372036854775806), number % 65535) FROM numbers(100); SELECT sparkbar(128)(toUInt64(9223372036854775806), number % 65535) FROM numbers(100); @@ -59,4 +59,10 @@ SELECT sparkbar(2)(toInt32(number), number) FROM numbers(10); -- { serverError SELECT sparkbar(2, 0)(number, number) FROM numbers(10); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } SELECT sparkbar(2, 0, 5, 8)(number, number) FROM numbers(10); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +-- it causes overflow, just check that it doesn't crash under UBSan, do not check the result it's not really reasonable +SELECT sparkbar(10)(number, toInt64(number)) FROM numbers(toUInt64(9223372036854775807), 20) FORMAT Null; +SELECT sparkbar(10)(number, -number) FROM numbers(toUInt64(9223372036854775807), 7) FORMAT Null; +SELECT sparkbar(10)(number, number) FROM numbers(18446744073709551615, 7) FORMAT Null; +SELECT sparkbar(16)(number, number) FROM numbers(18446744073709551600, 16) FORMAT Null; + DROP TABLE IF EXISTS spark_bar_test; From def7b19033398e535d5c0cb46efcfb9b102c6ab5 Mon Sep 17 00:00:00 2001 From: vdimir Date: Tue, 28 Mar 2023 18:00:25 +0000 Subject: [PATCH 291/377] Update 02016_aggregation_spark_bar.reference --- .../queries/0_stateless/02016_aggregation_spark_bar.reference | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/02016_aggregation_spark_bar.reference b/tests/queries/0_stateless/02016_aggregation_spark_bar.reference index 534942fc1d5..35a629d2bc0 100644 --- a/tests/queries/0_stateless/02016_aggregation_spark_bar.reference +++ b/tests/queries/0_stateless/02016_aggregation_spark_bar.reference @@ -46,7 +46,7 @@ SELECT sparkbar(5,toDate('2020-01-01'),toDate('2020-01-10'))(event_date,cnt) FRO ▃▄▆█ SELECT sparkbar(9,toDate('2020-01-01'),toDate('2020-01-10'))(event_date,cnt) FROM spark_bar_test; ▂▅▂▃▇▆█ -WITH number DIV 50 AS k, number % 50 AS value SELECT k, sparkbar(50, 0, 99)(number, value) FROM numbers(100) GROUP BY k ORDER BY k; +WITH number DIV 50 AS k, toUInt32(number % 50) AS value SELECT k, sparkbar(50, 0, 99)(number, value) FROM numbers(100) GROUP BY k ORDER BY k; 0 ▁▁▁▁▂▂▂▃▃▃▃▄▄▄▅▅▅▅▆▆▆▇▇▇█ 1 ▁▁▁▁▂▂▂▃▃▃▃▄▄▄▅▅▅▅▆▆▆▇▇▇█ SELECT sparkbar(128, 0, 9223372036854775806)(toUInt64(9223372036854775806), number % 65535) FROM numbers(100); @@ -54,7 +54,7 @@ SELECT sparkbar(128, 0, 9223372036854775806)(toUInt64(9223372036854775806), numb SELECT sparkbar(128)(toUInt64(9223372036854775806), number % 65535) FROM numbers(100); █ SELECT sparkbar(9)(x, y) FROM (SELECT * FROM Values('x UInt64, y UInt8', (18446744073709551615,255), (0,0), (0,0), (4036797895307271799,254))); - ▇ █ + █ █ SELECT sparkbar(8, 0, 7)((number + 1) % 8, 1), sparkbar(8, 0, 7)((number + 2) % 8, 1), sparkbar(8, 0, 7)((number + 3) % 8, 1) FROM numbers(7); ███████ █ ██████ ██ █████ SELECT sparkbar(2)(number, -number) FROM numbers(10); From 24b46774b0e72c67746fe2daf455458c357844e5 Mon Sep 17 00:00:00 2001 From: vdimir Date: Wed, 29 Mar 2023 09:34:42 +0000 Subject: [PATCH 292/377] update AggregateFunctionSparkbar.h --- .../AggregateFunctionSparkbar.h | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionSparkbar.h b/src/AggregateFunctions/AggregateFunctionSparkbar.h index 0927ff8994d..29fb65e8fff 100644 --- a/src/AggregateFunctions/AggregateFunctionSparkbar.h +++ b/src/AggregateFunctions/AggregateFunctionSparkbar.h @@ -171,7 +171,7 @@ private: } PaddedPODArray histogram(width, 0); - PaddedPODArray fhistogram(width, 0); + PaddedPODArray count_histogram(width, 0); /// The number of points in each bucket for (const auto & point : data.points) { @@ -189,7 +189,7 @@ private: if (std::numeric_limits::max() - histogram[index] > point.getMapped()) { histogram[index] += point.getMapped(); - fhistogram[index] += 1; + count_histogram[index] += 1; } else { @@ -200,8 +200,8 @@ private: for (size_t i = 0; i < histogram.size(); ++i) { - if (fhistogram[i] > 0) - histogram[i] /= fhistogram[i]; + if (count_histogram[i] > 0) + histogram[i] /= count_histogram[i]; } Y y_max = 0; @@ -219,24 +219,21 @@ private: return; } + /// Scale the histogram to the range [0, BAR_LEVELS] for (auto & y : histogram) { - constexpr auto bucket_num = static_cast(BAR_LEVELS - 1); - if (isNaN(y) || y <= 0) { y = 0; continue; } + constexpr auto levels_num = static_cast(BAR_LEVELS - 1); /// handle potential overflow - if (y_max > bucket_num && y >= std::numeric_limits::max() / bucket_num) - y = y / (y_max / bucket_num); + if (y_max > levels_num && y >= std::numeric_limits::max() / levels_num) + y = y / (y_max / levels_num) + 1; else - y = y * bucket_num / y_max; - - if (y < std::numeric_limits::max()) - y += 1; + y = y * levels_num / y_max + 1; } size_t sz = 0; From f54fd15112de580ce0330173597e22128dc05f5b Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 12:57:29 +0200 Subject: [PATCH 293/377] fix --- src/Databases/DatabaseReplicated.cpp | 2 +- src/Interpreters/DDLWorker.cpp | 1 - src/Interpreters/DDLWorker.h | 1 - src/Interpreters/executeDDLQueryOnCluster.cpp | 47 +++++++++---------- src/Interpreters/executeDDLQueryOnCluster.h | 3 +- tests/integration/helpers/client.py | 1 + 6 files changed, 25 insertions(+), 30 deletions(-) diff --git a/src/Databases/DatabaseReplicated.cpp b/src/Databases/DatabaseReplicated.cpp index 1d39796e4fc..76eea059174 100644 --- a/src/Databases/DatabaseReplicated.cpp +++ b/src/Databases/DatabaseReplicated.cpp @@ -661,7 +661,7 @@ BlockIO DatabaseReplicated::tryEnqueueReplicatedDDL(const ASTPtr & query, Contex String node_path = ddl_worker->tryEnqueueAndExecuteEntry(entry, query_context); Strings hosts_to_wait = getZooKeeper()->getChildren(zookeeper_path + "/replicas"); - return getDistributedDDLStatus(node_path, entry, query_context, hosts_to_wait); + return getDistributedDDLStatus(node_path, entry, query_context, &hosts_to_wait); } static UUID getTableUUIDIfReplicated(const String & metadata, ContextPtr context) diff --git a/src/Interpreters/DDLWorker.cpp b/src/Interpreters/DDLWorker.cpp index 5389d11a2ce..22bece0ef04 100644 --- a/src/Interpreters/DDLWorker.cpp +++ b/src/Interpreters/DDLWorker.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/src/Interpreters/DDLWorker.h b/src/Interpreters/DDLWorker.h index 62ca6cba5e8..6cf034edae8 100644 --- a/src/Interpreters/DDLWorker.h +++ b/src/Interpreters/DDLWorker.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include diff --git a/src/Interpreters/executeDDLQueryOnCluster.cpp b/src/Interpreters/executeDDLQueryOnCluster.cpp index a93cc2f06ba..5b8609fd8af 100644 --- a/src/Interpreters/executeDDLQueryOnCluster.cpp +++ b/src/Interpreters/executeDDLQueryOnCluster.cpp @@ -37,25 +37,16 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } -struct RetriesForDDL -{ - ZooKeeperRetriesInfo info; - ZooKeeperRetriesControl ctl; -}; - -static RetriesForDDL getRetriesForDistributedDDL() +static ZooKeeperRetriesInfo getRetriesInfo() { const auto & config_ref = Context::getGlobalContextInstance()->getConfigRef(); - auto info = ZooKeeperRetriesInfo( + return ZooKeeperRetriesInfo( "DistributedDDL", &Poco::Logger::get("DDLQueryStatusSource"), config_ref.getInt("distributed_ddl_keeper_max_retries", 5), config_ref.getInt("distributed_ddl_keeper_initial_backoff_ms", 100), config_ref.getInt("distributed_ddl_keeper_max_backoff_ms", 5000) - ); - - auto ctl = ZooKeeperRetriesControl("executeDDLQueryOnCluster", info); - return {info, ctl}; + ); } bool isSupportedAlterType(int type) @@ -195,7 +186,7 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, ContextPtr context, entry.tracing_context = OpenTelemetry::CurrentContext(); String node_path = ddl_worker.enqueueQuery(entry); - return getDistributedDDLStatus(node_path, entry, context, /* hosts_to_wait */ std::nullopt); + return getDistributedDDLStatus(node_path, entry, context, /* hosts_to_wait */ nullptr); } @@ -203,7 +194,7 @@ class DDLQueryStatusSource final : public ISource { public: DDLQueryStatusSource( - const String & zk_node_path, const DDLLogEntry & entry, ContextPtr context_, const std::optional & hosts_to_wait); + const String & zk_node_path, const DDLLogEntry & entry, ContextPtr context_, const Strings * hosts_to_wait); String getName() const override { return "DDLQueryStatus"; } Chunk generate() override; @@ -251,8 +242,7 @@ private: }; -BlockIO getDistributedDDLStatus(const String & node_path, const DDLLogEntry & entry, ContextPtr context, - const std::optional & hosts_to_wait) +BlockIO getDistributedDDLStatus(const String & node_path, const DDLLogEntry & entry, ContextPtr context, const Strings * hosts_to_wait) { BlockIO io; if (context->getSettingsRef().distributed_ddl_task_timeout == 0) @@ -313,8 +303,8 @@ Block DDLQueryStatusSource::getSampleBlock(ContextPtr context_, bool hosts_to_wa } DDLQueryStatusSource::DDLQueryStatusSource( - const String & zk_node_path, const DDLLogEntry & entry, ContextPtr context_, const std::optional & hosts_to_wait) - : ISource(getSampleBlock(context_, hosts_to_wait.has_value())) + const String & zk_node_path, const DDLLogEntry & entry, ContextPtr context_, const Strings * hosts_to_wait) + : ISource(getSampleBlock(context_, static_cast(hosts_to_wait))) , node_path(zk_node_path) , context(context_) , watch(CLOCK_MONOTONIC_COARSE) @@ -445,13 +435,17 @@ Chunk DDLQueryStatusSource::generate() Strings tmp_hosts; Strings tmp_active_hosts; - getRetriesForDistributedDDL().ctl.retryLoop([&]() { - auto zookeeper = context->getZooKeeper(); - node_exists = zookeeper->exists(node_path); - tmp_hosts = getChildrenAllowNoNode(zookeeper, fs::path(node_path) / node_to_wait); - tmp_active_hosts = getChildrenAllowNoNode(zookeeper, fs::path(node_path) / "active"); - }); + auto retries_info = getRetriesInfo(); + auto retries_ctl = ZooKeeperRetriesControl("executeDDLQueryOnCluster", retries_info); + retries_ctl.retryLoop([&]() + { + auto zookeeper = context->getZooKeeper(); + node_exists = zookeeper->exists(node_path); + tmp_hosts = getChildrenAllowNoNode(zookeeper, fs::path(node_path) / node_to_wait); + tmp_active_hosts = getChildrenAllowNoNode(zookeeper, fs::path(node_path) / "active"); + }); + } if (!node_exists) { @@ -481,7 +475,10 @@ Chunk DDLQueryStatusSource::generate() { String status_data; bool finished_exists = false; - getRetriesForDistributedDDL().ctl.retryLoop([&]() + + auto retries_info = getRetriesInfo(); + auto retries_ctl = ZooKeeperRetriesControl("executeDDLQueryOnCluster", retries_info); + retries_ctl.retryLoop([&]() { finished_exists = context->getZooKeeper()->tryGet(fs::path(node_path) / "finished" / host_id, status_data); }); diff --git a/src/Interpreters/executeDDLQueryOnCluster.h b/src/Interpreters/executeDDLQueryOnCluster.h index 19fb3fdb5a6..40db13d7ef5 100644 --- a/src/Interpreters/executeDDLQueryOnCluster.h +++ b/src/Interpreters/executeDDLQueryOnCluster.h @@ -43,8 +43,7 @@ struct DDLQueryOnClusterParams /// Returns DDLQueryStatusSource, which reads results of query execution on each host in the cluster. BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, ContextPtr context, const DDLQueryOnClusterParams & params = {}); -BlockIO getDistributedDDLStatus( - const String & node_path, const DDLLogEntry & entry, ContextPtr context, const std::optional & hosts_to_wait); +BlockIO getDistributedDDLStatus(const String & node_path, const DDLLogEntry & entry, ContextPtr context, const Strings * hosts_to_wait); bool maybeRemoveOnCluster(const ASTPtr & query_ptr, ContextPtr context); diff --git a/tests/integration/helpers/client.py b/tests/integration/helpers/client.py index 66e0f0bc907..c2676ac08a6 100644 --- a/tests/integration/helpers/client.py +++ b/tests/integration/helpers/client.py @@ -203,6 +203,7 @@ class CommandRequest: self.timer.start() def remove_trash_from_stderr(self, stderr): + # FIXME https://github.com/ClickHouse/ClickHouse/issues/48181 if not stderr: return stderr lines = stderr.split("\n") From 2d18689af67203c6512c651c474199824a4db092 Mon Sep 17 00:00:00 2001 From: vdimir Date: Fri, 31 Mar 2023 11:52:33 +0000 Subject: [PATCH 294/377] use common::addOverflow in AggregateFunctionSparkbar.h --- .../AggregateFunctionSparkbar.h | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionSparkbar.h b/src/AggregateFunctions/AggregateFunctionSparkbar.h index 29fb65e8fff..adbcea224d5 100644 --- a/src/AggregateFunctions/AggregateFunctionSparkbar.h +++ b/src/AggregateFunctions/AggregateFunctionSparkbar.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -44,14 +46,9 @@ struct AggregateFunctionSparkbarData auto [it, inserted] = points.insert({x, y}); if (!inserted) { - if (std::numeric_limits::max() - it->getMapped() > y) - { - it->getMapped() += y; - } - else - { - it->getMapped() = std::numeric_limits::max(); - } + Y res; + bool has_overfllow = common::addOverflow(it->getMapped(), y, res) + it->getMapped() = has_overfllow ? std::numeric_limits::max() : res; } return it->getMapped(); } @@ -186,15 +183,18 @@ private: Float64 w = histogram.size(); size_t index = std::min(static_cast(w / delta * value), histogram.size() - 1); - if (std::numeric_limits::max() - histogram[index] > point.getMapped()) + Y res; + bool has_overfllow = common::addOverflow(histogram[index], point.getMapped(), res); + if (unlikely(has_overfllow)) { - histogram[index] += point.getMapped(); - count_histogram[index] += 1; + /// In case of overflow, just saturate + /// Do not count new values, because we do not know how many of them were added + histogram[index] = std::numeric_limits::max(); } else { - /// In case of overflow, just saturate - histogram[index] = std::numeric_limits::max(); + histogram[index] = res; + count_histogram[index] += 1; } } @@ -230,10 +230,12 @@ private: constexpr auto levels_num = static_cast(BAR_LEVELS - 1); /// handle potential overflow - if (y_max > levels_num && y >= std::numeric_limits::max() / levels_num) + Y scaled; + bool has_overfllow = common::mulOverflow(y, levels_num, scaled); + if (has_overfllow) y = y / (y_max / levels_num) + 1; else - y = y * levels_num / y_max + 1; + y = scaled / y_max + 1; } size_t sz = 0; From aa8e5a107772b403cb3469e446d00194c448440e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Fri, 31 Mar 2023 14:09:21 +0200 Subject: [PATCH 295/377] Don't use CURRENT_WRITE_BUFFER_IS_EXHAUSTED for expected behaviour --- src/IO/CascadeWriteBuffer.cpp | 7 ++++--- src/IO/CascadeWriteBuffer.h | 2 +- src/IO/MemoryReadWriteBuffer.cpp | 8 +------- src/IO/MemoryReadWriteBuffer.h | 6 ++++++ 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/IO/CascadeWriteBuffer.cpp b/src/IO/CascadeWriteBuffer.cpp index 629cbff90af..f0d98027609 100644 --- a/src/IO/CascadeWriteBuffer.cpp +++ b/src/IO/CascadeWriteBuffer.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace DB @@ -35,9 +36,9 @@ void CascadeWriteBuffer::nextImpl() curr_buffer->position() = position(); curr_buffer->next(); } - catch (const Exception & e) + catch (const MemoryWriteBuffer::CurrentBufferExhausted &) { - if (curr_buffer_num < num_sources && e.code() == ErrorCodes::CURRENT_WRITE_BUFFER_IS_EXHAUSTED) + if (curr_buffer_num < num_sources) { /// TODO: protocol should require set(position(), 0) before Exception @@ -46,7 +47,7 @@ void CascadeWriteBuffer::nextImpl() curr_buffer = setNextBuffer(); } else - throw; + throw Exception(ErrorCodes::CURRENT_WRITE_BUFFER_IS_EXHAUSTED, "MemoryWriteBuffer limit is exhausted"); } set(curr_buffer->position(), curr_buffer->buffer().end() - curr_buffer->position()); diff --git a/src/IO/CascadeWriteBuffer.h b/src/IO/CascadeWriteBuffer.h index ebd4f262aa2..1059c5b8ddb 100644 --- a/src/IO/CascadeWriteBuffer.h +++ b/src/IO/CascadeWriteBuffer.h @@ -16,7 +16,7 @@ namespace ErrorCodes * (lazy_sources contains not pointers themself, but their delayed constructors) * * Firtly, CascadeWriteBuffer redirects data to first buffer of the sequence - * If current WriteBuffer cannot receive data anymore, it throws special exception CURRENT_WRITE_BUFFER_IS_EXHAUSTED in nextImpl() body, + * If current WriteBuffer cannot receive data anymore, it throws special exception MemoryWriteBuffer::CurrentBufferExhausted in nextImpl() body, * CascadeWriteBuffer prepare next buffer and continuously redirects data to it. * If there are no buffers anymore CascadeWriteBuffer throws an exception. * diff --git a/src/IO/MemoryReadWriteBuffer.cpp b/src/IO/MemoryReadWriteBuffer.cpp index 93ce5ce7ce9..d6f89108561 100644 --- a/src/IO/MemoryReadWriteBuffer.cpp +++ b/src/IO/MemoryReadWriteBuffer.cpp @@ -5,12 +5,6 @@ namespace DB { -namespace ErrorCodes -{ - extern const int CURRENT_WRITE_BUFFER_IS_EXHAUSTED; -} - - class ReadBufferFromMemoryWriteBuffer : public ReadBuffer, boost::noncopyable, private Allocator { public: @@ -118,7 +112,7 @@ void MemoryWriteBuffer::addChunk() if (0 == next_chunk_size) { set(position(), 0); - throw Exception(ErrorCodes::CURRENT_WRITE_BUFFER_IS_EXHAUSTED, "MemoryWriteBuffer limit is exhausted"); + throw MemoryWriteBuffer::CurrentBufferExhausted(); } } diff --git a/src/IO/MemoryReadWriteBuffer.h b/src/IO/MemoryReadWriteBuffer.h index bcaf9a9a965..ee128c355c6 100644 --- a/src/IO/MemoryReadWriteBuffer.h +++ b/src/IO/MemoryReadWriteBuffer.h @@ -16,6 +16,12 @@ namespace DB class MemoryWriteBuffer : public WriteBuffer, public IReadableWriteBuffer, boost::noncopyable, private Allocator { public: + /// Special exception to throw when the current WriteBuffer cannot receive data + class CurrentBufferExhausted : public std::exception + { + public: + const char * what() const noexcept override { return "MemoryWriteBuffer limit is exhausted"; } + }; /// Use max_total_size_ = 0 for unlimited storage explicit MemoryWriteBuffer( From 5206d4b9265c280415a7546d6f105bd45ccdca17 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 31 Mar 2023 14:12:55 +0200 Subject: [PATCH 296/377] Randomize compression of marks and indices in tests --- tests/clickhouse-test | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/clickhouse-test b/tests/clickhouse-test index a355c2f8e73..6b7745fd32d 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -589,6 +589,10 @@ class MergeTreeSettingsRandomizer: "merge_max_block_size": lambda: random.randint(1, 8192 * 3), "index_granularity": lambda: random.randint(1, 65536), "min_bytes_for_wide_part": threshold_generator(0.3, 0.3, 0, 1024 * 1024 * 1024), + "compress_marks": lambda: random.randint(0, 1), + "compress_primary_key": lambda: random.randint(0, 1), + "marks_compress_block_size": lambda: random.randint(8000, 100000), + "primary_key_compress_block_size": lambda: random.randint(8000, 100000), } @staticmethod From d158b187035a90b2f0296098a5be90a78649d360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Fri, 31 Mar 2023 14:23:30 +0200 Subject: [PATCH 297/377] Randomize allow_vertical_merges_from_compact_to_wide_parts in tests --- tests/clickhouse-test | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/clickhouse-test b/tests/clickhouse-test index a355c2f8e73..b95d0e60aee 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -582,6 +582,7 @@ class MergeTreeSettingsRandomizer: "vertical_merge_algorithm_min_columns_to_activate": threshold_generator( 0.4, 0.4, 1, 100 ), + "allow_vertical_merges_from_compact_to_wide_parts": lambda: random.randint(0, 1), "min_merge_bytes_to_use_direct_io": threshold_generator( 0.25, 0.25, 1, 10 * 1024 * 1024 * 1024 ), From 7c3c5ad6bba822722c511903362afe74725257d6 Mon Sep 17 00:00:00 2001 From: natasha Date: Fri, 31 Mar 2023 13:34:28 +0100 Subject: [PATCH 298/377] fix the test --- .../0_stateless/01318_alter_add_constraint_format.reference | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/01318_alter_add_constraint_format.reference b/tests/queries/0_stateless/01318_alter_add_constraint_format.reference index 7a3b41536e0..9f58d161539 100644 --- a/tests/queries/0_stateless/01318_alter_add_constraint_format.reference +++ b/tests/queries/0_stateless/01318_alter_add_constraint_format.reference @@ -1,2 +1 @@ -ALTER TABLE replicated_constraints1 - ADD CONSTRAINT IF NOT EXISTS b_constraint CHECK b > 10 +ALTER TABLE replicated_constraints1 ADD CONSTRAINT IF NOT EXISTS b_constraint CHECK b > 10 From bae92ddf4e4898d001b19cc611314872c42d3f21 Mon Sep 17 00:00:00 2001 From: natasha Date: Fri, 31 Mar 2023 13:34:55 +0100 Subject: [PATCH 299/377] fix the code, single line formatter was forgotten --- src/Parsers/ASTAlterQuery.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Parsers/ASTAlterQuery.cpp b/src/Parsers/ASTAlterQuery.cpp index 426b63a9d28..57bdcffdf75 100644 --- a/src/Parsers/ASTAlterQuery.cpp +++ b/src/Parsers/ASTAlterQuery.cpp @@ -612,7 +612,10 @@ void ASTAlterQuery::formatQueryImpl(const FormatSettings & settings, FormatState FormatStateStacked frame_nested = frame; frame_nested.need_parens = false; frame_nested.expression_list_always_start_on_new_line = true; - static_cast(command_list)->formatImplMultiline(settings, state, frame_nested); + frame_nested.expression_list_prepend_whitespace = true; + settings.one_line + ? command_list->formatImpl(settings, state, frame_nested) + : command_list->as().formatImplMultiline(settings, state, frame_nested); } } From 3b5f19394974db3e83ed3d356193867d5ff24293 Mon Sep 17 00:00:00 2001 From: natasha Date: Fri, 31 Mar 2023 13:34:28 +0100 Subject: [PATCH 300/377] fix the test --- .../0_stateless/01318_alter_add_constraint_format.reference | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/01318_alter_add_constraint_format.reference b/tests/queries/0_stateless/01318_alter_add_constraint_format.reference index 7a3b41536e0..9f58d161539 100644 --- a/tests/queries/0_stateless/01318_alter_add_constraint_format.reference +++ b/tests/queries/0_stateless/01318_alter_add_constraint_format.reference @@ -1,2 +1 @@ -ALTER TABLE replicated_constraints1 - ADD CONSTRAINT IF NOT EXISTS b_constraint CHECK b > 10 +ALTER TABLE replicated_constraints1 ADD CONSTRAINT IF NOT EXISTS b_constraint CHECK b > 10 From 6caacd41d4416792823b2c996c66babfafd42f6b Mon Sep 17 00:00:00 2001 From: natasha Date: Fri, 31 Mar 2023 13:34:55 +0100 Subject: [PATCH 301/377] fix the code, single line formatter was forgotten --- src/Parsers/ASTAlterQuery.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Parsers/ASTAlterQuery.cpp b/src/Parsers/ASTAlterQuery.cpp index 426b63a9d28..57bdcffdf75 100644 --- a/src/Parsers/ASTAlterQuery.cpp +++ b/src/Parsers/ASTAlterQuery.cpp @@ -612,7 +612,10 @@ void ASTAlterQuery::formatQueryImpl(const FormatSettings & settings, FormatState FormatStateStacked frame_nested = frame; frame_nested.need_parens = false; frame_nested.expression_list_always_start_on_new_line = true; - static_cast(command_list)->formatImplMultiline(settings, state, frame_nested); + frame_nested.expression_list_prepend_whitespace = true; + settings.one_line + ? command_list->formatImpl(settings, state, frame_nested) + : command_list->as().formatImplMultiline(settings, state, frame_nested); } } From 84c30f2f632977a55b0a10b3adbb2ace37565cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Fri, 31 Mar 2023 14:49:53 +0200 Subject: [PATCH 302/377] I've got no style --- tests/clickhouse-test | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/clickhouse-test b/tests/clickhouse-test index b95d0e60aee..42676d900a5 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -582,7 +582,9 @@ class MergeTreeSettingsRandomizer: "vertical_merge_algorithm_min_columns_to_activate": threshold_generator( 0.4, 0.4, 1, 100 ), - "allow_vertical_merges_from_compact_to_wide_parts": lambda: random.randint(0, 1), + "allow_vertical_merges_from_compact_to_wide_parts": lambda: random.randint( + 0, 1 + ), "min_merge_bytes_to_use_direct_io": threshold_generator( 0.25, 0.25, 1, 10 * 1024 * 1024 * 1024 ), From 644d83653d4a1b520b6e43bf504c47904cf89766 Mon Sep 17 00:00:00 2001 From: natasha Date: Fri, 31 Mar 2023 14:10:16 +0100 Subject: [PATCH 303/377] only set the necessary FormatStateStacked settings --- src/Parsers/ASTAlterQuery.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Parsers/ASTAlterQuery.cpp b/src/Parsers/ASTAlterQuery.cpp index 57bdcffdf75..61e5903fad5 100644 --- a/src/Parsers/ASTAlterQuery.cpp +++ b/src/Parsers/ASTAlterQuery.cpp @@ -611,11 +611,16 @@ void ASTAlterQuery::formatQueryImpl(const FormatSettings & settings, FormatState FormatStateStacked frame_nested = frame; frame_nested.need_parens = false; - frame_nested.expression_list_always_start_on_new_line = true; - frame_nested.expression_list_prepend_whitespace = true; - settings.one_line - ? command_list->formatImpl(settings, state, frame_nested) - : command_list->as().formatImplMultiline(settings, state, frame_nested); + if (settings.one_line) + { + frame_nested.expression_list_prepend_whitespace = true; + command_list->formatImpl(settings, state, frame_nested); + } + else + { + frame_nested.expression_list_always_start_on_new_line = true; + command_list->as().formatImplMultiline(settings, state, frame_nested); + } } } From 2a179aed4952383067544a65b8df8b1e97b7c931 Mon Sep 17 00:00:00 2001 From: vdimir Date: Fri, 31 Mar 2023 13:35:07 +0000 Subject: [PATCH 304/377] Fix usage common::addOverflow for floats in AggregateFunctionSparkbar --- .../AggregateFunctionSparkbar.h | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionSparkbar.h b/src/AggregateFunctions/AggregateFunctionSparkbar.h index adbcea224d5..78f7e9fcefa 100644 --- a/src/AggregateFunctions/AggregateFunctionSparkbar.h +++ b/src/AggregateFunctions/AggregateFunctionSparkbar.h @@ -46,9 +46,17 @@ struct AggregateFunctionSparkbarData auto [it, inserted] = points.insert({x, y}); if (!inserted) { - Y res; - bool has_overfllow = common::addOverflow(it->getMapped(), y, res) - it->getMapped() = has_overfllow ? std::numeric_limits::max() : res; + if constexpr (std::is_floating_point_v) + { + it->getMapped() += y; + return it->getMapped(); + } + else + { + Y res; + bool has_overfllow = common::addOverflow(it->getMapped(), y, res); + it->getMapped() = has_overfllow ? std::numeric_limits::max() : res; + } } return it->getMapped(); } @@ -184,7 +192,12 @@ private: size_t index = std::min(static_cast(w / delta * value), histogram.size() - 1); Y res; - bool has_overfllow = common::addOverflow(histogram[index], point.getMapped(), res); + bool has_overfllow = false; + if constexpr (std::is_floating_point_v) + res = histogram[index] + point.getMapped(); + else + has_overfllow = common::addOverflow(histogram[index], point.getMapped(), res); + if (unlikely(has_overfllow)) { /// In case of overflow, just saturate @@ -229,13 +242,20 @@ private: } constexpr auto levels_num = static_cast(BAR_LEVELS - 1); - /// handle potential overflow - Y scaled; - bool has_overfllow = common::mulOverflow(y, levels_num, scaled); - if (has_overfllow) + if constexpr (std::is_floating_point_v) + { y = y / (y_max / levels_num) + 1; + } else - y = scaled / y_max + 1; + { + Y scaled; + bool has_overfllow = common::mulOverflow(y, levels_num, scaled); + + if (has_overfllow) + y = y / (y_max / levels_num) + 1; + else + y = scaled / y_max + 1; + } } size_t sz = 0; From f715bd95f1c301eeac5b10742c8e0c1d30f7b886 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Fri, 31 Mar 2023 14:08:28 +0000 Subject: [PATCH 305/377] fix writing to StorageS3 --- src/Storages/StorageS3.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index 079289716a2..e24badbfd07 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -880,12 +880,12 @@ public: private: const String format; const Block sample_block; - ContextPtr context; + const ContextPtr context; const CompressionMethod compression_method; - const StorageS3::Configuration & s3_configuration; + const StorageS3::Configuration s3_configuration; const String bucket; const String key; - std::optional format_settings; + const std::optional format_settings; ExpressionActionsPtr partition_by_expr; From 75ffc8451b5576a68bc68977d8ab98000e14eac1 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 16:09:00 +0200 Subject: [PATCH 306/377] fix --- src/Parsers/SyncReplicaMode.h | 3 +- .../MergeTree/ReplicatedMergeTreeQueue.cpp | 39 +++++++++++-------- .../MergeTree/ReplicatedMergeTreeQueue.h | 5 +-- src/Storages/StorageReplicatedMergeTree.cpp | 6 +-- .../02438_sync_replica_lightweight.sql | 7 +++- 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/Parsers/SyncReplicaMode.h b/src/Parsers/SyncReplicaMode.h index a98e1cace50..6b19eb57fdc 100644 --- a/src/Parsers/SyncReplicaMode.h +++ b/src/Parsers/SyncReplicaMode.h @@ -1,8 +1,9 @@ #pragma once +#include namespace DB { -enum class SyncReplicaMode +enum class SyncReplicaMode : uint8_t { DEFAULT, STRICT, diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp index 7cc26c00098..34cdefc99e0 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp @@ -2463,11 +2463,33 @@ String ReplicatedMergeTreeMergePredicate::getCoveringVirtualPart(const String & ReplicatedMergeTreeQueue::SubscriberHandler -ReplicatedMergeTreeQueue::addSubscriber(ReplicatedMergeTreeQueue::SubscriberCallBack && callback) +ReplicatedMergeTreeQueue::addSubscriber(ReplicatedMergeTreeQueue::SubscriberCallBack && callback, + std::unordered_set & out_entry_names, SyncReplicaMode sync_mode) { std::lock_guard lock(state_mutex); std::lock_guard lock_subscribers(subscribers_mutex); + if (sync_mode != SyncReplicaMode::PULL) + { + /// We must get the list of entries to wait atomically with adding the callback + bool lightweight_entries_only = sync_mode == SyncReplicaMode::LIGHTWEIGHT; + static constexpr std::array lightweight_entries = + { + LogEntry::GET_PART, + LogEntry::ATTACH_PART, + LogEntry::DROP_RANGE, + LogEntry::REPLACE_RANGE, + LogEntry::DROP_PART + }; + out_entry_names.reserve(queue.size()); + for (const auto & entry : queue) + { + if (!lightweight_entries_only + || std::find(lightweight_entries.begin(), lightweight_entries.end(), entry->type) != lightweight_entries.end()) + out_entry_names.insert(entry->znode_name); + } + } + auto it = subscribers.emplace(subscribers.end(), std::move(callback)); /// Atomically notify about current size @@ -2476,21 +2498,6 @@ ReplicatedMergeTreeQueue::addSubscriber(ReplicatedMergeTreeQueue::SubscriberCall return SubscriberHandler(it, *this); } -std::unordered_set ReplicatedMergeTreeQueue::getEntryNamesSet(bool lightweight_entries_only) -{ - std::lock_guard lock(state_mutex); - std::unordered_set result; - result.reserve(queue.size()); - for (const auto & entry : queue) - { - bool is_lightweight = entry->type == LogEntry::GET_PART || entry->type == LogEntry::ATTACH_PART - || entry->type == LogEntry::DROP_RANGE || entry->type == LogEntry::REPLACE_RANGE || entry->type == LogEntry::DROP_PART; - if (!lightweight_entries_only || is_lightweight) - result.insert(entry->znode_name); - } - return result; -} - void ReplicatedMergeTreeQueue::notifySubscribersOnPartialShutdown() { size_t queue_size; diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h index 9b29c6cef5b..841c26bb361 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h +++ b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -425,9 +426,7 @@ public: ActionBlocker pull_log_blocker; /// Adds a subscriber - SubscriberHandler addSubscriber(SubscriberCallBack && callback); - - std::unordered_set getEntryNamesSet(bool lightweight_entries_only); + SubscriberHandler addSubscriber(SubscriberCallBack && callback, std::unordered_set & out_entry_names, SyncReplicaMode sync_mode); void notifySubscribersOnPartialShutdown(); diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 1450b08a4fb..cbfe3f8cab2 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -7593,10 +7593,6 @@ bool StorageReplicatedMergeTree::waitForProcessingQueue(UInt64 max_wait_millisec std::unordered_set wait_for_ids; bool was_interrupted = false; - if (sync_mode == SyncReplicaMode::DEFAULT) - wait_for_ids = queue.getEntryNamesSet(/* lightweight_entries_only */ false); - else if (sync_mode == SyncReplicaMode::LIGHTWEIGHT) - wait_for_ids = queue.getEntryNamesSet(/* lightweight_entries_only */ true); Poco::Event target_entry_event; auto callback = [this, &target_entry_event, &wait_for_ids, &was_interrupted, sync_mode] @@ -7624,7 +7620,7 @@ bool StorageReplicatedMergeTree::waitForProcessingQueue(UInt64 max_wait_millisec if (wait_for_ids.empty()) target_entry_event.set(); }; - const auto handler = queue.addSubscriber(std::move(callback)); + const auto handler = queue.addSubscriber(std::move(callback), wait_for_ids, sync_mode); if (!target_entry_event.tryWait(max_wait_milliseconds)) return false; diff --git a/tests/queries/0_stateless/02438_sync_replica_lightweight.sql b/tests/queries/0_stateless/02438_sync_replica_lightweight.sql index 207571da147..1da48d95d9b 100644 --- a/tests/queries/0_stateless/02438_sync_replica_lightweight.sql +++ b/tests/queries/0_stateless/02438_sync_replica_lightweight.sql @@ -7,10 +7,12 @@ create table rmt2 (n int) engine=ReplicatedMergeTree('/test/{database}/02438/', system stop replicated sends rmt1; system stop merges rmt2; +set insert_keeper_fault_injection_probability=0; + insert into rmt1 values (1); insert into rmt1 values (2); system sync replica rmt2 pull; -- does not wait -select type, new_part_name from system.replication_queue where database=currentDatabase() and table='rmt2'; +select type, new_part_name from system.replication_queue where database=currentDatabase() and table='rmt2' order by new_part_name; select 1, n, _part from rmt1 order by n; select 2, n, _part from rmt2 order by n; @@ -20,7 +22,7 @@ optimize table rmt1 final; system start replicated sends rmt1; system sync replica rmt2 lightweight; -- waits for fetches, not merges -select type, new_part_name from system.replication_queue where database=currentDatabase() and table='rmt2'; +select type, new_part_name from system.replication_queue where database=currentDatabase() and table='rmt2' order by new_part_name; select 3, n, _part from rmt1 order by n; select 4, n, _part from rmt2 order by n; @@ -28,6 +30,7 @@ system start merges rmt2; system sync replica rmt2; insert into rmt2 values (3); +system sync replica rmt2 pull; optimize table rmt2 final; system sync replica rmt1 strict; From e1d299640b77c91ec71d41a7a6eea8c7975c761d Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Fri, 31 Mar 2023 13:53:52 +0000 Subject: [PATCH 307/377] Small follow-up to #48017 --- docs/en/sql-reference/statements/show.md | 6 +++--- src/Parsers/ASTShowColumnsQuery.cpp | 9 ++++----- src/Parsers/ParserShowColumnsQuery.cpp | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/en/sql-reference/statements/show.md b/docs/en/sql-reference/statements/show.md index c0ab51ae444..f6d6d51b123 100644 --- a/docs/en/sql-reference/statements/show.md +++ b/docs/en/sql-reference/statements/show.md @@ -219,9 +219,9 @@ The optional keyword `FULL` causes the output to include the collation, comment - key - `PRI` if the column is part of the primary key, `SOR` if the column is part of the sorting key, empty otherwise (String) - default - Default expression of the column if it is of type `ALIAS`, `DEFAULT`, or `MATERIALIZED`, otherwise `NULL`. (Nullable(String)) - extra - Additional information, currently unused (String) -- collation - Collation of the column, always `NULL` because ClickHouse has no per-column collations, only if `FULL` keyword was specified (Nullable(String)) -- comment - Comment on the column, only if `FULL` keyword was specified (String) -- privilege - The privilege you have on this column, currently not available, only if `FULL` keyword was specified (String) +- collation - (only if `FULL` keyword was specified) Collation of the column, always `NULL` because ClickHouse has no per-column collations (Nullable(String)) +- comment - (only if `FULL` keyword was specified) Comment on the column (String) +- privilege - (only if `FULL` keyword was specified) The privilege you have on this column, currently not available (String) **Examples** diff --git a/src/Parsers/ASTShowColumnsQuery.cpp b/src/Parsers/ASTShowColumnsQuery.cpp index d14cbdc9b84..1cb4b651f0a 100644 --- a/src/Parsers/ASTShowColumnsQuery.cpp +++ b/src/Parsers/ASTShowColumnsQuery.cpp @@ -24,15 +24,14 @@ void ASTShowColumnsQuery::formatQueryImpl(const FormatSettings & settings, Forma << "COLUMNS" << (settings.hilite ? hilite_none : ""); - if (from_database.empty()) - settings.ostr << (settings.hilite ? hilite_keyword : "") << " FROM " << (settings.hilite ? hilite_none : "") << backQuoteIfNeed(from_table); - else - settings.ostr << (settings.hilite ? hilite_keyword : "") << " FROM " << (settings.hilite ? hilite_none : "") << backQuoteIfNeed(from_database) << "." << backQuoteIfNeed(from_table); + settings.ostr << (settings.hilite ? hilite_keyword : "") << " FROM " << (settings.hilite ? hilite_none : "") << backQuoteIfNeed(from_table); + if (!from_database.empty()) + settings.ostr << (settings.hilite ? hilite_keyword : "") << " FROM " << (settings.hilite ? hilite_none : "") << backQuoteIfNeed(from_database); if (!like.empty()) settings.ostr << (settings.hilite ? hilite_keyword : "") - << (not_like ? " NOT " : "") + << (not_like ? " NOT" : "") << (case_insensitive_like ? " ILIKE " : " LIKE") << (settings.hilite ? hilite_none : "") << DB::quote << like; diff --git a/src/Parsers/ParserShowColumnsQuery.cpp b/src/Parsers/ParserShowColumnsQuery.cpp index 1db31601437..03e66e4ae0f 100644 --- a/src/Parsers/ParserShowColumnsQuery.cpp +++ b/src/Parsers/ParserShowColumnsQuery.cpp @@ -13,7 +13,7 @@ namespace DB bool ParserShowColumnsQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) { ASTPtr like; - ASTPtr from_db; + ASTPtr from_database; ASTPtr from_table; auto query = std::make_shared(); @@ -43,10 +43,10 @@ bool ParserShowColumnsQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe if (!abbreviated_form) if (ParserKeyword("FROM").ignore(pos, expected) || ParserKeyword("IN").ignore(pos, expected)) - if (!ParserIdentifier().parse(pos, from_db, expected)) + if (!ParserIdentifier().parse(pos, from_database, expected)) return false; - tryGetIdentifierNameInto(from_db, query->from_database); + tryGetIdentifierNameInto(from_database, query->from_database); if (ParserKeyword("NOT").ignore(pos, expected)) query->not_like = true; From 9094772712b71fb80638e28e0e96c7bd5a11d6d0 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Fri, 31 Mar 2023 14:22:10 +0000 Subject: [PATCH 308/377] Replace manual checking by validateFunctionArgumentTypes() --- src/Functions/parseDateTime.cpp | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/src/Functions/parseDateTime.cpp b/src/Functions/parseDateTime.cpp index 785c423f3ea..8f1edc2049b 100644 --- a/src/Functions/parseDateTime.cpp +++ b/src/Functions/parseDateTime.cpp @@ -480,33 +480,15 @@ namespace DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { - if (arguments.size() != 2 && arguments.size() != 3) - throw Exception( - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, - "Number of arguments for function {} doesn't match: passed {}, should be 2 or 3", - getName(), - arguments.size()); + FunctionArgumentDescriptors args{ + {"time", &isString, nullptr, "String"}, + {"format", &isString, nullptr, "String"}, + }; - if (!isString(arguments[0].type)) - throw Exception( - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Illegal type {} of first argument of function {}. Should be String", - arguments[0].type->getName(), - getName()); + if (arguments.size() == 3) + args.emplace_back(FunctionArgumentDescriptor{"timezone", &isString, nullptr, "String"}); - if (!isString(arguments[1].type)) - throw Exception( - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Illegal type {} of second argument of function {}. Should be String", - arguments[0].type->getName(), - getName()); - - if (arguments.size() == 3 && !isString(arguments[2].type)) - throw Exception( - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Illegal type {} of third argument of function {}. Should be String", - arguments[0].type->getName(), - getName()); + validateFunctionArgumentTypes(*this, arguments, args); String time_zone_name = getTimeZone(arguments).getTimeZone(); DataTypePtr date_type = std::make_shared(time_zone_name); From 1e3abc9e84cfd661f30232e29d200b20999a0d6e Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Fri, 31 Mar 2023 13:29:20 +0000 Subject: [PATCH 309/377] Add strict mode for KeeperMap --- .../table-engines/special/keepermap.md | 5 +- src/Core/Settings.h | 1 + src/Interpreters/MutationsInterpreter.cpp | 9 ++ src/Storages/StorageKeeperMap.cpp | 137 +++++++++++++++--- src/Storages/StorageKeeperMap.h | 4 +- .../02706_keeper_map_insert_strict.reference | 3 + .../02706_keeper_map_insert_strict.sql | 20 +++ 7 files changed, 158 insertions(+), 21 deletions(-) create mode 100644 tests/queries/0_stateless/02706_keeper_map_insert_strict.reference create mode 100644 tests/queries/0_stateless/02706_keeper_map_insert_strict.sql diff --git a/docs/en/engines/table-engines/special/keepermap.md b/docs/en/engines/table-engines/special/keepermap.md index 680413039e7..e5c4dea2339 100644 --- a/docs/en/engines/table-engines/special/keepermap.md +++ b/docs/en/engines/table-engines/special/keepermap.md @@ -78,7 +78,8 @@ Of course, it's possible to manually run `CREATE TABLE` with same path on nonrel ### Inserts -When new rows are inserted into `KeeperMap`, if the key already exists, the value will be updated, otherwise new key is created. +When new rows are inserted into `KeeperMap`, if the key does not exist, a new entry for the key is created. +If the key exists, and setting `keeper_map_strict_mode` is set to `true`, an exception is thrown, otherwise, the value for the key is overwritten. Example: @@ -89,6 +90,7 @@ INSERT INTO keeper_map_table VALUES ('some key', 1, 'value', 3.2); ### Deletes Rows can be deleted using `DELETE` query or `TRUNCATE`. +If the key exists, and setting `keeper_map_strict_mode` is set to `true`, fetching and deleting data will succeed only if it can be executed atomically. ```sql DELETE FROM keeper_map_table WHERE key LIKE 'some%' AND v1 > 1; @@ -105,6 +107,7 @@ TRUNCATE TABLE keeper_map_table; ### Updates Values can be updated using `ALTER TABLE` query. Primary key cannot be updated. +If setting `keeper_map_strict_mode` is set to `true`, fetching and updating data will succeed only if it's executed atomically. ```sql ALTER TABLE keeper_map_table UPDATE v1 = v1 * 10 + 2 WHERE key LIKE 'some%' AND v3 > 3.1; diff --git a/src/Core/Settings.h b/src/Core/Settings.h index e9db155fb12..d985f51eec0 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -722,6 +722,7 @@ class IColumn; M(Bool, force_aggregation_in_order, false, "Force use of aggregation in order on remote nodes during distributed aggregation. PLEASE, NEVER CHANGE THIS SETTING VALUE MANUALLY!", IMPORTANT) \ M(UInt64, http_max_request_param_data_size, 10_MiB, "Limit on size of request data used as a query parameter in predefined HTTP requests.", 0) \ M(Bool, allow_experimental_undrop_table_query, false, "Allow to use undrop query to restore dropped table in a limited time", 0) \ + M(Bool, keeper_map_strict_mode, false, "Enforce additional checks during operations on KeeperMap. E.g. throw an exception on an insert for already existing key", 0) \ // End of COMMON_SETTINGS // Please add settings related to formats into the FORMAT_FACTORY_SETTINGS and move obsolete settings to OBSOLETE_SETTINGS. diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 0b52a1a51bc..d1fcc006ffb 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -550,6 +550,12 @@ void MutationsInterpreter::prepare(bool dry_run) if (source.hasLightweightDeleteMask()) all_columns.push_back({LightweightDeleteDescription::FILTER_COLUMN}); + if (return_all_columns) + { + for (const auto & column : source.getStorage()->getVirtuals()) + all_columns.push_back(column); + } + NameSet updated_columns; bool materialize_ttl_recalculate_only = source.materializeTTLRecalculateOnly(); @@ -906,6 +912,8 @@ void MutationsInterpreter::prepareMutationStages(std::vector & prepared_s { auto storage_snapshot = source.getStorageSnapshot(metadata_snapshot, context); auto options = GetColumnsOptions(GetColumnsOptions::AllPhysical).withExtendedObjects(); + if (return_all_columns) + options.withVirtuals(); auto all_columns = storage_snapshot->getColumns(options); /// Add _row_exists column if it is present in the part @@ -1256,6 +1264,7 @@ void MutationsInterpreter::validate() } QueryPlan plan; + initQueryPlan(stages.front(), plan); auto pipeline = addStreamsForLaterStages(stages, plan); } diff --git a/src/Storages/StorageKeeperMap.cpp b/src/Storages/StorageKeeperMap.cpp index f570f132463..58d02372f2a 100644 --- a/src/Storages/StorageKeeperMap.cpp +++ b/src/Storages/StorageKeeperMap.cpp @@ -59,6 +59,8 @@ namespace ErrorCodes namespace { +constexpr std::string_view version_column_name = "_version"; + std::string formattedAST(const ASTPtr & ast) { if (!ast) @@ -77,7 +79,6 @@ void verifyTableId(const StorageID & table_id) table_id.getDatabaseName(), database->getEngineName()); } - } } @@ -86,11 +87,13 @@ class StorageKeeperMapSink : public SinkToStorage { StorageKeeperMap & storage; std::unordered_map new_values; + std::unordered_map versions; size_t primary_key_pos; + ContextPtr context; public: - StorageKeeperMapSink(StorageKeeperMap & storage_, const StorageMetadataPtr & metadata_snapshot) - : SinkToStorage(metadata_snapshot->getSampleBlock()), storage(storage_) + StorageKeeperMapSink(StorageKeeperMap & storage_, Block header, ContextPtr context_) + : SinkToStorage(std::move(header)), storage(storage_), context(std::move(context_)) { auto primary_key = storage.getPrimaryKey(); assert(primary_key.size() == 1); @@ -113,18 +116,36 @@ public: wb_value.restart(); size_t idx = 0; + + int32_t version = -1; for (const auto & elem : block) { + if (elem.name == version_column_name) + { + version = assert_cast &>(*elem.column).getData()[i]; + continue; + } + elem.type->getDefaultSerialization()->serializeBinary(*elem.column, i, idx == primary_key_pos ? wb_key : wb_value, {}); ++idx; } auto key = base64Encode(wb_key.str(), /* url_encoding */ true); + + if (version != -1) + versions[key] = version; + new_values[std::move(key)] = std::move(wb_value.str()); } } void onFinish() override + { + finalize(/*strict*/ context->getSettingsRef().keeper_map_strict_mode); + } + + template + void finalize(bool strict) { auto zookeeper = storage.getClient(); @@ -147,21 +168,39 @@ public: for (const auto & [key, _] : new_values) key_paths.push_back(storage.fullPathForKey(key)); - auto results = zookeeper->exists(key_paths); + zkutil::ZooKeeper::MultiExistsResponse results; + + if constexpr (!for_update) + results = zookeeper->exists(key_paths); Coordination::Requests requests; requests.reserve(key_paths.size()); for (size_t i = 0; i < key_paths.size(); ++i) { auto key = fs::path(key_paths[i]).filename(); - if (results[i].error == Coordination::Error::ZOK) + + if constexpr (for_update) { - requests.push_back(zkutil::makeSetRequest(key_paths[i], new_values[key], -1)); + int32_t version = -1; + if (strict) + version = versions.at(key); + + requests.push_back(zkutil::makeSetRequest(key_paths[i], new_values[key], version)); } else { - requests.push_back(zkutil::makeCreateRequest(key_paths[i], new_values[key], zkutil::CreateMode::Persistent)); - ++new_keys_num; + if (results[i].error == Coordination::Error::ZOK) + { + if (strict) + throw Exception(ErrorCodes::KEEPER_EXCEPTION, "Value for key '{}' already exists", key); + + requests.push_back(zkutil::makeSetRequest(key_paths[i], new_values[key], -1)); + } + else + { + requests.push_back(zkutil::makeCreateRequest(key_paths[i], new_values[key], zkutil::CreateMode::Persistent)); + ++new_keys_num; + } } } @@ -193,6 +232,18 @@ class StorageKeeperMapSource : public ISource KeyContainerIter it; KeyContainerIter end; + bool with_version_column = false; + + static Block getHeader(Block header, bool with_version_column) + { + if (with_version_column) + header.insert( + {DataTypeInt32{}.createColumn(), + std::make_shared(), std::string{version_column_name}}); + + return header; + } + public: StorageKeeperMapSource( const StorageKeeperMap & storage_, @@ -200,8 +251,10 @@ public: size_t max_block_size_, KeyContainerPtr container_, KeyContainerIter begin_, - KeyContainerIter end_) - : ISource(header), storage(storage_), max_block_size(max_block_size_), container(std::move(container_)), it(begin_), end(end_) + KeyContainerIter end_, + bool with_version_column_) + : ISource(getHeader(header, with_version_column_)), storage(storage_), max_block_size(max_block_size_), container(std::move(container_)), it(begin_), end(end_) + , with_version_column(with_version_column_) { } @@ -225,12 +278,12 @@ public: for (auto & raw_key : raw_keys) raw_key = base64Encode(raw_key, /* url_encoding */ true); - return storage.getBySerializedKeys(raw_keys, nullptr); + return storage.getBySerializedKeys(raw_keys, nullptr, with_version_column); } else { size_t elem_num = std::min(max_block_size, static_cast(end - it)); - auto chunk = storage.getBySerializedKeys(std::span{it, it + elem_num}, nullptr); + auto chunk = storage.getBySerializedKeys(std::span{it, it + elem_num}, nullptr, with_version_column); it += elem_num; return chunk; } @@ -426,6 +479,16 @@ Pipe StorageKeeperMap::read( auto primary_key_type = sample_block.getByName(primary_key).type; std::tie(filtered_keys, all_scan) = getFilterKeys(primary_key, primary_key_type, query_info, context_); + bool with_version_column = false; + for (const auto & column : column_names) + { + if (column == version_column_name) + { + with_version_column = true; + break; + } + } + const auto process_keys = [&](KeyContainerPtr keys) -> Pipe { if (keys->empty()) @@ -449,7 +512,7 @@ Pipe StorageKeeperMap::read( using KeyContainer = typename KeyContainerPtr::element_type; pipes.emplace_back(std::make_shared>( - *this, sample_block, max_block_size, keys, keys->begin() + begin, keys->begin() + end)); + *this, sample_block, max_block_size, keys, keys->begin() + begin, keys->begin() + end, with_version_column)); } return Pipe::unitePipes(std::move(pipes)); }; @@ -461,10 +524,10 @@ Pipe StorageKeeperMap::read( return process_keys(std::move(filtered_keys)); } -SinkToStoragePtr StorageKeeperMap::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, ContextPtr /*context*/) +SinkToStoragePtr StorageKeeperMap::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, ContextPtr local_context) { checkTable(); - return std::make_shared(*this, metadata_snapshot); + return std::make_shared(*this, metadata_snapshot->getSampleBlock(), local_context); } void StorageKeeperMap::truncate(const ASTPtr &, const StorageMetadataPtr &, ContextPtr, TableExclusiveLockHolder &) @@ -554,6 +617,12 @@ void StorageKeeperMap::drop() dropTable(client, metadata_drop_lock); } +NamesAndTypesList StorageKeeperMap::getVirtuals() const +{ + return NamesAndTypesList{ + {std::string{version_column_name}, std::make_shared()}}; +} + zkutil::ZooKeeperPtr StorageKeeperMap::getClient() const { std::lock_guard lock{zookeeper_mutex}; @@ -670,13 +739,18 @@ Chunk StorageKeeperMap::getByKeys(const ColumnsWithTypeAndName & keys, PaddedPOD if (raw_keys.size() != keys[0].column->size()) throw Exception(ErrorCodes::LOGICAL_ERROR, "Assertion failed: {} != {}", raw_keys.size(), keys[0].column->size()); - return getBySerializedKeys(raw_keys, &null_map); + return getBySerializedKeys(raw_keys, &null_map, /* version_column */ false); } -Chunk StorageKeeperMap::getBySerializedKeys(const std::span keys, PaddedPODArray * null_map) const +Chunk StorageKeeperMap::getBySerializedKeys(const std::span keys, PaddedPODArray * null_map, bool with_version) const { Block sample_block = getInMemoryMetadataPtr()->getSampleBlock(); MutableColumns columns = sample_block.cloneEmptyColumns(); + MutableColumnPtr version_column = nullptr; + + if (with_version) + version_column = ColumnVector::create(); + size_t primary_key_pos = getPrimaryKeyPos(sample_block, getPrimaryKey()); if (null_map) @@ -706,6 +780,9 @@ Chunk StorageKeeperMap::getBySerializedKeys(const std::span k if (code == Coordination::Error::ZOK) { fillColumns(base64Decode(keys[i], true), response.data, primary_key_pos, sample_block, columns); + + if (version_column) + version_column->insert(response.stat.version); } else if (code == Coordination::Error::ZNONODE) { @@ -714,6 +791,9 @@ Chunk StorageKeeperMap::getBySerializedKeys(const std::span k (*null_map)[i] = 0; for (size_t col_idx = 0; col_idx < sample_block.columns(); ++col_idx) columns[col_idx]->insert(sample_block.getByPosition(col_idx).type->getDefault()); + + if (version_column) + version_column->insert(-1); } } else @@ -723,6 +803,10 @@ Chunk StorageKeeperMap::getBySerializedKeys(const std::span k } size_t num_rows = columns.at(0)->size(); + + if (version_column) + columns.push_back(std::move(version_column)); + return Chunk(std::move(columns), num_rows); } @@ -763,6 +847,8 @@ void StorageKeeperMap::mutate(const MutationCommands & commands, ContextPtr loca if (commands.empty()) return; + bool strict = local_context->getSettingsRef().keeper_map_strict_mode; + assert(commands.size() == 1); auto metadata_snapshot = getInMemoryMetadataPtr(); @@ -784,8 +870,10 @@ void StorageKeeperMap::mutate(const MutationCommands & commands, ContextPtr loca auto header = interpreter->getUpdatedHeader(); auto primary_key_pos = header.getPositionByName(primary_key); + auto version_position = header.getPositionByName(std::string{version_column_name}); auto client = getClient(); + Block block; while (executor.pull(block)) { @@ -793,14 +881,23 @@ void StorageKeeperMap::mutate(const MutationCommands & commands, ContextPtr loca auto column = column_type_name.column; auto size = column->size(); + WriteBufferFromOwnString wb_key; Coordination::Requests delete_requests; + for (size_t i = 0; i < size; ++i) { + int32_t version = -1; + if (strict) + { + const auto & version_column = block.getByPosition(version_position).column; + version = assert_cast &>(*version_column).getData()[i]; + } + wb_key.restart(); column_type_name.type->getDefaultSerialization()->serializeBinary(*column, i, wb_key, {}); - delete_requests.emplace_back(zkutil::makeRemoveRequest(fullPathForKey(base64Encode(wb_key.str(), true)), -1)); + delete_requests.emplace_back(zkutil::makeRemoveRequest(fullPathForKey(base64Encode(wb_key.str(), true)), version)); } Coordination::Responses responses; @@ -834,11 +931,13 @@ void StorageKeeperMap::mutate(const MutationCommands & commands, ContextPtr loca auto pipeline = QueryPipelineBuilder::getPipeline(interpreter->execute()); PullingPipelineExecutor executor(pipeline); - auto sink = std::make_shared(*this, metadata_snapshot); + auto sink = std::make_shared(*this, executor.getHeader(), local_context); Block block; while (executor.pull(block)) sink->consume(Chunk{block.getColumns(), block.rows()}); + + sink->finalize(local_context->getSettingsRef().keeper_map_strict_mode); sink->onFinish(); } diff --git a/src/Storages/StorageKeeperMap.h b/src/Storages/StorageKeeperMap.h index a16c662e547..f71ff3cc65a 100644 --- a/src/Storages/StorageKeeperMap.h +++ b/src/Storages/StorageKeeperMap.h @@ -46,11 +46,13 @@ public: void truncate(const ASTPtr &, const StorageMetadataPtr &, ContextPtr, TableExclusiveLockHolder &) override; void drop() override; + NamesAndTypesList getVirtuals() const override; + std::string getName() const override { return "KeeperMap"; } Names getPrimaryKey() const override { return {primary_key}; } Chunk getByKeys(const ColumnsWithTypeAndName & keys, PaddedPODArray & null_map, const Names &) const override; - Chunk getBySerializedKeys(std::span keys, PaddedPODArray * null_map) const; + Chunk getBySerializedKeys(std::span keys, PaddedPODArray * null_map, bool with_version) const; Block getSampleBlock(const Names &) const override; diff --git a/tests/queries/0_stateless/02706_keeper_map_insert_strict.reference b/tests/queries/0_stateless/02706_keeper_map_insert_strict.reference new file mode 100644 index 00000000000..a6bdbb192e4 --- /dev/null +++ b/tests/queries/0_stateless/02706_keeper_map_insert_strict.reference @@ -0,0 +1,3 @@ +1 1.1 +1 2.1 +1 2.1 diff --git a/tests/queries/0_stateless/02706_keeper_map_insert_strict.sql b/tests/queries/0_stateless/02706_keeper_map_insert_strict.sql new file mode 100644 index 00000000000..97c801ec46e --- /dev/null +++ b/tests/queries/0_stateless/02706_keeper_map_insert_strict.sql @@ -0,0 +1,20 @@ +-- Tags: no-ordinary-database, no-fasttest + +DROP TABLE IF EXISTS 02706_keeper_map_insert_strict SYNC; + +CREATE TABLE 02706_keeper_map_insert_strict (key UInt64, value Float64) Engine=KeeperMap('/' || currentDatabase() || '/test_02706_keeper_map_insert_strict') PRIMARY KEY(key); + +INSERT INTO 02706_keeper_map_insert_strict VALUES (1, 1.1), (2, 2.2); +SELECT * FROM 02706_keeper_map_insert_strict WHERE key = 1; + +SET keeper_map_strict_mode = false; + +INSERT INTO 02706_keeper_map_insert_strict VALUES (1, 2.1); +SELECT * FROM 02706_keeper_map_insert_strict WHERE key = 1; + +SET keeper_map_strict_mode = true; + +INSERT INTO 02706_keeper_map_insert_strict VALUES (1, 2.1); -- { serverError KEEPER_EXCEPTION } +SELECT * FROM 02706_keeper_map_insert_strict WHERE key = 1; + +DROP TABLE 02706_keeper_map_insert_strict; From 24eec7039e314b8d430aa191047134d7fff071c4 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Fri, 31 Mar 2023 16:56:01 +0200 Subject: [PATCH 310/377] fix --- ...{25402_show_columns.reference => 02706_show_columns.reference} | 0 .../{25402_show_columns.sql => 02706_show_columns.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/queries/0_stateless/{25402_show_columns.reference => 02706_show_columns.reference} (100%) rename tests/queries/0_stateless/{25402_show_columns.sql => 02706_show_columns.sql} (100%) diff --git a/tests/queries/0_stateless/25402_show_columns.reference b/tests/queries/0_stateless/02706_show_columns.reference similarity index 100% rename from tests/queries/0_stateless/25402_show_columns.reference rename to tests/queries/0_stateless/02706_show_columns.reference diff --git a/tests/queries/0_stateless/25402_show_columns.sql b/tests/queries/0_stateless/02706_show_columns.sql similarity index 100% rename from tests/queries/0_stateless/25402_show_columns.sql rename to tests/queries/0_stateless/02706_show_columns.sql From ae0707ba01a9020fbc41e1526a5b2dc8d79d6593 Mon Sep 17 00:00:00 2001 From: Alexander Gololobov <440544+davenger@users.noreply.github.com> Date: Fri, 31 Mar 2023 17:23:42 +0200 Subject: [PATCH 311/377] Remove unused error codes --- src/Functions/parseDateTime.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Functions/parseDateTime.cpp b/src/Functions/parseDateTime.cpp index 8f1edc2049b..9d8db4d43a3 100644 --- a/src/Functions/parseDateTime.cpp +++ b/src/Functions/parseDateTime.cpp @@ -20,9 +20,7 @@ namespace DB { namespace ErrorCodes { - extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_COLUMN; - extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int NOT_IMPLEMENTED; extern const int BAD_ARGUMENTS; extern const int VALUE_IS_OUT_OF_RANGE_OF_DATA_TYPE; From 048ea93c09bc4fff737c79f635de4b1e1353d36b Mon Sep 17 00:00:00 2001 From: kst-morozov Date: Fri, 31 Mar 2023 17:59:19 +0200 Subject: [PATCH 312/377] upadte doc --- .../settings.md | 2 +- .../settings/merge-tree-settings.md | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index 08be318f334..3d579b14366 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -1045,7 +1045,7 @@ Default value: `0`. Sets the number of threads performing background merges and mutations for tables with MergeTree engines. This setting is also could be applied at server startup from the `default` profile configuration for backward compatibility at the ClickHouse server start. You can only increase the number of threads at runtime. To lower the number of threads you have to restart the server. By adjusting this setting, you manage CPU and disk load. Smaller pool size utilizes less CPU and disk resources, but background processes advance slower which might eventually impact query performance. -Before changing it, please also take a look at related MergeTree settings, such as `number_of_free_entries_in_pool_to_lower_max_size_of_merge` and `number_of_free_entries_in_pool_to_execute_mutation`. +Before changing it, please also take a look at related MergeTree settings, such as [number_of_free_entries_in_pool_to_lower_max_size_of_merge](../../operations/settings/merge-tree-settings.md#number-of-free-entries-in-pool-to-lower-max-size-of-merge) and [number_of_free_entries_in_pool_to_execute_mutation](../../operations/settings/merge-tree-settings.md#number-of-free-entries-in-pool-to-execute-mutation). Possible values: diff --git a/docs/en/operations/settings/merge-tree-settings.md b/docs/en/operations/settings/merge-tree-settings.md index 5bc174727ad..f36694cb81b 100644 --- a/docs/en/operations/settings/merge-tree-settings.md +++ b/docs/en/operations/settings/merge-tree-settings.md @@ -553,6 +553,32 @@ Default value: 8192 Merge reads rows from parts in blocks of `merge_max_block_size` rows, then merges and writes the result into a new part. The read block is placed in RAM, so `merge_max_block_size` affects the size of the RAM required for the merge. Thus, merges can consume a large amount of RAM for tables with very wide rows (if the average row size is 100kb, then when merging 10 parts, (100kb * 10 * 8192) = ~ 8GB of RAM). By decreasing `merge_max_block_size`, you can reduce the amount of RAM required for a merge but slow down a merge. +## number_of_free_entries_in_pool_to_lower_max_size_of_merge {#number-of-free-entries-in-pool-to-lower-max-size-of-merge} + +When there is less than specified number of free entries in pool (or replicated queue), start to lower maximum size of merge to process (or to put in queue). +This is to allow small merges to process - not filling the pool with long running merges. + +Possible values: + +- Any positive integer. + +Default value: 8 + +## number_of_free_entries_in_pool_to_execute_mutation {#number-of-free-entries-in-pool-to-execute-mutation} + +When there is less than specified number of free entries in pool, do not execute part mutations. +This is to leave free threads for regular merges and avoid "Too many parts". + +Possible values: + +- Any positive integer. + +Default value: 20 + +**Usage** + +The value of the `number_of_free_entries_in_pool_to_execute_mutation` setting should be less than the value of the [background_pool_size](../../operations/settings/server-configuration-parameters/settings.md#background_pool_size) * [background_pool_size](../../operations/settings/server-configuration-parameters/settings.md#background_merges_mutations_concurrency_ratio). Otherwise, ClickHouse throws an exception. + ## max_part_loading_threads {#max-part-loading-threads} The maximum number of threads that read parts when ClickHouse starts. From 8c65cc734ab3ba2abce5ce8fc234ca9532b75fa4 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Fri, 31 Mar 2023 10:32:38 -0600 Subject: [PATCH 313/377] Update youtube-dislikes.md --- .../example-datasets/youtube-dislikes.md | 161 +++++++++++++++++- 1 file changed, 153 insertions(+), 8 deletions(-) diff --git a/docs/en/getting-started/example-datasets/youtube-dislikes.md b/docs/en/getting-started/example-datasets/youtube-dislikes.md index e3b162a8dbf..2609b8db852 100644 --- a/docs/en/getting-started/example-datasets/youtube-dislikes.md +++ b/docs/en/getting-started/example-datasets/youtube-dislikes.md @@ -67,7 +67,8 @@ CREATE TABLE youtube ( `id` String, `fetch_date` DateTime, - `upload_date` String, + `upload_date_str` String, + `upload_date` Date, `title` String, `uploader_id` String, `uploader` String, @@ -87,7 +88,7 @@ CREATE TABLE youtube `video_badges` String ) ENGINE = MergeTree -ORDER BY (upload_date, uploader); +ORDER BY (uploader, upload_date); ``` 3. The following command streams the records from the S3 files into the `youtube` table. @@ -101,8 +102,9 @@ INSERT INTO youtube SETTINGS input_format_null_as_default = 1 SELECT id, - parseDateTimeBestEffortUS(toString(fetch_date)) AS fetch_date, - upload_date, + parseDateTimeBestEffortUSOrZero(toString(fetch_date)) AS fetch_date, + upload_date AS upload_date_str, + toDate(parseDateTimeBestEffortUS(upload_date::String)) AS upload_date, ifNull(title, '') AS title, uploader_id, ifNull(uploader, '') AS uploader, @@ -121,12 +123,23 @@ SELECT ifNull(uploader_badges, '') AS uploader_badges, ifNull(video_badges, '') AS video_badges FROM s3Cluster( - 'default', - 'https://clickhouse-public-datasets.s3.amazonaws.com/youtube/original/files/*.zst', - 'JSONLines' - ); + 'default', + 'https://clickhouse-public-datasets.s3.amazonaws.com/youtube/original/files/*.zst', + 'JSONLines' +) +SETTINGS + max_download_threads = 24, + max_insert_threads = 64, + max_insert_block_size = 100000000, + min_insert_block_size_rows = 100000000, + min_insert_block_size_bytes = 500000000; ``` +Some comments about our `INSERT` command: + +- The `parseDateTimeBestEffortUSOrZero` function is handy when the incoming date fields may not be in the proper format. If `fetch_date` does not get parsed properly, it will be set to `0` +- + 4. Open a new tab in the SQL Console of ClickHouse Cloud (or a new `clickhouse-client` window) and watch the count increase. It will take a while to insert 4.56B rows, depending on your server resources. (Withtout any tweaking of settings, it takes about 4.5 hours.) ```sql @@ -276,6 +289,132 @@ ORDER BY Enabling comments seems to be correlated with a higher rate of engagement. +<<<<<<< Updated upstream +======= + +### How does the number of videos change over time - notable events? + +```sql +SELECT + toStartOfMonth(toDateTime(upload_date)) AS month, + uniq(uploader_id) AS uploaders, + count() as num_videos, + sum(view_count) as view_count +FROM youtube +WHERE (month >= '2005-01-01') AND (month < '2021-12-01') +GROUP BY month +ORDER BY month ASC +``` + +```response +┌──────month─┬─uploaders─┬─num_videos─┬───view_count─┐ +│ 2005-04-01 │ 5 │ 6 │ 213597737 │ +│ 2005-05-01 │ 6 │ 9 │ 2944005 │ +│ 2005-06-01 │ 165 │ 351 │ 18624981 │ +│ 2005-07-01 │ 395 │ 1168 │ 94164872 │ +│ 2005-08-01 │ 1171 │ 3128 │ 124540774 │ +│ 2005-09-01 │ 2418 │ 5206 │ 475536249 │ +│ 2005-10-01 │ 6750 │ 13747 │ 737593613 │ +│ 2005-11-01 │ 13706 │ 28078 │ 1896116976 │ +│ 2005-12-01 │ 24756 │ 49885 │ 2478418930 │ +│ 2006-01-01 │ 49992 │ 100447 │ 4532656581 │ +│ 2006-02-01 │ 67882 │ 138485 │ 5677516317 │ +│ 2006-03-01 │ 103358 │ 212237 │ 8430301366 │ +│ 2006-04-01 │ 114615 │ 234174 │ 9980760440 │ +│ 2006-05-01 │ 152682 │ 332076 │ 14129117212 │ +│ 2006-06-01 │ 193962 │ 429538 │ 17014143263 │ +│ 2006-07-01 │ 234401 │ 530311 │ 18721143410 │ +│ 2006-08-01 │ 281280 │ 614128 │ 20473502342 │ +│ 2006-09-01 │ 312434 │ 679906 │ 23158422265 │ +│ 2006-10-01 │ 404873 │ 897590 │ 27357846117 │ +``` + +A spike of uploaders [around covid is noticeable](https://www.theverge.com/2020/3/27/21197642/youtube-with-me-style-videos-views-coronavirus-cook-workout-study-home-beauty). + + +### More subtitiles over time and when + +With advances in speech recognition, it’s easier than ever to create subtitles for video with youtube adding auto-captioning in late 2009 - was the jump then? + +```sql +SELECT + toStartOfMonth(upload_date) AS month, + countIf(has_subtitles) / count() AS percent_subtitles, + percent_subtitles - any(percent_subtitles) OVER (ORDER BY month ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS previous +FROM youtube +WHERE (month >= '2015-01-01') AND (month < '2021-12-02') +GROUP BY month +ORDER BY month ASC +``` + +```response +┌──────month─┬───percent_subtitles─┬────────────────previous─┐ +│ 2015-01-01 │ 0.2652653881082824 │ 0.2652653881082824 │ +│ 2015-02-01 │ 0.3147556050309162 │ 0.049490216922633834 │ +│ 2015-03-01 │ 0.32460464492371877 │ 0.009849039892802558 │ +│ 2015-04-01 │ 0.33471963051468445 │ 0.010114985590965686 │ +│ 2015-05-01 │ 0.3168087575501062 │ -0.017910872964578273 │ +│ 2015-06-01 │ 0.3162609788438222 │ -0.0005477787062839745 │ +│ 2015-07-01 │ 0.31828767677518033 │ 0.0020266979313581235 │ +│ 2015-08-01 │ 0.3045551564286859 │ -0.013732520346494415 │ +│ 2015-09-01 │ 0.311221133995152 │ 0.006665977566466086 │ +│ 2015-10-01 │ 0.30574870926812175 │ -0.005472424727030245 │ +│ 2015-11-01 │ 0.31125409712077234 │ 0.0055053878526505895 │ +│ 2015-12-01 │ 0.3190967954651779 │ 0.007842698344405541 │ +│ 2016-01-01 │ 0.32636021432496176 │ 0.007263418859783877 │ + +``` + +The data results show a spike in 2009. Apparently at that, time YouTube was removing their community captions feature, which allowed you to upload captions for other people's video. +This prompted a very successful campaign to have creators add captions to their videos for hard of hearing and deaf viewers. + + +### Top uploaders over time + +```sql +WITH uploaders AS + ( + SELECT uploader + FROM youtube + GROUP BY uploader + ORDER BY sum(view_count) DESC + LIMIT 10 + ) +SELECT + month, + uploader, + sum(view_count) AS total_views, + avg(dislike_count / like_count) AS like_to_dislike_ratio +FROM youtube +WHERE uploader IN (uploaders) +GROUP BY + toStartOfMonth(upload_date) AS month, + uploader +ORDER BY + month ASC, + total_views DESC + +1001 rows in set. Elapsed: 34.917 sec. Processed 4.58 billion rows, 69.08 GB (131.15 million rows/s., 1.98 GB/s.) +``` + +```response +┌──────month─┬─uploader───────────────────┬─total_views─┬─like_to_dislike_ratio─┐ +│ 1970-01-01 │ T-Series │ 10957099 │ 0.022784656361208206 │ +│ 1970-01-01 │ Ryan's World │ 0 │ 0.003035559410234172 │ +│ 1970-01-01 │ SET India │ 0 │ nan │ +│ 2006-09-01 │ Cocomelon - Nursery Rhymes │ 256406497 │ 0.7005566715978622 │ +│ 2007-06-01 │ Cocomelon - Nursery Rhymes │ 33641320 │ 0.7088650914344298 │ +│ 2008-02-01 │ WWE │ 43733469 │ 0.07198856488734842 │ +│ 2008-03-01 │ WWE │ 16514541 │ 0.1230603715431997 │ +│ 2008-04-01 │ WWE │ 5907295 │ 0.2089399470159618 │ +│ 2008-05-01 │ WWE │ 7779627 │ 0.09101676560436774 │ +│ 2008-06-01 │ WWE │ 7018780 │ 0.0974184753155297 │ +│ 2008-07-01 │ WWE │ 4686447 │ 0.1263845422065158 │ +│ 2008-08-01 │ WWE │ 4514312 │ 0.08384574274791441 │ +│ 2008-09-01 │ WWE │ 3717092 │ 0.07872802579349912 │ +``` + +>>>>>>> Stashed changes ### How do like ratio changes as views go up? ```sql @@ -296,7 +435,13 @@ GROUP BY ORDER BY view_range ASC, is_comments_enabled ASC +<<<<<<< Updated upstream ); +======= +) + +20 rows in set. Elapsed: 9.043 sec. Processed 4.56 billion rows, 77.48 GB (503.99 million rows/s., 8.57 GB/s.) +>>>>>>> Stashed changes ``` ```response From 0b70a8600be22204c7489174b6407d3a0a97d859 Mon Sep 17 00:00:00 2001 From: Dan Roscigno Date: Fri, 31 Mar 2023 13:19:49 -0400 Subject: [PATCH 314/377] Update LVM info in tips.md --- docs/en/operations/tips.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/en/operations/tips.md b/docs/en/operations/tips.md index 13353cd8e6a..c5b489e92c5 100644 --- a/docs/en/operations/tips.md +++ b/docs/en/operations/tips.md @@ -51,10 +51,14 @@ But for storing archives with rare queries, shelves will work. ## RAID {#raid} When using HDD, you can combine their RAID-10, RAID-5, RAID-6 or RAID-50. -For Linux, software RAID is better (with `mdadm`). We do not recommend using LVM. +For Linux, software RAID is better (with `mdadm`). When creating RAID-10, select the `far` layout. If your budget allows, choose RAID-10. +LVM by itself (without RAID or mdadm) is ok, but making RAID with it or combining it with mdadm is a less explored option, and there will be more chances for mistakes +(selecting wrong chunk size; misalignment of chunks; choosing a wrong raid type; forgetting to cleanup disks). If you are confident +in using LVM, there is nothing against using it. + If you have more than 4 disks, use RAID-6 (preferred) or RAID-50, instead of RAID-5. When using RAID-5, RAID-6 or RAID-50, always increase stripe_cache_size, since the default value is usually not the best choice. From 4cb3c9260771a31199f2d793480f7ac3fa3a42fa Mon Sep 17 00:00:00 2001 From: Rich Raposa Date: Fri, 31 Mar 2023 12:38:45 -0600 Subject: [PATCH 315/377] Update merge-tree-settings.md For some reason the links are broken - I'm attempting to fix them here --- docs/en/operations/settings/merge-tree-settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/operations/settings/merge-tree-settings.md b/docs/en/operations/settings/merge-tree-settings.md index 41e66679874..4008b71ef8b 100644 --- a/docs/en/operations/settings/merge-tree-settings.md +++ b/docs/en/operations/settings/merge-tree-settings.md @@ -577,7 +577,7 @@ Default value: 20 **Usage** -The value of the `number_of_free_entries_in_pool_to_execute_mutation` setting should be less than the value of the [background_pool_size](../../operations/settings/server-configuration-parameters/settings.md#background_pool_size) * [background_pool_size](../../operations/settings/server-configuration-parameters/settings.md#background_merges_mutations_concurrency_ratio). Otherwise, ClickHouse throws an exception. +The value of the `number_of_free_entries_in_pool_to_execute_mutation` setting should be less than the value of the [background_pool_size](/docs/en/operations/server-configuration-parameters/settings#background_pool_size) * [background_pool_size](/docs/en/operations/server-configuration-parameters/settings#background_merges_mutations_concurrency_ratio). Otherwise, ClickHouse throws an exception. ## max_part_loading_threads {#max-part-loading-threads} From abe78469f78bc089f11cfbeaffae34591d5e1f2a Mon Sep 17 00:00:00 2001 From: rfraposa Date: Fri, 31 Mar 2023 12:41:22 -0600 Subject: [PATCH 316/377] Update youtube-dislikes.md --- .../example-datasets/youtube-dislikes.md | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/docs/en/getting-started/example-datasets/youtube-dislikes.md b/docs/en/getting-started/example-datasets/youtube-dislikes.md index 2609b8db852..c0a54906212 100644 --- a/docs/en/getting-started/example-datasets/youtube-dislikes.md +++ b/docs/en/getting-started/example-datasets/youtube-dislikes.md @@ -104,7 +104,7 @@ SELECT id, parseDateTimeBestEffortUSOrZero(toString(fetch_date)) AS fetch_date, upload_date AS upload_date_str, - toDate(parseDateTimeBestEffortUS(upload_date::String)) AS upload_date, + toDate(parseDateTimeBestEffortUSOrZero(upload_date::String)) AS upload_date, ifNull(title, '') AS title, uploader_id, ifNull(uploader, '') AS uploader, @@ -138,9 +138,17 @@ SETTINGS Some comments about our `INSERT` command: - The `parseDateTimeBestEffortUSOrZero` function is handy when the incoming date fields may not be in the proper format. If `fetch_date` does not get parsed properly, it will be set to `0` -- +- The `upload_date` column contains valid dates, but it also contains strings like "4 hours ago" - which is certainly not a valid date. We decided to store the original value in `upload_date_str` and attempt to parse it with `toDate(parseDateTimeBestEffortUSOrZero(upload_date::String))`. If the parsing fails we just get `0` +- We used `ifNull` to avoid getting `NULL` values in our table. If an incoming value is `NULL`, the `ifNull` function is setting the value to an empty string +- It takes a long time to download the data, so we added a `SETTINGS` clause to spread out the work over more threads while making sure the block sizes stayed fairly large -4. Open a new tab in the SQL Console of ClickHouse Cloud (or a new `clickhouse-client` window) and watch the count increase. It will take a while to insert 4.56B rows, depending on your server resources. (Withtout any tweaking of settings, it takes about 4.5 hours.) +Here is the response when the data is fully loaded: + +```response + +``` + +4. Open a new tab in the SQL Console of ClickHouse Cloud (or a new `clickhouse-client` window) and watch the count increase. It will take a while to insert 4.56B rows, depending on your server resources. (Without any tweaking of settings, it takes about 4.5 hours.) ```sql SELECT formatReadableQuantity(count()) @@ -237,7 +245,6 @@ The results look like: When commenting is disabled, are people more likely to like or dislike to express their feelings about a video? - ```sql SELECT concat('< ', formatReadableQuantity(view_range)) AS views, @@ -261,6 +268,7 @@ ORDER BY ``` ```response + ┌─views─────────────┬─is_comments_enabled─┬────prob_like_dislike─┐ │ < 10.00 │ false │ 0.08224180712685371 │ │ < 100.00 │ false │ 0.06346337759167248 │ @@ -285,12 +293,11 @@ ORDER BY └───────────────────┴─────────────────────┴──────────────────────┘ 22 rows in set. Elapsed: 8.460 sec. Processed 4.56 billion rows, 77.48 GB (538.73 million rows/s., 9.16 GB/s.) + ``` Enabling comments seems to be correlated with a higher rate of engagement. -<<<<<<< Updated upstream -======= ### How does the number of videos change over time - notable events? @@ -301,9 +308,8 @@ SELECT count() as num_videos, sum(view_count) as view_count FROM youtube -WHERE (month >= '2005-01-01') AND (month < '2021-12-01') GROUP BY month -ORDER BY month ASC +ORDER BY month ASC; ``` ```response @@ -340,11 +346,12 @@ With advances in speech recognition, it’s easier than ever to create subtitles SELECT toStartOfMonth(upload_date) AS month, countIf(has_subtitles) / count() AS percent_subtitles, - percent_subtitles - any(percent_subtitles) OVER (ORDER BY month ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS previous + percent_subtitles - any(percent_subtitles) OVER ( + ORDER BY month ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING + ) AS previous FROM youtube -WHERE (month >= '2015-01-01') AND (month < '2021-12-02') GROUP BY month -ORDER BY month ASC +ORDER BY month ASC; ``` ```response @@ -392,9 +399,7 @@ GROUP BY uploader ORDER BY month ASC, - total_views DESC - -1001 rows in set. Elapsed: 34.917 sec. Processed 4.58 billion rows, 69.08 GB (131.15 million rows/s., 1.98 GB/s.) + total_views DESC; ``` ```response @@ -414,7 +419,6 @@ ORDER BY │ 2008-09-01 │ WWE │ 3717092 │ 0.07872802579349912 │ ``` ->>>>>>> Stashed changes ### How do like ratio changes as views go up? ```sql @@ -435,13 +439,7 @@ GROUP BY ORDER BY view_range ASC, is_comments_enabled ASC -<<<<<<< Updated upstream ); -======= -) - -20 rows in set. Elapsed: 9.043 sec. Processed 4.56 billion rows, 77.48 GB (503.99 million rows/s., 8.57 GB/s.) ->>>>>>> Stashed changes ``` ```response @@ -467,8 +465,6 @@ ORDER BY │ < 10.00 billion │ false │ 1.77 │ │ < 10.00 billion │ true │ 19.5 │ └───────────────────┴─────────────────────┴────────────┘ - -20 rows in set. Elapsed: 63.664 sec. Processed 4.56 billion rows, 113.93 GB (71.59 million rows/s., 1.79 GB/s.) ``` ### How are views distributed? @@ -504,6 +500,4 @@ ARRAY JOIN │ 20th │ 16 │ │ 10th │ 6 │ └────────────┴─────────┘ - -12 rows in set. Elapsed: 1.864 sec. Processed 4.56 billion rows, 36.46 GB (2.45 billion rows/s., 19.56 GB/s.) ``` \ No newline at end of file From 1693e4664045ce44013ac43e465be49091424ca4 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 31 Mar 2023 19:03:20 +0000 Subject: [PATCH 317/377] Do not remove inputs from maybe compiled DAG. --- src/Interpreters/ActionsDAG.cpp | 2 +- .../0_stateless/02705_grouping_keys_equal_keys.reference | 6 ++++++ .../queries/0_stateless/02705_grouping_keys_equal_keys.sql | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/queries/0_stateless/02705_grouping_keys_equal_keys.reference create mode 100644 tests/queries/0_stateless/02705_grouping_keys_equal_keys.sql diff --git a/src/Interpreters/ActionsDAG.cpp b/src/Interpreters/ActionsDAG.cpp index ad809dca022..fb6f3cda99d 100644 --- a/src/Interpreters/ActionsDAG.cpp +++ b/src/Interpreters/ActionsDAG.cpp @@ -1051,7 +1051,7 @@ ActionsDAGPtr ActionsDAG::clone() const void ActionsDAG::compileExpressions(size_t min_count_to_compile_expression, const std::unordered_set & lazy_executed_nodes) { compileFunctions(min_count_to_compile_expression, lazy_executed_nodes); - removeUnusedActions(); + removeUnusedActions(/*allow_remove_inputs = */ false); } #endif diff --git a/tests/queries/0_stateless/02705_grouping_keys_equal_keys.reference b/tests/queries/0_stateless/02705_grouping_keys_equal_keys.reference new file mode 100644 index 00000000000..a9e2f17562a --- /dev/null +++ b/tests/queries/0_stateless/02705_grouping_keys_equal_keys.reference @@ -0,0 +1,6 @@ +1 +1 +1 +1 +1 +1 diff --git a/tests/queries/0_stateless/02705_grouping_keys_equal_keys.sql b/tests/queries/0_stateless/02705_grouping_keys_equal_keys.sql new file mode 100644 index 00000000000..fcf5b4d2ce5 --- /dev/null +++ b/tests/queries/0_stateless/02705_grouping_keys_equal_keys.sql @@ -0,0 +1,7 @@ +SELECT count() +FROM numbers(2) +GROUP BY +GROUPING SETS ( + (number, number + 0, number + 1), + (number % 1048576, number % -9223372036854775808), + (number / 2, number / 2)); From 04513504be5d9bf6a19d791399a6a30bd69969c8 Mon Sep 17 00:00:00 2001 From: robot-clickhouse Date: Fri, 31 Mar 2023 19:25:46 +0000 Subject: [PATCH 318/377] Update version_date.tsv and changelogs after v22.3.20.29-lts --- docs/changelogs/v22.3.20.29-lts.md | 29 ++++++++++++++++++++++++++++ utils/list-versions/version_date.tsv | 1 + 2 files changed, 30 insertions(+) create mode 100644 docs/changelogs/v22.3.20.29-lts.md diff --git a/docs/changelogs/v22.3.20.29-lts.md b/docs/changelogs/v22.3.20.29-lts.md new file mode 100644 index 00000000000..a54a320c4c1 --- /dev/null +++ b/docs/changelogs/v22.3.20.29-lts.md @@ -0,0 +1,29 @@ +--- +sidebar_position: 1 +sidebar_label: 2023 +--- + +# 2023 Changelog + +### ClickHouse release v22.3.20.29-lts (297b4dd5e55) FIXME as compared to v22.3.19.6-lts (467e0a7bd77) + +#### Improvement +* Backported in [#46979](https://github.com/ClickHouse/ClickHouse/issues/46979): Apply `ALTER TABLE table_name ON CLUSTER cluster MOVE PARTITION|PART partition_expr TO DISK|VOLUME 'disk_name'` to all replicas. Because `ALTER TABLE t MOVE` is not replicated. [#46402](https://github.com/ClickHouse/ClickHouse/pull/46402) ([lizhuoyu5](https://github.com/lzydmxy)). + +#### Bug Fix (user-visible misbehavior in an official stable release) + +* Fix incorrect alias recursion in QueryNormalizer [#46609](https://github.com/ClickHouse/ClickHouse/pull/46609) ([Raúl Marín](https://github.com/Algunenano)). +* Fix arithmetic operations in aggregate optimization [#46705](https://github.com/ClickHouse/ClickHouse/pull/46705) ([Duc Canh Le](https://github.com/canhld94)). +* Fix MSan report in `maxIntersections` function [#46847](https://github.com/ClickHouse/ClickHouse/pull/46847) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Fix wrong results of some LIKE searches when the LIKE pattern contains quoted non-quotable characters [#46875](https://github.com/ClickHouse/ClickHouse/pull/46875) ([Robert Schulze](https://github.com/rschu1ze)). +* Fix possible deadlock in QueryStatus [#47161](https://github.com/ClickHouse/ClickHouse/pull/47161) ([Kruglov Pavel](https://github.com/Avogar)). + +#### NOT FOR CHANGELOG / INSIGNIFICANT + +* Update typing for a new PyGithub version [#47123](https://github.com/ClickHouse/ClickHouse/pull/47123) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Add a manual trigger for release workflow [#47302](https://github.com/ClickHouse/ClickHouse/pull/47302) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Add a fuse for backport branches w/o a created PR [#47760](https://github.com/ClickHouse/ClickHouse/pull/47760) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Only valid Reviews.STATES overwrite existing reviews [#47789](https://github.com/ClickHouse/ClickHouse/pull/47789) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Place short return before big block, improve logging [#47822](https://github.com/ClickHouse/ClickHouse/pull/47822) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix tsan error lock-order-inversion [#47953](https://github.com/ClickHouse/ClickHouse/pull/47953) ([Kruglov Pavel](https://github.com/Avogar)). + diff --git a/utils/list-versions/version_date.tsv b/utils/list-versions/version_date.tsv index f46a422446e..f993c90d1b2 100644 --- a/utils/list-versions/version_date.tsv +++ b/utils/list-versions/version_date.tsv @@ -73,6 +73,7 @@ v22.4.5.9-stable 2022-05-06 v22.4.4.7-stable 2022-04-29 v22.4.3.3-stable 2022-04-26 v22.4.2.1-stable 2022-04-22 +v22.3.20.29-lts 2023-03-31 v22.3.19.6-lts 2023-02-27 v22.3.18.37-lts 2023-02-15 v22.3.17.13-lts 2023-01-12 From 648f80ee4e403fecbf99f8273391c798c8cb7798 Mon Sep 17 00:00:00 2001 From: robot-clickhouse Date: Fri, 31 Mar 2023 19:50:47 +0000 Subject: [PATCH 319/377] Update version_date.tsv and changelogs after v22.12.6.22-stable --- docs/changelogs/v22.12.6.22-stable.md | 26 ++++++++++++++++++++++++++ utils/list-versions/version_date.tsv | 2 ++ 2 files changed, 28 insertions(+) create mode 100644 docs/changelogs/v22.12.6.22-stable.md diff --git a/docs/changelogs/v22.12.6.22-stable.md b/docs/changelogs/v22.12.6.22-stable.md new file mode 100644 index 00000000000..f0bf7c92340 --- /dev/null +++ b/docs/changelogs/v22.12.6.22-stable.md @@ -0,0 +1,26 @@ +--- +sidebar_position: 1 +sidebar_label: 2023 +--- + +# 2023 Changelog + +### ClickHouse release v22.12.6.22-stable (10d87f90261) FIXME as compared to v22.12.5.34-stable (b82d6401ca1) + +#### Bug Fix (user-visible misbehavior in an official stable release) + +* Fix changing an expired role [#46772](https://github.com/ClickHouse/ClickHouse/pull/46772) ([Vitaly Baranov](https://github.com/vitlibar)). +* Fix bug in zero-copy replication disk choice during fetch [#47010](https://github.com/ClickHouse/ClickHouse/pull/47010) ([alesapin](https://github.com/alesapin)). +* Fix NOT_IMPLEMENTED error with CROSS JOIN and algorithm = auto [#47068](https://github.com/ClickHouse/ClickHouse/pull/47068) ([Vladimir C](https://github.com/vdimir)). +* Fix query parameters [#47488](https://github.com/ClickHouse/ClickHouse/pull/47488) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Hotfix for too verbose warnings in HTTP [#47903](https://github.com/ClickHouse/ClickHouse/pull/47903) ([Alexander Tokmakov](https://github.com/tavplubix)). + +#### NOT FOR CHANGELOG / INSIGNIFICANT + +* Better error messages in ReplicatedMergeTreeAttachThread [#47454](https://github.com/ClickHouse/ClickHouse/pull/47454) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Add a fuse for backport branches w/o a created PR [#47760](https://github.com/ClickHouse/ClickHouse/pull/47760) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Only valid Reviews.STATES overwrite existing reviews [#47789](https://github.com/ClickHouse/ClickHouse/pull/47789) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Place short return before big block, improve logging [#47822](https://github.com/ClickHouse/ClickHouse/pull/47822) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Artifacts s3 prefix [#47945](https://github.com/ClickHouse/ClickHouse/pull/47945) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix tsan error lock-order-inversion [#47953](https://github.com/ClickHouse/ClickHouse/pull/47953) ([Kruglov Pavel](https://github.com/Avogar)). + diff --git a/utils/list-versions/version_date.tsv b/utils/list-versions/version_date.tsv index f46a422446e..0cd3416f44c 100644 --- a/utils/list-versions/version_date.tsv +++ b/utils/list-versions/version_date.tsv @@ -8,6 +8,7 @@ v23.1.4.58-stable 2023-03-01 v23.1.3.5-stable 2023-02-03 v23.1.2.9-stable 2023-01-29 v23.1.1.3077-stable 2023-01-25 +v22.12.6.22-stable 2023-03-31 v22.12.5.34-stable 2023-03-10 v22.12.4.76-stable 2023-03-01 v22.12.3.5-stable 2023-01-10 @@ -73,6 +74,7 @@ v22.4.5.9-stable 2022-05-06 v22.4.4.7-stable 2022-04-29 v22.4.3.3-stable 2022-04-26 v22.4.2.1-stable 2022-04-22 +v22.3.20.29-lts 2023-03-31 v22.3.19.6-lts 2023-02-27 v22.3.18.37-lts 2023-02-15 v22.3.17.13-lts 2023-01-12 From 3f4aadfe7dc0a1795ce55d256696b6756704dc34 Mon Sep 17 00:00:00 2001 From: Vitaly Baranov Date: Fri, 31 Mar 2023 23:50:35 +0200 Subject: [PATCH 320/377] Add logging for concurrency checks for backups. --- src/Backups/BackupCoordinationLocal.cpp | 10 ++++++++-- src/Backups/BackupCoordinationLocal.h | 2 ++ src/Backups/BackupCoordinationRemote.cpp | 8 ++++++-- src/Backups/BackupCoordinationRemote.h | 1 + src/Backups/RestoreCoordinationLocal.cpp | 13 +++++++++++-- src/Backups/RestoreCoordinationLocal.h | 2 ++ src/Backups/RestoreCoordinationRemote.cpp | 6 +++++- src/Backups/RestoreCoordinationRemote.h | 1 + 8 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/Backups/BackupCoordinationLocal.cpp b/src/Backups/BackupCoordinationLocal.cpp index a96ed443b9c..f3a6c02d228 100644 --- a/src/Backups/BackupCoordinationLocal.cpp +++ b/src/Backups/BackupCoordinationLocal.cpp @@ -8,7 +8,8 @@ namespace DB { -BackupCoordinationLocal::BackupCoordinationLocal(bool plain_backup_) : file_infos(plain_backup_) +BackupCoordinationLocal::BackupCoordinationLocal(bool plain_backup_) + : log(&Poco::Logger::get("BackupCoordinationLocal")), file_infos(plain_backup_) { } @@ -125,7 +126,12 @@ bool BackupCoordinationLocal::startWritingFile(size_t data_file_index) bool BackupCoordinationLocal::hasConcurrentBackups(const std::atomic & num_active_backups) const { - return (num_active_backups > 1); + if (num_active_backups > 1) + { + LOG_WARNING(log, "Found concurrent backups: num_active_backups={}", num_active_backups); + return true; + } + return false; } } diff --git a/src/Backups/BackupCoordinationLocal.h b/src/Backups/BackupCoordinationLocal.h index db2070fa891..60fcc014720 100644 --- a/src/Backups/BackupCoordinationLocal.h +++ b/src/Backups/BackupCoordinationLocal.h @@ -52,6 +52,8 @@ public: bool hasConcurrentBackups(const std::atomic & num_active_backups) const override; private: + Poco::Logger * const log; + BackupCoordinationReplicatedTables TSA_GUARDED_BY(replicated_tables_mutex) replicated_tables; BackupCoordinationReplicatedAccess TSA_GUARDED_BY(replicated_access_mutex) replicated_access; BackupCoordinationReplicatedSQLObjects TSA_GUARDED_BY(replicated_sql_objects_mutex) replicated_sql_objects; diff --git a/src/Backups/BackupCoordinationRemote.cpp b/src/Backups/BackupCoordinationRemote.cpp index 9b4343a1d3b..d6463d08909 100644 --- a/src/Backups/BackupCoordinationRemote.cpp +++ b/src/Backups/BackupCoordinationRemote.cpp @@ -164,17 +164,18 @@ BackupCoordinationRemote::BackupCoordinationRemote( , current_host_index(findCurrentHostIndex(all_hosts, current_host)) , plain_backup(plain_backup_) , is_internal(is_internal_) + , log(&Poco::Logger::get("BackupCoordinationRemote")) { zookeeper_retries_info = ZooKeeperRetriesInfo( "BackupCoordinationRemote", - &Poco::Logger::get("BackupCoordinationRemote"), + log, keeper_settings.keeper_max_retries, keeper_settings.keeper_retry_initial_backoff_ms, keeper_settings.keeper_retry_max_backoff_ms); createRootNodes(); stage_sync.emplace( - zookeeper_path + "/stage", [this] { return getZooKeeper(); }, &Poco::Logger::get("BackupCoordination")); + zookeeper_path + "/stage", [this] { return getZooKeeper(); }, log); } BackupCoordinationRemote::~BackupCoordinationRemote() @@ -664,7 +665,10 @@ bool BackupCoordinationRemote::hasConcurrentBackups(const std::atomic &) const auto status = zk->get(root_zookeeper_path + "/" + existing_backup_path + "/stage"); if (status != Stage::COMPLETED) + { + LOG_WARNING(log, "Found a concurrent backup: {}, current backup: {}", existing_backup_uuid, toString(backup_uuid)); return true; + } } zk->createIfNotExists(backup_stage_path, ""); diff --git a/src/Backups/BackupCoordinationRemote.h b/src/Backups/BackupCoordinationRemote.h index e7f5ff3a211..5155f21c27a 100644 --- a/src/Backups/BackupCoordinationRemote.h +++ b/src/Backups/BackupCoordinationRemote.h @@ -104,6 +104,7 @@ private: const size_t current_host_index; const bool plain_backup; const bool is_internal; + Poco::Logger * const log; mutable ZooKeeperRetriesInfo zookeeper_retries_info; std::optional stage_sync; diff --git a/src/Backups/RestoreCoordinationLocal.cpp b/src/Backups/RestoreCoordinationLocal.cpp index 191cde40aa1..068c4fe7e52 100644 --- a/src/Backups/RestoreCoordinationLocal.cpp +++ b/src/Backups/RestoreCoordinationLocal.cpp @@ -1,10 +1,14 @@ #include +#include namespace DB { -RestoreCoordinationLocal::RestoreCoordinationLocal() = default; +RestoreCoordinationLocal::RestoreCoordinationLocal() : log(&Poco::Logger::get("RestoreCoordinationLocal")) +{ +} + RestoreCoordinationLocal::~RestoreCoordinationLocal() = default; void RestoreCoordinationLocal::setStage(const String &, const String &) @@ -49,7 +53,12 @@ bool RestoreCoordinationLocal::acquireReplicatedSQLObjects(const String &, UserD bool RestoreCoordinationLocal::hasConcurrentRestores(const std::atomic & num_active_restores) const { - return (num_active_restores > 1); + if (num_active_restores > 1) + { + LOG_WARNING(log, "Found concurrent backups: num_active_restores={}", num_active_restores); + return true; + } + return false; } } diff --git a/src/Backups/RestoreCoordinationLocal.h b/src/Backups/RestoreCoordinationLocal.h index bbe76cdf5fd..e27f0d1ef88 100644 --- a/src/Backups/RestoreCoordinationLocal.h +++ b/src/Backups/RestoreCoordinationLocal.h @@ -42,6 +42,8 @@ public: bool hasConcurrentRestores(const std::atomic & num_active_restores) const override; private: + Poco::Logger * const log; + std::set> acquired_tables_in_replicated_databases; std::unordered_set acquired_data_in_replicated_tables; mutable std::mutex mutex; diff --git a/src/Backups/RestoreCoordinationRemote.cpp b/src/Backups/RestoreCoordinationRemote.cpp index 10d085a696a..d93f99a3f2a 100644 --- a/src/Backups/RestoreCoordinationRemote.cpp +++ b/src/Backups/RestoreCoordinationRemote.cpp @@ -25,11 +25,12 @@ RestoreCoordinationRemote::RestoreCoordinationRemote( , current_host(current_host_) , current_host_index(BackupCoordinationRemote::findCurrentHostIndex(all_hosts, current_host)) , is_internal(is_internal_) + , log(&Poco::Logger::get("RestoreCoordinationRemote")) { createRootNodes(); stage_sync.emplace( - zookeeper_path + "/stage", [this] { return getZooKeeper(); }, &Poco::Logger::get("RestoreCoordination")); + zookeeper_path + "/stage", [this] { return getZooKeeper(); }, log); } RestoreCoordinationRemote::~RestoreCoordinationRemote() @@ -197,7 +198,10 @@ bool RestoreCoordinationRemote::hasConcurrentRestores(const std::atomic const auto status = zk->get(root_zookeeper_path + "/" + existing_restore_path + "/stage"); if (status != Stage::COMPLETED) + { + LOG_WARNING(log, "Found a concurrent restore: {}, current restore: {}", existing_restore_uuid, toString(restore_uuid)); return true; + } } zk->createIfNotExists(path, ""); diff --git a/src/Backups/RestoreCoordinationRemote.h b/src/Backups/RestoreCoordinationRemote.h index b78c2e96f9e..e524e42c440 100644 --- a/src/Backups/RestoreCoordinationRemote.h +++ b/src/Backups/RestoreCoordinationRemote.h @@ -59,6 +59,7 @@ private: const String current_host; const size_t current_host_index; const bool is_internal; + Poco::Logger * const log; std::optional stage_sync; From 3b6c5992377d00de0f0159960be090c6b957f060 Mon Sep 17 00:00:00 2001 From: rfraposa Date: Fri, 31 Mar 2023 16:03:44 -0600 Subject: [PATCH 321/377] Update youtube-dislikes.md --- .../example-datasets/youtube-dislikes.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/en/getting-started/example-datasets/youtube-dislikes.md b/docs/en/getting-started/example-datasets/youtube-dislikes.md index c0a54906212..ea12042c635 100644 --- a/docs/en/getting-started/example-datasets/youtube-dislikes.md +++ b/docs/en/getting-started/example-datasets/youtube-dislikes.md @@ -142,12 +142,6 @@ Some comments about our `INSERT` command: - We used `ifNull` to avoid getting `NULL` values in our table. If an incoming value is `NULL`, the `ifNull` function is setting the value to an empty string - It takes a long time to download the data, so we added a `SETTINGS` clause to spread out the work over more threads while making sure the block sizes stayed fairly large -Here is the response when the data is fully loaded: - -```response - -``` - 4. Open a new tab in the SQL Console of ClickHouse Cloud (or a new `clickhouse-client` window) and watch the count increase. It will take a while to insert 4.56B rows, depending on your server resources. (Without any tweaking of settings, it takes about 4.5 hours.) ```sql @@ -221,7 +215,7 @@ FROM youtube WHERE (title ILIKE '%ClickHouse%') OR (description ILIKE '%ClickHouse%') ORDER BY like_count DESC, - view_count DESC + view_count DESC; ``` This query has to process every row, and also parse through two columns of strings. Even then, we get decent performance at 4.15M rows/second: @@ -268,7 +262,6 @@ ORDER BY ``` ```response - ┌─views─────────────┬─is_comments_enabled─┬────prob_like_dislike─┐ │ < 10.00 │ false │ 0.08224180712685371 │ │ < 100.00 │ false │ 0.06346337759167248 │ @@ -293,7 +286,6 @@ ORDER BY └───────────────────┴─────────────────────┴──────────────────────┘ 22 rows in set. Elapsed: 8.460 sec. Processed 4.56 billion rows, 77.48 GB (538.73 million rows/s., 9.16 GB/s.) - ``` Enabling comments seems to be correlated with a higher rate of engagement. From f96e7b59a282ec18961bfb4de25e181776d9ff9d Mon Sep 17 00:00:00 2001 From: kssenii Date: Sat, 1 Apr 2023 13:19:07 +0200 Subject: [PATCH 322/377] Better --- .../ClickHouseDictionarySource.cpp | 120 +++++++++--------- .../ExternalDataSourceConfiguration.cpp | 101 +++++---------- 2 files changed, 89 insertions(+), 132 deletions(-) diff --git a/src/Dictionaries/ClickHouseDictionarySource.cpp b/src/Dictionaries/ClickHouseDictionarySource.cpp index 6c1304ea027..f02e2995876 100644 --- a/src/Dictionaries/ClickHouseDictionarySource.cpp +++ b/src/Dictionaries/ClickHouseDictionarySource.cpp @@ -7,11 +7,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include "DictionarySourceFactory.h" @@ -29,10 +29,6 @@ namespace ErrorCodes extern const int BAD_ARGUMENTS; } -static const std::unordered_set dictionary_allowed_keys = { - "host", "port", "user", "password", "quota_key", "db", "database", "table", - "update_field", "update_lag", "invalidate_query", "query", "where", "name", "secure"}; - namespace { constexpr size_t MAX_CONNECTIONS = 16; @@ -214,73 +210,75 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) const std::string & config_prefix, Block & sample_block, ContextPtr global_context, - const std::string & default_database [[maybe_unused]], + const std::string & default_database, bool created_from_ddl) -> DictionarySourcePtr { - bool secure = config.getBool(config_prefix + ".secure", false); - - UInt16 default_port = getPortFromContext(global_context, secure); + using Configuration = ClickHouseDictionarySource::Configuration; + std::optional configuration; std::string settings_config_prefix = config_prefix + ".clickhouse"; - - std::string host = config.getString(settings_config_prefix + ".host", "localhost"); - std::string user = config.getString(settings_config_prefix + ".user", "default"); - std::string password = config.getString(settings_config_prefix + ".password", ""); - std::string quota_key = config.getString(settings_config_prefix + ".quota_key", ""); - std::string db = config.getString(settings_config_prefix + ".db", default_database); - std::string table = config.getString(settings_config_prefix + ".table", ""); - UInt16 port = static_cast(config.getUInt(settings_config_prefix + ".port", default_port)); - auto has_config_key = [](const String & key) { return dictionary_allowed_keys.contains(key); }; - bool secure_from_named = false; - - auto named_collection = created_from_ddl - ? getExternalDataSourceConfiguration(config, settings_config_prefix, global_context, has_config_key) - : std::nullopt; + auto named_collection = created_from_ddl ? tryGetNamedCollectionWithOverrides(config, settings_config_prefix) : nullptr; if (named_collection) { - const auto & configuration = named_collection->configuration; - host = configuration.host; - user = configuration.username; - password = configuration.password; - quota_key = configuration.quota_key; - db = configuration.database; - table = configuration.table; - port = configuration.port; + validateNamedCollection( + *named_collection, {}, ValidateKeysMultiset{ + "secure", "host", "hostnmae", "port", "user", "username", "password", "quota_key", "name", + "db", "database", "table","query", "where", "invalidate_query", "update_field", "update_lag"}); - const auto & storage_specific_args = named_collection->specific_args; - for (const auto & [arg_name, arg_value] : storage_specific_args) - { - if (arg_name == "secure") - { - secure_from_named = checkAndGetLiteralArgument(arg_value, "secure"); - } - } + const auto secure = named_collection->getOrDefault("secure", false); + const auto default_port = getPortFromContext(global_context, secure); + const auto host = named_collection->getAnyOrDefault({"host", "hostname"}, "localhost"); + const auto port = static_cast(named_collection->getOrDefault("port", default_port)); + + configuration.emplace(Configuration{ + .host = host, + .user = named_collection->getAnyOrDefault({"user", "username"}, "default"), + .password = named_collection->getOrDefault("password", ""), + .quota_key = named_collection->getOrDefault("quota_key", ""), + .db = named_collection->getAnyOrDefault({"db", "database"}, default_database), + .table = named_collection->getOrDefault("table", ""), + .query = named_collection->getOrDefault("query", ""), + .where = named_collection->getOrDefault("where", ""), + .invalidate_query = named_collection->getOrDefault("invalidate_query", ""), + .update_field = named_collection->getOrDefault("update_field", ""), + .update_lag = named_collection->getOrDefault("update_lag", 1), + .port = port, + .is_local = isLocalAddress({host, port}, default_port), + .secure = secure, + }); + } + else + { + const auto secure = config.getBool(settings_config_prefix + ".secure", false); + const auto default_port = getPortFromContext(global_context, secure); + const auto host = config.getString(settings_config_prefix + ".host", "localhost"); + const auto port = static_cast(config.getUInt(settings_config_prefix + ".port", default_port)); + + configuration.emplace(Configuration{ + .host = host, + .user = config.getString(settings_config_prefix + ".user", "default"), + .password = config.getString(settings_config_prefix + ".password", ""), + .quota_key = config.getString(settings_config_prefix + ".quota_key", ""), + .db = config.getString(settings_config_prefix + ".db", default_database), + .table = config.getString(settings_config_prefix + ".table", ""), + .query = config.getString(settings_config_prefix + ".query", ""), + .where = config.getString(settings_config_prefix + ".where", ""), + .invalidate_query = config.getString(settings_config_prefix + ".invalidate_query", ""), + .update_field = config.getString(settings_config_prefix + ".update_field", ""), + .update_lag = config.getUInt64(settings_config_prefix + ".update_lag", 1), + .port = port, + .is_local = isLocalAddress({host, port}, default_port), + .secure = secure, + }); } - ClickHouseDictionarySource::Configuration configuration{ - .host = host, - .user = user, - .password = password, - .quota_key = quota_key, - .db = db, - .table = table, - .query = config.getString(settings_config_prefix + ".query", ""), - .where = config.getString(settings_config_prefix + ".where", ""), - .invalidate_query = config.getString(settings_config_prefix + ".invalidate_query", ""), - .update_field = config.getString(settings_config_prefix + ".update_field", ""), - .update_lag = config.getUInt64(settings_config_prefix + ".update_lag", 1), - .port = port, - .is_local = isLocalAddress({host, port}, default_port), - .secure = config.getBool(settings_config_prefix + ".secure", secure_from_named)}; - - ContextMutablePtr context; - if (configuration.is_local) + if (configuration->is_local) { /// We should set user info even for the case when the dictionary is loaded in-process (without TCP communication). Session session(global_context, ClientInfo::Interface::LOCAL); - session.authenticate(configuration.user, configuration.password, Poco::Net::SocketAddress{}); + session.authenticate(configuration->user, configuration->password, Poco::Net::SocketAddress{}); context = session.makeQueryContext(); } else @@ -288,7 +286,7 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) context = Context::createCopy(global_context); if (created_from_ddl) - context->getRemoteHostFilter().checkHostAndPort(configuration.host, toString(configuration.port)); + context->getRemoteHostFilter().checkHostAndPort(configuration->host, toString(configuration->port)); } context->applySettingsChanges(readSettingsFromDictionaryConfig(config, config_prefix)); @@ -296,10 +294,10 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory) String dictionary_name = config.getString(".dictionary.name", ""); String dictionary_database = config.getString(".dictionary.database", ""); - if (dictionary_name == configuration.table && dictionary_database == configuration.db) + if (dictionary_name == configuration->table && dictionary_database == configuration->db) throw Exception(ErrorCodes::BAD_ARGUMENTS, "ClickHouseDictionarySource table cannot be dictionary table"); - return std::make_unique(dict_struct, configuration, sample_block, context); + return std::make_unique(dict_struct, *configuration, sample_block, context); }; factory.registerSource("clickhouse", create_table_source); diff --git a/src/Storages/ExternalDataSourceConfiguration.cpp b/src/Storages/ExternalDataSourceConfiguration.cpp index 33e5772ed3e..e503c5edaab 100644 --- a/src/Storages/ExternalDataSourceConfiguration.cpp +++ b/src/Storages/ExternalDataSourceConfiguration.cpp @@ -21,6 +21,13 @@ namespace ErrorCodes IMPLEMENT_SETTINGS_TRAITS(EmptySettingsTraits, EMPTY_SETTINGS) +static const std::unordered_set dictionary_allowed_keys = { + "host", "port", "user", "password", "quota_key", "db", + "database", "table", "schema", "replica", + "update_field", "update_lag", "invalidate_query", "query", + "where", "name", "secure", "uri", "collection"}; + + template SettingsChanges getSettingsChangesFromConfig( const BaseSettings & settings, const Poco::Util::AbstractConfiguration & config, const String & config_prefix) @@ -72,53 +79,6 @@ void ExternalDataSourceConfiguration::set(const ExternalDataSourceConfiguration addresses_expr = conf.addresses_expr; } -namespace -{ -void initExternalDataSourceConfiguration(ExternalDataSourceConfiguration & configuration) -{ - configuration = ExternalDataSourceConfiguration(); - configuration.username = ""; -} - -void readNamedCollection(const Poco::Util::AbstractConfiguration & config, - std::string_view collection_prefix, - ExternalDataSourceConfiguration & configuration) -{ - auto get_path = [collection_prefix](std::string_view fname) - { - return fmt::format("{}.{}", collection_prefix, fname); - }; - - configuration.host = config.getString(get_path("host"), configuration.host); - configuration.port = config.getInt(get_path("port"), configuration.port); - configuration.username = config.getString(get_path("user"), configuration.username); - configuration.password = config.getString(get_path("password"), configuration.password); - configuration.quota_key = config.getString(get_path("quota_key"), configuration.quota_key); - configuration.database = config.getString(get_path("db"), config.getString(get_path("database"), configuration.database)); - configuration.table = config.getString(get_path("table"), config.getString(get_path("collection"), configuration.table)); - configuration.schema = config.getString(get_path("schema"), configuration.schema); - configuration.addresses_expr = config.getString(get_path("addresses_expr"), configuration.addresses_expr); -} - -using ConfigWithPrefix = std::pair; - -/// Logical priority is from left to right. -/// If first element of config_with_prefix_vect does not have a particular field, -/// second element is used, etc. -/// Technically values are overwritten from right to left. -/// If no luck, default values come into play. -void readNamedCollection(const std::vector & config_with_prefix_vect, - ExternalDataSourceConfiguration & configuration) -{ - initExternalDataSourceConfiguration(configuration); - - for (auto it = std::crbegin(config_with_prefix_vect); it != std::crend(config_with_prefix_vect); ++it) - { - readNamedCollection((*it).first, (*it).second, configuration); - } -} -} - static void validateConfigKeys( const Poco::Util::AbstractConfiguration & dict_config, const String & config_prefix, HasConfigKeyFunc has_config_key_func) @@ -139,7 +99,6 @@ std::optional getExternalDataSourceConfiguration( { validateConfigKeys(dict_config, dict_config_prefix, has_config_key); ExternalDataSourceConfiguration configuration; - StorageSpecificArgs non_common_args; auto collection_name = dict_config.getString(dict_config_prefix + ".name", ""); if (!collection_name.empty()) @@ -155,16 +114,15 @@ std::optional getExternalDataSourceConfiguration( if (!config.has(collection_prefix)) throw Exception(ErrorCodes::BAD_ARGUMENTS, "There is no collection named `{}` in config", collection_name); - readNamedCollection({{dict_config, dict_config_prefix}, {config, collection_prefix}}, configuration); - - - if (dict_config.has(dict_config_prefix + ".secure") || config.has(collection_prefix + ".secure")) - { - uint64_t secure = dict_config.getBool(dict_config_prefix + ".secure", config.getBool(collection_prefix + ".secure", false)); - - non_common_args.emplace_back(std::make_pair("secure", std::make_shared(secure))); - } - + configuration.host = dict_config.getString(dict_config_prefix + ".host", config.getString(collection_prefix + ".host", "")); + configuration.port = dict_config.getInt(dict_config_prefix + ".port", config.getUInt(collection_prefix + ".port", 0)); + configuration.username = dict_config.getString(dict_config_prefix + ".user", config.getString(collection_prefix + ".user", "")); + configuration.password = dict_config.getString(dict_config_prefix + ".password", config.getString(collection_prefix + ".password", "")); + configuration.quota_key = dict_config.getString(dict_config_prefix + ".quota_key", config.getString(collection_prefix + ".quota_key", "")); + configuration.database = dict_config.getString(dict_config_prefix + ".db", config.getString(dict_config_prefix + ".database", + config.getString(collection_prefix + ".db", config.getString(collection_prefix + ".database", "")))); + configuration.table = dict_config.getString(dict_config_prefix + ".table", config.getString(collection_prefix + ".table", "")); + configuration.schema = dict_config.getString(dict_config_prefix + ".schema", config.getString(collection_prefix + ".schema", "")); if (configuration.host.empty() || configuration.port == 0 || configuration.username.empty() || configuration.table.empty()) { @@ -172,7 +130,7 @@ std::optional getExternalDataSourceConfiguration( "Named collection of connection parameters is missing some " "of the parameters and dictionary parameters are not added"); } - return ExternalDataSourceInfo{ .configuration = configuration, .specific_args = non_common_args, .settings_changes = config_settings }; + return ExternalDataSourceInfo{ .configuration = configuration, .specific_args = {}, .settings_changes = config_settings }; } return std::nullopt; } @@ -251,7 +209,14 @@ ExternalDataSourcesByPriority getExternalDataSourceConfigurationByPriority( } else { - readNamedCollection({{dict_config, dict_config_prefix}}, common_configuration); + common_configuration.host = dict_config.getString(dict_config_prefix + ".host", ""); + common_configuration.port = dict_config.getUInt(dict_config_prefix + ".port", 0); + common_configuration.username = dict_config.getString(dict_config_prefix + ".user", ""); + common_configuration.password = dict_config.getString(dict_config_prefix + ".password", ""); + common_configuration.quota_key = dict_config.getString(dict_config_prefix + ".quota_key", ""); + common_configuration.database = dict_config.getString(dict_config_prefix + ".db", dict_config.getString(dict_config_prefix + ".database", "")); + common_configuration.table = dict_config.getString(fmt::format("{}.table", dict_config_prefix), ""); + common_configuration.schema = dict_config.getString(fmt::format("{}.schema", dict_config_prefix), ""); } ExternalDataSourcesByPriority configuration @@ -276,10 +241,11 @@ ExternalDataSourcesByPriority getExternalDataSourceConfigurationByPriority( validateConfigKeys(dict_config, replica_name, has_config_key); size_t priority = dict_config.getInt(replica_name + ".priority", 0); - - readNamedCollection({{dict_config, replica_name}, - {dict_config, dict_config_prefix}}, replica_configuration); - + replica_configuration.host = dict_config.getString(replica_name + ".host", common_configuration.host); + replica_configuration.port = dict_config.getUInt(replica_name + ".port", common_configuration.port); + replica_configuration.username = dict_config.getString(replica_name + ".user", common_configuration.username); + replica_configuration.password = dict_config.getString(replica_name + ".password", common_configuration.password); + replica_configuration.quota_key = dict_config.getString(replica_name + ".quota_key", common_configuration.quota_key); if (replica_configuration.host.empty() || replica_configuration.port == 0 || replica_configuration.username.empty() || replica_configuration.password.empty()) @@ -288,13 +254,6 @@ ExternalDataSourcesByPriority getExternalDataSourceConfigurationByPriority( "Named collection of connection parameters is missing some " "of the parameters and no other dictionary parameters are added"); } - if (replica_configuration.database != common_configuration.database - || replica_configuration.table != common_configuration.table - || replica_configuration.schema != common_configuration.schema) - { - throw Exception(ErrorCodes::BAD_ARGUMENTS, - "Named collection of connection parameters is not consistent"); - } configuration.replicas_configurations[priority].emplace_back(replica_configuration); } From 48b23dd012d95d2b2d477d4fa448f4e88f628c8e Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Sat, 1 Apr 2023 15:26:00 +0300 Subject: [PATCH 323/377] Fix race between DROP MatView and RESTART REPLICAS (#47863) * fix race between drop mv and restart replicas * unrelated: fix bad exception messages * fix test * fix * fix * fix * fix * fix test * fix * fix test * Update 02437_drop_mv_restart_replicas.sh * fix tests --- src/DataTypes/DataTypeDecimalBase.h | 7 ++- src/DataTypes/DataTypesDecimal.h | 8 ++- src/Functions/array/arrayAggregation.cpp | 6 +- src/Functions/pointInEllipses.cpp | 5 +- src/Functions/pointInPolygon.cpp | 4 +- src/Functions/svg.cpp | 11 +--- src/Interpreters/InterpreterDropQuery.cpp | 22 ++++--- src/Interpreters/InterpreterDropQuery.h | 3 +- src/Interpreters/InterpreterSystemQuery.cpp | 4 +- .../StorageMaterializedPostgreSQL.cpp | 2 +- src/Storages/StorageMaterializedView.cpp | 17 ++++- src/Storages/WindowView/StorageWindowView.cpp | 4 +- tests/ci/stress_tests.lib | 7 ++- tests/clickhouse-test | 13 ++-- .../test.py | 4 +- .../02437_drop_mv_restart_replicas.reference | 0 .../02437_drop_mv_restart_replicas.sh | 63 +++++++++++++++++++ 17 files changed, 139 insertions(+), 41 deletions(-) create mode 100644 tests/queries/0_stateless/02437_drop_mv_restart_replicas.reference create mode 100755 tests/queries/0_stateless/02437_drop_mv_restart_replicas.sh diff --git a/src/DataTypes/DataTypeDecimalBase.h b/src/DataTypes/DataTypeDecimalBase.h index 0be345ba879..adbe9c95b14 100644 --- a/src/DataTypes/DataTypeDecimalBase.h +++ b/src/DataTypes/DataTypeDecimalBase.h @@ -71,9 +71,12 @@ public: scale(scale_) { if (unlikely(precision < 1 || precision > maxPrecision())) - throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Precision {} is out of bounds", std::to_string(precision)); + throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, + "Precision {} is out of bounds (precision range: [1, {}])", + std::to_string(precision), maxPrecision()); if (unlikely(scale > maxPrecision())) - throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds", std::to_string(scale)); + throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds (max scale: {})", + std::to_string(scale), maxPrecision()); } TypeIndex getTypeId() const override { return TypeToTypeIndex; } diff --git a/src/DataTypes/DataTypesDecimal.h b/src/DataTypes/DataTypesDecimal.h index 7a49238b5be..583f7ea804a 100644 --- a/src/DataTypes/DataTypesDecimal.h +++ b/src/DataTypes/DataTypesDecimal.h @@ -116,7 +116,8 @@ inline ReturnType convertDecimalsImpl(const typename FromDataType::FieldType & v if (common::mulOverflow(static_cast(value.value), converted_value, converted_value)) { if constexpr (throw_exception) - throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "{} convert overflow", std::string(ToDataType::family_name)); + throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "{} convert overflow while multiplying {} by scale {}", + std::string(ToDataType::family_name), toString(value.value), toString(converted_value)); else return ReturnType(false); } @@ -136,7 +137,10 @@ inline ReturnType convertDecimalsImpl(const typename FromDataType::FieldType & v converted_value > std::numeric_limits::max()) { if constexpr (throw_exception) - throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "{} convert overflow", std::string(ToDataType::family_name)); + throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "{} convert overflow: {} is not in range ({}, {})", + std::string(ToDataType::family_name), toString(converted_value), + toString(std::numeric_limits::min()), + toString(std::numeric_limits::max())); else return ReturnType(false); } diff --git a/src/Functions/array/arrayAggregation.cpp b/src/Functions/array/arrayAggregation.cpp index 59991b7b313..8818ebde9f1 100644 --- a/src/Functions/array/arrayAggregation.cpp +++ b/src/Functions/array/arrayAggregation.cpp @@ -223,7 +223,8 @@ struct ArrayAggregateImpl auto result_scale = column_typed->getScale() * array_size; if (unlikely(result_scale > DecimalUtils::max_precision)) - throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds", result_scale); + throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds (max scale: {})", + result_scale, DecimalUtils::max_precision); res[i] = DecimalUtils::convertTo(product, static_cast(result_scale)); } @@ -332,7 +333,8 @@ struct ArrayAggregateImpl auto result_scale = column->getScale() * count; if (unlikely(result_scale > DecimalUtils::max_precision)) - throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds", result_scale); + throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds (max scale: {})", + result_scale, DecimalUtils::max_precision); res[i] = DecimalUtils::convertTo(aggregate_value, static_cast(result_scale)); } diff --git a/src/Functions/pointInEllipses.cpp b/src/Functions/pointInEllipses.cpp index 208f2ad2f82..2147428cee3 100644 --- a/src/Functions/pointInEllipses.cpp +++ b/src/Functions/pointInEllipses.cpp @@ -71,8 +71,9 @@ private: /// For array on stack, see below. if (arguments.size() > 10000) { - throw Exception(ErrorCodes::TOO_MANY_ARGUMENTS_FOR_FUNCTION, "Number of arguments of function {} is too large.", - getName()); + throw Exception(ErrorCodes::TOO_MANY_ARGUMENTS_FOR_FUNCTION, + "Number of arguments of function {} is too large (maximum: 10000).", + getName()); } for (const auto arg_idx : collections::range(0, arguments.size())) diff --git a/src/Functions/pointInPolygon.cpp b/src/Functions/pointInPolygon.cpp index 24ad1d20611..0e4467a8210 100644 --- a/src/Functions/pointInPolygon.cpp +++ b/src/Functions/pointInPolygon.cpp @@ -37,7 +37,7 @@ namespace DB { namespace ErrorCodes { - extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION; + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int BAD_ARGUMENTS; extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_COLUMN; @@ -87,7 +87,7 @@ public: { if (arguments.size() < 2) { - throw Exception(ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION, "Too few arguments"); + throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Function {} requires at least 2 arguments", getName()); } /** We allow function invocation in one of the following forms: diff --git a/src/Functions/svg.cpp b/src/Functions/svg.cpp index 69e619df901..f8f85216b3f 100644 --- a/src/Functions/svg.cpp +++ b/src/Functions/svg.cpp @@ -13,8 +13,7 @@ namespace DB namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; - extern const int TOO_MANY_ARGUMENTS_FOR_FUNCTION; - extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION; + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } class FunctionSvg : public IFunction @@ -48,13 +47,9 @@ public: DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override { - if (arguments.size() > 2) + if (arguments.empty() || arguments.size() > 2) { - throw Exception(ErrorCodes::TOO_MANY_ARGUMENTS_FOR_FUNCTION, "Too many arguments"); - } - else if (arguments.empty()) - { - throw Exception(ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION, "Too few arguments"); + throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Incorrect number of arguments: expected 1 or 2 arguments"); } else if (arguments.size() == 2 && checkAndGetDataType(arguments[1].get()) == nullptr) { diff --git a/src/Interpreters/InterpreterDropQuery.cpp b/src/Interpreters/InterpreterDropQuery.cpp index e16403bed67..0beb4492aef 100644 --- a/src/Interpreters/InterpreterDropQuery.cpp +++ b/src/Interpreters/InterpreterDropQuery.cpp @@ -355,19 +355,22 @@ BlockIO InterpreterDropQuery::executeToDatabaseImpl(const ASTDropQuery & query, /// Flush should not be done if shouldBeEmptyOnDetach() == false, /// since in this case getTablesIterator() may do some additional work, /// see DatabaseMaterializedMySQL::getTablesIterator() - for (auto iterator = database->getTablesIterator(getContext()); iterator->isValid(); iterator->next()) - { - iterator->table()->flush(); - } - auto table_context = Context::createCopy(getContext()); table_context->setInternalQuery(true); + /// Do not hold extra shared pointers to tables + std::vector> tables_to_drop; for (auto iterator = database->getTablesIterator(table_context); iterator->isValid(); iterator->next()) { + iterator->table()->flush(); + tables_to_drop.push_back({iterator->name(), iterator->table()->isDictionary()}); + } + + for (const auto & table : tables_to_drop) + { + query_for_table.setTable(table.first); + query_for_table.is_dictionary = table.second; DatabasePtr db; UUID table_to_wait = UUIDHelpers::Nil; - query_for_table.setTable(iterator->name()); - query_for_table.is_dictionary = iterator->table()->isDictionary(); executeToTableImpl(table_context, query_for_table, db, table_to_wait); uuids_to_wait.push_back(table_to_wait); } @@ -428,7 +431,8 @@ AccessRightsElements InterpreterDropQuery::getRequiredAccessForDDLOnCluster() co return required_access; } -void InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind kind, ContextPtr global_context, ContextPtr current_context, const StorageID & target_table_id, bool sync) +void InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind kind, ContextPtr global_context, ContextPtr current_context, + const StorageID & target_table_id, bool sync, bool ignore_sync_setting) { if (DatabaseCatalog::instance().tryGetTable(target_table_id, current_context)) { @@ -445,6 +449,8 @@ void InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind kind, ContextPtr /// and not allowed to drop inner table explicitly. Allowing to drop inner table without explicit grant /// looks like expected behaviour and we have tests for it. auto drop_context = Context::createCopy(global_context); + if (ignore_sync_setting) + drop_context->setSetting("database_atomic_wait_for_drop_and_detach_synchronously", false); drop_context->getClientInfo().query_kind = ClientInfo::QueryKind::SECONDARY_QUERY; if (auto txn = current_context->getZooKeeperMetadataTransaction()) { diff --git a/src/Interpreters/InterpreterDropQuery.h b/src/Interpreters/InterpreterDropQuery.h index afec26424ba..af7a4ddef25 100644 --- a/src/Interpreters/InterpreterDropQuery.h +++ b/src/Interpreters/InterpreterDropQuery.h @@ -24,7 +24,8 @@ public: /// Drop table or database. BlockIO execute() override; - static void executeDropQuery(ASTDropQuery::Kind kind, ContextPtr global_context, ContextPtr current_context, const StorageID & target_table_id, bool sync); + static void executeDropQuery(ASTDropQuery::Kind kind, ContextPtr global_context, ContextPtr current_context, + const StorageID & target_table_id, bool sync, bool ignore_sync_setting = false); bool supportsTransactions() const override; diff --git a/src/Interpreters/InterpreterSystemQuery.cpp b/src/Interpreters/InterpreterSystemQuery.cpp index b45618be1f8..e9905821fd1 100644 --- a/src/Interpreters/InterpreterSystemQuery.cpp +++ b/src/Interpreters/InterpreterSystemQuery.cpp @@ -604,6 +604,7 @@ void InterpreterSystemQuery::restoreReplica() StoragePtr InterpreterSystemQuery::tryRestartReplica(const StorageID & replica, ContextMutablePtr system_context, bool need_ddl_guard) { + LOG_TRACE(log, "Restarting replica {}", replica); auto table_ddl_guard = need_ddl_guard ? DatabaseCatalog::instance().getDDLGuard(replica.getDatabaseName(), replica.getTableName()) : nullptr; @@ -647,6 +648,7 @@ StoragePtr InterpreterSystemQuery::tryRestartReplica(const StorageID & replica, database->attachTable(system_context, replica.table_name, table, data_path); table->startup(); + LOG_TRACE(log, "Restarted replica {}", replica); return table; } @@ -693,11 +695,11 @@ void InterpreterSystemQuery::restartReplicas(ContextMutablePtr system_context) guard.second = catalog.getDDLGuard(guard.first.database_name, guard.first.table_name); size_t threads = std::min(static_cast(getNumberOfPhysicalCPUCores()), replica_names.size()); + LOG_DEBUG(log, "Will restart {} replicas using {} threads", replica_names.size(), threads); ThreadPool pool(CurrentMetrics::RestartReplicaThreads, CurrentMetrics::RestartReplicaThreadsActive, threads); for (auto & replica : replica_names) { - LOG_TRACE(log, "Restarting replica on {}", replica.getNameForLogs()); pool.scheduleOrThrowOnError([&]() { tryRestartReplica(replica, system_context, false); }); } pool.wait(); diff --git a/src/Storages/PostgreSQL/StorageMaterializedPostgreSQL.cpp b/src/Storages/PostgreSQL/StorageMaterializedPostgreSQL.cpp index d194c8b8201..78e72564ab7 100644 --- a/src/Storages/PostgreSQL/StorageMaterializedPostgreSQL.cpp +++ b/src/Storages/PostgreSQL/StorageMaterializedPostgreSQL.cpp @@ -250,7 +250,7 @@ void StorageMaterializedPostgreSQL::dropInnerTableIfAny(bool sync, ContextPtr lo auto nested_table = tryGetNested() != nullptr; if (nested_table) - InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind::Drop, getContext(), local_context, getNestedStorageID(), sync); + InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind::Drop, getContext(), local_context, getNestedStorageID(), sync, /* ignore_sync_setting */ true); } diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index ae3fa62b38c..b96c132d601 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -212,13 +212,26 @@ void StorageMaterializedView::drop() if (!select_query.select_table_id.empty()) DatabaseCatalog::instance().removeViewDependency(select_query.select_table_id, table_id); - dropInnerTableIfAny(true, getContext()); + /// Sync flag and the setting make sense for Atomic databases only. + /// However, with Atomic databases, IStorage::drop() can be called only from a background task in DatabaseCatalog. + /// Running synchronous DROP from that task leads to deadlock. + /// Usually dropInnerTableIfAny is no-op, because the inner table is dropped before enqueueing a drop task for the MV itself. + /// But there's a race condition with SYSTEM RESTART REPLICA: the inner table might be detached due to RESTART. + /// In this case, dropInnerTableIfAny will not find the inner table and will not drop it during executions of DROP query for the MV itself. + /// DDLGuard does not protect from that, because RESTART REPLICA acquires DDLGuard for the inner table name, + /// but DROP acquires DDLGuard for the name of MV. And we cannot acquire second DDLGuard for the inner name in DROP, + /// because it may lead to lock-order-inversion (DDLGuards must be acquired in lexicographical order). + dropInnerTableIfAny(/* sync */ false, getContext()); } void StorageMaterializedView::dropInnerTableIfAny(bool sync, ContextPtr local_context) { + /// We will use `sync` argument wneh this function is called from a DROP query + /// and will ignore database_atomic_wait_for_drop_and_detach_synchronously when it's called from drop task. + /// See the comment in StorageMaterializedView::drop if (has_inner_table && tryGetTargetTable()) - InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind::Drop, getContext(), local_context, target_table_id, sync); + InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind::Drop, getContext(), local_context, target_table_id, + sync, /* ignore_sync_setting */ true); } void StorageMaterializedView::truncate(const ASTPtr &, const StorageMetadataPtr &, ContextPtr local_context, TableExclusiveLockHolder &) diff --git a/src/Storages/WindowView/StorageWindowView.cpp b/src/Storages/WindowView/StorageWindowView.cpp index 3471e4ea6bf..bfa126c3525 100644 --- a/src/Storages/WindowView/StorageWindowView.cpp +++ b/src/Storages/WindowView/StorageWindowView.cpp @@ -1609,7 +1609,7 @@ void StorageWindowView::drop() { /// Must be guaranteed at this point for database engine Atomic that has_inner_table == false, /// because otherwise will be a deadlock. - dropInnerTableIfAny(true, getContext()); + dropInnerTableIfAny(false, getContext()); } void StorageWindowView::dropInnerTableIfAny(bool sync, ContextPtr local_context) @@ -1623,7 +1623,7 @@ void StorageWindowView::dropInnerTableIfAny(bool sync, ContextPtr local_context) ASTDropQuery::Kind::Drop, getContext(), local_context, inner_table_id, sync); if (has_inner_target_table) - InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind::Drop, getContext(), local_context, target_table_id, sync); + InterpreterDropQuery::executeDropQuery(ASTDropQuery::Kind::Drop, getContext(), local_context, target_table_id, sync, /* ignore_sync_setting */ true); } catch (...) { diff --git a/tests/ci/stress_tests.lib b/tests/ci/stress_tests.lib index 75195baaeeb..04df50b3248 100644 --- a/tests/ci/stress_tests.lib +++ b/tests/ci/stress_tests.lib @@ -149,9 +149,14 @@ function stop() if [ $check_hang == true ] then # We failed to stop the server with SIGTERM. Maybe it hang, let's collect stacktraces. - echo -e "Possible deadlock on shutdown (see gdb.log)$FAIL" >> /test_output/test_results.tsv + # Add a special status just in case, so it will be possible to find in the CI DB + echo -e "Warning: server did not stop yet$OK" >> /test_output/test_results.tsv kill -TERM "$(pidof gdb)" ||: sleep 5 + + # The server could finally stop while we were terminating gdb, let's recheck if it's still running + kill -s 0 $pid || return + echo -e "Possible deadlock on shutdown (see gdb.log)$FAIL" >> /test_output/test_results.tsv echo "thread apply all backtrace (on stop)" >> /test_output/gdb.log timeout 30m gdb -batch -ex 'thread apply all backtrace' -p "$pid" | ts '%Y-%m-%d %H:%M:%S' >> /test_output/gdb.log clickhouse stop --force diff --git a/tests/clickhouse-test b/tests/clickhouse-test index a355c2f8e73..d407f73d033 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -278,7 +278,7 @@ def need_retry(args, stdout, stderr, total_time): def get_processlist_with_stacktraces(args): try: if args.replicated_database: - return clickhouse_execute_json( + return clickhouse_execute( args, """ SELECT materialize(hostName() || '::' || tcpPort()::String) as host_port, * @@ -295,14 +295,14 @@ def get_processlist_with_stacktraces(args): WHERE query NOT LIKE '%system.processes%' GROUP BY p.* )) - ORDER BY elapsed DESC + ORDER BY elapsed DESC FORMAT Vertical """, settings={ "allow_introspection_functions": 1, }, ) else: - return clickhouse_execute_json( + return clickhouse_execute( args, """ SELECT @@ -315,7 +315,7 @@ def get_processlist_with_stacktraces(args): JOIN system.stack_trace s USING (query_id) WHERE query NOT LIKE '%system.processes%' GROUP BY p.* - ORDER BY elapsed DESC + ORDER BY elapsed DESC FORMAT Vertical """, settings={ "allow_introspection_functions": 1, @@ -2058,7 +2058,10 @@ def reportLogStats(args): 'Table {} is not replicated', '{} {}.{} already exists', 'Attempt to read after eof', 'Replica {} already exists', 'Convert overflow', 'key must be a tuple', 'Division by zero', 'No part {} in committed state', 'Files set to {}', 'Bytes set to {}', 'Sharding key {} is not used', - 'Cannot parse datetime', 'Bad get: has {}, requested {}', 'There is no {} in {}', 'Numeric overflow' + 'Cannot parse datetime', 'Bad get: has {}, requested {}', 'There is no {} in {}', 'Numeric overflow', + 'Polygon is not valid: {}', 'Decimal math overflow', '{} only accepts maps', 'Dictionary ({}) not found', + 'Unknown format {}', 'Invalid IPv4 value', 'Invalid IPv6 value', 'Unknown setting {}', + 'Unknown table function {}' ) AS known_short_messages SELECT count() AS c, message_format_string, substr(any(message), 1, 120) FROM system.text_log diff --git a/tests/integration/test_version_update_after_mutation/test.py b/tests/integration/test_version_update_after_mutation/test.py index 6b27c69462a..67f7ce47451 100644 --- a/tests/integration/test_version_update_after_mutation/test.py +++ b/tests/integration/test_version_update_after_mutation/test.py @@ -91,8 +91,8 @@ def test_mutate_and_upgrade(start_cluster): node2.query("OPTIMIZE TABLE mt FINAL") - assert node1.query("SELECT id FROM mt") == "1\n4\n" - assert node2.query("SELECT id FROM mt") == "1\n4\n" + assert node1.query("SELECT id FROM mt ORDER BY id") == "1\n4\n" + assert node2.query("SELECT id FROM mt ORDER BY id") == "1\n4\n" for node in [node1, node2]: node.query("DROP TABLE mt") diff --git a/tests/queries/0_stateless/02437_drop_mv_restart_replicas.reference b/tests/queries/0_stateless/02437_drop_mv_restart_replicas.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/02437_drop_mv_restart_replicas.sh b/tests/queries/0_stateless/02437_drop_mv_restart_replicas.sh new file mode 100755 index 00000000000..e4f52b6e4ad --- /dev/null +++ b/tests/queries/0_stateless/02437_drop_mv_restart_replicas.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Tags: long, zookeeper, race, no-ordinary-database + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +$CLICKHOUSE_CLIENT -q "create user u_$CLICKHOUSE_DATABASE" +$CLICKHOUSE_CLIENT -q "grant all on db_$CLICKHOUSE_DATABASE.* to u_$CLICKHOUSE_DATABASE" + +# For tests with Replicated +ENGINE=$($CLICKHOUSE_CLIENT -q "select replace(engine_full, '$CLICKHOUSE_DATABASE', 'db_$CLICKHOUSE_DATABASE') from system.databases where name='$CLICKHOUSE_DATABASE' format TSVRaw") +export ENGINE + +function thread_ddl() +{ + while true; do + $CLICKHOUSE_CLIENT -q "create database if not exists db_$CLICKHOUSE_DATABASE engine=$ENGINE" + $CLICKHOUSE_CLIENT -q "CREATE TABLE if not exists db_$CLICKHOUSE_DATABASE.test (test String, A Int64, B Int64) ENGINE = ReplicatedMergeTree ('/clickhouse/tables/{database}/test_02124/{table}', '1') ORDER BY tuple();" + $CLICKHOUSE_CLIENT -q "CREATE MATERIALIZED VIEW if not exists db_$CLICKHOUSE_DATABASE.test_mv_a Engine=ReplicatedMergeTree ('/clickhouse/tables/{database}/test_02124/{table}', '1') order by tuple() AS SELECT test, A, count() c FROM db_$CLICKHOUSE_DATABASE.test group by test, A;" + $CLICKHOUSE_CLIENT -q "CREATE MATERIALIZED VIEW if not exists db_$CLICKHOUSE_DATABASE.test_mv_b Engine=ReplicatedMergeTree ('/clickhouse/tables/{database}/test_02124/{table}', '1') partition by A order by tuple() AS SELECT test, A, count() c FROM db_$CLICKHOUSE_DATABASE.test group by test, A;" + $CLICKHOUSE_CLIENT -q "CREATE MATERIALIZED VIEW if not exists db_$CLICKHOUSE_DATABASE.test_mv_c Engine=ReplicatedMergeTree ('/clickhouse/tables/{database}/test_02124/{table}', '1') order by tuple() AS SELECT test, A, count() c FROM db_$CLICKHOUSE_DATABASE.test group by test, A;" + sleep 0.$RANDOM; + + # A kind of backoff + timeout 5s $CLICKHOUSE_CLIENT -q "select sleepEachRow(0.1) from system.dropped_tables format Null" 2>/dev/null ||: + + $CLICKHOUSE_CLIENT -q "drop database if exists db_$CLICKHOUSE_DATABASE" + done +} + +function thread_insert() +{ + while true; do + $CLICKHOUSE_CLIENT -q "INSERT INTO db_$CLICKHOUSE_DATABASE.test SELECT 'case1', number%3, rand() FROM numbers(5)" + sleep 0.$RANDOM; + done +} + +function thread_restart() +{ + while true; do + # The simplest way to restart only replicas from a specific database is to use a special user + $CLICKHOUSE_CLIENT --user "u_$CLICKHOUSE_DATABASE" -q "system restart replicas" + sleep 0.$RANDOM; + done +} + +export -f thread_ddl; +export -f thread_insert; +export -f thread_restart; + +TIMEOUT=15 + +timeout $TIMEOUT bash -c thread_ddl 2>&1| grep -Fa "Exception: " | grep -Fv -e "TABLE_IS_DROPPED" -e "UNKNOWN_TABLE" -e "DATABASE_NOT_EMPTY" & +timeout $TIMEOUT bash -c thread_insert 2> /dev/null & +timeout $TIMEOUT bash -c thread_restart 2>&1| grep -Fa "Exception: " | grep -Fv -e "is currently dropped or renamed" & + +wait + +timeout 45s $CLICKHOUSE_CLIENT -q "select sleepEachRow(0.3) from system.dropped_tables format Null" 2>/dev/null ||: + +$CLICKHOUSE_CLIENT -q "drop database if exists db_$CLICKHOUSE_DATABASE" 2>&1| grep -Fa "Exception: " | grep -Fv -e "TABLE_IS_DROPPED" -e "UNKNOWN_TABLE" -e "DATABASE_NOT_EMPTY" ||: From ba6ecd2d4ea063366097ed5bad05a358c4283b74 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 1 Apr 2023 15:02:58 +0200 Subject: [PATCH 324/377] Fix ThreadPool for DistributedSink Signed-off-by: Azat Khuzhin --- src/Common/CurrentMetrics.cpp | 2 ++ src/Storages/Distributed/DistributedSink.cpp | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Common/CurrentMetrics.cpp b/src/Common/CurrentMetrics.cpp index 542c48148c8..cde020509ee 100644 --- a/src/Common/CurrentMetrics.cpp +++ b/src/Common/CurrentMetrics.cpp @@ -126,6 +126,8 @@ M(DDLWorkerThreadsActive, "Number of threads in the DDLWORKER thread pool for ON CLUSTER queries running a task.") \ M(StorageDistributedThreads, "Number of threads in the StorageDistributed thread pool.") \ M(StorageDistributedThreadsActive, "Number of threads in the StorageDistributed thread pool running a task.") \ + M(DistributedInsertThreads, "Number of threads used for INSERT into Distributed.") \ + M(DistributedInsertThreadsActive, "Number of threads used for INSERT into Distributed running a task.") \ M(StorageS3Threads, "Number of threads in the StorageS3 thread pool.") \ M(StorageS3ThreadsActive, "Number of threads in the StorageS3 thread pool running a task.") \ M(MergeTreePartsLoaderThreads, "Number of threads in the MergeTree parts loader thread pool.") \ diff --git a/src/Storages/Distributed/DistributedSink.cpp b/src/Storages/Distributed/DistributedSink.cpp index 11b938cd722..720a951299a 100644 --- a/src/Storages/Distributed/DistributedSink.cpp +++ b/src/Storages/Distributed/DistributedSink.cpp @@ -41,6 +41,8 @@ namespace CurrentMetrics { extern const Metric DistributedSend; + extern const Metric DistributedInsertThreads; + extern const Metric DistributedInsertThreadsActive; } namespace ProfileEvents @@ -460,9 +462,10 @@ void DistributedSink::writeSync(const Block & block) size_t jobs_count = random_shard_insert ? 1 : (remote_jobs_count + local_jobs_count); size_t max_threads = std::min(settings.max_distributed_connections, jobs_count); - pool.emplace(/* max_threads_= */ max_threads, - /* max_free_threads_= */ max_threads, - /* queue_size_= */ jobs_count); + pool.emplace( + CurrentMetrics::DistributedInsertThreads, + CurrentMetrics::DistributedInsertThreadsActive, + max_threads, max_threads, jobs_count); if (!throttler && (settings.max_network_bandwidth || settings.max_network_bytes)) { From f69441b6339f79fae29c969f54f6cd687077a2eb Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 1 Apr 2023 15:22:13 +0200 Subject: [PATCH 325/377] Fix ThreadPool usage in gtest_thread_pool_limit Signed-off-by: Azat Khuzhin --- src/Common/tests/gtest_thread_pool_limit.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Common/tests/gtest_thread_pool_limit.cpp b/src/Common/tests/gtest_thread_pool_limit.cpp index bc67ffd0bc1..17f79d17894 100644 --- a/src/Common/tests/gtest_thread_pool_limit.cpp +++ b/src/Common/tests/gtest_thread_pool_limit.cpp @@ -1,16 +1,23 @@ #include #include #include +#include #include +namespace CurrentMetrics +{ + extern const Metric LocalThread; + extern const Metric LocalThreadActive; +} + /// Test for thread self-removal when number of free threads in pool is too large. /// Just checks that nothing weird happens. template int test() { - Pool pool(10, 2, 10); + Pool pool(CurrentMetrics::LocalThread, CurrentMetrics::LocalThreadActive, 10, 2, 10); std::atomic counter{0}; for (size_t i = 0; i < 10; ++i) From 3ede50ccfc54e32228e19bca415491940d00f051 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 1 Apr 2023 15:05:35 +0200 Subject: [PATCH 326/377] Fix MaxPushedDDLEntryID --- src/Interpreters/DDLWorker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Interpreters/DDLWorker.cpp b/src/Interpreters/DDLWorker.cpp index 22bece0ef04..c4529af2c51 100644 --- a/src/Interpreters/DDLWorker.cpp +++ b/src/Interpreters/DDLWorker.cpp @@ -1022,10 +1022,10 @@ String DDLWorker::enqueueQuery(DDLLogEntry & entry) { String str_buf = node_path.substr(query_path_prefix.length()); DB::ReadBufferFromString in(str_buf); - CurrentMetrics::Metric id; - readText(id, in); - id = std::max(*max_pushed_entry_metric, id); - CurrentMetrics::set(*max_pushed_entry_metric, id); + CurrentMetrics::Value pushed_entry; + readText(pushed_entry, in); + pushed_entry = std::max(CurrentMetrics::get(*max_pushed_entry_metric), pushed_entry); + CurrentMetrics::set(*max_pushed_entry_metric, pushed_entry); } /// We cannot create status dirs in a single transaction with previous request, From 81e635a293805a0d59ace0f47a596f686b0ef347 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 1 Apr 2023 11:48:47 +0200 Subject: [PATCH 327/377] Mark operator constexpr in Strongtypedef --- base/base/strong_typedef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/base/strong_typedef.h b/base/base/strong_typedef.h index 2ddea6412f5..b3b8bced688 100644 --- a/base/base/strong_typedef.h +++ b/base/base/strong_typedef.h @@ -35,7 +35,7 @@ public: Self & operator=(T && rhs) { t = std::move(rhs); return *this;} // NOLINTBEGIN(google-explicit-constructor) - operator const T & () const { return t; } + constexpr operator const T & () const { return t; } operator T & () { return t; } // NOLINTEND(google-explicit-constructor) From 8d0e516310edcf9bb4202113d15c4ab42f75c31b Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 1 Apr 2023 11:17:23 +0200 Subject: [PATCH 328/377] Use StrongTypedef for CurrentMetrics to avoid possible incorrect usage Signed-off-by: Azat Khuzhin --- programs/server/MetricsTransmitter.cpp | 2 +- src/Common/CurrentMetrics.cpp | 4 ++-- src/Common/CurrentMetrics.h | 3 ++- src/Disks/TemporaryFileOnDisk.cpp | 2 +- src/Disks/TemporaryFileOnDisk.h | 2 +- src/Interpreters/MetricLog.cpp | 2 +- src/Interpreters/TemporaryDataOnDisk.cpp | 2 +- src/Interpreters/TemporaryDataOnDisk.h | 4 ++-- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/programs/server/MetricsTransmitter.cpp b/programs/server/MetricsTransmitter.cpp index 2f28f0a1d16..1672b0cd4dc 100644 --- a/programs/server/MetricsTransmitter.cpp +++ b/programs/server/MetricsTransmitter.cpp @@ -110,7 +110,7 @@ void MetricsTransmitter::transmit(std::vector & prev_count if (send_metrics) { - for (size_t i = 0, end = CurrentMetrics::end(); i < end; ++i) + for (CurrentMetrics::Metric i = CurrentMetrics::Metric(0), end = CurrentMetrics::end(); i < end; ++i) { const auto value = CurrentMetrics::values[i].load(std::memory_order_relaxed); diff --git a/src/Common/CurrentMetrics.cpp b/src/Common/CurrentMetrics.cpp index cde020509ee..cfe9f41befe 100644 --- a/src/Common/CurrentMetrics.cpp +++ b/src/Common/CurrentMetrics.cpp @@ -186,10 +186,10 @@ namespace CurrentMetrics { - #define M(NAME, DOCUMENTATION) extern const Metric NAME = __COUNTER__; + #define M(NAME, DOCUMENTATION) extern const Metric NAME = Metric(__COUNTER__); APPLY_FOR_METRICS(M) #undef M - constexpr Metric END = __COUNTER__; + constexpr Metric END = Metric(__COUNTER__); std::atomic values[END] {}; /// Global variable, initialized by zeros. diff --git a/src/Common/CurrentMetrics.h b/src/Common/CurrentMetrics.h index 0ae16e2d08d..a1ef254485d 100644 --- a/src/Common/CurrentMetrics.h +++ b/src/Common/CurrentMetrics.h @@ -6,6 +6,7 @@ #include #include #include +#include /** Allows to count number of simultaneously happening processes or current value of some metric. * - for high-level profiling. @@ -22,7 +23,7 @@ namespace CurrentMetrics { /// Metric identifier (index in array). - using Metric = size_t; + using Metric = StrongTypedef; using Value = DB::Int64; /// Get name of metric by identifier. Returns statically allocated string. diff --git a/src/Disks/TemporaryFileOnDisk.cpp b/src/Disks/TemporaryFileOnDisk.cpp index d31b09b2185..6fe6fd5a1c9 100644 --- a/src/Disks/TemporaryFileOnDisk.cpp +++ b/src/Disks/TemporaryFileOnDisk.cpp @@ -27,7 +27,7 @@ TemporaryFileOnDisk::TemporaryFileOnDisk(const DiskPtr & disk_) : TemporaryFileOnDisk(disk_, "") {} -TemporaryFileOnDisk::TemporaryFileOnDisk(const DiskPtr & disk_, CurrentMetrics::Value metric_scope) +TemporaryFileOnDisk::TemporaryFileOnDisk(const DiskPtr & disk_, CurrentMetrics::Metric metric_scope) : TemporaryFileOnDisk(disk_) { sub_metric_increment.emplace(metric_scope); diff --git a/src/Disks/TemporaryFileOnDisk.h b/src/Disks/TemporaryFileOnDisk.h index 9ba59c3eaf0..4c376383087 100644 --- a/src/Disks/TemporaryFileOnDisk.h +++ b/src/Disks/TemporaryFileOnDisk.h @@ -17,7 +17,7 @@ class TemporaryFileOnDisk { public: explicit TemporaryFileOnDisk(const DiskPtr & disk_); - explicit TemporaryFileOnDisk(const DiskPtr & disk_, CurrentMetrics::Value metric_scope); + explicit TemporaryFileOnDisk(const DiskPtr & disk_, CurrentMetrics::Metric metric_scope); explicit TemporaryFileOnDisk(const DiskPtr & disk_, const String & prefix); ~TemporaryFileOnDisk(); diff --git a/src/Interpreters/MetricLog.cpp b/src/Interpreters/MetricLog.cpp index 6e98f84bc82..14a190b41ef 100644 --- a/src/Interpreters/MetricLog.cpp +++ b/src/Interpreters/MetricLog.cpp @@ -50,7 +50,7 @@ void MetricLogElement::appendToBlock(MutableColumns & columns) const columns[column_idx++]->insert(profile_events[i]); for (size_t i = 0, end = CurrentMetrics::end(); i < end; ++i) - columns[column_idx++]->insert(current_metrics[i]); + columns[column_idx++]->insert(current_metrics[i].toUnderType()); } diff --git a/src/Interpreters/TemporaryDataOnDisk.cpp b/src/Interpreters/TemporaryDataOnDisk.cpp index 25252f8226b..c57de88d964 100644 --- a/src/Interpreters/TemporaryDataOnDisk.cpp +++ b/src/Interpreters/TemporaryDataOnDisk.cpp @@ -49,7 +49,7 @@ TemporaryDataOnDisk::TemporaryDataOnDisk(TemporaryDataOnDiskScopePtr parent_) : TemporaryDataOnDiskScope(std::move(parent_), /* limit_ = */ 0) {} -TemporaryDataOnDisk::TemporaryDataOnDisk(TemporaryDataOnDiskScopePtr parent_, CurrentMetrics::Value metric_scope) +TemporaryDataOnDisk::TemporaryDataOnDisk(TemporaryDataOnDiskScopePtr parent_, CurrentMetrics::Metric metric_scope) : TemporaryDataOnDiskScope(std::move(parent_), /* limit_ = */ 0) , current_metric_scope(metric_scope) {} diff --git a/src/Interpreters/TemporaryDataOnDisk.h b/src/Interpreters/TemporaryDataOnDisk.h index f0e02f16fb6..f7a6249a1ee 100644 --- a/src/Interpreters/TemporaryDataOnDisk.h +++ b/src/Interpreters/TemporaryDataOnDisk.h @@ -85,7 +85,7 @@ public: explicit TemporaryDataOnDisk(TemporaryDataOnDiskScopePtr parent_); - explicit TemporaryDataOnDisk(TemporaryDataOnDiskScopePtr parent_, CurrentMetrics::Value metric_scope); + explicit TemporaryDataOnDisk(TemporaryDataOnDiskScopePtr parent_, CurrentMetrics::Metric metric_scope); /// If max_file_size > 0, then check that there's enough space on the disk and throw an exception in case of lack of free space TemporaryFileStream & createStream(const Block & header, size_t max_file_size = 0); @@ -102,7 +102,7 @@ private: mutable std::mutex mutex; std::vector streams TSA_GUARDED_BY(mutex); - typename CurrentMetrics::Value current_metric_scope = CurrentMetrics::TemporaryFilesUnknown; + typename CurrentMetrics::Metric current_metric_scope = CurrentMetrics::TemporaryFilesUnknown; }; /* From c64f9e6f07f57491ef3950d779b403e9cc2982b5 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 1 Apr 2023 11:52:23 +0200 Subject: [PATCH 329/377] Use StrongTypedef for ProfileEvents Signed-off-by: Azat Khuzhin --- programs/server/MetricsTransmitter.cpp | 4 ++-- src/Common/ProfileEvents.cpp | 10 +++++----- src/Common/ProfileEvents.h | 5 +++-- src/Interpreters/MetricLog.cpp | 2 +- src/Interpreters/ProfileEventsExt.cpp | 4 ++-- src/Server/PrometheusMetricsWriter.cpp | 2 +- src/Storages/System/StorageSystemEvents.cpp | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/programs/server/MetricsTransmitter.cpp b/programs/server/MetricsTransmitter.cpp index 1672b0cd4dc..ae9fa5ecc2c 100644 --- a/programs/server/MetricsTransmitter.cpp +++ b/programs/server/MetricsTransmitter.cpp @@ -87,7 +87,7 @@ void MetricsTransmitter::transmit(std::vector & prev_count if (send_events) { - for (size_t i = 0, end = ProfileEvents::end(); i < end; ++i) + for (ProfileEvents::Event i = ProfileEvents::Event(0), end = ProfileEvents::end(); i < end; ++i) { const auto counter = ProfileEvents::global_counters[i].load(std::memory_order_relaxed); const auto counter_increment = counter - prev_counters[i]; @@ -100,7 +100,7 @@ void MetricsTransmitter::transmit(std::vector & prev_count if (send_events_cumulative) { - for (size_t i = 0, end = ProfileEvents::end(); i < end; ++i) + for (ProfileEvents::Event i = ProfileEvents::Event(0), end = ProfileEvents::end(); i < end; ++i) { const auto counter = ProfileEvents::global_counters[i].load(std::memory_order_relaxed); std::string key{ProfileEvents::getName(static_cast(i))}; diff --git a/src/Common/ProfileEvents.cpp b/src/Common/ProfileEvents.cpp index 3cee4a8e718..1d035952f13 100644 --- a/src/Common/ProfileEvents.cpp +++ b/src/Common/ProfileEvents.cpp @@ -497,10 +497,10 @@ The server successfully detected this situation and will download merged part fr namespace ProfileEvents { -#define M(NAME, DOCUMENTATION) extern const Event NAME = __COUNTER__; +#define M(NAME, DOCUMENTATION) extern const Event NAME = Event(__COUNTER__); APPLY_FOR_EVENTS(M) #undef M -constexpr Event END = __COUNTER__; +constexpr Event END = Event(__COUNTER__); /// Global variable, initialized by zeros. Counter global_counters_array[END] {}; @@ -522,7 +522,7 @@ void Counters::resetCounters() { if (counters) { - for (Event i = 0; i < num_counters; ++i) + for (Event i = Event(0); i < num_counters; ++i) counters[i].store(0, std::memory_order_relaxed); } } @@ -540,7 +540,7 @@ Counters::Snapshot::Snapshot() Counters::Snapshot Counters::getPartiallyAtomicSnapshot() const { Snapshot res; - for (Event i = 0; i < num_counters; ++i) + for (Event i = Event(0); i < num_counters; ++i) res.counters_holder[i] = counters[i].load(std::memory_order_relaxed); return res; } @@ -616,7 +616,7 @@ CountersIncrement::CountersIncrement(Counters::Snapshot const & snapshot) CountersIncrement::CountersIncrement(Counters::Snapshot const & after, Counters::Snapshot const & before) { init(); - for (Event i = 0; i < Counters::num_counters; ++i) + for (Event i = Event(0); i < Counters::num_counters; ++i) increment_holder[i] = static_cast(after[i]) - static_cast(before[i]); } diff --git a/src/Common/ProfileEvents.h b/src/Common/ProfileEvents.h index 867b5b551c6..a36e68742cf 100644 --- a/src/Common/ProfileEvents.h +++ b/src/Common/ProfileEvents.h @@ -1,7 +1,8 @@ #pragma once #include -#include "base/types.h" +#include +#include #include #include #include @@ -14,7 +15,7 @@ namespace ProfileEvents { /// Event identifier (index in array). - using Event = size_t; + using Event = StrongTypedef; using Count = size_t; using Increment = Int64; using Counter = std::atomic; diff --git a/src/Interpreters/MetricLog.cpp b/src/Interpreters/MetricLog.cpp index 14a190b41ef..578cc118a6b 100644 --- a/src/Interpreters/MetricLog.cpp +++ b/src/Interpreters/MetricLog.cpp @@ -97,7 +97,7 @@ void MetricLog::metricThreadFunction() elem.milliseconds = timeInMilliseconds(current_time) - timeInSeconds(current_time) * 1000; elem.profile_events.resize(ProfileEvents::end()); - for (size_t i = 0, end = ProfileEvents::end(); i < end; ++i) + for (ProfileEvents::Event i = ProfileEvents::Event(0), end = ProfileEvents::end(); i < end; ++i) { const ProfileEvents::Count new_value = ProfileEvents::global_counters[i].load(std::memory_order_relaxed); auto & old_value = prev_profile_events[i]; diff --git a/src/Interpreters/ProfileEventsExt.cpp b/src/Interpreters/ProfileEventsExt.cpp index 7fbbe3c662b..44977524c56 100644 --- a/src/Interpreters/ProfileEventsExt.cpp +++ b/src/Interpreters/ProfileEventsExt.cpp @@ -32,7 +32,7 @@ void dumpToMapColumn(const Counters::Snapshot & counters, DB::IColumn * column, auto & value_column = tuple_column.getColumn(1); size_t size = 0; - for (Event event = 0; event < Counters::num_counters; ++event) + for (Event event = Event(0); event < Counters::num_counters; ++event) { UInt64 value = counters[event]; @@ -54,7 +54,7 @@ static void dumpProfileEvents(ProfileEventsSnapshot const & snapshot, DB::Mutabl size_t rows = 0; auto & name_column = columns[NAME_COLUMN_INDEX]; auto & value_column = columns[VALUE_COLUMN_INDEX]; - for (Event event = 0; event < Counters::num_counters; ++event) + for (Event event = Event(0); event < Counters::num_counters; ++event) { Int64 value = snapshot.counters[event]; diff --git a/src/Server/PrometheusMetricsWriter.cpp b/src/Server/PrometheusMetricsWriter.cpp index abf2a2c0b6b..2331e455225 100644 --- a/src/Server/PrometheusMetricsWriter.cpp +++ b/src/Server/PrometheusMetricsWriter.cpp @@ -59,7 +59,7 @@ void PrometheusMetricsWriter::write(WriteBuffer & wb) const { if (send_events) { - for (size_t i = 0, end = ProfileEvents::end(); i < end; ++i) + for (ProfileEvents::Event i = ProfileEvents::Event(0), end = ProfileEvents::end(); i < end; ++i) { const auto counter = ProfileEvents::global_counters[i].load(std::memory_order_relaxed); diff --git a/src/Storages/System/StorageSystemEvents.cpp b/src/Storages/System/StorageSystemEvents.cpp index be2d3f8d49e..b9b07cfe0ac 100644 --- a/src/Storages/System/StorageSystemEvents.cpp +++ b/src/Storages/System/StorageSystemEvents.cpp @@ -18,7 +18,7 @@ NamesAndTypesList StorageSystemEvents::getNamesAndTypes() void StorageSystemEvents::fillData(MutableColumns & res_columns, ContextPtr context, const SelectQueryInfo &) const { - for (size_t i = 0, end = ProfileEvents::end(); i < end; ++i) + for (ProfileEvents::Event i = ProfileEvents::Event(0), end = ProfileEvents::end(); i < end; ++i) { UInt64 value = ProfileEvents::global_counters[i]; From fbc99d778860355ee7e246a3503d0a02c43ddff5 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 1 Apr 2023 11:55:31 +0200 Subject: [PATCH 330/377] Use StrongTypedef for StatusInfo Signed-off-by: Azat Khuzhin --- src/Common/StatusInfo.cpp | 4 ++-- src/Common/StatusInfo.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Common/StatusInfo.cpp b/src/Common/StatusInfo.cpp index 32afc833001..1f9ddfaf4b9 100644 --- a/src/Common/StatusInfo.cpp +++ b/src/Common/StatusInfo.cpp @@ -8,10 +8,10 @@ namespace CurrentStatusInfo { - #define M(NAME, DOCUMENTATION, ENUM) extern const Status NAME = __COUNTER__; + #define M(NAME, DOCUMENTATION, ENUM) extern const Status NAME = Status(__COUNTER__); APPLY_FOR_STATUS(M) #undef M - constexpr Status END = __COUNTER__; + constexpr Status END = Status(__COUNTER__); std::mutex locks[END] {}; std::unordered_map values[END] {}; diff --git a/src/Common/StatusInfo.h b/src/Common/StatusInfo.h index 9aa185cd0c3..91e6d4d3b85 100644 --- a/src/Common/StatusInfo.h +++ b/src/Common/StatusInfo.h @@ -6,13 +6,14 @@ #include #include #include +#include #include #include namespace CurrentStatusInfo { - using Status = size_t; + using Status = StrongTypedef; using Key = std::string; const char * getName(Status event); From 179450542879d11711cd2415c3fa7eeab18188be Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 1 Apr 2023 16:02:24 +0200 Subject: [PATCH 331/377] check-style: do not count CurrentMetrics::get as metric --- utils/check-style/check-style | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/check-style/check-style b/utils/check-style/check-style index 7a1fa6ce123..a6cc20bb7c8 100755 --- a/utils/check-style/check-style +++ b/utils/check-style/check-style @@ -78,6 +78,7 @@ EXTERN_TYPES_EXCLUDES=( CurrentMetrics::add CurrentMetrics::sub + CurrentMetrics::get CurrentMetrics::set CurrentMetrics::end CurrentMetrics::Increment From ccb5b257248eae05ffc404ec4acbf1fd4cd9b075 Mon Sep 17 00:00:00 2001 From: kssenii Date: Sat, 1 Apr 2023 18:21:49 +0200 Subject: [PATCH 332/377] One more test --- .../configs/named_coll.xml | 8 ++++++++ .../integration/test_dictionaries_ddl/test.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/tests/integration/test_dictionaries_ddl/configs/named_coll.xml b/tests/integration/test_dictionaries_ddl/configs/named_coll.xml index b14113a301d..ba450ae014f 100644 --- a/tests/integration/test_dictionaries_ddl/configs/named_coll.xml +++ b/tests/integration/test_dictionaries_ddl/configs/named_coll.xml @@ -17,5 +17,13 @@ default 9440 + + localhost + 9000 + test + default + + select id, SomeValue1 from test.xml_dictionary_table + diff --git a/tests/integration/test_dictionaries_ddl/test.py b/tests/integration/test_dictionaries_ddl/test.py index 29da9d7af7b..7dda6fc245a 100644 --- a/tests/integration/test_dictionaries_ddl/test.py +++ b/tests/integration/test_dictionaries_ddl/test.py @@ -592,3 +592,22 @@ def test_secure(started_cluster): node1.query("DROP DICTIONARY test.clickhouse_secure") node1.query("DROP TABLE test.foo_dict") + + +def test_named_collection(started_cluster): + node1.query( + """ + CREATE DICTIONARY test.clickhouse_named_collection( + id UInt64, + SomeValue1 UInt8 + ) + PRIMARY KEY id + LAYOUT(FLAT()) + SOURCE(CLICKHOUSE(NAME click1)) + LIFETIME(MIN 1 MAX 10) + """ + ) + + node1.query( + "select dictGetUInt8('test.clickhouse_named_collection', 'SomeValue1', toUInt64(23))" + ) == "0\n" From 230adac9f6bfeaf8d86f33396e147910eab930fe Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Sat, 1 Apr 2023 18:23:59 +0200 Subject: [PATCH 333/377] fixes --- .../AggregateFunctionGroupArray.h | 9 ++-- .../AggregateFunctionGroupArrayInsertAt.h | 6 ++- .../AggregateFunctionGroupArrayMoving.h | 3 +- .../AggregateFunctionGroupBitmapData.h | 3 +- .../AggregateFunctionHistogram.h | 3 +- .../AggregateFunctionIntervalLengthSum.h | 2 +- .../AggregateFunctionMaxIntersections.h | 3 +- .../AggregateFunctionSequenceNextNode.h | 3 +- src/AggregateFunctions/QuantileExact.h | 3 +- src/AggregateFunctions/ReservoirSampler.h | 3 +- .../ReservoirSamplerDeterministic.h | 3 +- src/IO/ReadHelpers.h | 3 +- src/Server/TCPHandler.cpp | 3 +- tests/clickhouse-test | 12 +++++- ...nd_exception_messages_formatting.reference | 2 +- ..._log_and_exception_messages_formatting.sql | 2 +- .../02437_drop_mv_restart_replicas.sh | 3 +- ...replacing_merge_tree_is_deleted_column.sql | 42 +++++++++---------- 18 files changed, 66 insertions(+), 42 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionGroupArray.h b/src/AggregateFunctions/AggregateFunctionGroupArray.h index 5a799dc3641..7a5e6a8cb2d 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupArray.h +++ b/src/AggregateFunctions/AggregateFunctionGroupArray.h @@ -288,7 +288,8 @@ public: readVarUInt(size, buf); if (unlikely(size > AGGREGATE_FUNCTION_GROUP_ARRAY_MAX_ARRAY_SIZE)) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", AGGREGATE_FUNCTION_GROUP_ARRAY_MAX_ARRAY_SIZE); if (limit_num_elems && unlikely(size > max_elems)) throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size, it should not exceed {}", max_elems); @@ -367,7 +368,8 @@ struct GroupArrayNodeBase UInt64 size; readVarUInt(size, buf); if (unlikely(size > AGGREGATE_FUNCTION_GROUP_ARRAY_MAX_ARRAY_SIZE)) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", AGGREGATE_FUNCTION_GROUP_ARRAY_MAX_ARRAY_SIZE); Node * node = reinterpret_cast(arena->alignedAlloc(sizeof(Node) + size, alignof(Node))); node->size = size; @@ -621,7 +623,8 @@ public: return; if (unlikely(elems > AGGREGATE_FUNCTION_GROUP_ARRAY_MAX_ARRAY_SIZE)) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", AGGREGATE_FUNCTION_GROUP_ARRAY_MAX_ARRAY_SIZE); if (limit_num_elems && unlikely(elems > max_elems)) throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size, it should not exceed {}", max_elems); diff --git a/src/AggregateFunctions/AggregateFunctionGroupArrayInsertAt.h b/src/AggregateFunctions/AggregateFunctionGroupArrayInsertAt.h index dbd7b32b9ce..439bb613337 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupArrayInsertAt.h +++ b/src/AggregateFunctions/AggregateFunctionGroupArrayInsertAt.h @@ -79,7 +79,8 @@ public: { length_to_resize = applyVisitor(FieldVisitorConvertToNumber(), params[1]); if (length_to_resize > AGGREGATE_FUNCTION_GROUP_ARRAY_INSERT_AT_MAX_SIZE) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", AGGREGATE_FUNCTION_GROUP_ARRAY_INSERT_AT_MAX_SIZE); } } @@ -167,7 +168,8 @@ public: readVarUInt(size, buf); if (size > AGGREGATE_FUNCTION_GROUP_ARRAY_INSERT_AT_MAX_SIZE) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", AGGREGATE_FUNCTION_GROUP_ARRAY_INSERT_AT_MAX_SIZE); Array & arr = data(place).value; diff --git a/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.h b/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.h index 8d7010c10db..e6f79d7bca1 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.h +++ b/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.h @@ -144,7 +144,8 @@ public: readVarUInt(size, buf); if (unlikely(size > AGGREGATE_FUNCTION_MOVING_MAX_ARRAY_SIZE)) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", AGGREGATE_FUNCTION_MOVING_MAX_ARRAY_SIZE); if (size > 0) { diff --git a/src/AggregateFunctions/AggregateFunctionGroupBitmapData.h b/src/AggregateFunctions/AggregateFunctionGroupBitmapData.h index 62017251108..d99f0bf16ee 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupBitmapData.h +++ b/src/AggregateFunctions/AggregateFunctionGroupBitmapData.h @@ -127,7 +127,8 @@ public: if (size == 0) throw Exception(ErrorCodes::INCORRECT_DATA, "Incorrect size (0) in groupBitmap."); if (size > max_size) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size in groupBitmap."); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size in groupBitmap (maximum: {})", max_size); /// TODO: this is unnecessary copying - it will be better to read and deserialize in one pass. std::unique_ptr buf(new char[size]); diff --git a/src/AggregateFunctions/AggregateFunctionHistogram.h b/src/AggregateFunctions/AggregateFunctionHistogram.h index 62ed071856a..3a98737f199 100644 --- a/src/AggregateFunctions/AggregateFunctionHistogram.h +++ b/src/AggregateFunctions/AggregateFunctionHistogram.h @@ -294,7 +294,8 @@ public: throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too many bins"); static constexpr size_t max_size = 1_GiB; if (size > max_size) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size in histogram."); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size in histogram (maximum: {})", max_size); buf.readStrict(reinterpret_cast(points), size * sizeof(WeightedValue)); } diff --git a/src/AggregateFunctions/AggregateFunctionIntervalLengthSum.h b/src/AggregateFunctions/AggregateFunctionIntervalLengthSum.h index 625a2511b0d..e31c62802f1 100644 --- a/src/AggregateFunctions/AggregateFunctionIntervalLengthSum.h +++ b/src/AggregateFunctions/AggregateFunctionIntervalLengthSum.h @@ -117,7 +117,7 @@ struct AggregateFunctionIntervalLengthSumData readBinary(size, buf); if (unlikely(size > MAX_ARRAY_SIZE)) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size (maximum: {})", MAX_ARRAY_SIZE); segments.clear(); segments.reserve(size); diff --git a/src/AggregateFunctions/AggregateFunctionMaxIntersections.h b/src/AggregateFunctions/AggregateFunctionMaxIntersections.h index 563d56c6f40..2c54293eeec 100644 --- a/src/AggregateFunctions/AggregateFunctionMaxIntersections.h +++ b/src/AggregateFunctions/AggregateFunctionMaxIntersections.h @@ -140,7 +140,8 @@ public: readVarUInt(size, buf); if (unlikely(size > AGGREGATE_FUNCTION_MAX_INTERSECTIONS_MAX_ARRAY_SIZE)) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", AGGREGATE_FUNCTION_MAX_INTERSECTIONS_MAX_ARRAY_SIZE); auto & value = this->data(place).value; diff --git a/src/AggregateFunctions/AggregateFunctionSequenceNextNode.h b/src/AggregateFunctions/AggregateFunctionSequenceNextNode.h index 4fd7db4160e..77bd590ebbb 100644 --- a/src/AggregateFunctions/AggregateFunctionSequenceNextNode.h +++ b/src/AggregateFunctions/AggregateFunctionSequenceNextNode.h @@ -324,7 +324,8 @@ public: return; if (unlikely(size > max_node_size_deserialize)) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", max_node_size_deserialize); auto & value = data(place).value; diff --git a/src/AggregateFunctions/QuantileExact.h b/src/AggregateFunctions/QuantileExact.h index c67621a99ce..a92d1979bab 100644 --- a/src/AggregateFunctions/QuantileExact.h +++ b/src/AggregateFunctions/QuantileExact.h @@ -58,7 +58,8 @@ struct QuantileExactBase size_t size = 0; readVarUInt(size, buf); if (unlikely(size > QUANTILE_EXACT_MAX_ARRAY_SIZE)) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", QUANTILE_EXACT_MAX_ARRAY_SIZE); array.resize(size); buf.readStrict(reinterpret_cast(array.data()), size * sizeof(array[0])); } diff --git a/src/AggregateFunctions/ReservoirSampler.h b/src/AggregateFunctions/ReservoirSampler.h index ef0e7c6566e..3d723d5aace 100644 --- a/src/AggregateFunctions/ReservoirSampler.h +++ b/src/AggregateFunctions/ReservoirSampler.h @@ -213,7 +213,8 @@ public: size_t size = std::min(total_values, sample_count); static constexpr size_t MAX_RESERVOIR_SIZE = 1_GiB; if (unlikely(size > MAX_RESERVOIR_SIZE)) - throw DB::Exception(DB::ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw DB::Exception(DB::ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", MAX_RESERVOIR_SIZE); samples.resize(size); diff --git a/src/AggregateFunctions/ReservoirSamplerDeterministic.h b/src/AggregateFunctions/ReservoirSamplerDeterministic.h index 5e1d23ed2c2..bde33260f5a 100644 --- a/src/AggregateFunctions/ReservoirSamplerDeterministic.h +++ b/src/AggregateFunctions/ReservoirSamplerDeterministic.h @@ -166,7 +166,8 @@ public: static constexpr size_t MAX_RESERVOIR_SIZE = 1_GiB; if (unlikely(size > MAX_RESERVOIR_SIZE)) - throw DB::Exception(DB::ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size"); + throw DB::Exception(DB::ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", MAX_RESERVOIR_SIZE); samples.resize(size); for (size_t i = 0; i < size; ++i) diff --git a/src/IO/ReadHelpers.h b/src/IO/ReadHelpers.h index cac42c198b1..20ba73e0fa7 100644 --- a/src/IO/ReadHelpers.h +++ b/src/IO/ReadHelpers.h @@ -165,7 +165,8 @@ void readVectorBinary(std::vector & v, ReadBuffer & buf) readVarUInt(size, buf); if (size > DEFAULT_MAX_STRING_SIZE) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Too large array size."); + throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, + "Too large array size (maximum: {})", DEFAULT_MAX_STRING_SIZE); v.resize(size); for (size_t i = 0; i < size; ++i) diff --git a/src/Server/TCPHandler.cpp b/src/Server/TCPHandler.cpp index 501478c1163..a608219ed63 100644 --- a/src/Server/TCPHandler.cpp +++ b/src/Server/TCPHandler.cpp @@ -1202,7 +1202,8 @@ void TCPHandler::receiveHello() throw Exception(ErrorCodes::CLIENT_HAS_CONNECTED_TO_WRONG_PORT, "Client has connected to wrong port"); } else - throw NetException(ErrorCodes::UNEXPECTED_PACKET_FROM_CLIENT, "Unexpected packet from client"); + throw NetException(ErrorCodes::UNEXPECTED_PACKET_FROM_CLIENT, + "Unexpected packet from client (expected Hello, got {})", packet_type); } readStringBinary(client_name, *in); diff --git a/tests/clickhouse-test b/tests/clickhouse-test index d407f73d033..d85cd308702 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -2053,7 +2053,7 @@ def reportLogStats(args): 'No sharding key', 'No tables', 'Query: {}', 'Removed', 'Removed part {}', 'Removing parts.', 'Request URI: {}', 'Sending part {}', 'Sent handshake', 'Starting {}', 'Will mimic {}', 'Writing to {}', 'dropIfEmpty', 'loadAll {}', '{} ({}:{})', '{} -> {}', '{} {}', '{}: {}', 'Query was cancelled', - 'Table {} already exists', '{}%', 'Cancelled merging parts', 'All replicas are lost', + 'Table {} already exists.', '{}%', 'Cancelled merging parts', 'All replicas are lost', 'Cancelled mutating parts', 'Read object: {}', 'New segment: {}', 'Unknown geometry type {}', 'Table {} is not replicated', '{} {}.{} already exists', 'Attempt to read after eof', 'Replica {} already exists', 'Convert overflow', 'key must be a tuple', 'Division by zero', @@ -2061,7 +2061,15 @@ def reportLogStats(args): 'Cannot parse datetime', 'Bad get: has {}, requested {}', 'There is no {} in {}', 'Numeric overflow', 'Polygon is not valid: {}', 'Decimal math overflow', '{} only accepts maps', 'Dictionary ({}) not found', 'Unknown format {}', 'Invalid IPv4 value', 'Invalid IPv6 value', 'Unknown setting {}', - 'Unknown table function {}' + 'Unknown table function {}', 'Database {} already exists.', 'Table {} doesn''t exist', + 'Invalid credentials', 'Part {} already exists', 'Invalid mode: {}', 'Log pulling is cancelled', + 'JOIN {} cannot get JOIN keys', 'Unknown function {}{}', 'Cannot parse IPv6 {}', + 'Not found address of host: {}', '{} must contain a tuple', 'Unknown codec family: {}', + 'Expected const String column', 'Invalid partition format: {}', 'Cannot parse IPv4 {}', + 'AST is too deep. Maximum: {}', 'Array sizes are too large: {}', 'Unable to connect to HDFS: {}', + 'Shutdown is called for table', 'File is not inside {}', + 'Table {} doesn''t exist', 'Database {} doesn''t exist', 'Table {}.{} doesn''t exist', + 'File {} doesn''t exist', 'No such attribute ''{}''', 'User name ''{}'' is reserved' ) AS known_short_messages SELECT count() AS c, message_format_string, substr(any(message), 1, 120) FROM system.text_log diff --git a/tests/queries/0_stateless/00002_log_and_exception_messages_formatting.reference b/tests/queries/0_stateless/00002_log_and_exception_messages_formatting.reference index 032d7e396ff..fddfbd49de3 100644 --- a/tests/queries/0_stateless/00002_log_and_exception_messages_formatting.reference +++ b/tests/queries/0_stateless/00002_log_and_exception_messages_formatting.reference @@ -2,7 +2,7 @@ runtime messages 0.001 runtime exceptions 0.05 messages shorter than 10 10 messages shorter than 16 40 -exceptions shorter than 30 125 +exceptions shorter than 30 120 noisy messages 0.3 noisy Trace messages 0.16 noisy Debug messages 0.09 diff --git a/tests/queries/0_stateless/00002_log_and_exception_messages_formatting.sql b/tests/queries/0_stateless/00002_log_and_exception_messages_formatting.sql index 2bcceec8cbe..71f41c7a9d2 100644 --- a/tests/queries/0_stateless/00002_log_and_exception_messages_formatting.sql +++ b/tests/queries/0_stateless/00002_log_and_exception_messages_formatting.sql @@ -21,7 +21,7 @@ select 'messages shorter than 10', max2(countDistinctOrDefault(message_format_st select 'messages shorter than 16', max2(countDistinctOrDefault(message_format_string), 40) from logs where length(message_format_string) < 16; -- Same as above, but exceptions must be more informative. Feel free to update the threshold or remove this query if really necessary -select 'exceptions shorter than 30', max2(countDistinctOrDefault(message_format_string), 125) from logs where length(message_format_string) < 30 and message ilike '%DB::Exception%'; +select 'exceptions shorter than 30', max2(countDistinctOrDefault(message_format_string), 120) from logs where length(message_format_string) < 30 and message ilike '%DB::Exception%'; -- Avoid too noisy messages: top 1 message frequency must be less than 30%. We should reduce the threshold diff --git a/tests/queries/0_stateless/02437_drop_mv_restart_replicas.sh b/tests/queries/0_stateless/02437_drop_mv_restart_replicas.sh index e4f52b6e4ad..ca5e1245046 100755 --- a/tests/queries/0_stateless/02437_drop_mv_restart_replicas.sh +++ b/tests/queries/0_stateless/02437_drop_mv_restart_replicas.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -# Tags: long, zookeeper, race, no-ordinary-database +# Tags: long, zookeeper, race, no-ordinary-database, no-replicated-database +# FIXME remove no-replicated-database tag CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh diff --git a/tests/queries/0_stateless/02490_replacing_merge_tree_is_deleted_column.sql b/tests/queries/0_stateless/02490_replacing_merge_tree_is_deleted_column.sql index 615d42f12fa..8549300d49f 100644 --- a/tests/queries/0_stateless/02490_replacing_merge_tree_is_deleted_column.sql +++ b/tests/queries/0_stateless/02490_replacing_merge_tree_is_deleted_column.sql @@ -8,33 +8,33 @@ DROP TABLE IF EXISTS test; CREATE TABLE test (uid String, version UInt32, is_deleted UInt8) ENGINE = ReplacingMergeTree(version) Order by (uid); INSERT INTO test (*) VALUES ('d1', 1, 0), ('d2', 1, 0), ('d6', 1, 0), ('d4', 1, 0), ('d6', 2, 1), ('d3', 1, 0), ('d1', 2, 1), ('d5', 1, 0), ('d4', 2, 1), ('d1', 3, 0), ('d1', 4, 1), ('d4', 3, 0), ('d1', 5, 0); SELECT '== Test SELECT ... FINAL - no is_deleted =='; -select * from test FINAL; +select * from test FINAL order by uid; OPTIMIZE TABLE test FINAL CLEANUP; -select * from test; +select * from test order by uid; DROP TABLE IF EXISTS test; CREATE TABLE test (uid String, version UInt32, is_deleted UInt8) ENGINE = ReplacingMergeTree(version) Order by (uid) SETTINGS clean_deleted_rows='Always'; INSERT INTO test (*) VALUES ('d1', 1, 0), ('d2', 1, 0), ('d6', 1, 0), ('d4', 1, 0), ('d6', 2, 1), ('d3', 1, 0), ('d1', 2, 1), ('d5', 1, 0), ('d4', 2, 1), ('d1', 3, 0), ('d1', 4, 1), ('d4', 3, 0), ('d1', 5, 0); SELECT '== Test SELECT ... FINAL - no is_deleted SETTINGS clean_deleted_rows=Always =='; -select * from test FINAL; +select * from test FINAL order by uid; OPTIMIZE TABLE test FINAL CLEANUP; -select * from test; +select * from test order by uid; -- Test the new behaviour DROP TABLE IF EXISTS test; CREATE TABLE test (uid String, version UInt32, is_deleted UInt8) ENGINE = ReplacingMergeTree(version, is_deleted) Order by (uid); INSERT INTO test (*) VALUES ('d1', 1, 0), ('d2', 1, 0), ('d6', 1, 0), ('d4', 1, 0), ('d6', 2, 1), ('d3', 1, 0), ('d1', 2, 1), ('d5', 1, 0), ('d4', 2, 1), ('d1', 3, 0), ('d1', 4, 1), ('d4', 3, 0), ('d1', 5, 0); SELECT '== Test SELECT ... FINAL =='; -select * from test FINAL; -select * from test; +select * from test FINAL order by uid; +select * from test order by uid; SELECT '== Insert backups =='; INSERT INTO test (*) VALUES ('d6', 1, 0), ('d4', 1, 0), ('d6', 2, 1), ('d3', 1, 0), ('d1', 2, 1), ('d5', 1, 0), ('d4', 2, 1); -select * from test FINAL; +select * from test FINAL order by uid; SELECT '== Insert a second batch with overlaping data =='; INSERT INTO test (*) VALUES ('d4', 1, 0), ('d6', 2, 1), ('d3', 1, 0), ('d1', 2, 1), ('d5', 1, 0), ('d4', 2, 1), ('d1', 3, 1), ('d1', 4, 1), ('d4', 3, 0), ('d1', 5, 0), ('d2', 2, 1), ('d2', 3, 0), ('d3', 2, 1), ('d3', 3, 0); -select * from test FINAL; +select * from test FINAL order by uid; DROP TABLE IF EXISTS test; CREATE TABLE test (uid String, version UInt32, is_deleted UInt8) ENGINE = ReplacingMergeTree(version, is_deleted) Order by (uid); @@ -45,7 +45,7 @@ INSERT INTO test (*) VALUES ('d1', 1, 0), ('d1', 2, 1), ('d1', 3, 0), ('d1', 4, INSERT INTO test (*) VALUES ('d1', 1, 0), ('d1', 2, 1), ('d1', 3, 0), ('d1', 4, 1), ('d1', 5, 0), ('d2', 1, 0), ('d3', 1, 0), ('d4', 1, 0), ('d5', 1, 0), ('d6', 1, 0), ('d6', 2, 1); SELECT '== Only last version remains after OPTIMIZE W/ CLEANUP =='; OPTIMIZE TABLE test FINAL CLEANUP; -select * from test; +select * from test order by uid; -- insert d6 v=3 is_deleted=true (timestamp more recent so this version should be the one take into acount) INSERT INTO test (*) VALUES ('d1', 1, 0), ('d1', 2, 1), ('d1', 3, 0), ('d1', 4, 1), ('d1', 5, 0), ('d2', 1, 0), ('d3', 1, 0), ('d4', 1, 0), ('d5', 1, 0), ('d6', 1, 0), ('d6', 3, 1); @@ -53,7 +53,7 @@ INSERT INTO test (*) VALUES ('d1', 1, 0), ('d1', 2, 1), ('d1', 3, 0), ('d1', 4, SELECT '== OPTIMIZE W/ CLEANUP (remove d6) =='; OPTIMIZE TABLE test FINAL CLEANUP; -- No d6 anymore -select * from test; +select * from test order by uid; DROP TABLE IF EXISTS test; CREATE TABLE test (uid String, version UInt32, is_deleted UInt8) ENGINE = ReplacingMergeTree(version, is_deleted) Order by (uid) SETTINGS clean_deleted_rows='Always'; @@ -61,12 +61,12 @@ CREATE TABLE test (uid String, version UInt32, is_deleted UInt8) ENGINE = Replac SELECT '== Test of the SETTINGS clean_deleted_rows as Always =='; INSERT INTO test (*) VALUES ('d1', 1, 0), ('d2', 1, 0), ('d6', 1, 0), ('d4', 1, 0), ('d6', 2, 1), ('d3', 1, 0), ('d1', 2, 1), ('d5', 1, 0), ('d4', 2, 1), ('d1', 3, 0), ('d1', 4, 1), ('d4', 3, 0), ('d1', 5, 0); -- Even if the setting is set to Always, the SELECT FINAL doesn't delete rows -select * from test FINAL; -select * from test; +select * from test FINAL order by uid; +select * from test order by uid; OPTIMIZE TABLE test FINAL; -- d6 has to be removed since we set clean_deleted_rows as 'Always' -select * from test; +select * from test order by uid; SELECT '== Test of the SETTINGS clean_deleted_rows as Never =='; ALTER TABLE test MODIFY SETTING clean_deleted_rows='Never'; @@ -74,7 +74,7 @@ INSERT INTO test (*) VALUES ('d1', 1, 0), ('d2', 1, 0), ('d6', 1, 0), ('d4', 1, INSERT INTO test (*) VALUES ('d1', 1, 0), ('d2', 1, 0), ('d6', 1, 0), ('d4', 1, 0), ('d6', 2, 1), ('d3', 1, 0), ('d1', 2, 1), ('d5', 1, 0), ('d4', 2, 1), ('d1', 3, 0), ('d1', 4, 1), ('d4', 3, 0), ('d1', 5, 0); OPTIMIZE TABLE test FINAL; -- d6 has NOT to be removed since we set clean_deleted_rows as 'Never' -select * from test; +select * from test order by uid; DROP TABLE IF EXISTS testCleanupR1; @@ -92,7 +92,7 @@ OPTIMIZE TABLE testCleanupR1 FINAL CLEANUP; -- Only d3 to d5 remain SELECT '== (Replicas) Test optimize =='; -SELECT * FROM testCleanupR1; +SELECT * FROM testCleanupR1 order by uid; ------------------------------ @@ -110,7 +110,7 @@ OPTIMIZE TABLE testSettingsR1 FINAL; -- Only d3 to d5 remain SELECT '== (Replicas) Test settings =='; -SELECT * FROM testSettingsR1; +SELECT * FROM testSettingsR1 order by col1; ------------------------------ @@ -133,28 +133,28 @@ CREATE TABLE testMT (uid String, version UInt32, is_deleted UInt8) ENGINE = Merg INSERT INTO testMT (*) VALUES ('d1', 1, 1); OPTIMIZE TABLE testMT FINAL CLEANUP; -- { serverError CANNOT_ASSIGN_OPTIMIZE } OPTIMIZE TABLE testMT FINAL; -SELECT * FROM testMT; +SELECT * FROM testMT order by uid; CREATE TABLE testSummingMT (uid String, version UInt32, is_deleted UInt8) ENGINE = SummingMergeTree() Order by (uid) SETTINGS clean_deleted_rows='Always'; INSERT INTO testSummingMT (*) VALUES ('d1', 1, 1); OPTIMIZE TABLE testSummingMT FINAL CLEANUP; -- { serverError CANNOT_ASSIGN_OPTIMIZE } OPTIMIZE TABLE testSummingMT FINAL; -SELECT * FROM testSummingMT; +SELECT * FROM testSummingMT order by uid; CREATE TABLE testAggregatingMT (uid String, version UInt32, is_deleted UInt8) ENGINE = AggregatingMergeTree() Order by (uid) SETTINGS clean_deleted_rows='Always'; INSERT INTO testAggregatingMT (*) VALUES ('d1', 1, 1); OPTIMIZE TABLE testAggregatingMT FINAL CLEANUP; -- { serverError CANNOT_ASSIGN_OPTIMIZE } OPTIMIZE TABLE testAggregatingMT FINAL; -SELECT * FROM testAggregatingMT; +SELECT * FROM testAggregatingMT order by uid; CREATE TABLE testCollapsingMT (uid String, version UInt32, is_deleted UInt8, sign Int8) ENGINE = CollapsingMergeTree(sign) Order by (uid) SETTINGS clean_deleted_rows='Always'; INSERT INTO testCollapsingMT (*) VALUES ('d1', 1, 1, 1); OPTIMIZE TABLE testCollapsingMT FINAL CLEANUP; -- { serverError CANNOT_ASSIGN_OPTIMIZE } OPTIMIZE TABLE testCollapsingMT FINAL; -SELECT * FROM testCollapsingMT; +SELECT * FROM testCollapsingMT order by uid; CREATE TABLE testVersionedCMT (uid String, version UInt32, is_deleted UInt8, sign Int8) ENGINE = VersionedCollapsingMergeTree(sign, version) Order by (uid) SETTINGS clean_deleted_rows='Always'; INSERT INTO testVersionedCMT (*) VALUES ('d1', 1, 1, 1); OPTIMIZE TABLE testVersionedCMT FINAL CLEANUP; -- { serverError CANNOT_ASSIGN_OPTIMIZE } OPTIMIZE TABLE testVersionedCMT FINAL; -SELECT * FROM testVersionedCMT; +SELECT * FROM testVersionedCMT order by uid; From 225fee16dc82a079bee7517948742c04c396b656 Mon Sep 17 00:00:00 2001 From: Vitaly Baranov Date: Sat, 1 Apr 2023 19:46:53 +0200 Subject: [PATCH 334/377] Fix test "test_backup_all" and add more test cases. --- .../test_backup_restore_new/test.py | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/tests/integration/test_backup_restore_new/test.py b/tests/integration/test_backup_restore_new/test.py index 7c3374ffe7d..424799e2402 100644 --- a/tests/integration/test_backup_restore_new/test.py +++ b/tests/integration/test_backup_restore_new/test.py @@ -1184,7 +1184,8 @@ def test_restore_partition(): ) -def test_backup_all(): +@pytest.mark.parametrize("exclude_system_log_tables", [False, True]) +def test_backup_all(exclude_system_log_tables): create_and_fill_table() session_id = new_session_id() @@ -1201,20 +1202,34 @@ def test_backup_all(): instance.query("CREATE USER u1 IDENTIFIED BY 'qwe123' SETTINGS custom_a = 1") backup_name = new_backup_name() - instance.http_query( - f"BACKUP ALL TO {backup_name}", - params={"session_id": session_id}, - ) + + exclude_from_backup = [] + if exclude_system_log_tables: + system_log_tables = ( + instance.query( + "SELECT concat('system.', table) FROM system.tables WHERE (database = 'system') AND (table LIKE '%_log')" + ) + .rstrip("\n") + .split("\n") + ) + exclude_from_backup += system_log_tables + + backup_command = f"BACKUP ALL {'EXCEPT TABLES ' + ','.join(exclude_from_backup) if exclude_from_backup else ''} TO {backup_name}" + + instance.http_query(backup_command, params={"session_id": session_id}) instance.query("DROP TABLE test.table") instance.query("DROP FUNCTION two_and_half") instance.query("DROP USER u1") + restore_settings = [] + if not exclude_system_log_tables: + restore_settings.append("allow_non_empty_tables=true") + restore_command = f"RESTORE ALL FROM {backup_name} {'SETTINGS '+ ', '.join(restore_settings) if restore_settings else ''}" + session_id = new_session_id() instance.http_query( - f"RESTORE ALL FROM {backup_name}", - params={"session_id": session_id}, - method="POST", + restore_command, params={"session_id": session_id}, method="POST" ) assert instance.query("SELECT count(), sum(x) FROM test.table") == "100\t4950\n" @@ -1230,6 +1245,10 @@ def test_backup_all(): == "CREATE USER u1 IDENTIFIED WITH sha256_password SETTINGS custom_a = 1\n" ) + instance.query("DROP TABLE test.table") + instance.query("DROP FUNCTION two_and_half") + instance.query("DROP USER u1") + def test_operation_id(): create_and_fill_table(n=30) From a28996d0477d32c6f1aac7de54a62a2f03c10ada Mon Sep 17 00:00:00 2001 From: Dan Roscigno Date: Sat, 1 Apr 2023 18:50:49 -0400 Subject: [PATCH 335/377] Update docs/en/operations/tips.md --- docs/en/operations/tips.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/operations/tips.md b/docs/en/operations/tips.md index c5b489e92c5..6aff89fcff8 100644 --- a/docs/en/operations/tips.md +++ b/docs/en/operations/tips.md @@ -55,7 +55,7 @@ For Linux, software RAID is better (with `mdadm`). When creating RAID-10, select the `far` layout. If your budget allows, choose RAID-10. -LVM by itself (without RAID or mdadm) is ok, but making RAID with it or combining it with mdadm is a less explored option, and there will be more chances for mistakes +LVM by itself (without RAID or `mdadm`) is ok, but making RAID with it or combining it with mdadm is a less explored option, and there will be more chances for mistakes (selecting wrong chunk size; misalignment of chunks; choosing a wrong raid type; forgetting to cleanup disks). If you are confident in using LVM, there is nothing against using it. From 9061b039171db0e58dc6d28c1cf1cdaf80dc9ad5 Mon Sep 17 00:00:00 2001 From: Dan Roscigno Date: Sat, 1 Apr 2023 18:51:18 -0400 Subject: [PATCH 336/377] Update docs/en/operations/tips.md --- docs/en/operations/tips.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/operations/tips.md b/docs/en/operations/tips.md index 6aff89fcff8..693dcd0d21b 100644 --- a/docs/en/operations/tips.md +++ b/docs/en/operations/tips.md @@ -55,7 +55,7 @@ For Linux, software RAID is better (with `mdadm`). When creating RAID-10, select the `far` layout. If your budget allows, choose RAID-10. -LVM by itself (without RAID or `mdadm`) is ok, but making RAID with it or combining it with mdadm is a less explored option, and there will be more chances for mistakes +LVM by itself (without RAID or `mdadm`) is ok, but making RAID with it or combining it with `mdadm` is a less explored option, and there will be more chances for mistakes (selecting wrong chunk size; misalignment of chunks; choosing a wrong raid type; forgetting to cleanup disks). If you are confident in using LVM, there is nothing against using it. From 62ecac805ffc22660335d7fc710f38480902f9ea Mon Sep 17 00:00:00 2001 From: Dan Roscigno Date: Sat, 1 Apr 2023 18:58:48 -0400 Subject: [PATCH 337/377] Add mdadm to aspell-dict.txt --- utils/check-style/aspell-ignore/en/aspell-dict.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/check-style/aspell-ignore/en/aspell-dict.txt b/utils/check-style/aspell-ignore/en/aspell-dict.txt index 3c5af582692..8f72f07d7ec 100644 --- a/utils/check-style/aspell-ignore/en/aspell-dict.txt +++ b/utils/check-style/aspell-ignore/en/aspell-dict.txt @@ -374,6 +374,7 @@ llvm localhost macOS mariadb +mdadm miniselect msgpack msgpk From 740a5ef777e0271603c03f1c4dc9ace4a712577e Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Sun, 12 Mar 2023 17:03:17 +0800 Subject: [PATCH 338/377] enable return null and complext type --- .../sql-reference/functions/json-functions.md | 2 +- src/Functions/FunctionSQLJSON.h | 17 +++++++++++------ .../01889_sql_json_functions.reference | 16 +++++++++------- .../0_stateless/01889_sql_json_functions.sql | 1 + 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/docs/en/sql-reference/functions/json-functions.md b/docs/en/sql-reference/functions/json-functions.md index bfe2a541647..4c2372561b7 100644 --- a/docs/en/sql-reference/functions/json-functions.md +++ b/docs/en/sql-reference/functions/json-functions.md @@ -401,7 +401,7 @@ Before version 21.11 the order of arguments was wrong, i.e. JSON_QUERY(path, jso Parses a JSON and extract a value as JSON scalar. -If the value does not exist, an empty string will be returned. +If the value does not exist, NULL will be returned. Example: diff --git a/src/Functions/FunctionSQLJSON.h b/src/Functions/FunctionSQLJSON.h index d649752768c..94d38d1951b 100644 --- a/src/Functions/FunctionSQLJSON.h +++ b/src/Functions/FunctionSQLJSON.h @@ -3,9 +3,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -233,7 +235,11 @@ class JSONValueImpl public: using Element = typename JSONParser::Element; - static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &) { return std::make_shared(); } + static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &) + { + DataTypePtr string_type = std::make_shared(); + return std::make_shared(string_type); + } static size_t getNumberOfIndexArguments(const ColumnsWithTypeAndName & arguments) { return arguments.size() - 1; } @@ -247,10 +253,7 @@ public: { if (status == VisitorStatus::Ok) { - if (!(current_element.isArray() || current_element.isObject())) - { - break; - } + break; } else if (status == VisitorStatus::Error) { @@ -267,7 +270,8 @@ public: std::stringstream out; // STYLE_CHECK_ALLOW_STD_STRING_STREAM out << current_element.getElement(); auto output_str = out.str(); - ColumnString & col_str = assert_cast(dest); + ColumnNullable & col_null = assert_cast(dest); + ColumnString & col_str = assert_cast(col_null.getNestedColumn()); ColumnString::Chars & data = col_str.getChars(); ColumnString::Offsets & offsets = col_str.getOffsets(); @@ -282,6 +286,7 @@ public: { col_str.insertData(output_str.data(), output_str.size()); } + col_null.getNullMapColumn().insertValue(0); return true; } }; diff --git a/tests/queries/0_stateless/01889_sql_json_functions.reference b/tests/queries/0_stateless/01889_sql_json_functions.reference index c2c106e8632..be055e64ae7 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.reference +++ b/tests/queries/0_stateless/01889_sql_json_functions.reference @@ -1,8 +1,8 @@ -- { echo } SELECT '--JSON_VALUE--'; --JSON_VALUE-- -SELECT JSON_VALUE('{"hello":1}', '$'); -- root is a complex object => default value (empty string) - +SELECT JSON_VALUE('{"hello":1}', '$'); +{"hello":1} SELECT JSON_VALUE('{"hello":1}', '$.hello'); 1 SELECT JSON_VALUE('{"hello":1.2}', '$.hello'); @@ -14,13 +14,13 @@ world SELECT JSON_VALUE('{"hello":null}', '$.hello'); null SELECT JSON_VALUE('{"hello":["world","world2"]}', '$.hello'); - +["world","world2"] SELECT JSON_VALUE('{"hello":{"world":"!"}}', '$.hello'); - -SELECT JSON_VALUE('{hello:world}', '$.hello'); -- invalid json => default value (empty string) - +{"world":"!"} +SELECT JSON_VALUE('{hello:world}', '$.hello'); +null SELECT JSON_VALUE('', '$.hello'); - +null SELECT JSON_VALUE('{"foo foo":"bar"}', '$."foo foo"'); bar SELECT JSON_VALUE('{"hello":"\\uD83C\\uDF3A \\uD83C\\uDF38 \\uD83C\\uDF37 Hello, World \\uD83C\\uDF37 \\uD83C\\uDF38 \\uD83C\\uDF3A"}', '$.hello'); @@ -31,6 +31,8 @@ select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); \n\0 select JSON_VALUE('{"a":"\\u263a"}', '$.a'); ☺ +select JSON_VALUE('{"a":"b"}', "$.b"); +null SELECT '--JSON_QUERY--'; --JSON_QUERY-- SELECT JSON_QUERY('{"hello":1}', '$'); diff --git a/tests/queries/0_stateless/01889_sql_json_functions.sql b/tests/queries/0_stateless/01889_sql_json_functions.sql index e816443382c..622c4e3cb86 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.sql +++ b/tests/queries/0_stateless/01889_sql_json_functions.sql @@ -17,6 +17,7 @@ SELECT JSON_VALUE('{"hello":"\\uD83C\\uDF3A \\uD83C\\uDF38 \\uD83C\\uDF37 Hello, SELECT JSON_VALUE('{"a":"Hello \\"World\\" \\\\"}', '$.a'); select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); select JSON_VALUE('{"a":"\\u263a"}', '$.a'); +select JSON_VALUE('{"a":"b"}', "$.b"); SELECT '--JSON_QUERY--'; SELECT JSON_QUERY('{"hello":1}', '$'); From f971b544e138dfd1166e073cded75fe8640d78ae Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Sun, 12 Mar 2023 17:25:30 +0800 Subject: [PATCH 339/377] modify test --- tests/queries/0_stateless/01889_sql_json_functions.reference | 2 +- tests/queries/0_stateless/01889_sql_json_functions.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/01889_sql_json_functions.reference b/tests/queries/0_stateless/01889_sql_json_functions.reference index be055e64ae7..8275828e990 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.reference +++ b/tests/queries/0_stateless/01889_sql_json_functions.reference @@ -31,7 +31,7 @@ select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); \n\0 select JSON_VALUE('{"a":"\\u263a"}', '$.a'); ☺ -select JSON_VALUE('{"a":"b"}', "$.b"); +select JSON_VALUE('{"a":"b"}', '$.b'); null SELECT '--JSON_QUERY--'; --JSON_QUERY-- diff --git a/tests/queries/0_stateless/01889_sql_json_functions.sql b/tests/queries/0_stateless/01889_sql_json_functions.sql index 622c4e3cb86..290706ff15d 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.sql +++ b/tests/queries/0_stateless/01889_sql_json_functions.sql @@ -17,7 +17,7 @@ SELECT JSON_VALUE('{"hello":"\\uD83C\\uDF3A \\uD83C\\uDF38 \\uD83C\\uDF37 Hello, SELECT JSON_VALUE('{"a":"Hello \\"World\\" \\\\"}', '$.a'); select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); select JSON_VALUE('{"a":"\\u263a"}', '$.a'); -select JSON_VALUE('{"a":"b"}', "$.b"); +select JSON_VALUE('{"a":"b"}', '$.b'); SELECT '--JSON_QUERY--'; SELECT JSON_QUERY('{"hello":1}', '$'); From e0a10dc62fd18f0621a2b3d9eb872c11f7125fde Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Mon, 13 Mar 2023 18:19:23 +0800 Subject: [PATCH 340/377] enable nullable return type --- src/Core/Settings.h | 5 +++ src/Functions/FunctionSQLJSON.h | 38 ++++++++++++++++--- .../01889_sql_json_functions.reference | 6 +-- .../0_stateless/01889_sql_json_functions.sql | 2 +- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/Core/Settings.h b/src/Core/Settings.h index e9db155fb12..a9a210a6c58 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -941,10 +941,15 @@ class IColumn; M(Bool, input_format_bson_skip_fields_with_unsupported_types_in_schema_inference, false, "Skip fields with unsupported types while schema inference for format BSON.", 0) \ \ M(Bool, regexp_dict_allow_other_sources, false, "Allow regexp_tree dictionary to use sources other than yaml source.", 0) \ +<<<<<<< f971b544e138dfd1166e073cded75fe8640d78ae M(Bool, regexp_dict_allow_hyperscan, true, "Allow regexp_tree dictionary using Hyperscan library.", 0) \ \ M(Bool, dictionary_use_async_executor, false, "Execute a pipeline for reading from a dictionary with several threads. It's supported only by DIRECT dictionary with CLICKHOUSE source.", 0) \ +======= + M(Bool, regexp_dict_allow_hyperscan, false, "Allow regexp_tree dictionary using Hyperscan library.", 0) \ + M(Bool, function_return_type_allow_nullable, false, "Allow function to return nullable type.", 0) \ +>>>>>>> enable nullable return type // End of FORMAT_FACTORY_SETTINGS // Please add settings non-related to formats into the COMMON_SETTINGS above. diff --git a/src/Functions/FunctionSQLJSON.h b/src/Functions/FunctionSQLJSON.h index 94d38d1951b..39c22bea73f 100644 --- a/src/Functions/FunctionSQLJSON.h +++ b/src/Functions/FunctionSQLJSON.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "config.h" @@ -117,7 +118,6 @@ public: /// Parse JSON for every row Impl impl; - for (const auto i : collections::range(0, input_rows_count)) { std::string_view json{ @@ -156,7 +156,14 @@ public: DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { - return Impl::getReturnType(Name::name, arguments); + if constexpr (has_static_member_function_getReturnType, DataTypePtr(const char *, const ColumnsWithTypeAndName &, const bool &)>::value) + { + return Impl::getReturnType(Name::name, arguments, getContext()->getSettingsRef().function_return_type_allow_nullable); + } + else + { + return Impl::getReturnType(Name::name, arguments); + } } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override @@ -173,6 +180,9 @@ public: #endif return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth); } + +private: + BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION(getReturnType) }; struct NameJSONExists @@ -235,10 +245,22 @@ class JSONValueImpl public: using Element = typename JSONParser::Element; + static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &, const bool & allow_nullable) + { + if (allow_nullable) + { + DataTypePtr string_type = std::make_shared(); + return std::make_shared(string_type); + } + else + { + return std::make_shared(); + } + } + static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &) { - DataTypePtr string_type = std::make_shared(); - return std::make_shared(string_type); + return std::make_shared(); } static size_t getNumberOfIndexArguments(const ColumnsWithTypeAndName & arguments) { return arguments.size() - 1; } @@ -270,8 +292,9 @@ public: std::stringstream out; // STYLE_CHECK_ALLOW_STD_STRING_STREAM out << current_element.getElement(); auto output_str = out.str(); + bool is_nullable_col = typeid(dest) == typeid(ColumnNullable); ColumnNullable & col_null = assert_cast(dest); - ColumnString & col_str = assert_cast(col_null.getNestedColumn()); + ColumnString & col_str = is_nullable_col ? assert_cast(col_null.getNestedColumn()) : assert_cast(dest); ColumnString::Chars & data = col_str.getChars(); ColumnString::Offsets & offsets = col_str.getOffsets(); @@ -286,7 +309,10 @@ public: { col_str.insertData(output_str.data(), output_str.size()); } - col_null.getNullMapColumn().insertValue(0); + if (is_nullable_col) + { + col_null.getNullMapColumn().insertValue(0); + } return true; } }; diff --git a/tests/queries/0_stateless/01889_sql_json_functions.reference b/tests/queries/0_stateless/01889_sql_json_functions.reference index 8275828e990..d85decedda6 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.reference +++ b/tests/queries/0_stateless/01889_sql_json_functions.reference @@ -18,9 +18,9 @@ SELECT JSON_VALUE('{"hello":["world","world2"]}', '$.hello'); SELECT JSON_VALUE('{"hello":{"world":"!"}}', '$.hello'); {"world":"!"} SELECT JSON_VALUE('{hello:world}', '$.hello'); -null + SELECT JSON_VALUE('', '$.hello'); -null + SELECT JSON_VALUE('{"foo foo":"bar"}', '$."foo foo"'); bar SELECT JSON_VALUE('{"hello":"\\uD83C\\uDF3A \\uD83C\\uDF38 \\uD83C\\uDF37 Hello, World \\uD83C\\uDF37 \\uD83C\\uDF38 \\uD83C\\uDF3A"}', '$.hello'); @@ -31,7 +31,7 @@ select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); \n\0 select JSON_VALUE('{"a":"\\u263a"}', '$.a'); ☺ -select JSON_VALUE('{"a":"b"}', '$.b'); +select JSON_VALUE('{"a":"b"}', '$.b') settings function_return_type_allow_nullable=true; null SELECT '--JSON_QUERY--'; --JSON_QUERY-- diff --git a/tests/queries/0_stateless/01889_sql_json_functions.sql b/tests/queries/0_stateless/01889_sql_json_functions.sql index 290706ff15d..c428f959050 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.sql +++ b/tests/queries/0_stateless/01889_sql_json_functions.sql @@ -17,7 +17,7 @@ SELECT JSON_VALUE('{"hello":"\\uD83C\\uDF3A \\uD83C\\uDF38 \\uD83C\\uDF37 Hello, SELECT JSON_VALUE('{"a":"Hello \\"World\\" \\\\"}', '$.a'); select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); select JSON_VALUE('{"a":"\\u263a"}', '$.a'); -select JSON_VALUE('{"a":"b"}', '$.b'); +select JSON_VALUE('{"a":"b"}', '$.b') settings function_return_type_allow_nullable=true; SELECT '--JSON_QUERY--'; SELECT JSON_QUERY('{"hello":1}', '$'); From 05c508047adcfae00e74b74d479af6ee1a0f2989 Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Mon, 13 Mar 2023 18:22:02 +0800 Subject: [PATCH 341/377] docs fix --- docs/en/sql-reference/functions/json-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/sql-reference/functions/json-functions.md b/docs/en/sql-reference/functions/json-functions.md index 4c2372561b7..bfe2a541647 100644 --- a/docs/en/sql-reference/functions/json-functions.md +++ b/docs/en/sql-reference/functions/json-functions.md @@ -401,7 +401,7 @@ Before version 21.11 the order of arguments was wrong, i.e. JSON_QUERY(path, jso Parses a JSON and extract a value as JSON scalar. -If the value does not exist, NULL will be returned. +If the value does not exist, an empty string will be returned. Example: From 212619919307599476ab372392b93ebd82f820d7 Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Mon, 13 Mar 2023 19:12:44 +0800 Subject: [PATCH 342/377] check style --- src/Functions/FunctionSQLJSON.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/FunctionSQLJSON.h b/src/Functions/FunctionSQLJSON.h index 39c22bea73f..e3f5592db03 100644 --- a/src/Functions/FunctionSQLJSON.h +++ b/src/Functions/FunctionSQLJSON.h @@ -156,7 +156,7 @@ public: DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { - if constexpr (has_static_member_function_getReturnType, DataTypePtr(const char *, const ColumnsWithTypeAndName &, const bool &)>::value) + if constexpr (has_static_member_function_getReturnType, DataTypePtr(const char *, const ColumnsWithTypeAndName &, const bool &)>::value) { return Impl::getReturnType(Name::name, arguments, getContext()->getSettingsRef().function_return_type_allow_nullable); } From 0935ccf0e08aa7f4dc1615bee2186d8300ea62aa Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Tue, 14 Mar 2023 10:26:53 +0800 Subject: [PATCH 343/377] ci fix --- src/Functions/FunctionSQLJSON.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Functions/FunctionSQLJSON.h b/src/Functions/FunctionSQLJSON.h index e3f5592db03..a6eaa155a94 100644 --- a/src/Functions/FunctionSQLJSON.h +++ b/src/Functions/FunctionSQLJSON.h @@ -292,9 +292,20 @@ public: std::stringstream out; // STYLE_CHECK_ALLOW_STD_STRING_STREAM out << current_element.getElement(); auto output_str = out.str(); + auto cast_to_column_string = [&] (IColumn & col, bool & is_nullable_col) -> ColumnString & + { + if (is_nullable_col) + { + ColumnNullable & col_null = assert_cast(col); + return assert_cast(col_null.getNestedColumn()); + } + else + { + return assert_cast(dest); + } + }; bool is_nullable_col = typeid(dest) == typeid(ColumnNullable); - ColumnNullable & col_null = assert_cast(dest); - ColumnString & col_str = is_nullable_col ? assert_cast(col_null.getNestedColumn()) : assert_cast(dest); + ColumnString & col_str = cast_to_column_string(dest, is_nullable_col); ColumnString::Chars & data = col_str.getChars(); ColumnString::Offsets & offsets = col_str.getOffsets(); @@ -311,6 +322,7 @@ public: } if (is_nullable_col) { + ColumnNullable & col_null = assert_cast(dest); col_null.getNullMapColumn().insertValue(0); } return true; From 3adc42af5c23bf1c58188a77d8ec2b998dad2a4f Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Wed, 15 Mar 2023 11:28:02 +0800 Subject: [PATCH 344/377] ci fix --- tests/queries/0_stateless/01889_sql_json_functions.reference | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/01889_sql_json_functions.reference b/tests/queries/0_stateless/01889_sql_json_functions.reference index d85decedda6..9f0e9cd64c2 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.reference +++ b/tests/queries/0_stateless/01889_sql_json_functions.reference @@ -17,7 +17,7 @@ SELECT JSON_VALUE('{"hello":["world","world2"]}', '$.hello'); ["world","world2"] SELECT JSON_VALUE('{"hello":{"world":"!"}}', '$.hello'); {"world":"!"} -SELECT JSON_VALUE('{hello:world}', '$.hello'); +SELECT JSON_VALUE('{hello:world}', '$.hello'); -- invalid json => default value (empty string) SELECT JSON_VALUE('', '$.hello'); @@ -32,7 +32,7 @@ select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); select JSON_VALUE('{"a":"\\u263a"}', '$.a'); ☺ select JSON_VALUE('{"a":"b"}', '$.b') settings function_return_type_allow_nullable=true; -null +\N SELECT '--JSON_QUERY--'; --JSON_QUERY-- SELECT JSON_QUERY('{"hello":1}', '$'); From ce5f4cf4ab7c431755d2a63d17dda3f5386127ab Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Sat, 25 Mar 2023 21:54:45 +0800 Subject: [PATCH 345/377] code review fix --- docs/en/operations/settings/settings.md | 41 +++++++++ .../sql-reference/functions/json-functions.md | 4 +- src/Core/Settings.h | 5 -- src/Functions/FunctionSQLJSON.h | 86 ++++++++----------- .../01889_sql_json_functions.reference | 14 +-- .../0_stateless/01889_sql_json_functions.sql | 4 +- 6 files changed, 92 insertions(+), 62 deletions(-) diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index 5ea555aa56a..1ebf13d36b9 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -4049,3 +4049,44 @@ SELECT sum(number) FROM numbers(10000000000) SETTINGS partial_result_on_first_ca Possible values: `true`, `false` Default value: `false` +## function_return_type_allow_nullable + +Control whether allow to return `NULL` when value is not exist for JSON_VALUE function. + +```sql +SELECT JSON_VALUE('{"hello":"world"}', '$.b') settings function_return_type_allow_nullable=true; + +┌─JSON_VALUE('{"hello":"world"}', '$.b')─┐ +│ ᴺᵁᴸᴸ │ +└────────────────────────────────────────┘ + +1 row in set. Elapsed: 0.001 sec. +``` + +Possible values: + +- true — Allow. +- false — Disallow. + +Default value: `false`. + +## function_json_value_return_type_allow_complex + +Control whether allow to return complex type (such as: struct, array, map) for json_value function. + +```sql +SELECT JSON_VALUE('{"hello":{"world":"!"}}', '$.hello') settings function_json_value_return_type_allow_complex=true + +┌─JSON_VALUE('{"hello":{"world":"!"}}', '$.hello')─┐ +│ {"world":"!"} │ +└──────────────────────────────────────────────────┘ + +1 row in set. Elapsed: 0.001 sec. +``` + +Possible values: + +- true — Allow. +- false — Disallow. + +Default value: `false`. diff --git a/docs/en/sql-reference/functions/json-functions.md b/docs/en/sql-reference/functions/json-functions.md index bfe2a541647..81697f901c1 100644 --- a/docs/en/sql-reference/functions/json-functions.md +++ b/docs/en/sql-reference/functions/json-functions.md @@ -401,7 +401,7 @@ Before version 21.11 the order of arguments was wrong, i.e. JSON_QUERY(path, jso Parses a JSON and extract a value as JSON scalar. -If the value does not exist, an empty string will be returned. +If the value does not exist, an empty string will be returned by default, and by SET `function_return_type_allow_nullable` = `true`, `NULL` will be returned. If the value is complex type (such as: struct, array, map), an empty string will be returned by default, and by SET `function_json_value_return_type_allow_complex` = `true`, the complex value will be returned. Example: @@ -410,6 +410,8 @@ SELECT JSON_VALUE('{"hello":"world"}', '$.hello'); SELECT JSON_VALUE('{"array":[[0, 1, 2, 3, 4, 5], [0, -1, -2, -3, -4, -5]]}', '$.array[*][0 to 2, 4]'); SELECT JSON_VALUE('{"hello":2}', '$.hello'); SELECT toTypeName(JSON_VALUE('{"hello":2}', '$.hello')); +select JSON_VALUE('{"hello":"world"}', '$.b') settings function_return_type_allow_nullable=true; +select JSON_VALUE('{"hello":{"world":"!"}}', '$.hello') settings function_json_value_return_type_allow_complex=true; ``` Result: diff --git a/src/Core/Settings.h b/src/Core/Settings.h index a9a210a6c58..7271cb2c84f 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -941,15 +941,10 @@ class IColumn; M(Bool, input_format_bson_skip_fields_with_unsupported_types_in_schema_inference, false, "Skip fields with unsupported types while schema inference for format BSON.", 0) \ \ M(Bool, regexp_dict_allow_other_sources, false, "Allow regexp_tree dictionary to use sources other than yaml source.", 0) \ -<<<<<<< f971b544e138dfd1166e073cded75fe8640d78ae M(Bool, regexp_dict_allow_hyperscan, true, "Allow regexp_tree dictionary using Hyperscan library.", 0) \ \ M(Bool, dictionary_use_async_executor, false, "Execute a pipeline for reading from a dictionary with several threads. It's supported only by DIRECT dictionary with CLICKHOUSE source.", 0) \ - -======= M(Bool, regexp_dict_allow_hyperscan, false, "Allow regexp_tree dictionary using Hyperscan library.", 0) \ - M(Bool, function_return_type_allow_nullable, false, "Allow function to return nullable type.", 0) \ ->>>>>>> enable nullable return type // End of FORMAT_FACTORY_SETTINGS // Please add settings non-related to formats into the COMMON_SETTINGS above. diff --git a/src/Functions/FunctionSQLJSON.h b/src/Functions/FunctionSQLJSON.h index a6eaa155a94..8684a5e39e5 100644 --- a/src/Functions/FunctionSQLJSON.h +++ b/src/Functions/FunctionSQLJSON.h @@ -23,7 +23,6 @@ #include #include #include -#include #include "config.h" @@ -43,7 +42,8 @@ public: class Executor { public: - static ColumnPtr run(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count, uint32_t parse_depth) + static ColumnPtr run(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count, uint32_t parse_depth, + const bool & return_type_allow_complex) { MutableColumnPtr to{result_type->createColumn()}; to->reserve(input_rows_count); @@ -127,7 +127,7 @@ public: bool added_to_column = false; if (document_ok) { - added_to_column = impl.insertResultToColumn(*to, document, res); + added_to_column = impl.insertResultToColumn(*to, document, res, return_type_allow_complex); } if (!added_to_column) { @@ -156,14 +156,7 @@ public: DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { - if constexpr (has_static_member_function_getReturnType, DataTypePtr(const char *, const ColumnsWithTypeAndName &, const bool &)>::value) - { - return Impl::getReturnType(Name::name, arguments, getContext()->getSettingsRef().function_return_type_allow_nullable); - } - else - { - return Impl::getReturnType(Name::name, arguments); - } + return Impl::getReturnType(Name::name, arguments, getContext()); } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override @@ -176,13 +169,12 @@ public: unsigned parse_depth = static_cast(getContext()->getSettingsRef().max_parser_depth); #if USE_SIMDJSON if (getContext()->getSettingsRef().allow_simdjson) - return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth); + return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth, + getContext()->getSettingsRef().function_json_value_return_type_allow_complex); #endif - return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth); + return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth, + getContext()->getSettingsRef().function_json_value_return_type_allow_complex); } - -private: - BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION(getReturnType) }; struct NameJSONExists @@ -206,11 +198,11 @@ class JSONExistsImpl public: using Element = typename JSONParser::Element; - static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &) { return std::make_shared(); } + static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &, const ContextPtr &) { return std::make_shared(); } static size_t getNumberOfIndexArguments(const ColumnsWithTypeAndName & arguments) { return arguments.size() - 1; } - static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr) + static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const bool &) { GeneratorJSONPath generator_json_path(query_ptr); Element current_element = root; @@ -245,9 +237,9 @@ class JSONValueImpl public: using Element = typename JSONParser::Element; - static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &, const bool & allow_nullable) + static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &, const ContextPtr & context) { - if (allow_nullable) + if (context->getSettingsRef().function_return_type_allow_nullable) { DataTypePtr string_type = std::make_shared(); return std::make_shared(string_type); @@ -258,14 +250,9 @@ public: } } - static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &) - { - return std::make_shared(); - } - static size_t getNumberOfIndexArguments(const ColumnsWithTypeAndName & arguments) { return arguments.size() - 1; } - static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr) + static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const bool & return_type_allow_complex) { GeneratorJSONPath generator_json_path(query_ptr); Element current_element = root; @@ -275,7 +262,14 @@ public: { if (status == VisitorStatus::Ok) { - break; + if (return_type_allow_complex) + { + break; + } + else if (!(current_element.isArray() || current_element.isObject())) + { + break; + } } else if (status == VisitorStatus::Error) { @@ -292,22 +286,19 @@ public: std::stringstream out; // STYLE_CHECK_ALLOW_STD_STRING_STREAM out << current_element.getElement(); auto output_str = out.str(); - auto cast_to_column_string = [&] (IColumn & col, bool & is_nullable_col) -> ColumnString & + ColumnString * col_str; + if (isColumnNullable(dest)) { - if (is_nullable_col) - { - ColumnNullable & col_null = assert_cast(col); - return assert_cast(col_null.getNestedColumn()); - } - else - { - return assert_cast(dest); - } - }; - bool is_nullable_col = typeid(dest) == typeid(ColumnNullable); - ColumnString & col_str = cast_to_column_string(dest, is_nullable_col); - ColumnString::Chars & data = col_str.getChars(); - ColumnString::Offsets & offsets = col_str.getOffsets(); + ColumnNullable & col_null = assert_cast(dest); + col_null.getNullMapData().push_back(0); + col_str = assert_cast(&col_null.getNestedColumn()); + } + else + { + col_str = assert_cast(&dest); + } + ColumnString::Chars & data = col_str->getChars(); + ColumnString::Offsets & offsets = col_str->getOffsets(); if (current_element.isString()) { @@ -318,12 +309,7 @@ public: } else { - col_str.insertData(output_str.data(), output_str.size()); - } - if (is_nullable_col) - { - ColumnNullable & col_null = assert_cast(dest); - col_null.getNullMapColumn().insertValue(0); + col_str->insertData(output_str.data(), output_str.size()); } return true; } @@ -339,11 +325,11 @@ class JSONQueryImpl public: using Element = typename JSONParser::Element; - static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &) { return std::make_shared(); } + static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &, const ContextPtr &) { return std::make_shared(); } static size_t getNumberOfIndexArguments(const ColumnsWithTypeAndName & arguments) { return arguments.size() - 1; } - static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr) + static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const bool &) { GeneratorJSONPath generator_json_path(query_ptr); Element current_element = root; diff --git a/tests/queries/0_stateless/01889_sql_json_functions.reference b/tests/queries/0_stateless/01889_sql_json_functions.reference index 9f0e9cd64c2..0d7fb270009 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.reference +++ b/tests/queries/0_stateless/01889_sql_json_functions.reference @@ -1,8 +1,8 @@ -- { echo } SELECT '--JSON_VALUE--'; --JSON_VALUE-- -SELECT JSON_VALUE('{"hello":1}', '$'); -{"hello":1} +SELECT JSON_VALUE('{"hello":1}', '$'); -- root is a complex object => default value (empty string) + SELECT JSON_VALUE('{"hello":1}', '$.hello'); 1 SELECT JSON_VALUE('{"hello":1.2}', '$.hello'); @@ -14,9 +14,9 @@ world SELECT JSON_VALUE('{"hello":null}', '$.hello'); null SELECT JSON_VALUE('{"hello":["world","world2"]}', '$.hello'); -["world","world2"] + SELECT JSON_VALUE('{"hello":{"world":"!"}}', '$.hello'); -{"world":"!"} + SELECT JSON_VALUE('{hello:world}', '$.hello'); -- invalid json => default value (empty string) SELECT JSON_VALUE('', '$.hello'); @@ -31,8 +31,12 @@ select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); \n\0 select JSON_VALUE('{"a":"\\u263a"}', '$.a'); ☺ -select JSON_VALUE('{"a":"b"}', '$.b') settings function_return_type_allow_nullable=true; +select JSON_VALUE('{"hello":"world"}', '$.b') settings function_return_type_allow_nullable=true; \N +select JSON_VALUE('{"hello":{"world":"!"}}', '$.hello') settings function_json_value_return_type_allow_complex=true; +{"world":"!"} +SELECT JSON_VALUE('{"hello":["world","world2"]}', '$.hello') settings function_json_value_return_type_allow_complex=true; +["world","world2"] SELECT '--JSON_QUERY--'; --JSON_QUERY-- SELECT JSON_QUERY('{"hello":1}', '$'); diff --git a/tests/queries/0_stateless/01889_sql_json_functions.sql b/tests/queries/0_stateless/01889_sql_json_functions.sql index c428f959050..71ef6ee2ada 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.sql +++ b/tests/queries/0_stateless/01889_sql_json_functions.sql @@ -17,7 +17,9 @@ SELECT JSON_VALUE('{"hello":"\\uD83C\\uDF3A \\uD83C\\uDF38 \\uD83C\\uDF37 Hello, SELECT JSON_VALUE('{"a":"Hello \\"World\\" \\\\"}', '$.a'); select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); select JSON_VALUE('{"a":"\\u263a"}', '$.a'); -select JSON_VALUE('{"a":"b"}', '$.b') settings function_return_type_allow_nullable=true; +select JSON_VALUE('{"hello":"world"}', '$.b') settings function_return_type_allow_nullable=true; +select JSON_VALUE('{"hello":{"world":"!"}}', '$.hello') settings function_json_value_return_type_allow_complex=true; +SELECT JSON_VALUE('{"hello":["world","world2"]}', '$.hello') settings function_json_value_return_type_allow_complex=true; SELECT '--JSON_QUERY--'; SELECT JSON_QUERY('{"hello":1}', '$'); From 7d59f36336eb71fc42865ca890dc9e9b1928240b Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Sat, 25 Mar 2023 21:59:09 +0800 Subject: [PATCH 346/377] code style fix --- src/Functions/FunctionSQLJSON.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Functions/FunctionSQLJSON.h b/src/Functions/FunctionSQLJSON.h index 8684a5e39e5..f31a1980acc 100644 --- a/src/Functions/FunctionSQLJSON.h +++ b/src/Functions/FunctionSQLJSON.h @@ -42,8 +42,7 @@ public: class Executor { public: - static ColumnPtr run(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count, uint32_t parse_depth, - const bool & return_type_allow_complex) + static ColumnPtr run(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count, uint32_t parse_depth, const bool & return_type_allow_complex) { MutableColumnPtr to{result_type->createColumn()}; to->reserve(input_rows_count); From da4ff587af1dc4c137912d9cc643b86cddfdee32 Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Sun, 2 Apr 2023 00:18:36 +0800 Subject: [PATCH 347/377] review fix --- docs/en/operations/settings/settings.md | 4 ++-- src/Functions/FunctionSQLJSON.h | 20 +++++++++---------- .../01889_sql_json_functions.reference | 2 +- .../0_stateless/01889_sql_json_functions.sql | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index 1ebf13d36b9..bb134c4d9d5 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -4049,12 +4049,12 @@ SELECT sum(number) FROM numbers(10000000000) SETTINGS partial_result_on_first_ca Possible values: `true`, `false` Default value: `false` -## function_return_type_allow_nullable +## function_json_value_return_type_allow_nullable Control whether allow to return `NULL` when value is not exist for JSON_VALUE function. ```sql -SELECT JSON_VALUE('{"hello":"world"}', '$.b') settings function_return_type_allow_nullable=true; +SELECT JSON_VALUE('{"hello":"world"}', '$.b') settings function_json_value_return_type_allow_nullable=true; ┌─JSON_VALUE('{"hello":"world"}', '$.b')─┐ │ ᴺᵁᴸᴸ │ diff --git a/src/Functions/FunctionSQLJSON.h b/src/Functions/FunctionSQLJSON.h index f31a1980acc..9565ca5b242 100644 --- a/src/Functions/FunctionSQLJSON.h +++ b/src/Functions/FunctionSQLJSON.h @@ -42,7 +42,7 @@ public: class Executor { public: - static ColumnPtr run(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count, uint32_t parse_depth, const bool & return_type_allow_complex) + static ColumnPtr run(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count, uint32_t parse_depth, const ContextPtr & context) { MutableColumnPtr to{result_type->createColumn()}; to->reserve(input_rows_count); @@ -126,7 +126,7 @@ public: bool added_to_column = false; if (document_ok) { - added_to_column = impl.insertResultToColumn(*to, document, res, return_type_allow_complex); + added_to_column = impl.insertResultToColumn(*to, document, res, context); } if (!added_to_column) { @@ -168,11 +168,9 @@ public: unsigned parse_depth = static_cast(getContext()->getSettingsRef().max_parser_depth); #if USE_SIMDJSON if (getContext()->getSettingsRef().allow_simdjson) - return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth, - getContext()->getSettingsRef().function_json_value_return_type_allow_complex); + return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth, getContext()); #endif - return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth, - getContext()->getSettingsRef().function_json_value_return_type_allow_complex); + return FunctionSQLJSONHelpers::Executor::run(arguments, result_type, input_rows_count, parse_depth, getContext()); } }; @@ -201,7 +199,7 @@ public: static size_t getNumberOfIndexArguments(const ColumnsWithTypeAndName & arguments) { return arguments.size() - 1; } - static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const bool &) + static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const ContextPtr &) { GeneratorJSONPath generator_json_path(query_ptr); Element current_element = root; @@ -238,7 +236,7 @@ public: static DataTypePtr getReturnType(const char *, const ColumnsWithTypeAndName &, const ContextPtr & context) { - if (context->getSettingsRef().function_return_type_allow_nullable) + if (context->getSettingsRef().function_json_value_return_type_allow_nullable) { DataTypePtr string_type = std::make_shared(); return std::make_shared(string_type); @@ -251,7 +249,7 @@ public: static size_t getNumberOfIndexArguments(const ColumnsWithTypeAndName & arguments) { return arguments.size() - 1; } - static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const bool & return_type_allow_complex) + static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const ContextPtr & context) { GeneratorJSONPath generator_json_path(query_ptr); Element current_element = root; @@ -261,7 +259,7 @@ public: { if (status == VisitorStatus::Ok) { - if (return_type_allow_complex) + if (context->getSettingsRef().function_json_value_return_type_allow_complex) { break; } @@ -328,7 +326,7 @@ public: static size_t getNumberOfIndexArguments(const ColumnsWithTypeAndName & arguments) { return arguments.size() - 1; } - static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const bool &) + static bool insertResultToColumn(IColumn & dest, const Element & root, ASTPtr & query_ptr, const ContextPtr &) { GeneratorJSONPath generator_json_path(query_ptr); Element current_element = root; diff --git a/tests/queries/0_stateless/01889_sql_json_functions.reference b/tests/queries/0_stateless/01889_sql_json_functions.reference index 0d7fb270009..5ac1ff501e5 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.reference +++ b/tests/queries/0_stateless/01889_sql_json_functions.reference @@ -31,7 +31,7 @@ select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); \n\0 select JSON_VALUE('{"a":"\\u263a"}', '$.a'); ☺ -select JSON_VALUE('{"hello":"world"}', '$.b') settings function_return_type_allow_nullable=true; +select JSON_VALUE('{"hello":"world"}', '$.b') settings function_json_value_return_type_allow_nullable=true; \N select JSON_VALUE('{"hello":{"world":"!"}}', '$.hello') settings function_json_value_return_type_allow_complex=true; {"world":"!"} diff --git a/tests/queries/0_stateless/01889_sql_json_functions.sql b/tests/queries/0_stateless/01889_sql_json_functions.sql index 71ef6ee2ada..f174d04933c 100644 --- a/tests/queries/0_stateless/01889_sql_json_functions.sql +++ b/tests/queries/0_stateless/01889_sql_json_functions.sql @@ -17,7 +17,7 @@ SELECT JSON_VALUE('{"hello":"\\uD83C\\uDF3A \\uD83C\\uDF38 \\uD83C\\uDF37 Hello, SELECT JSON_VALUE('{"a":"Hello \\"World\\" \\\\"}', '$.a'); select JSON_VALUE('{"a":"\\n\\u0000"}', '$.a'); select JSON_VALUE('{"a":"\\u263a"}', '$.a'); -select JSON_VALUE('{"hello":"world"}', '$.b') settings function_return_type_allow_nullable=true; +select JSON_VALUE('{"hello":"world"}', '$.b') settings function_json_value_return_type_allow_nullable=true; select JSON_VALUE('{"hello":{"world":"!"}}', '$.hello') settings function_json_value_return_type_allow_complex=true; SELECT JSON_VALUE('{"hello":["world","world2"]}', '$.hello') settings function_json_value_return_type_allow_complex=true; From c2687c05443b0dd575d2e6b96322f47dfe124a85 Mon Sep 17 00:00:00 2001 From: KevinyhZou Date: Sun, 2 Apr 2023 14:14:35 +0800 Subject: [PATCH 348/377] review fix --- src/Core/Settings.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 7271cb2c84f..7f6fa19d660 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -722,6 +722,8 @@ class IColumn; M(Bool, force_aggregation_in_order, false, "Force use of aggregation in order on remote nodes during distributed aggregation. PLEASE, NEVER CHANGE THIS SETTING VALUE MANUALLY!", IMPORTANT) \ M(UInt64, http_max_request_param_data_size, 10_MiB, "Limit on size of request data used as a query parameter in predefined HTTP requests.", 0) \ M(Bool, allow_experimental_undrop_table_query, false, "Allow to use undrop query to restore dropped table in a limited time", 0) \ + M(Bool, function_json_value_return_type_allow_nullable, false, "Allow function to return nullable type.", 0) \ + M(Bool, function_json_value_return_type_allow_complex, false, "Allow function to return complex type, such as: struct, array, map.", 0) \ // End of COMMON_SETTINGS // Please add settings related to formats into the FORMAT_FACTORY_SETTINGS and move obsolete settings to OBSOLETE_SETTINGS. @@ -944,7 +946,6 @@ class IColumn; M(Bool, regexp_dict_allow_hyperscan, true, "Allow regexp_tree dictionary using Hyperscan library.", 0) \ \ M(Bool, dictionary_use_async_executor, false, "Execute a pipeline for reading from a dictionary with several threads. It's supported only by DIRECT dictionary with CLICKHOUSE source.", 0) \ - M(Bool, regexp_dict_allow_hyperscan, false, "Allow regexp_tree dictionary using Hyperscan library.", 0) \ // End of FORMAT_FACTORY_SETTINGS // Please add settings non-related to formats into the COMMON_SETTINGS above. From 907ed27ae2e04db05f9fdaccbed6d9d8baa2c7c6 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sun, 2 Apr 2023 10:09:39 +0200 Subject: [PATCH 349/377] Fix crash in EXPLAIN PIPELINE for Merge over Distributed CI: https://s3.amazonaws.com/clickhouse-test-reports/48314/179450542879d11711cd2415c3fa7eeab18188be/fuzzer_astfuzzerasan/report.html Signed-off-by: Azat Khuzhin --- src/Interpreters/IInterpreterUnionOrSelectQuery.cpp | 5 ++++- src/Interpreters/IInterpreterUnionOrSelectQuery.h | 1 + src/Interpreters/InterpreterSelectQueryAnalyzer.h | 1 + src/Storages/StorageMerge.cpp | 8 +++++--- src/Storages/StorageMerge.h | 2 +- .../02704_storage_merge_explain_graph_crash.sql | 9 ++++++++- 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Interpreters/IInterpreterUnionOrSelectQuery.cpp b/src/Interpreters/IInterpreterUnionOrSelectQuery.cpp index b6e910eac94..4aa87346e80 100644 --- a/src/Interpreters/IInterpreterUnionOrSelectQuery.cpp +++ b/src/Interpreters/IInterpreterUnionOrSelectQuery.cpp @@ -18,9 +18,12 @@ namespace DB QueryPipelineBuilder IInterpreterUnionOrSelectQuery::buildQueryPipeline() { QueryPlan query_plan; + return buildQueryPipeline(query_plan); +} +QueryPipelineBuilder IInterpreterUnionOrSelectQuery::buildQueryPipeline(QueryPlan & query_plan) +{ buildQueryPlan(query_plan); - return std::move(*query_plan.buildQueryPipeline( QueryPlanOptimizationSettings::fromContext(context), BuildQueryPipelineSettings::fromContext(context))); } diff --git a/src/Interpreters/IInterpreterUnionOrSelectQuery.h b/src/Interpreters/IInterpreterUnionOrSelectQuery.h index 1147070f48a..e4425a73505 100644 --- a/src/Interpreters/IInterpreterUnionOrSelectQuery.h +++ b/src/Interpreters/IInterpreterUnionOrSelectQuery.h @@ -35,6 +35,7 @@ public: virtual void buildQueryPlan(QueryPlan & query_plan) = 0; QueryPipelineBuilder buildQueryPipeline(); + QueryPipelineBuilder buildQueryPipeline(QueryPlan & query_plan); virtual void ignoreWithTotals() = 0; diff --git a/src/Interpreters/InterpreterSelectQueryAnalyzer.h b/src/Interpreters/InterpreterSelectQueryAnalyzer.h index de97400e01b..33497c3ea16 100644 --- a/src/Interpreters/InterpreterSelectQueryAnalyzer.h +++ b/src/Interpreters/InterpreterSelectQueryAnalyzer.h @@ -72,6 +72,7 @@ public: void setProperClientInfo(size_t replica_number, size_t count_participating_replicas); const Planner & getPlanner() const { return planner; } + Planner & getPlanner() { return planner; } private: ASTPtr query; diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 00ccfebff25..a76126c7879 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -649,14 +649,13 @@ QueryPipelineBuilderPtr ReadFromMerge::createSources( QueryProcessingStage::Complete, storage_snapshot, modified_query_info); + if (processed_stage <= storage_stage || (allow_experimental_analyzer && processed_stage == QueryProcessingStage::FetchColumns)) { /// If there are only virtual columns in query, you must request at least one other column. if (real_column_names.empty()) real_column_names.push_back(ExpressionActions::getSmallestColumn(storage_snapshot->metadata->getColumns().getAllPhysical()).name); - /// Steps for reading from child tables should have the same lifetime as the current step - /// because `builder` can have references to them (mainly for EXPLAIN PIPELINE). QueryPlan & plan = child_plans.emplace_back(); StorageView * view = dynamic_cast(storage.get()); @@ -709,12 +708,15 @@ QueryPipelineBuilderPtr ReadFromMerge::createSources( modified_context->setSetting("max_threads", streams_num); modified_context->setSetting("max_streams_to_max_threads_ratio", 1); + QueryPlan & plan = child_plans.emplace_back(); + if (allow_experimental_analyzer) { InterpreterSelectQueryAnalyzer interpreter(modified_query_info.query_tree, modified_context, SelectQueryOptions(processed_stage).ignoreProjections()); builder = std::make_unique(interpreter.buildQueryPipeline()); + plan = std::move(interpreter.getPlanner()).extractQueryPlan(); } else { @@ -723,7 +725,7 @@ QueryPipelineBuilderPtr ReadFromMerge::createSources( InterpreterSelectQuery interpreter{modified_query_info.query, modified_context, SelectQueryOptions(processed_stage).ignoreProjections()}; - builder = std::make_unique(interpreter.buildQueryPipeline()); + builder = std::make_unique(interpreter.buildQueryPipeline(plan)); } /** Materialization is needed, since from distributed storage the constants come materialized. diff --git a/src/Storages/StorageMerge.h b/src/Storages/StorageMerge.h index d53dcd34f5f..c4b6d815935 100644 --- a/src/Storages/StorageMerge.h +++ b/src/Storages/StorageMerge.h @@ -160,7 +160,7 @@ private: StorageSnapshotPtr merge_storage_snapshot; /// Store read plan for each child table. - /// It's needed to guarantee lifetime for child steps to be the same as for this step. + /// It's needed to guarantee lifetime for child steps to be the same as for this step (mainly for EXPLAIN PIPELINE). std::vector child_plans; SelectQueryInfo query_info; diff --git a/tests/queries/0_stateless/02704_storage_merge_explain_graph_crash.sql b/tests/queries/0_stateless/02704_storage_merge_explain_graph_crash.sql index b1725da6e82..44a8fe4f049 100644 --- a/tests/queries/0_stateless/02704_storage_merge_explain_graph_crash.sql +++ b/tests/queries/0_stateless/02704_storage_merge_explain_graph_crash.sql @@ -1,9 +1,16 @@ DROP TABLE IF EXISTS foo; +DROP TABLE IF EXISTS foo2; +DROP TABLE IF EXISTS foo2_dist; DROP TABLE IF EXISTS merge1; CREATE TABLE foo (`Id` Int32, `Val` Int32) ENGINE = MergeTree ORDER BY Id; INSERT INTO foo SELECT number, number FROM numbers(100); -CREATE TABLE merge1 AS foo ENGINE = Merge(currentDatabase(), '^foo'); +CREATE TABLE foo2 (`Id` Int32, `Val` Int32) ENGINE = MergeTree ORDER BY Id; +INSERT INTO foo2 SELECT number, number FROM numbers(100); +CREATE TABLE foo2_dist (`Id` UInt32, `Val` String) ENGINE = Distributed(test_shard_localhost, currentDatabase(), foo2); + +CREATE TABLE merge1 AS foo ENGINE = Merge(currentDatabase(), '^(foo|foo2_dist)$'); EXPLAIN PIPELINE graph = 1, compact = 1 SELECT * FROM merge1 FORMAT Null; +EXPLAIN PIPELINE graph = 1, compact = 1 SELECT * FROM merge1 FORMAT Null SETTINGS allow_experimental_analyzer=1; From 9a051bc27ec20213e351216bc8c32c9c91d76adc Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Sun, 2 Apr 2023 12:30:36 -0300 Subject: [PATCH 350/377] some complex query (it fails with analyzer enabled --- ...707_complex_query_fails_analyzer.reference | 10 ++ .../02707_complex_query_fails_analyzer.sql | 117 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 tests/queries/0_stateless/02707_complex_query_fails_analyzer.reference create mode 100644 tests/queries/0_stateless/02707_complex_query_fails_analyzer.sql diff --git a/tests/queries/0_stateless/02707_complex_query_fails_analyzer.reference b/tests/queries/0_stateless/02707_complex_query_fails_analyzer.reference new file mode 100644 index 00000000000..192f8aa904a --- /dev/null +++ b/tests/queries/0_stateless/02707_complex_query_fails_analyzer.reference @@ -0,0 +1,10 @@ +1 1 -59.952 +1 2 59.952 +1 3 -100 +2 1 -93.7611 +2 2 93.7611 +3 1 0 +3 2 0 +--------- +0 +0 diff --git a/tests/queries/0_stateless/02707_complex_query_fails_analyzer.sql b/tests/queries/0_stateless/02707_complex_query_fails_analyzer.sql new file mode 100644 index 00000000000..a9d83479d50 --- /dev/null +++ b/tests/queries/0_stateless/02707_complex_query_fails_analyzer.sql @@ -0,0 +1,117 @@ +DROP TABLE IF EXISTS srv_account_parts; +DROP TABLE IF EXISTS etl_batch; + +CREATE TABLE srv_account_parts( + shard_num UInt16, + account_ids Array(Int64) +)ENGINE = ReplacingMergeTree +ORDER BY shard_num +as select * from values ((0,[]),(1,[1,2,3]),(2,[1,2,3]),(3,[1])); + +CREATE TABLE etl_batch( + batch_id UInt64, + batch_start DateTime, + batch_start_day Date DEFAULT toDate(batch_start), + batch_load DateTime, + total_num_records UInt32, + etl_server_id Int32, + account_id UInt64, + shard_num UInt16 +)ENGINE = ReplacingMergeTree +PARTITION BY toYYYYMM(batch_start_day) +ORDER BY (batch_id, etl_server_id, account_id); + +insert into etl_batch(batch_id, batch_start, batch_load, total_num_records, etl_server_id, account_id, shard_num) +select number batch_id, + toDateTime('2022-01-01') + INTERVAL 23 HOUR batch_start, + batch_start batch_load, + 333 total_num_records, + 1 etl_server_id, + number%3+1 account_id, + 1 shard_num +from numbers(1000); + +insert into etl_batch(batch_id, batch_start, batch_load, total_num_records, etl_server_id, account_id, shard_num) +select number+2000 batch_id, + toDateTime('2022-01-01') + INTERVAL 23 HOUR batch_start, + batch_start batch_load, + 333 total_num_records, + 1 etl_server_id, + number%3+1 account_id, + 2 shard_num +from numbers(1000); + +insert into etl_batch(batch_id, batch_start, batch_load, total_num_records, etl_server_id, account_id, shard_num) +select number+4000 batch_id, + toDateTime('2022-01-01') + INTERVAL 3 HOUR batch_start, + batch_start batch_load, + 3333 total_num_records, + 1 etl_server_id, + 2 account_id, + 2 shard_num +from numbers(1000); + +insert into etl_batch(batch_id, batch_start, batch_load, total_num_records, etl_server_id, account_id, shard_num) +select number+6000 batch_id, + toDateTime('2022-01-01') + INTERVAL 23 HOUR batch_start, + batch_start batch_load, + 333 total_num_records, + 1 etl_server_id, + 1 account_id, + 2 shard_num +from numbers(1000); + +insert into etl_batch(batch_id, batch_start, batch_load, total_num_records, etl_server_id, account_id, shard_num) +select number+8000 batch_id, + toDateTime('2022-01-01') + INTERVAL 23 HOUR batch_start, + batch_start batch_load, + 1000 total_num_records, + 1 etl_server_id, + 3 account_id, + 3 shard_num +from numbers(1000); + +CREATE OR REPLACE VIEW v_num_records_by_node_bias_acc as +SELECT shard_num, + arrayJoin(account_ids) AS account_id, + records_24h, + records_12h, + IF (b = '',-100,xbias) AS bias, + IF (bias > 10,0,IF (bias > 0,1,IF (bias < -10,301,300))) AS sbias +FROM srv_account_parts + LEFT JOIN (SELECT account_id, + shard_num, + records_24h, + records_12h, + xbias, + 'b' AS b + FROM (SELECT account_id, + groupArray((shard_num,records_24h,records_12h)) AS ga, + arraySum(ga.2) AS tot24, + arraySum(ga.3) AS tot12, + arrayMap(i ->(((((i.2)*LENGTH(ga))*100) / tot24) - 100),ga) AS bias24, + arrayMap(i ->(((((i.3)*LENGTH(ga))*100) / tot12) - 100),ga) AS bias12, + arrayMap((i,j,k) ->(i,IF (tot12 = 0,0,IF (ABS(j) > ABS(k),j,k))),ga,bias24,bias12) AS a_bias + FROM (SELECT shard_num, + toInt64(account_id) AS account_id, + SUM(total_num_records) AS records_24h, + sumIf(total_num_records,batch_load >(toDateTime('2022-01-02') -(3600*12))) AS records_12h + FROM etl_batch FINAL PREWHERE (batch_start_day >= (toDate('2022-01-02') - 2)) AND (batch_load > (toDateTime('2022-01-02') - (3600*24))) + where (shard_num, account_id) in (select shard_num, arrayJoin(account_ids) from srv_account_parts) + GROUP BY shard_num,account_id) + GROUP BY account_id) + ARRAY JOIN (a_bias.1).1 AS shard_num,a_bias.2 AS xbias, (a_bias.1).2 AS records_24h, (a_bias.1).3 AS records_12h + ) s USING (shard_num,account_id); + +select account_id, shard_num, round(bias,4) +from v_num_records_by_node_bias_acc +order by account_id, shard_num, bias; + +select '---------'; + +SELECT a AS b FROM (SELECT 0 a) s LEFT JOIN (SELECT 0 b) t USING (b); + +SELECT arrayJoin(a) AS b FROM (SELECT [0] a) s LEFT JOIN (SELECT 0 b) t USING (b); + +DROP TABLE srv_account_parts; +DROP TABLE etl_batch; From ff1cc5598f4406e22fef41c2f7018363ad356817 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Sun, 2 Apr 2023 22:21:10 +0000 Subject: [PATCH 351/377] fix clang-tidy --- src/Storages/StorageS3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Storages/StorageS3.h b/src/Storages/StorageS3.h index 0bccc4a419f..f4d915e9c55 100644 --- a/src/Storages/StorageS3.h +++ b/src/Storages/StorageS3.h @@ -299,7 +299,7 @@ public: protected: static StorageS3::Configuration copyAndUpdateConfiguration(ContextPtr local_context, const Configuration & configuration); - static void updateConfiguration(ContextPtr local_context, Configuration & configuration); + static void updateConfiguration(ContextPtr ctx, StorageS3::Configuration & upd); private: friend class StorageS3Cluster; From 21d22bcd3b08b50d51ac85cbe0af24938c52d674 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 3 Apr 2023 01:06:26 +0200 Subject: [PATCH 352/377] Fix tests for constraints after merge --- .../01622_constraints_simple_optimization.sql | 6 ++--- .../01622_constraints_where_optimization.sql | 12 ++++----- .../01623_constraints_column_swap.sql | 26 +++++++++---------- .../01625_constraints_index_append.sh | 8 +++--- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/queries/0_stateless/01622_constraints_simple_optimization.sql b/tests/queries/0_stateless/01622_constraints_simple_optimization.sql index 21d75a48587..a4d0035c590 100644 --- a/tests/queries/0_stateless/01622_constraints_simple_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_simple_optimization.sql @@ -100,10 +100,10 @@ SELECT count() FROM constraint_test_constants WHERE 11 <= a; ---> assumption -> EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100); -- EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100); ---> the order of the generated checks is not consistent EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100); -EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100); -EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c > 100) SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100); -EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100); +EXPLAIN QUERY TREE SELECT count() FROM constraint_test_constants WHERE (a > 100 OR b > 100 OR c > 100) AND (a <= 100 OR b > 100 OR c > 100) AND (NOT b > 100 OR c > 100) AND (c <= 100) SETTINGS allow_experimental_analyzer = 1; DROP TABLE constraint_test_constants; diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.sql b/tests/queries/0_stateless/01622_constraints_where_optimization.sql index 562c8705a51..2818351a120 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.sql +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.sql @@ -8,15 +8,15 @@ CREATE TABLE t_constraints_where(a UInt32, b UInt32, CONSTRAINT c1 ASSUME b >= 5 INSERT INTO t_constraints_where VALUES (1, 7); EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 15; -- assumption -> 0 -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 15; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 15 SETTINGS allow_experimental_analyzer = 1; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 20; -- assumption -> 0 -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 20; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 20 SETTINGS allow_experimental_analyzer = 1; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b < 2; -- assumption -> 0 +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b < 2 SETTINGS allow_experimental_analyzer = 1; -- assumption -> 0 EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b > 20 OR b < 8 SETTINGS allow_experimental_analyzer = 1; -- assumption -> remove (b < 20) EXPLAIN SYNTAX SELECT count() FROM t_constraints_where PREWHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where PREWHERE b > 20 OR b < 8; -- assumption -> remove (b < 20) +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where PREWHERE b > 20 OR b < 8 SETTINGS allow_experimental_analyzer = 1; -- assumption -> remove (b < 20) DROP TABLE t_constraints_where; @@ -25,6 +25,6 @@ CREATE TABLE t_constraints_where(a UInt32, b UInt32, CONSTRAINT c1 ASSUME b < 10 INSERT INTO t_constraints_where VALUES (1, 7); EXPLAIN SYNTAX SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumption -> (b < 20) -> 0; -EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5; -- assumption -> (b < 20) -> 0; +EXPLAIN QUERY TREE SELECT count() FROM t_constraints_where WHERE b = 1 OR b < 18 OR b > 5 SETTINGS allow_experimental_analyzer = 1; -- assumption -> (b < 20) -> 0; DROP TABLE t_constraints_where; diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.sql b/tests/queries/0_stateless/01623_constraints_column_swap.sql index 6d70b78194d..3219ee3cda7 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.sql +++ b/tests/queries/0_stateless/01623_constraints_column_swap.sql @@ -13,22 +13,22 @@ INSERT INTO column_swap_test_test VALUES (1, 'cat', 1), (2, 'dog', 2); INSERT INTO column_swap_test_test SELECT number AS i, format('test {} kek {}', toString(number), toString(number + 10)) AS a, 1 AS b FROM system.numbers LIMIT 1000000; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 1 SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test PREWHERE cityHash64(a) = 1; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test PREWHERE cityHash64(a) = 1; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test PREWHERE cityHash64(a) = 1 SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE cityHash64(a) = 0 SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 0 SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, b + 3 FROM column_swap_test_test WHERE b = 1 SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10 FROM column_swap_test_test WHERE cityHash64(a) = 0; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10 FROM column_swap_test_test WHERE cityHash64(a) = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10 FROM column_swap_test_test WHERE cityHash64(a) = 0 SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT cityHash64(a) + 10, a FROM column_swap_test_test WHERE cityHash64(a) = 0; -EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, a FROM column_swap_test_test WHERE cityHash64(a) = 0; +EXPLAIN QUERY TREE SELECT cityHash64(a) + 10, a FROM column_swap_test_test WHERE cityHash64(a) = 0 SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT b + 10, a FROM column_swap_test_test WHERE b = 0; -EXPLAIN QUERY TREE SELECT b + 10, a FROM column_swap_test_test WHERE b = 0; +EXPLAIN QUERY TREE SELECT b + 10, a FROM column_swap_test_test WHERE b = 0 SETTINGS allow_experimental_analyzer = 1; DROP TABLE column_swap_test_test; @@ -36,13 +36,13 @@ CREATE TABLE column_swap_test_test (i Int64, a String, b String, CONSTRAINT c1 A INSERT INTO column_swap_test_test SELECT number AS i, toString(number) AS a, format('test {} kek {}', toString(number), toString(number + 10)) b FROM system.numbers LIMIT 1000000; EXPLAIN SYNTAX SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE a = 'c'; -EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE a = 'c'; +EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE a = 'c' SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; -EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; +EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1), a FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c' SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT substring(reverse(b), 1, 1) AS t1, a AS t2 FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; -EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1) AS t1, a AS t2 FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; +EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1) AS t1, a AS t2 FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c' SETTINGS allow_experimental_analyzer = 1; EXPLAIN SYNTAX SELECT substring(reverse(b), 1, 1) FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; -EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1) FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c'; +EXPLAIN QUERY TREE SELECT substring(reverse(b), 1, 1) FROM column_swap_test_test WHERE substring(reverse(b), 1, 1) = 'c' SETTINGS allow_experimental_analyzer = 1; DROP TABLE column_swap_test_test; @@ -53,6 +53,6 @@ CREATE TABLE t_bad_constraint(a UInt32, s String, CONSTRAINT c1 ASSUME a = toUIn INSERT INTO t_bad_constraint SELECT number, randomPrintableASCII(100) FROM numbers(10000); EXPLAIN SYNTAX SELECT a FROM t_bad_constraint; -EXPLAIN QUERY TREE SELECT a FROM t_bad_constraint; +EXPLAIN QUERY TREE SELECT a FROM t_bad_constraint SETTINGS allow_experimental_analyzer = 1; DROP TABLE t_bad_constraint; diff --git a/tests/queries/0_stateless/01625_constraints_index_append.sh b/tests/queries/0_stateless/01625_constraints_index_append.sh index f17ea422409..6f2dfab7b14 100755 --- a/tests/queries/0_stateless/01625_constraints_index_append.sh +++ b/tests/queries/0_stateless/01625_constraints_index_append.sh @@ -24,12 +24,12 @@ function run_with_settings() } run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a = 0" -run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a = 0" | grep -Fac "indexHint" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a = 0 SETTINGS allow_experimental_analyzer = 1" | grep -Fac "indexHint" run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a < 0" -run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a < 0" | grep -Fac "indexHint" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a < 0 SETTINGS allow_experimental_analyzer = 1" | grep -Fac "indexHint" run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a >= 0" -run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a >= 0" | grep -Fac "indexHint" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a >= 0 SETTINGS allow_experimental_analyzer = 1" | grep -Fac "indexHint" run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE 2 * b < 100" -run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE 2 * b < 100" | grep -Fac "indexHint" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE 2 * b < 100 SETTINGS allow_experimental_analyzer = 1" | grep -Fac "indexHint" $CLICKHOUSE_CLIENT --query "DROP TABLE index_append_test_test;" From ddf4ceda903effac920f96b711f6b4e5afbf08f4 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 3 Apr 2023 01:23:06 +0200 Subject: [PATCH 353/377] Update references --- .../01622_constraints_simple_optimization.reference | 3 +++ .../01622_constraints_where_optimization.reference | 6 ++++++ .../01623_constraints_column_swap.reference | 13 +++++++++++++ 3 files changed, 22 insertions(+) diff --git a/tests/queries/0_stateless/01622_constraints_simple_optimization.reference b/tests/queries/0_stateless/01622_constraints_simple_optimization.reference index 529351180b3..a375c35ca3e 100644 --- a/tests/queries/0_stateless/01622_constraints_simple_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_simple_optimization.reference @@ -52,6 +52,7 @@ QUERY id: 0 LIST id: 5, nodes: 2 COLUMN id: 6, column_name: c, result_type: Int64, source_id: 3 CONSTANT id: 7, constant_value: UInt64_100, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT count() AS `count()` FROM constraint_test_constants WHERE c > 100 @@ -69,6 +70,7 @@ QUERY id: 0 LIST id: 5, nodes: 2 COLUMN id: 6, column_name: c, result_type: Int64, source_id: 3 CONSTANT id: 7, constant_value: UInt64_100, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT count() AS `count()` FROM constraint_test_constants QUERY id: 0 @@ -79,3 +81,4 @@ QUERY id: 0 FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 JOIN TREE TABLE id: 3, table_name: default.constraint_test_constants + SETTINGS allow_experimental_analyzer=1 diff --git a/tests/queries/0_stateless/01622_constraints_where_optimization.reference b/tests/queries/0_stateless/01622_constraints_where_optimization.reference index 9bb42ed1c27..b5520d75b0e 100644 --- a/tests/queries/0_stateless/01622_constraints_where_optimization.reference +++ b/tests/queries/0_stateless/01622_constraints_where_optimization.reference @@ -11,6 +11,7 @@ QUERY id: 0 TABLE id: 3, table_name: default.t_constraints_where WHERE CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT count() FROM t_constraints_where WHERE 0 @@ -24,6 +25,7 @@ QUERY id: 0 TABLE id: 3, table_name: default.t_constraints_where WHERE CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT count() FROM t_constraints_where WHERE 0 @@ -37,6 +39,7 @@ QUERY id: 0 TABLE id: 3, table_name: default.t_constraints_where WHERE CONSTANT id: 4, constant_value: UInt64_0, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT count() FROM t_constraints_where WHERE b < 8 @@ -54,6 +57,7 @@ QUERY id: 0 LIST id: 5, nodes: 2 COLUMN id: 6, column_name: b, result_type: UInt32, source_id: 3 CONSTANT id: 7, constant_value: UInt64_8, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT count() FROM t_constraints_where PREWHERE (b > 20) OR (b < 8) @@ -71,6 +75,7 @@ QUERY id: 0 LIST id: 5, nodes: 2 COLUMN id: 6, column_name: b, result_type: UInt32, source_id: 3 CONSTANT id: 7, constant_value: UInt64_8, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT count() FROM t_constraints_where QUERY id: 0 @@ -81,3 +86,4 @@ QUERY id: 0 FUNCTION id: 2, function_name: count, function_type: aggregate, result_type: UInt64 JOIN TREE TABLE id: 3, table_name: default.t_constraints_where + SETTINGS allow_experimental_analyzer=1 diff --git a/tests/queries/0_stateless/01623_constraints_column_swap.reference b/tests/queries/0_stateless/01623_constraints_column_swap.reference index 124b5d06bed..3639ad47228 100644 --- a/tests/queries/0_stateless/01623_constraints_column_swap.reference +++ b/tests/queries/0_stateless/01623_constraints_column_swap.reference @@ -27,6 +27,7 @@ QUERY id: 0 LIST id: 12, nodes: 2 COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 CONSTANT id: 14, constant_value: UInt64_1, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT cityHash64(a) + 10, b + 3 @@ -56,6 +57,7 @@ QUERY id: 0 LIST id: 12, nodes: 2 COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 CONSTANT id: 14, constant_value: UInt64_1, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` @@ -85,6 +87,7 @@ QUERY id: 0 LIST id: 12, nodes: 2 COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 CONSTANT id: 14, constant_value: UInt64_0, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` @@ -114,6 +117,7 @@ QUERY id: 0 LIST id: 12, nodes: 2 COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 CONSTANT id: 14, constant_value: UInt64_0, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, (b AS b) + 3 AS `plus(b, 3)` @@ -143,6 +147,7 @@ QUERY id: 0 LIST id: 12, nodes: 2 COLUMN id: 13, column_name: b, result_type: UInt64, source_id: 5 CONSTANT id: 14, constant_value: UInt64_1, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT (b AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)` FROM column_swap_test_test WHERE b = 0 @@ -164,6 +169,7 @@ QUERY id: 0 LIST id: 8, nodes: 2 COLUMN id: 9, column_name: b, result_type: UInt64, source_id: 5 CONSTANT id: 10, constant_value: UInt64_0, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT (cityHash64(a) AS `cityHash64(a)`) + 10 AS `plus(cityHash64(a), 10)`, a AS a @@ -195,6 +201,7 @@ QUERY id: 0 LIST id: 13, nodes: 1 COLUMN id: 14, column_name: a, result_type: String, source_id: 7 CONSTANT id: 15, constant_value: UInt64_0, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT (cityHash64(a) AS b) + 10 AS `plus(b, 10)`, a AS a @@ -226,6 +233,7 @@ QUERY id: 0 LIST id: 13, nodes: 1 COLUMN id: 14, column_name: a, result_type: String, source_id: 7 CONSTANT id: 15, constant_value: UInt64_0, constant_value_type: UInt8 + SETTINGS allow_experimental_analyzer=1 SELECT a AS `substring(reverse(b), 1, 1)`, a AS a @@ -247,6 +255,7 @@ QUERY id: 0 LIST id: 6, nodes: 2 COLUMN id: 7, column_name: a, result_type: String, source_id: 3 CONSTANT id: 8, constant_value: \'c\', constant_value_type: String + SETTINGS allow_experimental_analyzer=1 SELECT a AS `substring(reverse(b), 1, 1)`, a AS a @@ -268,6 +277,7 @@ QUERY id: 0 LIST id: 6, nodes: 2 COLUMN id: 7, column_name: a, result_type: String, source_id: 3 CONSTANT id: 8, constant_value: \'c\', constant_value_type: String + SETTINGS allow_experimental_analyzer=1 SELECT a AS t1, a AS t2 @@ -289,6 +299,7 @@ QUERY id: 0 LIST id: 6, nodes: 2 COLUMN id: 7, column_name: a, result_type: String, source_id: 3 CONSTANT id: 8, constant_value: \'c\', constant_value_type: String + SETTINGS allow_experimental_analyzer=1 SELECT a AS `substring(reverse(b), 1, 1)` FROM column_swap_test_test WHERE a = \'c\' @@ -306,6 +317,7 @@ QUERY id: 0 LIST id: 5, nodes: 2 COLUMN id: 6, column_name: a, result_type: String, source_id: 3 CONSTANT id: 7, constant_value: \'c\', constant_value_type: String + SETTINGS allow_experimental_analyzer=1 SELECT a FROM t_bad_constraint QUERY id: 0 @@ -316,3 +328,4 @@ QUERY id: 0 COLUMN id: 2, column_name: a, result_type: UInt32, source_id: 3 JOIN TREE TABLE id: 3, table_name: default.t_bad_constraint + SETTINGS allow_experimental_analyzer=1 From 34f1be92183c6aea3126547bc9421c0a8972ed40 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 3 Apr 2023 01:25:48 +0200 Subject: [PATCH 354/377] Fix test --- .../0_stateless/01625_constraints_index_append.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/queries/0_stateless/01625_constraints_index_append.sh b/tests/queries/0_stateless/01625_constraints_index_append.sh index 6f2dfab7b14..acceedbb1d1 100755 --- a/tests/queries/0_stateless/01625_constraints_index_append.sh +++ b/tests/queries/0_stateless/01625_constraints_index_append.sh @@ -19,17 +19,19 @@ function run_with_settings() , optimize_substitute_columns = 1\ , optimize_append_index = 1" + if [[ $query =~ "EXPLAIN QUERY TREE" ]]; then query="${query}, allow_experimental_analyzer = 1"; fi + $CLICKHOUSE_CLIENT --query="$query" } run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a = 0" -run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a = 0 SETTINGS allow_experimental_analyzer = 1" | grep -Fac "indexHint" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a = 0" | grep -Fac "indexHint" run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a < 0" -run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a < 0 SETTINGS allow_experimental_analyzer = 1" | grep -Fac "indexHint" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a < 0" | grep -Fac "indexHint" run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE a >= 0" -run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a >= 0 SETTINGS allow_experimental_analyzer = 1" | grep -Fac "indexHint" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE a >= 0" | grep -Fac "indexHint" run_with_settings "EXPLAIN SYNTAX SELECT i FROM index_append_test_test WHERE 2 * b < 100" -run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE 2 * b < 100 SETTINGS allow_experimental_analyzer = 1" | grep -Fac "indexHint" +run_with_settings "EXPLAIN QUERY TREE SELECT i FROM index_append_test_test WHERE 2 * b < 100" | grep -Fac "indexHint" $CLICKHOUSE_CLIENT --query "DROP TABLE index_append_test_test;" From d7ee31638027f0fe9256cc5306f2e593281c0271 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Mon, 3 Apr 2023 07:24:08 +0000 Subject: [PATCH 355/377] Minor: Suggest ClickHouse-native function name over MySQL-compatibility alias --- .../functions/date-time-functions.md | 24 +++++++++---------- src/Functions/formatDateTime.cpp | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/en/sql-reference/functions/date-time-functions.md b/docs/en/sql-reference/functions/date-time-functions.md index d06ab253cf7..71b7fa07f18 100644 --- a/docs/en/sql-reference/functions/date-time-functions.md +++ b/docs/en/sql-reference/functions/date-time-functions.md @@ -1463,28 +1463,28 @@ Result: └───────────────────────┘ ``` -## FROM\_UNIXTIME +## fromUnixTimestamp Function converts Unix timestamp to a calendar date and a time of a day. When there is only a single argument of [Integer](../../sql-reference/data-types/int-uint.md) type, it acts in the same way as [toDateTime](../../sql-reference/functions/type-conversion-functions.md#todatetime) and return [DateTime](../../sql-reference/data-types/datetime.md) type. -FROM_UNIXTIME uses MySQL datetime format style, refer to https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format. +fromUnixTimestamp uses MySQL datetime format style, refer to https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format. -Alias: `fromUnixTimestamp`. +Alias: `FROM_UNIXTIME`. **Example:** Query: ```sql -SELECT FROM_UNIXTIME(423543535); +SELECT fromUnixTimestamp(423543535); ``` Result: ```text -┌─FROM_UNIXTIME(423543535)─┐ -│ 1983-06-04 10:58:55 │ -└──────────────────────────┘ +┌─fromUnixTimestamp(423543535)─┐ +│ 1983-06-04 10:58:55 │ +└──────────────────────────────┘ ``` When there are two or three arguments, the first an [Integer](../../sql-reference/data-types/int-uint.md), [Date](../../sql-reference/data-types/date.md), [Date32](../../sql-reference/data-types/date32.md), [DateTime](../../sql-reference/data-types/datetime.md) or [DateTime64](../../sql-reference/data-types/datetime64.md), the second a constant format string and the third an optional constant time zone string — it acts in the same way as [formatDateTime](#formatdatetime) and return [String](../../sql-reference/data-types/string.md#string) type. @@ -1492,7 +1492,7 @@ When there are two or three arguments, the first an [Integer](../../sql-referenc For example: ```sql -SELECT FROM_UNIXTIME(1234334543, '%Y-%m-%d %R:%S') AS DateTime; +SELECT fromUnixTimestamp(1234334543, '%Y-%m-%d %R:%S') AS DateTime; ``` ```text @@ -1505,11 +1505,12 @@ SELECT FROM_UNIXTIME(1234334543, '%Y-%m-%d %R:%S') AS DateTime; - [fromUnixTimestampInJodaSyntax](##fromUnixTimestampInJodaSyntax) - ## fromUnixTimestampInJodaSyntax -Similar to FROM_UNIXTIME, except that it formats time in Joda style instead of MySQL style. Refer to https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html. + +Similar to fromUnixTimestamp, except that it formats time in Joda style instead of MySQL style. Refer to https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html. **Example:** + Query: ``` sql SELECT fromUnixTimestampInJodaSyntax(1669804872, 'yyyy-MM-dd HH:mm:ss', 'UTC'); @@ -1517,12 +1518,11 @@ SELECT fromUnixTimestampInJodaSyntax(1669804872, 'yyyy-MM-dd HH:mm:ss', 'UTC'); Result: ``` -┌─fromUnixTimestampInJodaSyntax(1669804872, 'yyyy-MM-dd HH:mm:ss', 'UTC')─┐ +┌─fromUnixTimestampInJodaSyntax(1669804872, 'yyyy-MM-dd HH:mm:ss', 'UTC')────┐ │ 2022-11-30 10:41:12 │ └────────────────────────────────────────────────────────────────────────────┘ ``` - ## toModifiedJulianDay Converts a [Proleptic Gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar) date in text form `YYYY-MM-DD` to a [Modified Julian Day](https://en.wikipedia.org/wiki/Julian_day#Variants) number in Int32. This function supports date from `0000-01-01` to `9999-12-31`. It raises an exception if the argument cannot be parsed as a date, or the date is invalid. diff --git a/src/Functions/formatDateTime.cpp b/src/Functions/formatDateTime.cpp index bbb4c3ba5b0..dd96a44c17b 100644 --- a/src/Functions/formatDateTime.cpp +++ b/src/Functions/formatDateTime.cpp @@ -1405,7 +1405,7 @@ REGISTER_FUNCTION(FormatDateTime) factory.registerAlias("DATE_FORMAT", FunctionFormatDateTime::name); factory.registerFunction(); - factory.registerAlias("FROM_UNIXTIME", "fromUnixTimestamp"); + factory.registerAlias("FROM_UNIXTIME", FunctionFromUnixTimestamp::name); factory.registerFunction(); factory.registerFunction(); From b4ea2268ca38603d12d4b0ade370924be3fb1930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Mon, 3 Apr 2023 10:54:47 +0200 Subject: [PATCH 356/377] Adapt unit tests to the new exception --- src/IO/tests/gtest_cascade_and_memory_write_buffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IO/tests/gtest_cascade_and_memory_write_buffer.cpp b/src/IO/tests/gtest_cascade_and_memory_write_buffer.cpp index 36b4ec10de0..583ba9c97de 100644 --- a/src/IO/tests/gtest_cascade_and_memory_write_buffer.cpp +++ b/src/IO/tests/gtest_cascade_and_memory_write_buffer.cpp @@ -198,7 +198,7 @@ TEST(MemoryWriteBuffer, WriteAndReread) if (s > 1) { MemoryWriteBuffer buf(s - 1); - EXPECT_THROW(buf.write(data.data(), data.size()), DB::Exception); + EXPECT_THROW(buf.write(data.data(), data.size()), MemoryWriteBuffer::CurrentBufferExhausted); } } From 40a0ecf66a35f23addcf9211642ff301eb4c897a Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 3 Apr 2023 09:30:39 +0000 Subject: [PATCH 357/377] Fix --- src/Storages/StorageKeeperMap.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Storages/StorageKeeperMap.cpp b/src/Storages/StorageKeeperMap.cpp index 58d02372f2a..aeb206f1e05 100644 --- a/src/Storages/StorageKeeperMap.cpp +++ b/src/Storages/StorageKeeperMap.cpp @@ -93,7 +93,7 @@ class StorageKeeperMapSink : public SinkToStorage public: StorageKeeperMapSink(StorageKeeperMap & storage_, Block header, ContextPtr context_) - : SinkToStorage(std::move(header)), storage(storage_), context(std::move(context_)) + : SinkToStorage(header), storage(storage_), context(std::move(context_)) { auto primary_key = storage.getPrimaryKey(); assert(primary_key.size() == 1); @@ -171,7 +171,10 @@ public: zkutil::ZooKeeper::MultiExistsResponse results; if constexpr (!for_update) - results = zookeeper->exists(key_paths); + { + if (!strict) + results = zookeeper->exists(key_paths); + } Coordination::Requests requests; requests.reserve(key_paths.size()); @@ -189,11 +192,8 @@ public: } else { - if (results[i].error == Coordination::Error::ZOK) + if (!strict && results[i].error == Coordination::Error::ZOK) { - if (strict) - throw Exception(ErrorCodes::KEEPER_EXCEPTION, "Value for key '{}' already exists", key); - requests.push_back(zkutil::makeSetRequest(key_paths[i], new_values[key], -1)); } else @@ -937,8 +937,7 @@ void StorageKeeperMap::mutate(const MutationCommands & commands, ContextPtr loca while (executor.pull(block)) sink->consume(Chunk{block.getColumns(), block.rows()}); - sink->finalize(local_context->getSettingsRef().keeper_map_strict_mode); - sink->onFinish(); + sink->finalize(strict); } namespace From 95661c13bcf3fb5576b80d3afe9b760e508ccf41 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 3 Apr 2023 09:55:11 +0000 Subject: [PATCH 358/377] Add tests for strict update/delete --- ..._keeper_map_delete_update_strict.reference | 32 ++++++++++++++ .../02707_keeper_map_delete_update_strict.sql | 44 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/queries/0_stateless/02707_keeper_map_delete_update_strict.reference create mode 100644 tests/queries/0_stateless/02707_keeper_map_delete_update_strict.sql diff --git a/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.reference b/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.reference new file mode 100644 index 00000000000..8ca8c0ca5a2 --- /dev/null +++ b/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.reference @@ -0,0 +1,32 @@ +1 Some string 0 +2 Some other string 0 +3 random 0 +4 random2 0 +----------- +3 random 0 +4 random2 0 +----------- +3 random 0 +----------- +0 +----------- +1 String 10 +2 String 20 +3 String 30 +4 String 40 +----------- +1 String 10 +2 String 20 +3 Another 30 +4 Another 40 +----------- +1 String 10 +2 String 20 +3 Another 30 +4 Another 40 +----------- +1 String 102 +2 String 202 +3 Another 302 +4 Another 402 +----------- diff --git a/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.sql b/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.sql new file mode 100644 index 00000000000..1e14675d353 --- /dev/null +++ b/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.sql @@ -0,0 +1,44 @@ +-- Tags: no-ordinary-database, no-fasttest + +DROP TABLE IF EXISTS 02661_keepermap_delete_update; + +SET keeper_map_strict_mode = 1; + +CREATE TABLE 02661_keepermap_delete_update (key UInt64, value String, value2 UInt64) ENGINE=KeeperMap('/' || currentDatabase() || '/test02661_keepermap_delete_update') PRIMARY KEY(key); + +INSERT INTO 02661_keepermap_delete_update VALUES (1, 'Some string', 0), (2, 'Some other string', 0), (3, 'random', 0), (4, 'random2', 0); + +SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT '-----------'; + +DELETE FROM 02661_keepermap_delete_update WHERE value LIKE 'Some%string'; + +SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT '-----------'; + +ALTER TABLE 02661_keepermap_delete_update DELETE WHERE key >= 4; + +SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT '-----------'; + +DELETE FROM 02661_keepermap_delete_update WHERE 1 = 1; +SELECT count() FROM 02661_keepermap_delete_update; +SELECT '-----------'; + +INSERT INTO 02661_keepermap_delete_update VALUES (1, 'String', 10), (2, 'String', 20), (3, 'String', 30), (4, 'String', 40); +SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT '-----------'; + +ALTER TABLE 02661_keepermap_delete_update UPDATE value = 'Another' WHERE key > 2; +SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT '-----------'; + +ALTER TABLE 02661_keepermap_delete_update UPDATE key = key * 10 WHERE 1 = 1; -- { serverError 36 } +SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT '-----------'; + +ALTER TABLE 02661_keepermap_delete_update UPDATE value2 = value2 * 10 + 2 WHERE value2 < 100; +SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT '-----------'; + +DROP TABLE IF EXISTS 02661_keepermap_delete_update; From e79343c169fb0534526ee11353d497681edec7e5 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Mon, 3 Apr 2023 10:33:10 +0000 Subject: [PATCH 359/377] Make the column order in system.query_cache more intutitive --- .../System/StorageSystemQueryCache.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Storages/System/StorageSystemQueryCache.cpp b/src/Storages/System/StorageSystemQueryCache.cpp index 5e4a1e662e2..612322e4225 100644 --- a/src/Storages/System/StorageSystemQueryCache.cpp +++ b/src/Storages/System/StorageSystemQueryCache.cpp @@ -13,12 +13,12 @@ NamesAndTypesList StorageSystemQueryCache::getNamesAndTypes() { return { {"query", std::make_shared()}, - {"key_hash", std::make_shared()}, - {"expires_at", std::make_shared()}, + {"result_size", std::make_shared()}, {"stale", std::make_shared()}, - {"compressed", std::make_shared()}, {"shared", std::make_shared()}, - {"result_size", std::make_shared()} + {"compressed", std::make_shared()}, + {"expires_at", std::make_shared()}, + {"key_hash", std::make_shared()} }; } @@ -45,12 +45,12 @@ void StorageSystemQueryCache::fillData(MutableColumns & res_columns, ContextPtr continue; res_columns[0]->insert(key.queryStringFromAst()); /// approximates the original query string - res_columns[1]->insert(key.ast->getTreeHash().first); - res_columns[2]->insert(std::chrono::system_clock::to_time_t(key.expires_at)); - res_columns[3]->insert(key.expires_at < std::chrono::system_clock::now()); + res_columns[1]->insert(QueryCache::QueryResultWeight()(*query_result)); + res_columns[2]->insert(key.expires_at < std::chrono::system_clock::now()); + res_columns[3]->insert(!key.username.has_value()); res_columns[4]->insert(key.is_compressed); - res_columns[5]->insert(!key.username.has_value()); - res_columns[6]->insert(QueryCache::QueryResultWeight()(*query_result)); + res_columns[5]->insert(std::chrono::system_clock::to_time_t(key.expires_at)); + res_columns[6]->insert(key.ast->getTreeHash().first); } } From adfedf692a68cf9e93a4d6103c22aea19d7c019f Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 3 Apr 2023 13:13:19 +0200 Subject: [PATCH 360/377] Update table name --- .../02707_keeper_map_delete_update_strict.sql | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.sql b/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.sql index 1e14675d353..aaf4f2fd838 100644 --- a/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.sql +++ b/tests/queries/0_stateless/02707_keeper_map_delete_update_strict.sql @@ -1,44 +1,44 @@ -- Tags: no-ordinary-database, no-fasttest -DROP TABLE IF EXISTS 02661_keepermap_delete_update; +DROP TABLE IF EXISTS 02707_keepermap_delete_update; SET keeper_map_strict_mode = 1; -CREATE TABLE 02661_keepermap_delete_update (key UInt64, value String, value2 UInt64) ENGINE=KeeperMap('/' || currentDatabase() || '/test02661_keepermap_delete_update') PRIMARY KEY(key); +CREATE TABLE 02707_keepermap_delete_update (key UInt64, value String, value2 UInt64) ENGINE=KeeperMap('/' || currentDatabase() || '/test02707_keepermap_delete_update') PRIMARY KEY(key); -INSERT INTO 02661_keepermap_delete_update VALUES (1, 'Some string', 0), (2, 'Some other string', 0), (3, 'random', 0), (4, 'random2', 0); +INSERT INTO 02707_keepermap_delete_update VALUES (1, 'Some string', 0), (2, 'Some other string', 0), (3, 'random', 0), (4, 'random2', 0); -SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT * FROM 02707_keepermap_delete_update ORDER BY key; SELECT '-----------'; -DELETE FROM 02661_keepermap_delete_update WHERE value LIKE 'Some%string'; +DELETE FROM 02707_keepermap_delete_update WHERE value LIKE 'Some%string'; -SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT * FROM 02707_keepermap_delete_update ORDER BY key; SELECT '-----------'; -ALTER TABLE 02661_keepermap_delete_update DELETE WHERE key >= 4; +ALTER TABLE 02707_keepermap_delete_update DELETE WHERE key >= 4; -SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +SELECT * FROM 02707_keepermap_delete_update ORDER BY key; SELECT '-----------'; -DELETE FROM 02661_keepermap_delete_update WHERE 1 = 1; -SELECT count() FROM 02661_keepermap_delete_update; +DELETE FROM 02707_keepermap_delete_update WHERE 1 = 1; +SELECT count() FROM 02707_keepermap_delete_update; SELECT '-----------'; -INSERT INTO 02661_keepermap_delete_update VALUES (1, 'String', 10), (2, 'String', 20), (3, 'String', 30), (4, 'String', 40); -SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +INSERT INTO 02707_keepermap_delete_update VALUES (1, 'String', 10), (2, 'String', 20), (3, 'String', 30), (4, 'String', 40); +SELECT * FROM 02707_keepermap_delete_update ORDER BY key; SELECT '-----------'; -ALTER TABLE 02661_keepermap_delete_update UPDATE value = 'Another' WHERE key > 2; -SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +ALTER TABLE 02707_keepermap_delete_update UPDATE value = 'Another' WHERE key > 2; +SELECT * FROM 02707_keepermap_delete_update ORDER BY key; SELECT '-----------'; -ALTER TABLE 02661_keepermap_delete_update UPDATE key = key * 10 WHERE 1 = 1; -- { serverError 36 } -SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +ALTER TABLE 02707_keepermap_delete_update UPDATE key = key * 10 WHERE 1 = 1; -- { serverError 36 } +SELECT * FROM 02707_keepermap_delete_update ORDER BY key; SELECT '-----------'; -ALTER TABLE 02661_keepermap_delete_update UPDATE value2 = value2 * 10 + 2 WHERE value2 < 100; -SELECT * FROM 02661_keepermap_delete_update ORDER BY key; +ALTER TABLE 02707_keepermap_delete_update UPDATE value2 = value2 * 10 + 2 WHERE value2 < 100; +SELECT * FROM 02707_keepermap_delete_update ORDER BY key; SELECT '-----------'; -DROP TABLE IF EXISTS 02661_keepermap_delete_update; +DROP TABLE IF EXISTS 02707_keepermap_delete_update; From 11556a23adbc462d74d80eb2505c2133f8bc761d Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Mon, 3 Apr 2023 10:47:29 +0000 Subject: [PATCH 361/377] Extend tests for compression of query cache entries --- .../02494_query_cache_compression.reference | 6532 +++++++++++++++++ .../02494_query_cache_compression.sql | 444 ++ ...query_cache_disabled_compression.reference | 2 - ...02494_query_cache_disabled_compression.sql | 12 - ...ery_cache_squash_partial_results.reference | 7 +- ...494_query_cache_squash_partial_results.sql | 23 +- 6 files changed, 6994 insertions(+), 26 deletions(-) create mode 100644 tests/queries/0_stateless/02494_query_cache_compression.reference create mode 100644 tests/queries/0_stateless/02494_query_cache_compression.sql delete mode 100644 tests/queries/0_stateless/02494_query_cache_disabled_compression.reference delete mode 100644 tests/queries/0_stateless/02494_query_cache_disabled_compression.sql diff --git a/tests/queries/0_stateless/02494_query_cache_compression.reference b/tests/queries/0_stateless/02494_query_cache_compression.reference new file mode 100644 index 00000000000..1d206bbc4d2 --- /dev/null +++ b/tests/queries/0_stateless/02494_query_cache_compression.reference @@ -0,0 +1,6532 @@ +-- insert with enabled compression +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +-- read from cache +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +-- insert with disabled compression +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +-- read from cache +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +abc +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +def +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl +jkl diff --git a/tests/queries/0_stateless/02494_query_cache_compression.sql b/tests/queries/0_stateless/02494_query_cache_compression.sql new file mode 100644 index 00000000000..0f527dfde5e --- /dev/null +++ b/tests/queries/0_stateless/02494_query_cache_compression.sql @@ -0,0 +1,444 @@ +-- Tags: no-parallel +-- Tag no-parallel: Messes with internal cache + +SET allow_experimental_query_cache = true; + +SYSTEM DROP QUERY CACHE; + +DROP TABLE IF EXISTS t; + +-- Create test table with lot's of rows +CREATE TABLE t(c String) ENGINE=MergeTree ORDER BY c; +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +OPTIMIZE TABLE t FINAL; + +-- Run query which, store *compressed* result in query cache +SELECT '-- insert with enabled compression'; +SELECT * FROM t ORDER BY c +SETTINGS use_query_cache = true, query_cache_compress_entries = true; + +-- Run again to check that no bad things happen and that the result is as expected +SELECT '-- read from cache'; +SELECT * FROM t ORDER BY c +SETTINGS use_query_cache = true; + +SYSTEM DROP QUERY CACHE; + +-- Run query which, store *uncompressed* result in query cache +SELECT '-- insert with disabled compression'; +SELECT * FROM t ORDER BY c +SETTINGS use_query_cache = true, query_cache_compress_entries = false; + +-- Run again to check that no bad things happen and that the result is as expected +SELECT '-- read from cache'; +SELECT * FROM t ORDER BY c +SETTINGS use_query_cache = true; + +DROP TABLE t; diff --git a/tests/queries/0_stateless/02494_query_cache_disabled_compression.reference b/tests/queries/0_stateless/02494_query_cache_disabled_compression.reference deleted file mode 100644 index 6ed281c757a..00000000000 --- a/tests/queries/0_stateless/02494_query_cache_disabled_compression.reference +++ /dev/null @@ -1,2 +0,0 @@ -1 -1 diff --git a/tests/queries/0_stateless/02494_query_cache_disabled_compression.sql b/tests/queries/0_stateless/02494_query_cache_disabled_compression.sql deleted file mode 100644 index ca95ffd918d..00000000000 --- a/tests/queries/0_stateless/02494_query_cache_disabled_compression.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Tags: no-parallel --- Tag no-parallel: Messes with internal cache - -SET allow_experimental_query_cache = true; - -SYSTEM DROP QUERY CACHE; - --- Run query and store result in query cache but without compression which is on by default -SELECT 1 SETTINGS use_query_cache = true, query_cache_compress_entries = false; - --- Run again to check that no bad things happen and that the result is as expected -SELECT 1 SETTINGS use_query_cache = true; diff --git a/tests/queries/0_stateless/02494_query_cache_squash_partial_results.reference b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.reference index e3ffe57ae3e..2c4fa587dfc 100644 --- a/tests/queries/0_stateless/02494_query_cache_squash_partial_results.reference +++ b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.reference @@ -1,3 +1,4 @@ +-- insert with enabled squashing abc abc abc @@ -66,7 +67,7 @@ jkl jkl jkl jkl -- +-- read from cache abc abc abc @@ -135,7 +136,7 @@ jkl jkl jkl jkl --------------------- +-- insert with disabled squashing abc abc abc @@ -204,7 +205,7 @@ jkl jkl jkl jkl -- +-- read from cache abc abc abc diff --git a/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql index eee633b747e..d57773b51f8 100644 --- a/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql +++ b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql @@ -9,6 +9,7 @@ DROP TABLE IF EXISTS t; -- Create test table with "many" rows CREATE TABLE t(c String) ENGINE=MergeTree ORDER BY c; +SYSTEM STOP MERGES t; -- retain multiple parts to make the SELECT process multiple chunks INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); @@ -28,21 +29,25 @@ INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); INSERT INTO t values ('abc') ('def') ('ghi') ('jkl'); -- Run query which reads multiple chunks (small max_block_size), cache result in query cache, force squashing of partial results -SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true, query_cache_squash_partial_results = true; - -SELECT '-'; +SELECT '-- insert with enabled squashing'; +SELECT * FROM t ORDER BY c +SETTINGS max_block_size = 2, use_query_cache = true, query_cache_squash_partial_results = true; -- Run again to check that no bad things happen and that the result is as expected -SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true; +SELECT '-- read from cache'; +SELECT * FROM t ORDER BY c +SETTINGS max_block_size = 2, use_query_cache = true; -SELECT '--------------------'; +SYSTEM DROP QUERY CACHE; -- Run query which reads multiple chunks (small max_block_size), cache result in query cache, but **disable** squashing of partial results -SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true, query_cache_squash_partial_results = false; - -SELECT '-'; +SELECT '-- insert with disabled squashing'; +SELECT * FROM t ORDER BY c +SETTINGS max_block_size = 2, use_query_cache = true, query_cache_squash_partial_results = false; -- Run again to check that no bad things happen and that the result is as expected -SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true; +SELECT '-- read from cache'; +SELECT * FROM t ORDER BY c +SETTINGS max_block_size = 2, use_query_cache = true; DROP TABLE t; From 781907bda87a314828c26b6c61ea8cf1da891b21 Mon Sep 17 00:00:00 2001 From: robot-clickhouse Date: Mon, 3 Apr 2023 12:10:45 +0000 Subject: [PATCH 362/377] Update version_date.tsv and changelogs after v23.1.6.42-stable --- docs/changelogs/v23.1.6.42-stable.md | 34 ++++++++++++++++++++++++++++ utils/list-versions/version_date.tsv | 2 ++ 2 files changed, 36 insertions(+) create mode 100644 docs/changelogs/v23.1.6.42-stable.md diff --git a/docs/changelogs/v23.1.6.42-stable.md b/docs/changelogs/v23.1.6.42-stable.md new file mode 100644 index 00000000000..21fb9220443 --- /dev/null +++ b/docs/changelogs/v23.1.6.42-stable.md @@ -0,0 +1,34 @@ +--- +sidebar_position: 1 +sidebar_label: 2023 +--- + +# 2023 Changelog + +### ClickHouse release v23.1.6.42-stable (783ddf67991) FIXME as compared to v23.1.5.24-stable (0e51b53ba99) + +#### Build/Testing/Packaging Improvement +* Backported in [#48215](https://github.com/ClickHouse/ClickHouse/issues/48215): Use sccache as a replacement for ccache and using S3 as cache backend. [#46240](https://github.com/ClickHouse/ClickHouse/pull/46240) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Backported in [#48254](https://github.com/ClickHouse/ClickHouse/issues/48254): The `clickhouse/clickhouse-keeper` image used to be pushed only with tags `-alpine`, e.g. `latest-alpine`. As it was suggested in https://github.com/ClickHouse/examples/pull/2, now it will be pushed as suffixless too. [#48236](https://github.com/ClickHouse/ClickHouse/pull/48236) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). + +#### Bug Fix (user-visible misbehavior in an official stable release) + +* Fix changing an expired role [#46772](https://github.com/ClickHouse/ClickHouse/pull/46772) ([Vitaly Baranov](https://github.com/vitlibar)). +* Fix bug in zero-copy replication disk choice during fetch [#47010](https://github.com/ClickHouse/ClickHouse/pull/47010) ([alesapin](https://github.com/alesapin)). +* Fix NOT_IMPLEMENTED error with CROSS JOIN and algorithm = auto [#47068](https://github.com/ClickHouse/ClickHouse/pull/47068) ([Vladimir C](https://github.com/vdimir)). +* Disable logical expression optimizer for expression with aliases. [#47451](https://github.com/ClickHouse/ClickHouse/pull/47451) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Fix query parameters [#47488](https://github.com/ClickHouse/ClickHouse/pull/47488) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Parameterized view bug fix 47287 47247 [#47495](https://github.com/ClickHouse/ClickHouse/pull/47495) ([SmitaRKulkarni](https://github.com/SmitaRKulkarni)). +* Fix wait for zero copy lock during move [#47631](https://github.com/ClickHouse/ClickHouse/pull/47631) ([alesapin](https://github.com/alesapin)). +* Hotfix for too verbose warnings in HTTP [#47903](https://github.com/ClickHouse/ClickHouse/pull/47903) ([Alexander Tokmakov](https://github.com/tavplubix)). + +#### NOT FOR CHANGELOG / INSIGNIFICANT + +* Better error messages in ReplicatedMergeTreeAttachThread [#47454](https://github.com/ClickHouse/ClickHouse/pull/47454) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix `00933_test_fix_extra_seek_on_compressed_cache` in releases. [#47490](https://github.com/ClickHouse/ClickHouse/pull/47490) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Add a fuse for backport branches w/o a created PR [#47760](https://github.com/ClickHouse/ClickHouse/pull/47760) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Only valid Reviews.STATES overwrite existing reviews [#47789](https://github.com/ClickHouse/ClickHouse/pull/47789) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Place short return before big block, improve logging [#47822](https://github.com/ClickHouse/ClickHouse/pull/47822) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Artifacts s3 prefix [#47945](https://github.com/ClickHouse/ClickHouse/pull/47945) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix tsan error lock-order-inversion [#47953](https://github.com/ClickHouse/ClickHouse/pull/47953) ([Kruglov Pavel](https://github.com/Avogar)). + diff --git a/utils/list-versions/version_date.tsv b/utils/list-versions/version_date.tsv index 0cd3416f44c..24af79eef2f 100644 --- a/utils/list-versions/version_date.tsv +++ b/utils/list-versions/version_date.tsv @@ -1,8 +1,10 @@ v23.3.1.2823-lts 2023-03-31 +v23.2.5.46-stable 2023-04-03 v23.2.4.12-stable 2023-03-10 v23.2.3.17-stable 2023-03-06 v23.2.2.20-stable 2023-03-01 v23.2.1.2537-stable 2023-02-23 +v23.1.6.42-stable 2023-04-03 v23.1.5.24-stable 2023-03-10 v23.1.4.58-stable 2023-03-01 v23.1.3.5-stable 2023-02-03 From 00e335530e690f996a26f566ecee032fe9489bbc Mon Sep 17 00:00:00 2001 From: Yakov Olkhovskiy <99031427+yakov-olkhovskiy@users.noreply.github.com> Date: Mon, 3 Apr 2023 08:15:18 -0400 Subject: [PATCH 363/377] close client --- tests/clickhouse-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/clickhouse-test b/tests/clickhouse-test index 9067a8142bc..d0dfdd783f1 100755 --- a/tests/clickhouse-test +++ b/tests/clickhouse-test @@ -144,7 +144,7 @@ def clickhouse_execute_http( except Exception as ex: if i == max_http_retries - 1: raise ex - + client.close() sleep(i + 1) if res.status != 200: From 0af16e4b7a5a253e5f4f42a86fd318908283d73c Mon Sep 17 00:00:00 2001 From: robot-clickhouse Date: Mon, 3 Apr 2023 12:16:27 +0000 Subject: [PATCH 364/377] Update version_date.tsv and changelogs after v23.2.5.46-stable --- docs/changelogs/v23.2.5.46-stable.md | 40 ++++++++++++++++++++++++++++ utils/list-versions/version_date.tsv | 2 ++ 2 files changed, 42 insertions(+) create mode 100644 docs/changelogs/v23.2.5.46-stable.md diff --git a/docs/changelogs/v23.2.5.46-stable.md b/docs/changelogs/v23.2.5.46-stable.md new file mode 100644 index 00000000000..b3ce585848b --- /dev/null +++ b/docs/changelogs/v23.2.5.46-stable.md @@ -0,0 +1,40 @@ +--- +sidebar_position: 1 +sidebar_label: 2023 +--- + +# 2023 Changelog + +### ClickHouse release v23.2.5.46-stable (b50faecbb12) FIXME as compared to v23.2.4.12-stable (8fe866cb035) + +#### Improvement +* Backported in [#48164](https://github.com/ClickHouse/ClickHouse/issues/48164): Fixed `UNKNOWN_TABLE` exception when attaching to a materialized view that has dependent tables that are not available. This might be useful when trying to restore state from a backup. [#47975](https://github.com/ClickHouse/ClickHouse/pull/47975) ([MikhailBurdukov](https://github.com/MikhailBurdukov)). + +#### Build/Testing/Packaging Improvement +* Backported in [#48216](https://github.com/ClickHouse/ClickHouse/issues/48216): Use sccache as a replacement for ccache and using S3 as cache backend. [#46240](https://github.com/ClickHouse/ClickHouse/pull/46240) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Backported in [#48256](https://github.com/ClickHouse/ClickHouse/issues/48256): The `clickhouse/clickhouse-keeper` image used to be pushed only with tags `-alpine`, e.g. `latest-alpine`. As it was suggested in https://github.com/ClickHouse/examples/pull/2, now it will be pushed as suffixless too. [#48236](https://github.com/ClickHouse/ClickHouse/pull/48236) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). + +#### Bug Fix (user-visible misbehavior in an official stable release) + +* Fix changing an expired role [#46772](https://github.com/ClickHouse/ClickHouse/pull/46772) ([Vitaly Baranov](https://github.com/vitlibar)). +* Fix bug in zero-copy replication disk choice during fetch [#47010](https://github.com/ClickHouse/ClickHouse/pull/47010) ([alesapin](https://github.com/alesapin)). +* Fix NOT_IMPLEMENTED error with CROSS JOIN and algorithm = auto [#47068](https://github.com/ClickHouse/ClickHouse/pull/47068) ([Vladimir C](https://github.com/vdimir)). +* Disable logical expression optimizer for expression with aliases. [#47451](https://github.com/ClickHouse/ClickHouse/pull/47451) ([Nikolai Kochetov](https://github.com/KochetovNicolai)). +* Fix query parameters [#47488](https://github.com/ClickHouse/ClickHouse/pull/47488) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Parameterized view bug fix 47287 47247 [#47495](https://github.com/ClickHouse/ClickHouse/pull/47495) ([SmitaRKulkarni](https://github.com/SmitaRKulkarni)). +* Proper fix for bug in parquet, revert reverted [#45878](https://github.com/ClickHouse/ClickHouse/issues/45878) [#47538](https://github.com/ClickHouse/ClickHouse/pull/47538) ([Kruglov Pavel](https://github.com/Avogar)). +* Fix wait for zero copy lock during move [#47631](https://github.com/ClickHouse/ClickHouse/pull/47631) ([alesapin](https://github.com/alesapin)). +* Hotfix for too verbose warnings in HTTP [#47903](https://github.com/ClickHouse/ClickHouse/pull/47903) ([Alexander Tokmakov](https://github.com/tavplubix)). + +#### NOT FOR CHANGELOG / INSIGNIFICANT + +* fix: keeper systemd service file include invalid inline comment [#47105](https://github.com/ClickHouse/ClickHouse/pull/47105) ([SuperDJY](https://github.com/cmsxbc)). +* Better error messages in ReplicatedMergeTreeAttachThread [#47454](https://github.com/ClickHouse/ClickHouse/pull/47454) ([Alexander Tokmakov](https://github.com/tavplubix)). +* Fix `00933_test_fix_extra_seek_on_compressed_cache` in releases. [#47490](https://github.com/ClickHouse/ClickHouse/pull/47490) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Fix startup on older systemd versions [#47689](https://github.com/ClickHouse/ClickHouse/pull/47689) ([Thomas Casteleyn](https://github.com/Hipska)). +* Add a fuse for backport branches w/o a created PR [#47760](https://github.com/ClickHouse/ClickHouse/pull/47760) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Only valid Reviews.STATES overwrite existing reviews [#47789](https://github.com/ClickHouse/ClickHouse/pull/47789) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Place short return before big block, improve logging [#47822](https://github.com/ClickHouse/ClickHouse/pull/47822) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Artifacts s3 prefix [#47945](https://github.com/ClickHouse/ClickHouse/pull/47945) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix tsan error lock-order-inversion [#47953](https://github.com/ClickHouse/ClickHouse/pull/47953) ([Kruglov Pavel](https://github.com/Avogar)). + diff --git a/utils/list-versions/version_date.tsv b/utils/list-versions/version_date.tsv index 0cd3416f44c..24af79eef2f 100644 --- a/utils/list-versions/version_date.tsv +++ b/utils/list-versions/version_date.tsv @@ -1,8 +1,10 @@ v23.3.1.2823-lts 2023-03-31 +v23.2.5.46-stable 2023-04-03 v23.2.4.12-stable 2023-03-10 v23.2.3.17-stable 2023-03-06 v23.2.2.20-stable 2023-03-01 v23.2.1.2537-stable 2023-02-23 +v23.1.6.42-stable 2023-04-03 v23.1.5.24-stable 2023-03-10 v23.1.4.58-stable 2023-03-01 v23.1.3.5-stable 2023-02-03 From fb7c8ca1575610a85523db927cb8acc05aaa41ae Mon Sep 17 00:00:00 2001 From: Denny Crane Date: Mon, 3 Apr 2023 10:47:44 -0300 Subject: [PATCH 365/377] Update tuple-functions.md --- docs/en/sql-reference/functions/tuple-functions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/sql-reference/functions/tuple-functions.md b/docs/en/sql-reference/functions/tuple-functions.md index c248499be69..c4742d0bac7 100644 --- a/docs/en/sql-reference/functions/tuple-functions.md +++ b/docs/en/sql-reference/functions/tuple-functions.md @@ -208,7 +208,7 @@ Type: [Array](../../sql-reference/data-types/array.md)([Tuple](../../sql-referen Query: ``` sql -CREATE TABLE tupletest (`col` Tuple(user_ID UInt64, session_ID UInt64) ENGINE = Memory; +CREATE TABLE tupletest (col Tuple(user_ID UInt64, session_ID UInt64)) ENGINE = Memory; INSERT INTO tupletest VALUES (tuple( 100, 2502)), (tuple(1,100)); @@ -227,11 +227,11 @@ Result: It is possible to transform colums to rows using this function: ``` sql -CREATE TABLE tupletest (`col` Tuple(CPU Float64, Memory Float64, Disk Float64)) ENGINE = Memory; +CREATE TABLE tupletest (col Tuple(CPU Float64, Memory Float64, Disk Float64)) ENGINE = Memory; INSERT INTO tupletest VALUES(tuple(3.3, 5.5, 6.6)); -SELECT arrayJoin(tupleToNameValuePairs(col))FROM tupletest; +SELECT arrayJoin(tupleToNameValuePairs(col)) FROM tupletest; ``` Result: From e7d00c8f340497dde2a84325d3f1baf8616f74c0 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 3 Apr 2023 13:56:03 +0000 Subject: [PATCH 366/377] Don't replicate mutations for KeeperMap tables --- src/Databases/DatabaseReplicated.cpp | 8 ++++++++ src/Interpreters/InterpreterAlterQuery.cpp | 12 +++++++++--- .../0_stateless/02577_keepermap_delete_update.sql | 4 +++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Databases/DatabaseReplicated.cpp b/src/Databases/DatabaseReplicated.cpp index 76eea059174..efac04d9e15 100644 --- a/src/Databases/DatabaseReplicated.cpp +++ b/src/Databases/DatabaseReplicated.cpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace DB { @@ -1390,6 +1391,13 @@ bool DatabaseReplicated::shouldReplicateQuery(const ContextPtr & query_context, /// Some ALTERs are not replicated on database level if (const auto * alter = query_ptr->as()) { + auto table_id = query_context->resolveStorageID(*alter, Context::ResolveOrdinary); + StoragePtr table = DatabaseCatalog::instance().getTable(table_id, query_context); + + /// we never replicate KeeperMap operations because it doesn't make sense + if (auto * keeper_map = table->as()) + return false; + return !alter->isAttachAlter() && !alter->isFetchAlter() && !alter->isDropPartitionAlter(); } diff --git a/src/Interpreters/InterpreterAlterQuery.cpp b/src/Interpreters/InterpreterAlterQuery.cpp index fabcc6844e5..3dfa29fbb01 100644 --- a/src/Interpreters/InterpreterAlterQuery.cpp +++ b/src/Interpreters/InterpreterAlterQuery.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,7 @@ namespace ErrorCodes extern const int INCORRECT_QUERY; extern const int NOT_IMPLEMENTED; extern const int TABLE_IS_READ_ONLY; + extern const int BAD_ARGUMENTS; } @@ -72,16 +74,21 @@ BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter) if (!UserDefinedSQLFunctionFactory::instance().empty()) UserDefinedSQLFunctionVisitor::visit(query_ptr); + auto table_id = getContext()->resolveStorageID(alter, Context::ResolveOrdinary); + query_ptr->as().setDatabase(table_id.database_name); + StoragePtr table = DatabaseCatalog::instance().getTable(table_id, getContext()); + if (!alter.cluster.empty() && !maybeRemoveOnCluster(query_ptr, getContext())) { + if (table->as()) + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Mutations with ON CLUSTER are not allowed for KeeperMap tables"); + DDLQueryOnClusterParams params; params.access_to_check = getRequiredAccess(); return executeDDLQueryOnCluster(query_ptr, getContext(), params); } getContext()->checkAccess(getRequiredAccess()); - auto table_id = getContext()->resolveStorageID(alter, Context::ResolveOrdinary); - query_ptr->as().setDatabase(table_id.database_name); DatabasePtr database = DatabaseCatalog::instance().getDatabase(table_id.database_name); if (database->shouldReplicateQuery(getContext(), query_ptr)) @@ -91,7 +98,6 @@ BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter) return database->tryEnqueueReplicatedDDL(query_ptr, getContext()); } - StoragePtr table = DatabaseCatalog::instance().getTable(table_id, getContext()); checkStorageSupportsTransactionsIfNeeded(table, getContext()); if (table->isStaticStorage()) throw Exception(ErrorCodes::TABLE_IS_READ_ONLY, "Table is read-only"); diff --git a/tests/queries/0_stateless/02577_keepermap_delete_update.sql b/tests/queries/0_stateless/02577_keepermap_delete_update.sql index 199a653822c..942dd28cd46 100644 --- a/tests/queries/0_stateless/02577_keepermap_delete_update.sql +++ b/tests/queries/0_stateless/02577_keepermap_delete_update.sql @@ -31,7 +31,7 @@ ALTER TABLE 02661_keepermap_delete_update UPDATE value = 'Another' WHERE key > 2 SELECT * FROM 02661_keepermap_delete_update ORDER BY key; SELECT '-----------'; -ALTER TABLE 02661_keepermap_delete_update UPDATE key = key * 10 WHERE 1 = 1; -- { serverError 36 } +ALTER TABLE 02661_keepermap_delete_update UPDATE key = key * 10 WHERE 1 = 1; -- { serverError BAD_ARGUMENTS } SELECT * FROM 02661_keepermap_delete_update ORDER BY key; SELECT '-----------'; @@ -39,4 +39,6 @@ ALTER TABLE 02661_keepermap_delete_update UPDATE value2 = value2 * 10 + 2 WHERE SELECT * FROM 02661_keepermap_delete_update ORDER BY key; SELECT '-----------'; +ALTER TABLE 02661_keepermap_delete_update ON CLUSTER test_shard_localhost UPDATE value2 = value2 * 10 + 2 WHERE value2 < 100; -- { serverError BAD_ARGUMENTS } + DROP TABLE IF EXISTS 02661_keepermap_delete_update; From 12bee0573fe3dcde656942181a0d234994930c48 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 3 Apr 2023 14:54:14 +0000 Subject: [PATCH 367/377] Correctly check table --- src/Interpreters/InterpreterAlterQuery.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Interpreters/InterpreterAlterQuery.cpp b/src/Interpreters/InterpreterAlterQuery.cpp index 3dfa29fbb01..c683296a2ba 100644 --- a/src/Interpreters/InterpreterAlterQuery.cpp +++ b/src/Interpreters/InterpreterAlterQuery.cpp @@ -76,11 +76,11 @@ BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter) auto table_id = getContext()->resolveStorageID(alter, Context::ResolveOrdinary); query_ptr->as().setDatabase(table_id.database_name); - StoragePtr table = DatabaseCatalog::instance().getTable(table_id, getContext()); + StoragePtr table = DatabaseCatalog::instance().tryGetTable(table_id, getContext()); if (!alter.cluster.empty() && !maybeRemoveOnCluster(query_ptr, getContext())) { - if (table->as()) + if (table && table->as()) throw Exception(ErrorCodes::BAD_ARGUMENTS, "Mutations with ON CLUSTER are not allowed for KeeperMap tables"); DDLQueryOnClusterParams params; @@ -98,6 +98,9 @@ BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter) return database->tryEnqueueReplicatedDDL(query_ptr, getContext()); } + if (!table) + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Could not find table: {}", table_id.table_name); + checkStorageSupportsTransactionsIfNeeded(table, getContext()); if (table->isStaticStorage()) throw Exception(ErrorCodes::TABLE_IS_READ_ONLY, "Table is read-only"); From e81c2999a3ffe0733be88ced07456fca67119c4b Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 3 Apr 2023 16:56:45 +0200 Subject: [PATCH 368/377] Update src/Interpreters/InterpreterAlterQuery.cpp Co-authored-by: Alexander Tokmakov --- src/Interpreters/InterpreterAlterQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterAlterQuery.cpp b/src/Interpreters/InterpreterAlterQuery.cpp index c683296a2ba..49bc18534a8 100644 --- a/src/Interpreters/InterpreterAlterQuery.cpp +++ b/src/Interpreters/InterpreterAlterQuery.cpp @@ -99,7 +99,7 @@ BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter) } if (!table) - throw Exception(ErrorCodes::BAD_ARGUMENTS, "Could not find table: {}", table_id.table_name); + throw Exception(ErrorCodes::UNKNOWN_TABLE, "Could not find table: {}", table_id.table_name); checkStorageSupportsTransactionsIfNeeded(table, getContext()); if (table->isStaticStorage()) From f21c664744897be6da191b9e62d4768abc098194 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 3 Apr 2023 15:17:06 +0000 Subject: [PATCH 369/377] Add error code --- src/Interpreters/InterpreterAlterQuery.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interpreters/InterpreterAlterQuery.cpp b/src/Interpreters/InterpreterAlterQuery.cpp index 49bc18534a8..21f0fbadd09 100644 --- a/src/Interpreters/InterpreterAlterQuery.cpp +++ b/src/Interpreters/InterpreterAlterQuery.cpp @@ -41,6 +41,7 @@ namespace ErrorCodes extern const int NOT_IMPLEMENTED; extern const int TABLE_IS_READ_ONLY; extern const int BAD_ARGUMENTS; + extern const int UNKNOWN_TABLE; } From fea5fae5b00294fa0b0729cc7ee109a572510180 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Mon, 3 Apr 2023 17:42:57 +0000 Subject: [PATCH 370/377] Fix 02494_query_cache_drop.sql Failing with high rate in master after #45912 was merged --- tests/queries/0_stateless/02494_query_cache_drop_cache.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/queries/0_stateless/02494_query_cache_drop_cache.sql b/tests/queries/0_stateless/02494_query_cache_drop_cache.sql index 1f61472fcb0..078057a834f 100644 --- a/tests/queries/0_stateless/02494_query_cache_drop_cache.sql +++ b/tests/queries/0_stateless/02494_query_cache_drop_cache.sql @@ -3,6 +3,9 @@ SET allow_experimental_query_cache = true; +-- (it's silly to use what will be tested below but we have to assume other tests cluttered the query cache) +SYSTEM DROP QUERY CACHE; + -- Cache query result in query cache SELECT 1 SETTINGS use_query_cache = true; SELECT count(*) FROM system.query_cache; From 1cb8a7c45cd5894facc0abeb17e7e89c9a4b5821 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 4 Apr 2023 00:46:25 +0300 Subject: [PATCH 371/377] Update tips.md See Telegram. --- docs/en/operations/tips.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/operations/tips.md b/docs/en/operations/tips.md index 693dcd0d21b..8f6cf6ad147 100644 --- a/docs/en/operations/tips.md +++ b/docs/en/operations/tips.md @@ -74,7 +74,7 @@ Never set the block size too small or too large. You can use RAID-0 on SSD. Regardless of RAID use, always use replication for data security. -Enable NCQ with a long queue. For HDD, choose the CFQ scheduler, and for SSD, choose noop. Don’t reduce the ‘readahead’ setting. +Enable NCQ with a long queue. For HDD, choose the mq-deadline or CFQ scheduler, and for SSD, choose noop. Don’t reduce the ‘readahead’ setting. For HDD, enable the write cache. Make sure that [`fstrim`](https://en.wikipedia.org/wiki/Trim_(computing)) is enabled for NVME and SSD disks in your OS (usually it's implemented using a cronjob or systemd service). From 558b1eb37226b3307d866abc47f7b1abfc665cff Mon Sep 17 00:00:00 2001 From: rfraposa Date: Mon, 3 Apr 2023 19:35:23 -0600 Subject: [PATCH 372/377] Update clickhouse-local.md --- .../operations/utilities/clickhouse-local.md | 146 +++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/docs/en/operations/utilities/clickhouse-local.md b/docs/en/operations/utilities/clickhouse-local.md index 6bf1269c1d9..a23e0745dec 100644 --- a/docs/en/operations/utilities/clickhouse-local.md +++ b/docs/en/operations/utilities/clickhouse-local.md @@ -8,10 +8,150 @@ sidebar_label: clickhouse-local The `clickhouse-local` program enables you to perform fast processing on local files, without having to deploy and configure the ClickHouse server. It accepts data that represent tables and queries them using [ClickHouse SQL dialect](../../sql-reference/index.md). `clickhouse-local` uses the same core as ClickHouse server, so it supports most of the features and the same set of formats and table engines. -By default `clickhouse-local` has access to data on the same host, and it does not depend on the server's configuration. It also supports loading server configuration using `--config-file` argument. For temporary data, a unique temporary data directory is created by default. +## Download clickhouse-local + +`clickhouse-local` is executed using the same `clickhouse` binary that runs the ClickHouse server and `clickhouse-client`. The easiest way to download the latest version is with the following command: + +```bash +curl https://clickhouse.com/ | sh +``` + +:::note +The binary you just downloaded can run all sorts of ClickHouse tools and utilities. If you want to run ClickHouse as a database server, check out the [Quick Start](../../quick-start.mdx). +::: + +## Query data in a CSV file using SQL + +A common use of `clickhouse-local` is to run ad-hoc queries on files: where you don't have to insert the data into a table. `clickhouse-local` can stream the data from a file into a temporary table and execute your SQL. + +If the file is sitting on the same machine as `clickhouse-local`, use the `file` table engine. The following `reviews.tsv` file contains a sampling of Amazon product reviews: + +```bash +./clickhouse local -q "SELECT * FROM file('reviews.tsv')" +``` + +ClickHouse knows the file uses a tab-separated format from filename extension. If you need to explicitly specify the format, simply add one of the [many ClickHouse input formats](../../interfaces/formats.md): + ```bash + ./clickhouse local -q "SELECT * FROM file('reviews.tsv', 'TabSeparated')" + ``` + +The `file` table function creates a table, and you can use `DESCRIBE` to see the inferred schema: + +```bash +./clickhouse local -q "DESCRIBE file('reviews.tsv')" +``` + +```response +marketplace Nullable(String) +customer_id Nullable(Int64) +review_id Nullable(String) +product_id Nullable(String) +product_parent Nullable(Int64) +product_title Nullable(String) +product_category Nullable(String) +star_rating Nullable(Int64) +helpful_votes Nullable(Int64) +total_votes Nullable(Int64) +vine Nullable(String) +verified_purchase Nullable(String) +review_headline Nullable(String) +review_body Nullable(String) +review_date Nullable(Date) +``` + +Let's find a product with the highest rating: + +```bash +./clickhouse local -q "SELECT + argMax(product_title,star_rating), + max(star_rating) +FROM file('reviews.tsv')" +``` + +```response +Monopoly Junior Board Game 5 +``` + +## Query data in a Parquet file in AWS S3 + +If you have a file in S3, use `clickhouse-local` and the `s3` table function to query the file in place (without inserting the data into a ClickHouse table). We have a file named `house_0.parquet` in a public bucket that contains home prices of property sold in the United Kingdom. Let's see how many rows it has: + +```bash +./clickhouse local -q " +SELECT count() +FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/house_parquet/house_0.parquet')" +``` + +The file has 2.7M rows: + +```response +2772030 +``` + +It's always useful to see what the inferred schema that ClickHouse determines from the file: + +```bash +./clickhouse local -q "DESCRIBE s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/house_parquet/house_0.parquet')" +``` + +```response +price Nullable(Int64) +date Nullable(UInt16) +postcode1 Nullable(String) +postcode2 Nullable(String) +type Nullable(String) +is_new Nullable(UInt8) +duration Nullable(String) +addr1 Nullable(String) +addr2 Nullable(String) +street Nullable(String) +locality Nullable(String) +town Nullable(String) +district Nullable(String) +county Nullable(String) +``` + +Let's see what the most expensive neighborhoods are: + +```bash +./clickhouse local -q " +SELECT + town, + district, + count() AS c, + round(avg(price)) AS price, + bar(price, 0, 5000000, 100) +FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/house_parquet/house_0.parquet') +GROUP BY + town, + district +HAVING c >= 100 +ORDER BY price DESC +LIMIT 10" +``` + +```response +LONDON CITY OF LONDON 886 2271305 █████████████████████████████████████████████▍ +LEATHERHEAD ELMBRIDGE 206 1176680 ███████████████████████▌ +LONDON CITY OF WESTMINSTER 12577 1108221 ██████████████████████▏ +LONDON KENSINGTON AND CHELSEA 8728 1094496 █████████████████████▉ +HYTHE FOLKESTONE AND HYTHE 130 1023980 ████████████████████▍ +CHALFONT ST GILES CHILTERN 113 835754 ████████████████▋ +AMERSHAM BUCKINGHAMSHIRE 113 799596 ███████████████▉ +VIRGINIA WATER RUNNYMEDE 356 789301 ███████████████▊ +BARNET ENFIELD 282 740514 ██████████████▊ +NORTHWOOD THREE RIVERS 184 731609 ██████████████▋ +``` + +:::tip +When you are ready to insert your files into ClickHouse, startup a ClickHouse server and insert the results of your `file` and `s3` table functions into a `MergeTree` table. View the [Quick Start](../../quick-start.mdx) for more details. +::: + ## Usage {#usage} +By default `clickhouse-local` has access to data of a ClickHouse server on the same host, and it does not depend on the server's configuration. It also supports loading server configuration using `--config-file` argument. For temporary data, a unique temporary data directory is created by default. + Basic usage (Linux): ``` bash @@ -24,7 +164,9 @@ Basic usage (Mac): $ ./clickhouse local --structure "table_structure" --input-format "format_of_incoming_data" --query "query" ``` -Also supported on Windows through WSL2. +:::note +`clickhouse-local` is also supported on Windows through WSL2. +::: Arguments: From 3bd29f0aa978a4d20f26a7593b8da0d05614be19 Mon Sep 17 00:00:00 2001 From: taiyang-li <654010905@qq.com> Date: Tue, 4 Apr 2023 10:34:33 +0800 Subject: [PATCH 373/377] fix exception message --- src/Functions/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/map.cpp b/src/Functions/map.cpp index 14453de0646..21a4ab8de7d 100644 --- a/src/Functions/map.cpp +++ b/src/Functions/map.cpp @@ -179,7 +179,7 @@ public: if (const auto * keys_type = checkAndGetDataType(arguments[0].get())) key_type = keys_type->getNestedType(); else - throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "First argument for function {} must be Array or Map", getName()); + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "First argument for function {} must be an Array", getName()); DataTypePtr value_type; if (const auto * value_array_type = checkAndGetDataType(arguments[1].get())) From 7df9b2d563bc65c950bb2c2ee579615046df3278 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 4 Apr 2023 07:39:19 +0000 Subject: [PATCH 374/377] Small follow-up to #45912 - clear query cache at the end of the tests to minimize interaction with other query cache tests - generate data more elegantly --- .../02494_query_cache_compression.reference | 1728 ----------------- .../02494_query_cache_compression.sql | 411 +--- ...494_query_cache_squash_partial_results.sql | 2 +- 3 files changed, 3 insertions(+), 2138 deletions(-) diff --git a/tests/queries/0_stateless/02494_query_cache_compression.reference b/tests/queries/0_stateless/02494_query_cache_compression.reference index 1d206bbc4d2..922ab65540a 100644 --- a/tests/queries/0_stateless/02494_query_cache_compression.reference +++ b/tests/queries/0_stateless/02494_query_cache_compression.reference @@ -599,222 +599,6 @@ abc abc abc abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc def def def @@ -1115,222 +899,6 @@ def def def def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl jkl jkl jkl @@ -2232,222 +1800,6 @@ abc abc abc abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc def def def @@ -2748,222 +2100,6 @@ def def def def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl jkl jkl jkl @@ -3865,222 +3001,6 @@ abc abc abc abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc def def def @@ -4381,222 +3301,6 @@ def def def def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl jkl jkl jkl @@ -5498,222 +4202,6 @@ abc abc abc abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc -abc def def def @@ -6014,222 +4502,6 @@ def def def def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -def -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl -jkl jkl jkl jkl diff --git a/tests/queries/0_stateless/02494_query_cache_compression.sql b/tests/queries/0_stateless/02494_query_cache_compression.sql index 0f527dfde5e..619ae7d5cd8 100644 --- a/tests/queries/0_stateless/02494_query_cache_compression.sql +++ b/tests/queries/0_stateless/02494_query_cache_compression.sql @@ -4,419 +4,11 @@ SET allow_experimental_query_cache = true; SYSTEM DROP QUERY CACHE; - DROP TABLE IF EXISTS t; -- Create test table with lot's of rows CREATE TABLE t(c String) ENGINE=MergeTree ORDER BY c; -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); -INSERT INTO t values ('abc') ('def') ('abc') ('jkl'); +INSERT INTO t SELECT multiIf(n = 0, 'abc', n = 1, 'def', n = 2, 'abc', n = 3, 'jkl', '') FROM (SELECT number % 4 AS n FROM numbers(1200)); OPTIMIZE TABLE t FINAL; -- Run query which, store *compressed* result in query cache @@ -442,3 +34,4 @@ SELECT * FROM t ORDER BY c SETTINGS use_query_cache = true; DROP TABLE t; +SYSTEM DROP QUERY CACHE; diff --git a/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql index d57773b51f8..d18f6d57ff0 100644 --- a/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql +++ b/tests/queries/0_stateless/02494_query_cache_squash_partial_results.sql @@ -4,7 +4,6 @@ SET allow_experimental_query_cache = true; SYSTEM DROP QUERY CACHE; - DROP TABLE IF EXISTS t; -- Create test table with "many" rows @@ -51,3 +50,4 @@ SELECT * FROM t ORDER BY c SETTINGS max_block_size = 2, use_query_cache = true; DROP TABLE t; +SYSTEM DROP QUERY CACHE; From 57a412745d39074cd3e3740706da0e12760eab8c Mon Sep 17 00:00:00 2001 From: robot-clickhouse Date: Tue, 4 Apr 2023 11:35:29 +0000 Subject: [PATCH 375/377] Update version_date.tsv and changelogs after v22.8.16.32-lts --- docs/changelogs/v22.8.16.32-lts.md | 29 ++++++++++++++++++++++++++++ utils/list-versions/version_date.tsv | 1 + 2 files changed, 30 insertions(+) create mode 100644 docs/changelogs/v22.8.16.32-lts.md diff --git a/docs/changelogs/v22.8.16.32-lts.md b/docs/changelogs/v22.8.16.32-lts.md new file mode 100644 index 00000000000..27ec1f1f145 --- /dev/null +++ b/docs/changelogs/v22.8.16.32-lts.md @@ -0,0 +1,29 @@ +--- +sidebar_position: 1 +sidebar_label: 2023 +--- + +# 2023 Changelog + +### ClickHouse release v22.8.16.32-lts (7c4be737bd0) FIXME as compared to v22.8.15.23-lts (d36fa168bbf) + +#### Build/Testing/Packaging Improvement +* Backported in [#48344](https://github.com/ClickHouse/ClickHouse/issues/48344): Use sccache as a replacement for ccache and using S3 as cache backend. [#46240](https://github.com/ClickHouse/ClickHouse/pull/46240) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Backported in [#48250](https://github.com/ClickHouse/ClickHouse/issues/48250): The `clickhouse/clickhouse-keeper` image used to be pushed only with tags `-alpine`, e.g. `latest-alpine`. As it was suggested in https://github.com/ClickHouse/examples/pull/2, now it will be pushed as suffixless too. [#48236](https://github.com/ClickHouse/ClickHouse/pull/48236) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). + +#### Bug Fix (user-visible misbehavior in an official stable release) + +* Fix bug in zero-copy replication disk choice during fetch [#47010](https://github.com/ClickHouse/ClickHouse/pull/47010) ([alesapin](https://github.com/alesapin)). +* Fix query parameters [#47488](https://github.com/ClickHouse/ClickHouse/pull/47488) ([Alexey Milovidov](https://github.com/alexey-milovidov)). +* Fix wait for zero copy lock during move [#47631](https://github.com/ClickHouse/ClickHouse/pull/47631) ([alesapin](https://github.com/alesapin)). +* Fix crash in polygonsSymDifferenceCartesian [#47702](https://github.com/ClickHouse/ClickHouse/pull/47702) ([pufit](https://github.com/pufit)). +* Backport to 22.8: Fix moving broken parts to the detached for the object storage disk on startup [#48273](https://github.com/ClickHouse/ClickHouse/pull/48273) ([Aleksei Filatov](https://github.com/aalexfvk)). + +#### NOT FOR CHANGELOG / INSIGNIFICANT + +* Add a fuse for backport branches w/o a created PR [#47760](https://github.com/ClickHouse/ClickHouse/pull/47760) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Only valid Reviews.STATES overwrite existing reviews [#47789](https://github.com/ClickHouse/ClickHouse/pull/47789) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Place short return before big block, improve logging [#47822](https://github.com/ClickHouse/ClickHouse/pull/47822) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Artifacts s3 prefix [#47945](https://github.com/ClickHouse/ClickHouse/pull/47945) ([Mikhail f. Shiryaev](https://github.com/Felixoid)). +* Fix tsan error lock-order-inversion [#47953](https://github.com/ClickHouse/ClickHouse/pull/47953) ([Kruglov Pavel](https://github.com/Avogar)). + diff --git a/utils/list-versions/version_date.tsv b/utils/list-versions/version_date.tsv index 24af79eef2f..16ae3007938 100644 --- a/utils/list-versions/version_date.tsv +++ b/utils/list-versions/version_date.tsv @@ -36,6 +36,7 @@ v22.9.4.32-stable 2022-10-26 v22.9.3.18-stable 2022-09-30 v22.9.2.7-stable 2022-09-23 v22.9.1.2603-stable 2022-09-22 +v22.8.16.32-lts 2023-04-04 v22.8.15.23-lts 2023-03-10 v22.8.14.53-lts 2023-02-27 v22.8.13.20-lts 2023-01-29 From 2d4fbdf4b0cd60063f6b0eceb25ad2369d0d3607 Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Tue, 4 Apr 2023 14:27:58 +0200 Subject: [PATCH 376/377] add script for a slack bot --- utils/ci-slack-bot/ci-slack-bot.py | 179 +++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100755 utils/ci-slack-bot/ci-slack-bot.py diff --git a/utils/ci-slack-bot/ci-slack-bot.py b/utils/ci-slack-bot/ci-slack-bot.py new file mode 100755 index 00000000000..83b6d8aad6b --- /dev/null +++ b/utils/ci-slack-bot/ci-slack-bot.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 + +# A trivial stateless slack bot that notifies about new broken tests in ClickHouse CI. +# It checks what happened to our CI during the last check_period hours (1 hour) and notifies us in slack if necessary. +# This script should be executed once each check_period hours (1 hour). +# It will post duplicate messages if you run it more often; it will lose some messages if you run it less often. +# +# You can run it locally with no arguments, it will work in a dry-run mode. Or you can set your own SLACK_URL_DEFAULT. +# Feel free to add more checks, more details to messages, or better heuristics. +# NOTE There's no deployment automation for now, +# an AWS Lambda (slack-ci-bot-test lambda in CI-CD) has to be updated manually after changing this script. +# +# See also: https://aretestsgreenyet.com/ + +import os +import json +import base64 + +if os.environ.get("AWS_LAMBDA_ENV", "0") == "1": + # For AWS labmda (python 3.7) + from botocore.vendored import requests +else: + # For running locally + import requests + +DRY_RUN_MARK = "" + +MAX_FAILURES_DEFAULT = 50 +SLACK_URL_DEFAULT = DRY_RUN_MARK + +# Find tests that failed in master during the last check_period hours, +# but did not fail during the last 2 weeks. Assuming these tests were broken recently. +# NOTE: It may report flaky tests that fail too rarely. +NEW_BROKEN_TESTS_QUERY = """ +WITH + 1 AS check_period, + now() as now +SELECT test_name, any(report_url) +FROM checks +WHERE 1 + AND check_start_time >= now - INTERVAL 1 WEEK + AND (check_start_time + check_duration_ms / 1000) >= now - INTERVAL check_period HOUR + AND pull_request_number = 0 + AND test_status LIKE 'F%' + AND check_status != 'success' + AND test_name NOT IN ( + SELECT test_name FROM checks WHERE 1 + AND check_start_time >= now - INTERVAL 1 MONTH + AND (check_start_time + check_duration_ms / 1000) BETWEEN now - INTERVAL 2 WEEK AND now - INTERVAL check_period HOUR + AND pull_request_number = 0 + AND check_status != 'success' + AND test_status LIKE 'F%') + AND test_context_raw NOT LIKE '%CannotSendRequest%' and test_context_raw NOT LIKE '%Server does not respond to health check%' +GROUP BY test_name +""" + +# Returns total number of failed checks during the last 24 hours +# and previous value of that metric (check_period hours ago) +COUNT_FAILURES_QUERY = """ +WITH + 1 AS check_period, + '%' AS check_name_pattern, + now() as now +SELECT + countIf((check_start_time + check_duration_ms / 1000) >= now - INTERVAL 24 HOUR) AS new_val, + countIf((check_start_time + check_duration_ms / 1000) <= now - INTERVAL check_period HOUR) AS prev_val +FROM checks +WHERE 1 + AND check_start_time >= now - INTERVAL 1 WEEK + AND (check_start_time + check_duration_ms / 1000) >= now - INTERVAL 24 + check_period HOUR + AND pull_request_number = 0 + AND test_status LIKE 'F%' + AND check_status != 'success' + AND check_name ILIKE check_name_pattern +""" + +SLACK_MESSAGE_JSON = {"type": "mrkdwn", "text": None} + + +def get_play_url(query): + return ( + "https://play.clickhouse.com/play?user=play#" + + base64.b64encode(query.encode()).decode() + ) + + +def run_clickhouse_query(query): + url = "https://play.clickhouse.com/?user=play&query=" + requests.utils.quote(query) + res = requests.get(url) + if res.status_code != 200: + print("Failed to execute query: ", res.status_code, res.content) + raise Exception( + "Failed to execute query: {}: {}".format(res.status_code, res.content) + ) + + lines = res.text.strip().splitlines() + return [x.split("\t") for x in lines] + + +def get_new_broken_tests_message(broken_tests): + if not broken_tests: + return None + msg = "There are {} new broken tests in master:\n".format(len(broken_tests)) + for name, report in broken_tests: + msg += " - *{}* - <{}|Report>\n".format(name, report) + return msg + + +def get_too_many_failures_message(failures_count): + MAX_FAILURES = int(os.environ.get("MAX_FAILURES", MAX_FAILURES_DEFAULT)) + curr_failures = int(failures_count[0][0]) + prev_failures = int(failures_count[0][1]) + if curr_failures == 0: + return ( + "Looks like CI is completely broken: there are *no failures* at all... 0_o" + ) + if curr_failures < MAX_FAILURES: + return None + if prev_failures < MAX_FAILURES: + return "*CI is broken: there are {} failures during the last 24 hours*".format( + curr_failures + ) + if curr_failures < prev_failures: + return None + if (curr_failures - prev_failures) / prev_failures < 0.2: + return None + return "CI is broken and it's getting worse: there are {} failures during the last 24 hours".format( + curr_failures + ) + + +def send_to_slack(message): + SLACK_URL = os.environ.get("SLACK_URL", SLACK_URL_DEFAULT) + if SLACK_URL == DRY_RUN_MARK: + return + + payload = SLACK_MESSAGE_JSON.copy() + payload["text"] = message + res = requests.post(SLACK_URL, json.dumps(payload)) + if res.status_code != 200: + print("Failed to send a message to Slack: ", res.status_code, res.content) + raise Exception( + "Failed to send a message to Slack: {}: {}".format( + res.status_code, res.content + ) + ) + + +def query_and_alert_if_needed(query, get_message_func): + query_res = run_clickhouse_query(query) + print("Got result {} for query {}", query_res, query) + msg = get_message_func(query_res) + if msg is None: + return + + msg += "\nCI DB query: <{}|link>".format(get_play_url(query)) + print("Sending message to slack:", msg) + send_to_slack(msg) + + +def check_and_alert(): + query_and_alert_if_needed(NEW_BROKEN_TESTS_QUERY, get_new_broken_tests_message) + query_and_alert_if_needed(COUNT_FAILURES_QUERY, get_too_many_failures_message) + + +def lambda_handler(event, context): + try: + check_and_alert() + return {"statusCode": 200, "body": "OK"} + except Exception as e: + send_to_slack( + "I failed, please help me (see ClickHouse/utils/ci-slack-bot/ci-slack-bot.py): " + + str(e) + ) + return {"statusCode": 200, "body": "FAIL"} + + +if __name__ == "__main__": + check_and_alert() From 7aef9bb65ed777cc4a3c9281a000d7f8a652f2ee Mon Sep 17 00:00:00 2001 From: Dan Roscigno Date: Tue, 4 Apr 2023 08:41:58 -0400 Subject: [PATCH 377/377] Clarify SYSTEM RELOAD CONFIG and USERS closes #48201 --- docs/en/sql-reference/statements/system.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/sql-reference/statements/system.md b/docs/en/sql-reference/statements/system.md index fbd7c31a098..8c4b07333be 100644 --- a/docs/en/sql-reference/statements/system.md +++ b/docs/en/sql-reference/statements/system.md @@ -114,11 +114,11 @@ This will also create system tables even if message queue is empty. ## RELOAD CONFIG -Reloads ClickHouse configuration. Used when configuration is stored in ZooKeeper. +Reloads ClickHouse configuration. Used when configuration is stored in ZooKeeper. Note that `SYSTEM RELOAD CONFIG` does not reload `USER` configuration stored in ZooKeeper, it only reloads `USER` configuration that is stored in `users.xml`. To reload all `USER` config use `SYSTEM RELOAD USERS` ## RELOAD USERS -Reloads all access storages, including: users.xml, local disk access storage, replicated (in ZooKeeper) access storage. Note that `SYSTEM RELOAD CONFIG` will only reload users.xml access storage. +Reloads all access storages, including: users.xml, local disk access storage, replicated (in ZooKeeper) access storage. ## SHUTDOWN