mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-19 14:11:58 +00:00
Merge branch 'feature_multiple_disks' of https://github.com/ObjatieGroba/ClickHouse into feature_multiple_disks
This commit is contained in:
commit
eac8c8c0a4
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -79,3 +79,6 @@
|
||||
[submodule "contrib/hyperscan"]
|
||||
path = contrib/hyperscan
|
||||
url = https://github.com/ClickHouse-Extras/hyperscan.git
|
||||
[submodule "contrib/simdjson"]
|
||||
path = contrib/simdjson
|
||||
url = https://github.com/lemire/simdjson.git
|
||||
|
50
CHANGELOG.md
50
CHANGELOG.md
@ -1,3 +1,8 @@
|
||||
## ClickHouse release 19.5.3.8, 2019-04-18
|
||||
|
||||
### Bug fixes
|
||||
* Fixed type of setting `max_partitions_per_insert_block` from boolean to UInt64. [#5028](https://github.com/yandex/ClickHouse/pull/5028) ([Mohammad Hossein Sekhavat](https://github.com/mhsekhavat))
|
||||
|
||||
## ClickHouse release 19.5.2.6, 2019-04-15
|
||||
|
||||
### New Features
|
||||
@ -24,6 +29,7 @@
|
||||
* Fill `system.graphite_detentions` from a table config of `*GraphiteMergeTree` engine tables. [#4584](https://github.com/yandex/ClickHouse/pull/4584) ([Mikhail f. Shiryaev](https://github.com/Felixoid))
|
||||
* Rename `trigramDistance` function to `ngramDistance` and add more functions with `CaseInsensitive` and `UTF`. [#4602](https://github.com/yandex/ClickHouse/pull/4602) ([Danila Kutenin](https://github.com/danlark1))
|
||||
* Improved data skipping indices calculation. [#4640](https://github.com/yandex/ClickHouse/pull/4640) ([Nikita Vasilev](https://github.com/nikvas0))
|
||||
* Keep ordinary, `DEFAULT`, `MATERIALIZED` and `ALIAS` columns in a single list (fixes issue [#2867](https://github.com/yandex/ClickHouse/issues/2867)). [#4707](https://github.com/yandex/ClickHouse/pull/4707) ([Alex Zatelepin](https://github.com/ztlpn))
|
||||
|
||||
### Bug Fix
|
||||
|
||||
@ -34,7 +40,6 @@
|
||||
* Deadlock may happen while executing `DROP DATABASE dictionary` query. [#4701](https://github.com/yandex/ClickHouse/pull/4701) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fix undefinied behavior in `median` and `quantile` functions. [#4702](https://github.com/yandex/ClickHouse/pull/4702) ([hcz](https://github.com/hczhcz))
|
||||
* Fix compression level detection when `network_compression_method` in lowercase. Broken in v19.1. [#4706](https://github.com/yandex/ClickHouse/pull/4706) ([proller](https://github.com/proller))
|
||||
* Keep ordinary, `DEFAULT`, `MATERIALIZED` and `ALIAS` columns in a single list (fixes issue [#2867](https://github.com/yandex/ClickHouse/issues/2867)). [#4707](https://github.com/yandex/ClickHouse/pull/4707) ([Alex Zatelepin](https://github.com/ztlpn))
|
||||
* Fixed ignorance of `<timezone>UTC</timezone>` setting (fixes issue [#4658](https://github.com/yandex/ClickHouse/issues/4658)). [#4718](https://github.com/yandex/ClickHouse/pull/4718) ([proller](https://github.com/proller))
|
||||
* Fix `histogram` function behaviour with `Distributed` tables. [#4741](https://github.com/yandex/ClickHouse/pull/4741) ([olegkv](https://github.com/olegkv))
|
||||
* Fixed tsan report `destroy of a locked mutex`. [#4742](https://github.com/yandex/ClickHouse/pull/4742) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
@ -63,7 +68,7 @@
|
||||
* Fix rare bug when setting `min_bytes_to_use_direct_io` is greater than zero, which occures when thread have to seek backward in column file. [#4897](https://github.com/yandex/ClickHouse/pull/4897) ([alesapin](https://github.com/alesapin))
|
||||
* Fix wrong argument types for aggregate functions with `LowCardinality` arguments (fixes issue [#4919](https://github.com/yandex/ClickHouse/issues/4919)). [#4922](https://github.com/yandex/ClickHouse/pull/4922) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Fix wrong name qualification in `GLOBAL JOIN`. [#4969](https://github.com/yandex/ClickHouse/pull/4969) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
* Function `toISOWeek` result for year 1970. [#4988](https://github.com/yandex/ClickHouse/pull/4988) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fix function `toISOWeek` result for year 1970. [#4988](https://github.com/yandex/ClickHouse/pull/4988) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fix `DROP`, `TRUNCATE` and `OPTIMIZE` queries duplication, when executed on `ON CLUSTER` for `ReplicatedMergeTree*` tables family. [#4991](https://github.com/yandex/ClickHouse/pull/4991) ([alesapin](https://github.com/alesapin))
|
||||
|
||||
### Backward Incompatible Change
|
||||
@ -93,6 +98,47 @@
|
||||
* Disable usage of `mremap` when compiled with Thread Sanitizer. Surprisingly enough, TSan does not intercept `mremap` (though it does intercept `mmap`, `munmap`) that leads to false positives. Fixed TSan report in stateful tests. [#4859](https://github.com/yandex/ClickHouse/pull/4859) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Add test checking using format schema via HTTP interface. [#4864](https://github.com/yandex/ClickHouse/pull/4864) ([Vitaly Baranov](https://github.com/vitlibar))
|
||||
|
||||
## ClickHouse release 19.4.4.33, 2019-04-17
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Avoid `std::terminate` in case of memory allocation failure. Now `std::bad_alloc` exception is thrown as expected. [#4665](https://github.com/yandex/ClickHouse/pull/4665) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fixes capnproto reading from buffer. Sometimes files wasn't loaded successfully by HTTP. [#4674](https://github.com/yandex/ClickHouse/pull/4674) ([Vladislav](https://github.com/smirnov-vs))
|
||||
* Fix error `Unknown log entry type: 0` after `OPTIMIZE TABLE FINAL` query. [#4683](https://github.com/yandex/ClickHouse/pull/4683) ([Amos Bird](https://github.com/amosbird))
|
||||
* Wrong arguments to `hasAny` or `hasAll` functions may lead to segfault. [#4698](https://github.com/yandex/ClickHouse/pull/4698) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Deadlock may happen while executing `DROP DATABASE dictionary` query. [#4701](https://github.com/yandex/ClickHouse/pull/4701) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fix undefinied behavior in `median` and `quantile` functions. [#4702](https://github.com/yandex/ClickHouse/pull/4702) ([hcz](https://github.com/hczhcz))
|
||||
* Fix compression level detection when `network_compression_method` in lowercase. Broken in v19.1. [#4706](https://github.com/yandex/ClickHouse/pull/4706) ([proller](https://github.com/proller))
|
||||
* Fixed ignorance of `<timezone>UTC</timezone>` setting (fixes issue [#4658](https://github.com/yandex/ClickHouse/issues/4658)). [#4718](https://github.com/yandex/ClickHouse/pull/4718) ([proller](https://github.com/proller))
|
||||
* Fix `histogram` function behaviour with `Distributed` tables. [#4741](https://github.com/yandex/ClickHouse/pull/4741) ([olegkv](https://github.com/olegkv))
|
||||
* Fixed tsan report `destroy of a locked mutex`. [#4742](https://github.com/yandex/ClickHouse/pull/4742) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fixed TSan report on shutdown due to race condition in system logs usage. Fixed potential use-after-free on shutdown when part_log is enabled. [#4758](https://github.com/yandex/ClickHouse/pull/4758) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fix recheck parts in `ReplicatedMergeTreeAlterThread` in case of error. [#4772](https://github.com/yandex/ClickHouse/pull/4772) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Arithmetic operations on intermediate aggregate function states were not working for constant arguments (such as subquery results). [#4776](https://github.com/yandex/ClickHouse/pull/4776) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Always backquote column names in metadata. Otherwise it's impossible to create a table with column named `index` (server won't restart due to malformed `ATTACH` query in metadata). [#4782](https://github.com/yandex/ClickHouse/pull/4782) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fix crash in `ALTER ... MODIFY ORDER BY` on `Distributed` table. [#4790](https://github.com/yandex/ClickHouse/pull/4790) ([TCeason](https://github.com/TCeason))
|
||||
* Fix segfault in `JOIN ON` with enabled `enable_optimize_predicate_expression`. [#4794](https://github.com/yandex/ClickHouse/pull/4794) ([Winter Zhang](https://github.com/zhang2014))
|
||||
* Fix bug with adding an extraneous row after consuming a protobuf message from Kafka. [#4808](https://github.com/yandex/ClickHouse/pull/4808) ([Vitaly Baranov](https://github.com/vitlibar))
|
||||
* Fix segmentation fault in `clickhouse-copier`. [#4835](https://github.com/yandex/ClickHouse/pull/4835) ([proller](https://github.com/proller))
|
||||
* Fixed race condition in `SELECT` from `system.tables` if the table is renamed or altered concurrently. [#4836](https://github.com/yandex/ClickHouse/pull/4836) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fixed data race when fetching data part that is already obsolete. [#4839](https://github.com/yandex/ClickHouse/pull/4839) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fixed rare data race that can happen during `RENAME` table of MergeTree family. [#4844](https://github.com/yandex/ClickHouse/pull/4844) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fixed segmentation fault in function `arrayIntersect`. Segmentation fault could happen if function was called with mixed constant and ordinary arguments. [#4847](https://github.com/yandex/ClickHouse/pull/4847) ([Lixiang Qian](https://github.com/fancyqlx))
|
||||
* Fixed reading from `Array(LowCardinality)` column in rare case when column contained a long sequence of empty arrays. [#4850](https://github.com/yandex/ClickHouse/pull/4850) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Fix `No message received` exception while fetching parts between replicas. [#4856](https://github.com/yandex/ClickHouse/pull/4856) ([alesapin](https://github.com/alesapin))
|
||||
* Fixed `arrayIntersect` function wrong result in case of several repeated values in single array. [#4871](https://github.com/yandex/ClickHouse/pull/4871) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Fix a race condition during concurrent `ALTER COLUMN` queries that could lead to a server crash (fixes issue [#3421](https://github.com/yandex/ClickHouse/issues/3421)). [#4592](https://github.com/yandex/ClickHouse/pull/4592) ([Alex Zatelepin](https://github.com/ztlpn))
|
||||
* Fix parameter deduction in `ALTER MODIFY` of column `CODEC` when column type is not specified. [#4883](https://github.com/yandex/ClickHouse/pull/4883) ([alesapin](https://github.com/alesapin))
|
||||
* Functions `cutQueryStringAndFragment()` and `queryStringAndFragment()` now works correctly when `URL` contains a fragment and no query. [#4894](https://github.com/yandex/ClickHouse/pull/4894) ([Vitaly Baranov](https://github.com/vitlibar))
|
||||
* Fix rare bug when setting `min_bytes_to_use_direct_io` is greater than zero, which occures when thread have to seek backward in column file. [#4897](https://github.com/yandex/ClickHouse/pull/4897) ([alesapin](https://github.com/alesapin))
|
||||
* Fix wrong argument types for aggregate functions with `LowCardinality` arguments (fixes issue [#4919](https://github.com/yandex/ClickHouse/issues/4919)). [#4922](https://github.com/yandex/ClickHouse/pull/4922) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Fix function `toISOWeek` result for year 1970. [#4988](https://github.com/yandex/ClickHouse/pull/4988) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Fix `DROP`, `TRUNCATE` and `OPTIMIZE` queries duplication, when executed on `ON CLUSTER` for `ReplicatedMergeTree*` tables family. [#4991](https://github.com/yandex/ClickHouse/pull/4991) ([alesapin](https://github.com/alesapin))
|
||||
|
||||
### Improvements
|
||||
|
||||
* Keep ordinary, `DEFAULT`, `MATERIALIZED` and `ALIAS` columns in a single list (fixes issue [#2867](https://github.com/yandex/ClickHouse/issues/2867)). [#4707](https://github.com/yandex/ClickHouse/pull/4707) ([Alex Zatelepin](https://github.com/ztlpn))
|
||||
|
||||
## ClickHouse release 19.4.3.11, 2019-04-02
|
||||
|
||||
### Bug Fixes
|
||||
|
161
CHANGELOG_RU.md
161
CHANGELOG_RU.md
@ -1,3 +1,164 @@
|
||||
## ClickHouse release 19.5.3.8, 2019-04-18
|
||||
|
||||
### Исправления ошибок
|
||||
* Исправлен тип настройки `max_partitions_per_insert_block` с булевого на UInt64. [#5028](https://github.com/yandex/ClickHouse/pull/5028) ([Mohammad Hossein Sekhavat](https://github.com/mhsekhavat))
|
||||
|
||||
## ClickHouse release 19.5.2.6, 2019-04-15
|
||||
|
||||
### Новые возможности
|
||||
|
||||
* Добавлены функции для работы с несколькими регулярными выражениями с помощью библиотеки [Hyperscan](https://github.com/intel/hyperscan). (`multiMatchAny`, `multiMatchAnyIndex`, `multiFuzzyMatchAny`, `multiFuzzyMatchAnyIndex`). [#4780](https://github.com/yandex/ClickHouse/pull/4780), [#4841](https://github.com/yandex/ClickHouse/pull/4841) ([Danila Kutenin](https://github.com/danlark1))
|
||||
* Добавлена функция `multiSearchFirstPosition`. [#4780](https://github.com/yandex/ClickHouse/pull/4780) ([Danila Kutenin](https://github.com/danlark1))
|
||||
* Реализована возможность указания построчного ограничения доступа к таблицам. [#4792](https://github.com/yandex/ClickHouse/pull/4792) ([Ivan](https://github.com/abyss7))
|
||||
* Добавлен новый тип вторичного индекса на базе фильтра Блума (используется в функциях `equal`, `in` и `like`). [#4499](https://github.com/yandex/ClickHouse/pull/4499) ([Nikita Vasilev](https://github.com/nikvas0))
|
||||
* Добавлен `ASOF JOIN` которые позволяет джойнить строки по наиболее близкому известному значению. [#4774](https://github.com/yandex/ClickHouse/pull/4774) [#4867](https://github.com/yandex/ClickHouse/pull/4867) [#4863](https://github.com/yandex/ClickHouse/pull/4863) [#4875](https://github.com/yandex/ClickHouse/pull/4875) ([Martijn Bakker](https://github.com/Gladdy), [Artem Zuikov](https://github.com/4ertus2))
|
||||
* Теперь запрос `COMMA JOIN` переписывается `CROSS JOIN`. И затем оба переписываются в `INNER JOIN`, если это возможно. [#4661](https://github.com/yandex/ClickHouse/pull/4661) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
|
||||
### Улучшения
|
||||
|
||||
* Функции `topK` и `topKWeighted` теперь поддерживают произвольный `loadFactor` (исправляет issue [#4252](https://github.com/yandex/ClickHouse/issues/4252)). [#4634](https://github.com/yandex/ClickHouse/pull/4634) ([Kirill Danshin](https://github.com/kirillDanshin))
|
||||
* Добавлена возможность использования настройки `parallel_replicas_count > 1` для таблиц без семплирования (ранее настройка просто игнорировалась). [#4637](https://github.com/yandex/ClickHouse/pull/4637) ([Alexey Elymanov](https://github.com/digitalist))
|
||||
* Поддержан запрос `CREATE OR REPLACE VIEW`. Позволяет создать `VIEW` или изменить запрос в одном выражении. [#4654](https://github.com/yandex/ClickHouse/pull/4654) ([Boris Granveaud](https://github.com/bgranvea))
|
||||
* Движок таблиц `Buffer` теперь поддерживает `PREWHERE`. [#4671](https://github.com/yandex/ClickHouse/pull/4671) ([Yangkuan Liu](https://github.com/LiuYangkuan))
|
||||
* Теперь реплицируемые таблицы могу стартовать в `readonly` режиме даже при отсутствии zookeeper. [#4691](https://github.com/yandex/ClickHouse/pull/4691) ([alesapin](https://github.com/alesapin))
|
||||
* Исправлено мигание прогресс-бара в clickhouse-client. Проблема была наиболее заметна при использовании `FORMAT Null` в потоковых запросах. [#4811](https://github.com/yandex/ClickHouse/pull/4811) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Добавлена возможность отключения функций, использующих библиотеку `hyperscan`, для пользователей, чтобы ограничить возможное неконтролируемое потребление ресурсов. [#4816](https://github.com/yandex/ClickHouse/pull/4816) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Добавлено логирование номера версии во все исключения. [#4824](https://github.com/yandex/ClickHouse/pull/4824) ([proller](https://github.com/proller))
|
||||
* Добавлено ограничение на размер строк и количество параметров в функции `multiMatch`. Теперь они принимают строки умещающиеся в `unsigned int`. [#4834](https://github.com/yandex/ClickHouse/pull/4834) ([Danila Kutenin](https://github.com/danlark1))
|
||||
* Улучшено использование памяти и обработка ошибок в Hyperscan. [#4866](https://github.com/yandex/ClickHouse/pull/4866) ([Danila Kutenin](https://github.com/danlark1))
|
||||
* Теперь системная таблица `system.graphite_detentions` заполняется из конфигурационного файла для таблиц семейства `*GraphiteMergeTree`. [#4584](https://github.com/yandex/ClickHouse/pull/4584) ([Mikhail f. Shiryaev](https://github.com/Felixoid))
|
||||
* Функция `trigramDistance` переименована в функцию `ngramDistance`. Добавлено несколько функций с `CaseInsensitive` и `UTF`. [#4602](https://github.com/yandex/ClickHouse/pull/4602) ([Danila Kutenin](https://github.com/danlark1))
|
||||
* Улучшено вычисление вторичных индексов. [#4640](https://github.com/yandex/ClickHouse/pull/4640) ([Nikita Vasilev](https://github.com/nikvas0))
|
||||
* Теперь обычные колонки, а также колонки `DEFAULT`, `MATERIALIZED` и `ALIAS` хранятся в одном списке (исправляет issue [#2867](https://github.com/yandex/ClickHouse/issues/2867)). [#4707](https://github.com/yandex/ClickHouse/pull/4707) ([Alex Zatelepin](https://github.com/ztlpn))
|
||||
|
||||
### Исправления ошибок
|
||||
|
||||
* В случае невозможности выделить память вместо вызова `std::terminate` бросается исключение `std::bad_alloc`. [#4665](https://github.com/yandex/ClickHouse/pull/4665) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлены ошибки чтения capnproto из буфера. Иногда файлы не загружались по HTTP. [#4674](https://github.com/yandex/ClickHouse/pull/4674) ([Vladislav](https://github.com/smirnov-vs))
|
||||
* Исправлена ошибка `Unknown log entry type: 0` после запроса `OPTIMIZE TABLE FINAL`. [#4683](https://github.com/yandex/ClickHouse/pull/4683) ([Amos Bird](https://github.com/amosbird))
|
||||
* При передаче неправильных аргументов в `hasAny` и `hasAll` могла происходить ошибка сегментирования. [#4698](https://github.com/yandex/ClickHouse/pull/4698) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлен дедлок, который мог происходить при запросе `DROP DATABASE dictionary`. [#4701](https://github.com/yandex/ClickHouse/pull/4701) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлено неопределенное поведение в функциях `median` и `quantile`. [#4702](https://github.com/yandex/ClickHouse/pull/4702) ([hcz](https://github.com/hczhcz))
|
||||
* Исправлено определение уровня сжатия при указании настройки `network_compression_method` в нижнем регистре. Было сломано в v19.1. [#4706](https://github.com/yandex/ClickHouse/pull/4706) ([proller](https://github.com/proller))
|
||||
* Настройка `<timezone>UTC</timezone>` больше не игнорируется (исправляет issue [#4658](https://github.com/yandex/ClickHouse/issues/4658)). [#4718](https://github.com/yandex/ClickHouse/pull/4718) ([proller](https://github.com/proller))
|
||||
* Исправлено поведение функции `histogram` с `Distributed` таблицами. [#4741](https://github.com/yandex/ClickHouse/pull/4741) ([olegkv](https://github.com/olegkv))
|
||||
* Исправлено срабатывание thread-санитайзера с ошибкой `destroy of a locked mutex`. [#4742](https://github.com/yandex/ClickHouse/pull/4742) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлено срабатывание thread-санитайзера при завершении сервера, вызванное гонкой при использовании системных логов. Также исправлена потенциальная ошибка use-after-free при завершении сервера в котором был включен `part_log`. [#4758](https://github.com/yandex/ClickHouse/pull/4758) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлена перепроверка кусков в `ReplicatedMergeTreeAlterThread` при появлении ошибок. [#4772](https://github.com/yandex/ClickHouse/pull/4772) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Исправлена работа арифметических операций с промежуточными состояниями агрегатных функций для константных аргументов (таких как результаты подзапросов). [#4776](https://github.com/yandex/ClickHouse/pull/4776) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Теперь имена колонок всегда экранируются в файлах с метаинформацией. В противном случае было невозможно создать таблицу с колонкой с именем `index`. [#4782](https://github.com/yandex/ClickHouse/pull/4782) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлено падение в запросе `ALTER ... MODIFY ORDER BY` к `Distributed` таблице. [#4790](https://github.com/yandex/ClickHouse/pull/4790) ([TCeason](https://github.com/TCeason))
|
||||
* Исправлена ошибка сегментирования при запросах с `JOIN ON` и включенной настройкой `enable_optimize_predicate_expression`. [#4794](https://github.com/yandex/ClickHouse/pull/4794) ([Winter Zhang](https://github.com/zhang2014))
|
||||
* Исправлено добавление лишней строки после чтения protobuf-сообщения из таблицы с движком `Kafka`. [#4808](https://github.com/yandex/ClickHouse/pull/4808) ([Vitaly Baranov](https://github.com/vitlibar))
|
||||
* Исправлено падение при запросе с `JOIN ON` с не `nullable` и nullable колонкой. Также исправлено поведение при появлении `NULLs` среди ключей справа в`ANY JOIN` + `join_use_nulls`. [#4815](https://github.com/yandex/ClickHouse/pull/4815) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
* Исправлена ошибка сегментирования в `clickhouse-copier`. [#4835](https://github.com/yandex/ClickHouse/pull/4835) ([proller](https://github.com/proller))
|
||||
* Исправлена гонка при `SELECT` запросе из `system.tables` если таблица была конкурентно переименована или к ней был применен `ALTER` запрос. [#4836](https://github.com/yandex/ClickHouse/pull/4836) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлена гонка при скачивании куска, который уже является устаревшим. [#4839](https://github.com/yandex/ClickHouse/pull/4839) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлена редкая гонка при `RENAME` запросах к таблицам семейства MergeTree. [#4844](https://github.com/yandex/ClickHouse/pull/4844) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлена ошибка сегментирования в функции `arrayIntersect`. Ошибка возникала при вызове функции с константными и не константными аргументами. [#4847](https://github.com/yandex/ClickHouse/pull/4847) ([Lixiang Qian](https://github.com/fancyqlx))
|
||||
* Исправлена редкая ошибка при чтении из колонки типа `Array(LowCardinality)`, которая возникала, если в колонке содержалось большее количество подряд идущих пустых массивов. [#4850](https://github.com/yandex/ClickHouse/pull/4850) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Исправлено паление в запроса с `FULL/RIGHT JOIN` когда объединение происходило по nullable и не nullable колонке. [#4855](https://github.com/yandex/ClickHouse/pull/4855) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
* Исправлена ошибка `No message received`, возникавшая при скачивании кусков между репликами. [#4856](https://github.com/yandex/ClickHouse/pull/4856) ([alesapin](https://github.com/alesapin))
|
||||
* Исправлена ошибка в функции `arrayIntersect` приводившая к неправильным результатам в случае нескольких повторяющихся значений в массиве. [#4871](https://github.com/yandex/ClickHouse/pull/4871) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Исправлена гонка при конкурентных `ALTER COLUMN` запросах, которая могла приводить к падению сервера (исправляет issue [#3421](https://github.com/yandex/ClickHouse/issues/3421)). [#4592](https://github.com/yandex/ClickHouse/pull/4592) ([Alex Zatelepin](https://github.com/ztlpn))
|
||||
* Исправлен некорректный результат в `FULL/RIGHT JOIN` запросах с константной колонкой. [#4723](https://github.com/yandex/ClickHouse/pull/4723) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
* Исправлено появление дубликатов в `GLOBAL JOIN` со звездочкой. [#4705](https://github.com/yandex/ClickHouse/pull/4705) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
* Исправлено определение параметров кодеков в запросах `ALTER MODIFY`, если тип колонки не был указан. [#4883](https://github.com/yandex/ClickHouse/pull/4883) ([alesapin](https://github.com/alesapin))
|
||||
* Функции `cutQueryStringAndFragment()` и `queryStringAndFragment()` теперь работают корректно, когда `URL` содержит фрагмент, но не содержит запроса. [#4894](https://github.com/yandex/ClickHouse/pull/4894) ([Vitaly Baranov](https://github.com/vitlibar))
|
||||
* Исправлена редкая ошибка, возникавшая при установке настройки `min_bytes_to_use_direct_io` больше нуля. Она возникла при необходимости сдвинутся в файле, который уже прочитан до конца. [#4897](https://github.com/yandex/ClickHouse/pull/4897) ([alesapin](https://github.com/alesapin))
|
||||
* Исправлено неправильное определение типов аргументов для агрегатных функций с `LowCardinality` аргументами (исправляет [#4919](https://github.com/yandex/ClickHouse/issues/4919)). [#4922](https://github.com/yandex/ClickHouse/pull/4922) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Исправлена неверная квалификация имён в `GLOBAL JOIN`. [#4969](https://github.com/yandex/ClickHouse/pull/4969) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
* Исправлен результат функции `toISOWeek` для 1970 года. [#4988](https://github.com/yandex/ClickHouse/pull/4988) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлено дублирование `DROP`, `TRUNCATE` и `OPTIMIZE` запросов, когда они выполнялись `ON CLUSTER` для семейства таблиц `ReplicatedMergeTree*`. [#4991](https://github.com/yandex/ClickHouse/pull/4991) ([alesapin](https://github.com/alesapin))
|
||||
|
||||
### Обратно несовместимые изменения
|
||||
|
||||
* Настройка `insert_sample_with_metadata` переименована в `input_format_defaults_for_omitted_fields`. [#4771](https://github.com/yandex/ClickHouse/pull/4771) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
* Добавлена настройка `max_partitions_per_insert_block` (со значением по умолчанию 100). Если вставляемый блок содержит большое количество партиций, то бросается исключение. Лимит можно убрать выставив настройку в 0 (не рекомендуется). [#4845](https://github.com/yandex/ClickHouse/pull/4845) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Функции мультипоиска были переименованы (`multiPosition` в `multiSearchAllPositions`, `multiSearch` в `multiSearchAny`, `firstMatch` в `multiSearchFirstIndex`). [#4780](https://github.com/yandex/ClickHouse/pull/4780) ([Danila Kutenin](https://github.com/danlark1))
|
||||
|
||||
### Улучшение производительности
|
||||
|
||||
* Оптимизирован поиска с помощью алгоритма Volnitsky с помощью инлайнинга. Это дает около 5-10% улучшения производительности поиска для запросов ищущих множество слов или много одинаковых биграмм. [#4862](https://github.com/yandex/ClickHouse/pull/4862) ([Danila Kutenin](https://github.com/danlark1))
|
||||
* Исправлено снижение производительности при выставлении настройки `use_uncompressed_cache` больше нуля для запросов, данные которых целиком лежат в кеше. [#4913](https://github.com/yandex/ClickHouse/pull/4913) ([alesapin](https://github.com/alesapin))
|
||||
|
||||
|
||||
### Улучшения сборки/тестирования/пакетирования
|
||||
|
||||
* Более строгие настройки для debug-сборок: более гранулярные маппинги памяти и использование ASLR; добавлена защита памяти для кеша засечек и индекса. Это позволяет найти больше ошибок порчи памяти, которые не обнаруживают address-санитайзер и thread-санитайзер. [#4632](https://github.com/yandex/ClickHouse/pull/4632) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Добавлены настройки `ENABLE_PROTOBUF`, `ENABLE_PARQUET` и `ENABLE_BROTLI` которые позволяют отключить соответствующие компоненты. [#4669](https://github.com/yandex/ClickHouse/pull/4669) ([Silviu Caragea](https://github.com/silviucpp))
|
||||
* Теперь при зависании запросов во время работы тестов будет показан список запросов и стек-трейсы всех потоков. [#4675](https://github.com/yandex/ClickHouse/pull/4675) ([alesapin](https://github.com/alesapin))
|
||||
* Добавлены ретраи при ошибке `Connection loss` в `clickhouse-test`. [#4682](https://github.com/yandex/ClickHouse/pull/4682) ([alesapin](https://github.com/alesapin))
|
||||
* Добавлена возможность сборки под FreeBSD в `packager`-скрипт. [#4712](https://github.com/yandex/ClickHouse/pull/4712) [#4748](https://github.com/yandex/ClickHouse/pull/4748) ([alesapin](https://github.com/alesapin))
|
||||
* Теперь при установке предлагается установить пароль для пользователя `'default'`. [#4725](https://github.com/yandex/ClickHouse/pull/4725) ([proller](https://github.com/proller))
|
||||
* Убраны предупреждения из библиотеки `rdkafka` при сборке. [#4740](https://github.com/yandex/ClickHouse/pull/4740) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Добавлена возможность сборки без поддержки ssl. [#4750](https://github.com/yandex/ClickHouse/pull/4750) ([proller](https://github.com/proller))
|
||||
* Добавлена возможность запускать докер-образ с clickhouse-server из под любого пользователя. [#4753](https://github.com/yandex/ClickHouse/pull/4753) ([Mikhail f. Shiryaev](https://github.com/Felixoid))
|
||||
* Boost обновлен до 1.69. [#4793](https://github.com/yandex/ClickHouse/pull/4793) ([proller](https://github.com/proller))
|
||||
* Отключено использование `mremap` при сборке с thread-санитайзером, что приводило к ложным срабатываниям. Исправлены ошибки thread-санитайзера в stateful-тестах. [#4859](https://github.com/yandex/ClickHouse/pull/4859) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Добавлен тест проверяющий использование схемы форматов для HTTP-интерфейса. [#4864](https://github.com/yandex/ClickHouse/pull/4864) ([Vitaly Baranov](https://github.com/vitlibar))
|
||||
|
||||
## ClickHouse release 19.4.4.33, 2019-04-17
|
||||
|
||||
### Исправление ошибок
|
||||
* В случае невозможности выделить память вместо вызова `std::terminate` бросается исключение `std::bad_alloc`. [#4665](https://github.com/yandex/ClickHouse/pull/4665) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлены ошибки чтения capnproto из буфера. Иногда файлы не загружались по HTTP. [#4674](https://github.com/yandex/ClickHouse/pull/4674) ([Vladislav](https://github.com/smirnov-vs))
|
||||
* Исправлена ошибка `Unknown log entry type: 0` после запроса `OPTIMIZE TABLE FINAL`. [#4683](https://github.com/yandex/ClickHouse/pull/4683) ([Amos Bird](https://github.com/amosbird))
|
||||
* При передаче неправильных аргументов в `hasAny` и `hasAll` могла происходить ошибка сегментирования. [#4698](https://github.com/yandex/ClickHouse/pull/4698) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлен дедлок, который мог происходить при запросе `DROP DATABASE dictionary`. [#4701](https://github.com/yandex/ClickHouse/pull/4701) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлено неопределенное поведение в функциях `median` и `quantile`. [#4702](https://github.com/yandex/ClickHouse/pull/4702) ([hcz](https://github.com/hczhcz))
|
||||
* Исправлено определение уровня сжатия при указании настройки `network_compression_method` в нижнем регистре. Было сломано в v19.1. [#4706](https://github.com/yandex/ClickHouse/pull/4706) ([proller](https://github.com/proller))
|
||||
* Настройка `<timezone>UTC</timezone>` больше не игнорируется (исправляет issue [#4658](https://github.com/yandex/ClickHouse/issues/4658)). [#4718](https://github.com/yandex/ClickHouse/pull/4718) ([proller](https://github.com/proller))
|
||||
* Исправлено поведение функции `histogram` с `Distributed` таблицами. [#4741](https://github.com/yandex/ClickHouse/pull/4741) ([olegkv](https://github.com/olegkv))
|
||||
* Исправлено срабатывание thread-санитайзера с ошибкой `destroy of a locked mutex`. [#4742](https://github.com/yandex/ClickHouse/pull/4742) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлено срабатывание thread-санитайзера при завершении сервера, вызванное гонкой при использовании системных логов. Также исправлена потенциальная ошибка use-after-free при завершении сервера в котором был включен `part_log`. [#4758](https://github.com/yandex/ClickHouse/pull/4758) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлена перепроверка кусков в `ReplicatedMergeTreeAlterThread` при появлении ошибок. [#4772](https://github.com/yandex/ClickHouse/pull/4772) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Исправлена работа арифметических операций с промежуточными состояниями агрегатных функций для константных аргументов (таких как результаты подзапросов). [#4776](https://github.com/yandex/ClickHouse/pull/4776) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Теперь имена колонок всегда экранируются в файлах с метаинформацией. В противном случае было невозможно создать таблицу с колонкой с именем `index`. [#4782](https://github.com/yandex/ClickHouse/pull/4782) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлено падение в запросе `ALTER ... MODIFY ORDER BY` к `Distributed` таблице. [#4790](https://github.com/yandex/ClickHouse/pull/4790) ([TCeason](https://github.com/TCeason))
|
||||
* Исправлена ошибка сегментирования при запросах с `JOIN ON` и включенной настройкой `enable_optimize_predicate_expression`. [#4794](https://github.com/yandex/ClickHouse/pull/4794) ([Winter Zhang](https://github.com/zhang2014))
|
||||
* Исправлено добавление лишней строки после чтения protobuf-сообщения из таблицы с движком `Kafka`. [#4808](https://github.com/yandex/ClickHouse/pull/4808) ([Vitaly Baranov](https://github.com/vitlibar))
|
||||
* Исправлена ошибка сегментирования в `clickhouse-copier`. [#4835](https://github.com/yandex/ClickHouse/pull/4835) ([proller](https://github.com/proller))
|
||||
* Исправлена гонка при `SELECT` запросе из `system.tables` если таблица была конкурентно переименована или к ней был применен `ALTER` запрос. [#4836](https://github.com/yandex/ClickHouse/pull/4836) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлена гонка при скачивании куска, который уже является устаревшим. [#4839](https://github.com/yandex/ClickHouse/pull/4839) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлена редкая гонка при `RENAME` запросах к таблицам семейства MergeTree. [#4844](https://github.com/yandex/ClickHouse/pull/4844) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлена ошибка сегментирования в функции `arrayIntersect`. Ошибка возникала при вызове функции с константными и не константными аргументами. [#4847](https://github.com/yandex/ClickHouse/pull/4847) ([Lixiang Qian](https://github.com/fancyqlx))
|
||||
* Исправлена редкая ошибка при чтении из колонки типа `Array(LowCardinality)`, которая возникала, если в колонке содержалось большее количество подряд идущих пустых массивов. [#4850](https://github.com/yandex/ClickHouse/pull/4850) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Исправлена ошибка `No message received`, возникавшая при скачивании кусков между репликами. [#4856](https://github.com/yandex/ClickHouse/pull/4856) ([alesapin](https://github.com/alesapin))
|
||||
* Исправлена ошибка в функции `arrayIntersect` приводившая к неправильным результатам в случае нескольких повторяющихся значений в массиве. [#4871](https://github.com/yandex/ClickHouse/pull/4871) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Исправлена гонка при конкурентных `ALTER COLUMN` запросах, которая могла приводить к падению сервера (исправляет issue [#3421](https://github.com/yandex/ClickHouse/issues/3421)). [#4592](https://github.com/yandex/ClickHouse/pull/4592) ([Alex Zatelepin](https://github.com/ztlpn))
|
||||
* Исправлено определение параметров кодеков в запросах `ALTER MODIFY`, если тип колонки не был указан. [#4883](https://github.com/yandex/ClickHouse/pull/4883) ([alesapin](https://github.com/alesapin))
|
||||
* Функции `cutQueryStringAndFragment()` и `queryStringAndFragment()` теперь работают корректно, когда `URL` содержит фрагмент, но не содержит запроса. [#4894](https://github.com/yandex/ClickHouse/pull/4894) ([Vitaly Baranov](https://github.com/vitlibar))
|
||||
* Исправлена редкая ошибка, возникавшая при установке настройки `min_bytes_to_use_direct_io` больше нуля. Она возникла при необходимости сдвинутся в файле, который уже прочитан до конца. [#4897](https://github.com/yandex/ClickHouse/pull/4897) ([alesapin](https://github.com/alesapin))
|
||||
* Исправлено неправильное определение типов аргументов для агрегатных функций с `LowCardinality` аргументами (исправляет [#4919](https://github.com/yandex/ClickHouse/issues/4919)). [#4922](https://github.com/yandex/ClickHouse/pull/4922) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
* Исправлен результат функции `toISOWeek` для 1970 года. [#4988](https://github.com/yandex/ClickHouse/pull/4988) ([alexey-milovidov](https://github.com/alexey-milovidov))
|
||||
* Исправлено дублирование `DROP`, `TRUNCATE` и `OPTIMIZE` запросов, когда они выполнялись `ON CLUSTER` для семейства таблиц `ReplicatedMergeTree*`. [#4991](https://github.com/yandex/ClickHouse/pull/4991) ([alesapin](https://github.com/alesapin))
|
||||
|
||||
### Улучшения
|
||||
|
||||
* Теперь обычные колонки, а также колонки `DEFAULT`, `MATERIALIZED` и `ALIAS` хранятся в одном списке (исправляет issue [#2867](https://github.com/yandex/ClickHouse/issues/2867)). [#4707](https://github.com/yandex/ClickHouse/pull/4707) ([Alex Zatelepin](https://github.com/ztlpn))
|
||||
|
||||
## ClickHouse release 19.4.3.11, 2019-04-02
|
||||
|
||||
### Исправление ошибок
|
||||
|
||||
* Исправлено паление в запроса с `FULL/RIGHT JOIN` когда объединение происходило по nullable и не nullable колонке. [#4855](https://github.com/yandex/ClickHouse/pull/4855) ([Artem Zuikov](https://github.com/4ertus2))
|
||||
* Исправлена ошибка сегментирования в `clickhouse-copier`. [#4835](https://github.com/yandex/ClickHouse/pull/4835) ([proller](https://github.com/proller))
|
||||
|
||||
### Улучшения сборки/тестирования/пакетирования
|
||||
|
||||
* Добавлена возможность запускать докер-образ с clickhouse-server из под любого пользователя. [#4753](https://github.com/yandex/ClickHouse/pull/4753) ([Mikhail f. Shiryaev](https://github.com/Felixoid))
|
||||
|
||||
## ClickHouse release 19.4.2.7, 2019-03-30
|
||||
|
||||
### Исправление ошибок
|
||||
* Исправлена редкая ошибка при чтении из колонки типа `Array(LowCardinality)`, которая возникала, если в колонке содержалось большее количество подряд идущих пустых массивов. [#4850](https://github.com/yandex/ClickHouse/pull/4850) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
|
||||
|
||||
## ClickHouse release 19.4.1.3, 2019-03-19
|
||||
|
||||
### Исправление ошибок
|
||||
* Исправлено поведение удаленных запросов, которые одновременно содержали `LIMIT BY` и `LIMIT`. Раньше для таких запросов `LIMIT` мог быть выполнен до `LIMIT BY`, что приводило к перефильтрации. [#4708](https://github.com/yandex/ClickHouse/pull/4708) ([Constantin S. Pan](https://github.com/kvap))
|
||||
|
||||
## ClickHouse release 19.4.0.49, 2019-03-09
|
||||
|
||||
### Новые возможности
|
||||
|
@ -1,6 +1,15 @@
|
||||
project(ClickHouse)
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
cmake_policy(SET CMP0023 NEW)
|
||||
|
||||
foreach(policy
|
||||
CMP0023
|
||||
CMP0074 # CMake 3.12
|
||||
)
|
||||
if(POLICY ${policy})
|
||||
cmake_policy(SET ${policy} NEW)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS 1) # Write compile_commands.json
|
||||
set(CMAKE_LINK_DEPENDS_NO_SHARED 1) # Do not relink all depended targets on .so
|
||||
@ -318,6 +327,7 @@ include (cmake/find_consistent-hashing.cmake)
|
||||
include (cmake/find_base64.cmake)
|
||||
include (cmake/find_hyperscan.cmake)
|
||||
include (cmake/find_lfalloc.cmake)
|
||||
include (cmake/find_simdjson.cmake)
|
||||
find_contrib_lib(cityhash)
|
||||
find_contrib_lib(farmhash)
|
||||
find_contrib_lib(metrohash)
|
||||
|
@ -12,7 +12,6 @@ ClickHouse is an open-source column-oriented database management system that all
|
||||
* You can also [fill this form](https://forms.yandex.com/surveys/meet-yandex-clickhouse-team/) to meet Yandex ClickHouse team in person.
|
||||
|
||||
## Upcoming Events
|
||||
* [ClickHouse Community Meetup in Limassol](https://www.facebook.com/events/386638262181785/) on May 7.
|
||||
* ClickHouse at [Percona Live 2019](https://www.percona.com/live/19/other-open-source-databases-track) in Austin on May 28-30.
|
||||
* [ClickHouse Community Meetup in Beijing](https://www.huodongxing.com/event/2483759276200) on June 8.
|
||||
* [ClickHouse Community Meetup in Shenzhen](https://www.huodongxing.com/event/3483759917300) on October 20.
|
||||
|
@ -1,88 +1,147 @@
|
||||
# This file copied from contrib/poco/cmake/FindODBC.cmake to allow build without submodules
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#.rst:
|
||||
# FindMySQL
|
||||
# -------
|
||||
#
|
||||
# Find the ODBC driver manager includes and library.
|
||||
# Find ODBC Runtime
|
||||
#
|
||||
# ODBC is an open standard for connecting to different databases in a
|
||||
# semi-vendor-independent fashion. First you install the ODBC driver
|
||||
# manager. Then you need a driver for each separate database you want
|
||||
# to connect to (unless a generic one works). VTK includes neither
|
||||
# the driver manager nor the vendor-specific drivers: you have to find
|
||||
# those yourself.
|
||||
# This will define the following variables::
|
||||
#
|
||||
# This module defines
|
||||
# ODBC_INCLUDE_DIRECTORIES, where to find sql.h
|
||||
# ODBC_LIBRARIES, the libraries to link against to use ODBC
|
||||
# ODBC_FOUND. If false, you cannot build anything that requires ODBC.
|
||||
# ODBC_FOUND - True if the system has the libraries
|
||||
# ODBC_INCLUDE_DIRS - where to find the headers
|
||||
# ODBC_LIBRARIES - where to find the libraries
|
||||
# ODBC_DEFINITIONS - compile definitons
|
||||
#
|
||||
# Hints:
|
||||
# Set ``ODBC_ROOT_DIR`` to the root directory of an installation.
|
||||
#
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
option (ENABLE_ODBC "Enable ODBC" ${OS_LINUX})
|
||||
if (OS_LINUX)
|
||||
option (USE_INTERNAL_ODBC_LIBRARY "Set to FALSE to use system odbc library instead of bundled" ${NOT_UNBUNDLED})
|
||||
else ()
|
||||
option (USE_INTERNAL_ODBC_LIBRARY "Set to FALSE to use system odbc library instead of bundled" OFF)
|
||||
endif ()
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_ODBC QUIET odbc)
|
||||
|
||||
if (USE_INTERNAL_ODBC_LIBRARY AND NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/unixodbc/README")
|
||||
message (WARNING "submodule contrib/unixodbc is missing. to fix try run: \n git submodule update --init --recursive")
|
||||
set (USE_INTERNAL_ODBC_LIBRARY 0)
|
||||
endif ()
|
||||
if(WIN32)
|
||||
get_filename_component(kit_dir "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot]" REALPATH)
|
||||
get_filename_component(kit81_dir "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot81]" REALPATH)
|
||||
endif()
|
||||
|
||||
if (ENABLE_ODBC)
|
||||
if (USE_INTERNAL_ODBC_LIBRARY)
|
||||
set (ODBC_LIBRARIES unixodbc)
|
||||
set (ODBC_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/contrib/unixodbc/include)
|
||||
set (ODBC_FOUND 1)
|
||||
set (USE_ODBC 1)
|
||||
else ()
|
||||
find_path(ODBC_INCLUDE_DIRECTORIES
|
||||
find_path(ODBC_INCLUDE_DIR
|
||||
NAMES sql.h
|
||||
HINTS
|
||||
${ODBC_ROOT_DIR}/include
|
||||
${ODBC_ROOT_INCLUDE_DIRS}
|
||||
PATHS
|
||||
${PC_ODBC_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/include/iodbc
|
||||
/usr/include/odbc
|
||||
/usr/local/include
|
||||
/usr/local/include/iodbc
|
||||
/usr/local/include/odbc
|
||||
/usr/local/iodbc/include
|
||||
/usr/local/odbc/include
|
||||
/usr/local/iodbc/include
|
||||
"C:/Program Files/ODBC/include"
|
||||
"C:/Program Files/Microsoft SDKs/Windows/v7.0/include"
|
||||
"C:/Program Files/Microsoft SDKs/Windows/v6.0a/include"
|
||||
"C:/ODBC/include"
|
||||
"${kit_dir}/Include/um"
|
||||
"${kit81_dir}/Include/um"
|
||||
PATH_SUFFIXES
|
||||
odbc
|
||||
iodbc
|
||||
DOC "Specify the directory containing sql.h."
|
||||
)
|
||||
)
|
||||
|
||||
find_library(ODBC_LIBRARIES
|
||||
NAMES iodbc odbc iodbcinst odbcinst odbc32
|
||||
if(NOT ODBC_INCLUDE_DIR AND WIN32)
|
||||
set(ODBC_INCLUDE_DIR "")
|
||||
else()
|
||||
set(REQUIRED_INCLUDE_DIR ODBC_INCLUDE_DIR)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(WIN_ARCH x64)
|
||||
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(WIN_ARCH x86)
|
||||
endif()
|
||||
|
||||
find_library(ODBC_LIBRARY
|
||||
NAMES unixodbc iodbc odbc odbc32
|
||||
HINTS
|
||||
${ODBC_ROOT_DIR}/lib
|
||||
${ODBC_ROOT_LIBRARY_DIRS}
|
||||
PATHS
|
||||
${PC_ODBC_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/lib/iodbc
|
||||
/usr/lib/odbc
|
||||
/usr/local/lib
|
||||
/usr/local/lib/iodbc
|
||||
/usr/local/lib/odbc
|
||||
/usr/local/iodbc/lib
|
||||
/usr/local/odbc/lib
|
||||
/usr/local/iodbc/lib
|
||||
"C:/Program Files/ODBC/lib"
|
||||
"C:/ODBC/lib/debug"
|
||||
"C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Lib"
|
||||
"${kit81_dir}/Lib/winv6.3/um"
|
||||
"${kit_dir}/Lib/win8/um"
|
||||
PATH_SUFIXES
|
||||
odbc
|
||||
${WIN_ARCH}
|
||||
DOC "Specify the ODBC driver manager library here."
|
||||
)
|
||||
|
||||
if(NOT ODBC_LIBRARY AND WIN32)
|
||||
# List names of ODBC libraries on Windows
|
||||
set(ODBC_LIBRARY odbc32.lib)
|
||||
endif()
|
||||
|
||||
# List additional libraries required to use ODBC library
|
||||
if(WIN32 AND MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Intel")
|
||||
set(_odbc_required_libs_names odbccp32;ws2_32)
|
||||
endif()
|
||||
foreach(_lib_name IN LISTS _odbc_required_libs_names)
|
||||
find_library(_lib_path
|
||||
NAMES ${_lib_name}
|
||||
HINTS
|
||||
${ODBC_ROOT_DIR}/lib
|
||||
${ODBC_ROOT_LIBRARY_DIRS}
|
||||
PATHS
|
||||
${PC_ODBC_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/usr/local/odbc/lib
|
||||
/usr/local/iodbc/lib
|
||||
"C:/Program Files/ODBC/lib"
|
||||
"C:/ODBC/lib/debug"
|
||||
"C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Lib"
|
||||
PATH_SUFFIXES
|
||||
odbc
|
||||
)
|
||||
|
||||
# MinGW find usually fails
|
||||
if(MINGW)
|
||||
set(ODBC_INCLUDE_DIRECTORIES ".")
|
||||
set(ODBC_LIBRARIES odbc32)
|
||||
if (_lib_path)
|
||||
list(APPEND _odbc_required_libs_paths ${_lib_path})
|
||||
endif()
|
||||
unset(_lib_path CACHE)
|
||||
endforeach()
|
||||
unset(_odbc_lib_paths)
|
||||
unset(_odbc_required_libs_names)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(ODBC
|
||||
DEFAULT_MSG
|
||||
ODBC_INCLUDE_DIRECTORIES
|
||||
ODBC_LIBRARIES)
|
||||
|
||||
mark_as_advanced(ODBC_FOUND ODBC_LIBRARIES ODBC_INCLUDE_DIRECTORIES)
|
||||
endif ()
|
||||
endif ()
|
||||
find_package_handle_standard_args(ODBC
|
||||
FOUND_VAR ODBC_FOUND
|
||||
REQUIRED_VARS
|
||||
ODBC_LIBRARY
|
||||
${REQUIRED_INCLUDE_DIR}
|
||||
VERSION_VAR ODBC_VERSION
|
||||
)
|
||||
|
||||
message (STATUS "Using odbc: ${ODBC_INCLUDE_DIRECTORIES} : ${ODBC_LIBRARIES}")
|
||||
if(ODBC_FOUND)
|
||||
set(ODBC_LIBRARIES ${ODBC_LIBRARY} ${_odbc_required_libs_paths})
|
||||
set(ODBC_INCLUDE_DIRS ${ODBC_INCLUDE_DIR})
|
||||
set(ODBC_DEFINITIONS ${PC_ODBC_CFLAGS_OTHER})
|
||||
endif()
|
||||
|
||||
if(ODBC_FOUND AND NOT TARGET ODBC::ODBC)
|
||||
add_library(ODBC::ODBC UNKNOWN IMPORTED)
|
||||
set_target_properties(ODBC::ODBC PROPERTIES
|
||||
IMPORTED_LOCATION "${ODBC_LIBRARY}"
|
||||
INTERFACE_LINK_LIBRARIES "${_odbc_required_libs_paths}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PC_ODBC_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${ODBC_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(ODBC_LIBRARY ODBC_INCLUDE_DIR)
|
||||
|
@ -203,12 +203,12 @@ endforeach()
|
||||
|
||||
if(Poco_DataODBC_LIBRARY)
|
||||
list(APPEND Poco_DataODBC_LIBRARY ${ODBC_LIBRARIES} ${LTDL_LIBRARY})
|
||||
list(APPEND Poco_INCLUDE_DIRS ${ODBC_INCLUDE_DIRECTORIES})
|
||||
list(APPEND Poco_INCLUDE_DIRS ${ODBC_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(Poco_SQLODBC_LIBRARY)
|
||||
list(APPEND Poco_SQLODBC_LIBRARY ${ODBC_LIBRARIES} ${LTDL_LIBRARY})
|
||||
list(APPEND Poco_INCLUDE_DIRS ${ODBC_INCLUDE_DIRECTORIES})
|
||||
list(APPEND Poco_INCLUDE_DIRS ${ODBC_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(Poco_NetSSL_LIBRARY)
|
||||
|
@ -1,8 +1,7 @@
|
||||
|
||||
find_program (CCACHE_FOUND ccache)
|
||||
if (CCACHE_FOUND AND NOT CMAKE_CXX_COMPILER_LAUNCHER MATCHES "ccache" AND NOT CMAKE_CXX_COMPILER MATCHES "ccache")
|
||||
execute_process(COMMAND ${CCACHE_FOUND} "-V" OUTPUT_VARIABLE CCACHE_VERSION)
|
||||
string(REGEX REPLACE "ccache version ([0-9\\.]+).*" "\\1" CCACHE_VERSION ${CCACHE_VERSION} )
|
||||
string(REGEX REPLACE "ccache version ([0-9\\.]+).*" "\\1" CCACHE_VERSION ${CCACHE_VERSION})
|
||||
|
||||
if (CCACHE_VERSION VERSION_GREATER "3.2.0" OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
#message(STATUS "Using ${CCACHE_FOUND} ${CCACHE_VERSION}")
|
||||
|
@ -1,6 +1,9 @@
|
||||
option(ENABLE_ICU "Enable ICU" ON)
|
||||
|
||||
if(ENABLE_ICU)
|
||||
if (APPLE)
|
||||
set(ICU_ROOT "/usr/local/opt/icu4c" CACHE STRING "")
|
||||
endif()
|
||||
find_package(ICU COMPONENTS i18n uc data) # TODO: remove Modules/FindICU.cmake after cmake 3.7
|
||||
#set (ICU_LIBRARIES ${ICU_I18N_LIBRARY} ${ICU_UC_LIBRARY} ${ICU_DATA_LIBRARY} CACHE STRING "")
|
||||
if(ICU_FOUND)
|
||||
|
@ -1,4 +1,4 @@
|
||||
if (NOT SANITIZE AND NOT ARCH_ARM AND NOT ARCH_32 AND NOT ARCH_PPC64LE AND NOT OS_FREEBSD)
|
||||
if (NOT SANITIZE AND NOT ARCH_ARM AND NOT ARCH_32 AND NOT ARCH_PPC64LE AND NOT OS_FREEBSD AND NOT APPLE)
|
||||
option (ENABLE_LFALLOC "Set to FALSE to use system libgsasl library instead of bundled" ${NOT_UNBUNDLED})
|
||||
endif ()
|
||||
|
||||
|
@ -1,93 +1,34 @@
|
||||
# This file copied from contrib/poco/cmake/FindODBC.cmake to allow build without submodules
|
||||
|
||||
#
|
||||
# Find the ODBC driver manager includes and library.
|
||||
#
|
||||
# ODBC is an open standard for connecting to different databases in a
|
||||
# semi-vendor-independent fashion. First you install the ODBC driver
|
||||
# manager. Then you need a driver for each separate database you want
|
||||
# to connect to (unless a generic one works). VTK includes neither
|
||||
# the driver manager nor the vendor-specific drivers: you have to find
|
||||
# those yourself.
|
||||
#
|
||||
# This module defines
|
||||
# ODBC_INCLUDE_DIRECTORIES, where to find sql.h
|
||||
# ODBC_LIBRARIES, the libraries to link against to use ODBC
|
||||
# ODBC_FOUND. If false, you cannot build anything that requires ODBC.
|
||||
|
||||
option (ENABLE_ODBC "Enable ODBC" ${OS_LINUX})
|
||||
if (OS_LINUX)
|
||||
option (USE_INTERNAL_ODBC_LIBRARY "Set to FALSE to use system odbc library instead of bundled" ${NOT_UNBUNDLED})
|
||||
else ()
|
||||
option (USE_INTERNAL_ODBC_LIBRARY "Set to FALSE to use system odbc library instead of bundled" OFF)
|
||||
endif ()
|
||||
|
||||
if (USE_INTERNAL_ODBC_LIBRARY AND NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/unixodbc/README")
|
||||
message (WARNING "submodule contrib/unixodbc is missing. to fix try run: \n git submodule update --init --recursive")
|
||||
set (USE_INTERNAL_ODBC_LIBRARY 0)
|
||||
endif ()
|
||||
|
||||
set (ODBC_INCLUDE_DIRECTORIES ) # Include directories will be either used automatically by target_include_directories or set later.
|
||||
|
||||
if (ENABLE_ODBC)
|
||||
if (USE_INTERNAL_ODBC_LIBRARY)
|
||||
set (ODBC_LIBRARIES unixodbc)
|
||||
set (ODBC_FOUND 1)
|
||||
set (USE_ODBC 1)
|
||||
if(ENABLE_ODBC)
|
||||
if (OS_LINUX)
|
||||
option(USE_INTERNAL_ODBC_LIBRARY "Set to FALSE to use system odbc library instead of bundled" ${NOT_UNBUNDLED})
|
||||
else ()
|
||||
find_path(ODBC_INCLUDE_DIRECTORIES
|
||||
NAMES sql.h
|
||||
HINTS
|
||||
/usr/include
|
||||
/usr/include/iodbc
|
||||
/usr/include/odbc
|
||||
/usr/local/include
|
||||
/usr/local/include/iodbc
|
||||
/usr/local/include/odbc
|
||||
/usr/local/iodbc/include
|
||||
/usr/local/odbc/include
|
||||
"C:/Program Files/ODBC/include"
|
||||
"C:/Program Files/Microsoft SDKs/Windows/v7.0/include"
|
||||
"C:/Program Files/Microsoft SDKs/Windows/v6.0a/include"
|
||||
"C:/ODBC/include"
|
||||
DOC "Specify the directory containing sql.h."
|
||||
)
|
||||
option(USE_INTERNAL_ODBC_LIBRARY "Set to FALSE to use system odbc library instead of bundled" OFF)
|
||||
endif()
|
||||
|
||||
find_library(ODBC_LIBRARIES
|
||||
NAMES iodbc odbc iodbcinst odbcinst odbc32
|
||||
HINTS
|
||||
/usr/lib
|
||||
/usr/lib/iodbc
|
||||
/usr/lib/odbc
|
||||
/usr/local/lib
|
||||
/usr/local/lib/iodbc
|
||||
/usr/local/lib/odbc
|
||||
/usr/local/iodbc/lib
|
||||
/usr/local/odbc/lib
|
||||
"C:/Program Files/ODBC/lib"
|
||||
"C:/ODBC/lib/debug"
|
||||
"C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Lib"
|
||||
DOC "Specify the ODBC driver manager library here."
|
||||
)
|
||||
if(USE_INTERNAL_ODBC_LIBRARY AND NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/unixodbc/README")
|
||||
message(WARNING "submodule contrib/unixodbc is missing. to fix try run: \n git submodule update --init --recursive")
|
||||
set(USE_INTERNAL_ODBC_LIBRARY 0)
|
||||
set(MISSING_INTERNAL_ODBC_LIBRARY 1)
|
||||
endif()
|
||||
|
||||
# MinGW find usually fails
|
||||
if (MINGW)
|
||||
set(ODBC_INCLUDE_DIRECTORIES ".")
|
||||
set(ODBC_LIBRARIES odbc32)
|
||||
set(ODBC_INCLUDE_DIRS ) # Include directories will be either used automatically by target_include_directories or set later.
|
||||
if(USE_INTERNAL_ODBC_LIBRARY AND NOT MISSING_INTERNAL_ODBC_LIBRARY)
|
||||
set(ODBC_LIBRARY unixodbc)
|
||||
set(ODBC_LIBRARIES ${ODBC_LIBRARY})
|
||||
set(ODBC_INCLUDE_DIRS "${ClickHouse_SOURCE_DIR}/contrib/unixodbc/include")
|
||||
set(ODBC_FOUND 1)
|
||||
else()
|
||||
find_package(ODBC)
|
||||
endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(ODBC
|
||||
DEFAULT_MSG
|
||||
ODBC_INCLUDE_DIRECTORIES
|
||||
ODBC_LIBRARIES)
|
||||
if(ODBC_FOUND)
|
||||
set(USE_ODBC 1)
|
||||
set(ODBC_INCLUDE_DIRECTORIES ${ODBC_INCLUDE_DIRS}) # for old poco
|
||||
set(ODBC_INCLUDE_DIR ${ODBC_INCLUDE_DIRS}) # for old poco
|
||||
endif()
|
||||
|
||||
if (USE_STATIC_LIBRARIES)
|
||||
list(APPEND ODBC_LIBRARIES ${LTDL_LIBRARY})
|
||||
endif ()
|
||||
|
||||
mark_as_advanced(ODBC_FOUND ODBC_LIBRARIES ODBC_INCLUDE_DIRECTORIES)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
message (STATUS "Using odbc=${ODBC_FOUND}: ${ODBC_INCLUDE_DIRECTORIES} : ${ODBC_LIBRARIES}")
|
||||
message(STATUS "Using odbc=${USE_ODBC}: ${ODBC_INCLUDE_DIRS} : ${ODBC_LIBRARIES}")
|
||||
endif()
|
||||
|
@ -76,7 +76,7 @@ elseif (NOT MISSING_INTERNAL_POCO_LIBRARY)
|
||||
set (Poco_SQLODBC_INCLUDE_DIR
|
||||
"${ClickHouse_SOURCE_DIR}/contrib/poco/SQL/ODBC/include/"
|
||||
"${ClickHouse_SOURCE_DIR}/contrib/poco/Data/ODBC/include/"
|
||||
${ODBC_INCLUDE_DIRECTORIES}
|
||||
${ODBC_INCLUDE_DIRS}
|
||||
)
|
||||
set (Poco_SQLODBC_LIBRARY PocoSQLODBC ${ODBC_LIBRARIES} ${LTDL_LIBRARY})
|
||||
endif ()
|
||||
@ -88,7 +88,7 @@ elseif (NOT MISSING_INTERNAL_POCO_LIBRARY)
|
||||
set (USE_POCO_DATAODBC 1)
|
||||
set (Poco_DataODBC_INCLUDE_DIR
|
||||
"${ClickHouse_SOURCE_DIR}/contrib/poco/Data/ODBC/include/"
|
||||
${ODBC_INCLUDE_DIRECTORIES}
|
||||
${ODBC_INCLUDE_DIRS}
|
||||
)
|
||||
set (Poco_DataODBC_LIBRARY PocoDataODBC ${ODBC_LIBRARIES} ${LTDL_LIBRARY})
|
||||
endif ()
|
||||
|
14
cmake/find_simdjson.cmake
Normal file
14
cmake/find_simdjson.cmake
Normal file
@ -0,0 +1,14 @@
|
||||
if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/simdjson/include/simdjson/jsonparser.h")
|
||||
message (WARNING "submodule contrib/simdjson is missing. to fix try run: \n git submodule update --init --recursive")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
if (NOT HAVE_AVX2)
|
||||
message (WARNING "submodule contrib/simdjson requires AVX2 support")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
option (USE_SIMDJSON "Use simdjson" ON)
|
||||
|
||||
set (SIMDJSON_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/simdjson/include")
|
||||
set (SIMDJSON_LIBRARY "simdjson")
|
@ -60,6 +60,72 @@ if(OPENSSL_FOUND)
|
||||
set(USE_SSL 1)
|
||||
endif()
|
||||
|
||||
# used by new poco
|
||||
# part from /usr/share/cmake-*/Modules/FindOpenSSL.cmake, with removed all "EXISTS "
|
||||
if(OPENSSL_FOUND AND NOT USE_INTERNAL_SSL_LIBRARY)
|
||||
if(NOT TARGET OpenSSL::Crypto AND
|
||||
(OPENSSL_CRYPTO_LIBRARY OR
|
||||
LIB_EAY_LIBRARY_DEBUG OR
|
||||
LIB_EAY_LIBRARY_RELEASE)
|
||||
)
|
||||
add_library(OpenSSL::Crypto UNKNOWN IMPORTED)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
|
||||
if(OPENSSL_CRYPTO_LIBRARY)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${OPENSSL_CRYPTO_LIBRARY}")
|
||||
endif()
|
||||
if(LIB_EAY_LIBRARY_RELEASE)
|
||||
set_property(TARGET OpenSSL::Crypto APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
|
||||
IMPORTED_LOCATION_RELEASE "${LIB_EAY_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
if(LIB_EAY_LIBRARY_DEBUG)
|
||||
set_property(TARGET OpenSSL::Crypto APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
|
||||
IMPORTED_LOCATION_DEBUG "${LIB_EAY_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT TARGET OpenSSL::SSL AND
|
||||
(OPENSSL_SSL_LIBRARY OR
|
||||
SSL_EAY_LIBRARY_DEBUG OR
|
||||
SSL_EAY_LIBRARY_RELEASE)
|
||||
)
|
||||
add_library(OpenSSL::SSL UNKNOWN IMPORTED)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
|
||||
if(OPENSSL_SSL_LIBRARY)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${OPENSSL_SSL_LIBRARY}")
|
||||
endif()
|
||||
if(SSL_EAY_LIBRARY_RELEASE)
|
||||
set_property(TARGET OpenSSL::SSL APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
|
||||
IMPORTED_LOCATION_RELEASE "${SSL_EAY_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
if(SSL_EAY_LIBRARY_DEBUG)
|
||||
set_property(TARGET OpenSSL::SSL APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
|
||||
IMPORTED_LOCATION_DEBUG "${SSL_EAY_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
if(TARGET OpenSSL::Crypto)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES OpenSSL::Crypto)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
endif ()
|
||||
|
||||
message (STATUS "Using ssl=${USE_SSL}: ${OPENSSL_INCLUDE_DIR} : ${OPENSSL_LIBRARIES}")
|
||||
|
10
contrib/CMakeLists.txt
vendored
10
contrib/CMakeLists.txt
vendored
@ -120,6 +120,9 @@ if (USE_INTERNAL_SSL_LIBRARY)
|
||||
target_include_directories(${OPENSSL_CRYPTO_LIBRARY} SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
target_include_directories(${OPENSSL_SSL_LIBRARY} SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
set (POCO_SKIP_OPENSSL_FIND 1)
|
||||
|
||||
add_library(OpenSSL::Crypto ALIAS ${OPENSSL_CRYPTO_LIBRARY})
|
||||
add_library(OpenSSL::SSL ALIAS ${OPENSSL_SSL_LIBRARY})
|
||||
endif ()
|
||||
|
||||
if (ENABLE_MYSQL AND USE_INTERNAL_MYSQL_LIBRARY)
|
||||
@ -144,6 +147,7 @@ endif()
|
||||
|
||||
if (ENABLE_ODBC AND USE_INTERNAL_ODBC_LIBRARY)
|
||||
add_subdirectory (unixodbc-cmake)
|
||||
add_library(ODBC::ODBC ALIAS ${ODBC_LIBRARIES})
|
||||
endif ()
|
||||
|
||||
if (USE_INTERNAL_CAPNP_LIBRARY)
|
||||
@ -223,7 +227,7 @@ if (USE_INTERNAL_POCO_LIBRARY)
|
||||
set (ENABLE_TESTS 0)
|
||||
set (POCO_ENABLE_TESTS 0)
|
||||
set (CMAKE_DISABLE_FIND_PACKAGE_ZLIB 1)
|
||||
if (MSVC)
|
||||
if (MSVC OR NOT USE_POCO_DATAODBC)
|
||||
set (ENABLE_DATA_ODBC 0 CACHE INTERNAL "") # TODO (build fail)
|
||||
endif ()
|
||||
add_subdirectory (poco)
|
||||
@ -309,3 +313,7 @@ endif()
|
||||
if (USE_INTERNAL_HYPERSCAN_LIBRARY)
|
||||
add_subdirectory (hyperscan)
|
||||
endif()
|
||||
|
||||
if (USE_SIMDJSON)
|
||||
add_subdirectory (simdjson-cmake)
|
||||
endif()
|
||||
|
@ -358,8 +358,15 @@ static char* AllocWithMMap(uintptr_t sz, EMMapMode mode) {
|
||||
}
|
||||
#if defined(USE_LFALLOC_RANDOM_HINT)
|
||||
static thread_local std::mt19937_64 generator(std::random_device{}());
|
||||
std::uniform_int_distribution<intptr_t> distr(areaStart, areaFinish / 2);
|
||||
char* largeBlock = (char*)mmap(reinterpret_cast<void*>(distr(generator)), sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
std::uniform_int_distribution<intptr_t> distr(areaStart, areaFinish - sz - 1);
|
||||
char* largeBlock;
|
||||
static constexpr size_t MaxAttempts = 100;
|
||||
size_t attempt = 0;
|
||||
do
|
||||
{
|
||||
largeBlock = (char*)mmap(reinterpret_cast<void*>(distr(generator)), sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
++attempt;
|
||||
} while (uintptr_t(((char*)largeBlock - ALLOC_START) + sz) >= areaFinish && attempt < MaxAttempts && munmap(largeBlock, sz) == 0);
|
||||
#else
|
||||
char* largeBlock = (char*)mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
#endif
|
||||
|
@ -1,60 +1,54 @@
|
||||
set(LIBXML2_SOURCE_DIR ${CMAKE_SOURCE_DIR}/contrib/libxml2)
|
||||
set(LIBXML2_BINARY_DIR ${CMAKE_BINARY_DIR}/contrib/libxml2)
|
||||
|
||||
|
||||
set(SRCS
|
||||
${LIBXML2_SOURCE_DIR}/parser.c
|
||||
${LIBXML2_SOURCE_DIR}/HTMLparser.c
|
||||
${LIBXML2_SOURCE_DIR}/buf.c
|
||||
${LIBXML2_SOURCE_DIR}/xzlib.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlregexp.c
|
||||
${LIBXML2_SOURCE_DIR}/entities.c
|
||||
${LIBXML2_SOURCE_DIR}/rngparser.c
|
||||
${LIBXML2_SOURCE_DIR}/encoding.c
|
||||
${LIBXML2_SOURCE_DIR}/legacy.c
|
||||
${LIBXML2_SOURCE_DIR}/error.c
|
||||
${LIBXML2_SOURCE_DIR}/debugXML.c
|
||||
${LIBXML2_SOURCE_DIR}/xpointer.c
|
||||
${LIBXML2_SOURCE_DIR}/DOCBparser.c
|
||||
#${LIBXML2_SOURCE_DIR}/xmlcatalog.c
|
||||
${LIBXML2_SOURCE_DIR}/c14n.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlreader.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlstring.c
|
||||
${LIBXML2_SOURCE_DIR}/dict.c
|
||||
${LIBXML2_SOURCE_DIR}/xpath.c
|
||||
${LIBXML2_SOURCE_DIR}/tree.c
|
||||
${LIBXML2_SOURCE_DIR}/trionan.c
|
||||
${LIBXML2_SOURCE_DIR}/pattern.c
|
||||
${LIBXML2_SOURCE_DIR}/globals.c
|
||||
#${LIBXML2_SOURCE_DIR}/xmllint.c
|
||||
${LIBXML2_SOURCE_DIR}/chvalid.c
|
||||
${LIBXML2_SOURCE_DIR}/relaxng.c
|
||||
${LIBXML2_SOURCE_DIR}/list.c
|
||||
${LIBXML2_SOURCE_DIR}/xinclude.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlIO.c
|
||||
${LIBXML2_SOURCE_DIR}/triostr.c
|
||||
${LIBXML2_SOURCE_DIR}/hash.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlsave.c
|
||||
${LIBXML2_SOURCE_DIR}/HTMLtree.c
|
||||
${LIBXML2_SOURCE_DIR}/SAX.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlschemas.c
|
||||
${LIBXML2_SOURCE_DIR}/SAX2.c
|
||||
${LIBXML2_SOURCE_DIR}/threads.c
|
||||
#${LIBXML2_SOURCE_DIR}/runsuite.c
|
||||
${LIBXML2_SOURCE_DIR}/catalog.c
|
||||
${LIBXML2_SOURCE_DIR}/uri.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlmodule.c
|
||||
${LIBXML2_SOURCE_DIR}/xlink.c
|
||||
${LIBXML2_SOURCE_DIR}/entities.c
|
||||
${LIBXML2_SOURCE_DIR}/encoding.c
|
||||
${LIBXML2_SOURCE_DIR}/error.c
|
||||
${LIBXML2_SOURCE_DIR}/parserInternals.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlwriter.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlunicode.c
|
||||
#${LIBXML2_SOURCE_DIR}/runxmlconf.c
|
||||
${LIBXML2_SOURCE_DIR}/parser.c
|
||||
${LIBXML2_SOURCE_DIR}/tree.c
|
||||
${LIBXML2_SOURCE_DIR}/hash.c
|
||||
${LIBXML2_SOURCE_DIR}/list.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlIO.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlmemory.c
|
||||
${LIBXML2_SOURCE_DIR}/nanoftp.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlschemastypes.c
|
||||
${LIBXML2_SOURCE_DIR}/uri.c
|
||||
${LIBXML2_SOURCE_DIR}/valid.c
|
||||
${LIBXML2_SOURCE_DIR}/xlink.c
|
||||
${LIBXML2_SOURCE_DIR}/HTMLparser.c
|
||||
${LIBXML2_SOURCE_DIR}/HTMLtree.c
|
||||
${LIBXML2_SOURCE_DIR}/debugXML.c
|
||||
${LIBXML2_SOURCE_DIR}/xpath.c
|
||||
${LIBXML2_SOURCE_DIR}/xpointer.c
|
||||
${LIBXML2_SOURCE_DIR}/xinclude.c
|
||||
${LIBXML2_SOURCE_DIR}/nanohttp.c
|
||||
${LIBXML2_SOURCE_DIR}/nanoftp.c
|
||||
${LIBXML2_SOURCE_DIR}/DOCBparser.c
|
||||
${LIBXML2_SOURCE_DIR}/catalog.c
|
||||
${LIBXML2_SOURCE_DIR}/globals.c
|
||||
${LIBXML2_SOURCE_DIR}/threads.c
|
||||
${LIBXML2_SOURCE_DIR}/c14n.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlstring.c
|
||||
${LIBXML2_SOURCE_DIR}/buf.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlregexp.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlschemas.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlschemastypes.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlunicode.c
|
||||
${LIBXML2_SOURCE_DIR}/triostr.c
|
||||
#${LIBXML2_SOURCE_DIR}/trio.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlreader.c
|
||||
${LIBXML2_SOURCE_DIR}/relaxng.c
|
||||
${LIBXML2_SOURCE_DIR}/dict.c
|
||||
${LIBXML2_SOURCE_DIR}/SAX2.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlwriter.c
|
||||
${LIBXML2_SOURCE_DIR}/legacy.c
|
||||
${LIBXML2_SOURCE_DIR}/chvalid.c
|
||||
${LIBXML2_SOURCE_DIR}/pattern.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlsave.c
|
||||
${LIBXML2_SOURCE_DIR}/xmlmodule.c
|
||||
${LIBXML2_SOURCE_DIR}/schematron.c
|
||||
${LIBXML2_SOURCE_DIR}/xzlib.c
|
||||
)
|
||||
add_library(libxml2 ${SRCS})
|
||||
|
||||
|
2
contrib/lz4
vendored
2
contrib/lz4
vendored
@ -1 +1 @@
|
||||
Subproject commit 780aac520b69d6369f4e3995624c37e56d75498d
|
||||
Subproject commit 7a4e3b1fac5cd9d4ec7c8d0091329ba107ec2131
|
1
contrib/simdjson
vendored
Submodule
1
contrib/simdjson
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 681cd3369860f4eada49a387cbff93030f759c95
|
26
contrib/simdjson-cmake/CMakeLists.txt
Normal file
26
contrib/simdjson-cmake/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
if (NOT HAVE_AVX2)
|
||||
message (FATAL_ERROR "No AVX2 support")
|
||||
endif ()
|
||||
|
||||
if(MAKE_STATIC_LIBRARIES)
|
||||
set(SIMDJSON_LIB_TYPE STATIC)
|
||||
MESSAGE(STATUS "Building static library ${SIMDJSON_LIBRARY}")
|
||||
else()
|
||||
set(SIMDJSON_LIB_TYPE SHARED)
|
||||
MESSAGE(STATUS "Building dynamic library ${SIMDJSON_LIBRARY}")
|
||||
endif()
|
||||
|
||||
set(SIMDJSON_SRC_DIR "${SIMDJSON_INCLUDE_DIR}/../src")
|
||||
set(SIMDJSON_SRC
|
||||
${SIMDJSON_SRC_DIR}/jsonioutil.cpp
|
||||
${SIMDJSON_SRC_DIR}/jsonminifier.cpp
|
||||
${SIMDJSON_SRC_DIR}/jsonparser.cpp
|
||||
${SIMDJSON_SRC_DIR}/stage1_find_marks.cpp
|
||||
${SIMDJSON_SRC_DIR}/stage2_build_tape.cpp
|
||||
${SIMDJSON_SRC_DIR}/parsedjson.cpp
|
||||
${SIMDJSON_SRC_DIR}/parsedjsoniterator.cpp
|
||||
)
|
||||
|
||||
add_library(${SIMDJSON_LIBRARY} ${SIMDJSON_LIB_TYPE} ${SIMDJSON_SRC})
|
||||
target_include_directories(${SIMDJSON_LIBRARY} PRIVATE "${SIMDJSON_INCLUDE_DIR}")
|
||||
target_compile_options(${SIMDJSON_LIBRARY} PRIVATE -mavx2 -mbmi -mbmi2 -mpclmul)
|
@ -275,13 +275,13 @@ ${ODBC_SOURCE_DIR}/lst/_lstVisible.c
|
||||
|
||||
add_library(unixodbc ${SRCS})
|
||||
|
||||
target_link_libraries(unixodbc ltdl)
|
||||
target_link_libraries(unixodbc PRIVATE ltdl)
|
||||
|
||||
# SYSTEM_FILE_PATH was changed to /etc
|
||||
|
||||
target_include_directories(unixodbc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/linux_x86_64/private)
|
||||
target_include_directories(unixodbc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/linux_x86_64)
|
||||
target_include_directories(unixodbc PUBLIC ${ODBC_SOURCE_DIR}/include)
|
||||
target_include_directories(unixodbc SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/linux_x86_64/private)
|
||||
target_include_directories(unixodbc SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/linux_x86_64)
|
||||
target_include_directories(unixodbc SYSTEM PUBLIC ${ODBC_SOURCE_DIR}/include)
|
||||
|
||||
target_compile_definitions(unixodbc PRIVATE -DHAVE_CONFIG_H)
|
||||
|
||||
|
@ -257,8 +257,8 @@ if (USE_POCO_SQLODBC)
|
||||
target_link_libraries (clickhouse_common_io PRIVATE ${Poco_SQL_LIBRARY})
|
||||
target_link_libraries (dbms PRIVATE ${Poco_SQLODBC_LIBRARY} ${Poco_SQL_LIBRARY})
|
||||
if (NOT USE_INTERNAL_POCO_LIBRARY)
|
||||
target_include_directories (clickhouse_common_io SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_SQL_INCLUDE_DIR})
|
||||
target_include_directories (dbms SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_SQLODBC_INCLUDE_DIR} PUBLIC ${Poco_SQL_INCLUDE_DIR})
|
||||
target_include_directories (clickhouse_common_io SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_SQL_INCLUDE_DIR})
|
||||
target_include_directories (dbms SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_SQLODBC_INCLUDE_DIR} SYSTEM PUBLIC ${Poco_SQL_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -271,7 +271,7 @@ if (USE_POCO_DATAODBC)
|
||||
target_link_libraries (clickhouse_common_io PRIVATE ${Poco_Data_LIBRARY})
|
||||
target_link_libraries (dbms PRIVATE ${Poco_DataODBC_LIBRARY})
|
||||
if (NOT USE_INTERNAL_POCO_LIBRARY)
|
||||
target_include_directories (dbms SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_DataODBC_INCLUDE_DIR})
|
||||
target_include_directories (dbms SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_DataODBC_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
# This strings autochanged from release_lib.sh:
|
||||
set(VERSION_REVISION 54419)
|
||||
set(VERSION_REVISION 54420)
|
||||
set(VERSION_MAJOR 19)
|
||||
set(VERSION_MINOR 7)
|
||||
set(VERSION_MINOR 8)
|
||||
set(VERSION_PATCH 1)
|
||||
set(VERSION_GITHASH b0b369b30f04a5026d1da5c7d3fd5998d6de1fe4)
|
||||
set(VERSION_DESCRIBE v19.7.1.1-testing)
|
||||
set(VERSION_STRING 19.7.1.1)
|
||||
set(VERSION_GITHASH a76e504f45ff4a74e8c492bd269f022352d5f6d9)
|
||||
set(VERSION_DESCRIBE v19.8.1.1-testing)
|
||||
set(VERSION_STRING 19.8.1.1)
|
||||
# end of autochange
|
||||
|
||||
set(VERSION_EXTRA "" CACHE STRING "")
|
||||
|
@ -209,11 +209,6 @@ else ()
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/clickhouse-obfuscator DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT clickhouse)
|
||||
list(APPEND CLICKHOUSE_BUNDLE clickhouse-obfuscator)
|
||||
endif ()
|
||||
if (ENABLE_CLICKHOUSE_ODBC_BRIDGE)
|
||||
# just to be able to run integration tests
|
||||
add_custom_target (clickhouse-odbc-bridge-copy ALL COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/odbc-bridge/clickhouse-odbc-bridge clickhouse-odbc-bridge DEPENDS clickhouse-odbc-bridge)
|
||||
endif ()
|
||||
|
||||
|
||||
# install always because depian package want this files:
|
||||
add_custom_target (clickhouse-clang ALL COMMAND ${CMAKE_COMMAND} -E create_symlink clickhouse clickhouse-clang DEPENDS clickhouse)
|
||||
|
@ -451,14 +451,14 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv)
|
||||
("password", value<std::string>()->default_value(""), "")
|
||||
("database", value<std::string>()->default_value("default"), "")
|
||||
("stacktrace", "print stack traces of exceptions")
|
||||
|
||||
#define DECLARE_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) (#NAME, boost::program_options::value<std::string> (), DESCRIPTION)
|
||||
APPLY_FOR_SETTINGS(DECLARE_SETTING)
|
||||
#undef DECLARE_SETTING
|
||||
;
|
||||
|
||||
Settings settings;
|
||||
settings.addProgramOptions(desc);
|
||||
|
||||
boost::program_options::variables_map options;
|
||||
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), options);
|
||||
boost::program_options::notify(options);
|
||||
|
||||
if (options.count("help"))
|
||||
{
|
||||
@ -469,15 +469,6 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv)
|
||||
|
||||
print_stacktrace = options.count("stacktrace");
|
||||
|
||||
/// Extract `settings` and `limits` from received `options`
|
||||
Settings settings;
|
||||
|
||||
#define EXTRACT_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
if (options.count(#NAME)) \
|
||||
settings.set(#NAME, options[#NAME].as<std::string>());
|
||||
APPLY_FOR_SETTINGS(EXTRACT_SETTING)
|
||||
#undef EXTRACT_SETTING
|
||||
|
||||
UseSSL use_ssl;
|
||||
|
||||
Benchmark benchmark(
|
||||
|
@ -217,11 +217,12 @@ private:
|
||||
context.setApplicationType(Context::ApplicationType::CLIENT);
|
||||
|
||||
/// settings and limits could be specified in config file, but passed settings has higher priority
|
||||
#define EXTRACT_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
if (config().has(#NAME) && !context.getSettingsRef().NAME.changed) \
|
||||
context.setSetting(#NAME, config().getString(#NAME));
|
||||
APPLY_FOR_SETTINGS(EXTRACT_SETTING)
|
||||
#undef EXTRACT_SETTING
|
||||
for (auto && setting : context.getSettingsRef())
|
||||
{
|
||||
const String & name = setting.getName().toString();
|
||||
if (config().has(name) && !setting.isChanged())
|
||||
setting.setValue(config().getString(name));
|
||||
}
|
||||
|
||||
/// Set path for format schema files
|
||||
if (config().has("format_schema_path"))
|
||||
@ -479,6 +480,17 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
/// This is intended for testing purposes.
|
||||
if (config().getBool("always_load_suggestion_data", false))
|
||||
{
|
||||
#if USE_READLINE
|
||||
SCOPE_EXIT({ Suggest::instance().finalize(); });
|
||||
Suggest::instance().load(connection_parameters, config().getInt("suggestion_limit"));
|
||||
#else
|
||||
throw Exception("Command line suggestions cannot work without readline", ErrorCodes::BAD_ARGUMENTS);
|
||||
#endif
|
||||
}
|
||||
|
||||
query_id = config().getString("query_id", "");
|
||||
nonInteractive();
|
||||
|
||||
@ -805,8 +817,7 @@ private:
|
||||
{
|
||||
if (!old_settings)
|
||||
old_settings.emplace(context.getSettingsRef());
|
||||
for (const auto & change : settings_ast.as<ASTSetQuery>()->changes)
|
||||
context.setSetting(change.name, change.value);
|
||||
context.applySettingsChanges(settings_ast.as<ASTSetQuery>()->changes);
|
||||
};
|
||||
const auto * insert = parsed_query->as<ASTInsertQuery>();
|
||||
if (insert && insert->settings_ast)
|
||||
@ -836,7 +847,7 @@ private:
|
||||
if (change.name == "profile")
|
||||
current_profile = change.value.safeGet<String>();
|
||||
else
|
||||
context.setSetting(change.name, change.value);
|
||||
context.applySettingChange(change);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1604,8 +1615,6 @@ public:
|
||||
min_description_length = std::min(min_description_length, line_length - 2);
|
||||
}
|
||||
|
||||
#define DECLARE_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) (#NAME, po::value<std::string>(), DESCRIPTION)
|
||||
|
||||
/// Main commandline options related to client functionality and all parameters from Settings.
|
||||
po::options_description main_description("Main options", line_length, min_description_length);
|
||||
main_description.add_options()
|
||||
@ -1629,6 +1638,7 @@ public:
|
||||
("database,d", po::value<std::string>(), "database")
|
||||
("pager", po::value<std::string>(), "pager")
|
||||
("disable_suggestion,A", "Disable loading suggestion data. Note that suggestion data is loaded asynchronously through a second connection to ClickHouse server. Also it is reasonable to disable suggestion if you want to paste a query with TAB characters. Shorthand option -A is for those who get used to mysql client.")
|
||||
("always_load_suggestion_data", "Load suggestion data even if clickhouse-client is run in non-interactive mode. Used for testing.")
|
||||
("suggestion_limit", po::value<int>()->default_value(10000),
|
||||
"Suggestion limit for how many databases, tables and columns to fetch.")
|
||||
("multiline,m", "multiline")
|
||||
@ -1647,9 +1657,9 @@ public:
|
||||
("compression", po::value<bool>(), "enable or disable compression")
|
||||
("log-level", po::value<std::string>(), "client log level")
|
||||
("server_logs_file", po::value<std::string>(), "put server logs into specified file")
|
||||
APPLY_FOR_SETTINGS(DECLARE_SETTING)
|
||||
;
|
||||
#undef DECLARE_SETTING
|
||||
|
||||
context.getSettingsRef().addProgramOptions(main_description);
|
||||
|
||||
/// Commandline options related to external tables.
|
||||
po::options_description external_description("External tables options");
|
||||
@ -1665,6 +1675,8 @@ public:
|
||||
common_arguments.size(), common_arguments.data()).options(main_description).run();
|
||||
po::variables_map options;
|
||||
po::store(parsed, options);
|
||||
po::notify(options);
|
||||
|
||||
if (options.count("version") || options.count("V"))
|
||||
{
|
||||
showClientVersion();
|
||||
@ -1715,15 +1727,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract settings from the options.
|
||||
#define EXTRACT_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
if (options.count(#NAME)) \
|
||||
{ \
|
||||
context.setSetting(#NAME, options[#NAME].as<std::string>()); \
|
||||
config().setString(#NAME, options[#NAME].as<std::string>()); \
|
||||
/// Copy settings-related program options to config.
|
||||
/// TODO: Is this code necessary?
|
||||
for (const auto & setting : context.getSettingsRef())
|
||||
{
|
||||
const String name = setting.getName().toString();
|
||||
if (options.count(name))
|
||||
config().setString(name, options[name].as<std::string>());
|
||||
}
|
||||
APPLY_FOR_SETTINGS(EXTRACT_SETTING)
|
||||
#undef EXTRACT_SETTING
|
||||
|
||||
if (options.count("config-file") && options.count("config"))
|
||||
throw Exception("Two or more configuration files referenced in arguments", ErrorCodes::BAD_ARGUMENTS);
|
||||
@ -1782,6 +1793,13 @@ public:
|
||||
server_logs_file = options["server_logs_file"].as<std::string>();
|
||||
if (options.count("disable_suggestion"))
|
||||
config().setBool("disable_suggestion", true);
|
||||
if (options.count("always_load_suggestion_data"))
|
||||
{
|
||||
if (options.count("disable_suggestion"))
|
||||
throw Exception("Command line parameters disable_suggestion (-A) and always_load_suggestion_data cannot be specified simultaneously",
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
config().setBool("always_load_suggestion_data", true);
|
||||
}
|
||||
if (options.count("suggestion_limit"))
|
||||
config().setInt("suggestion_limit", options["suggestion_limit"].as<int>());
|
||||
}
|
||||
|
@ -69,11 +69,7 @@ void LocalServer::initialize(Poco::Util::Application & self)
|
||||
|
||||
void LocalServer::applyCmdSettings()
|
||||
{
|
||||
#define EXTRACT_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
if (cmd_settings.NAME.changed) \
|
||||
context->getSettingsRef().NAME = cmd_settings.NAME;
|
||||
APPLY_FOR_SETTINGS(EXTRACT_SETTING)
|
||||
#undef EXTRACT_SETTING
|
||||
context->getSettingsRef().copyChangesFrom(cmd_settings);
|
||||
}
|
||||
|
||||
/// If path is specified and not empty, will try to setup server environment and load existing metadata
|
||||
@ -414,7 +410,6 @@ void LocalServer::init(int argc, char ** argv)
|
||||
min_description_length = std::min(min_description_length, line_length - 2);
|
||||
}
|
||||
|
||||
#define DECLARE_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) (#NAME, po::value<std::string> (), DESCRIPTION)
|
||||
po::options_description description("Main options", line_length, min_description_length);
|
||||
description.add_options()
|
||||
("help", "produce help message")
|
||||
@ -435,13 +430,15 @@ void LocalServer::init(int argc, char ** argv)
|
||||
("verbose", "print query and other debugging info")
|
||||
("ignore-error", "do not stop processing if a query failed")
|
||||
("version,V", "print version information and exit")
|
||||
APPLY_FOR_SETTINGS(DECLARE_SETTING);
|
||||
#undef DECLARE_SETTING
|
||||
;
|
||||
|
||||
cmd_settings.addProgramOptions(description);
|
||||
|
||||
/// Parse main commandline options.
|
||||
po::parsed_options parsed = po::command_line_parser(argc, argv).options(description).run();
|
||||
po::variables_map options;
|
||||
po::store(parsed, options);
|
||||
po::notify(options);
|
||||
|
||||
if (options.count("version") || options.count("V"))
|
||||
{
|
||||
@ -457,13 +454,6 @@ void LocalServer::init(int argc, char ** argv)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/// Extract settings and limits from the options.
|
||||
#define EXTRACT_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
if (options.count(#NAME)) \
|
||||
cmd_settings.set(#NAME, options[#NAME].as<std::string>());
|
||||
APPLY_FOR_SETTINGS(EXTRACT_SETTING)
|
||||
#undef EXTRACT_SETTING
|
||||
|
||||
/// Save received data into the internal config.
|
||||
if (options.count("config-file"))
|
||||
config().setString("config-file", options["config-file"].as<std::string>());
|
||||
|
@ -912,8 +912,8 @@ public:
|
||||
size_t columns = header.columns();
|
||||
models.reserve(columns);
|
||||
|
||||
for (size_t i = 0; i < columns; ++i)
|
||||
models.emplace_back(factory.get(*header.getByPosition(i).type, hash(seed, i), markov_model_params));
|
||||
for (const auto & elem : header)
|
||||
models.emplace_back(factory.get(*elem.type, hash(seed, elem.name), markov_model_params));
|
||||
}
|
||||
|
||||
void train(const Columns & columns)
|
||||
@ -954,7 +954,7 @@ try
|
||||
("structure,S", po::value<std::string>(), "structure of the initial table (list of column and type names)")
|
||||
("input-format", po::value<std::string>(), "input format of the initial table data")
|
||||
("output-format", po::value<std::string>(), "default output format")
|
||||
("seed", po::value<std::string>(), "seed (arbitrary string), must be random string with at least 10 bytes length")
|
||||
("seed", po::value<std::string>(), "seed (arbitrary string), must be random string with at least 10 bytes length; note that a seed for each column is derived from this seed and a column name: you can obfuscate data for different tables and as long as you use identical seed and identical column names, the data for corresponding non-text columns for different tables will be transformed in the same way, so the data for different tables can be JOINed after obfuscation")
|
||||
("limit", po::value<UInt64>(), "if specified - stop after generating that number of rows")
|
||||
("silent", po::value<bool>()->default_value(false), "don't print information messages to stderr")
|
||||
("order", po::value<UInt64>()->default_value(5), "order of markov model to generate strings")
|
||||
|
@ -16,18 +16,20 @@ set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE PUBLIC ${ClickHouse_SOURCE_DIR}/libs/libdaemo
|
||||
|
||||
if (USE_POCO_SQLODBC)
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_SQLODBC_LIBRARY})
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_SQLODBC_INCLUDE_DIR})
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_SQLODBC_INCLUDE_DIR})
|
||||
endif ()
|
||||
if (Poco_SQL_FOUND)
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_SQL_LIBRARY})
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_SQL_INCLUDE_DIR})
|
||||
endif ()
|
||||
|
||||
if (USE_POCO_DATAODBC)
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_DataODBC_LIBRARY})
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_DataODBC_INCLUDE_DIR})
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_DataODBC_INCLUDE_DIR})
|
||||
endif()
|
||||
if (Poco_Data_FOUND)
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_Data_LIBRARY})
|
||||
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_Data_INCLUDE_DIR})
|
||||
endif ()
|
||||
|
||||
clickhouse_program_add_library(odbc-bridge)
|
||||
@ -37,12 +39,13 @@ clickhouse_program_add_library(odbc-bridge)
|
||||
# For this reason, we disabling -rdynamic linker flag. But we do it in strange way:
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
|
||||
|
||||
add_executable (clickhouse-odbc-bridge odbc-bridge.cpp)
|
||||
add_executable(clickhouse-odbc-bridge odbc-bridge.cpp)
|
||||
set_target_properties(clickhouse-odbc-bridge PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..)
|
||||
|
||||
clickhouse_program_link_split_binary(odbc-bridge)
|
||||
|
||||
install (TARGETS clickhouse-odbc-bridge RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT clickhouse)
|
||||
install(TARGETS clickhouse-odbc-bridge RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT clickhouse)
|
||||
|
||||
if (ENABLE_TESTS)
|
||||
add_subdirectory (tests)
|
||||
endif ()
|
||||
if(ENABLE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
@ -21,7 +21,7 @@ void extractSettings(
|
||||
const XMLConfigurationPtr & config,
|
||||
const std::string & key,
|
||||
const Strings & settings_list,
|
||||
std::map<std::string, std::string> & settings_to_apply)
|
||||
SettingsChanges & settings_to_apply)
|
||||
{
|
||||
for (const std::string & setup : settings_list)
|
||||
{
|
||||
@ -32,7 +32,7 @@ void extractSettings(
|
||||
if (value.empty())
|
||||
value = "true";
|
||||
|
||||
settings_to_apply[setup] = value;
|
||||
settings_to_apply.emplace_back(SettingChange{setup, value});
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ void PerformanceTestInfo::applySettings(XMLConfigurationPtr config)
|
||||
{
|
||||
if (config->has("settings"))
|
||||
{
|
||||
std::map<std::string, std::string> settings_to_apply;
|
||||
SettingsChanges settings_to_apply;
|
||||
Strings config_settings;
|
||||
config->keys("settings", config_settings);
|
||||
|
||||
@ -96,19 +96,7 @@ void PerformanceTestInfo::applySettings(XMLConfigurationPtr config)
|
||||
}
|
||||
|
||||
extractSettings(config, "settings", config_settings, settings_to_apply);
|
||||
|
||||
/// This macro goes through all settings in the Settings.h
|
||||
/// and, if found any settings in test's xml configuration
|
||||
/// with the same name, sets its value to settings
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
#define EXTRACT_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
it = settings_to_apply.find(#NAME); \
|
||||
if (it != settings_to_apply.end()) \
|
||||
settings.set(#NAME, settings_to_apply[#NAME]);
|
||||
|
||||
APPLY_FOR_SETTINGS(EXTRACT_SETTING)
|
||||
|
||||
#undef EXTRACT_SETTING
|
||||
settings.applyChanges(settings_to_apply);
|
||||
|
||||
if (settings_contain("average_rows_speed_precision"))
|
||||
TestStats::avg_rows_speed_precision =
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
const std::string & default_database_,
|
||||
const std::string & user_,
|
||||
const std::string & password_,
|
||||
const Settings & cmd_settings,
|
||||
const bool lite_output_,
|
||||
const std::string & profiles_file_,
|
||||
Strings && input_files_,
|
||||
@ -87,6 +88,7 @@ public:
|
||||
, input_files(input_files_)
|
||||
, log(&Poco::Logger::get("PerformanceTestSuite"))
|
||||
{
|
||||
global_context.getSettingsRef().copyChangesFrom(cmd_settings);
|
||||
if (input_files.size() < 1)
|
||||
throw Exception("No tests were specified", ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
@ -110,10 +112,6 @@ public:
|
||||
|
||||
return 0;
|
||||
}
|
||||
void setContextSetting(const String & name, const std::string & value)
|
||||
{
|
||||
global_context.setSetting(name, value);
|
||||
}
|
||||
|
||||
private:
|
||||
Connection connection;
|
||||
@ -326,7 +324,6 @@ try
|
||||
using Strings = DB::Strings;
|
||||
|
||||
|
||||
#define DECLARE_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) (#NAME, po::value<std::string>(), DESCRIPTION)
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
@ -348,9 +345,10 @@ try
|
||||
("input-files", value<Strings>()->multitoken(), "Input .xml files")
|
||||
("query-indexes", value<std::vector<size_t>>()->multitoken(), "Input query indexes")
|
||||
("recursive,r", "Recurse in directories to find all xml's")
|
||||
APPLY_FOR_SETTINGS(DECLARE_SETTING);
|
||||
#undef DECLARE_SETTING
|
||||
;
|
||||
|
||||
DB::Settings cmd_settings;
|
||||
cmd_settings.addProgramOptions(desc);
|
||||
|
||||
po::options_description cmdline_options;
|
||||
cmdline_options.add(desc);
|
||||
@ -397,6 +395,7 @@ try
|
||||
options["database"].as<std::string>(),
|
||||
options["user"].as<std::string>(),
|
||||
options["password"].as<std::string>(),
|
||||
cmd_settings,
|
||||
options.count("lite") > 0,
|
||||
options["profiles-file"].as<std::string>(),
|
||||
std::move(input_files),
|
||||
@ -408,15 +407,6 @@ try
|
||||
std::move(skip_names_regexp),
|
||||
queries_with_indexes,
|
||||
timeouts);
|
||||
/// Extract settings from the options.
|
||||
#define EXTRACT_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
if (options.count(#NAME)) \
|
||||
{ \
|
||||
performance_test_suite.setContextSetting(#NAME, options[#NAME].as<std::string>()); \
|
||||
}
|
||||
APPLY_FOR_SETTINGS(EXTRACT_SETTING)
|
||||
#undef EXTRACT_SETTING
|
||||
|
||||
return performance_test_suite.run();
|
||||
}
|
||||
catch (...)
|
||||
|
@ -497,8 +497,7 @@ void HTTPHandler::processQuery(
|
||||
settings.readonly = 2;
|
||||
}
|
||||
|
||||
auto readonly_before_query = settings.readonly;
|
||||
|
||||
SettingsChanges settings_changes;
|
||||
for (auto it = params.begin(); it != params.end(); ++it)
|
||||
{
|
||||
if (it->first == "database")
|
||||
@ -515,20 +514,12 @@ void HTTPHandler::processQuery(
|
||||
else
|
||||
{
|
||||
/// All other query parameters are treated as settings.
|
||||
String value;
|
||||
/// Setting is skipped if value wasn't changed.
|
||||
if (!settings.tryGet(it->first, value) || it->second != value)
|
||||
{
|
||||
if (readonly_before_query == 1)
|
||||
throw Exception("Cannot override setting (" + it->first + ") in readonly mode", ErrorCodes::READONLY);
|
||||
settings_changes.push_back({it->first, it->second});
|
||||
}
|
||||
}
|
||||
|
||||
if (readonly_before_query && it->first == "readonly")
|
||||
throw Exception("Setting 'readonly' cannot be overrided in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
context.setSetting(it->first, it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
context.checkSettingsConstraints(settings_changes);
|
||||
context.applySettingsChanges(settings_changes);
|
||||
|
||||
/// HTTP response compression is turned on only if the client signalled that they support it
|
||||
/// (using Accept-Encoding header) and 'enable_http_compression' setting is turned on.
|
||||
|
@ -370,8 +370,8 @@ void TCPHandler::processInsertQuery(const Settings & global_settings)
|
||||
if (client_revision >= DBMS_MIN_REVISION_WITH_COLUMN_DEFAULTS_METADATA)
|
||||
{
|
||||
const auto & db_and_table = query_context->getInsertionTable();
|
||||
if (auto * columns = ColumnsDescription::loadFromContext(*query_context, db_and_table.first, db_and_table.second))
|
||||
sendTableColumns(*columns);
|
||||
if (query_context->getSettingsRef().input_format_defaults_for_omitted_fields)
|
||||
sendTableColumns(query_context->getTable(db_and_table.first, db_and_table.second)->getColumns());
|
||||
}
|
||||
|
||||
/// Send block to the client - table structure.
|
||||
|
@ -104,7 +104,6 @@ public:
|
||||
if (event)
|
||||
{
|
||||
this->data(place).add(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ class QuantileTDigest
|
||||
{
|
||||
if (unmerged > 0)
|
||||
{
|
||||
RadixSort<RadixSortTraits>::execute(summary.data(), summary.size());
|
||||
RadixSort<RadixSortTraits>::executeLSD(summary.data(), summary.size());
|
||||
|
||||
if (summary.size() > 3)
|
||||
{
|
||||
|
@ -401,7 +401,7 @@ void Connection::sendQuery(
|
||||
if (settings)
|
||||
settings->serialize(*out);
|
||||
else
|
||||
writeStringBinary("", *out);
|
||||
writeStringBinary("" /* empty string is a marker of the end of settings */, *out);
|
||||
|
||||
writeVarUInt(stage, *out);
|
||||
writeVarUInt(static_cast<bool>(compression), *out);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <Common/Arena.h>
|
||||
#include <Common/SipHash.h>
|
||||
#include <Common/NaNUtils.h>
|
||||
#include <Common/RadixSort.h>
|
||||
#include <IO/WriteBuffer.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Columns/ColumnsCommon.h>
|
||||
@ -18,7 +19,6 @@
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
@ -68,19 +68,41 @@ struct ColumnVector<T>::greater
|
||||
bool operator()(size_t lhs, size_t rhs) const { return CompareHelper<T>::greater(parent.data[lhs], parent.data[rhs], nan_direction_hint); }
|
||||
};
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
struct ValueWithIndex
|
||||
{
|
||||
T value;
|
||||
UInt32 index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RadixSortTraits : RadixSortNumTraits<T>
|
||||
{
|
||||
using Element = ValueWithIndex<T>;
|
||||
static T & extractKey(Element & elem) { return elem.value; }
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ColumnVector<T>::getPermutation(bool reverse, size_t limit, int nan_direction_hint, IColumn::Permutation & res) const
|
||||
{
|
||||
size_t s = data.size();
|
||||
res.resize(s);
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[i] = i;
|
||||
|
||||
if (s == 0)
|
||||
return;
|
||||
|
||||
if (limit >= s)
|
||||
limit = 0;
|
||||
|
||||
if (limit)
|
||||
{
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[i] = i;
|
||||
|
||||
if (reverse)
|
||||
std::partial_sort(res.begin(), res.begin() + limit, res.end(), greater(*this, nan_direction_hint));
|
||||
else
|
||||
@ -88,6 +110,71 @@ void ColumnVector<T>::getPermutation(bool reverse, size_t limit, int nan_directi
|
||||
}
|
||||
else
|
||||
{
|
||||
/// A case for radix sort
|
||||
if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, UInt128>)
|
||||
{
|
||||
/// Thresholds on size. Lower threshold is arbitrary. Upper threshold is chosen by the type for histogram counters.
|
||||
if (s >= 256 && s <= std::numeric_limits<UInt32>::max())
|
||||
{
|
||||
PaddedPODArray<ValueWithIndex<T>> pairs(s);
|
||||
for (UInt32 i = 0; i < s; ++i)
|
||||
pairs[i] = {data[i], i};
|
||||
|
||||
RadixSort<RadixSortTraits<T>>::executeLSD(pairs.data(), s);
|
||||
|
||||
/// Radix sort treats all NaNs to be greater than all numbers.
|
||||
/// If the user needs the opposite, we must move them accordingly.
|
||||
size_t nans_to_move = 0;
|
||||
if (std::is_floating_point_v<T> && nan_direction_hint < 0)
|
||||
{
|
||||
for (ssize_t i = s - 1; i >= 0; --i)
|
||||
{
|
||||
if (isNaN(pairs[i].value))
|
||||
++nans_to_move;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reverse)
|
||||
{
|
||||
if (nans_to_move)
|
||||
{
|
||||
for (size_t i = 0; i < s - nans_to_move; ++i)
|
||||
res[i] = pairs[s - nans_to_move - 1 - i].index;
|
||||
for (size_t i = s - nans_to_move; i < s; ++i)
|
||||
res[i] = pairs[s - 1 - (i - (s - nans_to_move))].index;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[s - 1 - i] = pairs[i].index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nans_to_move)
|
||||
{
|
||||
for (size_t i = 0; i < nans_to_move; ++i)
|
||||
res[i] = pairs[i + s - nans_to_move].index;
|
||||
for (size_t i = nans_to_move; i < s; ++i)
|
||||
res[i] = pairs[i - nans_to_move].index;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[i] = pairs[i].index;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Default sorting algorithm.
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[i] = i;
|
||||
|
||||
if (reverse)
|
||||
pdqsort(res.begin(), res.end(), greater(*this, nan_direction_hint));
|
||||
else
|
||||
@ -95,6 +182,7 @@ void ColumnVector<T>::getPermutation(bool reverse, size_t limit, int nan_directi
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
const char * ColumnVector<T>::getFamilyName() const
|
||||
{
|
||||
|
318
dbms/src/Common/CpuId.h
Normal file
318
dbms/src/Common/CpuId.h
Normal file
@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Types.h>
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace Cpu
|
||||
{
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
inline UInt64 _xgetbv(UInt32 xcr) noexcept
|
||||
{
|
||||
UInt32 eax;
|
||||
UInt32 edx;
|
||||
__asm__ volatile(
|
||||
"xgetbv"
|
||||
: "=a"(eax), "=d"(edx)
|
||||
: "c"(xcr));
|
||||
return (static_cast<UInt64>(edx) << 32) | eax;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool cpuid(UInt32 op, UInt32 sub_op, UInt32 * res) noexcept
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
__cpuid_count(op, sub_op, res[0], res[1], res[2], res[3]);
|
||||
return true;
|
||||
#else
|
||||
(void)op;
|
||||
(void)sub_op;
|
||||
|
||||
memset(res, 0, 4 * sizeof(*res));
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool cpuid(UInt32 op, UInt32 * res) noexcept
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
__cpuid(op, res[0], res[1], res[2], res[3]);
|
||||
return true;
|
||||
#else
|
||||
(void)op;
|
||||
|
||||
memset(res, 0, 4 * sizeof(*res));
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define CPU_ID_ENUMERATE(OP) \
|
||||
OP(SSE) \
|
||||
OP(SSE2) \
|
||||
OP(SSE3) \
|
||||
OP(SSSE3) \
|
||||
OP(SSE41) \
|
||||
OP(SSE42) \
|
||||
OP(F16C) \
|
||||
OP(POPCNT) \
|
||||
OP(BMI1) \
|
||||
OP(BMI2) \
|
||||
OP(PCLMUL) \
|
||||
OP(AES) \
|
||||
OP(AVX) \
|
||||
OP(FMA) \
|
||||
OP(AVX2) \
|
||||
OP(AVX512F) \
|
||||
OP(AVX512DQ) \
|
||||
OP(AVX512IFMA) \
|
||||
OP(AVX512PF) \
|
||||
OP(AVX512ER) \
|
||||
OP(AVX512CD) \
|
||||
OP(AVX512BW) \
|
||||
OP(AVX512VL) \
|
||||
OP(AVX512VBMI) \
|
||||
OP(PREFETCHWT1) \
|
||||
OP(SHA) \
|
||||
OP(ADX) \
|
||||
OP(RDRAND) \
|
||||
OP(RDSEED) \
|
||||
OP(PCOMMIT) \
|
||||
OP(RDTSCP) \
|
||||
OP(CLFLUSHOPT) \
|
||||
OP(CLWB) \
|
||||
OP(XSAVE) \
|
||||
OP(OSXSAVE)
|
||||
|
||||
union CpuInfo
|
||||
{
|
||||
UInt32 info[4];
|
||||
|
||||
struct
|
||||
{
|
||||
UInt32 eax;
|
||||
UInt32 ebx;
|
||||
UInt32 ecx;
|
||||
UInt32 edx;
|
||||
};
|
||||
|
||||
inline CpuInfo(UInt32 op) noexcept { cpuid(op, info); }
|
||||
|
||||
inline CpuInfo(UInt32 op, UInt32 sub_op) noexcept { cpuid(op, sub_op, info); }
|
||||
};
|
||||
|
||||
#define DEF_NAME(X) inline bool have##X() noexcept;
|
||||
CPU_ID_ENUMERATE(DEF_NAME)
|
||||
#undef DEF_NAME
|
||||
|
||||
bool haveRDTSCP() noexcept
|
||||
{
|
||||
return (CpuInfo(0x80000001).edx >> 27) & 1u;
|
||||
}
|
||||
|
||||
bool haveSSE() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).edx >> 25) & 1u;
|
||||
}
|
||||
|
||||
bool haveSSE2() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).edx >> 26) & 1u;
|
||||
}
|
||||
|
||||
bool haveSSE3() noexcept
|
||||
{
|
||||
return CpuInfo(0x1).ecx & 1u;
|
||||
}
|
||||
|
||||
bool havePCLMUL() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 1) & 1u;
|
||||
}
|
||||
|
||||
bool haveSSSE3() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 9) & 1u;
|
||||
}
|
||||
|
||||
bool haveSSE41() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 19) & 1u;
|
||||
}
|
||||
|
||||
bool haveSSE42() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 20) & 1u;
|
||||
}
|
||||
|
||||
bool haveF16C() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 29) & 1u;
|
||||
}
|
||||
|
||||
bool havePOPCNT() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 23) & 1u;
|
||||
}
|
||||
|
||||
bool haveAES() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 25) & 1u;
|
||||
}
|
||||
|
||||
bool haveXSAVE() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 26) & 1u;
|
||||
}
|
||||
|
||||
bool haveOSXSAVE() noexcept
|
||||
{
|
||||
return (CpuInfo(0x1).ecx >> 27) & 1u;
|
||||
}
|
||||
|
||||
bool haveAVX() noexcept
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
// http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=375968
|
||||
return haveOSXSAVE() // implies haveXSAVE()
|
||||
&& (_xgetbv(0) & 6u) == 6u // XMM state and YMM state are enabled by OS
|
||||
&& ((CpuInfo(0x1).ecx >> 28) & 1u); // AVX bit
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool haveFMA() noexcept
|
||||
{
|
||||
return haveAVX() && ((CpuInfo(0x1).ecx >> 12) & 1u);
|
||||
}
|
||||
|
||||
bool haveAVX2() noexcept
|
||||
{
|
||||
return haveAVX() && ((CpuInfo(0x7, 0).ebx >> 5) & 1u);
|
||||
}
|
||||
|
||||
bool haveBMI1() noexcept
|
||||
{
|
||||
return (CpuInfo(0x7, 0).ebx >> 3) & 1u;
|
||||
}
|
||||
|
||||
bool haveBMI2() noexcept
|
||||
{
|
||||
return (CpuInfo(0x7, 0).ebx >> 8) & 1u;
|
||||
}
|
||||
|
||||
bool haveAVX512F() noexcept
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
// https://software.intel.com/en-us/articles/how-to-detect-knl-instruction-support
|
||||
return haveOSXSAVE() // implies haveXSAVE()
|
||||
&& (_xgetbv(0) & 6u) == 6u // XMM state and YMM state are enabled by OS
|
||||
&& ((_xgetbv(0) >> 5) & 7u) == 7u // ZMM state is enabled by OS
|
||||
&& CpuInfo(0x0).eax >= 0x7 // leaf 7 is present
|
||||
&& ((CpuInfo(0x7).ebx >> 16) & 1u); // AVX512F bit
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool haveAVX512DQ() noexcept
|
||||
{
|
||||
return haveAVX512F() && ((CpuInfo(0x7, 0).ebx >> 17) & 1u);
|
||||
}
|
||||
|
||||
bool haveRDSEED() noexcept
|
||||
{
|
||||
return CpuInfo(0x0).eax >= 0x7 && ((CpuInfo(0x7, 0).ebx >> 18) & 1u);
|
||||
}
|
||||
|
||||
bool haveADX() noexcept
|
||||
{
|
||||
return CpuInfo(0x0).eax >= 0x7 && ((CpuInfo(0x7, 0).ebx >> 19) & 1u);
|
||||
}
|
||||
|
||||
bool haveAVX512IFMA() noexcept
|
||||
{
|
||||
return haveAVX512F() && ((CpuInfo(0x7, 0).ebx >> 21) & 1u);
|
||||
}
|
||||
|
||||
bool havePCOMMIT() noexcept
|
||||
{
|
||||
return CpuInfo(0x0).eax >= 0x7 && ((CpuInfo(0x7, 0).ebx >> 22) & 1u);
|
||||
}
|
||||
|
||||
bool haveCLFLUSHOPT() noexcept
|
||||
{
|
||||
return CpuInfo(0x0).eax >= 0x7 && ((CpuInfo(0x7, 0).ebx >> 23) & 1u);
|
||||
}
|
||||
|
||||
bool haveCLWB() noexcept
|
||||
{
|
||||
return CpuInfo(0x0).eax >= 0x7 && ((CpuInfo(0x7, 0).ebx >> 24) & 1u);
|
||||
}
|
||||
|
||||
bool haveAVX512PF() noexcept
|
||||
{
|
||||
return haveAVX512F() && ((CpuInfo(0x7, 0).ebx >> 26) & 1u);
|
||||
}
|
||||
|
||||
bool haveAVX512ER() noexcept
|
||||
{
|
||||
return haveAVX512F() && ((CpuInfo(0x7, 0).ebx >> 27) & 1u);
|
||||
}
|
||||
|
||||
bool haveAVX512CD() noexcept
|
||||
{
|
||||
return haveAVX512F() && ((CpuInfo(0x7, 0).ebx >> 28) & 1u);
|
||||
}
|
||||
|
||||
bool haveSHA() noexcept
|
||||
{
|
||||
return CpuInfo(0x0).eax >= 0x7 && ((CpuInfo(0x7, 0).ebx >> 29) & 1u);
|
||||
}
|
||||
|
||||
bool haveAVX512BW() noexcept
|
||||
{
|
||||
return haveAVX512F() && ((CpuInfo(0x7, 0).ebx >> 30) & 1u);
|
||||
}
|
||||
|
||||
bool haveAVX512VL() noexcept
|
||||
{
|
||||
return haveAVX512F() && ((CpuInfo(0x7, 0).ebx >> 31) & 1u);
|
||||
}
|
||||
|
||||
bool havePREFETCHWT1() noexcept
|
||||
{
|
||||
return CpuInfo(0x0).eax >= 0x7 && ((CpuInfo(0x7, 0).ecx >> 0) & 1u);
|
||||
}
|
||||
|
||||
bool haveAVX512VBMI() noexcept
|
||||
{
|
||||
return haveAVX512F() && ((CpuInfo(0x7, 0).ecx >> 1) & 1u);
|
||||
}
|
||||
|
||||
bool haveRDRAND() noexcept
|
||||
{
|
||||
return CpuInfo(0x0).eax >= 0x7 && ((CpuInfo(0x1).ecx >> 30) & 1u);
|
||||
}
|
||||
|
||||
struct CpuFlagsCache
|
||||
{
|
||||
#define DEF_NAME(X) static inline bool have_##X = have##X();
|
||||
CPU_ID_ENUMERATE(DEF_NAME)
|
||||
#undef DEF_NAME
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -426,8 +426,9 @@ namespace ErrorCodes
|
||||
extern const int BROTLI_WRITE_FAILED = 449;
|
||||
extern const int BAD_TTL_EXPRESSION = 450;
|
||||
extern const int BAD_TTL_FILE = 451;
|
||||
extern const int UNKNOWN_SCHEMA = 452;
|
||||
extern const int UNKNOWN_DISK = 453;
|
||||
extern const int SETTING_CONSTRAINT_VIOLATION = 452;
|
||||
extern const int UNKNOWN_SCHEMA = 453;
|
||||
extern const int UNKNOWN_DISK = 454;
|
||||
|
||||
extern const int KEEPER_EXCEPTION = 999;
|
||||
extern const int POCO_EXCEPTION = 1000;
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/OptimizedRegularExpression.h>
|
||||
|
||||
|
||||
#define MIN_LENGTH_FOR_STRSTR 3
|
||||
#define MAX_SUBPATTERNS 5
|
||||
|
||||
@ -211,20 +210,18 @@ void OptimizedRegularExpressionImpl<thread_safe>::analyze(
|
||||
{
|
||||
if (!has_alternative_on_depth_0)
|
||||
{
|
||||
/** We choose the non-alternative substring of the maximum length, among the prefixes,
|
||||
* or a non-alternative substring of maximum length.
|
||||
*/
|
||||
/// We choose the non-alternative substring of the maximum length for first search.
|
||||
|
||||
/// Tuning for typical usage domain
|
||||
auto tuning_strings_condition = [](const std::string & str)
|
||||
{
|
||||
return str != "://" && str != "http://" && str != "www" && str != "Windows ";
|
||||
};
|
||||
size_t max_length = 0;
|
||||
Substrings::const_iterator candidate_it = trivial_substrings.begin();
|
||||
for (Substrings::const_iterator it = trivial_substrings.begin(); it != trivial_substrings.end(); ++it)
|
||||
{
|
||||
if (((it->second == 0 && candidate_it->second != 0)
|
||||
|| ((it->second == 0) == (candidate_it->second == 0) && it->first.size() > max_length))
|
||||
/// Tuning for typical usage domain
|
||||
&& (it->first.size() > strlen("://") || strncmp(it->first.data(), "://", strlen("://")))
|
||||
&& (it->first.size() > strlen("http://") || strncmp(it->first.data(), "http", strlen("http")))
|
||||
&& (it->first.size() > strlen("www.") || strncmp(it->first.data(), "www", strlen("www")))
|
||||
&& (it->first.size() > strlen("Windows ") || strncmp(it->first.data(), "Windows ", strlen("Windows "))))
|
||||
if (it->first.size() > max_length && tuning_strings_condition(it->first))
|
||||
{
|
||||
max_length = it->first.size();
|
||||
candidate_it = it;
|
||||
|
@ -37,8 +37,8 @@ class RWLockImpl::LockHolderImpl
|
||||
RWLock parent;
|
||||
GroupsContainer::iterator it_group;
|
||||
ClientsContainer::iterator it_client;
|
||||
ThreadToHolder::iterator it_thread;
|
||||
QueryIdToHolder::iterator it_query;
|
||||
ThreadToHolder::key_type thread_id;
|
||||
QueryIdToHolder::key_type query_id;
|
||||
CurrentMetrics::Increment active_client_increment;
|
||||
|
||||
LockHolderImpl(RWLock && parent, GroupsContainer::iterator it_group, ClientsContainer::iterator it_client);
|
||||
@ -122,24 +122,16 @@ RWLockImpl::LockHolder RWLockImpl::getLock(RWLockImpl::Type type, const String &
|
||||
|
||||
LockHolder res(new LockHolderImpl(shared_from_this(), it_group, it_client));
|
||||
|
||||
/// Wait a notification until we will be the only in the group.
|
||||
it_group->cv.wait(lock, [&] () { return it_group == queue.begin(); });
|
||||
|
||||
/// Insert myself (weak_ptr to the holder) to threads set to implement recursive lock
|
||||
it_thread = thread_to_holder.emplace(this_thread_id, res).first;
|
||||
res->it_thread = it_thread;
|
||||
thread_to_holder.emplace(this_thread_id, res);
|
||||
res->thread_id = this_thread_id;
|
||||
|
||||
if (query_id != RWLockImpl::NO_QUERY)
|
||||
it_query = query_id_to_holder.emplace(query_id, res).first;
|
||||
res->it_query = it_query;
|
||||
|
||||
/// We are first, we should not wait anything
|
||||
/// If we are not the first client in the group, a notification could be already sent
|
||||
if (it_group == queue.begin())
|
||||
{
|
||||
finalize_metrics();
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Wait a notification
|
||||
it_group->cv.wait(lock, [&] () { return it_group == queue.begin(); });
|
||||
query_id_to_holder.emplace(query_id, res);
|
||||
res->query_id = query_id;
|
||||
|
||||
finalize_metrics();
|
||||
return res;
|
||||
@ -151,10 +143,8 @@ RWLockImpl::LockHolderImpl::~LockHolderImpl()
|
||||
std::unique_lock lock(parent->mutex);
|
||||
|
||||
/// Remove weak_ptrs to the holder, since there are no owners of the current lock
|
||||
parent->thread_to_holder.erase(it_thread);
|
||||
|
||||
if (it_query != parent->query_id_to_holder.end())
|
||||
parent->query_id_to_holder.erase(it_query);
|
||||
parent->thread_to_holder.erase(thread_id);
|
||||
parent->query_id_to_holder.erase(query_id);
|
||||
|
||||
/// Removes myself from client list of our group
|
||||
it_group->clients.erase(it_client);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Types.h>
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
@ -64,15 +67,15 @@ struct RadixSortFloatTransform
|
||||
};
|
||||
|
||||
|
||||
template <typename _Element, typename _Key = _Element>
|
||||
template <typename TElement>
|
||||
struct RadixSortFloatTraits
|
||||
{
|
||||
using Element = _Element; /// The type of the element. It can be a structure with a key and some other payload. Or just a key.
|
||||
using Key = _Key; /// The key to sort.
|
||||
using Element = TElement; /// The type of the element. It can be a structure with a key and some other payload. Or just a key.
|
||||
using Key = Element; /// The key to sort by.
|
||||
using CountType = uint32_t; /// Type for calculating histograms. In the case of a known small number of elements, it can be less than size_t.
|
||||
|
||||
/// The type to which the key is transformed to do bit operations. This UInt is the same size as the key.
|
||||
using KeyBits = std::conditional_t<sizeof(_Key) == 8, uint64_t, uint32_t>;
|
||||
using KeyBits = std::conditional_t<sizeof(Key) == 8, uint64_t, uint32_t>;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8; /// With what pieces of the key, in bits, to do one pass - reshuffle of the array.
|
||||
|
||||
@ -85,12 +88,13 @@ struct RadixSortFloatTraits
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem)
|
||||
static Key & extractKey(Element & elem) { return elem; }
|
||||
|
||||
/// Used when fallback to comparison based sorting is needed.
|
||||
/// TODO: Correct handling of NaNs, NULLs, etc
|
||||
static bool less(Key x, Key y)
|
||||
{
|
||||
if constexpr (std::is_same_v<Element, Key>)
|
||||
return elem;
|
||||
else
|
||||
return *reinterpret_cast<Key *>(&elem);
|
||||
return x < y;
|
||||
}
|
||||
};
|
||||
|
||||
@ -105,6 +109,29 @@ struct RadixSortIdentityTransform
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename TElement>
|
||||
struct RadixSortUIntTraits
|
||||
{
|
||||
using Element = TElement;
|
||||
using Key = Element;
|
||||
using CountType = uint32_t;
|
||||
using KeyBits = Key;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8;
|
||||
|
||||
using Transform = RadixSortIdentityTransform<KeyBits>;
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
static Key & extractKey(Element & elem) { return elem; }
|
||||
|
||||
static bool less(Key x, Key y)
|
||||
{
|
||||
return x < y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename KeyBits>
|
||||
struct RadixSortSignedTransform
|
||||
{
|
||||
@ -115,53 +142,37 @@ struct RadixSortSignedTransform
|
||||
};
|
||||
|
||||
|
||||
template <typename _Element, typename _Key = _Element>
|
||||
struct RadixSortUIntTraits
|
||||
{
|
||||
using Element = _Element;
|
||||
using Key = _Key;
|
||||
using CountType = uint32_t;
|
||||
using KeyBits = _Key;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8;
|
||||
|
||||
using Transform = RadixSortIdentityTransform<KeyBits>;
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem)
|
||||
{
|
||||
if constexpr (std::is_same_v<Element, Key>)
|
||||
return elem;
|
||||
else
|
||||
return *reinterpret_cast<Key *>(&elem);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Element, typename _Key = _Element>
|
||||
template <typename TElement>
|
||||
struct RadixSortIntTraits
|
||||
{
|
||||
using Element = _Element;
|
||||
using Key = _Key;
|
||||
using Element = TElement;
|
||||
using Key = Element;
|
||||
using CountType = uint32_t;
|
||||
using KeyBits = std::make_unsigned_t<_Key>;
|
||||
using KeyBits = std::make_unsigned_t<Key>;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8;
|
||||
|
||||
using Transform = RadixSortSignedTransform<KeyBits>;
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem)
|
||||
static Key & extractKey(Element & elem) { return elem; }
|
||||
|
||||
static bool less(Key x, Key y)
|
||||
{
|
||||
if constexpr (std::is_same_v<Element, Key>)
|
||||
return elem;
|
||||
else
|
||||
return *reinterpret_cast<Key *>(&elem);
|
||||
return x < y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
using RadixSortNumTraits =
|
||||
std::conditional_t<std::is_integral_v<T>,
|
||||
std::conditional_t<std::is_unsigned_v<T>,
|
||||
RadixSortUIntTraits<T>,
|
||||
RadixSortIntTraits<T>>,
|
||||
RadixSortFloatTraits<T>>;
|
||||
|
||||
|
||||
template <typename Traits>
|
||||
struct RadixSort
|
||||
{
|
||||
@ -171,6 +182,9 @@ private:
|
||||
using CountType = typename Traits::CountType;
|
||||
using KeyBits = typename Traits::KeyBits;
|
||||
|
||||
// Use insertion sort if the size of the array is less than equal to this threshold
|
||||
static constexpr size_t INSERTION_SORT_THRESHOLD = 64;
|
||||
|
||||
static constexpr size_t HISTOGRAM_SIZE = 1 << Traits::PART_SIZE_BITS;
|
||||
static constexpr size_t PART_BITMASK = HISTOGRAM_SIZE - 1;
|
||||
static constexpr size_t KEY_BITS = sizeof(Key) * 8;
|
||||
@ -187,8 +201,113 @@ private:
|
||||
static KeyBits keyToBits(Key x) { return ext::bit_cast<KeyBits>(x); }
|
||||
static Key bitsToKey(KeyBits x) { return ext::bit_cast<Key>(x); }
|
||||
|
||||
static void insertionSortInternal(Element *arr, size_t size)
|
||||
{
|
||||
Element * end = arr + size;
|
||||
for (Element * i = arr + 1; i < end; ++i)
|
||||
{
|
||||
if (Traits::less(Traits::extractKey(*i), Traits::extractKey(*(i - 1))))
|
||||
{
|
||||
Element * j;
|
||||
Element tmp = *i;
|
||||
*i = *(i - 1);
|
||||
for (j = i - 1; j > arr && Traits::less(Traits::extractKey(tmp), Traits::extractKey(*(j - 1))); --j)
|
||||
*j = *(j - 1);
|
||||
*j = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Main MSD radix sort subroutine
|
||||
* Puts elements to buckets based on PASS-th digit, then recursively calls insertion sort or itself on the buckets
|
||||
*/
|
||||
template <size_t PASS>
|
||||
static inline void radixSortMSDInternal(Element * arr, size_t size, size_t limit)
|
||||
{
|
||||
Element * last_list[HISTOGRAM_SIZE + 1];
|
||||
Element ** last = last_list + 1;
|
||||
size_t count[HISTOGRAM_SIZE] = {0};
|
||||
|
||||
for (Element * i = arr; i < arr + size; ++i)
|
||||
++count[getPart(PASS, *i)];
|
||||
|
||||
last_list[0] = last_list[1] = arr;
|
||||
|
||||
size_t buckets_for_recursion = HISTOGRAM_SIZE;
|
||||
Element * finish = arr + size;
|
||||
for (size_t i = 1; i < HISTOGRAM_SIZE; ++i)
|
||||
{
|
||||
last[i] = last[i - 1] + count[i - 1];
|
||||
if (last[i] >= arr + limit)
|
||||
{
|
||||
buckets_for_recursion = i;
|
||||
finish = last[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, we have the following variables:
|
||||
* count[i] is the size of i-th bucket
|
||||
* last[i] is a pointer to the beginning of i-th bucket, last[-1] == last[0]
|
||||
* buckets_for_recursion is the number of buckets that should be sorted, the last of them only partially
|
||||
* finish is a pointer to the end of the first buckets_for_recursion buckets
|
||||
*/
|
||||
|
||||
// Scatter array elements to buckets until the first buckets_for_recursion buckets are full
|
||||
for (size_t i = 0; i < buckets_for_recursion; ++i)
|
||||
{
|
||||
Element * end = last[i - 1] + count[i];
|
||||
if (end == finish)
|
||||
{
|
||||
last[i] = end;
|
||||
break;
|
||||
}
|
||||
while (last[i] != end)
|
||||
{
|
||||
Element swapper = *last[i];
|
||||
KeyBits tag = getPart(PASS, swapper);
|
||||
if (tag != i)
|
||||
{
|
||||
do
|
||||
{
|
||||
std::swap(swapper, *last[tag]++);
|
||||
} while ((tag = getPart(PASS, swapper)) != i);
|
||||
*last[i] = swapper;
|
||||
}
|
||||
++last[i];
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (PASS > 0)
|
||||
{
|
||||
// Recursively sort buckets, except the last one
|
||||
for (size_t i = 0; i < buckets_for_recursion - 1; ++i)
|
||||
{
|
||||
Element * start = last[i - 1];
|
||||
size_t subsize = last[i] - last[i - 1];
|
||||
radixSortMSDInternalHelper<PASS - 1>(start, subsize, subsize);
|
||||
}
|
||||
|
||||
// Sort last necessary bucket with limit
|
||||
Element * start = last[buckets_for_recursion - 2];
|
||||
size_t subsize = last[buckets_for_recursion - 1] - last[buckets_for_recursion - 2];
|
||||
size_t sublimit = limit - (last[buckets_for_recursion - 1] - arr);
|
||||
radixSortMSDInternalHelper<PASS - 1>(start, subsize, sublimit);
|
||||
}
|
||||
}
|
||||
|
||||
// A helper to choose sorting algorithm based on array length
|
||||
template <size_t PASS>
|
||||
static inline void radixSortMSDInternalHelper(Element * arr, size_t size, size_t limit)
|
||||
{
|
||||
if (size <= INSERTION_SORT_THRESHOLD)
|
||||
insertionSortInternal(arr, size);
|
||||
else
|
||||
radixSortMSDInternal<PASS>(arr, size, limit);
|
||||
}
|
||||
|
||||
public:
|
||||
static void execute(Element * arr, size_t size)
|
||||
/// Least significant digit radix sort (stable)
|
||||
static void executeLSD(Element * arr, size_t size)
|
||||
{
|
||||
/// If the array is smaller than 256, then it is better to use another algorithm.
|
||||
|
||||
@ -209,8 +328,8 @@ public:
|
||||
if (!Traits::Transform::transform_is_simple)
|
||||
Traits::extractKey(arr[i]) = bitsToKey(Traits::Transform::forward(keyToBits(Traits::extractKey(arr[i]))));
|
||||
|
||||
for (size_t j = 0; j < NUM_PASSES; ++j)
|
||||
++histograms[j * HISTOGRAM_SIZE + getPart(j, keyToBits(Traits::extractKey(arr[i])))];
|
||||
for (size_t pass = 0; pass < NUM_PASSES; ++pass)
|
||||
++histograms[pass * HISTOGRAM_SIZE + getPart(pass, keyToBits(Traits::extractKey(arr[i])))];
|
||||
}
|
||||
|
||||
{
|
||||
@ -219,31 +338,31 @@ public:
|
||||
|
||||
for (size_t i = 0; i < HISTOGRAM_SIZE; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < NUM_PASSES; ++j)
|
||||
for (size_t pass = 0; pass < NUM_PASSES; ++pass)
|
||||
{
|
||||
size_t tmp = histograms[j * HISTOGRAM_SIZE + i] + sums[j];
|
||||
histograms[j * HISTOGRAM_SIZE + i] = sums[j] - 1;
|
||||
sums[j] = tmp;
|
||||
size_t tmp = histograms[pass * HISTOGRAM_SIZE + i] + sums[pass];
|
||||
histograms[pass * HISTOGRAM_SIZE + i] = sums[pass] - 1;
|
||||
sums[pass] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the elements in the order starting from the least bit piece, and then do a few passes on the number of pieces.
|
||||
for (size_t j = 0; j < NUM_PASSES; ++j)
|
||||
for (size_t pass = 0; pass < NUM_PASSES; ++pass)
|
||||
{
|
||||
Element * writer = j % 2 ? arr : swap_buffer;
|
||||
Element * reader = j % 2 ? swap_buffer : arr;
|
||||
Element * writer = pass % 2 ? arr : swap_buffer;
|
||||
Element * reader = pass % 2 ? swap_buffer : arr;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
size_t pos = getPart(j, keyToBits(Traits::extractKey(reader[i])));
|
||||
size_t pos = getPart(pass, keyToBits(Traits::extractKey(reader[i])));
|
||||
|
||||
/// Place the element on the next free position.
|
||||
auto & dest = writer[++histograms[j * HISTOGRAM_SIZE + pos]];
|
||||
auto & dest = writer[++histograms[pass * HISTOGRAM_SIZE + pos]];
|
||||
dest = reader[i];
|
||||
|
||||
/// On the last pass, we do the reverse transformation.
|
||||
if (!Traits::Transform::transform_is_simple && j == NUM_PASSES - 1)
|
||||
if (!Traits::Transform::transform_is_simple && pass == NUM_PASSES - 1)
|
||||
Traits::extractKey(dest) = bitsToKey(Traits::Transform::backward(keyToBits(Traits::extractKey(reader[i]))));
|
||||
}
|
||||
}
|
||||
@ -255,40 +374,53 @@ public:
|
||||
|
||||
allocator.deallocate(swap_buffer, size * sizeof(Element));
|
||||
}
|
||||
|
||||
/* Most significant digit radix sort
|
||||
* Usually slower than LSD and is not stable, but allows partial sorting
|
||||
*
|
||||
* Based on https://github.com/voutcn/kxsort, license:
|
||||
* The MIT License
|
||||
* Copyright (c) 2016 Dinghua Li <voutcn@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
static void executeMSD(Element * arr, size_t size, size_t limit)
|
||||
{
|
||||
limit = std::min(limit, size);
|
||||
radixSortMSDInternalHelper<NUM_PASSES - 1>(arr, size, limit);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Helper functions for numeric types.
|
||||
/// Use RadixSort with custom traits for complex types instead.
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_unsigned_v<T> && std::is_integral_v<T>, void>
|
||||
radixSort(T * arr, size_t size)
|
||||
void radixSortLSD(T *arr, size_t size)
|
||||
{
|
||||
return RadixSort<RadixSortUIntTraits<T>>::execute(arr, size);
|
||||
RadixSort<RadixSortNumTraits<T>>::executeLSD(arr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_signed_v<T> && std::is_integral_v<T>, void>
|
||||
radixSort(T * arr, size_t size)
|
||||
void radixSortMSD(T *arr, size_t size, size_t limit)
|
||||
{
|
||||
return RadixSort<RadixSortIntTraits<T>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T>, void>
|
||||
radixSort(T * arr, size_t size)
|
||||
{
|
||||
return RadixSort<RadixSortFloatTraits<T>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Key>
|
||||
std::enable_if_t<std::is_integral_v<_Key>, void>
|
||||
radixSort(_Element * arr, size_t size)
|
||||
{
|
||||
return RadixSort<RadixSortUIntTraits<_Element, _Key>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Key>
|
||||
std::enable_if_t<std::is_floating_point_v<_Key>, void>
|
||||
radixSort(_Element * arr, size_t size)
|
||||
{
|
||||
return RadixSort<RadixSortFloatTraits<_Element, _Key>>::execute(arr, size);
|
||||
RadixSort<RadixSortNumTraits<T>>::executeMSD(arr, size, limit);
|
||||
}
|
||||
|
17
dbms/src/Common/SettingsChanges.h
Normal file
17
dbms/src/Common/SettingsChanges.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Field.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
struct SettingChange
|
||||
{
|
||||
String name;
|
||||
Field value;
|
||||
};
|
||||
|
||||
using SettingsChanges = std::vector<SettingChange>;
|
||||
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
#cmakedefine01 USE_BROTLI
|
||||
#cmakedefine01 USE_SSL
|
||||
#cmakedefine01 USE_HYPERSCAN
|
||||
#cmakedefine01 USE_SIMDJSON
|
||||
#cmakedefine01 USE_LFALLOC
|
||||
#cmakedefine01 USE_LFALLOC_RANDOM_HINT
|
||||
|
||||
|
@ -16,7 +16,7 @@ void NO_INLINE sort1(Key * data, size_t size)
|
||||
|
||||
void NO_INLINE sort2(Key * data, size_t size)
|
||||
{
|
||||
radixSort(data, size);
|
||||
radixSortLSD(data, size);
|
||||
}
|
||||
|
||||
void NO_INLINE sort3(Key * data, size_t size)
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace LZ4
|
||||
{
|
||||
|
||||
@ -41,6 +40,10 @@ inline void copy8(UInt8 * dst, const UInt8 * src)
|
||||
|
||||
inline void wildCopy8(UInt8 * dst, const UInt8 * src, UInt8 * dst_end)
|
||||
{
|
||||
/// Unrolling with clang is doing >10% performance degrade.
|
||||
#if defined(__clang__)
|
||||
#pragma nounroll
|
||||
#endif
|
||||
do
|
||||
{
|
||||
copy8(dst, src);
|
||||
@ -204,7 +207,6 @@ inline void copyOverlap8Shuffle(UInt8 * op, const UInt8 *& match, const size_t o
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
template <> void inline copy<8>(UInt8 * dst, const UInt8 * src) { copy8(dst, src); }
|
||||
template <> void inline wildCopy<8>(UInt8 * dst, const UInt8 * src, UInt8 * dst_end) { wildCopy8(dst, src, dst_end); }
|
||||
template <> void inline copyOverlap<8, false>(UInt8 * op, const UInt8 *& match, const size_t offset) { copyOverlap8(op, match, offset); }
|
||||
@ -221,10 +223,12 @@ inline void copy16(UInt8 * dst, const UInt8 * src)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline void wildCopy16(UInt8 * dst, const UInt8 * src, UInt8 * dst_end)
|
||||
{
|
||||
/// Unrolling with clang is doing >10% performance degrade.
|
||||
#if defined(__clang__)
|
||||
#pragma nounroll
|
||||
#endif
|
||||
do
|
||||
{
|
||||
copy16(dst, src);
|
||||
@ -342,8 +346,73 @@ template <> void inline copyOverlap<16, false>(UInt8 * op, const UInt8 *& match,
|
||||
template <> void inline copyOverlap<16, true>(UInt8 * op, const UInt8 *& match, const size_t offset) { copyOverlap16Shuffle(op, match, offset); }
|
||||
|
||||
|
||||
/// See also https://stackoverflow.com/a/30669632
|
||||
inline void copy32(UInt8 * dst, const UInt8 * src)
|
||||
{
|
||||
/// There was an AVX here but with mash with SSE instructions, we got a big slowdown.
|
||||
#if defined(__SSE2__)
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(src)));
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + 16),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 16)));
|
||||
#else
|
||||
memcpy(dst, src, 16);
|
||||
memcpy(dst + 16, src + 16, 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void wildCopy32(UInt8 * dst, const UInt8 * src, UInt8 * dst_end)
|
||||
{
|
||||
/// Unrolling with clang is doing >10% performance degrade.
|
||||
#if defined(__clang__)
|
||||
#pragma nounroll
|
||||
#endif
|
||||
do
|
||||
{
|
||||
copy32(dst, src);
|
||||
dst += 32;
|
||||
src += 32;
|
||||
} while (dst < dst_end);
|
||||
}
|
||||
|
||||
inline void copyOverlap32(UInt8 * op, const UInt8 *& match, const size_t offset)
|
||||
{
|
||||
/// 4 % n.
|
||||
static constexpr int shift1[]
|
||||
= { 0, 1, 2, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 };
|
||||
|
||||
/// 8 % n - 4 % n
|
||||
static constexpr int shift2[]
|
||||
= { 0, 0, 0, 1, 0, -1, -2, -3, -4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 };
|
||||
|
||||
/// 16 % n - 8 % n
|
||||
static constexpr int shift3[]
|
||||
= { 0, 0, 0, -1, 0, -2, 2, 1, 8, -1, -2, -3, -4, -5, -6, -7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 };
|
||||
|
||||
/// 32 % n - 16 % n
|
||||
static constexpr int shift4[]
|
||||
= { 0, 0, 0, 1, 0, 1, -2, 2, 0, -2, -4, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9,-10,-11,-12,-13,-14,-15 };
|
||||
|
||||
op[0] = match[0];
|
||||
op[1] = match[1];
|
||||
op[2] = match[2];
|
||||
op[3] = match[3];
|
||||
|
||||
match += shift1[offset];
|
||||
memcpy(op + 4, match, 4);
|
||||
match += shift2[offset];
|
||||
memcpy(op + 8, match, 8);
|
||||
match += shift3[offset];
|
||||
memcpy(op + 16, match, 16);
|
||||
match += shift4[offset];
|
||||
}
|
||||
|
||||
|
||||
template <> void inline copy<32>(UInt8 * dst, const UInt8 * src) { copy32(dst, src); }
|
||||
template <> void inline wildCopy<32>(UInt8 * dst, const UInt8 * src, UInt8 * dst_end) { wildCopy32(dst, src, dst_end); }
|
||||
template <> void inline copyOverlap<32, false>(UInt8 * op, const UInt8 *& match, const size_t offset) { copyOverlap32(op, match, offset); }
|
||||
|
||||
|
||||
/// See also https://stackoverflow.com/a/30669632
|
||||
|
||||
template <size_t copy_amount, bool use_shuffle>
|
||||
void NO_INLINE decompressImpl(
|
||||
@ -355,6 +424,10 @@ void NO_INLINE decompressImpl(
|
||||
UInt8 * op = reinterpret_cast<UInt8 *>(dest);
|
||||
UInt8 * const output_end = op + dest_size;
|
||||
|
||||
/// Unrolling with clang is doing >10% performance degrade.
|
||||
#if defined(__clang__)
|
||||
#pragma nounroll
|
||||
#endif
|
||||
while (1)
|
||||
{
|
||||
size_t length;
|
||||
@ -464,6 +537,7 @@ void decompress(
|
||||
if (source_size == 0 || dest_size == 0)
|
||||
return;
|
||||
|
||||
|
||||
/// Don't run timer if the block is too small.
|
||||
if (dest_size >= 32768)
|
||||
{
|
||||
@ -472,13 +546,14 @@ void decompress(
|
||||
/// Run the selected method and measure time.
|
||||
|
||||
Stopwatch watch;
|
||||
|
||||
if (best_variant == 0)
|
||||
decompressImpl<16, true>(source, dest, dest_size);
|
||||
if (best_variant == 1)
|
||||
decompressImpl<16, false>(source, dest, dest_size);
|
||||
if (best_variant == 2)
|
||||
decompressImpl<8, true>(source, dest, dest_size);
|
||||
if (best_variant == 3)
|
||||
decompressImpl<32, false>(source, dest, dest_size);
|
||||
|
||||
watch.stop();
|
||||
|
||||
@ -531,7 +606,6 @@ void statistics(
|
||||
const UInt8 * ip = reinterpret_cast<const UInt8 *>(source);
|
||||
UInt8 * op = reinterpret_cast<UInt8 *>(dest);
|
||||
UInt8 * const output_end = op + dest_size;
|
||||
|
||||
while (1)
|
||||
{
|
||||
size_t length;
|
||||
|
@ -34,7 +34,7 @@ namespace LZ4
|
||||
* that is allowed to read/write.
|
||||
* This value is a little overestimation.
|
||||
*/
|
||||
static constexpr size_t ADDITIONAL_BYTES_AT_END_OF_BUFFER = 32;
|
||||
static constexpr size_t ADDITIONAL_BYTES_AT_END_OF_BUFFER = 64;
|
||||
|
||||
|
||||
/** When decompressing uniform sequence of blocks (for example, blocks from one file),
|
||||
@ -88,7 +88,7 @@ struct PerformanceStatistics
|
||||
};
|
||||
|
||||
/// Number of different algorithms to select from.
|
||||
static constexpr size_t NUM_ELEMENTS = 3;
|
||||
static constexpr size_t NUM_ELEMENTS = 4;
|
||||
|
||||
/// Cold invocations may be affected by additional memory latencies. Don't take first invocations into account.
|
||||
static constexpr double NUM_INVOCATIONS_TO_THROW_OFF = 2;
|
||||
|
@ -123,3 +123,7 @@
|
||||
#else
|
||||
#define OPTIMIZE(x)
|
||||
#endif
|
||||
|
||||
/// This number is only used for distributed version compatible.
|
||||
/// It could be any magic number.
|
||||
#define DBMS_DISTRIBUTED_SENDS_MAGIC_NUMBER 0xCAFECABE
|
||||
|
@ -1,12 +1,10 @@
|
||||
#include "Settings.h"
|
||||
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
#include <Core/Field.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Columns/ColumnArray.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <string.h>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -14,94 +12,13 @@ namespace DB
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int UNKNOWN_SETTING;
|
||||
extern const int THERE_IS_NO_PROFILE;
|
||||
extern const int NO_ELEMENTS_IN_CONFIG;
|
||||
}
|
||||
|
||||
|
||||
/// Set the configuration by name.
|
||||
void Settings::set(const String & name, const Field & value)
|
||||
{
|
||||
#define TRY_SET(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
else if (name == #NAME) NAME.set(value);
|
||||
IMPLEMENT_SETTINGS_COLLECTION(Settings, LIST_OF_SETTINGS)
|
||||
|
||||
if (false) {}
|
||||
APPLY_FOR_SETTINGS(TRY_SET)
|
||||
else
|
||||
throw Exception("Unknown setting " + name, ErrorCodes::UNKNOWN_SETTING);
|
||||
|
||||
#undef TRY_SET
|
||||
}
|
||||
|
||||
/// Set the configuration by name. Read the binary serialized value from the buffer (for interserver interaction).
|
||||
void Settings::set(const String & name, ReadBuffer & buf)
|
||||
{
|
||||
#define TRY_SET(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
else if (name == #NAME) NAME.set(buf);
|
||||
|
||||
if (false) {}
|
||||
APPLY_FOR_SETTINGS(TRY_SET)
|
||||
else
|
||||
throw Exception("Unknown setting " + name, ErrorCodes::UNKNOWN_SETTING);
|
||||
|
||||
#undef TRY_SET
|
||||
}
|
||||
|
||||
/// Skip the binary-serialized value from the buffer.
|
||||
void Settings::ignore(const String & name, ReadBuffer & buf)
|
||||
{
|
||||
#define TRY_IGNORE(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
else if (name == #NAME) decltype(NAME)(DEFAULT).set(buf);
|
||||
|
||||
if (false) {}
|
||||
APPLY_FOR_SETTINGS(TRY_IGNORE)
|
||||
else
|
||||
throw Exception("Unknown setting " + name, ErrorCodes::UNKNOWN_SETTING);
|
||||
|
||||
#undef TRY_IGNORE
|
||||
}
|
||||
|
||||
/** Set the setting by name. Read the value in text form from a string (for example, from a config, or from a URL parameter).
|
||||
*/
|
||||
void Settings::set(const String & name, const String & value)
|
||||
{
|
||||
#define TRY_SET(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
else if (name == #NAME) NAME.set(value);
|
||||
|
||||
if (false) {}
|
||||
APPLY_FOR_SETTINGS(TRY_SET)
|
||||
else
|
||||
throw Exception("Unknown setting " + name, ErrorCodes::UNKNOWN_SETTING);
|
||||
|
||||
#undef TRY_SET
|
||||
}
|
||||
|
||||
String Settings::get(const String & name) const
|
||||
{
|
||||
#define GET(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
else if (name == #NAME) return NAME.toString();
|
||||
|
||||
if (false) {}
|
||||
APPLY_FOR_SETTINGS(GET)
|
||||
else
|
||||
throw Exception("Unknown setting " + name, ErrorCodes::UNKNOWN_SETTING);
|
||||
|
||||
#undef GET
|
||||
}
|
||||
|
||||
bool Settings::tryGet(const String & name, String & value) const
|
||||
{
|
||||
#define TRY_GET(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
else if (name == #NAME) { value = NAME.toString(); return true; }
|
||||
|
||||
if (false) {}
|
||||
APPLY_FOR_SETTINGS(TRY_GET)
|
||||
else
|
||||
return false;
|
||||
|
||||
#undef TRY_GET
|
||||
}
|
||||
|
||||
/** Set the settings from the profile (in the server configuration, many settings can be listed in one profile).
|
||||
* The profile can also be set using the `set` functions, like the `profile` setting.
|
||||
@ -118,6 +35,8 @@ void Settings::setProfile(const String & profile_name, const Poco::Util::Abstrac
|
||||
|
||||
for (const std::string & key : config_keys)
|
||||
{
|
||||
if (key == "constraints")
|
||||
continue;
|
||||
if (key == "profile") /// Inheritance of one profile from another.
|
||||
setProfile(config.getString(elem + "." + key), config);
|
||||
else
|
||||
@ -139,47 +58,6 @@ void Settings::loadSettingsFromConfig(const String & path, const Poco::Util::Abs
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the settings from the buffer. They are written as a set of name-value pairs that go successively, ending with an empty `name`.
|
||||
/// If the `check_readonly` flag is set, `readonly` is set in the preferences, but some changes have occurred - throw an exception.
|
||||
void Settings::deserialize(ReadBuffer & buf)
|
||||
{
|
||||
auto before_readonly = readonly;
|
||||
|
||||
while (true)
|
||||
{
|
||||
String name;
|
||||
readBinary(name, buf);
|
||||
|
||||
/// An empty string is the marker for the end of the settings.
|
||||
if (name.empty())
|
||||
break;
|
||||
|
||||
/// If readonly = 2, then you can change the settings, except for the readonly setting.
|
||||
if (before_readonly == 0 || (before_readonly == 2 && name != "readonly"))
|
||||
set(name, buf);
|
||||
else
|
||||
ignore(name, buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Record the changed settings to the buffer. (For example, to send to a remote server.)
|
||||
void Settings::serialize(WriteBuffer & buf) const
|
||||
{
|
||||
#define WRITE(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
if (NAME.changed) \
|
||||
{ \
|
||||
writeStringBinary(#NAME, buf); \
|
||||
NAME.write(buf); \
|
||||
}
|
||||
|
||||
APPLY_FOR_SETTINGS(WRITE)
|
||||
|
||||
/// An empty string is a marker for the end of the settings.
|
||||
writeStringBinary("", buf);
|
||||
|
||||
#undef WRITE
|
||||
}
|
||||
|
||||
void Settings::dumpToArrayColumns(IColumn * column_names_, IColumn * column_values_, bool changed_only)
|
||||
{
|
||||
/// Convert ptr and make simple check
|
||||
@ -188,17 +66,20 @@ void Settings::dumpToArrayColumns(IColumn * column_names_, IColumn * column_valu
|
||||
|
||||
size_t size = 0;
|
||||
|
||||
#define ADD_SETTING(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
if (!changed_only || NAME.changed) \
|
||||
{ \
|
||||
if (column_names) \
|
||||
column_names->getData().insertData(#NAME, strlen(#NAME)); \
|
||||
if (column_values) \
|
||||
column_values->getData().insert(NAME.toString()); \
|
||||
++size; \
|
||||
for (const auto & setting : *this)
|
||||
{
|
||||
if (!changed_only || setting.isChanged())
|
||||
{
|
||||
if (column_names)
|
||||
{
|
||||
StringRef name = setting.getName();
|
||||
column_names->getData().insertData(name.data, name.size);
|
||||
}
|
||||
if (column_values)
|
||||
column_values->getData().insert(setting.getValueAsString());
|
||||
++size;
|
||||
}
|
||||
}
|
||||
APPLY_FOR_SETTINGS(ADD_SETTING)
|
||||
#undef ADD_SETTING
|
||||
|
||||
if (column_names)
|
||||
{
|
||||
@ -216,4 +97,16 @@ void Settings::dumpToArrayColumns(IColumn * column_names_, IColumn * column_valu
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::addProgramOptions(boost::program_options::options_description & options)
|
||||
{
|
||||
for (size_t index = 0; index != Settings::size(); ++index)
|
||||
{
|
||||
auto on_program_option
|
||||
= boost::function1<void, const std::string &>([this, index](const std::string & value) { set(index, value); });
|
||||
options.add(boost::shared_ptr<boost::program_options::option_description>(new boost::program_options::option_description(
|
||||
Settings::getName(index).data,
|
||||
boost::program_options::value<std::string>()->composing()->notifier(on_program_option),
|
||||
Settings::getDescription(index).data)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,15 +12,24 @@ namespace Poco
|
||||
}
|
||||
}
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace program_options
|
||||
{
|
||||
class options_description;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class IColumn;
|
||||
class Field;
|
||||
|
||||
|
||||
/** Settings of query execution.
|
||||
*/
|
||||
struct Settings
|
||||
struct Settings : public SettingsCollection<Settings>
|
||||
{
|
||||
/// For initialization from empty initializer-list to be "value initialization", not "aggregate initialization" in C++14.
|
||||
/// http://en.cppreference.com/w/cpp/language/aggregate_initialization
|
||||
@ -33,7 +42,7 @@ struct Settings
|
||||
* but we are not going to do it, because settings is used everywhere as static struct fields.
|
||||
*/
|
||||
|
||||
#define APPLY_FOR_SETTINGS(M) \
|
||||
#define LIST_OF_SETTINGS(M) \
|
||||
M(SettingUInt64, min_compress_block_size, 65536, "The actual size of the block to compress, if the uncompressed data less than max_compress_block_size is no less than this value and no less than the volume of data for one mark.") \
|
||||
M(SettingUInt64, max_compress_block_size, 1048576, "The maximum size of blocks of uncompressed data before compressing for writing to a table.") \
|
||||
M(SettingUInt64, max_block_size, DEFAULT_BLOCK_SIZE, "Maximum block size for reading") \
|
||||
@ -153,7 +162,8 @@ struct Settings
|
||||
\
|
||||
M(SettingBool, add_http_cors_header, false, "Write add http CORS header.") \
|
||||
\
|
||||
M(SettingBool, input_format_skip_unknown_fields, false, "Skip columns with unknown names from input data (it works for JSONEachRow and TSKV formats).") \
|
||||
M(SettingBool, input_format_skip_unknown_fields, false, "Skip columns with unknown names from input data (it works for JSONEachRow, CSVWithNames, TSVWithNames and TSKV formats).") \
|
||||
M(SettingBool, input_format_with_names_use_header, false, "For TSVWithNames and CSVWithNames input formats this controls whether format parser is to assume that column data appear in the input exactly as they are specified in the header.") \
|
||||
M(SettingBool, input_format_import_nested_json, false, "Map nested JSON data to nested tables (it works for JSONEachRow format).") \
|
||||
M(SettingBool, input_format_defaults_for_omitted_fields, false, "For input data calculate default expressions for omitted fields (it works for JSONEachRow format).") \
|
||||
\
|
||||
@ -216,25 +226,25 @@ struct Settings
|
||||
\
|
||||
M(SettingUInt64, max_rows_to_read, 0, "Limit on read rows from the most 'deep' sources. That is, only in the deepest subquery. When reading from a remote server, it is only checked on a remote server.") \
|
||||
M(SettingUInt64, max_bytes_to_read, 0, "Limit on read bytes (after decompression) from the most 'deep' sources. That is, only in the deepest subquery. When reading from a remote server, it is only checked on a remote server.") \
|
||||
M(SettingOverflowMode<false>, read_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowMode, read_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
\
|
||||
M(SettingUInt64, max_rows_to_group_by, 0, "") \
|
||||
M(SettingOverflowMode<true>, group_by_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowModeGroupBy, group_by_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingUInt64, max_bytes_before_external_group_by, 0, "") \
|
||||
\
|
||||
M(SettingUInt64, max_rows_to_sort, 0, "") \
|
||||
M(SettingUInt64, max_bytes_to_sort, 0, "") \
|
||||
M(SettingOverflowMode<false>, sort_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowMode, sort_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingUInt64, max_bytes_before_external_sort, 0, "") \
|
||||
M(SettingUInt64, max_bytes_before_remerge_sort, 1000000000, "In case of ORDER BY with LIMIT, when memory usage is higher than specified threshold, perform additional steps of merging blocks before final merge to keep just top LIMIT rows.") \
|
||||
\
|
||||
M(SettingUInt64, max_result_rows, 0, "Limit on result size in rows. Also checked for intermediate data sent from remote servers.") \
|
||||
M(SettingUInt64, max_result_bytes, 0, "Limit on result size in bytes (uncompressed). Also checked for intermediate data sent from remote servers.") \
|
||||
M(SettingOverflowMode<false>, result_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowMode, result_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
\
|
||||
/* TODO: Check also when merging and finalizing aggregate functions. */ \
|
||||
M(SettingSeconds, max_execution_time, 0, "") \
|
||||
M(SettingOverflowMode<false>, timeout_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowMode, timeout_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
\
|
||||
M(SettingUInt64, min_execution_speed, 0, "Minimum number of execution rows per second.") \
|
||||
M(SettingUInt64, max_execution_speed, 0, "Maximum number of execution rows per second.") \
|
||||
@ -256,20 +266,20 @@ struct Settings
|
||||
\
|
||||
M(SettingUInt64, max_rows_in_set, 0, "Maximum size of the set (in number of elements) resulting from the execution of the IN section.") \
|
||||
M(SettingUInt64, max_bytes_in_set, 0, "Maximum size of the set (in bytes in memory) resulting from the execution of the IN section.") \
|
||||
M(SettingOverflowMode<false>, set_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowMode, set_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
\
|
||||
M(SettingUInt64, max_rows_in_join, 0, "Maximum size of the hash table for JOIN (in number of rows).") \
|
||||
M(SettingUInt64, max_bytes_in_join, 0, "Maximum size of the hash table for JOIN (in number of bytes in memory).") \
|
||||
M(SettingOverflowMode<false>, join_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowMode, join_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingBool, join_any_take_last_row, false, "When disabled (default) ANY JOIN will take the first found row for a key. When enabled, it will take the last row seen if there are multiple rows for the same key.") \
|
||||
\
|
||||
M(SettingUInt64, max_rows_to_transfer, 0, "Maximum size (in rows) of the transmitted external table obtained when the GLOBAL IN/JOIN section is executed.") \
|
||||
M(SettingUInt64, max_bytes_to_transfer, 0, "Maximum size (in uncompressed bytes) of the transmitted external table obtained when the GLOBAL IN/JOIN section is executed.") \
|
||||
M(SettingOverflowMode<false>, transfer_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowMode, transfer_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
\
|
||||
M(SettingUInt64, max_rows_in_distinct, 0, "Maximum number of elements during execution of DISTINCT.") \
|
||||
M(SettingUInt64, max_bytes_in_distinct, 0, "Maximum total size of state (in uncompressed bytes) in memory for the execution of DISTINCT.") \
|
||||
M(SettingOverflowMode<false>, distinct_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
M(SettingOverflowMode, distinct_overflow_mode, OverflowMode::THROW, "What to do when the limit is exceeded.") \
|
||||
\
|
||||
M(SettingUInt64, max_memory_usage, 0, "Maximum memory usage for processing of single query. Zero means unlimited.") \
|
||||
M(SettingUInt64, max_memory_usage_for_user, 0, "Maximum memory usage for processing all concurrently running queries for the user. Zero means unlimited.") \
|
||||
@ -314,29 +324,7 @@ struct Settings
|
||||
\
|
||||
M(SettingUInt64, max_partitions_per_insert_block, 100, "Limit maximum number of partitions in single INSERTed block. Zero means unlimited. Throw exception if the block contains too many partitions. This setting is a safety threshold, because using large number of partitions is a common misconception.") \
|
||||
|
||||
#define DECLARE(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
TYPE NAME {DEFAULT};
|
||||
|
||||
APPLY_FOR_SETTINGS(DECLARE)
|
||||
|
||||
#undef DECLARE
|
||||
|
||||
/// Set setting by name.
|
||||
void set(const String & name, const Field & value);
|
||||
|
||||
/// Set setting by name. Read value, serialized in binary form from buffer (for inter-server communication).
|
||||
void set(const String & name, ReadBuffer & buf);
|
||||
|
||||
/// Skip value, serialized in binary form in buffer.
|
||||
void ignore(const String & name, ReadBuffer & buf);
|
||||
|
||||
/// Set setting by name. Read value in text form from string (for example, from configuration file or from URL parameter).
|
||||
void set(const String & name, const String & value);
|
||||
|
||||
/// Get setting by name. Converts value to String.
|
||||
String get(const String & name) const;
|
||||
|
||||
bool tryGet(const String & name, String & value) const;
|
||||
DECLARE_SETTINGS_COLLECTION(LIST_OF_SETTINGS)
|
||||
|
||||
/** Set multiple settings from "profile" (in server configuration file (users.xml), profiles contain groups of multiple settings).
|
||||
* The profile can also be set using the `set` functions, like the profile setting.
|
||||
@ -346,16 +334,12 @@ struct Settings
|
||||
/// Load settings from configuration file, at "path" prefix in configuration.
|
||||
void loadSettingsFromConfig(const String & path, const Poco::Util::AbstractConfiguration & config);
|
||||
|
||||
/// Read settings from buffer. They are serialized as list of contiguous name-value pairs, finished with empty name.
|
||||
/// If readonly=1 is set, ignore read settings.
|
||||
void deserialize(ReadBuffer & buf);
|
||||
|
||||
/// Write changed settings to buffer. (For example, to be sent to remote server.)
|
||||
void serialize(WriteBuffer & buf) const;
|
||||
|
||||
/// Dumps profile events to two columns of type Array(String)
|
||||
void dumpToArrayColumns(IColumn * column_names, IColumn * column_values, bool changed_only = true);
|
||||
|
||||
/// Adds program options to set the settings from a command line.
|
||||
/// (Don't forget to call notify() on the `variables_map` after parsing it!)
|
||||
void addProgramOptions(boost::program_options::options_description & options);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
@ -25,55 +25,97 @@ namespace ErrorCodes
|
||||
extern const int UNKNOWN_LOG_LEVEL;
|
||||
extern const int SIZE_OF_FIXED_STRING_DOESNT_MATCH;
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int UNKNOWN_SETTING;
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
String SettingInt<IntType>::toString() const
|
||||
|
||||
template <typename Type>
|
||||
String SettingNumber<Type>::toString() const
|
||||
{
|
||||
return DB::toString(value);
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
void SettingInt<IntType>::set(IntType x)
|
||||
template <typename Type>
|
||||
Field SettingNumber<Type>::toField() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void SettingNumber<Type>::set(Type x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
void SettingInt<IntType>::set(const Field & x)
|
||||
template <typename Type>
|
||||
void SettingNumber<Type>::set(const Field & x)
|
||||
{
|
||||
set(applyVisitor(FieldVisitorConvertToNumber<IntType>(), x));
|
||||
if (x.getType() == Field::Types::String)
|
||||
set(get<const String &>(x));
|
||||
else
|
||||
set(applyVisitor(FieldVisitorConvertToNumber<Type>(), x));
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
void SettingInt<IntType>::set(const String & x)
|
||||
template <typename Type>
|
||||
void SettingNumber<Type>::set(const String & x)
|
||||
{
|
||||
set(parse<IntType>(x));
|
||||
set(parse<Type>(x));
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
void SettingInt<IntType>::set(ReadBuffer & buf)
|
||||
template <typename Type>
|
||||
void SettingNumber<Type>::serialize(WriteBuffer & buf) const
|
||||
{
|
||||
IntType x = 0;
|
||||
readVarT(x, buf);
|
||||
if constexpr (std::is_integral_v<Type> && std::is_unsigned_v<Type>)
|
||||
writeVarUInt(static_cast<UInt64>(value), buf);
|
||||
else if constexpr (std::is_integral_v<Type> && std::is_signed_v<Type>)
|
||||
writeVarInt(static_cast<Int64>(value), buf);
|
||||
else
|
||||
{
|
||||
static_assert(std::is_floating_point_v<Type>);
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void SettingNumber<Type>::deserialize(ReadBuffer & buf)
|
||||
{
|
||||
if constexpr (std::is_integral_v<Type> && std::is_unsigned_v<Type>)
|
||||
{
|
||||
UInt64 x;
|
||||
readVarUInt(x, buf);
|
||||
set(static_cast<Type>(x));
|
||||
}
|
||||
else if constexpr (std::is_integral_v<Type> && std::is_signed_v<Type>)
|
||||
{
|
||||
Int64 x;
|
||||
readVarInt(x, buf);
|
||||
set(static_cast<Type>(x));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(std::is_floating_point_v<Type>);
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
void SettingInt<IntType>::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeVarT(value, buf);
|
||||
}
|
||||
|
||||
template struct SettingInt<UInt64>;
|
||||
template struct SettingInt<Int64>;
|
||||
template struct SettingNumber<UInt64>;
|
||||
template struct SettingNumber<Int64>;
|
||||
template struct SettingNumber<float>;
|
||||
template struct SettingNumber<bool>;
|
||||
|
||||
|
||||
String SettingMaxThreads::toString() const
|
||||
{
|
||||
/// Instead of the `auto` value, we output the actual value to make it easier to see.
|
||||
return DB::toString(value);
|
||||
return is_auto ? ("auto(" + DB::toString(value) + ")") : DB::toString(value);
|
||||
}
|
||||
|
||||
Field SettingMaxThreads::toField() const
|
||||
{
|
||||
return is_auto ? 0 : value;
|
||||
}
|
||||
|
||||
void SettingMaxThreads::set(UInt64 x)
|
||||
@ -86,31 +128,31 @@ void SettingMaxThreads::set(UInt64 x)
|
||||
void SettingMaxThreads::set(const Field & x)
|
||||
{
|
||||
if (x.getType() == Field::Types::String)
|
||||
set(safeGet<const String &>(x));
|
||||
set(get<const String &>(x));
|
||||
else
|
||||
set(safeGet<UInt64>(x));
|
||||
}
|
||||
|
||||
void SettingMaxThreads::set(const String & x)
|
||||
{
|
||||
if (x == "auto")
|
||||
if (startsWith(x, "auto"))
|
||||
setAuto();
|
||||
else
|
||||
set(parse<UInt64>(x));
|
||||
}
|
||||
|
||||
void SettingMaxThreads::set(ReadBuffer & buf)
|
||||
void SettingMaxThreads::serialize(WriteBuffer & buf) const
|
||||
{
|
||||
writeVarUInt(is_auto ? 0 : value, buf);
|
||||
}
|
||||
|
||||
void SettingMaxThreads::deserialize(ReadBuffer & buf)
|
||||
{
|
||||
UInt64 x = 0;
|
||||
readVarUInt(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingMaxThreads::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeVarUInt(is_auto ? 0 : value, buf);
|
||||
}
|
||||
|
||||
void SettingMaxThreads::setAuto()
|
||||
{
|
||||
value = getAutoValue();
|
||||
@ -119,390 +161,67 @@ void SettingMaxThreads::setAuto()
|
||||
|
||||
UInt64 SettingMaxThreads::getAutoValue() const
|
||||
{
|
||||
static auto res = getAutoValueImpl();
|
||||
static auto res = getNumberOfPhysicalCPUCores();
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Executed once for all time. Executed from one thread.
|
||||
UInt64 SettingMaxThreads::getAutoValueImpl() const
|
||||
|
||||
template <SettingTimespanIO io_unit>
|
||||
String SettingTimespan<io_unit>::toString() const
|
||||
{
|
||||
return getNumberOfPhysicalCPUCores();
|
||||
return DB::toString(value.totalMicroseconds() / microseconds_per_io_unit);
|
||||
}
|
||||
|
||||
|
||||
String SettingSeconds::toString() const
|
||||
template <SettingTimespanIO io_unit>
|
||||
Field SettingTimespan<io_unit>::toField() const
|
||||
{
|
||||
return DB::toString(totalSeconds());
|
||||
return value.totalMicroseconds() / microseconds_per_io_unit;
|
||||
}
|
||||
|
||||
void SettingSeconds::set(const Poco::Timespan & x)
|
||||
template <SettingTimespanIO io_unit>
|
||||
void SettingTimespan<io_unit>::set(const Poco::Timespan & x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingSeconds::set(UInt64 x)
|
||||
template <SettingTimespanIO io_unit>
|
||||
void SettingTimespan<io_unit>::set(UInt64 x)
|
||||
{
|
||||
set(Poco::Timespan(x, 0));
|
||||
set(Poco::Timespan(x * microseconds_per_io_unit));
|
||||
}
|
||||
|
||||
void SettingSeconds::set(const Field & x)
|
||||
template <SettingTimespanIO io_unit>
|
||||
void SettingTimespan<io_unit>::set(const Field & x)
|
||||
{
|
||||
if (x.getType() == Field::Types::String)
|
||||
set(get<const String &>(x));
|
||||
else
|
||||
set(safeGet<UInt64>(x));
|
||||
}
|
||||
|
||||
void SettingSeconds::set(const String & x)
|
||||
template <SettingTimespanIO io_unit>
|
||||
void SettingTimespan<io_unit>::set(const String & x)
|
||||
{
|
||||
set(parse<UInt64>(x));
|
||||
}
|
||||
|
||||
void SettingSeconds::set(ReadBuffer & buf)
|
||||
template <SettingTimespanIO io_unit>
|
||||
void SettingTimespan<io_unit>::serialize(WriteBuffer & buf) const
|
||||
{
|
||||
writeVarUInt(value.totalMicroseconds() / microseconds_per_io_unit, buf);
|
||||
}
|
||||
|
||||
template <SettingTimespanIO io_unit>
|
||||
void SettingTimespan<io_unit>::deserialize(ReadBuffer & buf)
|
||||
{
|
||||
UInt64 x = 0;
|
||||
readVarUInt(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingSeconds::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeVarUInt(value.totalSeconds(), buf);
|
||||
}
|
||||
|
||||
|
||||
String SettingMilliseconds::toString() const
|
||||
{
|
||||
return DB::toString(totalMilliseconds());
|
||||
}
|
||||
|
||||
void SettingMilliseconds::set(const Poco::Timespan & x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingMilliseconds::set(UInt64 x)
|
||||
{
|
||||
set(Poco::Timespan(x * 1000));
|
||||
}
|
||||
|
||||
void SettingMilliseconds::set(const Field & x)
|
||||
{
|
||||
set(safeGet<UInt64>(x));
|
||||
}
|
||||
|
||||
void SettingMilliseconds::set(const String & x)
|
||||
{
|
||||
set(parse<UInt64>(x));
|
||||
}
|
||||
|
||||
void SettingMilliseconds::set(ReadBuffer & buf)
|
||||
{
|
||||
UInt64 x = 0;
|
||||
readVarUInt(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingMilliseconds::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeVarUInt(value.totalMilliseconds(), buf);
|
||||
}
|
||||
|
||||
|
||||
String SettingFloat::toString() const
|
||||
{
|
||||
return DB::toString(value);
|
||||
}
|
||||
|
||||
void SettingFloat::set(float x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingFloat::set(const Field & x)
|
||||
{
|
||||
set(applyVisitor(FieldVisitorConvertToNumber<float>(), x));
|
||||
}
|
||||
|
||||
void SettingFloat::set(const String & x)
|
||||
{
|
||||
set(parse<float>(x));
|
||||
}
|
||||
|
||||
void SettingFloat::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingFloat::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
|
||||
LoadBalancing SettingLoadBalancing::getLoadBalancing(const String & s)
|
||||
{
|
||||
if (s == "random") return LoadBalancing::RANDOM;
|
||||
if (s == "nearest_hostname") return LoadBalancing::NEAREST_HOSTNAME;
|
||||
if (s == "in_order") return LoadBalancing::IN_ORDER;
|
||||
if (s == "first_or_random") return LoadBalancing::FIRST_OR_RANDOM;
|
||||
|
||||
throw Exception("Unknown load balancing mode: '" + s + "', must be one of 'random', 'nearest_hostname', 'in_order', 'first_or_random'",
|
||||
ErrorCodes::UNKNOWN_LOAD_BALANCING);
|
||||
}
|
||||
|
||||
String SettingLoadBalancing::toString() const
|
||||
{
|
||||
const char * strings[] = {"random", "nearest_hostname", "in_order", "first_or_random"};
|
||||
if (value < LoadBalancing::RANDOM || value > LoadBalancing::FIRST_OR_RANDOM)
|
||||
throw Exception("Unknown load balancing mode", ErrorCodes::UNKNOWN_LOAD_BALANCING);
|
||||
return strings[static_cast<size_t>(value)];
|
||||
}
|
||||
|
||||
void SettingLoadBalancing::set(LoadBalancing x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingLoadBalancing::set(const Field & x)
|
||||
{
|
||||
set(safeGet<const String &>(x));
|
||||
}
|
||||
|
||||
void SettingLoadBalancing::set(const String & x)
|
||||
{
|
||||
set(getLoadBalancing(x));
|
||||
}
|
||||
|
||||
void SettingLoadBalancing::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingLoadBalancing::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
|
||||
JoinStrictness SettingJoinStrictness::getJoinStrictness(const String & s)
|
||||
{
|
||||
if (s == "") return JoinStrictness::Unspecified;
|
||||
if (s == "ALL") return JoinStrictness::ALL;
|
||||
if (s == "ANY") return JoinStrictness::ANY;
|
||||
|
||||
throw Exception("Unknown join strictness mode: '" + s + "', must be one of '', 'ALL', 'ANY'",
|
||||
ErrorCodes::UNKNOWN_JOIN_STRICTNESS);
|
||||
}
|
||||
|
||||
String SettingJoinStrictness::toString() const
|
||||
{
|
||||
const char * strings[] = {"", "ALL", "ANY"};
|
||||
if (value < JoinStrictness::Unspecified || value > JoinStrictness::ANY)
|
||||
throw Exception("Unknown join strictness mode", ErrorCodes::UNKNOWN_JOIN_STRICTNESS);
|
||||
return strings[static_cast<size_t>(value)];
|
||||
}
|
||||
|
||||
void SettingJoinStrictness::set(JoinStrictness x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingJoinStrictness::set(const Field & x)
|
||||
{
|
||||
set(safeGet<const String &>(x));
|
||||
}
|
||||
|
||||
void SettingJoinStrictness::set(const String & x)
|
||||
{
|
||||
set(getJoinStrictness(x));
|
||||
}
|
||||
|
||||
void SettingJoinStrictness::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingJoinStrictness::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
|
||||
TotalsMode SettingTotalsMode::getTotalsMode(const String & s)
|
||||
{
|
||||
if (s == "before_having") return TotalsMode::BEFORE_HAVING;
|
||||
if (s == "after_having_exclusive") return TotalsMode::AFTER_HAVING_EXCLUSIVE;
|
||||
if (s == "after_having_inclusive") return TotalsMode::AFTER_HAVING_INCLUSIVE;
|
||||
if (s == "after_having_auto") return TotalsMode::AFTER_HAVING_AUTO;
|
||||
|
||||
throw Exception("Unknown totals mode: '" + s + "', must be one of 'before_having', 'after_having_exclusive', 'after_having_inclusive', 'after_having_auto'", ErrorCodes::UNKNOWN_TOTALS_MODE);
|
||||
}
|
||||
|
||||
String SettingTotalsMode::toString() const
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case TotalsMode::BEFORE_HAVING: return "before_having";
|
||||
case TotalsMode::AFTER_HAVING_EXCLUSIVE: return "after_having_exclusive";
|
||||
case TotalsMode::AFTER_HAVING_INCLUSIVE: return "after_having_inclusive";
|
||||
case TotalsMode::AFTER_HAVING_AUTO: return "after_having_auto";
|
||||
}
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void SettingTotalsMode::set(TotalsMode x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingTotalsMode::set(const Field & x)
|
||||
{
|
||||
set(safeGet<const String &>(x));
|
||||
}
|
||||
|
||||
void SettingTotalsMode::set(const String & x)
|
||||
{
|
||||
set(getTotalsMode(x));
|
||||
}
|
||||
|
||||
void SettingTotalsMode::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingTotalsMode::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
|
||||
template <bool enable_mode_any>
|
||||
OverflowMode SettingOverflowMode<enable_mode_any>::getOverflowModeForGroupBy(const String & s)
|
||||
{
|
||||
if (s == "throw") return OverflowMode::THROW;
|
||||
if (s == "break") return OverflowMode::BREAK;
|
||||
if (s == "any") return OverflowMode::ANY;
|
||||
|
||||
throw Exception("Unknown overflow mode: '" + s + "', must be one of 'throw', 'break', 'any'", ErrorCodes::UNKNOWN_OVERFLOW_MODE);
|
||||
}
|
||||
|
||||
template <bool enable_mode_any>
|
||||
OverflowMode SettingOverflowMode<enable_mode_any>::getOverflowMode(const String & s)
|
||||
{
|
||||
OverflowMode mode = getOverflowModeForGroupBy(s);
|
||||
|
||||
if (mode == OverflowMode::ANY && !enable_mode_any)
|
||||
throw Exception("Illegal overflow mode: 'any' is only for 'group_by_overflow_mode'", ErrorCodes::ILLEGAL_OVERFLOW_MODE);
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
template <bool enable_mode_any>
|
||||
String SettingOverflowMode<enable_mode_any>::toString() const
|
||||
{
|
||||
const char * strings[] = { "throw", "break", "any" };
|
||||
|
||||
if (value < OverflowMode::THROW || value > OverflowMode::ANY)
|
||||
throw Exception("Unknown overflow mode", ErrorCodes::UNKNOWN_OVERFLOW_MODE);
|
||||
|
||||
return strings[static_cast<size_t>(value)];
|
||||
}
|
||||
|
||||
template <bool enable_mode_any>
|
||||
void SettingOverflowMode<enable_mode_any>::set(OverflowMode x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
template <bool enable_mode_any>
|
||||
void SettingOverflowMode<enable_mode_any>::set(const Field & x)
|
||||
{
|
||||
set(safeGet<const String &>(x));
|
||||
}
|
||||
|
||||
template <bool enable_mode_any>
|
||||
void SettingOverflowMode<enable_mode_any>::set(const String & x)
|
||||
{
|
||||
set(getOverflowMode(x));
|
||||
}
|
||||
|
||||
template <bool enable_mode_any>
|
||||
void SettingOverflowMode<enable_mode_any>::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
template <bool enable_mode_any>
|
||||
void SettingOverflowMode<enable_mode_any>::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
template struct SettingOverflowMode<false>;
|
||||
template struct SettingOverflowMode<true>;
|
||||
|
||||
DistributedProductMode SettingDistributedProductMode::getDistributedProductMode(const String & s)
|
||||
{
|
||||
if (s == "deny") return DistributedProductMode::DENY;
|
||||
if (s == "local") return DistributedProductMode::LOCAL;
|
||||
if (s == "global") return DistributedProductMode::GLOBAL;
|
||||
if (s == "allow") return DistributedProductMode::ALLOW;
|
||||
|
||||
throw Exception("Unknown distributed product mode: '" + s + "', must be one of 'deny', 'local', 'global', 'allow'",
|
||||
ErrorCodes::UNKNOWN_DISTRIBUTED_PRODUCT_MODE);
|
||||
}
|
||||
|
||||
String SettingDistributedProductMode::toString() const
|
||||
{
|
||||
const char * strings[] = {"deny", "local", "global", "allow"};
|
||||
if (value < DistributedProductMode::DENY || value > DistributedProductMode::ALLOW)
|
||||
throw Exception("Unknown distributed product mode", ErrorCodes::UNKNOWN_DISTRIBUTED_PRODUCT_MODE);
|
||||
return strings[static_cast<size_t>(value)];
|
||||
}
|
||||
|
||||
void SettingDistributedProductMode::set(DistributedProductMode x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingDistributedProductMode::set(const Field & x)
|
||||
{
|
||||
set(safeGet<const String &>(x));
|
||||
}
|
||||
|
||||
void SettingDistributedProductMode::set(const String & x)
|
||||
{
|
||||
set(getDistributedProductMode(x));
|
||||
}
|
||||
|
||||
void SettingDistributedProductMode::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingDistributedProductMode::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
template struct SettingTimespan<SettingTimespanIO::SECOND>;
|
||||
template struct SettingTimespan<SettingTimespanIO::MILLISECOND>;
|
||||
|
||||
|
||||
String SettingString::toString() const
|
||||
@ -510,6 +229,11 @@ String SettingString::toString() const
|
||||
return value;
|
||||
}
|
||||
|
||||
Field SettingString::toField() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void SettingString::set(const String & x)
|
||||
{
|
||||
value = x;
|
||||
@ -521,24 +245,29 @@ void SettingString::set(const Field & x)
|
||||
set(safeGet<const String &>(x));
|
||||
}
|
||||
|
||||
void SettingString::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingString::write(WriteBuffer & buf) const
|
||||
void SettingString::serialize(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(value, buf);
|
||||
}
|
||||
|
||||
void SettingString::deserialize(ReadBuffer & buf)
|
||||
{
|
||||
String s;
|
||||
readBinary(s, buf);
|
||||
set(s);
|
||||
}
|
||||
|
||||
|
||||
String SettingChar::toString() const
|
||||
{
|
||||
return String(1, value);
|
||||
}
|
||||
|
||||
Field SettingChar::toField() const
|
||||
{
|
||||
return toString();
|
||||
}
|
||||
|
||||
void SettingChar::set(char x)
|
||||
{
|
||||
value = x;
|
||||
@ -559,108 +288,154 @@ void SettingChar::set(const Field & x)
|
||||
set(s);
|
||||
}
|
||||
|
||||
void SettingChar::set(ReadBuffer & buf)
|
||||
void SettingChar::serialize(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
void SettingChar::deserialize(ReadBuffer & buf)
|
||||
{
|
||||
String s;
|
||||
readBinary(s, buf);
|
||||
set(s);
|
||||
}
|
||||
|
||||
void SettingChar::write(WriteBuffer & buf) const
|
||||
|
||||
template <typename EnumType, typename Tag>
|
||||
void SettingEnum<EnumType, Tag>::serialize(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
|
||||
SettingDateTimeInputFormat::Value SettingDateTimeInputFormat::getValue(const String & s)
|
||||
template <typename EnumType, typename Tag>
|
||||
void SettingEnum<EnumType, Tag>::deserialize(ReadBuffer & buf)
|
||||
{
|
||||
if (s == "basic") return Value::Basic;
|
||||
if (s == "best_effort") return Value::BestEffort;
|
||||
|
||||
throw Exception("Unknown DateTime input format: '" + s + "', must be one of 'basic', 'best_effort'", ErrorCodes::BAD_ARGUMENTS);
|
||||
String s;
|
||||
readBinary(s, buf);
|
||||
set(s);
|
||||
}
|
||||
|
||||
String SettingDateTimeInputFormat::toString() const
|
||||
|
||||
#define IMPLEMENT_SETTING_ENUM(ENUM_NAME, LIST_OF_NAMES_MACRO, ERROR_CODE_FOR_UNEXPECTED_NAME) \
|
||||
IMPLEMENT_SETTING_ENUM_WITH_TAG(ENUM_NAME, void, LIST_OF_NAMES_MACRO, ERROR_CODE_FOR_UNEXPECTED_NAME)
|
||||
|
||||
#define IMPLEMENT_SETTING_ENUM_WITH_TAG(ENUM_NAME, TAG, LIST_OF_NAMES_MACRO, ERROR_CODE_FOR_UNEXPECTED_NAME) \
|
||||
template <> \
|
||||
String SettingEnum<ENUM_NAME, TAG>::toString() const \
|
||||
{ \
|
||||
using EnumType = ENUM_NAME; \
|
||||
using UnderlyingType = std::underlying_type<EnumType>::type; \
|
||||
switch (static_cast<UnderlyingType>(value)) \
|
||||
{ \
|
||||
LIST_OF_NAMES_MACRO(IMPLEMENT_SETTING_ENUM_TO_STRING_HELPER_) \
|
||||
} \
|
||||
throw Exception("Unknown " #ENUM_NAME, ERROR_CODE_FOR_UNEXPECTED_NAME); \
|
||||
} \
|
||||
\
|
||||
template <> \
|
||||
void SettingEnum<ENUM_NAME, TAG>::set(const String & s) \
|
||||
{ \
|
||||
using EnumType = ENUM_NAME; \
|
||||
LIST_OF_NAMES_MACRO(IMPLEMENT_SETTING_ENUM_FROM_STRING_HELPER_) \
|
||||
\
|
||||
String all_io_names; \
|
||||
LIST_OF_NAMES_MACRO(IMPLEMENT_SETTING_ENUM_CONCAT_NAMES_HELPER_) \
|
||||
throw Exception("Unknown " #ENUM_NAME " : '" + s + "', must be one of " + all_io_names, \
|
||||
ERROR_CODE_FOR_UNEXPECTED_NAME); \
|
||||
} \
|
||||
\
|
||||
template struct SettingEnum<ENUM_NAME, TAG>;
|
||||
|
||||
#define IMPLEMENT_SETTING_ENUM_TO_STRING_HELPER_(NAME, IO_NAME) \
|
||||
case static_cast<UnderlyingType>(EnumType::NAME): return IO_NAME;
|
||||
|
||||
#define IMPLEMENT_SETTING_ENUM_FROM_STRING_HELPER_(NAME, IO_NAME) \
|
||||
if (s == IO_NAME) \
|
||||
{ \
|
||||
set(EnumType::NAME); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define IMPLEMENT_SETTING_ENUM_CONCAT_NAMES_HELPER_(NAME, IO_NAME) \
|
||||
if (!all_io_names.empty()) \
|
||||
all_io_names += ", "; \
|
||||
all_io_names += String("'") + IO_NAME + "'";
|
||||
|
||||
|
||||
#define LOAD_BALANCING_LIST_OF_NAMES(M) \
|
||||
M(RANDOM, "random") \
|
||||
M(NEAREST_HOSTNAME, "nearest_hostname") \
|
||||
M(IN_ORDER, "in_order") \
|
||||
M(FIRST_OR_RANDOM, "first_or_random")
|
||||
IMPLEMENT_SETTING_ENUM(LoadBalancing, LOAD_BALANCING_LIST_OF_NAMES, ErrorCodes::UNKNOWN_LOAD_BALANCING)
|
||||
|
||||
|
||||
#define JOIN_STRICTNESS_LIST_OF_NAMES(M) \
|
||||
M(Unspecified, "") \
|
||||
M(ALL, "ALL") \
|
||||
M(ANY, "ANY")
|
||||
IMPLEMENT_SETTING_ENUM(JoinStrictness, JOIN_STRICTNESS_LIST_OF_NAMES, ErrorCodes::UNKNOWN_JOIN_STRICTNESS)
|
||||
|
||||
|
||||
#define TOTALS_MODE_LIST_OF_NAMES(M) \
|
||||
M(BEFORE_HAVING, "before_having") \
|
||||
M(AFTER_HAVING_EXCLUSIVE, "after_having_exclusive") \
|
||||
M(AFTER_HAVING_INCLUSIVE, "after_having_inclusive") \
|
||||
M(AFTER_HAVING_AUTO, "after_having_auto")
|
||||
IMPLEMENT_SETTING_ENUM(TotalsMode, TOTALS_MODE_LIST_OF_NAMES, ErrorCodes::UNKNOWN_TOTALS_MODE)
|
||||
|
||||
|
||||
#define OVERFLOW_MODE_LIST_OF_NAMES(M) \
|
||||
M(THROW, "throw") \
|
||||
M(BREAK, "break")
|
||||
IMPLEMENT_SETTING_ENUM(OverflowMode, OVERFLOW_MODE_LIST_OF_NAMES, ErrorCodes::UNKNOWN_OVERFLOW_MODE)
|
||||
|
||||
|
||||
#define OVERFLOW_MODE_LIST_OF_NAMES_WITH_ANY(M) \
|
||||
M(THROW, "throw") \
|
||||
M(BREAK, "break") \
|
||||
M(ANY, "any")
|
||||
IMPLEMENT_SETTING_ENUM_WITH_TAG(OverflowMode, SettingOverflowModeGroupByTag, OVERFLOW_MODE_LIST_OF_NAMES_WITH_ANY, ErrorCodes::UNKNOWN_OVERFLOW_MODE)
|
||||
|
||||
|
||||
#define DISTRIBUTED_PRODUCT_MODE_LIST_OF_NAMES(M) \
|
||||
M(DENY, "deny") \
|
||||
M(LOCAL, "local") \
|
||||
M(GLOBAL, "global") \
|
||||
M(ALLOW, "allow")
|
||||
IMPLEMENT_SETTING_ENUM(DistributedProductMode, DISTRIBUTED_PRODUCT_MODE_LIST_OF_NAMES, ErrorCodes::UNKNOWN_DISTRIBUTED_PRODUCT_MODE)
|
||||
|
||||
|
||||
#define DATE_TIME_INPUT_FORMAT_LIST_OF_NAMES(M) \
|
||||
M(Basic, "basic") \
|
||||
M(BestEffort, "best_effort")
|
||||
IMPLEMENT_SETTING_ENUM(FormatSettings::DateTimeInputFormat, DATE_TIME_INPUT_FORMAT_LIST_OF_NAMES, ErrorCodes::BAD_ARGUMENTS)
|
||||
|
||||
|
||||
#define LOGS_LEVEL_LIST_OF_NAMES(M) \
|
||||
M(none, "none") \
|
||||
M(error, "error") \
|
||||
M(warning, "warning") \
|
||||
M(information, "information") \
|
||||
M(debug, "debug") \
|
||||
M(trace, "trace")
|
||||
IMPLEMENT_SETTING_ENUM(LogsLevel, LOGS_LEVEL_LIST_OF_NAMES, ErrorCodes::BAD_ARGUMENTS)
|
||||
|
||||
|
||||
namespace details
|
||||
{
|
||||
const char * strings[] = {"basic", "best_effort"};
|
||||
if (value < Value::Basic || value > Value::BestEffort)
|
||||
throw Exception("Unknown DateTime input format", ErrorCodes::BAD_ARGUMENTS);
|
||||
return strings[static_cast<size_t>(value)];
|
||||
String SettingsCollectionUtils::deserializeName(ReadBuffer & buf)
|
||||
{
|
||||
String name;
|
||||
readBinary(name, buf);
|
||||
return name;
|
||||
}
|
||||
|
||||
void SettingsCollectionUtils::serializeName(const StringRef & name, WriteBuffer & buf) { writeBinary(name, buf); }
|
||||
|
||||
void SettingsCollectionUtils::throwNameNotFound(const StringRef & name)
|
||||
{
|
||||
throw Exception("Unknown setting " + name.toString(), ErrorCodes::UNKNOWN_SETTING);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingDateTimeInputFormat::set(Value x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingDateTimeInputFormat::set(const Field & x)
|
||||
{
|
||||
set(safeGet<const String &>(x));
|
||||
}
|
||||
|
||||
void SettingDateTimeInputFormat::set(const String & x)
|
||||
{
|
||||
set(getValue(x));
|
||||
}
|
||||
|
||||
void SettingDateTimeInputFormat::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingDateTimeInputFormat::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
|
||||
SettingLogsLevel::Value SettingLogsLevel::getValue(const String & s)
|
||||
{
|
||||
if (s == "none") return Value::none;
|
||||
if (s == "error") return Value::error;
|
||||
if (s == "warning") return Value::warning;
|
||||
if (s == "information") return Value::information;
|
||||
if (s == "debug") return Value::debug;
|
||||
if (s == "trace") return Value::trace;
|
||||
|
||||
throw Exception("Unknown logs level: '" + s + "', must be one of: none, error, warning, information, debug, trace", ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
String SettingLogsLevel::toString() const
|
||||
{
|
||||
const char * strings[] = {"none", "error", "warning", "information", "debug", "trace"};
|
||||
return strings[static_cast<size_t>(value)];
|
||||
}
|
||||
|
||||
void SettingLogsLevel::set(Value x)
|
||||
{
|
||||
value = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void SettingLogsLevel::set(const Field & x)
|
||||
{
|
||||
set(safeGet<const String &>(x));
|
||||
}
|
||||
|
||||
void SettingLogsLevel::set(const String & x)
|
||||
{
|
||||
set(getValue(x));
|
||||
}
|
||||
|
||||
void SettingLogsLevel::set(ReadBuffer & buf)
|
||||
{
|
||||
String x;
|
||||
readBinary(x, buf);
|
||||
set(x);
|
||||
}
|
||||
|
||||
void SettingLogsLevel::write(WriteBuffer & buf) const
|
||||
{
|
||||
writeBinary(toString(), buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,8 +3,11 @@
|
||||
#include <Poco/Timespan.h>
|
||||
#include <DataStreams/SizeLimits.h>
|
||||
#include <Formats/FormatSettings.h>
|
||||
#include <Compression/CompressionInfo.h>
|
||||
#include <common/StringRef.h>
|
||||
#include <Common/SettingsChanges.h>
|
||||
#include <Core/Types.h>
|
||||
#include <ext/singleton.h>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -22,24 +25,24 @@ class WriteBuffer;
|
||||
* and the remote server will use its default value.
|
||||
*/
|
||||
|
||||
template <typename IntType>
|
||||
struct SettingInt
|
||||
template <typename Type>
|
||||
struct SettingNumber
|
||||
{
|
||||
IntType value;
|
||||
Type value;
|
||||
bool changed = false;
|
||||
|
||||
SettingInt(IntType x = 0) : value(x) {}
|
||||
SettingNumber(Type x = 0) : value(x) {}
|
||||
|
||||
operator IntType() const { return value; }
|
||||
SettingInt & operator= (IntType x) { set(x); return *this; }
|
||||
operator Type() const { return value; }
|
||||
SettingNumber & operator= (Type x) { set(x); return *this; }
|
||||
|
||||
/// Serialize to a test string.
|
||||
String toString() const;
|
||||
|
||||
/// Serialize to binary stream suitable for transfer over network.
|
||||
void write(WriteBuffer & buf) const;
|
||||
/// Converts to a field.
|
||||
Field toField() const;
|
||||
|
||||
void set(IntType x);
|
||||
void set(Type x);
|
||||
|
||||
/// Read from SQL literal.
|
||||
void set(const Field & x);
|
||||
@ -47,13 +50,17 @@ struct SettingInt
|
||||
/// Read from text string.
|
||||
void set(const String & x);
|
||||
|
||||
/// Serialize to binary stream suitable for transfer over network.
|
||||
void serialize(WriteBuffer & buf) const;
|
||||
|
||||
/// Read from binary stream.
|
||||
void set(ReadBuffer & buf);
|
||||
void deserialize(ReadBuffer & buf);
|
||||
};
|
||||
|
||||
using SettingUInt64 = SettingInt<UInt64>;
|
||||
using SettingInt64 = SettingInt<Int64>;
|
||||
using SettingBool = SettingUInt64;
|
||||
using SettingUInt64 = SettingNumber<UInt64>;
|
||||
using SettingInt64 = SettingNumber<Int64>;
|
||||
using SettingFloat = SettingNumber<float>;
|
||||
using SettingBool = SettingNumber<bool>;
|
||||
|
||||
|
||||
/** Unlike SettingUInt64, supports the value of 'auto' - the number of processor cores without taking into account SMT.
|
||||
@ -72,248 +79,53 @@ struct SettingMaxThreads
|
||||
SettingMaxThreads & operator= (UInt64 x) { set(x); return *this; }
|
||||
|
||||
String toString() const;
|
||||
Field toField() const;
|
||||
|
||||
void set(UInt64 x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
void serialize(WriteBuffer & buf) const;
|
||||
void deserialize(ReadBuffer & buf);
|
||||
|
||||
void setAuto();
|
||||
UInt64 getAutoValue() const;
|
||||
|
||||
/// Executed once for all time. Executed from one thread.
|
||||
UInt64 getAutoValueImpl() const;
|
||||
};
|
||||
|
||||
|
||||
struct SettingSeconds
|
||||
enum class SettingTimespanIO { MILLISECOND, SECOND };
|
||||
|
||||
template <SettingTimespanIO io_unit>
|
||||
struct SettingTimespan
|
||||
{
|
||||
Poco::Timespan value;
|
||||
bool changed = false;
|
||||
|
||||
SettingSeconds(UInt64 seconds = 0) : value(seconds, 0) {}
|
||||
SettingTimespan(UInt64 x = 0) : value(x * microseconds_per_io_unit) {}
|
||||
|
||||
operator Poco::Timespan() const { return value; }
|
||||
SettingSeconds & operator= (const Poco::Timespan & x) { set(x); return *this; }
|
||||
SettingTimespan & operator= (const Poco::Timespan & x) { set(x); return *this; }
|
||||
|
||||
Poco::Timespan::TimeDiff totalSeconds() const { return value.totalSeconds(); }
|
||||
|
||||
String toString() const;
|
||||
|
||||
void set(const Poco::Timespan & x);
|
||||
|
||||
void set(UInt64 x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
};
|
||||
|
||||
|
||||
struct SettingMilliseconds
|
||||
{
|
||||
Poco::Timespan value;
|
||||
bool changed = false;
|
||||
|
||||
SettingMilliseconds(UInt64 milliseconds = 0) : value(milliseconds * 1000) {}
|
||||
|
||||
operator Poco::Timespan() const { return value; }
|
||||
SettingMilliseconds & operator= (const Poco::Timespan & x) { set(x); return *this; }
|
||||
|
||||
Poco::Timespan::TimeDiff totalMilliseconds() const { return value.totalMilliseconds(); }
|
||||
|
||||
String toString() const;
|
||||
Field toField() const;
|
||||
|
||||
void set(const Poco::Timespan & x);
|
||||
|
||||
void set(UInt64 x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
void write(WriteBuffer & buf) const;
|
||||
|
||||
void serialize(WriteBuffer & buf) const;
|
||||
void deserialize(ReadBuffer & buf);
|
||||
|
||||
static constexpr UInt64 microseconds_per_io_unit = (io_unit == SettingTimespanIO::MILLISECOND) ? 1000 : 1000000;
|
||||
};
|
||||
|
||||
|
||||
struct SettingFloat
|
||||
{
|
||||
float value;
|
||||
bool changed = false;
|
||||
|
||||
SettingFloat(float x = 0) : value(x) {}
|
||||
|
||||
operator float() const { return value; }
|
||||
SettingFloat & operator= (float x) { set(x); return *this; }
|
||||
|
||||
String toString() const;
|
||||
|
||||
void set(float x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
};
|
||||
|
||||
|
||||
/// TODO: X macro
|
||||
enum class LoadBalancing
|
||||
{
|
||||
/// among replicas with a minimum number of errors selected randomly
|
||||
RANDOM = 0,
|
||||
/// a replica is selected among the replicas with the minimum number of errors
|
||||
/// with the minimum number of distinguished characters in the replica name and local hostname
|
||||
NEAREST_HOSTNAME,
|
||||
/// replicas are walked through strictly in order; the number of errors does not matter
|
||||
IN_ORDER,
|
||||
/// if first replica one has higher number of errors,
|
||||
/// pick a random one from replicas with minimum number of errors
|
||||
FIRST_OR_RANDOM,
|
||||
};
|
||||
|
||||
struct SettingLoadBalancing
|
||||
{
|
||||
LoadBalancing value;
|
||||
bool changed = false;
|
||||
|
||||
SettingLoadBalancing(LoadBalancing x) : value(x) {}
|
||||
|
||||
operator LoadBalancing() const { return value; }
|
||||
SettingLoadBalancing & operator= (LoadBalancing x) { set(x); return *this; }
|
||||
|
||||
static LoadBalancing getLoadBalancing(const String & s);
|
||||
|
||||
String toString() const;
|
||||
|
||||
void set(LoadBalancing x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
};
|
||||
|
||||
|
||||
enum class JoinStrictness
|
||||
{
|
||||
Unspecified = 0, /// Query JOIN without strictness will throw Exception.
|
||||
ALL, /// Query JOIN without strictness -> ALL JOIN ...
|
||||
ANY, /// Query JOIN without strictness -> ANY JOIN ...
|
||||
};
|
||||
|
||||
|
||||
struct SettingJoinStrictness
|
||||
{
|
||||
JoinStrictness value;
|
||||
bool changed = false;
|
||||
|
||||
SettingJoinStrictness(JoinStrictness x) : value(x) {}
|
||||
|
||||
operator JoinStrictness() const { return value; }
|
||||
SettingJoinStrictness & operator= (JoinStrictness x) { set(x); return *this; }
|
||||
|
||||
static JoinStrictness getJoinStrictness(const String & s);
|
||||
|
||||
String toString() const;
|
||||
|
||||
void set(JoinStrictness x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
};
|
||||
|
||||
|
||||
/// Which rows should be included in TOTALS.
|
||||
enum class TotalsMode
|
||||
{
|
||||
BEFORE_HAVING = 0, /// Count HAVING for all read rows;
|
||||
/// including those not in max_rows_to_group_by
|
||||
/// and have not passed HAVING after grouping.
|
||||
AFTER_HAVING_INCLUSIVE = 1, /// Count on all rows except those that have not passed HAVING;
|
||||
/// that is, to include in TOTALS all the rows that did not pass max_rows_to_group_by.
|
||||
AFTER_HAVING_EXCLUSIVE = 2, /// Include only the rows that passed and max_rows_to_group_by, and HAVING.
|
||||
AFTER_HAVING_AUTO = 3, /// Automatically select between INCLUSIVE and EXCLUSIVE,
|
||||
};
|
||||
|
||||
struct SettingTotalsMode
|
||||
{
|
||||
TotalsMode value;
|
||||
bool changed = false;
|
||||
|
||||
SettingTotalsMode(TotalsMode x) : value(x) {}
|
||||
|
||||
operator TotalsMode() const { return value; }
|
||||
SettingTotalsMode & operator= (TotalsMode x) { set(x); return *this; }
|
||||
|
||||
static TotalsMode getTotalsMode(const String & s);
|
||||
|
||||
String toString() const;
|
||||
|
||||
void set(TotalsMode x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
};
|
||||
|
||||
|
||||
template <bool enable_mode_any>
|
||||
struct SettingOverflowMode
|
||||
{
|
||||
OverflowMode value;
|
||||
bool changed = false;
|
||||
|
||||
SettingOverflowMode(OverflowMode x = OverflowMode::THROW) : value(x) {}
|
||||
|
||||
operator OverflowMode() const { return value; }
|
||||
SettingOverflowMode & operator= (OverflowMode x) { set(x); return *this; }
|
||||
|
||||
static OverflowMode getOverflowModeForGroupBy(const String & s);
|
||||
static OverflowMode getOverflowMode(const String & s);
|
||||
|
||||
String toString() const;
|
||||
|
||||
void set(OverflowMode x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
};
|
||||
|
||||
/// The setting for executing distributed subqueries inside IN or JOIN sections.
|
||||
enum class DistributedProductMode
|
||||
{
|
||||
DENY = 0, /// Disable
|
||||
LOCAL, /// Convert to local query
|
||||
GLOBAL, /// Convert to global query
|
||||
ALLOW /// Enable
|
||||
};
|
||||
|
||||
struct SettingDistributedProductMode
|
||||
{
|
||||
DistributedProductMode value;
|
||||
bool changed = false;
|
||||
|
||||
SettingDistributedProductMode(DistributedProductMode x) : value(x) {}
|
||||
|
||||
operator DistributedProductMode() const { return value; }
|
||||
SettingDistributedProductMode & operator= (DistributedProductMode x) { set(x); return *this; }
|
||||
|
||||
static DistributedProductMode getDistributedProductMode(const String & s);
|
||||
|
||||
String toString() const;
|
||||
|
||||
void set(DistributedProductMode x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
void write(WriteBuffer & buf) const;
|
||||
};
|
||||
using SettingSeconds = SettingTimespan<SettingTimespanIO::SECOND>;
|
||||
using SettingMilliseconds = SettingTimespan<SettingTimespanIO::MILLISECOND>;
|
||||
|
||||
|
||||
struct SettingString
|
||||
@ -327,12 +139,13 @@ struct SettingString
|
||||
SettingString & operator= (const String & x) { set(x); return *this; }
|
||||
|
||||
String toString() const;
|
||||
Field toField() const;
|
||||
|
||||
void set(const String & x);
|
||||
void set(const Field & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
void serialize(WriteBuffer & buf) const;
|
||||
void deserialize(ReadBuffer & buf);
|
||||
};
|
||||
|
||||
|
||||
@ -348,40 +161,102 @@ public:
|
||||
SettingChar & operator= (char x) { set(x); return *this; }
|
||||
|
||||
String toString() const;
|
||||
Field toField() const;
|
||||
|
||||
void set(char x);
|
||||
void set(const String & x);
|
||||
void set(const Field & x);
|
||||
void set(ReadBuffer & buf);
|
||||
|
||||
void write(WriteBuffer & buf) const;
|
||||
void serialize(WriteBuffer & buf) const;
|
||||
void deserialize(ReadBuffer & buf);
|
||||
};
|
||||
|
||||
|
||||
struct SettingDateTimeInputFormat
|
||||
/// Template class to define enum-based settings.
|
||||
template <typename EnumType, typename Tag = void>
|
||||
struct SettingEnum
|
||||
{
|
||||
using Value = FormatSettings::DateTimeInputFormat;
|
||||
|
||||
Value value;
|
||||
EnumType value;
|
||||
bool changed = false;
|
||||
|
||||
SettingDateTimeInputFormat(Value x) : value(x) {}
|
||||
SettingEnum(EnumType x) : value(x) {}
|
||||
|
||||
operator Value() const { return value; }
|
||||
SettingDateTimeInputFormat & operator= (Value x) { set(x); return *this; }
|
||||
|
||||
static Value getValue(const String & s);
|
||||
operator EnumType() const { return value; }
|
||||
SettingEnum & operator= (EnumType x) { set(x); return *this; }
|
||||
|
||||
String toString() const;
|
||||
Field toField() const { return toString(); }
|
||||
|
||||
void set(Value x);
|
||||
void set(const Field & x);
|
||||
void set(EnumType x) { value = x; changed = true; }
|
||||
void set(const Field & x) { set(safeGet<const String &>(x)); }
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
void write(WriteBuffer & buf) const;
|
||||
|
||||
void serialize(WriteBuffer & buf) const;
|
||||
void deserialize(ReadBuffer & buf);
|
||||
};
|
||||
|
||||
|
||||
enum class LoadBalancing
|
||||
{
|
||||
/// among replicas with a minimum number of errors selected randomly
|
||||
RANDOM = 0,
|
||||
/// a replica is selected among the replicas with the minimum number of errors
|
||||
/// with the minimum number of distinguished characters in the replica name and local hostname
|
||||
NEAREST_HOSTNAME,
|
||||
/// replicas are walked through strictly in order; the number of errors does not matter
|
||||
IN_ORDER,
|
||||
/// if first replica one has higher number of errors,
|
||||
/// pick a random one from replicas with minimum number of errors
|
||||
FIRST_OR_RANDOM,
|
||||
};
|
||||
using SettingLoadBalancing = SettingEnum<LoadBalancing>;
|
||||
|
||||
|
||||
enum class JoinStrictness
|
||||
{
|
||||
Unspecified = 0, /// Query JOIN without strictness will throw Exception.
|
||||
ALL, /// Query JOIN without strictness -> ALL JOIN ...
|
||||
ANY, /// Query JOIN without strictness -> ANY JOIN ...
|
||||
};
|
||||
using SettingJoinStrictness = SettingEnum<JoinStrictness>;
|
||||
|
||||
|
||||
/// Which rows should be included in TOTALS.
|
||||
enum class TotalsMode
|
||||
{
|
||||
BEFORE_HAVING = 0, /// Count HAVING for all read rows;
|
||||
/// including those not in max_rows_to_group_by
|
||||
/// and have not passed HAVING after grouping.
|
||||
AFTER_HAVING_INCLUSIVE = 1, /// Count on all rows except those that have not passed HAVING;
|
||||
/// that is, to include in TOTALS all the rows that did not pass max_rows_to_group_by.
|
||||
AFTER_HAVING_EXCLUSIVE = 2, /// Include only the rows that passed and max_rows_to_group_by, and HAVING.
|
||||
AFTER_HAVING_AUTO = 3, /// Automatically select between INCLUSIVE and EXCLUSIVE,
|
||||
};
|
||||
using SettingTotalsMode = SettingEnum<TotalsMode>;
|
||||
|
||||
|
||||
/// The settings keeps OverflowMode which cannot be OverflowMode::ANY.
|
||||
using SettingOverflowMode = SettingEnum<OverflowMode>;
|
||||
struct SettingOverflowModeGroupByTag;
|
||||
|
||||
/// The settings keeps OverflowMode which can be OverflowMode::ANY.
|
||||
using SettingOverflowModeGroupBy = SettingEnum<OverflowMode, SettingOverflowModeGroupByTag>;
|
||||
|
||||
|
||||
/// The setting for executing distributed subqueries inside IN or JOIN sections.
|
||||
enum class DistributedProductMode
|
||||
{
|
||||
DENY = 0, /// Disable
|
||||
LOCAL, /// Convert to local query
|
||||
GLOBAL, /// Convert to global query
|
||||
ALLOW /// Enable
|
||||
};
|
||||
using SettingDistributedProductMode = SettingEnum<DistributedProductMode>;
|
||||
|
||||
|
||||
using SettingDateTimeInputFormat = SettingEnum<FormatSettings::DateTimeInputFormat>;
|
||||
|
||||
|
||||
enum class LogsLevel
|
||||
{
|
||||
none = 0, /// Disable
|
||||
@ -391,29 +266,392 @@ enum class LogsLevel
|
||||
debug,
|
||||
trace,
|
||||
};
|
||||
using SettingLogsLevel = SettingEnum<LogsLevel>;
|
||||
|
||||
class SettingLogsLevel
|
||||
|
||||
namespace details
|
||||
{
|
||||
struct SettingsCollectionUtils
|
||||
{
|
||||
static void serializeName(const StringRef & name, WriteBuffer & buf);
|
||||
static String deserializeName(ReadBuffer & buf);
|
||||
static void throwNameNotFound(const StringRef & name);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/** Template class to define collections of settings.
|
||||
* Example of usage:
|
||||
*
|
||||
* mysettings.h:
|
||||
* struct MySettings : public SettingsCollection<MySettings>
|
||||
* {
|
||||
* # define APPLY_FOR_MYSETTINGS(M) \
|
||||
* M(SettingUInt64, a, 100, "Description of a") \
|
||||
* M(SettingFloat, f, 3.11, "Description of f") \
|
||||
* M(SettingString, s, "default", "Description of s")
|
||||
*
|
||||
* DECLARE_SETTINGS_COLLECTION(MySettings, APPLY_FOR_MYSETTINGS)
|
||||
* };
|
||||
*
|
||||
* mysettings.cpp:
|
||||
* IMPLEMENT_SETTINGS_COLLECTION(MySettings, APPLY_FOR_MYSETTINGS)
|
||||
*/
|
||||
template <class Derived>
|
||||
class SettingsCollection
|
||||
{
|
||||
private:
|
||||
Derived & castToDerived() { return *static_cast<Derived *>(this); }
|
||||
const Derived & castToDerived() const { return *static_cast<const Derived *>(this); }
|
||||
|
||||
using GetStringFunction = String (*)(const Derived &);
|
||||
using GetFieldFunction = Field (*)(const Derived &);
|
||||
using SetStringFunction = void (*)(Derived &, const String &);
|
||||
using SetFieldFunction = void (*)(Derived &, const Field &);
|
||||
using SerializeFunction = void (*)(const Derived &, WriteBuffer & buf);
|
||||
using DeserializeFunction = void (*)(Derived &, ReadBuffer & buf);
|
||||
using CastValueWithoutApplyingFunction = Field (*)(const Field &);
|
||||
|
||||
struct MemberInfo
|
||||
{
|
||||
size_t offset_of_changed;
|
||||
StringRef name;
|
||||
StringRef description;
|
||||
GetStringFunction get_string;
|
||||
GetFieldFunction get_field;
|
||||
SetStringFunction set_string;
|
||||
SetFieldFunction set_field;
|
||||
SerializeFunction serialize;
|
||||
DeserializeFunction deserialize;
|
||||
CastValueWithoutApplyingFunction cast_value_without_applying;
|
||||
|
||||
bool isChanged(const Derived & collection) const { return *reinterpret_cast<const bool*>(reinterpret_cast<const UInt8*>(&collection) + offset_of_changed); }
|
||||
};
|
||||
|
||||
class MemberInfos
|
||||
{
|
||||
public:
|
||||
static const MemberInfos & instance()
|
||||
{
|
||||
static const MemberInfos single_instance;
|
||||
return single_instance;
|
||||
}
|
||||
|
||||
size_t size() const { return infos.size(); }
|
||||
const MemberInfo & operator[](size_t index) const { return infos[index]; }
|
||||
const MemberInfo * begin() const { return infos.data(); }
|
||||
const MemberInfo * end() const { return infos.data() + infos.size(); }
|
||||
|
||||
size_t findIndex(const StringRef & name) const
|
||||
{
|
||||
auto it = by_name_map.find(name);
|
||||
if (it == by_name_map.end())
|
||||
return static_cast<size_t>(-1); // npos
|
||||
return it->second;
|
||||
}
|
||||
|
||||
size_t findIndexStrict(const StringRef & name) const
|
||||
{
|
||||
auto it = by_name_map.find(name);
|
||||
if (it == by_name_map.end())
|
||||
details::SettingsCollectionUtils::throwNameNotFound(name);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const MemberInfo * find(const StringRef & name) const
|
||||
{
|
||||
auto it = by_name_map.find(name);
|
||||
if (it == by_name_map.end())
|
||||
return end();
|
||||
else
|
||||
return &infos[it->second];
|
||||
}
|
||||
|
||||
const MemberInfo * findStrict(const StringRef & name) const { return &infos[findIndexStrict(name)]; }
|
||||
|
||||
private:
|
||||
MemberInfos();
|
||||
|
||||
void add(MemberInfo && member)
|
||||
{
|
||||
size_t index = infos.size();
|
||||
infos.emplace_back(member);
|
||||
by_name_map.emplace(infos.back().name, index);
|
||||
}
|
||||
|
||||
std::vector<MemberInfo> infos;
|
||||
std::unordered_map<StringRef, size_t> by_name_map;
|
||||
};
|
||||
|
||||
static const MemberInfos & members() { return MemberInfos::instance(); }
|
||||
|
||||
public:
|
||||
using Value = LogsLevel;
|
||||
class const_iterator;
|
||||
|
||||
Value value;
|
||||
bool changed = false;
|
||||
/// Provides read-only access to a setting.
|
||||
class const_reference
|
||||
{
|
||||
public:
|
||||
const_reference(const Derived & collection_, const MemberInfo & member_) : collection(&collection_), member(&member_) {}
|
||||
const_reference(const const_reference & src) = default;
|
||||
const StringRef & getName() const { return member->name; }
|
||||
const StringRef & getDescription() const { return member->description; }
|
||||
bool isChanged() const { return member->isChanged(*collection); }
|
||||
Field getValue() const { return member->get_field(*collection); }
|
||||
String getValueAsString() const { return member->get_string(*collection); }
|
||||
protected:
|
||||
friend class SettingsCollection<Derived>::const_iterator;
|
||||
const_reference() : collection(nullptr), member(nullptr) {}
|
||||
const_reference & operator=(const const_reference &) = default;
|
||||
const Derived * collection;
|
||||
const MemberInfo * member;
|
||||
};
|
||||
|
||||
SettingLogsLevel(Value x) : value(x) {}
|
||||
/// Provides access to a setting.
|
||||
class reference : public const_reference
|
||||
{
|
||||
public:
|
||||
reference(Derived & collection_, const MemberInfo & member_) : const_reference(collection_, member_) {}
|
||||
reference(const const_reference & src) : const_reference(src) {}
|
||||
void setValue(const Field & value) { this->member->set_field(*const_cast<Derived *>(this->collection), value); }
|
||||
void setValue(const String & value) { this->member->set_string(*const_cast<Derived *>(this->collection), value); }
|
||||
};
|
||||
|
||||
operator Value() const { return value; }
|
||||
SettingLogsLevel & operator= (Value x) { set(x); return *this; }
|
||||
/// Iterator to iterating through all the settings.
|
||||
class const_iterator
|
||||
{
|
||||
public:
|
||||
const_iterator(const Derived & collection_, const MemberInfo * member_) : ref(const_cast<Derived &>(collection_), *member_) {}
|
||||
const_iterator() = default;
|
||||
const_iterator(const const_iterator & src) = default;
|
||||
const_iterator & operator =(const const_iterator & src) = default;
|
||||
const const_reference & operator *() const { return ref; }
|
||||
const const_reference * operator ->() const { return &ref; }
|
||||
const_iterator & operator ++() { ++ref.member; return *this; }
|
||||
const_iterator & operator ++(int) { const_iterator tmp = *this; ++*this; return tmp; }
|
||||
bool operator ==(const const_iterator & rhs) const { return ref.member == rhs.ref.member && ref.collection == rhs.ref.collection; }
|
||||
bool operator !=(const const_iterator & rhs) const { return !(*this == rhs); }
|
||||
protected:
|
||||
mutable reference ref;
|
||||
};
|
||||
|
||||
static Value getValue(const String & s);
|
||||
class iterator : public const_iterator
|
||||
{
|
||||
public:
|
||||
iterator(Derived & collection_, const MemberInfo * member_) : const_iterator(collection_, member_) {}
|
||||
iterator() = default;
|
||||
iterator(const const_iterator & src) : const_iterator(src) {}
|
||||
iterator & operator =(const const_iterator & src) { const_iterator::operator =(src); return *this; }
|
||||
reference & operator *() const { return this->ref; }
|
||||
reference * operator ->() const { return &this->ref; }
|
||||
iterator & operator ++() { const_iterator::operator ++(); return *this; }
|
||||
iterator & operator ++(int) { iterator tmp = *this; ++*this; return tmp; }
|
||||
};
|
||||
|
||||
String toString() const;
|
||||
/// Returns the number of settings.
|
||||
static size_t size() { return members().size(); }
|
||||
|
||||
void set(Value x);
|
||||
void set(const Field & x);
|
||||
void set(const String & x);
|
||||
void set(ReadBuffer & buf);
|
||||
void write(WriteBuffer & buf) const;
|
||||
/// Returns name of a setting by its index (0..size()-1).
|
||||
static StringRef getName(size_t index) { return members()[index].name; }
|
||||
|
||||
/// Returns description of a setting.
|
||||
static StringRef getDescription(size_t index) { return members()[index].description; }
|
||||
static StringRef getDescription(const String & name) { return members().findStrict(name)->description; }
|
||||
|
||||
/// Searches a setting by its name; returns `npos` if not found.
|
||||
static size_t findIndex(const String & name) { return members().findIndex(name); }
|
||||
static constexpr size_t npos = static_cast<size_t>(-1);
|
||||
|
||||
/// Searches a setting by its name; throws an exception if not found.
|
||||
static size_t findIndexStrict(const String & name) { return members().findIndexStrict(name); }
|
||||
|
||||
/// Casts a value to a type according to a specified setting without actual changing this settings.
|
||||
/// E.g. for SettingInt64 it casts Field to Field::Types::Int64.
|
||||
static Field castValueWithoutApplying(size_t index, const Field & value) { return members()[index].cast_value_without_applying(value); }
|
||||
static Field castValueWithoutApplying(const String & name, const Field & value) { return members().findStrict(name)->cast_value_without_applying(value); }
|
||||
|
||||
iterator begin() { return iterator(castToDerived(), members().begin()); }
|
||||
const_iterator begin() const { return const_iterator(castToDerived(), members().begin()); }
|
||||
iterator end() { return iterator(castToDerived(), members().end()); }
|
||||
const_iterator end() const { return const_iterator(castToDerived(), members().end()); }
|
||||
|
||||
/// Returns a proxy object for accessing to a setting. Throws an exception if there is not setting with such name.
|
||||
reference operator[](size_t index) { return reference(castToDerived(), members()[index]); }
|
||||
reference operator[](const String & name) { return reference(castToDerived(), *(members().findStrict(name))); }
|
||||
const_reference operator[](size_t index) const { return const_reference(castToDerived(), members()[index]); }
|
||||
const_reference operator[](const String & name) const { return const_reference(castToDerived(), *(members().findStrict(name))); }
|
||||
|
||||
/// Searches a setting by its name; returns end() if not found.
|
||||
iterator find(const String & name) { return iterator(castToDerived(), members().find(name)); }
|
||||
const_iterator find(const String & name) const { return const_iterator(castToDerived(), members().find(name)); }
|
||||
|
||||
/// Searches a setting by its name; throws an exception if not found.
|
||||
iterator findStrict(const String & name) { return iterator(castToDerived(), members().findStrict(name)); }
|
||||
const_iterator findStrict(const String & name) const { return const_iterator(castToDerived(), members().findStrict(name)); }
|
||||
|
||||
/// Sets setting's value.
|
||||
void set(size_t index, const Field & value) { (*this)[index].setValue(value); }
|
||||
void set(const String & name, const Field & value) { (*this)[name].setValue(value); }
|
||||
|
||||
/// Sets setting's value. Read value in text form from string (for example, from configuration file or from URL parameter).
|
||||
void set(size_t index, const String & value) { (*this)[index].setValue(value); }
|
||||
void set(const String & name, const String & value) { (*this)[name].setValue(value); }
|
||||
|
||||
/// Returns value of a setting.
|
||||
Field get(size_t index) const { return (*this)[index].getValue(); }
|
||||
Field get(const String & name) const { return (*this)[name].getValue(); }
|
||||
|
||||
/// Returns value of a setting converted to string.
|
||||
String getAsString(size_t index) const { return (*this)[index].getValueAsString(); }
|
||||
String getAsString(const String & name) const { return (*this)[name].getValueAsString(); }
|
||||
|
||||
/// Returns value of a setting; returns false if there is no setting with the specified name.
|
||||
bool tryGet(const String & name, Field & value) const
|
||||
{
|
||||
auto it = find(name);
|
||||
if (it == end())
|
||||
return false;
|
||||
value = it->getValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Returns value of a setting converted to string; returns false if there is no setting with the specified name.
|
||||
bool tryGet(const String & name, String & value) const
|
||||
{
|
||||
auto it = find(name);
|
||||
if (it == end())
|
||||
return false;
|
||||
value = it->getValueAsString();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Compares two collections of settings.
|
||||
bool operator ==(const Derived & rhs) const
|
||||
{
|
||||
for (const auto & member : members())
|
||||
{
|
||||
bool left_changed = member.isChanged(castToDerived());
|
||||
bool right_changed = member.isChanged(rhs);
|
||||
if (left_changed || right_changed)
|
||||
{
|
||||
if (left_changed != right_changed)
|
||||
return false;
|
||||
if (member.get_field(castToDerived()) != member.get_field(rhs))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator !=(const Derived & rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
/// Gathers all changed values (e.g. for applying them later to another collection of settings).
|
||||
SettingsChanges changes() const
|
||||
{
|
||||
SettingsChanges found_changes;
|
||||
for (const auto & member : members())
|
||||
{
|
||||
if (member.isChanged(castToDerived()))
|
||||
found_changes.emplace_back(member.name.toString(), member.get_field(castToDerived()));
|
||||
}
|
||||
return found_changes;
|
||||
}
|
||||
|
||||
/// Applies changes to the settings.
|
||||
void applyChange(const SettingChange & change)
|
||||
{
|
||||
set(change.name, change.value);
|
||||
}
|
||||
|
||||
void applyChanges(const SettingsChanges & changes)
|
||||
{
|
||||
for (const SettingChange & change : changes)
|
||||
applyChange(change);
|
||||
}
|
||||
|
||||
void copyChangesFrom(const Derived & src)
|
||||
{
|
||||
for (const auto & member : members())
|
||||
if (member.isChanged(src))
|
||||
member.set_field(castToDerived(), member.get_field(src));
|
||||
}
|
||||
|
||||
void copyChangesTo(Derived & dest) const
|
||||
{
|
||||
dest.copyChangesFrom(castToDerived());
|
||||
}
|
||||
|
||||
/// Writes the settings to buffer (e.g. to be sent to remote server).
|
||||
/// Only changed settings are written. They are written as list of contiguous name-value pairs,
|
||||
/// finished with empty name.
|
||||
void serialize(WriteBuffer & buf) const
|
||||
{
|
||||
for (const auto & member : members())
|
||||
{
|
||||
if (member.isChanged(castToDerived()))
|
||||
{
|
||||
details::SettingsCollectionUtils::serializeName(member.name, buf);
|
||||
member.serialize(castToDerived(), buf);
|
||||
}
|
||||
}
|
||||
details::SettingsCollectionUtils::serializeName(StringRef{} /* empty string is a marker of the end of settings */, buf);
|
||||
}
|
||||
|
||||
/// Reads the settings from buffer.
|
||||
void deserialize(ReadBuffer & buf)
|
||||
{
|
||||
const auto & the_members = members();
|
||||
while (true)
|
||||
{
|
||||
String name = details::SettingsCollectionUtils::deserializeName(buf);
|
||||
if (name.empty() /* empty string is a marker of the end of settings */)
|
||||
break;
|
||||
the_members.findStrict(name)->deserialize(castToDerived(), buf);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define DECLARE_SETTINGS_COLLECTION(LIST_OF_SETTINGS_MACRO) \
|
||||
LIST_OF_SETTINGS_MACRO(DECLARE_SETTINGS_COLLECTION_DECLARE_VARIABLES_HELPER_)
|
||||
|
||||
|
||||
#define IMPLEMENT_SETTINGS_COLLECTION(DERIVED_CLASS_NAME, LIST_OF_SETTINGS_MACRO) \
|
||||
template<> \
|
||||
SettingsCollection<DERIVED_CLASS_NAME>::MemberInfos::MemberInfos() \
|
||||
{ \
|
||||
using Derived = DERIVED_CLASS_NAME; \
|
||||
struct Functions \
|
||||
{ \
|
||||
LIST_OF_SETTINGS_MACRO(IMPLEMENT_SETTINGS_COLLECTION_DEFINE_FUNCTIONS_HELPER_) \
|
||||
}; \
|
||||
LIST_OF_SETTINGS_MACRO(IMPLEMENT_SETTINGS_COLLECTION_ADD_MEMBER_INFO_HELPER_) \
|
||||
}
|
||||
|
||||
|
||||
#define DECLARE_SETTINGS_COLLECTION_DECLARE_VARIABLES_HELPER_(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
TYPE NAME {DEFAULT};
|
||||
|
||||
|
||||
#define IMPLEMENT_SETTINGS_COLLECTION_DEFINE_FUNCTIONS_HELPER_(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
static String NAME##_getString(const Derived & collection) { return collection.NAME.toString(); } \
|
||||
static Field NAME##_getField(const Derived & collection) { return collection.NAME.toField(); } \
|
||||
static void NAME##_setString(Derived & collection, const String & value) { collection.NAME.set(value); } \
|
||||
static void NAME##_setField(Derived & collection, const Field & value) { collection.NAME.set(value); } \
|
||||
static void NAME##_serialize(const Derived & collection, WriteBuffer & buf) { collection.NAME.serialize(buf); } \
|
||||
static void NAME##_deserialize(Derived & collection, ReadBuffer & buf) { collection.NAME.deserialize(buf); } \
|
||||
static Field NAME##_castValueWithoutApplying(const Field & value) { TYPE temp{DEFAULT}; temp.set(value); return temp.toField(); }
|
||||
|
||||
|
||||
#define IMPLEMENT_SETTINGS_COLLECTION_ADD_MEMBER_INFO_HELPER_(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
static_assert(std::is_same_v<decltype(std::declval<Derived>().NAME.changed), bool>); \
|
||||
add({offsetof(Derived, NAME.changed), \
|
||||
StringRef(#NAME, strlen(#NAME)), StringRef(#DESCRIPTION, strlen(#DESCRIPTION)), \
|
||||
&Functions::NAME##_getString, &Functions::NAME##_getField, \
|
||||
&Functions::NAME##_setString, &Functions::NAME##_setField, \
|
||||
&Functions::NAME##_serialize, &Functions::NAME##_deserialize, \
|
||||
&Functions::NAME##_castValueWithoutApplying });
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include <DataStreams/AggregatingSortedBlockInputStream.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Common/StringUtils/StringUtils.h>
|
||||
#include <DataTypes/DataTypeAggregateFunction.h>
|
||||
#include <DataTypes/DataTypeCustomSimpleAggregateFunction.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -22,7 +24,7 @@ AggregatingSortedBlockInputStream::AggregatingSortedBlockInputStream(
|
||||
ColumnWithTypeAndName & column = header.safeGetByPosition(i);
|
||||
|
||||
/// We leave only states of aggregate functions.
|
||||
if (!startsWith(column.type->getName(), "AggregateFunction"))
|
||||
if (!dynamic_cast<const DataTypeAggregateFunction *>(column.type.get()) && !dynamic_cast<const DataTypeCustomSimpleAggregateFunction *>(column.type->getCustomName()))
|
||||
{
|
||||
column_numbers_not_to_aggregate.push_back(i);
|
||||
continue;
|
||||
@ -40,8 +42,18 @@ AggregatingSortedBlockInputStream::AggregatingSortedBlockInputStream(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto simple_aggr = dynamic_cast<const DataTypeCustomSimpleAggregateFunction *>(column.type->getCustomName()))
|
||||
{
|
||||
// simple aggregate function
|
||||
SimpleAggregateDescription desc{simple_aggr->getFunction(), i};
|
||||
columns_to_simple_aggregate.emplace_back(std::move(desc));
|
||||
}
|
||||
else
|
||||
{
|
||||
// standard aggregate function
|
||||
column_numbers_to_aggregate.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -91,7 +103,11 @@ void AggregatingSortedBlockInputStream::merge(MutableColumns & merged_columns, s
|
||||
|
||||
/// if there are enough rows accumulated and the last one is calculated completely
|
||||
if (key_differs && merged_rows >= max_block_size)
|
||||
{
|
||||
/// Write the simple aggregation result for the previous group.
|
||||
insertSimpleAggregationResult(merged_columns);
|
||||
return;
|
||||
}
|
||||
|
||||
queue.pop();
|
||||
|
||||
@ -110,6 +126,14 @@ void AggregatingSortedBlockInputStream::merge(MutableColumns & merged_columns, s
|
||||
for (auto & column_to_aggregate : columns_to_aggregate)
|
||||
column_to_aggregate->insertDefault();
|
||||
|
||||
/// Write the simple aggregation result for the previous group.
|
||||
if (merged_rows > 0)
|
||||
insertSimpleAggregationResult(merged_columns);
|
||||
|
||||
/// Reset simple aggregation states for next row
|
||||
for (auto & desc : columns_to_simple_aggregate)
|
||||
desc.createState();
|
||||
|
||||
++merged_rows;
|
||||
}
|
||||
|
||||
@ -127,6 +151,9 @@ void AggregatingSortedBlockInputStream::merge(MutableColumns & merged_columns, s
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the simple aggregation result for the previous group.
|
||||
insertSimpleAggregationResult(merged_columns);
|
||||
|
||||
finished = true;
|
||||
}
|
||||
|
||||
@ -138,6 +165,21 @@ void AggregatingSortedBlockInputStream::addRow(SortCursor & cursor)
|
||||
size_t j = column_numbers_to_aggregate[i];
|
||||
columns_to_aggregate[i]->insertMergeFrom(*cursor->all_columns[j], cursor->pos);
|
||||
}
|
||||
|
||||
for (auto & desc : columns_to_simple_aggregate)
|
||||
{
|
||||
auto & col = cursor->all_columns[desc.column_number];
|
||||
desc.add_function(desc.function.get(), desc.state.data(), &col, cursor->pos, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void AggregatingSortedBlockInputStream::insertSimpleAggregationResult(MutableColumns & merged_columns)
|
||||
{
|
||||
for (auto & desc : columns_to_simple_aggregate)
|
||||
{
|
||||
desc.function->insertResultInto(desc.state.data(), *merged_columns[desc.column_number]);
|
||||
desc.destroyState();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <DataStreams/MergingSortedBlockInputStream.h>
|
||||
#include <AggregateFunctions/IAggregateFunction.h>
|
||||
#include <Columns/ColumnAggregateFunction.h>
|
||||
#include <Common/AlignedBuffer.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -38,10 +39,13 @@ private:
|
||||
/// Read finished.
|
||||
bool finished = false;
|
||||
|
||||
struct SimpleAggregateDescription;
|
||||
|
||||
/// Columns with which numbers should be aggregated.
|
||||
ColumnNumbers column_numbers_to_aggregate;
|
||||
ColumnNumbers column_numbers_not_to_aggregate;
|
||||
std::vector<ColumnAggregateFunction *> columns_to_aggregate;
|
||||
std::vector<SimpleAggregateDescription> columns_to_simple_aggregate;
|
||||
|
||||
RowRef current_key; /// The current primary key.
|
||||
RowRef next_key; /// The primary key of the next row.
|
||||
@ -54,6 +58,53 @@ private:
|
||||
/** Extract all states of aggregate functions and merge them with the current group.
|
||||
*/
|
||||
void addRow(SortCursor & cursor);
|
||||
|
||||
/** Insert all values of current row for simple aggregate functions
|
||||
*/
|
||||
void insertSimpleAggregationResult(MutableColumns & merged_columns);
|
||||
|
||||
/// Stores information for aggregation of SimpleAggregateFunction columns
|
||||
struct SimpleAggregateDescription
|
||||
{
|
||||
/// An aggregate function 'anyLast', 'sum'...
|
||||
AggregateFunctionPtr function;
|
||||
IAggregateFunction::AddFunc add_function;
|
||||
size_t column_number;
|
||||
AlignedBuffer state;
|
||||
bool created = false;
|
||||
|
||||
SimpleAggregateDescription(const AggregateFunctionPtr & function_, const size_t column_number_) : function(function_), column_number(column_number_)
|
||||
{
|
||||
add_function = function->getAddressOfAddFunction();
|
||||
state.reset(function->sizeOfData(), function->alignOfData());
|
||||
}
|
||||
|
||||
void createState()
|
||||
{
|
||||
if (created)
|
||||
return;
|
||||
function->create(state.data());
|
||||
created = true;
|
||||
}
|
||||
|
||||
void destroyState()
|
||||
{
|
||||
if (!created)
|
||||
return;
|
||||
function->destroy(state.data());
|
||||
created = false;
|
||||
}
|
||||
|
||||
/// Explicitly destroy aggregation state if the stream is terminated
|
||||
~SimpleAggregateDescription()
|
||||
{
|
||||
destroyState();
|
||||
}
|
||||
|
||||
SimpleAggregateDescription() = default;
|
||||
SimpleAggregateDescription(SimpleAggregateDescription &&) = default;
|
||||
SimpleAggregateDescription(const SimpleAggregateDescription &) = delete;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <DataStreams/InputStreamFromASTInsertQuery.h>
|
||||
#include <DataStreams/AddingDefaultsBlockInputStream.h>
|
||||
#include <Storages/ColumnsDescription.h>
|
||||
#include <Storages/IStorage.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -49,10 +51,10 @@ InputStreamFromASTInsertQuery::InputStreamFromASTInsertQuery(
|
||||
|
||||
res_stream = context.getInputFormat(format, *input_buffer_contacenated, header, context.getSettings().max_insert_block_size);
|
||||
|
||||
auto columns_description = ColumnsDescription::loadFromContext(context, ast_insert_query->database, ast_insert_query->table);
|
||||
if (columns_description)
|
||||
if (context.getSettingsRef().input_format_defaults_for_omitted_fields)
|
||||
{
|
||||
auto column_defaults = columns_description->getDefaults();
|
||||
StoragePtr storage = context.getTable(ast_insert_query->database, ast_insert_query->table);
|
||||
auto column_defaults = storage->getColumns().getDefaults();
|
||||
if (!column_defaults.empty())
|
||||
res_stream = std::make_shared<AddingDefaultsBlockInputStream>(res_stream, column_defaults, context);
|
||||
}
|
||||
|
@ -5,9 +5,11 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
LimitByBlockInputStream::LimitByBlockInputStream(const BlockInputStreamPtr & input, size_t group_size_, const Names & columns)
|
||||
LimitByBlockInputStream::LimitByBlockInputStream(const BlockInputStreamPtr & input,
|
||||
size_t group_length_, size_t group_offset_, const Names & columns)
|
||||
: columns_names(columns)
|
||||
, group_size(group_size_)
|
||||
, group_length(group_length_)
|
||||
, group_offset(group_offset_)
|
||||
{
|
||||
children.push_back(input);
|
||||
}
|
||||
@ -37,7 +39,8 @@ Block LimitByBlockInputStream::readImpl()
|
||||
|
||||
hash.get128(key.low, key.high);
|
||||
|
||||
if (keys_counts[key]++ < group_size)
|
||||
auto count = keys_counts[key]++;
|
||||
if (count >= group_offset && count < group_length + group_offset)
|
||||
{
|
||||
inserted_count++;
|
||||
filter[i] = 1;
|
||||
|
@ -18,7 +18,7 @@ namespace DB
|
||||
class LimitByBlockInputStream : public IBlockInputStream
|
||||
{
|
||||
public:
|
||||
LimitByBlockInputStream(const BlockInputStreamPtr & input, size_t group_size_, const Names & columns);
|
||||
LimitByBlockInputStream(const BlockInputStreamPtr & input, size_t group_length_, size_t group_offset_, const Names & columns);
|
||||
|
||||
String getName() const override { return "LimitBy"; }
|
||||
|
||||
@ -34,7 +34,8 @@ private:
|
||||
using MapHashed = HashMap<UInt128, UInt64, UInt128TrivialHash>;
|
||||
|
||||
const Names columns_names;
|
||||
const size_t group_size;
|
||||
const size_t group_length;
|
||||
const size_t group_offset;
|
||||
MapHashed keys_counts;
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstddef>
|
||||
#include <Core/Types.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -10,21 +12,21 @@ class WriteBuffer;
|
||||
struct FormatSettings;
|
||||
class IColumn;
|
||||
|
||||
/** Further refinment of the properties of data type.
|
||||
*
|
||||
* Contains methods for serialization/deserialization.
|
||||
* Implementations of this interface represent a data type domain (example: IPv4)
|
||||
* which is a refinement of the exsitgin type with a name and specific text
|
||||
* representation.
|
||||
*
|
||||
* IDataTypeDomain is totally immutable object. You can always share them.
|
||||
/** Allow to customize an existing data type and set a different name and/or text serialization/deserialization methods.
|
||||
* See use in IPv4 and IPv6 data types, and also in SimpleAggregateFunction.
|
||||
*/
|
||||
class IDataTypeDomain
|
||||
class IDataTypeCustomName
|
||||
{
|
||||
public:
|
||||
virtual ~IDataTypeDomain() {}
|
||||
virtual ~IDataTypeCustomName() {}
|
||||
|
||||
virtual const char* getName() const = 0;
|
||||
virtual String getName() const = 0;
|
||||
};
|
||||
|
||||
class IDataTypeCustomTextSerialization
|
||||
{
|
||||
public:
|
||||
virtual ~IDataTypeCustomTextSerialization() {}
|
||||
|
||||
/** Text serialization for displaying on a terminal or saving into a text file, and the like.
|
||||
* Without escaping or quoting.
|
||||
@ -56,4 +58,31 @@ public:
|
||||
virtual void serializeTextXML(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const = 0;
|
||||
};
|
||||
|
||||
using DataTypeCustomNamePtr = std::unique_ptr<const IDataTypeCustomName>;
|
||||
using DataTypeCustomTextSerializationPtr = std::unique_ptr<const IDataTypeCustomTextSerialization>;
|
||||
|
||||
/** Describe a data type customization
|
||||
*/
|
||||
struct DataTypeCustomDesc
|
||||
{
|
||||
DataTypeCustomNamePtr name;
|
||||
DataTypeCustomTextSerializationPtr text_serialization;
|
||||
|
||||
DataTypeCustomDesc(DataTypeCustomNamePtr name_, DataTypeCustomTextSerializationPtr text_serialization_)
|
||||
: name(std::move(name_)), text_serialization(std::move(text_serialization_)) {}
|
||||
};
|
||||
|
||||
using DataTypeCustomDescPtr = std::unique_ptr<DataTypeCustomDesc>;
|
||||
|
||||
/** A simple implementation of IDataTypeCustomName
|
||||
*/
|
||||
class DataTypeCustomFixedName : public IDataTypeCustomName
|
||||
{
|
||||
private:
|
||||
String name;
|
||||
public:
|
||||
DataTypeCustomFixedName(String name_) : name(name_) {}
|
||||
String getName() const override { return name; }
|
||||
};
|
||||
|
||||
} // namespace DB
|
@ -1,9 +1,9 @@
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/formatIPv6.h>
|
||||
#include <DataTypes/DataTypeDomainWithSimpleSerialization.h>
|
||||
#include <DataTypes/DataTypeCustomSimpleTextSerialization.h>
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
#include <DataTypes/IDataTypeDomain.h>
|
||||
#include <DataTypes/DataTypeCustom.h>
|
||||
#include <Functions/FunctionHelpers.h>
|
||||
#include <Functions/FunctionsCoding.h>
|
||||
|
||||
@ -20,20 +20,15 @@ namespace ErrorCodes
|
||||
namespace
|
||||
{
|
||||
|
||||
class DataTypeDomainIPv4 : public DataTypeDomainWithSimpleSerialization
|
||||
class DataTypeCustomIPv4Serialization : public DataTypeCustomSimpleTextSerialization
|
||||
{
|
||||
public:
|
||||
const char * getName() const override
|
||||
{
|
||||
return "IPv4";
|
||||
}
|
||||
|
||||
void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override
|
||||
{
|
||||
const auto col = checkAndGetColumn<ColumnUInt32>(&column);
|
||||
if (!col)
|
||||
{
|
||||
throw Exception(String(getName()) + " domain can only serialize columns of type UInt32." + column.getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
throw Exception("IPv4 type can only serialize columns of type UInt32." + column.getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
|
||||
char buffer[IPV4_MAX_TEXT_LENGTH + 1] = {'\0'};
|
||||
@ -48,7 +43,7 @@ public:
|
||||
ColumnUInt32 * col = typeid_cast<ColumnUInt32 *>(&column);
|
||||
if (!col)
|
||||
{
|
||||
throw Exception(String(getName()) + " domain can only deserialize columns of type UInt32." + column.getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
throw Exception("IPv4 type can only deserialize columns of type UInt32." + column.getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
|
||||
char buffer[IPV4_MAX_TEXT_LENGTH + 1] = {'\0'};
|
||||
@ -63,20 +58,16 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class DataTypeDomainIPv6 : public DataTypeDomainWithSimpleSerialization
|
||||
class DataTypeCustomIPv6Serialization : public DataTypeCustomSimpleTextSerialization
|
||||
{
|
||||
public:
|
||||
const char * getName() const override
|
||||
{
|
||||
return "IPv6";
|
||||
}
|
||||
|
||||
void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override
|
||||
{
|
||||
const auto col = checkAndGetColumn<ColumnFixedString>(&column);
|
||||
if (!col)
|
||||
{
|
||||
throw Exception(String(getName()) + " domain can only serialize columns of type FixedString(16)." + column.getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
throw Exception("IPv6 type domain can only serialize columns of type FixedString(16)." + column.getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
|
||||
char buffer[IPV6_MAX_TEXT_LENGTH + 1] = {'\0'};
|
||||
@ -91,7 +82,7 @@ public:
|
||||
ColumnFixedString * col = typeid_cast<ColumnFixedString *>(&column);
|
||||
if (!col)
|
||||
{
|
||||
throw Exception(String(getName()) + " domain can only deserialize columns of type FixedString(16)." + column.getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
throw Exception("IPv6 type domain can only deserialize columns of type FixedString(16)." + column.getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
|
||||
char buffer[IPV6_MAX_TEXT_LENGTH + 1] = {'\0'};
|
||||
@ -100,7 +91,7 @@ public:
|
||||
std::string ipv6_value(IPV6_BINARY_LENGTH, '\0');
|
||||
if (!parseIPv6(buffer, reinterpret_cast<unsigned char *>(ipv6_value.data())))
|
||||
{
|
||||
throw Exception(String("Invalid ") + getName() + " value.", ErrorCodes::CANNOT_PARSE_DOMAIN_VALUE_FROM_STRING);
|
||||
throw Exception("Invalid IPv6 value.", ErrorCodes::CANNOT_PARSE_DOMAIN_VALUE_FROM_STRING);
|
||||
}
|
||||
|
||||
col->insertString(ipv6_value);
|
||||
@ -111,8 +102,17 @@ public:
|
||||
|
||||
void registerDataTypeDomainIPv4AndIPv6(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerDataTypeDomain("UInt32", std::make_unique<DataTypeDomainIPv4>());
|
||||
factory.registerDataTypeDomain("FixedString(16)", std::make_unique<DataTypeDomainIPv6>());
|
||||
factory.registerSimpleDataTypeCustom("IPv4", []
|
||||
{
|
||||
return std::make_pair(DataTypeFactory::instance().get("UInt32"),
|
||||
std::make_unique<DataTypeCustomDesc>(std::make_unique<DataTypeCustomFixedName>("IPv4"), std::make_unique<DataTypeCustomIPv4Serialization>()));
|
||||
});
|
||||
|
||||
factory.registerSimpleDataTypeCustom("IPv6", []
|
||||
{
|
||||
return std::make_pair(DataTypeFactory::instance().get("FixedString(16)"),
|
||||
std::make_unique<DataTypeCustomDesc>(std::make_unique<DataTypeCustomFixedName>("IPv6"), std::make_unique<DataTypeCustomIPv6Serialization>()));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace DB
|
137
dbms/src/DataTypes/DataTypeCustomSimpleAggregateFunction.cpp
Normal file
137
dbms/src/DataTypes/DataTypeCustomSimpleAggregateFunction.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
|
||||
#include <IO/ReadHelpers.h>
|
||||
|
||||
#include <Columns/ColumnAggregateFunction.h>
|
||||
|
||||
#include <DataTypes/DataTypeCustomSimpleAggregateFunction.h>
|
||||
#include <DataTypes/DataTypeLowCardinality.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
|
||||
#include <AggregateFunctions/AggregateFunctionFactory.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int SYNTAX_ERROR;
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS;
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
static const std::vector<String> supported_functions{"any", "anyLast", "min", "max", "sum"};
|
||||
|
||||
|
||||
String DataTypeCustomSimpleAggregateFunction::getName() const
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << "SimpleAggregateFunction(" << function->getName();
|
||||
|
||||
if (!parameters.empty())
|
||||
{
|
||||
stream << "(";
|
||||
for (size_t i = 0; i < parameters.size(); ++i)
|
||||
{
|
||||
if (i)
|
||||
stream << ", ";
|
||||
stream << applyVisitor(DB::FieldVisitorToString(), parameters[i]);
|
||||
}
|
||||
stream << ")";
|
||||
}
|
||||
|
||||
for (const auto & argument_type : argument_types)
|
||||
stream << ", " << argument_type->getName();
|
||||
|
||||
stream << ")";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
|
||||
static std::pair<DataTypePtr, DataTypeCustomDescPtr> create(const ASTPtr & arguments)
|
||||
{
|
||||
String function_name;
|
||||
AggregateFunctionPtr function;
|
||||
DataTypes argument_types;
|
||||
Array params_row;
|
||||
|
||||
if (!arguments || arguments->children.empty())
|
||||
throw Exception("Data type SimpleAggregateFunction requires parameters: "
|
||||
"name of aggregate function and list of data types for arguments", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
if (const ASTFunction * parametric = arguments->children[0]->as<ASTFunction>())
|
||||
{
|
||||
if (parametric->parameters)
|
||||
throw Exception("Unexpected level of parameters to aggregate function", ErrorCodes::SYNTAX_ERROR);
|
||||
function_name = parametric->name;
|
||||
|
||||
const ASTs & parameters = parametric->arguments->as<ASTExpressionList &>().children;
|
||||
params_row.resize(parameters.size());
|
||||
|
||||
for (size_t i = 0; i < parameters.size(); ++i)
|
||||
{
|
||||
const ASTLiteral * lit = parameters[i]->as<ASTLiteral>();
|
||||
if (!lit)
|
||||
throw Exception("Parameters to aggregate functions must be literals",
|
||||
ErrorCodes::PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS);
|
||||
|
||||
params_row[i] = lit->value;
|
||||
}
|
||||
}
|
||||
else if (auto opt_name = getIdentifierName(arguments->children[0]))
|
||||
{
|
||||
function_name = *opt_name;
|
||||
}
|
||||
else if (arguments->children[0]->as<ASTLiteral>())
|
||||
{
|
||||
throw Exception("Aggregate function name for data type SimpleAggregateFunction must be passed as identifier (without quotes) or function",
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
else
|
||||
throw Exception("Unexpected AST element passed as aggregate function name for data type SimpleAggregateFunction. Must be identifier or function.",
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
for (size_t i = 1; i < arguments->children.size(); ++i)
|
||||
argument_types.push_back(DataTypeFactory::instance().get(arguments->children[i]));
|
||||
|
||||
if (function_name.empty())
|
||||
throw Exception("Logical error: empty name of aggregate function passed", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
function = AggregateFunctionFactory::instance().get(function_name, argument_types, params_row);
|
||||
|
||||
// check function
|
||||
if (std::find(std::begin(supported_functions), std::end(supported_functions), function->getName()) == std::end(supported_functions))
|
||||
{
|
||||
throw Exception("Unsupported aggregate function " + function->getName() + ", supported functions are " + boost::algorithm::join(supported_functions, ","),
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
DataTypePtr storage_type = DataTypeFactory::instance().get(argument_types[0]->getName());
|
||||
|
||||
if (!function->getReturnType()->equals(*removeLowCardinality(storage_type)))
|
||||
{
|
||||
throw Exception("Incompatible data types between aggregate function '" + function->getName() + "' which returns " + function->getReturnType()->getName() + " and column storage type " + storage_type->getName(),
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
DataTypeCustomNamePtr custom_name = std::make_unique<DataTypeCustomSimpleAggregateFunction>(function, argument_types, params_row);
|
||||
|
||||
return std::make_pair(storage_type, std::make_unique<DataTypeCustomDesc>(std::move(custom_name), nullptr));
|
||||
}
|
||||
|
||||
void registerDataTypeDomainSimpleAggregateFunction(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerDataTypeCustom("SimpleAggregateFunction", create);
|
||||
}
|
||||
|
||||
}
|
42
dbms/src/DataTypes/DataTypeCustomSimpleAggregateFunction.h
Normal file
42
dbms/src/DataTypes/DataTypeCustomSimpleAggregateFunction.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <DataTypes/DataTypeCustom.h>
|
||||
#include <AggregateFunctions/IAggregateFunction.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
|
||||
#include <IO/ReadHelpers.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/** The type SimpleAggregateFunction(fct, type) is meant to be used in an AggregatingMergeTree. It behaves like a standard
|
||||
* data type but when rows are merged, an aggregation function is applied.
|
||||
*
|
||||
* The aggregation function is limited to simple functions whose merge state is the final result:
|
||||
* any, anyLast, min, max, sum
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* SimpleAggregateFunction(sum, Nullable(Float64))
|
||||
* SimpleAggregateFunction(anyLast, LowCardinality(Nullable(String)))
|
||||
* SimpleAggregateFunction(anyLast, IPv4)
|
||||
*
|
||||
* Technically, a standard IDataType is instanciated and customized with IDataTypeCustomName and DataTypeCustomDesc.
|
||||
*/
|
||||
|
||||
class DataTypeCustomSimpleAggregateFunction : public IDataTypeCustomName
|
||||
{
|
||||
private:
|
||||
const AggregateFunctionPtr function;
|
||||
const DataTypes argument_types;
|
||||
const Array parameters;
|
||||
|
||||
public:
|
||||
DataTypeCustomSimpleAggregateFunction(const AggregateFunctionPtr & function_, const DataTypes & argument_types_, const Array & parameters_)
|
||||
: function(function_), argument_types(argument_types_), parameters(parameters_) {}
|
||||
|
||||
const AggregateFunctionPtr getFunction() const { return function; }
|
||||
String getName() const override;
|
||||
};
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#include <DataTypes/DataTypeDomainWithSimpleSerialization.h>
|
||||
#include <DataTypes/DataTypeCustomSimpleTextSerialization.h>
|
||||
|
||||
#include <IO/ReadBufferFromString.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
@ -9,7 +9,7 @@ namespace
|
||||
{
|
||||
using namespace DB;
|
||||
|
||||
static String serializeToString(const DataTypeDomainWithSimpleSerialization & domain, const IColumn & column, size_t row_num, const FormatSettings & settings)
|
||||
static String serializeToString(const DataTypeCustomSimpleTextSerialization & domain, const IColumn & column, size_t row_num, const FormatSettings & settings)
|
||||
{
|
||||
WriteBufferFromOwnString buffer;
|
||||
domain.serializeText(column, row_num, buffer, settings);
|
||||
@ -17,7 +17,7 @@ static String serializeToString(const DataTypeDomainWithSimpleSerialization & do
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
static void deserializeFromString(const DataTypeDomainWithSimpleSerialization & domain, IColumn & column, const String & s, const FormatSettings & settings)
|
||||
static void deserializeFromString(const DataTypeCustomSimpleTextSerialization & domain, IColumn & column, const String & s, const FormatSettings & settings)
|
||||
{
|
||||
ReadBufferFromString istr(s);
|
||||
domain.deserializeText(column, istr, settings);
|
||||
@ -28,59 +28,59 @@ static void deserializeFromString(const DataTypeDomainWithSimpleSerialization &
|
||||
namespace DB
|
||||
{
|
||||
|
||||
DataTypeDomainWithSimpleSerialization::~DataTypeDomainWithSimpleSerialization()
|
||||
DataTypeCustomSimpleTextSerialization::~DataTypeCustomSimpleTextSerialization()
|
||||
{
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
writeEscapedString(serializeToString(*this, column, row_num, settings), ostr);
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
String str;
|
||||
readEscapedString(str, istr);
|
||||
deserializeFromString(*this, column, str, settings);
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::serializeTextQuoted(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::serializeTextQuoted(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
writeQuotedString(serializeToString(*this, column, row_num, settings), ostr);
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::deserializeTextQuoted(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::deserializeTextQuoted(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
String str;
|
||||
readQuotedString(str, istr);
|
||||
deserializeFromString(*this, column, str, settings);
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::serializeTextCSV(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::serializeTextCSV(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
writeCSVString(serializeToString(*this, column, row_num, settings), ostr);
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
String str;
|
||||
readCSVString(str, istr, settings.csv);
|
||||
deserializeFromString(*this, column, str, settings);
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::serializeTextJSON(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::serializeTextJSON(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
writeJSONString(serializeToString(*this, column, row_num, settings), ostr, settings);
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::deserializeTextJSON(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::deserializeTextJSON(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
String str;
|
||||
readJSONString(str, istr);
|
||||
deserializeFromString(*this, column, str, settings);
|
||||
}
|
||||
|
||||
void DataTypeDomainWithSimpleSerialization::serializeTextXML(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
void DataTypeCustomSimpleTextSerialization::serializeTextXML(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
writeXMLString(serializeToString(*this, column, row_num, settings), ostr);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <DataTypes/IDataTypeDomain.h>
|
||||
#include <DataTypes/DataTypeCustom.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -10,12 +10,12 @@ class WriteBuffer;
|
||||
struct FormatSettings;
|
||||
class IColumn;
|
||||
|
||||
/** Simple DataTypeDomain that uses serializeText/deserializeText
|
||||
/** Simple IDataTypeCustomTextSerialization that uses serializeText/deserializeText
|
||||
* for all serialization and deserialization. */
|
||||
class DataTypeDomainWithSimpleSerialization : public IDataTypeDomain
|
||||
class DataTypeCustomSimpleTextSerialization : public IDataTypeCustomTextSerialization
|
||||
{
|
||||
public:
|
||||
virtual ~DataTypeDomainWithSimpleSerialization() override;
|
||||
virtual ~DataTypeCustomSimpleTextSerialization() override;
|
||||
|
||||
// Methods that subclasses must override in order to get full serialization/deserialization support.
|
||||
virtual void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override = 0;
|
@ -1,5 +1,5 @@
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
#include <DataTypes/IDataTypeDomain.h>
|
||||
#include <DataTypes/DataTypeCustom.h>
|
||||
#include <Parsers/parseQuery.h>
|
||||
#include <Parsers/ParserCreateQuery.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
@ -115,19 +115,23 @@ void DataTypeFactory::registerSimpleDataType(const String & name, SimpleCreator
|
||||
}, case_sensitiveness);
|
||||
}
|
||||
|
||||
void DataTypeFactory::registerDataTypeDomain(const String & type_name, DataTypeDomainPtr domain, CaseSensitiveness case_sensitiveness)
|
||||
void DataTypeFactory::registerDataTypeCustom(const String & family_name, CreatorWithCustom creator, CaseSensitiveness case_sensitiveness)
|
||||
{
|
||||
all_domains.reserve(all_domains.size() + 1);
|
||||
|
||||
auto data_type = get(type_name);
|
||||
setDataTypeDomain(*data_type, *domain);
|
||||
|
||||
registerDataType(domain->getName(), [data_type](const ASTPtr & /*ast*/)
|
||||
registerDataType(family_name, [creator](const ASTPtr & ast)
|
||||
{
|
||||
return data_type;
|
||||
}, case_sensitiveness);
|
||||
auto res = creator(ast);
|
||||
res.first->setCustomization(std::move(res.second));
|
||||
|
||||
all_domains.emplace_back(std::move(domain));
|
||||
return res.first;
|
||||
}, case_sensitiveness);
|
||||
}
|
||||
|
||||
void DataTypeFactory::registerSimpleDataTypeCustom(const String &name, SimpleCreatorWithCustom creator, CaseSensitiveness case_sensitiveness)
|
||||
{
|
||||
registerDataTypeCustom(name, [creator](const ASTPtr & /*ast*/)
|
||||
{
|
||||
return creator();
|
||||
}, case_sensitiveness);
|
||||
}
|
||||
|
||||
const DataTypeFactory::Creator& DataTypeFactory::findCreatorByName(const String & family_name) const
|
||||
@ -153,11 +157,6 @@ const DataTypeFactory::Creator& DataTypeFactory::findCreatorByName(const String
|
||||
throw Exception("Unknown data type family: " + family_name, ErrorCodes::UNKNOWN_TYPE);
|
||||
}
|
||||
|
||||
void DataTypeFactory::setDataTypeDomain(const IDataType & data_type, const IDataTypeDomain & domain)
|
||||
{
|
||||
data_type.setDomain(&domain);
|
||||
}
|
||||
|
||||
void registerDataTypeNumbers(DataTypeFactory & factory);
|
||||
void registerDataTypeDecimal(DataTypeFactory & factory);
|
||||
void registerDataTypeDate(DataTypeFactory & factory);
|
||||
@ -175,6 +174,7 @@ void registerDataTypeNested(DataTypeFactory & factory);
|
||||
void registerDataTypeInterval(DataTypeFactory & factory);
|
||||
void registerDataTypeLowCardinality(DataTypeFactory & factory);
|
||||
void registerDataTypeDomainIPv4AndIPv6(DataTypeFactory & factory);
|
||||
void registerDataTypeDomainSimpleAggregateFunction(DataTypeFactory & factory);
|
||||
|
||||
|
||||
DataTypeFactory::DataTypeFactory()
|
||||
@ -196,6 +196,7 @@ DataTypeFactory::DataTypeFactory()
|
||||
registerDataTypeInterval(*this);
|
||||
registerDataTypeLowCardinality(*this);
|
||||
registerDataTypeDomainIPv4AndIPv6(*this);
|
||||
registerDataTypeDomainSimpleAggregateFunction(*this);
|
||||
}
|
||||
|
||||
DataTypeFactory::~DataTypeFactory()
|
||||
|
@ -17,9 +17,6 @@ namespace DB
|
||||
class IDataType;
|
||||
using DataTypePtr = std::shared_ptr<const IDataType>;
|
||||
|
||||
class IDataTypeDomain;
|
||||
using DataTypeDomainPtr = std::unique_ptr<const IDataTypeDomain>;
|
||||
|
||||
|
||||
/** Creates a data type by name of data type family and parameters.
|
||||
*/
|
||||
@ -28,6 +25,8 @@ class DataTypeFactory final : public ext::singleton<DataTypeFactory>, public IFa
|
||||
private:
|
||||
using SimpleCreator = std::function<DataTypePtr()>;
|
||||
using DataTypesDictionary = std::unordered_map<String, Creator>;
|
||||
using CreatorWithCustom = std::function<std::pair<DataTypePtr,DataTypeCustomDescPtr>(const ASTPtr & parameters)>;
|
||||
using SimpleCreatorWithCustom = std::function<std::pair<DataTypePtr,DataTypeCustomDescPtr>()>;
|
||||
|
||||
public:
|
||||
DataTypePtr get(const String & full_name) const;
|
||||
@ -40,11 +39,13 @@ public:
|
||||
/// Register a simple data type, that have no parameters.
|
||||
void registerSimpleDataType(const String & name, SimpleCreator creator, CaseSensitiveness case_sensitiveness = CaseSensitive);
|
||||
|
||||
// Register a domain - a refinement of existing type.
|
||||
void registerDataTypeDomain(const String & type_name, DataTypeDomainPtr domain, CaseSensitiveness case_sensitiveness = CaseSensitive);
|
||||
/// Register a customized type family
|
||||
void registerDataTypeCustom(const String & family_name, CreatorWithCustom creator, CaseSensitiveness case_sensitiveness = CaseSensitive);
|
||||
|
||||
/// Register a simple customized data type
|
||||
void registerSimpleDataTypeCustom(const String & name, SimpleCreatorWithCustom creator, CaseSensitiveness case_sensitiveness = CaseSensitive);
|
||||
|
||||
private:
|
||||
static void setDataTypeDomain(const IDataType & data_type, const IDataTypeDomain & domain);
|
||||
const Creator& findCreatorByName(const String & family_name) const;
|
||||
|
||||
private:
|
||||
@ -53,9 +54,6 @@ private:
|
||||
/// Case insensitive data types will be additionally added here with lowercased name.
|
||||
DataTypesDictionary case_insensitive_data_types;
|
||||
|
||||
// All domains are owned by factory and shared amongst DataType instances.
|
||||
std::vector<DataTypeDomainPtr> all_domains;
|
||||
|
||||
DataTypeFactory();
|
||||
~DataTypeFactory() override;
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
#include <DataTypes/IDataType.h>
|
||||
#include <DataTypes/IDataTypeDomain.h>
|
||||
#include <DataTypes/DataTypeCustom.h>
|
||||
#include <DataTypes/NestedUtils.h>
|
||||
|
||||
|
||||
@ -23,8 +23,7 @@ namespace ErrorCodes
|
||||
extern const int DATA_TYPE_CANNOT_BE_PROMOTED;
|
||||
}
|
||||
|
||||
IDataType::IDataType()
|
||||
: domain(nullptr)
|
||||
IDataType::IDataType() : custom_name(nullptr), custom_text_serialization(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@ -34,9 +33,9 @@ IDataType::~IDataType()
|
||||
|
||||
String IDataType::getName() const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_name)
|
||||
{
|
||||
return domain->getName();
|
||||
return custom_name->getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -142,9 +141,9 @@ void IDataType::insertDefaultInto(IColumn & column) const
|
||||
|
||||
void IDataType::serializeAsTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->serializeTextEscaped(column, row_num, ostr, settings);
|
||||
custom_text_serialization->serializeTextEscaped(column, row_num, ostr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -154,9 +153,9 @@ void IDataType::serializeAsTextEscaped(const IColumn & column, size_t row_num, W
|
||||
|
||||
void IDataType::deserializeAsTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->deserializeTextEscaped(column, istr, settings);
|
||||
custom_text_serialization->deserializeTextEscaped(column, istr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -166,9 +165,9 @@ void IDataType::deserializeAsTextEscaped(IColumn & column, ReadBuffer & istr, co
|
||||
|
||||
void IDataType::serializeAsTextQuoted(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->serializeTextQuoted(column, row_num, ostr, settings);
|
||||
custom_text_serialization->serializeTextQuoted(column, row_num, ostr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -178,9 +177,9 @@ void IDataType::serializeAsTextQuoted(const IColumn & column, size_t row_num, Wr
|
||||
|
||||
void IDataType::deserializeAsTextQuoted(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->deserializeTextQuoted(column, istr, settings);
|
||||
custom_text_serialization->deserializeTextQuoted(column, istr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -190,9 +189,9 @@ void IDataType::deserializeAsTextQuoted(IColumn & column, ReadBuffer & istr, con
|
||||
|
||||
void IDataType::serializeAsTextCSV(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->serializeTextCSV(column, row_num, ostr, settings);
|
||||
custom_text_serialization->serializeTextCSV(column, row_num, ostr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -202,9 +201,9 @@ void IDataType::serializeAsTextCSV(const IColumn & column, size_t row_num, Write
|
||||
|
||||
void IDataType::deserializeAsTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->deserializeTextCSV(column, istr, settings);
|
||||
custom_text_serialization->deserializeTextCSV(column, istr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -214,9 +213,9 @@ void IDataType::deserializeAsTextCSV(IColumn & column, ReadBuffer & istr, const
|
||||
|
||||
void IDataType::serializeAsText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->serializeText(column, row_num, ostr, settings);
|
||||
custom_text_serialization->serializeText(column, row_num, ostr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -226,9 +225,9 @@ void IDataType::serializeAsText(const IColumn & column, size_t row_num, WriteBuf
|
||||
|
||||
void IDataType::serializeAsTextJSON(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->serializeTextJSON(column, row_num, ostr, settings);
|
||||
custom_text_serialization->serializeTextJSON(column, row_num, ostr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -238,9 +237,9 @@ void IDataType::serializeAsTextJSON(const IColumn & column, size_t row_num, Writ
|
||||
|
||||
void IDataType::deserializeAsTextJSON(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->deserializeTextJSON(column, istr, settings);
|
||||
custom_text_serialization->deserializeTextJSON(column, istr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -250,9 +249,9 @@ void IDataType::deserializeAsTextJSON(IColumn & column, ReadBuffer & istr, const
|
||||
|
||||
void IDataType::serializeAsTextXML(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
if (domain)
|
||||
if (custom_text_serialization)
|
||||
{
|
||||
domain->serializeTextXML(column, row_num, ostr, settings);
|
||||
custom_text_serialization->serializeTextXML(column, row_num, ostr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -260,13 +259,14 @@ void IDataType::serializeAsTextXML(const IColumn & column, size_t row_num, Write
|
||||
}
|
||||
}
|
||||
|
||||
void IDataType::setDomain(const IDataTypeDomain* const new_domain) const
|
||||
void IDataType::setCustomization(DataTypeCustomDescPtr custom_desc_) const
|
||||
{
|
||||
if (domain != nullptr)
|
||||
{
|
||||
throw Exception("Type " + getName() + " already has a domain.", ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
domain = new_domain;
|
||||
/// replace only if not null
|
||||
if (custom_desc_->name)
|
||||
custom_name = std::move(custom_desc_->name);
|
||||
|
||||
if (custom_desc_->text_serialization)
|
||||
custom_text_serialization = std::move(custom_desc_->text_serialization);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <Common/COW.h>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <Core/Field.h>
|
||||
#include <DataTypes/DataTypeCustom.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -12,7 +13,6 @@ namespace DB
|
||||
class ReadBuffer;
|
||||
class WriteBuffer;
|
||||
|
||||
class IDataTypeDomain;
|
||||
class IDataType;
|
||||
struct FormatSettings;
|
||||
|
||||
@ -459,18 +459,19 @@ public:
|
||||
|
||||
private:
|
||||
friend class DataTypeFactory;
|
||||
/** Sets domain on existing DataType, can be considered as second phase
|
||||
* of construction explicitly done by DataTypeFactory.
|
||||
* Will throw an exception if domain is already set.
|
||||
/** Customize this DataType
|
||||
*/
|
||||
void setDomain(const IDataTypeDomain* newDomain) const;
|
||||
void setCustomization(DataTypeCustomDescPtr custom_desc_) const;
|
||||
|
||||
private:
|
||||
/** This is mutable to allow setting domain on `const IDataType` post construction,
|
||||
* simplifying creation of domains for all types, without them even knowing
|
||||
* of domain existence.
|
||||
/** This is mutable to allow setting custom name and serialization on `const IDataType` post construction.
|
||||
*/
|
||||
mutable IDataTypeDomain const* domain;
|
||||
mutable DataTypeCustomNamePtr custom_name;
|
||||
mutable DataTypeCustomTextSerializationPtr custom_text_serialization;
|
||||
|
||||
public:
|
||||
const IDataTypeCustomName * getCustomName() const { return custom_name.get(); }
|
||||
const IDataTypeCustomTextSerialization * getCustomTextSerialization() const { return custom_text_serialization.get(); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -23,7 +23,7 @@ endif()
|
||||
|
||||
if(USE_POCO_SQLODBC)
|
||||
target_link_libraries(clickhouse_dictionaries PRIVATE ${Poco_SQLODBC_LIBRARY} ${Poco_SQL_LIBRARY})
|
||||
target_include_directories(clickhouse_dictionaries SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_SQLODBC_INCLUDE_DIR} ${Poco_SQL_INCLUDE_DIR})
|
||||
target_include_directories(clickhouse_dictionaries SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_SQLODBC_INCLUDE_DIR} ${Poco_SQL_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(Poco_Data_FOUND)
|
||||
@ -32,7 +32,7 @@ endif()
|
||||
|
||||
if(USE_POCO_DATAODBC)
|
||||
target_link_libraries(clickhouse_dictionaries PRIVATE ${Poco_DataODBC_LIBRARY} ${Poco_Data_LIBRARY})
|
||||
target_include_directories(clickhouse_dictionaries SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_DataODBC_INCLUDE_DIR})
|
||||
target_include_directories(clickhouse_dictionaries SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_DataODBC_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(USE_POCO_MONGODB)
|
||||
|
@ -19,17 +19,7 @@ namespace ErrorCodes
|
||||
}
|
||||
|
||||
|
||||
CSVRowInputStream::CSVRowInputStream(ReadBuffer & istr_, const Block & header_, bool with_names_, const FormatSettings & format_settings)
|
||||
: istr(istr_), header(header_), with_names(with_names_), format_settings(format_settings)
|
||||
{
|
||||
size_t num_columns = header.columns();
|
||||
data_types.resize(num_columns);
|
||||
for (size_t i = 0; i < num_columns; ++i)
|
||||
data_types[i] = header.safeGetByPosition(i).type;
|
||||
}
|
||||
|
||||
|
||||
static void skipEndOfLine(ReadBuffer & istr)
|
||||
static inline void skipEndOfLine(ReadBuffer & istr)
|
||||
{
|
||||
/// \n (Unix) or \r\n (DOS/Windows) or \n\r (Mac OS Classic)
|
||||
|
||||
@ -53,7 +43,7 @@ static void skipEndOfLine(ReadBuffer & istr)
|
||||
}
|
||||
|
||||
|
||||
static void skipDelimiter(ReadBuffer & istr, const char delimiter, bool is_last_column)
|
||||
static inline void skipDelimiter(ReadBuffer & istr, const char delimiter, bool is_last_column)
|
||||
{
|
||||
if (is_last_column)
|
||||
{
|
||||
@ -99,37 +89,148 @@ static void skipRow(ReadBuffer & istr, const FormatSettings::CSV & settings, siz
|
||||
}
|
||||
|
||||
|
||||
CSVRowInputStream::CSVRowInputStream(ReadBuffer & istr_, const Block & header_, bool with_names_, const FormatSettings & format_settings)
|
||||
: istr(istr_), header(header_), with_names(with_names_), format_settings(format_settings)
|
||||
{
|
||||
const auto num_columns = header.columns();
|
||||
|
||||
data_types.resize(num_columns);
|
||||
column_indexes_by_names.reserve(num_columns);
|
||||
|
||||
for (size_t i = 0; i < num_columns; ++i)
|
||||
{
|
||||
const auto & column_info = header.getByPosition(i);
|
||||
|
||||
data_types[i] = column_info.type;
|
||||
column_indexes_by_names.emplace(column_info.name, i);
|
||||
}
|
||||
|
||||
column_indexes_for_input_fields.reserve(num_columns);
|
||||
read_columns.assign(num_columns, false);
|
||||
}
|
||||
|
||||
|
||||
void CSVRowInputStream::setupAllColumnsByTableSchema()
|
||||
{
|
||||
read_columns.assign(header.columns(), true);
|
||||
column_indexes_for_input_fields.resize(header.columns());
|
||||
|
||||
for (size_t i = 0; i < column_indexes_for_input_fields.size(); ++i)
|
||||
{
|
||||
column_indexes_for_input_fields[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSVRowInputStream::addInputColumn(const String & column_name)
|
||||
{
|
||||
const auto column_it = column_indexes_by_names.find(column_name);
|
||||
if (column_it == column_indexes_by_names.end())
|
||||
{
|
||||
if (format_settings.skip_unknown_fields)
|
||||
{
|
||||
column_indexes_for_input_fields.push_back(std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
"Unknown field found in CSV header: '" + column_name + "' " +
|
||||
"at position " + std::to_string(column_indexes_for_input_fields.size()) +
|
||||
"\nSet the 'input_format_skip_unknown_fields' parameter explicitly to ignore and proceed",
|
||||
ErrorCodes::INCORRECT_DATA
|
||||
);
|
||||
}
|
||||
|
||||
const auto column_index = column_it->second;
|
||||
|
||||
if (read_columns[column_index])
|
||||
throw Exception("Duplicate field found while parsing CSV header: " + column_name, ErrorCodes::INCORRECT_DATA);
|
||||
|
||||
read_columns[column_index] = true;
|
||||
column_indexes_for_input_fields.emplace_back(column_index);
|
||||
}
|
||||
|
||||
|
||||
void CSVRowInputStream::fillUnreadColumnsWithDefaults(MutableColumns & columns, RowReadExtension & row_read_extension)
|
||||
{
|
||||
/// It is safe to memorize this on the first run - the format guarantees this does not change
|
||||
if (unlikely(row_num == 1))
|
||||
{
|
||||
columns_to_fill_with_default_values.clear();
|
||||
for (size_t index = 0; index < read_columns.size(); ++index)
|
||||
if (read_columns[index] == 0)
|
||||
columns_to_fill_with_default_values.push_back(index);
|
||||
}
|
||||
|
||||
for (const auto column_index : columns_to_fill_with_default_values)
|
||||
data_types[column_index]->insertDefaultInto(*columns[column_index]);
|
||||
|
||||
row_read_extension.read_columns = read_columns;
|
||||
}
|
||||
|
||||
|
||||
void CSVRowInputStream::readPrefix()
|
||||
{
|
||||
/// In this format, we assume, that if first string field contain BOM as value, it will be written in quotes,
|
||||
/// so BOM at beginning of stream cannot be confused with BOM in first string value, and it is safe to skip it.
|
||||
skipBOMIfExists(istr);
|
||||
|
||||
size_t num_columns = data_types.size();
|
||||
String tmp;
|
||||
|
||||
if (with_names)
|
||||
skipRow(istr, format_settings.csv, num_columns);
|
||||
{
|
||||
if (format_settings.with_names_use_header)
|
||||
{
|
||||
String column_name;
|
||||
do
|
||||
{
|
||||
skipWhitespacesAndTabs(istr);
|
||||
readCSVString(column_name, istr, format_settings.csv);
|
||||
skipWhitespacesAndTabs(istr);
|
||||
|
||||
addInputColumn(column_name);
|
||||
}
|
||||
while (checkChar(format_settings.csv.delimiter, istr));
|
||||
|
||||
skipDelimiter(istr, format_settings.csv.delimiter, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
setupAllColumnsByTableSchema();
|
||||
skipRow(istr, format_settings.csv, column_indexes_for_input_fields.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setupAllColumnsByTableSchema();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CSVRowInputStream::read(MutableColumns & columns, RowReadExtension &)
|
||||
bool CSVRowInputStream::read(MutableColumns & columns, RowReadExtension & ext)
|
||||
{
|
||||
if (istr.eof())
|
||||
return false;
|
||||
|
||||
updateDiagnosticInfo();
|
||||
|
||||
size_t size = data_types.size();
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
String tmp;
|
||||
for (size_t input_position = 0; input_position < column_indexes_for_input_fields.size(); ++input_position)
|
||||
{
|
||||
const auto & column_index = column_indexes_for_input_fields[input_position];
|
||||
if (column_index)
|
||||
{
|
||||
skipWhitespacesAndTabs(istr);
|
||||
data_types[i]->deserializeAsTextCSV(*columns[i], istr, format_settings);
|
||||
data_types[*column_index]->deserializeAsTextCSV(*columns[*column_index], istr, format_settings);
|
||||
skipWhitespacesAndTabs(istr);
|
||||
|
||||
skipDelimiter(istr, format_settings.csv.delimiter, i + 1 == size);
|
||||
}
|
||||
else
|
||||
{
|
||||
readCSVString(tmp, istr, format_settings.csv);
|
||||
}
|
||||
|
||||
skipDelimiter(istr, format_settings.csv.delimiter, input_position + 1 == column_indexes_for_input_fields.size());
|
||||
}
|
||||
|
||||
fillUnreadColumnsWithDefaults(columns, ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -202,18 +303,22 @@ bool OPTIMIZE(1) CSVRowInputStream::parseRowAndPrintDiagnosticInfo(MutableColumn
|
||||
{
|
||||
const char delimiter = format_settings.csv.delimiter;
|
||||
|
||||
size_t size = data_types.size();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
for (size_t input_position = 0; input_position < column_indexes_for_input_fields.size(); ++input_position)
|
||||
{
|
||||
if (i == 0 && istr.eof())
|
||||
if (input_position == 0 && istr.eof())
|
||||
{
|
||||
out << "<End of stream>\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
out << "Column " << i << ", " << std::string((i < 10 ? 2 : i < 100 ? 1 : 0), ' ')
|
||||
<< "name: " << header.safeGetByPosition(i).name << ", " << std::string(max_length_of_column_name - header.safeGetByPosition(i).name.size(), ' ')
|
||||
<< "type: " << data_types[i]->getName() << ", " << std::string(max_length_of_data_type_name - data_types[i]->getName().size(), ' ');
|
||||
if (column_indexes_for_input_fields[input_position].has_value())
|
||||
{
|
||||
const auto & column_index = *column_indexes_for_input_fields[input_position];
|
||||
const auto & current_column_type = data_types[column_index];
|
||||
|
||||
out << "Column " << input_position << ", " << std::string((input_position < 10 ? 2 : input_position < 100 ? 1 : 0), ' ')
|
||||
<< "name: " << header.safeGetByPosition(column_index).name << ", " << std::string(max_length_of_column_name - header.safeGetByPosition(column_index).name.size(), ' ')
|
||||
<< "type: " << current_column_type->getName() << ", " << std::string(max_length_of_data_type_name - current_column_type->getName().size(), ' ');
|
||||
|
||||
BufferBase::Position prev_position = istr.position();
|
||||
BufferBase::Position curr_position = istr.position();
|
||||
@ -223,7 +328,7 @@ bool OPTIMIZE(1) CSVRowInputStream::parseRowAndPrintDiagnosticInfo(MutableColumn
|
||||
{
|
||||
skipWhitespacesAndTabs(istr);
|
||||
prev_position = istr.position();
|
||||
data_types[i]->deserializeAsTextCSV(*columns[i], istr, format_settings);
|
||||
current_column_type->deserializeAsTextCSV(*columns[column_index], istr, format_settings);
|
||||
curr_position = istr.position();
|
||||
skipWhitespacesAndTabs(istr);
|
||||
}
|
||||
@ -235,14 +340,14 @@ bool OPTIMIZE(1) CSVRowInputStream::parseRowAndPrintDiagnosticInfo(MutableColumn
|
||||
if (curr_position < prev_position)
|
||||
throw Exception("Logical error: parsing is non-deterministic.", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
if (isNumber(data_types[i]) || isDateOrDateTime(data_types[i]))
|
||||
if (isNumber(current_column_type) || isDateOrDateTime(current_column_type))
|
||||
{
|
||||
/// An empty string instead of a value.
|
||||
if (curr_position == prev_position)
|
||||
{
|
||||
out << "ERROR: text ";
|
||||
verbosePrintString(prev_position, std::min(prev_position + 10, istr.buffer().end()), out);
|
||||
out << " is not like " << data_types[i]->getName() << "\n";
|
||||
out << " is not like " << current_column_type->getName() << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -252,9 +357,9 @@ bool OPTIMIZE(1) CSVRowInputStream::parseRowAndPrintDiagnosticInfo(MutableColumn
|
||||
|
||||
if (exception)
|
||||
{
|
||||
if (data_types[i]->getName() == "DateTime")
|
||||
if (current_column_type->getName() == "DateTime")
|
||||
out << "ERROR: DateTime must be in YYYY-MM-DD hh:mm:ss or NNNNNNNNNN (unix timestamp, exactly 10 digits) format.\n";
|
||||
else if (data_types[i]->getName() == "Date")
|
||||
else if (current_column_type->getName() == "Date")
|
||||
out << "ERROR: Date must be in YYYY-MM-DD format.\n";
|
||||
else
|
||||
out << "ERROR\n";
|
||||
@ -263,25 +368,36 @@ bool OPTIMIZE(1) CSVRowInputStream::parseRowAndPrintDiagnosticInfo(MutableColumn
|
||||
|
||||
out << "\n";
|
||||
|
||||
if (data_types[i]->haveMaximumSizeOfValue())
|
||||
if (current_column_type->haveMaximumSizeOfValue())
|
||||
{
|
||||
if (*curr_position != '\n' && *curr_position != '\r' && *curr_position != delimiter)
|
||||
{
|
||||
out << "ERROR: garbage after " << data_types[i]->getName() << ": ";
|
||||
out << "ERROR: garbage after " << current_column_type->getName() << ": ";
|
||||
verbosePrintString(curr_position, std::min(curr_position + 10, istr.buffer().end()), out);
|
||||
out << "\n";
|
||||
|
||||
if (data_types[i]->getName() == "DateTime")
|
||||
if (current_column_type->getName() == "DateTime")
|
||||
out << "ERROR: DateTime must be in YYYY-MM-DD hh:mm:ss or NNNNNNNNNN (unix timestamp, exactly 10 digits) format.\n";
|
||||
else if (data_types[i]->getName() == "Date")
|
||||
else if (current_column_type->getName() == "Date")
|
||||
out << "ERROR: Date must be in YYYY-MM-DD format.\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static const String skipped_column_str = "<SKIPPED COLUMN>";
|
||||
out << "Column " << input_position << ", " << std::string((input_position < 10 ? 2 : input_position < 100 ? 1 : 0), ' ')
|
||||
<< "name: " << skipped_column_str << ", " << std::string(max_length_of_column_name - skipped_column_str.length(), ' ')
|
||||
<< "type: " << skipped_column_str << ", " << std::string(max_length_of_data_type_name - skipped_column_str.length(), ' ');
|
||||
|
||||
String tmp;
|
||||
readCSVString(tmp, istr, format_settings.csv);
|
||||
}
|
||||
|
||||
/// Delimiters
|
||||
if (i + 1 == size)
|
||||
if (input_position + 1 == column_indexes_for_input_fields.size())
|
||||
{
|
||||
if (istr.eof())
|
||||
return false;
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <Core/Block.h>
|
||||
#include <Formats/IRowInputStream.h>
|
||||
#include <Formats/FormatSettings.h>
|
||||
@ -21,7 +24,7 @@ public:
|
||||
*/
|
||||
CSVRowInputStream(ReadBuffer & istr_, const Block & header_, bool with_names_, const FormatSettings & format_settings);
|
||||
|
||||
bool read(MutableColumns & columns, RowReadExtension &) override;
|
||||
bool read(MutableColumns & columns, RowReadExtension & ext) override;
|
||||
void readPrefix() override;
|
||||
bool allowSyncAfterError() const override { return true; }
|
||||
void syncAfterError() override;
|
||||
@ -36,6 +39,19 @@ private:
|
||||
|
||||
const FormatSettings format_settings;
|
||||
|
||||
using IndexesMap = std::unordered_map<String, size_t>;
|
||||
IndexesMap column_indexes_by_names;
|
||||
|
||||
using OptionalIndexes = std::vector<std::optional<size_t>>;
|
||||
OptionalIndexes column_indexes_for_input_fields;
|
||||
|
||||
std::vector<UInt8> read_columns;
|
||||
std::vector<size_t> columns_to_fill_with_default_values;
|
||||
|
||||
void addInputColumn(const String & column_name);
|
||||
void setupAllColumnsByTableSchema();
|
||||
void fillUnreadColumnsWithDefaults(MutableColumns & columns, RowReadExtension& ext);
|
||||
|
||||
/// For convenient diagnostics in case of an error.
|
||||
|
||||
size_t row_num = 0;
|
||||
|
@ -40,6 +40,7 @@ BlockInputStreamPtr FormatFactory::getInput(const String & name, ReadBuffer & bu
|
||||
format_settings.csv.allow_single_quotes = settings.format_csv_allow_single_quotes;
|
||||
format_settings.csv.allow_double_quotes = settings.format_csv_allow_double_quotes;
|
||||
format_settings.values.interpret_expressions = settings.input_format_values_interpret_expressions;
|
||||
format_settings.with_names_use_header = settings.input_format_with_names_use_header;
|
||||
format_settings.skip_unknown_fields = settings.input_format_skip_unknown_fields;
|
||||
format_settings.import_nested_json = settings.input_format_import_nested_json;
|
||||
format_settings.date_time_input_format = settings.date_time_input_format;
|
||||
|
@ -48,6 +48,7 @@ struct FormatSettings
|
||||
Values values;
|
||||
|
||||
bool skip_unknown_fields = false;
|
||||
bool with_names_use_header = false;
|
||||
bool write_statistics = true;
|
||||
bool import_nested_json = false;
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <string>
|
||||
|
||||
#include <Core/Defines.h>
|
||||
|
||||
#include <IO/ReadHelpers.h>
|
||||
@ -20,47 +22,15 @@ namespace ErrorCodes
|
||||
}
|
||||
|
||||
|
||||
TabSeparatedRowInputStream::TabSeparatedRowInputStream(
|
||||
ReadBuffer & istr_, const Block & header_, bool with_names_, bool with_types_, const FormatSettings & format_settings)
|
||||
: istr(istr_), header(header_), with_names(with_names_), with_types(with_types_), format_settings(format_settings)
|
||||
static void skipTSVRow(ReadBuffer & istr, const size_t num_columns)
|
||||
{
|
||||
size_t num_columns = header.columns();
|
||||
data_types.resize(num_columns);
|
||||
for (size_t i = 0; i < num_columns; ++i)
|
||||
data_types[i] = header.safeGetByPosition(i).type;
|
||||
}
|
||||
NullSink null_sink;
|
||||
|
||||
|
||||
void TabSeparatedRowInputStream::readPrefix()
|
||||
{
|
||||
size_t num_columns = header.columns();
|
||||
String tmp;
|
||||
|
||||
if (with_names || with_types)
|
||||
{
|
||||
/// In this format, we assume that column name or type cannot contain BOM,
|
||||
/// so, if format has header,
|
||||
/// then BOM at beginning of stream cannot be confused with name or type of field, and it is safe to skip it.
|
||||
skipBOMIfExists(istr);
|
||||
}
|
||||
|
||||
if (with_names)
|
||||
{
|
||||
for (size_t i = 0; i < num_columns; ++i)
|
||||
{
|
||||
readEscapedString(tmp, istr);
|
||||
readEscapedStringInto(null_sink, istr);
|
||||
assertChar(i == num_columns - 1 ? '\n' : '\t', istr);
|
||||
}
|
||||
}
|
||||
|
||||
if (with_types)
|
||||
{
|
||||
for (size_t i = 0; i < num_columns; ++i)
|
||||
{
|
||||
readEscapedString(tmp, istr);
|
||||
assertChar(i == num_columns - 1 ? '\n' : '\t', istr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -77,23 +47,155 @@ static void checkForCarriageReturn(ReadBuffer & istr)
|
||||
}
|
||||
|
||||
|
||||
bool TabSeparatedRowInputStream::read(MutableColumns & columns, RowReadExtension &)
|
||||
TabSeparatedRowInputStream::TabSeparatedRowInputStream(
|
||||
ReadBuffer & istr_, const Block & header_, bool with_names_, bool with_types_, const FormatSettings & format_settings)
|
||||
: istr(istr_), header(header_), with_names(with_names_), with_types(with_types_), format_settings(format_settings)
|
||||
{
|
||||
const auto num_columns = header.columns();
|
||||
|
||||
data_types.resize(num_columns);
|
||||
column_indexes_by_names.reserve(num_columns);
|
||||
|
||||
for (size_t i = 0; i < num_columns; ++i)
|
||||
{
|
||||
const auto & column_info = header.getByPosition(i);
|
||||
|
||||
data_types[i] = column_info.type;
|
||||
column_indexes_by_names.emplace(column_info.name, i);
|
||||
}
|
||||
|
||||
column_indexes_for_input_fields.reserve(num_columns);
|
||||
read_columns.assign(num_columns, false);
|
||||
}
|
||||
|
||||
|
||||
void TabSeparatedRowInputStream::setupAllColumnsByTableSchema()
|
||||
{
|
||||
read_columns.assign(header.columns(), true);
|
||||
column_indexes_for_input_fields.resize(header.columns());
|
||||
|
||||
for (size_t i = 0; i < column_indexes_for_input_fields.size(); ++i)
|
||||
column_indexes_for_input_fields[i] = i;
|
||||
}
|
||||
|
||||
|
||||
void TabSeparatedRowInputStream::addInputColumn(const String & column_name)
|
||||
{
|
||||
const auto column_it = column_indexes_by_names.find(column_name);
|
||||
if (column_it == column_indexes_by_names.end())
|
||||
{
|
||||
if (format_settings.skip_unknown_fields)
|
||||
{
|
||||
column_indexes_for_input_fields.push_back(std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
"Unknown field found in TSV header: '" + column_name + "' " +
|
||||
"at position " + std::to_string(column_indexes_for_input_fields.size()) +
|
||||
"\nSet the 'input_format_skip_unknown_fields' parameter explicitly to ignore and proceed",
|
||||
ErrorCodes::INCORRECT_DATA
|
||||
);
|
||||
}
|
||||
|
||||
const auto column_index = column_it->second;
|
||||
|
||||
if (read_columns[column_index])
|
||||
throw Exception("Duplicate field found while parsing TSV header: " + column_name, ErrorCodes::INCORRECT_DATA);
|
||||
|
||||
read_columns[column_index] = true;
|
||||
column_indexes_for_input_fields.emplace_back(column_index);
|
||||
}
|
||||
|
||||
|
||||
void TabSeparatedRowInputStream::fillUnreadColumnsWithDefaults(MutableColumns & columns, RowReadExtension & row_read_extension)
|
||||
{
|
||||
/// It is safe to memorize this on the first run - the format guarantees this does not change
|
||||
if (unlikely(row_num == 1))
|
||||
{
|
||||
columns_to_fill_with_default_values.clear();
|
||||
for (size_t index = 0; index < read_columns.size(); ++index)
|
||||
if (read_columns[index] == 0)
|
||||
columns_to_fill_with_default_values.push_back(index);
|
||||
}
|
||||
|
||||
for (const auto column_index : columns_to_fill_with_default_values)
|
||||
data_types[column_index]->insertDefaultInto(*columns[column_index]);
|
||||
|
||||
row_read_extension.read_columns = read_columns;
|
||||
}
|
||||
|
||||
|
||||
void TabSeparatedRowInputStream::readPrefix()
|
||||
{
|
||||
if (with_names || with_types)
|
||||
{
|
||||
/// In this format, we assume that column name or type cannot contain BOM,
|
||||
/// so, if format has header,
|
||||
/// then BOM at beginning of stream cannot be confused with name or type of field, and it is safe to skip it.
|
||||
skipBOMIfExists(istr);
|
||||
}
|
||||
|
||||
if (with_names)
|
||||
{
|
||||
if (format_settings.with_names_use_header)
|
||||
{
|
||||
String column_name;
|
||||
do
|
||||
{
|
||||
readEscapedString(column_name, istr);
|
||||
addInputColumn(column_name);
|
||||
}
|
||||
while (checkChar('\t', istr));
|
||||
|
||||
if (!istr.eof())
|
||||
{
|
||||
checkForCarriageReturn(istr);
|
||||
assertChar('\n', istr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setupAllColumnsByTableSchema();
|
||||
skipTSVRow(istr, column_indexes_for_input_fields.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
setupAllColumnsByTableSchema();
|
||||
|
||||
if (with_types)
|
||||
{
|
||||
skipTSVRow(istr, column_indexes_for_input_fields.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TabSeparatedRowInputStream::read(MutableColumns & columns, RowReadExtension & ext)
|
||||
{
|
||||
if (istr.eof())
|
||||
return false;
|
||||
|
||||
updateDiagnosticInfo();
|
||||
|
||||
size_t size = data_types.size();
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
for (size_t input_position = 0; input_position < column_indexes_for_input_fields.size(); ++input_position)
|
||||
{
|
||||
data_types[i]->deserializeAsTextEscaped(*columns[i], istr, format_settings);
|
||||
const auto & column_index = column_indexes_for_input_fields[input_position];
|
||||
if (column_index)
|
||||
{
|
||||
data_types[*column_index]->deserializeAsTextEscaped(*columns[*column_index], istr, format_settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
NullSink null_sink;
|
||||
readEscapedStringInto(null_sink, istr);
|
||||
}
|
||||
|
||||
/// skip separators
|
||||
if (i + 1 == size)
|
||||
if (input_position + 1 < column_indexes_for_input_fields.size())
|
||||
{
|
||||
if (!istr.eof())
|
||||
assertChar('\t', istr);
|
||||
}
|
||||
else if (!istr.eof())
|
||||
{
|
||||
if (unlikely(row_num == 1))
|
||||
checkForCarriageReturn(istr);
|
||||
@ -101,9 +203,8 @@ bool TabSeparatedRowInputStream::read(MutableColumns & columns, RowReadExtension
|
||||
assertChar('\n', istr);
|
||||
}
|
||||
}
|
||||
else
|
||||
assertChar('\t', istr);
|
||||
}
|
||||
|
||||
fillUnreadColumnsWithDefaults(columns, ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -135,7 +236,7 @@ String TabSeparatedRowInputStream::getDiagnosticInfo()
|
||||
if (header.safeGetByPosition(i).type->getName().size() > max_length_of_data_type_name)
|
||||
max_length_of_data_type_name = header.safeGetByPosition(i).type->getName().size();
|
||||
|
||||
/// Roll back the cursor to the beginning of the previous or current line and pars all over again. But now we derive detailed information.
|
||||
/// Roll back the cursor to the beginning of the previous or current line and parse all over again. But now we derive detailed information.
|
||||
|
||||
if (pos_of_prev_row)
|
||||
{
|
||||
@ -173,25 +274,29 @@ String TabSeparatedRowInputStream::getDiagnosticInfo()
|
||||
bool OPTIMIZE(1) TabSeparatedRowInputStream::parseRowAndPrintDiagnosticInfo(
|
||||
MutableColumns & columns, WriteBuffer & out, size_t max_length_of_column_name, size_t max_length_of_data_type_name)
|
||||
{
|
||||
size_t size = data_types.size();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
for (size_t input_position = 0; input_position < column_indexes_for_input_fields.size(); ++input_position)
|
||||
{
|
||||
if (i == 0 && istr.eof())
|
||||
if (input_position == 0 && istr.eof())
|
||||
{
|
||||
out << "<End of stream>\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
out << "Column " << i << ", " << std::string((i < 10 ? 2 : i < 100 ? 1 : 0), ' ')
|
||||
<< "name: " << header.safeGetByPosition(i).name << ", " << std::string(max_length_of_column_name - header.safeGetByPosition(i).name.size(), ' ')
|
||||
<< "type: " << data_types[i]->getName() << ", " << std::string(max_length_of_data_type_name - data_types[i]->getName().size(), ' ');
|
||||
if (column_indexes_for_input_fields[input_position].has_value())
|
||||
{
|
||||
const auto & column_index = *column_indexes_for_input_fields[input_position];
|
||||
const auto & current_column_type = data_types[column_index];
|
||||
|
||||
out << "Column " << input_position << ", " << std::string((input_position < 10 ? 2 : input_position < 100 ? 1 : 0), ' ')
|
||||
<< "name: " << header.safeGetByPosition(column_index).name << ", " << std::string(max_length_of_column_name - header.safeGetByPosition(column_index).name.size(), ' ')
|
||||
<< "type: " << current_column_type->getName() << ", " << std::string(max_length_of_data_type_name - current_column_type->getName().size(), ' ');
|
||||
|
||||
auto prev_position = istr.position();
|
||||
std::exception_ptr exception;
|
||||
|
||||
try
|
||||
{
|
||||
data_types[i]->deserializeAsTextEscaped(*columns[i], istr, format_settings);
|
||||
current_column_type->deserializeAsTextEscaped(*columns[column_index], istr, format_settings);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -203,14 +308,14 @@ bool OPTIMIZE(1) TabSeparatedRowInputStream::parseRowAndPrintDiagnosticInfo(
|
||||
if (curr_position < prev_position)
|
||||
throw Exception("Logical error: parsing is non-deterministic.", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
if (isNumber(data_types[i]) || isDateOrDateTime(data_types[i]))
|
||||
if (isNumber(current_column_type) || isDateOrDateTime(current_column_type))
|
||||
{
|
||||
/// An empty string instead of a value.
|
||||
if (curr_position == prev_position)
|
||||
{
|
||||
out << "ERROR: text ";
|
||||
verbosePrintString(prev_position, std::min(prev_position + 10, istr.buffer().end()), out);
|
||||
out << " is not like " << data_types[i]->getName() << "\n";
|
||||
out << " is not like " << current_column_type->getName() << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -220,9 +325,9 @@ bool OPTIMIZE(1) TabSeparatedRowInputStream::parseRowAndPrintDiagnosticInfo(
|
||||
|
||||
if (exception)
|
||||
{
|
||||
if (data_types[i]->getName() == "DateTime")
|
||||
if (current_column_type->getName() == "DateTime")
|
||||
out << "ERROR: DateTime must be in YYYY-MM-DD hh:mm:ss or NNNNNNNNNN (unix timestamp, exactly 10 digits) format.\n";
|
||||
else if (data_types[i]->getName() == "Date")
|
||||
else if (current_column_type->getName() == "Date")
|
||||
out << "ERROR: Date must be in YYYY-MM-DD format.\n";
|
||||
else
|
||||
out << "ERROR\n";
|
||||
@ -231,25 +336,36 @@ bool OPTIMIZE(1) TabSeparatedRowInputStream::parseRowAndPrintDiagnosticInfo(
|
||||
|
||||
out << "\n";
|
||||
|
||||
if (data_types[i]->haveMaximumSizeOfValue())
|
||||
if (current_column_type->haveMaximumSizeOfValue())
|
||||
{
|
||||
if (*curr_position != '\n' && *curr_position != '\t')
|
||||
{
|
||||
out << "ERROR: garbage after " << data_types[i]->getName() << ": ";
|
||||
out << "ERROR: garbage after " << current_column_type->getName() << ": ";
|
||||
verbosePrintString(curr_position, std::min(curr_position + 10, istr.buffer().end()), out);
|
||||
out << "\n";
|
||||
|
||||
if (data_types[i]->getName() == "DateTime")
|
||||
if (current_column_type->getName() == "DateTime")
|
||||
out << "ERROR: DateTime must be in YYYY-MM-DD hh:mm:ss or NNNNNNNNNN (unix timestamp, exactly 10 digits) format.\n";
|
||||
else if (data_types[i]->getName() == "Date")
|
||||
else if (current_column_type->getName() == "Date")
|
||||
out << "ERROR: Date must be in YYYY-MM-DD format.\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static const String skipped_column_str = "<SKIPPED COLUMN>";
|
||||
out << "Column " << input_position << ", " << std::string((input_position < 10 ? 2 : input_position < 100 ? 1 : 0), ' ')
|
||||
<< "name: " << skipped_column_str << ", " << std::string(max_length_of_column_name - skipped_column_str.length(), ' ')
|
||||
<< "type: " << skipped_column_str << ", " << std::string(max_length_of_data_type_name - skipped_column_str.length(), ' ');
|
||||
|
||||
NullSink null_sink;
|
||||
readEscapedStringInto(null_sink, istr);
|
||||
}
|
||||
|
||||
/// Delimiters
|
||||
if (i + 1 == size)
|
||||
if (input_position + 1 == column_indexes_for_input_fields.size())
|
||||
{
|
||||
if (!istr.eof())
|
||||
{
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <Core/Block.h>
|
||||
#include <Formats/FormatSettings.h>
|
||||
#include <Formats/IRowInputStream.h>
|
||||
@ -22,7 +25,7 @@ public:
|
||||
TabSeparatedRowInputStream(
|
||||
ReadBuffer & istr_, const Block & header_, bool with_names_, bool with_types_, const FormatSettings & format_settings);
|
||||
|
||||
bool read(MutableColumns & columns, RowReadExtension &) override;
|
||||
bool read(MutableColumns & columns, RowReadExtension & ext) override;
|
||||
void readPrefix() override;
|
||||
bool allowSyncAfterError() const override { return true; }
|
||||
void syncAfterError() override;
|
||||
@ -37,6 +40,19 @@ private:
|
||||
const FormatSettings format_settings;
|
||||
DataTypes data_types;
|
||||
|
||||
using IndexesMap = std::unordered_map<String, size_t>;
|
||||
IndexesMap column_indexes_by_names;
|
||||
|
||||
using OptionalIndexes = std::vector<std::optional<size_t>>;
|
||||
OptionalIndexes column_indexes_for_input_fields;
|
||||
|
||||
std::vector<UInt8> read_columns;
|
||||
std::vector<size_t> columns_to_fill_with_default_values;
|
||||
|
||||
void addInputColumn(const String & column_name);
|
||||
void setupAllColumnsByTableSchema();
|
||||
void fillUnreadColumnsWithDefaults(MutableColumns & columns, RowReadExtension& ext);
|
||||
|
||||
/// For convenient diagnostics in case of an error.
|
||||
|
||||
size_t row_num = 0;
|
||||
|
@ -69,3 +69,8 @@ if (USE_HYPERSCAN)
|
||||
target_link_libraries (clickhouse_functions PRIVATE ${HYPERSCAN_LIBRARY})
|
||||
target_include_directories (clickhouse_functions SYSTEM PRIVATE ${HYPERSCAN_INCLUDE_DIR})
|
||||
endif ()
|
||||
|
||||
if (USE_SIMDJSON)
|
||||
target_link_libraries(clickhouse_functions PRIVATE ${SIMDJSON_LIBRARY})
|
||||
target_include_directories(clickhouse_functions PRIVATE ${SIMDJSON_INCLUDE_DIR})
|
||||
endif ()
|
||||
|
@ -9,7 +9,7 @@ using StoragePtr = std::shared_ptr<IStorage>;
|
||||
class Join;
|
||||
using JoinPtr = std::shared_ptr<Join>;
|
||||
|
||||
class FunctionJoinGet final : public IFunction, public std::enable_shared_from_this<FunctionJoinGet>
|
||||
class FunctionJoinGet final : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "joinGet";
|
||||
|
@ -406,7 +406,12 @@ private:
|
||||
{
|
||||
const ColumnAggregateFunction * columns[2];
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
if (auto argument_column_const = typeid_cast<const ColumnConst *>(block.getByPosition(arguments[i]).column.get()))
|
||||
columns[i] = typeid_cast<const ColumnAggregateFunction *>(argument_column_const->getDataColumnPtr().get());
|
||||
else
|
||||
columns[i] = typeid_cast<const ColumnAggregateFunction *>(block.getByPosition(arguments[i]).column.get());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < input_rows_count; ++i)
|
||||
{
|
||||
@ -511,7 +516,12 @@ private:
|
||||
{
|
||||
const ColumnAggregateFunction * columns[2];
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
if (auto argument_column_const = typeid_cast<const ColumnConst *>(block.getByPosition(arguments[i]).column.get()))
|
||||
columns[i] = typeid_cast<const ColumnAggregateFunction *>(argument_column_const->getDataColumnPtr().get());
|
||||
else
|
||||
columns[i] = typeid_cast<const ColumnAggregateFunction *>(block.getByPosition(arguments[i]).column.get());
|
||||
}
|
||||
|
||||
auto col_to = ColumnAggregateFunction::create(columns[0]->getAggregateFunction());
|
||||
|
||||
|
@ -28,6 +28,8 @@ void registerFunctionsCoding(FunctionFactory & factory)
|
||||
factory.registerFunction<FunctionBitmaskToArray>();
|
||||
factory.registerFunction<FunctionToIPv4>();
|
||||
factory.registerFunction<FunctionToIPv6>();
|
||||
factory.registerFunction<FunctionIPv6CIDRToRange>();
|
||||
factory.registerFunction<FunctionIPv4CIDRToRange>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,11 +12,13 @@
|
||||
#include <DataTypes/DataTypeDate.h>
|
||||
#include <DataTypes/DataTypeDateTime.h>
|
||||
#include <DataTypes/DataTypeUUID.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnFixedString.h>
|
||||
#include <Columns/ColumnArray.h>
|
||||
#include <Columns/ColumnConst.h>
|
||||
#include <Columns/ColumnTuple.h>
|
||||
#include <Functions/IFunction.h>
|
||||
#include <Functions/FunctionHelpers.h>
|
||||
|
||||
@ -1450,4 +1452,208 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class FunctionIPv6CIDRToRange : public IFunction
|
||||
{
|
||||
private:
|
||||
/// TODO Inefficient.
|
||||
/// NOTE IPv6 is stored in memory in big endian format that makes some difficulties.
|
||||
static void applyCIDRMask(const UInt8 * __restrict src, UInt8 * __restrict dst_lower, UInt8 * __restrict dst_upper, UInt8 bits_to_keep)
|
||||
{
|
||||
UInt8 mask[16]{};
|
||||
|
||||
UInt8 bytes_to_keep = bits_to_keep / 8;
|
||||
UInt8 bits_to_keep_in_last_byte = bits_to_keep % 8;
|
||||
|
||||
for (size_t i = 0; i < bits_to_keep / 8; ++i)
|
||||
mask[i] = 0xFFU;
|
||||
|
||||
if (bits_to_keep_in_last_byte)
|
||||
mask[bytes_to_keep] = 0xFFU << (8 - bits_to_keep_in_last_byte);
|
||||
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
{
|
||||
dst_lower[i] = src[i] & mask[i];
|
||||
dst_upper[i] = dst_lower[i] | ~mask[i];
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr auto name = "IPv6CIDRToRange";
|
||||
static FunctionPtr create(const Context &) { return std::make_shared<FunctionIPv6CIDRToRange>(); }
|
||||
|
||||
String getName() const override { return name; }
|
||||
size_t getNumberOfArguments() const override { return 2; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||
{
|
||||
const auto first_argument = checkAndGetDataType<DataTypeFixedString>(arguments[0].get());
|
||||
if (!first_argument || first_argument->getN() != IPV6_BINARY_LENGTH)
|
||||
throw Exception("Illegal type " + arguments[0]->getName() +
|
||||
" of first argument of function " + getName() +
|
||||
", expected FixedString(" + toString(IPV6_BINARY_LENGTH) + ")",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
const DataTypePtr & second_argument = arguments[1];
|
||||
if (!isUInt8(second_argument))
|
||||
throw Exception{"Illegal type " + second_argument->getName()
|
||||
+ " of second argument of function " + getName()
|
||||
+ ", expected numeric type.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
DataTypePtr element = DataTypeFactory::instance().get("IPv6");
|
||||
return std::make_shared<DataTypeTuple>(DataTypes{element, element});
|
||||
}
|
||||
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
|
||||
|
||||
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
||||
{
|
||||
const auto & col_type_name_ip = block.getByPosition(arguments[0]);
|
||||
const ColumnPtr & column_ip = col_type_name_ip.column;
|
||||
|
||||
const auto col_ip_in = checkAndGetColumn<ColumnFixedString>(column_ip.get());
|
||||
|
||||
if (!col_ip_in)
|
||||
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
|
||||
+ " of argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
if (col_ip_in->getN() != IPV6_BINARY_LENGTH)
|
||||
throw Exception("Illegal type " + col_type_name_ip.type->getName() +
|
||||
" of column " + col_ip_in->getName() +
|
||||
" argument of function " + getName() +
|
||||
", expected FixedString(" + toString(IPV6_BINARY_LENGTH) + ")",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
const auto & col_type_name_cidr = block.getByPosition(arguments[1]);
|
||||
const ColumnPtr & column_cidr = col_type_name_cidr.column;
|
||||
|
||||
const auto col_const_cidr_in = checkAndGetColumnConst<ColumnUInt8>(column_cidr.get());
|
||||
const auto col_cidr_in = checkAndGetColumn<ColumnUInt8>(column_cidr.get());
|
||||
|
||||
if (!col_const_cidr_in && !col_cidr_in)
|
||||
throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName()
|
||||
+ " of argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
const auto & vec_in = col_ip_in->getChars();
|
||||
|
||||
auto col_res_lower_range = ColumnFixedString::create(IPV6_BINARY_LENGTH);
|
||||
auto col_res_upper_range = ColumnFixedString::create(IPV6_BINARY_LENGTH);
|
||||
|
||||
ColumnString::Chars & vec_res_lower_range = col_res_lower_range->getChars();
|
||||
vec_res_lower_range.resize(input_rows_count * IPV6_BINARY_LENGTH);
|
||||
|
||||
ColumnString::Chars & vec_res_upper_range = col_res_upper_range->getChars();
|
||||
vec_res_upper_range.resize(input_rows_count * IPV6_BINARY_LENGTH);
|
||||
|
||||
for (size_t offset = 0; offset < input_rows_count; ++offset)
|
||||
{
|
||||
const size_t offset_ipv6 = offset * IPV6_BINARY_LENGTH;
|
||||
UInt8 cidr = col_const_cidr_in
|
||||
? col_const_cidr_in->getValue<UInt8>()
|
||||
: col_cidr_in->getData()[offset];
|
||||
|
||||
applyCIDRMask(&vec_in[offset_ipv6], &vec_res_lower_range[offset_ipv6], &vec_res_upper_range[offset_ipv6], cidr);
|
||||
}
|
||||
|
||||
block.getByPosition(result).column = ColumnTuple::create(Columns{std::move(col_res_lower_range), std::move(col_res_upper_range)});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class FunctionIPv4CIDRToRange : public IFunction
|
||||
{
|
||||
private:
|
||||
static inline std::pair<UInt32, UInt32> applyCIDRMask(UInt32 src, UInt8 bits_to_keep)
|
||||
{
|
||||
if (bits_to_keep >= 8 * sizeof(UInt32))
|
||||
return { src, src };
|
||||
if (bits_to_keep == 0)
|
||||
return { UInt32(0), UInt32(-1) };
|
||||
|
||||
UInt32 mask = UInt32(-1) << (8 * sizeof(UInt32) - bits_to_keep);
|
||||
UInt32 lower = src & mask;
|
||||
UInt32 upper = lower | ~mask;
|
||||
|
||||
return { lower, upper };
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr auto name = "IPv4CIDRToRange";
|
||||
static FunctionPtr create(const Context &) { return std::make_shared<FunctionIPv4CIDRToRange>(); }
|
||||
|
||||
String getName() const override { return name; }
|
||||
size_t getNumberOfArguments() const override { return 2; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||
{
|
||||
if (!WhichDataType(arguments[0]).isUInt32())
|
||||
throw Exception("Illegal type " + arguments[0]->getName() +
|
||||
" of first argument of function " + getName() +
|
||||
", expected UInt32",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
|
||||
const DataTypePtr & second_argument = arguments[1];
|
||||
if (!isUInt8(second_argument))
|
||||
throw Exception{"Illegal type " + second_argument->getName()
|
||||
+ " of second argument of function " + getName()
|
||||
+ ", expected numeric type.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
DataTypePtr element = DataTypeFactory::instance().get("IPv4");
|
||||
return std::make_shared<DataTypeTuple>(DataTypes{element, element});
|
||||
}
|
||||
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
|
||||
|
||||
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
||||
{
|
||||
const auto & col_type_name_ip = block.getByPosition(arguments[0]);
|
||||
const ColumnPtr & column_ip = col_type_name_ip.column;
|
||||
|
||||
const auto col_ip_in = checkAndGetColumn<ColumnUInt32>(column_ip.get());
|
||||
if (!col_ip_in)
|
||||
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
|
||||
+ " of argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
const auto & col_type_name_cidr = block.getByPosition(arguments[1]);
|
||||
const ColumnPtr & column_cidr = col_type_name_cidr.column;
|
||||
|
||||
const auto col_const_cidr_in = checkAndGetColumnConst<ColumnUInt8>(column_cidr.get());
|
||||
const auto col_cidr_in = checkAndGetColumn<ColumnUInt8>(column_cidr.get());
|
||||
|
||||
if (!col_const_cidr_in && !col_cidr_in)
|
||||
throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName()
|
||||
+ " of argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
const auto & vec_in = col_ip_in->getData();
|
||||
|
||||
auto col_res_lower_range = ColumnUInt32::create();
|
||||
auto col_res_upper_range = ColumnUInt32::create();
|
||||
|
||||
auto & vec_res_lower_range = col_res_lower_range->getData();
|
||||
vec_res_lower_range.resize(input_rows_count);
|
||||
|
||||
auto & vec_res_upper_range = col_res_upper_range->getData();
|
||||
vec_res_upper_range.resize(input_rows_count);
|
||||
|
||||
for (size_t i = 0; i < input_rows_count; ++i)
|
||||
{
|
||||
UInt8 cidr = col_const_cidr_in
|
||||
? col_const_cidr_in->getValue<UInt8>()
|
||||
: col_cidr_in->getData()[i];
|
||||
|
||||
std::tie(vec_res_lower_range[i], vec_res_upper_range[i]) = applyCIDRMask(vec_in[i], cidr);
|
||||
}
|
||||
|
||||
block.getByPosition(result).column = ColumnTuple::create(Columns{std::move(col_res_lower_range), std::move(col_res_upper_range)});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
378
dbms/src/Functions/FunctionsJSON.cpp
Normal file
378
dbms/src/Functions/FunctionsJSON.cpp
Normal file
@ -0,0 +1,378 @@
|
||||
#include <Functions/FunctionsJSON.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Common/config.h>
|
||||
|
||||
#if USE_SIMDJSON
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <DataTypes/DataTypeNullable.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
template <typename T>
|
||||
class JSONNullableImplBase
|
||||
{
|
||||
public:
|
||||
static DataTypePtr getType() { return std::make_shared<DataTypeNullable>(std::make_shared<T>()); }
|
||||
|
||||
static Field getDefault() { return {}; }
|
||||
};
|
||||
|
||||
class JSONHasImpl : public JSONNullableImplBase<DataTypeUInt8>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonHas"};
|
||||
|
||||
static Field getValue(ParsedJson::iterator &) { return {1}; }
|
||||
};
|
||||
|
||||
class JSONLengthImpl : public JSONNullableImplBase<DataTypeUInt64>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonLength"};
|
||||
|
||||
static Field getValue(ParsedJson::iterator & pjh)
|
||||
{
|
||||
if (!pjh.is_object_or_array())
|
||||
return getDefault();
|
||||
|
||||
size_t size = 0;
|
||||
|
||||
if (pjh.down())
|
||||
{
|
||||
size += 1;
|
||||
|
||||
while (pjh.next())
|
||||
size += 1;
|
||||
}
|
||||
|
||||
return {size};
|
||||
}
|
||||
};
|
||||
|
||||
class JSONTypeImpl : public JSONNullableImplBase<DataTypeString>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonType"};
|
||||
|
||||
static Field getValue(ParsedJson::iterator & pjh)
|
||||
{
|
||||
switch (pjh.get_type())
|
||||
{
|
||||
case '[':
|
||||
return "Array";
|
||||
case '{':
|
||||
return "Object";
|
||||
case '"':
|
||||
return "String";
|
||||
case 'l':
|
||||
return "Int64";
|
||||
case 'd':
|
||||
return "Float64";
|
||||
case 't':
|
||||
return "Bool";
|
||||
case 'f':
|
||||
return "Bool";
|
||||
case 'n':
|
||||
return "Null";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class JSONExtractImpl
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonExtract"};
|
||||
|
||||
static DataTypePtr getType(const DataTypePtr & type)
|
||||
{
|
||||
WhichDataType which{type};
|
||||
|
||||
if (which.isNativeUInt() || which.isNativeInt() || which.isFloat() || which.isEnum() || which.isDateOrDateTime()
|
||||
|| which.isStringOrFixedString() || which.isInterval())
|
||||
return std::make_shared<DataTypeNullable>(type);
|
||||
|
||||
if (which.isArray())
|
||||
{
|
||||
auto array_type = static_cast<const DataTypeArray *>(type.get());
|
||||
|
||||
return std::make_shared<DataTypeArray>(getType(array_type->getNestedType()));
|
||||
}
|
||||
|
||||
if (which.isTuple())
|
||||
{
|
||||
auto tuple_type = static_cast<const DataTypeTuple *>(type.get());
|
||||
|
||||
DataTypes types;
|
||||
types.reserve(tuple_type->getElements().size());
|
||||
|
||||
for (const DataTypePtr & element : tuple_type->getElements())
|
||||
{
|
||||
types.push_back(getType(element));
|
||||
}
|
||||
|
||||
return std::make_shared<DataTypeTuple>(std::move(types));
|
||||
}
|
||||
|
||||
throw Exception{"Unsupported return type schema: " + type->getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
}
|
||||
|
||||
static Field getDefault(const DataTypePtr & type)
|
||||
{
|
||||
WhichDataType which{type};
|
||||
|
||||
if (which.isNativeUInt() || which.isNativeInt() || which.isFloat() || which.isEnum() || which.isDateOrDateTime()
|
||||
|| which.isStringOrFixedString() || which.isInterval())
|
||||
return {};
|
||||
|
||||
if (which.isArray())
|
||||
return {Array{}};
|
||||
|
||||
if (which.isTuple())
|
||||
{
|
||||
auto tuple_type = static_cast<const DataTypeTuple *>(type.get());
|
||||
|
||||
Tuple tuple;
|
||||
tuple.toUnderType().reserve(tuple_type->getElements().size());
|
||||
|
||||
for (const DataTypePtr & element : tuple_type->getElements())
|
||||
tuple.toUnderType().push_back(getDefault(element));
|
||||
|
||||
return {tuple};
|
||||
}
|
||||
|
||||
// should not reach
|
||||
throw Exception{"Unsupported return type schema: " + type->getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
}
|
||||
|
||||
static Field getValue(ParsedJson::iterator & pjh, const DataTypePtr & type)
|
||||
{
|
||||
WhichDataType which{type};
|
||||
|
||||
if (which.isNativeUInt() || which.isNativeInt() || which.isEnum() || which.isDateOrDateTime() || which.isInterval())
|
||||
{
|
||||
if (pjh.is_integer())
|
||||
return {pjh.get_integer()};
|
||||
else
|
||||
return getDefault(type);
|
||||
}
|
||||
|
||||
if (which.isFloat())
|
||||
{
|
||||
if (pjh.is_integer())
|
||||
return {static_cast<double>(pjh.get_integer())};
|
||||
else if (pjh.is_double())
|
||||
return {pjh.get_double()};
|
||||
else
|
||||
return getDefault(type);
|
||||
}
|
||||
|
||||
if (which.isStringOrFixedString())
|
||||
{
|
||||
if (pjh.is_string())
|
||||
return {String{pjh.get_string()}};
|
||||
else
|
||||
return getDefault(type);
|
||||
}
|
||||
|
||||
if (which.isArray())
|
||||
{
|
||||
if (!pjh.is_object_or_array())
|
||||
return getDefault(type);
|
||||
|
||||
auto array_type = static_cast<const DataTypeArray *>(type.get());
|
||||
|
||||
Array array;
|
||||
|
||||
bool first = true;
|
||||
|
||||
while (first ? pjh.down() : pjh.next())
|
||||
{
|
||||
first = false;
|
||||
|
||||
ParsedJson::iterator pjh1{pjh};
|
||||
|
||||
array.push_back(getValue(pjh1, array_type->getNestedType()));
|
||||
}
|
||||
|
||||
return {array};
|
||||
}
|
||||
|
||||
if (which.isTuple())
|
||||
{
|
||||
if (!pjh.is_object_or_array())
|
||||
return getDefault(type);
|
||||
|
||||
auto tuple_type = static_cast<const DataTypeTuple *>(type.get());
|
||||
|
||||
Tuple tuple;
|
||||
tuple.toUnderType().reserve(tuple_type->getElements().size());
|
||||
|
||||
bool valid = true;
|
||||
bool first = true;
|
||||
|
||||
for (const DataTypePtr & element : tuple_type->getElements())
|
||||
{
|
||||
if (valid)
|
||||
{
|
||||
valid &= first ? pjh.down() : pjh.next();
|
||||
first = false;
|
||||
|
||||
ParsedJson::iterator pjh1{pjh};
|
||||
|
||||
tuple.toUnderType().push_back(getValue(pjh1, element));
|
||||
}
|
||||
else
|
||||
tuple.toUnderType().push_back(getDefault(element));
|
||||
}
|
||||
|
||||
return {tuple};
|
||||
}
|
||||
|
||||
// should not reach
|
||||
throw Exception{"Unsupported return type schema: " + type->getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
}
|
||||
};
|
||||
|
||||
class JSONExtractUIntImpl : public JSONNullableImplBase<DataTypeUInt64>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonExtractUInt"};
|
||||
|
||||
static Field getValue(ParsedJson::iterator & pjh)
|
||||
{
|
||||
if (pjh.is_integer())
|
||||
return {pjh.get_integer()};
|
||||
else
|
||||
return getDefault();
|
||||
}
|
||||
};
|
||||
|
||||
class JSONExtractIntImpl : public JSONNullableImplBase<DataTypeInt64>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonExtractInt"};
|
||||
|
||||
static Field getValue(ParsedJson::iterator & pjh)
|
||||
{
|
||||
if (pjh.is_integer())
|
||||
return {pjh.get_integer()};
|
||||
else
|
||||
return getDefault();
|
||||
}
|
||||
};
|
||||
|
||||
class JSONExtractFloatImpl : public JSONNullableImplBase<DataTypeFloat64>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonExtractFloat"};
|
||||
|
||||
static Field getValue(ParsedJson::iterator & pjh)
|
||||
{
|
||||
if (pjh.is_double())
|
||||
return {pjh.get_double()};
|
||||
else
|
||||
return getDefault();
|
||||
}
|
||||
};
|
||||
|
||||
class JSONExtractBoolImpl : public JSONNullableImplBase<DataTypeUInt8>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonExtractBool"};
|
||||
|
||||
static Field getValue(ParsedJson::iterator & pjh)
|
||||
{
|
||||
if (pjh.get_type() == 't')
|
||||
return {1};
|
||||
else if (pjh.get_type() == 'f')
|
||||
return {0};
|
||||
else
|
||||
return getDefault();
|
||||
}
|
||||
};
|
||||
|
||||
// class JSONExtractRawImpl: public JSONNullableImplBase<DataTypeString>
|
||||
// {
|
||||
// public:
|
||||
// static constexpr auto name {"jsonExtractRaw"};
|
||||
|
||||
// static Field getValue(ParsedJson::iterator & pjh)
|
||||
// {
|
||||
// //
|
||||
// }
|
||||
// };
|
||||
|
||||
class JSONExtractStringImpl : public JSONNullableImplBase<DataTypeString>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name{"jsonExtractString"};
|
||||
|
||||
static Field getValue(ParsedJson::iterator & pjh)
|
||||
{
|
||||
if (pjh.is_string())
|
||||
return {String{pjh.get_string()}};
|
||||
else
|
||||
return getDefault();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#else
|
||||
namespace DB
|
||||
{
|
||||
struct JSONHasImpl { static constexpr auto name{"jsonHas"}; };
|
||||
struct JSONLengthImpl { static constexpr auto name{"jsonLength"}; };
|
||||
struct JSONTypeImpl { static constexpr auto name{"jsonType"}; };
|
||||
struct JSONExtractImpl { static constexpr auto name{"jsonExtract"}; };
|
||||
struct JSONExtractUIntImpl { static constexpr auto name{"jsonExtractUInt"}; };
|
||||
struct JSONExtractIntImpl { static constexpr auto name{"jsonExtractInt"}; };
|
||||
struct JSONExtractFloatImpl { static constexpr auto name{"jsonExtractFloat"}; };
|
||||
struct JSONExtractBoolImpl { static constexpr auto name{"jsonExtractBool"}; };
|
||||
//struct JSONExtractRawImpl { static constexpr auto name {"jsonExtractRaw"}; };
|
||||
struct JSONExtractStringImpl { static constexpr auto name{"jsonExtractString"}; };
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void registerFunctionsJSON(FunctionFactory & factory)
|
||||
{
|
||||
#if USE_SIMDJSON
|
||||
if (__builtin_cpu_supports("avx2"))
|
||||
{
|
||||
factory.registerFunction<FunctionJSONBase<JSONHasImpl, false>>();
|
||||
factory.registerFunction<FunctionJSONBase<JSONLengthImpl, false>>();
|
||||
factory.registerFunction<FunctionJSONBase<JSONTypeImpl, false>>();
|
||||
factory.registerFunction<FunctionJSONBase<JSONExtractImpl, true>>();
|
||||
factory.registerFunction<FunctionJSONBase<JSONExtractUIntImpl, false>>();
|
||||
factory.registerFunction<FunctionJSONBase<JSONExtractIntImpl, false>>();
|
||||
factory.registerFunction<FunctionJSONBase<JSONExtractFloatImpl, false>>();
|
||||
factory.registerFunction<FunctionJSONBase<JSONExtractBoolImpl, false>>();
|
||||
// factory.registerFunction<FunctionJSONBase<
|
||||
// JSONExtractRawImpl,
|
||||
// false
|
||||
// >>();
|
||||
factory.registerFunction<FunctionJSONBase<JSONExtractStringImpl, false>>();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
factory.registerFunction<FunctionJSONDummy<JSONHasImpl>>();
|
||||
factory.registerFunction<FunctionJSONDummy<JSONLengthImpl>>();
|
||||
factory.registerFunction<FunctionJSONDummy<JSONTypeImpl>>();
|
||||
factory.registerFunction<FunctionJSONDummy<JSONExtractImpl>>();
|
||||
factory.registerFunction<FunctionJSONDummy<JSONExtractUIntImpl>>();
|
||||
factory.registerFunction<FunctionJSONDummy<JSONExtractIntImpl>>();
|
||||
factory.registerFunction<FunctionJSONDummy<JSONExtractFloatImpl>>();
|
||||
factory.registerFunction<FunctionJSONDummy<JSONExtractBoolImpl>>();
|
||||
//factory.registerFunction<FunctionJSONDummy<JSONExtractRawImpl>>();
|
||||
factory.registerFunction<FunctionJSONDummy<JSONExtractStringImpl>>();
|
||||
}
|
||||
|
||||
}
|
243
dbms/src/Functions/FunctionsJSON.h
Normal file
243
dbms/src/Functions/FunctionsJSON.h
Normal file
@ -0,0 +1,243 @@
|
||||
#pragma once
|
||||
|
||||
#include <Functions/IFunction.h>
|
||||
#include <Common/config.h>
|
||||
|
||||
#if USE_SIMDJSON
|
||||
|
||||
#include <Columns/ColumnConst.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <ext/range.h>
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#pragma clang diagnostic ignored "-Wnewline-eof"
|
||||
#endif
|
||||
|
||||
#include <simdjson/jsonparser.h>
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int CANNOT_ALLOCATE_MEMORY;
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
}
|
||||
|
||||
template <typename Impl, bool ExtraArg>
|
||||
class FunctionJSONBase : public IFunction
|
||||
{
|
||||
private:
|
||||
enum class Action
|
||||
{
|
||||
key = 1,
|
||||
index = 2,
|
||||
};
|
||||
|
||||
mutable std::vector<Action> actions;
|
||||
mutable DataTypePtr virtual_type;
|
||||
|
||||
bool tryMove(ParsedJson::iterator & pjh, Action action, const Field & accessor)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case Action::key:
|
||||
if (!pjh.is_object() || !pjh.move_to_key(accessor.get<String>().data()))
|
||||
return false;
|
||||
|
||||
break;
|
||||
case Action::index:
|
||||
if (!pjh.is_object_or_array() || !pjh.down())
|
||||
return false;
|
||||
|
||||
int steps = accessor.get<Int64>();
|
||||
|
||||
if (steps > 0)
|
||||
steps -= 1;
|
||||
else if (steps < 0)
|
||||
{
|
||||
steps += 1;
|
||||
|
||||
ParsedJson::iterator pjh1{pjh};
|
||||
|
||||
while (pjh1.next())
|
||||
steps += 1;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
for (const auto i : ext::range(0, steps))
|
||||
{
|
||||
(void)i;
|
||||
|
||||
if (!pjh.next())
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr auto name = Impl::name;
|
||||
|
||||
static FunctionPtr create(const Context &) { return std::make_shared<FunctionJSONBase>(); }
|
||||
|
||||
String getName() const override { return Impl::name; }
|
||||
|
||||
bool isVariadic() const override { return true; }
|
||||
|
||||
size_t getNumberOfArguments() const override { return 0; }
|
||||
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
if constexpr (ExtraArg)
|
||||
{
|
||||
if (arguments.size() < 2)
|
||||
throw Exception{"Function " + getName() + " requires at least two arguments", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH};
|
||||
|
||||
auto col_type_const = typeid_cast<const ColumnConst *>(arguments[1].column.get());
|
||||
|
||||
if (!col_type_const)
|
||||
throw Exception{"Illegal non-const column " + arguments[1].column->getName() + " of argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_COLUMN};
|
||||
|
||||
virtual_type = DataTypeFactory::instance().get(col_type_const->getValue<String>());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (arguments.size() < 1)
|
||||
throw Exception{"Function " + getName() + " requires at least one arguments", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH};
|
||||
}
|
||||
|
||||
if (!isString(arguments[0].type))
|
||||
throw Exception{"Illegal type " + arguments[0].type->getName() + " of argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
actions.reserve(arguments.size() - 1 - ExtraArg);
|
||||
|
||||
for (const auto i : ext::range(1 + ExtraArg, arguments.size()))
|
||||
{
|
||||
if (isString(arguments[i].type))
|
||||
actions.push_back(Action::key);
|
||||
else if (isInteger(arguments[i].type))
|
||||
actions.push_back(Action::index);
|
||||
else
|
||||
throw Exception{"Illegal type " + arguments[i].type->getName() + " of argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
}
|
||||
|
||||
if constexpr (ExtraArg)
|
||||
return Impl::getType(virtual_type);
|
||||
else
|
||||
return Impl::getType();
|
||||
}
|
||||
|
||||
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result_pos, size_t input_rows_count) override
|
||||
{
|
||||
MutableColumnPtr to{block.getByPosition(result_pos).type->createColumn()};
|
||||
to->reserve(input_rows_count);
|
||||
|
||||
const ColumnPtr & arg_json = block.getByPosition(arguments[0]).column;
|
||||
|
||||
auto col_json_const = typeid_cast<const ColumnConst *>(arg_json.get());
|
||||
|
||||
auto col_json_string
|
||||
= typeid_cast<const ColumnString *>(col_json_const ? col_json_const->getDataColumnPtr().get() : arg_json.get());
|
||||
|
||||
if (!col_json_string)
|
||||
throw Exception{"Illegal column " + arg_json->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
|
||||
|
||||
const ColumnString::Chars & chars = col_json_string->getChars();
|
||||
const ColumnString::Offsets & offsets = col_json_string->getOffsets();
|
||||
|
||||
size_t max_size = 1;
|
||||
|
||||
for (const auto i : ext::range(0, input_rows_count))
|
||||
if (max_size < offsets[i] - offsets[i - 1] - 1)
|
||||
max_size = offsets[i] - offsets[i - 1] - 1;
|
||||
|
||||
ParsedJson pj;
|
||||
if (!pj.allocateCapacity(max_size))
|
||||
throw Exception{"Can not allocate memory for " + std::to_string(max_size) + " units when parsing JSON",
|
||||
ErrorCodes::CANNOT_ALLOCATE_MEMORY};
|
||||
|
||||
for (const auto i : ext::range(0, input_rows_count))
|
||||
{
|
||||
bool ok = json_parse(&chars[offsets[i - 1]], offsets[i] - offsets[i - 1] - 1, pj) == 0;
|
||||
|
||||
ParsedJson::iterator pjh{pj};
|
||||
|
||||
for (const auto j : ext::range(0, actions.size()))
|
||||
{
|
||||
if (!ok)
|
||||
break;
|
||||
|
||||
ok = tryMove(pjh, actions[j], (*block.getByPosition(arguments[j + 1 + ExtraArg]).column)[i]);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if constexpr (ExtraArg)
|
||||
to->insert(Impl::getValue(pjh, virtual_type));
|
||||
else
|
||||
to->insert(Impl::getValue(pjh));
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (ExtraArg)
|
||||
to->insert(Impl::getDefault(virtual_type));
|
||||
else
|
||||
to->insert(Impl::getDefault());
|
||||
}
|
||||
}
|
||||
|
||||
block.getByPosition(result_pos).column = std::move(to);
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
class FunctionJSONDummy : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = Impl::name;
|
||||
static FunctionPtr create(const Context &) { return std::make_shared<FunctionJSONDummy>(); }
|
||||
|
||||
String getName() const override { return Impl::name; }
|
||||
bool isVariadic() const override { return true; }
|
||||
size_t getNumberOfArguments() const override { return 0; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName &) const override
|
||||
{
|
||||
throw Exception{"Function " + getName() + " is not supported without AVX2", ErrorCodes::NOT_IMPLEMENTED};
|
||||
}
|
||||
|
||||
void executeImpl(Block &, const ColumnNumbers &, size_t, size_t) override
|
||||
{
|
||||
throw Exception{"Function " + getName() + " is not supported without AVX2", ErrorCodes::NOT_IMPLEMENTED};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -449,44 +449,27 @@ struct NameMultiSearchFirstPositionCaseInsensitiveUTF8
|
||||
using FunctionPosition = FunctionsStringSearch<PositionImpl<PositionCaseSensitiveASCII>, NamePosition>;
|
||||
using FunctionPositionUTF8 = FunctionsStringSearch<PositionImpl<PositionCaseSensitiveUTF8>, NamePositionUTF8>;
|
||||
using FunctionPositionCaseInsensitive = FunctionsStringSearch<PositionImpl<PositionCaseInsensitiveASCII>, NamePositionCaseInsensitive>;
|
||||
using FunctionPositionCaseInsensitiveUTF8
|
||||
= FunctionsStringSearch<PositionImpl<PositionCaseInsensitiveUTF8>, NamePositionCaseInsensitiveUTF8>;
|
||||
using FunctionPositionCaseInsensitiveUTF8 = FunctionsStringSearch<PositionImpl<PositionCaseInsensitiveUTF8>, NamePositionCaseInsensitiveUTF8>;
|
||||
|
||||
using FunctionMultiSearchAllPositions
|
||||
= FunctionsMultiStringPosition<MultiSearchAllPositionsImpl<PositionCaseSensitiveASCII>, NameMultiSearchAllPositions>;
|
||||
using FunctionMultiSearchAllPositionsUTF8
|
||||
= FunctionsMultiStringPosition<MultiSearchAllPositionsImpl<PositionCaseSensitiveUTF8>, NameMultiSearchAllPositionsUTF8>;
|
||||
using FunctionMultiSearchAllPositionsCaseInsensitive
|
||||
= FunctionsMultiStringPosition<MultiSearchAllPositionsImpl<PositionCaseInsensitiveASCII>, NameMultiSearchAllPositionsCaseInsensitive>;
|
||||
using FunctionMultiSearchAllPositionsCaseInsensitiveUTF8 = FunctionsMultiStringPosition<
|
||||
MultiSearchAllPositionsImpl<PositionCaseInsensitiveUTF8>,
|
||||
NameMultiSearchAllPositionsCaseInsensitiveUTF8>;
|
||||
using FunctionMultiSearchAllPositions = FunctionsMultiStringPosition<MultiSearchAllPositionsImpl<PositionCaseSensitiveASCII>, NameMultiSearchAllPositions>;
|
||||
using FunctionMultiSearchAllPositionsUTF8 = FunctionsMultiStringPosition<MultiSearchAllPositionsImpl<PositionCaseSensitiveUTF8>, NameMultiSearchAllPositionsUTF8>;
|
||||
using FunctionMultiSearchAllPositionsCaseInsensitive = FunctionsMultiStringPosition<MultiSearchAllPositionsImpl<PositionCaseInsensitiveASCII>, NameMultiSearchAllPositionsCaseInsensitive>;
|
||||
using FunctionMultiSearchAllPositionsCaseInsensitiveUTF8 = FunctionsMultiStringPosition<MultiSearchAllPositionsImpl<PositionCaseInsensitiveUTF8>, NameMultiSearchAllPositionsCaseInsensitiveUTF8>;
|
||||
|
||||
using FunctionMultiSearch = FunctionsMultiStringSearch<MultiSearchImpl<PositionCaseSensitiveASCII>, NameMultiSearchAny>;
|
||||
using FunctionMultiSearchUTF8 = FunctionsMultiStringSearch<MultiSearchImpl<PositionCaseSensitiveUTF8>, NameMultiSearchAnyUTF8>;
|
||||
using FunctionMultiSearchCaseInsensitive
|
||||
= FunctionsMultiStringSearch<MultiSearchImpl<PositionCaseInsensitiveASCII>, NameMultiSearchAnyCaseInsensitive>;
|
||||
using FunctionMultiSearchCaseInsensitiveUTF8
|
||||
= FunctionsMultiStringSearch<MultiSearchImpl<PositionCaseInsensitiveUTF8>, NameMultiSearchAnyCaseInsensitiveUTF8>;
|
||||
using FunctionMultiSearchCaseInsensitive = FunctionsMultiStringSearch<MultiSearchImpl<PositionCaseInsensitiveASCII>, NameMultiSearchAnyCaseInsensitive>;
|
||||
using FunctionMultiSearchCaseInsensitiveUTF8 = FunctionsMultiStringSearch<MultiSearchImpl<PositionCaseInsensitiveUTF8>, NameMultiSearchAnyCaseInsensitiveUTF8>;
|
||||
|
||||
using FunctionMultiSearchFirstIndex
|
||||
= FunctionsMultiStringSearch<MultiSearchFirstIndexImpl<PositionCaseSensitiveASCII>, NameMultiSearchFirstIndex>;
|
||||
using FunctionMultiSearchFirstIndexUTF8
|
||||
= FunctionsMultiStringSearch<MultiSearchFirstIndexImpl<PositionCaseSensitiveUTF8>, NameMultiSearchFirstIndexUTF8>;
|
||||
using FunctionMultiSearchFirstIndexCaseInsensitive
|
||||
= FunctionsMultiStringSearch<MultiSearchFirstIndexImpl<PositionCaseInsensitiveASCII>, NameMultiSearchFirstIndexCaseInsensitive>;
|
||||
using FunctionMultiSearchFirstIndexCaseInsensitiveUTF8
|
||||
= FunctionsMultiStringSearch<MultiSearchFirstIndexImpl<PositionCaseInsensitiveUTF8>, NameMultiSearchFirstIndexCaseInsensitiveUTF8>;
|
||||
using FunctionMultiSearchFirstIndex = FunctionsMultiStringSearch<MultiSearchFirstIndexImpl<PositionCaseSensitiveASCII>, NameMultiSearchFirstIndex>;
|
||||
using FunctionMultiSearchFirstIndexUTF8 = FunctionsMultiStringSearch<MultiSearchFirstIndexImpl<PositionCaseSensitiveUTF8>, NameMultiSearchFirstIndexUTF8>;
|
||||
using FunctionMultiSearchFirstIndexCaseInsensitive = FunctionsMultiStringSearch<MultiSearchFirstIndexImpl<PositionCaseInsensitiveASCII>, NameMultiSearchFirstIndexCaseInsensitive>;
|
||||
using FunctionMultiSearchFirstIndexCaseInsensitiveUTF8 = FunctionsMultiStringSearch<MultiSearchFirstIndexImpl<PositionCaseInsensitiveUTF8>, NameMultiSearchFirstIndexCaseInsensitiveUTF8>;
|
||||
|
||||
using FunctionMultiSearchFirstPosition
|
||||
= FunctionsMultiStringSearch<MultiSearchFirstPositionImpl<PositionCaseSensitiveASCII>, NameMultiSearchFirstPosition>;
|
||||
using FunctionMultiSearchFirstPositionUTF8
|
||||
= FunctionsMultiStringSearch<MultiSearchFirstPositionImpl<PositionCaseSensitiveUTF8>, NameMultiSearchFirstPositionUTF8>;
|
||||
using FunctionMultiSearchFirstPositionCaseInsensitive
|
||||
= FunctionsMultiStringSearch<MultiSearchFirstPositionImpl<PositionCaseInsensitiveASCII>, NameMultiSearchFirstPositionCaseInsensitive>;
|
||||
using FunctionMultiSearchFirstPositionCaseInsensitiveUTF8 = FunctionsMultiStringSearch<
|
||||
MultiSearchFirstPositionImpl<PositionCaseInsensitiveUTF8>,
|
||||
NameMultiSearchFirstPositionCaseInsensitiveUTF8>;
|
||||
using FunctionMultiSearchFirstPosition = FunctionsMultiStringSearch<MultiSearchFirstPositionImpl<PositionCaseSensitiveASCII>, NameMultiSearchFirstPosition>;
|
||||
using FunctionMultiSearchFirstPositionUTF8 = FunctionsMultiStringSearch<MultiSearchFirstPositionImpl<PositionCaseSensitiveUTF8>, NameMultiSearchFirstPositionUTF8>;
|
||||
using FunctionMultiSearchFirstPositionCaseInsensitive = FunctionsMultiStringSearch<MultiSearchFirstPositionImpl<PositionCaseInsensitiveASCII>, NameMultiSearchFirstPositionCaseInsensitive>;
|
||||
using FunctionMultiSearchFirstPositionCaseInsensitiveUTF8 = FunctionsMultiStringSearch<MultiSearchFirstPositionImpl<PositionCaseInsensitiveUTF8>, NameMultiSearchFirstPositionCaseInsensitiveUTF8>;
|
||||
|
||||
|
||||
void registerFunctionsStringSearch(FunctionFactory & factory)
|
||||
|
@ -164,43 +164,46 @@ struct NgramDistanceImpl
|
||||
return num;
|
||||
}
|
||||
|
||||
template <bool SaveNgrams>
|
||||
static ALWAYS_INLINE inline size_t calculateNeedleStats(
|
||||
const char * data,
|
||||
const size_t size,
|
||||
NgramStats & ngram_stats,
|
||||
[[maybe_unused]] UInt16 * ngram_storage,
|
||||
size_t (*read_code_points)(CodePoint *, const char *&, const char *),
|
||||
UInt16 (*hash_functor)(const CodePoint *))
|
||||
{
|
||||
// To prevent size_t overflow below.
|
||||
if (size < N)
|
||||
return 0;
|
||||
|
||||
const char * start = data;
|
||||
const char * end = data + size;
|
||||
CodePoint cp[simultaneously_codepoints_num] = {};
|
||||
|
||||
/// read_code_points returns the position of cp where it stopped reading codepoints.
|
||||
size_t found = read_code_points(cp, start, end);
|
||||
/// We need to start for the first time here, because first N - 1 codepoints mean nothing.
|
||||
size_t i = N - 1;
|
||||
/// Initialize with this value because for the first time `found` does not initialize first N - 1 codepoints.
|
||||
size_t len = -N + 1;
|
||||
size_t len = 0;
|
||||
do
|
||||
{
|
||||
len += found - N + 1;
|
||||
for (; i + N <= found; ++i)
|
||||
++ngram_stats[hash_functor(cp + i)];
|
||||
{
|
||||
++len;
|
||||
UInt16 hash = hash_functor(cp + i);
|
||||
if constexpr (SaveNgrams)
|
||||
*ngram_storage++ = hash;
|
||||
++ngram_stats[hash];
|
||||
}
|
||||
i = 0;
|
||||
} while (start < end && (found = read_code_points(cp, start, end)));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
template <bool ReuseStats>
|
||||
static ALWAYS_INLINE inline UInt64 calculateHaystackStatsAndMetric(
|
||||
const char * data,
|
||||
const size_t size,
|
||||
NgramStats & ngram_stats,
|
||||
size_t & distance,
|
||||
[[maybe_unused]] UInt16 * ngram_storage,
|
||||
size_t (*read_code_points)(CodePoint *, const char *&, const char *),
|
||||
UInt16 (*hash_functor)(const CodePoint *))
|
||||
{
|
||||
@ -209,18 +212,6 @@ struct NgramDistanceImpl
|
||||
const char * end = data + size;
|
||||
CodePoint cp[simultaneously_codepoints_num] = {};
|
||||
|
||||
/// allocation tricks, most strings are relatively small
|
||||
static constexpr size_t small_buffer_size = 256;
|
||||
std::unique_ptr<UInt16[]> big_buffer;
|
||||
UInt16 small_buffer[small_buffer_size];
|
||||
UInt16 * ngram_storage = small_buffer;
|
||||
|
||||
if (size > small_buffer_size)
|
||||
{
|
||||
ngram_storage = new UInt16[size];
|
||||
big_buffer.reset(ngram_storage);
|
||||
}
|
||||
|
||||
/// read_code_points returns the position of cp where it stopped reading codepoints.
|
||||
size_t found = read_code_points(cp, start, end);
|
||||
/// We need to start for the first time here, because first N - 1 codepoints mean nothing.
|
||||
@ -235,21 +226,25 @@ struct NgramDistanceImpl
|
||||
--distance;
|
||||
else
|
||||
++distance;
|
||||
|
||||
ngram_storage[ngram_cnt++] = hash;
|
||||
if constexpr (ReuseStats)
|
||||
ngram_storage[ngram_cnt] = hash;
|
||||
++ngram_cnt;
|
||||
--ngram_stats[hash];
|
||||
}
|
||||
iter = 0;
|
||||
} while (start < end && (found = read_code_points(cp, start, end)));
|
||||
|
||||
/// Return the state of hash map to its initial.
|
||||
if constexpr (ReuseStats)
|
||||
{
|
||||
for (size_t i = 0; i < ngram_cnt; ++i)
|
||||
++ngram_stats[ngram_storage[i]];
|
||||
}
|
||||
return ngram_cnt;
|
||||
}
|
||||
|
||||
template <class Callback, class... Args>
|
||||
static inline size_t dispatchSearcher(Callback callback, Args &&... args)
|
||||
static inline auto dispatchSearcher(Callback callback, Args &&... args)
|
||||
{
|
||||
if constexpr (!UTF8)
|
||||
return callback(std::forward<Args>(args)..., readASCIICodePoints, ASCIIHash);
|
||||
@ -259,8 +254,7 @@ struct NgramDistanceImpl
|
||||
|
||||
static void constant_constant(std::string data, std::string needle, Float32 & res)
|
||||
{
|
||||
NgramStats common_stats;
|
||||
memset(common_stats, 0, sizeof(common_stats));
|
||||
NgramStats common_stats = {};
|
||||
|
||||
/// We use unsafe versions of getting ngrams, so I decided to use padded strings.
|
||||
const size_t needle_size = needle.size();
|
||||
@ -268,11 +262,11 @@ struct NgramDistanceImpl
|
||||
needle.resize(needle_size + default_padding);
|
||||
data.resize(data_size + default_padding);
|
||||
|
||||
size_t second_size = dispatchSearcher(calculateNeedleStats, needle.data(), needle_size, common_stats);
|
||||
size_t second_size = dispatchSearcher(calculateNeedleStats<false>, needle.data(), needle_size, common_stats, nullptr);
|
||||
size_t distance = second_size;
|
||||
if (data_size <= max_string_size)
|
||||
{
|
||||
size_t first_size = dispatchSearcher(calculateHaystackStatsAndMetric, data.data(), data_size, common_stats, distance);
|
||||
size_t first_size = dispatchSearcher(calculateHaystackStatsAndMetric<false>, data.data(), data_size, common_stats, distance, nullptr);
|
||||
res = distance * 1.f / std::max(first_size + second_size, size_t(1));
|
||||
}
|
||||
else
|
||||
@ -281,18 +275,89 @@ struct NgramDistanceImpl
|
||||
}
|
||||
}
|
||||
|
||||
static void vector_vector(
|
||||
const ColumnString::Chars & haystack_data,
|
||||
const ColumnString::Offsets & haystack_offsets,
|
||||
const ColumnString::Chars & needle_data,
|
||||
const ColumnString::Offsets & needle_offsets,
|
||||
PaddedPODArray<Float32> & res)
|
||||
{
|
||||
const size_t haystack_offsets_size = haystack_offsets.size();
|
||||
size_t prev_haystack_offset = 0;
|
||||
size_t prev_needle_offset = 0;
|
||||
|
||||
NgramStats common_stats = {};
|
||||
|
||||
/// The main motivation is to not allocate more on stack because we have already allocated a lot (128Kb).
|
||||
/// And we can reuse these storages in one thread because we care only about what was written to first places.
|
||||
std::unique_ptr<UInt16[]> needle_ngram_storage(new UInt16[max_string_size]);
|
||||
std::unique_ptr<UInt16[]> haystack_ngram_storage(new UInt16[max_string_size]);
|
||||
|
||||
for (size_t i = 0; i < haystack_offsets_size; ++i)
|
||||
{
|
||||
const char * haystack = reinterpret_cast<const char *>(&haystack_data[prev_haystack_offset]);
|
||||
const size_t haystack_size = haystack_offsets[i] - prev_haystack_offset - 1;
|
||||
const char * needle = reinterpret_cast<const char *>(&needle_data[prev_needle_offset]);
|
||||
const size_t needle_size = needle_offsets[i] - prev_needle_offset - 1;
|
||||
|
||||
if (needle_size <= max_string_size && haystack_size <= max_string_size)
|
||||
{
|
||||
/// Get needle stats.
|
||||
const size_t needle_stats_size = dispatchSearcher(
|
||||
calculateNeedleStats<true>,
|
||||
needle,
|
||||
needle_size,
|
||||
common_stats,
|
||||
needle_ngram_storage.get());
|
||||
|
||||
size_t distance = needle_stats_size;
|
||||
|
||||
/// Combine with haystack stats, return to initial needle stats.
|
||||
const size_t haystack_stats_size = dispatchSearcher(
|
||||
calculateHaystackStatsAndMetric<true>,
|
||||
haystack,
|
||||
haystack_size,
|
||||
common_stats,
|
||||
distance,
|
||||
haystack_ngram_storage.get());
|
||||
|
||||
/// Return to zero array stats.
|
||||
for (size_t j = 0; j < needle_stats_size; ++j)
|
||||
--common_stats[needle_ngram_storage[j]];
|
||||
|
||||
/// For now, common stats is a zero array.
|
||||
res[i] = distance * 1.f / std::max(haystack_stats_size + needle_stats_size, size_t(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Strings are too big, we are assuming they are not the same. This is done because of limiting number
|
||||
/// of bigrams added and not allocating too much memory.
|
||||
res[i] = 1.f;
|
||||
}
|
||||
|
||||
prev_needle_offset = needle_offsets[i];
|
||||
prev_haystack_offset = haystack_offsets[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void vector_constant(
|
||||
const ColumnString::Chars & data, const ColumnString::Offsets & offsets, std::string needle, PaddedPODArray<Float32> & res)
|
||||
const ColumnString::Chars & data,
|
||||
const ColumnString::Offsets & offsets,
|
||||
std::string needle,
|
||||
PaddedPODArray<Float32> & res)
|
||||
{
|
||||
/// zeroing our map
|
||||
NgramStats common_stats;
|
||||
memset(common_stats, 0, sizeof(common_stats));
|
||||
NgramStats common_stats = {};
|
||||
|
||||
/// The main motivation is to not allocate more on stack because we have already allocated a lot (128Kb).
|
||||
/// And we can reuse these storages in one thread because we care only about what was written to first places.
|
||||
std::unique_ptr<UInt16[]> ngram_storage(new UInt16[max_string_size]);
|
||||
|
||||
/// We use unsafe versions of getting ngrams, so I decided to use padded_data even in needle case.
|
||||
const size_t needle_size = needle.size();
|
||||
needle.resize(needle_size + default_padding);
|
||||
|
||||
const size_t needle_stats_size = dispatchSearcher(calculateNeedleStats, needle.data(), needle_size, common_stats);
|
||||
const size_t needle_stats_size = dispatchSearcher(calculateNeedleStats<false>, needle.data(), needle_size, common_stats, nullptr);
|
||||
|
||||
size_t distance = needle_stats_size;
|
||||
size_t prev_offset = 0;
|
||||
@ -303,7 +368,11 @@ struct NgramDistanceImpl
|
||||
if (haystack_size <= max_string_size)
|
||||
{
|
||||
size_t haystack_stats_size = dispatchSearcher(
|
||||
calculateHaystackStatsAndMetric, reinterpret_cast<const char *>(haystack), haystack_size, common_stats, distance);
|
||||
calculateHaystackStatsAndMetric<true>,
|
||||
reinterpret_cast<const char *>(haystack),
|
||||
haystack_size, common_stats,
|
||||
distance,
|
||||
ngram_storage.get());
|
||||
res[i] = distance * 1.f / std::max(haystack_stats_size + needle_stats_size, size_t(1));
|
||||
}
|
||||
else
|
||||
@ -339,11 +408,9 @@ struct NameNgramDistanceUTF8CaseInsensitive
|
||||
};
|
||||
|
||||
using FunctionNgramDistance = FunctionsStringSimilarity<NgramDistanceImpl<4, UInt8, false, false>, NameNgramDistance>;
|
||||
using FunctionNgramDistanceCaseInsensitive
|
||||
= FunctionsStringSimilarity<NgramDistanceImpl<4, UInt8, false, true>, NameNgramDistanceCaseInsensitive>;
|
||||
using FunctionNgramDistanceCaseInsensitive = FunctionsStringSimilarity<NgramDistanceImpl<4, UInt8, false, true>, NameNgramDistanceCaseInsensitive>;
|
||||
using FunctionNgramDistanceUTF8 = FunctionsStringSimilarity<NgramDistanceImpl<3, UInt32, true, false>, NameNgramDistanceUTF8>;
|
||||
using FunctionNgramDistanceCaseInsensitiveUTF8
|
||||
= FunctionsStringSimilarity<NgramDistanceImpl<3, UInt32, true, true>, NameNgramDistanceUTF8CaseInsensitive>;
|
||||
using FunctionNgramDistanceCaseInsensitiveUTF8 = FunctionsStringSimilarity<NgramDistanceImpl<3, UInt32, true, true>, NameNgramDistanceUTF8CaseInsensitive>;
|
||||
|
||||
void registerFunctionsStringSimilarity(FunctionFactory & factory)
|
||||
{
|
||||
|
@ -62,10 +62,7 @@ public:
|
||||
const ColumnConst * col_haystack_const = typeid_cast<const ColumnConst *>(&*column_haystack);
|
||||
const ColumnConst * col_needle_const = typeid_cast<const ColumnConst *>(&*column_needle);
|
||||
|
||||
if (!col_needle_const)
|
||||
throw Exception("Second argument of function " + getName() + " must be constant string.", ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
if (col_haystack_const)
|
||||
if (col_haystack_const && col_needle_const)
|
||||
{
|
||||
ResultType res{};
|
||||
const String & needle = col_needle_const->getValue<String>();
|
||||
@ -88,8 +85,9 @@ public:
|
||||
vec_res.resize(column_haystack->size());
|
||||
|
||||
const ColumnString * col_haystack_vector = checkAndGetColumn<ColumnString>(&*column_haystack);
|
||||
const ColumnString * col_needle_vector = checkAndGetColumn<ColumnString>(&*column_needle);
|
||||
|
||||
if (col_haystack_vector)
|
||||
if (col_haystack_vector && col_needle_const)
|
||||
{
|
||||
const String & needle = col_needle_const->getValue<String>();
|
||||
if (needle.size() > Impl::max_string_size)
|
||||
@ -101,6 +99,27 @@ public:
|
||||
}
|
||||
Impl::vector_constant(col_haystack_vector->getChars(), col_haystack_vector->getOffsets(), needle, vec_res);
|
||||
}
|
||||
else if (col_haystack_vector && col_needle_vector)
|
||||
{
|
||||
Impl::vector_vector(
|
||||
col_haystack_vector->getChars(),
|
||||
col_haystack_vector->getOffsets(),
|
||||
col_needle_vector->getChars(),
|
||||
col_needle_vector->getOffsets(),
|
||||
vec_res);
|
||||
}
|
||||
else if (col_haystack_const && col_needle_vector)
|
||||
{
|
||||
const String & needle = col_haystack_const->getValue<String>();
|
||||
if (needle.size() > Impl::max_string_size)
|
||||
{
|
||||
throw Exception(
|
||||
"String size of needle is too big for function " + getName() + ". Should be at most "
|
||||
+ std::to_string(Impl::max_string_size),
|
||||
ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
}
|
||||
Impl::vector_constant(col_needle_vector->getChars(), col_needle_vector->getOffsets(), needle, vec_res);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <Functions/GatherUtils/ArraySourceVisitor.h>
|
||||
#include <Functions/GatherUtils/ArraySinkVisitor.h>
|
||||
#include <Functions/GatherUtils/ValueSourceVisitor.h>
|
||||
#include <Core/TypeListNumber.h>
|
||||
|
||||
|
||||
namespace DB::GatherUtils
|
||||
{
|
||||
|
@ -135,9 +135,10 @@ namespace MultiRegexps
|
||||
for (const StringRef ref : str_patterns)
|
||||
{
|
||||
ptrns.push_back(ref.data);
|
||||
flags.push_back(HS_FLAG_DOTALL | HS_FLAG_ALLOWEMPTY | HS_FLAG_SINGLEMATCH);
|
||||
flags.push_back(HS_FLAG_DOTALL | HS_FLAG_ALLOWEMPTY | HS_FLAG_SINGLEMATCH | HS_FLAG_UTF8);
|
||||
if constexpr (CompileForEditDistance)
|
||||
{
|
||||
flags.back() &= ~HS_FLAG_UTF8;
|
||||
ext_exprs.emplace_back();
|
||||
ext_exprs.back().flags = HS_EXT_FLAG_EDIT_DISTANCE;
|
||||
ext_exprs.back().edit_distance = edit_distance.value();
|
||||
|
42
dbms/src/Functions/basename.cpp
Normal file
42
dbms/src/Functions/basename.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionStringToString.h>
|
||||
#include <Functions/FunctionsURL.h>
|
||||
#include <common/find_symbols.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/** Extract substring after the last slash or backslash.
|
||||
* If there are no slashes, return the string unchanged.
|
||||
* It is used to extract filename from path.
|
||||
*/
|
||||
struct ExtractBasename
|
||||
{
|
||||
static size_t getReserveLengthForElement() { return 16; } /// Just a guess.
|
||||
|
||||
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
||||
{
|
||||
res_data = data;
|
||||
res_size = size;
|
||||
|
||||
Pos pos = data;
|
||||
Pos end = pos + size;
|
||||
|
||||
if ((pos = find_last_symbols_or_null<'/', '\\'>(pos, end)))
|
||||
{
|
||||
++pos;
|
||||
res_data = pos;
|
||||
res_size = end - pos;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct NameBasename { static constexpr auto name = "basename"; };
|
||||
using FunctionBasename = FunctionStringToString<ExtractSubstringImpl<ExtractBasename>, NameBasename>;
|
||||
|
||||
void registerFunctionBasename(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionBasename>();
|
||||
}
|
||||
|
||||
}
|
51
dbms/src/Functions/ignoreExceptNull.cpp
Normal file
51
dbms/src/Functions/ignoreExceptNull.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include <Functions/IFunction.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/** ignoreExceptNull(...) is a function that takes any arguments, and always returns 0 except Null.
|
||||
*/
|
||||
class FunctionIgnoreExceptNull : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "ignoreExceptNull";
|
||||
static FunctionPtr create(const Context &)
|
||||
{
|
||||
return std::make_shared<FunctionIgnoreExceptNull>();
|
||||
}
|
||||
|
||||
bool isVariadic() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
size_t getNumberOfArguments() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
String getName() const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override
|
||||
{
|
||||
return std::make_shared<DataTypeUInt8>();
|
||||
}
|
||||
|
||||
void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override
|
||||
{
|
||||
block.getByPosition(result).column = DataTypeUInt8().createColumnConst(input_rows_count, UInt64(0));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void registerFunctionIgnoreExceptNull(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionIgnoreExceptNull>();
|
||||
}
|
||||
|
||||
}
|
@ -73,11 +73,6 @@ public:
|
||||
return std::make_shared<DataTypeUInt8>();
|
||||
}
|
||||
|
||||
bool useDefaultImplementationForNulls() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
|
||||
{
|
||||
/// Second argument must be ColumnSet.
|
||||
@ -89,7 +84,7 @@ public:
|
||||
|
||||
Block block_of_key_columns;
|
||||
|
||||
/// First argument may be tuple or single column.
|
||||
/// First argument may be a tuple or a single column.
|
||||
const ColumnWithTypeAndName & left_arg = block.getByPosition(arguments[0]);
|
||||
const ColumnTuple * tuple = typeid_cast<const ColumnTuple *>(left_arg.column.get());
|
||||
const ColumnConst * const_tuple = checkAndGetColumnConst<ColumnTuple>(left_arg.column.get());
|
||||
|
@ -40,6 +40,7 @@ void registerFunctionsMath(FunctionFactory &);
|
||||
void registerFunctionsGeo(FunctionFactory &);
|
||||
void registerFunctionsNull(FunctionFactory &);
|
||||
void registerFunctionsFindCluster(FunctionFactory &);
|
||||
void registerFunctionsJSON(FunctionFactory &);
|
||||
void registerFunctionTransform(FunctionFactory &);
|
||||
|
||||
#if USE_ICU
|
||||
@ -82,6 +83,7 @@ void registerFunctions()
|
||||
registerFunctionsGeo(factory);
|
||||
registerFunctionsNull(factory);
|
||||
registerFunctionsFindCluster(factory);
|
||||
registerFunctionsJSON(factory);
|
||||
registerFunctionTransform(factory);
|
||||
|
||||
#if USE_ICU
|
||||
|
@ -19,6 +19,7 @@ void registerFunctionSleep(FunctionFactory &);
|
||||
void registerFunctionSleepEachRow(FunctionFactory &);
|
||||
void registerFunctionMaterialize(FunctionFactory &);
|
||||
void registerFunctionIgnore(FunctionFactory &);
|
||||
void registerFunctionIgnoreExceptNull(FunctionFactory &);
|
||||
void registerFunctionIndexHint(FunctionFactory &);
|
||||
void registerFunctionIdentity(FunctionFactory &);
|
||||
void registerFunctionArrayJoin(FunctionFactory &);
|
||||
@ -42,6 +43,7 @@ void registerFunctionLowCardinalityKeys(FunctionFactory &);
|
||||
void registerFunctionsIn(FunctionFactory &);
|
||||
void registerFunctionJoinGet(FunctionFactory &);
|
||||
void registerFunctionFilesystem(FunctionFactory &);
|
||||
void registerFunctionBasename(FunctionFactory &);
|
||||
|
||||
void registerFunctionsMiscellaneous(FunctionFactory & factory)
|
||||
{
|
||||
@ -61,6 +63,7 @@ void registerFunctionsMiscellaneous(FunctionFactory & factory)
|
||||
registerFunctionSleepEachRow(factory);
|
||||
registerFunctionMaterialize(factory);
|
||||
registerFunctionIgnore(factory);
|
||||
registerFunctionIgnoreExceptNull(factory);
|
||||
registerFunctionIndexHint(factory);
|
||||
registerFunctionIdentity(factory);
|
||||
registerFunctionArrayJoin(factory);
|
||||
@ -84,6 +87,7 @@ void registerFunctionsMiscellaneous(FunctionFactory & factory)
|
||||
registerFunctionsIn(factory);
|
||||
registerFunctionJoinGet(factory);
|
||||
registerFunctionFilesystem(factory);
|
||||
registerFunctionBasename(factory);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||
@ -69,3 +71,5 @@ TEST(ReadBufferAIOTest, TestReadAfterAIO)
|
||||
EXPECT_EQ(read_after_eof_big, data.length());
|
||||
EXPECT_TRUE(testbuf.eof());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -328,10 +328,10 @@ void ActionsVisitor::visit(const ASTPtr & ast)
|
||||
if (!only_consts)
|
||||
{
|
||||
/// We are in the part of the tree that we are not going to compute. You just need to define types.
|
||||
/// Do not subquery and create sets. We treat "IN" as "ignore" function.
|
||||
/// Do not subquery and create sets. We treat "IN" as "ignoreExceptNull" function.
|
||||
|
||||
actions_stack.addAction(ExpressionAction::applyFunction(
|
||||
FunctionFactory::instance().get("ignore", context),
|
||||
FunctionFactory::instance().get("ignoreExceptNull", context),
|
||||
{ node->arguments->children.at(0)->getColumnName() },
|
||||
getColumnName()));
|
||||
}
|
||||
|
@ -191,12 +191,12 @@ void AsynchronousMetrics::update()
|
||||
"Cannot get replica delay for table: " + backQuoteIfNeed(db.first) + "." + backQuoteIfNeed(iterator->name()));
|
||||
}
|
||||
|
||||
calculateMax(max_part_count_for_partition, table_replicated_merge_tree->getData().getMaxPartsCountForPartition());
|
||||
calculateMax(max_part_count_for_partition, table_replicated_merge_tree->getMaxPartsCountForPartition());
|
||||
}
|
||||
|
||||
if (table_merge_tree)
|
||||
{
|
||||
calculateMax(max_part_count_for_partition, table_merge_tree->getData().getMaxPartsCountForPartition());
|
||||
calculateMax(max_part_count_for_partition, table_merge_tree->getMaxPartsCountForPartition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <Interpreters/Cluster.h>
|
||||
#include <Interpreters/InterserverIOHandler.h>
|
||||
#include <Interpreters/Compiler.h>
|
||||
#include <Interpreters/SettingsConstraints.h>
|
||||
#include <Interpreters/SystemLog.h>
|
||||
#include <Interpreters/QueryLog.h>
|
||||
#include <Interpreters/QueryThreadLog.h>
|
||||
@ -301,6 +302,8 @@ private:
|
||||
|
||||
|
||||
Context::Context() = default;
|
||||
Context::Context(const Context &) = default;
|
||||
Context & Context::operator=(const Context &) = default;
|
||||
|
||||
|
||||
Context Context::createGlobal(std::unique_ptr<IRuntimeComponentsFactory> runtime_components_factory)
|
||||
@ -623,14 +626,26 @@ void Context::calculateUserSettings()
|
||||
/// NOTE: we ignore global_context settings (from which it is usually copied)
|
||||
/// NOTE: global_context settings are immutable and not auto updated
|
||||
settings = Settings();
|
||||
settings_constraints = nullptr;
|
||||
|
||||
/// 2) Apply settings from default profile
|
||||
auto default_profile_name = getDefaultProfileName();
|
||||
if (profile != default_profile_name)
|
||||
settings.setProfile(default_profile_name, *shared->users_config);
|
||||
setProfile(default_profile_name);
|
||||
|
||||
/// 3) Apply settings from current user
|
||||
setProfile(profile);
|
||||
}
|
||||
|
||||
|
||||
void Context::setProfile(const String & profile)
|
||||
{
|
||||
settings.setProfile(profile, *shared->users_config);
|
||||
|
||||
auto new_constraints
|
||||
= settings_constraints ? std::make_shared<SettingsConstraints>(*settings_constraints) : std::make_shared<SettingsConstraints>();
|
||||
new_constraints->setProfile(profile, *shared->users_config);
|
||||
settings_constraints = std::move(new_constraints);
|
||||
}
|
||||
|
||||
|
||||
@ -691,9 +706,8 @@ void Context::checkDatabaseAccessRightsImpl(const std::string & database_name) c
|
||||
throw Exception("Access denied to database " + database_name + " for user " + client_info.current_user , ErrorCodes::DATABASE_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
void Context::addDependency(const DatabaseAndTableName & from, const DatabaseAndTableName & where)
|
||||
void Context::addDependencyUnsafe(const DatabaseAndTableName & from, const DatabaseAndTableName & where)
|
||||
{
|
||||
auto lock = getLock();
|
||||
checkDatabaseAccessRightsImpl(from.first);
|
||||
checkDatabaseAccessRightsImpl(where.first);
|
||||
shared->view_dependencies[from].insert(where);
|
||||
@ -704,9 +718,14 @@ void Context::addDependency(const DatabaseAndTableName & from, const DatabaseAnd
|
||||
table->updateDependencies();
|
||||
}
|
||||
|
||||
void Context::removeDependency(const DatabaseAndTableName & from, const DatabaseAndTableName & where)
|
||||
void Context::addDependency(const DatabaseAndTableName & from, const DatabaseAndTableName & where)
|
||||
{
|
||||
auto lock = getLock();
|
||||
addDependencyUnsafe(from, where);
|
||||
}
|
||||
|
||||
void Context::removeDependencyUnsafe(const DatabaseAndTableName & from, const DatabaseAndTableName & where)
|
||||
{
|
||||
checkDatabaseAccessRightsImpl(from.first);
|
||||
checkDatabaseAccessRightsImpl(where.first);
|
||||
shared->view_dependencies[from].erase(where);
|
||||
@ -717,6 +736,12 @@ void Context::removeDependency(const DatabaseAndTableName & from, const Database
|
||||
table->updateDependencies();
|
||||
}
|
||||
|
||||
void Context::removeDependency(const DatabaseAndTableName & from, const DatabaseAndTableName & where)
|
||||
{
|
||||
auto lock = getLock();
|
||||
removeDependencyUnsafe(from, where);
|
||||
}
|
||||
|
||||
Dependencies Context::getDependencies(const String & database_name, const String & table_name) const
|
||||
{
|
||||
auto lock = getLock();
|
||||
@ -1031,30 +1056,58 @@ void Context::setSettings(const Settings & settings_)
|
||||
}
|
||||
|
||||
|
||||
void Context::setSetting(const String & name, const Field & value)
|
||||
void Context::setSetting(const String & name, const String & value)
|
||||
{
|
||||
auto lock = getLock();
|
||||
if (name == "profile")
|
||||
{
|
||||
auto lock = getLock();
|
||||
settings.setProfile(value.safeGet<String>(), *shared->users_config);
|
||||
setProfile(value);
|
||||
return;
|
||||
}
|
||||
else
|
||||
settings.set(name, value);
|
||||
}
|
||||
|
||||
|
||||
void Context::setSetting(const String & name, const std::string & value)
|
||||
void Context::setSetting(const String & name, const Field & value)
|
||||
{
|
||||
auto lock = getLock();
|
||||
if (name == "profile")
|
||||
{
|
||||
auto lock = getLock();
|
||||
settings.setProfile(value, *shared->users_config);
|
||||
setProfile(value.safeGet<String>());
|
||||
return;
|
||||
}
|
||||
else
|
||||
settings.set(name, value);
|
||||
}
|
||||
|
||||
|
||||
void Context::applySettingChange(const SettingChange & change)
|
||||
{
|
||||
setSetting(change.name, change.value);
|
||||
}
|
||||
|
||||
|
||||
void Context::applySettingsChanges(const SettingsChanges & changes)
|
||||
{
|
||||
auto lock = getLock();
|
||||
for (const SettingChange & change : changes)
|
||||
applySettingChange(change);
|
||||
}
|
||||
|
||||
|
||||
void Context::checkSettingsConstraints(const SettingChange & change)
|
||||
{
|
||||
if (settings_constraints)
|
||||
settings_constraints->check(settings, change);
|
||||
}
|
||||
|
||||
|
||||
void Context::checkSettingsConstraints(const SettingsChanges & changes)
|
||||
{
|
||||
if (settings_constraints)
|
||||
settings_constraints->check(settings, changes);
|
||||
}
|
||||
|
||||
|
||||
String Context::getCurrentDatabase() const
|
||||
{
|
||||
return current_database;
|
||||
@ -1678,7 +1731,7 @@ const Schema & Context::getSchema(const String & name) const
|
||||
|
||||
if (!shared->merge_tree_schema_selector)
|
||||
{
|
||||
constexpr auto config_name = "storage_configuration.schemes";
|
||||
constexpr auto config_name = "storage_configuration.schemas";
|
||||
auto & config = getConfigRef();
|
||||
|
||||
shared->merge_tree_schema_selector = std::make_unique<SchemaSelector>(config, config_name, getDiskSelector());
|
||||
|
@ -79,6 +79,8 @@ class ActionLocksManager;
|
||||
using ActionLocksManagerPtr = std::shared_ptr<ActionLocksManager>;
|
||||
class ShellCommand;
|
||||
class ICompressionCodec;
|
||||
class SettingsConstraints;
|
||||
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
|
||||
@ -126,6 +128,7 @@ private:
|
||||
std::shared_ptr<QuotaForIntervals> quota; /// Current quota. By default - empty quota, that have no limits.
|
||||
String current_database;
|
||||
Settings settings; /// Setting for query execution.
|
||||
std::shared_ptr<const SettingsConstraints> settings_constraints;
|
||||
using ProgressCallback = std::function<void(const Progress & progress)>;
|
||||
ProgressCallback progress_callback; /// Callback for tracking progress of query execution.
|
||||
QueryStatus * process_list_elem = nullptr; /// For tracking total resource usage for query.
|
||||
@ -162,8 +165,8 @@ public:
|
||||
static Context createGlobal(std::unique_ptr<IRuntimeComponentsFactory> runtime_components_factory);
|
||||
static Context createGlobal();
|
||||
|
||||
Context(const Context &) = default;
|
||||
Context & operator=(const Context &) = default;
|
||||
Context(const Context &);
|
||||
Context & operator=(const Context &);
|
||||
~Context();
|
||||
|
||||
String getPath() const;
|
||||
@ -213,6 +216,10 @@ public:
|
||||
void removeDependency(const DatabaseAndTableName & from, const DatabaseAndTableName & where);
|
||||
Dependencies getDependencies(const String & database_name, const String & table_name) const;
|
||||
|
||||
/// Functions where we can lock the context manually
|
||||
void addDependencyUnsafe(const DatabaseAndTableName & from, const DatabaseAndTableName & where);
|
||||
void removeDependencyUnsafe(const DatabaseAndTableName & from, const DatabaseAndTableName & where);
|
||||
|
||||
/// Checking the existence of the table/database. Database can be empty - in this case the current database is used.
|
||||
bool isTableExist(const String & database_name, const String & table_name) const;
|
||||
bool isDatabaseExist(const String & database_name) const;
|
||||
@ -264,11 +271,15 @@ public:
|
||||
Settings getSettings() const;
|
||||
void setSettings(const Settings & settings_);
|
||||
|
||||
/// Set a setting by name.
|
||||
/// Set settings by name.
|
||||
void setSetting(const String & name, const String & value);
|
||||
void setSetting(const String & name, const Field & value);
|
||||
void applySettingChange(const SettingChange & change);
|
||||
void applySettingsChanges(const SettingsChanges & changes);
|
||||
|
||||
/// Set a setting by name. Read the value in text form from a string (for example, from a config, or from a URL parameter).
|
||||
void setSetting(const String & name, const std::string & value);
|
||||
/// Checks the constraints.
|
||||
void checkSettingsConstraints(const SettingChange & change);
|
||||
void checkSettingsConstraints(const SettingsChanges & changes);
|
||||
|
||||
const EmbeddedDictionaries & getEmbeddedDictionaries() const;
|
||||
const ExternalDictionaries & getExternalDictionaries() const;
|
||||
@ -489,6 +500,8 @@ private:
|
||||
*/
|
||||
void checkDatabaseAccessRightsImpl(const std::string & database_name) const;
|
||||
|
||||
void setProfile(const String & profile);
|
||||
|
||||
EmbeddedDictionaries & getEmbeddedDictionariesImpl(bool throw_on_error) const;
|
||||
ExternalDictionaries & getExternalDictionariesImpl(bool throw_on_error) const;
|
||||
ExternalModels & getExternalModelsImpl(bool throw_on_error) const;
|
||||
|
@ -513,13 +513,14 @@ void ExpressionAnalyzer::addJoinAction(ExpressionActionsPtr & actions, bool only
|
||||
columns_added_by_join_list));
|
||||
}
|
||||
|
||||
static void appendRequiredColumns(NameSet & required_columns, const Block & sample, const AnalyzedJoin & analyzed_join)
|
||||
static void appendRequiredColumns(
|
||||
NameSet & required_columns, const Block & sample, const Names & key_names_right, const JoinedColumnsList & columns_added_by_join)
|
||||
{
|
||||
for (auto & column : analyzed_join.key_names_right)
|
||||
for (auto & column : key_names_right)
|
||||
if (!sample.has(column))
|
||||
required_columns.insert(column);
|
||||
|
||||
for (auto & column : analyzed_join.columns_from_joined_table)
|
||||
for (auto & column : columns_added_by_join)
|
||||
if (!sample.has(column.name_and_type.name))
|
||||
required_columns.insert(column.name_and_type.name);
|
||||
}
|
||||
@ -606,7 +607,8 @@ bool ExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, bool only_ty
|
||||
Names action_columns = joined_block_actions->getRequiredColumns();
|
||||
NameSet required_columns(action_columns.begin(), action_columns.end());
|
||||
|
||||
appendRequiredColumns(required_columns, joined_block_actions->getSampleBlock(), analyzed_join);
|
||||
appendRequiredColumns(
|
||||
required_columns, joined_block_actions->getSampleBlock(), analyzed_join.key_names_right, columns_added_by_join);
|
||||
|
||||
Names original_columns = analyzed_join.getOriginalColumnNames(required_columns);
|
||||
|
||||
|
@ -383,7 +383,7 @@ ColumnsDescription InterpreterCreateQuery::setColumns(
|
||||
else if (!create.as_table.empty())
|
||||
{
|
||||
columns = as_storage->getColumns();
|
||||
indices = as_storage->getIndicesDescription();
|
||||
indices = as_storage->getIndices();
|
||||
}
|
||||
else if (create.select)
|
||||
{
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include <Parsers/ASTSelectWithUnionQuery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <Parsers/ParserSelectQuery.h>
|
||||
#include <Parsers/ExpressionListParsers.h>
|
||||
#include <Parsers/parseQuery.h>
|
||||
|
||||
#include <Interpreters/InterpreterSelectQuery.h>
|
||||
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
|
||||
@ -43,8 +45,7 @@
|
||||
|
||||
#include <Storages/MergeTree/MergeTreeWhereOptimizer.h>
|
||||
#include <Storages/IStorage.h>
|
||||
#include <Storages/StorageMergeTree.h>
|
||||
#include <Storages/StorageReplicatedMergeTree.h>
|
||||
#include <Storages/MergeTree/MergeTreeData.h>
|
||||
|
||||
#include <TableFunctions/ITableFunction.h>
|
||||
#include <TableFunctions/TableFunctionFactory.h>
|
||||
@ -590,13 +591,11 @@ void InterpreterSelectQuery::executeImpl(Pipeline & pipeline, const BlockInputSt
|
||||
|
||||
/// Try transferring some condition from WHERE to PREWHERE if enabled and viable
|
||||
if (settings.optimize_move_to_prewhere && query.where() && !query.prewhere() && !query.final())
|
||||
MergeTreeWhereOptimizer{query_info, context, merge_tree.getData(), query_analyzer->getRequiredSourceColumns(), log};
|
||||
MergeTreeWhereOptimizer{query_info, context, merge_tree, query_analyzer->getRequiredSourceColumns(), log};
|
||||
};
|
||||
|
||||
if (const StorageMergeTree * merge_tree = dynamic_cast<const StorageMergeTree *>(storage.get()))
|
||||
optimize_prewhere(*merge_tree);
|
||||
else if (const StorageReplicatedMergeTree * replicated_merge_tree = dynamic_cast<const StorageReplicatedMergeTree *>(storage.get()))
|
||||
optimize_prewhere(*replicated_merge_tree);
|
||||
if (const MergeTreeData * merge_tree_data = dynamic_cast<const MergeTreeData *>(storage.get()))
|
||||
optimize_prewhere(*merge_tree_data);
|
||||
}
|
||||
|
||||
AnalysisResult expressions;
|
||||
@ -1562,18 +1561,18 @@ void InterpreterSelectQuery::executePreLimit(Pipeline & pipeline)
|
||||
void InterpreterSelectQuery::executeLimitBy(Pipeline & pipeline)
|
||||
{
|
||||
auto & query = getSelectQuery();
|
||||
if (!query.limitByValue() || !query.limitBy())
|
||||
if (!query.limitByLength() || !query.limitBy())
|
||||
return;
|
||||
|
||||
Names columns;
|
||||
for (const auto & elem : query.limitBy()->children)
|
||||
columns.emplace_back(elem->getColumnName());
|
||||
|
||||
UInt64 value = getLimitUIntValue(query.limitByValue(), context);
|
||||
UInt64 length = getLimitUIntValue(query.limitByLength(), context);
|
||||
UInt64 offset = (query.limitByOffset() ? getLimitUIntValue(query.limitByOffset(), context) : 0);
|
||||
|
||||
pipeline.transform([&](auto & stream)
|
||||
{
|
||||
stream = std::make_shared<LimitByBlockInputStream>(stream, value, columns);
|
||||
stream = std::make_shared<LimitByBlockInputStream>(stream, length, offset, columns);
|
||||
});
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user