From f2d0f34449acd4b3f5e6b5a9151a90fdb50eae8f Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Thu, 19 Apr 2018 20:54:20 +0300 Subject: [PATCH 01/19] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73eba3377cb..60bfe2a8e8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ## Improvements: -* `ALTER TABLE ... DROP/DETACH PARTITION` queries are run in the front of replication queue. +* `ALTER TABLE ... DROP/DETACH PARTITION` queries are run at the front of the replication queue. * `SELECT ... FINAL` and `OPTIMIZE ... FINAL` can be used even when the table has a single data part. * A `query_log` table is recreated on the fly if it was deleted manually (Kirill Shvakov). * The `lengthUTF8` function runs faster (zhang2014). From 3ec6be4ce128404fb2a5c883804692cf0c986778 Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Thu, 19 Apr 2018 20:54:39 +0300 Subject: [PATCH 02/19] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60bfe2a8e8a..af9bec9534b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,7 @@ ## Build changes: * The build supports `ninja` instead of `make` and uses it by default for building releases. -* Renamed packages: `clickhouse-server-base` is now `clickhouse-common-static`; `clickhouse-server-common` is now `clickhouse-server`; `clickhouse-common-dbg` is now `clickhouse-common-static-dbg`. To install, use only `clickhouse-server clickhouse-client`. Packages with the old names will still load in the repositories for backward compatibility. +* Renamed packages: `clickhouse-server-base` is now `clickhouse-common-static`; `clickhouse-server-common` is now `clickhouse-server`; `clickhouse-common-dbg` is now `clickhouse-common-static-dbg`. To install, use `clickhouse-server clickhouse-client`. Packages with the old names will still load in the repositories for backward compatibility. ## Backward-incompatible changes: From 6adffe8fa79dcb4e8794840dfc4dc169dda5784f Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Thu, 19 Apr 2018 20:54:54 +0300 Subject: [PATCH 03/19] Update CHANGELOG_RU.md --- CHANGELOG_RU.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG_RU.md b/CHANGELOG_RU.md index cab00f1b219..b5fbf580421 100644 --- a/CHANGELOG_RU.md +++ b/CHANGELOG_RU.md @@ -44,7 +44,7 @@ ## Изменения сборки: * Поддержка `ninja` вместо `make` при сборке. `ninja` используется по-умолчанию при сборке релизов. -* Переименованы пакеты `clickhouse-server-base` в `clickhouse-common-static`; `clickhouse-server-common` в `clickhouse-server`; `clickhouse-common-dbg` в `clickhouse-common-static-dbg`. Для установки используйте только `clickhouse-server clickhouse-client`. Для совместимости, пакеты со старыми именами продолжают загружаться в репозиторий. +* Переименованы пакеты `clickhouse-server-base` в `clickhouse-common-static`; `clickhouse-server-common` в `clickhouse-server`; `clickhouse-common-dbg` в `clickhouse-common-static-dbg`. Для установки используйте `clickhouse-server clickhouse-client`. Для совместимости, пакеты со старыми именами продолжают загружаться в репозиторий. ## Обратно несовместимые изменения: From da94c7dd9146afb6895975310fe6f95911195475 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 19 Apr 2018 21:01:50 +0300 Subject: [PATCH 04/19] Exit from queueUpdatingThread when ZooKeeper session is expired (non-significant change) [#CLICKHOUSE-2] --- dbms/src/Storages/StorageReplicatedMergeTree.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index fb56172645b..4dd113bcf0b 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -1596,6 +1596,15 @@ void StorageReplicatedMergeTree::queueUpdatingThread() update_in_progress = false; queue_updating_event->wait(); } + catch (const zkutil::KeeperException & e) + { + tryLogCurrentException(log, __PRETTY_FUNCTION__); + + if (e.code == ZooKeeperImpl::ZooKeeper::ZSESSIONEXPIRED) + break; + else + queue_updating_event->tryWait(QUEUE_UPDATE_ERROR_SLEEP_MS); + } catch (...) { tryLogCurrentException(log, __PRETTY_FUNCTION__); From 12e33cfd85bf437add86ba82509b753ee9ff76f8 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 19 Apr 2018 21:16:18 +0300 Subject: [PATCH 05/19] Exit from threads when ZooKeeper session is expired (non significant change) [#CLICKHOUSE-2] --- dbms/src/Common/ZooKeeper/LeaderElection.h | 10 +++++++++- .../MergeTree/ReplicatedMergeTreeAlterThread.cpp | 11 ++++++++++- .../MergeTree/ReplicatedMergeTreeCleanupThread.cpp | 7 +++++++ .../MergeTree/ReplicatedMergeTreePartCheckThread.cpp | 9 +++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/dbms/src/Common/ZooKeeper/LeaderElection.h b/dbms/src/Common/ZooKeeper/LeaderElection.h index 1786cc76510..e730765e1f1 100644 --- a/dbms/src/Common/ZooKeeper/LeaderElection.h +++ b/dbms/src/Common/ZooKeeper/LeaderElection.h @@ -1,6 +1,7 @@ #pragma once #include "ZooKeeper.h" +#include "KeeperException.h" #include #include #include @@ -68,7 +69,7 @@ private: std::thread thread; std::atomic shutdown_called {false}; - zkutil::EventPtr event = std::make_shared(); + EventPtr event = std::make_shared(); CurrentMetrics::Increment metric_increment{CurrentMetrics::LeaderElection}; @@ -115,6 +116,13 @@ private: success = true; } + catch (const KeeperException & e) + { + DB::tryLogCurrentException("LeaderElection"); + + if (e.code == ZooKeeperImpl::ZooKeeper::ZSESSIONEXPIRED) + break; + } catch (...) { DB::tryLogCurrentException("LeaderElection"); diff --git a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeAlterThread.cpp b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeAlterThread.cpp index bc6f58f698a..3f88b9d38f9 100644 --- a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeAlterThread.cpp +++ b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeAlterThread.cpp @@ -188,12 +188,21 @@ void ReplicatedMergeTreeAlterThread::run() wakeup_event->wait(); } + catch (const zkutil::KeeperException & e) + { + tryLogCurrentException(log, __PRETTY_FUNCTION__); + + if (e.code == ZooKeeperImpl::ZooKeeper::ZSESSIONEXPIRED) + break; + + force_recheck_parts = true; + wakeup_event->tryWait(ALTER_ERROR_SLEEP_MS); + } catch (...) { tryLogCurrentException(log, __PRETTY_FUNCTION__); force_recheck_parts = true; - wakeup_event->tryWait(ALTER_ERROR_SLEEP_MS); } } diff --git a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp index 9ef2618ebc8..6b4fdbad390 100644 --- a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp +++ b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp @@ -36,6 +36,13 @@ void ReplicatedMergeTreeCleanupThread::run() { iterate(); } + catch (const zkutil::KeeperException & e) + { + tryLogCurrentException(log, __PRETTY_FUNCTION__); + + if (e.code == ZooKeeperImpl::ZooKeeper::ZSESSIONEXPIRED) + break; + } catch (...) { tryLogCurrentException(log, __PRETTY_FUNCTION__); diff --git a/dbms/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp b/dbms/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp index 6dbf462952a..e366ab972b0 100644 --- a/dbms/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp +++ b/dbms/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp @@ -381,6 +381,15 @@ void ReplicatedMergeTreePartCheckThread::run() } } } + catch (const zkutil::KeeperException & e) + { + tryLogCurrentException(log, __PRETTY_FUNCTION__); + + if (e.code == ZooKeeperImpl::ZooKeeper::ZSESSIONEXPIRED) + break; + + wakeup_event.tryWait(PART_CHECK_ERROR_SLEEP_MS); + } catch (...) { tryLogCurrentException(log, __PRETTY_FUNCTION__); From 89b67dd25a19895144adca2f8200558ac6aad6ce Mon Sep 17 00:00:00 2001 From: proller Date: Thu, 19 Apr 2018 21:19:12 +0300 Subject: [PATCH 06/19] Simpler disable logging to file in conf.d ( ) --- libs/libdaemon/src/BaseDaemon.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libs/libdaemon/src/BaseDaemon.cpp b/libs/libdaemon/src/BaseDaemon.cpp index e262b2df17c..ff1fbabceb1 100644 --- a/libs/libdaemon/src/BaseDaemon.cpp +++ b/libs/libdaemon/src/BaseDaemon.cpp @@ -706,17 +706,18 @@ void BaseDaemon::buildLoggers(Poco::Util::AbstractConfiguration & config) Poco::AutoPtr split = new SplitterChannel; auto log_level = config.getString("logger.level", "trace"); - if (config.hasProperty("logger.log")) + const auto log_path = config.getString("logger.log", ""); + if (!log_path.empty()) { - createDirectory(config.getString("logger.log")); - std::cerr << "Logging " << log_level << " to " << config.getString("logger.log") << std::endl; + createDirectory(log_path); + std::cerr << "Logging " << log_level << " to " << log_path << std::endl; // Set up two channel chains. Poco::AutoPtr pf = new OwnPatternFormatter(this); pf->setProperty("times", "local"); Poco::AutoPtr log = new FormattingChannel(pf); log_file = new FileChannel; - log_file->setProperty(Poco::FileChannel::PROP_PATH, Poco::Path(config.getString("logger.log")).absolute().toString()); + log_file->setProperty(Poco::FileChannel::PROP_PATH, Poco::Path(log_path).absolute().toString()); log_file->setProperty(Poco::FileChannel::PROP_ROTATION, config.getRawString("logger.size", "100M")); log_file->setProperty(Poco::FileChannel::PROP_ARCHIVE, "number"); log_file->setProperty(Poco::FileChannel::PROP_COMPRESS, config.getRawString("logger.compress", "true")); @@ -728,17 +729,18 @@ void BaseDaemon::buildLoggers(Poco::Util::AbstractConfiguration & config) log_file->open(); } - if (config.hasProperty("logger.errorlog")) + const auto errorlog_path = config.getString("logger.errorlog", ""); + if (!errorlog_path.empty()) { - createDirectory(config.getString("logger.errorlog")); - std::cerr << "Logging errors to " << config.getString("logger.errorlog") << std::endl; + createDirectory(errorlog_path); + std::cerr << "Logging errors to " << errorlog_path << std::endl; Poco::AutoPtr level = new Poco::LevelFilterChannel; level->setLevel(Message::PRIO_NOTICE); Poco::AutoPtr pf = new OwnPatternFormatter(this); pf->setProperty("times", "local"); Poco::AutoPtr errorlog = new FormattingChannel(pf); error_log_file = new FileChannel; - error_log_file->setProperty(Poco::FileChannel::PROP_PATH, Poco::Path(config.getString("logger.errorlog")).absolute().toString()); + error_log_file->setProperty(Poco::FileChannel::PROP_PATH, Poco::Path(errorlog_path).absolute().toString()); error_log_file->setProperty(Poco::FileChannel::PROP_ROTATION, config.getRawString("logger.size", "100M")); error_log_file->setProperty(Poco::FileChannel::PROP_ARCHIVE, "number"); error_log_file->setProperty(Poco::FileChannel::PROP_COMPRESS, config.getRawString("logger.compress", "true")); @@ -965,9 +967,9 @@ void BaseDaemon::initialize(Application & self) } /// Change path for logging. - if (config().hasProperty("logger.log")) + if (!log_path.empty()) { - std::string path = createDirectory(config().getString("logger.log")); + std::string path = createDirectory(log_path); if (is_daemon && chdir(path.c_str()) != 0) throw Poco::Exception("Cannot change directory to " + path); From 9a05dd616113d9d9782dbe939a8e9d075e8d8c57 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 19 Apr 2018 22:21:37 +0300 Subject: [PATCH 07/19] ZooKeeperImpl: fixed error with watches and chroot [#CLICKHOUSE-2] --- dbms/src/Common/ZooKeeper/ZooKeeperImpl.cpp | 62 ++++++++++----------- dbms/src/Common/ZooKeeper/ZooKeeperImpl.h | 2 +- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/dbms/src/Common/ZooKeeper/ZooKeeperImpl.cpp b/dbms/src/Common/ZooKeeper/ZooKeeperImpl.cpp index 6b10de7dee6..bca15656eda 100644 --- a/dbms/src/Common/ZooKeeper/ZooKeeperImpl.cpp +++ b/dbms/src/Common/ZooKeeper/ZooKeeperImpl.cpp @@ -432,6 +432,35 @@ void ZooKeeper::read(T & x) } +void addRootPath(String & path, const String & root_path) +{ + if (path.empty()) + throw Exception("Path cannot be empty", ZooKeeper::ZBADARGUMENTS); + + if (path[0] != '/') + throw Exception("Path must begin with /", ZooKeeper::ZBADARGUMENTS); + + if (root_path.empty()) + return; + + if (path.size() == 1) /// "/" + path = root_path; + else + path = root_path + path; +} + +void removeRootPath(String & path, const String & root_path) +{ + if (root_path.empty()) + return; + + if (path.size() <= root_path.size()) + throw Exception("Received path is not longer than root_path", ZooKeeper::ZDATAINCONSISTENCY); + + path = path.substr(root_path.size()); +} + + static constexpr int32_t protocol_version = 0; static constexpr ZooKeeper::XID watch_xid = -1; @@ -735,6 +764,7 @@ void ZooKeeper::sendThread() if (expired) break; + info.request->addRootPath(root_path); info.request->write(*out); if (info.request->xid == close_xid) @@ -844,35 +874,6 @@ ZooKeeper::ResponsePtr ZooKeeper::MultiRequest::makeResponse() const { return st ZooKeeper::ResponsePtr ZooKeeper::CloseRequest::makeResponse() const { return std::make_shared(); } -void addRootPath(String & path, const String & root_path) -{ - if (path.empty()) - throw Exception("Path cannot be empty", ZooKeeper::ZBADARGUMENTS); - - if (path[0] != '/') - throw Exception("Path must begin with /", ZooKeeper::ZBADARGUMENTS); - - if (root_path.empty()) - return; - - if (path.size() == 1) /// "/" - path = root_path; - else - path = root_path + path; -} - -void removeRootPath(String & path, const String & root_path) -{ - if (root_path.empty()) - return; - - if (path.size() <= root_path.size()) - throw Exception("Received path is not longer than root_path", ZooKeeper::ZDATAINCONSISTENCY); - - path = path.substr(root_path.size()); -} - - void ZooKeeper::CreateRequest::addRootPath(const String & root_path) { ZooKeeperImpl::addRootPath(path, root_path); } void ZooKeeper::RemoveRequest::addRootPath(const String & root_path) { ZooKeeperImpl::addRootPath(path, root_path); } void ZooKeeper::ExistsRequest::addRootPath(const String & root_path) { ZooKeeperImpl::addRootPath(path, root_path); } @@ -1108,7 +1109,6 @@ void ZooKeeper::finalize(bool error_send, bool error_receive) { tryLogCurrentException(__PRETTY_FUNCTION__); } - } if (info.watch) { @@ -1335,8 +1335,6 @@ void ZooKeeper::pushRequest(RequestInfo && info) { try { - info.request->addRootPath(root_path); - info.time = clock::now(); if (!info.request->xid) diff --git a/dbms/src/Common/ZooKeeper/ZooKeeperImpl.h b/dbms/src/Common/ZooKeeper/ZooKeeperImpl.h index 8a65d09b529..ad5facf7f6d 100644 --- a/dbms/src/Common/ZooKeeper/ZooKeeperImpl.h +++ b/dbms/src/Common/ZooKeeper/ZooKeeperImpl.h @@ -596,7 +596,7 @@ private: std::mutex operations_mutex; using WatchCallbacks = std::vector; - using Watches = std::map; + using Watches = std::map; Watches watches; std::mutex watches_mutex; From a29a99b7d348737ec2827ee71937dd3036bb1478 Mon Sep 17 00:00:00 2001 From: Vitaliy Lyudvichenko Date: Thu, 19 Apr 2018 22:40:42 +0300 Subject: [PATCH 08/19] Add test for ZooKeeperImpl with watch and chroot. [#CLICKHOUSE-2] --- .../gtest_zkutil_test_multi_exception.cpp | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/dbms/src/Common/ZooKeeper/tests/gtest_zkutil_test_multi_exception.cpp b/dbms/src/Common/ZooKeeper/tests/gtest_zkutil_test_multi_exception.cpp index edb51147fd8..915b31d420b 100644 --- a/dbms/src/Common/ZooKeeper/tests/gtest_zkutil_test_multi_exception.cpp +++ b/dbms/src/Common/ZooKeeper/tests/gtest_zkutil_test_multi_exception.cpp @@ -61,7 +61,7 @@ TEST(zkutil, multi_nice_exception_msg) String msg = getCurrentExceptionMessage(false); - bool msg_has_reqired_patterns = msg.find("/clickhouse_test/zkutil_multi/a") != std::string::npos && msg.find("#2") != std::string::npos; + bool msg_has_reqired_patterns = msg.find("#2") != std::string::npos; EXPECT_TRUE(msg_has_reqired_patterns) << msg; } } @@ -129,40 +129,54 @@ TEST(zkutil, multi_async) } } -/// Run this test under sudo -TEST(zkutil, multi_async_libzookeeper_segfault) +TEST(zkutil, watch_get_children_with_chroot) { - auto zookeeper = std::make_unique("localhost:2181", "", 1000); - zkutil::Requests ops; + try + { + const String zk_server = "localhost:2181"; + const String prefix = "/clickhouse_test/zkutil/watch_get_children_with_chroot"; - ops.emplace_back(zkutil::makeCheckRequest("/clickhouse_test/zkutil_multi", 0)); + /// Create chroot node firstly + auto zookeeper = std::make_unique(zk_server); + zookeeper->createAncestors(prefix + "/"); + zookeeper = std::make_unique(zk_server, "", zkutil::DEFAULT_SESSION_TIMEOUT, prefix); - /// Uncomment to test - //auto cmd = ShellCommand::execute("sudo service zookeeper restart"); - //cmd->wait(); + String queue_path = "/queue"; + zookeeper->tryRemoveRecursive(queue_path); + zookeeper->createAncestors(queue_path + "/"); - auto future = zookeeper->asyncMulti(ops); - auto res = future.get(); - - EXPECT_TRUE(zkutil::isHardwareError(res.error)); + zkutil::EventPtr event = std::make_shared(); + zookeeper->getChildren(queue_path, nullptr, event); + { + auto zookeeper2 = std::make_unique(zk_server, "", zkutil::DEFAULT_SESSION_TIMEOUT, prefix); + zookeeper2->create(queue_path + "/children-", "", zkutil::CreateMode::PersistentSequential); + } + event->wait(); + } + catch (...) + { + std::cerr << getCurrentExceptionMessage(true); + throw; + } } - TEST(zkutil, multi_create_sequential) { try { + const String zk_server = "localhost:2181"; + const String prefix = "/clickhouse_test/zkutil"; + /// Create chroot node firstly - auto zookeeper = std::make_unique("localhost:2181"); - zookeeper->createAncestors("/clickhouse_test/"); + auto zookeeper = std::make_unique(zk_server); + zookeeper->createAncestors(prefix + "/"); + zookeeper = std::make_unique(zk_server, "", zkutil::DEFAULT_SESSION_TIMEOUT, "/clickhouse_test"); - zookeeper = std::make_unique("localhost:2181", "", zkutil::DEFAULT_SESSION_TIMEOUT, "/clickhouse_test"); - zkutil::Requests ops; - - String base_path = "/zkutil/multi_create_sequential"; + String base_path = "/multi_create_sequential"; zookeeper->tryRemoveRecursive(base_path); zookeeper->createAncestors(base_path + "/"); + zkutil::Requests ops; String sequential_node_prefix = base_path + "/queue-"; ops.emplace_back(zkutil::makeCreateRequest(sequential_node_prefix, "", zkutil::CreateMode::EphemeralSequential)); auto results = zookeeper->multi(ops); @@ -180,3 +194,4 @@ TEST(zkutil, multi_create_sequential) } + From e9b03b3abc91bc0ab726c6d2b7e59b6edad24c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=81=A5?= Date: Fri, 20 Apr 2018 04:22:08 +0800 Subject: [PATCH 09/19] ISSUES-2242 add default data_path & metadata_path for system.tables with temporary (#2243) --- dbms/src/Storages/System/StorageSystemTables.cpp | 2 ++ .../0_stateless/00080_show_tables_and_system_tables.reference | 1 + .../0_stateless/00080_show_tables_and_system_tables.sql | 3 +++ 3 files changed, 6 insertions(+) diff --git a/dbms/src/Storages/System/StorageSystemTables.cpp b/dbms/src/Storages/System/StorageSystemTables.cpp index 9f9903d8eb2..e84762778f2 100644 --- a/dbms/src/Storages/System/StorageSystemTables.cpp +++ b/dbms/src/Storages/System/StorageSystemTables.cpp @@ -239,6 +239,8 @@ BlockInputStreams StorageSystemTables::read( res_columns[j++]->insert(table.first); res_columns[j++]->insert(table.second->getName()); res_columns[j++]->insert(UInt64(1)); + res_columns[j++]->insertDefault(); + res_columns[j++]->insertDefault(); if (has_metadata_modification_time) res_columns[j++]->insertDefault(); diff --git a/dbms/tests/queries/0_stateless/00080_show_tables_and_system_tables.reference b/dbms/tests/queries/0_stateless/00080_show_tables_and_system_tables.reference index ec753a0466a..3aacb4ff1cc 100644 --- a/dbms/tests/queries/0_stateless/00080_show_tables_and_system_tables.reference +++ b/dbms/tests/queries/0_stateless/00080_show_tables_and_system_tables.reference @@ -2,4 +2,5 @@ A B A 1 TinyLog CREATE TABLE test_show_tables.A ( A UInt8) ENGINE = TinyLog B 1 TinyLog CREATE TABLE test_show_tables.B ( A UInt8) ENGINE = TinyLog +test_temporary_table 0 diff --git a/dbms/tests/queries/0_stateless/00080_show_tables_and_system_tables.sql b/dbms/tests/queries/0_stateless/00080_show_tables_and_system_tables.sql index 53a91b6c3bb..62dfce68eed 100644 --- a/dbms/tests/queries/0_stateless/00080_show_tables_and_system_tables.sql +++ b/dbms/tests/queries/0_stateless/00080_show_tables_and_system_tables.sql @@ -9,6 +9,9 @@ SHOW TABLES from test_show_tables; SELECT name, toUInt32(metadata_modification_time) > 0, engine_full, create_table_query FROM system.tables WHERE database = 'test_show_tables' ORDER BY name FORMAT TSVRaw; +CREATE TEMPORARY TABLE test_temporary_table (id UInt64); +SELECT name FROM system.tables WHERE is_temporary = 1 AND name = 'test_temporary_table'; + DROP DATABASE test_show_tables; From a6c194fa6d5708a207d25c3df836aacbd461cc66 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 19 Apr 2018 23:32:56 +0300 Subject: [PATCH 10/19] Insignificant change #2246 --- dbms/src/Interpreters/InterpreterSelectQuery.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 7ed250e9036..4ee6470edff 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -630,8 +630,6 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(Pipeline QueryProcessingStage::Enum from_stage = QueryProcessingStage::FetchColumns; - query_analyzer->makeSetsForIndex(); - /// Initialize the initial data streams to which the query transforms are superimposed. Table or subquery or prepared input? if (!pipeline.streams.empty()) { @@ -676,6 +674,8 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(Pipeline optimize_prewhere(*merge_tree); } + query_analyzer->makeSetsForIndex(); + if (!dry_run) pipeline.streams = storage->read(required_columns, query_info, context, from_stage, max_block_size, max_streams); From c0978919e313cecfb57b7962e86fd5e3a4839470 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 19 Apr 2018 23:34:02 +0300 Subject: [PATCH 11/19] Fixed error with partition key IN, part 1 #2246 --- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 20 ++++++++++--------- dbms/src/Storages/MergeTree/MergeTreeData.h | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 6afe3557360..a66fc19016b 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -2175,7 +2175,7 @@ MergeTreeData::DataPartsVector MergeTreeData::Transaction::commit() return total_covered_parts; } -bool MergeTreeData::isPrimaryKeyOrPartitionKeyColumnPossiblyWrappedInFunctions(const ASTPtr & node) const +bool MergeTreeData::isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(const ASTPtr & node) const { String column_name = node->getColumnName(); @@ -2183,33 +2183,35 @@ bool MergeTreeData::isPrimaryKeyOrPartitionKeyColumnPossiblyWrappedInFunctions(c if (column_name == column.column_name) return true; - if (partition_expr_ast && partition_expr_ast->children.at(0)->getColumnName() == column_name) - return true; + for (const auto & column : minmax_idx_sort_descr) + if (column_name == column.column_name) + return true; if (const ASTFunction * func = typeid_cast(node.get())) if (func->arguments->children.size() == 1) - return isPrimaryKeyOrPartitionKeyColumnPossiblyWrappedInFunctions(func->arguments->children.front()); + return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(func->arguments->children.front()); return false; } bool MergeTreeData::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand) const { - /// Make sure that the left side of the IN operator contain part of the primary key. - /// If there is a tuple on the left side of the IN operator, at least one item of the tuple must be part of the primary key (probably wrapped by a chain of some acceptable functions). + /// Make sure that the left side of the IN operator contain part of the key. + /// If there is a tuple on the left side of the IN operator, at least one item of the tuple + /// must be part of the key (probably wrapped by a chain of some acceptable functions). const ASTFunction * left_in_operand_tuple = typeid_cast(left_in_operand.get()); if (left_in_operand_tuple && left_in_operand_tuple->name == "tuple") { for (const auto & item : left_in_operand_tuple->arguments->children) - if (isPrimaryKeyOrPartitionKeyColumnPossiblyWrappedInFunctions(item)) + if (isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(item)) return true; /// The tuple itself may be part of the primary key, so check that as a last resort. - return isPrimaryKeyOrPartitionKeyColumnPossiblyWrappedInFunctions(left_in_operand); + return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand); } else { - return isPrimaryKeyOrPartitionKeyColumnPossiblyWrappedInFunctions(left_in_operand); + return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand); } } diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.h b/dbms/src/Storages/MergeTree/MergeTreeData.h index d0b47b095d3..2c2ea67dc85 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.h +++ b/dbms/src/Storages/MergeTree/MergeTreeData.h @@ -652,7 +652,7 @@ private: std::lock_guard & data_parts_lock) const; /// Checks whether the column is in the primary key, possibly wrapped in a chain of functions with single argument. - bool isPrimaryKeyOrPartitionKeyColumnPossiblyWrappedInFunctions(const ASTPtr & node) const; + bool isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(const ASTPtr & node) const; }; } From 02abff4fdbb99d73b347309e08d6e3850ab68f8a Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 19 Apr 2018 23:45:27 +0300 Subject: [PATCH 12/19] Added failing test #2246 --- .../0_stateless/00623_in_partition_key.sql | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00623_in_partition_key.sql diff --git a/dbms/tests/queries/0_stateless/00623_in_partition_key.sql b/dbms/tests/queries/0_stateless/00623_in_partition_key.sql new file mode 100644 index 00000000000..7b8a22a5872 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00623_in_partition_key.sql @@ -0,0 +1,75 @@ +drop table if exists test.test54378; +create table test.test54378 (part_date Date, pk_date Date, date Date) Engine=MergeTree(part_date, pk_date, 8192); +insert into test.test54378 values ('2018-04-19', '2018-04-19', '2018-04-19'); + +select 111 from test.test54378 where part_date = '2018-04-19'; +select 112 from test.test54378 where part_date in ('2018-04-19'); +select 113 from test.test54378 where pk_date in ('2018-04-19'); +select 114 from test.test54378 where date in ('2018-04-19'); +SELECT '-'; +select 121 from test.test54378 where part_date = toDate('2018-04-19'); +select 122 from test.test54378 where part_date in (toDate('2018-04-19')); +select 123 from test.test54378 where pk_date in (toDate('2018-04-19')); +select 124 from test.test54378 where date in (toDate('2018-04-19')); +SELECT '-'; +select 131 from test.test54378 where part_date = (SELECT toDate('2018-04-19')); +select 132 from test.test54378 where part_date in (SELECT toDate('2018-04-19')); +select 133 from test.test54378 where pk_date in (SELECT toDate('2018-04-19')); +select 134 from test.test54378 where date in (SELECT toDate('2018-04-19')); + +SELECT '---'; + +select 211 from test.test54378 prewhere part_date = '2018-04-19'; +select 212 from test.test54378 prewhere part_date in ('2018-04-19'); +select 213 from test.test54378 prewhere pk_date in ('2018-04-19'); +select 214 from test.test54378 prewhere date in ('2018-04-19'); +SELECT '-'; +select 221 from test.test54378 prewhere part_date = toDate('2018-04-19'); +select 222 from test.test54378 prewhere part_date in (toDate('2018-04-19')); +select 223 from test.test54378 prewhere pk_date in (toDate('2018-04-19')); +select 224 from test.test54378 prewhere date in (toDate('2018-04-19')); +SELECT '-'; +select 231 from test.test54378 prewhere part_date = (SELECT toDate('2018-04-19')); +select 232 from test.test54378 prewhere part_date in (SELECT toDate('2018-04-19')); +select 233 from test.test54378 prewhere pk_date in (SELECT toDate('2018-04-19')); +select 234 from test.test54378 prewhere date in (SELECT toDate('2018-04-19')); + +SELECT '---'; + +SET optimize_move_to_prewhere = 0; + +select 311 from test.test54378 where part_date = '2018-04-19'; +select 312 from test.test54378 where part_date in ('2018-04-19'); +select 313 from test.test54378 where pk_date in ('2018-04-19'); +select 314 from test.test54378 where date in ('2018-04-19'); +SELECT '-'; +select 321 from test.test54378 where part_date = toDate('2018-04-19'); +select 322 from test.test54378 where part_date in (toDate('2018-04-19')); +select 323 from test.test54378 where pk_date in (toDate('2018-04-19')); +select 324 from test.test54378 where date in (toDate('2018-04-19')); +SELECT '-'; +select 331 from test.test54378 where part_date = (SELECT toDate('2018-04-19')); +select 332 from test.test54378 where part_date in (SELECT toDate('2018-04-19')); +select 333 from test.test54378 where pk_date in (SELECT toDate('2018-04-19')); +select 334 from test.test54378 where date in (SELECT toDate('2018-04-19')); + +SELECT '---'; + +SET optimize_move_to_prewhere = 1; + +select 411 from test.test54378 where part_date = '2018-04-19'; +select 412 from test.test54378 where part_date in ('2018-04-19'); +select 413 from test.test54378 where pk_date in ('2018-04-19'); +select 414 from test.test54378 where date in ('2018-04-19'); +SELECT '-'; +select 421 from test.test54378 where part_date = toDate('2018-04-19'); +select 422 from test.test54378 where part_date in (toDate('2018-04-19')); +select 423 from test.test54378 where pk_date in (toDate('2018-04-19')); +select 424 from test.test54378 where date in (toDate('2018-04-19')); +SELECT '-'; +select 431 from test.test54378 where part_date = (SELECT toDate('2018-04-19')); +select 432 from test.test54378 where part_date in (SELECT toDate('2018-04-19')); +select 433 from test.test54378 where pk_date in (SELECT toDate('2018-04-19')); +select 434 from test.test54378 where date in (SELECT toDate('2018-04-19')); + +drop table test.test54378; From ce0ac3f8f867f7261ca7a2ea5c4ea9a0772428ef Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 20 Apr 2018 00:34:04 +0300 Subject: [PATCH 13/19] Fixed error with Sets, part 2 #2246 --- .../CreatingSetsBlockInputStream.cpp | 20 +++++ .../CreatingSetsBlockInputStream.h | 11 +-- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 3 +- dbms/src/Interpreters/Set.cpp | 81 ++++++++++++------- dbms/src/Interpreters/Set.h | 10 ++- dbms/src/Storages/MergeTree/PKCondition.cpp | 6 -- dbms/src/Storages/StorageSet.cpp | 4 + 7 files changed, 90 insertions(+), 45 deletions(-) diff --git a/dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp b/dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp index b183226e4c9..d6daab281c9 100644 --- a/dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp +++ b/dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp @@ -16,6 +16,26 @@ namespace ErrorCodes } +CreatingSetsBlockInputStream::CreatingSetsBlockInputStream( + const BlockInputStreamPtr & input, + const SubqueriesForSets & subqueries_for_sets_, + const SizeLimits & network_transfer_limits) + : subqueries_for_sets(subqueries_for_sets_), + network_transfer_limits(network_transfer_limits) +{ + for (auto & elem : subqueries_for_sets) + { + if (elem.second.source) + { + children.push_back(elem.second.source); + elem.second.set->setHeader(elem.second.source->getHeader()); + } + } + + children.push_back(input); +} + + Block CreatingSetsBlockInputStream::readImpl() { Block res; diff --git a/dbms/src/DataStreams/CreatingSetsBlockInputStream.h b/dbms/src/DataStreams/CreatingSetsBlockInputStream.h index dc34866a60c..ff8fe5683c7 100644 --- a/dbms/src/DataStreams/CreatingSetsBlockInputStream.h +++ b/dbms/src/DataStreams/CreatingSetsBlockInputStream.h @@ -20,16 +20,7 @@ public: CreatingSetsBlockInputStream( const BlockInputStreamPtr & input, const SubqueriesForSets & subqueries_for_sets_, - const SizeLimits & network_transfer_limits) - : subqueries_for_sets(subqueries_for_sets_), - network_transfer_limits(network_transfer_limits) - { - for (auto & elem : subqueries_for_sets) - if (elem.second.source) - children.push_back(elem.second.source); - - children.push_back(input); - } + const SizeLimits & network_transfer_limits); String getName() const override { return "CreatingSets"; } diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 966bb9c5ef9..5dad9c4f323 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -1474,6 +1474,7 @@ void ExpressionAnalyzer::tryMakeSetFromSubquery(const ASTPtr & subquery_or_table SetPtr set = std::make_shared(SizeLimits(settings.max_rows_in_set, settings.max_bytes_in_set, settings.set_overflow_mode)); + set->setHeader(res.in->getHeader()); while (Block block = res.in->read()) { /// If the limits have been exceeded, give up and let the default subquery processing actions take place. @@ -2067,7 +2068,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries, const SetPtr & set = prepared_sets[child.get()]; /// If the argument is a set given by an enumeration of values (so, the set was already built), give it a unique name, - /// so that sets with the same record do not fuse together (they can have different types). + /// so that sets with the same literal representation do not fuse together (they can have different types). if (!set->empty()) column.name = getUniqueName(actions_stack.getSampleBlock(), "__set"); else diff --git a/dbms/src/Interpreters/Set.cpp b/dbms/src/Interpreters/Set.cpp index 6065a3029a6..931019739b0 100644 --- a/dbms/src/Interpreters/Set.cpp +++ b/dbms/src/Interpreters/Set.cpp @@ -62,7 +62,6 @@ void NO_INLINE Set::insertFromBlockImplCase( { typename Method::State state; state.init(key_columns); - size_t keys_size = key_columns.size(); /// For all rows for (size_t i = 0; i < rows; ++i) @@ -83,19 +82,17 @@ void NO_INLINE Set::insertFromBlockImplCase( } -bool Set::insertFromBlock(const Block & block, bool fill_set_elements) +void Set::setHeader(const Block & block) { std::unique_lock lock(rwlock); - size_t keys_size = block.columns(); + if (!empty()) + return; + + keys_size = block.columns(); ColumnRawPtrs key_columns; key_columns.reserve(keys_size); - - if (empty()) - { - data_types.clear(); - data_types.reserve(keys_size); - } + data_types.reserve(keys_size); /// The constant columns to the right of IN are not supported directly. For this, they first materialize. Columns materialized_columns; @@ -104,9 +101,42 @@ bool Set::insertFromBlock(const Block & block, bool fill_set_elements) for (size_t i = 0; i < keys_size; ++i) { key_columns.emplace_back(block.safeGetByPosition(i).column.get()); + data_types.emplace_back(block.safeGetByPosition(i).type); - if (empty()) - data_types.emplace_back(block.safeGetByPosition(i).type); + if (ColumnPtr converted = key_columns.back()->convertToFullColumnIfConst()) + { + materialized_columns.emplace_back(converted); + key_columns.back() = materialized_columns.back().get(); + } + } + + /// We will insert to the Set only keys, where all components are not NULL. + ColumnPtr null_map_holder; + ConstNullMapPtr null_map{}; + extractNestedColumnsAndNullMap(key_columns, null_map_holder, null_map); + + /// Choose data structure to use for the set. + data.init(data.chooseMethod(key_columns, key_sizes)); +} + + +bool Set::insertFromBlock(const Block & block, bool fill_set_elements) +{ + std::unique_lock lock(rwlock); + + if (empty()) + throw Exception("Method Set::setHeader must be called before Set::insertFromBlock", ErrorCodes::LOGICAL_ERROR); + + ColumnRawPtrs key_columns; + key_columns.reserve(keys_size); + + /// The constant columns to the right of IN are not supported directly. For this, they first materialize. + Columns materialized_columns; + + /// Remember the columns we will work with + for (size_t i = 0; i < keys_size; ++i) + { + key_columns.emplace_back(block.safeGetByPosition(i).column.get()); if (ColumnPtr converted = key_columns.back()->convertToFullColumnIfConst()) { @@ -122,10 +152,6 @@ bool Set::insertFromBlock(const Block & block, bool fill_set_elements) ConstNullMapPtr null_map{}; extractNestedColumnsAndNullMap(key_columns, null_map_holder, null_map); - /// Choose data structure to use for the set. - if (empty()) - data.init(data.chooseMethod(key_columns, key_sizes)); - switch (data.type) { case SetVariants::Type::EMPTY: @@ -153,6 +179,7 @@ bool Set::insertFromBlock(const Block & block, bool fill_set_elements) return limits.check(getTotalRowCount(), getTotalByteCount(), "IN-set", ErrorCodes::SET_SIZE_LIMIT_EXCEEDED); } + static Field extractValueFromNode(ASTPtr & node, const IDataType & type, const Context & context) { if (ASTLiteral * lit = typeid_cast(node.get())) @@ -173,16 +200,19 @@ void Set::createFromAST(const DataTypes & types, ASTPtr node, const Context & co { /// Will form a block with values from the set. - size_t size = types.size(); - MutableColumns columns(types.size()); - for (size_t i = 0; i < size; ++i) - columns[i] = types[i]->createColumn(); + Block header; + size_t num_columns = types.size(); + for (size_t i = 0; i < num_columns; ++i) + header.insert(ColumnWithTypeAndName(types[i]->createColumn(), types[i], "_" + toString(i))); + setHeader(header); + + MutableColumns columns = header.cloneEmptyColumns(); Row tuple_values; ASTExpressionList & list = typeid_cast(*node); for (auto & elem : list.children) { - if (types.size() == 1) + if (num_columns == 1) { Field value = extractValueFromNode(elem, *types[0], context); @@ -195,8 +225,9 @@ void Set::createFromAST(const DataTypes & types, ASTPtr node, const Context & co throw Exception("Incorrect element of set. Must be tuple.", ErrorCodes::INCORRECT_ELEMENT_OF_SET); size_t tuple_size = func->arguments->children.size(); - if (tuple_size != types.size()) - throw Exception("Incorrect size of tuple in set.", ErrorCodes::INCORRECT_ELEMENT_OF_SET); + if (tuple_size != num_columns) + throw Exception("Incorrect size of tuple in set: " + toString(tuple_size) + " instead of " + toString(num_columns), + ErrorCodes::INCORRECT_ELEMENT_OF_SET); if (tuple_values.empty()) tuple_values.resize(tuple_size); @@ -221,10 +252,7 @@ void Set::createFromAST(const DataTypes & types, ASTPtr node, const Context & co throw Exception("Incorrect element of set", ErrorCodes::INCORRECT_ELEMENT_OF_SET); } - Block block; - for (size_t i = 0, size = types.size(); i < size; ++i) - block.insert(ColumnWithTypeAndName(std::move(columns[i]), types[i], "_" + toString(i))); - + Block block = header.cloneWithColumns(std::move(columns)); insertFromBlock(block, fill_set_elements); } @@ -321,7 +349,6 @@ void NO_INLINE Set::executeImplCase( { typename Method::State state; state.init(key_columns); - size_t keys_size = key_columns.size(); /// NOTE Optimization is not used for consecutive identical values. diff --git a/dbms/src/Interpreters/Set.h b/dbms/src/Interpreters/Set.h index fb2ba9f26fd..620fe1ee3f7 100644 --- a/dbms/src/Interpreters/Set.h +++ b/dbms/src/Interpreters/Set.h @@ -38,6 +38,9 @@ public: bool empty() const { return data.empty(); } + /** Set can be created either from AST or from a stream of data (subquery result). + */ + /** Create a Set from expression (specified literally in the query). * 'types' - types of what are on the left hand side of IN. * 'node' - list of values: 1, 2, 3 or list of tuples: (1, 2), (3, 4), (5, 6). @@ -45,8 +48,12 @@ public: */ void createFromAST(const DataTypes & types, ASTPtr node, const Context & context, bool fill_set_elements); - /** Returns false, if some limit was exceeded and no need to insert more data. + /** Create a Set from stream. + * Call setHeader, then call insertFromBlock for each block. */ + void setHeader(const Block & header); + + /// Returns false, if some limit was exceeded and no need to insert more data. bool insertFromBlock(const Block & block, bool fill_set_elements); /** For columns of 'block', check belonging of corresponding rows to the set. @@ -62,6 +69,7 @@ public: SetElements & getSetElements() { return *set_elements.get(); } private: + size_t keys_size; Sizes key_sizes; SetVariants data; diff --git a/dbms/src/Storages/MergeTree/PKCondition.cpp b/dbms/src/Storages/MergeTree/PKCondition.cpp index 289f7b935ad..bc879e770ea 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.cpp +++ b/dbms/src/Storages/MergeTree/PKCondition.cpp @@ -496,12 +496,6 @@ bool PKCondition::isTupleIndexable( std::vector indexes_mapping; size_t num_key_columns = prepared_set->getDataTypes().size(); - if (num_key_columns == 0) - { - /// Empty set. It is "indexable" in a sense, that it implies that condition is always false (or true for NOT IN). - out.set_index = std::make_shared(prepared_set->getSetElements(), std::move(indexes_mapping)); - return true; - } const ASTFunction * node_tuple = typeid_cast(node.get()); if (node_tuple && node_tuple->name == "tuple") diff --git a/dbms/src/Storages/StorageSet.cpp b/dbms/src/Storages/StorageSet.cpp index 5ba5d737435..dc21abbfe01 100644 --- a/dbms/src/Storages/StorageSet.cpp +++ b/dbms/src/Storages/StorageSet.cpp @@ -107,6 +107,10 @@ StorageSet::StorageSet( : StorageSetOrJoinBase{path_, name_, columns_}, set(std::make_shared(SizeLimits())) { + Block header = getSampleBlock(); + header = header.sortColumns(); + set->setHeader(header); + restore(); } From 207a8cc03c5d9c9a9a32d9f49540036d703735ab Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 20 Apr 2018 00:36:58 +0300 Subject: [PATCH 14/19] Fixed error with Sets, part 2 #2246 --- dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp b/dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp index d6daab281c9..61a8b0be10a 100644 --- a/dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp +++ b/dbms/src/DataStreams/CreatingSetsBlockInputStream.cpp @@ -28,7 +28,9 @@ CreatingSetsBlockInputStream::CreatingSetsBlockInputStream( if (elem.second.source) { children.push_back(elem.second.source); - elem.second.set->setHeader(elem.second.source->getHeader()); + + if (elem.second.set) + elem.second.set->setHeader(elem.second.source->getHeader()); } } From 72f9f927ace86395a917d2b719ca376884356a81 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 20 Apr 2018 00:38:45 +0300 Subject: [PATCH 15/19] Added test result #2246 --- .../00623_in_partition_key.reference | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00623_in_partition_key.reference diff --git a/dbms/tests/queries/0_stateless/00623_in_partition_key.reference b/dbms/tests/queries/0_stateless/00623_in_partition_key.reference new file mode 100644 index 00000000000..8f1619079ec --- /dev/null +++ b/dbms/tests/queries/0_stateless/00623_in_partition_key.reference @@ -0,0 +1,59 @@ +111 +112 +113 +114 +- +121 +122 +123 +124 +- +131 +132 +133 +134 +--- +211 +212 +213 +214 +- +221 +222 +223 +224 +- +231 +232 +233 +234 +--- +311 +312 +313 +314 +- +321 +322 +323 +324 +- +331 +332 +333 +334 +--- +411 +412 +413 +414 +- +421 +422 +423 +424 +- +431 +432 +433 +434 From d71b3a95ef312df2d1bbd1060843345ce430fc1c Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 20 Apr 2018 03:20:36 +0300 Subject: [PATCH 16/19] Renamed PK to Key where it's appropriate #2246 --- dbms/src/Functions/FunctionsMiscellaneous.cpp | 2 +- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 4 +- dbms/src/Interpreters/Set.cpp | 12 +-- dbms/src/Interpreters/Set.h | 2 +- .../{PKCondition.cpp => KeyCondition.cpp} | 82 +++++++++---------- .../{PKCondition.h => KeyCondition.h} | 10 +-- dbms/src/Storages/MergeTree/MergeTreeData.h | 2 +- .../MergeTree/MergeTreeDataSelectExecutor.cpp | 12 +-- .../MergeTree/MergeTreeDataSelectExecutor.h | 6 +- .../MergeTree/MergeTreeWhereOptimizer.cpp | 6 +- 10 files changed, 69 insertions(+), 69 deletions(-) rename dbms/src/Storages/MergeTree/{PKCondition.cpp => KeyCondition.cpp} (93%) rename dbms/src/Storages/MergeTree/{PKCondition.h => KeyCondition.h} (98%) diff --git a/dbms/src/Functions/FunctionsMiscellaneous.cpp b/dbms/src/Functions/FunctionsMiscellaneous.cpp index f69dad39b52..4660f78bebf 100644 --- a/dbms/src/Functions/FunctionsMiscellaneous.cpp +++ b/dbms/src/Functions/FunctionsMiscellaneous.cpp @@ -813,7 +813,7 @@ public: /** The `indexHint` function takes any number of any arguments and always returns one. * - * This function has a special meaning (see ExpressionAnalyzer, PKCondition) + * This function has a special meaning (see ExpressionAnalyzer, KeyCondition) * - the expressions inside it are not evaluated; * - but when analyzing the index (selecting ranges for reading), this function is treated the same way, * as if instead of using it the expression itself would be. diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 5dad9c4f323..a56e7df74a7 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -2021,7 +2021,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries, } /// A special function `indexHint`. Everything that is inside it is not calculated - /// (and is used only for index analysis, see PKCondition). + /// (and is used only for index analysis, see KeyCondition). if (node->name == "indexHint") { actions_stack.addAction(ExpressionAction::addColumn(ColumnWithTypeAndName( @@ -2888,7 +2888,7 @@ void ExpressionAnalyzer::getRequiredSourceColumnsImpl(const ASTPtr & ast, } /// A special function `indexHint`. Everything that is inside it is not calculated - /// (and is used only for index analysis, see PKCondition). + /// (and is used only for index analysis, see KeyCondition). if (node->name == "indexHint") return; } diff --git a/dbms/src/Interpreters/Set.cpp b/dbms/src/Interpreters/Set.cpp index 931019739b0..0d9536da409 100644 --- a/dbms/src/Interpreters/Set.cpp +++ b/dbms/src/Interpreters/Set.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include namespace DB @@ -396,14 +396,14 @@ MergeTreeSetIndex::MergeTreeSetIndex(const SetElements & set_elements, std::vect std::sort(indexes_mapping.begin(), indexes_mapping.end(), [](const PKTuplePositionMapping & l, const PKTuplePositionMapping & r) { - return std::forward_as_tuple(l.pk_index, l.tuple_index) < std::forward_as_tuple(r.pk_index, r.tuple_index); + return std::forward_as_tuple(l.key_index, l.tuple_index) < std::forward_as_tuple(r.key_index, r.tuple_index); }); indexes_mapping.erase(std::unique( indexes_mapping.begin(), indexes_mapping.end(), [](const PKTuplePositionMapping & l, const PKTuplePositionMapping & r) { - return l.pk_index == r.pk_index; + return l.key_index == r.key_index; }), indexes_mapping.end()); for (size_t i = 0; i < set_elements.size(); ++i) @@ -435,10 +435,10 @@ BoolMask MergeTreeSetIndex::mayBeTrueInRange(const std::vector & key_rang for (size_t i = 0; i < indexes_mapping.size(); ++i) { - std::optional new_range = PKCondition::applyMonotonicFunctionsChainToRange( - key_ranges[indexes_mapping[i].pk_index], + std::optional new_range = KeyCondition::applyMonotonicFunctionsChainToRange( + key_ranges[indexes_mapping[i].key_index], indexes_mapping[i].functions, - data_types[indexes_mapping[i].pk_index]); + data_types[indexes_mapping[i].key_index]); if (!new_range) return {true, true}; diff --git a/dbms/src/Interpreters/Set.h b/dbms/src/Interpreters/Set.h index 620fe1ee3f7..4f40f3205de 100644 --- a/dbms/src/Interpreters/Set.h +++ b/dbms/src/Interpreters/Set.h @@ -170,7 +170,7 @@ public: struct PKTuplePositionMapping { size_t tuple_index; - size_t pk_index; + size_t key_index; std::vector functions; }; diff --git a/dbms/src/Storages/MergeTree/PKCondition.cpp b/dbms/src/Storages/MergeTree/KeyCondition.cpp similarity index 93% rename from dbms/src/Storages/MergeTree/PKCondition.cpp rename to dbms/src/Storages/MergeTree/KeyCondition.cpp index bc879e770ea..60b0699d4a0 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.cpp +++ b/dbms/src/Storages/MergeTree/KeyCondition.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -105,7 +105,7 @@ static String firstStringThatIsGreaterThanAllStringsWithPrefix(const String & pr /// A dictionary containing actions to the corresponding functions to turn them into `RPNElement` -const PKCondition::AtomMap PKCondition::atom_map +const KeyCondition::AtomMap KeyCondition::atom_map { { "notEquals", @@ -249,7 +249,7 @@ bool FieldWithInfinity::operator==(const FieldWithInfinity & other) const /** Calculate expressions, that depend only on constants. * For index to work when something like "WHERE Date = toDate(now())" is written. */ -Block PKCondition::getBlockWithConstants( +Block KeyCondition::getBlockWithConstants( const ASTPtr & query, const Context & context, const NamesAndTypesList & all_columns) { Block result @@ -265,19 +265,19 @@ Block PKCondition::getBlockWithConstants( } -PKCondition::PKCondition( +KeyCondition::KeyCondition( const SelectQueryInfo & query_info, const Context & context, const NamesAndTypesList & all_columns, const SortDescription & sort_descr_, - const ExpressionActionsPtr & pk_expr_) - : sort_descr(sort_descr_), pk_expr(pk_expr_), prepared_sets(query_info.sets) + const ExpressionActionsPtr & key_expr_) + : sort_descr(sort_descr_), key_expr(key_expr_), prepared_sets(query_info.sets) { for (size_t i = 0; i < sort_descr.size(); ++i) { std::string name = sort_descr[i].column_name; - if (!pk_columns.count(name)) - pk_columns[name] = i; + if (!key_columns.count(name)) + key_columns[name] = i; } /** Evaluation of expressions that depend only on constants. @@ -307,11 +307,11 @@ PKCondition::PKCondition( } } -bool PKCondition::addCondition(const String & column, const Range & range) +bool KeyCondition::addCondition(const String & column, const Range & range) { - if (!pk_columns.count(column)) + if (!key_columns.count(column)) return false; - rpn.emplace_back(RPNElement::FUNCTION_IN_RANGE, pk_columns[column], range); + rpn.emplace_back(RPNElement::FUNCTION_IN_RANGE, key_columns[column], range); rpn.emplace_back(RPNElement::FUNCTION_AND); return true; } @@ -368,7 +368,7 @@ static void applyFunction( } -void PKCondition::traverseAST(const ASTPtr & node, const Context & context, Block & block_with_constants) +void KeyCondition::traverseAST(const ASTPtr & node, const Context & context, Block & block_with_constants) { RPNElement element; @@ -401,7 +401,7 @@ void PKCondition::traverseAST(const ASTPtr & node, const Context & context, Bloc } -bool PKCondition::canConstantBeWrappedByMonotonicFunctions( +bool KeyCondition::canConstantBeWrappedByMonotonicFunctions( const ASTPtr & node, size_t & out_primary_key_column_num, DataTypePtr & out_primary_key_column_type, @@ -409,12 +409,12 @@ bool PKCondition::canConstantBeWrappedByMonotonicFunctions( DataTypePtr & out_type) { String expr_name = node->getColumnName(); - const auto & sample_block = pk_expr->getSampleBlock(); + const auto & sample_block = key_expr->getSampleBlock(); if (!sample_block.has(expr_name)) return false; bool found_transformation = false; - for (const ExpressionAction & a : pk_expr->getActions()) + for (const ExpressionAction & a : key_expr->getActions()) { /** The primary key functional expression constraint may be inferred from a plain column in the expression. * For example, if the primary key contains `toStartOfHour(Timestamp)` and query contains `WHERE Timestamp >= now()`, @@ -447,8 +447,8 @@ bool PKCondition::canConstantBeWrappedByMonotonicFunctions( expr_name = a.result_name; // Transformation results in a primary key expression, accept - auto it = pk_columns.find(expr_name); - if (pk_columns.end() != it) + auto it = key_columns.find(expr_name); + if (key_columns.end() != it) { out_primary_key_column_num = it->second; out_primary_key_column_type = sample_block.getByName(it->first).type; @@ -461,7 +461,7 @@ bool PKCondition::canConstantBeWrappedByMonotonicFunctions( return found_transformation; } -void PKCondition::getPKTuplePositionMapping( +void KeyCondition::getPKTuplePositionMapping( const ASTPtr & node, const Context & context, std::vector & indexes_mapping, @@ -472,20 +472,20 @@ void PKCondition::getPKTuplePositionMapping( index_mapping.tuple_index = tuple_index; DataTypePtr data_type; if (isPrimaryKeyPossiblyWrappedByMonotonicFunctions( - node, context, index_mapping.pk_index, + node, context, index_mapping.key_index, data_type, index_mapping.functions)) { indexes_mapping.push_back(index_mapping); - if (out_primary_key_column_num < index_mapping.pk_index) + if (out_primary_key_column_num < index_mapping.key_index) { - out_primary_key_column_num = index_mapping.pk_index; + out_primary_key_column_num = index_mapping.key_index; } } } /// Try to prepare PKTuplePositionMapping for tuples from IN expression. -bool PKCondition::isTupleIndexable( +bool KeyCondition::isTupleIndexable( const ASTPtr & node, const Context & context, RPNElement & out, @@ -530,7 +530,7 @@ bool PKCondition::isTupleIndexable( } -bool PKCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctions( +bool KeyCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctions( const ASTPtr & node, const Context & context, size_t & out_primary_key_column_num, @@ -561,7 +561,7 @@ bool PKCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctions( return true; } -bool PKCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl( +bool KeyCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl( const ASTPtr & node, size_t & out_primary_key_column_num, DataTypePtr & out_primary_key_column_type, @@ -570,11 +570,11 @@ bool PKCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl( /** By itself, the primary key column can be a functional expression. for example, `intHash32(UserID)`. * Therefore, use the full name of the expression for search. */ - const auto & sample_block = pk_expr->getSampleBlock(); + const auto & sample_block = key_expr->getSampleBlock(); String name = node->getColumnName(); - auto it = pk_columns.find(name); - if (pk_columns.end() != it) + auto it = key_columns.find(name); + if (key_columns.end() != it) { out_primary_key_column_num = it->second; out_primary_key_column_type = sample_block.getByName(it->first).type; @@ -620,7 +620,7 @@ static void castValueToType(const DataTypePtr & desired_type, Field & src_value, } -bool PKCondition::atomFromAST(const ASTPtr & node, const Context & context, Block & block_with_constants, RPNElement & out) +bool KeyCondition::atomFromAST(const ASTPtr & node, const Context & context, Block & block_with_constants, RPNElement & out) { /** Functions < > = != <= >= in `notIn`, where one argument is a constant, and the other is one of columns of primary key, * or itself, wrapped in a chain of possibly-monotonic functions, @@ -736,7 +736,7 @@ bool PKCondition::atomFromAST(const ASTPtr & node, const Context & context, Bloc return false; } -bool PKCondition::operatorFromAST(const ASTFunction * func, RPNElement & out) +bool KeyCondition::operatorFromAST(const ASTFunction * func, RPNElement & out) { /// Functions AND, OR, NOT. /** Also a special function `indexHint` - works as if instead of calling a function there are just parentheses @@ -764,7 +764,7 @@ bool PKCondition::operatorFromAST(const ASTFunction * func, RPNElement & out) return true; } -String PKCondition::toString() const +String KeyCondition::toString() const { String res; for (size_t i = 0; i < rpn.size(); ++i) @@ -896,7 +896,7 @@ static bool forAnyParallelogram( } -bool PKCondition::mayBeTrueInRange( +bool KeyCondition::mayBeTrueInRange( size_t used_key_size, const Field * left_pk, const Field * right_pk, @@ -933,7 +933,7 @@ bool PKCondition::mayBeTrueInRange( }); } -std::optional PKCondition::applyMonotonicFunctionsChainToRange( +std::optional KeyCondition::applyMonotonicFunctionsChainToRange( Range key_range, RPNElement::MonotonicFunctionsChain & functions, DataTypePtr current_type @@ -970,7 +970,7 @@ std::optional PKCondition::applyMonotonicFunctionsChainToRange( return key_range; } -bool PKCondition::mayBeTrueInRangeImpl(const std::vector & key_ranges, const DataTypes & data_types) const +bool KeyCondition::mayBeTrueInRangeImpl(const std::vector & key_ranges, const DataTypes & data_types) const { std::vector rpn_stack; for (size_t i = 0; i < rpn.size(); ++i) @@ -1054,30 +1054,30 @@ bool PKCondition::mayBeTrueInRangeImpl(const std::vector & key_ranges, co rpn_stack.emplace_back(true, false); } else - throw Exception("Unexpected function type in PKCondition::RPNElement", ErrorCodes::LOGICAL_ERROR); + throw Exception("Unexpected function type in KeyCondition::RPNElement", ErrorCodes::LOGICAL_ERROR); } if (rpn_stack.size() != 1) - throw Exception("Unexpected stack size in PKCondition::mayBeTrueInRange", ErrorCodes::LOGICAL_ERROR); + throw Exception("Unexpected stack size in KeyCondition::mayBeTrueInRange", ErrorCodes::LOGICAL_ERROR); return rpn_stack[0].can_be_true; } -bool PKCondition::mayBeTrueInRange( +bool KeyCondition::mayBeTrueInRange( size_t used_key_size, const Field * left_pk, const Field * right_pk, const DataTypes & data_types) const { return mayBeTrueInRange(used_key_size, left_pk, right_pk, data_types, true); } -bool PKCondition::mayBeTrueAfter( +bool KeyCondition::mayBeTrueAfter( size_t used_key_size, const Field * left_pk, const DataTypes & data_types) const { return mayBeTrueInRange(used_key_size, left_pk, nullptr, data_types, false); } -String PKCondition::RPNElement::toString() const +String KeyCondition::RPNElement::toString() const { auto print_wrapped_column = [this](std::ostringstream & ss) { @@ -1129,7 +1129,7 @@ String PKCondition::RPNElement::toString() const } -bool PKCondition::alwaysUnknownOrTrue() const +bool KeyCondition::alwaysUnknownOrTrue() const { std::vector rpn_stack; @@ -1166,14 +1166,14 @@ bool PKCondition::alwaysUnknownOrTrue() const rpn_stack.back() = arg1 | arg2; } else - throw Exception("Unexpected function type in PKCondition::RPNElement", ErrorCodes::LOGICAL_ERROR); + throw Exception("Unexpected function type in KeyCondition::RPNElement", ErrorCodes::LOGICAL_ERROR); } return rpn_stack[0]; } -size_t PKCondition::getMaxKeyColumn() const +size_t KeyCondition::getMaxKeyColumn() const { size_t res = 0; for (const auto & element : rpn) diff --git a/dbms/src/Storages/MergeTree/PKCondition.h b/dbms/src/Storages/MergeTree/KeyCondition.h similarity index 98% rename from dbms/src/Storages/MergeTree/PKCondition.h rename to dbms/src/Storages/MergeTree/KeyCondition.h index 17f54745b39..d82b0715919 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.h +++ b/dbms/src/Storages/MergeTree/KeyCondition.h @@ -224,16 +224,16 @@ private: * Constructs a reverse polish notation from these conditions * and can calculate (interpret) its satisfiability over key ranges. */ -class PKCondition +class KeyCondition { public: /// Does not take into account the SAMPLE section. all_columns - the set of all columns of the table. - PKCondition( + KeyCondition( const SelectQueryInfo & query_info, const Context & context, const NamesAndTypesList & all_columns, const SortDescription & sort_descr, - const ExpressionActionsPtr & pk_expr); + const ExpressionActionsPtr & key_expr); /// Whether the condition is feasible in the key range. /// left_pk and right_pk must contain all fields in the sort_descr in the appropriate order. @@ -374,8 +374,8 @@ private: RPN rpn; SortDescription sort_descr; - ColumnIndices pk_columns; - ExpressionActionsPtr pk_expr; + ColumnIndices key_columns; + ExpressionActionsPtr key_expr; PreparedSets prepared_sets; }; diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.h b/dbms/src/Storages/MergeTree/MergeTreeData.h index 2c2ea67dc85..004bd8f9354 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.h +++ b/dbms/src/Storages/MergeTree/MergeTreeData.h @@ -502,7 +502,7 @@ public: Names minmax_idx_columns; DataTypes minmax_idx_column_types; Int64 minmax_idx_date_column_pos = -1; /// In a common case minmax index includes a date column. - SortDescription minmax_idx_sort_descr; /// For use with PKCondition. + SortDescription minmax_idx_sort_descr; /// For use with KeyCondition. /// Limiting parallel sends per one table, used in DataPartsExchange std::atomic_uint current_table_sends {0}; diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index d8cfac7eb9e..333f9c7cc60 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -85,7 +85,7 @@ static Block getBlockWithPartColumn(const MergeTreeData::DataPartsVector & parts size_t MergeTreeDataSelectExecutor::getApproximateTotalRowsToRead( - const MergeTreeData::DataPartsVector & parts, const PKCondition & key_condition, const Settings & settings) const + const MergeTreeData::DataPartsVector & parts, const KeyCondition & key_condition, const Settings & settings) const { size_t full_marks_count = 0; @@ -198,7 +198,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( const Settings & settings = context.getSettingsRef(); SortDescription sort_descr = data.getPrimarySortDescription(); - PKCondition key_condition(query_info, context, available_real_and_virtual_columns, sort_descr, + KeyCondition key_condition(query_info, context, available_real_and_virtual_columns, sort_descr, data.getPrimaryExpression()); if (settings.force_primary_key && key_condition.alwaysUnknownOrTrue()) @@ -212,7 +212,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( throw Exception(exception_message.str(), ErrorCodes::INDEX_NOT_USED); } - std::optional minmax_idx_condition; + std::optional minmax_idx_condition; if (data.minmax_idx_expr) { minmax_idx_condition.emplace( @@ -843,7 +843,7 @@ void MergeTreeDataSelectExecutor::createPositiveSignCondition( /// Calculates a set of mark ranges, that could possibly contain keys, required by condition. /// In other words, it removes subranges from whole range, that definitely could not contain required keys. MarkRanges MergeTreeDataSelectExecutor::markRangesFromPKRange( - const MergeTreeData::DataPart::Index & index, const PKCondition & key_condition, const Settings & settings) const + const MergeTreeData::DataPart::Index & index, const KeyCondition & key_condition, const Settings & settings) const { size_t min_marks_for_seek = (settings.merge_tree_min_rows_for_seek + data.index_granularity - 1) / data.index_granularity; @@ -866,7 +866,7 @@ MarkRanges MergeTreeDataSelectExecutor::markRangesFromPKRange( */ std::vector ranges_stack{ {0, marks_count} }; - /// NOTE Creating temporary Field objects to pass to PKCondition. + /// NOTE Creating temporary Field objects to pass to KeyCondition. Row index_left(used_key_size); Row index_right(used_key_size); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h index 7bb5d8d8dfc..e40baa9c6da 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h +++ b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h @@ -9,7 +9,7 @@ namespace DB { -class PKCondition; +class KeyCondition; /** Executes SELECT queries on data from the merge tree. @@ -60,7 +60,7 @@ private: /// Get the approximate value (bottom estimate - only by full marks) of the number of rows falling under the index. size_t getApproximateTotalRowsToRead( const MergeTreeData::DataPartsVector & parts, - const PKCondition & key_condition, + const KeyCondition & key_condition, const Settings & settings) const; /// Create the expression "Sign == 1". @@ -71,7 +71,7 @@ private: MarkRanges markRangesFromPKRange( const MergeTreeData::DataPart::Index & index, - const PKCondition & key_condition, + const KeyCondition & key_condition, const Settings & settings) const; }; diff --git a/dbms/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp b/dbms/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp index 077ea9fed5d..3b35c127511 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -43,7 +43,7 @@ MergeTreeWhereOptimizer::MergeTreeWhereOptimizer( [] (const SortColumnDescription & col) { return col.column_name; })}, table_columns{ext::map(data.getColumns().getAllPhysical(), [] (const NameAndTypePair & col) { return col.name; })}, - block_with_constants{PKCondition::getBlockWithConstants(query_info.query, context, data.getColumns().getAllPhysical())}, + block_with_constants{KeyCondition::getBlockWithConstants(query_info.query, context, data.getColumns().getAllPhysical())}, prepared_sets(query_info.sets), log{log} { @@ -321,7 +321,7 @@ bool MergeTreeWhereOptimizer::isPrimaryKeyAtom(const IAST * const ast) const { if (const auto func = typeid_cast(ast)) { - if (!PKCondition::atom_map.count(func->name)) + if (!KeyCondition::atom_map.count(func->name)) return false; const auto & args = func->arguments->children; From 70be882b6484f00f91d73f131baae9e4cfe4f4d0 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 20 Apr 2018 03:27:25 +0300 Subject: [PATCH 17/19] Renamed PK to Key where it's appropriate #2246 --- dbms/src/Interpreters/Set.cpp | 6 +- dbms/src/Interpreters/Set.h | 6 +- dbms/src/Storages/MergeTree/KeyCondition.cpp | 113 +++++++++---------- dbms/src/Storages/MergeTree/KeyCondition.h | 50 ++++---- 4 files changed, 87 insertions(+), 88 deletions(-) diff --git a/dbms/src/Interpreters/Set.cpp b/dbms/src/Interpreters/Set.cpp index 0d9536da409..925479e05e1 100644 --- a/dbms/src/Interpreters/Set.cpp +++ b/dbms/src/Interpreters/Set.cpp @@ -389,19 +389,19 @@ void Set::executeOrdinary( } -MergeTreeSetIndex::MergeTreeSetIndex(const SetElements & set_elements, std::vector && index_mapping_) +MergeTreeSetIndex::MergeTreeSetIndex(const SetElements & set_elements, std::vector && index_mapping_) : ordered_set(), indexes_mapping(std::move(index_mapping_)) { std::sort(indexes_mapping.begin(), indexes_mapping.end(), - [](const PKTuplePositionMapping & l, const PKTuplePositionMapping & r) + [](const KeyTuplePositionMapping & l, const KeyTuplePositionMapping & r) { return std::forward_as_tuple(l.key_index, l.tuple_index) < std::forward_as_tuple(r.key_index, r.tuple_index); }); indexes_mapping.erase(std::unique( indexes_mapping.begin(), indexes_mapping.end(), - [](const PKTuplePositionMapping & l, const PKTuplePositionMapping & r) + [](const KeyTuplePositionMapping & l, const KeyTuplePositionMapping & r) { return l.key_index == r.key_index; }), indexes_mapping.end()); diff --git a/dbms/src/Interpreters/Set.h b/dbms/src/Interpreters/Set.h index 4f40f3205de..1ef8d95f775 100644 --- a/dbms/src/Interpreters/Set.h +++ b/dbms/src/Interpreters/Set.h @@ -167,21 +167,21 @@ public: * position of pk index and data type of this pk column * and functions chain applied to this column. */ - struct PKTuplePositionMapping + struct KeyTuplePositionMapping { size_t tuple_index; size_t key_index; std::vector functions; }; - MergeTreeSetIndex(const SetElements & set_elements, std::vector && indexes_mapping_); + MergeTreeSetIndex(const SetElements & set_elements, std::vector && indexes_mapping_); BoolMask mayBeTrueInRange(const std::vector & key_ranges, const DataTypes & data_types); private: using OrderedTuples = std::vector>; OrderedTuples ordered_set; - std::vector indexes_mapping; + std::vector indexes_mapping; }; } diff --git a/dbms/src/Storages/MergeTree/KeyCondition.cpp b/dbms/src/Storages/MergeTree/KeyCondition.cpp index 60b0699d4a0..3847ae6f285 100644 --- a/dbms/src/Storages/MergeTree/KeyCondition.cpp +++ b/dbms/src/Storages/MergeTree/KeyCondition.cpp @@ -403,8 +403,8 @@ void KeyCondition::traverseAST(const ASTPtr & node, const Context & context, Blo bool KeyCondition::canConstantBeWrappedByMonotonicFunctions( const ASTPtr & node, - size_t & out_primary_key_column_num, - DataTypePtr & out_primary_key_column_type, + size_t & out_key_column_num, + DataTypePtr & out_key_column_type, Field & out_value, DataTypePtr & out_type) { @@ -416,8 +416,8 @@ bool KeyCondition::canConstantBeWrappedByMonotonicFunctions( bool found_transformation = false; for (const ExpressionAction & a : key_expr->getActions()) { - /** The primary key functional expression constraint may be inferred from a plain column in the expression. - * For example, if the primary key contains `toStartOfHour(Timestamp)` and query contains `WHERE Timestamp >= now()`, + /** The key functional expression constraint may be inferred from a plain column in the expression. + * For example, if the key contains `toStartOfHour(Timestamp)` and query contains `WHERE Timestamp >= now()`, * it can be assumed that if `toStartOfHour()` is monotonic on [now(), inf), the `toStartOfHour(Timestamp) >= toStartOfHour(now())` * condition also holds, so the index may be used to select only parts satisfying this condition. * @@ -446,12 +446,12 @@ bool KeyCondition::canConstantBeWrappedByMonotonicFunctions( out_type.swap(new_type); expr_name = a.result_name; - // Transformation results in a primary key expression, accept + // Transformation results in a key expression, accept auto it = key_columns.find(expr_name); if (key_columns.end() != it) { - out_primary_key_column_num = it->second; - out_primary_key_column_type = sample_block.getByName(it->first).type; + out_key_column_num = it->second; + out_key_column_type = sample_block.getByName(it->first).type; found_transformation = true; break; } @@ -461,39 +461,39 @@ bool KeyCondition::canConstantBeWrappedByMonotonicFunctions( return found_transformation; } -void KeyCondition::getPKTuplePositionMapping( +void KeyCondition::getKeyTuplePositionMapping( const ASTPtr & node, const Context & context, - std::vector & indexes_mapping, + std::vector & indexes_mapping, const size_t tuple_index, - size_t & out_primary_key_column_num) + size_t & out_key_column_num) { - MergeTreeSetIndex::PKTuplePositionMapping index_mapping; + MergeTreeSetIndex::KeyTuplePositionMapping index_mapping; index_mapping.tuple_index = tuple_index; DataTypePtr data_type; - if (isPrimaryKeyPossiblyWrappedByMonotonicFunctions( + if (isKeyPossiblyWrappedByMonotonicFunctions( node, context, index_mapping.key_index, data_type, index_mapping.functions)) { indexes_mapping.push_back(index_mapping); - if (out_primary_key_column_num < index_mapping.key_index) + if (out_key_column_num < index_mapping.key_index) { - out_primary_key_column_num = index_mapping.key_index; + out_key_column_num = index_mapping.key_index; } } } -/// Try to prepare PKTuplePositionMapping for tuples from IN expression. +/// Try to prepare KeyTuplePositionMapping for tuples from IN expression. bool KeyCondition::isTupleIndexable( const ASTPtr & node, const Context & context, RPNElement & out, const SetPtr & prepared_set, - size_t & out_primary_key_column_num) + size_t & out_key_column_num) { - out_primary_key_column_num = 0; - std::vector indexes_mapping; + out_key_column_num = 0; + std::vector indexes_mapping; size_t num_key_columns = prepared_set->getDataTypes().size(); @@ -511,13 +511,13 @@ bool KeyCondition::isTupleIndexable( size_t current_tuple_index = 0; for (const auto & arg : node_tuple->arguments->children) { - getPKTuplePositionMapping(arg, context, indexes_mapping, current_tuple_index, out_primary_key_column_num); + getKeyTuplePositionMapping(arg, context, indexes_mapping, current_tuple_index, out_key_column_num); ++current_tuple_index; } } else { - getPKTuplePositionMapping(node, context, indexes_mapping, 0, out_primary_key_column_num); + getKeyTuplePositionMapping(node, context, indexes_mapping, 0, out_key_column_num); } if (indexes_mapping.empty()) @@ -530,44 +530,44 @@ bool KeyCondition::isTupleIndexable( } -bool KeyCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctions( +bool KeyCondition::isKeyPossiblyWrappedByMonotonicFunctions( const ASTPtr & node, const Context & context, - size_t & out_primary_key_column_num, - DataTypePtr & out_primary_key_res_column_type, + size_t & out_key_column_num, + DataTypePtr & out_key_res_column_type, RPNElement::MonotonicFunctionsChain & out_functions_chain) { std::vector chain_not_tested_for_monotonicity; - DataTypePtr primary_key_column_type; + DataTypePtr key_column_type; - if (!isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl(node, out_primary_key_column_num, primary_key_column_type, chain_not_tested_for_monotonicity)) + if (!isKeyPossiblyWrappedByMonotonicFunctionsImpl(node, out_key_column_num, key_column_type, chain_not_tested_for_monotonicity)) return false; for (auto it = chain_not_tested_for_monotonicity.rbegin(); it != chain_not_tested_for_monotonicity.rend(); ++it) { auto func_builder = FunctionFactory::instance().tryGet((*it)->name, context); - ColumnsWithTypeAndName arguments{{ nullptr, primary_key_column_type, "" }}; + ColumnsWithTypeAndName arguments{{ nullptr, key_column_type, "" }}; auto func = func_builder->build(arguments); if (!func || !func->hasInformationAboutMonotonicity()) return false; - primary_key_column_type = func->getReturnType(); + key_column_type = func->getReturnType(); out_functions_chain.push_back(func); } - out_primary_key_res_column_type = primary_key_column_type; + out_key_res_column_type = key_column_type; return true; } -bool KeyCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl( +bool KeyCondition::isKeyPossiblyWrappedByMonotonicFunctionsImpl( const ASTPtr & node, - size_t & out_primary_key_column_num, - DataTypePtr & out_primary_key_column_type, + size_t & out_key_column_num, + DataTypePtr & out_key_column_type, std::vector & out_functions_chain) { - /** By itself, the primary key column can be a functional expression. for example, `intHash32(UserID)`. + /** By itself, the key column can be a functional expression. for example, `intHash32(UserID)`. * Therefore, use the full name of the expression for search. */ const auto & sample_block = key_expr->getSampleBlock(); @@ -576,8 +576,8 @@ bool KeyCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl( auto it = key_columns.find(name); if (key_columns.end() != it) { - out_primary_key_column_num = it->second; - out_primary_key_column_type = sample_block.getByName(it->first).type; + out_key_column_num = it->second; + out_key_column_type = sample_block.getByName(it->first).type; return true; } @@ -589,8 +589,7 @@ bool KeyCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl( out_functions_chain.push_back(func); - if (!isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl(args[0], out_primary_key_column_num, out_primary_key_column_type, - out_functions_chain)) + if (!isKeyPossiblyWrappedByMonotonicFunctionsImpl(args[0], out_key_column_num, out_key_column_type, out_functions_chain)) return false; return true; @@ -612,7 +611,7 @@ static void castValueToType(const DataTypePtr & desired_type, Field & src_value, } catch (...) { - throw Exception("Primary key expression contains comparison between inconvertible types: " + + throw Exception("Key expression contains comparison between inconvertible types: " + desired_type->getName() + " and " + src_type->getName() + " inside " + queryToString(node), ErrorCodes::BAD_TYPE_OF_FIELD); @@ -622,7 +621,7 @@ static void castValueToType(const DataTypePtr & desired_type, Field & src_value, bool KeyCondition::atomFromAST(const ASTPtr & node, const Context & context, Block & block_with_constants, RPNElement & out) { - /** Functions < > = != <= >= in `notIn`, where one argument is a constant, and the other is one of columns of primary key, + /** Functions < > = != <= >= in `notIn`, where one argument is a constant, and the other is one of columns of key, * or itself, wrapped in a chain of possibly-monotonic functions, * or constant expression - number. */ @@ -635,9 +634,9 @@ bool KeyCondition::atomFromAST(const ASTPtr & node, const Context & context, Blo if (args.size() != 2) return false; - DataTypePtr key_expr_type; /// Type of expression containing primary key column - size_t key_arg_pos; /// Position of argument with primary key column (non-const argument) - size_t key_column_num; /// Number of a primary key column (inside sort_descr array) + DataTypePtr key_expr_type; /// Type of expression containing key column + size_t key_arg_pos; /// Position of argument with key column (non-const argument) + size_t key_column_num; /// Number of a key column (inside sort_descr array) RPNElement::MonotonicFunctionsChain chain; bool is_set_const = false; bool is_constant_transformed = false; @@ -649,7 +648,7 @@ bool KeyCondition::atomFromAST(const ASTPtr & node, const Context & context, Blo is_set_const = true; } else if (getConstant(args[1], block_with_constants, const_value, const_type) - && isPrimaryKeyPossiblyWrappedByMonotonicFunctions(args[0], context, key_column_num, key_expr_type, chain)) + && isKeyPossiblyWrappedByMonotonicFunctions(args[0], context, key_column_num, key_expr_type, chain)) { key_arg_pos = 0; } @@ -660,7 +659,7 @@ bool KeyCondition::atomFromAST(const ASTPtr & node, const Context & context, Blo is_constant_transformed = true; } else if (getConstant(args[0], block_with_constants, const_value, const_type) - && isPrimaryKeyPossiblyWrappedByMonotonicFunctions(args[1], context, key_column_num, key_expr_type, chain)) + && isKeyPossiblyWrappedByMonotonicFunctions(args[1], context, key_column_num, key_expr_type, chain)) { key_arg_pos = 1; } @@ -777,16 +776,16 @@ String KeyCondition::toString() const } -/** Index is the value of primary key every `index_granularity` rows. +/** Index is the value of key every `index_granularity` rows. * This value is called a "mark". That is, the index consists of marks. * - * The primary key is the tuple. - * The data is sorted by primary key in the sense of lexicographic order over tuples. + * The key is the tuple. + * The data is sorted by key in the sense of lexicographic order over tuples. * * A pair of marks specifies a segment with respect to the order over the tuples. * Denote it like this: [ x1 y1 z1 .. x2 y2 z2 ], - * where x1 y1 z1 - tuple - value of primary key in left border of segment; - * x2 y2 z2 - tuple - value of primary key in right boundary of segment. + * where x1 y1 z1 - tuple - value of key in left border of segment; + * x2 y2 z2 - tuple - value of key in right boundary of segment. * In this section there are data between these marks. * * Or, the last mark specifies the range open on the right: [ a b c .. + inf ) @@ -898,8 +897,8 @@ static bool forAnyParallelogram( bool KeyCondition::mayBeTrueInRange( size_t used_key_size, - const Field * left_pk, - const Field * right_pk, + const Field * left_key, + const Field * right_key, const DataTypes & data_types, bool right_bounded) const { @@ -907,19 +906,19 @@ bool KeyCondition::mayBeTrueInRange( /* std::cerr << "Checking for: ["; for (size_t i = 0; i != used_key_size; ++i) - std::cerr << (i != 0 ? ", " : "") << applyVisitor(FieldVisitorToString(), left_pk[i]); + std::cerr << (i != 0 ? ", " : "") << applyVisitor(FieldVisitorToString(), left_key[i]); std::cerr << " ... "; if (right_bounded) { for (size_t i = 0; i != used_key_size; ++i) - std::cerr << (i != 0 ? ", " : "") << applyVisitor(FieldVisitorToString(), right_pk[i]); + std::cerr << (i != 0 ? ", " : "") << applyVisitor(FieldVisitorToString(), right_key[i]); std::cerr << "]\n"; } else std::cerr << "+inf)\n";*/ - return forAnyParallelogram(used_key_size, left_pk, right_pk, true, right_bounded, key_ranges, 0, + return forAnyParallelogram(used_key_size, left_key, right_key, true, right_bounded, key_ranges, 0, [&] (const std::vector & key_ranges) { auto res = mayBeTrueInRangeImpl(key_ranges, data_types); @@ -1065,15 +1064,15 @@ bool KeyCondition::mayBeTrueInRangeImpl(const std::vector & key_ranges, c bool KeyCondition::mayBeTrueInRange( - size_t used_key_size, const Field * left_pk, const Field * right_pk, const DataTypes & data_types) const + size_t used_key_size, const Field * left_key, const Field * right_key, const DataTypes & data_types) const { - return mayBeTrueInRange(used_key_size, left_pk, right_pk, data_types, true); + return mayBeTrueInRange(used_key_size, left_key, right_key, data_types, true); } bool KeyCondition::mayBeTrueAfter( - size_t used_key_size, const Field * left_pk, const DataTypes & data_types) const + size_t used_key_size, const Field * left_key, const DataTypes & data_types) const { - return mayBeTrueInRange(used_key_size, left_pk, nullptr, data_types, false); + return mayBeTrueInRange(used_key_size, left_key, nullptr, data_types, false); } diff --git a/dbms/src/Storages/MergeTree/KeyCondition.h b/dbms/src/Storages/MergeTree/KeyCondition.h index d82b0715919..c7d55b0a575 100644 --- a/dbms/src/Storages/MergeTree/KeyCondition.h +++ b/dbms/src/Storages/MergeTree/KeyCondition.h @@ -236,22 +236,22 @@ public: const ExpressionActionsPtr & key_expr); /// Whether the condition is feasible in the key range. - /// left_pk and right_pk must contain all fields in the sort_descr in the appropriate order. - /// data_types - the types of the primary key columns. - bool mayBeTrueInRange(size_t used_key_size, const Field * left_pk, const Field * right_pk, const DataTypes & data_types) const; + /// left_key and right_key must contain all fields in the sort_descr in the appropriate order. + /// data_types - the types of the key columns. + bool mayBeTrueInRange(size_t used_key_size, const Field * left_key, const Field * right_key, const DataTypes & data_types) const; /// Is the condition valid in a semi-infinite (not limited to the right) key range. - /// left_pk must contain all the fields in the sort_descr in the appropriate order. - bool mayBeTrueAfter(size_t used_key_size, const Field * left_pk, const DataTypes & data_types) const; + /// left_key must contain all the fields in the sort_descr in the appropriate order. + bool mayBeTrueAfter(size_t used_key_size, const Field * left_key, const DataTypes & data_types) const; /// Checks that the index can not be used. bool alwaysUnknownOrTrue() const; - /// Get the maximum number of the primary key element used in the condition. + /// Get the maximum number of the key element used in the condition. size_t getMaxKeyColumn() const; /// Impose an additional condition: the value in the column column must be in the `range` range. - /// Returns whether there is such a column in the primary key. + /// Returns whether there is such a column in the key. bool addCondition(const String & column, const Range & range); String toString() const; @@ -296,7 +296,7 @@ public: MergeTreeSetIndexPtr set_index; /** A chain of possibly monotone functions. - * If the primary key column is wrapped in functions that can be monotonous in some value ranges + * If the key column is wrapped in functions that can be monotonous in some value ranges * (for example: -toFloat64(toDayOfWeek(date))), then here the functions will be located: toDayOfWeek, toFloat64, negate. */ using MonotonicFunctionsChain = std::vector; @@ -320,8 +320,8 @@ private: bool mayBeTrueInRange( size_t used_key_size, - const Field * left_pk, - const Field * right_pk, + const Field * left_key, + const Field * right_key, const DataTypes & data_types, bool right_bounded) const; @@ -331,45 +331,45 @@ private: bool atomFromAST(const ASTPtr & node, const Context & context, Block & block_with_constants, RPNElement & out); bool operatorFromAST(const ASTFunction * func, RPNElement & out); - /** Is node the primary key column - * or expression in which column of primary key is wrapped by chain of functions, + /** Is node the key column + * or expression in which column of key is wrapped by chain of functions, * that can be monotomic on certain ranges? - * If these conditions are true, then returns number of column in primary key, type of resulting expression + * If these conditions are true, then returns number of column in key, type of resulting expression * and fills chain of possibly-monotonic functions. */ - bool isPrimaryKeyPossiblyWrappedByMonotonicFunctions( + bool isKeyPossiblyWrappedByMonotonicFunctions( const ASTPtr & node, const Context & context, - size_t & out_primary_key_column_num, - DataTypePtr & out_primary_key_res_column_type, + size_t & out_key_column_num, + DataTypePtr & out_key_res_column_type, RPNElement::MonotonicFunctionsChain & out_functions_chain); - bool isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl( + bool isKeyPossiblyWrappedByMonotonicFunctionsImpl( const ASTPtr & node, - size_t & out_primary_key_column_num, - DataTypePtr & out_primary_key_column_type, + size_t & out_key_column_num, + DataTypePtr & out_key_column_type, std::vector & out_functions_chain); bool canConstantBeWrappedByMonotonicFunctions( const ASTPtr & node, - size_t & out_primary_key_column_num, - DataTypePtr & out_primary_key_column_type, + size_t & out_key_column_num, + DataTypePtr & out_key_column_type, Field & out_value, DataTypePtr & out_type); - void getPKTuplePositionMapping( + void getKeyTuplePositionMapping( const ASTPtr & node, const Context & context, - std::vector & indexes_mapping, + std::vector & indexes_mapping, const size_t tuple_index, - size_t & out_primary_key_column_num); + size_t & out_key_column_num); bool isTupleIndexable( const ASTPtr & node, const Context & context, RPNElement & out, const SetPtr & prepared_set, - size_t & out_primary_key_column_num); + size_t & out_key_column_num); RPN rpn; From 6b88a2a7a5ed69d0e2a1fad30a80adb8bdad9b24 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 20 Apr 2018 04:14:04 +0300 Subject: [PATCH 18/19] Better info in log #2246 --- dbms/src/Interpreters/InterpreterFactory.cpp | 9 ++++++++- dbms/src/Interpreters/Set.h | 3 +++ dbms/src/Storages/MergeTree/KeyCondition.cpp | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dbms/src/Interpreters/InterpreterFactory.cpp b/dbms/src/Interpreters/InterpreterFactory.cpp index 2e43efbb12e..9fff1356a55 100644 --- a/dbms/src/Interpreters/InterpreterFactory.cpp +++ b/dbms/src/Interpreters/InterpreterFactory.cpp @@ -50,7 +50,14 @@ namespace ErrorCodes static void throwIfReadOnly(Context & context) { if (context.getSettingsRef().readonly) - throw Exception("Cannot execute query in readonly mode", ErrorCodes::READONLY); + { + const auto & client_info = context.getClientInfo(); + if (client_info.interface == ClientInfo::Interface::HTTP && client_info.http_method == ClientInfo::HTTPMethod::GET) + throw Exception("Cannot execute query in readonly mode. " + "For queries over HTTP, method GET implies readonly. You should use method POST for modifying queries.", ErrorCodes::READONLY); + else + throw Exception("Cannot execute query in readonly mode", ErrorCodes::READONLY); + } } diff --git a/dbms/src/Interpreters/Set.h b/dbms/src/Interpreters/Set.h index 1ef8d95f775..e27bdf58ec6 100644 --- a/dbms/src/Interpreters/Set.h +++ b/dbms/src/Interpreters/Set.h @@ -176,7 +176,10 @@ public: MergeTreeSetIndex(const SetElements & set_elements, std::vector && indexes_mapping_); + size_t size() const { return ordered_set.size(); } + BoolMask mayBeTrueInRange(const std::vector & key_ranges, const DataTypes & data_types); + private: using OrderedTuples = std::vector>; OrderedTuples ordered_set; diff --git a/dbms/src/Storages/MergeTree/KeyCondition.cpp b/dbms/src/Storages/MergeTree/KeyCondition.cpp index 3847ae6f285..ccdf468ff13 100644 --- a/dbms/src/Storages/MergeTree/KeyCondition.cpp +++ b/dbms/src/Storages/MergeTree/KeyCondition.cpp @@ -1105,7 +1105,8 @@ String KeyCondition::RPNElement::toString() const { ss << "("; print_wrapped_column(ss); - ss << (function == FUNCTION_IN_SET ? " in set" : " notIn set"); + ss << (function == FUNCTION_IN_SET ? " in " : " notIn "); + ss << set_index->size() << "-element set"; ss << ")"; return ss.str(); } From c94b0a196061738326770a8b9063dadc21a39f15 Mon Sep 17 00:00:00 2001 From: Ivan He Date: Fri, 20 Apr 2018 08:54:50 +0000 Subject: [PATCH 19/19] fix typo of struct name --- dbms/src/Parsers/ASTShowProcesslistQuery.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/src/Parsers/ASTShowProcesslistQuery.h b/dbms/src/Parsers/ASTShowProcesslistQuery.h index d51fb509a56..2bf67c1951c 100644 --- a/dbms/src/Parsers/ASTShowProcesslistQuery.h +++ b/dbms/src/Parsers/ASTShowProcesslistQuery.h @@ -6,12 +6,12 @@ namespace DB { -struct ASTShowProcesslisIDAndQueryNames +struct ASTShowProcesslistIDAndQueryNames { static constexpr auto ID = "ShowProcesslistQuery"; static constexpr auto Query = "SHOW PROCESSLIST"; }; -using ASTShowProcesslistQuery = ASTQueryWithOutputImpl; +using ASTShowProcesslistQuery = ASTQueryWithOutputImpl; }