mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 16:42:05 +00:00
Merge branch 'master' into remove-leader-election-2
This commit is contained in:
commit
125b007360
@ -1,4 +1,4 @@
|
||||
## function-name {#function-name-in-lower-case}
|
||||
## functionName {#functionname-in-lower-case}
|
||||
|
||||
Short description.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
## setting-name {#setting-name-in-lower-case}
|
||||
## setting_name {#setting_name}
|
||||
|
||||
Description.
|
||||
|
||||
|
@ -12,6 +12,7 @@ toc_title: Integrations
|
||||
|
||||
- Relational database management systems
|
||||
- [MySQL](https://www.mysql.com)
|
||||
- [mysql2ch](https://github.com/long2ice/mysql2ch)
|
||||
- [ProxySQL](https://github.com/sysown/proxysql/wiki/ClickHouse-Support)
|
||||
- [clickhouse-mysql-data-reader](https://github.com/Altinity/clickhouse-mysql-data-reader)
|
||||
- [horgh-replicator](https://github.com/larsnovikov/horgh-replicator)
|
||||
|
@ -12,6 +12,8 @@ The following aggregate functions are supported:
|
||||
- [`groupBitAnd`](../../sql-reference/aggregate-functions/reference.md#groupbitand)
|
||||
- [`groupBitOr`](../../sql-reference/aggregate-functions/reference.md#groupbitor)
|
||||
- [`groupBitXor`](../../sql-reference/aggregate-functions/reference.md#groupbitxor)
|
||||
- [`groupArrayArray`](../../sql-reference/aggregate-functions/reference.md#agg_function-grouparray)
|
||||
- [`groupUniqArrayArray`](../../sql-reference/aggregate-functions/reference.md#groupuniqarrayx-groupuniqarraymax-sizex)
|
||||
|
||||
Values of the `SimpleAggregateFunction(func, Type)` look and stored the same way as `Type`, so you do not need to apply functions with `-Merge`/`-State` suffixes. `SimpleAggregateFunction` has better performance than `AggregateFunction` with same aggregation function.
|
||||
|
||||
|
@ -1200,4 +1200,52 @@ SELECT number, randomPrintableASCII(30) as str, length(str) FROM system.numbers
|
||||
└────────┴────────────────────────────────┴──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## randomString {#randomstring}
|
||||
|
||||
Generates a binary string of the specified length filled with random bytes (including zero bytes).
|
||||
|
||||
**Syntax**
|
||||
|
||||
``` sql
|
||||
randomString(length)
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
- `length` — String length. Positive integer.
|
||||
|
||||
**Returned value**
|
||||
|
||||
- String filled with random bytes.
|
||||
|
||||
Type: [String](../../sql-reference/data-types/string.md).
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
``` sql
|
||||
SELECT randomString(30) AS str, length(str) AS len FROM numbers(2) FORMAT Vertical;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
``` text
|
||||
Row 1:
|
||||
──────
|
||||
str: 3 G : pT ?w тi k aV f6
|
||||
len: 30
|
||||
|
||||
Row 2:
|
||||
──────
|
||||
str: 9 ,] ^ ) ]?? 8
|
||||
len: 30
|
||||
```
|
||||
|
||||
**See Also**
|
||||
|
||||
- [generateRandom](../../sql-reference/table-functions/generate.md#generaterandom)
|
||||
- [randomPrintableASCII](../../sql-reference/functions/other-functions.md#randomascii)
|
||||
|
||||
|
||||
[Original article](https://clickhouse.tech/docs/en/query_language/functions/other_functions/) <!--hide-->
|
||||
|
@ -51,7 +51,11 @@ Modifies how matching by "join keys" is performed
|
||||
|
||||
`ASOF JOIN` is useful when you need to join records that have no exact match.
|
||||
|
||||
Tables for `ASOF JOIN` must have an ordered sequence column. This column cannot be alone in a table, and should be one of the data types: `UInt32`, `UInt64`, `Float32`, `Float64`, `Date`, and `DateTime`.
|
||||
Algorithm requires the special column in tables. This column:
|
||||
|
||||
- Must contain an ordered sequence.
|
||||
- Can be one of the following types: [Int*, UInt*](../../data-types/int-uint.md), [Float*](../../data-types/float.md), [Date](../../data-types/date.md), [DateTime](../../data-types/datetime.md), [Decimal*](../../data-types/decimal.md).
|
||||
- Can't be the only column in the `JOIN` clause.
|
||||
|
||||
Syntax `ASOF JOIN ... ON`:
|
||||
|
||||
|
@ -12,6 +12,7 @@ toc_title: Integrations
|
||||
|
||||
- Relational database management systems
|
||||
- [MySQL](https://www.mysql.com)
|
||||
- [mysql2ch](https://github.com/long2ice/mysql2ch)
|
||||
- [ProxySQL](https://github.com/sysown/proxysql/wiki/ClickHouse-Support)
|
||||
- [clickhouse-mysql-data-reader](https://github.com/Altinity/clickhouse-mysql-data-reader)
|
||||
- [horgh-replicator](https://github.com/larsnovikov/horgh-replicator)
|
||||
|
@ -14,6 +14,7 @@ toc_title: "\u06CC\u06A9\u067E\u0627\u0631\u0686\u06AF\u06CC"
|
||||
|
||||
- سیستم های مدیریت پایگاه داده رابطه ای
|
||||
- [MySQL](https://www.mysql.com)
|
||||
- [mysql2ch](https://github.com/long2ice/mysql2ch)
|
||||
- [در حال بارگذاری](https://github.com/sysown/proxysql/wiki/ClickHouse-Support)
|
||||
- [تاتر-خروجی زیر-داده خوان](https://github.com/Altinity/clickhouse-mysql-data-reader)
|
||||
- [horgh-replicator](https://github.com/larsnovikov/horgh-replicator)
|
||||
|
@ -14,6 +14,7 @@ toc_title: "Int\xE9gration"
|
||||
|
||||
- Systèmes de gestion de bases de données relationnelles
|
||||
- [MySQL](https://www.mysql.com)
|
||||
- [mysql2ch](https://github.com/long2ice/mysql2ch)
|
||||
- [ProxySQL](https://github.com/sysown/proxysql/wiki/ClickHouse-Support)
|
||||
- [clickhouse-mysql-lecteur de données](https://github.com/Altinity/clickhouse-mysql-data-reader)
|
||||
- [horgh-réplicateur](https://github.com/larsnovikov/horgh-replicator)
|
||||
|
@ -14,6 +14,7 @@ toc_title: "\u7D71\u5408"
|
||||
|
||||
- リレーショナルデータベース管理システム
|
||||
- [MySQL](https://www.mysql.com)
|
||||
- [mysql2ch](https://github.com/long2ice/mysql2ch)
|
||||
- [ProxySQL](https://github.com/sysown/proxysql/wiki/ClickHouse-Support)
|
||||
- [clickhouse-mysql-データリーダー](https://github.com/Altinity/clickhouse-mysql-data-reader)
|
||||
- [horgh-レプリケーター](https://github.com/larsnovikov/horgh-replicator)
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
- Реляционные системы управления базами данных
|
||||
- [MySQL](https://www.mysql.com)
|
||||
- [mysql2ch](https://github.com/long2ice/mysql2ch)
|
||||
- [ProxySQL](https://github.com/sysown/proxysql/wiki/ClickHouse-Support)
|
||||
- [clickhouse-mysql-data-reader](https://github.com/Altinity/clickhouse-mysql-data-reader)
|
||||
- [horgh-replicator](https://github.com/larsnovikov/horgh-replicator)
|
||||
|
@ -78,7 +78,7 @@ ClickHouse проверит условия `min_part_size` и `min_part_size_rat
|
||||
<default_profile>default</default_profile>
|
||||
```
|
||||
|
||||
## dictionaries\_config {#dictionaries-config}
|
||||
## dictionaries\_config {#server_configuration_parameters-dictionaries_config}
|
||||
|
||||
Путь к конфигурации внешних словарей.
|
||||
|
||||
@ -95,7 +95,7 @@ ClickHouse проверит условия `min_part_size` и `min_part_size_rat
|
||||
<dictionaries_config>*_dictionary.xml</dictionaries_config>
|
||||
```
|
||||
|
||||
## dictionaries\_lazy\_load {#dictionaries-lazy-load}
|
||||
## dictionaries\_lazy\_load {#server_configuration_parameters-dictionaries_lazy_load}
|
||||
|
||||
Отложенная загрузка словарей.
|
||||
|
||||
|
@ -1153,4 +1153,52 @@ SELECT number, randomPrintableASCII(30) as str, length(str) FROM system.numbers
|
||||
└────────┴────────────────────────────────┴──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## randomString {#randomstring}
|
||||
|
||||
Генерирует бинарную строку заданной длины, заполненную случайными байтами (в том числе нулевыми).
|
||||
|
||||
**Синтаксис**
|
||||
|
||||
``` sql
|
||||
randomString(length)
|
||||
```
|
||||
|
||||
**Параметры**
|
||||
|
||||
- `length` — длина строки. Положительное целое число.
|
||||
|
||||
**Возвращаемое значение**
|
||||
|
||||
- Строка, заполненная случайными байтами.
|
||||
|
||||
Type: [String](../../sql-reference/data-types/string.md).
|
||||
|
||||
**Пример**
|
||||
|
||||
Запрос:
|
||||
|
||||
``` sql
|
||||
SELECT randomString(30) AS str, length(str) AS len FROM numbers(2) FORMAT Vertical;
|
||||
```
|
||||
|
||||
Ответ:
|
||||
|
||||
``` text
|
||||
Row 1:
|
||||
──────
|
||||
str: 3 G : pT ?w тi k aV f6
|
||||
len: 30
|
||||
|
||||
Row 2:
|
||||
──────
|
||||
str: 9 ,] ^ ) ]?? 8
|
||||
len: 30
|
||||
```
|
||||
|
||||
**Смотрите также**
|
||||
|
||||
- [generateRandom](../../sql-reference/table-functions/generate.md#generaterandom)
|
||||
- [randomPrintableASCII](../../sql-reference/functions/other-functions.md#randomascii)
|
||||
|
||||
|
||||
[Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/functions/other_functions/) <!--hide-->
|
||||
|
@ -45,7 +45,11 @@ FROM <left_table>
|
||||
|
||||
`ASOF JOIN` применим в том случае, когда необходимо объединять записи, которые не имеют точного совпадения.
|
||||
|
||||
Таблицы для `ASOF JOIN` должны иметь столбец с отсортированной последовательностью. Этот столбец не может быть единственным в таблице и должен быть одного из типов: `UInt32`, `UInt64`, `Float32`, `Float64`, `Date` и `DateTime`.
|
||||
Для работы алгоритма необходим специальный столбец в таблицах. Этот столбец:
|
||||
|
||||
- Должен содержать упорядоченную последовательность.
|
||||
- Может быть одного из следующих типов: [Int*, UInt*](../../data-types/int-uint.md), [Float*](../../data-types/float.md), [Date](../../data-types/date.md), [DateTime](../../data-types/datetime.md), [Decimal*](../../data-types/decimal.md).
|
||||
- Не может быть единственным столбцом в секции `JOIN`.
|
||||
|
||||
Синтаксис `ASOF JOIN ... ON`:
|
||||
|
||||
|
@ -38,7 +38,7 @@
|
||||
## RELOAD DICTIONARIES {#query_language-system-reload-dictionaries}
|
||||
|
||||
Перегружает все словари, которые были успешно загружены до этого.
|
||||
По умолчанию включена ленивая загрузка [dictionaries\_lazy\_load](../../sql-reference/statements/system.md#dictionaries-lazy-load), поэтому словари не загружаются автоматически при старте, а только при первом обращении через dictGet или SELECT к ENGINE=Dictionary. После этого такие словари (LOADED) будут перегружаться командой `system reload dictionaries`.
|
||||
По умолчанию включена ленивая загрузка [dictionaries\_lazy\_load](../../operations/server-configuration-parameters/settings.md#server_configuration_parameters-dictionaries_lazy_load), поэтому словари не загружаются автоматически при старте, а только при первом обращении через dictGet или SELECT к ENGINE=Dictionary. После этого такие словари (LOADED) будут перегружаться командой `system reload dictionaries`.
|
||||
Всегда возвращает `Ok.`, вне зависимости от результата обновления словарей.
|
||||
|
||||
## RELOAD DICTIONARY Dictionary\_name {#query_language-system-reload-dictionary}
|
||||
|
@ -1,4 +1,4 @@
|
||||
# generateRandom {#generateRandom}
|
||||
# generateRandom {#generaterandom}
|
||||
|
||||
Генерирует случайные данные с заданной схемой.
|
||||
Позволяет заполнять тестовые таблицы данными.
|
||||
|
@ -220,7 +220,7 @@ if __name__ == '__main__':
|
||||
arg_parser.add_argument('--website-dir', default=website_dir)
|
||||
arg_parser.add_argument('--output-dir', default='build')
|
||||
arg_parser.add_argument('--enable-stable-releases', action='store_true')
|
||||
arg_parser.add_argument('--stable-releases-limit', type=int, default='4')
|
||||
arg_parser.add_argument('--stable-releases-limit', type=int, default='3')
|
||||
arg_parser.add_argument('--lts-releases-limit', type=int, default='2')
|
||||
arg_parser.add_argument('--nav-limit', type=int, default='0')
|
||||
arg_parser.add_argument('--version-prefix', type=str, default='')
|
||||
|
@ -1,7 +1,7 @@
|
||||
Babel==2.8.0
|
||||
certifi==2020.4.5.2
|
||||
chardet==3.0.4
|
||||
googletrans==2.4.0
|
||||
googletrans==3.0.0
|
||||
idna==2.9
|
||||
Jinja2==2.11.2
|
||||
pandocfilters==1.4.2
|
||||
|
@ -14,6 +14,7 @@ toc_title: Entegrasyonlar
|
||||
|
||||
- İlişkisel veritabanı yönetim sistemleri
|
||||
- [MySQL](https://www.mysql.com)
|
||||
- [mysql2ch](https://github.com/long2ice/mysql2ch)
|
||||
- [ProxySQL](https://github.com/sysown/proxysql/wiki/ClickHouse-Support)
|
||||
- [clickhouse-mysql-data-reader](https://github.com/Altinity/clickhouse-mysql-data-reader)
|
||||
- [horgh-çoğaltıcı](https://github.com/larsnovikov/horgh-replicator)
|
||||
|
@ -1,7 +1,5 @@
|
||||
---
|
||||
machine_translated: true
|
||||
machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd
|
||||
toc_folder_title: "\u6885\u6811\u5BB6\u65CF"
|
||||
toc_folder_title: "合并树家族"
|
||||
toc_priority: 28
|
||||
---
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
---
|
||||
machine_translated: true
|
||||
machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd
|
||||
toc_priority: 37
|
||||
toc_title: "\u7248\u672C\u96C6\u5408\u5728\u65B0\u6811"
|
||||
---
|
||||
@ -33,23 +31,23 @@ CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
|
||||
|
||||
有关查询参数的说明,请参阅 [查询说明](../../../sql-reference/statements/create.md).
|
||||
|
||||
**发动机参数**
|
||||
**引擎参数**
|
||||
|
||||
``` sql
|
||||
VersionedCollapsingMergeTree(sign, version)
|
||||
```
|
||||
|
||||
- `sign` — Name of the column with the type of row: `1` 是一个 “state” 行, `-1` 是一个 “cancel” 划
|
||||
- `sign` — 指定行类型的列名: `1` 是一个 “state” 行, `-1` 是一个 “cancel” 划
|
||||
|
||||
列数据类型应为 `Int8`.
|
||||
|
||||
- `version` — Name of the column with the version of the object state.
|
||||
- `version` — 指定对象状态版本的列名。
|
||||
|
||||
列数据类型应为 `UInt*`.
|
||||
|
||||
**查询子句**
|
||||
**查询 Clauses**
|
||||
|
||||
当创建一个 `VersionedCollapsingMergeTree` 表,相同 [条款](mergetree.md) 需要创建一个时 `MergeTree` 桌子
|
||||
当创建一个 `VersionedCollapsingMergeTree` 表时,跟创建一个 `MergeTree`表的时候需要相同 [Clause](mergetree.md)
|
||||
|
||||
<details markdown="1">
|
||||
|
||||
@ -69,17 +67,17 @@ CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
|
||||
|
||||
所有的参数,除了 `sign` 和 `version` 具有相同的含义 `MergeTree`.
|
||||
|
||||
- `sign` — Name of the column with the type of row: `1` 是一个 “state” 行, `-1` 是一个 “cancel” 划
|
||||
- `sign` — 指定行类型的列名: `1` 是一个 “state” 行, `-1` 是一个 “cancel” 划
|
||||
|
||||
Column Data Type — `Int8`.
|
||||
|
||||
- `version` — Name of the column with the version of the object state.
|
||||
- `version` — 指定对象状态版本的列名。
|
||||
|
||||
列数据类型应为 `UInt*`.
|
||||
|
||||
</details>
|
||||
|
||||
## 崩溃 {#table_engines_versionedcollapsingmergetree}
|
||||
## 折叠 {#table_engines_versionedcollapsingmergetree}
|
||||
|
||||
### 数据 {#data}
|
||||
|
||||
@ -125,23 +123,23 @@ CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
|
||||
|
||||
1. 写入数据的程序应该记住对象的状态以取消它。 该 “cancel” 字符串应该是 “state” 与相反的字符串 `Sign`. 这增加了存储的初始大小,但允许快速写入数据。
|
||||
2. 列中长时间增长的数组由于写入负载而降低了引擎的效率。 数据越简单,效率就越高。
|
||||
3. `SELECT` 结果很大程度上取决于对象变化历史的一致性。 准备插入数据时要准确。 您可以通过不一致的数据获得不可预测的结果,例如会话深度等非负指标的负值。
|
||||
3. `SELECT` 结果很大程度上取决于对象变化历史的一致性。 准备插入数据时要准确。 不一致的数据将导致不可预测的结果,例如会话深度等非负指标的负值。
|
||||
|
||||
### 算法 {#table_engines-versionedcollapsingmergetree-algorithm}
|
||||
|
||||
当ClickHouse合并数据部分时,它会删除具有相同主键和版本且不同主键和版本的每对行 `Sign`. 行的顺序并不重要。
|
||||
当ClickHouse合并数据部分时,它会删除具有相同主键和版本但 `Sign`值不同的一对行. 行的顺序并不重要。
|
||||
|
||||
当ClickHouse插入数据时,它会按主键对行进行排序。 如果 `Version` 列不在主键中,ClickHouse将其隐式添加到主键作为最后一个字段并使用它进行排序。
|
||||
|
||||
## 选择数据 {#selecting-data}
|
||||
|
||||
ClickHouse不保证具有相同主键的所有行都将位于相同的结果数据部分中,甚至位于相同的物理服务器上。 对于写入数据和随后合并数据部分都是如此。 此外,ClickHouse流程 `SELECT` 具有多个线程的查询,并且无法预测结果中的行顺序。 这意味着聚合是必需的,如果有必要得到完全 “collapsed” 从数据 `VersionedCollapsingMergeTree` 桌子
|
||||
ClickHouse不保证具有相同主键的所有行都将位于相同的结果数据部分中,甚至位于相同的物理服务器上。 对于写入数据和随后合并数据部分都是如此。 此外,ClickHouse流程 `SELECT` 具有多个线程的查询,并且无法预测结果中的行顺序。 这意味着,如果有必要从`VersionedCollapsingMergeTree` 表中得到完全 “collapsed” 的数据,聚合是必需的。
|
||||
|
||||
要完成折叠,请使用 `GROUP BY` 考虑符号的子句和聚合函数。 例如,要计算数量,请使用 `sum(Sign)` 而不是 `count()`. 要计算的东西的总和,使用 `sum(Sign * x)` 而不是 `sum(x)`,并添加 `HAVING sum(Sign) > 0`.
|
||||
|
||||
聚合 `count`, `sum` 和 `avg` 可以这样计算。 聚合 `uniq` 如果对象至少具有一个非折叠状态,则可以计算。 聚合 `min` 和 `max` 无法计算是因为 `VersionedCollapsingMergeTree` 不保存折叠状态值的历史记录。
|
||||
|
||||
如果您需要提取数据 “collapsing” 但是,如果没有聚合(例如,要检查是否存在其最新值与某些条件匹配的行),则可以使用 `FINAL` 修饰符 `FROM` 条款 这种方法效率低下,不应与大型表一起使用。
|
||||
如果您需要提取数据 “collapsing” 但是,如果没有聚合(例如,要检查是否存在其最新值与某些条件匹配的行),则可以使用 `FINAL` 修饰 `FROM` 条件这种方法效率低下,不应与大型表一起使用。
|
||||
|
||||
## 使用示例 {#example-of-use}
|
||||
|
||||
@ -233,6 +231,6 @@ SELECT * FROM UAct FINAL
|
||||
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
|
||||
```
|
||||
|
||||
这是一个非常低效的方式来选择数据。 不要把它用于大桌子。
|
||||
这是一个非常低效的方式来选择数据。 不要把它用于数据量大的表。
|
||||
|
||||
[原始文章](https://clickhouse.tech/docs/en/operations/table_engines/versionedcollapsingmergetree/) <!--hide-->
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
- 关系数据库管理系统
|
||||
- [MySQL](https://www.mysql.com)
|
||||
- [mysql2ch](https://github.com/long2ice/mysql2ch)
|
||||
- [ProxySQL](https://github.com/sysown/proxysql/wiki/ClickHouse-Support)
|
||||
- [clickhouse-mysql-data-reader](https://github.com/Altinity/clickhouse-mysql-data-reader)
|
||||
- [horgh-复制器](https://github.com/larsnovikov/horgh-replicator)
|
||||
|
@ -1,15 +1,13 @@
|
||||
---
|
||||
machine_translated: true
|
||||
machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd
|
||||
toc_folder_title: "SQL\u53C2\u8003"
|
||||
toc_folder_title: SQL参考
|
||||
toc_hidden: true
|
||||
toc_priority: 28
|
||||
toc_title: "\u9690\u85CF"
|
||||
toc_title: hidden
|
||||
---
|
||||
|
||||
# SQL参考 {#sql-reference}
|
||||
|
||||
ClickHouse支持以下类型的查询:
|
||||
ClickHouse支持以下形式的查询:
|
||||
|
||||
- [SELECT](statements/select/index.md)
|
||||
- [INSERT INTO](statements/insert-into.md)
|
||||
@ -17,4 +15,4 @@ ClickHouse支持以下类型的查询:
|
||||
- [ALTER](statements/alter.md#query_language_queries_alter)
|
||||
- [其他类型的查询](statements/misc.md)
|
||||
|
||||
[原始文章](https://clickhouse.tech/docs/en/sql-reference/) <!--hide-->
|
||||
[原始文档](https://clickhouse.tech/docs/zh/sql-reference/) <!--hide-->
|
||||
|
@ -1,156 +1,162 @@
|
||||
---
|
||||
machine_translated: true
|
||||
machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd
|
||||
toc_priority: 31
|
||||
toc_title: "\u8BED\u6CD5"
|
||||
toc_title: SQL语法
|
||||
---
|
||||
|
||||
# 语法 {#syntax}
|
||||
|
||||
系统中有两种类型的解析器:完整SQL解析器(递归下降解析器)和数据格式解析器(快速流解析器)。
|
||||
在所有情况下,除了 `INSERT` 查询时,只使用完整的SQL解析器。
|
||||
该 `INSERT` 查询使用两个解析器:
|
||||
# SQL语法 {#syntax}
|
||||
|
||||
CH有2类解析器:完整SQL解析器(递归式解析器),以及数据格式解析器(快速流式解析器)
|
||||
除了 `INSERT` 查询,其它情况下仅使用完整SQL解析器。
|
||||
`INSERT`查询会同时使用2种解析器:
|
||||
``` sql
|
||||
INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')
|
||||
```
|
||||
|
||||
该 `INSERT INTO t VALUES` 片段由完整的解析器解析,并且数据 `(1, 'Hello, world'), (2, 'abc'), (3, 'def')` 由快速流解析器解析。 您也可以通过使用 [input\_format\_values\_interpret\_expressions](../operations/settings/settings.md#settings-input_format_values_interpret_expressions) 设置。 当 `input_format_values_interpret_expressions = 1`,ClickHouse首先尝试使用fast stream解析器解析值。 如果失败,ClickHouse将尝试对数据使用完整的解析器,将其视为SQL [表达式](#syntax-expressions).
|
||||
含`INSERT INTO t VALUES` 的部分由完整SQL解析器处理,包含数据的部分 `(1, 'Hello, world'), (2, 'abc'), (3, 'def')` 交给快速流式解析器解析。通过设置参数 [input\_format\_values\_interpret\_expressions](../operations/settings/settings.md#settings-input_format_values_interpret_expressions),你也可以对数据部分开启完整SQL解析器。当 `input_format_values_interpret_expressions = 1` 时,CH优先采用快速流式解析器来解析数据。如果失败,CH再尝试用完整SQL解析器来处理,就像处理SQL [expression](#syntax-expressions) 一样。
|
||||
|
||||
数据可以有任何格式。 当接收到查询时,服务器计算不超过 [max\_query\_size](../operations/settings/settings.md#settings-max_query_size) RAM中请求的字节(默认为1MB),其余的是流解析。
|
||||
它允许避免与大的问题 `INSERT` 查询。
|
||||
数据可以采用任何格式。当CH接受到请求时,服务端先在内存中计算不超过 [max\_query\_size](../operations/settings/settings.md#settings-max_query_size) 字节的请求数据(默认1 mb),然后剩下部分交给快速流式解析器。
|
||||
|
||||
使用时 `Values` 格式为 `INSERT` 查询,它可能看起来数据被解析相同的表达式 `SELECT` 查询,但事实并非如此。 该 `Values` 格式更为有限。
|
||||
这将避免在处理大型的 `INSERT`语句时出现问题。
|
||||
|
||||
本文的其余部分将介绍完整的解析器。 有关格式解析器的详细信息,请参阅 [格式](../interfaces/formats.md) 科。
|
||||
当 `INSERT` 语句中使用 `Values` 形式时,看起来 数据部分的解析和解析`SELECT` 中的表达式相同,但并不是这样的。 `Values` 形式非常有限。
|
||||
该篇的剩余部分涵盖了完整SQL解析器。关于格式解析的更多信息,参见 [Formats](../interfaces/formats.md) 章节。
|
||||
|
||||
## 空间 {#spaces}
|
||||
## 空字符 {#spaces}
|
||||
|
||||
语法结构之间可能有任意数量的空格符号(包括查询的开始和结束)。 空格符号包括空格、制表符、换行符、CR和换页符。
|
||||
sql语句中(包含sql的起始和结束)可以有任意的空字符,这些空字符类型包括:空格字符,tab制表符,换行符,CR符,换页符等。
|
||||
|
||||
## 评论 {#comments}
|
||||
## 注释 {#comments}
|
||||
|
||||
ClickHouse支持SQL风格和C风格的注释。
|
||||
SQL风格的注释以下开头 `--` 并继续到线的末尾,一个空格后 `--` 可以省略。
|
||||
C型是从 `/*` 到 `*/`并且可以是多行,也不需要空格。
|
||||
CH支持SQL风格或C语言风格的注释:
|
||||
- SQL风格的注释以 `--` 开始,直到行末,`--` 后紧跟的空格可以忽略
|
||||
- C语言风格的注释以 `/*` 开始,以 `*/` 结束,支持多行形式,同样可以省略 `/*` 后的空格
|
||||
|
||||
## 关键词 {#syntax-keywords}
|
||||
## 关键字 {#syntax-keywords}
|
||||
|
||||
当关键字对应于以下关键字时,不区分大小写:
|
||||
以下场景的关键字是大小写不敏感的:
|
||||
- 标准SQL。例如,`SELECT`, `select` 和 `SeLeCt` 都是允许的
|
||||
- 在某些流行的RDBMS中被实现的关键字,例如,`DateTime` 和 `datetime`是一样的
|
||||
|
||||
- SQL标准。 例如, `SELECT`, `select` 和 `SeLeCt` 都是有效的。
|
||||
- 在一些流行的DBMS(MySQL或Postgres)中实现。 例如, `DateTime` 是一样的 `datetime`.
|
||||
|
||||
数据类型名称是否区分大小写可以在 `system.data_type_families` 桌子
|
||||
你可以在系统表 [system.data_type_families](../operations/system-tables.md#system_tables-data_type_families) 中检查某个数据类型的名称是否是大小写敏感型。
|
||||
|
||||
与标准SQL相比,所有其他关键字(包括函数名称)都是 **区分大小写**.
|
||||
和标准SQL相反,所有其它的关键字都是 **大小写敏感的**,包括函数名称。
|
||||
In contrast to standard SQL, all other keywords (including functions names) are **case-sensitive**.
|
||||
|
||||
不保留关键字;它们仅在相应的上下文中被视为保留关键字。 如果您使用 [标识符](#syntax-identifiers) 使用与关键字相同的名称,将它们括在双引号或反引号中。 例如,查询 `SELECT "FROM" FROM table_name` 是有效的,如果表 `table_name` 具有名称的列 `"FROM"`.
|
||||
关键字不是保留的;它们仅在相应的上下文中才会被处理。如果你使用和关键字同名的 [变量名](#syntax-identifiers) ,需要使用双引号或转移符将它们包含起来。例如:如果表 `table_name` 包含列 `"FROM"`,那么 `SELECT "FROM" FROM table_name` 是合法的
|
||||
|
||||
## 标识符 {#syntax-identifiers}
|
||||
## 变量名 {#syntax-identifiers}
|
||||
|
||||
标识符是:
|
||||
变量包括:
|
||||
Identifiers are:
|
||||
|
||||
- 集群、数据库、表、分区和列名称。
|
||||
- 功能。
|
||||
- 数据类型。
|
||||
- [表达式别名](#syntax-expression_aliases).
|
||||
- 集群,数据库,表,分区,列名称
|
||||
- 函数
|
||||
- 数据类型
|
||||
- 表达式别名
|
||||
|
||||
标识符可以是引号或非引号。 后者是优选的。
|
||||
变量名可以使用反引号包含起来
|
||||
|
||||
非引号标识符必须与正则表达式匹配 `^[a-zA-Z_][0-9a-zA-Z_]*$` 并且不能等于 [关键词](#syntax-keywords). 例: `x, _1, X_y__Z123_.`
|
||||
没有使用反引号包含的变量名,必须匹配正则表达式 `^[a-zA-Z_][0-9a-zA-Z_]*$`,并且不能和 [关键字]相同
|
||||
|
||||
如果要使用与关键字相同的标识符,或者要在标识符中使用其他符号,请使用双引号或反引号对其进行引用,例如, `"id"`, `` `id` ``.
|
||||
如果想使用和关键字同名的变量名称,或者在变量名称中包含其它符号,你需要通过双引号或转义符号,例如: `"id"`, `` `id` ``
|
||||
|
||||
## 文字数 {#literals}
|
||||
## 字符 {#literals}
|
||||
|
||||
有数字,字符串,复合和 `NULL` 文字。
|
||||
CH包含数字,字母,括号,NULL值等字符
|
||||
|
||||
### 数字 {#numeric}
|
||||
|
||||
数值文字尝试进行分析:
|
||||
数字类型字符会被做如下解析:
|
||||
- 首先,当做64位的有符号整数,使用该函数 [strtoull](https://en.cppreference.com/w/cpp/string/byte/strtoul)
|
||||
- 如果失败,解析成64位无符号整数,同样使用函数 [strtoull](https://en.cppreference.com/w/cpp/string/byte/strtoul)
|
||||
|
||||
- 首先,作为一个64位有符号的数字,使用 [strtoull](https://en.cppreference.com/w/cpp/string/byte/strtoul) 功能。
|
||||
- 如果不成功,作为64位无符号数,使用 [strtoll](https://en.cppreference.com/w/cpp/string/byte/strtol) 功能。
|
||||
- 如果不成功,作为一个浮点数使用 [strtod](https://en.cppreference.com/w/cpp/string/byte/strtof) 功能。
|
||||
- 否则,将返回错误。
|
||||
- 如果还失败了,试图解析成浮点型数值,使用函数 [strtod](https://en.cppreference.com/w/cpp/string/byte/strtof)
|
||||
Numeric literal tries to be parsed:
|
||||
|
||||
文本值具有该值适合的最小类型。
|
||||
例如,1被解析为 `UInt8`,但256被解析为 `UInt16`. 有关详细信息,请参阅 [数据类型](../sql-reference/data-types/index.md).
|
||||
- 最后,以上情形都不符合时,返回异常
|
||||
|
||||
例: `1`, `18446744073709551615`, `0xDEADBEEF`, `01`, `0.1`, `1e100`, `-1e-100`, `inf`, `nan`.
|
||||
|
||||
### 字符串 {#syntax-string-literal}
|
||||
数字类型的值类型为能容纳该值的最小数据类型。
|
||||
例如:1 解析成 `UInt8`型,256 则解析成 `UInt16`。更多信息,参见 [数据类型](../sql-reference/data-types/index.md)
|
||||
|
||||
仅支持单引号中的字符串文字。 封闭的字符可以反斜杠转义。 以下转义序列具有相应的特殊值: `\b`, `\f`, `\r`, `\n`, `\t`, `\0`, `\a`, `\v`, `\xHH`. 在所有其他情况下,转义序列的格式为 `\c`,哪里 `c` 是任何字符,被转换为 `c`. 这意味着你可以使用序列 `\'`和`\\`. 该值将具有 [字符串](../sql-reference/data-types/string.md) 类型。
|
||||
例如: `1`, `18446744073709551615`, `0xDEADBEEF`, `01`, `0.1`, `1e100`, `-1e-100`, `inf`, `nan`.
|
||||
|
||||
在字符串文字中,你至少需要转义 `'` 和 `\`. 单引号可以用单引号,文字转义 `'It\'s'` 和 `'It''s'` 是平等的。
|
||||
### 字母 {#syntax-string-literal}
|
||||
CH只支持用单引号包含的字母。特殊字符可通过反斜杠进行转义。下列转义字符都有相应的实际值: `\b`, `\f`, `\r`, `\n`, `\t`, `\0`, `\a`, `\v`, `\xHH`。其它情况下,以 `\c`形式出现的转义字符,当`c`表示任意字符时,转义字符会转换成`c`。这意味着你可以使用 `\'`和`\\`。该值将拥有[String](../sql-reference/data-types/string.md)类型。
|
||||
|
||||
### 化合物 {#compound}
|
||||
|
||||
数组使用方括号构造 `[1, 2, 3]`. Nuples用圆括号构造 `(1, 'Hello, world!', 2)`.
|
||||
从技术上讲,这些不是文字,而是分别具有数组创建运算符和元组创建运算符的表达式。
|
||||
数组必须至少包含一个项目,元组必须至少包含两个项目。
|
||||
有一个单独的情况下,当元组出现在 `IN` a条款 `SELECT` 查询。 查询结果可以包含元组,但元组不能保存到数据库(除了具有以下内容的表 [记忆](../engines/table-engines/special/memory.md) 发动机)。
|
||||
在字符串中,你至少需要对 `'` 和 `\` 进行转义。单引号可以使用单引号转义,例如 `'It\'s'` 和 `'It''s'` 是相同的。
|
||||
|
||||
### NULL {#null-literal}
|
||||
### 括号 {#compound}
|
||||
数组都是使用方括号进行构造 `[1, 2, 3]`,元组则使用圆括号 `(1, 'Hello, world!', 2)`
|
||||
|
||||
指示该值丢失。
|
||||
从技术上来讲,这些都不是字符串,而是包含创建数组和元组运算符的表达式。
|
||||
|
||||
为了存储 `NULL` 在表字段中,它必须是 [可为空](../sql-reference/data-types/nullable.md) 类型。
|
||||
创建一个数组必须至少包含一个元素,创建一个元组至少包含2个元素
|
||||
|
||||
根据数据格式(输入或输出), `NULL` 可能有不同的表示。 有关详细信息,请参阅以下文档 [数据格式](../interfaces/formats.md#formats).
|
||||
当元组出现在 `SELECT` 查询的 `IN` 部分时,是一种例外情形。查询结果可以包含元组,但是元组类型不能保存到数据库中(除非表采用 [内存表](../engines/table-engines/special/memory.md)引擎)
|
||||
|
||||
处理有许多细微差别 `NULL`. 例如,如果比较操作的至少一个参数是 `NULL`,此操作的结果也是 `NULL`. 对于乘法,加法和其他操作也是如此。 有关详细信息,请阅读每个操作的文档。
|
||||
|
||||
在查询中,您可以检查 `NULL` 使用 [IS NULL](operators/index.md#operator-is-null) 和 [IS NOT NULL](operators/index.md) 运算符及相关功能 `isNull` 和 `isNotNull`.
|
||||
### NULL值 {#null-literal}
|
||||
|
||||
## 功能 {#functions}
|
||||
代表不存在的值
|
||||
|
||||
函数调用像一个标识符一样写入,并在圆括号中包含一个参数列表(可能是空的)。 与标准SQL相比,括号是必需的,即使是空的参数列表。 示例: `now()`.
|
||||
有常规函数和聚合函数(请参阅部分 “Aggregate functions”). 某些聚合函数可以包含括号中的两个参数列表。 示例: `quantile (0.9) (x)`. 这些聚合函数被调用 “parametric” 函数,并在第一个列表中的参数被调用 “parameters”. 不带参数的聚合函数的语法与常规函数的语法相同。
|
||||
为了能在表字段中存储NULL值,该字段必须声明为 [空值](../sql-reference/data-types/nullable.md) 类型
|
||||
根据数据的格式(输入或输出),NULL值有不同的表现形式。更多信息参见文档 [数据格式](../interfaces/formats.md#formats)
|
||||
|
||||
## 运营商 {#operators}
|
||||
在处理 `NULL`时存在很多细微差别。例如,比较运算的至少一个参数为 `NULL` ,该结果也是 `NULL` 。与之类似的还有乘法运算, 加法运算,以及其它运算。更多信息,请参阅每种运算的文档部分。
|
||||
|
||||
在查询解析过程中,运算符会转换为相应的函数,同时考虑它们的优先级和关联性。
|
||||
例如,表达式 `1 + 2 * 3 + 4` 转化为 `plus(plus(1, multiply(2, 3)), 4)`.
|
||||
在语句中,可以通过 [是否为NULL](operators/index.md#operator-is-null) 以及 [是否不为NULL](operators/index.md) 运算符,以及 `isNull` 、 `isNotNull` 函数来检查 `NULL` 值
|
||||
|
||||
## 数据类型和数据库表引擎 {#data_types-and-database-table-engines}
|
||||
## 函数 {#functions}
|
||||
函数调用的写法,类似于变量并带有被圆括号包含的参数列表(可能为空)。与标准SQL不同,圆括号是必须的,不管参数列表是否为空。例如: `now()`。
|
||||
|
||||
数据类型和表引擎 `CREATE` 查询的编写方式与标识符或函数相同。 换句话说,它们可能包含也可能不包含括号中的参数列表。 有关详细信息,请参阅部分 “Data types,” “Table engines,” 和 “CREATE”.
|
||||
函数分为常规函数和聚合函数(参见“Aggregate functions”一章)。有些聚合函数包含2个参数列表,第一个参数列表中的参数被称为“parameters”。不包含“parameters”的聚合函数语法和常规函数是一样的。
|
||||
|
||||
|
||||
## 运算符 {#operators}
|
||||
|
||||
在查询解析阶段,运算符会被转换成对应的函数,使用时请注意它们的优先级。例如:
|
||||
表达式 `1 + 2 * 3 + 4` 会被解析成 `plus(plus(1, multiply(2, 3)), 4)`.
|
||||
|
||||
|
||||
## 数据类型及数据库/表引擎 {#data_types-and-database-table-engines}
|
||||
|
||||
`CREATE` 语句中的数据类型和表引擎写法与变量或函数类似。
|
||||
换句话说,它们可以用括号包含参数列表。更多信息,参见“数据类型,” “数据表引擎” 和 “CREATE语句”等章节
|
||||
|
||||
## 表达式别名 {#syntax-expression_aliases}
|
||||
|
||||
别名是查询中表达式的用户定义名称。
|
||||
别名是用户对表达式的自定义名称
|
||||
|
||||
``` sql
|
||||
expr AS alias
|
||||
```
|
||||
|
||||
- `AS` — The keyword for defining aliases. You can define the alias for a table name or a column name in a `SELECT` 子句不使用 `AS` 关键字。
|
||||
- `AS` — 用于定义别名的关键字。可以对表或select语句中的列定义别名(`AS` 可以省略)
|
||||
例如, `SELECT table_name_alias.column_name FROM table_name table_name_alias`.
|
||||
|
||||
For example, `SELECT table_name_alias.column_name FROM table_name table_name_alias`.
|
||||
在 [CAST函数](sql_reference/functions/type_conversion_functions.md#type_conversion_function-cast) 中,`AS`有其它含义。请参见该函数的说明部分。
|
||||
|
||||
In the [CAST](sql_reference/functions/type_conversion_functions.md#type_conversion_function-cast) function, the `AS` keyword has another meaning. See the description of the function.
|
||||
|
||||
- `expr` — Any expression supported by ClickHouse.
|
||||
- `expr` — 任意CH支持的表达式.
|
||||
|
||||
For example, `SELECT column_name * 2 AS double FROM some_table`.
|
||||
例如, `SELECT column_name * 2 AS double FROM some_table`.
|
||||
|
||||
- `alias` — Name for `expr`. 别名应符合 [标识符](#syntax-identifiers) 语法
|
||||
- `alias` — `expr` 的名称。别名必须符合 [变量名]](#syntax-identifiers) 语法.
|
||||
|
||||
For example, `SELECT "table t".column_name FROM table_name AS "table t"`.
|
||||
例如, `SELECT "table t".column_name FROM table_name AS "table t"`.
|
||||
|
||||
### 使用注意事项 {#notes-on-usage}
|
||||
### 用法注意 {#notes-on-usage}
|
||||
|
||||
别名对于查询或子查询是全局的,您可以在查询的任何部分中为任何表达式定义别名。 例如, `SELECT (1 AS n) + 2, n`.
|
||||
别名在当前查询或子查询中是全局可见的,你可以在查询语句的任何位置对表达式定义别名
|
||||
|
||||
别名在子查询和子查询之间不可见。 例如,在执行查询时 `SELECT (SELECT sum(b.a) + num FROM b) - a.a AS num FROM a` ClickHouse生成异常 `Unknown identifier: num`.
|
||||
别名在当前查询的子查询及不同子查询中是不可见的。例如,执行如下查询SQL: `SELECT (SELECT sum(b.a) + num FROM b) - a.a AS num FROM a` ,CH会提示异常 `Unknown identifier: num`.
|
||||
|
||||
如果为结果列定义了别名 `SELECT` 子查询的子句,这些列在外部查询中可见。 例如, `SELECT n + m FROM (SELECT 1 AS n, 2 AS m)`.
|
||||
|
||||
小心使用与列或表名相同的别名。 让我们考虑以下示例:
|
||||
如果给select子查询语句的结果列定义其别名,那么在外层可以使用该别名。例如, `SELECT n + m FROM (SELECT 1 AS n, 2 AS m)`.
|
||||
|
||||
注意列的别名和表的别名相同时的情形,考虑如下示例:
|
||||
``` sql
|
||||
CREATE TABLE t
|
||||
(
|
||||
@ -172,16 +178,18 @@ Received exception from server (version 18.14.17):
|
||||
Code: 184. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception: Aggregate function sum(b) is found inside another aggregate function in query.
|
||||
```
|
||||
|
||||
在这个例子中,我们声明表 `t` 带柱 `b`. 然后,在选择数据时,我们定义了 `sum(b) AS b` 别名 由于别名是全局的,ClickHouse替换了文字 `b` 在表达式中 `argMax(a, b)` 用表达式 `sum(b)`. 这种替换导致异常。
|
||||
在这个示例中,先声明了表 `t` 以及列 `b`。然后,在查询数据时,又定义了别名 `sum(b) AS b`。由于别名是全局的,CH使用表达式 `sum(b)` 来替换表达式 `argMax(a, b)` 中的变量 `b`。这种替换导致出现异常。
|
||||
|
||||
## 星号 {#asterisk}
|
||||
|
||||
在一个 `SELECT` 查询中,星号可以替换表达式。 有关详细信息,请参阅部分 “SELECT”.
|
||||
select查询中,星号可以代替表达式使用。详情请参见“select”部分
|
||||
|
||||
|
||||
## 表达式 {#syntax-expressions}
|
||||
|
||||
表达式是函数、标识符、文字、运算符的应用程序、括号中的表达式、子查询或星号。 它还可以包含别名。
|
||||
表达式列表是一个或多个用逗号分隔的表达式。
|
||||
函数和运算符,反过来,可以有表达式作为参数。
|
||||
|
||||
[原始文章](https://clickhouse.tech/docs/en/sql_reference/syntax/) <!--hide-->
|
||||
An expression is a function, identifier, literal, application of an operator, expression in brackets, subquery, or asterisk. It can also contain an alias.
|
||||
A list of expressions is one or more expressions separated by commas.
|
||||
Functions and operators, in turn, can have expressions as arguments.
|
||||
|
||||
[原始文档](https://clickhouse.tech/docs/en/sql_reference/syntax/) <!--hide-->
|
||||
|
@ -869,7 +869,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
|
||||
|
||||
if (listen_try)
|
||||
{
|
||||
LOG_ERROR(log, "{}. If it is an IPv6 or IPv4 address and your host has disabled IPv6 or IPv4, then consider to "
|
||||
LOG_WARNING(log, "{}. If it is an IPv6 or IPv4 address and your host has disabled IPv6 or IPv4, then consider to "
|
||||
"specify not disabled IPv4 or IPv6 address to listen in <listen_host> element of configuration "
|
||||
"file. Example for disabled IPv6: <listen_host>0.0.0.0</listen_host> ."
|
||||
" Example for disabled IPv4: <listen_host>::</listen_host>",
|
||||
@ -1013,7 +1013,8 @@ int Server::main(const std::vector<std::string> & /*args*/)
|
||||
}
|
||||
|
||||
if (servers.empty())
|
||||
throw Exception("No servers started (add valid listen_host and 'tcp_port' or 'http_port' to configuration file.)", ErrorCodes::NO_ELEMENTS_IN_CONFIG);
|
||||
throw Exception("No servers started (add valid listen_host and 'tcp_port' or 'http_port' to configuration file.)",
|
||||
ErrorCodes::NO_ELEMENTS_IN_CONFIG);
|
||||
|
||||
global_context->enableNamedSessions();
|
||||
|
||||
|
@ -4,10 +4,10 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#if __has_include(<sanitizer/asan_interface.h>)
|
||||
#include <Core/Defines.h>
|
||||
#if __has_include(<sanitizer/asan_interface.h>) && defined(ADDRESS_SANITIZER)
|
||||
# include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
#include <Core/Defines.h>
|
||||
#include <Common/memcpySmall.h>
|
||||
#include <Common/ProfileEvents.h>
|
||||
#include <Common/Allocator.h>
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if __has_include(<sanitizer/asan_interface.h>)
|
||||
#include <Core/Defines.h>
|
||||
#if __has_include(<sanitizer/asan_interface.h>) && defined(ADDRESS_SANITIZER)
|
||||
# include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
#include <Core/Defines.h>
|
||||
#include <Common/Arena.h>
|
||||
#include <Common/BitHelpers.h>
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Field.h>
|
||||
#include <Core/AccurateComparison.h>
|
||||
#include <common/demangle.h>
|
||||
|
||||
|
||||
@ -14,7 +13,6 @@ namespace DB
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int CANNOT_CONVERT_TYPE;
|
||||
extern const int BAD_TYPE_OF_FIELD;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
@ -177,243 +175,6 @@ template <> constexpr bool isDecimalField<DecimalField<Decimal64>>() { return tr
|
||||
template <> constexpr bool isDecimalField<DecimalField<Decimal128>>() { return true; }
|
||||
|
||||
|
||||
/** More precise comparison, used for index.
|
||||
* Differs from Field::operator< and Field::operator== in that it also compares values of different types.
|
||||
* Comparison rules are same as in FunctionsComparison (to be consistent with expression evaluation in query).
|
||||
*/
|
||||
class FieldVisitorAccurateEquals : public StaticVisitor<bool>
|
||||
{
|
||||
public:
|
||||
bool operator() (const UInt64 &, const Null &) const { return false; }
|
||||
bool operator() (const UInt64 & l, const UInt64 & r) const { return l == r; }
|
||||
bool operator() (const UInt64 & l, const UInt128 & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const UInt64 & l, const Int64 & r) const { return accurate::equalsOp(l, r); }
|
||||
bool operator() (const UInt64 & l, const Float64 & r) const { return accurate::equalsOp(l, r); }
|
||||
bool operator() (const UInt64 & l, const String & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const UInt64 & l, const Array & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const UInt64 & l, const Tuple & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const UInt64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
|
||||
|
||||
bool operator() (const Int64 &, const Null &) const { return false; }
|
||||
bool operator() (const Int64 & l, const UInt64 & r) const { return accurate::equalsOp(l, r); }
|
||||
bool operator() (const Int64 & l, const UInt128 & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Int64 & l, const Int64 & r) const { return l == r; }
|
||||
bool operator() (const Int64 & l, const Float64 & r) const { return accurate::equalsOp(l, r); }
|
||||
bool operator() (const Int64 & l, const String & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Int64 & l, const Array & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Int64 & l, const Tuple & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Int64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
|
||||
|
||||
bool operator() (const Float64 &, const Null &) const { return false; }
|
||||
bool operator() (const Float64 & l, const UInt64 & r) const { return accurate::equalsOp(l, r); }
|
||||
bool operator() (const Float64 & l, const UInt128 & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Float64 & l, const Int64 & r) const { return accurate::equalsOp(l, r); }
|
||||
bool operator() (const Float64 & l, const Float64 & r) const { return l == r; }
|
||||
bool operator() (const Float64 & l, const String & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Float64 & l, const Array & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Float64 & l, const Tuple & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Float64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const Null &, const T &) const
|
||||
{
|
||||
return std::is_same_v<T, Null>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const String & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, String>)
|
||||
return l == r;
|
||||
if constexpr (std::is_same_v<T, UInt128>)
|
||||
return stringToUUID(l) == r;
|
||||
if constexpr (std::is_same_v<T, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const UInt128 & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, UInt128>)
|
||||
return l == r;
|
||||
if constexpr (std::is_same_v<T, String>)
|
||||
return l == stringToUUID(r);
|
||||
if constexpr (std::is_same_v<T, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const Array & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, Array>)
|
||||
return l == r;
|
||||
if constexpr (std::is_same_v<T, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const Tuple & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, Tuple>)
|
||||
return l == r;
|
||||
if constexpr (std::is_same_v<T, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator() (const DecimalField<T> & l, const U & r) const
|
||||
{
|
||||
if constexpr (isDecimalField<U>())
|
||||
return l == r;
|
||||
if constexpr (std::is_same_v<U, Int64> || std::is_same_v<U, UInt64>)
|
||||
return l == DecimalField<Decimal128>(r, 0);
|
||||
if constexpr (std::is_same_v<U, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T> bool operator() (const UInt64 & l, const DecimalField<T> & r) const { return DecimalField<Decimal128>(l, 0) == r; }
|
||||
template <typename T> bool operator() (const Int64 & l, const DecimalField<T> & r) const { return DecimalField<Decimal128>(l, 0) == r; }
|
||||
template <typename T> bool operator() (const Float64 & l, const DecimalField<T> & r) const { return cantCompare(l, r); }
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const AggregateFunctionStateData & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, AggregateFunctionStateData>)
|
||||
return l == r;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T, typename U>
|
||||
bool cantCompare(const T &, const U &) const
|
||||
{
|
||||
if constexpr (std::is_same_v<U, Null>)
|
||||
return false;
|
||||
throw Exception("Cannot compare " + demangle(typeid(T).name()) + " with " + demangle(typeid(U).name()),
|
||||
ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
}
|
||||
};
|
||||
|
||||
class FieldVisitorAccurateLess : public StaticVisitor<bool>
|
||||
{
|
||||
public:
|
||||
bool operator() (const UInt64 &, const Null &) const { return false; }
|
||||
bool operator() (const UInt64 & l, const UInt64 & r) const { return l < r; }
|
||||
bool operator() (const UInt64 & l, const UInt128 & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const UInt64 & l, const Int64 & r) const { return accurate::lessOp(l, r); }
|
||||
bool operator() (const UInt64 & l, const Float64 & r) const { return accurate::lessOp(l, r); }
|
||||
bool operator() (const UInt64 & l, const String & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const UInt64 & l, const Array & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const UInt64 & l, const Tuple & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const UInt64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
|
||||
|
||||
bool operator() (const Int64 &, const Null &) const { return false; }
|
||||
bool operator() (const Int64 & l, const UInt64 & r) const { return accurate::lessOp(l, r); }
|
||||
bool operator() (const Int64 & l, const UInt128 & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Int64 & l, const Int64 & r) const { return l < r; }
|
||||
bool operator() (const Int64 & l, const Float64 & r) const { return accurate::lessOp(l, r); }
|
||||
bool operator() (const Int64 & l, const String & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Int64 & l, const Array & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Int64 & l, const Tuple & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Int64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
|
||||
|
||||
bool operator() (const Float64 &, const Null &) const { return false; }
|
||||
bool operator() (const Float64 & l, const UInt64 & r) const { return accurate::lessOp(l, r); }
|
||||
bool operator() (const Float64 & l, const UInt128 & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Float64 & l, const Int64 & r) const { return accurate::lessOp(l, r); }
|
||||
bool operator() (const Float64 & l, const Float64 & r) const { return l < r; }
|
||||
bool operator() (const Float64 & l, const String & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Float64 & l, const Array & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Float64 & l, const Tuple & r) const { return cantCompare(l, r); }
|
||||
bool operator() (const Float64 & l, const AggregateFunctionStateData & r) const { return cantCompare(l, r); }
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const Null &, const T &) const
|
||||
{
|
||||
return !std::is_same_v<T, Null>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const String & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, String>)
|
||||
return l < r;
|
||||
if constexpr (std::is_same_v<T, UInt128>)
|
||||
return stringToUUID(l) < r;
|
||||
if constexpr (std::is_same_v<T, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const UInt128 & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, UInt128>)
|
||||
return l < r;
|
||||
if constexpr (std::is_same_v<T, String>)
|
||||
return l < stringToUUID(r);
|
||||
if constexpr (std::is_same_v<T, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const Array & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, Array>)
|
||||
return l < r;
|
||||
if constexpr (std::is_same_v<T, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const Tuple & l, const T & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, Tuple>)
|
||||
return l < r;
|
||||
if constexpr (std::is_same_v<T, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator() (const DecimalField<T> & l, const U & r) const
|
||||
{
|
||||
if constexpr (isDecimalField<U>())
|
||||
return l < r;
|
||||
if constexpr (std::is_same_v<U, Int64> || std::is_same_v<U, UInt64>)
|
||||
return l < DecimalField<Decimal128>(r, 0);
|
||||
if constexpr (std::is_same_v<U, Null>)
|
||||
return false;
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
template <typename T> bool operator() (const UInt64 & l, const DecimalField<T> & r) const { return DecimalField<Decimal128>(l, 0) < r; }
|
||||
template <typename T> bool operator() (const Int64 & l, const DecimalField<T> & r) const { return DecimalField<Decimal128>(l, 0) < r; }
|
||||
template <typename T> bool operator() (const Float64 &, const DecimalField<T> &) const { return false; }
|
||||
|
||||
template <typename T>
|
||||
bool operator() (const AggregateFunctionStateData & l, const T & r) const
|
||||
{
|
||||
return cantCompare(l, r);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T, typename U>
|
||||
bool cantCompare(const T &, const U &) const
|
||||
{
|
||||
throw Exception("Cannot compare " + demangle(typeid(T).name()) + " with " + demangle(typeid(U).name()),
|
||||
ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Implements `+=` operation.
|
||||
* Returns false if the result is zero.
|
||||
*/
|
||||
|
142
src/Common/FieldVisitorsAccurateComparison.h
Normal file
142
src/Common/FieldVisitorsAccurateComparison.h
Normal file
@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Field.h>
|
||||
#include <Core/AccurateComparison.h>
|
||||
#include <common/demangle.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <IO/ReadBufferFromString.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_TYPE_OF_FIELD;
|
||||
}
|
||||
|
||||
/** More precise comparison, used for index.
|
||||
* Differs from Field::operator< and Field::operator== in that it also compares values of different types.
|
||||
* Comparison rules are same as in FunctionsComparison (to be consistent with expression evaluation in query).
|
||||
*/
|
||||
class FieldVisitorAccurateEquals : public StaticVisitor<bool>
|
||||
{
|
||||
public:
|
||||
template <typename T, typename U>
|
||||
bool operator() (const T & l, const U & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, Null> || std::is_same_v<U, Null>)
|
||||
return std::is_same_v<T, U>;
|
||||
else
|
||||
{
|
||||
if constexpr (std::is_same_v<T, U>)
|
||||
return l == r;
|
||||
|
||||
if constexpr (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
|
||||
return accurate::equalsOp(l, r);
|
||||
|
||||
if constexpr (isDecimalField<T>() && isDecimalField<U>())
|
||||
return l == r;
|
||||
|
||||
if constexpr (isDecimalField<T>() && std::is_arithmetic_v<U>)
|
||||
return l == DecimalField<Decimal128>(r, 0);
|
||||
|
||||
if constexpr (std::is_arithmetic_v<T> && isDecimalField<U>())
|
||||
return DecimalField<Decimal128>(l, 0) == r;
|
||||
|
||||
if constexpr (std::is_same_v<T, String>)
|
||||
{
|
||||
if constexpr (std::is_same_v<U, UInt128>)
|
||||
return stringToUUID(l) == r;
|
||||
|
||||
if constexpr (std::is_arithmetic_v<U>)
|
||||
{
|
||||
ReadBufferFromString in(l);
|
||||
T parsed;
|
||||
readText(parsed, in);
|
||||
return operator()(parsed, r);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<U, String>)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, UInt128>)
|
||||
return l == stringToUUID(r);
|
||||
|
||||
if constexpr (std::is_arithmetic_v<T>)
|
||||
{
|
||||
ReadBufferFromString in(r);
|
||||
T parsed;
|
||||
readText(parsed, in);
|
||||
return operator()(l, parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception("Cannot compare " + demangle(typeid(T).name()) + " with " + demangle(typeid(U).name()),
|
||||
ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class FieldVisitorAccurateLess : public StaticVisitor<bool>
|
||||
{
|
||||
public:
|
||||
template <typename T, typename U>
|
||||
bool operator() (const T & l, const U & r) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, Null> || std::is_same_v<U, Null>)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
if constexpr (std::is_same_v<T, U>)
|
||||
return l < r;
|
||||
|
||||
if constexpr (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
|
||||
return accurate::lessOp(l, r);
|
||||
|
||||
if constexpr (isDecimalField<T>() && isDecimalField<U>())
|
||||
return l < r;
|
||||
|
||||
if constexpr (isDecimalField<T>() && std::is_arithmetic_v<U>)
|
||||
return l < DecimalField<Decimal128>(r, 0);
|
||||
|
||||
if constexpr (std::is_arithmetic_v<T> && isDecimalField<U>())
|
||||
return DecimalField<Decimal128>(l, 0) < r;
|
||||
|
||||
if constexpr (std::is_same_v<T, String>)
|
||||
{
|
||||
if constexpr (std::is_same_v<U, UInt128>)
|
||||
return stringToUUID(l) < r;
|
||||
|
||||
if constexpr (std::is_arithmetic_v<U>)
|
||||
{
|
||||
ReadBufferFromString in(l);
|
||||
T parsed;
|
||||
readText(parsed, in);
|
||||
return operator()(parsed, r);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<U, String>)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, UInt128>)
|
||||
return l < stringToUUID(r);
|
||||
|
||||
if constexpr (std::is_arithmetic_v<T>)
|
||||
{
|
||||
ReadBufferFromString in(r);
|
||||
T parsed;
|
||||
readText(parsed, in);
|
||||
return operator()(l, parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception("Cannot compare " + demangle(typeid(T).name()) + " with " + demangle(typeid(U).name()),
|
||||
ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -272,12 +272,12 @@ struct ODBCBridgeMixin
|
||||
return AccessType::ODBC;
|
||||
}
|
||||
|
||||
static std::unique_ptr<ShellCommand> startBridge(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log, const Poco::Timespan & http_timeout)
|
||||
static std::unique_ptr<ShellCommand> startBridge(
|
||||
const Poco::Util::AbstractConfiguration & config, Poco::Logger * log, const Poco::Timespan & http_timeout)
|
||||
{
|
||||
/// Path to executable folder
|
||||
Poco::Path path{config.getString("application.dir", "/usr/bin")};
|
||||
|
||||
|
||||
std::vector<std::string> cmd_args;
|
||||
path.setFileName("clickhouse-odbc-bridge");
|
||||
|
||||
|
@ -87,7 +87,7 @@
|
||||
#define DBMS_DISTRIBUTED_SIGNATURE_HEADER 0xCAFEDACEull
|
||||
#define DBMS_DISTRIBUTED_SIGNATURE_HEADER_OLD_FORMAT 0xCAFECABEull
|
||||
|
||||
#if !__has_include(<sanitizer/asan_interface.h>)
|
||||
#if !__has_include(<sanitizer/asan_interface.h>) || !defined(ADDRESS_SANITIZER)
|
||||
# define ASAN_UNPOISON_MEMORY_REGION(a, b)
|
||||
# define ASAN_POISON_MEMORY_REGION(a, b)
|
||||
#endif
|
||||
|
@ -360,6 +360,7 @@ struct Settings : public SettingsCollection<Settings>
|
||||
M(SettingBool, optimize_trivial_count_query, true, "Process trivial 'SELECT count() FROM table' query from metadata.", 0) \
|
||||
M(SettingUInt64, mutations_sync, 0, "Wait for synchronous execution of ALTER TABLE UPDATE/DELETE queries (mutations). 0 - execute asynchronously. 1 - wait current server. 2 - wait all replicas if they exist.", 0) \
|
||||
M(SettingBool, optimize_arithmetic_operations_in_aggregate_functions, true, "Move arithmetic operations out of aggregation functions", 0) \
|
||||
M(SettingBool, optimize_duplicate_order_by_and_distinct, true, "Remove duplicate ORDER BY and DISTINCT if it's possible", 0) \
|
||||
M(SettingBool, optimize_if_chain_to_miltiif, false, "Replace if(cond1, then1, if(cond2, ...)) chains to multiIf. Currently it's not beneficial for numeric types.", 0) \
|
||||
M(SettingBool, allow_experimental_alter_materialized_view_structure, false, "Allow atomic alter on Materialized views. Work in progress.", 0) \
|
||||
M(SettingBool, enable_early_constant_folding, true, "Enable query optimization where we analyze function and subqueries results and rewrite query if there're constants there", 0) \
|
||||
@ -376,6 +377,7 @@ struct Settings : public SettingsCollection<Settings>
|
||||
M(SettingBool, materialize_ttl_after_modify, true, "Apply TTL for old data, after ALTER MODIFY TTL query", 0) \
|
||||
\
|
||||
M(SettingBool, allow_experimental_geo_types, false, "Allow geo data types such as Point, Ring, Polygon, MultiPolygon", 0) \
|
||||
M(SettingBool, data_type_default_nullable, false, "Data types without NULL or NOT NULL will make Nullable", 0) \
|
||||
\
|
||||
/** Obsolete settings that do nothing but left for compatibility reasons. Remove each one after half a year of obsolescence. */ \
|
||||
\
|
||||
|
@ -30,7 +30,7 @@ namespace ErrorCodes
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
static const std::vector<String> supported_functions{"any", "anyLast", "min", "max", "sum", "groupBitAnd", "groupBitOr", "groupBitXor", "sumMap"};
|
||||
static const std::vector<String> supported_functions{"any", "anyLast", "min", "max", "sum", "groupBitAnd", "groupBitOr", "groupBitXor", "sumMap", "groupArrayArray", "groupUniqArrayArray"};
|
||||
|
||||
|
||||
String DataTypeCustomSimpleAggregateFunction::getName() const
|
||||
|
@ -45,6 +45,8 @@ public:
|
||||
void deserializeProtobuf(IColumn & column, ProtobufReader & protobuf, bool allow_add_row, bool & row_added) const override;
|
||||
|
||||
bool equals(const IDataType & rhs) const override;
|
||||
|
||||
bool canBePromoted() const override { return false; }
|
||||
};
|
||||
|
||||
/** Tansform-type wrapper for DateTime64, applies given Transform to DateTime64 value or only to a whole part of it.
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <Parsers/ASTNameTypePair.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Common/assert_cast.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/WriteBufferFromString.h>
|
||||
|
@ -16,13 +16,18 @@ void DataTypeUUID::serializeText(const IColumn & column, size_t row_num, WriteBu
|
||||
writeText(UUID(assert_cast<const ColumnUInt128 &>(column).getData()[row_num]), ostr);
|
||||
}
|
||||
|
||||
void DataTypeUUID::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings &) const
|
||||
void DataTypeUUID::deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const
|
||||
{
|
||||
UUID x;
|
||||
readText(x, istr);
|
||||
assert_cast<ColumnUInt128 &>(column).getData().push_back(x);
|
||||
}
|
||||
|
||||
void DataTypeUUID::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
|
||||
{
|
||||
deserializeText(column, istr, settings);
|
||||
}
|
||||
|
||||
void DataTypeUUID::serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const
|
||||
{
|
||||
serializeText(column, row_num, ostr, settings);
|
||||
|
@ -17,6 +17,7 @@ public:
|
||||
bool equals(const IDataType & rhs) const override;
|
||||
|
||||
void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override;
|
||||
void deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override;
|
||||
void serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override;
|
||||
void deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override;
|
||||
void serializeTextQuoted(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override;
|
||||
@ -30,6 +31,8 @@ public:
|
||||
|
||||
bool canBeUsedInBitOperations() const override { return true; }
|
||||
bool canBeInsideNullable() const override { return true; }
|
||||
|
||||
bool canBePromoted() const override { return false; }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <DataTypes/DataTypeEnum.h>
|
||||
#include <DataTypes/getLeastSupertype.h>
|
||||
|
||||
#include <Interpreters/convertFieldToType.h>
|
||||
#include <Interpreters/castColumn.h>
|
||||
|
||||
#include <Functions/IFunctionAdaptors.h>
|
||||
@ -51,7 +52,6 @@ namespace DB
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int TOO_LARGE_STRING_SIZE;
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int LOGICAL_ERROR;
|
||||
@ -812,94 +812,51 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool executeDateOrDateTimeOrEnumOrUUIDWithConstString(
|
||||
bool executeWithConstString(
|
||||
Block & block, size_t result, const IColumn * col_left_untyped, const IColumn * col_right_untyped,
|
||||
const DataTypePtr & left_type, const DataTypePtr & right_type, bool left_is_num, size_t input_rows_count)
|
||||
const DataTypePtr & left_type, const DataTypePtr & right_type, size_t input_rows_count)
|
||||
{
|
||||
/// This is no longer very special case - comparing dates, datetimes, and enumerations with a string constant.
|
||||
const IColumn * column_string_untyped = !left_is_num ? col_left_untyped : col_right_untyped;
|
||||
const IColumn * column_number = left_is_num ? col_left_untyped : col_right_untyped;
|
||||
const IDataType * number_type = left_is_num ? left_type.get() : right_type.get();
|
||||
/// To compare something with const string, we cast constant to appropriate type and compare as usual.
|
||||
/// It is ok to throw exception if value is not convertible.
|
||||
/// We should deal with possible overflows, e.g. toUInt8(1) = '257' should return false.
|
||||
|
||||
WhichDataType which(number_type);
|
||||
const ColumnConst * left_const = checkAndGetColumnConstStringOrFixedString(col_left_untyped);
|
||||
const ColumnConst * right_const = checkAndGetColumnConstStringOrFixedString(col_right_untyped);
|
||||
|
||||
const bool legal_types = which.isDateOrDateTime() || which.isEnum() || which.isUUID();
|
||||
|
||||
const auto column_string = checkAndGetColumnConst<ColumnString>(column_string_untyped);
|
||||
if (!column_string || !legal_types)
|
||||
if (!left_const && !right_const)
|
||||
return false;
|
||||
|
||||
StringRef string_value = column_string->getDataAt(0);
|
||||
const IDataType * type_string = left_const ? left_type.get() : right_type.get();
|
||||
const DataTypePtr & type_to_compare = !left_const ? left_type : right_type;
|
||||
|
||||
if (which.isDate())
|
||||
Field string_value = left_const ? left_const->getField() : right_const->getField();
|
||||
Field converted = convertFieldToType(string_value, *type_to_compare, type_string);
|
||||
|
||||
/// If not possible to convert, comparison with =, <, >, <=, >= yields to false and comparison with != yields to true.
|
||||
if (converted.isNull())
|
||||
{
|
||||
DayNum date;
|
||||
ReadBufferFromMemory in(string_value.data, string_value.size);
|
||||
readDateText(date, in);
|
||||
if (!in.eof())
|
||||
throw Exception("String is too long for Date: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
|
||||
ColumnPtr parsed_const_date_holder = DataTypeDate().createColumnConst(input_rows_count, date);
|
||||
const ColumnConst * parsed_const_date = assert_cast<const ColumnConst *>(parsed_const_date_holder.get());
|
||||
executeNumLeftType<DataTypeDate::FieldType>(block, result,
|
||||
left_is_num ? col_left_untyped : parsed_const_date,
|
||||
left_is_num ? parsed_const_date : col_right_untyped);
|
||||
block.getByPosition(result).column = DataTypeUInt8().createColumnConst(input_rows_count,
|
||||
std::is_same_v<Op<int, int>, NotEqualsOp<int, int>>);
|
||||
}
|
||||
else if (which.isDateTime())
|
||||
else
|
||||
{
|
||||
time_t date_time;
|
||||
ReadBufferFromMemory in(string_value.data, string_value.size);
|
||||
readDateTimeText(date_time, in, dynamic_cast<const DataTypeDateTime &>(*number_type).getTimeZone());
|
||||
if (!in.eof())
|
||||
throw Exception("String is too long for DateTime: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
auto column_converted = type_to_compare->createColumnConst(input_rows_count, converted);
|
||||
|
||||
ColumnPtr parsed_const_date_time_holder = DataTypeDateTime().createColumnConst(input_rows_count, UInt64(date_time));
|
||||
const ColumnConst * parsed_const_date_time = assert_cast<const ColumnConst *>(parsed_const_date_time_holder.get());
|
||||
executeNumLeftType<DataTypeDateTime::FieldType>(block, result,
|
||||
left_is_num ? col_left_untyped : parsed_const_date_time,
|
||||
left_is_num ? parsed_const_date_time : col_right_untyped);
|
||||
}
|
||||
else if (which.isUUID())
|
||||
Block tmp_block
|
||||
{
|
||||
UUID uuid;
|
||||
ReadBufferFromMemory in(string_value.data, string_value.size);
|
||||
readText(uuid, in);
|
||||
if (!in.eof())
|
||||
throw Exception("String is too long for UUID: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
{ left_const ? column_converted : col_left_untyped->getPtr(), type_to_compare, "" },
|
||||
{ !left_const ? column_converted : col_right_untyped->getPtr(), type_to_compare, "" },
|
||||
block.getByPosition(result)
|
||||
};
|
||||
|
||||
ColumnPtr parsed_const_uuid_holder = DataTypeUUID().createColumnConst(input_rows_count, uuid);
|
||||
const ColumnConst * parsed_const_uuid = assert_cast<const ColumnConst *>(parsed_const_uuid_holder.get());
|
||||
executeNumLeftType<DataTypeUUID::FieldType>(block, result,
|
||||
left_is_num ? col_left_untyped : parsed_const_uuid,
|
||||
left_is_num ? parsed_const_uuid : col_right_untyped);
|
||||
executeImpl(tmp_block, {0, 1}, 2, input_rows_count);
|
||||
|
||||
block.getByPosition(result).column = std::move(tmp_block.getByPosition(2).column);
|
||||
}
|
||||
|
||||
else if (which.isEnum8())
|
||||
executeEnumWithConstString<DataTypeEnum8>(block, result, column_number, column_string,
|
||||
number_type, left_is_num, input_rows_count);
|
||||
else if (which.isEnum16())
|
||||
executeEnumWithConstString<DataTypeEnum16>(block, result, column_number, column_string,
|
||||
number_type, left_is_num, input_rows_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Comparison between DataTypeEnum<T> and string constant containing the name of an enum element
|
||||
template <typename EnumType>
|
||||
void executeEnumWithConstString(
|
||||
Block & block, const size_t result, const IColumn * column_number, const ColumnConst * column_string,
|
||||
const IDataType * type_untyped, const bool left_is_num, size_t input_rows_count)
|
||||
{
|
||||
const auto type = static_cast<const EnumType *>(type_untyped);
|
||||
|
||||
const Field x = castToNearestFieldType(type->getValue(column_string->getValue<String>()));
|
||||
const auto enum_col = type->createColumnConst(input_rows_count, x);
|
||||
|
||||
executeNumLeftType<typename EnumType::FieldType>(block, result,
|
||||
left_is_num ? column_number : enum_col.get(),
|
||||
left_is_num ? enum_col.get() : column_number);
|
||||
}
|
||||
|
||||
void executeTuple(Block & block, size_t result, const ColumnWithTypeAndName & c0, const ColumnWithTypeAndName & c1,
|
||||
size_t input_rows_count)
|
||||
{
|
||||
@ -1124,17 +1081,11 @@ public:
|
||||
bool has_date = left.isDate() || right.isDate();
|
||||
|
||||
if (!((both_represented_by_number && !has_date) /// Do not allow compare date and number.
|
||||
|| (left.isStringOrFixedString() && right.isStringOrFixedString())
|
||||
|| (left.isStringOrFixedString() || right.isStringOrFixedString()) /// Everything can be compared with string by conversion.
|
||||
/// You can compare the date, datetime, or datatime64 and an enumeration with a constant string.
|
||||
|| (left.isString() && right.isDateOrDateTime())
|
||||
|| (left.isDateOrDateTime() && right.isString())
|
||||
|| (left.isDateOrDateTime() && right.isDateOrDateTime() && left.idx == right.idx) /// only date vs date, or datetime vs datetime
|
||||
|| (left.isUUID() && right.isUUID())
|
||||
|| (left.isUUID() && right.isString())
|
||||
|| (left.isString() && right.isUUID())
|
||||
|| (left.isEnum() && right.isEnum() && arguments[0]->getName() == arguments[1]->getName()) /// only equivalent enum type values can be compared against
|
||||
|| (left.isEnum() && right.isString())
|
||||
|| (left.isString() && right.isEnum())
|
||||
|| (left_tuple && right_tuple && left_tuple->getElements().size() == right_tuple->getElements().size())
|
||||
|| (arguments[0]->equals(*arguments[1]))))
|
||||
{
|
||||
@ -1151,7 +1102,8 @@ public:
|
||||
|
||||
if (left_tuple && right_tuple)
|
||||
{
|
||||
auto adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(FunctionComparison<Op, Name>::create(context)));
|
||||
auto adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(
|
||||
FunctionComparison<Op, Name>::create(context)));
|
||||
|
||||
size_t size = left_tuple->getElements().size();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
@ -1201,6 +1153,9 @@ public:
|
||||
const bool left_is_num = col_left_untyped->isNumeric();
|
||||
const bool right_is_num = col_right_untyped->isNumeric();
|
||||
|
||||
const bool left_is_string = isStringOrFixedString(which_left);
|
||||
const bool right_is_string = isStringOrFixedString(which_right);
|
||||
|
||||
bool date_and_datetime = (left_type != right_type) &&
|
||||
which_left.isDateOrDateTime() && which_right.isDateOrDateTime();
|
||||
|
||||
@ -1226,64 +1181,14 @@ public:
|
||||
{
|
||||
executeTuple(block, result, col_with_type_and_name_left, col_with_type_and_name_right, input_rows_count);
|
||||
}
|
||||
else if (which_left.idx != which_right.idx
|
||||
&& (which_left.isDateTime64() || which_right.isDateTime64())
|
||||
&& (which_left.isStringOrFixedString() || which_right.isStringOrFixedString()))
|
||||
else if (left_is_string && right_is_string && executeString(block, result, col_left_untyped, col_right_untyped))
|
||||
{
|
||||
/** Special case of comparing DateTime64 against a string.
|
||||
*
|
||||
* Can't be moved to executeDateOrDateTimeOrEnumOrUUIDWithConstString()
|
||||
* since DateTime64 is basically a Decimal, but we do similar things, except type inference.
|
||||
* Outline:
|
||||
* - Extract string content
|
||||
* - Parse it as a ColumnDateTime64 value (same type as DateTime64, means same precision)
|
||||
* - Fabricate a column with type and name
|
||||
* - Compare left and right comlumns as DateTime64 columns.
|
||||
*/
|
||||
|
||||
const size_t datetime64_col_index = which_left.isDateTime64() ? 0 : 1;
|
||||
const size_t string_col_index = which_left.isStringOrFixedString() ? 0 : 1;
|
||||
|
||||
const auto & datetime64_col_with_type_and_name = block.getByPosition(arguments[datetime64_col_index]);
|
||||
const auto & string_col_with_type_and_name = block.getByPosition(arguments[string_col_index]);
|
||||
|
||||
if (!isColumnConst(*string_col_with_type_and_name.column))
|
||||
throw Exception(getName() + ", illegal column type of argument #" + std::to_string(string_col_index)
|
||||
+ " '" + string_col_with_type_and_name.name + "'"
|
||||
" expected const String or const FixedString,"
|
||||
" got " + string_col_with_type_and_name.type->getName(),
|
||||
ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
if (datetime64_col_with_type_and_name.column->size() == 0 || string_col_with_type_and_name.column->size() == 0)
|
||||
{
|
||||
// For some reason, when both left and right columns are empty (dry run while building a header block)
|
||||
// executeDecimal() fills result column with bogus value.
|
||||
block.getByPosition(result).column = ColumnUInt8::create();
|
||||
return;
|
||||
}
|
||||
|
||||
auto parsed_tmp_column_holder = datetime64_col_with_type_and_name.type->createColumn();
|
||||
|
||||
else if (executeWithConstString(
|
||||
block, result, col_left_untyped, col_right_untyped,
|
||||
left_type, right_type,
|
||||
input_rows_count))
|
||||
{
|
||||
const StringRef string_value = string_col_with_type_and_name.column->getDataAt(0);
|
||||
ReadBufferFromMemory in(string_value.data, string_value.size);
|
||||
datetime64_col_with_type_and_name.type->deserializeAsWholeText(*parsed_tmp_column_holder, in, FormatSettings{});
|
||||
|
||||
if (!in.eof())
|
||||
throw Exception(getName() + ": String is too long for " + datetime64_col_with_type_and_name.type->getName() + " : " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
}
|
||||
|
||||
// It is necessary to wrap tmp column in ColumnConst to avoid overflow when comparing.
|
||||
// (non-const columns are expected to have same number of rows as every other column in block).
|
||||
const ColumnWithTypeAndName parsed_tmp_col_with_type_and_name{
|
||||
ColumnConst::create(std::move(parsed_tmp_column_holder), 1),
|
||||
datetime64_col_with_type_and_name.type,
|
||||
string_col_with_type_and_name.name};
|
||||
|
||||
executeDecimal(block, result,
|
||||
which_left.isDateTime64() ? datetime64_col_with_type_and_name : parsed_tmp_col_with_type_and_name,
|
||||
which_right.isDateTime64() ? datetime64_col_with_type_and_name : parsed_tmp_col_with_type_and_name);
|
||||
|
||||
}
|
||||
else if (isColumnedAsDecimal(left_type) || isColumnedAsDecimal(right_type))
|
||||
{
|
||||
@ -1294,19 +1199,10 @@ public:
|
||||
|
||||
executeDecimal(block, result, col_with_type_and_name_left, col_with_type_and_name_right);
|
||||
}
|
||||
else if (!left_is_num && !right_is_num && executeString(block, result, col_left_untyped, col_right_untyped))
|
||||
{
|
||||
}
|
||||
else if (left_type->equals(*right_type))
|
||||
{
|
||||
executeGenericIdenticalTypes(block, result, col_left_untyped, col_right_untyped);
|
||||
}
|
||||
else if (executeDateOrDateTimeOrEnumOrUUIDWithConstString(
|
||||
block, result, col_left_untyped, col_right_untyped,
|
||||
left_type, right_type,
|
||||
left_is_num, input_rows_count))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
executeGeneric(block, result, col_with_type_and_name_left, col_with_type_and_name_right);
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <Columns/ColumnFixedString.h>
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
#include <Columns/ColumnNullable.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <Common/FieldVisitorsAccurateComparison.h>
|
||||
#include <Common/memcmpSmall.h>
|
||||
#include <Common/assert_cast.h>
|
||||
|
||||
|
@ -196,18 +196,17 @@ struct BloomFilterHash
|
||||
const ColumnString::Chars & data = index_column->getChars();
|
||||
const ColumnString::Offsets & offsets = index_column->getOffsets();
|
||||
|
||||
ColumnString::Offset current_offset = pos;
|
||||
for (size_t index = 0, size = vec.size(); index < size; ++index)
|
||||
{
|
||||
ColumnString::Offset current_offset = offsets[index + pos - 1];
|
||||
size_t length = offsets[index + pos] - current_offset - 1 /* terminating zero */;
|
||||
UInt64 city_hash = CityHash_v1_0_2::CityHash64(
|
||||
reinterpret_cast<const char *>(&data[current_offset]), offsets[index + pos] - current_offset - 1);
|
||||
reinterpret_cast<const char *>(&data[current_offset]), length);
|
||||
|
||||
if constexpr (is_first)
|
||||
vec[index] = city_hash;
|
||||
else
|
||||
vec[index] = CityHash_v1_0_2::Hash128to64(CityHash_v1_0_2::uint128(vec[index], city_hash));
|
||||
|
||||
current_offset = offsets[index + pos];
|
||||
}
|
||||
}
|
||||
else if (const auto * fixed_string_index_column = typeid_cast<const ColumnFixedString *>(column))
|
||||
|
@ -202,11 +202,11 @@ private:
|
||||
{
|
||||
std::optional<size_t> left_table_pos = IdentifierSemantic::getMembership(left);
|
||||
if (!left_table_pos)
|
||||
left_table_pos = IdentifierSemantic::chooseTable(left, tables);
|
||||
left_table_pos = IdentifierSemantic::chooseTableColumnMatch(left, tables);
|
||||
|
||||
std::optional<size_t> right_table_pos = IdentifierSemantic::getMembership(right);
|
||||
if (!right_table_pos)
|
||||
right_table_pos = IdentifierSemantic::chooseTable(right, tables);
|
||||
right_table_pos = IdentifierSemantic::chooseTableColumnMatch(right, tables);
|
||||
|
||||
if (left_table_pos && right_table_pos && (*left_table_pos != *right_table_pos))
|
||||
{
|
||||
|
72
src/Interpreters/DuplicateDistinctVisitor.h
Normal file
72
src/Interpreters/DuplicateDistinctVisitor.h
Normal file
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTSetQuery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <Parsers/IAST.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/// Removes duplicate DISTINCT from queries.
|
||||
class DuplicateDistinctMatcher
|
||||
{
|
||||
public:
|
||||
struct Data
|
||||
{
|
||||
bool is_distinct;
|
||||
std::vector<String> last_ids;
|
||||
};
|
||||
|
||||
static void visit(const ASTPtr & ast, Data & data)
|
||||
{
|
||||
auto * select_query = ast->as<ASTSelectQuery>();
|
||||
if (select_query)
|
||||
visit(*select_query, data);
|
||||
}
|
||||
|
||||
static void visit(ASTSelectQuery & select_query, Data & data)
|
||||
{
|
||||
if (!select_query.distinct || !select_query.select())
|
||||
return;
|
||||
|
||||
/// Optimize shouldn't work for distributed tables
|
||||
for (const auto & elem : select_query.children)
|
||||
{
|
||||
if (elem->as<ASTSetQuery>() && !elem->as<ASTSetQuery>()->is_standalone)
|
||||
return;
|
||||
}
|
||||
|
||||
auto expression_list = select_query.select();
|
||||
std::vector<String> current_ids;
|
||||
|
||||
if (expression_list->children.empty())
|
||||
return;
|
||||
|
||||
current_ids.reserve(expression_list->children.size());
|
||||
for (const auto & id : expression_list->children)
|
||||
current_ids.push_back(id->getColumnName());
|
||||
|
||||
if (data.is_distinct && current_ids == data.last_ids)
|
||||
select_query.distinct = false;
|
||||
|
||||
data.is_distinct = true;
|
||||
data.last_ids = std::move(current_ids);
|
||||
}
|
||||
|
||||
static bool needChildVisit(const ASTPtr &, const ASTPtr &)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using DuplicateDistinctVisitor = InDepthNodeVisitor<DuplicateDistinctMatcher, false>;
|
||||
|
||||
}
|
127
src/Interpreters/DuplicateOrderByVisitor.h
Normal file
127
src/Interpreters/DuplicateOrderByVisitor.h
Normal file
@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTSetQuery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <Parsers/IAST.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/// Checks if SELECT has stateful functions
|
||||
class ASTFunctionStatefulData
|
||||
{
|
||||
public:
|
||||
using TypeToVisit = ASTFunction;
|
||||
|
||||
const Context & context;
|
||||
bool & is_stateful;
|
||||
void visit(ASTFunction & ast_function, ASTPtr &)
|
||||
{
|
||||
if (ast_function.name == "any" || ast_function.name == "groupArray")
|
||||
{
|
||||
is_stateful = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto & function = FunctionFactory::instance().tryGet(ast_function.name, context);
|
||||
|
||||
if (function && function->isStateful())
|
||||
{
|
||||
is_stateful = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using ASTFunctionStatefulMatcher = OneTypeMatcher<ASTFunctionStatefulData>;
|
||||
using ASTFunctionStatefulVisitor = InDepthNodeVisitor<ASTFunctionStatefulMatcher, true>;
|
||||
|
||||
|
||||
/// Erases unnecessary ORDER BY from subquery
|
||||
class DuplicateOrderByFromSubqueriesData
|
||||
{
|
||||
public:
|
||||
using TypeToVisit = ASTSelectQuery;
|
||||
|
||||
bool done = false;
|
||||
|
||||
void visit(ASTSelectQuery & select_query, ASTPtr &)
|
||||
{
|
||||
if (done)
|
||||
return;
|
||||
|
||||
if (select_query.orderBy() && !select_query.limitBy() && !select_query.limitByOffset() &&
|
||||
!select_query.limitByLength() && !select_query.limitLength() && !select_query.limitOffset())
|
||||
{
|
||||
select_query.setExpression(ASTSelectQuery::Expression::ORDER_BY, nullptr);
|
||||
}
|
||||
|
||||
done = true;
|
||||
}
|
||||
};
|
||||
|
||||
using DuplicateOrderByFromSubqueriesMatcher = OneTypeMatcher<DuplicateOrderByFromSubqueriesData>;
|
||||
using DuplicateOrderByFromSubqueriesVisitor = InDepthNodeVisitor<DuplicateOrderByFromSubqueriesMatcher, true>;
|
||||
|
||||
|
||||
/// Finds SELECT that can be optimized
|
||||
class DuplicateOrderByData
|
||||
{
|
||||
public:
|
||||
using TypeToVisit = ASTSelectQuery;
|
||||
|
||||
const Context & context;
|
||||
bool done = false;
|
||||
|
||||
void visit(ASTSelectQuery & select_query, ASTPtr &)
|
||||
{
|
||||
if (done)
|
||||
return;
|
||||
|
||||
/// Disable optimization for distributed tables
|
||||
for (const auto & elem : select_query.children)
|
||||
{
|
||||
if (elem->as<ASTSetQuery>() && !elem->as<ASTSetQuery>()->is_standalone)
|
||||
return;
|
||||
}
|
||||
|
||||
if (select_query.orderBy() || select_query.groupBy())
|
||||
{
|
||||
for (auto & elem : select_query.children)
|
||||
{
|
||||
if (elem->as<ASTExpressionList>())
|
||||
{
|
||||
bool is_stateful = false;
|
||||
ASTFunctionStatefulVisitor::Data data{context, is_stateful};
|
||||
ASTFunctionStatefulVisitor(data).visit(elem);
|
||||
if (is_stateful)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto select_table_ptr = select_query.tables())
|
||||
{
|
||||
if (auto * select_table = select_table_ptr->as<ASTTablesInSelectQuery>())
|
||||
{
|
||||
if (!select_table->children.empty())
|
||||
{
|
||||
DuplicateOrderByFromSubqueriesVisitor::Data data{false};
|
||||
DuplicateOrderByFromSubqueriesVisitor(data).visit(select_table->children[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using DuplicateOrderByMatcher = OneTypeMatcher<DuplicateOrderByData>;
|
||||
using DuplicateOrderByVisitor = InDepthNodeVisitor<DuplicateOrderByMatcher, true>;
|
||||
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
#include <Interpreters/FillingRow.h>
|
||||
#include <Common/FieldVisitorsAccurateComparison.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include <Core/SortDescription.h>
|
||||
#include <Columns/IColumn.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
@ -16,7 +16,8 @@ namespace
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
std::optional<size_t> tryChooseTable(const ASTIdentifier & identifier, const std::vector<T> & tables, bool allow_ambiguous)
|
||||
std::optional<size_t> tryChooseTable(const ASTIdentifier & identifier, const std::vector<T> & tables,
|
||||
bool allow_ambiguous, bool column_match [[maybe_unused]] = false)
|
||||
{
|
||||
using ColumnMatch = IdentifierSemantic::ColumnMatch;
|
||||
|
||||
@ -27,6 +28,13 @@ std::optional<size_t> tryChooseTable(const ASTIdentifier & identifier, const std
|
||||
for (size_t i = 0; i < tables.size(); ++i)
|
||||
{
|
||||
auto match = IdentifierSemantic::canReferColumnToTable(identifier, tables[i]);
|
||||
|
||||
if constexpr (std::is_same_v<T, TableWithColumnNamesAndTypes>)
|
||||
{
|
||||
if (column_match && match == ColumnMatch::NoMatch && identifier.isShort() && tables[i].hasColumn(identifier.shortName()))
|
||||
match = ColumnMatch::ColumnName;
|
||||
}
|
||||
|
||||
if (match != ColumnMatch::NoMatch)
|
||||
{
|
||||
if (match > best_match)
|
||||
@ -125,12 +133,17 @@ std::optional<size_t> IdentifierSemantic::chooseTable(const ASTIdentifier & iden
|
||||
return tryChooseTable<DatabaseAndTableWithAlias>(identifier, tables, ambiguous);
|
||||
}
|
||||
|
||||
std::optional<size_t> IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const std::vector<TableWithColumnNamesAndTypes> & tables,
|
||||
bool ambiguous)
|
||||
std::optional<size_t> IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const TablesWithColumns & tables, bool ambiguous)
|
||||
{
|
||||
return tryChooseTable<TableWithColumnNamesAndTypes>(identifier, tables, ambiguous);
|
||||
}
|
||||
|
||||
std::optional<size_t> IdentifierSemantic::chooseTableColumnMatch(const ASTIdentifier & identifier, const TablesWithColumns & tables,
|
||||
bool ambiguous)
|
||||
{
|
||||
return tryChooseTable<TableWithColumnNamesAndTypes>(identifier, tables, ambiguous, true);
|
||||
}
|
||||
|
||||
StorageID IdentifierSemantic::extractDatabaseAndTable(const ASTIdentifier & identifier)
|
||||
{
|
||||
if (identifier.name_parts.size() > 2)
|
||||
@ -191,14 +204,9 @@ IdentifierSemantic::ColumnMatch IdentifierSemantic::canReferColumnToTable(const
|
||||
}
|
||||
|
||||
IdentifierSemantic::ColumnMatch IdentifierSemantic::canReferColumnToTable(const ASTIdentifier & identifier,
|
||||
const TableWithColumnNamesAndTypes & db_and_table)
|
||||
const TableWithColumnNamesAndTypes & table_with_columns)
|
||||
{
|
||||
ColumnMatch match = canReferColumnToTable(identifier, db_and_table.table);
|
||||
#if 0
|
||||
if (match == ColumnMatch::NoMatch && identifier.isShort() && db_and_table.hasColumn(identifier.shortName()))
|
||||
match = ColumnMatch::ColumnName;
|
||||
#endif
|
||||
return match;
|
||||
return canReferColumnToTable(identifier, table_with_columns.table);
|
||||
}
|
||||
|
||||
/// Strip qualificators from left side of column name.
|
||||
|
@ -41,7 +41,7 @@ struct IdentifierSemantic
|
||||
static std::optional<String> extractNestedName(const ASTIdentifier & identifier, const String & table_name);
|
||||
|
||||
static ColumnMatch canReferColumnToTable(const ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
static ColumnMatch canReferColumnToTable(const ASTIdentifier & identifier, const TableWithColumnNamesAndTypes & db_and_table);
|
||||
static ColumnMatch canReferColumnToTable(const ASTIdentifier & identifier, const TableWithColumnNamesAndTypes & table_with_columns);
|
||||
|
||||
static void setColumnShortName(ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
static void setColumnLongName(ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
@ -52,7 +52,9 @@ struct IdentifierSemantic
|
||||
static std::optional<size_t> getMembership(const ASTIdentifier & identifier);
|
||||
static std::optional<size_t> chooseTable(const ASTIdentifier &, const std::vector<DatabaseAndTableWithAlias> & tables,
|
||||
bool allow_ambiguous = false);
|
||||
static std::optional<size_t> chooseTable(const ASTIdentifier &, const std::vector<TableWithColumnNamesAndTypes> & tables,
|
||||
static std::optional<size_t> chooseTable(const ASTIdentifier &, const TablesWithColumns & tables,
|
||||
bool allow_ambiguous = false);
|
||||
static std::optional<size_t> chooseTableColumnMatch(const ASTIdentifier &, const TablesWithColumns & tables,
|
||||
bool allow_ambiguous = false);
|
||||
|
||||
private:
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <Common/typeid_cast.h>
|
||||
|
||||
#include <Core/Defines.h>
|
||||
#include <Core/Settings.h>
|
||||
|
||||
#include <IO/WriteBufferFromFile.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
@ -71,6 +72,7 @@ namespace ErrorCodes
|
||||
extern const int BAD_DATABASE_FOR_TEMPORARY_TABLE;
|
||||
extern const int SUSPICIOUS_TYPE_FOR_LOW_CARDINALITY;
|
||||
extern const int DICTIONARY_ALREADY_EXISTS;
|
||||
extern const int ILLEGAL_SYNTAX_FOR_DATA_TYPE;
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
}
|
||||
|
||||
@ -276,6 +278,7 @@ ColumnsDescription InterpreterCreateQuery::getColumnsDescription(
|
||||
|
||||
/** all default_expressions as a single expression list,
|
||||
* mixed with conversion-columns for each explicitly specified type */
|
||||
|
||||
ASTPtr default_expr_list = std::make_shared<ASTExpressionList>();
|
||||
NamesAndTypesList column_names_and_types;
|
||||
|
||||
@ -284,9 +287,23 @@ ColumnsDescription InterpreterCreateQuery::getColumnsDescription(
|
||||
const auto & col_decl = ast->as<ASTColumnDeclaration &>();
|
||||
|
||||
DataTypePtr column_type = nullptr;
|
||||
|
||||
if (col_decl.type)
|
||||
{
|
||||
column_type = DataTypeFactory::instance().get(col_decl.type);
|
||||
|
||||
if (col_decl.null_modifier)
|
||||
{
|
||||
if (column_type->isNullable())
|
||||
throw Exception("Cant use [NOT] NULL modifier with Nullable type", ErrorCodes::ILLEGAL_SYNTAX_FOR_DATA_TYPE);
|
||||
if (*col_decl.null_modifier)
|
||||
column_type = makeNullable(column_type);
|
||||
}
|
||||
else if (context.getSettingsRef().data_type_default_nullable)
|
||||
{
|
||||
column_type = makeNullable(column_type);
|
||||
}
|
||||
|
||||
column_names_and_types.emplace_back(col_decl.name, column_type);
|
||||
}
|
||||
else
|
||||
|
@ -4,20 +4,15 @@
|
||||
#include <DataStreams/OneBlockInputStream.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
#include <Interpreters/IdentifierSemantic.h>
|
||||
#include <Interpreters/getTableExpressions.h>
|
||||
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
|
||||
#include <Interpreters/InterpreterSelectQuery.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Parsers/DumpASTNode.h>
|
||||
#include <Parsers/ASTSubquery.h>
|
||||
#include <Parsers/queryToString.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Parsers/ASTExplainQuery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <Parsers/ASTSelectWithUnionQuery.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
|
||||
#include <Core/Field.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Storages/StorageView.h>
|
||||
#include <sstream>
|
||||
|
||||
@ -31,56 +26,30 @@ namespace
|
||||
{
|
||||
struct Data
|
||||
{
|
||||
bool analyzed = false;
|
||||
const Context & context;
|
||||
};
|
||||
|
||||
static bool needChildVisit(ASTPtr &, ASTPtr &) { return true; }
|
||||
static bool needChildVisit(ASTPtr & node, ASTPtr &)
|
||||
{
|
||||
return !node->as<ASTSelectQuery>();
|
||||
}
|
||||
|
||||
static void visit(ASTPtr & ast, Data & data)
|
||||
{
|
||||
if (auto * select_query = ast->as<ASTSelectQuery>())
|
||||
visit(*select_query, ast, data);
|
||||
if (auto * union_select_query = ast->as<ASTSelectWithUnionQuery>())
|
||||
visit(*union_select_query, ast, data);
|
||||
if (auto * select = ast->as<ASTSelectQuery>())
|
||||
visit(*select, ast, data);
|
||||
}
|
||||
|
||||
static void visit(ASTSelectQuery & select_query, ASTPtr &, Data & data)
|
||||
static void visit(ASTSelectQuery & select, ASTPtr & node, Data & data)
|
||||
{
|
||||
if (!select_query.tables())
|
||||
return;
|
||||
|
||||
for (const auto & child : select_query.tables()->children)
|
||||
{
|
||||
auto * tables_element = child->as<ASTTablesInSelectQueryElement>();
|
||||
|
||||
if (tables_element && tables_element->table_expression)
|
||||
visit(*tables_element->table_expression->as<ASTTableExpression>(), select_query, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void visit(ASTSelectWithUnionQuery &, ASTPtr & node, Data & data)
|
||||
{
|
||||
if (!data.analyzed)
|
||||
{
|
||||
data.analyzed = true;
|
||||
InterpreterSelectWithUnionQuery interpreter(
|
||||
InterpreterSelectQuery interpreter(
|
||||
node, data.context, SelectQueryOptions(QueryProcessingStage::FetchColumns).analyze().modify());
|
||||
}
|
||||
}
|
||||
|
||||
static void visit(ASTTableExpression & expression, ASTSelectQuery & select_query, Data & data)
|
||||
const SelectQueryInfo & query_info = interpreter.getQueryInfo();
|
||||
if (query_info.view_query)
|
||||
{
|
||||
if (data.context.getSettingsRef().enable_optimize_predicate_expression && expression.database_and_table_name)
|
||||
{
|
||||
if (const auto * identifier = expression.database_and_table_name->as<ASTIdentifier>())
|
||||
{
|
||||
auto table_id = data.context.resolveStorageID(*identifier);
|
||||
const auto & storage = DatabaseCatalog::instance().getTable(table_id, data.context);
|
||||
|
||||
if (auto * storage_view = dynamic_cast<StorageView *>(storage.get()))
|
||||
storage_view->getRuntimeViewQuery(&select_query, data.context, true);
|
||||
}
|
||||
ASTPtr tmp;
|
||||
StorageView::replaceWithSubquery(select, query_info.view_query->clone(), tmp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <Storages/MergeTree/MergeTreeData.h>
|
||||
#include <Storages/MergeTree/MergeTreeWhereOptimizer.h>
|
||||
#include <Storages/IStorage.h>
|
||||
#include <Storages/StorageView.h>
|
||||
|
||||
#include <TableFunctions/ITableFunction.h>
|
||||
|
||||
@ -37,7 +38,7 @@
|
||||
#include <Core/Field.h>
|
||||
#include <Core/Types.h>
|
||||
#include <Columns/Collator.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <Common/FieldVisitorsAccurateComparison.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Common/checkStackSize.h>
|
||||
#include <ext/map.h>
|
||||
@ -186,6 +187,26 @@ static Context getSubqueryContext(const Context & context)
|
||||
return subquery_context;
|
||||
}
|
||||
|
||||
static void rewriteMultipleJoins(ASTPtr & query, const TablesWithColumns & tables, const String & database, const Settings & settings)
|
||||
{
|
||||
ASTSelectQuery & select = query->as<ASTSelectQuery &>();
|
||||
|
||||
Aliases aliases;
|
||||
if (ASTPtr with = select.with())
|
||||
QueryAliasesNoSubqueriesVisitor(aliases).visit(with);
|
||||
QueryAliasesNoSubqueriesVisitor(aliases).visit(select.select());
|
||||
|
||||
CrossToInnerJoinVisitor::Data cross_to_inner{tables, aliases, database};
|
||||
CrossToInnerJoinVisitor(cross_to_inner).visit(query);
|
||||
|
||||
size_t rewriter_version = settings.multiple_joins_rewriter_version;
|
||||
if (!rewriter_version || rewriter_version > 2)
|
||||
throw Exception("Bad multiple_joins_rewriter_version setting value: " + settings.multiple_joins_rewriter_version.toString(),
|
||||
ErrorCodes::INVALID_SETTING_VALUE);
|
||||
JoinToSubqueryTransformVisitor::Data join_to_subs_data{tables, aliases, rewriter_version};
|
||||
JoinToSubqueryTransformVisitor(join_to_subs_data).visit(query);
|
||||
}
|
||||
|
||||
InterpreterSelectQuery::InterpreterSelectQuery(
|
||||
const ASTPtr & query_ptr_,
|
||||
const Context & context_,
|
||||
@ -242,29 +263,14 @@ InterpreterSelectQuery::InterpreterSelectQuery(
|
||||
/// Rewrite JOINs
|
||||
if (!has_input && joined_tables.tablesCount() > 1)
|
||||
{
|
||||
ASTSelectQuery & select = getSelectQuery();
|
||||
rewriteMultipleJoins(query_ptr, joined_tables.tablesWithColumns(), context->getCurrentDatabase(), settings);
|
||||
|
||||
Aliases aliases;
|
||||
if (ASTPtr with = select.with())
|
||||
QueryAliasesNoSubqueriesVisitor(aliases).visit(with);
|
||||
QueryAliasesNoSubqueriesVisitor(aliases).visit(select.select());
|
||||
|
||||
CrossToInnerJoinVisitor::Data cross_to_inner{joined_tables.tablesWithColumns(), aliases, context->getCurrentDatabase()};
|
||||
CrossToInnerJoinVisitor(cross_to_inner).visit(query_ptr);
|
||||
|
||||
size_t rewriter_version = settings.multiple_joins_rewriter_version;
|
||||
if (!rewriter_version || rewriter_version > 2)
|
||||
throw Exception("Bad multiple_joins_rewriter_version setting value: " + settings.multiple_joins_rewriter_version.toString(),
|
||||
ErrorCodes::INVALID_SETTING_VALUE);
|
||||
JoinToSubqueryTransformVisitor::Data join_to_subs_data{joined_tables.tablesWithColumns(), aliases, rewriter_version};
|
||||
JoinToSubqueryTransformVisitor(join_to_subs_data).visit(query_ptr);
|
||||
|
||||
joined_tables.reset(select);
|
||||
joined_tables.reset(getSelectQuery());
|
||||
joined_tables.resolveTables();
|
||||
|
||||
if (storage && joined_tables.isLeftTableSubquery())
|
||||
{
|
||||
/// Rewritten with subquery. Free storage here locks here.
|
||||
/// Rewritten with subquery. Free storage locks here.
|
||||
storage = {};
|
||||
table_lock.release();
|
||||
table_id = StorageID::createEmpty();
|
||||
@ -288,12 +294,28 @@ InterpreterSelectQuery::InterpreterSelectQuery(
|
||||
if (storage)
|
||||
row_policy_filter = context->getRowPolicyCondition(table_id.getDatabaseName(), table_id.getTableName(), RowPolicy::SELECT_FILTER);
|
||||
|
||||
StorageView * view = nullptr;
|
||||
if (storage)
|
||||
view = dynamic_cast<StorageView *>(storage.get());
|
||||
|
||||
auto analyze = [&] (bool try_move_to_prewhere)
|
||||
{
|
||||
/// Allow push down and other optimizations for VIEW: replace with subquery and rewrite it.
|
||||
ASTPtr view_table;
|
||||
if (view)
|
||||
view->replaceWithSubquery(getSelectQuery(), view_table);
|
||||
|
||||
syntax_analyzer_result = SyntaxAnalyzer(*context).analyzeSelect(
|
||||
query_ptr, SyntaxAnalyzerResult(source_header.getNamesAndTypesList(), storage),
|
||||
options, joined_tables.tablesWithColumns(), required_result_column_names, table_join);
|
||||
|
||||
if (view)
|
||||
{
|
||||
/// Restore original view name. Save rewritten subquery for future usage in StorageView.
|
||||
query_info.view_query = view->restoreViewName(getSelectQuery(), view_table);
|
||||
view = nullptr;
|
||||
}
|
||||
|
||||
if (try_move_to_prewhere && storage && !row_policy_filter && query.where() && !query.prewhere() && !query.final())
|
||||
{
|
||||
/// PREWHERE optimization: transfer some condition from WHERE to PREWHERE if enabled and viable
|
||||
|
@ -88,6 +88,8 @@ public:
|
||||
|
||||
size_t getMaxStreams() const { return max_streams; }
|
||||
|
||||
const SelectQueryInfo & getQueryInfo() const { return query_info; }
|
||||
|
||||
private:
|
||||
InterpreterSelectQuery(
|
||||
const ASTPtr & query_ptr_,
|
||||
|
@ -585,8 +585,9 @@ std::vector<TableNeededColumns> normalizeColumnNamesExtractNeeded(
|
||||
for (ASTIdentifier * ident : identifiers)
|
||||
{
|
||||
bool got_alias = aliases.count(ident->name);
|
||||
bool allow_ambiguous = got_alias; /// allow ambiguous column overridden by an alias
|
||||
|
||||
if (auto table_pos = IdentifierSemantic::chooseTable(*ident, tables))
|
||||
if (auto table_pos = IdentifierSemantic::chooseTableColumnMatch(*ident, tables, allow_ambiguous))
|
||||
{
|
||||
if (!ident->isShort())
|
||||
{
|
||||
|
@ -34,7 +34,8 @@ public:
|
||||
void makeFakeTable(StoragePtr storage, const Block & source_header);
|
||||
std::shared_ptr<TableJoin> makeTableJoin(const ASTSelectQuery & select_query);
|
||||
|
||||
const std::vector<TableWithColumnNamesAndTypes> & tablesWithColumns() const { return tables_with_columns; }
|
||||
const TablesWithColumns & tablesWithColumns() const { return tables_with_columns; }
|
||||
TablesWithColumns moveTablesWithColumns() { return std::move(tables_with_columns); }
|
||||
|
||||
bool isLeftTableSubquery() const;
|
||||
bool isLeftTableFunction() const;
|
||||
@ -49,7 +50,7 @@ public:
|
||||
private:
|
||||
Context context;
|
||||
std::vector<const ASTTableExpression *> table_expressions;
|
||||
std::vector<TableWithColumnNamesAndTypes> tables_with_columns;
|
||||
TablesWithColumns tables_with_columns;
|
||||
|
||||
/// Legacy (duplicated left table values)
|
||||
ASTPtr left_table_expression;
|
||||
|
@ -23,12 +23,15 @@
|
||||
#include <Interpreters/getTableExpressions.h>
|
||||
#include <Interpreters/OptimizeIfChains.h>
|
||||
#include <Interpreters/ArithmeticOperationsInAgrFuncOptimize.h>
|
||||
#include <Interpreters/DuplicateDistinctVisitor.h>
|
||||
#include <Interpreters/DuplicateOrderByVisitor.h>
|
||||
|
||||
#include <Parsers/ASTExpressionList.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTOrderByElement.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTSetQuery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <Parsers/queryToString.h>
|
||||
|
||||
@ -370,6 +373,18 @@ void optimizeOrderBy(const ASTSelectQuery * select_query)
|
||||
elems = std::move(unique_elems);
|
||||
}
|
||||
|
||||
/// Optimize duplicate ORDER BY and DISTINCT
|
||||
void optimizeDuplicateOrderByAndDistinct(ASTPtr & query, bool optimize_duplicate_order_by_and_distinct, const Context & context)
|
||||
{
|
||||
if (optimize_duplicate_order_by_and_distinct)
|
||||
{
|
||||
DuplicateOrderByVisitor::Data order_by_data{context, false};
|
||||
DuplicateOrderByVisitor(order_by_data).visit(query);
|
||||
DuplicateDistinctVisitor::Data distinct_data{};
|
||||
DuplicateDistinctVisitor(distinct_data).visit(query);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove duplicate items from LIMIT BY.
|
||||
void optimizeLimitBy(const ASTSelectQuery * select_query)
|
||||
{
|
||||
@ -831,6 +846,9 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyzeSelect(
|
||||
/// Remove duplicate items from ORDER BY.
|
||||
optimizeOrderBy(select_query);
|
||||
|
||||
/// Remove duplicate ORDER BY and DISTINCT from subqueries.
|
||||
optimizeDuplicateOrderByAndDistinct(query, settings.optimize_duplicate_order_by_and_distinct, context);
|
||||
|
||||
/// Remove duplicated elements from LIMIT BY clause.
|
||||
optimizeLimitBy(select_query);
|
||||
|
||||
|
@ -33,8 +33,6 @@ namespace ErrorCodes
|
||||
{
|
||||
extern const int ARGUMENT_OUT_OF_BOUND;
|
||||
extern const int TYPE_MISMATCH;
|
||||
extern const int TOO_LARGE_STRING_SIZE;
|
||||
extern const int CANNOT_CONVERT_TYPE;
|
||||
}
|
||||
|
||||
|
||||
@ -124,42 +122,6 @@ static Field convertDecimalType(const Field & from, const To & type)
|
||||
}
|
||||
|
||||
|
||||
DayNum stringToDate(const String & s)
|
||||
{
|
||||
ReadBufferFromString in(s);
|
||||
DayNum date{};
|
||||
|
||||
readDateText(date, in);
|
||||
if (!in.eof())
|
||||
throw Exception("String is too long for Date: " + s, ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
UInt64 stringToDateTime(const String & s)
|
||||
{
|
||||
ReadBufferFromString in(s);
|
||||
time_t date_time{};
|
||||
|
||||
readDateTimeText(date_time, in);
|
||||
if (!in.eof())
|
||||
throw Exception("String is too long for DateTime: " + s, ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
|
||||
return UInt64(date_time);
|
||||
}
|
||||
|
||||
DateTime64::NativeType stringToDateTime64(const String & s, UInt32 scale)
|
||||
{
|
||||
ReadBufferFromString in(s);
|
||||
DateTime64 datetime64 {0};
|
||||
|
||||
readDateTime64Text(datetime64, scale, in);
|
||||
if (!in.eof())
|
||||
throw Exception("String is too long for DateTime64: " + s, ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
|
||||
return datetime64.value;
|
||||
}
|
||||
|
||||
Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const IDataType * from_type_hint)
|
||||
{
|
||||
WhichDataType which_type(type);
|
||||
@ -184,7 +146,7 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID
|
||||
{
|
||||
return static_cast<const DataTypeDateTime &>(type).getTimeZone().fromDayNum(DayNum(src.get<UInt64>()));
|
||||
}
|
||||
else if (type.isValueRepresentedByNumber())
|
||||
else if (type.isValueRepresentedByNumber() && src.getType() != Field::Types::String)
|
||||
{
|
||||
if (which_type.isUInt8()) return convertNumericType<UInt8>(src, type);
|
||||
if (which_type.isUInt16()) return convertNumericType<UInt16>(src, type);
|
||||
@ -200,9 +162,6 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID
|
||||
if (const auto * ptype = typeid_cast<const DataTypeDecimal<Decimal64> *>(&type)) return convertDecimalType(src, *ptype);
|
||||
if (const auto * ptype = typeid_cast<const DataTypeDecimal<Decimal128> *>(&type)) return convertDecimalType(src, *ptype);
|
||||
|
||||
if (!which_type.isDateOrDateTime() && !which_type.isUUID() && !which_type.isEnum())
|
||||
throw Exception{"Cannot convert field to type " + type.getName(), ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
|
||||
if (which_type.isEnum() && (src.getType() == Field::Types::UInt64 || src.getType() == Field::Types::Int64))
|
||||
{
|
||||
/// Convert UInt64 or Int64 to Enum's value
|
||||
@ -214,36 +173,20 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID
|
||||
/// We don't need any conversion UInt64 is under type of Date and DateTime
|
||||
return src;
|
||||
}
|
||||
// TODO (vnemkov): extra cases for DateTime64: converting from integer, converting from Decimal
|
||||
|
||||
if (src.getType() == Field::Types::String)
|
||||
if (which_type.isUUID() && src.getType() == Field::Types::UInt128)
|
||||
{
|
||||
if (which_type.isDate())
|
||||
/// Already in needed type.
|
||||
return src;
|
||||
}
|
||||
|
||||
if (which_type.isDateTime64() && src.getType() == Field::Types::Decimal64)
|
||||
{
|
||||
/// Convert 'YYYY-MM-DD' Strings to Date
|
||||
return stringToDate(src.get<const String &>());
|
||||
}
|
||||
else if (which_type.isDateTime())
|
||||
{
|
||||
/// Convert 'YYYY-MM-DD hh:mm:ss' Strings to DateTime
|
||||
return stringToDateTime(src.get<const String &>());
|
||||
}
|
||||
else if (which_type.isDateTime64())
|
||||
{
|
||||
const auto * date_time64 = typeid_cast<const DataTypeDateTime64 *>(&type);
|
||||
/// Convert 'YYYY-MM-DD hh:mm:ss.NNNNNNNNN' Strings to DateTime
|
||||
return stringToDateTime64(src.get<const String &>(), date_time64->getScale());
|
||||
}
|
||||
else if (which_type.isUUID())
|
||||
{
|
||||
return stringToUUID(src.get<const String &>());
|
||||
}
|
||||
else if (which_type.isEnum())
|
||||
{
|
||||
/// Convert String to Enum's value
|
||||
return dynamic_cast<const IDataTypeEnum &>(type).castToValue(src);
|
||||
}
|
||||
/// Already in needed type.
|
||||
return src;
|
||||
}
|
||||
|
||||
/// TODO Conversion from integers to DateTime64
|
||||
}
|
||||
else if (which_type.isStringOrFixedString())
|
||||
{
|
||||
@ -328,17 +271,37 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID
|
||||
return src;
|
||||
}
|
||||
|
||||
/// Conversion from string by parsing.
|
||||
if (src.getType() == Field::Types::String)
|
||||
{
|
||||
const auto col = type.createColumn();
|
||||
ReadBufferFromString buffer(src.get<String>());
|
||||
type.deserializeAsTextEscaped(*col, buffer, FormatSettings{});
|
||||
/// Promote data type to avoid overflows. Note that overflows in the largest data type are still possible.
|
||||
const IDataType * type_to_parse = &type;
|
||||
DataTypePtr holder;
|
||||
|
||||
return (*col)[0];
|
||||
if (type.canBePromoted())
|
||||
{
|
||||
holder = type.promoteNumericType();
|
||||
type_to_parse = holder.get();
|
||||
}
|
||||
|
||||
const auto col = type_to_parse->createColumn();
|
||||
ReadBufferFromString in_buffer(src.get<String>());
|
||||
try
|
||||
{
|
||||
type_to_parse->deserializeAsWholeText(*col, in_buffer, FormatSettings{});
|
||||
}
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage(fmt::format("while converting '{}' to {}", src.get<String>(), type.getName()));
|
||||
throw;
|
||||
}
|
||||
if (!in_buffer.eof())
|
||||
throw Exception(ErrorCodes::TYPE_MISMATCH, "Cannot convert string {} to type {}", src.get<String>(), type.getName());
|
||||
|
||||
Field parsed = (*col)[0];
|
||||
return convertFieldToType(parsed, type, from_type_hint);
|
||||
}
|
||||
|
||||
// TODO (nemkov): should we attempt to parse value using or `type.deserializeAsTextEscaped()` type.deserializeAsTextEscaped() ?
|
||||
throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: "
|
||||
+ Field::Types::toString(src.getType()), ErrorCodes::TYPE_MISMATCH);
|
||||
}
|
||||
|
@ -49,9 +49,6 @@ void ASTColumnDeclaration::formatImpl(const FormatSettings & settings, FormatSta
|
||||
{
|
||||
frame.need_parens = false;
|
||||
|
||||
if (!settings.one_line)
|
||||
settings.ostr << settings.nl_or_ws << std::string(4 * frame.indent, ' ');
|
||||
|
||||
/// We have to always backquote column names to avoid ambiguouty with INDEX and other declarations in CREATE query.
|
||||
settings.ostr << backQuote(name);
|
||||
|
||||
@ -61,6 +58,12 @@ void ASTColumnDeclaration::formatImpl(const FormatSettings & settings, FormatSta
|
||||
type->formatImpl(settings, state, frame);
|
||||
}
|
||||
|
||||
if (null_modifier)
|
||||
{
|
||||
settings.ostr << ' ' << (settings.hilite ? hilite_keyword : "")
|
||||
<< (*null_modifier ? "" : "NOT ") << "NULL" << (settings.hilite ? hilite_none : "");
|
||||
}
|
||||
|
||||
if (default_expression)
|
||||
{
|
||||
settings.ostr << ' ' << (settings.hilite ? hilite_keyword : "") << default_specifier << (settings.hilite ? hilite_none : "") << ' ';
|
||||
|
@ -13,6 +13,7 @@ class ASTColumnDeclaration : public IAST
|
||||
public:
|
||||
String name;
|
||||
ASTPtr type;
|
||||
std::optional<bool> null_modifier;
|
||||
String default_specifier;
|
||||
ASTPtr default_expression;
|
||||
ASTPtr comment;
|
||||
|
@ -19,10 +19,6 @@ ASTPtr ASTConstraintDeclaration::clone() const
|
||||
|
||||
void ASTConstraintDeclaration::formatImpl(const FormatSettings & s, FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
frame.need_parens = false;
|
||||
std::string indent_str = s.one_line ? "" : std::string(4 * frame.indent, ' ');
|
||||
|
||||
s.ostr << s.nl_or_ws << indent_str;
|
||||
s.ostr << backQuoteIfNeed(name);
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << " CHECK " << (s.hilite ? hilite_none : "");
|
||||
expr->formatImpl(s, state, frame);
|
||||
|
@ -108,17 +108,9 @@ void ASTColumnsElement::formatImpl(const FormatSettings & s, FormatState & state
|
||||
return;
|
||||
}
|
||||
|
||||
frame.need_parens = false;
|
||||
std::string indent_str = s.one_line ? "" : std::string(4 * frame.indent, ' ');
|
||||
|
||||
s.ostr << s.nl_or_ws << indent_str;
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << prefix << (s.hilite ? hilite_none : "");
|
||||
|
||||
FormatSettings nested_settings = s;
|
||||
nested_settings.one_line = true;
|
||||
nested_settings.nl_or_ws = ' ';
|
||||
|
||||
elem->formatImpl(nested_settings, state, frame);
|
||||
s.ostr << ' ';
|
||||
elem->formatImpl(s, state, frame);
|
||||
}
|
||||
|
||||
|
||||
@ -172,7 +164,12 @@ void ASTColumns::formatImpl(const FormatSettings & s, FormatState & state, Forma
|
||||
}
|
||||
|
||||
if (!list.children.empty())
|
||||
{
|
||||
if (s.one_line)
|
||||
list.formatImpl(s, state, frame);
|
||||
else
|
||||
list.formatImplMultiline(s, state, frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -273,11 +270,12 @@ void ASTCreateQuery::formatQueryImpl(const FormatSettings & settings, FormatStat
|
||||
<< (!as_database.empty() ? backQuoteIfNeed(as_database) + "." : "") << backQuoteIfNeed(as_table);
|
||||
}
|
||||
|
||||
frame.expression_list_always_start_on_new_line = true;
|
||||
|
||||
if (columns_list)
|
||||
{
|
||||
settings.ostr << (settings.one_line ? " (" : "\n(");
|
||||
FormatStateStacked frame_nested = frame;
|
||||
++frame_nested.indent;
|
||||
columns_list->formatImpl(settings, state, frame_nested);
|
||||
settings.ostr << (settings.one_line ? ")" : "\n)");
|
||||
}
|
||||
@ -286,11 +284,15 @@ void ASTCreateQuery::formatQueryImpl(const FormatSettings & settings, FormatStat
|
||||
{
|
||||
settings.ostr << (settings.one_line ? " (" : "\n(");
|
||||
FormatStateStacked frame_nested = frame;
|
||||
++frame_nested.indent;
|
||||
if (settings.one_line)
|
||||
dictionary_attributes_list->formatImpl(settings, state, frame_nested);
|
||||
else
|
||||
dictionary_attributes_list->formatImplMultiline(settings, state, frame_nested);
|
||||
settings.ostr << (settings.one_line ? ")" : "\n)");
|
||||
}
|
||||
|
||||
frame.expression_list_always_start_on_new_line = false;
|
||||
|
||||
if (storage)
|
||||
storage->formatImpl(settings, state, frame);
|
||||
|
||||
|
@ -34,9 +34,6 @@ void ASTDictionaryAttributeDeclaration::formatImpl(const FormatSettings & settin
|
||||
{
|
||||
frame.need_parens = false;
|
||||
|
||||
if (!settings.one_line)
|
||||
settings.ostr << settings.nl_or_ws << std::string(4 * frame.indent, ' ');
|
||||
|
||||
settings.ostr << backQuote(name);
|
||||
|
||||
if (type)
|
||||
|
@ -37,14 +37,14 @@ void ASTExpressionList::formatImplMultiline(const FormatSettings & settings, For
|
||||
{
|
||||
if (separator)
|
||||
settings.ostr << separator;
|
||||
settings.ostr << ' ';
|
||||
}
|
||||
|
||||
|
||||
if (children.size() > 1)
|
||||
if (children.size() > 1 || frame.expression_list_always_start_on_new_line)
|
||||
settings.ostr << indent_str;
|
||||
|
||||
(*it)->formatImpl(settings, state, frame);
|
||||
FormatStateStacked frame_nested = frame;
|
||||
frame_nested.expression_list_always_start_on_new_line = false;
|
||||
(*it)->formatImpl(settings, state, frame_nested);
|
||||
}
|
||||
}
|
||||
|
||||
|
35
src/Parsers/ASTIndexDeclaration.cpp
Normal file
35
src/Parsers/ASTIndexDeclaration.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include <Parsers/ASTIndexDeclaration.h>
|
||||
#include <Common/quoteString.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
ASTPtr ASTIndexDeclaration::clone() const
|
||||
{
|
||||
auto res = std::make_shared<ASTIndexDeclaration>();
|
||||
|
||||
res->name = name;
|
||||
res->granularity = granularity;
|
||||
|
||||
if (expr)
|
||||
res->set(res->expr, expr->clone());
|
||||
if (type)
|
||||
res->set(res->type, type->clone());
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void ASTIndexDeclaration::formatImpl(const FormatSettings & s, FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
s.ostr << backQuoteIfNeed(name);
|
||||
s.ostr << " ";
|
||||
expr->formatImpl(s, state, frame);
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << " TYPE " << (s.hilite ? hilite_none : "");
|
||||
type->formatImpl(s, state, frame);
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << " GRANULARITY " << (s.hilite ? hilite_none : "");
|
||||
s.ostr << granularity;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Field.h>
|
||||
#include <Core/Types.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <Parsers/ASTExpressionList.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/IAST.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -27,34 +20,8 @@ public:
|
||||
/** Get the text that identifies this element. */
|
||||
String getID(char) const override { return "Index"; }
|
||||
|
||||
ASTPtr clone() const override
|
||||
{
|
||||
auto res = std::make_shared<ASTIndexDeclaration>();
|
||||
|
||||
res->name = name;
|
||||
res->granularity = granularity;
|
||||
|
||||
if (expr)
|
||||
res->set(res->expr, expr->clone());
|
||||
if (type)
|
||||
res->set(res->type, type->clone());
|
||||
return res;
|
||||
}
|
||||
|
||||
void formatImpl(const FormatSettings & s, FormatState & state, FormatStateStacked frame) const override
|
||||
{
|
||||
frame.need_parens = false;
|
||||
std::string indent_str = s.one_line ? "" : std::string(4 * frame.indent, ' ');
|
||||
|
||||
s.ostr << s.nl_or_ws << indent_str;
|
||||
s.ostr << backQuoteIfNeed(name);
|
||||
s.ostr << " ";
|
||||
expr->formatImpl(s, state, frame);
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << " TYPE " << (s.hilite ? hilite_none : "");
|
||||
type->formatImpl(s, state, frame);
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << " GRANULARITY " << (s.hilite ? hilite_none : "");
|
||||
s.ostr << granularity;
|
||||
}
|
||||
ASTPtr clone() const override;
|
||||
void formatImpl(const FormatSettings & s, FormatState & state, FormatStateStacked frame) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
33
src/Parsers/ASTNameTypePair.cpp
Normal file
33
src/Parsers/ASTNameTypePair.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include <Parsers/ASTNameTypePair.h>
|
||||
#include <Common/quoteString.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
ASTPtr ASTNameTypePair::clone() const
|
||||
{
|
||||
auto res = std::make_shared<ASTNameTypePair>(*this);
|
||||
res->children.clear();
|
||||
|
||||
if (type)
|
||||
{
|
||||
res->type = type;
|
||||
res->children.push_back(res->type);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void ASTNameTypePair::formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
|
||||
|
||||
settings.ostr << indent_str << backQuoteIfNeed(name) << ' ';
|
||||
type->formatImpl(settings, state, frame);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Parsers/IAST.h>
|
||||
#include <Common/quoteString.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -19,29 +18,10 @@ public:
|
||||
|
||||
/** Get the text that identifies this element. */
|
||||
String getID(char delim) const override { return "NameTypePair" + (delim + name); }
|
||||
|
||||
ASTPtr clone() const override
|
||||
{
|
||||
auto res = std::make_shared<ASTNameTypePair>(*this);
|
||||
res->children.clear();
|
||||
|
||||
if (type)
|
||||
{
|
||||
res->type = type;
|
||||
res->children.push_back(res->type);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
ASTPtr clone() const override;
|
||||
|
||||
protected:
|
||||
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
|
||||
{
|
||||
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
|
||||
|
||||
settings.ostr << settings.nl_or_ws << indent_str << backQuoteIfNeed(name) << " ";
|
||||
type->formatImpl(settings, state, frame);
|
||||
}
|
||||
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
};
|
||||
|
||||
|
||||
|
@ -202,6 +202,7 @@ public:
|
||||
{
|
||||
UInt8 indent = 0;
|
||||
bool need_parens = false;
|
||||
bool expression_list_always_start_on_new_line = false; /// Line feed and indent before expression list even if it's of single element.
|
||||
const IAST * current_select = nullptr;
|
||||
};
|
||||
|
||||
|
@ -157,7 +157,7 @@ bool ParserTablePropertyDeclaration::parseImpl(Pos & pos, ASTPtr & node, Expecte
|
||||
|
||||
ParserIndexDeclaration index_p;
|
||||
ParserConstraintDeclaration constraint_p;
|
||||
ParserColumnDeclaration column_p;
|
||||
ParserColumnDeclaration column_p{true, true};
|
||||
|
||||
ASTPtr new_node = nullptr;
|
||||
|
||||
@ -796,7 +796,6 @@ bool ParserCreateDictionaryQuery::parseImpl(IParser::Pos & pos, ASTPtr & node, E
|
||||
ParserDictionaryAttributeDeclarationList attributes_p;
|
||||
ParserDictionary dictionary_p;
|
||||
|
||||
|
||||
bool if_not_exists = false;
|
||||
|
||||
ASTPtr database;
|
||||
|
@ -92,7 +92,8 @@ template <typename NameParser>
|
||||
class IParserColumnDeclaration : public IParserBase
|
||||
{
|
||||
public:
|
||||
explicit IParserColumnDeclaration(bool require_type_ = true) : require_type(require_type_)
|
||||
explicit IParserColumnDeclaration(bool require_type_ = true, bool allow_null_modifiers_ = false)
|
||||
: require_type(require_type_), allow_null_modifiers(allow_null_modifiers_)
|
||||
{
|
||||
}
|
||||
|
||||
@ -104,6 +105,7 @@ protected:
|
||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||
|
||||
bool require_type = true;
|
||||
bool allow_null_modifiers = false;
|
||||
};
|
||||
|
||||
using ParserColumnDeclaration = IParserColumnDeclaration<ParserIdentifier>;
|
||||
@ -115,6 +117,8 @@ bool IParserColumnDeclaration<NameParser>::parseImpl(Pos & pos, ASTPtr & node, E
|
||||
NameParser name_parser;
|
||||
ParserIdentifierWithOptionalParameters type_parser;
|
||||
ParserKeyword s_default{"DEFAULT"};
|
||||
ParserKeyword s_null{"NULL"};
|
||||
ParserKeyword s_not{"NOT"};
|
||||
ParserKeyword s_materialized{"MATERIALIZED"};
|
||||
ParserKeyword s_alias{"ALIAS"};
|
||||
ParserKeyword s_comment{"COMMENT"};
|
||||
@ -135,6 +139,7 @@ bool IParserColumnDeclaration<NameParser>::parseImpl(Pos & pos, ASTPtr & node, E
|
||||
*/
|
||||
ASTPtr type;
|
||||
String default_specifier;
|
||||
std::optional<bool> null_modifier;
|
||||
ASTPtr default_expression;
|
||||
ASTPtr comment_expression;
|
||||
ASTPtr codec_expression;
|
||||
@ -163,6 +168,17 @@ bool IParserColumnDeclaration<NameParser>::parseImpl(Pos & pos, ASTPtr & node, E
|
||||
if (require_type && !type && !default_expression)
|
||||
return false; /// reject column name without type
|
||||
|
||||
if (type && allow_null_modifiers)
|
||||
{
|
||||
if (s_not.ignore(pos, expected))
|
||||
{
|
||||
if (!s_null.ignore(pos, expected))
|
||||
return false;
|
||||
null_modifier.emplace(false);
|
||||
}
|
||||
else if (s_null.ignore(pos, expected))
|
||||
null_modifier.emplace(true);
|
||||
}
|
||||
|
||||
if (s_comment.ignore(pos, expected))
|
||||
{
|
||||
@ -193,6 +209,8 @@ bool IParserColumnDeclaration<NameParser>::parseImpl(Pos & pos, ASTPtr & node, E
|
||||
column_declaration->children.push_back(std::move(type));
|
||||
}
|
||||
|
||||
column_declaration->null_modifier = null_modifier;
|
||||
|
||||
if (default_expression)
|
||||
{
|
||||
column_declaration->default_specifier = default_specifier;
|
||||
|
@ -26,9 +26,11 @@ SRCS(
|
||||
ASTFunctionWithKeyValueArguments.cpp
|
||||
ASTGrantQuery.cpp
|
||||
ASTIdentifier.cpp
|
||||
ASTIndexDeclaration.cpp
|
||||
ASTInsertQuery.cpp
|
||||
ASTKillQueryQuery.cpp
|
||||
ASTLiteral.cpp
|
||||
ASTNameTypePair.cpp
|
||||
ASTOptimizeQuery.cpp
|
||||
ASTOrderByElement.cpp
|
||||
ASTPartition.cpp
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "HTTPHandlerFactory.h"
|
||||
|
||||
#include <re2/stringpiece.h>
|
||||
#include <Poco/Util/LayeredConfiguration.h>
|
||||
|
||||
#include "HTTPHandler.h"
|
||||
@ -21,6 +20,9 @@ namespace ErrorCodes
|
||||
extern const int INVALID_CONFIG_PARAMETER;
|
||||
}
|
||||
|
||||
static void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IServer & server);
|
||||
static void addDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IServer & server, AsynchronousMetrics & async_metrics);
|
||||
|
||||
HTTPRequestHandlerFactoryMain::HTTPRequestHandlerFactoryMain(const std::string & name_)
|
||||
: log(&Poco::Logger::get(name_)), name(name_)
|
||||
{
|
||||
@ -65,7 +67,8 @@ HTTPRequestHandlerFactoryMain::TThis * HTTPRequestHandlerFactoryMain::addHandler
|
||||
return this;
|
||||
}
|
||||
|
||||
static inline auto createHandlersFactoryFromConfig(IServer & server, const std::string & name, const String & prefix)
|
||||
static inline auto createHandlersFactoryFromConfig(
|
||||
IServer & server, const std::string & name, const String & prefix, AsynchronousMetrics & async_metrics)
|
||||
{
|
||||
auto main_handler_factory = std::make_unique<HTTPRequestHandlerFactoryMain>(name);
|
||||
|
||||
@ -74,66 +77,46 @@ static inline auto createHandlersFactoryFromConfig(IServer & server, const std::
|
||||
|
||||
for (const auto & key : keys)
|
||||
{
|
||||
if (!startsWith(key, "rule"))
|
||||
throw Exception("Unknown element in config: " + prefix + "." + key + ", must be 'rule'", ErrorCodes::UNKNOWN_ELEMENT_IN_CONFIG);
|
||||
|
||||
if (key == "defaults")
|
||||
addDefaultHandlersFactory(*main_handler_factory, server, async_metrics);
|
||||
else if (startsWith(key, "rule"))
|
||||
{
|
||||
const auto & handler_type = server.config().getString(prefix + "." + key + ".handler.type", "");
|
||||
|
||||
if (handler_type.empty())
|
||||
throw Exception("Handler type in config is not specified here: " + prefix + "." + key + ".handler.type",
|
||||
ErrorCodes::INVALID_CONFIG_PARAMETER);
|
||||
|
||||
if (handler_type == "static")
|
||||
main_handler_factory->addHandler(createStaticHandlerFactory(server, prefix + "." + key));
|
||||
else if (handler_type == "dynamic_query_handler")
|
||||
main_handler_factory->addHandler(createDynamicHandlerFactory(server, prefix + "." + key));
|
||||
else if (handler_type == "predefined_query_handler")
|
||||
main_handler_factory->addHandler(createPredefinedHandlerFactory(server, prefix + "." + key));
|
||||
else if (handler_type.empty())
|
||||
throw Exception("Handler type in config is not specified here: " +
|
||||
prefix + "." + key + ".handler.type", ErrorCodes::INVALID_CONFIG_PARAMETER);
|
||||
else if (handler_type == "prometheus")
|
||||
main_handler_factory->addHandler(createPrometheusHandlerFactory(server, async_metrics, prefix + "." + key));
|
||||
else if (handler_type == "replicas_status")
|
||||
main_handler_factory->addHandler(createReplicasStatusHandlerFactory(server, prefix + "." + key));
|
||||
else
|
||||
throw Exception("Unknown handler type '" + handler_type +"' in config here: " +
|
||||
prefix + "." + key + ".handler.type",ErrorCodes::INVALID_CONFIG_PARAMETER);
|
||||
throw Exception("Unknown handler type '" + handler_type + "' in config here: " + prefix + "." + key + ".handler.type",
|
||||
ErrorCodes::INVALID_CONFIG_PARAMETER);
|
||||
}
|
||||
else
|
||||
throw Exception("Unknown element in config: " + prefix + "." + key + ", must be 'rule' or 'defaults'",
|
||||
ErrorCodes::UNKNOWN_ELEMENT_IN_CONFIG);
|
||||
}
|
||||
|
||||
return main_handler_factory.release();
|
||||
}
|
||||
|
||||
static const auto ping_response_expression = "Ok.\n";
|
||||
static const auto root_response_expression = "config://http_server_default_response";
|
||||
|
||||
static inline Poco::Net::HTTPRequestHandlerFactory * createHTTPHandlerFactory(
|
||||
IServer & server, const std::string & name, AsynchronousMetrics & async_metrics)
|
||||
static inline Poco::Net::HTTPRequestHandlerFactory * createHTTPHandlerFactory(IServer & server, const std::string & name, AsynchronousMetrics & async_metrics)
|
||||
{
|
||||
if (server.config().has("http_handlers"))
|
||||
return createHandlersFactoryFromConfig(server, name, "http_handlers");
|
||||
return createHandlersFactoryFromConfig(server, name, "http_handlers", async_metrics);
|
||||
else
|
||||
{
|
||||
auto factory = std::make_unique<HTTPRequestHandlerFactoryMain>(name);
|
||||
|
||||
auto root_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(server, root_response_expression);
|
||||
root_handler->attachStrictPath("/")->allowGetAndHeadRequest();
|
||||
factory->addHandler(root_handler.release());
|
||||
|
||||
auto ping_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(server, ping_response_expression);
|
||||
ping_handler->attachStrictPath("/ping")->allowGetAndHeadRequest();
|
||||
factory->addHandler(ping_handler.release());
|
||||
|
||||
auto replicas_status_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<ReplicasStatusHandler>>(server);
|
||||
replicas_status_handler->attachNonStrictPath("/replicas_status")->allowGetAndHeadRequest();
|
||||
factory->addHandler(replicas_status_handler.release());
|
||||
|
||||
auto query_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(server, "query");
|
||||
query_handler->allowPostAndGetParamsRequest();
|
||||
factory->addHandler(query_handler.release());
|
||||
|
||||
/// We check that prometheus handler will be served on current (default) port.
|
||||
/// Otherwise it will be created separately, see below.
|
||||
if (server.config().has("prometheus") && server.config().getInt("prometheus.port", 0) == 0)
|
||||
{
|
||||
auto prometheus_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(
|
||||
server, PrometheusMetricsWriter(server.config(), "prometheus", async_metrics));
|
||||
prometheus_handler->attachStrictPath(server.config().getString("prometheus.endpoint", "/metrics"))->allowGetAndHeadRequest();
|
||||
factory->addHandler(prometheus_handler.release());
|
||||
}
|
||||
|
||||
addDefaultHandlersFactory(*factory, server, async_metrics);
|
||||
return factory.release();
|
||||
}
|
||||
}
|
||||
@ -141,18 +124,7 @@ static inline Poco::Net::HTTPRequestHandlerFactory * createHTTPHandlerFactory(
|
||||
static inline Poco::Net::HTTPRequestHandlerFactory * createInterserverHTTPHandlerFactory(IServer & server, const std::string & name)
|
||||
{
|
||||
auto factory = std::make_unique<HTTPRequestHandlerFactoryMain>(name);
|
||||
|
||||
auto root_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(server, root_response_expression);
|
||||
root_handler->attachStrictPath("/")->allowGetAndHeadRequest();
|
||||
factory->addHandler(root_handler.release());
|
||||
|
||||
auto ping_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(server, ping_response_expression);
|
||||
ping_handler->attachStrictPath("/ping")->allowGetAndHeadRequest();
|
||||
factory->addHandler(ping_handler.release());
|
||||
|
||||
auto replicas_status_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<ReplicasStatusHandler>>(server);
|
||||
replicas_status_handler->attachNonStrictPath("/replicas_status")->allowGetAndHeadRequest();
|
||||
factory->addHandler(replicas_status_handler.release());
|
||||
addCommonDefaultHandlersFactory(*factory, server);
|
||||
|
||||
auto main_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<InterserverIOHTTPHandler>>(server);
|
||||
main_handler->allowPostAndGetParamsRequest();
|
||||
@ -180,4 +152,41 @@ Poco::Net::HTTPRequestHandlerFactory * createHandlerFactory(IServer & server, As
|
||||
throw Exception("LOGICAL ERROR: Unknown HTTP handler factory name.", ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
|
||||
static const auto ping_response_expression = "Ok.\n";
|
||||
static const auto root_response_expression = "config://http_server_default_response";
|
||||
|
||||
void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IServer & server)
|
||||
{
|
||||
auto root_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(server, root_response_expression);
|
||||
root_handler->attachStrictPath("/")->allowGetAndHeadRequest();
|
||||
factory.addHandler(root_handler.release());
|
||||
|
||||
auto ping_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(server, ping_response_expression);
|
||||
ping_handler->attachStrictPath("/ping")->allowGetAndHeadRequest();
|
||||
factory.addHandler(ping_handler.release());
|
||||
|
||||
auto replicas_status_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<ReplicasStatusHandler>>(server);
|
||||
replicas_status_handler->attachNonStrictPath("/replicas_status")->allowGetAndHeadRequest();
|
||||
factory.addHandler(replicas_status_handler.release());
|
||||
}
|
||||
|
||||
void addDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IServer & server, AsynchronousMetrics & async_metrics)
|
||||
{
|
||||
addCommonDefaultHandlersFactory(factory, server);
|
||||
|
||||
auto query_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(server, "query");
|
||||
query_handler->allowPostAndGetParamsRequest();
|
||||
factory.addHandler(query_handler.release());
|
||||
|
||||
/// We check that prometheus handler will be served on current (default) port.
|
||||
/// Otherwise it will be created separately, see createHandlerFactory(...).
|
||||
if (server.config().has("prometheus") && server.config().getInt("prometheus.port", 0) == 0)
|
||||
{
|
||||
auto prometheus_handler = std::make_unique<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(
|
||||
server, PrometheusMetricsWriter(server.config(), "prometheus", async_metrics));
|
||||
prometheus_handler->attachStrictPath(server.config().getString("prometheus.endpoint", "/metrics"))->allowGetAndHeadRequest();
|
||||
factory.addHandler(prometheus_handler.release());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -109,6 +109,10 @@ Poco::Net::HTTPRequestHandlerFactory * createDynamicHandlerFactory(IServer & ser
|
||||
|
||||
Poco::Net::HTTPRequestHandlerFactory * createPredefinedHandlerFactory(IServer & server, const std::string & config_prefix);
|
||||
|
||||
Poco::Net::HTTPRequestHandlerFactory * createReplicasStatusHandlerFactory(IServer & server, const std::string & config_prefix);
|
||||
|
||||
Poco::Net::HTTPRequestHandlerFactory * createPrometheusHandlerFactory(IServer & server, AsynchronousMetrics & async_metrics, const std::string & config_prefix);
|
||||
|
||||
Poco::Net::HTTPRequestHandlerFactory * createHandlerFactory(IServer & server, AsynchronousMetrics & async_metrics, const std::string & name);
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <Common/CurrentMetrics.h>
|
||||
|
||||
#include <IO/WriteBufferFromHTTPServerResponse.h>
|
||||
#include <Server/HTTPHandlerRequestFilter.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -40,4 +41,10 @@ void PrometheusRequestHandler::handleRequest(
|
||||
}
|
||||
}
|
||||
|
||||
Poco::Net::HTTPRequestHandlerFactory * createPrometheusHandlerFactory(IServer & server, AsynchronousMetrics & async_metrics, const std::string & config_prefix)
|
||||
{
|
||||
return addFiltersFromConfig(new HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>(
|
||||
server, PrometheusMetricsWriter(server.config(), config_prefix + ".handler", async_metrics)), server.config(), config_prefix);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,8 +7,11 @@
|
||||
#include <Databases/IDatabase.h>
|
||||
#include <IO/HTTPCommon.h>
|
||||
|
||||
#include <Poco/Net/HTTPRequestHandlerFactory.h>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
#include <Poco/Net/HTTPServerResponse.h>
|
||||
#include <Server/HTTPHandlerFactory.h>
|
||||
#include <Server/HTTPHandlerRequestFilter.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -104,5 +107,9 @@ void ReplicasStatusHandler::handleRequest(Poco::Net::HTTPServerRequest & request
|
||||
}
|
||||
}
|
||||
|
||||
Poco::Net::HTTPRequestHandlerFactory * createReplicasStatusHandlerFactory(IServer & server, const std::string & config_prefix)
|
||||
{
|
||||
return addFiltersFromConfig(new HandlingRuleHTTPHandlerFactory<ReplicasStatusHandler>(server), server.config(), config_prefix);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <Interpreters/misc.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/IFunction.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <Common/FieldVisitorsAccurateComparison.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Interpreters/convertFieldToType.h>
|
||||
#include <Interpreters/Set.h>
|
||||
|
@ -139,7 +139,6 @@ BlocksWithPartition MergeTreeDataWriter::splitBlockIntoParts(const Block & block
|
||||
return result;
|
||||
|
||||
data.check(block, true);
|
||||
block.checkNumberOfRows();
|
||||
|
||||
if (!data.hasPartitionKey()) /// Table is not partitioned.
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Interpreters/PreparedSets.h>
|
||||
#include <Interpreters/DatabaseAndTableWithAlias.h>
|
||||
#include <Core/SortDescription.h>
|
||||
#include <Core/Names.h>
|
||||
#include <memory>
|
||||
@ -70,6 +71,7 @@ using ReadInOrderOptimizerPtr = std::shared_ptr<const ReadInOrderOptimizer>;
|
||||
struct SelectQueryInfo
|
||||
{
|
||||
ASTPtr query;
|
||||
ASTPtr view_query; /// Optimized VIEW query
|
||||
|
||||
SyntaxAnalyzerResultPtr syntax_analyzer_result;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
#include <Interpreters/InterpreterSelectQuery.h>
|
||||
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
|
||||
#include <Interpreters/PredicateExpressionsOptimizer.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Interpreters/getTableExpressions.h>
|
||||
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/ASTSubquery.h>
|
||||
@ -30,7 +28,6 @@ namespace ErrorCodes
|
||||
{
|
||||
extern const int INCORRECT_QUERY;
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int ALIAS_REQUIRED;
|
||||
}
|
||||
|
||||
|
||||
@ -60,9 +57,12 @@ Pipes StorageView::read(
|
||||
Pipes pipes;
|
||||
|
||||
ASTPtr current_inner_query = inner_query;
|
||||
|
||||
if (context.getSettings().enable_optimize_predicate_expression)
|
||||
current_inner_query = getRuntimeViewQuery(*query_info.query->as<const ASTSelectQuery>(), context);
|
||||
if (query_info.view_query)
|
||||
{
|
||||
if (!query_info.view_query->as<ASTSelectWithUnionQuery>())
|
||||
throw Exception("Unexpected optimized VIEW query", ErrorCodes::LOGICAL_ERROR);
|
||||
current_inner_query = query_info.view_query->clone();
|
||||
}
|
||||
|
||||
InterpreterSelectWithUnionQuery interpreter(current_inner_query, context, {}, column_names);
|
||||
|
||||
@ -87,60 +87,52 @@ Pipes StorageView::read(
|
||||
return pipes;
|
||||
}
|
||||
|
||||
ASTPtr StorageView::getRuntimeViewQuery(const ASTSelectQuery & outer_query, const Context & context)
|
||||
static ASTTableExpression * getFirstTableExpression(ASTSelectQuery & select_query)
|
||||
{
|
||||
auto temp_outer_query = outer_query.clone();
|
||||
auto * new_outer_select = temp_outer_query->as<ASTSelectQuery>();
|
||||
return getRuntimeViewQuery(new_outer_select, context, false);
|
||||
}
|
||||
|
||||
|
||||
static void replaceTableNameWithSubquery(ASTSelectQuery * select_query, ASTPtr & subquery)
|
||||
{
|
||||
auto * select_element = select_query->tables()->children[0]->as<ASTTablesInSelectQueryElement>();
|
||||
auto * select_element = select_query.tables()->children[0]->as<ASTTablesInSelectQueryElement>();
|
||||
|
||||
if (!select_element->table_expression)
|
||||
throw Exception("Logical error: incorrect table expression", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
auto * table_expression = select_element->table_expression->as<ASTTableExpression>();
|
||||
return select_element->table_expression->as<ASTTableExpression>();
|
||||
}
|
||||
|
||||
void StorageView::replaceWithSubquery(ASTSelectQuery & outer_query, ASTPtr view_query, ASTPtr & view_name)
|
||||
{
|
||||
ASTTableExpression * table_expression = getFirstTableExpression(outer_query);
|
||||
|
||||
if (!table_expression->database_and_table_name)
|
||||
throw Exception("Logical error: incorrect table expression", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
const auto alias = table_expression->database_and_table_name->tryGetAlias();
|
||||
DatabaseAndTableWithAlias db_table(table_expression->database_and_table_name);
|
||||
String alias = db_table.alias.empty() ? db_table.table : db_table.alias;
|
||||
|
||||
view_name = table_expression->database_and_table_name;
|
||||
table_expression->database_and_table_name = {};
|
||||
table_expression->subquery = std::make_shared<ASTSubquery>();
|
||||
table_expression->subquery->children.push_back(subquery);
|
||||
table_expression->children.push_back(table_expression->subquery);
|
||||
if (!alias.empty())
|
||||
table_expression->subquery->children.push_back(view_query);
|
||||
table_expression->subquery->setAlias(alias);
|
||||
|
||||
for (auto & child : table_expression->children)
|
||||
if (child.get() == view_name.get())
|
||||
child = view_query;
|
||||
}
|
||||
|
||||
|
||||
ASTPtr StorageView::getRuntimeViewQuery(ASTSelectQuery * outer_query, const Context & context, bool normalize)
|
||||
ASTPtr StorageView::restoreViewName(ASTSelectQuery & select_query, const ASTPtr & view_name)
|
||||
{
|
||||
auto runtime_view_query = inner_query->clone();
|
||||
ASTTableExpression * table_expression = getFirstTableExpression(select_query);
|
||||
|
||||
/// TODO: remove getTableExpressions and getTablesWithColumns
|
||||
{
|
||||
const auto & table_expressions = getTableExpressions(*outer_query);
|
||||
const auto & tables_with_columns = getDatabaseAndTablesWithColumns(table_expressions, context);
|
||||
if (!table_expression->subquery)
|
||||
throw Exception("Logical error: incorrect table expression", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
replaceTableNameWithSubquery(outer_query, runtime_view_query);
|
||||
if (context.getSettingsRef().joined_subquery_requires_alias && tables_with_columns.size() > 1)
|
||||
{
|
||||
for (const auto & pr : tables_with_columns)
|
||||
if (pr.table.table.empty() && pr.table.alias.empty())
|
||||
throw Exception("Not unique subquery in FROM requires an alias (or joined_subquery_requires_alias=0 to disable restriction).",
|
||||
ErrorCodes::ALIAS_REQUIRED);
|
||||
}
|
||||
ASTPtr subquery = table_expression->subquery;
|
||||
table_expression->subquery = {};
|
||||
table_expression->database_and_table_name = view_name;
|
||||
|
||||
if (PredicateExpressionsOptimizer(context, tables_with_columns, context.getSettings()).optimize(*outer_query) && normalize)
|
||||
InterpreterSelectWithUnionQuery(
|
||||
runtime_view_query, context, SelectQueryOptions(QueryProcessingStage::FetchColumns).analyze().modify(), {});
|
||||
}
|
||||
|
||||
return runtime_view_query;
|
||||
for (auto & child : table_expression->children)
|
||||
if (child.get() == subquery.get())
|
||||
child = view_name;
|
||||
return subquery->children[0];
|
||||
}
|
||||
|
||||
void registerStorageView(StorageFactory & factory)
|
||||
|
@ -29,9 +29,13 @@ public:
|
||||
size_t max_block_size,
|
||||
unsigned num_streams) override;
|
||||
|
||||
ASTPtr getRuntimeViewQuery(const ASTSelectQuery & outer_query, const Context & context);
|
||||
void replaceWithSubquery(ASTSelectQuery & select_query, ASTPtr & view_name) const
|
||||
{
|
||||
replaceWithSubquery(select_query, inner_query->clone(), view_name);
|
||||
}
|
||||
|
||||
ASTPtr getRuntimeViewQuery(ASTSelectQuery * outer_query, const Context & context, bool normalize);
|
||||
static void replaceWithSubquery(ASTSelectQuery & outer_query, ASTPtr view_query, ASTPtr & view_name);
|
||||
static ASTPtr restoreViewName(ASTSelectQuery & select_query, const ASTPtr & view_name);
|
||||
|
||||
private:
|
||||
ASTPtr inner_query;
|
||||
|
@ -113,3 +113,38 @@ def test_relative_path_static_handler():
|
||||
assert 'text/html; charset=UTF-8' == cluster.instance.http_request('test_get_relative_path_static_handler', method='GET', headers={'XXX': 'xxx'}).headers['Content-Type']
|
||||
assert '<html><body>Relative Path File</body></html>\n' == cluster.instance.http_request('test_get_relative_path_static_handler', method='GET', headers={'XXX': 'xxx'}).content
|
||||
|
||||
def test_defaults_http_handlers():
|
||||
with contextlib.closing(SimpleCluster(ClickHouseCluster(__file__), "defaults_handlers", "test_defaults_handlers")) as cluster:
|
||||
assert 200 == cluster.instance.http_request('', method='GET').status_code
|
||||
assert 'Default server response' == cluster.instance.http_request('', method='GET').content
|
||||
|
||||
assert 200 == cluster.instance.http_request('ping', method='GET').status_code
|
||||
assert 'Ok.\n' == cluster.instance.http_request('ping', method='GET').content
|
||||
|
||||
assert 200 == cluster.instance.http_request('replicas_status', method='GET').status_code
|
||||
assert 'Ok.\n' == cluster.instance.http_request('replicas_status', method='GET').content
|
||||
|
||||
assert 200 == cluster.instance.http_request('?query=SELECT+1', method='GET').status_code
|
||||
assert '1\n' == cluster.instance.http_request('?query=SELECT+1', method='GET').content
|
||||
|
||||
def test_prometheus_handler():
|
||||
with contextlib.closing(SimpleCluster(ClickHouseCluster(__file__), "prometheus_handler", "test_prometheus_handler")) as cluster:
|
||||
assert 404 == cluster.instance.http_request('', method='GET', headers={'XXX': 'xxx'}).status_code
|
||||
|
||||
assert 404 == cluster.instance.http_request('test_prometheus', method='GET', headers={'XXX': 'bad'}).status_code
|
||||
|
||||
assert 404 == cluster.instance.http_request('test_prometheus', method='POST', headers={'XXX': 'xxx'}).status_code
|
||||
|
||||
assert 200 == cluster.instance.http_request('test_prometheus', method='GET', headers={'XXX': 'xxx'}).status_code
|
||||
assert 'ClickHouseProfileEvents_Query' in cluster.instance.http_request('test_prometheus', method='GET', headers={'XXX': 'xxx'}).content
|
||||
|
||||
def test_replicas_status_handler():
|
||||
with contextlib.closing(SimpleCluster(ClickHouseCluster(__file__), "replicas_status_handler", "test_replicas_status_handler")) as cluster:
|
||||
assert 404 == cluster.instance.http_request('', method='GET', headers={'XXX': 'xxx'}).status_code
|
||||
|
||||
assert 404 == cluster.instance.http_request('test_replicas_status', method='GET', headers={'XXX': 'bad'}).status_code
|
||||
|
||||
assert 404 == cluster.instance.http_request('test_replicas_status', method='POST', headers={'XXX': 'xxx'}).status_code
|
||||
|
||||
assert 200 == cluster.instance.http_request('test_replicas_status', method='GET', headers={'XXX': 'xxx'}).status_code
|
||||
assert 'Ok.\n' == cluster.instance.http_request('test_replicas_status', method='GET', headers={'XXX': 'xxx'}).content
|
||||
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<yandex>
|
||||
<http_server_default_response>Default server response</http_server_default_response>
|
||||
|
||||
<http_handlers>
|
||||
<defaults/>
|
||||
</http_handlers>
|
||||
</yandex>
|
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<yandex>
|
||||
<http_handlers>
|
||||
<rule>
|
||||
<methods>GET</methods>
|
||||
<headers><XXX>xxx</XXX></headers>
|
||||
<url>/test_prometheus</url>
|
||||
<handler>
|
||||
<type>prometheus</type>
|
||||
<events>true</events>
|
||||
<metrics>true</metrics>
|
||||
<asynchronous_metrics>true</asynchronous_metrics>
|
||||
</handler>
|
||||
</rule>
|
||||
</http_handlers>
|
||||
</yandex>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<yandex>
|
||||
<http_handlers>
|
||||
<rule>
|
||||
<methods>GET</methods>
|
||||
<headers><XXX>xxx</XXX></headers>
|
||||
<url>/test_replicas_status</url>
|
||||
<handler><type>replicas_status</type></handler>
|
||||
</rule>
|
||||
</http_handlers>
|
||||
</yandex>
|
10
tests/performance/duplicate_order_by_and_distinct.xml
Normal file
10
tests/performance/duplicate_order_by_and_distinct.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<test>
|
||||
<preconditions>
|
||||
<table_exists>hits_10m_single</table_exists>
|
||||
</preconditions>
|
||||
|
||||
<query>SELECT * FROM (SELECT CounterID, EventDate FROM hits_10m_single ORDER BY CounterID DESC) ORDER BY EventDate, CounterID FORMAT Null</query>
|
||||
<query>SELECT DISTINCT * FROM (SELECT DISTINCT CounterID, EventDate FROM hits_10m_single) FORMAT Null</query>
|
||||
<query>SELECT DISTINCT * FROM (SELECT DISTINCT CounterID, EventDate FROM hits_10m_single ORDER BY CounterID DESC) ORDER BY toStartOfWeek(EventDate) FORMAT Null</query>
|
||||
|
||||
</test>
|
@ -4,7 +4,7 @@ DROP TABLE IF EXISTS numbers2;
|
||||
CREATE TABLE numbers1 ENGINE = Memory AS SELECT number as _table FROM numbers(1000);
|
||||
CREATE TABLE numbers2 ENGINE = Memory AS SELECT number as _table FROM numbers(1000);
|
||||
|
||||
SELECT count() FROM merge(currentDatabase(), '^numbers\\d+$') WHERE _table='numbers1'; -- { serverError 43 }
|
||||
SELECT count() FROM merge(currentDatabase(), '^numbers\\d+$') WHERE _table='numbers1'; -- { serverError 53 }
|
||||
SELECT count() FROM merge(currentDatabase(), '^numbers\\d+$') WHERE _table=1;
|
||||
|
||||
DROP TABLE numbers1;
|
||||
|
@ -1,3 +1,5 @@
|
||||
0
|
||||
1
|
||||
-42 -42 1 0 0 0 1 1
|
||||
42 42 1 0 0 0 1 1
|
||||
-42 -42.42000 0 0 1 1 0 1
|
||||
|
@ -19,7 +19,7 @@ INSERT INTO decimal (a, b, c, d, e, f, g, h, i, j) VALUES (-42, -42, -42, -0.42,
|
||||
|
||||
SELECT a > toFloat64(0) FROM decimal; -- { serverError 43 }
|
||||
SELECT g > toFloat32(0) FROM decimal; -- { serverError 43 }
|
||||
SELECT a > '0.0' FROM decimal; -- { serverError 43 }
|
||||
SELECT a > '0.0' FROM decimal ORDER BY a;
|
||||
|
||||
SELECT a, b, a = b, a < b, a > b, a != b, a <= b, a >= b FROM decimal ORDER BY a;
|
||||
SELECT a, g, a = g, a < g, a > g, a != g, a <= g, a >= g FROM decimal ORDER BY a;
|
||||
|
@ -39,6 +39,6 @@ SimpleAggregateFunction(sum, Float64)
|
||||
7 14
|
||||
8 16
|
||||
9 18
|
||||
1 1 2 2.2.2.2 3 ([1,2,3],[2,1,1])
|
||||
10 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222 20 20.20.20.20 5 ([2,3,4],[2,1,1])
|
||||
SimpleAggregateFunction(anyLast, Nullable(String)) SimpleAggregateFunction(anyLast, LowCardinality(Nullable(String))) SimpleAggregateFunction(anyLast, IPv4) SimpleAggregateFunction(groupBitOr, UInt32) SimpleAggregateFunction(sumMap, Tuple(Array(Int32), Array(Int64)))
|
||||
1 1 2 2.2.2.2 3 ([1,2,3],[2,1,1]) [1,2,2,3,4] [4,2,1,3]
|
||||
10 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222 20 20.20.20.20 5 ([2,3,4],[2,1,1]) [] []
|
||||
SimpleAggregateFunction(anyLast, Nullable(String)) SimpleAggregateFunction(anyLast, LowCardinality(Nullable(String))) SimpleAggregateFunction(anyLast, IPv4) SimpleAggregateFunction(groupBitOr, UInt32) SimpleAggregateFunction(sumMap, Tuple(Array(Int32), Array(Int64))) SimpleAggregateFunction(groupArrayArray, Array(Int32)) SimpleAggregateFunction(groupUniqArrayArray, Array(Int32))
|
||||
|
@ -25,16 +25,18 @@ create table simple (
|
||||
low_str SimpleAggregateFunction(anyLast,LowCardinality(Nullable(String))),
|
||||
ip SimpleAggregateFunction(anyLast,IPv4),
|
||||
status SimpleAggregateFunction(groupBitOr, UInt32),
|
||||
tup SimpleAggregateFunction(sumMap, Tuple(Array(Int32), Array(Int64)))
|
||||
tup SimpleAggregateFunction(sumMap, Tuple(Array(Int32), Array(Int64))),
|
||||
arr SimpleAggregateFunction(groupArrayArray, Array(Int32)),
|
||||
uniq_arr SimpleAggregateFunction(groupUniqArrayArray, Array(Int32))
|
||||
) engine=AggregatingMergeTree order by id;
|
||||
insert into simple values(1,'1','1','1.1.1.1', 1, ([1,2], [1,1]));
|
||||
insert into simple values(1,null,'2','2.2.2.2', 2, ([1,3], [1,1]));
|
||||
insert into simple values(1,'1','1','1.1.1.1', 1, ([1,2], [1,1]), [1,2], [1,2]);
|
||||
insert into simple values(1,null,'2','2.2.2.2', 2, ([1,3], [1,1]), [2,3,4], [2,3,4]);
|
||||
-- String longer then MAX_SMALL_STRING_SIZE (actual string length is 100)
|
||||
insert into simple values(10,'10','10','10.10.10.10', 4, ([2,3], [1,1]));
|
||||
insert into simple values(10,'2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222','20','20.20.20.20', 1, ([2, 4], [1,1]));
|
||||
insert into simple values(10,'10','10','10.10.10.10', 4, ([2,3], [1,1]), [], []);
|
||||
insert into simple values(10,'2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222','20','20.20.20.20', 1, ([2, 4], [1,1]), [], []);
|
||||
|
||||
select * from simple final order by id;
|
||||
select toTypeName(nullable_str),toTypeName(low_str),toTypeName(ip),toTypeName(status), toTypeName(tup) from simple limit 1;
|
||||
select toTypeName(nullable_str),toTypeName(low_str),toTypeName(ip),toTypeName(status), toTypeName(tup), toTypeName(arr), toTypeName(uniq_arr) from simple limit 1;
|
||||
|
||||
optimize table simple final;
|
||||
|
||||
|
@ -43,7 +43,7 @@ SELECT COUNT() FROM bloom_filter_types_test WHERE f32 = 1 SETTINGS max_rows_to_r
|
||||
SELECT COUNT() FROM bloom_filter_types_test WHERE f64 = 1 SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_types_test WHERE date = '1970-01-02' SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_types_test WHERE date_time = toDateTime('1970-01-01 03:00:01', 'Europe/Moscow') SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_types_test WHERE str = '1' SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_types_test WHERE str = '1' SETTINGS max_rows_to_read = 12;
|
||||
SELECT COUNT() FROM bloom_filter_types_test WHERE fixed_string = toFixedString('1', 5) SETTINGS max_rows_to_read = 12;
|
||||
|
||||
SELECT COUNT() FROM bloom_filter_types_test WHERE str IN ( SELECT str FROM bloom_filter_types_test);
|
||||
@ -122,7 +122,7 @@ SELECT COUNT() FROM bloom_filter_null_types_test WHERE f32 = 1 SETTINGS max_rows
|
||||
SELECT COUNT() FROM bloom_filter_null_types_test WHERE f64 = 1 SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_null_types_test WHERE date = '1970-01-02' SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_null_types_test WHERE date_time = toDateTime('1970-01-01 03:00:01', 'Europe/Moscow') SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_null_types_test WHERE str = '1' SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_null_types_test WHERE str = '1' SETTINGS max_rows_to_read = 12;
|
||||
SELECT COUNT() FROM bloom_filter_null_types_test WHERE fixed_string = toFixedString('1', 5) SETTINGS max_rows_to_read = 12;
|
||||
|
||||
SELECT COUNT() FROM bloom_filter_null_types_test WHERE isNull(i8);
|
||||
@ -150,7 +150,7 @@ CREATE TABLE bloom_filter_lc_null_types_test (order_key UInt64, str LowCardinali
|
||||
INSERT INTO bloom_filter_lc_null_types_test SELECT number AS order_key, toString(number) AS str, toFixedString(toString(number), 5) AS fixed_string FROM system.numbers LIMIT 100;
|
||||
INSERT INTO bloom_filter_lc_null_types_test SELECT 0 AS order_key, NULL AS str, NULL AS fixed_string;
|
||||
|
||||
SELECT COUNT() FROM bloom_filter_lc_null_types_test WHERE str = '1' SETTINGS max_rows_to_read = 6;
|
||||
SELECT COUNT() FROM bloom_filter_lc_null_types_test WHERE str = '1' SETTINGS max_rows_to_read = 12;
|
||||
SELECT COUNT() FROM bloom_filter_lc_null_types_test WHERE fixed_string = toFixedString('1', 5) SETTINGS max_rows_to_read = 12;
|
||||
|
||||
SELECT COUNT() FROM bloom_filter_lc_null_types_test WHERE isNull(str);
|
||||
|
@ -1,4 +1,4 @@
|
||||
SELECT \n date, \n id, \n name, \n value\nFROM \n(\n SELECT \n date, \n id, \n name, \n value\n FROM default.test\n WHERE id = 1\n)\nWHERE id = 1
|
||||
SELECT \n date, \n id, \n name, \n value\nFROM \n(\n SELECT \n date, \n id, \n name, \n value\n FROM default.test\n WHERE id = 2\n)\nWHERE id = 2
|
||||
SELECT id\nFROM \n(\n SELECT \n date, \n id, \n name, \n value\n FROM default.test\n WHERE id = 1\n)\nWHERE id = 1
|
||||
SELECT id\nFROM \n(\n SELECT \n date, \n id, \n name, \n value\n FROM default.test\n WHERE id = 1\n) AS s\nWHERE id = 1
|
||||
SELECT \n date,\n id,\n name,\n value\nFROM \n(\n SELECT *\n FROM default.test\n HAVING id = 1\n) AS test_view\nWHERE id = 1
|
||||
SELECT \n date,\n id,\n name,\n value\nFROM \n(\n SELECT *\n FROM default.test\n HAVING id = 2\n) AS test_view\nWHERE id = 2
|
||||
SELECT id\nFROM \n(\n SELECT *\n FROM default.test\n HAVING id = 1\n) AS test_view\nWHERE id = 1
|
||||
SELECT id\nFROM \n(\n SELECT *\n FROM default.test\n HAVING id = 1\n) AS s\nWHERE id = 1
|
||||
|
@ -0,0 +1,3 @@
|
||||
1 1 1
|
||||
2 2 0
|
||||
1 val11 val21 val31
|
@ -0,0 +1,35 @@
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
DROP TABLE IF EXISTS t3;
|
||||
DROP TABLE IF EXISTS view1;
|
||||
|
||||
CREATE TABLE t1 (id UInt32, value1 String) ENGINE MergeTree() ORDER BY id;
|
||||
CREATE TABLE t2 (id UInt32, value2 String) ENGINE MergeTree() ORDER BY id;
|
||||
CREATE TABLE t3 (id UInt32, value3 String) ENGINE MergeTree() ORDER BY id;
|
||||
|
||||
INSERT INTO t1 (id, value1) VALUES (1, 'val11');
|
||||
INSERT INTO t2 (id, value2) VALUES (1, 'val21');
|
||||
INSERT INTO t3 (id, value3) VALUES (1, 'val31');
|
||||
|
||||
SET multiple_joins_rewriter_version = 2;
|
||||
SET enable_optimize_predicate_expression = 1;
|
||||
|
||||
SELECT t1.id, t2.id as id, t3.id as value
|
||||
FROM (select number as id, 42 as value from numbers(4)) t1
|
||||
LEFT JOIN (select number as id, 42 as value from numbers(3)) t2 ON t1.id = t2.id
|
||||
LEFT JOIN (select number as id, 42 as value from numbers(2)) t3 ON t1.id = t3.id
|
||||
WHERE id > 0 AND value < 42;
|
||||
|
||||
CREATE VIEW IF NOT EXISTS view1 AS
|
||||
SELECT t1.id AS id, t1.value1 AS value1, t2.value2 AS value2, t3.value3 AS value3
|
||||
FROM t1
|
||||
LEFT JOIN t2 ON t1.id = t2.id
|
||||
LEFT JOIN t3 ON t1.id = t3.id
|
||||
WHERE t1.id > 0;
|
||||
|
||||
SELECT * FROM view1 WHERE id = 1;
|
||||
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
DROP TABLE IF EXISTS t3;
|
||||
DROP TABLE IF EXISTS view1;
|
@ -1,12 +1,12 @@
|
||||
-- Error cases:
|
||||
-- non-const string column
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT DT64 = materialize(S); -- {serverError 44}
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT materialize(S) = toDateTime64(S, 3); -- {serverError 44}
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT * WHERE DT64 = materialize(S); -- {serverError 44}
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT * WHERE materialize(S) = DT64; -- {serverError 44}
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT DT64 = materialize(S); -- {serverError 43}
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT materialize(S) = toDateTime64(S, 3); -- {serverError 43}
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT * WHERE DT64 = materialize(S); -- {serverError 43}
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT * WHERE materialize(S) = DT64; -- {serverError 43}
|
||||
|
||||
SELECT * WHERE toDateTime64(123.345, 3) == 'ABCD'; -- {serverError 131} -- invalid DateTime64 string
|
||||
SELECT * WHERE toDateTime64(123.345, 3) == '2020-02-05 14:34:12.33333333333333333333333333333333333333333333333333333333'; -- {serverError 131} -- invalid string length
|
||||
SELECT * WHERE toDateTime64(123.345, 3) == 'ABCD'; -- {serverError 53} -- invalid DateTime64 string
|
||||
SELECT * WHERE toDateTime64(123.345, 3) == '2020-02-05 14:34:12.33333333333333333333333333333333333333333333333333333333'; -- {serverError 53} -- invalid string length
|
||||
|
||||
SELECT 'in SELECT';
|
||||
WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT DT64 = S;
|
||||
|
@ -0,0 +1,4 @@
|
||||
Nullable(Int32) Int32 Nullable(Int32) Int32
|
||||
CREATE TABLE default.data_null\n(\n `a` Nullable(Int32),\n `b` Int32,\n `c` Nullable(Int32),\n `d` Int32\n)\nENGINE = Memory()
|
||||
Nullable(Int32) Int32 Nullable(Int32) Nullable(Int32)
|
||||
CREATE TABLE default.set_null\n(\n `a` Nullable(Int32),\n `b` Int32,\n `c` Nullable(Int32),\n `d` Nullable(Int32)\n)\nENGINE = Memory()
|
50
tests/queries/0_stateless/01269_create_with_null.sql
Normal file
50
tests/queries/0_stateless/01269_create_with_null.sql
Normal file
@ -0,0 +1,50 @@
|
||||
DROP TABLE IF EXISTS data_null;
|
||||
DROP TABLE IF EXISTS set_null;
|
||||
|
||||
SET data_type_default_nullable='false';
|
||||
|
||||
CREATE TABLE data_null (
|
||||
a INT NULL,
|
||||
b INT NOT NULL,
|
||||
c Nullable(INT),
|
||||
d INT
|
||||
) engine=Memory();
|
||||
|
||||
|
||||
INSERT INTO data_null VALUES (NULL, 2, NULL, 4);
|
||||
|
||||
SELECT toTypeName(a), toTypeName(b), toTypeName(c), toTypeName(d) FROM data_null;
|
||||
|
||||
SHOW CREATE TABLE data_null;
|
||||
|
||||
CREATE TABLE data_null_error (
|
||||
a Nullable(INT) NULL,
|
||||
b INT NOT NULL,
|
||||
c Nullable(INT)
|
||||
) engine=Memory(); --{serverError 377}
|
||||
|
||||
|
||||
CREATE TABLE data_null_error (
|
||||
a INT NULL,
|
||||
b Nullable(INT) NOT NULL,
|
||||
c Nullable(INT)
|
||||
) engine=Memory(); --{serverError 377}
|
||||
|
||||
SET data_type_default_nullable='true';
|
||||
|
||||
CREATE TABLE set_null (
|
||||
a INT NULL,
|
||||
b INT NOT NULL,
|
||||
c Nullable(INT),
|
||||
d INT
|
||||
) engine=Memory();
|
||||
|
||||
|
||||
INSERT INTO set_null VALUES (NULL, 2, NULL, NULL);
|
||||
|
||||
SELECT toTypeName(a), toTypeName(b), toTypeName(c), toTypeName(d) FROM set_null;
|
||||
|
||||
SHOW CREATE TABLE set_null;
|
||||
|
||||
DROP TABLE data_null;
|
||||
DROP TABLE set_null;
|
@ -1 +1 @@
|
||||
SELECT -2487, globalNullIn(toIntervalMinute(-88074), 'qEkek..'), [-27.537293]; -- { serverError 70 }
|
||||
SELECT -2487, globalNullIn(toIntervalMinute(-88074), 'qEkek..'), [-27.537293]; -- { serverError 53 }
|
||||
|
@ -0,0 +1,14 @@
|
||||
SELECT number\nFROM \n(\n SELECT number\n FROM \n (\n SELECT DISTINCT number\n FROM numbers(3)\n )\n)\nORDER BY number ASC
|
||||
0
|
||||
1
|
||||
2
|
||||
SELECT DISTINCT number\nFROM \n(\n SELECT DISTINCT number\n FROM \n (\n SELECT DISTINCT number\n FROM numbers(3)\n ORDER BY number ASC\n )\n ORDER BY number ASC\n)\nORDER BY number ASC
|
||||
0
|
||||
1
|
||||
2
|
||||
SELECT number\nFROM \n(\n SELECT DISTINCT number\n FROM \n (\n SELECT DISTINCT number % 2 AS number\n FROM numbers(3)\n )\n)\nORDER BY number ASC
|
||||
0
|
||||
1
|
||||
SELECT DISTINCT number\nFROM \n(\n SELECT DISTINCT number\n FROM \n (\n SELECT DISTINCT number % 2 AS number\n FROM numbers(3)\n ORDER BY number ASC\n )\n ORDER BY number ASC\n)\nORDER BY number ASC
|
||||
0
|
||||
1
|
@ -0,0 +1,124 @@
|
||||
set enable_debug_queries = 1;
|
||||
set optimize_duplicate_order_by_and_distinct = 1;
|
||||
|
||||
analyze SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM numbers(3)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number;
|
||||
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM numbers(3)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number;
|
||||
|
||||
set optimize_duplicate_order_by_and_distinct = 0;
|
||||
|
||||
analyze SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM numbers(3)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number;
|
||||
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM numbers(3)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number;
|
||||
|
||||
set optimize_duplicate_order_by_and_distinct = 1;
|
||||
|
||||
analyze SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT number % 2
|
||||
AS number
|
||||
FROM numbers(3)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number;
|
||||
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT number % 2
|
||||
AS number
|
||||
FROM numbers(3)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number;
|
||||
|
||||
set optimize_duplicate_order_by_and_distinct = 0;
|
||||
|
||||
analyze SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT number % 2
|
||||
AS number
|
||||
FROM numbers(3)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number;
|
||||
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT *
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT number % 2
|
||||
AS number
|
||||
FROM numbers(3)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number
|
||||
)
|
||||
ORDER BY number;
|
@ -0,0 +1,2 @@
|
||||
0
|
||||
0
|
@ -0,0 +1,20 @@
|
||||
set optimize_duplicate_order_by_and_distinct = 1;
|
||||
SELECT DISTINCT number
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT number
|
||||
FROM remote('127.0.0.{1,2}', system.numbers)
|
||||
LIMIT 1
|
||||
SETTINGS distributed_group_by_no_merge = 1
|
||||
);
|
||||
|
||||
set optimize_duplicate_order_by_and_distinct = 0;
|
||||
SELECT DISTINCT number
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT number
|
||||
FROM remote('127.0.0.{1,2}', system.numbers)
|
||||
LIMIT 1
|
||||
SETTINGS distributed_group_by_no_merge = 1
|
||||
);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user