Merge branch 'master' into idisk-seekable-readbuffer

This commit is contained in:
alexey-milovidov 2020-02-02 03:54:50 +03:00 committed by GitHub
commit 3a1e3f612e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
832 changed files with 8448 additions and 5522 deletions

6
.gitignore vendored
View File

@ -13,12 +13,14 @@
/build_* /build_*
/build-* /build-*
/docs/build /docs/build
/docs/publish
/docs/edit /docs/edit
/docs/tools/venv/ /docs/tools/venv/
/docs/en/development/build/
/docs/ru/development/build/
/docs/en/single.md /docs/en/single.md
/docs/ru/single.md /docs/ru/single.md
/docs/zh/single.md
/docs/ja/single.md
/docs/fa/single.md
# callgrind files # callgrind files
callgrind.out.* callgrind.out.*

View File

@ -1,3 +1,302 @@
## ClickHouse release v20.1
### ClickHouse release v20.1.2.4, 2020-01-22
### Backward Incompatible Change
* Make the setting `merge_tree_uniform_read_distribution` obsolete. The server still recognizes this setting but it has no effect. [#8308](https://github.com/ClickHouse/ClickHouse/pull/8308) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Changed return type of the function `greatCircleDistance` to `Float32` because now the result of calculation is `Float32`. [#7993](https://github.com/ClickHouse/ClickHouse/pull/7993) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Now it's expected that query parameters are represented in "escaped" format. For example, to pass string `a<tab>b` you have to write `a\tb` or `a\<tab>b` and respectively, `a%5Ctb` or `a%5C%09b` in URL. This is needed to add the possibility to pass NULL as `\N`. This fixes [#7488](https://github.com/ClickHouse/ClickHouse/issues/7488). [#8517](https://github.com/ClickHouse/ClickHouse/pull/8517) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Enable `use_minimalistic_part_header_in_zookeeper` setting for `ReplicatedMergeTree` by default. This will significantly reduce amount of data stored in ZooKeeper. This setting is supported since version 19.1 and we already use it in production in multiple services without any issues for more than half a year. Disable this setting if you have a chance to downgrade to versions older than 19.1. [#6850](https://github.com/ClickHouse/ClickHouse/pull/6850) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Data skipping indices are production ready and enabled by default. The settings `allow_experimental_data_skipping_indices`, `allow_experimental_cross_to_join_conversion` and `allow_experimental_multiple_joins_emulation` are now obsolete and do nothing. [#7974](https://github.com/ClickHouse/ClickHouse/pull/7974) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Add new `ANY JOIN` logic for `StorageJoin` consistent with `JOIN` operation. To upgrade without changes in behaviour you need add `SETTINGS any_join_distinct_right_table_keys = 1` to Engine Join tables metadata or recreate these tables after upgrade. [#8400](https://github.com/ClickHouse/ClickHouse/pull/8400) ([Artem Zuikov](https://github.com/4ertus2))
* Require server to be restarted to apply the changes in logging configuration. This is a temporary workaround to avoid the bug where the server logs to a deleted log file (see [#8696](https://github.com/ClickHouse/ClickHouse/issues/8696)). [#8707](https://github.com/ClickHouse/ClickHouse/pull/8707) ([Alexander Kuzmenkov](https://github.com/akuzm))
### New Feature
* Added information about part paths to `system.merges`. [#8043](https://github.com/ClickHouse/ClickHouse/pull/8043) ([Vladimir Chebotarev](https://github.com/excitoon))
* Add ability to execute `SYSTEM RELOAD DICTIONARY` query in `ON CLUSTER` mode. [#8288](https://github.com/ClickHouse/ClickHouse/pull/8288) ([Guillaume Tassery](https://github.com/YiuRULE))
* Add ability to execute `CREATE DICTIONARY` queries in `ON CLUSTER` mode. [#8163](https://github.com/ClickHouse/ClickHouse/pull/8163) ([alesapin](https://github.com/alesapin))
* Now user's profile in `users.xml` can inherit multiple profiles. [#8343](https://github.com/ClickHouse/ClickHouse/pull/8343) ([Mikhail f. Shiryaev](https://github.com/Felixoid))
* Added `system.stack_trace` table that allows to look at stack traces of all server threads. This is useful for developers to introspect server state. This fixes [#7576](https://github.com/ClickHouse/ClickHouse/issues/7576). [#8344](https://github.com/ClickHouse/ClickHouse/pull/8344) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Add `DateTime64` datatype with configurable sub-second precision. [#7170](https://github.com/ClickHouse/ClickHouse/pull/7170) ([Vasily Nemkov](https://github.com/Enmk))
* Add table function `clusterAllReplicas` which allows to query all the nodes in the cluster. [#8493](https://github.com/ClickHouse/ClickHouse/pull/8493) ([kiran sunkari](https://github.com/kiransunkari))
* Add aggregate function `categoricalInformationValue` which calculates the information value of a discrete feature. [#8117](https://github.com/ClickHouse/ClickHouse/pull/8117) ([hcz](https://github.com/hczhcz))
* Speed up parsing of data files in `CSV`, `TSV` and `JSONEachRow` format by doing it in parallel. [#7780](https://github.com/ClickHouse/ClickHouse/pull/7780) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Add function `bankerRound` which performs banker's rounding. [#8112](https://github.com/ClickHouse/ClickHouse/pull/8112) ([hcz](https://github.com/hczhcz))
* Support more languages in embedded dictionary for region names: 'ru', 'en', 'ua', 'uk', 'by', 'kz', 'tr', 'de', 'uz', 'lv', 'lt', 'et', 'pt', 'he', 'vi'. [#8189](https://github.com/ClickHouse/ClickHouse/pull/8189) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Improvements in consistency of `ANY JOIN` logic. Now `t1 ANY LEFT JOIN t2` equals `t2 ANY RIGHT JOIN t1`. [#7665](https://github.com/ClickHouse/ClickHouse/pull/7665) ([Artem Zuikov](https://github.com/4ertus2))
* Add setting `any_join_distinct_right_table_keys` which enables old behaviour for `ANY INNER JOIN`. [#7665](https://github.com/ClickHouse/ClickHouse/pull/7665) ([Artem Zuikov](https://github.com/4ertus2))
* Add new `SEMI` and `ANTI JOIN`. Old `ANY INNER JOIN` behaviour now available as `SEMI LEFT JOIN`. [#7665](https://github.com/ClickHouse/ClickHouse/pull/7665) ([Artem Zuikov](https://github.com/4ertus2))
* Added `Distributed` format for `File` engine and `file` table function which allows to read from `.bin` files generated by asynchronous inserts into `Distributed` table. [#8535](https://github.com/ClickHouse/ClickHouse/pull/8535) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Add optional reset column argument for `runningAccumulate` which allows to reset aggregation results for each new key value. [#8326](https://github.com/ClickHouse/ClickHouse/pull/8326) ([Sergey Kononenko](https://github.com/kononencheg))
* Add ability to use ClickHouse as Prometheus endpoint. [#7900](https://github.com/ClickHouse/ClickHouse/pull/7900) ([vdimir](https://github.com/Vdimir))
* Add section `<remote_url_allow_hosts>` in `config.xml` which restricts allowed hosts for remote table engines and table functions `URL`, `S3`, `HDFS`. [#7154](https://github.com/ClickHouse/ClickHouse/pull/7154) ([Mikhail Korotov](https://github.com/millb))
* Added function `greatCircleAngle` which calculates the distance on a sphere in degrees. [#8105](https://github.com/ClickHouse/ClickHouse/pull/8105) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Changed Earth radius to be consistent with H3 library. [#8105](https://github.com/ClickHouse/ClickHouse/pull/8105) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Added `JSONCompactEachRow` and `JSONCompactEachRowWithNamesAndTypes` formats for input and output. [#7841](https://github.com/ClickHouse/ClickHouse/pull/7841) ([Mikhail Korotov](https://github.com/millb))
* Added feature for file-related table engines and table functions (`File`, `S3`, `URL`, `HDFS`) which allows to read and write `gzip` files based on additional engine parameter or file extension. [#7840](https://github.com/ClickHouse/ClickHouse/pull/7840) ([Andrey Bodrov](https://github.com/apbodrov))
* Added the `randomASCII(length)` function, generating a string with a random set of [ASCII](https://en.wikipedia.org/wiki/ASCII#Printable_characters) printable characters. [#8401](https://github.com/ClickHouse/ClickHouse/pull/8401) ([BayoNet](https://github.com/BayoNet))
* Added function `JSONExtractArrayRaw` which returns an array on unparsed json array elements from `JSON` string. [#8081](https://github.com/ClickHouse/ClickHouse/pull/8081) ([Oleg Matrokhin](https://github.com/errx))
* Add `arrayZip` function which allows to combine multiple arrays of equal lengths into one array of tuples. [#8149](https://github.com/ClickHouse/ClickHouse/pull/8149) ([Winter Zhang](https://github.com/zhang2014))
* Add ability to move data between disks according to configured `TTL`-expressions for `*MergeTree` table engines family. [#8140](https://github.com/ClickHouse/ClickHouse/pull/8140) ([Vladimir Chebotarev](https://github.com/excitoon))
* Added new aggregate function `avgWeighted` which allows to calculate weighted average. [#7898](https://github.com/ClickHouse/ClickHouse/pull/7898) ([Andrey Bodrov](https://github.com/apbodrov))
* Now parallel parsing is enabled by default for `TSV`, `TSKV`, `CSV` and `JSONEachRow` formats. [#7894](https://github.com/ClickHouse/ClickHouse/pull/7894) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov))
* Add several geo functions from `H3` library: `h3GetResolution`, `h3EdgeAngle`, `h3EdgeLength`, `h3IsValid` and `h3kRing`. [#8034](https://github.com/ClickHouse/ClickHouse/pull/8034) ([Konstantin Malanchev](https://github.com/hombit))
* Added support for brotli (`br`) compression in file-related storages and table functions. This fixes [#8156](https://github.com/ClickHouse/ClickHouse/issues/8156). [#8526](https://github.com/ClickHouse/ClickHouse/pull/8526) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Add `groupBit*` functions for the `SimpleAggregationFunction` type. [#8485](https://github.com/ClickHouse/ClickHouse/pull/8485) ([Guillaume Tassery](https://github.com/YiuRULE))
### Bug Fix
* Fix rename of tables with `Distributed` engine. Fixes issue [#7868](https://github.com/ClickHouse/ClickHouse/issues/7868). [#8306](https://github.com/ClickHouse/ClickHouse/pull/8306) ([tavplubix](https://github.com/tavplubix))
* Now dictionaries support `EXPRESSION` for attributes in arbitrary string in non-ClickHouse SQL dialect. [#8098](https://github.com/ClickHouse/ClickHouse/pull/8098) ([alesapin](https://github.com/alesapin))
* Fix broken `INSERT SELECT FROM mysql(...)` query. This fixes [#8070](https://github.com/ClickHouse/ClickHouse/issues/8070) and [#7960](https://github.com/ClickHouse/ClickHouse/issues/7960). [#8234](https://github.com/ClickHouse/ClickHouse/pull/8234) ([tavplubix](https://github.com/tavplubix))
* Fix error "Mismatch column sizes" when inserting default `Tuple` from `JSONEachRow`. This fixes [#5653](https://github.com/ClickHouse/ClickHouse/issues/5653). [#8606](https://github.com/ClickHouse/ClickHouse/pull/8606) ([tavplubix](https://github.com/tavplubix))
* Now an exception will be thrown in case of using `WITH TIES` alongside `LIMIT BY`. Also add ability to use `TOP` with `LIMIT BY`. This fixes [#7472](https://github.com/ClickHouse/ClickHouse/issues/7472). [#7637](https://github.com/ClickHouse/ClickHouse/pull/7637) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov))
* Fix unintendent dependency from fresh glibc version in `clickhouse-odbc-bridge` binary. [#8046](https://github.com/ClickHouse/ClickHouse/pull/8046) ([Amos Bird](https://github.com/amosbird))
* Fix bug in check function of `*MergeTree` engines family. Now it doesn't fail in case when we have equal amount of rows in last granule and last mark (non-final). [#8047](https://github.com/ClickHouse/ClickHouse/pull/8047) ([alesapin](https://github.com/alesapin))
* Fix insert into `Enum*` columns after `ALTER` query, when underlying numeric type is equal to table specified type. This fixes [#7836](https://github.com/ClickHouse/ClickHouse/issues/7836). [#7908](https://github.com/ClickHouse/ClickHouse/pull/7908) ([Anton Popov](https://github.com/CurtizJ))
* Allowed non-constant negative "size" argument for function `substring`. It was not allowed by mistake. This fixes [#4832](https://github.com/ClickHouse/ClickHouse/issues/4832). [#7703](https://github.com/ClickHouse/ClickHouse/pull/7703) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix parsing bug when wrong number of arguments passed to `(O|J)DBC` table engine. [#7709](https://github.com/ClickHouse/ClickHouse/pull/7709) ([alesapin](https://github.com/alesapin))
* Using command name of the running clickhouse process when sending logs to syslog. In previous versions, empty string was used instead of command name. [#8460](https://github.com/ClickHouse/ClickHouse/pull/8460) ([Michael Nacharov](https://github.com/mnach))
* Fix check of allowed hosts for `localhost`. This PR fixes the solution provided in [#8241](https://github.com/ClickHouse/ClickHouse/pull/8241). [#8342](https://github.com/ClickHouse/ClickHouse/pull/8342) ([Vitaly Baranov](https://github.com/vitlibar))
* Fix rare crash in `argMin` and `argMax` functions for long string arguments, when result is used in `runningAccumulate` function. This fixes [#8325](https://github.com/ClickHouse/ClickHouse/issues/8325) [#8341](https://github.com/ClickHouse/ClickHouse/pull/8341) ([dinosaur](https://github.com/769344359))
* Fix memory overcommit for tables with `Buffer` engine. [#8345](https://github.com/ClickHouse/ClickHouse/pull/8345) ([Azat Khuzhin](https://github.com/azat))
* Fixed potential bug in functions that can take `NULL` as one of the arguments and return non-NULL. [#8196](https://github.com/ClickHouse/ClickHouse/pull/8196) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Better metrics calculations in thread pool for background processes for `MergeTree` table engines. [#8194](https://github.com/ClickHouse/ClickHouse/pull/8194) ([Vladimir Chebotarev](https://github.com/excitoon))
* Fix function `IN` inside `WHERE` statement when row-level table filter is present. Fixes [#6687](https://github.com/ClickHouse/ClickHouse/issues/6687) [#8357](https://github.com/ClickHouse/ClickHouse/pull/8357) ([Ivan](https://github.com/abyss7))
* Now an exception is thrown if the integral value is not parsed completely for settings values. [#7678](https://github.com/ClickHouse/ClickHouse/pull/7678) ([Mikhail Korotov](https://github.com/millb))
* Fix exception when aggregate function is used in query to distributed table with more than two local shards. [#8164](https://github.com/ClickHouse/ClickHouse/pull/8164) ([小路](https://github.com/nicelulu))
* Now bloom filter can handle zero length arrays and doesn't perform redundant calculations. [#8242](https://github.com/ClickHouse/ClickHouse/pull/8242) ([achimbab](https://github.com/achimbab))
* Fixed checking if a client host is allowed by matching the client host to `host_regexp` specified in `users.xml`. [#8241](https://github.com/ClickHouse/ClickHouse/pull/8241) ([Vitaly Baranov](https://github.com/vitlibar))
* Relax ambiguous column check that leads to false positives in multiple `JOIN ON` section. [#8385](https://github.com/ClickHouse/ClickHouse/pull/8385) ([Artem Zuikov](https://github.com/4ertus2))
* Fixed possible server crash (`std::terminate`) when the server cannot send or write data in `JSON` or `XML` format with values of `String` data type (that require `UTF-8` validation) or when compressing result data with Brotli algorithm or in some other rare cases. This fixes [#7603](https://github.com/ClickHouse/ClickHouse/issues/7603) [#8384](https://github.com/ClickHouse/ClickHouse/pull/8384) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix race condition in `StorageDistributedDirectoryMonitor` found by CI. This fixes [#8364](https://github.com/ClickHouse/ClickHouse/issues/8364). [#8383](https://github.com/ClickHouse/ClickHouse/pull/8383) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Now background merges in `*MergeTree` table engines family preserve storage policy volume order more accurately. [#8549](https://github.com/ClickHouse/ClickHouse/pull/8549) ([Vladimir Chebotarev](https://github.com/excitoon))
* Now table engine `Kafka` works properly with `Native` format. This fixes [#6731](https://github.com/ClickHouse/ClickHouse/issues/6731) [#7337](https://github.com/ClickHouse/ClickHouse/issues/7337) [#8003](https://github.com/ClickHouse/ClickHouse/issues/8003). [#8016](https://github.com/ClickHouse/ClickHouse/pull/8016) ([filimonov](https://github.com/filimonov))
* Fixed formats with headers (like `CSVWithNames`) which were throwing exception about EOF for table engine `Kafka`. [#8016](https://github.com/ClickHouse/ClickHouse/pull/8016) ([filimonov](https://github.com/filimonov))
* Fixed a bug with making set from subquery in right part of `IN` section. This fixes [#5767](https://github.com/ClickHouse/ClickHouse/issues/5767) and [#2542](https://github.com/ClickHouse/ClickHouse/issues/2542). [#7755](https://github.com/ClickHouse/ClickHouse/pull/7755) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov))
* Fix possible crash while reading from storage `File`. [#7756](https://github.com/ClickHouse/ClickHouse/pull/7756) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Fixed reading of the files in `Parquet` format containing columns of type `list`. [#8334](https://github.com/ClickHouse/ClickHouse/pull/8334) ([maxulan](https://github.com/maxulan))
* Fix error `Not found column` for distributed queries with `PREWHERE` condition dependent on sampling key if `max_parallel_replicas > 1`. [#7913](https://github.com/ClickHouse/ClickHouse/pull/7913) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Fix error `Not found column` if query used `PREWHERE` dependent on table's alias and the result set was empty because of primary key condition. [#7911](https://github.com/ClickHouse/ClickHouse/pull/7911) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Fixed return type for functions `rand` and `randConstant` in case of `Nullable` argument. Now functions always return `UInt32` and never `Nullable(UInt32)`. [#8204](https://github.com/ClickHouse/ClickHouse/pull/8204) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Disabled predicate push-down for `WITH FILL` expression. This fixes [#7784](https://github.com/ClickHouse/ClickHouse/issues/7784). [#7789](https://github.com/ClickHouse/ClickHouse/pull/7789) ([Winter Zhang](https://github.com/zhang2014))
* Fixed incorrect `count()` result for `SummingMergeTree` when `FINAL` section is used. [#3280](https://github.com/ClickHouse/ClickHouse/issues/3280) [#7786](https://github.com/ClickHouse/ClickHouse/pull/7786) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov))
* Fix possible incorrect result for constant functions from remote servers. It happened for queries with functions like `version()`, `uptime()`, etc. which returns different constant values for different servers. This fixes [#7666](https://github.com/ClickHouse/ClickHouse/issues/7666). [#7689](https://github.com/ClickHouse/ClickHouse/pull/7689) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Fix complicated bug in push-down predicate optimization which leads to wrong results. This fixes a lot of issues on push-down predicate optimization. [#8503](https://github.com/ClickHouse/ClickHouse/pull/8503) ([Winter Zhang](https://github.com/zhang2014))
* Fix crash in `CREATE TABLE .. AS dictionary` query. [#8508](https://github.com/ClickHouse/ClickHouse/pull/8508) ([Azat Khuzhin](https://github.com/azat))
* Several improvements ClickHouse grammar in `.g4` file. [#8294](https://github.com/ClickHouse/ClickHouse/pull/8294) ([taiyang-li](https://github.com/taiyang-li))
* Fix bug that leads to crashes in `JOIN`s with tables with engine `Join`. This fixes [#7556](https://github.com/ClickHouse/ClickHouse/issues/7556) [#8254](https://github.com/ClickHouse/ClickHouse/issues/8254) [#7915](https://github.com/ClickHouse/ClickHouse/issues/7915) [#8100](https://github.com/ClickHouse/ClickHouse/issues/8100). [#8298](https://github.com/ClickHouse/ClickHouse/pull/8298) ([Artem Zuikov](https://github.com/4ertus2))
* Fix redundant dictionaries reload on `CREATE DATABASE`. [#7916](https://github.com/ClickHouse/ClickHouse/pull/7916) ([Azat Khuzhin](https://github.com/azat))
* Limit maximum number of streams for read from `StorageFile` and `StorageHDFS`. Fixes https://github.com/ClickHouse/ClickHouse/issues/7650. [#7981](https://github.com/ClickHouse/ClickHouse/pull/7981) ([alesapin](https://github.com/alesapin))
* Fix bug in `ALTER ... MODIFY ... CODEC` query, when user specify both default expression and codec. Fixes [8593](https://github.com/ClickHouse/ClickHouse/issues/8593). [#8614](https://github.com/ClickHouse/ClickHouse/pull/8614) ([alesapin](https://github.com/alesapin))
* Fix error in background merge of columns with `SimpleAggregateFunction(LowCardinality)` type. [#8613](https://github.com/ClickHouse/ClickHouse/pull/8613) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Fixed type check in function `toDateTime64`. [#8375](https://github.com/ClickHouse/ClickHouse/pull/8375) ([Vasily Nemkov](https://github.com/Enmk))
* Now server do not crash on `LEFT` or `FULL JOIN` with and Join engine and unsupported `join_use_nulls` settings. [#8479](https://github.com/ClickHouse/ClickHouse/pull/8479) ([Artem Zuikov](https://github.com/4ertus2))
* Now `DROP DICTIONARY IF EXISTS db.dict` query doesn't throw exception if `db` doesn't exist. [#8185](https://github.com/ClickHouse/ClickHouse/pull/8185) ([Vitaly Baranov](https://github.com/vitlibar))
* Fix possible crashes in table functions (`file`, `mysql`, `remote`) caused by usage of reference to removed `IStorage` object. Fix incorrect parsing of columns specified at insertion into table function. [#7762](https://github.com/ClickHouse/ClickHouse/pull/7762) ([tavplubix](https://github.com/tavplubix))
* Ensure network be up before starting `clickhouse-server`. This fixes [#7507](https://github.com/ClickHouse/ClickHouse/issues/7507). [#8570](https://github.com/ClickHouse/ClickHouse/pull/8570) ([Zhichang Yu](https://github.com/yuzhichang))
* Fix timeouts handling for secure connections, so queries doesn't hang indefenitely. This fixes [#8126](https://github.com/ClickHouse/ClickHouse/issues/8126). [#8128](https://github.com/ClickHouse/ClickHouse/pull/8128) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix `clickhouse-copier`'s redundant contention between concurrent workers. [#7816](https://github.com/ClickHouse/ClickHouse/pull/7816) ([Ding Xiang Fei](https://github.com/dingxiangfei2009))
* Now mutations doesn't skip attached parts, even if their mutation version were larger than current mutation version. [#7812](https://github.com/ClickHouse/ClickHouse/pull/7812) ([Zhichang Yu](https://github.com/yuzhichang)) [#8250](https://github.com/ClickHouse/ClickHouse/pull/8250) ([alesapin](https://github.com/alesapin))
* Ignore redundant copies of `*MergeTree` data parts after move to another disk and server restart. [#7810](https://github.com/ClickHouse/ClickHouse/pull/7810) ([Vladimir Chebotarev](https://github.com/excitoon))
* Fix crash in `FULL JOIN` with `LowCardinality` in `JOIN` key. [#8252](https://github.com/ClickHouse/ClickHouse/pull/8252) ([Artem Zuikov](https://github.com/4ertus2))
* Forbidden to use column name more than once in insert query like `INSERT INTO tbl (x, y, x)`. This fixes [#5465](https://github.com/ClickHouse/ClickHouse/issues/5465), [#7681](https://github.com/ClickHouse/ClickHouse/issues/7681). [#7685](https://github.com/ClickHouse/ClickHouse/pull/7685) ([alesapin](https://github.com/alesapin))
* Added fallback for detection the number of physical CPU cores for unknown CPUs (using the number of logical CPU cores). This fixes [#5239](https://github.com/ClickHouse/ClickHouse/issues/5239). [#7726](https://github.com/ClickHouse/ClickHouse/pull/7726) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix `There's no column` error for materialized and alias columns. [#8210](https://github.com/ClickHouse/ClickHouse/pull/8210) ([Artem Zuikov](https://github.com/4ertus2))
* Fixed sever crash when `EXISTS` query was used without `TABLE` or `DICTIONARY` qualifier. Just like `EXISTS t`. This fixes [#8172](https://github.com/ClickHouse/ClickHouse/issues/8172). This bug was introduced in version 19.17. [#8213](https://github.com/ClickHouse/ClickHouse/pull/8213) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix rare bug with error `"Sizes of columns doesn't match"` that might appear when using `SimpleAggregateFunction` column. [#7790](https://github.com/ClickHouse/ClickHouse/pull/7790) ([Boris Granveaud](https://github.com/bgranvea))
* Fix bug where user with empty `allow_databases` got access to all databases (and same for `allow_dictionaries`). [#7793](https://github.com/ClickHouse/ClickHouse/pull/7793) ([DeifyTheGod](https://github.com/DeifyTheGod))
* Fix client crash when server already disconnected from client. [#8071](https://github.com/ClickHouse/ClickHouse/pull/8071) ([Azat Khuzhin](https://github.com/azat))
* Fix `ORDER BY` behaviour in case of sorting by primary key prefix and non primary key suffix. [#7759](https://github.com/ClickHouse/ClickHouse/pull/7759) ([Anton Popov](https://github.com/CurtizJ))
* Check if qualified column present in the table. This fixes [#6836](https://github.com/ClickHouse/ClickHouse/issues/6836). [#7758](https://github.com/ClickHouse/ClickHouse/pull/7758) ([Artem Zuikov](https://github.com/4ertus2))
* Fixed behavior with `ALTER MOVE` ran immediately after merge finish moves superpart of specified. Fixes [#8103](https://github.com/ClickHouse/ClickHouse/issues/8103). [#8104](https://github.com/ClickHouse/ClickHouse/pull/8104) ([Vladimir Chebotarev](https://github.com/excitoon))
* Fix possible server crash while using `UNION` with different number of columns. Fixes [#7279](https://github.com/ClickHouse/ClickHouse/issues/7279). [#7929](https://github.com/ClickHouse/ClickHouse/pull/7929) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Fix size of result substring for function `substr` with negative size. [#8589](https://github.com/ClickHouse/ClickHouse/pull/8589) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Now server does not execute part mutation in `MergeTree` if there are not enough free threads in background pool. [#8588](https://github.com/ClickHouse/ClickHouse/pull/8588) ([tavplubix](https://github.com/tavplubix))
* Fix a minor typo on formatting `UNION ALL` AST. [#7999](https://github.com/ClickHouse/ClickHouse/pull/7999) ([litao91](https://github.com/litao91))
* Fixed incorrect bloom filter results for negative numbers. This fixes [#8317](https://github.com/ClickHouse/ClickHouse/issues/8317). [#8566](https://github.com/ClickHouse/ClickHouse/pull/8566) ([Winter Zhang](https://github.com/zhang2014))
* Fixed potential buffer overflow in decompress. Malicious user can pass fabricated compressed data that will cause read after buffer. This issue was found by Eldar Zaitov from Yandex information security team. [#8404](https://github.com/ClickHouse/ClickHouse/pull/8404) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix incorrect result because of integers overflow in `arrayIntersect`. [#7777](https://github.com/ClickHouse/ClickHouse/pull/7777) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Now `OPTIMIZE TABLE` query will not wait for offline replicas to perform the operation. [#8314](https://github.com/ClickHouse/ClickHouse/pull/8314) ([javi santana](https://github.com/javisantana))
* Fixed `ALTER TTL` parser for `Replicated*MergeTree` tables. [#8318](https://github.com/ClickHouse/ClickHouse/pull/8318) ([Vladimir Chebotarev](https://github.com/excitoon))
* Fix communication between server and client, so server read temporary tables info after query failure. [#8084](https://github.com/ClickHouse/ClickHouse/pull/8084) ([Azat Khuzhin](https://github.com/azat))
* Fix `bitmapAnd` function error when intersecting an aggregated bitmap and a scalar bitmap. [#8082](https://github.com/ClickHouse/ClickHouse/pull/8082) ([Yue Huang](https://github.com/moon03432))
* Refine the definition of `ZXid` according to the ZooKeeper Programmer's Guide which fixes bug in `clickhouse-cluster-copier`. [#8088](https://github.com/ClickHouse/ClickHouse/pull/8088) ([Ding Xiang Fei](https://github.com/dingxiangfei2009))
* `odbc` table function now respects `external_table_functions_use_nulls` setting. [#7506](https://github.com/ClickHouse/ClickHouse/pull/7506) ([Vasily Nemkov](https://github.com/Enmk))
* Fixed bug that lead to a rare data race. [#8143](https://github.com/ClickHouse/ClickHouse/pull/8143) ([Alexander Kazakov](https://github.com/Akazz))
* Now `SYSTEM RELOAD DICTIONARY` reloads a dictionary completely, ignoring `update_field`. This fixes [#7440](https://github.com/ClickHouse/ClickHouse/issues/7440). [#8037](https://github.com/ClickHouse/ClickHouse/pull/8037) ([Vitaly Baranov](https://github.com/vitlibar))
* Add ability to check if dictionary exists in create query. [#8032](https://github.com/ClickHouse/ClickHouse/pull/8032) ([alesapin](https://github.com/alesapin))
* Fix `Float*` parsing in `Values` format. This fixes [#7817](https://github.com/ClickHouse/ClickHouse/issues/7817). [#7870](https://github.com/ClickHouse/ClickHouse/pull/7870) ([tavplubix](https://github.com/tavplubix))
* Fix crash when we cannot reserve space in some background operations of `*MergeTree` table engines family. [#7873](https://github.com/ClickHouse/ClickHouse/pull/7873) ([Vladimir Chebotarev](https://github.com/excitoon))
* Fix crash of merge operation when table contains `SimpleAggregateFunction(LowCardinality)` column. This fixes [#8515](https://github.com/ClickHouse/ClickHouse/issues/8515). [#8522](https://github.com/ClickHouse/ClickHouse/pull/8522) ([Azat Khuzhin](https://github.com/azat))
* Restore support of all ICU locales and add the ability to apply collations for constant expressions. Also add language name to `system.collations` table. [#8051](https://github.com/ClickHouse/ClickHouse/pull/8051) ([alesapin](https://github.com/alesapin))
* Fix bug when external dictionaries with zero minimal lifetime (`LIFETIME(MIN 0 MAX N)`, `LIFETIME(N)`) don't update in background. [#7983](https://github.com/ClickHouse/ClickHouse/pull/7983) ([alesapin](https://github.com/alesapin))
* Fix crash when external dictionary with ClickHouse source has subquery in query. [#8351](https://github.com/ClickHouse/ClickHouse/pull/8351) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Fix incorrect parsing of file extension in table with engine `URL`. This fixes [#8157](https://github.com/ClickHouse/ClickHouse/issues/8157). [#8419](https://github.com/ClickHouse/ClickHouse/pull/8419) ([Andrey Bodrov](https://github.com/apbodrov))
* Fix `CHECK TABLE` query for `*MergeTree` tables without key. Fixes [#7543](https://github.com/ClickHouse/ClickHouse/issues/7543). [#7979](https://github.com/ClickHouse/ClickHouse/pull/7979) ([alesapin](https://github.com/alesapin))
* Fixed conversion of `Float64` to MySQL type. [#8079](https://github.com/ClickHouse/ClickHouse/pull/8079) ([Yuriy Baranov](https://github.com/yurriy))
* Now if table was not completely dropped because of server crash, server will try to restore and load it. [#8176](https://github.com/ClickHouse/ClickHouse/pull/8176) ([tavplubix](https://github.com/tavplubix))
* Fixed crash in table function `file` while inserting into file that doesn't exist. Now in this case file would be created and then insert would be processed. [#8177](https://github.com/ClickHouse/ClickHouse/pull/8177) ([Olga Khvostikova](https://github.com/stavrolia))
* Fix rare deadlock which can happen when `trace_log` is in enabled. [#7838](https://github.com/ClickHouse/ClickHouse/pull/7838) ([filimonov](https://github.com/filimonov))
* Add ability to work with different types besides `Date` in `RangeHashed` external dictionary created from DDL query. Fixes [7899](https://github.com/ClickHouse/ClickHouse/issues/7899). [#8275](https://github.com/ClickHouse/ClickHouse/pull/8275) ([alesapin](https://github.com/alesapin))
* Fixes crash when `now64()` is called with result of another function. [#8270](https://github.com/ClickHouse/ClickHouse/pull/8270) ([Vasily Nemkov](https://github.com/Enmk))
* Fixed bug with detecting client IP for connections through mysql wire protocol. [#7743](https://github.com/ClickHouse/ClickHouse/pull/7743) ([Dmitry Muzyka](https://github.com/dmitriy-myz))
* Fix empty array handling in `arraySplit` function. This fixes [#7708](https://github.com/ClickHouse/ClickHouse/issues/7708). [#7747](https://github.com/ClickHouse/ClickHouse/pull/7747) ([hcz](https://github.com/hczhcz))
* Fixed the issue when `pid-file` of another running `clickhouse-server` may be deleted. [#8487](https://github.com/ClickHouse/ClickHouse/pull/8487) ([Weiqing Xu](https://github.com/weiqxu))
* Fix dictionary reload if it has `invalidate_query`, which stopped updates and some exception on previous update tries. [#8029](https://github.com/ClickHouse/ClickHouse/pull/8029) ([alesapin](https://github.com/alesapin))
* Fixed error in function `arrayReduce` that may lead to "double free" and error in aggregate function combinator `Resample` that may lead to memory leak. Added aggregate function `aggThrow`. This function can be used for testing purposes. [#8446](https://github.com/ClickHouse/ClickHouse/pull/8446) ([alexey-milovidov](https://github.com/alexey-milovidov))
### Improvement
* Improved logging when working with `S3` table engine. [#8251](https://github.com/ClickHouse/ClickHouse/pull/8251) ([Grigory Pervakov](https://github.com/GrigoryPervakov))
* Printed help message when no arguments are passed when calling `clickhouse-local`. This fixes [#5335](https://github.com/ClickHouse/ClickHouse/issues/5335). [#8230](https://github.com/ClickHouse/ClickHouse/pull/8230) ([Andrey Nagorny](https://github.com/Melancholic))
* Add setting `mutations_sync` which allows to wait `ALTER UPDATE/DELETE` queries synchronously. [#8237](https://github.com/ClickHouse/ClickHouse/pull/8237) ([alesapin](https://github.com/alesapin))
* Allow to set up relative `user_files_path` in `config.xml` (in the way similar to `format_schema_path`). [#7632](https://github.com/ClickHouse/ClickHouse/pull/7632) ([hcz](https://github.com/hczhcz))
* Add exception for illegal types for conversion functions with `-OrZero` postfix. [#7880](https://github.com/ClickHouse/ClickHouse/pull/7880) ([Andrey Konyaev](https://github.com/akonyaev90))
* Simplify format of the header of data sending to a shard in a distributed query. [#8044](https://github.com/ClickHouse/ClickHouse/pull/8044) ([Vitaly Baranov](https://github.com/vitlibar))
* `Live View` table engine refactoring. [#8519](https://github.com/ClickHouse/ClickHouse/pull/8519) ([vzakaznikov](https://github.com/vzakaznikov))
* Add additional checks for external dictionaries created from DDL-queries. [#8127](https://github.com/ClickHouse/ClickHouse/pull/8127) ([alesapin](https://github.com/alesapin))
* Fix error `Column ... already exists` while using `FINAL` and `SAMPLE` together, e.g. `select count() from table final sample 1/2`. Fixes [#5186](https://github.com/ClickHouse/ClickHouse/issues/5186). [#7907](https://github.com/ClickHouse/ClickHouse/pull/7907) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Now table the first argument of `joinGet` function can be table indentifier. [#7707](https://github.com/ClickHouse/ClickHouse/pull/7707) ([Amos Bird](https://github.com/amosbird))
* Allow using `MaterializedView` with subqueries above `Kafka` tables. [#8197](https://github.com/ClickHouse/ClickHouse/pull/8197) ([filimonov](https://github.com/filimonov))
* Now background moves between disks run it the seprate thread pool. [#7670](https://github.com/ClickHouse/ClickHouse/pull/7670) ([Vladimir Chebotarev](https://github.com/excitoon))
* `SYSTEM RELOAD DICTIONARY` now executes synchronously. [#8240](https://github.com/ClickHouse/ClickHouse/pull/8240) ([Vitaly Baranov](https://github.com/vitlibar))
* Stack traces now display physical addresses (offsets in object file) instead of virtual memory addresses (where the object file was loaded). That allows the use of `addr2line` when binary is position independent and ASLR is active. This fixes [#8360](https://github.com/ClickHouse/ClickHouse/issues/8360). [#8387](https://github.com/ClickHouse/ClickHouse/pull/8387) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Support new syntax for row-level security filters: `<table name='table_name'>…</table>`. Fixes [#5779](https://github.com/ClickHouse/ClickHouse/issues/5779). [#8381](https://github.com/ClickHouse/ClickHouse/pull/8381) ([Ivan](https://github.com/abyss7))
* Now `cityHash` function can work with `Decimal` and `UUID` types. Fixes [#5184](https://github.com/ClickHouse/ClickHouse/issues/5184). [#7693](https://github.com/ClickHouse/ClickHouse/pull/7693) ([Mikhail Korotov](https://github.com/millb))
* Removed fixed index granularity (it was 1024) from system logs because it's obsolete after implementation of adaptive granularity. [#7698](https://github.com/ClickHouse/ClickHouse/pull/7698) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Enabled MySQL compatibility server when ClickHouse is compiled without SSL. [#7852](https://github.com/ClickHouse/ClickHouse/pull/7852) ([Yuriy Baranov](https://github.com/yurriy))
* Now server checksums distributed batches, which gives more verbose errors in case of corrupted data in batch. [#7914](https://github.com/ClickHouse/ClickHouse/pull/7914) ([Azat Khuzhin](https://github.com/azat))
* Support `DROP DATABASE`, `DETACH TABLE`, `DROP TABLE` and `ATTACH TABLE` for `MySQL` database engine. [#8202](https://github.com/ClickHouse/ClickHouse/pull/8202) ([Winter Zhang](https://github.com/zhang2014))
* Add authentication in S3 table function and table engine. [#7623](https://github.com/ClickHouse/ClickHouse/pull/7623) ([Vladimir Chebotarev](https://github.com/excitoon))
* Added check for extra parts of `MergeTree` at different disks, in order to not allow to miss data parts at undefined disks. [#8118](https://github.com/ClickHouse/ClickHouse/pull/8118) ([Vladimir Chebotarev](https://github.com/excitoon))
* Enable SSL support for Mac client and server. [#8297](https://github.com/ClickHouse/ClickHouse/pull/8297) ([Ivan](https://github.com/abyss7))
* Now ClickHouse can work as MySQL federated server (see https://dev.mysql.com/doc/refman/5.7/en/federated-create-server.html). [#7717](https://github.com/ClickHouse/ClickHouse/pull/7717) ([Maxim Fedotov](https://github.com/MaxFedotov))
* `clickhouse-client` now only enable `bracketed-paste` when multiquery is on and multiline is off. This fixes (#7757)[https://github.com/ClickHouse/ClickHouse/issues/7757]. [#7761](https://github.com/ClickHouse/ClickHouse/pull/7761) ([Amos Bird](https://github.com/amosbird))
* Support `Array(Decimal)` in `if` function. [#7721](https://github.com/ClickHouse/ClickHouse/pull/7721) ([Artem Zuikov](https://github.com/4ertus2))
* Support Decimals in `arrayDifference`, `arrayCumSum` and `arrayCumSumNegative` functions. [#7724](https://github.com/ClickHouse/ClickHouse/pull/7724) ([Artem Zuikov](https://github.com/4ertus2))
* Added `lifetime` column to `system.dictionaries` table. [#6820](https://github.com/ClickHouse/ClickHouse/issues/6820) [#7727](https://github.com/ClickHouse/ClickHouse/pull/7727) ([kekekekule](https://github.com/kekekekule))
* Improved check for existing parts on different disks for `*MergeTree` table engines. Addresses [#7660](https://github.com/ClickHouse/ClickHouse/issues/7660). [#8440](https://github.com/ClickHouse/ClickHouse/pull/8440) ([Vladimir Chebotarev](https://github.com/excitoon))
* Integration with `AWS SDK` for `S3` interactions which allows to use all S3 features out of the box. [#8011](https://github.com/ClickHouse/ClickHouse/pull/8011) ([Pavel Kovalenko](https://github.com/Jokser))
* Added support for subqueries in `Live View` tables. [#7792](https://github.com/ClickHouse/ClickHouse/pull/7792) ([vzakaznikov](https://github.com/vzakaznikov))
* Check for using `Date` or `DateTime` column from `TTL` expressions was removed. [#7920](https://github.com/ClickHouse/ClickHouse/pull/7920) ([Vladimir Chebotarev](https://github.com/excitoon))
* Information about disk was added to `system.detached_parts` table. [#7833](https://github.com/ClickHouse/ClickHouse/pull/7833) ([Vladimir Chebotarev](https://github.com/excitoon))
* Now settings `max_(table|partition)_size_to_drop` can be changed without a restart. [#7779](https://github.com/ClickHouse/ClickHouse/pull/7779) ([Grigory Pervakov](https://github.com/GrigoryPervakov))
* Slightly better usability of error messages. Ask user not to remove the lines below `Stack trace:`. [#7897](https://github.com/ClickHouse/ClickHouse/pull/7897) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Better reading messages from `Kafka` engine in various formats after [#7935](https://github.com/ClickHouse/ClickHouse/issues/7935). [#8035](https://github.com/ClickHouse/ClickHouse/pull/8035) ([Ivan](https://github.com/abyss7))
* Better compatibility with MySQL clients which don't support `sha2_password` auth plugin. [#8036](https://github.com/ClickHouse/ClickHouse/pull/8036) ([Yuriy Baranov](https://github.com/yurriy))
* Support more column types in MySQL compatibility server. [#7975](https://github.com/ClickHouse/ClickHouse/pull/7975) ([Yuriy Baranov](https://github.com/yurriy))
* Implement `ORDER BY` optimization for `Merge`, `Buffer` and `Materilized View` storages with underlying `MergeTree` tables. [#8130](https://github.com/ClickHouse/ClickHouse/pull/8130) ([Anton Popov](https://github.com/CurtizJ))
* Now we always use POSIX implementation of `getrandom` to have better compatibility with old kernels (< 3.17). [#7940](https://github.com/ClickHouse/ClickHouse/pull/7940) ([Amos Bird](https://github.com/amosbird))
* Better check for valid destination in a move TTL rule. [#8410](https://github.com/ClickHouse/ClickHouse/pull/8410) ([Vladimir Chebotarev](https://github.com/excitoon))
* Better checks for broken insert batches for `Distributed` table engine. [#7933](https://github.com/ClickHouse/ClickHouse/pull/7933) ([Azat Khuzhin](https://github.com/azat))
* Add column with array of parts name which mutations must process in future to `system.mutations` table. [#8179](https://github.com/ClickHouse/ClickHouse/pull/8179) ([alesapin](https://github.com/alesapin))
* Parallel merge sort optimization for processors. [#8552](https://github.com/ClickHouse/ClickHouse/pull/8552) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* The settings `mark_cache_min_lifetime` is now obsolete and does nothing. In previous versions, mark cache can grow in memory larger than `mark_cache_size` to accomodate data within `mark_cache_min_lifetime` seconds. That was leading to confusion and higher memory usage than expected, that is especially bad on memory constrained systems. If you will see performance degradation after installing this release, you should increase the `mark_cache_size`. [#8484](https://github.com/ClickHouse/ClickHouse/pull/8484) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Preparation to use `tid` everywhere. This is needed for [#7477](https://github.com/ClickHouse/ClickHouse/issues/7477). [#8276](https://github.com/ClickHouse/ClickHouse/pull/8276) ([alexey-milovidov](https://github.com/alexey-milovidov))
### Performance Improvement
* Performance optimizations in processors pipeline. [#7988](https://github.com/ClickHouse/ClickHouse/pull/7988) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Non-blocking updates of expired keys in cache dictionaries (with permission to read old ones). [#8303](https://github.com/ClickHouse/ClickHouse/pull/8303) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov))
* Compile ClickHouse without `-fno-omit-frame-pointer` globally to spare one more register. [#8097](https://github.com/ClickHouse/ClickHouse/pull/8097) ([Amos Bird](https://github.com/amosbird))
* Speedup `greatCircleDistance` function and add performance tests for it. [#7307](https://github.com/ClickHouse/ClickHouse/pull/7307) ([Olga Khvostikova](https://github.com/stavrolia))
* Improved performance of function `roundDown`. [#8465](https://github.com/ClickHouse/ClickHouse/pull/8465) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Improved performance of `max`, `min`, `argMin`, `argMax` for `DateTime64` data type. [#8199](https://github.com/ClickHouse/ClickHouse/pull/8199) ([Vasily Nemkov](https://github.com/Enmk))
* Improved performance of sorting without a limit or with big limit and external sorting. [#8545](https://github.com/ClickHouse/ClickHouse/pull/8545) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Improved performance of formatting floating point numbers up to 6 times. [#8542](https://github.com/ClickHouse/ClickHouse/pull/8542) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Improved performance of `modulo` function. [#7750](https://github.com/ClickHouse/ClickHouse/pull/7750) ([Amos Bird](https://github.com/amosbird))
* Optimized `ORDER BY` and merging with single column key. [#8335](https://github.com/ClickHouse/ClickHouse/pull/8335) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Better implementation for `arrayReduce`, `-Array` and `-State` combinators. [#7710](https://github.com/ClickHouse/ClickHouse/pull/7710) ([Amos Bird](https://github.com/amosbird))
* Now `PREWHERE` should be optimized to be at least as efficient as `WHERE`. [#7769](https://github.com/ClickHouse/ClickHouse/pull/7769) ([Amos Bird](https://github.com/amosbird))
* Improve the way `round` and `roundBankers` handling negative numbers. [#8229](https://github.com/ClickHouse/ClickHouse/pull/8229) ([hcz](https://github.com/hczhcz))
* Improved decoding performance of `DoubleDelta` and `Gorilla` codecs by roughly 30-40%. This fixes [#7082](https://github.com/ClickHouse/ClickHouse/issues/7082). [#8019](https://github.com/ClickHouse/ClickHouse/pull/8019) ([Vasily Nemkov](https://github.com/Enmk))
* Improved performance of `base64` related functions. [#8444](https://github.com/ClickHouse/ClickHouse/pull/8444) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Added a function `geoDistance`. It is similar to `greatCircleDistance` but uses approximation to WGS-84 ellipsoid model. The performance of both functions are near the same. [#8086](https://github.com/ClickHouse/ClickHouse/pull/8086) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Faster `min` and `max` aggregation functions for `Decimal` data type. [#8144](https://github.com/ClickHouse/ClickHouse/pull/8144) ([Artem Zuikov](https://github.com/4ertus2))
* Vectorize processing `arrayReduce`. [#7608](https://github.com/ClickHouse/ClickHouse/pull/7608) ([Amos Bird](https://github.com/amosbird))
* `if` chains are now optimized as `multiIf`. [#8355](https://github.com/ClickHouse/ClickHouse/pull/8355) ([kamalov-ruslan](https://github.com/kamalov-ruslan))
* Fix performance regression of `Kafka` table engine introduced in 19.15. This fixes [#7261](https://github.com/ClickHouse/ClickHouse/issues/7261). [#7935](https://github.com/ClickHouse/ClickHouse/pull/7935) ([filimonov](https://github.com/filimonov))
* Removed "pie" code generation that `gcc` from Debian packages occasionally brings by default. [#8483](https://github.com/ClickHouse/ClickHouse/pull/8483) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Parallel parsing data formats [#6553](https://github.com/ClickHouse/ClickHouse/pull/6553) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov))
* Enable optimized parser of `Values` with expressions by default (`input_format_values_deduce_templates_of_expressions=1`). [#8231](https://github.com/ClickHouse/ClickHouse/pull/8231) ([tavplubix](https://github.com/tavplubix))
### Build/Testing/Packaging Improvement
* Build fixes for `ARM` and in minimal mode. [#8304](https://github.com/ClickHouse/ClickHouse/pull/8304) ([proller](https://github.com/proller))
* Add coverage file flush for `clickhouse-server` when std::atexit is not called. Also slightly improved logging in stateless tests with coverage. [#8267](https://github.com/ClickHouse/ClickHouse/pull/8267) ([alesapin](https://github.com/alesapin))
* Update LLVM library in contrib. Avoid using LLVM from OS packages. [#8258](https://github.com/ClickHouse/ClickHouse/pull/8258) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Make bundled `curl` build fully quiet. [#8232](https://github.com/ClickHouse/ClickHouse/pull/8232) [#8203](https://github.com/ClickHouse/ClickHouse/pull/8203) ([Pavel Kovalenko](https://github.com/Jokser))
* Fix some `MemorySanitizer` warnings. [#8235](https://github.com/ClickHouse/ClickHouse/pull/8235) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Use `add_warning` and `no_warning` macros in `CMakeLists.txt`. [#8604](https://github.com/ClickHouse/ClickHouse/pull/8604) ([Ivan](https://github.com/abyss7))
* Add support of Minio S3 Compatible object (https://min.io/) for better integration tests. [#7863](https://github.com/ClickHouse/ClickHouse/pull/7863) [#7875](https://github.com/ClickHouse/ClickHouse/pull/7875) ([Pavel Kovalenko](https://github.com/Jokser))
* Imported `libc` headers to contrib. It allows to make builds more consistent across various systems (only for `x86_64-linux-gnu`). [#5773](https://github.com/ClickHouse/ClickHouse/pull/5773) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Remove `-fPIC` from some libraries. [#8464](https://github.com/ClickHouse/ClickHouse/pull/8464) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Clean `CMakeLists.txt` for curl. See https://github.com/ClickHouse/ClickHouse/pull/8011#issuecomment-569478910 [#8459](https://github.com/ClickHouse/ClickHouse/pull/8459) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Silent warnings in `CapNProto` library. [#8220](https://github.com/ClickHouse/ClickHouse/pull/8220) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Add performance tests for short string optimized hash tables. [#7679](https://github.com/ClickHouse/ClickHouse/pull/7679) ([Amos Bird](https://github.com/amosbird))
* Now ClickHouse will build on `AArch64` even if `MADV_FREE` is not available. This fixes [#8027](https://github.com/ClickHouse/ClickHouse/issues/8027). [#8243](https://github.com/ClickHouse/ClickHouse/pull/8243) ([Amos Bird](https://github.com/amosbird))
* Update `zlib-ng` to fix memory sanitizer problems. [#7182](https://github.com/ClickHouse/ClickHouse/pull/7182) [#8206](https://github.com/ClickHouse/ClickHouse/pull/8206) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Enable internal MySQL library on non-Linux system, because usage of OS packages is very fragile and usually doesn't work at all. This fixes [#5765](https://github.com/ClickHouse/ClickHouse/issues/5765). [#8426](https://github.com/ClickHouse/ClickHouse/pull/8426) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fixed build on some systems after enabling `libc++`. This supersedes [#8374](https://github.com/ClickHouse/ClickHouse/issues/8374). [#8380](https://github.com/ClickHouse/ClickHouse/pull/8380) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Make `Field` methods more type-safe to find more errors. [#7386](https://github.com/ClickHouse/ClickHouse/pull/7386) [#8209](https://github.com/ClickHouse/ClickHouse/pull/8209) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Added missing files to the `libc-headers` submodule. [#8507](https://github.com/ClickHouse/ClickHouse/pull/8507) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix wrong `JSON` quoting in performance test output. [#8497](https://github.com/ClickHouse/ClickHouse/pull/8497) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Now stack trace is displayed for `std::exception` and `Poco::Exception`. In previous versions it was available only for `DB::Exception`. This improves diagnostics. [#8501](https://github.com/ClickHouse/ClickHouse/pull/8501) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Porting `clock_gettime` and `clock_nanosleep` for fresh glibc versions. [#8054](https://github.com/ClickHouse/ClickHouse/pull/8054) ([Amos Bird](https://github.com/amosbird))
* Enable `part_log` in example config for developers. [#8609](https://github.com/ClickHouse/ClickHouse/pull/8609) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix async nature of reload in `01036_no_superfluous_dict_reload_on_create_database*`. [#8111](https://github.com/ClickHouse/ClickHouse/pull/8111) ([Azat Khuzhin](https://github.com/azat))
* Fixed codec performance tests. [#8615](https://github.com/ClickHouse/ClickHouse/pull/8615) ([Vasily Nemkov](https://github.com/Enmk))
* Add install scripts for `.tgz` build and documentation for them. [#8612](https://github.com/ClickHouse/ClickHouse/pull/8612) [#8591](https://github.com/ClickHouse/ClickHouse/pull/8591) ([alesapin](https://github.com/alesapin))
* Removed old `ZSTD` test (it was created in year 2016 to reproduce the bug that pre 1.0 version of ZSTD has had). This fixes [#8618](https://github.com/ClickHouse/ClickHouse/issues/8618). [#8619](https://github.com/ClickHouse/ClickHouse/pull/8619) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fixed build on Mac OS Catalina. [#8600](https://github.com/ClickHouse/ClickHouse/pull/8600) ([meo](https://github.com/meob))
* Increased number of rows in codec performance tests to make results noticeable. [#8574](https://github.com/ClickHouse/ClickHouse/pull/8574) ([Vasily Nemkov](https://github.com/Enmk))
* In debug builds, treat `LOGICAL_ERROR` exceptions as assertion failures, so that they are easier to notice. [#8475](https://github.com/ClickHouse/ClickHouse/pull/8475) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Make formats-related performance test more deterministic. [#8477](https://github.com/ClickHouse/ClickHouse/pull/8477) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Update `lz4` to fix a MemorySanitizer failure. [#8181](https://github.com/ClickHouse/ClickHouse/pull/8181) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Suppress a known MemorySanitizer false positive in exception handling. [#8182](https://github.com/ClickHouse/ClickHouse/pull/8182) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Update `gcc` and `g++` to version 9 in `build/docker/build.sh` [#7766](https://github.com/ClickHouse/ClickHouse/pull/7766) ([TLightSky](https://github.com/tlightsky))
* Add performance test case to test that `PREWHERE` is worse than `WHERE`. [#7768](https://github.com/ClickHouse/ClickHouse/pull/7768) ([Amos Bird](https://github.com/amosbird))
* Progress towards fixing one flacky test. [#8621](https://github.com/ClickHouse/ClickHouse/pull/8621) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Avoid MemorySanitizer report for data from `libunwind`. [#8539](https://github.com/ClickHouse/ClickHouse/pull/8539) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Updated `libc++` to the latest version. [#8324](https://github.com/ClickHouse/ClickHouse/pull/8324) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Build ICU library from sources. This fixes [#6460](https://github.com/ClickHouse/ClickHouse/issues/6460). [#8219](https://github.com/ClickHouse/ClickHouse/pull/8219) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Switched from `libressl` to `openssl`. ClickHouse should support TLS 1.3 and SNI after this change. This fixes [#8171](https://github.com/ClickHouse/ClickHouse/issues/8171). [#8218](https://github.com/ClickHouse/ClickHouse/pull/8218) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fixed UBSan report when using `chacha20_poly1305` from SSL (happens on connect to https://yandex.ru/). [#8214](https://github.com/ClickHouse/ClickHouse/pull/8214) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix mode of default password file for `.deb` linux distros. [#8075](https://github.com/ClickHouse/ClickHouse/pull/8075) ([proller](https://github.com/proller))
* Improved expression for getting `clickhouse-server` PID in `clickhouse-test`. [#8063](https://github.com/ClickHouse/ClickHouse/pull/8063) ([Alexander Kazakov](https://github.com/Akazz))
* Updated contrib/googletest to v1.10.0. [#8587](https://github.com/ClickHouse/ClickHouse/pull/8587) ([Alexander Burmak](https://github.com/Alex-Burmak))
* Fixed ThreadSaninitizer report in `base64` library. Also updated this library to the latest version, but it doesn't matter. This fixes [#8397](https://github.com/ClickHouse/ClickHouse/issues/8397). [#8403](https://github.com/ClickHouse/ClickHouse/pull/8403) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Fix `00600_replace_running_query` for processors. [#8272](https://github.com/ClickHouse/ClickHouse/pull/8272) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Remove support for `tcmalloc` to make `CMakeLists.txt` simpler. [#8310](https://github.com/ClickHouse/ClickHouse/pull/8310) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Release gcc builds now use `libc++` instead of `libstdc++`. Recently `libc++` was used only with clang. This will improve consistency of build configurations and portability. [#8311](https://github.com/ClickHouse/ClickHouse/pull/8311) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Enable ICU library for build with MemorySanitizer. [#8222](https://github.com/ClickHouse/ClickHouse/pull/8222) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Suppress warnings from `CapNProto` library. [#8224](https://github.com/ClickHouse/ClickHouse/pull/8224) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Removed special cases of code for `tcmalloc`, because it's no longer supported. [#8225](https://github.com/ClickHouse/ClickHouse/pull/8225) ([alexey-milovidov](https://github.com/alexey-milovidov))
* In CI coverage task, kill the server gracefully to allow it to save the coverage report. This fixes incomplete coverage reports we've been seeing lately. [#8142](https://github.com/ClickHouse/ClickHouse/pull/8142) ([alesapin](https://github.com/alesapin))
* Performance tests for all codecs against `Float64` and `UInt64` values. [#8349](https://github.com/ClickHouse/ClickHouse/pull/8349) ([Vasily Nemkov](https://github.com/Enmk))
* `termcap` is very much deprecated and lead to various problems (f.g. missing "up" cap and echoing `^J` instead of multi line) . Favor `terminfo` or bundled `ncurses`. [#7737](https://github.com/ClickHouse/ClickHouse/pull/7737) ([Amos Bird](https://github.com/amosbird))
* Fix `test_storage_s3` integration test. [#7734](https://github.com/ClickHouse/ClickHouse/pull/7734) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Support `StorageFile(<format>, null) ` to insert block into given format file without actually write to disk. This is required for performance tests. [#8455](https://github.com/ClickHouse/ClickHouse/pull/8455) ([Amos Bird](https://github.com/amosbird))
* Added argument `--print-time` to functional tests which prints execution time per test. [#8001](https://github.com/ClickHouse/ClickHouse/pull/8001) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Added asserts to `KeyCondition` while evaluating RPN. This will fix warning from gcc-9. [#8279](https://github.com/ClickHouse/ClickHouse/pull/8279) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Dump cmake options in CI builds. [#8273](https://github.com/ClickHouse/ClickHouse/pull/8273) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Don't generate debug info for some fat libraries. [#8271](https://github.com/ClickHouse/ClickHouse/pull/8271) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Make `log_to_console.xml` always log to stderr, regardless of is it interactive or not. [#8395](https://github.com/ClickHouse/ClickHouse/pull/8395) ([Alexander Kuzmenkov](https://github.com/akuzm))
* Removed some unused features from `clickhouse-performance-test` tool. [#8555](https://github.com/ClickHouse/ClickHouse/pull/8555) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Now we will also search for `lld-X` with corresponding `clang-X` version. [#8092](https://github.com/ClickHouse/ClickHouse/pull/8092) ([alesapin](https://github.com/alesapin))
* Parquet build improvement. [#8421](https://github.com/ClickHouse/ClickHouse/pull/8421) ([maxulan](https://github.com/maxulan))
* More GCC warnings [#8221](https://github.com/ClickHouse/ClickHouse/pull/8221) ([kreuzerkrieg](https://github.com/kreuzerkrieg))
* Package for Arch Linux now allows to run ClickHouse server, and not only client. [#8534](https://github.com/ClickHouse/ClickHouse/pull/8534) ([Vladimir Chebotarev](https://github.com/excitoon))
* Fix test with processors. Tiny performance fixes. [#7672](https://github.com/ClickHouse/ClickHouse/pull/7672) ([Nikolai Kochetov](https://github.com/KochetovNicolai))
* Update contrib/protobuf. [#8256](https://github.com/ClickHouse/ClickHouse/pull/8256) ([Matwey V. Kornilov](https://github.com/matwey))
* In preparation of switching to c++20 as a new year celebration. "May the C++ force be with ClickHouse." [#8447](https://github.com/ClickHouse/ClickHouse/pull/8447) ([Amos Bird](https://github.com/amosbird))
### Experimental Feature
* Added experimental setting `min_bytes_to_use_mmap_io`. It allows to read big files without copying data from kernel to userspace. The setting is disabled by default. Recommended threshold is about 64 MB, because mmap/munmap is slow. [#8520](https://github.com/ClickHouse/ClickHouse/pull/8520) ([alexey-milovidov](https://github.com/alexey-milovidov))
* Reworked quotas as a part of access control system. Added new table `system.quotas`, new functions `currentQuota`, `currentQuotaKey`, new SQL syntax `CREATE QUOTA`, `ALTER QUOTA`, `DROP QUOTA`, `SHOW QUOTA`. [#7257](https://github.com/ClickHouse/ClickHouse/pull/7257) ([Vitaly Baranov](https://github.com/vitlibar))
* Allow skipping unknown settings with warnings instead of throwing exceptions. [#7653](https://github.com/ClickHouse/ClickHouse/pull/7653) ([Vitaly Baranov](https://github.com/vitlibar))
* Reworked row policies as a part of access control system. Added new table `system.row_policies`, new function `currentRowPolicies()`, new SQL syntax `CREATE POLICY`, `ALTER POLICY`, `DROP POLICY`, `SHOW CREATE POLICY`, `SHOW POLICIES`. [#7808](https://github.com/ClickHouse/ClickHouse/pull/7808) ([Vitaly Baranov](https://github.com/vitlibar))
### Security Fix
* Fixed the possibility of reading directories structure in tables with `File` table engine. This fixes [#8536](https://github.com/ClickHouse/ClickHouse/issues/8536). [#8537](https://github.com/ClickHouse/ClickHouse/pull/8537) ([alexey-milovidov](https://github.com/alexey-milovidov))
## ClickHouse release v19.17 ## ClickHouse release v19.17
### ClickHouse release v19.17.6.36, 2019-12-27 ### ClickHouse release v19.17.6.36, 2019-12-27

View File

@ -15,3 +15,4 @@ ClickHouse is an open-source column-oriented database management system that all
## Upcoming Events ## Upcoming Events
* [ClickHouse Meetup in San Francisco](https://www.eventbrite.com/e/clickhouse-february-meetup-registration-88496227599) on February 5. * [ClickHouse Meetup in San Francisco](https://www.eventbrite.com/e/clickhouse-february-meetup-registration-88496227599) on February 5.
* [ClickHouse Meetup in New York](https://www.meetup.com/Uber-Engineering-Events-New-York/events/268328663/) on February 11.

View File

@ -306,7 +306,7 @@ if (USE_INTERNAL_AWS_S3_LIBRARY)
# The library is large - avoid bloat. # The library is large - avoid bloat.
target_compile_options (aws_s3 PRIVATE -g0) target_compile_options (aws_s3 PRIVATE -g0)
target_compile_options (aws_s3_checksums PRIVATE -g0) target_compile_options (aws_s3_checksums PRIVATE -g0)
target_compile_options (libcurl PRIVATE -g0) target_compile_options (curl PRIVATE -g0)
endif () endif ()
if (USE_BASE64) if (USE_BASE64)

View File

@ -102,4 +102,4 @@ if (OPENSSL_FOUND)
target_link_libraries(aws_s3 PRIVATE ${OPENSSL_LIBRARIES}) target_link_libraries(aws_s3 PRIVATE ${OPENSSL_LIBRARIES})
endif() endif()
target_link_libraries(aws_s3 PRIVATE aws_s3_checksums libcurl) target_link_libraries(aws_s3 PRIVATE aws_s3_checksums curl)

View File

@ -1,61 +0,0 @@
include(CheckCSourceCompiles)
option(CURL_HIDDEN_SYMBOLS "Set to ON to hide libcurl internal symbols (=hide all symbols that aren't officially external)." ON)
mark_as_advanced(CURL_HIDDEN_SYMBOLS)
if(CURL_HIDDEN_SYMBOLS)
set(SUPPORTS_SYMBOL_HIDING FALSE)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(SUPPORTS_SYMBOL_HIDING TRUE)
set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))")
set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden")
elseif(CMAKE_COMPILER_IS_GNUCC)
if(NOT CMAKE_VERSION VERSION_LESS 2.8.10)
set(GCC_VERSION ${CMAKE_C_COMPILER_VERSION})
else()
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
endif()
if(NOT GCC_VERSION VERSION_LESS 3.4)
# note: this is considered buggy prior to 4.0 but the autotools don't care, so let's ignore that fact
set(SUPPORTS_SYMBOL_HIDING TRUE)
set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))")
set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden")
endif()
elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.0)
set(SUPPORTS_SYMBOL_HIDING TRUE)
set(_SYMBOL_EXTERN "__global")
set(_CFLAG_SYMBOLS_HIDE "-xldscope=hidden")
elseif(CMAKE_C_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0)
# note: this should probably just check for version 9.1.045 but I'm not 100% sure
# so let's do it the same way autotools do.
set(SUPPORTS_SYMBOL_HIDING TRUE)
set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))")
set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden")
check_c_source_compiles("#include <stdio.h>
int main (void) { printf(\"icc fvisibility bug test\"); return 0; }" _no_bug)
if(NOT _no_bug)
set(SUPPORTS_SYMBOL_HIDING FALSE)
set(_SYMBOL_EXTERN "")
set(_CFLAG_SYMBOLS_HIDE "")
endif()
elseif(MSVC)
set(SUPPORTS_SYMBOL_HIDING TRUE)
endif()
set(HIDES_CURL_PRIVATE_SYMBOLS ${SUPPORTS_SYMBOL_HIDING})
elseif(MSVC)
if(NOT CMAKE_VERSION VERSION_LESS 3.7)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) #present since 3.4.3 but broken
set(HIDES_CURL_PRIVATE_SYMBOLS FALSE)
else()
message(WARNING "Hiding private symbols regardless CURL_HIDDEN_SYMBOLS being disabled.")
set(HIDES_CURL_PRIVATE_SYMBOLS TRUE)
endif()
else()
set(HIDES_CURL_PRIVATE_SYMBOLS FALSE)
endif()
set(CURL_CFLAG_SYMBOLS_HIDE ${_CFLAG_SYMBOLS_HIDE})
set(CURL_EXTERN_SYMBOL ${_SYMBOL_EXTERN})

View File

@ -1,617 +0,0 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#ifdef TIME_WITH_SYS_TIME
/* Time with sys/time test */
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
int
main ()
{
if ((struct tm *) 0)
return 0;
;
return 0;
}
#endif
#ifdef HAVE_FCNTL_O_NONBLOCK
/* headers for FCNTL_O_NONBLOCK test */
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
/* */
#if defined(sun) || defined(__sun__) || \
defined(__SUNPRO_C) || defined(__SUNPRO_CC)
# if defined(__SVR4) || defined(__srv4__)
# define PLATFORM_SOLARIS
# else
# define PLATFORM_SUNOS4
# endif
#endif
#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX41)
# define PLATFORM_AIX_V3
#endif
/* */
#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || defined(__BEOS__)
#error "O_NONBLOCK does not work on this platform"
#endif
int
main ()
{
/* O_NONBLOCK source test */
int flags = 0;
if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK))
return 1;
return 0;
}
#endif
/* tests for gethostbyaddr_r or gethostbyname_r */
#if defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) || \
defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) || \
defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) || \
defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \
defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
# define _REENTRANT
/* no idea whether _REENTRANT is always set, just invent a new flag */
# define TEST_GETHOSTBYFOO_REENTRANT
#endif
#if defined(HAVE_GETHOSTBYADDR_R_5) || \
defined(HAVE_GETHOSTBYADDR_R_7) || \
defined(HAVE_GETHOSTBYADDR_R_8) || \
defined(HAVE_GETHOSTBYNAME_R_3) || \
defined(HAVE_GETHOSTBYNAME_R_5) || \
defined(HAVE_GETHOSTBYNAME_R_6) || \
defined(TEST_GETHOSTBYFOO_REENTRANT)
#include <sys/types.h>
#include <netdb.h>
int main(void)
{
char *address = "example.com";
int length = 0;
int type = 0;
struct hostent h;
int rc = 0;
#if defined(HAVE_GETHOSTBYADDR_R_5) || \
defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) || \
\
defined(HAVE_GETHOSTBYNAME_R_3) || \
defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
struct hostent_data hdata;
#elif defined(HAVE_GETHOSTBYADDR_R_7) || \
defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) || \
defined(HAVE_GETHOSTBYADDR_R_8) || \
defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) || \
\
defined(HAVE_GETHOSTBYNAME_R_5) || \
defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
defined(HAVE_GETHOSTBYNAME_R_6) || \
defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
char buffer[8192];
int h_errnop;
struct hostent *hp;
#endif
#ifndef gethostbyaddr_r
(void)gethostbyaddr_r;
#endif
#if defined(HAVE_GETHOSTBYADDR_R_5) || \
defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT)
rc = gethostbyaddr_r(address, length, type, &h, &hdata);
(void)rc;
#elif defined(HAVE_GETHOSTBYADDR_R_7) || \
defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT)
hp = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &h_errnop);
(void)hp;
#elif defined(HAVE_GETHOSTBYADDR_R_8) || \
defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT)
rc = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &hp, &h_errnop);
(void)rc;
#endif
#if defined(HAVE_GETHOSTBYNAME_R_3) || \
defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
rc = gethostbyname_r(address, &h, &hdata);
#elif defined(HAVE_GETHOSTBYNAME_R_5) || \
defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT)
rc = gethostbyname_r(address, &h, buffer, 8192, &h_errnop);
(void)hp; /* not used for test */
#elif defined(HAVE_GETHOSTBYNAME_R_6) || \
defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
rc = gethostbyname_r(address, &h, buffer, 8192, &hp, &h_errnop);
#endif
(void)length;
(void)type;
(void)rc;
return 0;
}
#endif
#ifdef HAVE_SOCKLEN_T
#ifdef _WIN32
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#endif
int
main ()
{
if ((socklen_t *) 0)
return 0;
if (sizeof (socklen_t))
return 0;
;
return 0;
}
#endif
#ifdef HAVE_IN_ADDR_T
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int
main ()
{
if ((in_addr_t *) 0)
return 0;
if (sizeof (in_addr_t))
return 0;
;
return 0;
}
#endif
#ifdef HAVE_BOOL_T
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif
int
main ()
{
if (sizeof (bool *) )
return 0;
;
return 0;
}
#endif
#ifdef STDC_HEADERS
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <float.h>
int main() { return 0; }
#endif
#ifdef RETSIGTYPE_TEST
#include <sys/types.h>
#include <signal.h>
#ifdef signal
# undef signal
#endif
#ifdef __cplusplus
extern "C" void (*signal (int, void (*)(int)))(int);
#else
void (*signal ()) ();
#endif
int
main ()
{
return 0;
}
#endif
#ifdef HAVE_INET_NTOA_R_DECL
#include <arpa/inet.h>
typedef void (*func_type)();
int main()
{
#ifndef inet_ntoa_r
func_type func;
func = (func_type)inet_ntoa_r;
(void)func;
#endif
return 0;
}
#endif
#ifdef HAVE_INET_NTOA_R_DECL_REENTRANT
#define _REENTRANT
#include <arpa/inet.h>
typedef void (*func_type)();
int main()
{
#ifndef inet_ntoa_r
func_type func;
func = (func_type)&inet_ntoa_r;
(void)func;
#endif
return 0;
}
#endif
#ifdef HAVE_GETADDRINFO
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(void) {
struct addrinfo hints, *ai;
int error;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
#ifndef getaddrinfo
(void)getaddrinfo;
#endif
error = getaddrinfo("127.0.0.1", "8080", &hints, &ai);
if (error) {
return 1;
}
return 0;
}
#endif
#ifdef HAVE_FILE_OFFSET_BITS
#ifdef _FILE_OFFSET_BITS
#undef _FILE_OFFSET_BITS
#endif
#define _FILE_OFFSET_BITS 64
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int main () { ; return 0; }
#endif
#ifdef HAVE_IOCTLSOCKET
/* includes start */
#ifdef HAVE_WINDOWS_H
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# else
# ifdef HAVE_WINSOCK_H
# include <winsock.h>
# endif
# endif
#endif
int
main ()
{
/* ioctlsocket source code */
int socket;
unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);
;
return 0;
}
#endif
#ifdef HAVE_IOCTLSOCKET_CAMEL
/* includes start */
#ifdef HAVE_WINDOWS_H
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# else
# ifdef HAVE_WINSOCK_H
# include <winsock.h>
# endif
# endif
#endif
int
main ()
{
/* IoctlSocket source code */
if(0 != IoctlSocket(0, 0, 0))
return 1;
;
return 0;
}
#endif
#ifdef HAVE_IOCTLSOCKET_CAMEL_FIONBIO
/* includes start */
#ifdef HAVE_WINDOWS_H
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# else
# ifdef HAVE_WINSOCK_H
# include <winsock.h>
# endif
# endif
#endif
int
main ()
{
/* IoctlSocket source code */
long flags = 0;
if(0 != ioctlsocket(0, FIONBIO, &flags))
return 1;
;
return 0;
}
#endif
#ifdef HAVE_IOCTLSOCKET_FIONBIO
/* includes start */
#ifdef HAVE_WINDOWS_H
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# else
# ifdef HAVE_WINSOCK_H
# include <winsock.h>
# endif
# endif
#endif
int
main ()
{
int flags = 0;
if(0 != ioctlsocket(0, FIONBIO, &flags))
return 1;
;
return 0;
}
#endif
#ifdef HAVE_IOCTL_FIONBIO
/* headers for FIONBIO test */
/* includes start */
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_STROPTS_H
# include <stropts.h>
#endif
int
main ()
{
int flags = 0;
if(0 != ioctl(0, FIONBIO, &flags))
return 1;
;
return 0;
}
#endif
#ifdef HAVE_IOCTL_SIOCGIFADDR
/* headers for FIONBIO test */
/* includes start */
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_STROPTS_H
# include <stropts.h>
#endif
#include <net/if.h>
int
main ()
{
struct ifreq ifr;
if(0 != ioctl(0, SIOCGIFADDR, &ifr))
return 1;
;
return 0;
}
#endif
#ifdef HAVE_SETSOCKOPT_SO_NONBLOCK
/* includes start */
#ifdef HAVE_WINDOWS_H
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# else
# ifdef HAVE_WINSOCK_H
# include <winsock.h>
# endif
# endif
#endif
/* includes start */
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
/* includes end */
int
main ()
{
if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0))
return 1;
;
return 0;
}
#endif
#ifdef HAVE_GLIBC_STRERROR_R
#include <string.h>
#include <errno.h>
void check(char c) {}
int
main () {
char buffer[1024];
/* This will not compile if strerror_r does not return a char* */
check(strerror_r(EACCES, buffer, sizeof(buffer))[0]);
return 0;
}
#endif
#ifdef HAVE_POSIX_STRERROR_R
#include <string.h>
#include <errno.h>
/* float, because a pointer can't be implicitly cast to float */
void check(float f) {}
int
main () {
char buffer[1024];
/* This will not compile if strerror_r does not return an int */
check(strerror_r(EACCES, buffer, sizeof(buffer)));
return 0;
}
#endif
#ifdef HAVE_FSETXATTR_6
#include <sys/xattr.h> /* header from libc, not from libattr */
int
main() {
fsetxattr(0, 0, 0, 0, 0, 0);
return 0;
}
#endif
#ifdef HAVE_FSETXATTR_5
#include <sys/xattr.h> /* header from libc, not from libattr */
int
main() {
fsetxattr(0, 0, 0, 0, 0);
return 0;
}
#endif
#ifdef HAVE_CLOCK_GETTIME_MONOTONIC
#include <time.h>
int
main() {
struct timespec ts = {0, 0};
clock_gettime(CLOCK_MONOTONIC, &ts);
return 0;
}
#endif
#ifdef HAVE_BUILTIN_AVAILABLE
int
main() {
if(__builtin_available(macOS 10.12, *)) {}
return 0;
}
#endif
#ifdef HAVE_VARIADIC_MACROS_C99
#define c99_vmacro3(first, ...) fun3(first, __VA_ARGS__)
#define c99_vmacro2(first, ...) fun2(first, __VA_ARGS__)
int fun3(int arg1, int arg2, int arg3);
int fun2(int arg1, int arg2);
int fun3(int arg1, int arg2, int arg3) {
return arg1 + arg2 + arg3;
}
int fun2(int arg1, int arg2) {
return arg1 + arg2;
}
int
main() {
int res3 = c99_vmacro3(1, 2, 3);
int res2 = c99_vmacro2(1, 2);
(void)res3;
(void)res2;
return 0;
}
#endif
#ifdef HAVE_VARIADIC_MACROS_GCC
#define gcc_vmacro3(first, args...) fun3(first, args)
#define gcc_vmacro2(first, args...) fun2(first, args)
int fun3(int arg1, int arg2, int arg3);
int fun2(int arg1, int arg2);
int fun3(int arg1, int arg2, int arg3) {
return arg1 + arg2 + arg3;
}
int fun2(int arg1, int arg2) {
return arg1 + arg2;
}
int
main() {
int res3 = gcc_vmacro3(1, 2, 3);
int res2 = gcc_vmacro2(1, 2);
(void)res3;
(void)res2;
return 0;
}
#endif

View File

@ -1,84 +0,0 @@
#File defines convenience macros for available feature testing
# This macro checks if the symbol exists in the library and if it
# does, it prepends library to the list. It is intended to be called
# multiple times with a sequence of possibly dependent libraries in
# order of least-to-most-dependent. Some libraries depend on others
# to link correctly.
macro(check_library_exists_concat LIBRARY SYMBOL VARIABLE)
check_library_exists("${LIBRARY};${CURL_LIBS}" ${SYMBOL} "${CMAKE_LIBRARY_PATH}"
${VARIABLE})
if(${VARIABLE})
set(CURL_LIBS ${LIBRARY} ${CURL_LIBS})
endif()
endmacro()
# Check if header file exists and add it to the list.
# This macro is intended to be called multiple times with a sequence of
# possibly dependent header files. Some headers depend on others to be
# compiled correctly.
macro(check_include_file_concat FILE VARIABLE)
check_include_files("${CURL_INCLUDES};${FILE}" ${VARIABLE})
if(${VARIABLE})
set(CURL_INCLUDES ${CURL_INCLUDES} ${FILE})
set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D${VARIABLE}")
endif()
endmacro()
# For other curl specific tests, use this macro.
macro(curl_internal_test CURL_TEST)
if(NOT DEFINED "${CURL_TEST}")
set(MACRO_CHECK_FUNCTION_DEFINITIONS
"-D${CURL_TEST} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS}")
if(CMAKE_REQUIRED_LIBRARIES)
set(CURL_TEST_ADD_LIBRARIES
"-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
endif()
try_compile(${CURL_TEST}
${CMAKE_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c
CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
"${CURL_TEST_ADD_LIBRARIES}"
OUTPUT_VARIABLE OUTPUT)
if(${CURL_TEST})
set(${CURL_TEST} 1 CACHE INTERNAL "Curl test ${FUNCTION}")
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
"Performing Curl Test ${CURL_TEST} passed with the following output:\n"
"${OUTPUT}\n")
else()
set(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}")
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
"Performing Curl Test ${CURL_TEST} failed with the following output:\n"
"${OUTPUT}\n")
endif()
endif()
endmacro()
macro(curl_nroff_check)
find_program(NROFF NAMES gnroff nroff)
if(NROFF)
# Need a way to write to stdin, this will do
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt" "test")
# Tests for a valid nroff option to generate a manpage
foreach(_MANOPT "-man" "-mandoc")
execute_process(COMMAND "${NROFF}" ${_MANOPT}
OUTPUT_VARIABLE NROFF_MANOPT_OUTPUT
INPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt"
ERROR_QUIET)
# Save the option if it was valid
if(NROFF_MANOPT_OUTPUT)
set(NROFF_MANOPT ${_MANOPT})
set(NROFF_USEFUL ON)
break()
endif()
endforeach()
# No need for the temporary file
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt")
if(NOT NROFF_USEFUL)
message(WARNING "Found no *nroff option to get plaintext from man pages")
endif()
else()
message(WARNING "Found no *nroff program")
endif()
endmacro()

View File

@ -1,260 +0,0 @@
include(CheckCSourceCompiles)
# The begin of the sources (macros and includes)
set(_source_epilogue "#undef inline")
macro(add_header_include check header)
if(${check})
set(_source_epilogue "${_source_epilogue}\n#include <${header}>")
endif()
endmacro()
set(signature_call_conv)
if(HAVE_WINDOWS_H)
add_header_include(HAVE_WINSOCK2_H "winsock2.h")
add_header_include(HAVE_WINDOWS_H "windows.h")
add_header_include(HAVE_WINSOCK_H "winsock.h")
set(_source_epilogue
"${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif")
set(signature_call_conv "PASCAL")
if(HAVE_LIBWS2_32)
set(CMAKE_REQUIRED_LIBRARIES ws2_32)
endif()
else()
add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
endif()
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
check_c_source_compiles("${_source_epilogue}
int main(void) {
recv(0, 0, 0, 0);
return 0;
}" curl_cv_recv)
if(curl_cv_recv)
if(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown")
foreach(recv_retv "int" "ssize_t" )
foreach(recv_arg1 "SOCKET" "int" )
foreach(recv_arg2 "char *" "void *" )
foreach(recv_arg3 "int" "size_t" "socklen_t" "unsigned int")
foreach(recv_arg4 "int" "unsigned int")
if(NOT curl_cv_func_recv_done)
unset(curl_cv_func_recv_test CACHE)
check_c_source_compiles("
${_source_epilogue}
extern ${recv_retv} ${signature_call_conv}
recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4});
int main(void) {
${recv_arg1} s=0;
${recv_arg2} buf=0;
${recv_arg3} len=0;
${recv_arg4} flags=0;
${recv_retv} res = recv(s, buf, len, flags);
(void) res;
return 0;
}"
curl_cv_func_recv_test)
if(curl_cv_func_recv_test)
set(curl_cv_func_recv_args
"${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}")
set(RECV_TYPE_ARG1 "${recv_arg1}")
set(RECV_TYPE_ARG2 "${recv_arg2}")
set(RECV_TYPE_ARG3 "${recv_arg3}")
set(RECV_TYPE_ARG4 "${recv_arg4}")
set(RECV_TYPE_RETV "${recv_retv}")
set(HAVE_RECV 1)
set(curl_cv_func_recv_done 1)
endif()
endif()
endforeach()
endforeach()
endforeach()
endforeach()
endforeach()
else()
string(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG1 "${curl_cv_func_recv_args}")
string(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG2 "${curl_cv_func_recv_args}")
string(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG3 "${curl_cv_func_recv_args}")
string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" RECV_TYPE_ARG4 "${curl_cv_func_recv_args}")
string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" RECV_TYPE_RETV "${curl_cv_func_recv_args}")
endif()
if("${curl_cv_func_recv_args}" STREQUAL "unknown")
message(FATAL_ERROR "Cannot find proper types to use for recv args")
endif()
else()
message(FATAL_ERROR "Unable to link function recv")
endif()
set(curl_cv_func_recv_args "${curl_cv_func_recv_args}" CACHE INTERNAL "Arguments for recv")
set(HAVE_RECV 1)
check_c_source_compiles("${_source_epilogue}
int main(void) {
send(0, 0, 0, 0);
return 0;
}" curl_cv_send)
if(curl_cv_send)
if(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown")
foreach(send_retv "int" "ssize_t" )
foreach(send_arg1 "SOCKET" "int" "ssize_t" )
foreach(send_arg2 "const char *" "const void *" "void *" "char *")
foreach(send_arg3 "int" "size_t" "socklen_t" "unsigned int")
foreach(send_arg4 "int" "unsigned int")
if(NOT curl_cv_func_send_done)
unset(curl_cv_func_send_test CACHE)
check_c_source_compiles("
${_source_epilogue}
extern ${send_retv} ${signature_call_conv}
send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4});
int main(void) {
${send_arg1} s=0;
${send_arg2} buf=0;
${send_arg3} len=0;
${send_arg4} flags=0;
${send_retv} res = send(s, buf, len, flags);
(void) res;
return 0;
}"
curl_cv_func_send_test)
if(curl_cv_func_send_test)
string(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}")
string(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}")
set(curl_cv_func_send_args
"${send_arg1},${send_arg2},${send_arg3},${send_arg4},${send_retv},${send_qual_arg2}")
set(SEND_TYPE_ARG1 "${send_arg1}")
set(SEND_TYPE_ARG2 "${send_arg2}")
set(SEND_TYPE_ARG3 "${send_arg3}")
set(SEND_TYPE_ARG4 "${send_arg4}")
set(SEND_TYPE_RETV "${send_retv}")
set(HAVE_SEND 1)
set(curl_cv_func_send_done 1)
endif()
endif()
endforeach()
endforeach()
endforeach()
endforeach()
endforeach()
else()
string(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG1 "${curl_cv_func_send_args}")
string(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG2 "${curl_cv_func_send_args}")
string(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG3 "${curl_cv_func_send_args}")
string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG4 "${curl_cv_func_send_args}")
string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" SEND_TYPE_RETV "${curl_cv_func_send_args}")
string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" SEND_QUAL_ARG2 "${curl_cv_func_send_args}")
endif()
if("${curl_cv_func_send_args}" STREQUAL "unknown")
message(FATAL_ERROR "Cannot find proper types to use for send args")
endif()
set(SEND_QUAL_ARG2 "const")
else()
message(FATAL_ERROR "Unable to link function send")
endif()
set(curl_cv_func_send_args "${curl_cv_func_send_args}" CACHE INTERNAL "Arguments for send")
set(HAVE_SEND 1)
check_c_source_compiles("${_source_epilogue}
int main(void) {
int flag = MSG_NOSIGNAL;
(void)flag;
return 0;
}" HAVE_MSG_NOSIGNAL)
if(NOT HAVE_WINDOWS_H)
add_header_include(HAVE_SYS_TIME_H "sys/time.h")
add_header_include(TIME_WITH_SYS_TIME "time.h")
add_header_include(HAVE_TIME_H "time.h")
endif()
check_c_source_compiles("${_source_epilogue}
int main(void) {
struct timeval ts;
ts.tv_sec = 0;
ts.tv_usec = 0;
(void)ts;
return 0;
}" HAVE_STRUCT_TIMEVAL)
set(HAVE_SIG_ATOMIC_T 1)
set(CMAKE_REQUIRED_FLAGS)
if(HAVE_SIGNAL_H)
set(CMAKE_REQUIRED_FLAGS "-DHAVE_SIGNAL_H")
set(CMAKE_EXTRA_INCLUDE_FILES "signal.h")
endif()
check_type_size("sig_atomic_t" SIZEOF_SIG_ATOMIC_T)
if(HAVE_SIZEOF_SIG_ATOMIC_T)
check_c_source_compiles("
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
int main(void) {
static volatile sig_atomic_t dummy = 0;
(void)dummy;
return 0;
}" HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
if(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
set(HAVE_SIG_ATOMIC_T_VOLATILE 1)
endif()
endif()
if(HAVE_WINDOWS_H)
set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h)
else()
set(CMAKE_EXTRA_INCLUDE_FILES)
if(HAVE_SYS_SOCKET_H)
set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
endif()
endif()
check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE)
if(HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE)
set(HAVE_STRUCT_SOCKADDR_STORAGE 1)
endif()
unset(CMAKE_TRY_COMPILE_TARGET_TYPE)
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
# if not cross-compilation...
include(CheckCSourceRuns)
set(CMAKE_REQUIRED_FLAGS "")
if(HAVE_SYS_POLL_H)
set(CMAKE_REQUIRED_FLAGS "-DHAVE_SYS_POLL_H")
elseif(HAVE_POLL_H)
set(CMAKE_REQUIRED_FLAGS "-DHAVE_POLL_H")
endif()
check_c_source_runs("
#include <stdlib.h>
#include <sys/time.h>
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#elif HAVE_POLL_H
# include <poll.h>
#endif
int main(void)
{
if(0 != poll(0, 0, 10)) {
return 1; /* fail */
}
else {
/* detect the 10.12 poll() breakage */
struct timeval before, after;
int rc;
size_t us;
gettimeofday(&before, NULL);
rc = poll(NULL, 0, 500);
gettimeofday(&after, NULL);
us = (after.tv_sec - before.tv_sec) * 1000000 +
(after.tv_usec - before.tv_usec);
if(us < 400000) {
return 1;
}
}
return 0;
}" HAVE_POLL_FINE)
endif()

View File

@ -1,28 +0,0 @@
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.haxx.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################
CMake files under this directory were reused from project curl.
Here are links to original source files:
https://github.com/curl/curl/blob/master/CMake/CurlSymbolHiding.cmake
https://github.com/curl/curl/blob/master/CMake/CurlTests,c
https://github.com/curl/curl/blob/master/CMake/Macros.cmake
https://github.com/curl/curl/blob/master/CMake/OtherTests.cmake

File diff suppressed because it is too large Load Diff

View File

@ -1,605 +1,148 @@
#*************************************************************************** set (CURL_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl)
# _ _ ____ _
# Project ___| | | | _ \| | set (SRCS
# / __| | | | |_) | | ${CURL_DIR}/lib/file.c
# | (__| |_| | _ <| |___ ${CURL_DIR}/lib/timeval.c
# \___|\___/|_| \_\_____| ${CURL_DIR}/lib/base64.c
# ${CURL_DIR}/lib/hostip.c
# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. ${CURL_DIR}/lib/progress.c
# ${CURL_DIR}/lib/formdata.c
# This software is licensed as described in the file COPYING, which ${CURL_DIR}/lib/cookie.c
# you should have received as part of this distribution. The terms ${CURL_DIR}/lib/http.c
# are also available at https://curl.haxx.se/docs/copyright.html. ${CURL_DIR}/lib/sendf.c
# ${CURL_DIR}/lib/url.c
# You may opt to use, copy, modify, merge, publish, distribute and/or sell ${CURL_DIR}/lib/dict.c
# copies of the Software, and permit persons to whom the Software is ${CURL_DIR}/lib/if2ip.c
# furnished to do so, under the terms of the COPYING file. ${CURL_DIR}/lib/speedcheck.c
# ${CURL_DIR}/lib/ldap.c
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY ${CURL_DIR}/lib/version.c
# KIND, either express or implied. ${CURL_DIR}/lib/getenv.c
# ${CURL_DIR}/lib/escape.c
########################################################################### ${CURL_DIR}/lib/mprintf.c
${CURL_DIR}/lib/telnet.c
# NOTE: ${CURL_DIR}/lib/netrc.c
# This file is shrinked and reworked version of original curl CMakeLists.txt ${CURL_DIR}/lib/getinfo.c
# Original file link https://github.com/curl/curl/blob/3b8bbbbd1609c638a3d3d0acb148a33dedb67be3/CMakeLists.txt ${CURL_DIR}/lib/transfer.c
# If you need to update curl building you can find patch file in this directory ${CURL_DIR}/lib/strcase.c
# and apply it to fresh original CMakeLists.txt file. ${CURL_DIR}/lib/easy.c
cmake_minimum_required(VERSION 3.0 FATAL_ERROR) ${CURL_DIR}/lib/security.c
${CURL_DIR}/lib/curl_fnmatch.c
SET(CURL_SOURCE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl) ${CURL_DIR}/lib/fileinfo.c
SET(CURL_LIBRARY_DIR ${CURL_SOURCE_DIR}/lib) ${CURL_DIR}/lib/wildcard.c
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") ${CURL_DIR}/lib/krb5.c
# Disable status messages when perform checks ${CURL_DIR}/lib/memdebug.c
set(CMAKE_REQUIRED_QUIET TRUE) ${CURL_DIR}/lib/http_chunks.c
${CURL_DIR}/lib/strtok.c
include(Macros) ${CURL_DIR}/lib/connect.c
include(CMakeDependentOption) ${CURL_DIR}/lib/llist.c
include(CheckCCompilerFlag) ${CURL_DIR}/lib/hash.c
${CURL_DIR}/lib/multi.c
file(READ ${CURL_SOURCE_DIR}/include/curl/curlver.h CURL_VERSION_H_CONTENTS) ${CURL_DIR}/lib/content_encoding.c
string(REGEX MATCH "#define LIBCURL_VERSION \"[^\"]*" ${CURL_DIR}/lib/share.c
CURL_VERSION ${CURL_VERSION_H_CONTENTS}) ${CURL_DIR}/lib/http_digest.c
string(REGEX REPLACE "[^\"]+\"" "" CURL_VERSION ${CURL_VERSION}) ${CURL_DIR}/lib/md4.c
string(REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+" ${CURL_DIR}/lib/md5.c
CURL_VERSION_NUM ${CURL_VERSION_H_CONTENTS}) ${CURL_DIR}/lib/http_negotiate.c
string(REGEX REPLACE "[^0]+0x" "" CURL_VERSION_NUM ${CURL_VERSION_NUM}) ${CURL_DIR}/lib/inet_pton.c
${CURL_DIR}/lib/strtoofft.c
message(STATUS "Use curl version=[${CURL_VERSION}]") ${CURL_DIR}/lib/strerror.c
set(OPERATING_SYSTEM "${CMAKE_SYSTEM_NAME}") ${CURL_DIR}/lib/amigaos.c
set(OS "\"${CMAKE_SYSTEM_NAME}\"") ${CURL_DIR}/lib/hostasyn.c
${CURL_DIR}/lib/hostip4.c
option(PICKY_COMPILER "Enable picky compiler options" ON) ${CURL_DIR}/lib/hostip6.c
option(ENABLE_THREADED_RESOLVER "Set to ON to enable threaded DNS lookup" ON) ${CURL_DIR}/lib/hostsyn.c
${CURL_DIR}/lib/inet_ntop.c
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) ${CURL_DIR}/lib/parsedate.c
if(PICKY_COMPILER) ${CURL_DIR}/lib/select.c
foreach(_CCOPT -pedantic -Wall -W -Wpointer-arith -Wwrite-strings -Wunused -Wshadow -Winline -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wno-long-long -Wfloat-equal -Wno-multichar -Wsign-compare -Wundef -Wno-format-nonliteral -Wendif-labels -Wstrict-prototypes -Wdeclaration-after-statement -Wstrict-aliasing=3 -Wcast-align -Wtype-limits -Wold-style-declaration -Wmissing-parameter-type -Wempty-body -Wclobbered -Wignored-qualifiers -Wconversion -Wno-sign-conversion -Wvla -Wdouble-promotion -Wno-system-headers -Wno-pedantic-ms-format) ${CURL_DIR}/lib/splay.c
# surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new ${CURL_DIR}/lib/strdup.c
# test result in. ${CURL_DIR}/lib/socks.c
check_c_compiler_flag(${_CCOPT} OPT${_CCOPT}) ${CURL_DIR}/lib/curl_addrinfo.c
if(OPT${_CCOPT}) ${CURL_DIR}/lib/socks_gssapi.c
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_CCOPT}") ${CURL_DIR}/lib/socks_sspi.c
endif() ${CURL_DIR}/lib/curl_sspi.c
endforeach() ${CURL_DIR}/lib/slist.c
endif() ${CURL_DIR}/lib/nonblock.c
endif() ${CURL_DIR}/lib/curl_memrchr.c
${CURL_DIR}/lib/imap.c
# initialize CURL_LIBS ${CURL_DIR}/lib/pop3.c
set(CURL_LIBS "") ${CURL_DIR}/lib/smtp.c
${CURL_DIR}/lib/pingpong.c
include(CurlSymbolHiding) ${CURL_DIR}/lib/rtsp.c
${CURL_DIR}/lib/curl_threads.c
# Http only ${CURL_DIR}/lib/warnless.c
set(CURL_DISABLE_FTP ON) ${CURL_DIR}/lib/hmac.c
set(CURL_DISABLE_LDAP ON) ${CURL_DIR}/lib/curl_rtmp.c
set(CURL_DISABLE_LDAPS ON) ${CURL_DIR}/lib/openldap.c
set(CURL_DISABLE_TELNET ON) ${CURL_DIR}/lib/curl_gethostname.c
set(CURL_DISABLE_DICT ON) ${CURL_DIR}/lib/gopher.c
set(CURL_DISABLE_FILE ON) ${CURL_DIR}/lib/idn_win32.c
set(CURL_DISABLE_TFTP ON) ${CURL_DIR}/lib/http_proxy.c
set(CURL_DISABLE_RTSP ON) ${CURL_DIR}/lib/non-ascii.c
set(CURL_DISABLE_POP3 ON) ${CURL_DIR}/lib/asyn-thread.c
set(CURL_DISABLE_IMAP ON) ${CURL_DIR}/lib/curl_gssapi.c
set(CURL_DISABLE_SMTP ON) ${CURL_DIR}/lib/http_ntlm.c
set(CURL_DISABLE_GOPHER ON) ${CURL_DIR}/lib/curl_ntlm_wb.c
${CURL_DIR}/lib/curl_ntlm_core.c
option(CURL_DISABLE_COOKIES "to disable cookies support" OFF) ${CURL_DIR}/lib/curl_sasl.c
mark_as_advanced(CURL_DISABLE_COOKIES) ${CURL_DIR}/lib/rand.c
${CURL_DIR}/lib/curl_multibyte.c
option(CURL_DISABLE_CRYPTO_AUTH "to disable cryptographic authentication" OFF) ${CURL_DIR}/lib/hostcheck.c
mark_as_advanced(CURL_DISABLE_CRYPTO_AUTH) ${CURL_DIR}/lib/conncache.c
${CURL_DIR}/lib/dotdot.c
option(CURL_DISABLE_VERBOSE_STRINGS "to disable verbose strings" OFF) ${CURL_DIR}/lib/x509asn1.c
mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS) ${CURL_DIR}/lib/http2.c
${CURL_DIR}/lib/smb.c
option(ENABLE_IPV6 "Define if you want to enable IPv6 support" ON) ${CURL_DIR}/lib/curl_endian.c
mark_as_advanced(ENABLE_IPV6) ${CURL_DIR}/lib/curl_des.c
${CURL_DIR}/lib/system_win32.c
if(ENABLE_IPV6 AND NOT WIN32) ${CURL_DIR}/lib/mime.c
include(CheckStructHasMember) ${CURL_DIR}/lib/sha256.c
check_struct_has_member("struct sockaddr_in6" sin6_addr "netinet/in.h" ${CURL_DIR}/lib/setopt.c
HAVE_SOCKADDR_IN6_SIN6_ADDR) ${CURL_DIR}/lib/curl_path.c
check_struct_has_member("struct sockaddr_in6" sin6_scope_id "netinet/in.h" ${CURL_DIR}/lib/curl_ctype.c
HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) ${CURL_DIR}/lib/curl_range.c
if(NOT HAVE_SOCKADDR_IN6_SIN6_ADDR) ${CURL_DIR}/lib/psl.c
message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support") ${CURL_DIR}/lib/doh.c
# Force the feature off as this name is used as guard macro... ${CURL_DIR}/lib/urlapi.c
set(ENABLE_IPV6 OFF ${CURL_DIR}/lib/curl_get_line.c
CACHE BOOL "Define if you want to enable IPv6 support" FORCE) ${CURL_DIR}/lib/altsvc.c
endif() ${CURL_DIR}/lib/socketpair.c
endif() ${CURL_DIR}/lib/vauth/vauth.c
${CURL_DIR}/lib/vauth/cleartext.c
# We need ansi c-flags, especially on HP ${CURL_DIR}/lib/vauth/cram.c
set(CMAKE_C_FLAGS "${CMAKE_ANSI_CFLAGS} ${CMAKE_C_FLAGS}") ${CURL_DIR}/lib/vauth/digest.c
set(CMAKE_REQUIRED_FLAGS ${CMAKE_ANSI_CFLAGS}) ${CURL_DIR}/lib/vauth/digest_sspi.c
${CURL_DIR}/lib/vauth/krb5_gssapi.c
# Include all the necessary files for macros ${CURL_DIR}/lib/vauth/krb5_sspi.c
include(CheckFunctionExists) ${CURL_DIR}/lib/vauth/ntlm.c
include(CheckIncludeFile) ${CURL_DIR}/lib/vauth/ntlm_sspi.c
include(CheckIncludeFiles) ${CURL_DIR}/lib/vauth/oauth2.c
include(CheckLibraryExists) ${CURL_DIR}/lib/vauth/spnego_gssapi.c
include(CheckSymbolExists) ${CURL_DIR}/lib/vauth/spnego_sspi.c
include(CheckTypeSize) ${CURL_DIR}/lib/vtls/openssl.c
include(CheckCSourceCompiles) ${CURL_DIR}/lib/vtls/gtls.c
${CURL_DIR}/lib/vtls/vtls.c
if(ENABLE_THREADED_RESOLVER) ${CURL_DIR}/lib/vtls/nss.c
find_package(Threads REQUIRED) ${CURL_DIR}/lib/vtls/polarssl.c
set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT}) ${CURL_DIR}/lib/vtls/polarssl_threadlock.c
set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT}) ${CURL_DIR}/lib/vtls/wolfssl.c
set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) ${CURL_DIR}/lib/vtls/schannel.c
endif() ${CURL_DIR}/lib/vtls/schannel_verify.c
${CURL_DIR}/lib/vtls/sectransp.c
# Check for all needed libraries ${CURL_DIR}/lib/vtls/gskit.c
${CURL_DIR}/lib/vtls/mbedtls.c
# We don't want any plugin loading at runtime. It is harmful. ${CURL_DIR}/lib/vtls/mesalink.c
#check_library_exists_concat("${CMAKE_DL_LIBS}" dlopen HAVE_LIBDL) ${CURL_DIR}/lib/vtls/bearssl.c
${CURL_DIR}/lib/vquic/ngtcp2.c
# This is unneeded. ${CURL_DIR}/lib/vquic/quiche.c
#check_library_exists_concat("socket" connect HAVE_LIBSOCKET) ${CURL_DIR}/lib/vssh/libssh2.c
${CURL_DIR}/lib/vssh/libssh.c
set (NOT_NEED_LIBNSL 1) )
set (gethostname HAVE_GETHOSTNAME 1)
add_library (curl ${SRCS})
# From cmake/find/ssl.cmake
if (OPENSSL_FOUND) target_compile_definitions(curl PRIVATE HAVE_CONFIG_H BUILDING_LIBCURL CURL_HIDDEN_SYMBOLS libcurl_EXPORTS)
set(SSL_ENABLED ON) target_include_directories(curl PUBLIC ${CURL_DIR}/include ${CURL_DIR}/lib .)
set(USE_OPENSSL ON)
target_compile_definitions(curl PRIVATE OS="${CMAKE_SYSTEM_NAME}")
list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES})
check_include_file("openssl/crypto.h" HAVE_OPENSSL_CRYPTO_H)
check_include_file("openssl/err.h" HAVE_OPENSSL_ERR_H)
check_include_file("openssl/pem.h" HAVE_OPENSSL_PEM_H)
check_include_file("openssl/rsa.h" HAVE_OPENSSL_RSA_H)
check_include_file("openssl/ssl.h" HAVE_OPENSSL_SSL_H)
check_include_file("openssl/x509.h" HAVE_OPENSSL_X509_H)
check_include_file("openssl/rand.h" HAVE_OPENSSL_RAND_H)
check_symbol_exists(RAND_status "${CURL_INCLUDES}" HAVE_RAND_STATUS)
check_symbol_exists(RAND_screen "${CURL_INCLUDES}" HAVE_RAND_SCREEN)
check_symbol_exists(RAND_egd "${CURL_INCLUDES}" HAVE_RAND_EGD)
endif()
# Check for idn
# No, we don't need that.
# check_library_exists_concat("idn2" idn2_lookup_ul HAVE_LIBIDN2)
# Check for symbol dlopen (same as HAVE_LIBDL)
# We don't want any plugin loading at runtime. It is harmful.
# check_library_exists("${CURL_LIBS}" dlopen "" HAVE_DLOPEN)
# From /cmake/find/zlib.cmake
if (ZLIB_FOUND)
set(HAVE_ZLIB_H ON)
set(HAVE_LIBZ ON)
set(USE_ZLIB ON)
list(APPEND CURL_LIBS ${ZLIB_LIBRARIES})
endif()
option(ENABLE_UNIX_SOCKETS "Define if you want Unix domain sockets support" OFF)
if(ENABLE_UNIX_SOCKETS)
include(CheckStructHasMember)
check_struct_has_member("struct sockaddr_un" sun_path "sys/un.h" USE_UNIX_SOCKETS)
else()
unset(USE_UNIX_SOCKETS CACHE)
endif()
# CA handling
# Explicitly set to most common case
if (OPENSSL_FOUND)
set(CURL_CA_BUNDLE "/etc/ssl/certs/ca-certificates.crt")
set(CURL_CA_BUNDLE_SET TRUE CACHE BOOL "Path to the CA bundle has been set")
set(CURL_CA_PATH "/etc/ssl/certs")
set(CURL_CA_PATH_SET TRUE CACHE BOOL "Path to the CA bundle has been set")
endif()
check_include_file_concat("stdio.h" HAVE_STDIO_H)
check_include_file_concat("inttypes.h" HAVE_INTTYPES_H)
check_include_file_concat("sys/filio.h" HAVE_SYS_FILIO_H)
check_include_file_concat("sys/ioctl.h" HAVE_SYS_IOCTL_H)
check_include_file_concat("sys/param.h" HAVE_SYS_PARAM_H)
check_include_file_concat("sys/poll.h" HAVE_SYS_POLL_H)
check_include_file_concat("sys/resource.h" HAVE_SYS_RESOURCE_H)
check_include_file_concat("sys/select.h" HAVE_SYS_SELECT_H)
check_include_file_concat("sys/socket.h" HAVE_SYS_SOCKET_H)
check_include_file_concat("sys/sockio.h" HAVE_SYS_SOCKIO_H)
check_include_file_concat("sys/stat.h" HAVE_SYS_STAT_H)
check_include_file_concat("sys/time.h" HAVE_SYS_TIME_H)
check_include_file_concat("sys/types.h" HAVE_SYS_TYPES_H)
check_include_file_concat("sys/uio.h" HAVE_SYS_UIO_H)
check_include_file_concat("sys/un.h" HAVE_SYS_UN_H)
check_include_file_concat("sys/utime.h" HAVE_SYS_UTIME_H)
check_include_file_concat("sys/xattr.h" HAVE_SYS_XATTR_H)
check_include_file_concat("alloca.h" HAVE_ALLOCA_H)
check_include_file_concat("arpa/inet.h" HAVE_ARPA_INET_H)
#check_include_file_concat("arpa/tftp.h" HAVE_ARPA_TFTP_H)
check_include_file_concat("assert.h" HAVE_ASSERT_H)
check_include_file_concat("crypto.h" HAVE_CRYPTO_H)
check_include_file_concat("des.h" HAVE_DES_H)
check_include_file_concat("err.h" HAVE_ERR_H)
check_include_file_concat("errno.h" HAVE_ERRNO_H)
check_include_file_concat("fcntl.h" HAVE_FCNTL_H)
#check_include_file_concat("idn2.h" HAVE_IDN2_H)
check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H)
check_include_file_concat("io.h" HAVE_IO_H)
check_include_file_concat("krb.h" HAVE_KRB_H)
check_include_file_concat("libgen.h" HAVE_LIBGEN_H)
check_include_file_concat("locale.h" HAVE_LOCALE_H)
check_include_file_concat("net/if.h" HAVE_NET_IF_H)
check_include_file_concat("netdb.h" HAVE_NETDB_H)
check_include_file_concat("netinet/in.h" HAVE_NETINET_IN_H)
check_include_file_concat("netinet/tcp.h" HAVE_NETINET_TCP_H)
check_include_file_concat("pem.h" HAVE_PEM_H)
check_include_file_concat("poll.h" HAVE_POLL_H)
check_include_file_concat("pwd.h" HAVE_PWD_H)
check_include_file_concat("rsa.h" HAVE_RSA_H)
check_include_file_concat("setjmp.h" HAVE_SETJMP_H)
check_include_file_concat("sgtty.h" HAVE_SGTTY_H)
check_include_file_concat("signal.h" HAVE_SIGNAL_H)
check_include_file_concat("ssl.h" HAVE_SSL_H)
check_include_file_concat("stdbool.h" HAVE_STDBOOL_H)
check_include_file_concat("stdint.h" HAVE_STDINT_H)
check_include_file_concat("stdio.h" HAVE_STDIO_H)
check_include_file_concat("stdlib.h" HAVE_STDLIB_H)
check_include_file_concat("string.h" HAVE_STRING_H)
check_include_file_concat("strings.h" HAVE_STRINGS_H)
check_include_file_concat("stropts.h" HAVE_STROPTS_H)
check_include_file_concat("termio.h" HAVE_TERMIO_H)
check_include_file_concat("termios.h" HAVE_TERMIOS_H)
check_include_file_concat("time.h" HAVE_TIME_H)
check_include_file_concat("unistd.h" HAVE_UNISTD_H)
check_include_file_concat("utime.h" HAVE_UTIME_H)
check_include_file_concat("x509.h" HAVE_X509_H)
check_include_file_concat("process.h" HAVE_PROCESS_H)
check_include_file_concat("stddef.h" HAVE_STDDEF_H)
#check_include_file_concat("dlfcn.h" HAVE_DLFCN_H)
check_include_file_concat("malloc.h" HAVE_MALLOC_H)
check_include_file_concat("memory.h" HAVE_MEMORY_H)
check_include_file_concat("netinet/if_ether.h" HAVE_NETINET_IF_ETHER_H)
check_include_file_concat("stdint.h" HAVE_STDINT_H)
check_include_file_concat("sockio.h" HAVE_SOCKIO_H)
check_include_file_concat("sys/utsname.h" HAVE_SYS_UTSNAME_H)
check_type_size(size_t SIZEOF_SIZE_T)
check_type_size(ssize_t SIZEOF_SSIZE_T)
check_type_size("long long" SIZEOF_LONG_LONG)
check_type_size("long" SIZEOF_LONG)
check_type_size("short" SIZEOF_SHORT)
check_type_size("int" SIZEOF_INT)
check_type_size("__int64" SIZEOF___INT64)
check_type_size("long double" SIZEOF_LONG_DOUBLE)
check_type_size("time_t" SIZEOF_TIME_T)
set(HAVE_LONGLONG 1)
set(HAVE_LL 1)
set(RANDOM_FILE /dev/urandom)
check_symbol_exists(basename "${CURL_INCLUDES}" HAVE_BASENAME)
check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET)
check_symbol_exists(select "${CURL_INCLUDES}" HAVE_SELECT)
check_symbol_exists(poll "${CURL_INCLUDES}" HAVE_POLL)
check_symbol_exists(strdup "${CURL_INCLUDES}" HAVE_STRDUP)
check_symbol_exists(strstr "${CURL_INCLUDES}" HAVE_STRSTR)
check_symbol_exists(strtok_r "${CURL_INCLUDES}" HAVE_STRTOK_R)
check_symbol_exists(strftime "${CURL_INCLUDES}" HAVE_STRFTIME)
check_symbol_exists(uname "${CURL_INCLUDES}" HAVE_UNAME)
check_symbol_exists(strcasecmp "${CURL_INCLUDES}" HAVE_STRCASECMP)
#check_symbol_exists(stricmp "${CURL_INCLUDES}" HAVE_STRICMP)
#check_symbol_exists(strcmpi "${CURL_INCLUDES}" HAVE_STRCMPI)
#check_symbol_exists(strncmpi "${CURL_INCLUDES}" HAVE_STRNCMPI)
check_symbol_exists(alarm "${CURL_INCLUDES}" HAVE_ALARM)
#check_symbol_exists(gethostbyaddr "${CURL_INCLUDES}" HAVE_GETHOSTBYADDR)
check_symbol_exists(gethostbyaddr_r "${CURL_INCLUDES}" HAVE_GETHOSTBYADDR_R)
check_symbol_exists(gettimeofday "${CURL_INCLUDES}" HAVE_GETTIMEOFDAY)
check_symbol_exists(inet_addr "${CURL_INCLUDES}" HAVE_INET_ADDR)
#check_symbol_exists(inet_ntoa "${CURL_INCLUDES}" HAVE_INET_NTOA)
check_symbol_exists(inet_ntoa_r "${CURL_INCLUDES}" HAVE_INET_NTOA_R)
check_symbol_exists(tcsetattr "${CURL_INCLUDES}" HAVE_TCSETATTR)
check_symbol_exists(tcgetattr "${CURL_INCLUDES}" HAVE_TCGETATTR)
check_symbol_exists(perror "${CURL_INCLUDES}" HAVE_PERROR)
check_symbol_exists(closesocket "${CURL_INCLUDES}" HAVE_CLOSESOCKET)
check_symbol_exists(setvbuf "${CURL_INCLUDES}" HAVE_SETVBUF)
check_symbol_exists(sigsetjmp "${CURL_INCLUDES}" HAVE_SIGSETJMP)
check_symbol_exists(getpass_r "${CURL_INCLUDES}" HAVE_GETPASS_R)
#check_symbol_exists(strlcat "${CURL_INCLUDES}" HAVE_STRLCAT)
#check_symbol_exists(getpwuid "${CURL_INCLUDES}" HAVE_GETPWUID)
check_symbol_exists(getpwuid_r "${CURL_INCLUDES}" HAVE_GETPWUID_R)
check_symbol_exists(geteuid "${CURL_INCLUDES}" HAVE_GETEUID)
check_symbol_exists(usleep "${CURL_INCLUDES}" HAVE_USLEEP)
check_symbol_exists(utime "${CURL_INCLUDES}" HAVE_UTIME)
check_symbol_exists(gmtime_r "${CURL_INCLUDES}" HAVE_GMTIME_R)
check_symbol_exists(localtime_r "${CURL_INCLUDES}" HAVE_LOCALTIME_R)
#check_symbol_exists(gethostbyname "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME)
check_symbol_exists(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R)
check_symbol_exists(signal "${CURL_INCLUDES}" HAVE_SIGNAL_FUNC)
check_symbol_exists(SIGALRM "${CURL_INCLUDES}" HAVE_SIGNAL_MACRO)
set(HAVE_SIGNAL 1)
check_symbol_exists(uname "${CURL_INCLUDES}" HAVE_UNAME)
check_symbol_exists(strtoll "${CURL_INCLUDES}" HAVE_STRTOLL)
#check_symbol_exists(_strtoi64 "${CURL_INCLUDES}" HAVE__STRTOI64)
check_symbol_exists(strerror_r "${CURL_INCLUDES}" HAVE_STRERROR_R)
check_symbol_exists(siginterrupt "${CURL_INCLUDES}" HAVE_SIGINTERRUPT)
check_symbol_exists(perror "${CURL_INCLUDES}" HAVE_PERROR)
check_symbol_exists(fork "${CURL_INCLUDES}" HAVE_FORK)
check_symbol_exists(getaddrinfo "${CURL_INCLUDES}" HAVE_GETADDRINFO)
check_symbol_exists(freeaddrinfo "${CURL_INCLUDES}" HAVE_FREEADDRINFO)
check_symbol_exists(freeifaddrs "${CURL_INCLUDES}" HAVE_FREEIFADDRS)
check_symbol_exists(pipe "${CURL_INCLUDES}" HAVE_PIPE)
check_symbol_exists(ftruncate "${CURL_INCLUDES}" HAVE_FTRUNCATE)
check_symbol_exists(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME)
check_symbol_exists(getpeername "${CURL_INCLUDES}" HAVE_GETPEERNAME)
check_symbol_exists(getsockname "${CURL_INCLUDES}" HAVE_GETSOCKNAME)
check_symbol_exists(if_nametoindex "${CURL_INCLUDES}" HAVE_IF_NAMETOINDEX)
check_symbol_exists(getrlimit "${CURL_INCLUDES}" HAVE_GETRLIMIT)
check_symbol_exists(setlocale "${CURL_INCLUDES}" HAVE_SETLOCALE)
check_symbol_exists(setmode "${CURL_INCLUDES}" HAVE_SETMODE)
check_symbol_exists(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT)
check_symbol_exists(fcntl "${CURL_INCLUDES}" HAVE_FCNTL)
check_symbol_exists(ioctl "${CURL_INCLUDES}" HAVE_IOCTL)
check_symbol_exists(setsockopt "${CURL_INCLUDES}" HAVE_SETSOCKOPT)
check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME)
check_symbol_exists(fsetxattr "${CURL_INCLUDES}" HAVE_FSETXATTR)
if(HAVE_FSETXATTR)
foreach(CURL_TEST HAVE_FSETXATTR_5 HAVE_FSETXATTR_6)
curl_internal_test(${CURL_TEST})
endforeach()
endif()
# sigaction and sigsetjmp are special. Use special mechanism for
# detecting those, but only if previous attempt failed.
if(HAVE_SIGNAL_H)
check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION)
endif()
if(NOT HAVE_SIGSETJMP)
if(HAVE_SETJMP_H)
check_symbol_exists(sigsetjmp "setjmp.h" HAVE_MACRO_SIGSETJMP)
if(HAVE_MACRO_SIGSETJMP)
set(HAVE_SIGSETJMP 1)
endif()
endif()
endif()
# If there is no stricmp(), do not allow LDAP to parse URLs
if(NOT HAVE_STRICMP)
set(HAVE_LDAP_URL_PARSE 1)
endif()
# Do curl specific tests
foreach(CURL_TEST
HAVE_FCNTL_O_NONBLOCK
HAVE_IOCTLSOCKET
HAVE_IOCTLSOCKET_CAMEL
HAVE_IOCTLSOCKET_CAMEL_FIONBIO
HAVE_IOCTLSOCKET_FIONBIO
HAVE_IOCTL_FIONBIO
HAVE_IOCTL_SIOCGIFADDR
HAVE_SETSOCKOPT_SO_NONBLOCK
HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
TIME_WITH_SYS_TIME
HAVE_O_NONBLOCK
HAVE_GETHOSTBYADDR_R_5
HAVE_GETHOSTBYADDR_R_7
HAVE_GETHOSTBYADDR_R_8
HAVE_GETHOSTBYADDR_R_5_REENTRANT
HAVE_GETHOSTBYADDR_R_7_REENTRANT
HAVE_GETHOSTBYADDR_R_8_REENTRANT
HAVE_GETHOSTBYNAME_R_3
HAVE_GETHOSTBYNAME_R_5
HAVE_GETHOSTBYNAME_R_6
HAVE_GETHOSTBYNAME_R_3_REENTRANT
HAVE_GETHOSTBYNAME_R_5_REENTRANT
HAVE_GETHOSTBYNAME_R_6_REENTRANT
HAVE_IN_ADDR_T
HAVE_BOOL_T
STDC_HEADERS
RETSIGTYPE_TEST
HAVE_INET_NTOA_R_DECL
HAVE_INET_NTOA_R_DECL_REENTRANT
HAVE_GETADDRINFO
HAVE_FILE_OFFSET_BITS
HAVE_VARIADIC_MACROS_C99
HAVE_VARIADIC_MACROS_GCC
)
curl_internal_test(${CURL_TEST})
endforeach()
if(HAVE_FILE_OFFSET_BITS)
set(_FILE_OFFSET_BITS 64)
set(CMAKE_REQUIRED_FLAGS "-D_FILE_OFFSET_BITS=64")
endif()
check_type_size("off_t" SIZEOF_OFF_T)
# include this header to get the type
set(CMAKE_REQUIRED_INCLUDES "${CURL_SOURCE_DIR}/include")
set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h")
check_type_size("curl_off_t" SIZEOF_CURL_OFF_T)
set(CMAKE_EXTRA_INCLUDE_FILES "")
foreach(CURL_TEST
HAVE_GLIBC_STRERROR_R
HAVE_POSIX_STRERROR_R
)
curl_internal_test(${CURL_TEST})
endforeach()
# Check for reentrant
foreach(CURL_TEST
HAVE_GETHOSTBYADDR_R_5
HAVE_GETHOSTBYADDR_R_7
HAVE_GETHOSTBYADDR_R_8
HAVE_GETHOSTBYNAME_R_3
HAVE_GETHOSTBYNAME_R_5
HAVE_GETHOSTBYNAME_R_6
HAVE_INET_NTOA_R_DECL_REENTRANT)
if(NOT ${CURL_TEST})
if(${CURL_TEST}_REENTRANT)
set(NEED_REENTRANT 1)
endif()
endif()
endforeach()
if(NEED_REENTRANT)
foreach(CURL_TEST
HAVE_GETHOSTBYADDR_R_5
HAVE_GETHOSTBYADDR_R_7
HAVE_GETHOSTBYADDR_R_8
HAVE_GETHOSTBYNAME_R_3
HAVE_GETHOSTBYNAME_R_5
HAVE_GETHOSTBYNAME_R_6)
set(${CURL_TEST} 0)
if(${CURL_TEST}_REENTRANT)
set(${CURL_TEST} 1)
endif()
endforeach()
endif()
if(HAVE_INET_NTOA_R_DECL_REENTRANT)
set(HAVE_INET_NTOA_R_DECL 1)
set(NEED_REENTRANT 1)
endif()
# Check clock_gettime(CLOCK_MONOTONIC, x) support
curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)
# Check compiler support of __builtin_available()
curl_internal_test(HAVE_BUILTIN_AVAILABLE)
# Some other minor tests
if(NOT HAVE_IN_ADDR_T)
set(in_addr_t "unsigned long")
endif()
# Check for nonblocking
set(HAVE_DISABLED_NONBLOCKING 1)
if(HAVE_FIONBIO OR
HAVE_IOCTLSOCKET OR
HAVE_IOCTLSOCKET_CASE OR
HAVE_O_NONBLOCK)
set(HAVE_DISABLED_NONBLOCKING)
endif()
set(CURL_PULL_SYS_TYPES_H ${HAVE_SYS_TYPES_H})
set(CURL_PULL_SYS_SOCKET_H ${HAVE_SYS_SOCKET_H})
set(CURL_PULL_SYS_POLL_H ${HAVE_SYS_POLL_H})
set(CURL_PULL_STDINT_H ${HAVE_STDINT_H})
set(CURL_PULL_INTTYPES_H ${HAVE_INTTYPES_H})
include(CMake/OtherTests.cmake)
SET(LIB_VAUTH_CFILES
"${CURL_LIBRARY_DIR}/vauth/vauth.c" "${CURL_LIBRARY_DIR}/vauth/cleartext.c" "${CURL_LIBRARY_DIR}/vauth/cram.c"
"${CURL_LIBRARY_DIR}/vauth/digest.c" "${CURL_LIBRARY_DIR}/vauth/digest_sspi.c" "${CURL_LIBRARY_DIR}/vauth/krb5_gssapi.c"
"${CURL_LIBRARY_DIR}/vauth/krb5_sspi.c" "${CURL_LIBRARY_DIR}/vauth/ntlm.c" "${CURL_LIBRARY_DIR}/vauth/ntlm_sspi.c" "${CURL_LIBRARY_DIR}/vauth/oauth2.c"
"${CURL_LIBRARY_DIR}/vauth/spnego_gssapi.c" "${CURL_LIBRARY_DIR}/vauth/spnego_sspi.c")
SET(LIB_VAUTH_HFILES "${CURL_LIBRARY_DIR}/vauth/vauth.h" "${CURL_LIBRARY_DIR}/vauth/digest.h" "${CURL_LIBRARY_DIR}/vauth/ntlm.h")
SET(LIB_VTLS_CFILES "${CURL_LIBRARY_DIR}/vtls/openssl.c" "${CURL_LIBRARY_DIR}/vtls/gtls.c" "${CURL_LIBRARY_DIR}/vtls/vtls.c" "${CURL_LIBRARY_DIR}/vtls/nss.c"
"${CURL_LIBRARY_DIR}/vtls/polarssl.c" "${CURL_LIBRARY_DIR}/vtls/polarssl_threadlock.c"
"${CURL_LIBRARY_DIR}/vtls/wolfssl.c" "${CURL_LIBRARY_DIR}/vtls/schannel.c" "${CURL_LIBRARY_DIR}/vtls/schannel_verify.c"
"${CURL_LIBRARY_DIR}/vtls/sectransp.c" "${CURL_LIBRARY_DIR}/vtls/gskit.c" "${CURL_LIBRARY_DIR}/vtls/mbedtls.c" "${CURL_LIBRARY_DIR}/vtls/mesalink.c"
"${CURL_LIBRARY_DIR}/vtls/bearssl.c")
SET(LIB_VTLS_HFILES "${CURL_LIBRARY_DIR}/vtls/openssl.h" "${CURL_LIBRARY_DIR}/vtls/vtls.h" "${CURL_LIBRARY_DIR}/vtls/gtls.h"
"${CURL_LIBRARY_DIR}/vtls/nssg.h" "${CURL_LIBRARY_DIR}/vtls/polarssl.h" "${CURL_LIBRARY_DIR}/vtls/polarssl_threadlock.h"
"${CURL_LIBRARY_DIR}/vtls/wolfssl.h" "${CURL_LIBRARY_DIR}/vtls/schannel.h" "${CURL_LIBRARY_DIR}/vtls/sectransp.h" "${CURL_LIBRARY_DIR}/vtls/gskit.h"
"${CURL_LIBRARY_DIR}/vtls/mbedtls.h" "${CURL_LIBRARY_DIR}/vtls/mesalink.h" "${CURL_LIBRARY_DIR}/vtls/bearssl.h")
SET(LIB_VQUIC_CFILES "${CURL_LIBRARY_DIR}/vquic/ngtcp2.c" "${CURL_LIBRARY_DIR}/vquic/quiche.c")
SET(LIB_VQUIC_HFILES "${CURL_LIBRARY_DIR}/vquic/ngtcp2.h" "${CURL_LIBRARY_DIR}/vquic/quiche.h")
SET(LIB_VSSH_CFILES "${CURL_LIBRARY_DIR}/vssh/libssh2.c" "${CURL_LIBRARY_DIR}/vssh/libssh.c")
SET(LIB_VSSH_HFILES "${CURL_LIBRARY_DIR}/vssh/ssh.h")
SET(LIB_CFILES "${CURL_LIBRARY_DIR}/file.c"
"${CURL_LIBRARY_DIR}/timeval.c" "${CURL_LIBRARY_DIR}/base64.c" "${CURL_LIBRARY_DIR}/hostip.c" "${CURL_LIBRARY_DIR}/progress.c" "${CURL_LIBRARY_DIR}/formdata.c"
"${CURL_LIBRARY_DIR}/cookie.c" "${CURL_LIBRARY_DIR}/http.c" "${CURL_LIBRARY_DIR}/sendf.c" "${CURL_LIBRARY_DIR}/url.c" "${CURL_LIBRARY_DIR}/dict.c" "${CURL_LIBRARY_DIR}/if2ip.c" "${CURL_LIBRARY_DIR}/speedcheck.c"
"${CURL_LIBRARY_DIR}/ldap.c" "${CURL_LIBRARY_DIR}/version.c" "${CURL_LIBRARY_DIR}/getenv.c" "${CURL_LIBRARY_DIR}/escape.c" "${CURL_LIBRARY_DIR}/mprintf.c" "${CURL_LIBRARY_DIR}/telnet.c" "${CURL_LIBRARY_DIR}/netrc.c"
"${CURL_LIBRARY_DIR}/getinfo.c" "${CURL_LIBRARY_DIR}/transfer.c" "${CURL_LIBRARY_DIR}/strcase.c" "${CURL_LIBRARY_DIR}/easy.c" "${CURL_LIBRARY_DIR}/security.c" "${CURL_LIBRARY_DIR}/curl_fnmatch.c"
"${CURL_LIBRARY_DIR}/fileinfo.c" "${CURL_LIBRARY_DIR}/wildcard.c" "${CURL_LIBRARY_DIR}/krb5.c" "${CURL_LIBRARY_DIR}/memdebug.c" "${CURL_LIBRARY_DIR}/http_chunks.c"
"${CURL_LIBRARY_DIR}/strtok.c" "${CURL_LIBRARY_DIR}/connect.c" "${CURL_LIBRARY_DIR}/llist.c" "${CURL_LIBRARY_DIR}/hash.c" "${CURL_LIBRARY_DIR}/multi.c" "${CURL_LIBRARY_DIR}/content_encoding.c" "${CURL_LIBRARY_DIR}/share.c"
"${CURL_LIBRARY_DIR}/http_digest.c" "${CURL_LIBRARY_DIR}/md4.c" "${CURL_LIBRARY_DIR}/md5.c" "${CURL_LIBRARY_DIR}/http_negotiate.c" "${CURL_LIBRARY_DIR}/inet_pton.c" "${CURL_LIBRARY_DIR}/strtoofft.c"
"${CURL_LIBRARY_DIR}/strerror.c" "${CURL_LIBRARY_DIR}/amigaos.c" "${CURL_LIBRARY_DIR}/hostasyn.c" "${CURL_LIBRARY_DIR}/hostip4.c" "${CURL_LIBRARY_DIR}/hostip6.c" "${CURL_LIBRARY_DIR}/hostsyn.c"
"${CURL_LIBRARY_DIR}/inet_ntop.c" "${CURL_LIBRARY_DIR}/parsedate.c" "${CURL_LIBRARY_DIR}/select.c" "${CURL_LIBRARY_DIR}/splay.c" "${CURL_LIBRARY_DIR}/strdup.c" "${CURL_LIBRARY_DIR}/socks.c"
"${CURL_LIBRARY_DIR}/curl_addrinfo.c" "${CURL_LIBRARY_DIR}/socks_gssapi.c" "${CURL_LIBRARY_DIR}/socks_sspi.c"
"${CURL_LIBRARY_DIR}/curl_sspi.c" "${CURL_LIBRARY_DIR}/slist.c" "${CURL_LIBRARY_DIR}/nonblock.c" "${CURL_LIBRARY_DIR}/curl_memrchr.c" "${CURL_LIBRARY_DIR}/imap.c" "${CURL_LIBRARY_DIR}/pop3.c" "${CURL_LIBRARY_DIR}/smtp.c"
"${CURL_LIBRARY_DIR}/pingpong.c" "${CURL_LIBRARY_DIR}/rtsp.c" "${CURL_LIBRARY_DIR}/curl_threads.c" "${CURL_LIBRARY_DIR}/warnless.c" "${CURL_LIBRARY_DIR}/hmac.c" "${CURL_LIBRARY_DIR}/curl_rtmp.c"
"${CURL_LIBRARY_DIR}/openldap.c" "${CURL_LIBRARY_DIR}/curl_gethostname.c" "${CURL_LIBRARY_DIR}/gopher.c" "${CURL_LIBRARY_DIR}/idn_win32.c"
"${CURL_LIBRARY_DIR}/http_proxy.c" "${CURL_LIBRARY_DIR}/non-ascii.c" "${CURL_LIBRARY_DIR}/asyn-ares.c" "${CURL_LIBRARY_DIR}/asyn-thread.c" "${CURL_LIBRARY_DIR}/curl_gssapi.c"
"${CURL_LIBRARY_DIR}/http_ntlm.c" "${CURL_LIBRARY_DIR}/curl_ntlm_wb.c" "${CURL_LIBRARY_DIR}/curl_ntlm_core.c" "${CURL_LIBRARY_DIR}/curl_sasl.c" "${CURL_LIBRARY_DIR}/rand.c"
"${CURL_LIBRARY_DIR}/curl_multibyte.c" "${CURL_LIBRARY_DIR}/hostcheck.c" "${CURL_LIBRARY_DIR}/conncache.c" "${CURL_LIBRARY_DIR}/dotdot.c"
"${CURL_LIBRARY_DIR}/x509asn1.c" "${CURL_LIBRARY_DIR}/http2.c" "${CURL_LIBRARY_DIR}/smb.c" "${CURL_LIBRARY_DIR}/curl_endian.c" "${CURL_LIBRARY_DIR}/curl_des.c" "${CURL_LIBRARY_DIR}/system_win32.c"
"${CURL_LIBRARY_DIR}/mime.c" "${CURL_LIBRARY_DIR}/sha256.c" "${CURL_LIBRARY_DIR}/setopt.c" "${CURL_LIBRARY_DIR}/curl_path.c" "${CURL_LIBRARY_DIR}/curl_ctype.c" "${CURL_LIBRARY_DIR}/curl_range.c" "${CURL_LIBRARY_DIR}/psl.c"
"${CURL_LIBRARY_DIR}/doh.c" "${CURL_LIBRARY_DIR}/urlapi.c" "${CURL_LIBRARY_DIR}/curl_get_line.c" "${CURL_LIBRARY_DIR}/altsvc.c" "${CURL_LIBRARY_DIR}/socketpair.c")
SET(LIB_HFILES "${CURL_LIBRARY_DIR}/arpa_telnet.h" "${CURL_LIBRARY_DIR}/netrc.h" "${CURL_LIBRARY_DIR}/file.h" "${CURL_LIBRARY_DIR}/timeval.h" "${CURL_LIBRARY_DIR}/hostip.h" "${CURL_LIBRARY_DIR}/progress.h"
"${CURL_LIBRARY_DIR}/formdata.h" "${CURL_LIBRARY_DIR}/cookie.h" "${CURL_LIBRARY_DIR}/http.h" "${CURL_LIBRARY_DIR}/sendf.h" "${CURL_LIBRARY_DIR}/url.h" "${CURL_LIBRARY_DIR}/dict.h" "${CURL_LIBRARY_DIR}/if2ip.h"
"${CURL_LIBRARY_DIR}/speedcheck.h" "${CURL_LIBRARY_DIR}/urldata.h" "${CURL_LIBRARY_DIR}/curl_ldap.h" "${CURL_LIBRARY_DIR}/escape.h" "${CURL_LIBRARY_DIR}/telnet.h" "${CURL_LIBRARY_DIR}/getinfo.h"
"${CURL_LIBRARY_DIR}/strcase.h" "${CURL_LIBRARY_DIR}/curl_sec.h" "${CURL_LIBRARY_DIR}/memdebug.h" "${CURL_LIBRARY_DIR}/http_chunks.h" "${CURL_LIBRARY_DIR}/curl_fnmatch.h"
"${CURL_LIBRARY_DIR}/wildcard.h" "${CURL_LIBRARY_DIR}/fileinfo.h" "${CURL_LIBRARY_DIR}/strtok.h" "${CURL_LIBRARY_DIR}/connect.h" "${CURL_LIBRARY_DIR}/llist.h"
"${CURL_LIBRARY_DIR}/hash.h" "${CURL_LIBRARY_DIR}/content_encoding.h" "${CURL_LIBRARY_DIR}/share.h" "${CURL_LIBRARY_DIR}/curl_md4.h" "${CURL_LIBRARY_DIR}/curl_md5.h" "${CURL_LIBRARY_DIR}/http_digest.h"
"${CURL_LIBRARY_DIR}/http_negotiate.h" "${CURL_LIBRARY_DIR}/inet_pton.h" "${CURL_LIBRARY_DIR}/amigaos.h" "${CURL_LIBRARY_DIR}/strtoofft.h" "${CURL_LIBRARY_DIR}/strerror.h"
"${CURL_LIBRARY_DIR}/inet_ntop.h" "${CURL_LIBRARY_DIR}/curlx.h" "${CURL_LIBRARY_DIR}/curl_memory.h" "${CURL_LIBRARY_DIR}/curl_setup.h" "${CURL_LIBRARY_DIR}/transfer.h" "${CURL_LIBRARY_DIR}/select.h"
"${CURL_LIBRARY_DIR}/easyif.h" "${CURL_LIBRARY_DIR}/multiif.h" "${CURL_LIBRARY_DIR}/parsedate.h" "${CURL_LIBRARY_DIR}/sockaddr.h" "${CURL_LIBRARY_DIR}/splay.h" "${CURL_LIBRARY_DIR}/strdup.h"
"${CURL_LIBRARY_DIR}/socks.h" "${CURL_LIBRARY_DIR}/curl_base64.h" "${CURL_LIBRARY_DIR}/curl_addrinfo.h" "${CURL_LIBRARY_DIR}/curl_sspi.h"
"${CURL_LIBRARY_DIR}/slist.h" "${CURL_LIBRARY_DIR}/nonblock.h" "${CURL_LIBRARY_DIR}/curl_memrchr.h" "${CURL_LIBRARY_DIR}/imap.h" "${CURL_LIBRARY_DIR}/pop3.h" "${CURL_LIBRARY_DIR}/smtp.h" "${CURL_LIBRARY_DIR}/pingpong.h"
"${CURL_LIBRARY_DIR}/rtsp.h" "${CURL_LIBRARY_DIR}/curl_threads.h" "${CURL_LIBRARY_DIR}/warnless.h" "${CURL_LIBRARY_DIR}/curl_hmac.h" "${CURL_LIBRARY_DIR}/curl_rtmp.h"
"${CURL_LIBRARY_DIR}/curl_gethostname.h" "${CURL_LIBRARY_DIR}/gopher.h" "${CURL_LIBRARY_DIR}/http_proxy.h" "${CURL_LIBRARY_DIR}/non-ascii.h" "${CURL_LIBRARY_DIR}/asyn.h"
"${CURL_LIBRARY_DIR}/http_ntlm.h" "${CURL_LIBRARY_DIR}/curl_gssapi.h" "${CURL_LIBRARY_DIR}/curl_ntlm_wb.h" "${CURL_LIBRARY_DIR}/curl_ntlm_core.h"
"${CURL_LIBRARY_DIR}/curl_sasl.h" "${CURL_LIBRARY_DIR}/curl_multibyte.h" "${CURL_LIBRARY_DIR}/hostcheck.h" "${CURL_LIBRARY_DIR}/conncache.h"
"${CURL_LIBRARY_DIR}/multihandle.h" "${CURL_LIBRARY_DIR}/setup-vms.h" "${CURL_LIBRARY_DIR}/dotdot.h"
"${CURL_LIBRARY_DIR}/x509asn1.h" "${CURL_LIBRARY_DIR}/http2.h" "${CURL_LIBRARY_DIR}/sigpipe.h" "${CURL_LIBRARY_DIR}/smb.h" "${CURL_LIBRARY_DIR}/curl_endian.h" "${CURL_LIBRARY_DIR}/curl_des.h"
"${CURL_LIBRARY_DIR}/curl_printf.h" "${CURL_LIBRARY_DIR}/system_win32.h" "${CURL_LIBRARY_DIR}/rand.h" "${CURL_LIBRARY_DIR}/mime.h" "${CURL_LIBRARY_DIR}/curl_sha256.h" "${CURL_LIBRARY_DIR}/setopt.h"
"${CURL_LIBRARY_DIR}/curl_path.h" "${CURL_LIBRARY_DIR}/curl_ctype.h" "${CURL_LIBRARY_DIR}/curl_range.h" "${CURL_LIBRARY_DIR}/psl.h" "${CURL_LIBRARY_DIR}/doh.h" "${CURL_LIBRARY_DIR}/urlapi-int.h"
"${CURL_LIBRARY_DIR}/curl_get_line.h" "${CURL_LIBRARY_DIR}/altsvc.h" "${CURL_LIBRARY_DIR}/quic.h" "${CURL_LIBRARY_DIR}/socketpair.h")
SET(LIB_RCFILES "${CURL_LIBRARY_DIR}/libcurl.rc")
SET(CSOURCES ${LIB_CFILES} ${LIB_VAUTH_CFILES} ${LIB_VTLS_CFILES}
${LIB_VQUIC_CFILES} ${LIB_VSSH_CFILES})
SET(HHEADERS ${LIB_HFILES} ${LIB_VAUTH_HFILES} ${LIB_VTLS_HFILES}
${LIB_VQUIC_HFILES} ${LIB_VSSH_HFILES})
configure_file(${CURL_SOURCE_DIR}/lib/curl_config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/curl/curl_config.h)
list(APPEND HHEADERS
${CMAKE_CURRENT_BINARY_DIR}/curl/curl_config.h
)
add_library(libcurl ${HHEADERS} ${CSOURCES})
if(NOT BUILD_SHARED_LIBS)
set_target_properties(libcurl PROPERTIES INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB)
endif()
if(HIDES_CURL_PRIVATE_SYMBOLS)
set_property(TARGET libcurl APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
set_property(TARGET libcurl APPEND PROPERTY COMPILE_FLAGS ${CURL_CFLAG_SYMBOLS_HIDE})
endif()
if(OPENSSL_FOUND)
target_include_directories(libcurl PUBLIC ${OPENSSL_INCLUDE_DIR})
message("-- Including openssl ${OPENSSL_INCLUDE_DIR} to curl")
endif()
if(ZLIB_FOUND)
target_include_directories(libcurl PUBLIC ${ZLIB_INCLUDE_DIRS}})
message("-- Including zlib ${ZLIB_INCLUDE_DIRS} to curl")
endif()
target_compile_definitions(libcurl PUBLIC -DHAVE_CONFIG_H)
target_compile_definitions(libcurl PUBLIC -DBUILDING_LIBCURL)
target_include_directories(libcurl PUBLIC "${CURL_SOURCE_DIR}/include" "${CURL_LIBRARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/curl")
target_link_libraries(libcurl ${CURL_LIBS})

View File

@ -0,0 +1,38 @@
#define CURL_DISABLE_FTP
#define CURL_DISABLE_TFTP
#define CURL_DISABLE_LDAP
#define CURL_EXTERN_SYMBOL __attribute__ ((__visibility__ ("default")))
#define SIZEOF_SHORT 2
#define SIZEOF_INT 4
#define SIZEOF_LONG 8
#define SIZEOF_CURL_OFF_T 8
#define SIZEOF_SIZE_T 8
#define HAVE_FCNTL_O_NONBLOCK
#define HAVE_LONGLONG
#define HAVE_POLL_FINE
#define HAVE_SOCKET
#define HAVE_STRUCT_TIMEVAL
#define HAVE_RECV
#define RECV_TYPE_ARG1 int
#define RECV_TYPE_ARG2 void*
#define RECV_TYPE_ARG3 size_t
#define RECV_TYPE_ARG4 int
#define RECV_TYPE_RETV ssize_t
#define HAVE_SEND
#define SEND_TYPE_ARG1 int
#define SEND_TYPE_ARG2 void*
#define SEND_QUAL_ARG2 const
#define SEND_TYPE_ARG3 size_t
#define SEND_TYPE_ARG4 int
#define SEND_TYPE_RETV ssize_t
#define HAVE_ARPA_INET_H
#define HAVE_ERRNO_H
#define HAVE_FCNTL_H
#define HAVE_NETDB_H
#define HAVE_SYS_STAT_H
#define HAVE_UNISTD_H

View File

@ -9,7 +9,10 @@ cat "$QUERIES_FILE" | sed "s|{table}|\"${TABLE}\"|g" | while read query; do
echo -n "[" echo -n "["
for i in $(seq 1 $TRIES); do for i in $(seq 1 $TRIES); do
while true; do while true; do
RES=$(command time -f %e -o /dev/stdout curl -sS --location-trusted -H "Authorization: OAuth $YT_TOKEN" "$YT_PROXY.yt.yandex.net/query?default_format=Null&database=*$YT_CLIQUE_ID" --data-binary @- <<< "$query" 2>/dev/null) && break; RES=$(command time -f %e -o /dev/stdout curl -sS -G --data-urlencode "query=$query" --data "default_format=Null&max_memory_usage=100000000000&max_memory_usage_for_all_queries=100000000000&max_concurrent_queries_for_user=100&database=*$YT_CLIQUE_ID" --location-trusted -H "Authorization: OAuth $YT_TOKEN" "$YT_PROXY.yt.yandex.net/query" 2>/dev/null);
if [[ $? == 0 ]]; then
[[ $RES =~ 'fail|Exception' ]] || break;
fi
done done
[[ "$?" == "0" ]] && echo -n "${RES}" || echo -n "null" [[ "$?" == "0" ]] && echo -n "${RES}" || echo -n "null"

View File

@ -34,10 +34,10 @@ SELECT WatchID, ClientIP, count() AS c, sum(Refresh), avg(ResolutionWidth) FROM
SELECT URL, count() AS c FROM {table} GROUP BY URL ORDER BY c DESC LIMIT 10; SELECT URL, count() AS c FROM {table} GROUP BY URL ORDER BY c DESC LIMIT 10;
SELECT 1, URL, count() AS c FROM {table} GROUP BY 1, URL ORDER BY c DESC LIMIT 10; SELECT 1, URL, count() AS c FROM {table} GROUP BY 1, URL ORDER BY c DESC LIMIT 10;
SELECT ClientIP AS x, x - 1, x - 2, x - 3, count() AS c FROM {table} GROUP BY x, x - 1, x - 2, x - 3 ORDER BY c DESC LIMIT 10; SELECT ClientIP AS x, x - 1, x - 2, x - 3, count() AS c FROM {table} GROUP BY x, x - 1, x - 2, x - 3 ORDER BY c DESC LIMIT 10;
SELECT URL, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= toDate('2013-07-01') AND EventDate <= toDate('2013-07-31') AND NOT DontCountHits AND NOT Refresh AND notEmpty(URL) GROUP BY URL ORDER BY PageViews DESC LIMIT 10; SELECT URL, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT DontCountHits AND NOT Refresh AND notEmpty(URL) GROUP BY URL ORDER BY PageViews DESC LIMIT 10;
SELECT Title, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= toDate('2013-07-01') AND EventDate <= toDate('2013-07-31') AND NOT DontCountHits AND NOT Refresh AND notEmpty(Title) GROUP BY Title ORDER BY PageViews DESC LIMIT 10; SELECT Title, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT DontCountHits AND NOT Refresh AND notEmpty(Title) GROUP BY Title ORDER BY PageViews DESC LIMIT 10;
SELECT URL, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= toDate('2013-07-01') AND EventDate <= toDate('2013-07-31') AND NOT Refresh AND IsLink AND NOT IsDownload GROUP BY URL ORDER BY PageViews DESC LIMIT 1000; SELECT URL, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT Refresh AND IsLink AND NOT IsDownload GROUP BY URL ORDER BY PageViews DESC LIMIT 1000;
SELECT TraficSourceID, SearchEngineID, AdvEngineID, ((SearchEngineID = 0 AND AdvEngineID = 0) ? Referer : '') AS Src, URL AS Dst, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= toDate('2013-07-01') AND EventDate <= toDate('2013-07-31') AND NOT Refresh GROUP BY TraficSourceID, SearchEngineID, AdvEngineID, Src, Dst ORDER BY PageViews DESC LIMIT 1000; SELECT TraficSourceID, SearchEngineID, AdvEngineID, ((SearchEngineID = 0 AND AdvEngineID = 0) ? Referer : '') AS Src, URL AS Dst, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT Refresh GROUP BY TraficSourceID, SearchEngineID, AdvEngineID, Src, Dst ORDER BY PageViews DESC LIMIT 1000;
SELECT URLHash, EventDate, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= toDate('2013-07-01') AND EventDate <= toDate('2013-07-31') AND NOT Refresh AND TraficSourceID IN (-1, 6) AND RefererHash = halfMD5('http://example.ru/') GROUP BY URLHash, EventDate ORDER BY PageViews DESC LIMIT 100; SELECT URLHash, EventDate, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT Refresh AND TraficSourceID IN (-1, 6) AND RefererHash = halfMD5('http://example.ru/') GROUP BY URLHash, EventDate ORDER BY PageViews DESC LIMIT 100;
SELECT WindowClientWidth, WindowClientHeight, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= toDate('2013-07-01') AND EventDate <= toDate('2013-07-31') AND NOT Refresh AND NOT DontCountHits AND URLHash = halfMD5('http://example.ru/') GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESC LIMIT 10000; SELECT WindowClientWidth, WindowClientHeight, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT Refresh AND NOT DontCountHits AND URLHash = halfMD5('http://example.ru/') GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESC LIMIT 10000;
SELECT toStartOfMinute(EventTime) AS Minute, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= toDate('2013-07-01') AND EventDate <= toDate('2013-07-02') AND NOT Refresh AND NOT DontCountHits GROUP BY Minute ORDER BY Minute; SELECT toStartOfMinute(EventTime) AS Minute, count() AS PageViews FROM {table} WHERE CounterID = 62 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-02' AND NOT Refresh AND NOT DontCountHits GROUP BY Minute ORDER BY Minute;

View File

@ -513,6 +513,7 @@ private:
if (input.empty()) if (input.empty())
break; break;
has_vertical_output_suffix = false;
if (input.ends_with("\\G")) if (input.ends_with("\\G"))
{ {
input.resize(input.size() - 2); input.resize(input.size() - 2);

View File

@ -65,6 +65,8 @@ void Suggest::loadImpl(Connection & connection, const ConnectionTimeouts & timeo
" UNION ALL " " UNION ALL "
"SELECT name FROM system.data_type_families" "SELECT name FROM system.data_type_families"
" UNION ALL " " UNION ALL "
"SELECT name FROM system.merge_tree_settings"
" UNION ALL "
"SELECT name FROM system.settings" "SELECT name FROM system.settings"
" UNION ALL " " UNION ALL "
"SELECT cluster FROM system.clusters" "SELECT cluster FROM system.clusters"

View File

@ -2,8 +2,10 @@
#include <Access/MultipleAccessStorage.h> #include <Access/MultipleAccessStorage.h>
#include <Access/MemoryAccessStorage.h> #include <Access/MemoryAccessStorage.h>
#include <Access/UsersConfigAccessStorage.h> #include <Access/UsersConfigAccessStorage.h>
#include <Access/User.h>
#include <Access/QuotaContextFactory.h> #include <Access/QuotaContextFactory.h>
#include <Access/RowPolicyContextFactory.h> #include <Access/RowPolicyContextFactory.h>
#include <Access/AccessRightsContext.h>
namespace DB namespace DB
@ -33,6 +35,49 @@ AccessControlManager::~AccessControlManager()
} }
UserPtr AccessControlManager::getUser(const String & user_name) const
{
return getUser(user_name, {}, nullptr);
}
UserPtr AccessControlManager::getUser(
const String & user_name, const std::function<void(const UserPtr &)> & on_change, ext::scope_guard * subscription) const
{
UUID id = getID<User>(user_name);
if (on_change && subscription)
{
*subscription = subscribeForChanges(id, [on_change](const UUID &, const AccessEntityPtr & user)
{
if (user)
on_change(typeid_cast<UserPtr>(user));
});
}
return read<User>(id);
}
UserPtr AccessControlManager::authorizeAndGetUser(
const String & user_name,
const String & password,
const Poco::Net::IPAddress & address) const
{
return authorizeAndGetUser(user_name, password, address, {}, nullptr);
}
UserPtr AccessControlManager::authorizeAndGetUser(
const String & user_name,
const String & password,
const Poco::Net::IPAddress & address,
const std::function<void(const UserPtr &)> & on_change,
ext::scope_guard * subscription) const
{
auto user = getUser(user_name, on_change, subscription);
user->allowed_client_hosts.checkContains(address, user_name);
user->authentication.checkPassword(password, user_name);
return user;
}
void AccessControlManager::loadFromConfig(const Poco::Util::AbstractConfiguration & users_config) void AccessControlManager::loadFromConfig(const Poco::Util::AbstractConfiguration & users_config)
{ {
auto & users_config_access_storage = dynamic_cast<UsersConfigAccessStorage &>(getStorageByIndex(1)); auto & users_config_access_storage = dynamic_cast<UsersConfigAccessStorage &>(getStorageByIndex(1));
@ -40,6 +85,12 @@ void AccessControlManager::loadFromConfig(const Poco::Util::AbstractConfiguratio
} }
std::shared_ptr<const AccessRightsContext> AccessControlManager::getAccessRightsContext(const ClientInfo & client_info, const AccessRights & granted_to_user, const Settings & settings, const String & current_database)
{
return std::make_shared<AccessRightsContext>(client_info, granted_to_user, settings, current_database);
}
std::shared_ptr<QuotaContext> AccessControlManager::createQuotaContext( std::shared_ptr<QuotaContext> AccessControlManager::createQuotaContext(
const String & user_name, const Poco::Net::IPAddress & address, const String & custom_quota_key) const String & user_name, const Poco::Net::IPAddress & address, const String & custom_quota_key)
{ {

View File

@ -2,6 +2,7 @@
#include <Access/MultipleAccessStorage.h> #include <Access/MultipleAccessStorage.h>
#include <Poco/AutoPtr.h> #include <Poco/AutoPtr.h>
#include <ext/scope_guard.h>
#include <memory> #include <memory>
@ -19,11 +20,17 @@ namespace Poco
namespace DB namespace DB
{ {
struct User;
using UserPtr = std::shared_ptr<const User>;
class QuotaContext; class QuotaContext;
class QuotaContextFactory; class QuotaContextFactory;
struct QuotaUsageInfo; struct QuotaUsageInfo;
class RowPolicyContext; class RowPolicyContext;
class RowPolicyContextFactory; class RowPolicyContextFactory;
class AccessRights;
class AccessRightsContext;
class ClientInfo;
struct Settings;
/// Manages access control entities. /// Manages access control entities.
@ -35,6 +42,13 @@ public:
void loadFromConfig(const Poco::Util::AbstractConfiguration & users_config); void loadFromConfig(const Poco::Util::AbstractConfiguration & users_config);
UserPtr getUser(const String & user_name) const;
UserPtr getUser(const String & user_name, const std::function<void(const UserPtr &)> & on_change, ext::scope_guard * subscription) const;
UserPtr authorizeAndGetUser(const String & user_name, const String & password, const Poco::Net::IPAddress & address) const;
UserPtr authorizeAndGetUser(const String & user_name, const String & password, const Poco::Net::IPAddress & address, const std::function<void(const UserPtr &)> & on_change, ext::scope_guard * subscription) const;
std::shared_ptr<const AccessRightsContext> getAccessRightsContext(const ClientInfo & client_info, const AccessRights & granted_to_user, const Settings & settings, const String & current_database);
std::shared_ptr<QuotaContext> std::shared_ptr<QuotaContext>
createQuotaContext(const String & user_name, const Poco::Net::IPAddress & address, const String & custom_quota_key); createQuotaContext(const String & user_name, const Poco::Net::IPAddress & address, const String & custom_quota_key);

View File

@ -0,0 +1,498 @@
#pragma once
#include <Access/AccessType.h>
#include <Core/Types.h>
#include <Common/Exception.h>
#include <ext/range.h>
#include <ext/push_back.h>
#include <bitset>
#include <unordered_map>
namespace DB
{
/// Represents a combination of access types which can be granted globally, on databases, tables, columns, etc.
/// For example "SELECT, CREATE USER" is an access type.
class AccessFlags
{
public:
AccessFlags(AccessType type);
/// The same as AccessFlags(AccessType::NONE).
AccessFlags() = default;
/// Constructs from a string like "SELECT".
AccessFlags(const std::string_view & keyword);
/// Constructs from a list of strings like "SELECT, UPDATE, INSERT".
AccessFlags(const std::vector<std::string_view> & keywords);
AccessFlags(const Strings & keywords);
AccessFlags(const AccessFlags & src) = default;
AccessFlags(AccessFlags && src) = default;
AccessFlags & operator =(const AccessFlags & src) = default;
AccessFlags & operator =(AccessFlags && src) = default;
/// Returns the access type which contains two specified access types.
AccessFlags & operator |=(const AccessFlags & other) { flags |= other.flags; return *this; }
friend AccessFlags operator |(const AccessFlags & left, const AccessFlags & right) { return AccessFlags(left) |= right; }
/// Returns the access type which contains the common part of two access types.
AccessFlags & operator &=(const AccessFlags & other) { flags &= other.flags; return *this; }
friend AccessFlags operator &(const AccessFlags & left, const AccessFlags & right) { return AccessFlags(left) &= right; }
/// Returns the access type which contains only the part of the first access type which is not the part of the second access type.
/// (lhs - rhs) is the same as (lhs & ~rhs).
AccessFlags & operator -=(const AccessFlags & other) { flags &= ~other.flags; return *this; }
friend AccessFlags operator -(const AccessFlags & left, const AccessFlags & right) { return AccessFlags(left) -= right; }
AccessFlags operator ~() const { AccessFlags res; res.flags = ~flags; return res; }
bool isEmpty() const { return flags.none(); }
explicit operator bool() const { return !isEmpty(); }
bool contains(const AccessFlags & other) const { return (flags & other.flags) == other.flags; }
friend bool operator ==(const AccessFlags & left, const AccessFlags & right) { return left.flags == right.flags; }
friend bool operator !=(const AccessFlags & left, const AccessFlags & right) { return !(left == right); }
void clear() { flags.reset(); }
/// Returns a comma-separated list of keywords, like "SELECT, CREATE USER, UPDATE".
String toString() const;
/// Returns a list of keywords.
std::vector<std::string_view> toKeywords() const;
/// Returns the access types which could be granted on the database level.
/// For example, SELECT can be granted on the database level, but CREATE_USER cannot.
static AccessFlags databaseLevel();
/// Returns the access types which could be granted on the table/dictionary level.
static AccessFlags tableLevel();
/// Returns the access types which could be granted on the column/attribute level.
static AccessFlags columnLevel();
private:
static constexpr size_t NUM_FLAGS = 128;
using Flags = std::bitset<NUM_FLAGS>;
Flags flags;
AccessFlags(const Flags & flags_) : flags(flags_) {}
template <typename = void>
class Impl;
};
namespace ErrorCodes
{
extern const int UNKNOWN_ACCESS_TYPE;
}
template <typename>
class AccessFlags::Impl
{
public:
static const Impl & instance()
{
static const Impl res;
return res;
}
Flags accessTypeToFlags(AccessType type) const
{
return access_type_to_flags_mapping[static_cast<size_t>(type)];
}
Flags keywordToFlags(const std::string_view & keyword) const
{
auto it = keyword_to_flags_map.find(keyword);
if (it == keyword_to_flags_map.end())
{
String uppercased_keyword{keyword};
boost::to_upper(uppercased_keyword);
it = keyword_to_flags_map.find(uppercased_keyword);
if (it == keyword_to_flags_map.end())
throw Exception("Unknown access type: " + String(keyword), ErrorCodes::UNKNOWN_ACCESS_TYPE);
}
return it->second;
}
Flags keywordsToFlags(const std::vector<std::string_view> & keywords) const
{
Flags res;
for (const auto & keyword : keywords)
res |= keywordToFlags(keyword);
return res;
}
Flags keywordsToFlags(const Strings & keywords) const
{
Flags res;
for (const auto & keyword : keywords)
res |= keywordToFlags(keyword);
return res;
}
std::vector<std::string_view> flagsToKeywords(const Flags & flags_) const
{
std::vector<std::string_view> keywords;
flagsToKeywordsRec(flags_, keywords, *flags_to_keyword_tree);
if (keywords.empty())
keywords.push_back("USAGE");
return keywords;
}
String flagsToString(const Flags & flags_) const
{
String str;
for (const auto & keyword : flagsToKeywords(flags_))
{
if (!str.empty())
str += ", ";
str += keyword;
}
return str;
}
const Flags & getDatabaseLevelFlags() const { return all_grantable_on_level[DATABASE_LEVEL]; }
const Flags & getTableLevelFlags() const { return all_grantable_on_level[TABLE_LEVEL]; }
const Flags & getColumnLevelFlags() const { return all_grantable_on_level[COLUMN_LEVEL]; }
private:
enum Level
{
UNKNOWN_LEVEL = -1,
GLOBAL_LEVEL = 0,
DATABASE_LEVEL = 1,
TABLE_LEVEL = 2,
VIEW_LEVEL = 2,
DICTIONARY_LEVEL = 2,
COLUMN_LEVEL = 3,
};
struct Node;
using NodePtr = std::unique_ptr<Node>;
using Nodes = std::vector<NodePtr>;
template <typename... Args>
static Nodes nodes(Args&& ... args)
{
Nodes res;
ext::push_back(res, std::move(args)...);
return res;
}
struct Node
{
std::string_view keyword;
std::vector<String> aliases;
Flags flags;
Level level = UNKNOWN_LEVEL;
Nodes children;
Node(std::string_view keyword_, size_t flag_, Level level_)
: keyword(keyword_), level(level_)
{
flags.set(flag_);
}
Node(std::string_view keyword_, Nodes children_)
: keyword(keyword_), children(std::move(children_))
{
for (const auto & child : children)
flags |= child->flags;
}
template <typename... Args>
Node(std::string_view keyword_, NodePtr first_child, Args &&... other_children)
: Node(keyword_, nodes(std::move(first_child), std::move(other_children)...)) {}
};
static void flagsToKeywordsRec(const Flags & flags_, std::vector<std::string_view> & keywords, const Node & start_node)
{
Flags matching_flags = (flags_ & start_node.flags);
if (matching_flags.any())
{
if (matching_flags == start_node.flags)
{
keywords.push_back(start_node.keyword);
}
else
{
for (const auto & child : start_node.children)
flagsToKeywordsRec(flags_, keywords, *child);
}
}
}
static void makeFlagsToKeywordTree(NodePtr & flags_to_keyword_tree_)
{
size_t next_flag = 0;
Nodes all;
auto show = std::make_unique<Node>("SHOW", next_flag++, COLUMN_LEVEL);
auto exists = std::make_unique<Node>("EXISTS", next_flag++, COLUMN_LEVEL);
ext::push_back(all, std::move(show), std::move(exists));
auto select = std::make_unique<Node>("SELECT", next_flag++, COLUMN_LEVEL);
auto insert = std::make_unique<Node>("INSERT", next_flag++, COLUMN_LEVEL);
ext::push_back(all, std::move(select), std::move(insert));
auto update = std::make_unique<Node>("UPDATE", next_flag++, COLUMN_LEVEL);
ext::push_back(update->aliases, "ALTER UPDATE");
auto delet = std::make_unique<Node>("DELETE", next_flag++, TABLE_LEVEL);
ext::push_back(delet->aliases, "ALTER DELETE");
auto add_column = std::make_unique<Node>("ADD COLUMN", next_flag++, COLUMN_LEVEL);
add_column->aliases.push_back("ALTER ADD COLUMN");
auto modify_column = std::make_unique<Node>("MODIFY COLUMN", next_flag++, COLUMN_LEVEL);
modify_column->aliases.push_back("ALTER MODIFY COLUMN");
auto drop_column = std::make_unique<Node>("DROP COLUMN", next_flag++, COLUMN_LEVEL);
drop_column->aliases.push_back("ALTER DROP COLUMN");
auto comment_column = std::make_unique<Node>("COMMENT COLUMN", next_flag++, COLUMN_LEVEL);
comment_column->aliases.push_back("ALTER COMMENT COLUMN");
auto clear_column = std::make_unique<Node>("CLEAR COLUMN", next_flag++, COLUMN_LEVEL);
clear_column->aliases.push_back("ALTER CLEAR COLUMN");
auto alter_column = std::make_unique<Node>("ALTER COLUMN", std::move(add_column), std::move(modify_column), std::move(drop_column), std::move(comment_column), std::move(clear_column));
auto alter_order_by = std::make_unique<Node>("ALTER ORDER BY", next_flag++, TABLE_LEVEL);
alter_order_by->aliases.push_back("MODIFY ORDER BY");
alter_order_by->aliases.push_back("ALTER MODIFY ORDER BY");
auto add_index = std::make_unique<Node>("ADD INDEX", next_flag++, TABLE_LEVEL);
add_index->aliases.push_back("ALTER ADD INDEX");
auto drop_index = std::make_unique<Node>("DROP INDEX", next_flag++, TABLE_LEVEL);
drop_index->aliases.push_back("ALTER DROP INDEX");
auto materialize_index = std::make_unique<Node>("MATERIALIZE INDEX", next_flag++, TABLE_LEVEL);
materialize_index->aliases.push_back("ALTER MATERIALIZE INDEX");
auto clear_index = std::make_unique<Node>("CLEAR INDEX", next_flag++, TABLE_LEVEL);
clear_index->aliases.push_back("ALTER CLEAR INDEX");
auto index = std::make_unique<Node>("INDEX", std::move(alter_order_by), std::move(add_index), std::move(drop_index), std::move(materialize_index), std::move(clear_index));
index->aliases.push_back("ALTER INDEX");
auto add_constraint = std::make_unique<Node>("ADD CONSTRAINT", next_flag++, TABLE_LEVEL);
add_constraint->aliases.push_back("ALTER ADD CONSTRAINT");
auto drop_constraint = std::make_unique<Node>("DROP CONSTRAINT", next_flag++, TABLE_LEVEL);
drop_constraint->aliases.push_back("ALTER DROP CONSTRAINT");
auto alter_constraint = std::make_unique<Node>("CONSTRAINT", std::move(add_constraint), std::move(drop_constraint));
alter_constraint->aliases.push_back("ALTER CONSTRAINT");
auto modify_ttl = std::make_unique<Node>("MODIFY TTL", next_flag++, TABLE_LEVEL);
modify_ttl->aliases.push_back("ALTER MODIFY TTL");
auto modify_setting = std::make_unique<Node>("MODIFY SETTING", next_flag++, TABLE_LEVEL);
modify_setting->aliases.push_back("ALTER MODIFY SETTING");
auto move_partition = std::make_unique<Node>("MOVE PARTITION", next_flag++, TABLE_LEVEL);
ext::push_back(move_partition->aliases, "ALTER MOVE PARTITION", "MOVE PART", "ALTER MOVE PART");
auto fetch_partition = std::make_unique<Node>("FETCH PARTITION", next_flag++, TABLE_LEVEL);
ext::push_back(fetch_partition->aliases, "ALTER FETCH PARTITION");
auto freeze_partition = std::make_unique<Node>("FREEZE PARTITION", next_flag++, TABLE_LEVEL);
ext::push_back(freeze_partition->aliases, "ALTER FREEZE PARTITION");
auto alter_table = std::make_unique<Node>("ALTER TABLE", std::move(update), std::move(delet), std::move(alter_column), std::move(index), std::move(alter_constraint), std::move(modify_ttl), std::move(modify_setting), std::move(move_partition), std::move(fetch_partition), std::move(freeze_partition));
auto refresh_view = std::make_unique<Node>("REFRESH VIEW", next_flag++, VIEW_LEVEL);
ext::push_back(refresh_view->aliases, "ALTER LIVE VIEW REFRESH");
auto modify_view_query = std::make_unique<Node>("MODIFY VIEW QUERY", next_flag++, VIEW_LEVEL);
auto alter_view = std::make_unique<Node>("ALTER VIEW", std::move(refresh_view), std::move(modify_view_query));
auto alter = std::make_unique<Node>("ALTER", std::move(alter_table), std::move(alter_view));
ext::push_back(all, std::move(alter));
auto create_database = std::make_unique<Node>("CREATE DATABASE", next_flag++, DATABASE_LEVEL);
ext::push_back(create_database->aliases, "ATTACH DATABASE");
auto create_table = std::make_unique<Node>("CREATE TABLE", next_flag++, TABLE_LEVEL);
ext::push_back(create_table->aliases, "ATTACH TABLE");
auto create_view = std::make_unique<Node>("CREATE VIEW", next_flag++, VIEW_LEVEL);
ext::push_back(create_view->aliases, "ATTACH VIEW");
auto create_dictionary = std::make_unique<Node>("CREATE DICTIONARY", next_flag++, DICTIONARY_LEVEL);
ext::push_back(create_dictionary->aliases, "ATTACH DICTIONARY");
auto create = std::make_unique<Node>("CREATE", std::move(create_database), std::move(create_table), std::move(create_view), std::move(create_dictionary));
ext::push_back(create->aliases, "ATTACH");
ext::push_back(all, std::move(create));
auto create_temporary_table = std::make_unique<Node>("CREATE TEMPORARY TABLE", next_flag++, GLOBAL_LEVEL);
ext::push_back(all, std::move(create_temporary_table));
auto drop_database = std::make_unique<Node>("DROP DATABASE", next_flag++, DATABASE_LEVEL);
auto drop_table = std::make_unique<Node>("DROP TABLE", next_flag++, TABLE_LEVEL);
auto drop_view = std::make_unique<Node>("DROP VIEW", next_flag++, VIEW_LEVEL);
auto drop_dictionary = std::make_unique<Node>("DROP DICTIONARY", next_flag++, DICTIONARY_LEVEL);
auto drop = std::make_unique<Node>("DROP", std::move(drop_database), std::move(drop_table), std::move(drop_view), std::move(drop_dictionary));
ext::push_back(all, std::move(drop));
auto detach_database = std::make_unique<Node>("DETACH DATABASE", next_flag++, DATABASE_LEVEL);
auto detach_table = std::make_unique<Node>("DETACH TABLE", next_flag++, TABLE_LEVEL);
auto detach_view = std::make_unique<Node>("DETACH VIEW", next_flag++, VIEW_LEVEL);
auto detach_dictionary = std::make_unique<Node>("DETACH DICTIONARY", next_flag++, DICTIONARY_LEVEL);
auto detach = std::make_unique<Node>("DETACH", std::move(detach_database), std::move(detach_table), std::move(detach_view), std::move(detach_dictionary));
ext::push_back(all, std::move(detach));
auto truncate_table = std::make_unique<Node>("TRUNCATE TABLE", next_flag++, TABLE_LEVEL);
auto truncate_view = std::make_unique<Node>("TRUNCATE VIEW", next_flag++, VIEW_LEVEL);
auto truncate = std::make_unique<Node>("TRUNCATE", std::move(truncate_table), std::move(truncate_view));
ext::push_back(all, std::move(truncate));
auto optimize = std::make_unique<Node>("OPTIMIZE", next_flag++, TABLE_LEVEL);
optimize->aliases.push_back("OPTIMIZE TABLE");
ext::push_back(all, std::move(optimize));
auto kill_query = std::make_unique<Node>("KILL QUERY", next_flag++, GLOBAL_LEVEL);
auto kill_mutation = std::make_unique<Node>("KILL MUTATION", next_flag++, TABLE_LEVEL);
auto kill = std::make_unique<Node>("KILL", std::move(kill_query), std::move(kill_mutation));
ext::push_back(all, std::move(kill));
auto create_user = std::make_unique<Node>("CREATE USER", next_flag++, GLOBAL_LEVEL);
ext::push_back(create_user->aliases, "ALTER USER", "DROP USER", "CREATE ROLE", "DROP ROLE", "CREATE POLICY", "ALTER POLICY", "DROP POLICY", "CREATE QUOTA", "ALTER QUOTA", "DROP QUOTA");
ext::push_back(all, std::move(create_user));
auto shutdown = std::make_unique<Node>("SHUTDOWN", next_flag++, GLOBAL_LEVEL);
ext::push_back(shutdown->aliases, "SYSTEM SHUTDOWN", "SYSTEM KILL");
auto drop_cache = std::make_unique<Node>("DROP CACHE", next_flag++, GLOBAL_LEVEL);
ext::push_back(drop_cache->aliases, "SYSTEM DROP CACHE", "DROP DNS CACHE", "SYSTEM DROP DNS CACHE", "DROP MARK CACHE", "SYSTEM DROP MARK CACHE", "DROP UNCOMPRESSED CACHE", "SYSTEM DROP UNCOMPRESSED CACHE", "DROP COMPILED EXPRESSION CACHE", "SYSTEM DROP COMPILED EXPRESSION CACHE");
auto reload_config = std::make_unique<Node>("RELOAD CONFIG", next_flag++, GLOBAL_LEVEL);
ext::push_back(reload_config->aliases, "SYSTEM RELOAD CONFIG");
auto reload_dictionary = std::make_unique<Node>("RELOAD DICTIONARY", next_flag++, GLOBAL_LEVEL);
ext::push_back(reload_dictionary->aliases, "SYSTEM RELOAD DICTIONARY", "RELOAD DICTIONARIES", "SYSTEM RELOAD DICTIONARIES", "RELOAD EMBEDDED DICTIONARIES", "SYSTEM RELOAD EMBEDDED DICTIONARIES");
auto stop_merges = std::make_unique<Node>("STOP MERGES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_merges->aliases, "SYSTEM STOP MERGES", "START MERGES", "SYSTEM START MERGES");
auto stop_ttl_merges = std::make_unique<Node>("STOP TTL MERGES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_ttl_merges->aliases, "SYSTEM STOP TTL MERGES", "START TTL MERGES", "SYSTEM START TTL MERGES");
auto stop_fetches = std::make_unique<Node>("STOP FETCHES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_fetches->aliases, "SYSTEM STOP FETCHES", "START FETCHES", "SYSTEM START FETCHES");
auto stop_moves = std::make_unique<Node>("STOP MOVES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_moves->aliases, "SYSTEM STOP MOVES", "START MOVES", "SYSTEM START MOVES");
auto stop_distributed_sends = std::make_unique<Node>("STOP DISTRIBUTED SENDS", next_flag++, TABLE_LEVEL);
ext::push_back(stop_distributed_sends->aliases, "SYSTEM STOP DISTRIBUTED SENDS", "START DISTRIBUTED SENDS", "SYSTEM START DISTRIBUTED SENDS");
auto stop_replicated_sends = std::make_unique<Node>("STOP REPLICATED SENDS", next_flag++, TABLE_LEVEL);
ext::push_back(stop_replicated_sends->aliases, "SYSTEM STOP REPLICATED SENDS", "START REPLICATED SENDS", "SYSTEM START REPLICATED SENDS");
auto stop_replication_queues = std::make_unique<Node>("STOP REPLICATION QUEUES", next_flag++, TABLE_LEVEL);
ext::push_back(stop_replication_queues->aliases, "SYSTEM STOP REPLICATION QUEUES", "START REPLICATION QUEUES", "SYSTEM START REPLICATION QUEUES");
auto sync_replica = std::make_unique<Node>("SYNC REPLICA", next_flag++, TABLE_LEVEL);
ext::push_back(sync_replica->aliases, "SYSTEM SYNC REPLICA");
auto restart_replica = std::make_unique<Node>("RESTART REPLICA", next_flag++, TABLE_LEVEL);
ext::push_back(restart_replica->aliases, "SYSTEM RESTART REPLICA");
auto flush_distributed = std::make_unique<Node>("FLUSH DISTRIBUTED", next_flag++, TABLE_LEVEL);
ext::push_back(flush_distributed->aliases, "SYSTEM FLUSH DISTRIBUTED");
auto flush_logs = std::make_unique<Node>("FLUSH LOGS", next_flag++, GLOBAL_LEVEL);
ext::push_back(flush_logs->aliases, "SYSTEM FLUSH LOGS");
auto system = std::make_unique<Node>("SYSTEM", std::move(shutdown), std::move(drop_cache), std::move(reload_config), std::move(reload_dictionary), std::move(stop_merges), std::move(stop_ttl_merges), std::move(stop_fetches), std::move(stop_moves), std::move(stop_distributed_sends), std::move(stop_replicated_sends), std::move(stop_replication_queues), std::move(sync_replica), std::move(restart_replica), std::move(flush_distributed), std::move(flush_logs));
ext::push_back(all, std::move(system));
auto dict_get = std::make_unique<Node>("dictGet()", next_flag++, DICTIONARY_LEVEL);
dict_get->aliases.push_back("dictHas()");
dict_get->aliases.push_back("dictGetHierarchy()");
dict_get->aliases.push_back("dictIsIn()");
ext::push_back(all, std::move(dict_get));
auto address_to_line = std::make_unique<Node>("addressToLine()", next_flag++, GLOBAL_LEVEL);
auto address_to_symbol = std::make_unique<Node>("addressToSymbol()", next_flag++, GLOBAL_LEVEL);
auto demangle = std::make_unique<Node>("demangle()", next_flag++, GLOBAL_LEVEL);
auto introspection = std::make_unique<Node>("INTROSPECTION", std::move(address_to_line), std::move(address_to_symbol), std::move(demangle));
ext::push_back(introspection->aliases, "INTROSPECTION FUNCTIONS");
ext::push_back(all, std::move(introspection));
auto file = std::make_unique<Node>("file()", next_flag++, GLOBAL_LEVEL);
auto url = std::make_unique<Node>("url()", next_flag++, GLOBAL_LEVEL);
auto input = std::make_unique<Node>("input()", next_flag++, GLOBAL_LEVEL);
auto values = std::make_unique<Node>("values()", next_flag++, GLOBAL_LEVEL);
auto numbers = std::make_unique<Node>("numbers()", next_flag++, GLOBAL_LEVEL);
auto merge = std::make_unique<Node>("merge()", next_flag++, DATABASE_LEVEL);
auto remote = std::make_unique<Node>("remote()", next_flag++, GLOBAL_LEVEL);
ext::push_back(remote->aliases, "remoteSecure()", "cluster()");
auto mysql = std::make_unique<Node>("mysql()", next_flag++, GLOBAL_LEVEL);
auto odbc = std::make_unique<Node>("odbc()", next_flag++, GLOBAL_LEVEL);
auto jdbc = std::make_unique<Node>("jdbc()", next_flag++, GLOBAL_LEVEL);
auto hdfs = std::make_unique<Node>("hdfs()", next_flag++, GLOBAL_LEVEL);
auto s3 = std::make_unique<Node>("s3()", next_flag++, GLOBAL_LEVEL);
auto table_functions = std::make_unique<Node>("TABLE FUNCTIONS", std::move(file), std::move(url), std::move(input), std::move(values), std::move(numbers), std::move(merge), std::move(remote), std::move(mysql), std::move(odbc), std::move(jdbc), std::move(hdfs), std::move(s3));
ext::push_back(all, std::move(table_functions));
flags_to_keyword_tree_ = std::make_unique<Node>("ALL", std::move(all));
flags_to_keyword_tree_->aliases.push_back("ALL PRIVILEGES");
}
void makeKeywordToFlagsMap(std::unordered_map<std::string_view, Flags> & keyword_to_flags_map_, Node * start_node = nullptr)
{
if (!start_node)
{
start_node = flags_to_keyword_tree.get();
keyword_to_flags_map_["USAGE"] = {};
keyword_to_flags_map_["NONE"] = {};
keyword_to_flags_map_["NO PRIVILEGES"] = {};
}
start_node->aliases.emplace_back(start_node->keyword);
for (auto & alias : start_node->aliases)
{
boost::to_upper(alias);
keyword_to_flags_map_[alias] = start_node->flags;
}
for (auto & child : start_node->children)
makeKeywordToFlagsMap(keyword_to_flags_map_, child.get());
}
void makeAccessTypeToFlagsMapping(std::vector<Flags> & access_type_to_flags_mapping_)
{
access_type_to_flags_mapping_.resize(MAX_ACCESS_TYPE);
for (auto access_type : ext::range_with_static_cast<AccessType>(0, MAX_ACCESS_TYPE))
{
auto str = toKeyword(access_type);
auto it = keyword_to_flags_map.find(str);
if (it == keyword_to_flags_map.end())
{
String uppercased{str};
boost::to_upper(uppercased);
it = keyword_to_flags_map.find(uppercased);
}
access_type_to_flags_mapping_[static_cast<size_t>(access_type)] = it->second;
}
}
void collectAllGrantableOnLevel(std::vector<Flags> & all_grantable_on_level_, const Node * start_node = nullptr)
{
if (!start_node)
{
start_node = flags_to_keyword_tree.get();
all_grantable_on_level.resize(COLUMN_LEVEL + 1);
}
for (int i = 0; i <= start_node->level; ++i)
all_grantable_on_level_[i] |= start_node->flags;
for (const auto & child : start_node->children)
collectAllGrantableOnLevel(all_grantable_on_level_, child.get());
}
Impl()
{
makeFlagsToKeywordTree(flags_to_keyword_tree);
makeKeywordToFlagsMap(keyword_to_flags_map);
makeAccessTypeToFlagsMapping(access_type_to_flags_mapping);
collectAllGrantableOnLevel(all_grantable_on_level);
}
std::unique_ptr<Node> flags_to_keyword_tree;
std::unordered_map<std::string_view, Flags> keyword_to_flags_map;
std::vector<Flags> access_type_to_flags_mapping;
std::vector<Flags> all_grantable_on_level;
};
inline AccessFlags::AccessFlags(AccessType type) : flags(Impl<>::instance().accessTypeToFlags(type)) {}
inline AccessFlags::AccessFlags(const std::string_view & keyword) : flags(Impl<>::instance().keywordToFlags(keyword)) {}
inline AccessFlags::AccessFlags(const std::vector<std::string_view> & keywords) : flags(Impl<>::instance().keywordsToFlags(keywords)) {}
inline AccessFlags::AccessFlags(const Strings & keywords) : flags(Impl<>::instance().keywordsToFlags(keywords)) {}
inline String AccessFlags::toString() const { return Impl<>::instance().flagsToString(flags); }
inline std::vector<std::string_view> AccessFlags::toKeywords() const { return Impl<>::instance().flagsToKeywords(flags); }
inline AccessFlags AccessFlags::databaseLevel() { return Impl<>::instance().getDatabaseLevelFlags(); }
inline AccessFlags AccessFlags::tableLevel() { return Impl<>::instance().getTableLevelFlags(); }
inline AccessFlags AccessFlags::columnLevel() { return Impl<>::instance().getColumnLevelFlags(); }
inline AccessFlags operator |(AccessType left, AccessType right) { return AccessFlags(left) | right; }
inline AccessFlags operator &(AccessType left, AccessType right) { return AccessFlags(left) & right; }
inline AccessFlags operator -(AccessType left, AccessType right) { return AccessFlags(left) - right; }
inline AccessFlags operator ~(AccessType x) { return ~AccessFlags(x); }
}

View File

@ -0,0 +1,729 @@
#include <Access/AccessRights.h>
#include <Common/Exception.h>
#include <boost/range/adaptor/map.hpp>
#include <unordered_map>
namespace DB
{
namespace ErrorCodes
{
extern const int INVALID_GRANT;
extern const int LOGICAL_ERROR;
}
namespace
{
enum Level
{
GLOBAL_LEVEL,
DATABASE_LEVEL,
TABLE_LEVEL,
COLUMN_LEVEL,
};
enum RevokeMode
{
NORMAL_REVOKE_MODE, /// for AccessRights::revoke()
PARTIAL_REVOKE_MODE, /// for AccessRights::partialRevoke()
FULL_REVOKE_MODE, /// for AccessRights::fullRevoke()
};
struct Helper
{
static const Helper & instance()
{
static const Helper res;
return res;
}
const AccessFlags database_level_flags = AccessFlags::databaseLevel();
const AccessFlags table_level_flags = AccessFlags::tableLevel();
const AccessFlags column_level_flags = AccessFlags::columnLevel();
const AccessFlags show_flag = AccessType::SHOW;
const AccessFlags exists_flag = AccessType::EXISTS;
const AccessFlags create_table_flag = AccessType::CREATE_TABLE;
const AccessFlags create_temporary_table_flag = AccessType::CREATE_TEMPORARY_TABLE;
};
}
struct AccessRights::Node
{
public:
std::shared_ptr<const String> node_name;
Level level = GLOBAL_LEVEL;
AccessFlags explicit_grants;
AccessFlags partial_revokes;
AccessFlags inherited_access; /// the access inherited from the parent node
AccessFlags raw_access; /// raw_access = (inherited_access - partial_revokes) | explicit_grants
AccessFlags access; /// access = raw_access | implicit_access
AccessFlags min_access; /// min_access = access & child[0].access & ... | child[N-1].access
AccessFlags max_access; /// max_access = access | child[0].access | ... | child[N-1].access
std::unique_ptr<std::unordered_map<std::string_view, Node>> children;
Node() = default;
Node(const Node & src) { *this = src; }
Node & operator =(const Node & src)
{
node_name = src.node_name;
level = src.level;
inherited_access = src.inherited_access;
explicit_grants = src.explicit_grants;
partial_revokes = src.partial_revokes;
access = src.access;
min_access = src.min_access;
max_access = src.max_access;
if (src.children)
children = std::make_unique<std::unordered_map<std::string_view, Node>>(*src.children);
else
children = nullptr;
return *this;
}
void grant(AccessFlags access_to_grant, const Helper & helper)
{
if (!access_to_grant)
return;
if (level == GLOBAL_LEVEL)
{
/// Everything can be granted on the global level.
}
else if (level == DATABASE_LEVEL)
{
AccessFlags grantable = access_to_grant & helper.database_level_flags;
if (!grantable)
throw Exception(access_to_grant.toString() + " cannot be granted on the database level", ErrorCodes::INVALID_GRANT);
access_to_grant = grantable;
}
else if (level == TABLE_LEVEL)
{
AccessFlags grantable = access_to_grant & helper.table_level_flags;
if (!grantable)
throw Exception(access_to_grant.toString() + " cannot be granted on the table level", ErrorCodes::INVALID_GRANT);
access_to_grant = grantable;
}
else if (level == COLUMN_LEVEL)
{
AccessFlags grantable = access_to_grant & helper.column_level_flags;
if (!grantable)
throw Exception(access_to_grant.toString() + " cannot be granted on the column level", ErrorCodes::INVALID_GRANT);
access_to_grant = grantable;
}
explicit_grants |= access_to_grant - partial_revokes;
partial_revokes -= access_to_grant;
calculateAllAccessRec(helper);
}
template <typename ... Args>
void grant(const AccessFlags & access_to_grant, const Helper & helper, const std::string_view & name, const Args &... subnames)
{
auto & child = getChild(name);
child.grant(access_to_grant, helper, subnames...);
eraseChildIfEmpty(child);
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
}
template <typename StringT>
void grant(const AccessFlags & access_to_grant, const Helper & helper, const std::vector<StringT> & names)
{
for (const auto & name : names)
{
auto & child = getChild(name);
child.grant(access_to_grant, helper);
eraseChildIfEmpty(child);
}
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
}
template <int mode>
void revoke(const AccessFlags & access_to_revoke, const Helper & helper)
{
if constexpr (mode == NORMAL_REVOKE_MODE)
{
explicit_grants -= access_to_revoke;
}
else if constexpr (mode == PARTIAL_REVOKE_MODE)
{
partial_revokes |= access_to_revoke - explicit_grants;
explicit_grants -= access_to_revoke;
}
else /// mode == FULL_REVOKE_MODE
{
fullRevokeRec(access_to_revoke);
}
calculateAllAccessRec(helper);
}
template <int mode, typename... Args>
void revoke(const AccessFlags & access_to_revoke, const Helper & helper, const std::string_view & name, const Args &... subnames)
{
Node * child;
if (mode == NORMAL_REVOKE_MODE)
{
if (!(child = tryGetChild(name)))
return;
}
else
child = &getChild(name);
child->revoke<mode>(access_to_revoke, helper, subnames...);
eraseChildIfEmpty(*child);
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
}
template <int mode, typename StringT>
void revoke(const AccessFlags & access_to_revoke, const Helper & helper, const std::vector<StringT> & names)
{
Node * child;
for (const auto & name : names)
{
if (mode == NORMAL_REVOKE_MODE)
{
if (!(child = tryGetChild(name)))
continue;
}
else
child = &getChild(name);
child->revoke<mode>(access_to_revoke, helper);
eraseChildIfEmpty(*child);
}
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
}
bool isGranted(const AccessFlags & flags) const
{
return min_access.contains(flags);
}
template <typename... Args>
bool isGranted(AccessFlags flags, const std::string_view & name, const Args &... subnames) const
{
if (min_access.contains(flags))
return true;
if (!max_access.contains(flags))
return false;
const Node * child = tryGetChild(name);
if (child)
return child->isGranted(flags, subnames...);
else
return access.contains(flags);
}
template <typename StringT>
bool isGranted(AccessFlags flags, const std::vector<StringT> & names) const
{
if (min_access.contains(flags))
return true;
if (!max_access.contains(flags))
return false;
for (const auto & name : names)
{
const Node * child = tryGetChild(name);
if (child)
{
if (!child->isGranted(flags, name))
return false;
}
else
{
if (!access.contains(flags))
return false;
}
}
return true;
}
friend bool operator ==(const Node & left, const Node & right)
{
if ((left.explicit_grants != right.explicit_grants) || (left.partial_revokes != right.partial_revokes))
return false;
if (!left.children)
return !right.children;
if (!right.children)
return false;
return *left.children == *right.children;
}
friend bool operator!=(const Node & left, const Node & right) { return !(left == right); }
bool isEmpty() const
{
return !explicit_grants && !partial_revokes && !children;
}
void merge(const Node & other, const Helper & helper)
{
mergeRawAccessRec(other);
calculateGrantsAndPartialRevokesRec();
calculateAllAccessRec(helper);
}
private:
Node * tryGetChild(const std::string_view & name)
{
if (!children)
return nullptr;
auto it = children->find(name);
if (it == children->end())
return nullptr;
return &it->second;
}
const Node * tryGetChild(const std::string_view & name) const
{
if (!children)
return nullptr;
auto it = children->find(name);
if (it == children->end())
return nullptr;
return &it->second;
}
Node & getChild(const std::string_view & name)
{
auto * child = tryGetChild(name);
if (child)
return *child;
if (!children)
children = std::make_unique<std::unordered_map<std::string_view, Node>>();
auto new_child_name = std::make_shared<const String>(name);
Node & new_child = (*children)[*new_child_name];
new_child.node_name = std::move(new_child_name);
new_child.level = static_cast<Level>(level + 1);
new_child.inherited_access = raw_access;
new_child.raw_access = raw_access;
return new_child;
}
void eraseChildIfEmpty(Node & child)
{
if (!child.isEmpty())
return;
auto it = children->find(*child.node_name);
children->erase(it);
if (children->empty())
children = nullptr;
}
void calculateImplicitAccess(const Helper & helper)
{
access = raw_access;
if (access & helper.database_level_flags)
access |= helper.show_flag | helper.exists_flag;
else if ((level >= DATABASE_LEVEL) && children)
access |= helper.exists_flag;
if ((level == GLOBAL_LEVEL) && (access & helper.create_table_flag))
access |= helper.create_temporary_table_flag;
}
void calculateMinAndMaxAccess()
{
min_access = access;
max_access = access;
if (children)
{
for (const auto & child : *children | boost::adaptors::map_values)
{
min_access &= child.min_access;
max_access |= child.max_access;
}
}
}
void calculateAllAccessRec(const Helper & helper)
{
partial_revokes &= inherited_access;
raw_access = (inherited_access - partial_revokes) | explicit_grants;
/// Traverse tree.
if (children)
{
for (auto it = children->begin(); it != children->end();)
{
auto & child = it->second;
child.inherited_access = raw_access;
child.calculateAllAccessRec(helper);
if (child.isEmpty())
it = children->erase(it);
else
++it;
}
if (children->empty())
children = nullptr;
}
calculateImplicitAccess(helper);
calculateMinAndMaxAccess();
}
void fullRevokeRec(const AccessFlags & access_to_revoke)
{
explicit_grants -= access_to_revoke;
partial_revokes |= access_to_revoke;
if (children)
{
for (auto & child : *children | boost::adaptors::map_values)
child.fullRevokeRec(access_to_revoke);
}
}
void mergeRawAccessRec(const Node & rhs)
{
if (rhs.children)
{
for (const auto & [rhs_childname, rhs_child] : *rhs.children)
getChild(rhs_childname).mergeRawAccessRec(rhs_child);
}
raw_access |= rhs.raw_access;
if (children)
{
for (auto & [lhs_childname, lhs_child] : *children)
{
lhs_child.inherited_access = raw_access;
if (!rhs.tryGetChild(lhs_childname))
lhs_child.raw_access |= rhs.raw_access;
}
}
}
void calculateGrantsAndPartialRevokesRec()
{
explicit_grants = raw_access - inherited_access;
partial_revokes = inherited_access - raw_access;
if (children)
{
for (auto & child : *children | boost::adaptors::map_values)
child.calculateGrantsAndPartialRevokesRec();
}
}
};
AccessRights::AccessRights() = default;
AccessRights::~AccessRights() = default;
AccessRights::AccessRights(AccessRights && src) = default;
AccessRights & AccessRights::operator =(AccessRights && src) = default;
AccessRights::AccessRights(const AccessRights & src)
{
*this = src;
}
AccessRights & AccessRights::operator =(const AccessRights & src)
{
if (src.root)
root = std::make_unique<Node>(*src.root);
else
root = nullptr;
return *this;
}
AccessRights::AccessRights(const AccessFlags & access)
{
grant(access);
}
bool AccessRights::isEmpty() const
{
return !root;
}
void AccessRights::clear()
{
root = nullptr;
}
template <typename... Args>
void AccessRights::grantImpl(const AccessFlags & access, const Args &... args)
{
if (!root)
root = std::make_unique<Node>();
root->grant(access, Helper::instance(), args...);
if (root->isEmpty())
root = nullptr;
}
void AccessRights::grantImpl(const AccessRightsElement & element, std::string_view current_database)
{
if (element.any_database)
{
grantImpl(element.access_flags);
}
else if (element.any_table)
{
if (element.database.empty())
grantImpl(element.access_flags, current_database);
else
grantImpl(element.access_flags, element.database);
}
else if (element.any_column)
{
if (element.database.empty())
grantImpl(element.access_flags, current_database, element.table);
else
grantImpl(element.access_flags, element.database, element.table);
}
else
{
if (element.database.empty())
grantImpl(element.access_flags, current_database, element.table, element.columns);
else
grantImpl(element.access_flags, element.database, element.table, element.columns);
}
}
void AccessRights::grantImpl(const AccessRightsElements & elements, std::string_view current_database)
{
for (const auto & element : elements)
grantImpl(element, current_database);
}
void AccessRights::grant(const AccessFlags & access) { grantImpl(access); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database) { grantImpl(access, database); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table) { grantImpl(access, database, table); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) { grantImpl(access, database, table, column); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { grantImpl(access, database, table, columns); }
void AccessRights::grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) { grantImpl(access, database, table, columns); }
void AccessRights::grant(const AccessRightsElement & element, std::string_view current_database) { grantImpl(element, current_database); }
void AccessRights::grant(const AccessRightsElements & elements, std::string_view current_database) { grantImpl(elements, current_database); }
template <int mode, typename... Args>
void AccessRights::revokeImpl(const AccessFlags & access, const Args &... args)
{
if (!root)
return;
root->revoke<mode>(access, Helper::instance(), args...);
if (root->isEmpty())
root = nullptr;
}
template <int mode>
void AccessRights::revokeImpl(const AccessRightsElement & element, std::string_view current_database)
{
if (element.any_database)
{
revokeImpl<mode>(element.access_flags);
}
else if (element.any_table)
{
if (element.database.empty())
revokeImpl<mode>(element.access_flags, current_database);
else
revokeImpl<mode>(element.access_flags, element.database);
}
else if (element.any_column)
{
if (element.database.empty())
revokeImpl<mode>(element.access_flags, current_database, element.table);
else
revokeImpl<mode>(element.access_flags, element.database, element.table);
}
else
{
if (element.database.empty())
revokeImpl<mode>(element.access_flags, current_database, element.table, element.columns);
else
revokeImpl<mode>(element.access_flags, element.database, element.table, element.columns);
}
}
template <int mode>
void AccessRights::revokeImpl(const AccessRightsElements & elements, std::string_view current_database)
{
for (const auto & element : elements)
revokeImpl<mode>(element, current_database);
}
void AccessRights::revoke(const AccessFlags & access) { revokeImpl<NORMAL_REVOKE_MODE>(access); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database) { revokeImpl<NORMAL_REVOKE_MODE>(access, database); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table) { revokeImpl<NORMAL_REVOKE_MODE>(access, database, table); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl<NORMAL_REVOKE_MODE>(access, database, table, column); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl<NORMAL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl<NORMAL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::revoke(const AccessRightsElement & element, std::string_view current_database) { revokeImpl<NORMAL_REVOKE_MODE>(element, current_database); }
void AccessRights::revoke(const AccessRightsElements & elements, std::string_view current_database) { revokeImpl<NORMAL_REVOKE_MODE>(elements, current_database); }
void AccessRights::partialRevoke(const AccessFlags & access) { revokeImpl<PARTIAL_REVOKE_MODE>(access); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database, table); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database, table, column); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl<PARTIAL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::partialRevoke(const AccessRightsElement & element, std::string_view current_database) { revokeImpl<PARTIAL_REVOKE_MODE>(element, current_database); }
void AccessRights::partialRevoke(const AccessRightsElements & elements, std::string_view current_database) { revokeImpl<PARTIAL_REVOKE_MODE>(elements, current_database); }
void AccessRights::fullRevoke(const AccessFlags & access) { revokeImpl<FULL_REVOKE_MODE>(access); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database) { revokeImpl<FULL_REVOKE_MODE>(access, database); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table) { revokeImpl<FULL_REVOKE_MODE>(access, database, table); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) { revokeImpl<FULL_REVOKE_MODE>(access, database, table, column); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) { revokeImpl<FULL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) { revokeImpl<FULL_REVOKE_MODE>(access, database, table, columns); }
void AccessRights::fullRevoke(const AccessRightsElement & element, std::string_view current_database) { revokeImpl<FULL_REVOKE_MODE>(element, current_database); }
void AccessRights::fullRevoke(const AccessRightsElements & elements, std::string_view current_database) { revokeImpl<FULL_REVOKE_MODE>(elements, current_database); }
AccessRights::Elements AccessRights::getElements() const
{
if (!root)
return {};
Elements res;
if (root->explicit_grants)
res.grants.push_back({root->explicit_grants});
if (root->children)
{
for (const auto & [db_name, db_node] : *root->children)
{
if (db_node.partial_revokes)
res.partial_revokes.push_back({db_node.partial_revokes, db_name});
if (db_node.explicit_grants)
res.grants.push_back({db_node.explicit_grants, db_name});
if (db_node.children)
{
for (const auto & [table_name, table_node] : *db_node.children)
{
if (table_node.partial_revokes)
res.partial_revokes.push_back({table_node.partial_revokes, db_name, table_name});
if (table_node.explicit_grants)
res.grants.push_back({table_node.explicit_grants, db_name, table_name});
if (table_node.children)
{
for (const auto & [column_name, column_node] : *table_node.children)
{
if (column_node.partial_revokes)
res.partial_revokes.push_back({column_node.partial_revokes, db_name, table_name, column_name});
if (column_node.explicit_grants)
res.grants.push_back({column_node.explicit_grants, db_name, table_name, column_name});
}
}
}
}
}
}
return res;
}
String AccessRights::toString() const
{
auto elements = getElements();
String res;
if (!elements.grants.empty())
{
res += "GRANT ";
res += elements.grants.toString();
}
if (!elements.partial_revokes.empty())
{
if (!res.empty())
res += ", ";
res += "REVOKE ";
res += elements.partial_revokes.toString();
}
if (res.empty())
res = "GRANT USAGE ON *.*";
return res;
}
template <typename... Args>
bool AccessRights::isGrantedImpl(const AccessFlags & access, const Args &... args) const
{
if (!root)
return access.isEmpty();
return root->isGranted(access, args...);
}
bool AccessRights::isGrantedImpl(const AccessRightsElement & element, std::string_view current_database) const
{
if (element.any_database)
{
return isGrantedImpl(element.access_flags);
}
else if (element.any_table)
{
if (element.database.empty())
return isGrantedImpl(element.access_flags, current_database);
else
return isGrantedImpl(element.access_flags, element.database);
}
else if (element.any_column)
{
if (element.database.empty())
return isGrantedImpl(element.access_flags, current_database, element.table);
else
return isGrantedImpl(element.access_flags, element.database, element.table);
}
else
{
if (element.database.empty())
return isGrantedImpl(element.access_flags, current_database, element.table, element.columns);
else
return isGrantedImpl(element.access_flags, element.database, element.table, element.columns);
}
}
bool AccessRights::isGrantedImpl(const AccessRightsElements & elements, std::string_view current_database) const
{
for (const auto & element : elements)
if (!isGrantedImpl(element, current_database))
return false;
return true;
}
bool AccessRights::isGranted(const AccessFlags & access) const { return isGrantedImpl(access); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database) const { return isGrantedImpl(access, database); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return isGrantedImpl(access, database, table); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return isGrantedImpl(access, database, table, column); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return isGrantedImpl(access, database, table, columns); }
bool AccessRights::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return isGrantedImpl(access, database, table, columns); }
bool AccessRights::isGranted(const AccessRightsElement & element, std::string_view current_database) const { return isGrantedImpl(element, current_database); }
bool AccessRights::isGranted(const AccessRightsElements & elements, std::string_view current_database) const { return isGrantedImpl(elements, current_database); }
bool operator ==(const AccessRights & left, const AccessRights & right)
{
if (!left.root)
return !right.root;
if (!right.root)
return false;
return *left.root == *right.root;
}
void AccessRights::merge(const AccessRights & other)
{
if (!root)
{
*this = other;
return;
}
if (other.root)
{
root->merge(*other.root, Helper::instance());
if (root->isEmpty())
root = nullptr;
}
}
}

View File

@ -0,0 +1,137 @@
#pragma once
#include <Core/Types.h>
#include <Access/AccessRightsElement.h>
#include <memory>
#include <vector>
namespace DB
{
/// Represents a set of access types granted on databases, tables, columns, etc.
/// For example, "GRANT SELECT, UPDATE ON db.*, GRANT INSERT ON db2.mytbl2" are access rights.
class AccessRights
{
public:
AccessRights();
AccessRights(const AccessFlags & access);
~AccessRights();
AccessRights(const AccessRights & src);
AccessRights & operator =(const AccessRights & src);
AccessRights(AccessRights && src);
AccessRights & operator =(AccessRights && src);
bool isEmpty() const;
/// Revokes everything. It's the same as fullRevoke(AccessType::ALL).
void clear();
/// Grants access on a specified database/table/column.
/// Does nothing if the specified access has been already granted.
void grant(const AccessFlags & access);
void grant(const AccessFlags & access, const std::string_view & database);
void grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table);
void grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column);
void grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
void grant(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns);
void grant(const AccessRightsElement & access, std::string_view current_database = {});
void grant(const AccessRightsElements & access, std::string_view current_database = {});
/// Revokes a specified access granted earlier on a specified database/table/column.
/// Does nothing if the specified access is not granted.
/// If the specified access is granted but on upper level (e.g. database for table, table for columns)
/// or lower level, the function also does nothing.
/// This function implements the standard SQL REVOKE behaviour.
void revoke(const AccessFlags & access);
void revoke(const AccessFlags & access, const std::string_view & database);
void revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table);
void revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column);
void revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
void revoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns);
void revoke(const AccessRightsElement & access, std::string_view current_database = {});
void revoke(const AccessRightsElements & access, std::string_view current_database = {});
/// Revokes a specified access granted earlier on a specified database/table/column or on lower levels.
/// The function also restricts access if it's granted on upper level.
/// For example, an access could be granted on a database and then revoked on a table in this database.
/// This function implements the MySQL REVOKE behaviour with partial_revokes is ON.
void partialRevoke(const AccessFlags & access);
void partialRevoke(const AccessFlags & access, const std::string_view & database);
void partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table);
void partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column);
void partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
void partialRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns);
void partialRevoke(const AccessRightsElement & access, std::string_view current_database = {});
void partialRevoke(const AccessRightsElements & access, std::string_view current_database = {});
/// Revokes a specified access granted earlier on a specified database/table/column or on lower levels.
/// The function also restricts access if it's granted on upper level.
/// For example, fullRevoke(AccessType::ALL) revokes all grants at all, just like clear();
/// fullRevoke(AccessType::SELECT, db) means it's not allowed to execute SELECT in that database anymore (from any table).
void fullRevoke(const AccessFlags & access);
void fullRevoke(const AccessFlags & access, const std::string_view & database);
void fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table);
void fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column);
void fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns);
void fullRevoke(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns);
void fullRevoke(const AccessRightsElement & access, std::string_view current_database = {});
void fullRevoke(const AccessRightsElements & access, std::string_view current_database = {});
/// Returns the information about all the access granted.
struct Elements
{
AccessRightsElements grants;
AccessRightsElements partial_revokes;
};
Elements getElements() const;
/// Returns the information about all the access granted as a string.
String toString() const;
/// Whether a specified access granted.
bool isGranted(const AccessFlags & access) const;
bool isGranted(const AccessFlags & access, const std::string_view & database) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
bool isGranted(const AccessRightsElement & access, std::string_view current_database = {}) const;
bool isGranted(const AccessRightsElements & access, std::string_view current_database = {}) const;
friend bool operator ==(const AccessRights & left, const AccessRights & right);
friend bool operator !=(const AccessRights & left, const AccessRights & right) { return !(left == right); }
/// Merges two sets of access rights together.
/// It's used to combine access rights from multiple roles.
void merge(const AccessRights & other);
private:
template <typename... Args>
void grantImpl(const AccessFlags & access, const Args &... args);
void grantImpl(const AccessRightsElement & access, std::string_view current_database);
void grantImpl(const AccessRightsElements & access, std::string_view current_database);
template <int mode, typename... Args>
void revokeImpl(const AccessFlags & access, const Args &... args);
template <int mode>
void revokeImpl(const AccessRightsElement & access, std::string_view current_database);
template <int mode>
void revokeImpl(const AccessRightsElements & access, std::string_view current_database);
template <typename... Args>
bool isGrantedImpl(const AccessFlags & access, const Args &... args) const;
bool isGrantedImpl(const AccessRightsElement & access, std::string_view current_database) const;
bool isGrantedImpl(const AccessRightsElements & access, std::string_view current_database) const;
template <typename... Args>
AccessFlags getAccessImpl(const Args &... args) const;
struct Node;
std::unique_ptr<Node> root;
};
}

View File

@ -0,0 +1,288 @@
#include <Access/AccessRightsContext.h>
#include <Common/Exception.h>
#include <Common/quoteString.h>
#include <Core/Settings.h>
#include <Poco/Logger.h>
#include <common/logger_useful.h>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <assert.h>
namespace DB
{
namespace ErrorCodes
{
extern const int ACCESS_DENIED;
extern const int READONLY;
extern const int QUERY_IS_PROHIBITED;
extern const int FUNCTION_NOT_ALLOWED;
}
namespace
{
enum CheckAccessRightsMode
{
RETURN_FALSE_IF_ACCESS_DENIED,
LOG_WARNING_IF_ACCESS_DENIED,
THROW_IF_ACCESS_DENIED,
};
String formatSkippedMessage()
{
return "";
}
String formatSkippedMessage(const std::string_view & database)
{
return ". Skipped database " + backQuoteIfNeed(database);
}
String formatSkippedMessage(const std::string_view & database, const std::string_view & table)
{
String str = ". Skipped table ";
if (!database.empty())
str += backQuoteIfNeed(database) + ".";
str += backQuoteIfNeed(table);
return str;
}
String formatSkippedMessage(const std::string_view & database, const std::string_view & table, const std::string_view & column)
{
String str = ". Skipped column " + backQuoteIfNeed(column) + " ON ";
if (!database.empty())
str += backQuoteIfNeed(database) + ".";
str += backQuoteIfNeed(table);
return str;
}
template <typename StringT>
String formatSkippedMessage(const std::string_view & database, const std::string_view & table, const std::vector<StringT> & columns)
{
if (columns.size() == 1)
return formatSkippedMessage(database, table, columns[0]);
String str = ". Skipped columns ";
bool need_comma = false;
for (const auto & column : columns)
{
if (std::exchange(need_comma, true))
str += ", ";
str += backQuoteIfNeed(column);
}
str += " ON ";
if (!database.empty())
str += backQuoteIfNeed(database) + ".";
str += backQuoteIfNeed(table);
return str;
}
}
AccessRightsContext::AccessRightsContext()
{
auto everything_granted = boost::make_shared<AccessRights>();
everything_granted->grant(AccessType::ALL);
result_access_cache[0] = std::move(everything_granted);
}
AccessRightsContext::AccessRightsContext(const ClientInfo & client_info_, const AccessRights & granted_to_user_, const Settings & settings, const String & current_database_)
: user_name(client_info_.current_user)
, granted_to_user(granted_to_user_)
, readonly(settings.readonly)
, allow_ddl(settings.allow_ddl)
, allow_introspection(settings.allow_introspection_functions)
, current_database(current_database_)
, interface(client_info_.interface)
, http_method(client_info_.http_method)
, trace_log(&Poco::Logger::get("AccessRightsContext (" + user_name + ")"))
{
}
template <int mode, typename... Args>
bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessFlags & access, const Args &... args) const
{
auto result_access = calculateResultAccess();
bool is_granted = result_access->isGranted(access, args...);
if (trace_log)
LOG_TRACE(trace_log, "Access " << (is_granted ? "granted" : "denied") << ": " << (AccessRightsElement{access, args...}.toString()));
if (is_granted)
return true;
if constexpr (mode == RETURN_FALSE_IF_ACCESS_DENIED)
return false;
if constexpr (mode == LOG_WARNING_IF_ACCESS_DENIED)
{
if (!log_)
return false;
}
auto show_error = [&](const String & msg, [[maybe_unused]] int error_code)
{
if constexpr (mode == THROW_IF_ACCESS_DENIED)
throw Exception(msg, error_code);
else if constexpr (mode == LOG_WARNING_IF_ACCESS_DENIED)
LOG_WARNING(log_, msg + formatSkippedMessage(args...));
};
if (readonly && calculateResultAccess(false, allow_ddl, allow_introspection)->isGranted(access, args...))
{
if (interface == ClientInfo::Interface::HTTP && http_method == ClientInfo::HTTPMethod::GET)
show_error(
"Cannot execute query in readonly mode. "
"For queries over HTTP, method GET implies readonly. You should use method POST for modifying queries",
ErrorCodes::READONLY);
else
show_error("Cannot execute query in readonly mode", ErrorCodes::READONLY);
}
else if (!allow_ddl && calculateResultAccess(readonly, true, allow_introspection)->isGranted(access, args...))
{
show_error("Cannot execute query. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
}
else if (!allow_introspection && calculateResultAccess(readonly, allow_ddl, true)->isGranted(access, args...))
{
show_error("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
}
else
{
show_error(
user_name + ": Not enough privileges. To perform this operation you should have grant "
+ AccessRightsElement{access, args...}.toString(),
ErrorCodes::ACCESS_DENIED);
}
return false;
}
template <int mode>
bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessRightsElement & element) const
{
if (element.any_database)
{
return checkImpl<mode>(log_, element.access_flags);
}
else if (element.any_table)
{
if (element.database.empty())
return checkImpl<mode>(log_, element.access_flags, current_database);
else
return checkImpl<mode>(log_, element.access_flags, element.database);
}
else if (element.any_column)
{
if (element.database.empty())
return checkImpl<mode>(log_, element.access_flags, current_database, element.table);
else
return checkImpl<mode>(log_, element.access_flags, element.database, element.table);
}
else
{
if (element.database.empty())
return checkImpl<mode>(log_, element.access_flags, current_database, element.table, element.columns);
else
return checkImpl<mode>(log_, element.access_flags, element.database, element.table, element.columns);
}
}
template <int mode>
bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessRightsElements & elements) const
{
for (const auto & element : elements)
if (!checkImpl<mode>(log_, element))
return false;
return true;
}
void AccessRightsContext::check(const AccessFlags & access) const { checkImpl<THROW_IF_ACCESS_DENIED>(nullptr, access); }
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database) const { checkImpl<THROW_IF_ACCESS_DENIED>(nullptr, access, database); }
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { checkImpl<THROW_IF_ACCESS_DENIED>(nullptr, access, database, table); }
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkImpl<THROW_IF_ACCESS_DENIED>(nullptr, access, database, table, column); }
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkImpl<THROW_IF_ACCESS_DENIED>(nullptr, access, database, table, columns); }
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkImpl<THROW_IF_ACCESS_DENIED>(nullptr, access, database, table, columns); }
void AccessRightsContext::check(const AccessRightsElement & access) const { checkImpl<THROW_IF_ACCESS_DENIED>(nullptr, access); }
void AccessRightsContext::check(const AccessRightsElements & access) const { checkImpl<THROW_IF_ACCESS_DENIED>(nullptr, access); }
bool AccessRightsContext::isGranted(const AccessFlags & access) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED>(nullptr, access); }
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED>(nullptr, access, database); }
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED>(nullptr, access, database, table); }
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED>(nullptr, access, database, table, column); }
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED>(nullptr, access, database, table, columns); }
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED>(nullptr, access, database, table, columns); }
bool AccessRightsContext::isGranted(const AccessRightsElement & access) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED>(nullptr, access); }
bool AccessRightsContext::isGranted(const AccessRightsElements & access) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED>(nullptr, access); }
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED>(log_, access); }
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED>(log_, access, database); }
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED>(log_, access, database, table); }
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED>(log_, access, database, table, column); }
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED>(log_, access, database, table, columns); }
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED>(log_, access, database, table, columns); }
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessRightsElement & access) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED>(log_, access); }
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessRightsElements & access) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED>(log_, access); }
boost::shared_ptr<const AccessRights> AccessRightsContext::calculateResultAccess() const
{
auto res = result_access_cache[0].load();
if (res)
return res;
return calculateResultAccess(readonly, allow_ddl, allow_introspection);
}
boost::shared_ptr<const AccessRights> AccessRightsContext::calculateResultAccess(UInt64 readonly_, bool allow_ddl_, bool allow_introspection_) const
{
size_t cache_index = static_cast<size_t>(readonly_ != readonly)
+ static_cast<size_t>(allow_ddl_ != allow_ddl) * 2 +
+ static_cast<size_t>(allow_introspection_ != allow_introspection) * 3;
assert(cache_index < std::size(result_access_cache));
auto cached = result_access_cache[cache_index].load();
if (cached)
return cached;
std::lock_guard lock{mutex};
cached = result_access_cache[cache_index].load();
if (cached)
return cached;
auto result_ptr = boost::make_shared<AccessRights>();
auto & result = *result_ptr;
result = granted_to_user;
static const AccessFlags table_ddl = AccessType::CREATE_DATABASE | AccessType::CREATE_TABLE | AccessType::CREATE_VIEW
| AccessType::ALTER_TABLE | AccessType::ALTER_VIEW | AccessType::DROP_DATABASE | AccessType::DROP_TABLE | AccessType::DROP_VIEW
| AccessType::DETACH_DATABASE | AccessType::DETACH_TABLE | AccessType::DETACH_VIEW | AccessType::TRUNCATE;
static const AccessFlags dictionary_ddl = AccessType::CREATE_DICTIONARY | AccessType::DROP_DICTIONARY | AccessType::DETACH_DICTIONARY;
static const AccessFlags table_and_dictionary_ddl = table_ddl | dictionary_ddl;
static const AccessFlags write_table_access = AccessType::INSERT | AccessType::OPTIMIZE;
if (readonly_)
result.fullRevoke(write_table_access | AccessType::SYSTEM);
if (readonly_ || !allow_ddl_)
result.fullRevoke(table_and_dictionary_ddl);
if (readonly_ == 1)
{
/// Table functions are forbidden in readonly mode.
/// For example, for readonly = 2 - allowed.
result.fullRevoke(AccessType::CREATE_TEMPORARY_TABLE | AccessType::TABLE_FUNCTIONS);
}
if (!allow_introspection_)
result.fullRevoke(AccessType::INTROSPECTION);
result_access_cache[cache_index].store(result_ptr);
return std::move(result_ptr);
}
}

View File

@ -0,0 +1,81 @@
#pragma once
#include <Access/AccessRights.h>
#include <Interpreters/ClientInfo.h>
#include <boost/smart_ptr/atomic_shared_ptr.hpp>
#include <mutex>
namespace Poco { class Logger; }
namespace DB
{
struct Settings;
class AccessRightsContext
{
public:
/// Default constructor creates access rights' context which allows everything.
AccessRightsContext();
AccessRightsContext(const ClientInfo & client_info_, const AccessRights & granted_to_user, const Settings & settings, const String & current_database_);
/// Checks if a specified access granted, and throws an exception if not.
/// Empty database means the current database.
void check(const AccessFlags & access) const;
void check(const AccessFlags & access, const std::string_view & database) const;
void check(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const;
void check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
void check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
void check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
void check(const AccessRightsElement & access) const;
void check(const AccessRightsElements & access) const;
/// Checks if a specified access granted.
bool isGranted(const AccessFlags & access) const;
bool isGranted(const AccessFlags & access, const std::string_view & database) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
bool isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
bool isGranted(const AccessRightsElement & access) const;
bool isGranted(const AccessRightsElements & access) const;
/// Checks if a specified access granted, and logs a warning if not.
bool isGranted(Poco::Logger * log_, const AccessFlags & access) const;
bool isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database) const;
bool isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table) const;
bool isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
bool isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
bool isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
bool isGranted(Poco::Logger * log_, const AccessRightsElement & access) const;
bool isGranted(Poco::Logger * log_, const AccessRightsElements & access) const;
private:
template <int mode, typename... Args>
bool checkImpl(Poco::Logger * log_, const AccessFlags & access, const Args &... args) const;
template <int mode>
bool checkImpl(Poco::Logger * log_, const AccessRightsElement & access) const;
template <int mode>
bool checkImpl(Poco::Logger * log_, const AccessRightsElements & access) const;
boost::shared_ptr<const AccessRights> calculateResultAccess() const;
boost::shared_ptr<const AccessRights> calculateResultAccess(UInt64 readonly_, bool allow_ddl_, bool allow_introspection_) const;
const String user_name;
const AccessRights granted_to_user;
const UInt64 readonly = 0;
const bool allow_ddl = true;
const bool allow_introspection = true;
const String current_database;
const ClientInfo::Interface interface = ClientInfo::Interface::TCP;
const ClientInfo::HTTPMethod http_method = ClientInfo::HTTPMethod::UNKNOWN;
Poco::Logger * const trace_log = nullptr;
mutable boost::atomic_shared_ptr<const AccessRights> result_access_cache[4];
mutable std::mutex mutex;
};
}

View File

@ -0,0 +1,86 @@
#include <Access/AccessRightsElement.h>
#include <Dictionaries/IDictionary.h>
#include <Common/quoteString.h>
namespace DB
{
void AccessRightsElement::setDatabase(const String & new_database)
{
database = new_database;
any_database = false;
}
void AccessRightsElement::replaceEmptyDatabase(const String & new_database)
{
if (isEmptyDatabase())
setDatabase(new_database);
}
bool AccessRightsElement::isEmptyDatabase() const
{
return !any_database && database.empty();
}
String AccessRightsElement::toString() const
{
String columns_in_parentheses;
if (!any_column)
{
for (const auto & column : columns)
{
columns_in_parentheses += columns_in_parentheses.empty() ? "(" : ", ";
columns_in_parentheses += backQuoteIfNeed(column);
}
columns_in_parentheses += ")";
}
String msg;
for (const std::string_view & keyword : access_flags.toKeywords())
{
if (!msg.empty())
msg += ", ";
msg += String{keyword} + columns_in_parentheses;
}
msg += " ON ";
if (any_database)
msg += "*.";
else if (!database.empty() && (database != IDictionary::NO_DATABASE_TAG))
msg += backQuoteIfNeed(database) + ".";
if (any_table)
msg += "*";
else
msg += backQuoteIfNeed(table);
return msg;
}
void AccessRightsElements::replaceEmptyDatabase(const String & new_database)
{
for (auto & element : *this)
element.replaceEmptyDatabase(new_database);
}
String AccessRightsElements::toString() const
{
String res;
bool need_comma = false;
for (auto & element : *this)
{
if (std::exchange(need_comma, true))
res += ", ";
res += element.toString();
}
if (res.empty())
res = "USAGE ON *.*";
return res;
}
}

View File

@ -0,0 +1,100 @@
#pragma once
#include <Access/AccessFlags.h>
namespace DB
{
/// An element of access rights which can be represented by single line
/// GRANT ... ON ...
struct AccessRightsElement
{
AccessFlags access_flags;
String database;
String table;
Strings columns;
bool any_database = true;
bool any_table = true;
bool any_column = true;
AccessRightsElement() = default;
AccessRightsElement(const AccessRightsElement &) = default;
AccessRightsElement & operator=(const AccessRightsElement &) = default;
AccessRightsElement(AccessRightsElement &&) = default;
AccessRightsElement & operator=(AccessRightsElement &&) = default;
AccessRightsElement(AccessFlags access_flags_) : access_flags(access_flags_) {}
AccessRightsElement(AccessFlags access_flags_, const std::string_view & database_)
: access_flags(access_flags_), database(database_), any_database(false)
{
}
AccessRightsElement(AccessFlags access_flags_, const std::string_view & database_, const std::string_view & table_)
: access_flags(access_flags_), database(database_), table(table_), any_database(false), any_table(false)
{
}
AccessRightsElement(
AccessFlags access_flags_, const std::string_view & database_, const std::string_view & table_, const std::string_view & column_)
: access_flags(access_flags_)
, database(database_)
, table(table_)
, columns({String{column_}})
, any_database(false)
, any_table(false)
, any_column(false)
{
}
AccessRightsElement(
AccessFlags access_flags_,
const std::string_view & database_,
const std::string_view & table_,
const std::vector<std::string_view> & columns_)
: access_flags(access_flags_), database(database_), table(table_), any_database(false), any_table(false), any_column(false)
{
columns.resize(columns_.size());
for (size_t i = 0; i != columns_.size(); ++i)
columns[i] = String{columns_[i]};
}
AccessRightsElement(
AccessFlags access_flags_, const std::string_view & database_, const std::string_view & table_, const Strings & columns_)
: access_flags(access_flags_)
, database(database_)
, table(table_)
, columns(columns_)
, any_database(false)
, any_table(false)
, any_column(false)
{
}
/// Sets the database.
void setDatabase(const String & new_database);
/// If the database is empty, replaces it with `new_database`. Otherwise does nothing.
void replaceEmptyDatabase(const String & new_database);
bool isEmptyDatabase() const;
/// Returns a human-readable representation like "SELECT, UPDATE(x, y) ON db.table".
/// The returned string isn't prefixed with the "GRANT" keyword.
String toString() const;
};
/// Multiple elements of access rights.
class AccessRightsElements : public std::vector<AccessRightsElement>
{
public:
/// Replaces the empty database with `new_database`.
void replaceEmptyDatabase(const String & new_database);
/// Returns a human-readable representation like "SELECT, UPDATE(x, y) ON db.table".
/// The returned string isn't prefixed with the "GRANT" keyword.
String toString() const;
};
}

View File

@ -0,0 +1,328 @@
#pragma once
#include <Core/Types.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <array>
namespace DB
{
/// Represents an access type which can be granted on databases, tables, columns, etc.
enum class AccessType
{
NONE, /// no access
ALL, /// full access
SHOW, /// allows to execute SHOW TABLES, SHOW CREATE TABLE, SHOW DATABASES and so on
/// (granted implicitly with any other grant)
EXISTS, /// allows to execute EXISTS, USE, i.e. to check existence
/// (granted implicitly on the database level with any other grant on the database and lower levels,
/// e.g. "GRANT SELECT(x) ON db.table" also grants EXISTS on db.*)
SELECT,
INSERT,
UPDATE, /// allows to execute ALTER UPDATE
DELETE, /// allows to execute ALTER DELETE
ADD_COLUMN,
DROP_COLUMN,
MODIFY_COLUMN,
COMMENT_COLUMN,
CLEAR_COLUMN,
ALTER_COLUMN, /// allow to execute ALTER {ADD|DROP|MODIFY...} COLUMN
ALTER_ORDER_BY,
ADD_INDEX,
DROP_INDEX,
MATERIALIZE_INDEX,
CLEAR_INDEX,
INDEX, /// allows to execute ALTER ORDER BY or ALTER {ADD|DROP...} INDEX
ADD_CONSTRAINT,
DROP_CONSTRAINT,
ALTER_CONSTRAINT, /// allows to execute ALTER {ADD|DROP} CONSTRAINT
MODIFY_TTL, /// allows to execute ALTER MODIFY TTL
MODIFY_SETTING, /// allows to execute ALTER MODIFY SETTING
MOVE_PARTITION,
FETCH_PARTITION,
FREEZE_PARTITION,
ALTER_TABLE, /// allows to execute ALTER TABLE ...
REFRESH_VIEW, /// allows to execute ALTER LIVE VIEW REFRESH
MODIFY_VIEW_QUERY, /// allows to execute ALTER TABLE MODIFY QUERY
ALTER_VIEW, /// allows to execute ALTER LIVE VIEW REFRESH, ALTER TABLE MODIFY QUERY
ALTER, /// allows to execute ALTER {TABLE|LIVE VIEW} ...
CREATE_DATABASE, /// allows to execute {CREATE|ATTACH} DATABASE
CREATE_TABLE, /// allows to execute {CREATE|ATTACH} TABLE
CREATE_VIEW, /// allows to execute {CREATE|ATTACH} VIEW
CREATE_DICTIONARY, /// allows to execute {CREATE|ATTACH} DICTIONARY
CREATE_TEMPORARY_TABLE, /// allows to create and manipulate temporary tables and views.
CREATE, /// allows to execute {CREATE|ATTACH} [TEMPORARY] {DATABASE|TABLE|VIEW|DICTIONARY}
ATTACH_DATABASE, /// allows to execute {CREATE|ATTACH} DATABASE
ATTACH_TABLE, /// allows to execute {CREATE|ATTACH} TABLE
ATTACH_VIEW, /// allows to execute {CREATE|ATTACH} VIEW
ATTACH_DICTIONARY, /// allows to execute {CREATE|ATTACH} DICTIONARY
ATTACH, /// allows to execute {CREATE|ATTACH} {DATABASE|TABLE|VIEW|DICTIONARY}
DROP_DATABASE,
DROP_TABLE,
DROP_VIEW,
DROP_DICTIONARY,
DROP, /// allows to execute DROP {DATABASE|TABLE|VIEW|DICTIONARY}
DETACH_DATABASE,
DETACH_TABLE,
DETACH_VIEW,
DETACH_DICTIONARY,
DETACH, /// allows to execute DETACH {DATABASE|TABLE|VIEW|DICTIONARY}
TRUNCATE_TABLE,
TRUNCATE_VIEW,
TRUNCATE, /// allows to execute TRUNCATE {TABLE|VIEW}
OPTIMIZE, /// allows to execute OPTIMIZE TABLE
KILL_QUERY, /// allows to kill a query started by another user (anyone can kill his own queries)
KILL_MUTATION, /// allows to kill a mutation
KILL, /// allows to execute KILL {MUTATION|QUERY}
CREATE_USER, /// allows to create, alter and drop users, roles, quotas, row policies.
ALTER_USER,
DROP_USER,
CREATE_ROLE,
DROP_ROLE,
CREATE_POLICY,
ALTER_POLICY,
DROP_POLICY,
CREATE_QUOTA,
ALTER_QUOTA,
DROP_QUOTA,
SHUTDOWN,
DROP_CACHE,
RELOAD_CONFIG,
RELOAD_DICTIONARY,
STOP_MERGES,
STOP_TTL_MERGES,
STOP_FETCHES,
STOP_MOVES,
STOP_DISTRIBUTED_SENDS,
STOP_REPLICATED_SENDS,
STOP_REPLICATION_QUEUES,
SYNC_REPLICA,
RESTART_REPLICA,
FLUSH_DISTRIBUTED,
FLUSH_LOGS,
SYSTEM, /// allows to execute SYSTEM {SHUTDOWN|RELOAD CONFIG|...}
dictGet, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
dictHas, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
dictGetHierarchy, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
dictIsIn, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
addressToLine, /// allows to execute function addressToLine
addressToSymbol, /// allows to execute function addressToSymbol
demangle, /// allows to execute function demangle
INTROSPECTION, /// allows to execute functions addressToLine, addressToSymbol, demangle
file,
url,
input,
values,
numbers,
merge,
remote,
mysql,
odbc,
jdbc,
hdfs,
s3,
TABLE_FUNCTIONS, /// allows to execute any table function
};
constexpr size_t MAX_ACCESS_TYPE = static_cast<size_t>(AccessType::TABLE_FUNCTIONS) + 1;
std::string_view toString(AccessType type);
namespace impl
{
template <typename = void>
class AccessTypeToKeywordConverter
{
public:
static const AccessTypeToKeywordConverter & instance()
{
static const AccessTypeToKeywordConverter res;
return res;
}
std::string_view convert(AccessType type) const
{
return access_type_to_keyword_mapping[static_cast<size_t>(type)];
}
private:
void addToMapping(AccessType type, const std::string_view & str)
{
String str2{str};
boost::replace_all(str2, "_", " ");
if (islower(str2[0]))
str2 += "()";
access_type_to_keyword_mapping[static_cast<size_t>(type)] = str2;
}
AccessTypeToKeywordConverter()
{
#define ACCESS_TYPE_TO_KEYWORD_CASE(type) \
addToMapping(AccessType::type, #type)
ACCESS_TYPE_TO_KEYWORD_CASE(NONE);
ACCESS_TYPE_TO_KEYWORD_CASE(ALL);
ACCESS_TYPE_TO_KEYWORD_CASE(SHOW);
ACCESS_TYPE_TO_KEYWORD_CASE(EXISTS);
ACCESS_TYPE_TO_KEYWORD_CASE(SELECT);
ACCESS_TYPE_TO_KEYWORD_CASE(INSERT);
ACCESS_TYPE_TO_KEYWORD_CASE(UPDATE);
ACCESS_TYPE_TO_KEYWORD_CASE(DELETE);
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(COMMENT_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(CLEAR_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_COLUMN);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_ORDER_BY);
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(MATERIALIZE_INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(CLEAR_INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(INDEX);
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_CONSTRAINT);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_CONSTRAINT);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_CONSTRAINT);
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_TTL);
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_SETTING);
ACCESS_TYPE_TO_KEYWORD_CASE(MOVE_PARTITION);
ACCESS_TYPE_TO_KEYWORD_CASE(FETCH_PARTITION);
ACCESS_TYPE_TO_KEYWORD_CASE(FREEZE_PARTITION);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(REFRESH_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_VIEW_QUERY);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_DATABASE);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_TEMPORARY_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_DATABASE);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_DATABASE);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_DATABASE);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH);
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE_TABLE);
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE_VIEW);
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE);
ACCESS_TYPE_TO_KEYWORD_CASE(OPTIMIZE);
ACCESS_TYPE_TO_KEYWORD_CASE(KILL_QUERY);
ACCESS_TYPE_TO_KEYWORD_CASE(KILL_MUTATION);
ACCESS_TYPE_TO_KEYWORD_CASE(KILL);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_USER);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_USER);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_USER);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_ROLE);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_ROLE);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_POLICY);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_POLICY);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_POLICY);
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_QUOTA);
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_QUOTA);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_QUOTA);
ACCESS_TYPE_TO_KEYWORD_CASE(SHUTDOWN);
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_CACHE);
ACCESS_TYPE_TO_KEYWORD_CASE(RELOAD_CONFIG);
ACCESS_TYPE_TO_KEYWORD_CASE(RELOAD_DICTIONARY);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_MERGES);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_TTL_MERGES);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_FETCHES);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_MOVES);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_DISTRIBUTED_SENDS);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_REPLICATED_SENDS);
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_REPLICATION_QUEUES);
ACCESS_TYPE_TO_KEYWORD_CASE(SYNC_REPLICA);
ACCESS_TYPE_TO_KEYWORD_CASE(RESTART_REPLICA);
ACCESS_TYPE_TO_KEYWORD_CASE(FLUSH_DISTRIBUTED);
ACCESS_TYPE_TO_KEYWORD_CASE(FLUSH_LOGS);
ACCESS_TYPE_TO_KEYWORD_CASE(SYSTEM);
ACCESS_TYPE_TO_KEYWORD_CASE(dictGet);
ACCESS_TYPE_TO_KEYWORD_CASE(dictHas);
ACCESS_TYPE_TO_KEYWORD_CASE(dictGetHierarchy);
ACCESS_TYPE_TO_KEYWORD_CASE(dictIsIn);
ACCESS_TYPE_TO_KEYWORD_CASE(addressToLine);
ACCESS_TYPE_TO_KEYWORD_CASE(addressToSymbol);
ACCESS_TYPE_TO_KEYWORD_CASE(demangle);
ACCESS_TYPE_TO_KEYWORD_CASE(INTROSPECTION);
ACCESS_TYPE_TO_KEYWORD_CASE(file);
ACCESS_TYPE_TO_KEYWORD_CASE(url);
ACCESS_TYPE_TO_KEYWORD_CASE(input);
ACCESS_TYPE_TO_KEYWORD_CASE(values);
ACCESS_TYPE_TO_KEYWORD_CASE(numbers);
ACCESS_TYPE_TO_KEYWORD_CASE(merge);
ACCESS_TYPE_TO_KEYWORD_CASE(remote);
ACCESS_TYPE_TO_KEYWORD_CASE(mysql);
ACCESS_TYPE_TO_KEYWORD_CASE(odbc);
ACCESS_TYPE_TO_KEYWORD_CASE(jdbc);
ACCESS_TYPE_TO_KEYWORD_CASE(hdfs);
ACCESS_TYPE_TO_KEYWORD_CASE(s3);
ACCESS_TYPE_TO_KEYWORD_CASE(TABLE_FUNCTIONS);
#undef ACCESS_TYPE_TO_KEYWORD_CASE
}
std::array<String, MAX_ACCESS_TYPE> access_type_to_keyword_mapping;
};
}
inline std::string_view toKeyword(AccessType type) { return impl::AccessTypeToKeywordConverter<>::instance().convert(type); }
}

View File

@ -1,6 +1,7 @@
#include <Access/IAccessEntity.h> #include <Access/IAccessEntity.h>
#include <Access/Quota.h> #include <Access/Quota.h>
#include <Access/RowPolicy.h> #include <Access/RowPolicy.h>
#include <Access/User.h>
#include <common/demangle.h> #include <common/demangle.h>
@ -8,6 +9,8 @@ namespace DB
{ {
String IAccessEntity::getTypeName(std::type_index type) String IAccessEntity::getTypeName(std::type_index type)
{ {
if (type == typeid(User))
return "User";
if (type == typeid(Quota)) if (type == typeid(Quota))
return "Quota"; return "Quota";
if (type == typeid(RowPolicy)) if (type == typeid(RowPolicy))

View File

@ -299,44 +299,24 @@ std::vector<UUID> IAccessStorage::tryUpdate(const std::vector<UUID> & ids, const
} }
IAccessStorage::SubscriptionPtr IAccessStorage::subscribeForChanges(std::type_index type, const OnChangedHandler & handler) const ext::scope_guard IAccessStorage::subscribeForChanges(std::type_index type, const OnChangedHandler & handler) const
{ {
return subscribeForChangesImpl(type, handler); return subscribeForChangesImpl(type, handler);
} }
IAccessStorage::SubscriptionPtr IAccessStorage::subscribeForChanges(const UUID & id, const OnChangedHandler & handler) const ext::scope_guard IAccessStorage::subscribeForChanges(const UUID & id, const OnChangedHandler & handler) const
{ {
return subscribeForChangesImpl(id, handler); return subscribeForChangesImpl(id, handler);
} }
IAccessStorage::SubscriptionPtr IAccessStorage::subscribeForChanges(const std::vector<UUID> & ids, const OnChangedHandler & handler) const ext::scope_guard IAccessStorage::subscribeForChanges(const std::vector<UUID> & ids, const OnChangedHandler & handler) const
{ {
if (ids.empty()) ext::scope_guard subscriptions;
return nullptr;
if (ids.size() == 1)
return subscribeForChangesImpl(ids[0], handler);
std::vector<SubscriptionPtr> subscriptions;
subscriptions.reserve(ids.size());
for (const auto & id : ids) for (const auto & id : ids)
{ subscriptions.join(subscribeForChangesImpl(id, handler));
auto subscription = subscribeForChangesImpl(id, handler); return subscriptions;
if (subscription)
subscriptions.push_back(std::move(subscription));
}
class SubscriptionImpl : public Subscription
{
public:
SubscriptionImpl(std::vector<SubscriptionPtr> subscriptions_)
: subscriptions(std::move(subscriptions_)) {}
private:
std::vector<SubscriptionPtr> subscriptions;
};
return std::make_unique<SubscriptionImpl>(std::move(subscriptions));
} }

View File

@ -3,6 +3,7 @@
#include <Access/IAccessEntity.h> #include <Access/IAccessEntity.h>
#include <Core/Types.h> #include <Core/Types.h>
#include <Core/UUID.h> #include <Core/UUID.h>
#include <ext/scope_guard.h>
#include <functional> #include <functional>
#include <optional> #include <optional>
#include <vector> #include <vector>
@ -109,26 +110,19 @@ public:
/// Updates multiple entities in the storage. Returns the list of successfully updated. /// Updates multiple entities in the storage. Returns the list of successfully updated.
std::vector<UUID> tryUpdate(const std::vector<UUID> & ids, const UpdateFunc & update_func); std::vector<UUID> tryUpdate(const std::vector<UUID> & ids, const UpdateFunc & update_func);
class Subscription
{
public:
virtual ~Subscription() {}
};
using SubscriptionPtr = std::unique_ptr<Subscription>;
using OnChangedHandler = std::function<void(const UUID & /* id */, const AccessEntityPtr & /* new or changed entity, null if removed */)>; using OnChangedHandler = std::function<void(const UUID & /* id */, const AccessEntityPtr & /* new or changed entity, null if removed */)>;
/// Subscribes for all changes. /// Subscribes for all changes.
/// Can return nullptr if cannot subscribe (identifier not found) or if it doesn't make sense (the storage is read-only). /// Can return nullptr if cannot subscribe (identifier not found) or if it doesn't make sense (the storage is read-only).
SubscriptionPtr subscribeForChanges(std::type_index type, const OnChangedHandler & handler) const; ext::scope_guard subscribeForChanges(std::type_index type, const OnChangedHandler & handler) const;
template <typename EntityType> template <typename EntityType>
SubscriptionPtr subscribeForChanges(OnChangedHandler handler) const { return subscribeForChanges(typeid(EntityType), handler); } ext::scope_guard subscribeForChanges(OnChangedHandler handler) const { return subscribeForChanges(typeid(EntityType), handler); }
/// Subscribes for changes of a specific entry. /// Subscribes for changes of a specific entry.
/// Can return nullptr if cannot subscribe (identifier not found) or if it doesn't make sense (the storage is read-only). /// Can return nullptr if cannot subscribe (identifier not found) or if it doesn't make sense (the storage is read-only).
SubscriptionPtr subscribeForChanges(const UUID & id, const OnChangedHandler & handler) const; ext::scope_guard subscribeForChanges(const UUID & id, const OnChangedHandler & handler) const;
SubscriptionPtr subscribeForChanges(const std::vector<UUID> & ids, const OnChangedHandler & handler) const; ext::scope_guard subscribeForChanges(const std::vector<UUID> & ids, const OnChangedHandler & handler) const;
bool hasSubscription(std::type_index type) const; bool hasSubscription(std::type_index type) const;
bool hasSubscription(const UUID & id) const; bool hasSubscription(const UUID & id) const;
@ -142,8 +136,8 @@ protected:
virtual UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) = 0; virtual UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) = 0;
virtual void removeImpl(const UUID & id) = 0; virtual void removeImpl(const UUID & id) = 0;
virtual void updateImpl(const UUID & id, const UpdateFunc & update_func) = 0; virtual void updateImpl(const UUID & id, const UpdateFunc & update_func) = 0;
virtual SubscriptionPtr subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const = 0; virtual ext::scope_guard subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const = 0;
virtual SubscriptionPtr subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const = 0; virtual ext::scope_guard subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const = 0;
virtual bool hasSubscriptionImpl(const UUID & id) const = 0; virtual bool hasSubscriptionImpl(const UUID & id) const = 0;
virtual bool hasSubscriptionImpl(std::type_index type) const = 0; virtual bool hasSubscriptionImpl(std::type_index type) const = 0;

View File

@ -6,7 +6,7 @@
namespace DB namespace DB
{ {
MemoryAccessStorage::MemoryAccessStorage(const String & storage_name_) MemoryAccessStorage::MemoryAccessStorage(const String & storage_name_)
: IAccessStorage(storage_name_), shared_ptr_to_this{std::make_shared<const MemoryAccessStorage *>(this)} : IAccessStorage(storage_name_)
{ {
} }
@ -256,85 +256,38 @@ void MemoryAccessStorage::prepareNotifications(const Entry & entry, bool remove,
} }
IAccessStorage::SubscriptionPtr MemoryAccessStorage::subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const ext::scope_guard MemoryAccessStorage::subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const
{ {
class SubscriptionImpl : public Subscription std::lock_guard lock{mutex};
auto handler_it = handlers_by_type.emplace(type, handler);
return [this, handler_it]
{ {
public: std::lock_guard lock2{mutex};
SubscriptionImpl( handlers_by_type.erase(handler_it);
const MemoryAccessStorage & storage_,
std::type_index type_,
const OnChangedHandler & handler_)
: storage_weak(storage_.shared_ptr_to_this)
{
std::lock_guard lock{storage_.mutex};
handler_it = storage_.handlers_by_type.emplace(type_, handler_);
}
~SubscriptionImpl() override
{
auto storage = storage_weak.lock();
if (storage)
{
std::lock_guard lock{(*storage)->mutex};
(*storage)->handlers_by_type.erase(handler_it);
}
}
private:
std::weak_ptr<const MemoryAccessStorage *> storage_weak;
std::unordered_multimap<std::type_index, OnChangedHandler>::iterator handler_it;
}; };
return std::make_unique<SubscriptionImpl>(*this, type, handler);
} }
IAccessStorage::SubscriptionPtr MemoryAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const ext::scope_guard MemoryAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const
{ {
class SubscriptionImpl : public Subscription std::lock_guard lock{mutex};
auto it = entries.find(id);
if (it == entries.end())
return {};
const Entry & entry = it->second;
auto handler_it = entry.handlers_by_id.insert(entry.handlers_by_id.end(), handler);
return [this, id, handler_it]
{ {
public: std::lock_guard lock2{mutex};
SubscriptionImpl( auto it2 = entries.find(id);
const MemoryAccessStorage & storage_, if (it2 != entries.end())
const UUID & id_,
const OnChangedHandler & handler_)
: storage_weak(storage_.shared_ptr_to_this),
id(id_)
{ {
std::lock_guard lock{storage_.mutex}; const Entry & entry2 = it2->second;
auto it = storage_.entries.find(id); entry2.handlers_by_id.erase(handler_it);
if (it == storage_.entries.end())
{
storage_weak.reset();
return;
}
const Entry & entry = it->second;
handler_it = entry.handlers_by_id.insert(entry.handlers_by_id.end(), handler_);
} }
~SubscriptionImpl() override
{
auto storage = storage_weak.lock();
if (storage)
{
std::lock_guard lock{(*storage)->mutex};
auto it = (*storage)->entries.find(id);
if (it != (*storage)->entries.end())
{
const Entry & entry = it->second;
entry.handlers_by_id.erase(handler_it);
}
}
}
private:
std::weak_ptr<const MemoryAccessStorage *> storage_weak;
UUID id;
std::list<OnChangedHandler>::iterator handler_it;
}; };
return std::make_unique<SubscriptionImpl>(*this, id, handler);
} }

View File

@ -29,8 +29,8 @@ private:
UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) override; UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) override;
void removeImpl(const UUID & id) override; void removeImpl(const UUID & id) override;
void updateImpl(const UUID & id, const UpdateFunc & update_func) override; void updateImpl(const UUID & id, const UpdateFunc & update_func) override;
SubscriptionPtr subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const override; ext::scope_guard subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const override;
SubscriptionPtr subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const override; ext::scope_guard subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const override;
bool hasSubscriptionImpl(const UUID & id) const override; bool hasSubscriptionImpl(const UUID & id) const override;
bool hasSubscriptionImpl(std::type_index type) const override; bool hasSubscriptionImpl(std::type_index type) const override;
@ -60,6 +60,5 @@ private:
std::unordered_map<UUID, Entry> entries; /// We want to search entries both by ID and by the pair of name and type. std::unordered_map<UUID, Entry> entries; /// We want to search entries both by ID and by the pair of name and type.
std::unordered_map<NameTypePair, Entry *, Hash> names; /// and by the pair of name and type. std::unordered_map<NameTypePair, Entry *, Hash> names; /// and by the pair of name and type.
mutable std::unordered_multimap<std::type_index, OnChangedHandler> handlers_by_type; mutable std::unordered_multimap<std::type_index, OnChangedHandler> handlers_by_type;
std::shared_ptr<const MemoryAccessStorage *> shared_ptr_to_this; /// We need weak pointers to `this` to implement subscriptions.
}; };
} }

View File

@ -185,41 +185,21 @@ void MultipleAccessStorage::updateImpl(const UUID & id, const UpdateFunc & updat
} }
IAccessStorage::SubscriptionPtr MultipleAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const ext::scope_guard MultipleAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const
{ {
auto storage = findStorage(id); auto storage = findStorage(id);
if (!storage) if (!storage)
return nullptr; return {};
return storage->subscribeForChanges(id, handler); return storage->subscribeForChanges(id, handler);
} }
IAccessStorage::SubscriptionPtr MultipleAccessStorage::subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const ext::scope_guard MultipleAccessStorage::subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const
{ {
std::vector<SubscriptionPtr> subscriptions; ext::scope_guard subscriptions;
for (const auto & nested_storage : nested_storages) for (const auto & nested_storage : nested_storages)
{ subscriptions.join(nested_storage->subscribeForChanges(type, handler));
auto subscription = nested_storage->subscribeForChanges(type, handler); return subscriptions;
if (subscription)
subscriptions.emplace_back(std::move(subscription));
}
if (subscriptions.empty())
return nullptr;
if (subscriptions.size() == 1)
return std::move(subscriptions[0]);
class SubscriptionImpl : public Subscription
{
public:
SubscriptionImpl(std::vector<SubscriptionPtr> subscriptions_)
: subscriptions(std::move(subscriptions_)) {}
private:
std::vector<SubscriptionPtr> subscriptions;
};
return std::make_unique<SubscriptionImpl>(std::move(subscriptions));
} }

View File

@ -38,8 +38,8 @@ protected:
UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) override; UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) override;
void removeImpl(const UUID & id) override; void removeImpl(const UUID & id) override;
void updateImpl(const UUID & id, const UpdateFunc & update_func) override; void updateImpl(const UUID & id, const UpdateFunc & update_func) override;
SubscriptionPtr subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const override; ext::scope_guard subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const override;
SubscriptionPtr subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const override; ext::scope_guard subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const override;
bool hasSubscriptionImpl(const UUID & id) const override; bool hasSubscriptionImpl(const UUID & id) const override;
bool hasSubscriptionImpl(std::type_index type) const override; bool hasSubscriptionImpl(std::type_index type) const override;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Access/QuotaContext.h> #include <Access/QuotaContext.h>
#include <Access/IAccessStorage.h> #include <ext/scope_guard.h>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
@ -56,7 +56,7 @@ private:
mutable std::mutex mutex; mutable std::mutex mutex;
std::unordered_map<UUID /* quota id */, QuotaInfo> all_quotas; std::unordered_map<UUID /* quota id */, QuotaInfo> all_quotas;
bool all_quotas_read = false; bool all_quotas_read = false;
IAccessStorage::SubscriptionPtr subscription; ext::scope_guard subscription;
std::vector<std::weak_ptr<QuotaContext>> contexts; std::vector<std::weak_ptr<QuotaContext>> contexts;
}; };
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Access/RowPolicyContext.h> #include <Access/RowPolicyContext.h>
#include <Access/IAccessStorage.h> #include <ext/scope_guard.h>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@ -46,7 +46,7 @@ private:
const AccessControlManager & access_control_manager; const AccessControlManager & access_control_manager;
std::unordered_map<UUID, PolicyInfo> all_policies; std::unordered_map<UUID, PolicyInfo> all_policies;
bool all_policies_read = false; bool all_policies_read = false;
IAccessStorage::SubscriptionPtr subscription; ext::scope_guard subscription;
std::vector<std::weak_ptr<RowPolicyContext>> contexts; std::vector<std::weak_ptr<RowPolicyContext>> contexts;
std::mutex mutex; std::mutex mutex;
}; };

16
dbms/src/Access/User.cpp Normal file
View File

@ -0,0 +1,16 @@
#include <Access/User.h>
namespace DB
{
bool User::equal(const IAccessEntity & other) const
{
if (!IAccessEntity::equal(other))
return false;
const auto & other_user = typeid_cast<const User &>(other);
return (authentication == other_user.authentication) && (allowed_client_hosts == other_user.allowed_client_hosts)
&& (access == other_user.access) && (profile == other_user.profile);
}
}

26
dbms/src/Access/User.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <Access/IAccessEntity.h>
#include <Access/Authentication.h>
#include <Access/AllowedClientHosts.h>
#include <Access/AccessRights.h>
#include <Core/Types.h>
namespace DB
{
/** User and ACL.
*/
struct User : public IAccessEntity
{
Authentication authentication;
AllowedClientHosts allowed_client_hosts;
AccessRights access;
String profile;
bool equal(const IAccessEntity & other) const override;
std::shared_ptr<IAccessEntity> clone() const override { return cloneImpl<User>(); }
};
using UserPtr = std::shared_ptr<const User>;
}

View File

@ -1,6 +1,8 @@
#include <Access/UsersConfigAccessStorage.h> #include <Access/UsersConfigAccessStorage.h>
#include <Access/Quota.h> #include <Access/Quota.h>
#include <Access/RowPolicy.h> #include <Access/RowPolicy.h>
#include <Access/User.h>
#include <Dictionaries/IDictionary.h>
#include <Common/StringUtils/StringUtils.h> #include <Common/StringUtils/StringUtils.h>
#include <Common/quoteString.h> #include <Common/quoteString.h>
#include <Poco/Util/AbstractConfiguration.h> #include <Poco/Util/AbstractConfiguration.h>
@ -10,10 +12,19 @@
namespace DB namespace DB
{ {
namespace ErrorCodes
{
extern const int BAD_ARGUMENTS;
extern const int UNKNOWN_ADDRESS_PATTERN_TYPE;
}
namespace namespace
{ {
char getTypeChar(std::type_index type) char getTypeChar(std::type_index type)
{ {
if (type == typeid(User))
return 'U';
if (type == typeid(Quota)) if (type == typeid(Quota))
return 'Q'; return 'Q';
if (type == typeid(RowPolicy)) if (type == typeid(RowPolicy))
@ -37,6 +48,139 @@ namespace
UUID generateID(const IAccessEntity & entity) { return generateID(entity.getType(), entity.getFullName()); } UUID generateID(const IAccessEntity & entity) { return generateID(entity.getType(), entity.getFullName()); }
UserPtr parseUser(const Poco::Util::AbstractConfiguration & config, const String & user_name)
{
auto user = std::make_shared<User>();
user->setName(user_name);
String user_config = "users." + user_name;
bool has_password = config.has(user_config + ".password");
bool has_password_sha256_hex = config.has(user_config + ".password_sha256_hex");
bool has_password_double_sha1_hex = config.has(user_config + ".password_double_sha1_hex");
if (has_password + has_password_sha256_hex + has_password_double_sha1_hex > 1)
throw Exception("More than one field of 'password', 'password_sha256_hex', 'password_double_sha1_hex' is used to specify password for user " + user_name + ". Must be only one of them.",
ErrorCodes::BAD_ARGUMENTS);
if (!has_password && !has_password_sha256_hex && !has_password_double_sha1_hex)
throw Exception("Either 'password' or 'password_sha256_hex' or 'password_double_sha1_hex' must be specified for user " + user_name + ".", ErrorCodes::BAD_ARGUMENTS);
if (has_password)
{
user->authentication = Authentication{Authentication::PLAINTEXT_PASSWORD};
user->authentication.setPassword(config.getString(user_config + ".password"));
}
else if (has_password_sha256_hex)
{
user->authentication = Authentication{Authentication::SHA256_PASSWORD};
user->authentication.setPasswordHashHex(config.getString(user_config + ".password_sha256_hex"));
}
else if (has_password_double_sha1_hex)
{
user->authentication = Authentication{Authentication::DOUBLE_SHA1_PASSWORD};
user->authentication.setPasswordHashHex(config.getString(user_config + ".password_double_sha1_hex"));
}
user->profile = config.getString(user_config + ".profile");
/// Fill list of allowed hosts.
const auto networks_config = user_config + ".networks";
if (config.has(networks_config))
{
Poco::Util::AbstractConfiguration::Keys keys;
config.keys(networks_config, keys);
for (const String & key : keys)
{
String value = config.getString(networks_config + "." + key);
if (key.starts_with("ip"))
user->allowed_client_hosts.addSubnet(value);
else if (key.starts_with("host_regexp"))
user->allowed_client_hosts.addHostRegexp(value);
else if (key.starts_with("host"))
user->allowed_client_hosts.addHostName(value);
else
throw Exception("Unknown address pattern type: " + key, ErrorCodes::UNKNOWN_ADDRESS_PATTERN_TYPE);
}
}
/// Fill list of allowed databases.
const auto databases_config = user_config + ".allow_databases";
std::optional<Strings> databases;
if (config.has(databases_config))
{
Poco::Util::AbstractConfiguration::Keys keys;
config.keys(databases_config, keys);
databases.emplace();
databases->reserve(keys.size());
for (const auto & key : keys)
{
const auto database_name = config.getString(databases_config + "." + key);
databases->push_back(database_name);
}
}
/// Fill list of allowed dictionaries.
const auto dictionaries_config = user_config + ".allow_dictionaries";
std::optional<Strings> dictionaries;
if (config.has(dictionaries_config))
{
Poco::Util::AbstractConfiguration::Keys keys;
config.keys(dictionaries_config, keys);
dictionaries.emplace();
dictionaries->reserve(keys.size());
for (const auto & key : keys)
{
const auto dictionary_name = config.getString(dictionaries_config + "." + key);
dictionaries->push_back(dictionary_name);
}
}
user->access.grant(AccessType::ALL); /// By default all databases are accessible.
if (databases)
{
user->access.fullRevoke(AccessFlags::databaseLevel());
for (const String & database : *databases)
user->access.grant(AccessFlags::databaseLevel(), database);
user->access.grant(AccessFlags::databaseLevel(), "system"); /// Anyone has access to the "system" database.
}
if (dictionaries)
{
user->access.fullRevoke(AccessType::dictGet, IDictionary::NO_DATABASE_TAG);
for (const String & dictionary : *dictionaries)
user->access.grant(AccessType::dictGet, IDictionary::NO_DATABASE_TAG, dictionary);
}
else if (databases)
user->access.grant(AccessType::dictGet, IDictionary::NO_DATABASE_TAG);
return user;
}
std::vector<AccessEntityPtr> parseUsers(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
{
Poco::Util::AbstractConfiguration::Keys user_names;
config.keys("users", user_names);
std::vector<AccessEntityPtr> users;
users.reserve(user_names.size());
for (const auto & user_name : user_names)
{
try
{
users.push_back(parseUser(config, user_name));
}
catch (...)
{
tryLogCurrentException(log, "Could not parse user " + backQuote(user_name));
}
}
return users;
}
QuotaPtr parseQuota(const Poco::Util::AbstractConfiguration & config, const String & quota_name, const Strings & user_names) QuotaPtr parseQuota(const Poco::Util::AbstractConfiguration & config, const String & quota_name, const Strings & user_names)
{ {
auto quota = std::make_shared<Quota>(); auto quota = std::make_shared<Quota>();
@ -192,6 +336,8 @@ UsersConfigAccessStorage::~UsersConfigAccessStorage() {}
void UsersConfigAccessStorage::loadFromConfig(const Poco::Util::AbstractConfiguration & config) void UsersConfigAccessStorage::loadFromConfig(const Poco::Util::AbstractConfiguration & config)
{ {
std::vector<std::pair<UUID, AccessEntityPtr>> all_entities; std::vector<std::pair<UUID, AccessEntityPtr>> all_entities;
for (const auto & entity : parseUsers(config, getLogger()))
all_entities.emplace_back(generateID(*entity), entity);
for (const auto & entity : parseQuotas(config, getLogger())) for (const auto & entity : parseQuotas(config, getLogger()))
all_entities.emplace_back(generateID(*entity), entity); all_entities.emplace_back(generateID(*entity), entity);
for (const auto & entity : parseRowPolicies(config, getLogger())) for (const auto & entity : parseRowPolicies(config, getLogger()))
@ -250,13 +396,13 @@ void UsersConfigAccessStorage::updateImpl(const UUID & id, const UpdateFunc &)
} }
IAccessStorage::SubscriptionPtr UsersConfigAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const ext::scope_guard UsersConfigAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const
{ {
return memory_storage.subscribeForChanges(id, handler); return memory_storage.subscribeForChanges(id, handler);
} }
IAccessStorage::SubscriptionPtr UsersConfigAccessStorage::subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const ext::scope_guard UsersConfigAccessStorage::subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const
{ {
return memory_storage.subscribeForChanges(type, handler); return memory_storage.subscribeForChanges(type, handler);
} }

View File

@ -32,8 +32,8 @@ private:
UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) override; UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) override;
void removeImpl(const UUID & id) override; void removeImpl(const UUID & id) override;
void updateImpl(const UUID & id, const UpdateFunc & update_func) override; void updateImpl(const UUID & id, const UpdateFunc & update_func) override;
SubscriptionPtr subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const override; ext::scope_guard subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const override;
SubscriptionPtr subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const override; ext::scope_guard subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const override;
bool hasSubscriptionImpl(const UUID & id) const override; bool hasSubscriptionImpl(const UUID & id) const override;
bool hasSubscriptionImpl(std::type_index type) const override; bool hasSubscriptionImpl(std::type_index type) const override;

View File

@ -83,6 +83,8 @@ AggregateFunctionPtr createAggregateFunctionSumMap(const std::string & name, con
AggregateFunctionPtr res(createWithNumericBasedType<Function>(*keys_type, keys_type, values_types, arguments)); AggregateFunctionPtr res(createWithNumericBasedType<Function>(*keys_type, keys_type, values_types, arguments));
if (!res) if (!res)
res.reset(createWithDecimalType<Function>(*keys_type, keys_type, values_types, arguments)); res.reset(createWithDecimalType<Function>(*keys_type, keys_type, values_types, arguments));
if (!res)
res.reset(createWithStringType<Function>(*keys_type, keys_type, values_types, arguments));
if (!res) if (!res)
throw Exception("Illegal type of argument for aggregate function " + name, ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); throw Exception("Illegal type of argument for aggregate function " + name, ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
@ -106,6 +108,8 @@ AggregateFunctionPtr createAggregateFunctionSumMapFiltered(const std::string & n
AggregateFunctionPtr res(createWithNumericBasedType<Function>(*keys_type, keys_type, values_types, keys_to_keep, arguments, params)); AggregateFunctionPtr res(createWithNumericBasedType<Function>(*keys_type, keys_type, values_types, keys_to_keep, arguments, params));
if (!res) if (!res)
res.reset(createWithDecimalType<Function>(*keys_type, keys_type, values_types, keys_to_keep, arguments, params)); res.reset(createWithDecimalType<Function>(*keys_type, keys_type, values_types, keys_to_keep, arguments, params));
if (!res)
res.reset(createWithStringType<Function>(*keys_type, keys_type, values_types, keys_to_keep, arguments, params));
if (!res) if (!res)
throw Exception("Illegal type of argument for aggregate function " + name, ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); throw Exception("Illegal type of argument for aggregate function " + name, ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);

View File

@ -10,6 +10,7 @@
#include <Columns/ColumnTuple.h> #include <Columns/ColumnTuple.h>
#include <Columns/ColumnVector.h> #include <Columns/ColumnVector.h>
#include <Columns/ColumnDecimal.h> #include <Columns/ColumnDecimal.h>
#include <Columns/ColumnString.h>
#include <Common/FieldVisitors.h> #include <Common/FieldVisitors.h>
#include <Common/assert_cast.h> #include <Common/assert_cast.h>
@ -56,8 +57,6 @@ class AggregateFunctionSumMapBase : public IAggregateFunctionDataHelper<
AggregateFunctionSumMapData<NearestFieldType<T>>, Derived> AggregateFunctionSumMapData<NearestFieldType<T>>, Derived>
{ {
private: private:
using ColVecType = std::conditional_t<IsDecimalNumber<T>, ColumnDecimal<T>, ColumnVector<T>>;
DataTypePtr keys_type; DataTypePtr keys_type;
DataTypes values_types; DataTypes values_types;
@ -84,9 +83,10 @@ public:
void add(AggregateDataPtr place, const IColumn ** columns, const size_t row_num, Arena *) const override void add(AggregateDataPtr place, const IColumn ** columns, const size_t row_num, Arena *) const override
{ {
// Column 0 contains array of keys of known type // Column 0 contains array of keys of known type
Field key_field;
const ColumnArray & array_column0 = assert_cast<const ColumnArray &>(*columns[0]); const ColumnArray & array_column0 = assert_cast<const ColumnArray &>(*columns[0]);
const IColumn::Offsets & offsets0 = array_column0.getOffsets(); const IColumn::Offsets & offsets0 = array_column0.getOffsets();
const auto & keys_vec = static_cast<const ColVecType &>(array_column0.getData()); const IColumn & key_column = array_column0.getData();
const size_t keys_vec_offset = offsets0[row_num - 1]; const size_t keys_vec_offset = offsets0[row_num - 1];
const size_t keys_vec_size = (offsets0[row_num] - keys_vec_offset); const size_t keys_vec_size = (offsets0[row_num] - keys_vec_offset);
@ -111,7 +111,8 @@ public:
using IteratorType = typename MapType::iterator; using IteratorType = typename MapType::iterator;
array_column.getData().get(values_vec_offset + i, value); array_column.getData().get(values_vec_offset + i, value);
const auto & key = keys_vec.getElement(keys_vec_offset + i); key_column.get(keys_vec_offset + i, key_field);
auto && key = key_field.get<T>();
if (!keepKey(key)) if (!keepKey(key))
{ {
@ -121,7 +122,7 @@ public:
IteratorType it; IteratorType it;
if constexpr (IsDecimalNumber<T>) if constexpr (IsDecimalNumber<T>)
{ {
UInt32 scale = keys_vec.getData().getScale(); UInt32 scale = static_cast<const ColumnDecimal<T> &>(key_column).getData().getScale();
it = merged_maps.find(DecimalField<T>(key, scale)); it = merged_maps.find(DecimalField<T>(key, scale));
} }
else else
@ -139,7 +140,7 @@ public:
if constexpr (IsDecimalNumber<T>) if constexpr (IsDecimalNumber<T>)
{ {
UInt32 scale = keys_vec.getData().getScale(); UInt32 scale = static_cast<const ColumnDecimal<T> &>(key_column).getData().getScale();
merged_maps.emplace(DecimalField<T>(key, scale), std::move(new_values)); merged_maps.emplace(DecimalField<T>(key, scale), std::move(new_values));
} }
else else

View File

@ -149,4 +149,13 @@ static IAggregateFunction * createWithTwoNumericTypes(const IDataType & first_ty
return nullptr; return nullptr;
} }
template <template <typename> class AggregateFunctionTemplate, typename... TArgs>
static IAggregateFunction * createWithStringType(const IDataType & argument_type, TArgs && ... args)
{
WhichDataType which(argument_type);
if (which.idx == TypeIndex::String) return new AggregateFunctionTemplate<String>(std::forward<TArgs>(args)...);
if (which.idx == TypeIndex::FixedString) return new AggregateFunctionTemplate<String>(std::forward<TArgs>(args)...);
return nullptr;
}
} }

View File

@ -469,7 +469,7 @@ namespace ErrorCodes
extern const int ACCESS_ENTITY_FOUND_DUPLICATES = 494; extern const int ACCESS_ENTITY_FOUND_DUPLICATES = 494;
extern const int ACCESS_ENTITY_STORAGE_READONLY = 495; extern const int ACCESS_ENTITY_STORAGE_READONLY = 495;
extern const int QUOTA_REQUIRES_CLIENT_KEY = 496; extern const int QUOTA_REQUIRES_CLIENT_KEY = 496;
extern const int NOT_ENOUGH_PRIVILEGES = 497; extern const int ACCESS_DENIED = 497;
extern const int LIMIT_BY_WITH_TIES_IS_NOT_SUPPORTED = 498; extern const int LIMIT_BY_WITH_TIES_IS_NOT_SUPPORTED = 498;
extern const int S3_ERROR = 499; extern const int S3_ERROR = 499;
extern const int CANNOT_CREATE_DATABASE = 501; extern const int CANNOT_CREATE_DATABASE = 501;
@ -479,6 +479,8 @@ namespace ErrorCodes
extern const int CANNOT_DELETE_DIRECTORY = 505; extern const int CANNOT_DELETE_DIRECTORY = 505;
extern const int UNEXPECTED_ERROR_CODE = 506; extern const int UNEXPECTED_ERROR_CODE = 506;
extern const int UNABLE_TO_SKIP_UNUSED_SHARDS = 507; extern const int UNABLE_TO_SKIP_UNUSED_SHARDS = 507;
extern const int UNKNOWN_ACCESS_TYPE = 508;
extern const int INVALID_GRANT = 509;
extern const int KEEPER_EXCEPTION = 999; extern const int KEEPER_EXCEPTION = 999;
extern const int POCO_EXCEPTION = 1000; extern const int POCO_EXCEPTION = 1000;

View File

@ -184,7 +184,7 @@ template <> constexpr bool isDecimalField<DecimalField<Decimal128>>() { return t
class FieldVisitorAccurateEquals : public StaticVisitor<bool> class FieldVisitorAccurateEquals : public StaticVisitor<bool>
{ {
public: public:
bool operator() (const UInt64 & l, const Null & r) const { return cantCompare(l, r); } bool operator() (const UInt64 &, const Null &) const { return false; }
bool operator() (const UInt64 & l, const UInt64 & r) const { return l == r; } bool operator() (const UInt64 & l, const UInt64 & r) const { return l == r; }
bool operator() (const UInt64 & l, const UInt128 & r) const { return cantCompare(l, r); } bool operator() (const UInt64 & l, const UInt128 & r) const { return cantCompare(l, r); }
bool operator() (const UInt64 & l, const Int64 & r) const { return accurate::equalsOp(l, r); } bool operator() (const UInt64 & l, const Int64 & r) const { return accurate::equalsOp(l, r); }
@ -194,7 +194,7 @@ public:
bool operator() (const UInt64 & l, const Tuple & r) const { return cantCompare(l, r); } bool operator() (const UInt64 & l, const Tuple & r) const { return cantCompare(l, r); }
bool operator() (const UInt64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); } bool operator() (const UInt64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
bool operator() (const Int64 & l, const Null & r) const { return cantCompare(l, r); } bool operator() (const Int64 &, const Null &) const { return false; }
bool operator() (const Int64 & l, const UInt64 & r) const { return accurate::equalsOp(l, r); } bool operator() (const Int64 & l, const UInt64 & r) const { return accurate::equalsOp(l, r); }
bool operator() (const Int64 & l, const UInt128 & r) const { return cantCompare(l, r); } bool operator() (const Int64 & l, const UInt128 & r) const { return cantCompare(l, r); }
bool operator() (const Int64 & l, const Int64 & r) const { return l == r; } bool operator() (const Int64 & l, const Int64 & r) const { return l == r; }
@ -204,7 +204,7 @@ public:
bool operator() (const Int64 & l, const Tuple & r) const { return cantCompare(l, r); } bool operator() (const Int64 & l, const Tuple & r) const { return cantCompare(l, r); }
bool operator() (const Int64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); } bool operator() (const Int64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
bool operator() (const Float64 & l, const Null & r) const { return cantCompare(l, r); } bool operator() (const Float64 &, const Null &) const { return false; }
bool operator() (const Float64 & l, const UInt64 & r) const { return accurate::equalsOp(l, r); } bool operator() (const Float64 & l, const UInt64 & r) const { return accurate::equalsOp(l, r); }
bool operator() (const Float64 & l, const UInt128 & r) const { return cantCompare(l, r); } bool operator() (const Float64 & l, const UInt128 & r) const { return cantCompare(l, r); }
bool operator() (const Float64 & l, const Int64 & r) const { return accurate::equalsOp(l, r); } bool operator() (const Float64 & l, const Int64 & r) const { return accurate::equalsOp(l, r); }
@ -227,6 +227,8 @@ public:
return l == r; return l == r;
if constexpr (std::is_same_v<T, UInt128>) if constexpr (std::is_same_v<T, UInt128>)
return stringToUUID(l) == r; return stringToUUID(l) == r;
if constexpr (std::is_same_v<T, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -237,6 +239,8 @@ public:
return l == r; return l == r;
if constexpr (std::is_same_v<T, String>) if constexpr (std::is_same_v<T, String>)
return l == stringToUUID(r); return l == stringToUUID(r);
if constexpr (std::is_same_v<T, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -245,6 +249,8 @@ public:
{ {
if constexpr (std::is_same_v<T, Array>) if constexpr (std::is_same_v<T, Array>)
return l == r; return l == r;
if constexpr (std::is_same_v<T, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -253,6 +259,8 @@ public:
{ {
if constexpr (std::is_same_v<T, Tuple>) if constexpr (std::is_same_v<T, Tuple>)
return l == r; return l == r;
if constexpr (std::is_same_v<T, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -263,6 +271,8 @@ public:
return l == r; return l == r;
if constexpr (std::is_same_v<U, Int64> || std::is_same_v<U, UInt64>) if constexpr (std::is_same_v<U, Int64> || std::is_same_v<U, UInt64>)
return l == DecimalField<Decimal128>(r, 0); return l == DecimalField<Decimal128>(r, 0);
if constexpr (std::is_same_v<U, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -289,11 +299,10 @@ private:
} }
}; };
class FieldVisitorAccurateLess : public StaticVisitor<bool> class FieldVisitorAccurateLess : public StaticVisitor<bool>
{ {
public: public:
bool operator() (const UInt64 & l, const Null & r) const { return cantCompare(l, r); } bool operator() (const UInt64 &, const Null &) const { return false; }
bool operator() (const UInt64 & l, const UInt64 & r) const { return l < r; } bool operator() (const UInt64 & l, const UInt64 & r) const { return l < r; }
bool operator() (const UInt64 & l, const UInt128 & r) const { return cantCompare(l, r); } bool operator() (const UInt64 & l, const UInt128 & r) const { return cantCompare(l, r); }
bool operator() (const UInt64 & l, const Int64 & r) const { return accurate::lessOp(l, r); } bool operator() (const UInt64 & l, const Int64 & r) const { return accurate::lessOp(l, r); }
@ -303,7 +312,7 @@ public:
bool operator() (const UInt64 & l, const Tuple & r) const { return cantCompare(l, r); } bool operator() (const UInt64 & l, const Tuple & r) const { return cantCompare(l, r); }
bool operator() (const UInt64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); } bool operator() (const UInt64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
bool operator() (const Int64 & l, const Null & r) const { return cantCompare(l, r); } bool operator() (const Int64 &, const Null &) const { return false; }
bool operator() (const Int64 & l, const UInt64 & r) const { return accurate::lessOp(l, r); } bool operator() (const Int64 & l, const UInt64 & r) const { return accurate::lessOp(l, r); }
bool operator() (const Int64 & l, const UInt128 & r) const { return cantCompare(l, r); } bool operator() (const Int64 & l, const UInt128 & r) const { return cantCompare(l, r); }
bool operator() (const Int64 & l, const Int64 & r) const { return l < r; } bool operator() (const Int64 & l, const Int64 & r) const { return l < r; }
@ -313,7 +322,7 @@ public:
bool operator() (const Int64 & l, const Tuple & r) const { return cantCompare(l, r); } bool operator() (const Int64 & l, const Tuple & r) const { return cantCompare(l, r); }
bool operator() (const Int64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); } bool operator() (const Int64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
bool operator() (const Float64 & l, const Null & r) const { return cantCompare(l, r); } bool operator() (const Float64 &, const Null &) const { return false; }
bool operator() (const Float64 & l, const UInt64 & r) const { return accurate::lessOp(l, r); } bool operator() (const Float64 & l, const UInt64 & r) const { return accurate::lessOp(l, r); }
bool operator() (const Float64 & l, const UInt128 & r) const { return cantCompare(l, r); } bool operator() (const Float64 & l, const UInt128 & r) const { return cantCompare(l, r); }
bool operator() (const Float64 & l, const Int64 & r) const { return accurate::lessOp(l, r); } bool operator() (const Float64 & l, const Int64 & r) const { return accurate::lessOp(l, r); }
@ -336,6 +345,8 @@ public:
return l < r; return l < r;
if constexpr (std::is_same_v<T, UInt128>) if constexpr (std::is_same_v<T, UInt128>)
return stringToUUID(l) < r; return stringToUUID(l) < r;
if constexpr (std::is_same_v<T, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -346,6 +357,8 @@ public:
return l < r; return l < r;
if constexpr (std::is_same_v<T, String>) if constexpr (std::is_same_v<T, String>)
return l < stringToUUID(r); return l < stringToUUID(r);
if constexpr (std::is_same_v<T, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -354,6 +367,8 @@ public:
{ {
if constexpr (std::is_same_v<T, Array>) if constexpr (std::is_same_v<T, Array>)
return l < r; return l < r;
if constexpr (std::is_same_v<T, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -362,6 +377,8 @@ public:
{ {
if constexpr (std::is_same_v<T, Tuple>) if constexpr (std::is_same_v<T, Tuple>)
return l < r; return l < r;
if constexpr (std::is_same_v<T, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }
@ -370,8 +387,10 @@ public:
{ {
if constexpr (isDecimalField<U>()) if constexpr (isDecimalField<U>())
return l < r; return l < r;
else if constexpr (std::is_same_v<U, Int64> || std::is_same_v<U, UInt64>) if constexpr (std::is_same_v<U, Int64> || std::is_same_v<U, UInt64>)
return l < DecimalField<Decimal128>(r, 0); return l < DecimalField<Decimal128>(r, 0);
if constexpr (std::is_same_v<U, Null>)
return false;
return cantCompare(l, r); return cantCompare(l, r);
} }

View File

@ -265,7 +265,19 @@ static void toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offs
uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0; uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0;
const void * physical_addr = reinterpret_cast<const void *>(uintptr_t(virtual_addr) - virtual_offset); const void * physical_addr = reinterpret_cast<const void *>(uintptr_t(virtual_addr) - virtual_offset);
out << i << ". " << physical_addr << " "; out << i << ". ";
if (object)
{
if (std::filesystem::exists(object->name))
{
auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first;
DB::Dwarf::LocationInfo location;
if (dwarf_it->second.findAddress(uintptr_t(physical_addr), location, DB::Dwarf::LocationInfoMode::FAST))
out << location.file.toString() << ":" << location.line << ": ";
}
}
auto symbol = symbol_index.findSymbol(virtual_addr); auto symbol = symbol_index.findSymbol(virtual_addr);
if (symbol) if (symbol)
@ -276,22 +288,8 @@ static void toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offs
else else
out << "?"; out << "?";
out << " "; out << " @ " << physical_addr;
out << " in " << (object ? object->name : "?");
if (object)
{
if (std::filesystem::exists(object->name))
{
auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first;
DB::Dwarf::LocationInfo location;
if (dwarf_it->second.findAddress(uintptr_t(physical_addr), location, DB::Dwarf::LocationInfoMode::FAST))
out << location.file.toString() << ":" << location.line;
}
out << " in " << object->name;
}
else
out << "?";
callback(out.str()); callback(out.str());
out.str({}); out.str({});

View File

@ -151,10 +151,18 @@ public:
func = std::forward<Function>(func), func = std::forward<Function>(func),
args = std::make_tuple(std::forward<Args>(args)...)] args = std::make_tuple(std::forward<Args>(args)...)]
{ {
try
{ {
/// Thread status holds raw pointer on query context, thus it always must be destroyed
/// before sending signal that permits to join this thread.
DB::ThreadStatus thread_status; DB::ThreadStatus thread_status;
std::apply(func, args); std::apply(func, args);
} }
catch (...)
{
state->set();
throw;
}
state->set(); state->set();
}); });
} }

View File

@ -203,6 +203,9 @@ protected:
/// Set to non-nullptr only if we have enough capabilities. /// Set to non-nullptr only if we have enough capabilities.
std::unique_ptr<TaskStatsInfoGetter> taskstats_getter; std::unique_ptr<TaskStatsInfoGetter> taskstats_getter;
private:
void setupState(const ThreadGroupStatusPtr & thread_group_);
}; };
} }

View File

@ -8,6 +8,11 @@ using namespace DB;
TEST(Common, makeRegexpPatternFromGlobs) TEST(Common, makeRegexpPatternFromGlobs)
{ {
EXPECT_EQ(makeRegexpPatternFromGlobs("?"), "[^/]");
EXPECT_EQ(makeRegexpPatternFromGlobs("*"), "[^/]*");
EXPECT_EQ(makeRegexpPatternFromGlobs("/?"), "/[^/]");
EXPECT_EQ(makeRegexpPatternFromGlobs("/*"), "/[^/]*");
EXPECT_EQ(makeRegexpPatternFromGlobs("*_{{a,b,c,d}}/?.csv"), "[^/]*_\\{(a|b|c|d)\\}/[^/]\\.csv");
EXPECT_EQ(makeRegexpPatternFromGlobs("f{01..09}"), "f(1|2|3|4|5|6|7|8|9)"); EXPECT_EQ(makeRegexpPatternFromGlobs("f{01..09}"), "f(1|2|3|4|5|6|7|8|9)");
EXPECT_EQ(makeRegexpPatternFromGlobs("f{01..9}"), "f(1|2|3|4|5|6|7|8|9)"); EXPECT_EQ(makeRegexpPatternFromGlobs("f{01..9}"), "f(1|2|3|4|5|6|7|8|9)");
EXPECT_EQ(makeRegexpPatternFromGlobs("f{0001..0000009}"), "f(1|2|3|4|5|6|7|8|9)"); EXPECT_EQ(makeRegexpPatternFromGlobs("f{0001..0000009}"), "f(1|2|3|4|5|6|7|8|9)");

View File

@ -7,6 +7,7 @@
#include <Common/PODArray.h> #include <Common/PODArray.h>
#include <Core/Types.h> #include <Core/Types.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Access/User.h>
#include <IO/copyData.h> #include <IO/copyData.h>
#include <IO/LimitReadBuffer.h> #include <IO/LimitReadBuffer.h>
#include <IO/ReadBuffer.h> #include <IO/ReadBuffer.h>

View File

@ -338,6 +338,7 @@ struct Settings : public SettingsCollection<Settings>
M(SettingChar, format_csv_delimiter, ',', "The character to be considered as a delimiter in CSV data. If setting with a string, a string has to have a length of 1.", 0) \ M(SettingChar, format_csv_delimiter, ',', "The character to be considered as a delimiter in CSV data. If setting with a string, a string has to have a length of 1.", 0) \
M(SettingBool, format_csv_allow_single_quotes, 1, "If it is set to true, allow strings in single quotes.", 0) \ M(SettingBool, format_csv_allow_single_quotes, 1, "If it is set to true, allow strings in single quotes.", 0) \
M(SettingBool, format_csv_allow_double_quotes, 1, "If it is set to true, allow strings in double quotes.", 0) \ M(SettingBool, format_csv_allow_double_quotes, 1, "If it is set to true, allow strings in double quotes.", 0) \
M(SettingBool, output_format_csv_crlf_end_of_line, false, "If it is set true, end of line will be \\r\\n instead of \\n.", 0) \
M(SettingBool, input_format_csv_unquoted_null_literal_as_null, false, "Consider unquoted NULL literal as \\N", 0) \ M(SettingBool, input_format_csv_unquoted_null_literal_as_null, false, "Consider unquoted NULL literal as \\N", 0) \
\ \
M(SettingDateTimeInputFormat, date_time_input_format, FormatSettings::DateTimeInputFormat::Basic, "Method to read DateTime from text input formats. Possible values: 'basic' and 'best_effort'.", 0) \ M(SettingDateTimeInputFormat, date_time_input_format, FormatSettings::DateTimeInputFormat::Basic, "Method to read DateTime from text input formats. Possible values: 'basic' and 'best_effort'.", 0) \
@ -388,6 +389,7 @@ struct Settings : public SettingsCollection<Settings>
M(SettingBool, optimize_trivial_count_query, true, "Process trivial 'SELECT count() FROM table' query from metadata.", 0) \ M(SettingBool, optimize_trivial_count_query, true, "Process trivial 'SELECT count() FROM table' query from metadata.", 0) \
M(SettingUInt64, mutations_sync, 0, "Wait for synchronous execution of ALTER TABLE UPDATE/DELETE queries (mutations). 0 - execute asynchronously. 1 - wait current server. 2 - wait all replicas if they exist.", 0) \ M(SettingUInt64, mutations_sync, 0, "Wait for synchronous execution of ALTER TABLE UPDATE/DELETE queries (mutations). 0 - execute asynchronously. 1 - wait current server. 2 - wait all replicas if they exist.", 0) \
M(SettingBool, optimize_if_chain_to_miltiif, false, "Replace if(cond1, then1, if(cond2, ...)) chains to multiIf. Currently it's not beneficial for numeric types.", 0) \ M(SettingBool, optimize_if_chain_to_miltiif, false, "Replace if(cond1, then1, if(cond2, ...)) chains to multiIf. Currently it's not beneficial for numeric types.", 0) \
M(SettingBool, allow_experimental_alter_materialized_view_structure, false, "Allow atomic alter on Materialized views. Work in progress.", 0) \
\ \
/** Obsolete settings that do nothing but left for compatibility reasons. Remove each one after half a year of obsolescence. */ \ /** Obsolete settings that do nothing but left for compatibility reasons. Remove each one after half a year of obsolescence. */ \
\ \

View File

@ -1,28 +0,0 @@
#include <DataStreams/AddingMissedBlockInputStream.h>
#include <Interpreters/addMissingDefaults.h>
namespace DB
{
AddingMissedBlockInputStream::AddingMissedBlockInputStream(
const BlockInputStreamPtr & input_,
const Block & header_,
const ColumnDefaults & column_defaults_,
const Context & context_)
: input(input_), header(header_),
column_defaults(column_defaults_), context(context_)
{
children.emplace_back(input);
}
Block AddingMissedBlockInputStream::readImpl()
{
Block src = children.back()->read();
if (!src)
return src;
return addMissingDefaults(src, header.getNamesAndTypesList(), column_defaults, context);
}
}

View File

@ -17,6 +17,8 @@ namespace DB
namespace ErrorCodes namespace ErrorCodes
{ {
extern const int TOO_SLOW; extern const int TOO_SLOW;
extern const int LOGICAL_ERROR;
extern const int TIMEOUT_EXCEEDED;
} }
static void limitProgressingSpeed(size_t total_progress_size, size_t max_speed_in_seconds, UInt64 total_elapsed_microseconds) static void limitProgressingSpeed(size_t total_progress_size, size_t max_speed_in_seconds, UInt64 total_elapsed_microseconds)
@ -88,4 +90,29 @@ void ExecutionSpeedLimits::throttle(
} }
} }
static bool handleOverflowMode(OverflowMode mode, const String & message, int code)
{
switch (mode)
{
case OverflowMode::THROW:
throw Exception(message, code);
case OverflowMode::BREAK:
return false;
default:
throw Exception("Logical error: unknown overflow mode", ErrorCodes::LOGICAL_ERROR);
}
}
bool ExecutionSpeedLimits::checkTimeLimit(UInt64 elapsed_ns, OverflowMode overflow_mode)
{
if (max_execution_time != 0
&& elapsed_ns > static_cast<UInt64>(max_execution_time.totalMicroseconds()) * 1000)
return handleOverflowMode(overflow_mode,
"Timeout exceeded: elapsed " + toString(static_cast<double>(elapsed_ns) / 1000000000ULL)
+ " seconds, maximum: " + toString(max_execution_time.totalMicroseconds() / 1000000.0),
ErrorCodes::TIMEOUT_EXCEEDED);
return true;
}
} }

View File

@ -2,6 +2,7 @@
#include <Poco/Timespan.h> #include <Poco/Timespan.h>
#include <Core/Types.h> #include <Core/Types.h>
#include <DataStreams/SizeLimits.h>
namespace DB namespace DB
{ {
@ -23,6 +24,8 @@ public:
/// Pause execution in case if speed limits were exceeded. /// Pause execution in case if speed limits were exceeded.
void throttle(size_t read_rows, size_t read_bytes, size_t total_rows_to_read, UInt64 total_elapsed_microseconds); void throttle(size_t read_rows, size_t read_bytes, size_t total_rows_to_read, UInt64 total_elapsed_microseconds);
bool checkTimeLimit(UInt64 elapsed_ns, OverflowMode overflow_mode);
}; };
} }

View File

@ -203,30 +203,9 @@ void IBlockInputStream::updateExtremes(Block & block)
} }
static bool handleOverflowMode(OverflowMode mode, const String & message, int code)
{
switch (mode)
{
case OverflowMode::THROW:
throw Exception(message, code);
case OverflowMode::BREAK:
return false;
default:
throw Exception("Logical error: unknown overflow mode", ErrorCodes::LOGICAL_ERROR);
}
}
bool IBlockInputStream::checkTimeLimit() bool IBlockInputStream::checkTimeLimit()
{ {
if (limits.speed_limits.max_execution_time != 0 return limits.speed_limits.checkTimeLimit(info.total_stopwatch.elapsed(), limits.timeout_overflow_mode);
&& info.total_stopwatch.elapsed() > static_cast<UInt64>(limits.speed_limits.max_execution_time.totalMicroseconds()) * 1000)
return handleOverflowMode(limits.timeout_overflow_mode,
"Timeout exceeded: elapsed " + toString(info.total_stopwatch.elapsedSeconds())
+ " seconds, maximum: " + toString(limits.speed_limits.max_execution_time.totalMicroseconds() / 1000000.0),
ErrorCodes::TIMEOUT_EXCEEDED);
return true;
} }

View File

@ -156,7 +156,7 @@ SummingSortedBlockInputStream::SummingSortedBlockInputStream(
|| endsWith(name, "Key") || endsWith(name, "Key")
|| endsWith(name, "Type")) || endsWith(name, "Type"))
{ {
if (!nested_type.isValueRepresentedByInteger()) if (!nested_type.isValueRepresentedByInteger() && !isStringOrFixedString(nested_type))
break; break;
map_desc.key_col_nums.push_back(*column_num_it); map_desc.key_col_nums.push_back(*column_num_it);

View File

@ -13,7 +13,7 @@ namespace DB
namespace ErrorCodes namespace ErrorCodes
{ {
extern const int LOGICAL_ERROR; extern const int BAD_ARGUMENTS;
} }
@ -70,7 +70,7 @@ public:
{ {
const auto it = value_to_name_map.find(value); const auto it = value_to_name_map.find(value);
if (it == std::end(value_to_name_map)) if (it == std::end(value_to_name_map))
throw Exception{"Unexpected value " + toString(value) + " for type " + getName(), ErrorCodes::LOGICAL_ERROR}; throw Exception{"Unexpected value " + toString(value) + " for type " + getName(), ErrorCodes::BAD_ARGUMENTS};
return it->second; return it->second;
} }
@ -79,7 +79,7 @@ public:
{ {
const auto it = name_to_value_map.find(field_name); const auto it = name_to_value_map.find(field_name);
if (!it) if (!it)
throw Exception{"Unknown element '" + field_name.toString() + "' for type " + getName(), ErrorCodes::LOGICAL_ERROR}; throw Exception{"Unknown element '" + field_name.toString() + "' for type " + getName(), ErrorCodes::BAD_ARGUMENTS};
return it->getMapped(); return it->getMapped();
} }

View File

@ -245,21 +245,20 @@ void DatabaseOnDisk::renameTable(
ASTPtr DatabaseOnDisk::getCreateTableQueryImpl(const Context & context, const String & table_name, bool throw_on_error) const ASTPtr DatabaseOnDisk::getCreateTableQueryImpl(const Context & context, const String & table_name, bool throw_on_error) const
{ {
ASTPtr ast; ASTPtr ast;
bool has_table = tryGetTable(context, table_name) != nullptr;
auto table_metadata_path = getObjectMetadataPath(table_name); auto table_metadata_path = getObjectMetadataPath(table_name);
ast = getCreateQueryFromMetadata(context, table_metadata_path, throw_on_error); try
if (!ast && throw_on_error)
{ {
/// Handle system.* tables for which there are no table.sql files. ast = getCreateQueryFromMetadata(context, table_metadata_path, throw_on_error);
bool has_table = tryGetTable(context, table_name) != nullptr; }
catch (const Exception & e)
auto msg = has_table {
? "There is no CREATE TABLE query for table " if (!has_table && e.code() == ErrorCodes::FILE_DOESNT_EXIST && throw_on_error)
: "There is no metadata file for table "; throw Exception{"Table " + backQuote(table_name) + " doesn't exist",
ErrorCodes::CANNOT_GET_CREATE_TABLE_QUERY};
throw Exception(msg + backQuote(table_name), ErrorCodes::CANNOT_GET_CREATE_TABLE_QUERY); else if (throw_on_error)
throw;
} }
return ast; return ast;
} }

View File

@ -237,7 +237,7 @@ void DatabaseOrdinary::alterTable(
ParserCreateQuery parser; ParserCreateQuery parser;
ASTPtr ast = parseQuery(parser, statement.data(), statement.data() + statement.size(), "in file " + table_metadata_path, 0); ASTPtr ast = parseQuery(parser, statement.data(), statement.data() + statement.size(), "in file " + table_metadata_path, 0);
const auto & ast_create_query = ast->as<ASTCreateQuery &>(); auto & ast_create_query = ast->as<ASTCreateQuery &>();
ASTPtr new_columns = InterpreterCreateQuery::formatColumns(metadata.columns); ASTPtr new_columns = InterpreterCreateQuery::formatColumns(metadata.columns);
ASTPtr new_indices = InterpreterCreateQuery::formatIndices(metadata.indices); ASTPtr new_indices = InterpreterCreateQuery::formatIndices(metadata.indices);
@ -247,6 +247,11 @@ void DatabaseOrdinary::alterTable(
ast_create_query.columns_list->setOrReplace(ast_create_query.columns_list->indices, new_indices); ast_create_query.columns_list->setOrReplace(ast_create_query.columns_list->indices, new_indices);
ast_create_query.columns_list->setOrReplace(ast_create_query.columns_list->constraints, new_constraints); ast_create_query.columns_list->setOrReplace(ast_create_query.columns_list->constraints, new_constraints);
if (metadata.select)
{
ast->replace(ast_create_query.select, metadata.select);
}
ASTStorage & storage_ast = *ast_create_query.storage; ASTStorage & storage_ast = *ast_create_query.storage;
/// ORDER BY may change, but cannot appear, it's required construction /// ORDER BY may change, but cannot appear, it's required construction
if (metadata.order_by_ast && storage_ast.order_by) if (metadata.order_by_ast && storage_ast.order_by)

View File

@ -28,7 +28,6 @@ namespace ErrorCodes
{ {
extern const int NOT_IMPLEMENTED; extern const int NOT_IMPLEMENTED;
extern const int CANNOT_GET_CREATE_TABLE_QUERY; extern const int CANNOT_GET_CREATE_TABLE_QUERY;
extern const int CANNOT_GET_CREATE_TABLE_QUERY;
extern const int CANNOT_GET_CREATE_DICTIONARY_QUERY; extern const int CANNOT_GET_CREATE_DICTIONARY_QUERY;
} }

View File

@ -28,8 +28,22 @@ struct IDictionaryBase : public IExternalLoadable
virtual const std::string & getDatabase() const = 0; virtual const std::string & getDatabase() const = 0;
virtual const std::string & getName() const = 0; virtual const std::string & getName() const = 0;
virtual const std::string & getFullName() const = 0; virtual const std::string & getFullName() const = 0;
const std::string & getLoadableName() const override { return getFullName(); } const std::string & getLoadableName() const override { return getFullName(); }
/// Specifies that no database is used.
/// Sometimes we cannot simply use an empty string for that because an empty string is
/// usually replaced with the current database.
static constexpr char NO_DATABASE_TAG[] = "<no_database>";
std::string_view getDatabaseOrNoDatabaseTag() const
{
const std::string & database = getDatabase();
if (!database.empty())
return database;
return NO_DATABASE_TAG;
}
virtual std::string getTypeName() const = 0; virtual std::string getTypeName() const = 0;
virtual size_t getBytesAllocated() const = 0; virtual size_t getBytesAllocated() const = 0;

View File

@ -244,8 +244,9 @@ namespace
const String & bucket_, const String & bucket_,
Metadata metadata_, Metadata metadata_,
const String & s3_path_, const String & s3_path_,
size_t min_upload_part_size,
size_t buf_size_) size_t buf_size_)
: WriteBufferFromS3(client_ptr_, bucket_, s3_path_, DEFAULT_BLOCK_SIZE, buf_size_) : WriteBufferFromS3(client_ptr_, bucket_, s3_path_, min_upload_part_size, buf_size_)
, metadata(std::move(metadata_)) , metadata(std::move(metadata_))
, s3_path(s3_path_) , s3_path(s3_path_)
{ {
@ -336,12 +337,14 @@ private:
}; };
DiskS3::DiskS3(String name_, std::shared_ptr<Aws::S3::S3Client> client_, String bucket_, String s3_root_path_, String metadata_path_) DiskS3::DiskS3(String name_, std::shared_ptr<Aws::S3::S3Client> client_, String bucket_, String s3_root_path_,
String metadata_path_, size_t min_upload_part_size_)
: name(std::move(name_)) : name(std::move(name_))
, client(std::move(client_)) , client(std::move(client_))
, bucket(std::move(bucket_)) , bucket(std::move(bucket_))
, s3_root_path(std::move(s3_root_path_)) , s3_root_path(std::move(s3_root_path_))
, metadata_path(std::move(metadata_path_)) , metadata_path(std::move(metadata_path_))
, min_upload_part_size(min_upload_part_size_)
{ {
} }
@ -470,7 +473,7 @@ std::unique_ptr<WriteBuffer> DiskS3::writeFile(const String & path, size_t buf_s
LOG_DEBUG(&Logger::get("DiskS3"), "Write to file by path: " << backQuote(metadata_path + path) << " New S3 path: " << s3_path); LOG_DEBUG(&Logger::get("DiskS3"), "Write to file by path: " << backQuote(metadata_path + path) << " New S3 path: " << s3_path);
return std::make_unique<WriteIndirectBufferFromS3>(client, bucket, metadata, s3_path, buf_size); return std::make_unique<WriteIndirectBufferFromS3>(client, bucket, metadata, s3_path, min_upload_part_size, buf_size);
} }
else else
{ {
@ -481,7 +484,7 @@ std::unique_ptr<WriteBuffer> DiskS3::writeFile(const String & path, size_t buf_s
"Append to file by path: " << backQuote(metadata_path + path) << " New S3 path: " << s3_path "Append to file by path: " << backQuote(metadata_path + path) << " New S3 path: " << s3_path
<< " Existing S3 objects: " << metadata.s3_objects_count); << " Existing S3 objects: " << metadata.s3_objects_count);
return std::make_unique<WriteIndirectBufferFromS3>(client, bucket, metadata, s3_path, buf_size); return std::make_unique<WriteIndirectBufferFromS3>(client, bucket, metadata, s3_path, min_upload_part_size, buf_size);
} }
} }
@ -625,7 +628,8 @@ void registerDiskS3(DiskFactory & factory)
String metadata_path = context.getPath() + "disks/" + name + "/"; String metadata_path = context.getPath() + "disks/" + name + "/";
auto s3disk = std::make_shared<DiskS3>(name, client, uri.bucket, uri.key, metadata_path); auto s3disk = std::make_shared<DiskS3>(name, client, uri.bucket, uri.key, metadata_path,
context.getSettingsRef().s3_min_upload_part_size);
/// This code is used only to check access to the corresponding disk. /// This code is used only to check access to the corresponding disk.
checkWriteAccess(s3disk); checkWriteAccess(s3disk);

View File

@ -21,7 +21,8 @@ class DiskS3 : public IDisk
public: public:
friend class DiskS3Reservation; friend class DiskS3Reservation;
DiskS3(String name_, std::shared_ptr<Aws::S3::S3Client> client_, String bucket_, String s3_root_path_, String metadata_path_); DiskS3(String name_, std::shared_ptr<Aws::S3::S3Client> client_, String bucket_, String s3_root_path_,
String metadata_path_, size_t min_upload_part_size_);
const String & getName() const override { return name; } const String & getName() const override { return name; }
@ -80,6 +81,7 @@ private:
const String bucket; const String bucket;
const String s3_root_path; const String s3_root_path;
const String metadata_path; const String metadata_path;
size_t min_upload_part_size;
UInt64 reserved_bytes = 0; UInt64 reserved_bytes = 0;
UInt64 reservation_count = 0; UInt64 reservation_count = 0;

View File

@ -82,6 +82,7 @@ static FormatSettings getOutputFormatSetting(const Settings & settings, const Co
format_settings.csv.delimiter = settings.format_csv_delimiter; format_settings.csv.delimiter = settings.format_csv_delimiter;
format_settings.csv.allow_single_quotes = settings.format_csv_allow_single_quotes; 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.csv.allow_double_quotes = settings.format_csv_allow_double_quotes;
format_settings.csv.crlf_end_of_line = settings.output_format_csv_crlf_end_of_line;
format_settings.pretty.max_rows = settings.output_format_pretty_max_rows; format_settings.pretty.max_rows = settings.output_format_pretty_max_rows;
format_settings.pretty.max_column_pad_width = settings.output_format_pretty_max_column_pad_width; format_settings.pretty.max_column_pad_width = settings.output_format_pretty_max_column_pad_width;
format_settings.pretty.color = settings.output_format_pretty_color; format_settings.pretty.color = settings.output_format_pretty_color;

View File

@ -29,6 +29,7 @@ struct FormatSettings
bool allow_double_quotes = true; bool allow_double_quotes = true;
bool unquoted_null_literal_as_null = false; bool unquoted_null_literal_as_null = false;
bool empty_as_default = false; bool empty_as_default = false;
bool crlf_end_of_line = false;
}; };
CSV csv; CSV csv;

View File

@ -18,6 +18,8 @@
#include <Columns/ColumnString.h> #include <Columns/ColumnString.h>
#include <Columns/ColumnTuple.h> #include <Columns/ColumnTuple.h>
#include <Access/AccessFlags.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Interpreters/ExternalDictionariesLoader.h> #include <Interpreters/ExternalDictionariesLoader.h>
@ -48,7 +50,6 @@ namespace ErrorCodes
extern const int TYPE_MISMATCH; extern const int TYPE_MISMATCH;
extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_COLUMN;
extern const int BAD_ARGUMENTS; extern const int BAD_ARGUMENTS;
extern const int DICTIONARY_ACCESS_DENIED;
} }
/** Functions that use plug-ins (external) dictionaries_loader. /** Functions that use plug-ins (external) dictionaries_loader.
@ -126,12 +127,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>()); auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get(); const auto dict_ptr = dict.get();
context.checkAccess(AccessType::dictHas, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
if (!executeDispatchSimple<FlatDictionary>(block, arguments, result, dict_ptr) && if (!executeDispatchSimple<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatchSimple<HashedDictionary>(block, arguments, result, dict_ptr) && !executeDispatchSimple<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -301,12 +297,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>()); auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get(); const auto dict_ptr = dict.get();
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) && if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) && !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -487,12 +478,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>()); auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get(); const auto dict_ptr = dict.get();
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) && if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) && !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -829,12 +815,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>()); auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get(); const auto dict_ptr = dict.get();
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!context.hasDictionaryAccessRights(dict_ptr->getName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) && if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) && !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -1093,12 +1074,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>()); auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get(); const auto dict_ptr = dict.get();
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!context.hasDictionaryAccessRights(dict_ptr->getName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) && if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) && !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -1670,12 +1646,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>()); auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get(); const auto dict_ptr = dict.get();
context.checkAccess(AccessType::dictGetHierarchy, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!context.hasDictionaryAccessRights(dict_ptr->getName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) && if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) && !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
@ -1839,12 +1810,7 @@ private:
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>()); auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
const auto dict_ptr = dict.get(); const auto dict_ptr = dict.get();
context.checkAccess(AccessType::dictIsIn, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
if (!context.hasDictionaryAccessRights(dict_ptr->getName()))
{
throw Exception{"For function " + getName() + ", cannot access dictionary "
+ dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
}
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr)
&& !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) && !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr)

View File

@ -13,6 +13,7 @@
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <IO/WriteBufferFromArena.h> #include <IO/WriteBufferFromArena.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
#include <Access/AccessFlags.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <mutex> #include <mutex>
@ -28,7 +29,6 @@ namespace ErrorCodes
extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_COLUMN;
extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int FUNCTION_NOT_ALLOWED;
} }
class FunctionAddressToLine : public IFunction class FunctionAddressToLine : public IFunction
@ -37,9 +37,7 @@ public:
static constexpr auto name = "addressToLine"; static constexpr auto name = "addressToLine";
static FunctionPtr create(const Context & context) static FunctionPtr create(const Context & context)
{ {
if (!context.getSettingsRef().allow_introspection_functions) context.checkAccess(AccessType::addressToLine);
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
return std::make_shared<FunctionAddressToLine>(); return std::make_shared<FunctionAddressToLine>();
} }

View File

@ -7,6 +7,7 @@
#include <Functions/IFunctionImpl.h> #include <Functions/IFunctionImpl.h>
#include <Functions/FunctionHelpers.h> #include <Functions/FunctionHelpers.h>
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Access/AccessFlags.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
@ -19,7 +20,6 @@ namespace ErrorCodes
extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_COLUMN;
extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int FUNCTION_NOT_ALLOWED;
} }
class FunctionAddressToSymbol : public IFunction class FunctionAddressToSymbol : public IFunction
@ -28,8 +28,7 @@ public:
static constexpr auto name = "addressToSymbol"; static constexpr auto name = "addressToSymbol";
static FunctionPtr create(const Context & context) static FunctionPtr create(const Context & context)
{ {
if (!context.getSettingsRef().allow_introspection_functions) context.checkAccess(AccessType::addressToSymbol);
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
return std::make_shared<FunctionAddressToSymbol>(); return std::make_shared<FunctionAddressToSymbol>();
} }

View File

@ -6,6 +6,7 @@
#include <Functions/FunctionHelpers.h> #include <Functions/FunctionHelpers.h>
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
#include <Access/AccessFlags.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
@ -17,7 +18,6 @@ namespace ErrorCodes
extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_COLUMN;
extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int FUNCTION_NOT_ALLOWED;
} }
class FunctionDemangle : public IFunction class FunctionDemangle : public IFunction
@ -26,9 +26,7 @@ public:
static constexpr auto name = "demangle"; static constexpr auto name = "demangle";
static FunctionPtr create(const Context & context) static FunctionPtr create(const Context & context)
{ {
if (!context.getSettingsRef().allow_introspection_functions) context.checkAccess(AccessType::demangle);
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
return std::make_shared<FunctionDemangle>(); return std::make_shared<FunctionDemangle>();
} }

View File

@ -4,14 +4,22 @@
# include <DataTypes/DataTypesNumber.h> # include <DataTypes/DataTypesNumber.h>
# include <Functions/FunctionFactory.h> # include <Functions/FunctionFactory.h>
# include <Functions/IFunction.h> # include <Functions/IFunction.h>
# include <IO/WriteHelpers.h>
# include <Common/typeid_cast.h> # include <Common/typeid_cast.h>
# include <ext/range.h> # include <ext/range.h>
# include <h3api.h> # include <h3api.h>
# include <constants.h>
namespace DB namespace DB
{ {
namespace ErrorCodes
{
extern const int ARGUMENT_OUT_OF_BOUND;
}
// Average metric edge length of H3 hexagon. The edge length `e` for given resolution `res` can // Average metric edge length of H3 hexagon. The edge length `e` for given resolution `res` can
// be used for converting metric search radius `radius` to hexagon search ring size `k` that is // be used for converting metric search radius `radius` to hexagon search ring size `k` that is
// used by `H3kRing` function. For small enough search area simple flat approximation can be used, // used by `H3kRing` function. For small enough search area simple flat approximation can be used,
@ -50,7 +58,10 @@ public:
for (const auto row : ext::range(0, input_rows_count)) for (const auto row : ext::range(0, input_rows_count))
{ {
const int resolution = col_hindex->getUInt(row); const UInt64 resolution = col_hindex->getUInt(row);
if (resolution > MAX_H3_RES)
throw Exception("The argument 'resolution' (" + toString(resolution) + ") of function " + getName()
+ " is out of bounds because the maximum resolution in H3 library is " + toString(MAX_H3_RES), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
Float64 res = edgeLengthM(resolution); Float64 res = edgeLengthM(resolution);

View File

@ -0,0 +1,66 @@
#include "config_functions.h"
#if USE_H3
# include <Columns/ColumnsNumber.h>
# include <DataTypes/DataTypesNumber.h>
# include <Functions/FunctionFactory.h>
# include <Functions/IFunction.h>
# include <Common/typeid_cast.h>
# include <ext/range.h>
# include <h3api.h>
namespace DB
{
class FunctionH3GetBaseCell : public IFunction
{
public:
static constexpr auto name = "h3GetBaseCell";
static FunctionPtr create(const Context &) { return std::make_shared<FunctionH3GetBaseCell>(); }
std::string getName() const override { return name; }
size_t getNumberOfArguments() const override { return 1; }
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
auto arg = arguments[0].get();
if (!WhichDataType(arg).isUInt64())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(1) + " of function " + getName() + ". Must be UInt64",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return std::make_shared<DataTypeUInt8>();
}
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
{
const auto col_hindex = block.getByPosition(arguments[0]).column.get();
auto dst = ColumnVector<UInt8>::create();
auto & dst_data = dst->getData();
dst_data.resize(input_rows_count);
for (const auto row : ext::range(0, input_rows_count))
{
const UInt64 hindex = col_hindex->getUInt(row);
UInt8 res = h3GetBaseCell(hindex);
dst_data[row] = res;
}
block.getByPosition(result).column = std::move(dst);
}
};
void registerFunctionH3GetBaseCell(FunctionFactory & factory)
{
factory.registerFunction<FunctionH3GetBaseCell>();
}
}
#endif

View File

@ -0,0 +1,66 @@
#include "config_functions.h"
#if USE_H3
# include <Columns/ColumnsNumber.h>
# include <DataTypes/DataTypesNumber.h>
# include <Functions/FunctionFactory.h>
# include <Functions/IFunction.h>
# include <Common/typeid_cast.h>
# include <ext/range.h>
# include <h3api.h>
namespace DB
{
class FunctionH3HexAreaM2 : public IFunction
{
public:
static constexpr auto name = "h3HexAreaM2";
static FunctionPtr create(const Context &) { return std::make_shared<FunctionH3HexAreaM2>(); }
std::string getName() const override { return name; }
size_t getNumberOfArguments() const override { return 1; }
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
auto arg = arguments[0].get();
if (!WhichDataType(arg).isUInt8())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(1) + " of function " + getName() + ". Must be UInt8",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return std::make_shared<DataTypeFloat64>();
}
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
{
const auto col_hindex = block.getByPosition(arguments[0]).column.get();
auto dst = ColumnVector<Float64>::create();
auto & dst_data = dst->getData();
dst_data.resize(input_rows_count);
for (const auto row : ext::range(0, input_rows_count))
{
const int resolution = col_hindex->getUInt(row);
Float64 res = hexAreaM2(resolution);
dst_data[row] = res;
}
block.getByPosition(result).column = std::move(dst);
}
};
void registerFunctionH3HexAreaM2(FunctionFactory & factory)
{
factory.registerFunction<FunctionH3HexAreaM2>();
}
}
#endif

View File

@ -0,0 +1,74 @@
#include "config_functions.h"
#if USE_H3
# include <Columns/ColumnsNumber.h>
# include <DataTypes/DataTypesNumber.h>
# include <Functions/FunctionFactory.h>
# include <Functions/IFunction.h>
# include <Common/typeid_cast.h>
# include <ext/range.h>
# include <h3api.h>
namespace DB
{
class FunctionH3IndexesAreNeighbors : public IFunction
{
public:
static constexpr auto name = "h3IndexesAreNeighbors";
static FunctionPtr create(const Context &) { return std::make_shared<FunctionH3IndexesAreNeighbors>(); }
std::string getName() const override { return name; }
size_t getNumberOfArguments() const override { return 2; }
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
auto arg = arguments[0].get();
if (!WhichDataType(arg).isUInt64())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(1) + " of function " + getName() + ". Must be UInt64",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
arg = arguments[1].get();
if (!WhichDataType(arg).isUInt64())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(2) + " of function " + getName() + ". Must be UInt64",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return std::make_shared<DataTypeUInt8>();
}
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
{
const auto col_hindex_origin = block.getByPosition(arguments[0]).column.get();
const auto col_hindex_dest = block.getByPosition(arguments[1]).column.get();
auto dst = ColumnVector<UInt8>::create();
auto & dst_data = dst->getData();
dst_data.resize(input_rows_count);
for (const auto row : ext::range(0, input_rows_count))
{
const UInt64 hindex_origin = col_hindex_origin->getUInt(row);
const UInt64 hindex_dest = col_hindex_dest->getUInt(row);
UInt8 res = h3IndexesAreNeighbors(hindex_origin, hindex_dest);
dst_data[row] = res;
}
block.getByPosition(result).column = std::move(dst);
}
};
void registerFunctionH3IndexesAreNeighbors(FunctionFactory & factory)
{
factory.registerFunction<FunctionH3IndexesAreNeighbors>();
}
}
#endif

View File

@ -0,0 +1,91 @@
#include "config_functions.h"
#if USE_H3
# include <Columns/ColumnArray.h>
# include <Columns/ColumnsNumber.h>
# include <DataTypes/DataTypeArray.h>
# include <DataTypes/DataTypesNumber.h>
# include <Functions/FunctionFactory.h>
# include <Functions/IFunction.h>
# include <Common/typeid_cast.h>
# include <ext/range.h>
# include <h3api.h>
namespace DB
{
class FunctionH3ToChildren : public IFunction
{
public:
static constexpr auto name = "h3ToChildren";
static FunctionPtr create(const Context &) { return std::make_shared<FunctionH3ToChildren>(); }
std::string getName() const override { return name; }
size_t getNumberOfArguments() const override { return 2; }
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
auto arg = arguments[0].get();
if (!WhichDataType(arg).isUInt64())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(1) + " of function " + getName() + ". Must be UInt64",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
arg = arguments[1].get();
if (!WhichDataType(arg).isUInt8())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(2) + " of function " + getName() + ". Must be UInt8",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeUInt64>());
}
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
{
const auto col_hindex = block.getByPosition(arguments[0]).column.get();
const auto col_resolution = block.getByPosition(arguments[1]).column.get();
auto dst = ColumnArray::create(ColumnUInt64::create());
auto & dst_data = dst->getData();
auto & dst_offsets = dst->getOffsets();
dst_offsets.resize(input_rows_count);
auto current_offset = 0;
std::vector<H3Index> hindex_vec;
for (const auto row : ext::range(0, input_rows_count))
{
const UInt64 parent_hindex = col_hindex->getUInt(row);
const UInt8 child_resolution = col_resolution->getUInt(row);
const auto vec_size = maxH3ToChildrenSize(parent_hindex, child_resolution);
hindex_vec.resize(vec_size);
h3ToChildren(parent_hindex, child_resolution, hindex_vec.data());
dst_data.reserve(dst_data.size() + vec_size);
for (auto hindex : hindex_vec)
{
if (hindex != 0)
{
++current_offset;
dst_data.insert(hindex);
}
}
dst_offsets[row] = current_offset;
}
block.getByPosition(result).column = std::move(dst);
}
};
void registerFunctionH3ToChildren(FunctionFactory & factory)
{
factory.registerFunction<FunctionH3ToChildren>();
}
}
#endif

View File

@ -0,0 +1,74 @@
#include "config_functions.h"
#if USE_H3
# include <Columns/ColumnsNumber.h>
# include <DataTypes/DataTypesNumber.h>
# include <Functions/FunctionFactory.h>
# include <Functions/IFunction.h>
# include <Common/typeid_cast.h>
# include <ext/range.h>
# include <h3api.h>
namespace DB
{
class FunctionH3ToParent : public IFunction
{
public:
static constexpr auto name = "h3ToParent";
static FunctionPtr create(const Context &) { return std::make_shared<FunctionH3ToParent>(); }
std::string getName() const override { return name; }
size_t getNumberOfArguments() const override { return 2; }
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
auto arg = arguments[0].get();
if (!WhichDataType(arg).isUInt64())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(1) + " of function " + getName() + ". Must be UInt64",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
arg = arguments[1].get();
if (!WhichDataType(arg).isUInt8())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(2) + " of function " + getName() + ". Must be UInt8",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return std::make_shared<DataTypeUInt64>();
}
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
{
const auto col_hindex = block.getByPosition(arguments[0]).column.get();
const auto col_resolution = block.getByPosition(arguments[1]).column.get();
auto dst = ColumnVector<UInt64>::create();
auto & dst_data = dst->getData();
dst_data.resize(input_rows_count);
for (const auto row : ext::range(0, input_rows_count))
{
const UInt64 hindex = col_hindex->getUInt(row);
const UInt8 resolution = col_resolution->getUInt(row);
UInt64 res = h3ToParent(hindex, resolution);
dst_data[row] = res;
}
block.getByPosition(result).column = std::move(dst);
}
};
void registerFunctionH3ToParent(FunctionFactory & factory)
{
factory.registerFunction<FunctionH3ToParent>();
}
}
#endif

View File

@ -0,0 +1,82 @@
#include "config_functions.h"
#if USE_H3
# include <Columns/ColumnString.h>
# include <DataTypes/DataTypeString.h>
# include <Functions/FunctionFactory.h>
# include <Functions/IFunction.h>
# include <Common/typeid_cast.h>
# include <h3api.h>
# define H3_INDEX_STRING_LENGTH 17 // includes \0 terminator
namespace DB
{
class FunctionH3ToString : public IFunction
{
public:
static constexpr auto name = "h3ToString";
static FunctionPtr create(const Context &) { return std::make_shared<FunctionH3ToString>(); }
std::string getName() const override { return name; }
size_t getNumberOfArguments() const override { return 1; }
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
auto arg = arguments[0].get();
if (!WhichDataType(arg).isUInt64())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(1) + " of function " + getName() + ". Must be UInt64",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return std::make_shared<DataTypeString>();
}
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
{
const auto col_hindex = block.getByPosition(arguments[0]).column.get();
auto col_res = ColumnString::create();
auto & vec_res = col_res->getChars();
auto & vec_offsets = col_res->getOffsets();
vec_offsets.resize(input_rows_count);
vec_res.resize_fill(input_rows_count * H3_INDEX_STRING_LENGTH, '\0');
char * begin = reinterpret_cast<char *>(vec_res.data());
char * pos = begin;
for (size_t i = 0; i < input_rows_count; ++i)
{
const UInt64 hindex = col_hindex->getUInt(i);
if (!h3IsValid(hindex))
{
throw Exception("Invalid H3 index: " + std::to_string(hindex), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
}
h3ToString(hindex, pos, H3_INDEX_STRING_LENGTH);
// move to end of the index
while (*pos != '\0')
{
pos++;
}
vec_offsets[i] = ++pos - begin;
}
vec_res.resize(pos - begin);
block.getByPosition(result).column = std::move(col_res);
}
};
void registerFunctionH3ToString(FunctionFactory & factory)
{
factory.registerFunction<FunctionH3ToString>();
}
}
#endif

View File

@ -19,6 +19,13 @@ void registerFunctionH3EdgeLengthM(FunctionFactory &);
void registerFunctionH3GetResolution(FunctionFactory &); void registerFunctionH3GetResolution(FunctionFactory &);
void registerFunctionH3IsValid(FunctionFactory &); void registerFunctionH3IsValid(FunctionFactory &);
void registerFunctionH3KRing(FunctionFactory &); void registerFunctionH3KRing(FunctionFactory &);
void registerFunctionH3GetBaseCell(FunctionFactory &);
void registerFunctionH3ToParent(FunctionFactory &);
void registerFunctionH3ToChildren(FunctionFactory &);
void registerFunctionH3IndexesAreNeighbors(FunctionFactory &);
void registerFunctionStringToH3(FunctionFactory &);
void registerFunctionH3ToString(FunctionFactory &);
void registerFunctionH3HexAreaM2(FunctionFactory &);
#endif #endif
@ -38,6 +45,13 @@ void registerFunctionsGeo(FunctionFactory & factory)
registerFunctionH3GetResolution(factory); registerFunctionH3GetResolution(factory);
registerFunctionH3IsValid(factory); registerFunctionH3IsValid(factory);
registerFunctionH3KRing(factory); registerFunctionH3KRing(factory);
registerFunctionH3GetBaseCell(factory);
registerFunctionH3ToParent(factory);
registerFunctionH3ToChildren(factory);
registerFunctionH3IndexesAreNeighbors(factory);
registerFunctionStringToH3(factory);
registerFunctionH3ToString(factory);
registerFunctionH3HexAreaM2(factory);
#endif #endif
} }

View File

@ -0,0 +1,97 @@
#include "config_functions.h"
#if USE_H3
# include <Functions/GatherUtils/GatherUtils.h>
# include <Functions/GatherUtils/Sources.h>
# include <DataTypes/DataTypeString.h>
# include <DataTypes/DataTypesNumber.h>
# include <Columns/ColumnString.h>
# include <Functions/FunctionFactory.h>
# include <Functions/IFunction.h>
# include <Common/typeid_cast.h>
# include <h3api.h>
namespace DB
{
using namespace GatherUtils;
class FunctionStringToH3 : public IFunction
{
public:
static constexpr auto name = "stringToH3";
static FunctionPtr create(const Context &) { return std::make_shared<FunctionStringToH3>(); }
std::string getName() const override { return name; }
size_t getNumberOfArguments() const override { return 1; }
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
auto arg = arguments[0].get();
if (!WhichDataType(arg).isStringOrFixedString())
throw Exception(
"Illegal type " + arg->getName() + " of argument " + std::to_string(1) + " of function " + getName() + ". Must be String or FixedString",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return std::make_shared<DataTypeUInt64>();
}
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
{
const auto col_hindex = block.getByPosition(arguments[0]).column.get();
auto dst = ColumnVector<UInt64>::create();
auto & dst_data = dst->getData();
dst_data.resize(input_rows_count);
if (auto * h3index = checkAndGetColumn<ColumnString>(col_hindex))
execute<StringSource>(StringSource(*h3index), dst_data);
else if (auto * h3index_fixed = checkAndGetColumn<ColumnFixedString>(col_hindex))
execute<FixedStringSource>(FixedStringSource(*h3index_fixed), dst_data);
else if (const ColumnConst * h3index_const = checkAndGetColumnConst<ColumnString>(col_hindex))
execute<ConstSource<StringSource>>(ConstSource<StringSource>(*h3index_const), dst_data);
else if (const ColumnConst * h3index_const_fixed = checkAndGetColumnConst<ColumnFixedString>(col_hindex))
execute<ConstSource<FixedStringSource>>(ConstSource<FixedStringSource>(*h3index_const_fixed), dst_data);
else
throw Exception("Illegal column as argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN);
block.getByPosition(result).column = std::move(dst);
}
private:
template <typename H3IndexSource>
static void execute(H3IndexSource h3index_source, PaddedPODArray<UInt64> & res_data)
{
size_t row_num = 0;
while (!h3index_source.isEnd())
{
auto h3index = h3index_source.getWhole();
// covert to std::string and get the c_str to have the delimiting \0 at the end.
auto h3index_str = StringRef(h3index.data, h3index.size).toString();
res_data[row_num] = stringToH3(h3index_str.c_str());
if (res_data[row_num] == 0)
{
throw Exception("Invalid H3 index: " + h3index_str, ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
}
h3index_source.next();
++row_num;
}
}
};
void registerFunctionStringToH3(FunctionFactory & factory)
{
factory.registerFunction<FunctionStringToH3>();
}
}
#endif

View File

@ -30,29 +30,23 @@ inline void forEachTable(Context & context, F && f)
void ActionLocksManager::add(StorageActionBlockType action_type) void ActionLocksManager::add(StorageActionBlockType action_type)
{ {
forEachTable(global_context, [&] (const StoragePtr & table) forEachTable(global_context, [&](const StoragePtr & table) { add(table, action_type); });
{
ActionLock action_lock = table->getActionLock(action_type);
if (!action_lock.expired())
{
std::lock_guard lock(mutex);
storage_locks[table.get()][action_type] = std::move(action_lock);
}
});
} }
void ActionLocksManager::add(const String & database_name, const String & table_name, StorageActionBlockType action_type) void ActionLocksManager::add(const String & database_name, const String & table_name, StorageActionBlockType action_type)
{ {
if (auto table = global_context.tryGetTable(database_name, table_name)) if (auto table = global_context.tryGetTable(database_name, table_name))
{ add(table, action_type);
ActionLock action_lock = table->getActionLock(action_type); }
if (!action_lock.expired()) void ActionLocksManager::add(const StoragePtr & table, StorageActionBlockType action_type)
{ {
std::lock_guard lock(mutex); ActionLock action_lock = table->getActionLock(action_type);
storage_locks[table.get()][action_type] = std::move(action_lock);
} if (!action_lock.expired())
{
std::lock_guard lock(mutex);
storage_locks[table.get()][action_type] = std::move(action_lock);
} }
} }
@ -67,12 +61,15 @@ void ActionLocksManager::remove(StorageActionBlockType action_type)
void ActionLocksManager::remove(const String & database_name, const String & table_name, StorageActionBlockType action_type) void ActionLocksManager::remove(const String & database_name, const String & table_name, StorageActionBlockType action_type)
{ {
if (auto table = global_context.tryGetTable(database_name, table_name)) if (auto table = global_context.tryGetTable(database_name, table_name))
{ remove(table, action_type);
std::lock_guard lock(mutex); }
if (storage_locks.count(table.get())) void ActionLocksManager::remove(const StoragePtr & table, StorageActionBlockType action_type)
storage_locks[table.get()].erase(action_type); {
} std::lock_guard lock(mutex);
if (storage_locks.count(table.get()))
storage_locks[table.get()].erase(action_type);
} }
void ActionLocksManager::cleanExpired() void ActionLocksManager::cleanExpired()

View File

@ -24,11 +24,13 @@ public:
void add(StorageActionBlockType action_type); void add(StorageActionBlockType action_type);
/// Add new lock for a table if it has not been already added /// Add new lock for a table if it has not been already added
void add(const String & database_name, const String & table_name, StorageActionBlockType action_type); void add(const String & database_name, const String & table_name, StorageActionBlockType action_type);
void add(const StoragePtr & table, StorageActionBlockType action_type);
/// Remove locks for all tables /// Remove locks for all tables
void remove(StorageActionBlockType action_type); void remove(StorageActionBlockType action_type);
/// Removes a lock for a table if it exists /// Removes a lock for a table if it exists
void remove(const String & database_name, const String & table_name, StorageActionBlockType action_type); void remove(const String & database_name, const String & table_name, StorageActionBlockType action_type);
void remove(const StoragePtr & table, StorageActionBlockType action_type);
/// Removes all locks of non-existing tables /// Removes all locks of non-existing tables
void cleanExpired(); void cleanExpired();

View File

@ -330,7 +330,7 @@ void ActionsMatcher::visit(const ASTFunction & node, const ASTPtr & ast, Data &
/// Let's find the type of the first argument (then getActionsImpl will be called again and will not affect anything). /// Let's find the type of the first argument (then getActionsImpl will be called again and will not affect anything).
visit(node.arguments->children.at(0), data); visit(node.arguments->children.at(0), data);
if ((prepared_set = makeSet(node, data, data.no_subqueries))) if (!data.no_makeset && (prepared_set = makeSet(node, data, data.no_subqueries)))
{ {
/// Transform tuple or subquery into a set. /// Transform tuple or subquery into a set.
} }

View File

@ -72,6 +72,7 @@ public:
PreparedSets & prepared_sets; PreparedSets & prepared_sets;
SubqueriesForSets & subqueries_for_sets; SubqueriesForSets & subqueries_for_sets;
bool no_subqueries; bool no_subqueries;
bool no_makeset;
bool only_consts; bool only_consts;
bool no_storage_or_local; bool no_storage_or_local;
size_t visit_depth; size_t visit_depth;
@ -80,7 +81,7 @@ public:
Data(const Context & context_, SizeLimits set_size_limit_, size_t subquery_depth_, Data(const Context & context_, SizeLimits set_size_limit_, size_t subquery_depth_,
const NamesAndTypesList & source_columns_, const ExpressionActionsPtr & actions, const NamesAndTypesList & source_columns_, const ExpressionActionsPtr & actions,
PreparedSets & prepared_sets_, SubqueriesForSets & subqueries_for_sets_, PreparedSets & prepared_sets_, SubqueriesForSets & subqueries_for_sets_,
bool no_subqueries_, bool only_consts_, bool no_storage_or_local_) bool no_subqueries_, bool no_makeset_, bool only_consts_, bool no_storage_or_local_)
: context(context_), : context(context_),
set_size_limit(set_size_limit_), set_size_limit(set_size_limit_),
subquery_depth(subquery_depth_), subquery_depth(subquery_depth_),
@ -88,6 +89,7 @@ public:
prepared_sets(prepared_sets_), prepared_sets(prepared_sets_),
subqueries_for_sets(subqueries_for_sets_), subqueries_for_sets(subqueries_for_sets_),
no_subqueries(no_subqueries_), no_subqueries(no_subqueries_),
no_makeset(no_makeset_),
only_consts(only_consts_), only_consts(only_consts_),
no_storage_or_local(no_storage_or_local_), no_storage_or_local(no_storage_or_local_),
visit_depth(0), visit_depth(0),

View File

@ -27,11 +27,12 @@
#include <Interpreters/ActionLocksManager.h> #include <Interpreters/ActionLocksManager.h>
#include <Core/Settings.h> #include <Core/Settings.h>
#include <Access/AccessControlManager.h> #include <Access/AccessControlManager.h>
#include <Access/User.h>
#include <Access/SettingsConstraints.h> #include <Access/SettingsConstraints.h>
#include <Access/QuotaContext.h> #include <Access/QuotaContext.h>
#include <Access/RowPolicyContext.h> #include <Access/RowPolicyContext.h>
#include <Access/AccessRightsContext.h>
#include <Interpreters/ExpressionJIT.h> #include <Interpreters/ExpressionJIT.h>
#include <Interpreters/UsersManager.h>
#include <Dictionaries/Embedded/GeoDictionariesLoader.h> #include <Dictionaries/Embedded/GeoDictionariesLoader.h>
#include <Interpreters/EmbeddedDictionaries.h> #include <Interpreters/EmbeddedDictionaries.h>
#include <Interpreters/ExternalDictionariesLoader.h> #include <Interpreters/ExternalDictionariesLoader.h>
@ -95,8 +96,7 @@ namespace ErrorCodes
extern const int LOGICAL_ERROR; extern const int LOGICAL_ERROR;
extern const int SCALAR_ALREADY_EXISTS; extern const int SCALAR_ALREADY_EXISTS;
extern const int UNKNOWN_SCALAR; extern const int UNKNOWN_SCALAR;
extern const int NOT_ENOUGH_PRIVILEGES; extern const int ACCESS_DENIED;
extern const int UNKNOWN_POLICY;
} }
@ -140,7 +140,6 @@ struct ContextShared
String default_profile_name; /// Default profile name used for default values. String default_profile_name; /// Default profile name used for default values.
String system_profile_name; /// Profile used by system processes String system_profile_name; /// Profile used by system processes
AccessControlManager access_control_manager; AccessControlManager access_control_manager;
std::unique_ptr<UsersManager> users_manager; /// Known users.
mutable UncompressedCachePtr uncompressed_cache; /// The cache of decompressed blocks. mutable UncompressedCachePtr uncompressed_cache; /// The cache of decompressed blocks.
mutable MarkCachePtr mark_cache; /// Cache of marks in compressed files. mutable MarkCachePtr mark_cache; /// Cache of marks in compressed files.
ProcessList process_list; /// Executing queries at the moment. ProcessList process_list; /// Executing queries at the moment.
@ -234,8 +233,6 @@ struct ContextShared
std::cerr.flush(); std::cerr.flush();
std::terminate(); std::terminate();
} }
initialize();
} }
@ -318,12 +315,6 @@ struct ContextShared
trace_collector = std::make_unique<TraceCollector>(trace_log); trace_collector = std::make_unique<TraceCollector>(trace_log);
} }
private:
void initialize()
{
users_manager = std::make_unique<UsersManager>();
}
}; };
@ -337,6 +328,7 @@ Context Context::createGlobal()
Context res; Context res;
res.quota = std::make_shared<QuotaContext>(); res.quota = std::make_shared<QuotaContext>();
res.row_policy = std::make_shared<RowPolicyContext>(); res.row_policy = std::make_shared<RowPolicyContext>();
res.access_rights = std::make_shared<AccessRightsContext>();
res.shared = std::make_shared<ContextShared>(); res.shared = std::make_shared<ContextShared>();
return res; return res;
} }
@ -643,26 +635,27 @@ const AccessControlManager & Context::getAccessControlManager() const
return shared->access_control_manager; return shared->access_control_manager;
} }
void Context::checkQuotaManagementIsAllowed() template <typename... Args>
void Context::checkAccessImpl(const Args &... args) const
{ {
if (!is_quota_management_allowed) getAccessRights()->check(args...);
throw Exception(
"User " + client_info.current_user + " doesn't have enough privileges to manage quotas", ErrorCodes::NOT_ENOUGH_PRIVILEGES);
} }
void Context::checkRowPolicyManagementIsAllowed() void Context::checkAccess(const AccessFlags & access) const { return checkAccessImpl(access); }
{ void Context::checkAccess(const AccessFlags & access, const std::string_view & database) const { return checkAccessImpl(access, database); }
if (!is_row_policy_management_allowed) void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkAccessImpl(access, database, table); }
throw Exception( void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkAccessImpl(access, database, table, column); }
"User " + client_info.current_user + " doesn't have enough privileges to manage row policies", ErrorCodes::NOT_ENOUGH_PRIVILEGES); void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkAccessImpl(access, database, table, columns); }
} void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkAccessImpl(access, database, table, columns); }
void Context::checkAccess(const AccessRightsElement & access) const { return checkAccessImpl(access); }
void Context::checkAccess(const AccessRightsElements & access) const { return checkAccessImpl(access); }
void Context::setUsersConfig(const ConfigurationPtr & config) void Context::setUsersConfig(const ConfigurationPtr & config)
{ {
auto lock = getLock(); auto lock = getLock();
shared->users_config = config; shared->users_config = config;
shared->access_control_manager.loadFromConfig(*shared->users_config); shared->access_control_manager.loadFromConfig(*shared->users_config);
shared->users_manager->loadFromConfig(*shared->users_config);
} }
ConfigurationPtr Context::getUsersConfig() ConfigurationPtr Context::getUsersConfig()
@ -674,8 +667,6 @@ ConfigurationPtr Context::getUsersConfig()
void Context::calculateUserSettings() void Context::calculateUserSettings()
{ {
auto lock = getLock(); auto lock = getLock();
auto user = getUser(client_info.current_user);
String profile = user->profile; String profile = user->profile;
/// 1) Set default settings (hardcoded values) /// 1) Set default settings (hardcoded values)
@ -691,14 +682,14 @@ void Context::calculateUserSettings()
/// 3) Apply settings from current user /// 3) Apply settings from current user
setProfile(profile); setProfile(profile);
quota = getAccessControlManager().createQuotaContext(
client_info.current_user, client_info.current_address.host(), client_info.quota_key);
is_quota_management_allowed = user->is_quota_management_allowed;
row_policy = getAccessControlManager().getRowPolicyContext(client_info.current_user);
is_row_policy_management_allowed = user->is_row_policy_management_allowed;
} }
void Context::calculateAccessRights()
{
auto lock = getLock();
if (user)
std::atomic_store(&access_rights, getAccessControlManager().getAccessRightsContext(client_info, user->access, settings, current_database));
}
void Context::setProfile(const String & profile) void Context::setProfile(const String & profile)
{ {
@ -710,17 +701,15 @@ void Context::setProfile(const String & profile)
settings_constraints = std::move(new_constraints); settings_constraints = std::move(new_constraints);
} }
std::shared_ptr<const User> Context::getUser(const String & user_name) std::shared_ptr<const User> Context::getUser(const String & user_name) const
{ {
return shared->users_manager->getUser(user_name); return shared->access_control_manager.getUser(user_name);
} }
void Context::setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key) void Context::setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key)
{ {
auto lock = getLock(); auto lock = getLock();
auto user_props = shared->users_manager->authorizeAndGetUser(name, password, address.host());
client_info.current_user = name; client_info.current_user = name;
client_info.current_address = address; client_info.current_address = address;
client_info.current_password = password; client_info.current_password = password;
@ -728,47 +717,27 @@ void Context::setUser(const String & name, const String & password, const Poco::
if (!quota_key.empty()) if (!quota_key.empty())
client_info.quota_key = quota_key; client_info.quota_key = quota_key;
user = shared->access_control_manager.authorizeAndGetUser(
name,
password,
address.host(),
[this](const UserPtr & changed_user)
{
user = changed_user;
calculateAccessRights();
},
&subscription_for_user_change.subscription);
quota = getAccessControlManager().createQuotaContext(
client_info.current_user, client_info.current_address.host(), client_info.quota_key);
row_policy = getAccessControlManager().getRowPolicyContext(client_info.current_user);
calculateUserSettings(); calculateUserSettings();
calculateAccessRights();
} }
void Context::checkDatabaseAccessRights(const std::string & database_name) const
{
auto lock = getLock();
checkDatabaseAccessRightsImpl(database_name);
}
bool Context::hasDatabaseAccessRights(const String & database_name) const
{
auto lock = getLock();
return client_info.current_user.empty() || (database_name == "system") ||
shared->users_manager->hasAccessToDatabase(client_info.current_user, database_name);
}
bool Context::hasDictionaryAccessRights(const String & dictionary_name) const
{
auto lock = getLock();
return client_info.current_user.empty() ||
shared->users_manager->hasAccessToDictionary(client_info.current_user, dictionary_name);
}
void Context::checkDatabaseAccessRightsImpl(const std::string & database_name) const
{
if (client_info.current_user.empty() || (database_name == "system"))
{
/// An unnamed user, i.e. server, has access to all databases.
/// All users have access to the database system.
return;
}
if (!shared->users_manager->hasAccessToDatabase(client_info.current_user, database_name))
throw Exception("Access denied to database " + database_name + " for user " + client_info.current_user , ErrorCodes::DATABASE_ACCESS_DENIED);
}
void Context::addDependencyUnsafe(const StorageID & from, const StorageID & where) void Context::addDependencyUnsafe(const StorageID & from, const StorageID & where)
{ {
checkDatabaseAccessRightsImpl(from.database_name);
checkDatabaseAccessRightsImpl(where.database_name);
shared->view_dependencies[from].insert(where); shared->view_dependencies[from].insert(where);
// Notify table of dependencies change // Notify table of dependencies change
@ -785,8 +754,6 @@ void Context::addDependency(const StorageID & from, const StorageID & where)
void Context::removeDependencyUnsafe(const StorageID & from, const StorageID & where) void Context::removeDependencyUnsafe(const StorageID & from, const StorageID & where)
{ {
checkDatabaseAccessRightsImpl(from.database_name);
checkDatabaseAccessRightsImpl(where.database_name);
shared->view_dependencies[from].erase(where); shared->view_dependencies[from].erase(where);
// Notify table of dependencies change // Notify table of dependencies change
@ -806,16 +773,6 @@ Dependencies Context::getDependencies(const StorageID & from) const
auto lock = getLock(); auto lock = getLock();
String db = resolveDatabase(from.database_name, current_database); String db = resolveDatabase(from.database_name, current_database);
if (from.database_name.empty() && tryGetExternalTable(from.table_name))
{
/// Table is temporary. Access granted.
}
else
{
checkDatabaseAccessRightsImpl(db);
}
ViewDependencies::const_iterator iter = shared->view_dependencies.find(StorageID(db, from.table_name, from.uuid)); ViewDependencies::const_iterator iter = shared->view_dependencies.find(StorageID(db, from.table_name, from.uuid));
if (iter == shared->view_dependencies.end()) if (iter == shared->view_dependencies.end())
return {}; return {};
@ -826,10 +783,7 @@ Dependencies Context::getDependencies(const StorageID & from) const
bool Context::isTableExist(const String & database_name, const String & table_name) const bool Context::isTableExist(const String & database_name, const String & table_name) const
{ {
auto lock = getLock(); auto lock = getLock();
String db = resolveDatabase(database_name, current_database); String db = resolveDatabase(database_name, current_database);
checkDatabaseAccessRightsImpl(db);
Databases::const_iterator it = shared->databases.find(db); Databases::const_iterator it = shared->databases.find(db);
return shared->databases.end() != it return shared->databases.end() != it
&& it->second->isTableExist(*this, table_name); && it->second->isTableExist(*this, table_name);
@ -838,10 +792,7 @@ bool Context::isTableExist(const String & database_name, const String & table_na
bool Context::isDictionaryExists(const String & database_name, const String & dictionary_name) const bool Context::isDictionaryExists(const String & database_name, const String & dictionary_name) const
{ {
auto lock = getLock(); auto lock = getLock();
String db = resolveDatabase(database_name, current_database); String db = resolveDatabase(database_name, current_database);
checkDatabaseAccessRightsImpl(db);
Databases::const_iterator it = shared->databases.find(db); Databases::const_iterator it = shared->databases.find(db);
return shared->databases.end() != it && it->second->isDictionaryExist(*this, dictionary_name); return shared->databases.end() != it && it->second->isDictionaryExist(*this, dictionary_name);
} }
@ -850,7 +801,6 @@ bool Context::isDatabaseExist(const String & database_name) const
{ {
auto lock = getLock(); auto lock = getLock();
String db = resolveDatabase(database_name, current_database); String db = resolveDatabase(database_name, current_database);
checkDatabaseAccessRightsImpl(db);
return shared->databases.end() != shared->databases.find(db); return shared->databases.end() != shared->databases.find(db);
} }
@ -860,28 +810,20 @@ bool Context::isExternalTableExist(const String & table_name) const
} }
void Context::assertTableDoesntExist(const String & database_name, const String & table_name, bool check_database_access_rights) const void Context::assertTableDoesntExist(const String & database_name, const String & table_name) const
{ {
auto lock = getLock(); auto lock = getLock();
String db = resolveDatabase(database_name, current_database); String db = resolveDatabase(database_name, current_database);
if (check_database_access_rights)
checkDatabaseAccessRightsImpl(db);
Databases::const_iterator it = shared->databases.find(db); Databases::const_iterator it = shared->databases.find(db);
if (shared->databases.end() != it && it->second->isTableExist(*this, table_name)) if (shared->databases.end() != it && it->second->isTableExist(*this, table_name))
throw Exception("Table " + backQuoteIfNeed(db) + "." + backQuoteIfNeed(table_name) + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); throw Exception("Table " + backQuoteIfNeed(db) + "." + backQuoteIfNeed(table_name) + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS);
} }
void Context::assertDatabaseExists(const String & database_name, bool check_database_access_rights) const void Context::assertDatabaseExists(const String & database_name) const
{ {
auto lock = getLock(); auto lock = getLock();
String db = resolveDatabase(database_name, current_database); String db = resolveDatabase(database_name, current_database);
if (check_database_access_rights)
checkDatabaseAccessRightsImpl(db);
if (shared->databases.end() == shared->databases.find(db)) if (shared->databases.end() == shared->databases.find(db))
throw Exception("Database " + backQuoteIfNeed(db) + " doesn't exist", ErrorCodes::UNKNOWN_DATABASE); throw Exception("Database " + backQuoteIfNeed(db) + " doesn't exist", ErrorCodes::UNKNOWN_DATABASE);
} }
@ -890,10 +832,7 @@ void Context::assertDatabaseExists(const String & database_name, bool check_data
void Context::assertDatabaseDoesntExist(const String & database_name) const void Context::assertDatabaseDoesntExist(const String & database_name) const
{ {
auto lock = getLock(); auto lock = getLock();
String db = resolveDatabase(database_name, current_database); String db = resolveDatabase(database_name, current_database);
checkDatabaseAccessRightsImpl(db);
if (shared->databases.end() != shared->databases.find(db)) if (shared->databases.end() != shared->databases.find(db))
throw Exception("Database " + backQuoteIfNeed(db) + " already exists.", ErrorCodes::DATABASE_ALREADY_EXISTS); throw Exception("Database " + backQuoteIfNeed(db) + " already exists.", ErrorCodes::DATABASE_ALREADY_EXISTS);
} }
@ -986,7 +925,6 @@ StoragePtr Context::getTableImpl(const StorageID & table_id, std::optional<Excep
} }
db = resolveDatabase(table_id.database_name, current_database); db = resolveDatabase(table_id.database_name, current_database);
checkDatabaseAccessRightsImpl(db);
Databases::const_iterator it = shared->databases.find(db); Databases::const_iterator it = shared->databases.find(db);
if (shared->databases.end() == it) if (shared->databases.end() == it)
@ -1143,7 +1081,15 @@ Settings Context::getSettings() const
void Context::setSettings(const Settings & settings_) void Context::setSettings(const Settings & settings_)
{ {
auto lock = getLock();
bool old_readonly = settings.readonly;
bool old_allow_ddl = settings.allow_ddl;
bool old_allow_introspection_functions = settings.allow_introspection_functions;
settings = settings_; settings = settings_;
if ((settings.readonly != old_readonly) || (settings.allow_ddl != old_allow_ddl) || (settings.allow_introspection_functions != old_allow_introspection_functions))
calculateAccessRights();
} }
@ -1156,6 +1102,9 @@ void Context::setSetting(const String & name, const String & value)
return; return;
} }
settings.set(name, value); settings.set(name, value);
if (name == "readonly" || name == "allow_ddl" || name == "allow_introspection_functions")
calculateAccessRights();
} }
@ -1168,6 +1117,9 @@ void Context::setSetting(const String & name, const Field & value)
return; return;
} }
settings.set(name, value); settings.set(name, value);
if (name == "readonly" || name == "allow_ddl" || name == "allow_introspection_functions")
calculateAccessRights();
} }
@ -1222,6 +1174,7 @@ void Context::setCurrentDatabase(const String & name)
auto lock = getLock(); auto lock = getLock();
assertDatabaseExists(name); assertDatabaseExists(name);
current_database = name; current_database = name;
calculateAccessRights();
} }
@ -1589,9 +1542,9 @@ std::pair<String, UInt16> Context::getInterserverIOAddress() const
return { shared->interserver_io_host, shared->interserver_io_port }; return { shared->interserver_io_host, shared->interserver_io_port };
} }
void Context::setInterserverCredentials(const String & user, const String & password) void Context::setInterserverCredentials(const String & user_, const String & password)
{ {
shared->interserver_io_user = user; shared->interserver_io_user = user_;
shared->interserver_io_password = password; shared->interserver_io_password = password;
} }

View File

@ -6,13 +6,13 @@
#include <Core/Types.h> #include <Core/Types.h>
#include <DataStreams/IBlockStream_fwd.h> #include <DataStreams/IBlockStream_fwd.h>
#include <Interpreters/ClientInfo.h> #include <Interpreters/ClientInfo.h>
#include <Interpreters/Users.h>
#include <Parsers/IAST_fwd.h> #include <Parsers/IAST_fwd.h>
#include <Common/LRUCache.h> #include <Common/LRUCache.h>
#include <Common/MultiVersion.h> #include <Common/MultiVersion.h>
#include <Common/ThreadPool.h> #include <Common/ThreadPool.h>
#include "config_core.h" #include "config_core.h"
#include <Storages/IStorage_fwd.h> #include <Storages/IStorage_fwd.h>
#include <ext/scope_guard.h>
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <condition_variable> #include <condition_variable>
@ -43,8 +43,13 @@ namespace DB
struct ContextShared; struct ContextShared;
class Context; class Context;
struct User;
class AccessRightsContext;
class QuotaContext; class QuotaContext;
class RowPolicyContext; class RowPolicyContext;
class AccessFlags;
struct AccessRightsElement;
class AccessRightsElements;
class EmbeddedDictionaries; class EmbeddedDictionaries;
class ExternalDictionariesLoader; class ExternalDictionariesLoader;
class ExternalModelsLoader; class ExternalModelsLoader;
@ -127,6 +132,16 @@ struct IHostContext
using IHostContextPtr = std::shared_ptr<IHostContext>; using IHostContextPtr = std::shared_ptr<IHostContext>;
/// Subscription for user's change. This subscription cannot be copied with the context,
/// that's why we had to move it into a separate structure.
struct SubscriptionForUserChange
{
ext::scope_guard subscription;
SubscriptionForUserChange() {}
SubscriptionForUserChange(const SubscriptionForUserChange &) {}
SubscriptionForUserChange & operator =(const SubscriptionForUserChange &) { subscription = {}; return *this; }
};
/** A set of known objects that can be used in the query. /** A set of known objects that can be used in the query.
* Consists of a shared part (always common to all sessions and queries) * Consists of a shared part (always common to all sessions and queries)
* and copied part (which can be its own for each session or query). * and copied part (which can be its own for each session or query).
@ -145,10 +160,11 @@ private:
InputInitializer input_initializer_callback; InputInitializer input_initializer_callback;
InputBlocksReader input_blocks_reader; InputBlocksReader input_blocks_reader;
std::shared_ptr<const User> user;
SubscriptionForUserChange subscription_for_user_change;
std::shared_ptr<const AccessRightsContext> access_rights;
std::shared_ptr<QuotaContext> quota; /// Current quota. By default - empty quota, that have no limits. std::shared_ptr<QuotaContext> quota; /// Current quota. By default - empty quota, that have no limits.
bool is_quota_management_allowed = false; /// Whether the current user is allowed to manage quotas via SQL commands.
std::shared_ptr<RowPolicyContext> row_policy; std::shared_ptr<RowPolicyContext> row_policy;
bool is_row_policy_management_allowed = false; /// Whether the current user is allowed to manage row policies via SQL commands.
String current_database; String current_database;
Settings settings; /// Setting for query execution. Settings settings; /// Setting for query execution.
std::shared_ptr<const SettingsConstraints> settings_constraints; std::shared_ptr<const SettingsConstraints> settings_constraints;
@ -219,10 +235,21 @@ public:
AccessControlManager & getAccessControlManager(); AccessControlManager & getAccessControlManager();
const AccessControlManager & getAccessControlManager() const; const AccessControlManager & getAccessControlManager() const;
std::shared_ptr<const AccessRightsContext> getAccessRights() const { return std::atomic_load(&access_rights); }
/// Checks access rights.
/// Empty database means the current database.
void checkAccess(const AccessFlags & access) const;
void checkAccess(const AccessFlags & access, const std::string_view & database) const;
void checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const;
void checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
void checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
void checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
void checkAccess(const AccessRightsElement & access) const;
void checkAccess(const AccessRightsElements & access) const;
std::shared_ptr<QuotaContext> getQuota() const { return quota; } std::shared_ptr<QuotaContext> getQuota() const { return quota; }
void checkQuotaManagementIsAllowed();
std::shared_ptr<RowPolicyContext> getRowPolicy() const { return row_policy; } std::shared_ptr<RowPolicyContext> getRowPolicy() const { return row_policy; }
void checkRowPolicyManagementIsAllowed();
/** Take the list of users, quotas and configuration profiles from this config. /** Take the list of users, quotas and configuration profiles from this config.
* The list of users is completely replaced. * The list of users is completely replaced.
@ -233,12 +260,10 @@ public:
/// Must be called before getClientInfo. /// Must be called before getClientInfo.
void setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key); void setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key);
std::shared_ptr<const User> getUser() const { return user; }
/// Used by MySQL Secure Password Authentication plugin. /// Used by MySQL Secure Password Authentication plugin.
std::shared_ptr<const User> getUser(const String & user_name); std::shared_ptr<const User> getUser(const String & user_name) const;
/// Compute and set actual user settings, client_info.current_user should be set
void calculateUserSettings();
/// We have to copy external tables inside executeQuery() to track limits. Therefore, set callback for it. Must set once. /// We have to copy external tables inside executeQuery() to track limits. Therefore, set callback for it. Must set once.
void setExternalTablesInitializer(ExternalTablesInitializer && initializer); void setExternalTablesInitializer(ExternalTablesInitializer && initializer);
@ -272,19 +297,9 @@ public:
bool isDatabaseExist(const String & database_name) const; bool isDatabaseExist(const String & database_name) const;
bool isDictionaryExists(const String & database_name, const String & dictionary_name) const; bool isDictionaryExists(const String & database_name, const String & dictionary_name) const;
bool isExternalTableExist(const String & table_name) const; bool isExternalTableExist(const String & table_name) const;
bool hasDatabaseAccessRights(const String & database_name) const; void assertTableDoesntExist(const String & database_name, const String & table_name) const;
void assertDatabaseExists(const String & database_name) const;
bool hasDictionaryAccessRights(const String & dictionary_name) const;
/** The parameter check_database_access_rights exists to not check the permissions of the database again,
* when assertTableDoesntExist or assertDatabaseExists is called inside another function that already
* made this check.
*/
void assertTableDoesntExist(const String & database_name, const String & table_name, bool check_database_acccess_rights = true) const;
void assertDatabaseExists(const String & database_name, bool check_database_acccess_rights = true) const;
void assertDatabaseDoesntExist(const String & database_name) const; void assertDatabaseDoesntExist(const String & database_name) const;
void checkDatabaseAccessRights(const std::string & database_name) const;
const Scalars & getScalars() const; const Scalars & getScalars() const;
const Block & getScalar(const String & name) const; const Block & getScalar(const String & name) const;
@ -589,12 +604,19 @@ public:
MySQLWireContext mysql; MySQLWireContext mysql;
private: private:
/// Compute and set actual user settings, client_info.current_user should be set
void calculateUserSettings();
void calculateAccessRights();
/** Check if the current client has access to the specified database. /** Check if the current client has access to the specified database.
* If access is denied, throw an exception. * If access is denied, throw an exception.
* NOTE: This method should always be called when the `shared->mutex` mutex is acquired. * NOTE: This method should always be called when the `shared->mutex` mutex is acquired.
*/ */
void checkDatabaseAccessRightsImpl(const std::string & database_name) const; void checkDatabaseAccessRightsImpl(const std::string & database_name) const;
template <typename... Args>
void checkAccessImpl(const Args &... args) const;
void setProfile(const String & profile); void setProfile(const String & profile);
EmbeddedDictionaries & getEmbeddedDictionariesImpl(bool throw_on_error) const; EmbeddedDictionaries & getEmbeddedDictionariesImpl(bool throw_on_error) const;

View File

@ -16,6 +16,7 @@
#include <Interpreters/executeQuery.h> #include <Interpreters/executeQuery.h>
#include <Interpreters/Cluster.h> #include <Interpreters/Cluster.h>
#include <Interpreters/AddDefaultDatabaseVisitor.h> #include <Interpreters/AddDefaultDatabaseVisitor.h>
#include <Access/AccessRightsElement.h>
#include <Common/DNSResolver.h> #include <Common/DNSResolver.h>
#include <Common/Macros.h> #include <Common/Macros.h>
#include <Common/getFQDNOrHostName.h> #include <Common/getFQDNOrHostName.h>
@ -1242,7 +1243,7 @@ private:
}; };
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, NameSet && query_databases) BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, AccessRightsElements && query_required_access)
{ {
/// Remove FORMAT <fmt> and INTO OUTFILE <file> if exists /// Remove FORMAT <fmt> and INTO OUTFILE <file> if exists
ASTPtr query_ptr = query_ptr_->clone(); ASTPtr query_ptr = query_ptr_->clone();
@ -1271,51 +1272,77 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont
ClusterPtr cluster = context.getCluster(query->cluster); ClusterPtr cluster = context.getCluster(query->cluster);
DDLWorker & ddl_worker = context.getDDLWorker(); DDLWorker & ddl_worker = context.getDDLWorker();
/// Check database access rights, assume that all servers have the same users config /// Enumerate hosts which will be used to send query.
NameSet databases_to_access;
const String & current_database = context.getCurrentDatabase();
Cluster::AddressesWithFailover shards = cluster->getShardsAddresses(); Cluster::AddressesWithFailover shards = cluster->getShardsAddresses();
std::vector<HostID> hosts; std::vector<HostID> hosts;
bool use_shard_default_db = false;
bool use_local_default_db = false;
for (const auto & shard : shards) for (const auto & shard : shards)
{ {
for (const auto & addr : shard) for (const auto & addr : shard)
{
hosts.emplace_back(addr); hosts.emplace_back(addr);
}
/// Expand empty database name to shards' default (o current) database name if (hosts.empty())
for (const String & database : query_databases) throw Exception("No hosts defined to execute distributed DDL query", ErrorCodes::LOGICAL_ERROR);
/// The current database in a distributed query need to be replaced with either
/// the local current database or a shard's default database.
bool need_replace_current_database
= (std::find_if(
query_required_access.begin(),
query_required_access.end(),
[](const AccessRightsElement & elem) { return elem.isEmptyDatabase(); })
!= query_required_access.end());
if (need_replace_current_database)
{
bool use_local_default_database = false;
Strings shard_default_databases;
for (const auto & shard : shards)
{
for (const auto & addr : shard)
{ {
if (database.empty()) if (!addr.default_database.empty())
{ shard_default_databases.push_back(addr.default_database);
bool has_shard_default_db = !addr.default_database.empty();
use_shard_default_db |= has_shard_default_db;
use_local_default_db |= !has_shard_default_db;
databases_to_access.emplace(has_shard_default_db ? addr.default_database : current_database);
}
else else
databases_to_access.emplace(database); use_local_default_database = true;
}
}
std::sort(shard_default_databases.begin(), shard_default_databases.end());
shard_default_databases.erase(std::unique(shard_default_databases.begin(), shard_default_databases.end()), shard_default_databases.end());
assert(use_local_default_database || !shard_default_databases.empty());
if (use_local_default_database && !shard_default_databases.empty())
throw Exception("Mixed local default DB and shard default DB in DDL query", ErrorCodes::NOT_IMPLEMENTED);
if (use_local_default_database)
{
const String & current_database = context.getCurrentDatabase();
AddDefaultDatabaseVisitor visitor(current_database);
visitor.visitDDL(query_ptr);
query_required_access.replaceEmptyDatabase(current_database);
}
else
{
size_t old_num_elements = query_required_access.size();
for (size_t i = 0; i != old_num_elements; ++i)
{
auto & element = query_required_access[i];
if (element.isEmptyDatabase())
{
element.setDatabase(shard_default_databases[0]);
for (size_t j = 1; j != shard_default_databases.size(); ++j)
{
query_required_access.push_back(element);
query_required_access.back().setDatabase(shard_default_databases[j]);
}
}
} }
} }
} }
if (use_shard_default_db && use_local_default_db) /// Check access rights, assume that all servers have the same users config
throw Exception("Mixed local default DB and shard default DB in DDL query", ErrorCodes::NOT_IMPLEMENTED); context.checkAccess(query_required_access);
if (databases_to_access.empty())
throw Exception("No databases to access in distributed DDL query", ErrorCodes::LOGICAL_ERROR);
for (const String & database : databases_to_access)
context.checkDatabaseAccessRights(database);
if (use_local_default_db)
{
AddDefaultDatabaseVisitor visitor(current_database);
visitor.visitDDL(query_ptr);
}
DDLLogEntry entry; DDLLogEntry entry;
entry.hosts = std::move(hosts); entry.hosts = std::move(hosts);

View File

@ -17,12 +17,13 @@ namespace DB
{ {
class ASTAlterQuery; class ASTAlterQuery;
class AccessRightsElements;
struct DDLLogEntry; struct DDLLogEntry;
struct DDLTask; struct DDLTask;
/// Pushes distributed DDL query to the queue /// Pushes distributed DDL query to the queue
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, NameSet && query_databases); BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, AccessRightsElements && query_required_access);
class DDLWorker class DDLWorker

View File

@ -122,7 +122,7 @@ void ExpressionAnalyzer::analyzeAggregation()
ASTPtr array_join_expression_list = select_query->array_join_expression_list(is_array_join_left); ASTPtr array_join_expression_list = select_query->array_join_expression_list(is_array_join_left);
if (array_join_expression_list) if (array_join_expression_list)
{ {
getRootActions(array_join_expression_list, true, temp_actions); getRootActionsNoMakeSet(array_join_expression_list, true, temp_actions, false);
addMultipleArrayJoinAction(temp_actions, is_array_join_left); addMultipleArrayJoinAction(temp_actions, is_array_join_left);
array_join_columns.clear(); array_join_columns.clear();
@ -134,7 +134,7 @@ void ExpressionAnalyzer::analyzeAggregation()
const ASTTablesInSelectQueryElement * join = select_query->join(); const ASTTablesInSelectQueryElement * join = select_query->join();
if (join) if (join)
{ {
getRootActions(analyzedJoin().leftKeysList(), true, temp_actions); getRootActionsNoMakeSet(analyzedJoin().leftKeysList(), true, temp_actions, false);
addJoinAction(temp_actions); addJoinAction(temp_actions);
} }
} }
@ -155,7 +155,7 @@ void ExpressionAnalyzer::analyzeAggregation()
for (ssize_t i = 0; i < ssize_t(group_asts.size()); ++i) for (ssize_t i = 0; i < ssize_t(group_asts.size()); ++i)
{ {
ssize_t size = group_asts.size(); ssize_t size = group_asts.size();
getRootActions(group_asts[i], true, temp_actions); getRootActionsNoMakeSet(group_asts[i], true, temp_actions, false);
const auto & column_name = group_asts[i]->getColumnName(); const auto & column_name = group_asts[i]->getColumnName();
const auto & block = temp_actions->getSampleBlock(); const auto & block = temp_actions->getSampleBlock();
@ -340,7 +340,18 @@ void ExpressionAnalyzer::getRootActions(const ASTPtr & ast, bool no_subqueries,
LogAST log; LogAST log;
ActionsVisitor::Data visitor_data(context, settings.size_limits_for_set, subquery_depth, ActionsVisitor::Data visitor_data(context, settings.size_limits_for_set, subquery_depth,
sourceColumns(), actions, prepared_sets, subqueries_for_sets, sourceColumns(), actions, prepared_sets, subqueries_for_sets,
no_subqueries, only_consts, !isRemoteStorage()); no_subqueries, false, only_consts, !isRemoteStorage());
ActionsVisitor(visitor_data, log.stream()).visit(ast);
visitor_data.updateActions(actions);
}
void ExpressionAnalyzer::getRootActionsNoMakeSet(const ASTPtr & ast, bool no_subqueries, ExpressionActionsPtr & actions, bool only_consts)
{
LogAST log;
ActionsVisitor::Data visitor_data(context, settings.size_limits_for_set, subquery_depth,
sourceColumns(), actions, prepared_sets, subqueries_for_sets,
no_subqueries, true, only_consts, !isRemoteStorage());
ActionsVisitor(visitor_data, log.stream()).visit(ast); ActionsVisitor(visitor_data, log.stream()).visit(ast);
visitor_data.updateActions(actions); visitor_data.updateActions(actions);
} }

View File

@ -134,6 +134,12 @@ protected:
void getRootActions(const ASTPtr & ast, bool no_subqueries, ExpressionActionsPtr & actions, bool only_consts = false); void getRootActions(const ASTPtr & ast, bool no_subqueries, ExpressionActionsPtr & actions, bool only_consts = false);
/** Similar to getRootActions but do not make sets when analyzing IN functions. It's used in
* analyzeAggregation which happens earlier than analyzing PREWHERE and WHERE. If we did, the
* prepared sets would not be applicable for MergeTree index optimization.
*/
void getRootActionsNoMakeSet(const ASTPtr & ast, bool no_subqueries, ExpressionActionsPtr & actions, bool only_consts = false);
/** Add aggregation keys to aggregation_keys, aggregate functions to aggregate_descriptions, /** Add aggregation keys to aggregation_keys, aggregate functions to aggregate_descriptions,
* Create a set of columns aggregated_columns resulting after the aggregation, if any, * Create a set of columns aggregated_columns resulting after the aggregation, if any,
* or after all the actions that are normally performed before aggregation. * or after all the actions that are normally performed before aggregation.

View File

@ -247,10 +247,11 @@ struct LLVMContext
}; };
template <typename... Ts, typename F> template <typename... Ts>
static bool castToEither(IColumn * column, F && f) static bool castToEitherWithNullable(IColumn * column)
{ {
return ((typeid_cast<Ts *>(column) ? f(*typeid_cast<Ts *>(column)) : false) || ...); return ((typeid_cast<Ts *>(column)
|| (typeid_cast<ColumnNullable *>(column) && typeid_cast<Ts *>(&(typeid_cast<ColumnNullable *>(column)->getNestedColumn())))) || ...);
} }
class LLVMExecutableFunction : public IExecutableFunctionImpl class LLVMExecutableFunction : public IExecutableFunctionImpl
@ -280,12 +281,12 @@ public:
if (block_size) if (block_size)
{ {
if (!castToEither< if (!castToEitherWithNullable<
ColumnUInt8, ColumnUInt16, ColumnUInt32, ColumnUInt64, ColumnUInt8, ColumnUInt16, ColumnUInt32, ColumnUInt64,
ColumnInt8, ColumnInt16, ColumnInt32, ColumnInt64, ColumnInt8, ColumnInt16, ColumnInt32, ColumnInt64,
ColumnFloat32, ColumnFloat64>(col_res.get(), [block_size](auto & col) { col.getData().resize(block_size); return true; })) ColumnFloat32, ColumnFloat64>(col_res.get()))
throw Exception("Unexpected column in LLVMExecutableFunction: " + col_res->getName(), ErrorCodes::LOGICAL_ERROR); throw Exception("Unexpected column in LLVMExecutableFunction: " + col_res->getName(), ErrorCodes::LOGICAL_ERROR);
col_res = col_res->cloneResized(block_size);
std::vector<ColumnData> columns(arguments.size() + 1); std::vector<ColumnData> columns(arguments.size() + 1);
for (size_t i = 0; i < arguments.size(); ++i) for (size_t i = 0; i < arguments.size(); ++i)
{ {

View File

@ -4,12 +4,14 @@
#include <Interpreters/AddDefaultDatabaseVisitor.h> #include <Interpreters/AddDefaultDatabaseVisitor.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Parsers/ASTAlterQuery.h> #include <Parsers/ASTAlterQuery.h>
#include <Parsers/ASTAssignment.h>
#include <Storages/IStorage.h> #include <Storages/IStorage.h>
#include <Storages/AlterCommands.h> #include <Storages/AlterCommands.h>
#include <Storages/MutationCommands.h> #include <Storages/MutationCommands.h>
#include <Storages/PartitionCommands.h> #include <Storages/PartitionCommands.h>
#include <Storages/LiveView/LiveViewCommands.h> #include <Storages/LiveView/LiveViewCommands.h>
#include <Storages/LiveView/StorageLiveView.h> #include <Storages/LiveView/StorageLiveView.h>
#include <Access/AccessRightsElement.h>
#include <Common/typeid_cast.h> #include <Common/typeid_cast.h>
#include <algorithm> #include <algorithm>
@ -36,7 +38,9 @@ BlockIO InterpreterAlterQuery::execute()
const auto & alter = query_ptr->as<ASTAlterQuery &>(); const auto & alter = query_ptr->as<ASTAlterQuery &>();
if (!alter.cluster.empty()) if (!alter.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {alter.database}); return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
context.checkAccess(getRequiredAccess());
const String & table_name = alter.table; const String & table_name = alter.table;
String database_name = alter.database.empty() ? context.getCurrentDatabase() : alter.database; String database_name = alter.database.empty() ? context.getCurrentDatabase() : alter.database;
@ -113,4 +117,158 @@ BlockIO InterpreterAlterQuery::execute()
return {}; return {};
} }
AccessRightsElements InterpreterAlterQuery::getRequiredAccess() const
{
AccessRightsElements required_access;
const auto & alter = query_ptr->as<ASTAlterQuery &>();
for (ASTAlterCommand * command_ast : alter.command_list->commands)
{
auto column_name = [&]() -> String { return getIdentifierName(command_ast->column); };
auto column_name_from_col_decl = [&]() -> std::string_view { return command_ast->col_decl->as<ASTColumnDeclaration &>().name; };
auto column_names_from_update_assignments = [&]() -> std::vector<std::string_view>
{
std::vector<std::string_view> column_names;
for (const ASTPtr & assignment_ast : command_ast->update_assignments->children)
column_names.emplace_back(assignment_ast->as<const ASTAssignment &>().column_name);
return column_names;
};
switch (command_ast->type)
{
case ASTAlterCommand::UPDATE:
{
required_access.emplace_back(AccessType::UPDATE, alter.database, alter.table, column_names_from_update_assignments());
break;
}
case ASTAlterCommand::DELETE:
{
required_access.emplace_back(AccessType::DELETE, alter.database, alter.table);
break;
}
case ASTAlterCommand::ADD_COLUMN:
{
required_access.emplace_back(AccessType::ADD_COLUMN, alter.database, alter.table, column_name_from_col_decl());
break;
}
case ASTAlterCommand::DROP_COLUMN:
{
if (command_ast->clear_column)
required_access.emplace_back(AccessType::CLEAR_COLUMN, alter.database, alter.table, column_name());
else
required_access.emplace_back(AccessType::DROP_COLUMN, alter.database, alter.table, column_name());
break;
}
case ASTAlterCommand::MODIFY_COLUMN:
{
required_access.emplace_back(AccessType::MODIFY_COLUMN, alter.database, alter.table, column_name_from_col_decl());
break;
}
case ASTAlterCommand::COMMENT_COLUMN:
{
required_access.emplace_back(AccessType::COMMENT_COLUMN, alter.database, alter.table, column_name());
break;
}
case ASTAlterCommand::MODIFY_ORDER_BY:
{
required_access.emplace_back(AccessType::ALTER_ORDER_BY, alter.database, alter.table);
break;
}
case ASTAlterCommand::ADD_INDEX:
{
required_access.emplace_back(AccessType::ADD_INDEX, alter.database, alter.table);
break;
}
case ASTAlterCommand::DROP_INDEX:
{
if (command_ast->clear_index)
required_access.emplace_back(AccessType::CLEAR_INDEX, alter.database, alter.table);
else
required_access.emplace_back(AccessType::DROP_INDEX, alter.database, alter.table);
break;
}
case ASTAlterCommand::MATERIALIZE_INDEX:
{
required_access.emplace_back(AccessType::MATERIALIZE_INDEX, alter.database, alter.table);
break;
}
case ASTAlterCommand::ADD_CONSTRAINT:
{
required_access.emplace_back(AccessType::ADD_CONSTRAINT, alter.database, alter.table);
break;
}
case ASTAlterCommand::DROP_CONSTRAINT:
{
required_access.emplace_back(AccessType::DROP_CONSTRAINT, alter.database, alter.table);
break;
}
case ASTAlterCommand::MODIFY_TTL:
{
required_access.emplace_back(AccessType::MODIFY_TTL, alter.database, alter.table);
break;
}
case ASTAlterCommand::MODIFY_SETTING:
{
required_access.emplace_back(AccessType::MODIFY_SETTING, alter.database, alter.table);
break;
}
case ASTAlterCommand::ATTACH_PARTITION:
{
required_access.emplace_back(AccessType::INSERT, alter.database, alter.table);
break;
}
case ASTAlterCommand::DROP_PARTITION: [[fallthrough]];
case ASTAlterCommand::DROP_DETACHED_PARTITION:
{
required_access.emplace_back(AccessType::DELETE, alter.database, alter.table);
break;
}
case ASTAlterCommand::MOVE_PARTITION:
{
if ((command_ast->move_destination_type == PartDestinationType::DISK)
|| (command_ast->move_destination_type == PartDestinationType::VOLUME))
{
required_access.emplace_back(AccessType::MOVE_PARTITION, alter.database, alter.table);
}
else if (command_ast->move_destination_type == PartDestinationType::TABLE)
{
required_access.emplace_back(AccessType::SELECT | AccessType::DELETE, alter.database, alter.table);
required_access.emplace_back(AccessType::INSERT, command_ast->to_database, command_ast->to_table);
}
break;
}
case ASTAlterCommand::REPLACE_PARTITION:
{
required_access.emplace_back(AccessType::SELECT, command_ast->from_database, command_ast->from_table);
required_access.emplace_back(AccessType::DELETE | AccessType::INSERT, alter.database, alter.table);
break;
}
case ASTAlterCommand::FETCH_PARTITION:
{
required_access.emplace_back(AccessType::FETCH_PARTITION, alter.database, alter.table);
break;
}
case ASTAlterCommand::FREEZE_PARTITION: [[fallthrough]];
case ASTAlterCommand::FREEZE_ALL:
{
required_access.emplace_back(AccessType::FREEZE_PARTITION, alter.database, alter.table);
break;
}
case ASTAlterCommand::MODIFY_QUERY:
{
required_access.emplace_back(AccessType::MODIFY_VIEW_QUERY, alter.database, alter.table);
break;
}
case ASTAlterCommand::LIVE_VIEW_REFRESH:
{
required_access.emplace_back(AccessType::REFRESH_VIEW, alter.database, alter.table);
break;
}
case ASTAlterCommand::NO_TYPE: break;
}
}
return required_access;
}
} }

View File

@ -6,8 +6,9 @@
namespace DB namespace DB
{ {
class Context; class Context;
class AccessRightsElements;
/** Allows you add or remove a column in the table. /** Allows you add or remove a column in the table.
* It also allows you to manipulate the partitions of the MergeTree family tables. * It also allows you to manipulate the partitions of the MergeTree family tables.
@ -20,6 +21,8 @@ public:
BlockIO execute() override; BlockIO execute() override;
private: private:
AccessRightsElements getRequiredAccess() const;
ASTPtr query_ptr; ASTPtr query_ptr;
const Context & context; const Context & context;

View File

@ -1,5 +1,6 @@
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Interpreters/InterpreterCheckQuery.h> #include <Interpreters/InterpreterCheckQuery.h>
#include <Access/AccessFlags.h>
#include <Storages/IStorage.h> #include <Storages/IStorage.h>
#include <Parsers/ASTCheckQuery.h> #include <Parsers/ASTCheckQuery.h>
#include <DataStreams/OneBlockInputStream.h> #include <DataStreams/OneBlockInputStream.h>
@ -40,6 +41,7 @@ BlockIO InterpreterCheckQuery::execute()
const String & table_name = check.table; const String & table_name = check.table;
String database_name = check.database.empty() ? context.getCurrentDatabase() : check.database; String database_name = check.database.empty() ? context.getCurrentDatabase() : check.database;
context.checkAccess(AccessType::SHOW, database_name, table_name);
StoragePtr table = context.getTable(database_name, table_name); StoragePtr table = context.getTable(database_name, table_name);
auto check_results = table->checkData(query_ptr, context); auto check_results = table->checkData(query_ptr, context);

View File

@ -32,6 +32,8 @@
#include <Interpreters/ExpressionActions.h> #include <Interpreters/ExpressionActions.h>
#include <Interpreters/AddDefaultDatabaseVisitor.h> #include <Interpreters/AddDefaultDatabaseVisitor.h>
#include <Access/AccessRightsElement.h>
#include <DataTypes/DataTypeFactory.h> #include <DataTypes/DataTypeFactory.h>
#include <DataTypes/NestedUtils.h> #include <DataTypes/NestedUtils.h>
#include <DataTypes/DataTypesNumber.h> #include <DataTypes/DataTypesNumber.h>
@ -79,9 +81,6 @@ InterpreterCreateQuery::InterpreterCreateQuery(const ASTPtr & query_ptr_, Contex
BlockIO InterpreterCreateQuery::createDatabase(ASTCreateQuery & create) BlockIO InterpreterCreateQuery::createDatabase(ASTCreateQuery & create)
{ {
if (!create.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {create.database});
String database_name = create.database; String database_name = create.database;
auto guard = context.getDDLGuard(database_name, ""); auto guard = context.getDDLGuard(database_name, "");
@ -538,15 +537,6 @@ void InterpreterCreateQuery::setEngine(ASTCreateQuery & create) const
BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create) BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
{ {
if (!create.cluster.empty())
{
NameSet databases{create.database};
if (!create.to_table.empty())
databases.emplace(create.to_database);
return executeDDLQueryOnCluster(query_ptr, context, std::move(databases));
}
/// Temporary tables are created out of databases. /// Temporary tables are created out of databases.
if (create.temporary && !create.database.empty() && !create.is_live_view) if (create.temporary && !create.database.empty() && !create.is_live_view)
throw Exception("Temporary tables cannot be inside a database. You should not specify a database for a temporary table.", throw Exception("Temporary tables cannot be inside a database. You should not specify a database for a temporary table.",
@ -690,9 +680,6 @@ BlockIO InterpreterCreateQuery::fillTableIfNeeded(const ASTCreateQuery & create)
BlockIO InterpreterCreateQuery::createDictionary(ASTCreateQuery & create) BlockIO InterpreterCreateQuery::createDictionary(ASTCreateQuery & create)
{ {
if (!create.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {create.database});
String dictionary_name = create.table; String dictionary_name = create.table;
if (create.database.empty()) if (create.database.empty())
@ -730,7 +717,11 @@ BlockIO InterpreterCreateQuery::createDictionary(ASTCreateQuery & create)
BlockIO InterpreterCreateQuery::execute() BlockIO InterpreterCreateQuery::execute()
{ {
auto & create = query_ptr->as<ASTCreateQuery &>(); auto & create = query_ptr->as<ASTCreateQuery &>();
checkAccess(create); if (!create.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
context.checkAccess(getRequiredAccess());
ASTQueryWithOutput::resetOutputASTIfExist(create); ASTQueryWithOutput::resetOutputASTIfExist(create);
/// CREATE|ATTACH DATABASE /// CREATE|ATTACH DATABASE
@ -743,43 +734,47 @@ BlockIO InterpreterCreateQuery::execute()
} }
void InterpreterCreateQuery::checkAccess(const ASTCreateQuery & create) AccessRightsElements InterpreterCreateQuery::getRequiredAccess() const
{ {
/// Internal queries (initiated by the server itself) always have access to everything. /// Internal queries (initiated by the server itself) always have access to everything.
if (internal) if (internal)
return; return {};
const Settings & settings = context.getSettingsRef(); AccessRightsElements required_access;
auto readonly = settings.readonly; const auto & create = query_ptr->as<const ASTCreateQuery &>();
auto allow_ddl = settings.allow_ddl;
if (!readonly && allow_ddl) if (create.table.empty())
return;
/// CREATE|ATTACH DATABASE
if (!create.database.empty() && create.table.empty())
{ {
if (readonly) required_access.emplace_back(AccessType::CREATE_DATABASE, create.database);
throw Exception("Cannot create database in readonly mode", ErrorCodes::READONLY);
throw Exception("Cannot create database. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
} }
String object = "table"; else if (create.is_dictionary)
if (create.is_dictionary)
{ {
if (readonly) required_access.emplace_back(AccessType::CREATE_DICTIONARY, create.database, create.table);
throw Exception("Cannot create dictionary in readonly mode", ErrorCodes::READONLY); }
object = "dictionary"; else if (create.is_view || create.is_materialized_view || create.is_live_view)
{
if (create.temporary)
required_access.emplace_back(AccessType::CREATE_TEMPORARY_TABLE);
else
{
if (create.replace_view)
required_access.emplace_back(AccessType::DROP_VIEW | AccessType::CREATE_VIEW, create.database, create.table);
else
required_access.emplace_back(AccessType::CREATE_VIEW, create.database, create.table);
}
}
else
{
if (create.temporary)
required_access.emplace_back(AccessType::CREATE_TEMPORARY_TABLE);
else
required_access.emplace_back(AccessType::CREATE_TABLE, create.database, create.table);
} }
if (create.temporary && readonly >= 2) if (!create.to_table.empty())
return; required_access.emplace_back(AccessType::INSERT, create.to_database, create.to_table);
if (readonly) return required_access;
throw Exception("Cannot create table or dictionary in readonly mode", ErrorCodes::READONLY);
throw Exception("Cannot create " + object + ". DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
} }
} }

View File

@ -64,7 +64,7 @@ private:
TableProperties setProperties(ASTCreateQuery & create) const; TableProperties setProperties(ASTCreateQuery & create) const;
void validateTableStructure(const ASTCreateQuery & create, const TableProperties & properties) const; void validateTableStructure(const ASTCreateQuery & create, const TableProperties & properties) const;
void setEngine(ASTCreateQuery & create) const; void setEngine(ASTCreateQuery & create) const;
void checkAccess(const ASTCreateQuery & create); AccessRightsElements getRequiredAccess() const;
/// Create IStorage and add it to database. If table already exists and IF NOT EXISTS specified, do nothing and return false. /// Create IStorage and add it to database. If table already exists and IF NOT EXISTS specified, do nothing and return false.
bool doCreateTable(const ASTCreateQuery & create, const TableProperties & properties); bool doCreateTable(const ASTCreateQuery & create, const TableProperties & properties);

View File

@ -3,6 +3,7 @@
#include <Parsers/ASTRoleList.h> #include <Parsers/ASTRoleList.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Access/AccessControlManager.h> #include <Access/AccessControlManager.h>
#include <Access/AccessFlags.h>
#include <ext/range.h> #include <ext/range.h>
#include <boost/range/algorithm/find_if.hpp> #include <boost/range/algorithm/find_if.hpp>
#include <boost/range/algorithm/upper_bound.hpp> #include <boost/range/algorithm/upper_bound.hpp>
@ -13,9 +14,9 @@ namespace DB
{ {
BlockIO InterpreterCreateQuotaQuery::execute() BlockIO InterpreterCreateQuotaQuery::execute()
{ {
context.checkQuotaManagementIsAllowed();
const auto & query = query_ptr->as<const ASTCreateQuotaQuery &>(); const auto & query = query_ptr->as<const ASTCreateQuotaQuery &>();
auto & access_control = context.getAccessControlManager(); auto & access_control = context.getAccessControlManager();
context.checkAccess(query.alter ? AccessType::ALTER_QUOTA : AccessType::CREATE_QUOTA);
if (query.alter) if (query.alter)
{ {

View File

@ -4,6 +4,7 @@
#include <Parsers/formatAST.h> #include <Parsers/formatAST.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Access/AccessControlManager.h> #include <Access/AccessControlManager.h>
#include <Access/AccessFlags.h>
#include <boost/range/algorithm/sort.hpp> #include <boost/range/algorithm/sort.hpp>
@ -11,9 +12,9 @@ namespace DB
{ {
BlockIO InterpreterCreateRowPolicyQuery::execute() BlockIO InterpreterCreateRowPolicyQuery::execute()
{ {
context.checkRowPolicyManagementIsAllowed();
const auto & query = query_ptr->as<const ASTCreateRowPolicyQuery &>(); const auto & query = query_ptr->as<const ASTCreateRowPolicyQuery &>();
auto & access_control = context.getAccessControlManager(); auto & access_control = context.getAccessControlManager();
context.checkAccess(query.alter ? AccessType::ALTER_POLICY : AccessType::CREATE_POLICY);
if (query.alter) if (query.alter)
{ {

View File

@ -10,6 +10,7 @@
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Interpreters/InterpreterDescribeQuery.h> #include <Interpreters/InterpreterDescribeQuery.h>
#include <Interpreters/IdentifierSemantic.h> #include <Interpreters/IdentifierSemantic.h>
#include <Access/AccessFlags.h>
#include <Parsers/ASTIdentifier.h> #include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTFunction.h> #include <Parsers/ASTFunction.h>
#include <Parsers/ASTTablesInSelectQuery.h> #include <Parsers/ASTTablesInSelectQuery.h>
@ -89,6 +90,9 @@ BlockInputStreamPtr InterpreterDescribeQuery::executeImpl()
String table_name; String table_name;
std::tie(database_name, table_name) = IdentifierSemantic::extractDatabaseAndTable(identifier); std::tie(database_name, table_name) = IdentifierSemantic::extractDatabaseAndTable(identifier);
if (!database_name.empty() || !context.isExternalTableExist(table_name))
context.checkAccess(AccessType::SHOW, database_name, table_name);
table = context.getTable(database_name, table_name); table = context.getTable(database_name, table_name);
} }

View File

@ -2,6 +2,7 @@
#include <Parsers/ASTDropAccessEntityQuery.h> #include <Parsers/ASTDropAccessEntityQuery.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Access/AccessControlManager.h> #include <Access/AccessControlManager.h>
#include <Access/AccessFlags.h>
#include <Access/Quota.h> #include <Access/Quota.h>
#include <Access/RowPolicy.h> #include <Access/RowPolicy.h>
#include <boost/range/algorithm/transform.hpp> #include <boost/range/algorithm/transform.hpp>
@ -19,7 +20,7 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
{ {
case Kind::QUOTA: case Kind::QUOTA:
{ {
context.checkQuotaManagementIsAllowed(); context.checkAccess(AccessType::DROP_QUOTA);
if (query.if_exists) if (query.if_exists)
access_control.tryRemove(access_control.find<Quota>(query.names)); access_control.tryRemove(access_control.find<Quota>(query.names));
else else
@ -28,7 +29,7 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
} }
case Kind::ROW_POLICY: case Kind::ROW_POLICY:
{ {
context.checkRowPolicyManagementIsAllowed(); context.checkAccess(AccessType::DROP_POLICY);
Strings full_names; Strings full_names;
boost::range::transform( boost::range::transform(
query.row_policies_names, std::back_inserter(full_names), query.row_policies_names, std::back_inserter(full_names),

View File

@ -5,6 +5,7 @@
#include <Interpreters/DDLWorker.h> #include <Interpreters/DDLWorker.h>
#include <Interpreters/InterpreterDropQuery.h> #include <Interpreters/InterpreterDropQuery.h>
#include <Interpreters/ExternalDictionariesLoader.h> #include <Interpreters/ExternalDictionariesLoader.h>
#include <Access/AccessRightsElement.h>
#include <Parsers/ASTDropQuery.h> #include <Parsers/ASTDropQuery.h>
#include <Storages/IStorage.h> #include <Storages/IStorage.h>
#include <Common/escapeForFileName.h> #include <Common/escapeForFileName.h>
@ -35,11 +36,8 @@ InterpreterDropQuery::InterpreterDropQuery(const ASTPtr & query_ptr_, Context &
BlockIO InterpreterDropQuery::execute() BlockIO InterpreterDropQuery::execute()
{ {
auto & drop = query_ptr->as<ASTDropQuery &>(); auto & drop = query_ptr->as<ASTDropQuery &>();
checkAccess(drop);
if (!drop.cluster.empty()) if (!drop.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, context, {drop.database}); return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccessForDDLOnCluster());
if (!drop.table.empty()) if (!drop.table.empty())
{ {
@ -56,60 +54,70 @@ BlockIO InterpreterDropQuery::execute()
BlockIO InterpreterDropQuery::executeToTable( BlockIO InterpreterDropQuery::executeToTable(
String & database_name_, const String & database_name_,
String & table_name, const String & table_name,
ASTDropQuery::Kind kind, ASTDropQuery::Kind kind,
bool if_exists, bool if_exists,
bool if_temporary, bool is_temporary,
bool no_ddl_lock) bool no_ddl_lock)
{ {
if (if_temporary || database_name_.empty()) if (is_temporary || database_name_.empty())
{ {
auto & session_context = context.hasSessionContext() ? context.getSessionContext() : context; auto & session_context = context.hasSessionContext() ? context.getSessionContext() : context;
if (session_context.isExternalTableExist(table_name)) if (session_context.isExternalTableExist(table_name))
return executeToTemporaryTable(table_name, kind); return executeToTemporaryTable(table_name, kind);
} }
if (is_temporary)
{
if (if_exists)
return {};
throw Exception("Temporary table " + backQuoteIfNeed(table_name) + " doesn't exist.",
ErrorCodes::UNKNOWN_TABLE);
}
String database_name = database_name_.empty() ? context.getCurrentDatabase() : database_name_; String database_name = database_name_.empty() ? context.getCurrentDatabase() : database_name_;
auto ddl_guard = (!no_ddl_lock ? context.getDDLGuard(database_name, table_name) : nullptr); auto ddl_guard = (!no_ddl_lock ? context.getDDLGuard(database_name, table_name) : nullptr);
DatabaseAndTable database_and_table = tryGetDatabaseAndTable(database_name, table_name, if_exists); auto [database, table] = tryGetDatabaseAndTable(database_name, table_name, if_exists);
if (database_and_table.first && database_and_table.second) if (database && table)
{ {
auto table_id = database_and_table.second->getStorageID(); auto table_id = table->getStorageID();
if (kind == ASTDropQuery::Kind::Detach) if (kind == ASTDropQuery::Kind::Detach)
{ {
database_and_table.second->shutdown(); context.checkAccess(table->isView() ? AccessType::DETACH_VIEW : AccessType::DETACH_TABLE,
database_name, table_name);
table->shutdown();
/// If table was already dropped by anyone, an exception will be thrown /// If table was already dropped by anyone, an exception will be thrown
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId()); auto table_lock = table->lockExclusively(context.getCurrentQueryId());
/// Drop table from memory, don't touch data and metadata /// Drop table from memory, don't touch data and metadata
database_and_table.first->detachTable(table_id.table_name); database->detachTable(table_name);
} }
else if (kind == ASTDropQuery::Kind::Truncate) else if (kind == ASTDropQuery::Kind::Truncate)
{ {
database_and_table.second->checkTableCanBeDropped(); context.checkAccess(table->isView() ? AccessType::TRUNCATE_VIEW : AccessType::TRUNCATE_TABLE,
database_name, table_name);
table->checkTableCanBeDropped();
/// If table was already dropped by anyone, an exception will be thrown /// If table was already dropped by anyone, an exception will be thrown
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId()); auto table_lock = table->lockExclusively(context.getCurrentQueryId());
/// Drop table data, don't touch metadata /// Drop table data, don't touch metadata
database_and_table.second->truncate(query_ptr, context, table_lock); table->truncate(query_ptr, context, table_lock);
} }
else if (kind == ASTDropQuery::Kind::Drop) else if (kind == ASTDropQuery::Kind::Drop)
{ {
database_and_table.second->checkTableCanBeDropped(); context.checkAccess(table->isView() ? AccessType::DROP_VIEW : AccessType::DROP_TABLE,
database_name, table_name);
table->checkTableCanBeDropped();
database_and_table.second->shutdown(); table->shutdown();
/// If table was already dropped by anyone, an exception will be thrown /// If table was already dropped by anyone, an exception will be thrown
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId()); auto table_lock = table->lockExclusively(context.getCurrentQueryId());
const std::string metadata_file_without_extension =
database_and_table.first->getMetadataPath()
+ escapeForFileName(table_id.table_name);
const std::string metadata_file_without_extension = database->getMetadataPath() + escapeForFileName(table_id.table_name);
const auto prev_metadata_name = metadata_file_without_extension + ".sql"; const auto prev_metadata_name = metadata_file_without_extension + ".sql";
const auto drop_metadata_name = metadata_file_without_extension + ".sql.tmp_drop"; const auto drop_metadata_name = metadata_file_without_extension + ".sql.tmp_drop";
@ -120,7 +128,7 @@ BlockIO InterpreterDropQuery::executeToTable(
if (Poco::File(prev_metadata_name).exists()) if (Poco::File(prev_metadata_name).exists())
Poco::File(prev_metadata_name).renameTo(drop_metadata_name); Poco::File(prev_metadata_name).renameTo(drop_metadata_name);
/// Delete table data /// Delete table data
database_and_table.second->drop(table_lock); table->drop(table_lock);
} }
catch (...) catch (...)
{ {
@ -129,11 +137,11 @@ BlockIO InterpreterDropQuery::executeToTable(
throw; throw;
} }
String table_data_path_relative = database_and_table.first->getTableDataPath(table_name); String table_data_path_relative = database->getTableDataPath(table_name);
/// Delete table metadata and table itself from memory /// Delete table metadata and table itself from memory
database_and_table.first->removeTable(context, table_id.table_name); database->removeTable(context, table_name);
database_and_table.second->is_dropped = true; table->is_dropped = true;
/// If it is not virtual database like Dictionary then drop remaining data dir /// If it is not virtual database like Dictionary then drop remaining data dir
if (!table_data_path_relative.empty()) if (!table_data_path_relative.empty())
@ -150,8 +158,8 @@ BlockIO InterpreterDropQuery::executeToTable(
BlockIO InterpreterDropQuery::executeToDictionary( BlockIO InterpreterDropQuery::executeToDictionary(
String & database_name_, const String & database_name_,
String & dictionary_name, const String & dictionary_name,
ASTDropQuery::Kind kind, ASTDropQuery::Kind kind,
bool if_exists, bool if_exists,
bool is_temporary, bool is_temporary,
@ -179,6 +187,7 @@ BlockIO InterpreterDropQuery::executeToDictionary(
if (kind == ASTDropQuery::Kind::Detach) if (kind == ASTDropQuery::Kind::Detach)
{ {
/// Drop dictionary from memory, don't touch data and metadata /// Drop dictionary from memory, don't touch data and metadata
context.checkAccess(AccessType::DETACH_DICTIONARY, database_name, dictionary_name);
database->detachDictionary(dictionary_name, context); database->detachDictionary(dictionary_name, context);
} }
else if (kind == ASTDropQuery::Kind::Truncate) else if (kind == ASTDropQuery::Kind::Truncate)
@ -187,12 +196,13 @@ BlockIO InterpreterDropQuery::executeToDictionary(
} }
else if (kind == ASTDropQuery::Kind::Drop) else if (kind == ASTDropQuery::Kind::Drop)
{ {
context.checkAccess(AccessType::DROP_DICTIONARY, database_name, dictionary_name);
database->removeDictionary(context, dictionary_name); database->removeDictionary(context, dictionary_name);
} }
return {}; return {};
} }
BlockIO InterpreterDropQuery::executeToTemporaryTable(String & table_name, ASTDropQuery::Kind kind) BlockIO InterpreterDropQuery::executeToTemporaryTable(const String & table_name, ASTDropQuery::Kind kind)
{ {
if (kind == ASTDropQuery::Kind::Detach) if (kind == ASTDropQuery::Kind::Detach)
throw Exception("Unable to detach temporary table.", ErrorCodes::SYNTAX_ERROR); throw Exception("Unable to detach temporary table.", ErrorCodes::SYNTAX_ERROR);
@ -225,7 +235,7 @@ BlockIO InterpreterDropQuery::executeToTemporaryTable(String & table_name, ASTDr
return {}; return {};
} }
BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQuery::Kind kind, bool if_exists) BlockIO InterpreterDropQuery::executeToDatabase(const String & database_name, ASTDropQuery::Kind kind, bool if_exists)
{ {
auto ddl_guard = context.getDDLGuard(database_name, ""); auto ddl_guard = context.getDDLGuard(database_name, "");
@ -237,11 +247,14 @@ BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQ
} }
else if (kind == ASTDropQuery::Kind::Detach) else if (kind == ASTDropQuery::Kind::Detach)
{ {
context.checkAccess(AccessType::DETACH_DATABASE, database_name);
context.detachDatabase(database_name); context.detachDatabase(database_name);
database->shutdown(); database->shutdown();
} }
else if (kind == ASTDropQuery::Kind::Drop) else if (kind == ASTDropQuery::Kind::Drop)
{ {
context.checkAccess(AccessType::DROP_DATABASE, database_name);
for (auto iterator = database->getTablesIterator(context); iterator->isValid(); iterator->next()) for (auto iterator = database->getTablesIterator(context); iterator->isValid(); iterator->next())
{ {
String current_table_name = iterator->name(); String current_table_name = iterator->name();
@ -281,12 +294,12 @@ BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQ
return {}; return {};
} }
DatabasePtr InterpreterDropQuery::tryGetDatabase(String & database_name, bool if_exists) DatabasePtr InterpreterDropQuery::tryGetDatabase(const String & database_name, bool if_exists)
{ {
return if_exists ? context.tryGetDatabase(database_name) : context.getDatabase(database_name); return if_exists ? context.tryGetDatabase(database_name) : context.getDatabase(database_name);
} }
DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(String & database_name, String & table_name, bool if_exists) DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(const String & database_name, const String & table_name, bool if_exists)
{ {
DatabasePtr database = tryGetDatabase(database_name, if_exists); DatabasePtr database = tryGetDatabase(database_name, if_exists);
@ -302,20 +315,38 @@ DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(String & database_
return {}; return {};
} }
void InterpreterDropQuery::checkAccess(const ASTDropQuery & drop)
AccessRightsElements InterpreterDropQuery::getRequiredAccessForDDLOnCluster() const
{ {
const Settings & settings = context.getSettingsRef(); AccessRightsElements required_access;
auto readonly = settings.readonly; const auto & drop = query_ptr->as<const ASTDropQuery &>();
bool allow_ddl = settings.allow_ddl;
/// It's allowed to drop temporary tables. if (drop.table.empty())
if ((!readonly && allow_ddl) || (drop.database.empty() && context.tryGetExternalTable(drop.table) && readonly >= 2)) {
return; if (drop.kind == ASTDropQuery::Kind::Detach)
required_access.emplace_back(AccessType::DETACH_DATABASE, drop.database);
else if (drop.kind == ASTDropQuery::Kind::Drop)
required_access.emplace_back(AccessType::DROP_DATABASE, drop.database);
}
else if (drop.is_dictionary)
{
if (drop.kind == ASTDropQuery::Kind::Detach)
required_access.emplace_back(AccessType::DETACH_DICTIONARY, drop.database, drop.table);
else if (drop.kind == ASTDropQuery::Kind::Drop)
required_access.emplace_back(AccessType::DROP_DICTIONARY, drop.database, drop.table);
}
else if (!drop.temporary)
{
/// It can be view or table.
if (drop.kind == ASTDropQuery::Kind::Drop)
required_access.emplace_back(AccessType::DROP_TABLE | AccessType::DROP_VIEW, drop.database, drop.table);
else if (drop.kind == ASTDropQuery::Kind::Truncate)
required_access.emplace_back(AccessType::TRUNCATE_TABLE | AccessType::TRUNCATE_VIEW, drop.database, drop.table);
else if (drop.kind == ASTDropQuery::Kind::Detach)
required_access.emplace_back(AccessType::DETACH_TABLE | AccessType::DETACH_VIEW, drop.database, drop.table);
}
if (readonly) return required_access;
throw Exception("Cannot drop table in readonly mode", ErrorCodes::READONLY);
throw Exception("Cannot drop table. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
} }
} }

View File

@ -24,20 +24,20 @@ public:
BlockIO execute() override; BlockIO execute() override;
private: private:
void checkAccess(const ASTDropQuery & drop); AccessRightsElements getRequiredAccessForDDLOnCluster() const;
ASTPtr query_ptr; ASTPtr query_ptr;
Context & context; Context & context;
BlockIO executeToDatabase(String & database_name, ASTDropQuery::Kind kind, bool if_exists); BlockIO executeToDatabase(const String & database_name, ASTDropQuery::Kind kind, bool if_exists);
BlockIO executeToTable(String & database_name, String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool if_temporary, bool no_ddl_lock); BlockIO executeToTable(const String & database_name, const String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
BlockIO executeToDictionary(String & database_name, String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool if_temporary, bool no_ddl_lock); BlockIO executeToDictionary(const String & database_name, const String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
DatabasePtr tryGetDatabase(String & database_name, bool exists); DatabasePtr tryGetDatabase(const String & database_name, bool exists);
DatabaseAndTable tryGetDatabaseAndTable(String & database_name, String & table_name, bool if_exists); DatabaseAndTable tryGetDatabaseAndTable(const String & database_name, const String & table_name, bool if_exists);
BlockIO executeToTemporaryTable(String & table_name, ASTDropQuery::Kind kind); BlockIO executeToTemporaryTable(const String & table_name, ASTDropQuery::Kind kind);
}; };
} }

Some files were not shown because too many files have changed in this diff Show More