mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 08:40:50 +00:00
Merge branch 'master' of https://github.com/ClickHouse/ClickHouse into interactive-mode-for-clickhouse-local
This commit is contained in:
commit
2129230b1e
2
AUTHORS
2
AUTHORS
@ -1,2 +1,2 @@
|
||||
To see the list of authors who created the source code of ClickHouse, published and distributed by YANDEX LLC as the owner,
|
||||
To see the list of authors who created the source code of ClickHouse, published and distributed by ClickHouse, Inc. as the owner,
|
||||
run "SELECT * FROM system.contributors;" query on any ClickHouse server.
|
||||
|
@ -28,15 +28,16 @@ The following versions of ClickHouse server are currently being supported with s
|
||||
| 21.3 | ✅ |
|
||||
| 21.4 | :x: |
|
||||
| 21.5 | :x: |
|
||||
| 21.6 | ✅ |
|
||||
| 21.6 | :x: |
|
||||
| 21.7 | ✅ |
|
||||
| 21.8 | ✅ |
|
||||
| 21.9 | ✅ |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We're extremely grateful for security researchers and users that report vulnerabilities to the ClickHouse Open Source Community. All reports are thoroughly investigated by developers.
|
||||
|
||||
To report a potential vulnerability in ClickHouse please send the details about it to [clickhouse-feedback@yandex-team.com](mailto:clickhouse-feedback@yandex-team.com).
|
||||
To report a potential vulnerability in ClickHouse please send the details about it to [security@clickhouse.com](mailto:security@clickhouse.com).
|
||||
|
||||
### When Should I Report a Vulnerability?
|
||||
|
||||
|
2
contrib/libhdfs3
vendored
2
contrib/libhdfs3
vendored
@ -1 +1 @@
|
||||
Subproject commit 095b9d48b400abb72d967cb0539af13b1e3d90cf
|
||||
Subproject commit 082e55f17d1c58bf124290fb044fea40e985ec11
|
9
debian/control
vendored
9
debian/control
vendored
@ -1,16 +1,13 @@
|
||||
Source: clickhouse
|
||||
Section: database
|
||||
Priority: optional
|
||||
Maintainer: Alexey Milovidov <milovidov@yandex-team.ru>
|
||||
Maintainer: Alexey Milovidov <milovidov@clickhouse.com>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
cmake | cmake3,
|
||||
ninja-build,
|
||||
clang-11,
|
||||
llvm-11,
|
||||
clang-13,
|
||||
llvm-13,
|
||||
libc6-dev,
|
||||
libicu-dev,
|
||||
libreadline-dev,
|
||||
gperf,
|
||||
tzdata
|
||||
Standards-Version: 3.9.8
|
||||
|
||||
|
@ -6,4 +6,4 @@ toc_title: Cloud
|
||||
# ClickHouse Cloud Service {#clickhouse-cloud-service}
|
||||
|
||||
!!! info "Info"
|
||||
Detailed public description for ClickHouse cloud services is not ready yet, please [contact us](/company/#contact) to learn more.
|
||||
Detailed public description for ClickHouse cloud services is not ready yet, please [contact us](https://clickhouse.com/company/#contact) to learn more.
|
||||
|
@ -6,4 +6,4 @@ toc_title: Support
|
||||
# ClickHouse Commercial Support Service {#clickhouse-commercial-support-service}
|
||||
|
||||
!!! info "Info"
|
||||
Detailed public description for ClickHouse support services is not ready yet, please [contact us](/company/#contact) to learn more.
|
||||
Detailed public description for ClickHouse support services is not ready yet, please [contact us](https://clickhouse.com/company/#contact) to learn more.
|
||||
|
@ -114,15 +114,25 @@ To do so, create the `/Library/LaunchDaemons/limit.maxfiles.plist` file with the
|
||||
</plist>
|
||||
```
|
||||
|
||||
Execute the following command:
|
||||
Give the file correct permissions:
|
||||
|
||||
``` bash
|
||||
sudo chown root:wheel /Library/LaunchDaemons/limit.maxfiles.plist
|
||||
```
|
||||
|
||||
Reboot.
|
||||
Validate that the file is correct:
|
||||
|
||||
To check if it’s working, you can use `ulimit -n` command.
|
||||
``` bash
|
||||
plutil /Library/LaunchDaemons/limit.maxfiles.plist
|
||||
```
|
||||
|
||||
Load the file (or reboot):
|
||||
|
||||
``` bash
|
||||
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
|
||||
```
|
||||
|
||||
To check if it’s working, use the `ulimit -n` or `launchctl limit maxfiles` commands.
|
||||
|
||||
## Run ClickHouse server:
|
||||
|
||||
|
@ -100,8 +100,8 @@ For a description of parameters, see the [CREATE query description](../../../sql
|
||||
- `min_merge_bytes_to_use_direct_io` — The minimum data volume for merge operation that is required for using direct I/O access to the storage disk. When merging data parts, ClickHouse calculates the total storage volume of all the data to be merged. If the volume exceeds `min_merge_bytes_to_use_direct_io` bytes, ClickHouse reads and writes the data to the storage disk using the direct I/O interface (`O_DIRECT` option). If `min_merge_bytes_to_use_direct_io = 0`, then direct I/O is disabled. Default value: `10 * 1024 * 1024 * 1024` bytes.
|
||||
<a name="mergetree_setting-merge_with_ttl_timeout"></a>
|
||||
- `merge_with_ttl_timeout` — Minimum delay in seconds before repeating a merge with delete TTL. Default value: `14400` seconds (4 hours).
|
||||
- `merge_with_recompression_ttl_timeout` — Minimum delay in seconds before repeating a merge with recompression TTL. Default value: `14400` seconds (4 hours).
|
||||
- `try_fetch_recompressed_part_timeout` — Timeout (in seconds) before starting merge with recompression. During this time ClickHouse tries to fetch recompressed part from replica which assigned this merge with recompression. Default value: `7200` seconds (2 hours).
|
||||
- `merge_with_recompression_ttl_timeout` — Minimum delay in seconds before repeating a merge with recompression TTL. Default value: `14400` seconds (4 hours).
|
||||
- `try_fetch_recompressed_part_timeout` — Timeout (in seconds) before starting merge with recompression. During this time ClickHouse tries to fetch recompressed part from replica which assigned this merge with recompression. Default value: `7200` seconds (2 hours).
|
||||
- `write_final_mark` — Enables or disables writing the final index mark at the end of data part (after the last byte). Default value: 1. Don’t turn it off.
|
||||
- `merge_max_block_size` — Maximum number of rows in block for merge operations. Default value: 8192.
|
||||
- `storage_policy` — Storage policy. See [Using Multiple Block Devices for Data Storage](#table_engine-mergetree-multiple-volumes).
|
||||
@ -335,7 +335,16 @@ SELECT count() FROM table WHERE u64 * i32 == 10 AND u64 * length(s) >= 1234
|
||||
|
||||
The optional `false_positive` parameter is the probability of receiving a false positive response from the filter. Possible values: (0, 1). Default value: 0.025.
|
||||
|
||||
Supported data types: `Int*`, `UInt*`, `Float*`, `Enum`, `Date`, `DateTime`, `String`, `FixedString`, `Array`, `LowCardinality`, `Nullable`, `UUID`.
|
||||
Supported data types: `Int*`, `UInt*`, `Float*`, `Enum`, `Date`, `DateTime`, `String`, `FixedString`, `Array`, `LowCardinality`, `Nullable`, `UUID`, `Map`.
|
||||
|
||||
For `Map` data type client can specify if index should be created for keys or values using [mapKeys](../../../sql-reference/functions/tuple-map-functions.md#mapkeys) or [mapValues](../../../sql-reference/functions/tuple-map-functions.md#mapvalues) function.
|
||||
|
||||
Example of index creation for `Map` data type
|
||||
|
||||
```
|
||||
INDEX map_key_index mapKeys(map_column) TYPE bloom_filter GRANULARITY 1
|
||||
INDEX map_key_index mapValues(map_column) TYPE bloom_filter GRANULARITY 1
|
||||
```
|
||||
|
||||
The following functions can use it: [equals](../../../sql-reference/functions/comparison-functions.md), [notEquals](../../../sql-reference/functions/comparison-functions.md), [in](../../../sql-reference/functions/in-functions.md), [notIn](../../../sql-reference/functions/in-functions.md), [has](../../../sql-reference/functions/array-functions.md).
|
||||
|
||||
@ -398,7 +407,7 @@ Projections are an experimental feature. To enable them you must set the [allow_
|
||||
Projections are not supported in the `SELECT` statements with the [FINAL](../../../sql-reference/statements/select/from.md#select-from-final) modifier.
|
||||
|
||||
### Projection Query {#projection-query}
|
||||
A projection query is what defines a projection. It implicitly selects data from the parent table.
|
||||
A projection query is what defines a projection. It implicitly selects data from the parent table.
|
||||
**Syntax**
|
||||
|
||||
```sql
|
||||
@ -548,7 +557,7 @@ ORDER BY d
|
||||
TTL d + INTERVAL 1 MONTH DELETE WHERE toDayOfWeek(d) = 1;
|
||||
```
|
||||
|
||||
Creating a table, where expired rows are recompressed:
|
||||
Creating a table, where expired rows are recompressed:
|
||||
|
||||
```sql
|
||||
CREATE TABLE table_for_recompression
|
||||
|
@ -7,5 +7,5 @@ toc_title: GitHub Events
|
||||
|
||||
Dataset contains all events on GitHub from 2011 to Dec 6 2020, the size is 3.1 billion records. Download size is 75 GB and it will require up to 200 GB space on disk if stored in a table with lz4 compression.
|
||||
|
||||
Full dataset description, insights, download instruction and interactive queries are posted [here](https://github-sql.github.io/explorer/).
|
||||
Full dataset description, insights, download instruction and interactive queries are posted [here](https://ghe.clickhouse.tech/).
|
||||
|
||||
|
@ -1553,18 +1553,20 @@ ClickHouse supports reading and writing [MessagePack](https://msgpack.org/) data
|
||||
|
||||
### Data Types Matching {#data-types-matching-msgpack}
|
||||
|
||||
| MsgPack data type | ClickHouse data type |
|
||||
|---------------------------------|----------------------------------------------------------------------------------|
|
||||
| `uint N`, `positive fixint` | [UIntN](../sql-reference/data-types/int-uint.md) |
|
||||
| `int N` | [IntN](../sql-reference/data-types/int-uint.md) |
|
||||
| `fixstr`, `str 8`, `str 16`, `str 32` | [String](../sql-reference/data-types/string.md), [FixedString](../sql-reference/data-types/fixedstring.md) |
|
||||
| `float 32` | [Float32](../sql-reference/data-types/float.md) |
|
||||
| `float 64` | [Float64](../sql-reference/data-types/float.md) |
|
||||
| `uint 16` | [Date](../sql-reference/data-types/date.md) |
|
||||
| `uint 32` | [DateTime](../sql-reference/data-types/datetime.md) |
|
||||
| `uint 64` | [DateTime64](../sql-reference/data-types/datetime.md) |
|
||||
| `fixarray`, `array 16`, `array 32`| [Array](../sql-reference/data-types/array.md) |
|
||||
| `nil` | [Nothing](../sql-reference/data-types/special-data-types/nothing.md) |
|
||||
| MessagePack data type (`INSERT`) | ClickHouse data type | MessagePack data type (`SELECT`) |
|
||||
|--------------------------------------------------------------------|-----------------------------------------------------------|------------------------------------|
|
||||
| `uint N`, `positive fixint` | [UIntN](../sql-reference/data-types/int-uint.md) | `uint N` |
|
||||
| `int N` | [IntN](../sql-reference/data-types/int-uint.md) | `int N` |
|
||||
| `bool` | [UInt8](../sql-reference/data-types/int-uint.md) | `uint 8` |
|
||||
| `fixstr`, `str 8`, `str 16`, `str 32`, `bin 8`, `bin 16`, `bin 32` | [String](../sql-reference/data-types/string.md) | `bin 8`, `bin 16`, `bin 32` |
|
||||
| `fixstr`, `str 8`, `str 16`, `str 32`, `bin 8`, `bin 16`, `bin 32` | [FixedString](../sql-reference/data-types/fixedstring.md) | `bin 8`, `bin 16`, `bin 32` |
|
||||
| `float 32` | [Float32](../sql-reference/data-types/float.md) | `float 32` |
|
||||
| `float 64` | [Float64](../sql-reference/data-types/float.md) | `float 64` |
|
||||
| `uint 16` | [Date](../sql-reference/data-types/date.md) | `uint 16` |
|
||||
| `uint 32` | [DateTime](../sql-reference/data-types/datetime.md) | `uint 32` |
|
||||
| `uint 64` | [DateTime64](../sql-reference/data-types/datetime.md) | `uint 64` |
|
||||
| `fixarray`, `array 16`, `array 32` | [Array](../sql-reference/data-types/array.md) | `fixarray`, `array 16`, `array 32` |
|
||||
| `fixmap`, `map 16`, `map 32` | [Map](../sql-reference/data-types/map.md) | `fixmap`, `map 16`, `map 32` |
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -810,7 +810,7 @@ If ClickHouse should read more than `merge_tree_max_bytes_to_use_cache` bytes in
|
||||
|
||||
The cache of uncompressed blocks stores data extracted for queries. ClickHouse uses this cache to speed up responses to repeated small queries. This setting protects the cache from trashing by queries that read a large amount of data. The [uncompressed_cache_size](../../operations/server-configuration-parameters/settings.md#server-settings-uncompressed_cache_size) server setting defines the size of the cache of uncompressed blocks.
|
||||
|
||||
Possible value:
|
||||
Possible values:
|
||||
|
||||
- Any positive integer.
|
||||
|
||||
@ -818,23 +818,23 @@ Default value: 2013265920.
|
||||
|
||||
## merge_tree_clear_old_temporary_directories_interval_seconds {#setting-merge-tree-clear-old-temporary-directories-interval-seconds}
|
||||
|
||||
The interval in seconds for ClickHouse to execute the cleanup old temporary directories.
|
||||
Sets the interval in seconds for ClickHouse to execute the cleanup of old temporary directories.
|
||||
|
||||
Possible value:
|
||||
Possible values:
|
||||
|
||||
- Any positive integer.
|
||||
|
||||
Default value: 60.
|
||||
Default value: `60` seconds.
|
||||
|
||||
## merge_tree_clear_old_parts_interval_seconds {#setting-merge-tree-clear-old-parts-interval-seconds}
|
||||
|
||||
The interval in seconds for ClickHouse to execute the cleanup old parts, WALs, and mutations.
|
||||
Sets the interval in seconds for ClickHouse to execute the cleanup of old parts, WALs, and mutations.
|
||||
|
||||
Possible value:
|
||||
Possible values:
|
||||
|
||||
- Any positive integer.
|
||||
|
||||
Default value: 1.
|
||||
Default value: `1` second.
|
||||
|
||||
## min_bytes_to_use_direct_io {#settings-min-bytes-to-use-direct-io}
|
||||
|
||||
@ -3604,6 +3604,30 @@ Possible values:
|
||||
|
||||
Default value: `1000`.
|
||||
|
||||
## log_queries_probability {#log-queries-probability}
|
||||
|
||||
Allows a user to write to [query_log](../../operations/system-tables/query_log.md), [query_thread_log](../../operations/system-tables/query_thread_log.md), and [query_views_log](../../operations/system-tables/query_views_log.md) system tables only a sample of queries selected randomly with the specified probability. It helps to reduce the load with a large volume of queries in a second.
|
||||
|
||||
Possible values:
|
||||
|
||||
- 0 — Queries are not logged in the system tables.
|
||||
- Positive floating-point number in the range [0..1]. For example, if the setting value is `0.5`, about half of the queries are logged in the system tables.
|
||||
- 1 — All queries are logged in the system tables.
|
||||
|
||||
Default value: `1`.
|
||||
|
||||
## short_circuit_function_evaluation {#short-circuit-function-evaluation}
|
||||
|
||||
Allows calculating the [if](../../sql-reference/functions/conditional-functions.md#if), [multiIf](../../sql-reference/functions/conditional-functions.md#multiif), [and](../../sql-reference/functions/logical-functions.md#logical-and-function), and [or](../../sql-reference/functions/logical-functions.md#logical-or-function) functions according to a [short scheme](https://en.wikipedia.org/wiki/Short-circuit_evaluation). This helps optimize the execution of complex expressions in these functions and prevent possible exceptions (such as division by zero when it is not expected).
|
||||
|
||||
Possible values:
|
||||
|
||||
- `enable` — Enables short-circuit function evaluation for functions that are suitable for it (can throw an exception or computationally heavy).
|
||||
- `force_enable` — Enables short-circuit function evaluation for all functions.
|
||||
- `disable` — Disables short-circuit function evaluation.
|
||||
|
||||
Default value: `enable`.
|
||||
|
||||
## max_hyperscan_regexp_length {#max-hyperscan-regexp-length}
|
||||
|
||||
Defines the maximum length for each regular expression in the [hyperscan multi-match functions](../../sql-reference/functions/string-search-functions.md#multimatchanyhaystack-pattern1-pattern2-patternn).
|
||||
@ -3629,7 +3653,6 @@ Result:
|
||||
┌─multiMatchAny('abcd', ['ab', 'bcd', 'c', 'd'])─┐
|
||||
│ 1 │
|
||||
└────────────────────────────────────────────────┘
|
||||
|
||||
```
|
||||
|
||||
Query:
|
||||
@ -3648,7 +3671,6 @@ Exception: Regexp length too large.
|
||||
|
||||
- [max_hyperscan_regexp_total_length](#max-hyperscan-regexp-total-length)
|
||||
|
||||
|
||||
## max_hyperscan_regexp_total_length {#max-hyperscan-regexp-total-length}
|
||||
|
||||
Sets the maximum length total of all regular expressions in each [hyperscan multi-match function](../../sql-reference/functions/string-search-functions.md#multimatchanyhaystack-pattern1-pattern2-patternn).
|
||||
|
@ -24,6 +24,8 @@ Each query creates one or two rows in the `query_log` table, depending on the st
|
||||
2. If an error occurred during query processing, two events with the `QueryStart` and `ExceptionWhileProcessing` types are created.
|
||||
3. If an error occurred before launching the query, a single event with the `ExceptionBeforeStart` type is created.
|
||||
|
||||
You can use the [log_queries_probability](../../operations/settings/settings.md#log-queries-probability) setting to reduce the number of queries, registered in the `query_log` table.
|
||||
|
||||
Columns:
|
||||
|
||||
- `type` ([Enum8](../../sql-reference/data-types/enum.md)) — Type of an event that occurred when executing the query. Values:
|
||||
|
@ -11,6 +11,8 @@ The flushing period of data is set in `flush_interval_milliseconds` parameter of
|
||||
|
||||
ClickHouse does not delete data from the table automatically. See [Introduction](../../operations/system-tables/index.md#system-tables-introduction) for more details.
|
||||
|
||||
You can use the [log_queries_probability](../../operations/settings/settings.md#log-queries-probability) setting to reduce the number of queries, registered in the `query_thread_log` table.
|
||||
|
||||
Columns:
|
||||
|
||||
- `event_date` ([Date](../../sql-reference/data-types/date.md)) — The date when the thread has finished execution of the query.
|
||||
|
@ -11,6 +11,8 @@ The flushing period of data is set in `flush_interval_milliseconds` parameter of
|
||||
|
||||
ClickHouse does not delete data from the table automatically. See [Introduction](../../operations/system-tables/index.md#system-tables-introduction) for more details.
|
||||
|
||||
You can use the [log_queries_probability](../../operations/settings/settings.md#log-queries-probability) setting to reduce the number of queries, registered in the `query_views_log` table.
|
||||
|
||||
Columns:
|
||||
|
||||
- `event_date` ([Date](../../sql-reference/data-types/date.md)) — The date when the last event of the view happened.
|
||||
@ -43,10 +45,14 @@ Columns:
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
``` sql
|
||||
SELECT * FROM system.query_views_log LIMIT 1 \G
|
||||
SELECT * FROM system.query_views_log LIMIT 1 \G;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
``` text
|
||||
Row 1:
|
||||
──────
|
||||
@ -77,5 +83,4 @@ stack_trace:
|
||||
- [system.query_log](../../operations/system-tables/query_log.md#system_tables-query_log) — Description of the `query_log` system table which contains common information about queries execution.
|
||||
- [system.query_thread_log](../../operations/system-tables/query_thread_log.md#system_tables-query_thread_log) — This table contains information about each query execution thread.
|
||||
|
||||
|
||||
[Original article](https://clickhouse.com/docs/en/operations/system_tables/query_thread_log) <!--hide-->
|
||||
|
@ -66,6 +66,8 @@ ClickHouse-specific aggregate functions:
|
||||
- [quantileDeterministic](../../../sql-reference/aggregate-functions/reference/quantiledeterministic.md)
|
||||
- [quantileTDigest](../../../sql-reference/aggregate-functions/reference/quantiletdigest.md)
|
||||
- [quantileTDigestWeighted](../../../sql-reference/aggregate-functions/reference/quantiletdigestweighted.md)
|
||||
- [quantileBFloat16](../../../sql-reference/aggregate-functions/reference/quantilebfloat16.md#quantilebfloat16)
|
||||
- [quantileBFloat16Weighted](../../../sql-reference/aggregate-functions/reference/quantilebfloat16.md#quantilebfloat16weighted)
|
||||
- [simpleLinearRegression](../../../sql-reference/aggregate-functions/reference/simplelinearregression.md)
|
||||
- [stochasticLinearRegression](../../../sql-reference/aggregate-functions/reference/stochasticlinearregression.md)
|
||||
- [stochasticLogisticRegression](../../../sql-reference/aggregate-functions/reference/stochasticlogisticregression.md)
|
||||
|
@ -58,6 +58,10 @@ Result:
|
||||
```
|
||||
Note that all floating point values in the example are truncated to 1.0 when converting to `bfloat16`.
|
||||
|
||||
# quantileBFloat16Weighted {#quantilebfloat16weighted}
|
||||
|
||||
Like `quantileBFloat16` but takes into account the weight of each sequence member.
|
||||
|
||||
**See Also**
|
||||
|
||||
- [median](../../../sql-reference/aggregate-functions/reference/median.md#median)
|
||||
|
@ -107,7 +107,7 @@ bitmapSubsetLimit(bitmap, range_start, cardinality_limit)
|
||||
|
||||
The subset.
|
||||
|
||||
Type: `Bitmap object`.
|
||||
Type: [Bitmap object](#bitmap_functions-bitmapbuild).
|
||||
|
||||
**Example**
|
||||
|
||||
@ -125,9 +125,9 @@ Result:
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
## subBitmap {#subBitmap}
|
||||
## subBitmap {#subbitmap}
|
||||
|
||||
Creates a subset of bitmap limit the results to `cardinality_limit` with offset of `offset`.
|
||||
Returns the bitmap elements, starting from the `offset` position. The number of returned elements is limited by the `cardinality_limit` parameter. Analog of the [substring](string-functions.md#substring)) string function, but for bitmap.
|
||||
|
||||
**Syntax**
|
||||
|
||||
@ -137,15 +137,15 @@ subBitmap(bitmap, offset, cardinality_limit)
|
||||
|
||||
**Arguments**
|
||||
|
||||
- `bitmap` – [Bitmap object](#bitmap_functions-bitmapbuild).
|
||||
- `offset` – the number of offsets. Type: [UInt32](../../sql-reference/data-types/int-uint.md).
|
||||
- `cardinality_limit` – The subset cardinality upper limit. Type: [UInt32](../../sql-reference/data-types/int-uint.md).
|
||||
- `bitmap` – The bitmap. Type: [Bitmap object](#bitmap_functions-bitmapbuild).
|
||||
- `offset` – The position of the first element of the subset. Type: [UInt32](../../sql-reference/data-types/int-uint.md).
|
||||
- `cardinality_limit` – The maximum number of elements in the subset. Type: [UInt32](../../sql-reference/data-types/int-uint.md).
|
||||
|
||||
**Returned value**
|
||||
|
||||
The subset.
|
||||
|
||||
Type: `Bitmap object`.
|
||||
Type: [Bitmap object](#bitmap_functions-bitmapbuild).
|
||||
|
||||
**Example**
|
||||
|
||||
|
@ -12,11 +12,13 @@ Controls conditional branching. Unlike most systems, ClickHouse always evaluate
|
||||
**Syntax**
|
||||
|
||||
``` sql
|
||||
SELECT if(cond, then, else)
|
||||
if(cond, then, else)
|
||||
```
|
||||
|
||||
If the condition `cond` evaluates to a non-zero value, returns the result of the expression `then`, and the result of the expression `else`, if present, is skipped. If the `cond` is zero or `NULL`, then the result of the `then` expression is skipped and the result of the `else` expression, if present, is returned.
|
||||
|
||||
You can use the [short_circuit_function_evaluation](../../operations/settings/settings.md#short-circuit-function-evaluation) setting to calculate the `if` function according to a short scheme. If this setting is enabled, `then` expression is evaluated only on rows where `cond` is true, `else` expression – where `cond` is false. For example, an exception about division by zero is not thrown when executing the query `SELECT if(number = 0, 0, intDiv(42, number)) FROM numbers(10)`, because `intDiv(42, number)` will be evaluated only for numbers that doesn't satisfy condition `number = 0`.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- `cond` – The condition for evaluation that can be zero or not. The type is UInt8, Nullable(UInt8) or NULL.
|
||||
@ -115,9 +117,15 @@ Returns `then` if the `cond` evaluates to be true (greater than zero), otherwise
|
||||
|
||||
Allows you to write the [CASE](../../sql-reference/operators/index.md#operator_case) operator more compactly in the query.
|
||||
|
||||
Syntax: `multiIf(cond_1, then_1, cond_2, then_2, ..., else)`
|
||||
**Syntax**
|
||||
|
||||
**Arguments:**
|
||||
``` sql
|
||||
multiIf(cond_1, then_1, cond_2, then_2, ..., else)
|
||||
```
|
||||
|
||||
You can use the [short_circuit_function_evaluation](../../operations/settings/settings.md#short-circuit-function-evaluation) setting to calculate the `multiIf` function according to a short scheme. If this setting is enabled, `then_i` expression is evaluated only on rows where `((NOT cond_1) AND (NOT cond_2) AND ... AND (NOT cond_{i-1}) AND cond_i)` is true, `cond_i` will be evaluated only on rows where `((NOT cond_1) AND (NOT cond_2) AND ... AND (NOT cond_{i-1}))` is true. For example, an exception about division by zero is not thrown when executing the query `SELECT multiIf(number = 2, intDiv(1, number), number = 5) FROM numbers(10)`.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- `cond_N` — The condition for the function to return `then_N`.
|
||||
- `then_N` — The result of the function when executed.
|
||||
@ -201,4 +209,3 @@ FROM LEFT_RIGHT
|
||||
│ 4 │ ᴺᵁᴸᴸ │ Both equal │
|
||||
└──────┴───────┴──────────────────┘
|
||||
```
|
||||
|
||||
|
@ -594,4 +594,114 @@ Result:
|
||||
└─────┘
|
||||
```
|
||||
|
||||
## h3ResIsClassIII {#h3resisclassIII}
|
||||
|
||||
Returns whether [H3](#h3index) index has a resolution with Class III orientation.
|
||||
|
||||
**Syntax**
|
||||
|
||||
``` sql
|
||||
h3ResIsClassIII(index)
|
||||
```
|
||||
|
||||
**Parameter**
|
||||
|
||||
- `index` — Hexagon index number. Type: [UInt64](../../../sql-reference/data-types/int-uint.md).
|
||||
|
||||
**Returned value**
|
||||
|
||||
- `1` — Index has a resolution with Class III orientation.
|
||||
- `0` — Index doesn't have a resolution with Class III orientation.
|
||||
|
||||
Type: [UInt8](../../../sql-reference/data-types/int-uint.md).
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
``` sql
|
||||
SELECT h3ResIsClassIII(617420388352917503) as res;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
``` text
|
||||
┌─res─┐
|
||||
│ 1 │
|
||||
└─────┘
|
||||
```
|
||||
|
||||
## h3IsPentagon {#h3ispentagon }
|
||||
|
||||
Returns whether this [H3](#h3index) index represents a pentagonal cell.
|
||||
|
||||
**Syntax**
|
||||
|
||||
``` sql
|
||||
h3IsPentagon(index)
|
||||
```
|
||||
|
||||
**Parameter**
|
||||
|
||||
- `index` — Hexagon index number. Type: [UInt64](../../../sql-reference/data-types/int-uint.md).
|
||||
|
||||
**Returned value**
|
||||
|
||||
- `1` — Index represents a pentagonal cell.
|
||||
- `0` — Index doesn't represent a pentagonal cell.
|
||||
|
||||
Type: [UInt8](../../../sql-reference/data-types/int-uint.md).
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
``` sql
|
||||
SELECT SELECT h3IsPentagon(644721767722457330) as pentagon;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
``` text
|
||||
┌─pentagon─┐
|
||||
│ 0 │
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
## h3GetFaces {#h3getfaces}
|
||||
|
||||
Returns icosahedron faces intersected by a given [H3](#h3index) index.
|
||||
|
||||
**Syntax**
|
||||
|
||||
``` sql
|
||||
h3GetFaces(index)
|
||||
```
|
||||
|
||||
**Parameter**
|
||||
|
||||
- `index` — Hexagon index number. Type: [UInt64](../../../sql-reference/data-types/int-uint.md).
|
||||
|
||||
**Returned values**
|
||||
|
||||
- Array containing icosahedron faces intersected by a given H3 index.
|
||||
|
||||
Type: [Array](../../../sql-reference/data-types/array.md)([UInt64](../../../sql-reference/data-types/int-uint.md)).
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
``` sql
|
||||
SELECT SELECT h3GetFaces(599686042433355775) as faces;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
``` text
|
||||
┌─faces─┐
|
||||
│ [7] │
|
||||
└───────┘
|
||||
```
|
||||
|
||||
[Original article](https://clickhouse.com/docs/en/sql-reference/functions/geo/h3) <!--hide-->
|
||||
|
@ -19,6 +19,8 @@ Calculates the result of the logical conjunction between two or more values. Cor
|
||||
and(val1, val2...)
|
||||
```
|
||||
|
||||
You can use the [short_circuit_function_evaluation](../../operations/settings/settings.md#short-circuit-function-evaluation) setting to calculate the `and` function according to a short scheme. If this setting is enabled, `vali` is evaluated only on rows where `(val1 AND val2 AND ... AND val{i-1})` is true. For example, an exception about division by zero is not thrown when executing the query `SELECT and(number = 2, intDiv(1, number)) FROM numbers(10)`.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- `val1, val2, ...` — List of at least two values. [Int](../../sql-reference/data-types/int-uint.md), [UInt](../../sql-reference/data-types/int-uint.md), [Float](../../sql-reference/data-types/float.md) or [Nullable](../../sql-reference/data-types/nullable.md).
|
||||
@ -68,9 +70,11 @@ Calculates the result of the logical disjunction between two or more values. Cor
|
||||
**Syntax**
|
||||
|
||||
``` sql
|
||||
and(val1, val2...)
|
||||
or(val1, val2...)
|
||||
```
|
||||
|
||||
You can use the [short_circuit_function_evaluation](../../operations/settings/settings.md#short-circuit-function-evaluation) setting to calculate the `or` function according to a short scheme. If this setting is enabled, `vali` is evaluated only on rows where `((NOT val1) AND (NOT val2) AND ... AND (NOT val{i-1}))` is true. For example, an exception about division by zero is not thrown when executing the query `SELECT or(number = 0, intDiv(1, number) != 0) FROM numbers(10)`.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- `val1, val2, ...` — List of at least two values. [Int](../../sql-reference/data-types/int-uint.md), [UInt](../../sql-reference/data-types/int-uint.md), [Float](../../sql-reference/data-types/float.md) or [Nullable](../../sql-reference/data-types/nullable.md).
|
||||
|
@ -176,7 +176,7 @@ roundBankers(4.5) = 4
|
||||
roundBankers(3.55, 1) = 3.6
|
||||
roundBankers(3.65, 1) = 3.6
|
||||
roundBankers(10.35, 1) = 10.4
|
||||
roundBankers(10.755, 2) = 11,76
|
||||
roundBankers(10.755, 2) = 10.76
|
||||
```
|
||||
|
||||
**See Also**
|
||||
|
@ -11,7 +11,7 @@ ALTER TABLE [db].name [ON CLUSTER cluster] MODIFY ORDER BY new_expression
|
||||
|
||||
The command changes the [sorting key](../../../engines/table-engines/mergetree-family/mergetree.md) of the table to `new_expression` (an expression or a tuple of expressions). Primary key remains the same.
|
||||
|
||||
The command is lightweight in a sense that it only changes metadata. To keep the property that data part rows are ordered by the sorting key expression you cannot add expressions containing existing columns to the sorting key (only columns added by the `ADD COLUMN` command in the same `ALTER` query).
|
||||
The command is lightweight in a sense that it only changes metadata. To keep the property that data part rows are ordered by the sorting key expression you cannot add expressions containing existing columns to the sorting key (only columns added by the `ADD COLUMN` command in the same `ALTER` query, without default column value).
|
||||
|
||||
!!! note "Note"
|
||||
It only works for tables in the [`MergeTree`](../../../engines/table-engines/mergetree-family/mergetree.md) family (including [replicated](../../../engines/table-engines/mergetree-family/replication.md) tables).
|
||||
|
@ -104,6 +104,28 @@ There are many nuances to processing `NULL`. For example, if at least one of the
|
||||
|
||||
In queries, you can check `NULL` using the [IS NULL](../sql-reference/operators/index.md#operator-is-null) and [IS NOT NULL](../sql-reference/operators/index.md) operators and the related functions `isNull` and `isNotNull`.
|
||||
|
||||
### Heredoc {#heredeoc}
|
||||
|
||||
A [heredoc](https://en.wikipedia.org/wiki/Here_document) is a way to define a string (often multiline), while maintaining the original formatting. A heredoc is defined as a custom string literal, placed between two `$` symbols, for example `$heredoc$`. A value between two heredocs is processed "as-is".
|
||||
|
||||
You can use a heredoc to embed snippets of SQL, HTML, or XML code, etc.
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
```sql
|
||||
SELECT $smth$SHOW CREATE VIEW my_view$smth$;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```text
|
||||
┌─'SHOW CREATE VIEW my_view'─┐
|
||||
│ SHOW CREATE VIEW my_view │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## Functions {#functions}
|
||||
|
||||
Function calls are written like an identifier with a list of arguments (possibly empty) in round brackets. In contrast to standard SQL, the brackets are required, even for an empty argument list. Example: `now()`.
|
||||
|
@ -8,4 +8,4 @@ toc_title: "\u30AF\u30E9\u30A6\u30C9"
|
||||
# ClickHouse Cloud Service {#clickhouse-cloud-service}
|
||||
|
||||
!!! info "Info"
|
||||
Detailed public description for ClickHouse cloud services is not ready yet, please [contact us](/company/#contact) to learn more.
|
||||
Detailed public description for ClickHouse cloud services is not ready yet, please [contact us](https://clickhouse.com/company/#contact) to learn more.
|
||||
|
@ -6,4 +6,4 @@ toc_title: "Поставщики облачных услуг ClickHouse"
|
||||
# Поставщики облачных услуг ClickHouse {#clickhouse-cloud-service-providers}
|
||||
|
||||
!!! info "Info"
|
||||
Detailed public description for ClickHouse cloud services is not ready yet, please [contact us](/company/#contact) to learn more.
|
||||
Detailed public description for ClickHouse cloud services is not ready yet, please [contact us](https://clickhouse.com/company/#contact) to learn more.
|
||||
|
@ -801,12 +801,32 @@ ClickHouse может парсить только базовый формат `Y
|
||||
|
||||
Кэш несжатых блоков хранит данные, извлечённые при выполнении запросов. ClickHouse использует кэш для ускорения ответов на повторяющиеся небольшие запросы. Настройка защищает кэш от переполнения. Настройка сервера [uncompressed_cache_size](../server-configuration-parameters/settings.md#server-settings-uncompressed_cache_size) определяет размер кэша несжатых блоков.
|
||||
|
||||
Возможное значение:
|
||||
Возможные значения:
|
||||
|
||||
- Положительное целое число.
|
||||
|
||||
Значение по умолчанию: 2013265920.
|
||||
|
||||
## merge_tree_clear_old_temporary_directories_interval_seconds {#setting-merge-tree-clear-old-temporary-directories-interval-seconds}
|
||||
|
||||
Задает интервал в секундах для удаления старых временных каталогов на сервере ClickHouse.
|
||||
|
||||
Возможные значения:
|
||||
|
||||
- Положительное целое число.
|
||||
|
||||
Значение по умолчанию: `60` секунд.
|
||||
|
||||
## merge_tree_clear_old_parts_interval_seconds {#setting-merge-tree-clear-old-parts-interval-seconds}
|
||||
|
||||
Задает интервал в секундах для удаления старых кусков данных, журналов предзаписи (WAL) и мутаций на сервере ClickHouse .
|
||||
|
||||
Возможные значения:
|
||||
|
||||
- Положительное целое число.
|
||||
|
||||
Значение по умолчанию: `1` секунда.
|
||||
|
||||
## min_bytes_to_use_direct_io {#settings-min-bytes-to-use-direct-io}
|
||||
|
||||
Минимальный объём данных, необходимый для прямого (небуферизованного) чтения/записи (direct I/O) на диск.
|
||||
@ -3376,6 +3396,30 @@ SETTINGS index_granularity = 8192 │
|
||||
|
||||
Значение по умолчанию: `1000`.
|
||||
|
||||
## log_queries_probability {#log-queries-probability}
|
||||
|
||||
Позволяет пользователю записывать в системные таблицы [query_log](../../operations/system-tables/query_log.md), [query_thread_log](../../operations/system-tables/query_thread_log.md) и [query_views_log](../../operations/system-tables/query_views_log.md) только часть запросов, выбранных случайным образом, с указанной вероятностью. Это помогает снизить нагрузку при большом объеме запросов в секунду.
|
||||
|
||||
Возможные значения:
|
||||
|
||||
- 0 — запросы не регистрируются в системных таблицах.
|
||||
- Положительное число с плавающей точкой в диапазоне [0..1]. Например, при значении настройки, равном `0.5`, примерно половина запросов регистрируется в системных таблицах.
|
||||
- 1 — все запросы регистрируются в системных таблицах.
|
||||
|
||||
Значение по умолчанию: `1`.
|
||||
|
||||
## short_circuit_function_evaluation {#short-circuit-function-evaluation}
|
||||
|
||||
Позволяет вычислять функции [if](../../sql-reference/functions/conditional-functions.md#if), [multiIf](../../sql-reference/functions/conditional-functions.md#multiif), [and](../../sql-reference/functions/logical-functions.md#logical-and-function) и [or](../../sql-reference/functions/logical-functions.md#logical-or-function) по [короткой схеме](https://ru-wikipedia-org.turbopages.org/ru.wikipedia.org/s/wiki/Вычисления_по_короткой_схеме). Это помогает оптимизировать выполнение сложных выражений в этих функциях и предотвратить возможные исключения (например, деление на ноль, когда оно не ожидается).
|
||||
|
||||
Возможные значения:
|
||||
|
||||
- `enable` — по короткой схеме вычисляются функции, которые подходят для этого (могут сгенерировать исключение или требуют сложных вычислений).
|
||||
- `force_enable` — все функции вычисляются по короткой схеме.
|
||||
- `disable` — вычисление функций по короткой схеме отключено.
|
||||
|
||||
Значение по умолчанию: `enable`.
|
||||
|
||||
## max_hyperscan_regexp_length {#max-hyperscan-regexp-length}
|
||||
|
||||
Задает максимальную длину каждого регулярного выражения в [hyperscan-функциях](../../sql-reference/functions/string-search-functions.md#multimatchanyhaystack-pattern1-pattern2-patternn) поиска множественных совпадений в строке.
|
||||
@ -3401,7 +3445,6 @@ SELECT multiMatchAny('abcd', ['ab','bcd','c','d']) SETTINGS max_hyperscan_regexp
|
||||
┌─multiMatchAny('abcd', ['ab', 'bcd', 'c', 'd'])─┐
|
||||
│ 1 │
|
||||
└────────────────────────────────────────────────┘
|
||||
|
||||
```
|
||||
|
||||
Запрос:
|
||||
@ -3420,7 +3463,6 @@ Exception: Regexp length too large.
|
||||
|
||||
- [max_hyperscan_regexp_total_length](#max-hyperscan-regexp-total-length)
|
||||
|
||||
|
||||
## max_hyperscan_regexp_total_length {#max-hyperscan-regexp-total-length}
|
||||
|
||||
Задает максимальную общую длину всех регулярных выражений в каждой [hyperscan-функции](../../sql-reference/functions/string-search-functions.md#multimatchanyhaystack-pattern1-pattern2-patternn) поиска множественных совпадений в строке.
|
||||
|
@ -24,6 +24,8 @@ ClickHouse не удаляет данные из таблица автомати
|
||||
2. Если во время обработки запроса возникла ошибка, создаются два события с типами `QueryStart` и `ExceptionWhileProcessing`.
|
||||
3. Если ошибка произошла ещё до запуска запроса, создается одно событие с типом `ExceptionBeforeStart`.
|
||||
|
||||
Чтобы уменьшить количество запросов, регистрирующихся в таблице `query_log`, вы можете использовать настройку [log_queries_probability](../../operations/settings/settings.md#log-queries-probability).
|
||||
|
||||
Столбцы:
|
||||
|
||||
- `type` ([Enum8](../../sql-reference/data-types/enum.md)) — тип события, произошедшего при выполнении запроса. Значения:
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
ClickHouse не удаляет данные из таблицы автоматически. Подробности в разделе [Введение](#system-tables-introduction).
|
||||
|
||||
Чтобы уменьшить количество запросов, регистрирующихся в таблице `query_thread_log`, вы можете использовать настройку [log_queries_probability](../../operations/settings/settings.md#log-queries-probability).
|
||||
|
||||
Столбцы:
|
||||
|
||||
- `event_date` ([Date](../../sql-reference/data-types/date.md)) — дата завершения выполнения запроса потоком.
|
||||
|
@ -61,6 +61,8 @@ toc_hidden: true
|
||||
- [quantileDeterministic](../../../sql-reference/aggregate-functions/reference/quantiledeterministic.md)
|
||||
- [quantileTDigest](../../../sql-reference/aggregate-functions/reference/quantiletdigest.md)
|
||||
- [quantileTDigestWeighted](../../../sql-reference/aggregate-functions/reference/quantiletdigestweighted.md)
|
||||
- [quantileBFloat16](../../../sql-reference/aggregate-functions/reference/quantilebfloat16.md#quantilebfloat16)
|
||||
- [quantileBFloat16Weighted](../../../sql-reference/aggregate-functions/reference/quantilebfloat16.md#quantilebfloat16weighted)
|
||||
- [simpleLinearRegression](../../../sql-reference/aggregate-functions/reference/simplelinearregression.md)
|
||||
- [stochasticLinearRegression](../../../sql-reference/aggregate-functions/reference/stochasticlinearregression.md)
|
||||
- [stochasticLogisticRegression](../../../sql-reference/aggregate-functions/reference/stochasticlogisticregression.md)
|
||||
|
@ -58,6 +58,10 @@ SELECT quantileBFloat16(0.75)(a), quantileBFloat16(0.75)(b) FROM example_table;
|
||||
```
|
||||
Обратите внимание, что все числа с плавающей точкой в примере были округлены до 1.0 при преобразовании к `bfloat16`.
|
||||
|
||||
# quantileBFloat16Weighted {#quantilebfloat16weighted}
|
||||
|
||||
Версия функции `quantileBFloat16`, которая учитывает вес каждого элемента последовательности.
|
||||
|
||||
**См. также**
|
||||
|
||||
- [median](../../../sql-reference/aggregate-functions/reference/median.md#median)
|
||||
|
@ -66,15 +66,14 @@ bitmapSubsetLimit(bitmap, range_start, cardinality_limit)
|
||||
**Аргументы**
|
||||
|
||||
- `bitmap` – битмап. [Bitmap object](#bitmap_functions-bitmapbuild).
|
||||
|
||||
- `range_start` – начальная точка подмножества. [UInt32](../../sql-reference/functions/bitmap-functions.md#bitmap-functions).
|
||||
- `cardinality_limit` – Верхний предел подмножества. [UInt32](../../sql-reference/functions/bitmap-functions.md#bitmap-functions).
|
||||
- `cardinality_limit` – верхний предел подмножества. [UInt32](../../sql-reference/functions/bitmap-functions.md#bitmap-functions).
|
||||
|
||||
**Возвращаемое значение**
|
||||
|
||||
Подмножество битмапа.
|
||||
|
||||
Тип: `Bitmap object`.
|
||||
Тип: [Bitmap object](#bitmap_functions-bitmapbuild).
|
||||
|
||||
**Пример**
|
||||
|
||||
@ -92,6 +91,44 @@ SELECT bitmapToArray(bitmapSubsetLimit(bitmapBuild([0,1,2,3,4,5,6,7,8,9,10,11,12
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
## subBitmap {#subbitmap}
|
||||
|
||||
Возвращает элементы битмапа, начиная с позиции `offset`. Число возвращаемых элементов ограничивается параметром `cardinality_limit`. Аналог строковой функции [substring](string-functions.md#substring)), но для битмапа.
|
||||
|
||||
**Синтаксис**
|
||||
|
||||
``` sql
|
||||
subBitmap(bitmap, offset, cardinality_limit)
|
||||
```
|
||||
|
||||
**Аргументы**
|
||||
|
||||
- `bitmap` – битмап. Тип: [Bitmap object](#bitmap_functions-bitmapbuild).
|
||||
- `offset` – позиция первого элемента возвращаемого подмножества. Тип: [UInt32](../../sql-reference/data-types/int-uint.md).
|
||||
- `cardinality_limit` – максимальное число элементов возвращаемого подмножества. Тип: [UInt32](../../sql-reference/data-types/int-uint.md).
|
||||
|
||||
**Возвращаемое значение**
|
||||
|
||||
Подмножество битмапа.
|
||||
|
||||
Тип: [Bitmap object](#bitmap_functions-bitmapbuild).
|
||||
|
||||
**Пример**
|
||||
|
||||
Запрос:
|
||||
|
||||
``` sql
|
||||
SELECT bitmapToArray(subBitmap(bitmapBuild([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,100,200,500]), toUInt32(10), toUInt32(10))) AS res;
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
``` text
|
||||
┌─res─────────────────────────────┐
|
||||
│ [10,11,12,13,14,15,16,17,18,19] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## bitmapContains {#bitmap_functions-bitmapcontains}
|
||||
|
||||
Проверяет вхождение элемента в битовый массив.
|
||||
|
@ -12,11 +12,13 @@ toc_title: "Условные функции"
|
||||
**Синтаксис**
|
||||
|
||||
``` sql
|
||||
SELECT if(cond, then, else)
|
||||
if(cond, then, else)
|
||||
```
|
||||
|
||||
Если условие `cond` не равно нулю, то возвращается результат выражения `then`. Если условие `cond` равно нулю или является NULL, то результат выражения `then` пропускается и возвращается результат выражения `else`.
|
||||
|
||||
Чтобы вычислять функцию `if` по короткой схеме, используйте настройку [short_circuit_function_evaluation](../../operations/settings/settings.md#short-circuit-function-evaluation). Если настройка включена, то выражение `then` вычисляется только для строк, где условие `cond` верно, а выражение `else` – для строк, где условие `cond` неверно. Например, при выполнении запроса `SELECT if(number = 0, 0, intDiv(42, number)) FROM numbers(10)` не будет сгенерировано исключение из-за деления на ноль, так как `intDiv(42, number)` будет вычислено только для чисел, которые не удовлетворяют условию `number = 0`.
|
||||
|
||||
**Аргументы**
|
||||
|
||||
- `cond` – проверяемое условие. Может быть [UInt8](../../sql-reference/functions/conditional-functions.md) или `NULL`.
|
||||
@ -77,7 +79,13 @@ SELECT if(0, plus(2, 2), plus(2, 6));
|
||||
|
||||
Позволяет более компактно записать оператор [CASE](../operators/index.md#operator_case) в запросе.
|
||||
|
||||
multiIf(cond_1, then_1, cond_2, then_2...else)
|
||||
**Синтаксис**
|
||||
|
||||
``` sql
|
||||
multiIf(cond_1, then_1, cond_2, then_2, ..., else)
|
||||
```
|
||||
|
||||
Чтобы вычислять функцию `multiIf` по короткой схеме, используйте настройку [short_circuit_function_evaluation](../../operations/settings/settings.md#short-circuit-function-evaluation). Если настройка включена, то выражение `then_i` вычисляется только для строк, где условие `((NOT cond_1) AND (NOT cond_2) AND ... AND (NOT cond_{i-1}) AND cond_i)` верно, `cond_i` вычисляется только для строк, где условие `((NOT cond_1) AND (NOT cond_2) AND ... AND (NOT cond_{i-1}))` верно. Например, при выполнении запроса `SELECT multiIf(number = 2, intDiv(1, number), number = 5) FROM numbers(10)` не будет сгенерировано исключение из-за деления на ноль.
|
||||
|
||||
**Аргументы**
|
||||
|
||||
@ -110,4 +118,3 @@ SELECT if(0, plus(2, 2), plus(2, 6));
|
||||
│ ᴺᵁᴸᴸ │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
@ -19,6 +19,8 @@ toc_title: "Логические функции"
|
||||
and(val1, val2...)
|
||||
```
|
||||
|
||||
Чтобы вычислять функцию `and` по короткой схеме, используйте настройку [short_circuit_function_evaluation](../../operations/settings/settings.md#short-circuit-function-evaluation). Если настройка включена, то выражение `vali` вычисляется только для строк, где условие `(val1 AND val2 AND ... AND val{i-1})` верно. Например, при выполнении запроса `SELECT and(number = 2, intDiv(1, number)) FROM numbers(10)` не будет сгенерировано исключение из-за деления на ноль.
|
||||
|
||||
**Аргументы**
|
||||
|
||||
- `val1, val2, ...` — список из как минимум двух значений. [Int](../../sql-reference/data-types/int-uint.md), [UInt](../../sql-reference/data-types/int-uint.md), [Float](../../sql-reference/data-types/float.md) или [Nullable](../../sql-reference/data-types/nullable.md).
|
||||
@ -71,6 +73,8 @@ SELECT and(NULL, 1, 10, -2);
|
||||
and(val1, val2...)
|
||||
```
|
||||
|
||||
Чтобы вычислять функцию `or` по короткой схеме, используйте настройку [short_circuit_function_evaluation](../../operations/settings/settings.md#short-circuit-function-evaluation). Если настройка включена, то выражение `vali` вычисляется только для строк, где условие `((NOT val1) AND (NOT val2) AND ... AND (NOT val{i-1}))` верно. Например, при выполнении запроса `SELECT or(number = 0, intDiv(1, number) != 0) FROM numbers(10)` не будет сгенерировано исключение из-за деления на ноль.
|
||||
|
||||
**Аргументы**
|
||||
|
||||
- `val1, val2, ...` — список из как минимум двух значений. [Int](../../sql-reference/data-types/int-uint.md), [UInt](../../sql-reference/data-types/int-uint.md), [Float](../../sql-reference/data-types/float.md) или [Nullable](../../sql-reference/data-types/nullable.md).
|
||||
|
@ -3,7 +3,7 @@ toc_priority: 31
|
||||
toc_title: "Синтаксис"
|
||||
---
|
||||
|
||||
# Синтаксис {#sintaksis}
|
||||
# Синтаксис {#syntax}
|
||||
|
||||
В системе есть два вида парсеров: полноценный парсер SQL (recursive descent parser) и парсер форматов данных (быстрый потоковый парсер).
|
||||
Во всех случаях кроме запроса INSERT, используется только полноценный парсер SQL.
|
||||
@ -21,11 +21,11 @@ INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')
|
||||
|
||||
Далее пойдёт речь о полноценном парсере. О парсерах форматов, смотри раздел «Форматы».
|
||||
|
||||
## Пробелы {#probely}
|
||||
## Пробелы {#spaces}
|
||||
|
||||
Между синтаксическими конструкциями (в том числе, в начале и конце запроса) может быть расположено произвольное количество пробельных символов. К пробельным символам относятся пробел, таб, перевод строки, CR, form feed.
|
||||
|
||||
## Комментарии {#kommentarii}
|
||||
## Комментарии {#comments}
|
||||
|
||||
Поддерживаются комментарии в SQL-стиле и C-стиле.
|
||||
Комментарии в SQL-стиле: от `--` до конца строки. Пробел после `--` может не ставиться.
|
||||
@ -63,7 +63,7 @@ INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')
|
||||
|
||||
Существуют: числовые, строковые, составные литералы и `NULL`.
|
||||
|
||||
### Числовые {#chislovye}
|
||||
### Числовые {#numeric}
|
||||
|
||||
Числовой литерал пытается распарситься:
|
||||
|
||||
@ -83,7 +83,7 @@ INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')
|
||||
|
||||
Минимальный набор символов, которых вам необходимо экранировать в строковых литералах: `'` и `\`. Одинарная кавычка может быть экранирована одинарной кавычкой, литералы `'It\'s'` и `'It''s'` эквивалентны.
|
||||
|
||||
### Составные {#sostavnye}
|
||||
### Составные {#compound}
|
||||
|
||||
Поддерживаются конструкции для массивов: `[1, 2, 3]` и кортежей: `(1, 'Hello, world!', 2)`.
|
||||
На самом деле, это вовсе не литералы, а выражение с оператором создания массива и оператором создания кортежа, соответственно.
|
||||
@ -102,17 +102,39 @@ INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')
|
||||
|
||||
В запросах можно проверить `NULL` с помощью операторов [IS NULL](operators/index.md#operator-is-null) и [IS NOT NULL](operators/index.md), а также соответствующих функций `isNull` и `isNotNull`.
|
||||
|
||||
## Функции {#funktsii}
|
||||
### Heredoc {#heredeoc}
|
||||
|
||||
Синтаксис [heredoc](https://ru.wikipedia.org/wiki/Heredoc-синтаксис) — это способ определения строк с сохранением исходного формата (часто с переносом строки). `Heredoc` задается как произвольный строковый литерал между двумя символами `$`, например `$heredoc$`. Значение между двумя `heredoc` обрабатывается "как есть".
|
||||
|
||||
Синтаксис `heredoc` часто используют для вставки кусков кода SQL, HTML, XML и т.п.
|
||||
|
||||
**Пример**
|
||||
|
||||
Запрос:
|
||||
|
||||
```sql
|
||||
SELECT $smth$SHOW CREATE VIEW my_view$smth$;
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
┌─'SHOW CREATE VIEW my_view'─┐
|
||||
│ SHOW CREATE VIEW my_view │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## Функции {#functions}
|
||||
|
||||
Функции записываются как идентификатор со списком аргументов (возможно, пустым) в скобках. В отличие от стандартного SQL, даже в случае пустого списка аргументов, скобки обязательны. Пример: `now()`.
|
||||
Бывают обычные и агрегатные функции (смотрите раздел «Агрегатные функции»). Некоторые агрегатные функции могут содержать два списка аргументов в круглых скобках. Пример: `quantile(0.9)(x)`. Такие агрегатные функции называются «параметрическими», а первый список аргументов называется «параметрами». Синтаксис агрегатных функций без параметров ничем не отличается от обычных функций.
|
||||
|
||||
## Операторы {#operatory}
|
||||
## Операторы {#operators}
|
||||
|
||||
Операторы преобразуются в соответствующие им функции во время парсинга запроса, с учётом их приоритета и ассоциативности.
|
||||
Например, выражение `1 + 2 * 3 + 4` преобразуется в `plus(plus(1, multiply(2, 3)), 4)`.
|
||||
|
||||
## Типы данных и движки таблиц {#tipy-dannykh-i-dvizhki-tablits}
|
||||
## Типы данных и движки таблиц {#data_types-and-database-table-engines}
|
||||
|
||||
Типы данных и движки таблиц в запросе `CREATE` записываются также, как идентификаторы или также как функции. То есть, могут содержать или не содержать список аргументов в круглых скобках. Подробнее смотрите разделы «Типы данных», «Движки таблиц», «CREATE».
|
||||
|
||||
|
@ -5,4 +5,4 @@ toc_priority: 82
|
||||
|
||||
# Что нового в ClickHouse?
|
||||
|
||||
Планы развития вкратце изложены [здесь](extended-roadmap.md), а новости по предыдущим релизам подробно описаны в [журнале изменений](changelog/index.md).
|
||||
Планы развития вкратце изложены [здесь](https://github.com/ClickHouse/ClickHouse/issues/17623), а новости по предыдущим релизам подробно описаны в [журнале изменений](changelog/index.md).
|
||||
|
@ -93,7 +93,7 @@ def build_for_lang(lang, args):
|
||||
site_url=f'{website_url}/docs/{lang}/',
|
||||
docs_dir=os.path.join(args.docs_dir, lang),
|
||||
site_dir=site_dir,
|
||||
strict=False, # TODO: fix issues and return True
|
||||
strict=True,
|
||||
theme=theme_cfg,
|
||||
copyright='©2016–2021 ClickHouse, Inc.',
|
||||
use_directory_urls=True,
|
||||
|
@ -8,6 +8,9 @@ import subprocess
|
||||
|
||||
|
||||
def test_single_page(input_path, lang):
|
||||
if not (lang == 'en' or lang == 'ru'):
|
||||
return
|
||||
|
||||
with open(input_path) as f:
|
||||
soup = bs4.BeautifulSoup(
|
||||
f,
|
||||
@ -33,11 +36,8 @@ def test_single_page(input_path, lang):
|
||||
logging.info('Link to nowhere: %s' % href)
|
||||
|
||||
if links_to_nowhere:
|
||||
if lang == 'en' or lang == 'ru':
|
||||
logging.error(f'Found {links_to_nowhere} links to nowhere in {lang}')
|
||||
# TODO: restore sys.exit(1) here
|
||||
else:
|
||||
logging.warning(f'Found {links_to_nowhere} links to nowhere in {lang}')
|
||||
logging.error(f'Found {links_to_nowhere} links to nowhere in {lang}')
|
||||
sys.exit(1)
|
||||
|
||||
if len(anchor_points) <= 10:
|
||||
logging.error('Html parsing is probably broken')
|
||||
|
@ -8,4 +8,4 @@ toc_title: 云
|
||||
# ClickHouse Cloud Service {#clickhouse-cloud-service}
|
||||
|
||||
!!! info "Info"
|
||||
Detailed public description for ClickHouse cloud services is not ready yet, please [contact us](/company/#contact) to learn more.
|
||||
Detailed public description for ClickHouse cloud services is not ready yet, please [contact us](https://clickhouse.com/company/#contact) to learn more.
|
||||
|
@ -6,4 +6,4 @@ toc_title: 支持
|
||||
# ClickHouse 商业支持服务提供商 {#clickhouse-commercial-support-service-providers}
|
||||
|
||||
!!! info "Info"
|
||||
Detailed public description for ClickHouse support services is not ready yet, please [contact us](/company/#contact) to learn more.
|
||||
Detailed public description for ClickHouse support services is not ready yet, please [contact us](https://clickhouse.com/company/#contact) to learn more.
|
||||
|
@ -44,6 +44,7 @@ int mainEntryClickHouseFormat(int argc, char ** argv)
|
||||
|
||||
boost::program_options::options_description desc = createOptionsDescription("Allowed options", getTerminalWidth());
|
||||
desc.add_options()
|
||||
("query", po::value<std::string>(), "query to format")
|
||||
("help,h", "produce help message")
|
||||
("hilite", "add syntax highlight with ANSI terminal escape sequences")
|
||||
("oneline", "format in single line")
|
||||
@ -86,8 +87,16 @@ int mainEntryClickHouseFormat(int argc, char ** argv)
|
||||
}
|
||||
|
||||
String query;
|
||||
ReadBufferFromFileDescriptor in(STDIN_FILENO);
|
||||
readStringUntilEOF(query, in);
|
||||
|
||||
if (options.count("query"))
|
||||
{
|
||||
query = options["query"].as<std::string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadBufferFromFileDescriptor in(STDIN_FILENO);
|
||||
readStringUntilEOF(query, in);
|
||||
}
|
||||
|
||||
if (obfuscate)
|
||||
{
|
||||
|
@ -0,0 +1,98 @@
|
||||
#include <AggregateFunctions/IAggregateFunction.h>
|
||||
#include <AggregateFunctions/AggregateFunctionFactory.h>
|
||||
#include <AggregateFunctions/FactoryHelpers.h>
|
||||
#include <Common/ExponentiallySmoothedCounter.h>
|
||||
#include <Common/FieldVisitorConvertToNumber.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
}
|
||||
|
||||
|
||||
/** See the comments in ExponentiallySmoothedCounter.h
|
||||
*/
|
||||
class AggregateFunctionExponentialMovingAverage final
|
||||
: public IAggregateFunctionDataHelper<ExponentiallySmoothedAverage, AggregateFunctionExponentialMovingAverage>
|
||||
{
|
||||
private:
|
||||
String name;
|
||||
Float64 half_decay;
|
||||
|
||||
public:
|
||||
AggregateFunctionExponentialMovingAverage(const DataTypes & argument_types_, const Array & params)
|
||||
: IAggregateFunctionDataHelper<ExponentiallySmoothedAverage, AggregateFunctionExponentialMovingAverage>(argument_types_, params)
|
||||
{
|
||||
if (params.size() != 1)
|
||||
throw Exception{"Aggregate function " + getName() + " requires exactly one parameter: half decay time.",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH};
|
||||
|
||||
half_decay = applyVisitor(FieldVisitorConvertToNumber<Float64>(), params[0]);
|
||||
}
|
||||
|
||||
String getName() const override
|
||||
{
|
||||
return "exponentialMovingAverage";
|
||||
}
|
||||
|
||||
DataTypePtr getReturnType() const override
|
||||
{
|
||||
return std::make_shared<DataTypeNumber<Float64>>();
|
||||
}
|
||||
|
||||
bool allocatesMemoryInArena() const override { return false; }
|
||||
|
||||
void add(AggregateDataPtr __restrict place, const IColumn ** columns, size_t row_num, Arena *) const override
|
||||
{
|
||||
const auto & value = columns[0]->getFloat64(row_num);
|
||||
const auto & time = columns[1]->getFloat64(row_num);
|
||||
this->data(place).add(value, time, half_decay);
|
||||
}
|
||||
|
||||
void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs, Arena *) const override
|
||||
{
|
||||
this->data(place).merge(this->data(rhs), half_decay);
|
||||
}
|
||||
|
||||
void serialize(ConstAggregateDataPtr __restrict place, WriteBuffer & buf) const override
|
||||
{
|
||||
writeBinary(this->data(place).value, buf);
|
||||
writeBinary(this->data(place).time, buf);
|
||||
}
|
||||
|
||||
void deserialize(AggregateDataPtr __restrict place, ReadBuffer & buf, Arena *) const override
|
||||
{
|
||||
readBinary(this->data(place).value, buf);
|
||||
readBinary(this->data(place).time, buf);
|
||||
}
|
||||
|
||||
void insertResultInto(AggregateDataPtr __restrict place, IColumn & to, Arena *) const override
|
||||
{
|
||||
auto & column = assert_cast<ColumnVector<Float64> &>(to);
|
||||
column.getData().push_back(this->data(place).get(half_decay));
|
||||
}
|
||||
};
|
||||
|
||||
void registerAggregateFunctionExponentialMovingAverage(AggregateFunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction("exponentialMovingAverage",
|
||||
[](const std::string & name, const DataTypes & argument_types, const Array & params, const Settings *) -> AggregateFunctionPtr
|
||||
{
|
||||
assertBinary(name, argument_types);
|
||||
for (const auto & type : argument_types)
|
||||
if (!isNumber(*type))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Both arguments for aggregate function {} must have numeric type, got {}", name, type->getName());
|
||||
return std::make_shared<AggregateFunctionExponentialMovingAverage>(argument_types, params);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -102,7 +102,7 @@ namespace
|
||||
// This range is hardcoded below
|
||||
if (precision_param > 20 || precision_param < 12)
|
||||
throw Exception(
|
||||
"Parameter for aggregate function " + name + " is out or range: [12, 20].", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
"Parameter for aggregate function " + name + " is out of range: [12, 20].", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
precision = precision_param;
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/RadixSort.h>
|
||||
#include <Common/PODArray.h>
|
||||
#include <Core/AccurateComparison.h>
|
||||
#include <IO/WriteBuffer.h>
|
||||
#include <IO/ReadBuffer.h>
|
||||
#include <IO/VarInt.h>
|
||||
@ -14,8 +16,9 @@ struct Settings;
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int TOO_LARGE_ARRAY_SIZE;
|
||||
extern const int CANNOT_PARSE_INPUT_ASSERTION_FAILED;
|
||||
extern const int DECIMAL_OVERFLOW;
|
||||
extern const int TOO_LARGE_ARRAY_SIZE;
|
||||
}
|
||||
|
||||
|
||||
@ -314,7 +317,7 @@ public:
|
||||
compress();
|
||||
|
||||
if (centroids.size() == 1)
|
||||
return centroids.front().mean;
|
||||
return checkOverflow<ResultType>(centroids.front().mean);
|
||||
|
||||
Float64 x = level * count;
|
||||
Float64 prev_x = 0;
|
||||
@ -333,11 +336,11 @@ public:
|
||||
Float64 right = current_x - 0.5 * (c.count == 1);
|
||||
|
||||
if (x <= left)
|
||||
return prev_mean;
|
||||
return checkOverflow<ResultType>(prev_mean);
|
||||
else if (x >= right)
|
||||
return c.mean;
|
||||
return checkOverflow<ResultType>(c.mean);
|
||||
else
|
||||
return interpolate(x, left, prev_mean, right, c.mean);
|
||||
return checkOverflow<ResultType>(interpolate(x, left, prev_mean, right, c.mean));
|
||||
}
|
||||
|
||||
sum += c.count;
|
||||
@ -346,7 +349,7 @@ public:
|
||||
prev_x = current_x;
|
||||
}
|
||||
|
||||
return centroids.back().mean;
|
||||
return checkOverflow<ResultType>(centroids.back().mean);
|
||||
}
|
||||
|
||||
/** Get multiple quantiles (`size` parts).
|
||||
@ -438,6 +441,16 @@ public:
|
||||
{
|
||||
getManyImpl(levels, indices, size, result);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename ResultType>
|
||||
static ResultType checkOverflow(Value val)
|
||||
{
|
||||
ResultType result;
|
||||
if (accurate::convertNumeric(val, result))
|
||||
return result;
|
||||
throw DB::Exception("Numeric overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,9 @@ void registerAggregateFunctionWelchTTest(AggregateFunctionFactory &);
|
||||
void registerAggregateFunctionStudentTTest(AggregateFunctionFactory &);
|
||||
void registerAggregateFunctionSingleValueOrNull(AggregateFunctionFactory &);
|
||||
void registerAggregateFunctionSequenceNextNode(AggregateFunctionFactory &);
|
||||
void registerAggregateFunctionExponentialMovingAverage(AggregateFunctionFactory &);
|
||||
void registerAggregateFunctionSparkbar(AggregateFunctionFactory &);
|
||||
void registerAggregateFunctionIntervalLengthSum(AggregateFunctionFactory &);
|
||||
|
||||
class AggregateFunctionCombinatorFactory;
|
||||
void registerAggregateFunctionCombinatorIf(AggregateFunctionCombinatorFactory &);
|
||||
@ -66,8 +68,6 @@ void registerAggregateFunctionCombinatorDistinct(AggregateFunctionCombinatorFact
|
||||
|
||||
void registerWindowFunctions(AggregateFunctionFactory & factory);
|
||||
|
||||
void registerAggregateFunctionIntervalLengthSum(AggregateFunctionFactory &);
|
||||
|
||||
void registerAggregateFunctions()
|
||||
{
|
||||
{
|
||||
@ -116,11 +116,11 @@ void registerAggregateFunctions()
|
||||
registerAggregateFunctionWelchTTest(factory);
|
||||
registerAggregateFunctionStudentTTest(factory);
|
||||
registerAggregateFunctionSingleValueOrNull(factory);
|
||||
registerAggregateFunctionIntervalLengthSum(factory);
|
||||
registerAggregateFunctionExponentialMovingAverage(factory);
|
||||
registerAggregateFunctionSparkbar(factory);
|
||||
|
||||
registerWindowFunctions(factory);
|
||||
|
||||
registerAggregateFunctionIntervalLengthSum(factory);
|
||||
registerAggregateFunctionSparkbar(factory);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -131,10 +131,16 @@ void Connection::connect(const ConnectionTimeouts & timeouts)
|
||||
}
|
||||
catch (Poco::TimeoutException & e)
|
||||
{
|
||||
/// disconnect() will reset the socket, get timeouts before.
|
||||
const std::string & message = fmt::format("{} ({}, receive timeout {} ms, send timeout {} ms)",
|
||||
e.displayText(), getDescription(),
|
||||
socket->getReceiveTimeout().totalMilliseconds(),
|
||||
socket->getSendTimeout().totalMilliseconds());
|
||||
|
||||
disconnect();
|
||||
|
||||
/// Add server address to exception. Also Exception will remember stack trace. It's a pity that more precise exception type is lost.
|
||||
throw NetException(e.displayText() + " (" + getDescription() + ")", ErrorCodes::SOCKET_TIMEOUT);
|
||||
throw NetException(message, ErrorCodes::SOCKET_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -414,7 +420,12 @@ void Connection::sendQuery(
|
||||
if (!connected)
|
||||
connect(timeouts);
|
||||
|
||||
TimeoutSetter timeout_setter(*socket, timeouts.send_timeout, timeouts.receive_timeout, true);
|
||||
/// Query is not executed within sendQuery() function.
|
||||
///
|
||||
/// And what this means that temporary timeout (via TimeoutSetter) is not
|
||||
/// enough, since next query can use timeout from the previous query in this case.
|
||||
socket->setReceiveTimeout(timeouts.receive_timeout);
|
||||
socket->setSendTimeout(timeouts.send_timeout);
|
||||
|
||||
if (settings)
|
||||
{
|
||||
|
@ -156,7 +156,7 @@ Poco::AutoPtr<Poco::XML::Document> YAMLParser::parse(const String& path)
|
||||
throw Exception(ErrorCodes::CANNOT_OPEN_FILE, "Unable to open YAML configuration file {}", path);
|
||||
}
|
||||
Poco::AutoPtr<Poco::XML::Document> xml = new Document;
|
||||
Poco::AutoPtr<Poco::XML::Element> root_node = xml->createElement("yandex");
|
||||
Poco::AutoPtr<Poco::XML::Element> root_node = xml->createElement("clickhouse");
|
||||
xml->appendChild(root_node);
|
||||
try
|
||||
{
|
||||
|
@ -534,6 +534,13 @@ ExecutionStatus ExecutionStatus::fromCurrentException(const std::string & start_
|
||||
return ExecutionStatus(getCurrentExceptionCode(), msg);
|
||||
}
|
||||
|
||||
ExecutionStatus ExecutionStatus::fromText(const std::string & data)
|
||||
{
|
||||
ExecutionStatus status;
|
||||
status.deserializeText(data);
|
||||
return status;
|
||||
}
|
||||
|
||||
ParsingException::ParsingException() = default;
|
||||
ParsingException::ParsingException(const std::string & msg, int code)
|
||||
: Exception(msg, code)
|
||||
|
@ -184,6 +184,8 @@ struct ExecutionStatus
|
||||
|
||||
static ExecutionStatus fromCurrentException(const std::string & start_of_message = "");
|
||||
|
||||
static ExecutionStatus fromText(const std::string & data);
|
||||
|
||||
std::string serializeText() const;
|
||||
|
||||
void deserializeText(const std::string & data);
|
||||
|
114
src/Common/ExponentiallySmoothedCounter.h
Normal file
114
src/Common/ExponentiallySmoothedCounter.h
Normal file
@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/** https://en.wikipedia.org/wiki/Exponential_smoothing
|
||||
*
|
||||
* Exponentially smoothed average over time is weighted average with weight proportional to negative exponent of the time passed.
|
||||
* For example, the last value is taken with weight 1/2, the value one second ago with weight 1/4, two seconds ago - 1/8, etc.
|
||||
* It can be understood as an average over sliding window, but with different kernel.
|
||||
*
|
||||
* As an advantage, it is easy to update. Instead of collecting values and calculating a series of x1 / 2 + x2 / 4 + x3 / 8...
|
||||
* just calculate x_old / 2 + x_new / 2.
|
||||
*
|
||||
* It is often used for resource usage metrics. For example, "load average" in Linux is exponentially smoothed moving average.
|
||||
* We can use exponentially smoothed counters in query scheduler.
|
||||
*/
|
||||
struct ExponentiallySmoothedAverage
|
||||
{
|
||||
/// The sum. It contains the last value and all previous values scaled accordingly to the difference of their time to the reference time.
|
||||
/// Older values are summed with exponentially smaller coefficients.
|
||||
/// To obtain the average, you have to divide this value to the sum of all coefficients (see 'sumWeights').
|
||||
|
||||
double value = 0;
|
||||
|
||||
/// The point of reference. You can translate the value to a different point of reference (see 'remap').
|
||||
/// You can imagine that the value exponentially decays over time.
|
||||
/// But it is also meaningful to treat the whole counters as constants over time but in another non-linear coordinate system,
|
||||
/// that inflates over time, while the counter itself does not change
|
||||
/// (it continues to be the same physical quantity, but only changes its representation in the "usual" coordinate system).
|
||||
|
||||
/// Recap: the whole counter is one dimensional and it can be represented as a curve formed by two dependent coordinates in 2d plane,
|
||||
/// the space can be represented by (value, time) coordinates, and the curves will be exponentially decaying over time,
|
||||
/// alternatively the space can be represented by (exponentially_adjusted_value, time) and then the curves will be constant over time.
|
||||
|
||||
/// Also useful analogy is the exponential representation of a number: x = a * exp(b) = a * e (where e = exp(b))
|
||||
/// a number x is represented by a curve in 2d plane that can be parametrized by coordinates (a, b) or (a, e).
|
||||
|
||||
double time = 0;
|
||||
|
||||
|
||||
ExponentiallySmoothedAverage()
|
||||
{
|
||||
}
|
||||
|
||||
ExponentiallySmoothedAverage(double current_value, double current_time)
|
||||
: value(current_value), time(current_time)
|
||||
{
|
||||
}
|
||||
|
||||
/// How much value decays after time_passed.
|
||||
static double scale(double time_passed, double half_decay_time)
|
||||
{
|
||||
return exp2(-time_passed / half_decay_time);
|
||||
}
|
||||
|
||||
/// Sum of weights of all values. Divide by it to get the average.
|
||||
static double sumWeights(double half_decay_time)
|
||||
{
|
||||
double k = scale(1.0, half_decay_time);
|
||||
return 1 / (1 - k);
|
||||
}
|
||||
|
||||
/// Obtain the same counter in another point of reference.
|
||||
ExponentiallySmoothedAverage remap(double current_time, double half_decay_time) const
|
||||
{
|
||||
return ExponentiallySmoothedAverage(value * scale(current_time - time, half_decay_time), current_time);
|
||||
}
|
||||
|
||||
/// Merge two counters. It is done by moving to the same point of reference and summing the values.
|
||||
static ExponentiallySmoothedAverage merge(const ExponentiallySmoothedAverage & a, const ExponentiallySmoothedAverage & b, double half_decay_time)
|
||||
{
|
||||
if (a.time > b.time)
|
||||
return ExponentiallySmoothedAverage(a.value + b.remap(a.time, half_decay_time).value, a.time);
|
||||
if (a.time < b.time)
|
||||
return ExponentiallySmoothedAverage(b.value + a.remap(b.time, half_decay_time).value, b.time);
|
||||
|
||||
return ExponentiallySmoothedAverage(a.value + b.value, a.time);
|
||||
}
|
||||
|
||||
void merge(const ExponentiallySmoothedAverage & other, double half_decay_time)
|
||||
{
|
||||
*this = merge(*this, other, half_decay_time);
|
||||
}
|
||||
|
||||
void add(double new_value, double current_time, double half_decay_time)
|
||||
{
|
||||
merge(ExponentiallySmoothedAverage(new_value, current_time), half_decay_time);
|
||||
}
|
||||
|
||||
/// Calculate the average from the sum.
|
||||
double get(double half_decay_time) const
|
||||
{
|
||||
return value / sumWeights(half_decay_time);
|
||||
}
|
||||
|
||||
double get(double current_time, double half_decay_time) const
|
||||
{
|
||||
return remap(current_time, half_decay_time).get(half_decay_time);
|
||||
}
|
||||
|
||||
/// Compare two counters (by moving to the same point of reference and comparing sums).
|
||||
/// You can store the counters in container and sort it without changing the stored values over time.
|
||||
bool less(const ExponentiallySmoothedAverage & other, double half_decay_time) const
|
||||
{
|
||||
return remap(other.time, half_decay_time).value < other.value;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -111,7 +111,7 @@ void FileChecker::save() const
|
||||
std::unique_ptr<WriteBuffer> out = disk->writeFile(tmp_files_info_path);
|
||||
|
||||
/// So complex JSON structure - for compatibility with the old format.
|
||||
writeCString("{\"yandex\":{", *out);
|
||||
writeCString("{\"clickhouse\":{", *out);
|
||||
|
||||
auto settings = FormatSettings();
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
@ -153,7 +153,7 @@ void FileChecker::load()
|
||||
}
|
||||
JSON json(out.str());
|
||||
|
||||
JSON files = json["yandex"];
|
||||
JSON files = json.has("clickhouse") ? json["clickhouse"] : json["yandex"];
|
||||
for (const JSON file : files) // NOLINT
|
||||
map[unescapeForFileName(file.getName())] = file.getValue()["size"].toUInt();
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <Common/SettingsChanges.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace
|
||||
|
@ -5,6 +5,9 @@
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class IColumn;
|
||||
|
||||
struct SettingChange
|
||||
{
|
||||
String name;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/ReadBufferFromFile.h>
|
||||
#include <IO/ZstdDeflatingAppendableWriteBuffer.h>
|
||||
#include <filesystem>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
@ -10,6 +11,7 @@
|
||||
#include <Common/SipHash.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
@ -17,6 +19,7 @@ namespace ErrorCodes
|
||||
{
|
||||
extern const int CHECKSUM_DOESNT_MATCH;
|
||||
extern const int CORRUPTED_DATA;
|
||||
extern const int UNSUPPORTED_METHOD;
|
||||
extern const int UNKNOWN_FORMAT_VERSION;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
@ -29,7 +32,7 @@ constexpr auto DEFAULT_PREFIX = "changelog";
|
||||
std::string formatChangelogPath(const std::string & prefix, const ChangelogFileDescription & name)
|
||||
{
|
||||
std::filesystem::path path(prefix);
|
||||
path /= std::filesystem::path(name.prefix + "_" + std::to_string(name.from_log_index) + "_" + std::to_string(name.to_log_index) + ".bin");
|
||||
path /= std::filesystem::path(name.prefix + "_" + std::to_string(name.from_log_index) + "_" + std::to_string(name.to_log_index) + "." + name.extension);
|
||||
return path;
|
||||
}
|
||||
|
||||
@ -46,6 +49,7 @@ ChangelogFileDescription getChangelogFileDescription(const std::string & path_st
|
||||
result.prefix = filename_parts[0];
|
||||
result.from_log_index = parse<uint64_t>(filename_parts[1]);
|
||||
result.to_log_index = parse<uint64_t>(filename_parts[2]);
|
||||
result.extension = path.extension();
|
||||
result.path = path_str;
|
||||
return result;
|
||||
}
|
||||
@ -75,52 +79,53 @@ class ChangelogWriter
|
||||
public:
|
||||
ChangelogWriter(const std::string & filepath_, WriteMode mode, uint64_t start_index_)
|
||||
: filepath(filepath_)
|
||||
, plain_buf(filepath, DBMS_DEFAULT_BUFFER_SIZE, mode == WriteMode::Rewrite ? -1 : (O_APPEND | O_CREAT | O_WRONLY))
|
||||
, file_buf(filepath, DBMS_DEFAULT_BUFFER_SIZE, mode == WriteMode::Rewrite ? -1 : (O_APPEND | O_CREAT | O_WRONLY))
|
||||
, start_index(start_index_)
|
||||
{}
|
||||
|
||||
|
||||
off_t appendRecord(ChangelogRecord && record)
|
||||
{
|
||||
off_t result = plain_buf.count();
|
||||
writeIntBinary(computeRecordChecksum(record), plain_buf);
|
||||
|
||||
writeIntBinary(record.header.version, plain_buf);
|
||||
writeIntBinary(record.header.index, plain_buf);
|
||||
writeIntBinary(record.header.term, plain_buf);
|
||||
writeIntBinary(record.header.value_type, plain_buf);
|
||||
writeIntBinary(record.header.blob_size, plain_buf);
|
||||
|
||||
if (record.header.blob_size != 0)
|
||||
plain_buf.write(reinterpret_cast<char *>(record.blob->data_begin()), record.blob->size());
|
||||
|
||||
entries_written++;
|
||||
|
||||
return result;
|
||||
auto compression_method = chooseCompressionMethod(filepath_, "");
|
||||
if (compression_method != CompressionMethod::Zstd && compression_method != CompressionMethod::None)
|
||||
{
|
||||
throw Exception(ErrorCodes::UNSUPPORTED_METHOD, "Unsupported coordination log serialization format {}", toContentEncodingName(compression_method));
|
||||
}
|
||||
else if (compression_method == CompressionMethod::Zstd)
|
||||
{
|
||||
compressed_buffer = std::make_unique<ZstdDeflatingAppendableWriteBuffer>(file_buf, /* compression level = */ 3, /* append_to_existing_stream = */ mode == WriteMode::Append);
|
||||
}
|
||||
else
|
||||
{
|
||||
/// no compression, only file buffer
|
||||
}
|
||||
}
|
||||
|
||||
void truncateToLength(off_t new_length)
|
||||
|
||||
void appendRecord(ChangelogRecord && record)
|
||||
{
|
||||
plain_buf.next();
|
||||
plain_buf.truncate(new_length);
|
||||
plain_buf.seek(new_length, SEEK_SET);
|
||||
writeIntBinary(computeRecordChecksum(record), getBuffer());
|
||||
|
||||
writeIntBinary(record.header.version, getBuffer());
|
||||
writeIntBinary(record.header.index, getBuffer());
|
||||
writeIntBinary(record.header.term, getBuffer());
|
||||
writeIntBinary(record.header.value_type, getBuffer());
|
||||
writeIntBinary(record.header.blob_size, getBuffer());
|
||||
|
||||
if (record.header.blob_size != 0)
|
||||
getBuffer().write(reinterpret_cast<char *>(record.blob->data_begin()), record.blob->size());
|
||||
}
|
||||
|
||||
void flush(bool force_fsync)
|
||||
{
|
||||
plain_buf.next();
|
||||
if (compressed_buffer)
|
||||
{
|
||||
/// Flush compressed data to WriteBufferFromFile working_buffer
|
||||
compressed_buffer->next();
|
||||
}
|
||||
|
||||
/// Flush working buffer to file system
|
||||
file_buf.next();
|
||||
|
||||
/// Fsync file system if needed
|
||||
if (force_fsync)
|
||||
plain_buf.sync();
|
||||
}
|
||||
|
||||
uint64_t getEntriesWritten() const
|
||||
{
|
||||
return entries_written;
|
||||
}
|
||||
|
||||
void setEntriesWritten(uint64_t entries_written_)
|
||||
{
|
||||
entries_written = entries_written_;
|
||||
file_buf.sync();
|
||||
}
|
||||
|
||||
uint64_t getStartIndex() const
|
||||
@ -128,15 +133,17 @@ public:
|
||||
return start_index;
|
||||
}
|
||||
|
||||
void setStartIndex(uint64_t start_index_)
|
||||
private:
|
||||
WriteBuffer & getBuffer()
|
||||
{
|
||||
start_index = start_index_;
|
||||
if (compressed_buffer)
|
||||
return *compressed_buffer;
|
||||
return file_buf;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string filepath;
|
||||
WriteBufferFromFile plain_buf;
|
||||
uint64_t entries_written = 0;
|
||||
WriteBufferFromFile file_buf;
|
||||
std::unique_ptr<WriteBuffer> compressed_buffer;
|
||||
uint64_t start_index;
|
||||
};
|
||||
|
||||
@ -167,30 +174,32 @@ class ChangelogReader
|
||||
public:
|
||||
explicit ChangelogReader(const std::string & filepath_)
|
||||
: filepath(filepath_)
|
||||
, read_buf(filepath)
|
||||
{}
|
||||
{
|
||||
auto compression_method = chooseCompressionMethod(filepath, "");
|
||||
auto read_buffer_from_file = std::make_unique<ReadBufferFromFile>(filepath);
|
||||
read_buf = wrapReadBufferWithCompressionMethod(std::move(read_buffer_from_file), compression_method);
|
||||
}
|
||||
|
||||
/// start_log_index -- all entries with index < start_log_index will be skipped, but accounted into total_entries_read_from_log
|
||||
ChangelogReadResult readChangelog(IndexToLogEntry & logs, uint64_t start_log_index, IndexToOffset & index_to_offset, Poco::Logger * log)
|
||||
ChangelogReadResult readChangelog(IndexToLogEntry & logs, uint64_t start_log_index, Poco::Logger * log)
|
||||
{
|
||||
uint64_t previous_index = 0;
|
||||
ChangelogReadResult result{};
|
||||
try
|
||||
{
|
||||
while (!read_buf.eof())
|
||||
while (!read_buf->eof())
|
||||
{
|
||||
result.last_position = read_buf.count();
|
||||
result.last_position = read_buf->count();
|
||||
/// Read checksum
|
||||
Checksum record_checksum;
|
||||
readIntBinary(record_checksum, read_buf);
|
||||
readIntBinary(record_checksum, *read_buf);
|
||||
|
||||
/// Read header
|
||||
ChangelogRecord record;
|
||||
readIntBinary(record.header.version, read_buf);
|
||||
readIntBinary(record.header.index, read_buf);
|
||||
readIntBinary(record.header.term, read_buf);
|
||||
readIntBinary(record.header.value_type, read_buf);
|
||||
readIntBinary(record.header.blob_size, read_buf);
|
||||
readIntBinary(record.header.version, *read_buf);
|
||||
readIntBinary(record.header.index, *read_buf);
|
||||
readIntBinary(record.header.term, *read_buf);
|
||||
readIntBinary(record.header.value_type, *read_buf);
|
||||
readIntBinary(record.header.blob_size, *read_buf);
|
||||
|
||||
if (record.header.version > CURRENT_CHANGELOG_VERSION)
|
||||
throw Exception(ErrorCodes::UNKNOWN_FORMAT_VERSION, "Unsupported changelog version {} on path {}", record.header.version, filepath);
|
||||
@ -200,18 +209,12 @@ public:
|
||||
{
|
||||
auto buffer = nuraft::buffer::alloc(record.header.blob_size);
|
||||
auto * buffer_begin = reinterpret_cast<char *>(buffer->data_begin());
|
||||
read_buf.readStrict(buffer_begin, record.header.blob_size);
|
||||
read_buf->readStrict(buffer_begin, record.header.blob_size);
|
||||
record.blob = buffer;
|
||||
}
|
||||
else
|
||||
record.blob = nullptr;
|
||||
|
||||
/// Check changelog integrity
|
||||
if (previous_index != 0 && previous_index + 1 != record.header.index)
|
||||
throw Exception(ErrorCodes::CORRUPTED_DATA, "Previous log entry {}, next log entry {}, seems like some entries skipped", previous_index, record.header.index);
|
||||
|
||||
previous_index = record.header.index;
|
||||
|
||||
/// Compare checksums
|
||||
Checksum checksum = computeRecordChecksum(record);
|
||||
if (checksum != record_checksum)
|
||||
@ -223,7 +226,7 @@ public:
|
||||
|
||||
/// Check for duplicated changelog ids
|
||||
if (logs.count(record.header.index) != 0)
|
||||
throw Exception(ErrorCodes::CORRUPTED_DATA, "Duplicated index id {} in log {}", record.header.index, filepath);
|
||||
std::erase_if(logs, [record] (const auto & item) { return item.first >= record.header.index; });
|
||||
|
||||
result.total_entries_read_from_log += 1;
|
||||
|
||||
@ -238,7 +241,6 @@ public:
|
||||
|
||||
/// Put it into in memory structure
|
||||
logs.emplace(record.header.index, log_entry);
|
||||
index_to_offset[record.header.index] = result.last_position;
|
||||
result.last_read_index = record.header.index;
|
||||
|
||||
if (result.total_entries_read_from_log % 50000 == 0)
|
||||
@ -266,18 +268,20 @@ public:
|
||||
|
||||
private:
|
||||
std::string filepath;
|
||||
ReadBufferFromFile read_buf;
|
||||
std::unique_ptr<ReadBuffer> read_buf;
|
||||
};
|
||||
|
||||
Changelog::Changelog(
|
||||
const std::string & changelogs_dir_,
|
||||
uint64_t rotate_interval_,
|
||||
bool force_sync_,
|
||||
Poco::Logger * log_)
|
||||
Poco::Logger * log_,
|
||||
bool compress_logs_)
|
||||
: changelogs_dir(changelogs_dir_)
|
||||
, rotate_interval(rotate_interval_)
|
||||
, force_sync(force_sync_)
|
||||
, log(log_)
|
||||
, compress_logs(compress_logs_)
|
||||
{
|
||||
/// Load all files in changelog directory
|
||||
namespace fs = std::filesystem;
|
||||
@ -339,10 +343,15 @@ void Changelog::readChangelogAndInitWriter(uint64_t last_commited_log_index, uin
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ChangelogReader reader(changelog_description.path);
|
||||
last_log_read_result = reader.readChangelog(logs, start_to_read_from, index_to_start_pos, log);
|
||||
last_log_read_result = reader.readChangelog(logs, start_to_read_from, log);
|
||||
last_log_read_result->log_start_index = changelog_description.from_log_index;
|
||||
|
||||
if (last_log_read_result->error)
|
||||
{
|
||||
last_log_is_not_complete = true;
|
||||
break;
|
||||
}
|
||||
/// Otherwise we have already initialized it
|
||||
if (min_log_id == 0)
|
||||
min_log_id = last_log_read_result->first_read_index;
|
||||
@ -350,13 +359,11 @@ void Changelog::readChangelogAndInitWriter(uint64_t last_commited_log_index, uin
|
||||
if (last_log_read_result->last_read_index != 0)
|
||||
max_log_id = last_log_read_result->last_read_index;
|
||||
|
||||
last_log_read_result->log_start_index = changelog_description.from_log_index;
|
||||
|
||||
/// How many entries we have in the last changelog
|
||||
uint64_t expected_entries_in_log = changelog_description.expectedEntriesCountInLog();
|
||||
|
||||
/// May happen after truncate, crash or simply unfinished log
|
||||
if (last_log_read_result->total_entries_read_from_log < expected_entries_in_log)
|
||||
/// Unfinished log
|
||||
if (last_log_read_result->error || last_log_read_result->total_entries_read_from_log < expected_entries_in_log)
|
||||
{
|
||||
last_log_is_not_complete = true;
|
||||
break;
|
||||
@ -376,19 +383,29 @@ void Changelog::readChangelogAndInitWriter(uint64_t last_commited_log_index, uin
|
||||
else if (last_log_is_not_complete) /// if it's complete just start new one
|
||||
{
|
||||
assert(last_log_read_result != std::nullopt);
|
||||
assert(!existing_changelogs.empty());
|
||||
|
||||
/// Actually they shouldn't exist, but to be sure we remove them
|
||||
removeAllLogsAfter(last_log_read_result->log_start_index);
|
||||
|
||||
assert(!existing_changelogs.empty());
|
||||
/// This log, even if it finished with error shouldn't be removed
|
||||
assert(existing_changelogs.find(last_log_read_result->log_start_index) != existing_changelogs.end());
|
||||
assert(existing_changelogs.find(last_log_read_result->log_start_index)->first == existing_changelogs.rbegin()->first);
|
||||
|
||||
/// Continue to write into incomplete existing log
|
||||
/// Continue to write into incomplete existing log if it doesn't finished with error
|
||||
auto description = existing_changelogs[last_log_read_result->log_start_index];
|
||||
|
||||
if (last_log_read_result->error)
|
||||
initWriter(description, last_log_read_result->total_entries_read_from_log, /* truncate_to_offset = */ last_log_read_result->last_position);
|
||||
if (last_log_read_result->last_read_index == 0 || last_log_read_result->error) /// If it's broken log then remove it
|
||||
{
|
||||
LOG_INFO(log, "Removing log {} because it's empty or read finished with error", description.path);
|
||||
std::filesystem::remove(description.path);
|
||||
existing_changelogs.erase(last_log_read_result->log_start_index);
|
||||
std::erase_if(logs, [last_log_read_result] (const auto & item) { return item.first >= last_log_read_result->log_start_index; });
|
||||
}
|
||||
else
|
||||
initWriter(description, last_log_read_result->total_entries_read_from_log);
|
||||
{
|
||||
initWriter(description);
|
||||
}
|
||||
}
|
||||
|
||||
/// Start new log if we don't initialize writer from previous log. All logs can be "complete".
|
||||
@ -397,33 +414,32 @@ void Changelog::readChangelogAndInitWriter(uint64_t last_commited_log_index, uin
|
||||
}
|
||||
|
||||
|
||||
void Changelog::initWriter(const ChangelogFileDescription & description, uint64_t entries_already_written, std::optional<uint64_t> truncate_to_offset)
|
||||
void Changelog::initWriter(const ChangelogFileDescription & description)
|
||||
{
|
||||
if (description.expectedEntriesCountInLog() != rotate_interval)
|
||||
LOG_TRACE(log, "Looks like rotate_logs_interval was changed, current {}, expected entries in last log {}", rotate_interval, description.expectedEntriesCountInLog());
|
||||
|
||||
LOG_TRACE(log, "Continue to write into {}", description.path);
|
||||
current_writer = std::make_unique<ChangelogWriter>(description.path, WriteMode::Append, description.from_log_index);
|
||||
current_writer->setEntriesWritten(entries_already_written);
|
||||
|
||||
if (truncate_to_offset)
|
||||
{
|
||||
LOG_WARNING(log, "Changelog {} contain broken enties, truncating all broken log entries", description.path);
|
||||
current_writer->truncateToLength(*truncate_to_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void Changelog::removeAllLogsAfter(uint64_t start_to_remove_from_id)
|
||||
void Changelog::removeAllLogsAfter(uint64_t remove_after_log_start_index)
|
||||
{
|
||||
auto start_to_remove_from = existing_changelogs.upper_bound(start_to_remove_from_id);
|
||||
auto start_to_remove_from_itr = existing_changelogs.upper_bound(remove_after_log_start_index);
|
||||
if (start_to_remove_from_itr == existing_changelogs.end())
|
||||
return;
|
||||
|
||||
size_t start_to_remove_from_log_id = start_to_remove_from_itr->first;
|
||||
|
||||
/// All subsequent logs shouldn't exist. But they may exist if we crashed after writeAt started. Remove them.
|
||||
for (auto itr = start_to_remove_from; itr != existing_changelogs.end();)
|
||||
for (auto itr = start_to_remove_from_itr; itr != existing_changelogs.end();)
|
||||
{
|
||||
LOG_WARNING(log, "Removing changelog {}, because it's goes after broken changelog entry", itr->second.path);
|
||||
std::filesystem::remove(itr->second.path);
|
||||
itr = existing_changelogs.erase(itr);
|
||||
}
|
||||
|
||||
std::erase_if(logs, [start_to_remove_from_log_id] (const auto & item) { return item.first >= start_to_remove_from_log_id; });
|
||||
}
|
||||
|
||||
void Changelog::removeAllLogs()
|
||||
@ -435,6 +451,7 @@ void Changelog::removeAllLogs()
|
||||
std::filesystem::remove(itr->second.path);
|
||||
itr = existing_changelogs.erase(itr);
|
||||
}
|
||||
logs.clear();
|
||||
}
|
||||
|
||||
void Changelog::rotate(uint64_t new_start_log_index)
|
||||
@ -447,6 +464,10 @@ void Changelog::rotate(uint64_t new_start_log_index)
|
||||
new_description.prefix = DEFAULT_PREFIX;
|
||||
new_description.from_log_index = new_start_log_index;
|
||||
new_description.to_log_index = new_start_log_index + rotate_interval - 1;
|
||||
new_description.extension = "bin";
|
||||
|
||||
if (compress_logs)
|
||||
new_description.extension += "." + toContentEncodingName(CompressionMethod::Zstd);
|
||||
|
||||
new_description.path = formatChangelogPath(changelogs_dir, new_description);
|
||||
|
||||
@ -482,24 +503,18 @@ void Changelog::appendEntry(uint64_t index, const LogEntryPtr & log_entry)
|
||||
min_log_id = index;
|
||||
|
||||
const auto & current_changelog_description = existing_changelogs[current_writer->getStartIndex()];
|
||||
const bool log_is_complete = current_writer->getEntriesWritten() == current_changelog_description.expectedEntriesCountInLog();
|
||||
const bool log_is_complete = index - current_writer->getStartIndex() == current_changelog_description.expectedEntriesCountInLog();
|
||||
|
||||
if (log_is_complete)
|
||||
rotate(index);
|
||||
|
||||
const auto offset = current_writer->appendRecord(buildRecord(index, log_entry));
|
||||
if (!index_to_start_pos.try_emplace(index, offset).second)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Record with index {} already exists", index);
|
||||
|
||||
current_writer->appendRecord(buildRecord(index, log_entry));
|
||||
logs[index] = makeClone(log_entry);
|
||||
max_log_id = index;
|
||||
}
|
||||
|
||||
void Changelog::writeAt(uint64_t index, const LogEntryPtr & log_entry)
|
||||
{
|
||||
if (index_to_start_pos.count(index) == 0)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot write at index {} because changelog doesn't contain it", index);
|
||||
|
||||
/// This write_at require to overwrite everything in this file and also in previous file(s)
|
||||
const bool go_to_previous_file = index < current_writer->getStartIndex();
|
||||
|
||||
@ -516,14 +531,7 @@ void Changelog::writeAt(uint64_t index, const LogEntryPtr & log_entry)
|
||||
|
||||
/// Initialize writer from this log file
|
||||
current_writer = std::make_unique<ChangelogWriter>(description.path, WriteMode::Append, index_changelog->first);
|
||||
current_writer->setEntriesWritten(description.to_log_index - description.from_log_index + 1);
|
||||
}
|
||||
|
||||
/// Truncate current file
|
||||
current_writer->truncateToLength(index_to_start_pos[index]);
|
||||
|
||||
if (go_to_previous_file)
|
||||
{
|
||||
/// Remove all subsequent files if overwritten something in previous one
|
||||
auto to_remove_itr = existing_changelogs.upper_bound(index);
|
||||
for (auto itr = to_remove_itr; itr != existing_changelogs.end();)
|
||||
@ -533,20 +541,9 @@ void Changelog::writeAt(uint64_t index, const LogEntryPtr & log_entry)
|
||||
}
|
||||
}
|
||||
|
||||
auto entries_written = current_writer->getEntriesWritten();
|
||||
/// Remove redundant logs from memory
|
||||
/// Everything >= index must be removed
|
||||
for (uint64_t i = index; ; ++i)
|
||||
{
|
||||
auto log_itr = logs.find(i);
|
||||
if (log_itr == logs.end())
|
||||
break;
|
||||
|
||||
logs.erase(log_itr);
|
||||
index_to_start_pos.erase(i);
|
||||
entries_written--;
|
||||
}
|
||||
current_writer->setEntriesWritten(entries_written);
|
||||
std::erase_if(logs, [index] (const auto & item) { return item.first >= index; });
|
||||
|
||||
/// Now we can actually override entry at index
|
||||
appendEntry(index, log_entry);
|
||||
@ -579,7 +576,6 @@ void Changelog::compact(uint64_t up_to_log_index)
|
||||
}
|
||||
|
||||
LOG_INFO(log, "Removing changelog {} because of compaction", itr->second.path);
|
||||
std::erase_if(index_to_start_pos, [right_index = itr->second.to_log_index] (const auto & item) { return item.first <= right_index; });
|
||||
std::filesystem::remove(itr->second.path);
|
||||
itr = existing_changelogs.erase(itr);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <optional>
|
||||
#include <IO/WriteBufferFromFile.h>
|
||||
#include <IO/HashingWriteBuffer.h>
|
||||
#include <Compression/CompressedWriteBuffer.h>
|
||||
#include <IO/CompressionMethod.h>
|
||||
#include <Disks/IDisk.h>
|
||||
|
||||
namespace DB
|
||||
@ -25,9 +25,10 @@ enum class ChangelogVersion : uint8_t
|
||||
{
|
||||
V0 = 0,
|
||||
V1 = 1, /// with 64 bit buffer header
|
||||
V2 = 2, /// with compression and duplicate records
|
||||
};
|
||||
|
||||
static constexpr auto CURRENT_CHANGELOG_VERSION = ChangelogVersion::V1;
|
||||
static constexpr auto CURRENT_CHANGELOG_VERSION = ChangelogVersion::V2;
|
||||
|
||||
struct ChangelogRecordHeader
|
||||
{
|
||||
@ -52,6 +53,7 @@ struct ChangelogFileDescription
|
||||
std::string prefix;
|
||||
uint64_t from_log_index;
|
||||
uint64_t to_log_index;
|
||||
std::string extension;
|
||||
|
||||
std::string path;
|
||||
|
||||
@ -71,7 +73,8 @@ class Changelog
|
||||
{
|
||||
|
||||
public:
|
||||
Changelog(const std::string & changelogs_dir_, uint64_t rotate_interval_, bool force_sync_, Poco::Logger * log_);
|
||||
Changelog(const std::string & changelogs_dir_, uint64_t rotate_interval_,
|
||||
bool force_sync_, Poco::Logger * log_, bool compress_logs_ = true);
|
||||
|
||||
/// Read changelog from files on changelogs_dir_ skipping all entries before from_log_index
|
||||
/// Truncate broken entries, remove files after broken entries.
|
||||
@ -130,25 +133,24 @@ private:
|
||||
void rotate(uint64_t new_start_log_index);
|
||||
|
||||
/// Remove all changelogs from disk with start_index bigger than start_to_remove_from_id
|
||||
void removeAllLogsAfter(uint64_t start_to_remove_from_id);
|
||||
void removeAllLogsAfter(uint64_t remove_after_log_start_index);
|
||||
/// Remove all logs from disk
|
||||
void removeAllLogs();
|
||||
/// Init writer for existing log with some entries already written
|
||||
void initWriter(const ChangelogFileDescription & description, uint64_t entries_already_written, std::optional<uint64_t> truncate_to_offset = {});
|
||||
void initWriter(const ChangelogFileDescription & description);
|
||||
|
||||
private:
|
||||
const std::string changelogs_dir;
|
||||
const uint64_t rotate_interval;
|
||||
const bool force_sync;
|
||||
Poco::Logger * log;
|
||||
bool compress_logs;
|
||||
|
||||
/// Currently existing changelogs
|
||||
std::map<uint64_t, ChangelogFileDescription> existing_changelogs;
|
||||
|
||||
/// Current writer for changelog file
|
||||
std::unique_ptr<ChangelogWriter> current_writer;
|
||||
/// Mapping log_id -> binary offset in log file
|
||||
IndexToOffset index_to_start_pos;
|
||||
/// Mapping log_id -> log_entry
|
||||
IndexToLogEntry logs;
|
||||
/// Start log_id which exists in all "active" logs
|
||||
|
@ -11,6 +11,7 @@ namespace DB
|
||||
|
||||
struct Settings;
|
||||
|
||||
|
||||
/** These settings represent fine tunes for internal details of Coordination storages
|
||||
* and should not be changed by the user without a reason.
|
||||
*/
|
||||
@ -34,7 +35,8 @@ struct Settings;
|
||||
M(UInt64, fresh_log_gap, 200, "When node became fresh", 0) \
|
||||
M(UInt64, max_requests_batch_size, 100, "Max size of batch in requests count before it will be sent to RAFT", 0) \
|
||||
M(Bool, quorum_reads, false, "Execute read requests as writes through whole RAFT consesus with similar speed", 0) \
|
||||
M(Bool, force_sync, true, "Call fsync on each change in RAFT changelog", 0)
|
||||
M(Bool, force_sync, true, "Call fsync on each change in RAFT changelog", 0) \
|
||||
M(Bool, compress_logs, true, "Write compressed coordination logs in ZSTD format", 0)
|
||||
|
||||
DECLARE_SETTINGS_TRAITS(CoordinationSettingsTraits, LIST_OF_COORDINATION_SETTINGS)
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
#include <Coordination/KeeperLogStore.h>
|
||||
#include <IO/CompressionMethod.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
KeeperLogStore::KeeperLogStore(const std::string & changelogs_path, uint64_t rotate_interval_, bool force_sync_)
|
||||
KeeperLogStore::KeeperLogStore(const std::string & changelogs_path, uint64_t rotate_interval_, bool force_sync_, bool compress_logs_)
|
||||
: log(&Poco::Logger::get("KeeperLogStore"))
|
||||
, changelog(changelogs_path, rotate_interval_, force_sync_, log)
|
||||
, changelog(changelogs_path, rotate_interval_, force_sync_, log, compress_logs_)
|
||||
{
|
||||
if (force_sync_)
|
||||
LOG_INFO(log, "force_sync enabled");
|
||||
|
@ -13,7 +13,7 @@ namespace DB
|
||||
class KeeperLogStore : public nuraft::log_store
|
||||
{
|
||||
public:
|
||||
KeeperLogStore(const std::string & changelogs_path, uint64_t rotate_interval_, bool force_sync_);
|
||||
KeeperLogStore(const std::string & changelogs_path, uint64_t rotate_interval_, bool force_sync_, bool compress_logs_);
|
||||
|
||||
/// Read log storage from filesystem starting from last_commited_log_index
|
||||
void init(uint64_t last_commited_log_index, uint64_t logs_to_keep);
|
||||
|
@ -34,7 +34,7 @@ KeeperStateManager::KeeperStateManager(int server_id_, const std::string & host,
|
||||
: my_server_id(server_id_)
|
||||
, my_port(port)
|
||||
, secure(false)
|
||||
, log_store(nuraft::cs_new<KeeperLogStore>(logs_path, 5000, false))
|
||||
, log_store(nuraft::cs_new<KeeperLogStore>(logs_path, 5000, false, false))
|
||||
, cluster_config(nuraft::cs_new<nuraft::cluster_config>())
|
||||
{
|
||||
auto peer_config = nuraft::cs_new<nuraft::srv_config>(my_server_id, host + ":" + std::to_string(port));
|
||||
@ -51,7 +51,7 @@ KeeperStateManager::KeeperStateManager(
|
||||
, secure(config.getBool(config_prefix + ".raft_configuration.secure", false))
|
||||
, log_store(nuraft::cs_new<KeeperLogStore>(
|
||||
getLogsPathFromConfig(config_prefix, config, standalone_keeper),
|
||||
coordination_settings->rotate_log_storage_interval, coordination_settings->force_sync))
|
||||
coordination_settings->rotate_log_storage_interval, coordination_settings->force_sync, coordination_settings->compress_logs))
|
||||
, cluster_config(nuraft::cs_new<nuraft::cluster_config>())
|
||||
{
|
||||
|
||||
|
@ -53,14 +53,23 @@ struct ChangelogDirTest
|
||||
}
|
||||
};
|
||||
|
||||
TEST(CoordinationTest, BuildTest)
|
||||
struct CompressionParam
|
||||
{
|
||||
bool enable_compression;
|
||||
std::string extension;
|
||||
};
|
||||
|
||||
class CoordinationTest : public ::testing::TestWithParam<CompressionParam>
|
||||
{};
|
||||
|
||||
TEST_P(CoordinationTest, BuildTest)
|
||||
{
|
||||
DB::InMemoryLogStore store;
|
||||
DB::SummingStateMachine machine;
|
||||
EXPECT_EQ(1, 1);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, BufferSerde)
|
||||
TEST_P(CoordinationTest, BufferSerde)
|
||||
{
|
||||
Coordination::ZooKeeperRequestPtr request = Coordination::ZooKeeperRequestFactory::instance().get(Coordination::OpNum::Get);
|
||||
request->xid = 3;
|
||||
@ -173,7 +182,7 @@ nuraft::ptr<nuraft::buffer> getBuffer(int64_t number)
|
||||
}
|
||||
|
||||
|
||||
TEST(CoordinationTest, TestSummingRaft1)
|
||||
TEST_P(CoordinationTest, TestSummingRaft1)
|
||||
{
|
||||
ChangelogDirTest test("./logs");
|
||||
SummingRaftServer s1(1, "localhost", 44444, "./logs");
|
||||
@ -204,10 +213,11 @@ DB::LogEntryPtr getLogEntry(const std::string & s, size_t term)
|
||||
return nuraft::cs_new<nuraft::log_entry>(term, bufwriter.getBuffer());
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestSimple)
|
||||
TEST_P(CoordinationTest, ChangelogTestSimple)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
auto entry = getLogEntry("hello world", 77);
|
||||
changelog.append(entry);
|
||||
@ -220,17 +230,19 @@ TEST(CoordinationTest, ChangelogTestSimple)
|
||||
EXPECT_EQ(changelog.log_entries(1, 2)->size(), 1);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestFile)
|
||||
|
||||
TEST_P(CoordinationTest, ChangelogTestFile)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
auto entry = getLogEntry("hello world", 77);
|
||||
changelog.append(entry);
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
for (const auto & p : fs::directory_iterator("./logs"))
|
||||
EXPECT_EQ(p.path(), "./logs/changelog_1_5.bin");
|
||||
EXPECT_EQ(p.path(), "./logs/changelog_1_5.bin" + params.extension);
|
||||
|
||||
changelog.append(entry);
|
||||
changelog.append(entry);
|
||||
@ -239,14 +251,15 @@ TEST(CoordinationTest, ChangelogTestFile)
|
||||
changelog.append(entry);
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogReadWrite)
|
||||
TEST_P(CoordinationTest, ChangelogReadWrite)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 1000, true);
|
||||
DB::KeeperLogStore changelog("./logs", 1000, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
{
|
||||
@ -256,7 +269,8 @@ TEST(CoordinationTest, ChangelogReadWrite)
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_EQ(changelog.size(), 10);
|
||||
DB::KeeperLogStore changelog_reader("./logs", 1000, true);
|
||||
|
||||
DB::KeeperLogStore changelog_reader("./logs", 1000, true, params.enable_compression);
|
||||
changelog_reader.init(1, 0);
|
||||
EXPECT_EQ(changelog_reader.size(), 10);
|
||||
EXPECT_EQ(changelog_reader.last_entry()->get_term(), changelog.last_entry()->get_term());
|
||||
@ -272,10 +286,11 @@ TEST(CoordinationTest, ChangelogReadWrite)
|
||||
EXPECT_EQ(10, entries_from_range->size());
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogWriteAt)
|
||||
TEST_P(CoordinationTest, ChangelogWriteAt)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 1000, true);
|
||||
DB::KeeperLogStore changelog("./logs", 1000, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
{
|
||||
@ -295,7 +310,7 @@ TEST(CoordinationTest, ChangelogWriteAt)
|
||||
EXPECT_EQ(changelog.entry_at(7)->get_term(), 77);
|
||||
EXPECT_EQ(changelog.next_slot(), 8);
|
||||
|
||||
DB::KeeperLogStore changelog_reader("./logs", 1000, true);
|
||||
DB::KeeperLogStore changelog_reader("./logs", 1000, true, params.enable_compression);
|
||||
changelog_reader.init(1, 0);
|
||||
|
||||
EXPECT_EQ(changelog_reader.size(), changelog.size());
|
||||
@ -305,10 +320,11 @@ TEST(CoordinationTest, ChangelogWriteAt)
|
||||
}
|
||||
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestAppendAfterRead)
|
||||
TEST_P(CoordinationTest, ChangelogTestAppendAfterRead)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
for (size_t i = 0; i < 7; ++i)
|
||||
{
|
||||
@ -318,10 +334,10 @@ TEST(CoordinationTest, ChangelogTestAppendAfterRead)
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_EQ(changelog.size(), 7);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
|
||||
DB::KeeperLogStore changelog_reader("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog_reader("./logs", 5, true, params.enable_compression);
|
||||
changelog_reader.init(1, 0);
|
||||
|
||||
EXPECT_EQ(changelog_reader.size(), 7);
|
||||
@ -332,8 +348,8 @@ TEST(CoordinationTest, ChangelogTestAppendAfterRead)
|
||||
}
|
||||
changelog_reader.end_of_append_batch(0, 0);
|
||||
EXPECT_EQ(changelog_reader.size(), 10);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
|
||||
size_t logs_count = 0;
|
||||
for (const auto & _ [[maybe_unused]]: fs::directory_iterator("./logs"))
|
||||
@ -345,9 +361,9 @@ TEST(CoordinationTest, ChangelogTestAppendAfterRead)
|
||||
changelog_reader.append(entry);
|
||||
changelog_reader.end_of_append_batch(0, 0);
|
||||
EXPECT_EQ(changelog_reader.size(), 11);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
|
||||
logs_count = 0;
|
||||
for (const auto & _ [[maybe_unused]]: fs::directory_iterator("./logs"))
|
||||
@ -356,10 +372,11 @@ TEST(CoordinationTest, ChangelogTestAppendAfterRead)
|
||||
EXPECT_EQ(logs_count, 3);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestCompaction)
|
||||
TEST_P(CoordinationTest, ChangelogTestCompaction)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
@ -377,7 +394,7 @@ TEST(CoordinationTest, ChangelogTestCompaction)
|
||||
EXPECT_EQ(changelog.start_index(), 3);
|
||||
EXPECT_EQ(changelog.next_slot(), 4);
|
||||
EXPECT_EQ(changelog.last_entry()->get_term(), 20);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
|
||||
auto e1 = getLogEntry("hello world", 30);
|
||||
changelog.append(e1);
|
||||
@ -389,20 +406,20 @@ TEST(CoordinationTest, ChangelogTestCompaction)
|
||||
changelog.append(e4);
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
|
||||
changelog.compact(6);
|
||||
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
|
||||
EXPECT_EQ(changelog.size(), 1);
|
||||
EXPECT_EQ(changelog.start_index(), 7);
|
||||
EXPECT_EQ(changelog.next_slot(), 8);
|
||||
EXPECT_EQ(changelog.last_entry()->get_term(), 60);
|
||||
/// And we able to read it
|
||||
DB::KeeperLogStore changelog_reader("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog_reader("./logs", 5, true, params.enable_compression);
|
||||
changelog_reader.init(7, 0);
|
||||
|
||||
EXPECT_EQ(changelog_reader.size(), 1);
|
||||
@ -411,10 +428,11 @@ TEST(CoordinationTest, ChangelogTestCompaction)
|
||||
EXPECT_EQ(changelog_reader.last_entry()->get_term(), 60);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestBatchOperations)
|
||||
TEST_P(CoordinationTest, ChangelogTestBatchOperations)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 100, true);
|
||||
DB::KeeperLogStore changelog("./logs", 100, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
{
|
||||
@ -427,7 +445,7 @@ TEST(CoordinationTest, ChangelogTestBatchOperations)
|
||||
|
||||
auto entries = changelog.pack(1, 5);
|
||||
|
||||
DB::KeeperLogStore apply_changelog("./logs", 100, true);
|
||||
DB::KeeperLogStore apply_changelog("./logs", 100, true, params.enable_compression);
|
||||
apply_changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
@ -455,10 +473,11 @@ TEST(CoordinationTest, ChangelogTestBatchOperations)
|
||||
EXPECT_EQ(apply_changelog.entry_at(12)->get_term(), 40);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestBatchOperationsEmpty)
|
||||
TEST_P(CoordinationTest, ChangelogTestBatchOperationsEmpty)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 100, true);
|
||||
DB::KeeperLogStore changelog("./logs", 100, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
{
|
||||
@ -472,7 +491,7 @@ TEST(CoordinationTest, ChangelogTestBatchOperationsEmpty)
|
||||
auto entries = changelog.pack(5, 5);
|
||||
|
||||
ChangelogDirTest test1("./logs1");
|
||||
DB::KeeperLogStore changelog_new("./logs1", 100, true);
|
||||
DB::KeeperLogStore changelog_new("./logs1", 100, true, params.enable_compression);
|
||||
changelog_new.init(1, 0);
|
||||
EXPECT_EQ(changelog_new.size(), 0);
|
||||
|
||||
@ -494,15 +513,16 @@ TEST(CoordinationTest, ChangelogTestBatchOperationsEmpty)
|
||||
EXPECT_EQ(changelog_new.start_index(), 5);
|
||||
EXPECT_EQ(changelog_new.next_slot(), 11);
|
||||
|
||||
DB::KeeperLogStore changelog_reader("./logs1", 100, true);
|
||||
DB::KeeperLogStore changelog_reader("./logs1", 100, true, params.enable_compression);
|
||||
changelog_reader.init(5, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestWriteAtPreviousFile)
|
||||
TEST_P(CoordinationTest, ChangelogTestWriteAtPreviousFile)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 33; ++i)
|
||||
@ -512,13 +532,13 @@ TEST(CoordinationTest, ChangelogTestWriteAtPreviousFile)
|
||||
}
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
|
||||
EXPECT_EQ(changelog.size(), 33);
|
||||
|
||||
@ -530,16 +550,16 @@ TEST(CoordinationTest, ChangelogTestWriteAtPreviousFile)
|
||||
EXPECT_EQ(changelog.next_slot(), 8);
|
||||
EXPECT_EQ(changelog.last_entry()->get_term(), 5555);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
|
||||
DB::KeeperLogStore changelog_read("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog_read("./logs", 5, true, params.enable_compression);
|
||||
changelog_read.init(1, 0);
|
||||
EXPECT_EQ(changelog_read.size(), 7);
|
||||
EXPECT_EQ(changelog_read.start_index(), 1);
|
||||
@ -547,10 +567,11 @@ TEST(CoordinationTest, ChangelogTestWriteAtPreviousFile)
|
||||
EXPECT_EQ(changelog_read.last_entry()->get_term(), 5555);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestWriteAtFileBorder)
|
||||
TEST_P(CoordinationTest, ChangelogTestWriteAtFileBorder)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 33; ++i)
|
||||
@ -560,13 +581,13 @@ TEST(CoordinationTest, ChangelogTestWriteAtFileBorder)
|
||||
}
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
|
||||
EXPECT_EQ(changelog.size(), 33);
|
||||
|
||||
@ -578,16 +599,16 @@ TEST(CoordinationTest, ChangelogTestWriteAtFileBorder)
|
||||
EXPECT_EQ(changelog.next_slot(), 12);
|
||||
EXPECT_EQ(changelog.last_entry()->get_term(), 5555);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
|
||||
DB::KeeperLogStore changelog_read("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog_read("./logs", 5, true, params.enable_compression);
|
||||
changelog_read.init(1, 0);
|
||||
EXPECT_EQ(changelog_read.size(), 11);
|
||||
EXPECT_EQ(changelog_read.start_index(), 1);
|
||||
@ -595,10 +616,11 @@ TEST(CoordinationTest, ChangelogTestWriteAtFileBorder)
|
||||
EXPECT_EQ(changelog_read.last_entry()->get_term(), 5555);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestWriteAtAllFiles)
|
||||
TEST_P(CoordinationTest, ChangelogTestWriteAtAllFiles)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 33; ++i)
|
||||
@ -608,13 +630,13 @@ TEST(CoordinationTest, ChangelogTestWriteAtAllFiles)
|
||||
}
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
|
||||
EXPECT_EQ(changelog.size(), 33);
|
||||
|
||||
@ -626,20 +648,21 @@ TEST(CoordinationTest, ChangelogTestWriteAtAllFiles)
|
||||
EXPECT_EQ(changelog.next_slot(), 2);
|
||||
EXPECT_EQ(changelog.last_entry()->get_term(), 5555);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestStartNewLogAfterRead)
|
||||
TEST_P(CoordinationTest, ChangelogTestStartNewLogAfterRead)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 35; ++i)
|
||||
@ -649,17 +672,17 @@ TEST(CoordinationTest, ChangelogTestStartNewLogAfterRead)
|
||||
}
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
EXPECT_EQ(changelog.size(), 35);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_36_40.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_36_40.bin" + params.extension));
|
||||
|
||||
|
||||
DB::KeeperLogStore changelog_reader("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog_reader("./logs", 5, true, params.enable_compression);
|
||||
changelog_reader.init(1, 0);
|
||||
|
||||
auto entry = getLogEntry("36_hello_world", 360);
|
||||
@ -667,22 +690,23 @@ TEST(CoordinationTest, ChangelogTestStartNewLogAfterRead)
|
||||
changelog_reader.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_EQ(changelog_reader.size(), 36);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_36_40.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_36_40.bin" + params.extension));
|
||||
}
|
||||
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestReadAfterBrokenTruncate)
|
||||
TEST_P(CoordinationTest, ChangelogTestReadAfterBrokenTruncate)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
|
||||
DB::KeeperLogStore changelog("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog("./logs", 5, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 35; ++i)
|
||||
@ -692,32 +716,32 @@ TEST(CoordinationTest, ChangelogTestReadAfterBrokenTruncate)
|
||||
}
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
EXPECT_EQ(changelog.size(), 35);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
|
||||
DB::WriteBufferFromFile plain_buf("./logs/changelog_11_15.bin", DBMS_DEFAULT_BUFFER_SIZE, O_APPEND | O_CREAT | O_WRONLY);
|
||||
DB::WriteBufferFromFile plain_buf("./logs/changelog_11_15.bin" + params.extension, DBMS_DEFAULT_BUFFER_SIZE, O_APPEND | O_CREAT | O_WRONLY);
|
||||
plain_buf.truncate(0);
|
||||
|
||||
DB::KeeperLogStore changelog_reader("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog_reader("./logs", 5, true, params.enable_compression);
|
||||
changelog_reader.init(1, 0);
|
||||
changelog_reader.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_EQ(changelog_reader.size(), 10);
|
||||
EXPECT_EQ(changelog_reader.last_entry()->get_term(), 90);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
|
||||
auto entry = getLogEntry("h", 7777);
|
||||
changelog_reader.append(entry);
|
||||
@ -725,26 +749,27 @@ TEST(CoordinationTest, ChangelogTestReadAfterBrokenTruncate)
|
||||
EXPECT_EQ(changelog_reader.size(), 11);
|
||||
EXPECT_EQ(changelog_reader.last_entry()->get_term(), 7777);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_5.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_6_10.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_11_15.bin" + params.extension));
|
||||
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_16_20.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_25.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_26_30.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_31_35.bin" + params.extension));
|
||||
|
||||
DB::KeeperLogStore changelog_reader2("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog_reader2("./logs", 5, true, params.enable_compression);
|
||||
changelog_reader2.init(1, 0);
|
||||
EXPECT_EQ(changelog_reader2.size(), 11);
|
||||
EXPECT_EQ(changelog_reader2.last_entry()->get_term(), 7777);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestReadAfterBrokenTruncate2)
|
||||
TEST_P(CoordinationTest, ChangelogTestReadAfterBrokenTruncate2)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
|
||||
DB::KeeperLogStore changelog("./logs", 20, true);
|
||||
DB::KeeperLogStore changelog("./logs", 20, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 35; ++i)
|
||||
@ -754,37 +779,37 @@ TEST(CoordinationTest, ChangelogTestReadAfterBrokenTruncate2)
|
||||
}
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_20.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_40.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_20.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_40.bin" + params.extension));
|
||||
|
||||
DB::WriteBufferFromFile plain_buf("./logs/changelog_1_20.bin", DBMS_DEFAULT_BUFFER_SIZE, O_APPEND | O_CREAT | O_WRONLY);
|
||||
DB::WriteBufferFromFile plain_buf("./logs/changelog_1_20.bin" + params.extension, DBMS_DEFAULT_BUFFER_SIZE, O_APPEND | O_CREAT | O_WRONLY);
|
||||
plain_buf.truncate(140);
|
||||
|
||||
DB::KeeperLogStore changelog_reader("./logs", 20, true);
|
||||
DB::KeeperLogStore changelog_reader("./logs", 20, true, params.enable_compression);
|
||||
changelog_reader.init(1, 0);
|
||||
|
||||
EXPECT_EQ(changelog_reader.size(), 2);
|
||||
EXPECT_EQ(changelog_reader.last_entry()->get_term(), 450);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_20.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_40.bin"));
|
||||
EXPECT_EQ(changelog_reader.size(), 0);
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_20.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_40.bin" + params.extension));
|
||||
auto entry = getLogEntry("hello_world", 7777);
|
||||
changelog_reader.append(entry);
|
||||
changelog_reader.end_of_append_batch(0, 0);
|
||||
EXPECT_EQ(changelog_reader.size(), 3);
|
||||
EXPECT_EQ(changelog_reader.size(), 1);
|
||||
EXPECT_EQ(changelog_reader.last_entry()->get_term(), 7777);
|
||||
|
||||
|
||||
DB::KeeperLogStore changelog_reader2("./logs", 20, true);
|
||||
DB::KeeperLogStore changelog_reader2("./logs", 1, true, params.enable_compression);
|
||||
changelog_reader2.init(1, 0);
|
||||
EXPECT_EQ(changelog_reader2.size(), 3);
|
||||
EXPECT_EQ(changelog_reader2.size(), 1);
|
||||
EXPECT_EQ(changelog_reader2.last_entry()->get_term(), 7777);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, ChangelogTestLostFiles)
|
||||
TEST_P(CoordinationTest, ChangelogTestLostFiles)
|
||||
{
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest test("./logs");
|
||||
|
||||
DB::KeeperLogStore changelog("./logs", 20, true);
|
||||
DB::KeeperLogStore changelog("./logs", 20, true, params.enable_compression);
|
||||
changelog.init(1, 0);
|
||||
|
||||
for (size_t i = 0; i < 35; ++i)
|
||||
@ -794,19 +819,19 @@ TEST(CoordinationTest, ChangelogTestLostFiles)
|
||||
}
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_20.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_40.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_20.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_21_40.bin" + params.extension));
|
||||
|
||||
fs::remove("./logs/changelog_1_20.bin");
|
||||
fs::remove("./logs/changelog_1_20.bin" + params.extension);
|
||||
|
||||
DB::KeeperLogStore changelog_reader("./logs", 20, true);
|
||||
DB::KeeperLogStore changelog_reader("./logs", 20, true, params.enable_compression);
|
||||
/// It should print error message, but still able to start
|
||||
changelog_reader.init(5, 0);
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_1_20.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_40.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_1_20.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_21_40.bin" + params.extension));
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, SnapshotableHashMapSimple)
|
||||
TEST_P(CoordinationTest, SnapshotableHashMapSimple)
|
||||
{
|
||||
DB::SnapshotableHashTable<int> hello;
|
||||
EXPECT_TRUE(hello.insert("hello", 5));
|
||||
@ -821,7 +846,7 @@ TEST(CoordinationTest, SnapshotableHashMapSimple)
|
||||
EXPECT_EQ(hello.size(), 0);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, SnapshotableHashMapTrySnapshot)
|
||||
TEST_P(CoordinationTest, SnapshotableHashMapTrySnapshot)
|
||||
{
|
||||
DB::SnapshotableHashTable<int> map_snp;
|
||||
EXPECT_TRUE(map_snp.insert("/hello", 7));
|
||||
@ -907,7 +932,7 @@ void addNode(DB::KeeperStorage & storage, const std::string & path, const std::s
|
||||
storage.container.insertOrReplace(path, node);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, TestStorageSnapshotSimple)
|
||||
TEST_P(CoordinationTest, TestStorageSnapshotSimple)
|
||||
{
|
||||
ChangelogDirTest test("./snapshots");
|
||||
DB::KeeperSnapshotManager manager("./snapshots", 3);
|
||||
@ -954,7 +979,7 @@ TEST(CoordinationTest, TestStorageSnapshotSimple)
|
||||
EXPECT_EQ(restored_storage->session_and_timeout.size(), 2);
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, TestStorageSnapshotMoreWrites)
|
||||
TEST_P(CoordinationTest, TestStorageSnapshotMoreWrites)
|
||||
{
|
||||
ChangelogDirTest test("./snapshots");
|
||||
DB::KeeperSnapshotManager manager("./snapshots", 3);
|
||||
@ -994,7 +1019,7 @@ TEST(CoordinationTest, TestStorageSnapshotMoreWrites)
|
||||
}
|
||||
|
||||
|
||||
TEST(CoordinationTest, TestStorageSnapshotManySnapshots)
|
||||
TEST_P(CoordinationTest, TestStorageSnapshotManySnapshots)
|
||||
{
|
||||
ChangelogDirTest test("./snapshots");
|
||||
DB::KeeperSnapshotManager manager("./snapshots", 3);
|
||||
@ -1032,7 +1057,7 @@ TEST(CoordinationTest, TestStorageSnapshotManySnapshots)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, TestStorageSnapshotMode)
|
||||
TEST_P(CoordinationTest, TestStorageSnapshotMode)
|
||||
{
|
||||
ChangelogDirTest test("./snapshots");
|
||||
DB::KeeperSnapshotManager manager("./snapshots", 3);
|
||||
@ -1083,7 +1108,7 @@ TEST(CoordinationTest, TestStorageSnapshotMode)
|
||||
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, TestStorageSnapshotBroken)
|
||||
TEST_P(CoordinationTest, TestStorageSnapshotBroken)
|
||||
{
|
||||
ChangelogDirTest test("./snapshots");
|
||||
DB::KeeperSnapshotManager manager("./snapshots", 3);
|
||||
@ -1121,7 +1146,7 @@ nuraft::ptr<nuraft::log_entry> getLogEntryFromZKRequest(size_t term, int64_t ses
|
||||
return nuraft::cs_new<nuraft::log_entry>(term, buffer);
|
||||
}
|
||||
|
||||
void testLogAndStateMachine(Coordination::CoordinationSettingsPtr settings, uint64_t total_logs)
|
||||
void testLogAndStateMachine(Coordination::CoordinationSettingsPtr settings, uint64_t total_logs, bool enable_compression)
|
||||
{
|
||||
using namespace Coordination;
|
||||
using namespace DB;
|
||||
@ -1133,7 +1158,7 @@ void testLogAndStateMachine(Coordination::CoordinationSettingsPtr settings, uint
|
||||
SnapshotsQueue snapshots_queue{1};
|
||||
auto state_machine = std::make_shared<KeeperStateMachine>(queue, snapshots_queue, "./snapshots", settings);
|
||||
state_machine->init();
|
||||
DB::KeeperLogStore changelog("./logs", settings->rotate_log_storage_interval, true);
|
||||
DB::KeeperLogStore changelog("./logs", settings->rotate_log_storage_interval, true, enable_compression);
|
||||
changelog.init(state_machine->last_commit_index() + 1, settings->reserved_log_items);
|
||||
for (size_t i = 1; i < total_logs + 1; ++i)
|
||||
{
|
||||
@ -1173,7 +1198,7 @@ void testLogAndStateMachine(Coordination::CoordinationSettingsPtr settings, uint
|
||||
restore_machine->init();
|
||||
EXPECT_EQ(restore_machine->last_commit_index(), total_logs - total_logs % settings->snapshot_distance);
|
||||
|
||||
DB::KeeperLogStore restore_changelog("./logs", settings->rotate_log_storage_interval, true);
|
||||
DB::KeeperLogStore restore_changelog("./logs", settings->rotate_log_storage_interval, true, enable_compression);
|
||||
restore_changelog.init(restore_machine->last_commit_index() + 1, settings->reserved_log_items);
|
||||
|
||||
EXPECT_EQ(restore_changelog.size(), std::min(settings->reserved_log_items + total_logs % settings->snapshot_distance, total_logs));
|
||||
@ -1199,77 +1224,78 @@ void testLogAndStateMachine(Coordination::CoordinationSettingsPtr settings, uint
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, TestStateMachineAndLogStore)
|
||||
TEST_P(CoordinationTest, TestStateMachineAndLogStore)
|
||||
{
|
||||
using namespace Coordination;
|
||||
using namespace DB;
|
||||
auto params = GetParam();
|
||||
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 10;
|
||||
settings->reserved_log_items = 10;
|
||||
settings->rotate_log_storage_interval = 10;
|
||||
testLogAndStateMachine(settings, 37);
|
||||
testLogAndStateMachine(settings, 37, params.enable_compression);
|
||||
}
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 10;
|
||||
settings->reserved_log_items = 10;
|
||||
settings->rotate_log_storage_interval = 10;
|
||||
testLogAndStateMachine(settings, 11);
|
||||
testLogAndStateMachine(settings, 11, params.enable_compression);
|
||||
}
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 10;
|
||||
settings->reserved_log_items = 10;
|
||||
settings->rotate_log_storage_interval = 10;
|
||||
testLogAndStateMachine(settings, 40);
|
||||
testLogAndStateMachine(settings, 40, params.enable_compression);
|
||||
}
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 10;
|
||||
settings->reserved_log_items = 20;
|
||||
settings->rotate_log_storage_interval = 30;
|
||||
testLogAndStateMachine(settings, 40);
|
||||
testLogAndStateMachine(settings, 40, params.enable_compression);
|
||||
}
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 10;
|
||||
settings->reserved_log_items = 0;
|
||||
settings->rotate_log_storage_interval = 10;
|
||||
testLogAndStateMachine(settings, 40);
|
||||
testLogAndStateMachine(settings, 40, params.enable_compression);
|
||||
}
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 1;
|
||||
settings->reserved_log_items = 1;
|
||||
settings->rotate_log_storage_interval = 32;
|
||||
testLogAndStateMachine(settings, 32);
|
||||
testLogAndStateMachine(settings, 32, params.enable_compression);
|
||||
}
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 10;
|
||||
settings->reserved_log_items = 7;
|
||||
settings->rotate_log_storage_interval = 1;
|
||||
testLogAndStateMachine(settings, 33);
|
||||
testLogAndStateMachine(settings, 33, params.enable_compression);
|
||||
}
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 37;
|
||||
settings->reserved_log_items = 1000;
|
||||
settings->rotate_log_storage_interval = 5000;
|
||||
testLogAndStateMachine(settings, 33);
|
||||
testLogAndStateMachine(settings, 33, params.enable_compression);
|
||||
}
|
||||
{
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
settings->snapshot_distance = 37;
|
||||
settings->reserved_log_items = 1000;
|
||||
settings->rotate_log_storage_interval = 5000;
|
||||
testLogAndStateMachine(settings, 45);
|
||||
testLogAndStateMachine(settings, 45, params.enable_compression);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, TestEphemeralNodeRemove)
|
||||
TEST_P(CoordinationTest, TestEphemeralNodeRemove)
|
||||
{
|
||||
using namespace Coordination;
|
||||
using namespace DB;
|
||||
@ -1300,12 +1326,13 @@ TEST(CoordinationTest, TestEphemeralNodeRemove)
|
||||
}
|
||||
|
||||
|
||||
TEST(CoordinationTest, TestRotateIntervalChanges)
|
||||
TEST_P(CoordinationTest, TestRotateIntervalChanges)
|
||||
{
|
||||
using namespace Coordination;
|
||||
auto params = GetParam();
|
||||
ChangelogDirTest snapshots("./logs");
|
||||
{
|
||||
DB::KeeperLogStore changelog("./logs", 100, true);
|
||||
DB::KeeperLogStore changelog("./logs", 100, true, params.enable_compression);
|
||||
|
||||
changelog.init(0, 3);
|
||||
for (size_t i = 1; i < 55; ++i)
|
||||
@ -1319,9 +1346,9 @@ TEST(CoordinationTest, TestRotateIntervalChanges)
|
||||
}
|
||||
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_100.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_100.bin" + params.extension));
|
||||
|
||||
DB::KeeperLogStore changelog_1("./logs", 10, true);
|
||||
DB::KeeperLogStore changelog_1("./logs", 10, true, params.enable_compression);
|
||||
changelog_1.init(0, 50);
|
||||
for (size_t i = 0; i < 55; ++i)
|
||||
{
|
||||
@ -1332,10 +1359,10 @@ TEST(CoordinationTest, TestRotateIntervalChanges)
|
||||
changelog_1.end_of_append_batch(0, 0);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_100.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_101_110.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_1_100.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_101_110.bin" + params.extension));
|
||||
|
||||
DB::KeeperLogStore changelog_2("./logs", 7, true);
|
||||
DB::KeeperLogStore changelog_2("./logs", 7, true, params.enable_compression);
|
||||
changelog_2.init(98, 55);
|
||||
|
||||
for (size_t i = 0; i < 17; ++i)
|
||||
@ -1349,13 +1376,13 @@ TEST(CoordinationTest, TestRotateIntervalChanges)
|
||||
|
||||
changelog_2.compact(105);
|
||||
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_1_100.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_101_110.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_111_117.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_118_124.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_125_131.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_1_100.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_101_110.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_111_117.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_118_124.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_125_131.bin" + params.extension));
|
||||
|
||||
DB::KeeperLogStore changelog_3("./logs", 5, true);
|
||||
DB::KeeperLogStore changelog_3("./logs", 5, true, params.enable_compression);
|
||||
changelog_3.init(116, 3);
|
||||
for (size_t i = 0; i < 17; ++i)
|
||||
{
|
||||
@ -1367,17 +1394,17 @@ TEST(CoordinationTest, TestRotateIntervalChanges)
|
||||
}
|
||||
|
||||
changelog_3.compact(125);
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_101_110.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_111_117.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_118_124.bin"));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_101_110.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_111_117.bin" + params.extension));
|
||||
EXPECT_FALSE(fs::exists("./logs/changelog_118_124.bin" + params.extension));
|
||||
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_125_131.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_132_136.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_137_141.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_142_146.bin"));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_125_131.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_132_136.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_137_141.bin" + params.extension));
|
||||
EXPECT_TRUE(fs::exists("./logs/changelog_142_146.bin" + params.extension));
|
||||
}
|
||||
|
||||
TEST(CoordinationTest, TestSessionExpiryQueue)
|
||||
TEST_P(CoordinationTest, TestSessionExpiryQueue)
|
||||
{
|
||||
using namespace Coordination;
|
||||
SessionExpiryQueue queue(500);
|
||||
@ -1395,6 +1422,56 @@ TEST(CoordinationTest, TestSessionExpiryQueue)
|
||||
}
|
||||
|
||||
|
||||
TEST_P(CoordinationTest, TestCompressedLogsMultipleRewrite)
|
||||
{
|
||||
using namespace Coordination;
|
||||
auto test_params = GetParam();
|
||||
ChangelogDirTest snapshots("./logs");
|
||||
DB::KeeperLogStore changelog("./logs", 100, true, test_params.enable_compression);
|
||||
|
||||
changelog.init(0, 3);
|
||||
for (size_t i = 1; i < 55; ++i)
|
||||
{
|
||||
std::shared_ptr<ZooKeeperCreateRequest> request = std::make_shared<ZooKeeperCreateRequest>();
|
||||
request->path = "/hello_" + std::to_string(i);
|
||||
auto entry = getLogEntryFromZKRequest(0, 1, request);
|
||||
changelog.append(entry);
|
||||
changelog.end_of_append_batch(0, 0);
|
||||
}
|
||||
|
||||
|
||||
DB::KeeperLogStore changelog1("./logs", 100, true, test_params.enable_compression);
|
||||
changelog1.init(0, 3);
|
||||
for (size_t i = 55; i < 70; ++i)
|
||||
{
|
||||
std::shared_ptr<ZooKeeperCreateRequest> request = std::make_shared<ZooKeeperCreateRequest>();
|
||||
request->path = "/hello_" + std::to_string(i);
|
||||
auto entry = getLogEntryFromZKRequest(0, 1, request);
|
||||
changelog1.append(entry);
|
||||
changelog1.end_of_append_batch(0, 0);
|
||||
}
|
||||
|
||||
DB::KeeperLogStore changelog2("./logs", 100, true, test_params.enable_compression);
|
||||
changelog2.init(0, 3);
|
||||
for (size_t i = 70; i < 80; ++i)
|
||||
{
|
||||
std::shared_ptr<ZooKeeperCreateRequest> request = std::make_shared<ZooKeeperCreateRequest>();
|
||||
request->path = "/hello_" + std::to_string(i);
|
||||
auto entry = getLogEntryFromZKRequest(0, 1, request);
|
||||
changelog2.append(entry);
|
||||
changelog2.end_of_append_batch(0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(CoordinationTestSuite,
|
||||
CoordinationTest,
|
||||
::testing::ValuesIn(std::initializer_list<CompressionParam>{
|
||||
CompressionParam{true, ".zstd"},
|
||||
CompressionParam{false, ""}
|
||||
})
|
||||
);
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
Poco::AutoPtr<Poco::ConsoleChannel> channel(new Poco::ConsoleChannel(std::cerr));
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "Utils.h"
|
||||
#include <Common/parseRemoteDescription.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <IO/WriteBufferFromString.h>
|
||||
#include <IO/Operators.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -18,7 +20,7 @@ namespace postgres
|
||||
{
|
||||
|
||||
PoolWithFailover::PoolWithFailover(
|
||||
const Poco::Util::AbstractConfiguration & config, const String & config_prefix,
|
||||
const DB::ExternalDataSourcesConfigurationByPriority & configurations_by_priority,
|
||||
size_t pool_size, size_t pool_wait_timeout_, size_t max_tries_)
|
||||
: pool_wait_timeout(pool_wait_timeout_)
|
||||
, max_tries(max_tries_)
|
||||
@ -26,45 +28,19 @@ PoolWithFailover::PoolWithFailover(
|
||||
LOG_TRACE(&Poco::Logger::get("PostgreSQLConnectionPool"), "PostgreSQL connection pool size: {}, connection wait timeout: {}, max failover tries: {}",
|
||||
pool_size, pool_wait_timeout, max_tries_);
|
||||
|
||||
auto db = config.getString(config_prefix + ".db", "");
|
||||
auto host = config.getString(config_prefix + ".host", "");
|
||||
auto port = config.getUInt(config_prefix + ".port", 0);
|
||||
auto user = config.getString(config_prefix + ".user", "");
|
||||
auto password = config.getString(config_prefix + ".password", "");
|
||||
|
||||
if (config.has(config_prefix + ".replica"))
|
||||
for (const auto & [priority, configurations] : configurations_by_priority)
|
||||
{
|
||||
Poco::Util::AbstractConfiguration::Keys config_keys;
|
||||
config.keys(config_prefix, config_keys);
|
||||
|
||||
for (const auto & config_key : config_keys)
|
||||
for (const auto & replica_configuration : configurations)
|
||||
{
|
||||
if (config_key.starts_with("replica"))
|
||||
{
|
||||
std::string replica_name = config_prefix + "." + config_key;
|
||||
size_t priority = config.getInt(replica_name + ".priority", 0);
|
||||
|
||||
auto replica_host = config.getString(replica_name + ".host", host);
|
||||
auto replica_port = config.getUInt(replica_name + ".port", port);
|
||||
auto replica_user = config.getString(replica_name + ".user", user);
|
||||
auto replica_password = config.getString(replica_name + ".password", password);
|
||||
|
||||
auto connection_string = formatConnectionString(db, replica_host, replica_port, replica_user, replica_password).first;
|
||||
replicas_with_priority[priority].emplace_back(connection_string, pool_size);
|
||||
}
|
||||
auto connection_string = formatConnectionString(replica_configuration.database,
|
||||
replica_configuration.host, replica_configuration.port, replica_configuration.username, replica_configuration.password).first;
|
||||
replicas_with_priority[priority].emplace_back(connection_string, pool_size, getConnectionForLog(replica_configuration.host, replica_configuration.port));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto connection_string = formatConnectionString(db, host, port, user, password).first;
|
||||
replicas_with_priority[0].emplace_back(connection_string, pool_size);
|
||||
}
|
||||
}
|
||||
|
||||
PoolWithFailover::PoolWithFailover(
|
||||
const std::string & database,
|
||||
const RemoteDescription & addresses,
|
||||
const std::string & user, const std::string & password,
|
||||
const DB::StoragePostgreSQLConfiguration & configuration,
|
||||
size_t pool_size, size_t pool_wait_timeout_, size_t max_tries_)
|
||||
: pool_wait_timeout(pool_wait_timeout_)
|
||||
, max_tries(max_tries_)
|
||||
@ -73,11 +49,11 @@ PoolWithFailover::PoolWithFailover(
|
||||
pool_size, pool_wait_timeout, max_tries_);
|
||||
|
||||
/// Replicas have the same priority, but traversed replicas are moved to the end of the queue.
|
||||
for (const auto & [host, port] : addresses)
|
||||
for (const auto & [host, port] : configuration.addresses)
|
||||
{
|
||||
LOG_DEBUG(&Poco::Logger::get("PostgreSQLPoolWithFailover"), "Adding address host: {}, port: {} to connection pool", host, port);
|
||||
auto connection_string = formatConnectionString(database, host, port, user, password).first;
|
||||
replicas_with_priority[0].emplace_back(connection_string, pool_size);
|
||||
auto connection_string = formatConnectionString(configuration.database, host, port, configuration.username, configuration.password).first;
|
||||
replicas_with_priority[0].emplace_back(connection_string, pool_size, getConnectionForLog(host, port));
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +61,7 @@ ConnectionHolderPtr PoolWithFailover::get()
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
DB::WriteBufferFromOwnString error_message;
|
||||
for (size_t try_idx = 0; try_idx < max_tries; ++try_idx)
|
||||
{
|
||||
for (auto & priority : replicas_with_priority)
|
||||
@ -115,6 +92,7 @@ ConnectionHolderPtr PoolWithFailover::get()
|
||||
catch (const pqxx::broken_connection & pqxx_error)
|
||||
{
|
||||
LOG_ERROR(log, "Connection error: {}", pqxx_error.what());
|
||||
error_message << "Try " << try_idx + 1 << ". Connection to `" << replica.name_for_log << "` failed: " << pqxx_error.what() << "\n";
|
||||
|
||||
replica.pool->returnObject(std::move(connection));
|
||||
continue;
|
||||
@ -136,7 +114,7 @@ ConnectionHolderPtr PoolWithFailover::get()
|
||||
}
|
||||
}
|
||||
|
||||
throw DB::Exception(DB::ErrorCodes::POSTGRESQL_CONNECTION_FAILURE, "Unable to connect to any of the replicas");
|
||||
throw DB::Exception(DB::ErrorCodes::POSTGRESQL_CONNECTION_FAILURE, error_message.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <mutex>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <Storages/ExternalDataSourceConfiguration.h>
|
||||
|
||||
|
||||
namespace postgres
|
||||
@ -27,17 +28,13 @@ public:
|
||||
static constexpr inline auto POSTGRESQL_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES = 5;
|
||||
|
||||
PoolWithFailover(
|
||||
const Poco::Util::AbstractConfiguration & config,
|
||||
const std::string & config_prefix,
|
||||
const DB::ExternalDataSourcesConfigurationByPriority & configurations_by_priority,
|
||||
size_t pool_size = POSTGRESQL_POOL_DEFAULT_SIZE,
|
||||
size_t pool_wait_timeout = POSTGRESQL_POOL_WAIT_TIMEOUT,
|
||||
size_t max_tries_ = POSTGRESQL_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES);
|
||||
|
||||
PoolWithFailover(
|
||||
const std::string & database,
|
||||
const RemoteDescription & addresses,
|
||||
const std::string & user,
|
||||
const std::string & password,
|
||||
const DB::StoragePostgreSQLConfiguration & configuration,
|
||||
size_t pool_size = POSTGRESQL_POOL_DEFAULT_SIZE,
|
||||
size_t pool_wait_timeout = POSTGRESQL_POOL_WAIT_TIMEOUT,
|
||||
size_t max_tries_ = POSTGRESQL_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES);
|
||||
@ -51,9 +48,10 @@ private:
|
||||
{
|
||||
String connection_string;
|
||||
PoolPtr pool;
|
||||
String name_for_log;
|
||||
|
||||
PoolHolder(const String & connection_string_, size_t pool_size)
|
||||
: connection_string(connection_string_), pool(std::make_shared<Pool>(pool_size)) {}
|
||||
PoolHolder(const String & connection_string_, size_t pool_size, const String & name_for_log_)
|
||||
: connection_string(connection_string_), pool(std::make_shared<Pool>(pool_size)), name_for_log(name_for_log_) {}
|
||||
};
|
||||
|
||||
/// Highest priority is 0, the bigger the number in map, the less the priority
|
||||
|
@ -3,6 +3,7 @@
|
||||
#if USE_LIBPQXX
|
||||
|
||||
#include <IO/Operators.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
namespace postgres
|
||||
{
|
||||
@ -19,6 +20,11 @@ ConnectionInfo formatConnectionString(String dbname, String host, UInt16 port, S
|
||||
return std::make_pair(out.str(), host + ':' + DB::toString(port));
|
||||
}
|
||||
|
||||
String getConnectionForLog(const String & host, UInt16 port)
|
||||
{
|
||||
return host + ":" + DB::toString(port);
|
||||
}
|
||||
|
||||
String formatNameForLogs(const String & postgres_database_name, const String & postgres_table_name)
|
||||
{
|
||||
/// Logger for StorageMaterializedPostgreSQL - both db and table names.
|
||||
|
@ -22,6 +22,8 @@ namespace postgres
|
||||
|
||||
ConnectionInfo formatConnectionString(String dbname, String host, UInt16 port, String user, String password);
|
||||
|
||||
String getConnectionForLog(const String & host, UInt16 port);
|
||||
|
||||
String formatNameForLogs(const String & postgres_database_name, const String & postgres_table_name);
|
||||
|
||||
}
|
||||
|
@ -386,6 +386,7 @@ class IColumn;
|
||||
M(Bool, low_cardinality_allow_in_native_format, true, "Use LowCardinality type in Native format. Otherwise, convert LowCardinality columns to ordinary for select query, and convert ordinary columns to required LowCardinality for insert query.", 0) \
|
||||
M(Bool, cancel_http_readonly_queries_on_client_close, false, "Cancel HTTP readonly queries when a client closes the connection without waiting for response.", 0) \
|
||||
M(Bool, external_table_functions_use_nulls, true, "If it is set to true, external table functions will implicitly use Nullable type if needed. Otherwise NULLs will be substituted with default values. Currently supported only by 'mysql', 'postgresql' and 'odbc' table functions.", 0) \
|
||||
M(Bool, external_table_strict_query, false, "If it is set to true, transforming expression to local filter is forbidden for queries to external tables.", 0) \
|
||||
\
|
||||
M(Bool, allow_hyperscan, true, "Allow functions that use Hyperscan library. Disable to avoid potentially long compilation times and excessive resource usage.", 0) \
|
||||
M(UInt64, max_hyperscan_regexp_length, 0, "Max length of regexp than can be used in hyperscan multi-match functions. Zero means unlimited.", 0) \
|
||||
@ -515,8 +516,8 @@ class IColumn;
|
||||
M(Milliseconds, async_insert_busy_timeout_ms, 200, "Maximum time to wait before dumping collected data per query since the first data appeared", 0) \
|
||||
M(Milliseconds, async_insert_stale_timeout_ms, 0, "Maximum time to wait before dumping collected data per query since the last data appeared. Zero means no timeout at all", 0) \
|
||||
\
|
||||
M(Int64, remote_disk_read_backoff_threashold, 10000, "Max wait time when trying to read data for remote disk", 0) \
|
||||
M(Int64, remote_disk_read_backoff_max_tries, 5, "Max attempts to read with backoff", 0) \
|
||||
M(Int64, remote_fs_read_backoff_threshold, 10000, "Max wait time when trying to read data for remote disk", 0) \
|
||||
M(Int64, remote_fs_read_backoff_max_tries, 5, "Max attempts to read with backoff", 0) \
|
||||
\
|
||||
/** Experimental functions */ \
|
||||
M(Bool, allow_experimental_funnel_functions, false, "Enable experimental functions for funnel analysis.", 0) \
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/formatAST.h>
|
||||
#include <Common/Macros.h>
|
||||
#include <Storages/ExternalDataSourceConfiguration.h>
|
||||
#include <filesystem>
|
||||
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
@ -38,6 +39,7 @@
|
||||
#include <Databases/PostgreSQL/DatabasePostgreSQL.h> // Y_IGNORE
|
||||
#include <Databases/PostgreSQL/DatabaseMaterializedPostgreSQL.h>
|
||||
#include <Storages/PostgreSQL/MaterializedPostgreSQLSettings.h>
|
||||
#include <Storages/StoragePostgreSQL.h>
|
||||
#endif
|
||||
|
||||
#if USE_SQLITE
|
||||
@ -141,40 +143,66 @@ DatabasePtr DatabaseFactory::getImpl(const ASTCreateQuery & create, const String
|
||||
else if (engine_name == "MySQL" || engine_name == "MaterializeMySQL" || engine_name == "MaterializedMySQL")
|
||||
{
|
||||
const ASTFunction * engine = engine_define->engine;
|
||||
if (!engine->arguments || engine->arguments->children.size() != 4)
|
||||
throw Exception(
|
||||
engine_name + " Database require mysql_hostname, mysql_database_name, mysql_username, mysql_password arguments.",
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
if (!engine->arguments)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Engine `{}` must have arguments", engine_name);
|
||||
|
||||
StorageMySQLConfiguration configuration;
|
||||
ASTs & arguments = engine->arguments->children;
|
||||
arguments[1] = evaluateConstantExpressionOrIdentifierAsLiteral(arguments[1], context);
|
||||
|
||||
const auto & host_port = safeGetLiteralValue<String>(arguments[0], engine_name);
|
||||
const auto & mysql_database_name = safeGetLiteralValue<String>(arguments[1], engine_name);
|
||||
const auto & mysql_user_name = safeGetLiteralValue<String>(arguments[2], engine_name);
|
||||
const auto & mysql_user_password = safeGetLiteralValue<String>(arguments[3], engine_name);
|
||||
if (auto named_collection = getExternalDataSourceConfiguration(arguments, context, true))
|
||||
{
|
||||
auto [common_configuration, storage_specific_args] = named_collection.value();
|
||||
|
||||
configuration.set(common_configuration);
|
||||
configuration.addresses = {std::make_pair(configuration.host, configuration.port)};
|
||||
|
||||
if (!storage_specific_args.empty())
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"MySQL database require mysql_hostname, mysql_database_name, mysql_username, mysql_password arguments.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (arguments.size() != 4)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"MySQL database require mysql_hostname, mysql_database_name, mysql_username, mysql_password arguments.");
|
||||
|
||||
|
||||
arguments[1] = evaluateConstantExpressionOrIdentifierAsLiteral(arguments[1], context);
|
||||
const auto & host_port = safeGetLiteralValue<String>(arguments[0], engine_name);
|
||||
|
||||
if (engine_name == "MySQL")
|
||||
{
|
||||
size_t max_addresses = context->getSettingsRef().glob_expansion_max_elements;
|
||||
configuration.addresses = parseRemoteDescriptionForExternalDatabase(host_port, max_addresses, 3306);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto & [remote_host, remote_port] = parseAddress(host_port, 3306);
|
||||
configuration.host = remote_host;
|
||||
configuration.port = remote_port;
|
||||
}
|
||||
|
||||
configuration.database = safeGetLiteralValue<String>(arguments[1], engine_name);
|
||||
configuration.username = safeGetLiteralValue<String>(arguments[2], engine_name);
|
||||
configuration.password = safeGetLiteralValue<String>(arguments[3], engine_name);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (engine_name == "MySQL")
|
||||
{
|
||||
auto mysql_database_settings = std::make_unique<ConnectionMySQLSettings>();
|
||||
/// Split into replicas if needed.
|
||||
size_t max_addresses = context->getSettingsRef().glob_expansion_max_elements;
|
||||
auto addresses = parseRemoteDescriptionForExternalDatabase(host_port, max_addresses, 3306);
|
||||
auto mysql_pool = mysqlxx::PoolWithFailover(mysql_database_name, addresses, mysql_user_name, mysql_user_password);
|
||||
auto mysql_pool = mysqlxx::PoolWithFailover(configuration.database, configuration.addresses, configuration.username, configuration.password);
|
||||
|
||||
mysql_database_settings->loadFromQueryContext(context);
|
||||
mysql_database_settings->loadFromQuery(*engine_define); /// higher priority
|
||||
|
||||
return std::make_shared<DatabaseMySQL>(
|
||||
context, database_name, metadata_path, engine_define, mysql_database_name, std::move(mysql_database_settings), std::move(mysql_pool));
|
||||
context, database_name, metadata_path, engine_define, configuration.database, std::move(mysql_database_settings), std::move(mysql_pool));
|
||||
}
|
||||
|
||||
const auto & [remote_host_name, remote_port] = parseAddress(host_port, 3306);
|
||||
MySQLClient client(remote_host_name, remote_port, mysql_user_name, mysql_user_password);
|
||||
auto mysql_pool = mysqlxx::Pool(mysql_database_name, remote_host_name, mysql_user_name, mysql_user_password, remote_port);
|
||||
|
||||
MySQLClient client(configuration.host, configuration.port, configuration.username, configuration.password);
|
||||
auto mysql_pool = mysqlxx::Pool(configuration.database, configuration.host, configuration.username, configuration.password, configuration.port);
|
||||
|
||||
auto materialize_mode_settings = std::make_unique<MaterializedMySQLSettings>();
|
||||
|
||||
@ -183,12 +211,12 @@ DatabasePtr DatabaseFactory::getImpl(const ASTCreateQuery & create, const String
|
||||
|
||||
if (create.uuid == UUIDHelpers::Nil)
|
||||
return std::make_shared<DatabaseMaterializedMySQL<DatabaseOrdinary>>(
|
||||
context, database_name, metadata_path, uuid, mysql_database_name,
|
||||
std::move(mysql_pool), std::move(client), std::move(materialize_mode_settings));
|
||||
context, database_name, metadata_path, uuid, configuration.database, std::move(mysql_pool),
|
||||
std::move(client), std::move(materialize_mode_settings));
|
||||
else
|
||||
return std::make_shared<DatabaseMaterializedMySQL<DatabaseAtomic>>(
|
||||
context, database_name, metadata_path, uuid, mysql_database_name,
|
||||
std::move(mysql_pool), std::move(client), std::move(materialize_mode_settings));
|
||||
context, database_name, metadata_path, uuid, configuration.database, std::move(mysql_pool),
|
||||
std::move(client), std::move(materialize_mode_settings));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -242,77 +270,109 @@ DatabasePtr DatabaseFactory::getImpl(const ASTCreateQuery & create, const String
|
||||
else if (engine_name == "PostgreSQL")
|
||||
{
|
||||
const ASTFunction * engine = engine_define->engine;
|
||||
|
||||
if (!engine->arguments || engine->arguments->children.size() < 4 || engine->arguments->children.size() > 6)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"{} Database require `host:port`, `database_name`, `username`, `password` [, `schema` = "", `use_table_cache` = 0].",
|
||||
engine_name);
|
||||
if (!engine->arguments)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Engine `{}` must have arguments", engine_name);
|
||||
|
||||
ASTs & engine_args = engine->arguments->children;
|
||||
auto use_table_cache = false;
|
||||
StoragePostgreSQLConfiguration configuration;
|
||||
|
||||
for (auto & engine_arg : engine_args)
|
||||
engine_arg = evaluateConstantExpressionOrIdentifierAsLiteral(engine_arg, context);
|
||||
if (auto named_collection = getExternalDataSourceConfiguration(engine_args, context, true))
|
||||
{
|
||||
auto [common_configuration, storage_specific_args] = named_collection.value();
|
||||
|
||||
const auto & host_port = safeGetLiteralValue<String>(engine_args[0], engine_name);
|
||||
const auto & postgres_database_name = safeGetLiteralValue<String>(engine_args[1], engine_name);
|
||||
const auto & username = safeGetLiteralValue<String>(engine_args[2], engine_name);
|
||||
const auto & password = safeGetLiteralValue<String>(engine_args[3], engine_name);
|
||||
configuration.set(common_configuration);
|
||||
configuration.addresses = {std::make_pair(configuration.host, configuration.port)};
|
||||
|
||||
String schema;
|
||||
if (engine->arguments->children.size() >= 5)
|
||||
schema = safeGetLiteralValue<String>(engine_args[4], engine_name);
|
||||
for (const auto & [arg_name, arg_value] : storage_specific_args)
|
||||
{
|
||||
if (arg_name == "use_table_cache")
|
||||
use_table_cache = true;
|
||||
else
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Unexpected key-value argument."
|
||||
"Got: {}, but expected one of:"
|
||||
"host, port, username, password, database, schema, use_table_cache.", arg_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (engine_args.size() < 4 || engine_args.size() > 6)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"PostgreSQL Database require `host:port`, `database_name`, `username`, `password`"
|
||||
"[, `schema` = "", `use_table_cache` = 0");
|
||||
|
||||
auto use_table_cache = 0;
|
||||
if (engine->arguments->children.size() >= 6)
|
||||
for (auto & engine_arg : engine_args)
|
||||
engine_arg = evaluateConstantExpressionOrIdentifierAsLiteral(engine_arg, context);
|
||||
|
||||
const auto & host_port = safeGetLiteralValue<String>(engine_args[0], engine_name);
|
||||
size_t max_addresses = context->getSettingsRef().glob_expansion_max_elements;
|
||||
|
||||
configuration.addresses = parseRemoteDescriptionForExternalDatabase(host_port, max_addresses, 5432);
|
||||
configuration.database = safeGetLiteralValue<String>(engine_args[1], engine_name);
|
||||
configuration.username = safeGetLiteralValue<String>(engine_args[2], engine_name);
|
||||
configuration.password = safeGetLiteralValue<String>(engine_args[3], engine_name);
|
||||
|
||||
if (engine_args.size() >= 5)
|
||||
configuration.schema = safeGetLiteralValue<String>(engine_args[4], engine_name);
|
||||
}
|
||||
|
||||
if (engine_args.size() >= 6)
|
||||
use_table_cache = safeGetLiteralValue<UInt8>(engine_args[5], engine_name);
|
||||
|
||||
/// Split into replicas if needed.
|
||||
size_t max_addresses = context->getSettingsRef().glob_expansion_max_elements;
|
||||
auto addresses = parseRemoteDescriptionForExternalDatabase(host_port, max_addresses, 5432);
|
||||
|
||||
/// no connection is made here
|
||||
auto connection_pool = std::make_shared<postgres::PoolWithFailover>(
|
||||
postgres_database_name,
|
||||
addresses,
|
||||
username, password,
|
||||
auto pool = std::make_shared<postgres::PoolWithFailover>(configuration,
|
||||
context->getSettingsRef().postgresql_connection_pool_size,
|
||||
context->getSettingsRef().postgresql_connection_pool_wait_timeout);
|
||||
|
||||
return std::make_shared<DatabasePostgreSQL>(
|
||||
context, metadata_path, engine_define, database_name, postgres_database_name, schema, connection_pool, use_table_cache);
|
||||
context, metadata_path, engine_define, database_name, configuration, pool, use_table_cache);
|
||||
}
|
||||
else if (engine_name == "MaterializedPostgreSQL")
|
||||
{
|
||||
const ASTFunction * engine = engine_define->engine;
|
||||
|
||||
if (!engine->arguments || engine->arguments->children.size() != 4)
|
||||
{
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"{} Database require `host:port`, `database_name`, `username`, `password`.",
|
||||
engine_name);
|
||||
}
|
||||
if (!engine->arguments)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Engine `{}` must have arguments", engine_name);
|
||||
|
||||
ASTs & engine_args = engine->arguments->children;
|
||||
StoragePostgreSQLConfiguration configuration;
|
||||
|
||||
for (auto & engine_arg : engine_args)
|
||||
engine_arg = evaluateConstantExpressionOrIdentifierAsLiteral(engine_arg, context);
|
||||
if (auto named_collection = getExternalDataSourceConfiguration(engine_args, context, true))
|
||||
{
|
||||
auto [common_configuration, storage_specific_args] = named_collection.value();
|
||||
configuration.set(common_configuration);
|
||||
|
||||
const auto & host_port = safeGetLiteralValue<String>(engine_args[0], engine_name);
|
||||
const auto & postgres_database_name = safeGetLiteralValue<String>(engine_args[1], engine_name);
|
||||
const auto & username = safeGetLiteralValue<String>(engine_args[2], engine_name);
|
||||
const auto & password = safeGetLiteralValue<String>(engine_args[3], engine_name);
|
||||
if (!storage_specific_args.empty())
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"MaterializedPostgreSQL Database requires only `host`, `port`, `database_name`, `username`, `password`.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (engine_args.size() != 4)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"MaterializedPostgreSQL Database require `host:port`, `database_name`, `username`, `password`.");
|
||||
|
||||
auto parsed_host_port = parseAddress(host_port, 5432);
|
||||
auto connection_info = postgres::formatConnectionString(postgres_database_name, parsed_host_port.first, parsed_host_port.second, username, password);
|
||||
for (auto & engine_arg : engine_args)
|
||||
engine_arg = evaluateConstantExpressionOrIdentifierAsLiteral(engine_arg, context);
|
||||
|
||||
auto parsed_host_port = parseAddress(safeGetLiteralValue<String>(engine_args[0], engine_name), 5432);
|
||||
|
||||
configuration.host = parsed_host_port.first;
|
||||
configuration.port = parsed_host_port.second;
|
||||
configuration.database = safeGetLiteralValue<String>(engine_args[1], engine_name);
|
||||
configuration.username = safeGetLiteralValue<String>(engine_args[2], engine_name);
|
||||
configuration.password = safeGetLiteralValue<String>(engine_args[3], engine_name);
|
||||
}
|
||||
|
||||
auto connection_info = postgres::formatConnectionString(
|
||||
configuration.database, configuration.host, configuration.port, configuration.username, configuration.password);
|
||||
|
||||
auto postgresql_replica_settings = std::make_unique<MaterializedPostgreSQLSettings>();
|
||||
|
||||
if (engine_define->settings)
|
||||
postgresql_replica_settings->loadFromQuery(*engine_define);
|
||||
|
||||
return std::make_shared<DatabaseMaterializedPostgreSQL>(
|
||||
context, metadata_path, uuid, create.attach,
|
||||
database_name, postgres_database_name, connection_info,
|
||||
database_name, configuration.database, connection_info,
|
||||
std::move(postgresql_replica_settings));
|
||||
}
|
||||
|
||||
|
@ -42,12 +42,17 @@ void DatabaseMemory::dropTable(
|
||||
try
|
||||
{
|
||||
table->drop();
|
||||
fs::path table_data_dir{getTableDataPath(table_name)};
|
||||
if (fs::exists(table_data_dir))
|
||||
fs::remove_all(table_data_dir);
|
||||
if (table->storesDataOnDisk())
|
||||
{
|
||||
assert(database_name != DatabaseCatalog::TEMPORARY_DATABASE);
|
||||
fs::path table_data_dir{getTableDataPath(table_name)};
|
||||
if (fs::exists(table_data_dir))
|
||||
fs::remove_all(table_data_dir);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
assert(database_name != DatabaseCatalog::TEMPORARY_DATABASE);
|
||||
attachTableUnlocked(table_name, table, lock);
|
||||
throw;
|
||||
}
|
||||
|
@ -39,16 +39,14 @@ DatabasePostgreSQL::DatabasePostgreSQL(
|
||||
const String & metadata_path_,
|
||||
const ASTStorage * database_engine_define_,
|
||||
const String & dbname_,
|
||||
const String & postgres_dbname_,
|
||||
const String & postgres_schema_,
|
||||
const StoragePostgreSQLConfiguration & configuration_,
|
||||
postgres::PoolWithFailoverPtr pool_,
|
||||
bool cache_tables_)
|
||||
: IDatabase(dbname_)
|
||||
, WithContext(context_->getGlobalContext())
|
||||
, metadata_path(metadata_path_)
|
||||
, database_engine_define(database_engine_define_->clone())
|
||||
, postgres_dbname(postgres_dbname_)
|
||||
, postgres_schema(postgres_schema_)
|
||||
, configuration(configuration_)
|
||||
, pool(std::move(pool_))
|
||||
, cache_tables(cache_tables_)
|
||||
{
|
||||
@ -59,17 +57,17 @@ DatabasePostgreSQL::DatabasePostgreSQL(
|
||||
|
||||
String DatabasePostgreSQL::getTableNameForLogs(const String & table_name) const
|
||||
{
|
||||
if (postgres_schema.empty())
|
||||
return fmt::format("{}.{}", postgres_dbname, table_name);
|
||||
return fmt::format("{}.{}.{}", postgres_dbname, postgres_schema, table_name);
|
||||
if (configuration.schema.empty())
|
||||
return fmt::format("{}.{}", configuration.database, table_name);
|
||||
return fmt::format("{}.{}.{}", configuration.database, configuration.schema, table_name);
|
||||
}
|
||||
|
||||
|
||||
String DatabasePostgreSQL::formatTableName(const String & table_name) const
|
||||
{
|
||||
if (postgres_schema.empty())
|
||||
if (configuration.schema.empty())
|
||||
return doubleQuoteString(table_name);
|
||||
return fmt::format("{}.{}", doubleQuoteString(postgres_schema), doubleQuoteString(table_name));
|
||||
return fmt::format("{}.{}", doubleQuoteString(configuration.schema), doubleQuoteString(table_name));
|
||||
}
|
||||
|
||||
|
||||
@ -78,7 +76,7 @@ bool DatabasePostgreSQL::empty() const
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
auto connection_holder = pool->get();
|
||||
auto tables_list = fetchPostgreSQLTablesList(connection_holder->get(), postgres_schema);
|
||||
auto tables_list = fetchPostgreSQLTablesList(connection_holder->get(), configuration.schema);
|
||||
|
||||
for (const auto & table_name : tables_list)
|
||||
if (!detached_or_dropped.count(table_name))
|
||||
@ -94,7 +92,7 @@ DatabaseTablesIteratorPtr DatabasePostgreSQL::getTablesIterator(ContextPtr local
|
||||
|
||||
Tables tables;
|
||||
auto connection_holder = pool->get();
|
||||
auto table_names = fetchPostgreSQLTablesList(connection_holder->get(), postgres_schema);
|
||||
auto table_names = fetchPostgreSQLTablesList(connection_holder->get(), configuration.schema);
|
||||
|
||||
for (const auto & table_name : table_names)
|
||||
if (!detached_or_dropped.count(table_name))
|
||||
@ -125,7 +123,7 @@ bool DatabasePostgreSQL::checkPostgresTable(const String & table_name) const
|
||||
"WHERE schemaname != 'pg_catalog' AND {} "
|
||||
"AND tablename = '{}'",
|
||||
formatTableName(table_name),
|
||||
(postgres_schema.empty() ? "schemaname != 'information_schema'" : "schemaname = " + quoteString(postgres_schema)),
|
||||
(configuration.schema.empty() ? "schemaname != 'information_schema'" : "schemaname = " + quoteString(configuration.schema)),
|
||||
formatTableName(table_name)));
|
||||
}
|
||||
catch (pqxx::undefined_table const &)
|
||||
@ -179,7 +177,7 @@ StoragePtr DatabasePostgreSQL::fetchTable(const String & table_name, ContextPtr,
|
||||
|
||||
auto storage = StoragePostgreSQL::create(
|
||||
StorageID(database_name, table_name), pool, table_name,
|
||||
ColumnsDescription{*columns}, ConstraintsDescription{}, String{}, postgres_schema);
|
||||
ColumnsDescription{*columns}, ConstraintsDescription{}, String{}, configuration.schema, configuration.on_conflict);
|
||||
|
||||
if (cache_tables)
|
||||
cached_tables[table_name] = storage;
|
||||
@ -306,7 +304,7 @@ void DatabasePostgreSQL::removeOutdatedTables()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex};
|
||||
auto connection_holder = pool->get();
|
||||
auto actual_tables = fetchPostgreSQLTablesList(connection_holder->get(), postgres_schema);
|
||||
auto actual_tables = fetchPostgreSQLTablesList(connection_holder->get(), configuration.schema);
|
||||
|
||||
if (cache_tables)
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <Core/BackgroundSchedulePool.h>
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Core/PostgreSQL/PoolWithFailover.h>
|
||||
|
||||
#include <Storages/ExternalDataSourceConfiguration.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -32,8 +32,7 @@ public:
|
||||
const String & metadata_path_,
|
||||
const ASTStorage * database_engine_define,
|
||||
const String & dbname_,
|
||||
const String & postgres_dbname_,
|
||||
const String & postgres_schema_,
|
||||
const StoragePostgreSQLConfiguration & configuration,
|
||||
postgres::PoolWithFailoverPtr pool_,
|
||||
bool cache_tables_);
|
||||
|
||||
@ -70,8 +69,7 @@ protected:
|
||||
private:
|
||||
String metadata_path;
|
||||
ASTPtr database_engine_define;
|
||||
String postgres_dbname;
|
||||
String postgres_schema;
|
||||
StoragePostgreSQLConfiguration configuration;
|
||||
postgres::PoolWithFailoverPtr pool;
|
||||
const bool cache_tables;
|
||||
|
||||
|
@ -9,6 +9,7 @@ PEERDIR(
|
||||
|
||||
|
||||
SRCS(
|
||||
DDLDependencyVisitor.cpp
|
||||
DatabaseAtomic.cpp
|
||||
DatabaseDictionary.cpp
|
||||
DatabaseFactory.cpp
|
||||
@ -30,6 +31,7 @@ SRCS(
|
||||
SQLite/DatabaseSQLite.cpp
|
||||
SQLite/SQLiteUtils.cpp
|
||||
SQLite/fetchSQLiteTableStructure.cpp
|
||||
TablesLoader.cpp
|
||||
|
||||
)
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include "DictionarySourceFactory.h"
|
||||
#include "DictionaryStructure.h"
|
||||
#include "registerDictionaries.h"
|
||||
#include <Storages/ExternalDataSourceConfiguration.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -13,19 +15,20 @@ void registerDictionarySourceMongoDB(DictionarySourceFactory & factory)
|
||||
const Poco::Util::AbstractConfiguration & config,
|
||||
const std::string & root_config_prefix,
|
||||
Block & sample_block,
|
||||
ContextPtr,
|
||||
ContextPtr context,
|
||||
const std::string & /* default_database */,
|
||||
bool /* created_from_ddl */)
|
||||
{
|
||||
const auto config_prefix = root_config_prefix + ".mongodb";
|
||||
auto configuration = getExternalDataSourceConfiguration(config, config_prefix, context);
|
||||
return std::make_unique<MongoDBDictionarySource>(dict_struct,
|
||||
config.getString(config_prefix + ".uri", ""),
|
||||
config.getString(config_prefix + ".host", ""),
|
||||
config.getUInt(config_prefix + ".port", 0),
|
||||
config.getString(config_prefix + ".user", ""),
|
||||
config.getString(config_prefix + ".password", ""),
|
||||
configuration.host,
|
||||
configuration.port,
|
||||
configuration.username,
|
||||
configuration.password,
|
||||
config.getString(config_prefix + ".method", ""),
|
||||
config.getString(config_prefix + ".db", ""),
|
||||
configuration.database,
|
||||
config.getString(config_prefix + ".collection"),
|
||||
sample_block);
|
||||
};
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <Core/Settings.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Processors/Pipe.h>
|
||||
#include <Storages/ExternalDataSourceConfiguration.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -32,38 +34,43 @@ void registerDictionarySourceMysql(DictionarySourceFactory & factory)
|
||||
[[maybe_unused]] const std::string & config_prefix,
|
||||
[[maybe_unused]] Block & sample_block,
|
||||
[[maybe_unused]] ContextPtr global_context,
|
||||
const std::string & /* default_database */,
|
||||
bool /* created_from_ddl */) -> DictionarySourcePtr {
|
||||
const std::string & /* default_database */,
|
||||
[[maybe_unused]] bool created_from_ddl) -> DictionarySourcePtr {
|
||||
#if USE_MYSQL
|
||||
StreamSettings mysql_input_stream_settings(global_context->getSettingsRef()
|
||||
, config.getBool(config_prefix + ".mysql.close_connection", false) || config.getBool(config_prefix + ".mysql.share_connection", false)
|
||||
, false
|
||||
, config.getBool(config_prefix + ".mysql.fail_on_connection_loss", false) ? 1 : default_num_tries_on_connection_loss);
|
||||
StreamSettings mysql_input_stream_settings(
|
||||
global_context->getSettingsRef(),
|
||||
config.getBool(config_prefix + ".mysql.close_connection", false) || config.getBool(config_prefix + ".mysql.share_connection", false),
|
||||
false,
|
||||
config.getBool(config_prefix + ".mysql.fail_on_connection_loss", false) ? 1 : default_num_tries_on_connection_loss);
|
||||
|
||||
auto settings_config_prefix = config_prefix + ".mysql";
|
||||
|
||||
auto table = config.getString(settings_config_prefix + ".table", "");
|
||||
auto where = config.getString(settings_config_prefix + ".where", "");
|
||||
auto configuration = getExternalDataSourceConfiguration(config, settings_config_prefix, global_context);
|
||||
auto query = config.getString(settings_config_prefix + ".query", "");
|
||||
|
||||
if (query.empty() && table.empty())
|
||||
if (query.empty() && configuration.table.empty())
|
||||
throw Exception(ErrorCodes::UNSUPPORTED_METHOD, "MySQL dictionary source configuration must contain table or query field");
|
||||
|
||||
MySQLDictionarySource::Configuration configuration
|
||||
MySQLDictionarySource::Configuration dictionary_configuration
|
||||
{
|
||||
.db = config.getString(settings_config_prefix + ".db", ""),
|
||||
.table = table,
|
||||
.db = configuration.database,
|
||||
.table = configuration.table,
|
||||
.query = query,
|
||||
.where = where,
|
||||
.where = config.getString(settings_config_prefix + ".where", ""),
|
||||
.invalidate_query = config.getString(settings_config_prefix + ".invalidate_query", ""),
|
||||
.update_field = config.getString(settings_config_prefix + ".update_field", ""),
|
||||
.update_lag = config.getUInt64(settings_config_prefix + ".update_lag", 1),
|
||||
.dont_check_update_time = config.getBool(settings_config_prefix + ".dont_check_update_time", false)
|
||||
};
|
||||
|
||||
auto pool = std::make_shared<mysqlxx::PoolWithFailover>(mysqlxx::PoolFactory::instance().get(config, settings_config_prefix));
|
||||
std::shared_ptr<mysqlxx::PoolWithFailover> pool;
|
||||
if (created_from_ddl)
|
||||
{
|
||||
std::vector<std::pair<String, UInt16>> addresses{std::make_pair(configuration.host, configuration.port)};
|
||||
pool = std::make_shared<mysqlxx::PoolWithFailover>(configuration.database, addresses, configuration.username, configuration.password);
|
||||
}
|
||||
else
|
||||
pool = std::make_shared<mysqlxx::PoolWithFailover>(mysqlxx::PoolFactory::instance().get(config, settings_config_prefix));
|
||||
|
||||
return std::make_unique<MySQLDictionarySource>(dict_struct, configuration, std::move(pool), sample_block, mysql_input_stream_settings);
|
||||
return std::make_unique<MySQLDictionarySource>(dict_struct, dictionary_configuration, std::move(pool), sample_block, mysql_input_stream_settings);
|
||||
#else
|
||||
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED,
|
||||
"Dictionary source of type `mysql` is disabled because ClickHouse was built without mysql support.");
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <DataStreams/PostgreSQLSource.h>
|
||||
#include "readInvalidateQuery.h"
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Storages/ExternalDataSourceConfiguration.h>
|
||||
#endif
|
||||
|
||||
|
||||
@ -177,22 +178,24 @@ void registerDictionarySourcePostgreSQL(DictionarySourceFactory & factory)
|
||||
const Poco::Util::AbstractConfiguration & config,
|
||||
const std::string & config_prefix,
|
||||
Block & sample_block,
|
||||
ContextPtr global_context,
|
||||
ContextPtr context,
|
||||
const std::string & /* default_database */,
|
||||
bool /* created_from_ddl */) -> DictionarySourcePtr
|
||||
{
|
||||
#if USE_LIBPQXX
|
||||
const auto settings_config_prefix = config_prefix + ".postgresql";
|
||||
auto pool = std::make_shared<postgres::PoolWithFailover>(
|
||||
config, settings_config_prefix,
|
||||
global_context->getSettingsRef().postgresql_connection_pool_size,
|
||||
global_context->getSettingsRef().postgresql_connection_pool_wait_timeout);
|
||||
|
||||
PostgreSQLDictionarySource::Configuration configuration
|
||||
auto configuration = getExternalDataSourceConfigurationByPriority(config, settings_config_prefix, context);
|
||||
auto pool = std::make_shared<postgres::PoolWithFailover>(
|
||||
configuration.replicas_configurations,
|
||||
context->getSettingsRef().postgresql_connection_pool_size,
|
||||
context->getSettingsRef().postgresql_connection_pool_wait_timeout);
|
||||
|
||||
PostgreSQLDictionarySource::Configuration dictionary_configuration
|
||||
{
|
||||
.db = config.getString(fmt::format("{}.db", settings_config_prefix), ""),
|
||||
.schema = config.getString(fmt::format("{}.schema", settings_config_prefix), ""),
|
||||
.table = config.getString(fmt::format("{}.table", settings_config_prefix), ""),
|
||||
.db = configuration.database,
|
||||
.schema = configuration.schema,
|
||||
.table = configuration.table,
|
||||
.query = config.getString(fmt::format("{}.query", settings_config_prefix), ""),
|
||||
.where = config.getString(fmt::format("{}.where", settings_config_prefix), ""),
|
||||
.invalidate_query = config.getString(fmt::format("{}.invalidate_query", settings_config_prefix), ""),
|
||||
@ -200,13 +203,13 @@ void registerDictionarySourcePostgreSQL(DictionarySourceFactory & factory)
|
||||
.update_lag = config.getUInt64(fmt::format("{}.update_lag", settings_config_prefix), 1)
|
||||
};
|
||||
|
||||
return std::make_unique<PostgreSQLDictionarySource>(dict_struct, configuration, pool, sample_block);
|
||||
return std::make_unique<PostgreSQLDictionarySource>(dict_struct, dictionary_configuration, pool, sample_block);
|
||||
#else
|
||||
(void)dict_struct;
|
||||
(void)config;
|
||||
(void)config_prefix;
|
||||
(void)sample_block;
|
||||
(void)global_context;
|
||||
(void)context;
|
||||
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED,
|
||||
"Dictionary source of type `postgresql` is disabled because ClickHouse was built without postgresql support.");
|
||||
#endif
|
||||
|
@ -40,7 +40,7 @@ void DiskWebServer::initialize(const String & uri_path) const
|
||||
ReadWriteBufferFromHTTP::OutStreamCallback(),
|
||||
ConnectionTimeouts::getHTTPTimeouts(getContext()));
|
||||
String file_name;
|
||||
FileData file_data;
|
||||
FileData file_data{};
|
||||
|
||||
String dir_name = fs::path(uri_path.substr(url.size())) / "";
|
||||
LOG_TRACE(&Poco::Logger::get("DiskWeb"), "Adding directory: {}", dir_name);
|
||||
@ -112,23 +112,29 @@ public:
|
||||
const String & uri_,
|
||||
RemoteMetadata metadata_,
|
||||
ContextPtr context_,
|
||||
size_t buf_size_)
|
||||
size_t buf_size_,
|
||||
size_t backoff_threshold_,
|
||||
size_t max_tries_)
|
||||
: ReadIndirectBufferFromRemoteFS<ReadIndirectBufferFromWebServer>(metadata_)
|
||||
, uri(uri_)
|
||||
, context(context_)
|
||||
, buf_size(buf_size_)
|
||||
, backoff_threshold(backoff_threshold_)
|
||||
, max_tries(max_tries_)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<ReadIndirectBufferFromWebServer> createReadBuffer(const String & path) override
|
||||
{
|
||||
return std::make_unique<ReadIndirectBufferFromWebServer>(fs::path(uri) / path, context, buf_size);
|
||||
return std::make_unique<ReadIndirectBufferFromWebServer>(fs::path(uri) / path, context, buf_size, backoff_threshold, max_tries);
|
||||
}
|
||||
|
||||
private:
|
||||
String uri;
|
||||
ContextPtr context;
|
||||
size_t buf_size;
|
||||
size_t backoff_threshold;
|
||||
size_t max_tries;
|
||||
};
|
||||
|
||||
|
||||
@ -190,7 +196,8 @@ std::unique_ptr<ReadBufferFromFileBase> DiskWebServer::readFile(const String & p
|
||||
RemoteMetadata meta(path, remote_path);
|
||||
meta.remote_fs_objects.emplace_back(std::make_pair(remote_path, iter->second.size));
|
||||
|
||||
auto reader = std::make_unique<ReadBufferFromWebServer>(url, meta, getContext(), read_settings.remote_fs_buffer_size);
|
||||
auto reader = std::make_unique<ReadBufferFromWebServer>(url, meta, getContext(),
|
||||
read_settings.remote_fs_buffer_size, read_settings.remote_fs_backoff_threshold, read_settings.remote_fs_backoff_max_tries);
|
||||
return std::make_unique<SeekAvoidingReadBuffer>(std::move(reader), min_bytes_for_seek);
|
||||
}
|
||||
|
||||
|
@ -185,8 +185,8 @@ private:
|
||||
|
||||
struct FileData
|
||||
{
|
||||
FileType type;
|
||||
size_t size;
|
||||
FileType type{};
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
using Files = std::unordered_map<String, FileData>; /// file path -> file data
|
||||
|
@ -22,18 +22,17 @@ namespace ErrorCodes
|
||||
|
||||
static const auto WAIT_MS = 10;
|
||||
|
||||
ReadIndirectBufferFromWebServer::ReadIndirectBufferFromWebServer(const String & url_,
|
||||
ContextPtr context_,
|
||||
size_t buf_size_)
|
||||
|
||||
ReadIndirectBufferFromWebServer::ReadIndirectBufferFromWebServer(
|
||||
const String & url_, ContextPtr context_, size_t buf_size_, size_t backoff_threshold_, size_t max_tries_)
|
||||
: BufferWithOwnMemory<SeekableReadBuffer>(buf_size_)
|
||||
, log(&Poco::Logger::get("ReadIndirectBufferFromWebServer"))
|
||||
, context(context_)
|
||||
, url(url_)
|
||||
, buf_size(buf_size_)
|
||||
, backoff_threshold_ms(backoff_threshold_)
|
||||
, max_tries(max_tries_)
|
||||
{
|
||||
const auto & settings = context->getSettingsRef();
|
||||
wait_threshold_ms = settings.remote_disk_read_backoff_threashold;
|
||||
max_tries = settings.remote_disk_read_backoff_max_tries;
|
||||
}
|
||||
|
||||
|
||||
@ -79,7 +78,7 @@ bool ReadIndirectBufferFromWebServer::nextImpl()
|
||||
WriteBufferFromOwnString error_msg;
|
||||
for (size_t i = 0; (i < max_tries) && !successful_read && !next_result; ++i)
|
||||
{
|
||||
while (milliseconds_to_wait < wait_threshold_ms)
|
||||
while (milliseconds_to_wait < backoff_threshold_ms)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -18,7 +18,8 @@ class ReadIndirectBufferFromWebServer : public BufferWithOwnMemory<SeekableReadB
|
||||
public:
|
||||
explicit ReadIndirectBufferFromWebServer(const String & url_,
|
||||
ContextPtr context_,
|
||||
size_t buf_size_ = DBMS_DEFAULT_BUFFER_SIZE);
|
||||
size_t buf_size_ = DBMS_DEFAULT_BUFFER_SIZE,
|
||||
size_t backoff_threshold_ = 10000, size_t max_tries_ = 4);
|
||||
|
||||
bool nextImpl() override;
|
||||
|
||||
@ -39,7 +40,7 @@ private:
|
||||
|
||||
off_t offset = 0;
|
||||
|
||||
size_t wait_threshold_ms;
|
||||
size_t backoff_threshold_ms;
|
||||
size_t max_tries;
|
||||
};
|
||||
|
||||
|
@ -3,10 +3,12 @@
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionHelpers.h>
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <DataTypes/DataTypeMap.h>
|
||||
#include <DataTypes/DataTypeNullable.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/getLeastSupertype.h>
|
||||
#include <Columns/ColumnArray.h>
|
||||
#include <Columns/ColumnMap.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnFixedString.h>
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
@ -373,23 +375,108 @@ public:
|
||||
|
||||
size_t getNumberOfArguments() const override { return 2; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
const DataTypeArray * array_type = checkAndGetDataType<DataTypeArray>(arguments[0].get());
|
||||
auto first_argument_type = arguments[0].type;
|
||||
auto second_argument_type = arguments[1].type;
|
||||
|
||||
if (!array_type)
|
||||
throw Exception("First argument for function " + getName() + " must be an array.",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
const DataTypeArray * array_type = checkAndGetDataType<DataTypeArray>(first_argument_type.get());
|
||||
const DataTypeMap * map_type = checkAndGetDataType<DataTypeMap>(first_argument_type.get());
|
||||
|
||||
if (!arguments[1]->onlyNull() && !allowArguments(array_type->getNestedType(), arguments[1]))
|
||||
DataTypePtr inner_type;
|
||||
|
||||
/// If map is first argument only has(map_column, key) function is supported
|
||||
if constexpr (std::is_same_v<ConcreteAction, HasAction>)
|
||||
{
|
||||
if (!array_type && !map_type)
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"First argument for function {} must be an array or map.",
|
||||
getName());
|
||||
|
||||
inner_type = map_type ? map_type->getKeyType() : array_type->getNestedType();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_type)
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"First argument for function {} must be an array.",
|
||||
getName());
|
||||
|
||||
inner_type = array_type->getNestedType();
|
||||
}
|
||||
|
||||
if (!second_argument_type->onlyNull() && !allowArguments(inner_type, second_argument_type))
|
||||
{
|
||||
const char * first_argument_type_name = map_type ? "map" : "array";
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Types of array and 2nd argument of function `{}` must be identical up to nullability, cardinality, "
|
||||
"Types of {} and 2nd argument of function `{}` must be identical up to nullability, cardinality, "
|
||||
"numeric types, or Enum and numeric type. Passed: {} and {}.",
|
||||
getName(), arguments[0]->getName(), arguments[1]->getName());
|
||||
first_argument_type_name,
|
||||
getName(),
|
||||
first_argument_type->getName(),
|
||||
second_argument_type->getName());
|
||||
}
|
||||
|
||||
return std::make_shared<DataTypeNumber<ResultType>>();
|
||||
}
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t /*input_rows_count*/) const override
|
||||
{
|
||||
if constexpr (std::is_same_v<ConcreteAction, HasAction>)
|
||||
{
|
||||
if (isMap(arguments[0].type))
|
||||
{
|
||||
auto non_const_map_column = arguments[0].column->convertToFullColumnIfConst();
|
||||
|
||||
const auto & map_column = assert_cast<const ColumnMap &>(*non_const_map_column);
|
||||
const auto & map_array_column = map_column.getNestedColumn();
|
||||
auto offsets = map_array_column.getOffsetsPtr();
|
||||
auto keys = map_column.getNestedData().getColumnPtr(0);
|
||||
auto array_column = ColumnArray::create(std::move(keys), std::move(offsets));
|
||||
|
||||
const auto & type_map = assert_cast<const DataTypeMap &>(*arguments[0].type);
|
||||
auto array_type = std::make_shared<DataTypeArray>(type_map.getKeyType());
|
||||
|
||||
auto arguments_copy = arguments;
|
||||
arguments_copy[0].column = std::move(array_column);
|
||||
arguments_copy[0].type = std::move(array_type);
|
||||
arguments_copy[0].name = arguments[0].name;
|
||||
|
||||
return executeArrayImpl(arguments_copy, result_type);
|
||||
}
|
||||
}
|
||||
|
||||
return executeArrayImpl(arguments, result_type);
|
||||
}
|
||||
|
||||
private:
|
||||
using ResultType = typename ConcreteAction::ResultType;
|
||||
using ResultColumnType = ColumnVector<ResultType>;
|
||||
using ResultColumnPtr = decltype(ResultColumnType::create());
|
||||
|
||||
using NullMaps = std::pair<const NullMap *, const NullMap *>;
|
||||
|
||||
struct ExecutionData
|
||||
{
|
||||
const IColumn& left;
|
||||
const IColumn& right;
|
||||
const ColumnArray::Offsets& offsets;
|
||||
ColumnPtr result_column;
|
||||
NullMaps maps;
|
||||
ResultColumnPtr result { ResultColumnType::create() };
|
||||
|
||||
inline void moveResult() { result_column = std::move(result); }
|
||||
};
|
||||
|
||||
static inline bool allowArguments(const DataTypePtr & inner_type, const DataTypePtr & arg)
|
||||
{
|
||||
auto inner_type_decayed = removeNullable(removeLowCardinality(inner_type));
|
||||
auto arg_decayed = removeNullable(removeLowCardinality(arg));
|
||||
|
||||
return ((isNativeNumber(inner_type_decayed) || isEnum(inner_type_decayed)) && isNativeNumber(arg_decayed))
|
||||
|| getLeastSupertype({inner_type_decayed, arg_decayed});
|
||||
}
|
||||
|
||||
/**
|
||||
* If one or both arguments passed to this function are nullable,
|
||||
* we create a new column that contains non-nullable arguments:
|
||||
@ -404,7 +491,7 @@ public:
|
||||
* (they are vectors of Fields, which may represent the NULL value),
|
||||
* they do not require any preprocessing.
|
||||
*/
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t /*input_rows_count*/) const override
|
||||
ColumnPtr executeArrayImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type) const
|
||||
{
|
||||
const ColumnPtr & ptr = arguments[0].column;
|
||||
|
||||
@ -419,11 +506,13 @@ public:
|
||||
if (col_array)
|
||||
nullable = checkAndGetColumn<ColumnNullable>(col_array->getData());
|
||||
|
||||
auto & arg_column = arguments[1].column;
|
||||
const auto & arg_column = arguments[1].column;
|
||||
const ColumnNullable * arg_nullable = checkAndGetColumn<ColumnNullable>(*arg_column);
|
||||
|
||||
if (!nullable && !arg_nullable)
|
||||
{
|
||||
return executeOnNonNullable(arguments, result_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
@ -483,34 +572,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
using ResultType = typename ConcreteAction::ResultType;
|
||||
using ResultColumnType = ColumnVector<ResultType>;
|
||||
using ResultColumnPtr = decltype(ResultColumnType::create());
|
||||
|
||||
using NullMaps = std::pair<const NullMap *, const NullMap *>;
|
||||
|
||||
struct ExecutionData
|
||||
{
|
||||
const IColumn& left;
|
||||
const IColumn& right;
|
||||
const ColumnArray::Offsets& offsets;
|
||||
ColumnPtr result_column;
|
||||
NullMaps maps;
|
||||
ResultColumnPtr result { ResultColumnType::create() };
|
||||
|
||||
inline void moveResult() { result_column = std::move(result); }
|
||||
};
|
||||
|
||||
static inline bool allowArguments(const DataTypePtr & array_inner_type, const DataTypePtr & arg)
|
||||
{
|
||||
auto inner_type_decayed = removeNullable(removeLowCardinality(array_inner_type));
|
||||
auto arg_decayed = removeNullable(removeLowCardinality(arg));
|
||||
|
||||
return ((isNativeNumber(inner_type_decayed) || isEnum(inner_type_decayed)) && isNativeNumber(arg_decayed))
|
||||
|| getLeastSupertype({inner_type_decayed, arg_decayed});
|
||||
}
|
||||
|
||||
#define INTEGRAL_TPL_PACK UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Float32, Float64
|
||||
|
||||
ColumnPtr executeOnNonNullable(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type) const
|
||||
|
105
src/Functions/h3GetFaces.cpp
Normal file
105
src/Functions/h3GetFaces.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
# include "config_functions.h"
|
||||
#endif
|
||||
|
||||
#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 <common/range.h>
|
||||
|
||||
#include <h3api.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class FunctionH3GetFaces : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "h3GetFaces";
|
||||
|
||||
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionH3GetFaces>(); }
|
||||
|
||||
std::string getName() const override { return name; }
|
||||
|
||||
size_t getNumberOfArguments() const override { return 1; }
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||
{
|
||||
const auto * arg = arguments[0].get();
|
||||
if (!WhichDataType(arg).isUInt64())
|
||||
throw Exception(
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of argument {} of function {}. Must be UInt64",
|
||||
arg->getName(), 1, getName());
|
||||
|
||||
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeUInt8>());
|
||||
}
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
||||
{
|
||||
const auto * column = checkAndGetColumn<ColumnUInt64>(arguments[0].column.get());
|
||||
const auto & data = column->getData();
|
||||
|
||||
auto result_column_data = ColumnUInt8::create();
|
||||
auto & result_data = result_column_data->getData();
|
||||
|
||||
auto result_column_offsets = ColumnArray::ColumnOffsets::create();
|
||||
auto & result_offsets = result_column_offsets->getData();
|
||||
result_offsets.resize(input_rows_count);
|
||||
|
||||
auto current_offset = 0;
|
||||
std::vector<int> faces;
|
||||
|
||||
for (size_t row = 0; row < input_rows_count; row++)
|
||||
{
|
||||
int max_faces = maxFaceCount(data[row]);
|
||||
|
||||
faces.resize(max_faces);
|
||||
|
||||
// function name h3GetFaces (v3.x) changed to getIcosahedronFaces (v4.0.0).
|
||||
getIcosahedronFaces(data[row], faces.data());
|
||||
|
||||
for (int i = 0; i < max_faces; i++)
|
||||
{
|
||||
// valid icosahedron faces are represented by integers 0-19
|
||||
if (faces[i] >= 0 && faces[i] <= 19)
|
||||
{
|
||||
++current_offset;
|
||||
result_data.emplace_back(faces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
result_offsets[row] = current_offset;
|
||||
faces.clear();
|
||||
}
|
||||
|
||||
return ColumnArray::create(std::move(result_column_data), std::move(result_column_offsets));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void registerFunctionH3GetFaces(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionH3GetFaces>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
79
src/Functions/h3IsPentagon.cpp
Normal file
79
src/Functions/h3IsPentagon.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
# include "config_functions.h"
|
||||
#endif
|
||||
|
||||
#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 <common/range.h>
|
||||
|
||||
#include <h3api.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class FunctionH3IsPentagon : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "h3IsPentagon";
|
||||
|
||||
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionH3IsPentagon>(); }
|
||||
|
||||
std::string getName() const override { return name; }
|
||||
|
||||
size_t getNumberOfArguments() const override { return 1; }
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||
{
|
||||
const auto * arg = arguments[0].get();
|
||||
if (!WhichDataType(arg).isUInt64())
|
||||
throw Exception(
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of argument {} of function {}. Must be UInt64",
|
||||
arg->getName(), 1, getName());
|
||||
|
||||
return std::make_shared<DataTypeUInt8>();
|
||||
}
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
||||
{
|
||||
const auto * column = checkAndGetColumn<ColumnUInt64>(arguments[0].column.get());
|
||||
const auto & data = column->getData();
|
||||
|
||||
auto dst = ColumnVector<UInt8>::create();
|
||||
auto & dst_data = dst->getData();
|
||||
dst_data.resize(input_rows_count);
|
||||
|
||||
for (size_t row = 0 ; row < input_rows_count ; row++)
|
||||
{
|
||||
UInt8 res = isPentagon(data[row]);
|
||||
dst_data[row] = res;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void registerFunctionH3IsPentagon(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionH3IsPentagon>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
79
src/Functions/h3ResIsClassIII.cpp
Normal file
79
src/Functions/h3ResIsClassIII.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
# include "config_functions.h"
|
||||
#endif
|
||||
|
||||
#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 <common/range.h>
|
||||
|
||||
#include <h3api.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class FunctionH3ResIsClassIII : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "h3ResIsClassIII";
|
||||
|
||||
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionH3ResIsClassIII>(); }
|
||||
|
||||
std::string getName() const override { return name; }
|
||||
|
||||
size_t getNumberOfArguments() const override { return 1; }
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||
{
|
||||
const auto * arg = arguments[0].get();
|
||||
if (!WhichDataType(arg).isUInt64())
|
||||
throw Exception(
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of argument {} of function {}. Must be UInt64",
|
||||
arg->getName(), 1, getName());
|
||||
|
||||
return std::make_shared<DataTypeUInt8>();
|
||||
}
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
||||
{
|
||||
const auto * column = checkAndGetColumn<ColumnUInt64>(arguments[0].column.get());
|
||||
const auto & data = column->getData();
|
||||
|
||||
auto dst = ColumnVector<UInt8>::create();
|
||||
auto & dst_data = dst->getData();
|
||||
dst_data.resize(input_rows_count);
|
||||
|
||||
for (size_t row = 0 ; row < input_rows_count ; row++)
|
||||
{
|
||||
UInt8 res = isResClassIII(data[row]);
|
||||
dst_data[row] = res;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void registerFunctionH3ResIsClassIII(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionH3ResIsClassIII>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -155,61 +155,25 @@ public:
|
||||
return NameMapContains::name;
|
||||
}
|
||||
|
||||
size_t getNumberOfArguments() const override { return 2; }
|
||||
size_t getNumberOfArguments() const override { return impl.getNumberOfArguments(); }
|
||||
|
||||
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
|
||||
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & arguments) const override
|
||||
{
|
||||
return impl.isSuitableForShortCircuitArgumentsExecution(arguments);
|
||||
}
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
if (arguments.size() != 2)
|
||||
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
||||
+ toString(arguments.size()) + ", should be 2",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
const DataTypeMap * map_type = checkAndGetDataType<DataTypeMap>(arguments[0].type.get());
|
||||
|
||||
if (!map_type)
|
||||
throw Exception{"First argument for function " + getName() + " must be a map",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
auto key_type = map_type->getKeyType();
|
||||
|
||||
if (!(isNumber(arguments[1].type) && isNumber(key_type))
|
||||
&& key_type->getName() != arguments[1].type->getName())
|
||||
throw Exception{"Second argument for function " + getName() + " must be a " + key_type->getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
return std::make_shared<DataTypeUInt8>();
|
||||
return impl.getReturnTypeImpl(arguments);
|
||||
}
|
||||
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override
|
||||
{
|
||||
bool is_const = isColumnConst(*arguments[0].column);
|
||||
const ColumnMap * col_map = is_const ? checkAndGetColumnConstData<ColumnMap>(arguments[0].column.get()) : checkAndGetColumn<ColumnMap>(arguments[0].column.get());
|
||||
const DataTypeMap * map_type = checkAndGetDataType<DataTypeMap>(arguments[0].type.get());
|
||||
if (!col_map || !map_type)
|
||||
throw Exception{"First argument for function " + getName() + " must be a map", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
auto key_type = map_type->getKeyType();
|
||||
const auto & nested_column = col_map->getNestedColumn();
|
||||
const auto & keys_data = col_map->getNestedData().getColumn(0);
|
||||
|
||||
/// Prepare arguments to call arrayIndex for check has the array element.
|
||||
ColumnPtr column_array = ColumnArray::create(keys_data.getPtr(), nested_column.getOffsetsPtr());
|
||||
ColumnsWithTypeAndName new_arguments =
|
||||
{
|
||||
{
|
||||
is_const ? ColumnConst::create(std::move(column_array), keys_data.size()) : std::move(column_array),
|
||||
std::make_shared<DataTypeArray>(key_type),
|
||||
""
|
||||
},
|
||||
arguments[1]
|
||||
};
|
||||
|
||||
return FunctionArrayIndex<HasAction, NameMapContains>().executeImpl(new_arguments, result_type, input_rows_count);
|
||||
return impl.executeImpl(arguments, result_type, input_rows_count);
|
||||
}
|
||||
|
||||
private:
|
||||
FunctionArrayIndex<HasAction, NameMapContains> impl;
|
||||
};
|
||||
|
||||
|
||||
|
@ -42,6 +42,9 @@ void registerFunctionH3IndexesAreNeighbors(FunctionFactory &);
|
||||
void registerFunctionStringToH3(FunctionFactory &);
|
||||
void registerFunctionH3ToString(FunctionFactory &);
|
||||
void registerFunctionH3HexAreaM2(FunctionFactory &);
|
||||
void registerFunctionH3ResIsClassIII(FunctionFactory &);
|
||||
void registerFunctionH3IsPentagon(FunctionFactory &);
|
||||
void registerFunctionH3GetFaces(FunctionFactory &);
|
||||
#endif
|
||||
|
||||
#if USE_S2_GEOMETRY
|
||||
@ -95,6 +98,9 @@ void registerFunctionsGeo(FunctionFactory & factory)
|
||||
registerFunctionStringToH3(factory);
|
||||
registerFunctionH3ToString(factory);
|
||||
registerFunctionH3HexAreaM2(factory);
|
||||
registerFunctionH3ResIsClassIII(factory);
|
||||
registerFunctionH3IsPentagon(factory);
|
||||
registerFunctionH3GetFaces(factory);
|
||||
#endif
|
||||
|
||||
#if USE_S2_GEOMETRY
|
||||
|
@ -299,11 +299,15 @@ SRCS(
|
||||
h3EdgeAngle.cpp
|
||||
h3EdgeLengthM.cpp
|
||||
h3GetBaseCell.cpp
|
||||
h3GetFaces.cpp
|
||||
h3GetResolution.cpp
|
||||
h3HexAreaM2.cpp
|
||||
h3IndexesAreNeighbors.cpp
|
||||
h3IsPentagon.cpp
|
||||
h3IsValid.cpp
|
||||
h3ResIsClassIII.cpp
|
||||
h3ToChildren.cpp
|
||||
h3ToGeoBoundary.cpp
|
||||
h3ToParent.cpp
|
||||
h3ToString.cpp
|
||||
h3kRing.cpp
|
||||
|
@ -54,7 +54,9 @@ bool ReadBufferFromPocoSocket::nextImpl()
|
||||
}
|
||||
catch (const Poco::TimeoutException &)
|
||||
{
|
||||
throw NetException("Timeout exceeded while reading from socket (" + peer_address.toString() + ")", ErrorCodes::SOCKET_TIMEOUT);
|
||||
throw NetException(fmt::format("Timeout exceeded while reading from socket ({}, {} ms)",
|
||||
peer_address.toString(),
|
||||
socket.impl()->getReceiveTimeout().totalMilliseconds()), ErrorCodes::SOCKET_TIMEOUT);
|
||||
}
|
||||
catch (const Poco::IOException & e)
|
||||
{
|
||||
|
@ -66,6 +66,9 @@ struct ReadSettings
|
||||
/// For 'pread_threadpool' method. Lower is more priority.
|
||||
size_t priority = 0;
|
||||
|
||||
size_t remote_fs_backoff_threshold = 10000;
|
||||
size_t remote_fs_backoff_max_tries = 4;
|
||||
|
||||
ReadSettings adjustBufferSize(size_t file_size) const
|
||||
{
|
||||
ReadSettings res = *this;
|
||||
|
@ -280,7 +280,7 @@ protected:
|
||||
|
||||
auto credentials_view = credentials_doc.View();
|
||||
access_key = credentials_view.GetString("AccessKeyId");
|
||||
LOG_ERROR(logger, "Successfully pulled credentials from EC2MetadataService with access key {}.", access_key);
|
||||
LOG_TRACE(logger, "Successfully pulled credentials from EC2MetadataService with access key {}.", access_key);
|
||||
|
||||
secret_key = credentials_view.GetString("SecretAccessKey");
|
||||
token = credentials_view.GetString("Token");
|
||||
|
@ -57,7 +57,9 @@ void WriteBufferFromPocoSocket::nextImpl()
|
||||
}
|
||||
catch (const Poco::TimeoutException &)
|
||||
{
|
||||
throw NetException("Timeout exceeded while writing to socket (" + peer_address.toString() + ")", ErrorCodes::SOCKET_TIMEOUT);
|
||||
throw NetException(fmt::format("Timeout exceeded while writing to socket ({}, {} ms)",
|
||||
peer_address.toString(),
|
||||
socket.impl()->getSendTimeout().totalMilliseconds()), ErrorCodes::SOCKET_TIMEOUT);
|
||||
}
|
||||
catch (const Poco::IOException & e)
|
||||
{
|
||||
|
164
src/IO/ZstdDeflatingAppendableWriteBuffer.cpp
Normal file
164
src/IO/ZstdDeflatingAppendableWriteBuffer.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
#include <IO/ZstdDeflatingAppendableWriteBuffer.h>
|
||||
#include <Common/MemoryTracker.h>
|
||||
#include <Common/Exception.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ZSTD_ENCODER_FAILED;
|
||||
}
|
||||
|
||||
ZstdDeflatingAppendableWriteBuffer::ZstdDeflatingAppendableWriteBuffer(
|
||||
WriteBuffer & out_, int compression_level, bool append_to_existing_stream_,
|
||||
size_t buf_size, char * existing_memory, size_t alignment)
|
||||
: BufferWithOwnMemory<WriteBuffer>(buf_size, existing_memory, alignment)
|
||||
, out(out_)
|
||||
, append_to_existing_stream(append_to_existing_stream_)
|
||||
{
|
||||
cctx = ZSTD_createCCtx();
|
||||
if (cctx == nullptr)
|
||||
throw Exception(ErrorCodes::ZSTD_ENCODER_FAILED, "zstd stream encoder init failed: zstd version: {}", ZSTD_VERSION_STRING);
|
||||
size_t ret = ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, compression_level);
|
||||
if (ZSTD_isError(ret))
|
||||
throw Exception(ErrorCodes::ZSTD_ENCODER_FAILED, "zstd stream encoder option setting failed: error code: {}; zstd version: {}", ret, ZSTD_VERSION_STRING);
|
||||
|
||||
input = {nullptr, 0, 0};
|
||||
output = {nullptr, 0, 0};
|
||||
}
|
||||
|
||||
void ZstdDeflatingAppendableWriteBuffer::nextImpl()
|
||||
{
|
||||
if (!offset())
|
||||
return;
|
||||
|
||||
ZSTD_EndDirective mode = ZSTD_e_flush;
|
||||
|
||||
input.src = reinterpret_cast<unsigned char *>(working_buffer.begin());
|
||||
input.size = offset();
|
||||
input.pos = 0;
|
||||
|
||||
if (first_write && append_to_existing_stream)
|
||||
{
|
||||
addEmptyBlock();
|
||||
first_write = false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool ended = false;
|
||||
do
|
||||
{
|
||||
out.nextIfAtEnd();
|
||||
|
||||
output.dst = reinterpret_cast<unsigned char *>(out.buffer().begin());
|
||||
output.size = out.buffer().size();
|
||||
output.pos = out.offset();
|
||||
|
||||
size_t compression_result = ZSTD_compressStream2(cctx, &output, &input, mode);
|
||||
if (ZSTD_isError(compression_result))
|
||||
throw Exception(
|
||||
ErrorCodes::ZSTD_ENCODER_FAILED, "Zstd stream encoding failed: error code: {}; zstd version: {}", ZSTD_getErrorName(compression_result), ZSTD_VERSION_STRING);
|
||||
|
||||
out.position() = out.buffer().begin() + output.pos;
|
||||
|
||||
bool everything_was_compressed = (input.pos == input.size);
|
||||
bool everything_was_flushed = compression_result == 0;
|
||||
|
||||
ended = everything_was_compressed && everything_was_flushed;
|
||||
} while (!ended);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
/// Do not try to write next time after exception.
|
||||
out.position() = out.buffer().begin();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void ZstdDeflatingAppendableWriteBuffer::finish()
|
||||
{
|
||||
if (finished || first_write)
|
||||
{
|
||||
/// Nothing was written or we have already finished
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
finishImpl();
|
||||
out.finalize();
|
||||
finished = true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
/// Do not try to flush next time after exception.
|
||||
out.position() = out.buffer().begin();
|
||||
finished = true;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ZstdDeflatingAppendableWriteBuffer::~ZstdDeflatingAppendableWriteBuffer()
|
||||
{
|
||||
/// FIXME move final flush into the caller
|
||||
MemoryTracker::LockExceptionInThread lock(VariableContext::Global);
|
||||
|
||||
finish();
|
||||
|
||||
try
|
||||
{
|
||||
int err = ZSTD_freeCCtx(cctx);
|
||||
/// This is just in case, since it is impossible to get an error by using this wrapper.
|
||||
if (unlikely(err))
|
||||
throw Exception(ErrorCodes::ZSTD_ENCODER_FAILED, "ZSTD_freeCCtx failed: error: '{}'; zstd version: {}", ZSTD_getErrorName(err), ZSTD_VERSION_STRING);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
/// It is OK not to terminate under an error from ZSTD_freeCCtx()
|
||||
/// since all data already written to the stream.
|
||||
tryLogCurrentException(__PRETTY_FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
void ZstdDeflatingAppendableWriteBuffer::finishImpl()
|
||||
{
|
||||
next();
|
||||
|
||||
out.nextIfAtEnd();
|
||||
|
||||
input.src = reinterpret_cast<unsigned char *>(working_buffer.begin());
|
||||
input.size = offset();
|
||||
input.pos = 0;
|
||||
|
||||
output.dst = reinterpret_cast<unsigned char *>(out.buffer().begin());
|
||||
output.size = out.buffer().size();
|
||||
output.pos = out.offset();
|
||||
|
||||
size_t remaining = ZSTD_compressStream2(cctx, &output, &input, ZSTD_e_end);
|
||||
while (remaining != 0)
|
||||
{
|
||||
if (ZSTD_isError(remaining))
|
||||
throw Exception(ErrorCodes::ZSTD_ENCODER_FAILED, "zstd stream encoder end failed: error: '{}' zstd version: {}", ZSTD_getErrorName(remaining), ZSTD_VERSION_STRING);
|
||||
|
||||
remaining = ZSTD_compressStream2(cctx, &output, &input, ZSTD_e_end);
|
||||
}
|
||||
out.position() = out.buffer().begin() + output.pos;
|
||||
}
|
||||
|
||||
void ZstdDeflatingAppendableWriteBuffer::addEmptyBlock()
|
||||
{
|
||||
/// HACK: https://github.com/facebook/zstd/issues/2090#issuecomment-620158967
|
||||
static const char empty_block[3] = {0x01, 0x00, 0x00};
|
||||
|
||||
if (out.buffer().size() - out.offset() < sizeof(empty_block))
|
||||
out.next();
|
||||
|
||||
std::memcpy(out.buffer().begin() + out.offset(), empty_block, sizeof(empty_block));
|
||||
|
||||
out.position() = out.buffer().begin() + out.offset() + sizeof(empty_block);
|
||||
}
|
||||
|
||||
}
|
71
src/IO/ZstdDeflatingAppendableWriteBuffer.h
Normal file
71
src/IO/ZstdDeflatingAppendableWriteBuffer.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <IO/BufferWithOwnMemory.h>
|
||||
#include <IO/CompressionMethod.h>
|
||||
#include <IO/WriteBuffer.h>
|
||||
|
||||
#include <zstd.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/// Performs stream compression using zstd library and writes compressed data to out_ WriteBuffer.
|
||||
/// Main differences from ZstdDeflatingWriteBuffer:
|
||||
/// 1) Allows to continue to write to the same output even if finalize() (or destructor) was not called, for example
|
||||
/// when server was killed with 9 signal. Natively zstd doesn't support such feature because
|
||||
/// ZSTD_decompressStream expect to see empty block at the end of each frame. There is not API function for it
|
||||
/// so we just use HACK and add empty block manually on the first write (see addEmptyBlock). Maintainers of zstd
|
||||
/// said that there is no risks of compatibility issues https://github.com/facebook/zstd/issues/2090#issuecomment-620158967.
|
||||
/// 2) Doesn't support internal ZSTD check-summing, because ZSTD checksums written at the end of frame (frame epilogue).
|
||||
///
|
||||
class ZstdDeflatingAppendableWriteBuffer : public BufferWithOwnMemory<WriteBuffer>
|
||||
{
|
||||
public:
|
||||
ZstdDeflatingAppendableWriteBuffer(
|
||||
WriteBuffer & out_,
|
||||
int compression_level,
|
||||
bool append_to_existing_stream_, /// if true then out mustn't be empty
|
||||
size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE,
|
||||
char * existing_memory = nullptr,
|
||||
size_t alignment = 0);
|
||||
|
||||
/// Write terminating ZSTD_e_end: empty block + frame epilogue. BTW it
|
||||
/// should be almost noop, because frame epilogue contains only checksums,
|
||||
/// and they are disabled for this buffer.
|
||||
void finalize() override { finish(); }
|
||||
|
||||
~ZstdDeflatingAppendableWriteBuffer() override;
|
||||
|
||||
void sync() override
|
||||
{
|
||||
next();
|
||||
out.sync();
|
||||
}
|
||||
|
||||
private:
|
||||
/// NOTE: will fill compressed data to the out.working_buffer, but will not call out.next method until the buffer is full
|
||||
void nextImpl() override;
|
||||
|
||||
/// Flush all pending data and write zstd footer to the underlying buffer.
|
||||
/// After the first call to this function, subsequent calls will have no effect and
|
||||
/// an attempt to write to this buffer will result in exception.
|
||||
void finish();
|
||||
void finishImpl();
|
||||
/// Adding zstd empty block to out.working_buffer
|
||||
void addEmptyBlock();
|
||||
|
||||
WriteBuffer & out;
|
||||
/// We appending data to existing stream so on the first nextImpl call we
|
||||
/// will append empty block.
|
||||
bool append_to_existing_stream;
|
||||
ZSTD_CCtx * cctx;
|
||||
ZSTD_inBuffer input;
|
||||
ZSTD_outBuffer output;
|
||||
/// Flipped in finish call
|
||||
bool finished = false;
|
||||
/// Flipped on the first nextImpl call
|
||||
bool first_write = true;
|
||||
};
|
||||
|
||||
}
|
@ -40,7 +40,7 @@ ZstdDeflatingWriteBuffer::~ZstdDeflatingWriteBuffer()
|
||||
int err = ZSTD_freeCCtx(cctx);
|
||||
/// This is just in case, since it is impossible to get an error by using this wrapper.
|
||||
if (unlikely(err))
|
||||
throw Exception(ErrorCodes::ZSTD_ENCODER_FAILED, "ZSTD_freeCCtx failed: error code: {}; zstd version: {}", err, ZSTD_VERSION_STRING);
|
||||
throw Exception(ErrorCodes::ZSTD_ENCODER_FAILED, "ZSTD_freeCCtx failed: error: '{}'; zstd version: {}", ZSTD_getErrorName(err), ZSTD_VERSION_STRING);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -73,9 +73,17 @@ void ZstdDeflatingWriteBuffer::nextImpl()
|
||||
output.pos = out->offset();
|
||||
|
||||
|
||||
ZSTD_compressStream2(cctx, &output, &input, mode);
|
||||
size_t compression_result = ZSTD_compressStream2(cctx, &output, &input, mode);
|
||||
if (ZSTD_isError(compression_result))
|
||||
throw Exception(
|
||||
ErrorCodes::ZSTD_ENCODER_FAILED, "Zstd stream encoding failed: error: '{}'; zstd version: {}", ZSTD_getErrorName(compression_result), ZSTD_VERSION_STRING);
|
||||
|
||||
out->position() = out->buffer().begin() + output.pos;
|
||||
ended = (input.pos == input.size);
|
||||
|
||||
bool everything_was_compressed = (input.pos == input.size);
|
||||
bool everything_was_flushed = compression_result == 0;
|
||||
|
||||
ended = everything_was_compressed && everything_was_flushed;
|
||||
} while (!ended);
|
||||
}
|
||||
catch (...)
|
||||
|
@ -24,6 +24,11 @@ public:
|
||||
|
||||
~ZstdDeflatingWriteBuffer() override;
|
||||
|
||||
void sync() override
|
||||
{
|
||||
out->sync();
|
||||
}
|
||||
|
||||
private:
|
||||
void nextImpl() override;
|
||||
|
||||
|
@ -52,10 +52,10 @@ bool ZstdInflatingReadBuffer::nextImpl()
|
||||
size_t ret = ZSTD_decompressStream(dctx, &output, &input);
|
||||
if (ZSTD_isError(ret))
|
||||
throw Exception(
|
||||
ErrorCodes::ZSTD_DECODER_FAILED, "Zstd stream decoding failed: error code: {}; zstd version: {}", ret, ZSTD_VERSION_STRING);
|
||||
ErrorCodes::ZSTD_DECODER_FAILED, "Zstd stream encoding failed: error '{}'; zstd version: {}", ZSTD_getErrorName(ret), ZSTD_VERSION_STRING);
|
||||
|
||||
/// Check that something has changed after decompress (input or output position)
|
||||
assert(output.pos > 0 || in->position() < in->buffer().begin() + input.pos);
|
||||
assert(in->eof() || output.pos > 0 || in->position() < in->buffer().begin() + input.pos);
|
||||
|
||||
/// move position to the end of read data
|
||||
in->position() = in->buffer().begin() + input.pos;
|
||||
|
@ -2844,6 +2844,9 @@ ReadSettings Context::getReadSettings() const
|
||||
res.local_fs_prefetch = settings.local_filesystem_read_prefetch;
|
||||
res.remote_fs_prefetch = settings.remote_filesystem_read_prefetch;
|
||||
|
||||
res.remote_fs_backoff_threshold = settings.remote_fs_read_backoff_threshold;
|
||||
res.remote_fs_backoff_max_tries = settings.remote_fs_read_backoff_max_tries;
|
||||
|
||||
res.local_fs_buffer_size = settings.max_read_buffer_size;
|
||||
res.direct_io_threshold = settings.min_bytes_to_use_direct_io;
|
||||
res.mmap_threshold = settings.min_bytes_to_use_mmap_io;
|
||||
|
@ -772,7 +772,9 @@ bool DDLWorker::tryExecuteQueryOnLeaderReplica(
|
||||
String shard_path = task.getShardNodePath();
|
||||
String is_executed_path = fs::path(shard_path) / "executed";
|
||||
String tries_to_execute_path = fs::path(shard_path) / "tries_to_execute";
|
||||
zookeeper->createAncestors(fs::path(shard_path) / ""); /* appends "/" at the end of shard_path */
|
||||
assert(shard_path.starts_with(String(fs::path(task.entry_path) / "shards" / "")));
|
||||
zookeeper->createIfNotExists(fs::path(task.entry_path) / "shards", "");
|
||||
zookeeper->createIfNotExists(shard_path, "");
|
||||
|
||||
/// Leader replica creates is_executed_path node on successful query execution.
|
||||
/// We will remove create_shard_flag from zk operations list, if current replica is just waiting for leader to execute the query.
|
||||
|
@ -14,18 +14,19 @@ namespace ErrorCodes
|
||||
/// Mapping from quantile functions for single value to plural
|
||||
static const std::unordered_map<String, String> quantile_fuse_name_mapping = {
|
||||
{NameQuantile::name, NameQuantiles::name},
|
||||
{NameQuantileBFloat16::name, NameQuantilesBFloat16::name},
|
||||
{NameQuantileBFloat16Weighted::name, NameQuantilesBFloat16Weighted::name},
|
||||
{NameQuantileDeterministic::name, NameQuantilesDeterministic::name},
|
||||
{NameQuantileExact::name, NameQuantilesExact::name},
|
||||
{NameQuantileExactLow::name, NameQuantilesExactLow::name},
|
||||
{NameQuantileExactHigh::name, NameQuantilesExactHigh::name},
|
||||
{NameQuantileExactExclusive::name, NameQuantilesExactExclusive::name},
|
||||
{NameQuantileExactHigh::name, NameQuantilesExactHigh::name},
|
||||
{NameQuantileExactInclusive::name, NameQuantilesExactInclusive::name},
|
||||
{NameQuantileExactLow::name, NameQuantilesExactLow::name},
|
||||
{NameQuantileExactWeighted::name, NameQuantilesExactWeighted::name},
|
||||
{NameQuantileTiming::name, NameQuantilesTiming::name},
|
||||
{NameQuantileTimingWeighted::name, NameQuantilesTimingWeighted::name},
|
||||
{NameQuantileTDigest::name, NameQuantilesTDigest::name},
|
||||
{NameQuantileTDigestWeighted::name, NameQuantilesTDigestWeighted::name},
|
||||
{NameQuantileBFloat16::name, NameQuantilesBFloat16::name}
|
||||
{NameQuantileTiming::name, NameQuantilesTiming::name},
|
||||
{NameQuantileTimingWeighted::name, NameQuantilesTimingWeighted::name},
|
||||
};
|
||||
|
||||
String GatherFunctionQuantileData::getFusedName(const String & func_name)
|
||||
@ -54,7 +55,8 @@ void GatherFunctionQuantileData::FuseQuantileAggregatesData::addFuncNode(ASTPtr
|
||||
bool need_two_args = func->name == NameQuantileDeterministic::name
|
||||
|| func->name == NameQuantileExactWeighted::name
|
||||
|| func->name == NameQuantileTimingWeighted::name
|
||||
|| func->name == NameQuantileTDigestWeighted::name;
|
||||
|| func->name == NameQuantileTDigestWeighted::name
|
||||
|| func->name == NameQuantileBFloat16Weighted::name;
|
||||
if (arguments.size() != (need_two_args ? 2 : 1))
|
||||
return;
|
||||
|
||||
|
@ -63,7 +63,6 @@ public:
|
||||
|
||||
std::shared_ptr<const IExternalLoadable> clone() const override
|
||||
{
|
||||
std::cerr << "UserDefinedExecutableFunction::clone " << this << std::endl;
|
||||
return std::make_shared<UserDefinedExecutableFunction>(configuration, lifetime, process_pool);
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,41 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
|
||||
DataTypePtr getCoordinationErrorCodesEnumType()
|
||||
{
|
||||
return std::make_shared<DataTypeEnum8>(
|
||||
DataTypeEnum8::Values
|
||||
{
|
||||
{"ZOK", static_cast<Int8>(Coordination::Error::ZOK)},
|
||||
|
||||
{"ZSYSTEMERROR", static_cast<Int8>(Coordination::Error::ZSYSTEMERROR)},
|
||||
{"ZRUNTIMEINCONSISTENCY", static_cast<Int8>(Coordination::Error::ZRUNTIMEINCONSISTENCY)},
|
||||
{"ZDATAINCONSISTENCY", static_cast<Int8>(Coordination::Error::ZDATAINCONSISTENCY)},
|
||||
{"ZCONNECTIONLOSS", static_cast<Int8>(Coordination::Error::ZCONNECTIONLOSS)},
|
||||
{"ZMARSHALLINGERROR", static_cast<Int8>(Coordination::Error::ZMARSHALLINGERROR)},
|
||||
{"ZUNIMPLEMENTED", static_cast<Int8>(Coordination::Error::ZUNIMPLEMENTED)},
|
||||
{"ZOPERATIONTIMEOUT", static_cast<Int8>(Coordination::Error::ZOPERATIONTIMEOUT)},
|
||||
{"ZBADARGUMENTS", static_cast<Int8>(Coordination::Error::ZBADARGUMENTS)},
|
||||
{"ZINVALIDSTATE", static_cast<Int8>(Coordination::Error::ZINVALIDSTATE)},
|
||||
|
||||
{"ZAPIERROR", static_cast<Int8>(Coordination::Error::ZAPIERROR)},
|
||||
{"ZNONODE", static_cast<Int8>(Coordination::Error::ZNONODE)},
|
||||
{"ZNOAUTH", static_cast<Int8>(Coordination::Error::ZNOAUTH)},
|
||||
{"ZBADVERSION", static_cast<Int8>(Coordination::Error::ZBADVERSION)},
|
||||
{"ZNOCHILDRENFOREPHEMERALS", static_cast<Int8>(Coordination::Error::ZNOCHILDRENFOREPHEMERALS)},
|
||||
{"ZNODEEXISTS", static_cast<Int8>(Coordination::Error::ZNODEEXISTS)},
|
||||
{"ZNOTEMPTY", static_cast<Int8>(Coordination::Error::ZNOTEMPTY)},
|
||||
{"ZSESSIONEXPIRED", static_cast<Int8>(Coordination::Error::ZSESSIONEXPIRED)},
|
||||
{"ZINVALIDCALLBACK", static_cast<Int8>(Coordination::Error::ZINVALIDCALLBACK)},
|
||||
{"ZINVALIDACL", static_cast<Int8>(Coordination::Error::ZINVALIDACL)},
|
||||
{"ZAUTHFAILED", static_cast<Int8>(Coordination::Error::ZAUTHFAILED)},
|
||||
{"ZCLOSING", static_cast<Int8>(Coordination::Error::ZCLOSING)},
|
||||
{"ZNOTHING", static_cast<Int8>(Coordination::Error::ZNOTHING)},
|
||||
{"ZSESSIONMOVED", static_cast<Int8>(Coordination::Error::ZSESSIONMOVED)},
|
||||
});
|
||||
}
|
||||
|
||||
NamesAndTypesList ZooKeeperLogElement::getNamesAndTypes()
|
||||
{
|
||||
auto type_enum = std::make_shared<DataTypeEnum8>(
|
||||
@ -52,36 +87,7 @@ NamesAndTypesList ZooKeeperLogElement::getNamesAndTypes()
|
||||
{"SessionID", static_cast<Int16>(Coordination::OpNum::SessionID)},
|
||||
});
|
||||
|
||||
auto error_enum = std::make_shared<DataTypeEnum8>(
|
||||
DataTypeEnum8::Values
|
||||
{
|
||||
{"ZOK", static_cast<Int8>(Coordination::Error::ZOK)},
|
||||
|
||||
{"ZSYSTEMERROR", static_cast<Int8>(Coordination::Error::ZSYSTEMERROR)},
|
||||
{"ZRUNTIMEINCONSISTENCY", static_cast<Int8>(Coordination::Error::ZRUNTIMEINCONSISTENCY)},
|
||||
{"ZDATAINCONSISTENCY", static_cast<Int8>(Coordination::Error::ZDATAINCONSISTENCY)},
|
||||
{"ZCONNECTIONLOSS", static_cast<Int8>(Coordination::Error::ZCONNECTIONLOSS)},
|
||||
{"ZMARSHALLINGERROR", static_cast<Int8>(Coordination::Error::ZMARSHALLINGERROR)},
|
||||
{"ZUNIMPLEMENTED", static_cast<Int8>(Coordination::Error::ZUNIMPLEMENTED)},
|
||||
{"ZOPERATIONTIMEOUT", static_cast<Int8>(Coordination::Error::ZOPERATIONTIMEOUT)},
|
||||
{"ZBADARGUMENTS", static_cast<Int8>(Coordination::Error::ZBADARGUMENTS)},
|
||||
{"ZINVALIDSTATE", static_cast<Int8>(Coordination::Error::ZINVALIDSTATE)},
|
||||
|
||||
{"ZAPIERROR", static_cast<Int8>(Coordination::Error::ZAPIERROR)},
|
||||
{"ZNONODE", static_cast<Int8>(Coordination::Error::ZNONODE)},
|
||||
{"ZNOAUTH", static_cast<Int8>(Coordination::Error::ZNOAUTH)},
|
||||
{"ZBADVERSION", static_cast<Int8>(Coordination::Error::ZBADVERSION)},
|
||||
{"ZNOCHILDRENFOREPHEMERALS", static_cast<Int8>(Coordination::Error::ZNOCHILDRENFOREPHEMERALS)},
|
||||
{"ZNODEEXISTS", static_cast<Int8>(Coordination::Error::ZNODEEXISTS)},
|
||||
{"ZNOTEMPTY", static_cast<Int8>(Coordination::Error::ZNOTEMPTY)},
|
||||
{"ZSESSIONEXPIRED", static_cast<Int8>(Coordination::Error::ZSESSIONEXPIRED)},
|
||||
{"ZINVALIDCALLBACK", static_cast<Int8>(Coordination::Error::ZINVALIDCALLBACK)},
|
||||
{"ZINVALIDACL", static_cast<Int8>(Coordination::Error::ZINVALIDACL)},
|
||||
{"ZAUTHFAILED", static_cast<Int8>(Coordination::Error::ZAUTHFAILED)},
|
||||
{"ZCLOSING", static_cast<Int8>(Coordination::Error::ZCLOSING)},
|
||||
{"ZNOTHING", static_cast<Int8>(Coordination::Error::ZNOTHING)},
|
||||
{"ZSESSIONMOVED", static_cast<Int8>(Coordination::Error::ZSESSIONMOVED)},
|
||||
});
|
||||
auto error_enum = getCoordinationErrorCodesEnumType();
|
||||
|
||||
auto watch_type_enum = std::make_shared<DataTypeEnum8>(
|
||||
DataTypeEnum8::Values
|
||||
|
@ -73,4 +73,6 @@ class ZooKeeperLog : public SystemLog<ZooKeeperLogElement>
|
||||
using SystemLog<ZooKeeperLogElement>::SystemLog;
|
||||
};
|
||||
|
||||
DataTypePtr getCoordinationErrorCodesEnumType();
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ SRCS(
|
||||
ApplyWithSubqueryVisitor.cpp
|
||||
ArithmeticOperationsInAgrFuncOptimize.cpp
|
||||
ArrayJoinAction.cpp
|
||||
AsynchronousInsertQueue.cpp
|
||||
AsynchronousMetricLog.cpp
|
||||
AsynchronousMetrics.cpp
|
||||
BloomFilter.cpp
|
||||
|
@ -1524,6 +1524,25 @@ bool ParserNull::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parseNumber(char * buffer, size_t size, bool negative, int base, Field & res)
|
||||
{
|
||||
errno = 0; /// Functions strto* don't clear errno.
|
||||
|
||||
char * pos_integer = buffer;
|
||||
UInt64 uint_value = std::strtoull(buffer, &pos_integer, base);
|
||||
|
||||
if (pos_integer == buffer + size && errno != ERANGE && (!negative || uint_value <= (1ULL << 63)))
|
||||
{
|
||||
if (negative)
|
||||
res = static_cast<Int64>(-uint_value);
|
||||
else
|
||||
res = uint_value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParserNumber::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
@ -1564,6 +1583,22 @@ bool ParserNumber::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
Float64 float_value = std::strtod(buf, &pos_double);
|
||||
if (pos_double != buf + pos->size() || errno == ERANGE)
|
||||
{
|
||||
/// Try to parse number as binary literal representation. Example: 0b0001.
|
||||
if (pos->size() > 2 && buf[0] == '0' && buf[1] == 'b')
|
||||
{
|
||||
char * buf_skip_prefix = buf + 2;
|
||||
|
||||
if (parseNumber(buf_skip_prefix, pos->size() - 2, negative, 2, res))
|
||||
{
|
||||
auto literal = std::make_shared<ASTLiteral>(res);
|
||||
literal->begin = literal_begin;
|
||||
literal->end = ++pos;
|
||||
node = literal;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
expected.add(pos, "number");
|
||||
return false;
|
||||
}
|
||||
@ -1578,22 +1613,13 @@ bool ParserNumber::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
|
||||
/// try to use more exact type: UInt64
|
||||
|
||||
char * pos_integer = buf;
|
||||
|
||||
errno = 0;
|
||||
UInt64 uint_value = std::strtoull(buf, &pos_integer, 0);
|
||||
if (pos_integer == pos_double && errno != ERANGE && (!negative || uint_value <= (1ULL << 63)))
|
||||
{
|
||||
if (negative)
|
||||
res = static_cast<Int64>(-uint_value);
|
||||
else
|
||||
res = uint_value;
|
||||
}
|
||||
parseNumber(buf, pos->size(), negative, 0, res);
|
||||
|
||||
auto literal = std::make_shared<ASTLiteral>(res);
|
||||
literal->begin = literal_begin;
|
||||
literal->end = ++pos;
|
||||
node = literal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user