mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 16:50:48 +00:00
Merge branch 'master' into feature/suggestions
This commit is contained in:
commit
35c0ab9783
@ -1,11 +1,4 @@
|
||||
en:
|
||||
|
||||
## Improvements:
|
||||
* `clickhouse-client`: option --ask-password for interactively ask for credentials #1044
|
||||
# en:
|
||||
|
||||
|
||||
|
||||
ru:
|
||||
|
||||
## Улучшения:
|
||||
* `clickhouse-client`: опция --ask-password для интерактивного ввода пароля #1044
|
||||
# ru:
|
||||
|
@ -1,3 +1,73 @@
|
||||
# ClickHouse release 1.1.54388, 2018-06-28
|
||||
|
||||
## Новые возможности:
|
||||
* Добавлена поддержка запроса `ALTER TABLE t DELETE WHERE` для реплицированных таблиц и таблица `system.mutations`.
|
||||
* Добавлена поддержка запроса `ALTER TABLE t [REPLACE|ATTACH] PARTITION` для *MergeTree-таблиц.
|
||||
* Добавлена поддержка запроса `TRUNCATE TABLE` ([Winter Zhang](https://github.com/yandex/ClickHouse/pull/2260))
|
||||
* Добавлено несколько новых `SYSTEM`-запросов для реплицированных таблиц (`RESTART REPLICAS`, `SYNC REPLICA`, `[STOP|START] [MERGES|FETCHES|REPLICATED SENDS|REPLICATION QUEUES]`).
|
||||
* Добавлена возможность записи в таблицу с движком MySQL и соответствующую табличную функцию ([sundy-li](https://github.com/yandex/ClickHouse/pull/2294)).
|
||||
* Добавлена табличная функция `url()` и движок таблиц `URL` ([Александр Сапин](https://github.com/yandex/ClickHouse/pull/2501)).
|
||||
* Добавлена агрегатная функция `windowFunnel` ([sundy-li](https://github.com/yandex/ClickHouse/pull/2352)).
|
||||
* Добавлены функции `startsWith` и `endsWith` для строк ([Вадим Плахтинский](https://github.com/yandex/ClickHouse/pull/2429)).
|
||||
* В табличной функции `numbers()` добавлена возможность указывать offset ([Winter Zhang](https://github.com/yandex/ClickHouse/pull/2535)).
|
||||
* Добавлена возможность интерактивного ввода пароля в `clickhouse-client`.
|
||||
* Добавлена возможность отправки логов сервера в syslog ([Александр Крашенинников](https://github.com/yandex/ClickHouse/pull/2459)).
|
||||
* Добавлена поддержка логирования в словарях с источником shared library ([Александр Сапин](https://github.com/yandex/ClickHouse/pull/2472)).
|
||||
* Добавлена поддержка произвольного разделителя в формате CSV ([Иван Жуков](https://github.com/yandex/ClickHouse/pull/2263))
|
||||
* Добавлена настройка `date_time_input_format`. Если переключить эту настройку в значение `'best_effort'`, значения DateTime будут читаться в широком диапазоне форматов.
|
||||
* Добавлена утилита `clickhouse-obfuscator` для обфускации данных. Пример использования: публикация данных, используемых в тестах производительности.
|
||||
|
||||
## Экспериментальные возможности:
|
||||
* Добавлена возможность вычислять аргументы функции `and` только там, где они нужны ([Анастасия Царькова](https://github.com/yandex/ClickHouse/pull/2272))
|
||||
* Добавлена возможность JIT-компиляции в нативный код некоторых выражений ([pyos](https://github.com/yandex/ClickHouse/pull/2277)).
|
||||
|
||||
## Исправление ошибок:
|
||||
* Исправлено появление дублей в запросе с `DISTINCT` и `ORDER BY`.
|
||||
* Запросы с `ARRAY JOIN` и `arrayFilter` раньше возвращали некорректный результат.
|
||||
* Исправлена ошибка при чтении столбца-массива из Nested-структуры ([#2066](https://github.com/yandex/ClickHouse/issues/2066)).
|
||||
* Исправлена ошибка при анализе запросов с секцией HAVING вида `HAVING tuple IN (...)`.
|
||||
* Исправлена ошибка при анализе запросов с рекурсивными алиасами.
|
||||
* Исправлена ошибка при чтении из ReplacingMergeTree с условием в PREWHERE, фильтрующим все строки ([#2525](https://github.com/yandex/ClickHouse/issues/2525)).
|
||||
* Настройки профиля пользователя не применялись при использовании сессий в HTTP-интерфейсе.
|
||||
* Исправлено применение настроек из параметров командной строки в программе clickhouse-local.
|
||||
* Клиентская библиотека ZooKeeper теперь использует таймаут сессии, полученный от сервера.
|
||||
* Исправлена ошибка в клиентской библиотеке ZooKeeper, из-за которой ожидание ответа от сервера могло длиться дольше таймаута.
|
||||
* Исправлено отсечение ненужных кусков при запросе с условием на столбцы ключа партиционирования ([#2342](https://github.com/yandex/ClickHouse/issues/2342)).
|
||||
* После `CLEAR COLUMN IN PARTITION` в соответствующей партиции теперь возможны слияния ([#2315](https://github.com/yandex/ClickHouse/issues/2315)).
|
||||
* Исправлено соответствие типов в табличной функции ODBC ([sundy-li](https://github.com/yandex/ClickHouse/pull/2268)).
|
||||
* Исправлено некорректное сравнение типов `DateTime` с таймзоной и без неё ([Александр Бочаров](https://github.com/yandex/ClickHouse/pull/2400)).
|
||||
* Исправлен синтаксический разбор и форматирование оператора `CAST`.
|
||||
* Исправлена вставка в материализованное представление в случае, если движок таблицы представления - Distributed ([Babacar Diassé](https://github.com/yandex/ClickHouse/pull/2411)).
|
||||
* Исправлен race condition при записи данных из движка `Kafka` в материализованные представления ([Yangkuan Liu](https://github.com/yandex/ClickHouse/pull/2448)).
|
||||
* Исправлена SSRF в табличной функции remote().
|
||||
* Исправлен выход из `clickhouse-client` в multiline-режиме ([#2510](https://github.com/yandex/ClickHouse/issues/2510)).
|
||||
|
||||
## Улучшения:
|
||||
* Фоновые задачи в реплицированных таблицах теперь выполняются не в отдельных потоках, а в пуле потоков ([Silviu Caragea](https://github.com/yandex/ClickHouse/pull/1722))
|
||||
* Улучшена производительность разжатия LZ4.
|
||||
* Ускорен анализ запроса с большим числом JOIN-ов и подзапросов.
|
||||
* DNS-кэш теперь автоматически обновляется при большом числе сетевых ошибок.
|
||||
* Вставка в таблицу теперь не происходит, если вставка в одно из её материализованных представлений невозможна из-за того, что в нём много кусков.
|
||||
* Исправлено несоответствие в значениях счётчиков событий `Query`, `SelectQuery`, `InsertQuery`.
|
||||
* Разрешены выражения вида `tuple IN (SELECT tuple)`, если типы кортежей совпадают.
|
||||
* Сервер с реплицированными таблицами теперь может стартовать, даже если не сконфигурирован ZooKeeper.
|
||||
* При расчёте количества доступных ядер CPU теперь учитываются ограничения cgroups ([Atri Sharma](https://github.com/yandex/ClickHouse/pull/2325)).
|
||||
* Добавлен chown директорий конфигов в конфигурационном файле systemd ([Михаил Ширяев](https://github.com/yandex/ClickHouse/pull/2421)).
|
||||
|
||||
## Изменения сборки:
|
||||
* Добавлена возможность сборки компилятором gcc8.
|
||||
* Добавлена возможность сборки llvm из submodule.
|
||||
* Используемая версия библиотеки librdkafka обновлена до v0.11.4.
|
||||
* Добавлена возможность использования библиотеки libcpuid из системы, используемая версия библиотеки обновлена до 0.4.0.
|
||||
* Исправлена сборка с использованием библиотеки vectorclass ([Babacar Diassé](https://github.com/yandex/ClickHouse/pull/2274)).
|
||||
* Cmake теперь по умолчанию генерирует файлы для ninja (как при использовании `-G Ninja`).
|
||||
* Добавлена возможность использования библиотеки libtinfo вместо libtermcap ([Георгий Кондратьев](https://github.com/yandex/ClickHouse/pull/2519)).
|
||||
* Исправлен конфликт заголовочных файлов в Fedora Rawhide ([#2520](https://github.com/yandex/ClickHouse/issues/2520)).
|
||||
|
||||
## Обратно несовместимые изменения:
|
||||
* Убран escaping в форматах `Vertical` и `Pretty*`, удалён формат `VerticalRaw`.
|
||||
|
||||
|
||||
# ClickHouse release 1.1.54385, 2018-06-01
|
||||
## Исправление ошибок:
|
||||
* Исправлена ошибка, которая в некоторых случаях приводила к блокировке операций с ZooKeeper.
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## Technical info
|
||||
Developer guide for writing code for ClickHouse is published on official website alongside the usage and operations documentation:
|
||||
https://clickhouse.yandex/docs/en/development/index.html
|
||||
https://clickhouse.yandex/docs/en/development/architecture/
|
||||
|
||||
## Legal info
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This strings autochanged from release_lib.sh:
|
||||
set(VERSION_DESCRIBE v1.1.54387-testing)
|
||||
set(VERSION_REVISION 54387)
|
||||
set(VERSION_GITHASH 7ce4ebf1e1a5fefd3161b6f615eec0730d75ec34)
|
||||
set(VERSION_DESCRIBE v1.1.54388-testing)
|
||||
set(VERSION_REVISION 54388)
|
||||
set(VERSION_GITHASH 2447755700f40af317cb80ba8800b94d6350d148)
|
||||
# end of autochange
|
||||
|
||||
set (VERSION_MAJOR 1)
|
||||
|
@ -87,6 +87,7 @@ namespace DB
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int NOT_IMPLEMENTED;
|
||||
extern const int CANNOT_SEEK_THROUGH_FILE;
|
||||
}
|
||||
|
||||
@ -503,8 +504,11 @@ private:
|
||||
CodePoint readCodePoint(const char *& pos, const char * end)
|
||||
{
|
||||
size_t length = UTF8::seqLength(*pos);
|
||||
|
||||
if (pos + length > end)
|
||||
length = end - pos;
|
||||
if (length > sizeof(CodePoint))
|
||||
length = sizeof(CodePoint);
|
||||
|
||||
CodePoint res = 0;
|
||||
memcpy(&res, pos, length);
|
||||
@ -679,7 +683,7 @@ public:
|
||||
}
|
||||
|
||||
if (table.end() == it)
|
||||
throw Exception("Logical error in markov model");
|
||||
throw Exception("Logical error in markov model", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
size_t offset_from_begin_of_string = pos - data;
|
||||
size_t determinator_sliding_window_size = params.determinator_sliding_window_size;
|
||||
@ -700,7 +704,8 @@ public:
|
||||
/// If string is greater than desired_size, increase probability of end.
|
||||
double end_probability_multiplier = 0;
|
||||
Int64 num_bytes_after_desired_size = (pos - data) - desired_size;
|
||||
if (num_bytes_after_desired_size)
|
||||
|
||||
if (num_bytes_after_desired_size > 0)
|
||||
end_probability_multiplier = std::pow(1.25, num_bytes_after_desired_size);
|
||||
|
||||
CodePoint code = it->second.sample(determinator, end_probability_multiplier);
|
||||
@ -708,6 +713,14 @@ public:
|
||||
if (code == END)
|
||||
break;
|
||||
|
||||
if (num_bytes_after_desired_size > 0)
|
||||
{
|
||||
/// Heuristic: break at ASCII non-alnum code point.
|
||||
/// This allows to be close to desired_size but not break natural looking words.
|
||||
if (code < 128 && !isAlphaNumericASCII(code))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!writeCodePoint(code, pos, end))
|
||||
break;
|
||||
|
||||
@ -881,7 +894,7 @@ public:
|
||||
if (auto type = typeid_cast<const DataTypeNullable *>(&data_type))
|
||||
return std::make_unique<NullableModel>(get(*type->getNestedType(), seed, markov_model_params));
|
||||
|
||||
throw Exception("Unsupported data type");
|
||||
throw Exception("Unsupported data type", ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -421,14 +421,26 @@ void HTTPHandler::processQuery(
|
||||
|
||||
std::unique_ptr<ReadBuffer> in;
|
||||
|
||||
// Used in case of POST request with form-data, but it not to be expectd to be deleted after that scope
|
||||
std::string full_query;
|
||||
|
||||
/// Support for "external data for query processing".
|
||||
if (startsWith(request.getContentType().data(), "multipart/form-data"))
|
||||
{
|
||||
in = std::move(in_param);
|
||||
ExternalTablesHandler handler(context, params);
|
||||
|
||||
/// Params are of both form params POST and uri (GET params)
|
||||
params.load(request, istr, handler);
|
||||
|
||||
for (const auto & it : params)
|
||||
{
|
||||
if (it.first == "query")
|
||||
{
|
||||
full_query += it.second;
|
||||
}
|
||||
}
|
||||
in = std::make_unique<ReadBufferFromString>(full_query);
|
||||
|
||||
/// Erase unneeded parameters to avoid confusing them later with context settings or query
|
||||
/// parameters.
|
||||
for (const auto & it : handler.names)
|
||||
|
@ -42,13 +42,14 @@ bool BackgroundSchedulePool::TaskInfo::schedule()
|
||||
|
||||
scheduled = true;
|
||||
|
||||
if (!executing)
|
||||
{
|
||||
if (delayed)
|
||||
pool.cancelDelayedTask(shared_from_this(), lock);
|
||||
if (delayed)
|
||||
pool.cancelDelayedTask(shared_from_this(), lock);
|
||||
|
||||
/// If the task is not executing at the moment, enqueue it for immediate execution.
|
||||
/// But if it is currently executing, do nothing because it will be enqueued
|
||||
/// at the end of the execute() method.
|
||||
if (!executing)
|
||||
pool.queue.enqueueNotification(new TaskNotification(shared_from_this()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -123,7 +124,6 @@ void BackgroundSchedulePool::TaskInfo::execute()
|
||||
if (scheduled)
|
||||
pool.queue.enqueueNotification(new TaskNotification(shared_from_this()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
zkutil::WatchCallback BackgroundSchedulePool::TaskInfo::getWatchCallback()
|
||||
|
@ -70,6 +70,9 @@ public:
|
||||
std::mutex exec_mutex;
|
||||
std::mutex schedule_mutex;
|
||||
|
||||
/// Invariants:
|
||||
/// * If deactivated is true then scheduled, delayed and executing are all false.
|
||||
/// * scheduled and delayed cannot be true at the same time.
|
||||
bool deactivated = false;
|
||||
bool scheduled = false;
|
||||
bool delayed = false;
|
||||
|
@ -54,7 +54,7 @@ struct RadixSortFloatTransform
|
||||
|
||||
static KeyBits forward(KeyBits x)
|
||||
{
|
||||
return x ^ (-(x >> (sizeof(KeyBits) * 8 - 1) | (KeyBits(1) << (sizeof(KeyBits) * 8 - 1))));
|
||||
return x ^ ((-(x >> (sizeof(KeyBits) * 8 - 1))) | (KeyBits(1) << (sizeof(KeyBits) * 8 - 1)));
|
||||
}
|
||||
|
||||
static KeyBits backward(KeyBits x)
|
||||
|
@ -109,7 +109,8 @@ void ReplacingSortedBlockInputStream::merge(MutableColumns & merged_columns, std
|
||||
}
|
||||
|
||||
/// We will write the data for the last primary key.
|
||||
insertRow(merged_columns, merged_rows);
|
||||
if (!selected_row.empty())
|
||||
insertRow(merged_columns, merged_rows);
|
||||
|
||||
finished = true;
|
||||
}
|
||||
|
@ -1304,11 +1304,9 @@ DataTypePtr FunctionArrayEnumerateUniq::getReturnTypeImpl(const DataTypes & argu
|
||||
|
||||
void FunctionArrayEnumerateUniq::executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/)
|
||||
{
|
||||
Columns array_columns(arguments.size());
|
||||
const ColumnArray::Offsets * offsets = nullptr;
|
||||
ColumnRawPtrs data_columns(arguments.size());
|
||||
ColumnRawPtrs original_data_columns(arguments.size());
|
||||
ColumnRawPtrs null_maps(arguments.size());
|
||||
ColumnRawPtrs data_columns;
|
||||
data_columns.reserve(arguments.size());
|
||||
|
||||
bool has_nullable_columns = false;
|
||||
|
||||
@ -1327,7 +1325,7 @@ void FunctionArrayEnumerateUniq::executeImpl(Block & block, const ColumnNumbers
|
||||
array_ptr = const_array->convertToFullColumn();
|
||||
array = checkAndGetColumn<ColumnArray>(array_ptr.get());
|
||||
}
|
||||
array_columns[i] = array_ptr;
|
||||
|
||||
const ColumnArray::Offsets & offsets_i = array->getOffsets();
|
||||
if (i == 0)
|
||||
offsets = &offsets_i;
|
||||
@ -1335,7 +1333,22 @@ void FunctionArrayEnumerateUniq::executeImpl(Block & block, const ColumnNumbers
|
||||
throw Exception("Lengths of all arrays passed to " + getName() + " must be equal.",
|
||||
ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH);
|
||||
|
||||
data_columns[i] = &array->getData();
|
||||
auto * array_data = &array->getData();
|
||||
if (auto * tuple_column = checkAndGetColumn<ColumnTuple>(array_data))
|
||||
{
|
||||
for (const auto & element : tuple_column->getColumns())
|
||||
data_columns.push_back(element.get());
|
||||
}
|
||||
else
|
||||
data_columns.push_back(array_data);
|
||||
}
|
||||
|
||||
size_t num_columns = data_columns.size();
|
||||
ColumnRawPtrs original_data_columns(num_columns);
|
||||
ColumnRawPtrs null_maps(num_columns);
|
||||
|
||||
for (size_t i = 0; i < num_columns; ++i)
|
||||
{
|
||||
original_data_columns[i] = data_columns[i];
|
||||
|
||||
if (data_columns[i]->isColumnNullable())
|
||||
@ -1349,7 +1362,7 @@ void FunctionArrayEnumerateUniq::executeImpl(Block & block, const ColumnNumbers
|
||||
null_maps[i] = nullptr;
|
||||
}
|
||||
|
||||
const ColumnArray * first_array = checkAndGetColumn<ColumnArray>(array_columns[0].get());
|
||||
const ColumnArray * first_array = checkAndGetColumn<ColumnArray>(block.getByPosition(arguments.at(0)).column.get());
|
||||
const IColumn * first_null_map = null_maps[0];
|
||||
auto res_nested = ColumnUInt32::create();
|
||||
|
||||
@ -1357,7 +1370,7 @@ void FunctionArrayEnumerateUniq::executeImpl(Block & block, const ColumnNumbers
|
||||
if (!offsets->empty())
|
||||
res_values.resize(offsets->back());
|
||||
|
||||
if (arguments.size() == 1)
|
||||
if (num_columns == 1)
|
||||
{
|
||||
if (!( executeNumber<UInt8>(first_array, first_null_map, res_values)
|
||||
|| executeNumber<UInt16>(first_array, first_null_map, res_values)
|
||||
|
@ -191,7 +191,7 @@ struct ContextShared
|
||||
Context::ConfigReloadCallback config_reload_callback;
|
||||
|
||||
ContextShared(std::shared_ptr<IRuntimeComponentsFactory> runtime_components_factory_)
|
||||
: runtime_components_factory(std::move(runtime_components_factory_))
|
||||
: runtime_components_factory(std::move(runtime_components_factory_)), macros(std::make_unique<Macros>())
|
||||
{
|
||||
/// TODO: make it singleton (?)
|
||||
static std::atomic<size_t> num_calls{0};
|
||||
|
@ -1983,26 +1983,34 @@ bool ExpressionAnalyzer::isThereArrayJoin(const ASTPtr & ast)
|
||||
void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries, bool only_consts, ScopeStack & actions_stack,
|
||||
ProjectionManipulatorPtr projection_manipulator)
|
||||
{
|
||||
String ast_column_name;
|
||||
auto getColumnName = [&ast, &ast_column_name]()
|
||||
{
|
||||
if (ast_column_name.empty())
|
||||
ast_column_name = ast->getColumnName();
|
||||
|
||||
return ast_column_name;
|
||||
};
|
||||
|
||||
/// If the result of the calculation already exists in the block.
|
||||
if ((typeid_cast<ASTFunction *>(ast.get()) || typeid_cast<ASTLiteral *>(ast.get()))
|
||||
&& projection_manipulator->tryToGetFromUpperProjection(ast->getColumnName()))
|
||||
&& projection_manipulator->tryToGetFromUpperProjection(getColumnName()))
|
||||
return;
|
||||
|
||||
if (ASTIdentifier * node = typeid_cast<ASTIdentifier *>(ast.get()))
|
||||
if (typeid_cast<ASTIdentifier *>(ast.get()))
|
||||
{
|
||||
std::string name = node->getColumnName();
|
||||
if (!only_consts && !projection_manipulator->tryToGetFromUpperProjection(ast->getColumnName()))
|
||||
if (!only_consts && !projection_manipulator->tryToGetFromUpperProjection(getColumnName()))
|
||||
{
|
||||
/// The requested column is not in the block.
|
||||
/// If such a column exists in the table, then the user probably forgot to surround it with an aggregate function or add it to GROUP BY.
|
||||
|
||||
bool found = false;
|
||||
for (const auto & column_name_type : source_columns)
|
||||
if (column_name_type.name == name)
|
||||
if (column_name_type.name == getColumnName())
|
||||
found = true;
|
||||
|
||||
if (found)
|
||||
throw Exception("Column " + name + " is not under aggregate function and not in GROUP BY.",
|
||||
throw Exception("Column " + getColumnName() + " is not under aggregate function and not in GROUP BY.",
|
||||
ErrorCodes::NOT_AN_AGGREGATE);
|
||||
}
|
||||
}
|
||||
@ -2021,7 +2029,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
getActionsImpl(arg, no_subqueries, only_consts, actions_stack, projection_manipulator);
|
||||
if (!only_consts)
|
||||
{
|
||||
String result_name = projection_manipulator->getColumnName(node->getColumnName());
|
||||
String result_name = projection_manipulator->getColumnName(getColumnName());
|
||||
actions_stack.addAction(ExpressionAction::copyColumn(projection_manipulator->getColumnName(arg->getColumnName()), result_name));
|
||||
NameSet joined_columns;
|
||||
joined_columns.insert(result_name);
|
||||
@ -2049,7 +2057,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
/// We are in the part of the tree that we are not going to compute. You just need to define types.
|
||||
/// Do not subquery and create sets. We insert an arbitrary column of the correct type.
|
||||
ColumnWithTypeAndName fake_column;
|
||||
fake_column.name = projection_manipulator->getColumnName(node->getColumnName());
|
||||
fake_column.name = projection_manipulator->getColumnName(getColumnName());
|
||||
fake_column.type = std::make_shared<DataTypeUInt8>();
|
||||
actions_stack.addAction(ExpressionAction::addColumn(fake_column, projection_manipulator->getProjectionSourceColumn(), false));
|
||||
getActionsImpl(node->arguments->children.at(0), no_subqueries, only_consts, actions_stack,
|
||||
@ -2065,7 +2073,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
{
|
||||
actions_stack.addAction(ExpressionAction::addColumn(ColumnWithTypeAndName(
|
||||
ColumnConst::create(ColumnUInt8::create(1, 1), 1), std::make_shared<DataTypeUInt8>(),
|
||||
projection_manipulator->getColumnName(node->getColumnName())), projection_manipulator->getProjectionSourceColumn(), false));
|
||||
projection_manipulator->getColumnName(getColumnName())), projection_manipulator->getProjectionSourceColumn(), false));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2073,7 +2081,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
return;
|
||||
|
||||
const FunctionBuilderPtr & function_builder = FunctionFactory::instance().get(node->name, context);
|
||||
auto projection_action = getProjectionAction(node->name, actions_stack, projection_manipulator, node->getColumnName(), context);
|
||||
auto projection_action = getProjectionAction(node->name, actions_stack, projection_manipulator, getColumnName(), context);
|
||||
|
||||
Names argument_names;
|
||||
DataTypes argument_types;
|
||||
@ -2085,6 +2093,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
for (size_t arg = 0; arg < node->arguments->children.size(); ++arg)
|
||||
{
|
||||
auto & child = node->arguments->children[arg];
|
||||
auto child_column_name = child->getColumnName();
|
||||
|
||||
ASTFunction * lambda = typeid_cast<ASTFunction *>(child.get());
|
||||
if (lambda && lambda->name == "lambda")
|
||||
@ -2115,7 +2124,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
if (!set->empty())
|
||||
column.name = getUniqueName(actions_stack.getSampleBlock(), "__set");
|
||||
else
|
||||
column.name = child->getColumnName();
|
||||
column.name = child_column_name;
|
||||
|
||||
column.name = projection_manipulator->getColumnName(column.name);
|
||||
|
||||
@ -2135,8 +2144,8 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
projection_action->preArgumentAction();
|
||||
getActionsImpl(child, no_subqueries, only_consts, actions_stack,
|
||||
projection_manipulator);
|
||||
std::string name = projection_manipulator->getColumnName(child->getColumnName());
|
||||
projection_action->postArgumentAction(child->getColumnName());
|
||||
std::string name = projection_manipulator->getColumnName(child_column_name);
|
||||
projection_action->postArgumentAction(child_column_name);
|
||||
if (actions_stack.getSampleBlock().has(name))
|
||||
{
|
||||
argument_types.push_back(actions_stack.getSampleBlock().getByName(name).type);
|
||||
@ -2239,7 +2248,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
actions_stack.addAction(
|
||||
ExpressionAction::applyFunction(function_builder,
|
||||
argument_names,
|
||||
projection_manipulator->getColumnName(node->getColumnName()),
|
||||
projection_manipulator->getColumnName(getColumnName()),
|
||||
projection_manipulator->getProjectionSourceColumn()));
|
||||
}
|
||||
}
|
||||
@ -2251,7 +2260,7 @@ void ExpressionAnalyzer::getActionsImpl(const ASTPtr & ast, bool no_subqueries,
|
||||
ColumnWithTypeAndName column;
|
||||
column.column = type->createColumnConst(1, convertFieldToType(node->value, *type));
|
||||
column.type = type;
|
||||
column.name = node->getColumnName();
|
||||
column.name = getColumnName();
|
||||
|
||||
actions_stack.addAction(ExpressionAction::addColumn(column, "", false));
|
||||
projection_manipulator->tryToGetFromUpperProjection(column.name);
|
||||
|
21
dbms/src/Parsers/ASTAsterisk.cpp
Normal file
21
dbms/src/Parsers/ASTAsterisk.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include <Parsers/ASTAsterisk.h>
|
||||
#include <IO/WriteBuffer.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
ASTPtr ASTAsterisk::clone() const
|
||||
{
|
||||
auto clone = std::make_shared<ASTAsterisk>(*this);
|
||||
clone->cloneChildren();
|
||||
return std::move(clone);
|
||||
}
|
||||
|
||||
void ASTAsterisk::appendColumnName(WriteBuffer & ostr) const { ostr.write('*'); }
|
||||
|
||||
void ASTAsterisk::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
|
||||
{
|
||||
settings.ostr << "*";
|
||||
}
|
||||
|
||||
}
|
@ -10,19 +10,11 @@ class ASTAsterisk : public IAST
|
||||
{
|
||||
public:
|
||||
String getID() const override { return "Asterisk"; }
|
||||
ASTPtr clone() const override
|
||||
{
|
||||
auto clone = std::make_shared<ASTAsterisk>(*this);
|
||||
clone->cloneChildren();
|
||||
return std::move(clone);
|
||||
}
|
||||
String getColumnName() const override { return "*"; }
|
||||
ASTPtr clone() const override;
|
||||
void appendColumnName(WriteBuffer & ostr) const override;
|
||||
|
||||
protected:
|
||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override
|
||||
{
|
||||
settings.ostr << "*";
|
||||
}
|
||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,32 +9,30 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
String ASTFunction::getColumnNameImpl() const
|
||||
void ASTFunction::appendColumnNameImpl(WriteBuffer & ostr) const
|
||||
{
|
||||
WriteBufferFromOwnString wb;
|
||||
writeString(name, wb);
|
||||
writeString(name, ostr);
|
||||
|
||||
if (parameters)
|
||||
{
|
||||
writeChar('(', wb);
|
||||
for (ASTs::const_iterator it = parameters->children.begin(); it != parameters->children.end(); ++it)
|
||||
writeChar('(', ostr);
|
||||
for (auto it = parameters->children.begin(); it != parameters->children.end(); ++it)
|
||||
{
|
||||
if (it != parameters->children.begin())
|
||||
writeCString(", ", wb);
|
||||
writeString((*it)->getColumnName(), wb);
|
||||
writeCString(", ", ostr);
|
||||
(*it)->appendColumnName(ostr);
|
||||
}
|
||||
writeChar(')', wb);
|
||||
writeChar(')', ostr);
|
||||
}
|
||||
|
||||
writeChar('(', wb);
|
||||
for (ASTs::const_iterator it = arguments->children.begin(); it != arguments->children.end(); ++it)
|
||||
writeChar('(', ostr);
|
||||
for (auto it = arguments->children.begin(); it != arguments->children.end(); ++it)
|
||||
{
|
||||
if (it != arguments->children.begin())
|
||||
writeCString(", ", wb);
|
||||
writeString((*it)->getColumnName(), wb);
|
||||
writeCString(", ", ostr);
|
||||
(*it)->appendColumnName(ostr);
|
||||
}
|
||||
writeChar(')', wb);
|
||||
return wb.str();
|
||||
writeChar(')', ostr);
|
||||
}
|
||||
|
||||
/** Get the text that identifies this element. */
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
|
||||
protected:
|
||||
void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
String getColumnNameImpl() const override;
|
||||
void appendColumnNameImpl(WriteBuffer & ostr) const override;
|
||||
};
|
||||
|
||||
|
||||
|
@ -37,4 +37,9 @@ void ASTIdentifier::formatImplWithoutAlias(const FormatSettings & settings, Form
|
||||
}
|
||||
}
|
||||
|
||||
void ASTIdentifier::appendColumnNameImpl(WriteBuffer & ostr) const
|
||||
{
|
||||
writeString(name, ostr);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
|
||||
protected:
|
||||
void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
String getColumnNameImpl() const override { return name; }
|
||||
void appendColumnNameImpl(WriteBuffer & ostr) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -7,8 +7,7 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
|
||||
String ASTLiteral::getColumnNameImpl() const
|
||||
void ASTLiteral::appendColumnNameImpl(WriteBuffer & ostr) const
|
||||
{
|
||||
/// Special case for very large arrays. Instead of listing all elements, will use hash of them.
|
||||
/// (Otherwise column name will be too long, that will lead to significant slowdown of expression analysis.)
|
||||
@ -19,10 +18,17 @@ String ASTLiteral::getColumnNameImpl() const
|
||||
applyVisitor(FieldVisitorHash(hash), value);
|
||||
UInt64 low, high;
|
||||
hash.get128(low, high);
|
||||
return "__array_" + toString(low) + "_" + toString(high);
|
||||
|
||||
writeCString("__array_", ostr);
|
||||
writeText(low, ostr);
|
||||
ostr.write('_');
|
||||
writeText(high, ostr);
|
||||
}
|
||||
else
|
||||
{
|
||||
String column_name = applyVisitor(FieldVisitorToString(), value);
|
||||
writeString(column_name, ostr);
|
||||
}
|
||||
|
||||
return applyVisitor(FieldVisitorToString(), value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ protected:
|
||||
settings.ostr << applyVisitor(FieldVisitorToString(), value);
|
||||
}
|
||||
|
||||
String getColumnNameImpl() const override;
|
||||
void appendColumnNameImpl(WriteBuffer & ostr) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
#include <Parsers/ASTQualifiedAsterisk.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
String ASTQualifiedAsterisk::getColumnName() const
|
||||
void ASTQualifiedAsterisk::appendColumnName(WriteBuffer & ostr) const
|
||||
{
|
||||
const auto & qualifier = children.at(0);
|
||||
return qualifier->getColumnName() + ".*";
|
||||
qualifier->appendColumnName(ostr);
|
||||
writeCString(".*", ostr);
|
||||
}
|
||||
|
||||
void ASTQualifiedAsterisk::formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
|
||||
|
@ -19,7 +19,7 @@ public:
|
||||
clone->cloneChildren();
|
||||
return std::move(clone);
|
||||
}
|
||||
String getColumnName() const override;
|
||||
void appendColumnName(WriteBuffer & ostr) const override;
|
||||
|
||||
protected:
|
||||
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
|
@ -4,14 +4,30 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
String ASTSubquery::getColumnNameImpl() const
|
||||
void ASTSubquery::appendColumnNameImpl(WriteBuffer & ostr) const
|
||||
{
|
||||
/// This is a hack. We use alias, if available, because otherwise tree could change during analysis.
|
||||
if (!alias.empty())
|
||||
return alias;
|
||||
writeString(alias, ostr);
|
||||
|
||||
Hash hash = getTreeHash();
|
||||
return "__subquery_" + toString(hash.first) + "_" + toString(hash.second);
|
||||
writeCString("__subquery_", ostr);
|
||||
writeText(hash.first, ostr);
|
||||
ostr.write('_');
|
||||
writeText(hash.second, ostr);
|
||||
}
|
||||
|
||||
void ASTSubquery::formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
std::string indent_str = settings.one_line ? "" : std::string(4u * frame.indent, ' ');
|
||||
std::string nl_or_nothing = settings.one_line ? "" : "\n";
|
||||
|
||||
settings.ostr << nl_or_nothing << indent_str << "(" << nl_or_nothing;
|
||||
FormatStateStacked frame_nested = frame;
|
||||
frame_nested.need_parens = false;
|
||||
++frame_nested.indent;
|
||||
children[0]->formatImpl(settings, state, frame_nested);
|
||||
settings.ostr << nl_or_nothing << indent_str << ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,20 +29,8 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
|
||||
{
|
||||
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
|
||||
std::string nl_or_nothing = settings.one_line ? "" : "\n";
|
||||
|
||||
settings.ostr << nl_or_nothing << indent_str << "(" << nl_or_nothing;
|
||||
FormatStateStacked frame_nested = frame;
|
||||
frame_nested.need_parens = false;
|
||||
++frame_nested.indent;
|
||||
children[0]->formatImpl(settings, state, frame_nested);
|
||||
settings.ostr << nl_or_nothing << indent_str << ")";
|
||||
}
|
||||
|
||||
String getColumnNameImpl() const override;
|
||||
void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
void appendColumnNameImpl(WriteBuffer & ostr) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -33,4 +33,12 @@ void ASTWithAlias::formatImpl(const FormatSettings & settings, FormatState & sta
|
||||
}
|
||||
}
|
||||
|
||||
void ASTWithAlias::appendColumnName(WriteBuffer & ostr) const
|
||||
{
|
||||
if (prefer_alias_to_column_name && !alias.empty())
|
||||
writeString(alias, ostr);
|
||||
else
|
||||
appendColumnNameImpl(ostr);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ public:
|
||||
|
||||
using IAST::IAST;
|
||||
|
||||
String getColumnName() const override final { return prefer_alias_to_column_name && !alias.empty() ? alias : getColumnNameImpl(); }
|
||||
String getAliasOrColumnName() const override { return alias.empty() ? getColumnNameImpl() : alias; }
|
||||
void appendColumnName(WriteBuffer & ostr) const final;
|
||||
String getAliasOrColumnName() const override { return alias.empty() ? getColumnName() : alias; }
|
||||
String tryGetAlias() const override { return alias; }
|
||||
void setAlias(const String & to) override { alias = to; }
|
||||
|
||||
@ -31,7 +31,7 @@ public:
|
||||
virtual void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const = 0;
|
||||
|
||||
protected:
|
||||
virtual String getColumnNameImpl() const = 0;
|
||||
virtual void appendColumnNameImpl(WriteBuffer & ostr) const = 0;
|
||||
};
|
||||
|
||||
/// helper for setting aliases and chaining result to other functions
|
||||
|
@ -101,4 +101,12 @@ void IAST::cloneChildren()
|
||||
child = child->clone();
|
||||
}
|
||||
|
||||
|
||||
String IAST::getColumnName() const
|
||||
{
|
||||
WriteBufferFromOwnString write_buffer;
|
||||
appendColumnName(write_buffer);
|
||||
return write_buffer.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,7 +46,11 @@ public:
|
||||
virtual ~IAST() = default;
|
||||
|
||||
/** Get the canonical name of the column if the element is a column */
|
||||
virtual String getColumnName() const { throw Exception("Trying to get name of not a column: " + getID(), ErrorCodes::NOT_A_COLUMN); }
|
||||
String getColumnName() const;
|
||||
virtual void appendColumnName(WriteBuffer &) const
|
||||
{
|
||||
throw Exception("Trying to get name of not a column: " + getID(), ErrorCodes::NOT_A_COLUMN);
|
||||
}
|
||||
|
||||
/** Get the alias, if any, or the canonical name of the column, if it is not. */
|
||||
virtual String getAliasOrColumnName() const { return getColumnName(); }
|
||||
|
@ -46,7 +46,6 @@ void ReplicatedMergeTreeCleanupThread::run()
|
||||
}
|
||||
|
||||
task->scheduleAfter(CLEANUP_SLEEP_MS);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
175
dbms/tests/integration/test_replicated_mutations/test.py
Normal file
175
dbms/tests/integration/test_replicated_mutations/test.py
Normal file
@ -0,0 +1,175 @@
|
||||
import time
|
||||
import threading
|
||||
import random
|
||||
from collections import Counter
|
||||
|
||||
import pytest
|
||||
|
||||
from helpers.cluster import ClickHouseCluster
|
||||
|
||||
|
||||
cluster = ClickHouseCluster(__file__)
|
||||
|
||||
node1 = cluster.add_instance('node1', with_zookeeper=True)
|
||||
node2 = cluster.add_instance('node2', with_zookeeper=True)
|
||||
nodes = [node1, node2]
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def started_cluster():
|
||||
try:
|
||||
cluster.start()
|
||||
|
||||
for node in nodes:
|
||||
node.query("DROP TABLE IF EXISTS test_mutations")
|
||||
|
||||
for node in nodes:
|
||||
node.query("CREATE TABLE test_mutations(d Date, x UInt32, i UInt32) ENGINE ReplicatedMergeTree('/clickhouse/tables/test/test_mutations', '{instance}') ORDER BY x PARTITION BY toYYYYMM(d)")
|
||||
|
||||
yield cluster
|
||||
|
||||
finally:
|
||||
cluster.shutdown()
|
||||
|
||||
|
||||
class Runner:
|
||||
def __init__(self):
|
||||
self.mtx = threading.Lock()
|
||||
self.total_inserted_xs = 0
|
||||
self.total_inserted_rows = 0
|
||||
|
||||
self.total_mutations = 0
|
||||
self.total_deleted_xs = 0
|
||||
self.total_deleted_rows = 0
|
||||
|
||||
self.current_xs = Counter()
|
||||
|
||||
self.currently_inserting_xs = Counter()
|
||||
self.currently_deleting_xs = set()
|
||||
|
||||
self.stop_ev = threading.Event()
|
||||
|
||||
def do_insert(self, thread_num):
|
||||
self.stop_ev.wait(random.random())
|
||||
|
||||
# Each thread inserts a small random number of rows with random year, month 01 and day determined
|
||||
# by the thread number. The idea is to avoid spurious duplicates and to insert into a
|
||||
# nontrivial number of partitions.
|
||||
month = '01'
|
||||
day = str(thread_num + 1).zfill(2)
|
||||
i = 1
|
||||
while not self.stop_ev.is_set():
|
||||
xs = [random.randint(1, 10) for _ in range(random.randint(1, 10))]
|
||||
with self.mtx:
|
||||
xs = [x for x in xs if x not in self.currently_deleting_xs]
|
||||
if len(xs) == 0:
|
||||
continue
|
||||
for x in xs:
|
||||
self.currently_inserting_xs[x] += 1
|
||||
|
||||
year = 2000 + random.randint(0, 10)
|
||||
date_str = '{year}-{month}-{day}'.format(year=year, month=month, day=day)
|
||||
payload = ''
|
||||
for x in xs:
|
||||
payload += '{date_str} {x} {i}\n'.format(date_str=date_str, x=x, i=i)
|
||||
i += 1
|
||||
|
||||
try:
|
||||
print 'thread {}: insert for {}: {}'.format(thread_num, date_str, ','.join(str(x) for x in xs))
|
||||
random.choice(nodes).query("INSERT INTO test_mutations FORMAT TSV", payload)
|
||||
|
||||
with self.mtx:
|
||||
for x in xs:
|
||||
self.current_xs[x] += 1
|
||||
self.total_inserted_xs += sum(xs)
|
||||
self.total_inserted_rows += len(xs)
|
||||
|
||||
except Exception, e:
|
||||
print 'Exception while inserting,', e
|
||||
finally:
|
||||
with self.mtx:
|
||||
for x in xs:
|
||||
self.currently_inserting_xs[x] -= 1
|
||||
|
||||
self.stop_ev.wait(0.2 + random.random() / 5)
|
||||
|
||||
def do_delete(self, thread_num):
|
||||
self.stop_ev.wait(1.0 + random.random())
|
||||
|
||||
while not self.stop_ev.is_set():
|
||||
chosen = False
|
||||
with self.mtx:
|
||||
if self.current_xs:
|
||||
x = random.choice(list(self.current_xs.elements()))
|
||||
|
||||
if self.currently_inserting_xs[x] == 0 and x not in self.currently_deleting_xs:
|
||||
chosen = True
|
||||
self.currently_deleting_xs.add(x)
|
||||
to_delete_count = self.current_xs[x]
|
||||
|
||||
if not chosen:
|
||||
self.stop_ev.wait(0.1 * random.random())
|
||||
continue
|
||||
|
||||
try:
|
||||
print 'thread {}: delete {} * {}'.format(thread_num, to_delete_count, x)
|
||||
random.choice(nodes).query("ALTER TABLE test_mutations DELETE WHERE x = {}".format(x))
|
||||
|
||||
with self.mtx:
|
||||
self.total_mutations += 1
|
||||
self.current_xs[x] -= to_delete_count
|
||||
self.total_deleted_xs += to_delete_count * x
|
||||
self.total_deleted_rows += to_delete_count
|
||||
|
||||
except Exception, e:
|
||||
print 'Exception while deleting,', e
|
||||
finally:
|
||||
with self.mtx:
|
||||
self.currently_deleting_xs.remove(x)
|
||||
|
||||
self.stop_ev.wait(1.0 + random.random() * 2)
|
||||
|
||||
|
||||
def test_mutations(started_cluster):
|
||||
DURATION_SECONDS = 50
|
||||
|
||||
runner = Runner()
|
||||
|
||||
threads = []
|
||||
for thread_num in range(5):
|
||||
threads.append(threading.Thread(target=runner.do_insert, args=(thread_num, )))
|
||||
|
||||
for thread_num in (11, 12, 13):
|
||||
threads.append(threading.Thread(target=runner.do_delete, args=(thread_num,)))
|
||||
|
||||
for t in threads:
|
||||
t.start()
|
||||
|
||||
time.sleep(DURATION_SECONDS)
|
||||
runner.stop_ev.set()
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
# Sanity check: at least something was inserted and something was deleted
|
||||
assert runner.total_inserted_rows > 0
|
||||
assert runner.total_mutations > 0
|
||||
|
||||
all_done = False
|
||||
for i in range(100): # wait for replication 10 seconds max
|
||||
time.sleep(0.1)
|
||||
|
||||
def get_done_mutations(node):
|
||||
return int(node.query("SELECT sum(is_done) FROM system.mutations WHERE table = 'test_mutations'").rstrip())
|
||||
|
||||
if all([get_done_mutations(n) == runner.total_mutations for n in nodes]):
|
||||
all_done = True
|
||||
break
|
||||
|
||||
print node1.query("SELECT mutation_id, command, parts_to_do, is_done FROM system.mutations WHERE table = 'test_mutations' FORMAT TSVWithNames")
|
||||
assert all_done
|
||||
|
||||
expected_sum = runner.total_inserted_xs - runner.total_deleted_xs
|
||||
actual_sums = []
|
||||
for i, node in enumerate(nodes):
|
||||
actual_sums.append(int(node.query("SELECT sum(x) FROM test_mutations").rstrip()))
|
||||
assert actual_sums[i] == expected_sum
|
3
dbms/tests/queries/0_stateless/00625_query_in_form_data.reference
Executable file
3
dbms/tests/queries/0_stateless/00625_query_in_form_data.reference
Executable file
@ -0,0 +1,3 @@
|
||||
1
|
||||
1 Hello
|
||||
2 World
|
10
dbms/tests/queries/0_stateless/00625_query_in_form_data.sh
Executable file
10
dbms/tests/queries/0_stateless/00625_query_in_form_data.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
. $CURDIR/../shell_config.sh
|
||||
|
||||
${CLICKHOUSE_CURL} ${CLICKHOUSE_URL}?query="select" -X POST -F "query= 1;" 2>/dev/null
|
||||
|
||||
|
||||
echo -ne '1,Hello\n2,World\n' | ${CLICKHOUSE_CURL} -sSF 'file=@-' "${CLICKHOUSE_URL}?file_format=CSV&file_types=UInt8,String&query=SELE" -X POST -F "query=CT * FROM file" 2>/dev/null
|
@ -0,0 +1,5 @@
|
||||
DROP TABLE IF EXISTS test.final_test;
|
||||
CREATE TABLE test.final_test (id String, version Date) ENGINE = ReplacingMergeTree(version, id, 8192);
|
||||
INSERT INTO test.final_test (id, version) VALUES ('2018-01-01', '2018-01-01');
|
||||
SELECT * FROM test.final_test FINAL PREWHERE id == '2018-01-02';
|
||||
DROP TABLE test.final_test;
|
@ -0,0 +1 @@
|
||||
-2
|
@ -0,0 +1 @@
|
||||
SELECT quantileTDigest(0.5)(arrayJoin([-1, -2, -3]));
|
@ -0,0 +1,12 @@
|
||||
[1,1,2,3,2,4]
|
||||
[1,2,1,3,2,3]
|
||||
[1,1,1,2,1,2]
|
||||
[1,1,1,2,1,2]
|
||||
[1,1,1,2,1,2]
|
||||
[1,1,1,2,1,2]
|
||||
[1,1,1,2,3,2,4,2,3]
|
||||
[1,2,3,1,4,2,3,4,5]
|
||||
[1,1,1,1,2,1,2,1,2]
|
||||
[1,1,1,1,2,1,2,1,2]
|
||||
[1,1,1,1,2,1,2,1,2]
|
||||
[1,1,1,1,2,1,2,1,2]
|
@ -0,0 +1,20 @@
|
||||
drop table if exists test.tab;
|
||||
create table test.tab (val UInt32, n Nested(x UInt8, y String)) engine = Memory;
|
||||
insert into test.tab values (1, [1, 2, 1, 1, 2, 1], ['a', 'a', 'b', 'a', 'b', 'b']);
|
||||
select arrayEnumerateUniq(n.x) from test.tab;
|
||||
select arrayEnumerateUniq(n.y) from test.tab;
|
||||
select arrayEnumerateUniq(n.x, n.y) from test.tab;
|
||||
select arrayEnumerateUniq(arrayMap((a, b) -> (a, b), n.x, n.y)) from test.tab;
|
||||
select arrayEnumerateUniq(arrayMap((a, b) -> (a, b), n.x, n.y), n.x) from test.tab;
|
||||
select arrayEnumerateUniq(arrayMap((a, b) -> (a, b), n.x, n.y), arrayMap((a, b) -> (b, a), n.x, n.y)) from test.tab;
|
||||
|
||||
drop table test.tab;
|
||||
create table test.tab (val UInt32, n Nested(x Nullable(UInt8), y String)) engine = Memory;
|
||||
insert into test.tab values (1, [1, Null, 2, 1, 1, 2, 1, Null, Null], ['a', 'a', 'a', 'b', 'a', 'b', 'b', 'b', 'a']);
|
||||
select arrayEnumerateUniq(n.x) from test.tab;
|
||||
select arrayEnumerateUniq(n.y) from test.tab;
|
||||
select arrayEnumerateUniq(n.x, n.y) from test.tab;
|
||||
select arrayEnumerateUniq(arrayMap((a, b) -> (a, b), n.x, n.y)) from test.tab;
|
||||
select arrayEnumerateUniq(arrayMap((a, b) -> (a, b), n.x, n.y), n.x) from test.tab;
|
||||
select arrayEnumerateUniq(arrayMap((a, b) -> (a, b), n.x, n.y), arrayMap((a, b) -> (b, a), n.x, n.y)) from test.tab;
|
||||
|
4
debian/changelog
vendored
4
debian/changelog
vendored
@ -1,5 +1,5 @@
|
||||
clickhouse (1.1.54387) unstable; urgency=low
|
||||
clickhouse (1.1.54388) unstable; urgency=low
|
||||
|
||||
* Modified source code
|
||||
|
||||
-- <robot-metrika-test@yandex-team.ru> Fri, 22 Jun 2018 21:10:11 +0300
|
||||
-- <robot-metrika-test@yandex-team.ru> Wed, 27 Jun 2018 16:10:59 +0300
|
||||
|
@ -70,7 +70,7 @@ Docker image: <https://hub.docker.com/r/yandex/clickhouse-server/>
|
||||
|
||||
RPM packages for CentOS or RHEL: <https://github.com/Altinity/clickhouse-rpm-install>
|
||||
|
||||
Gentoo overlay: <https://github.com/kmeaw/clickhouse-overlay>
|
||||
Gentoo: `emerge clickhouse`
|
||||
|
||||
## Launch
|
||||
|
||||
|
@ -7,9 +7,9 @@ Settings are configured in layers, so each subsequent layer redefines the previo
|
||||
|
||||
Ways to configure settings, in order of priority:
|
||||
|
||||
- Settings in the server config file.
|
||||
- Settings in the server config file `users.xml`.
|
||||
|
||||
Settings from user profiles.
|
||||
Set it in user profile in `<profiles>` element.
|
||||
|
||||
- Session settings.
|
||||
|
||||
@ -21,4 +21,3 @@ Similarly, you can use ClickHouse sessions in the HTTP protocol. To do this, you
|
||||
- When using the HTTP API, pass CGI parameters (`URL?setting_1=value&setting_2=value...`).
|
||||
|
||||
Settings that can only be made in the server config file are not covered in this section.
|
||||
|
||||
|
@ -1,13 +1,15 @@
|
||||
# numbers
|
||||
|
||||
`numbers(N)` – Returns a table with the single 'number' column (UInt64) that contains integers from 0 to N-1.
|
||||
`numbers(N, M)` - Returns a table with the single 'number' column (UInt64) that contains integers from N to (N + M - 1).
|
||||
|
||||
Similar to the `system.numbers` table, it can be used for testing and generating successive values.
|
||||
Similar to the `system.numbers` table, it can be used for testing and generating successive values, `numbers(N, M)` more efficient than `system.numbers`.
|
||||
|
||||
The following two queries are equivalent:
|
||||
The following queries are equivalent:
|
||||
|
||||
```sql
|
||||
SELECT * FROM numbers(10);
|
||||
SELECT * FROM numbers(0, 10);
|
||||
SELECT * FROM system.numbers LIMIT 10;
|
||||
```
|
||||
|
||||
|
@ -16,7 +16,7 @@ grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not su
|
||||
|
||||
В целях тестирования и разработки, система может быть установлена на один сервер или на рабочий компьютер.
|
||||
|
||||
### Установка из пакетов для Debian/Ubuntu
|
||||
### Установка из пакетов для Debian/Ubuntu
|
||||
|
||||
Пропишите в `/etc/apt/sources.list` (или в отдельный файл `/etc/apt/sources.list.d/clickhouse.list`) репозитории:
|
||||
|
||||
@ -70,7 +70,7 @@ Docker образ: <https://hub.docker.com/r/yandex/clickhouse-server/>
|
||||
|
||||
RPM пакеты для CentOS, RHEL: <https://github.com/Altinity/clickhouse-rpm-install>
|
||||
|
||||
Gentoo overlay: <https://github.com/kmeaw/clickhouse-overlay>
|
||||
Gentoo: `emerge clickhouse`
|
||||
|
||||
## Запуск
|
||||
|
||||
|
@ -436,6 +436,48 @@ ALTER TABLE [db.]table FETCH PARTITION 'name' FROM 'path-in-zookeeper'
|
||||
|
||||
<a name="query_language_queries_show_databases"></a>
|
||||
|
||||
### Мутации
|
||||
|
||||
Мутации - разновидность запроса ALTER, позволяющая изменять или удалять данные в таблице. В отличие от стандартных запросов `DELETE` и `UPDATE`, рассчитанных на точечное изменение данных, область применения мутаций - достаточно тяжёлые изменения, затрагивающие много строк в таблице.
|
||||
|
||||
Функциональность находится в состоянии beta и доступна начиная с версии 1.1.54388. Реализована поддержка Replicated*MergeTree таблиц (в скором времени будет добавлена поддержка и для нереплицированных MergeTree).
|
||||
|
||||
Конвертировать существующие таблицы для работы с мутациями не нужно. Но после применения первой мутации формат данных таблицы становится несовместимым с предыдущими версиями и откатиться на предыдущую версию уже не получится.
|
||||
|
||||
На данный момент доступна команда `ALTER DELETE`:
|
||||
|
||||
```sql
|
||||
ALTER TABLE [db.]table DELETE WHERE expr
|
||||
```
|
||||
|
||||
Выражение `expr` должно иметь тип UInt8. Запрос удаляет строки таблицы, для которых это выражение принимает ненулевое значение.
|
||||
|
||||
В одном запросе можно указать несколько команд через запятую.
|
||||
|
||||
Для *MergeTree-таблиц мутации выполняются, перезаписывая данные по кускам (parts). При этом атомарности нет - куски заменяются на помутированные по мере выполнения и запрос `SELECT`, заданный во время выполнения мутации, увидит данные как из измененных кусков, так и из кусков, которые еще не были изменены.
|
||||
|
||||
Мутации линейно упорядочены между собой и накладываются на каждый кусок в порядке добавления. Мутации также упорядочены со вставками - гарантируется, что данные, вставленные в таблицу до начала выполнения запроса мутации, будут изменены, а данные, вставленные после окончания запроса мутации, изменены не будут. При этом мутации никак не блокируют вставки.
|
||||
|
||||
Для реплицированных таблиц запрос завершается немедленно после добавления информации о мутации в ZooKeeper. Сама мутация выполняется асинхронно, следить за ходом её выполнения можно по таблице `system.mutations`. Добавленные мутации будут выполняться до конца даже в случае перезапуска серверов ClickHouse. Откатить мутацию после её добавления нельзя.
|
||||
|
||||
#### Сиситемная таблица system.mutations
|
||||
|
||||
Таблица содержит информацию о ходе выполнения мутаций MergeTree-таблиц. Каждой команде мутации соответствует одна строка. В таблице есть следующие столбцы:
|
||||
|
||||
**database**, **table** - имя БД и таблицы, к которой была применена мутация.
|
||||
|
||||
**mutation_id** - ID запроса. Для реплицированных таблиц эти ID соответствуют именам записей в директории `<table_path_in_zookeeper>/mutations/` в ZooKeeper.
|
||||
|
||||
**command** - Команда мутации (часть запроса после `ALTER TABLE [db.]table`).
|
||||
|
||||
**create_time** - Время создания мутации.
|
||||
|
||||
**block_numbers.partition_id**, **block_numbers.number** - Nested-столбец, для каждой партиции содержащий номер блока, полученный этой мутацией (в каждой партиции будут изменены только куски, содержащие блоки с номерами, меньшими номера, полученного мутацией в этой партиции).
|
||||
|
||||
**parts_to_do** - Количество кусков таблицы, которые ещё предстоит изменить.
|
||||
|
||||
**is_done** - Завершена ли мутация. Замечание: даже если `parts_to_do = 0`, возможна ситуация, когда мутация ещё не завершена из-за долго выполняющейся вставки, которая добавляет данные, которые нужно будет мутировать.
|
||||
|
||||
## SHOW DATABASES
|
||||
|
||||
```sql
|
||||
|
@ -11,6 +11,6 @@ SELECT * FROM system.numbers LIMIT 10;
|
||||
```
|
||||
Примеры:
|
||||
```sql
|
||||
-- генарация последовательности всех дат от 2010-01-01 до 2010-12-31
|
||||
-- генерация последовательности всех дат от 2010-01-01 до 2010-12-31
|
||||
select toDate('2010-01-01') + number as d FROM numbers(365);
|
||||
```
|
||||
|
@ -85,17 +85,18 @@ void Connection::connect(const char* db,
|
||||
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||
|
||||
/// Set timeouts.
|
||||
if (mysql_options(driver.get(), MYSQL_OPT_CONNECT_TIMEOUT, reinterpret_cast<const char *>(&timeout)))
|
||||
if (mysql_options(driver.get(), MYSQL_OPT_CONNECT_TIMEOUT, &timeout))
|
||||
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||
|
||||
if (mysql_options(driver.get(), MYSQL_OPT_READ_TIMEOUT, reinterpret_cast<const char *>(&rw_timeout)))
|
||||
if (mysql_options(driver.get(), MYSQL_OPT_READ_TIMEOUT, &rw_timeout))
|
||||
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||
|
||||
if (mysql_options(driver.get(), MYSQL_OPT_WRITE_TIMEOUT, reinterpret_cast<const char *>(&rw_timeout)))
|
||||
if (mysql_options(driver.get(), MYSQL_OPT_WRITE_TIMEOUT, &rw_timeout))
|
||||
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||
|
||||
/// Enables ability to use query LOAD DATA LOCAL INFILE with servers were compiled without --enable-local-infile option.
|
||||
if (mysql_options(driver.get(), MYSQL_OPT_LOCAL_INFILE, nullptr))
|
||||
/// Disable LOAD DATA LOCAL INFILE because it is insecure.
|
||||
unsigned enable_local_infile = 0;
|
||||
if (mysql_options(driver.get(), MYSQL_OPT_LOCAL_INFILE, &enable_local_infile))
|
||||
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||
|
||||
/// Specifies particular ssl key and certificate if it needs
|
||||
|
@ -106,9 +106,11 @@ static void mutate(pcg64 & generator, void * src, size_t length)
|
||||
&& isAlphaASCII(pos[2]))
|
||||
{
|
||||
auto res = rand(generator, 0, 3);
|
||||
if (res == 2)
|
||||
if (res == 2)
|
||||
{
|
||||
std::swap(pos[0], pos[1]);
|
||||
if (res == 3)
|
||||
}
|
||||
else if (res == 3)
|
||||
std::swap(pos[1], pos[2]);
|
||||
|
||||
pos += 3;
|
||||
@ -124,7 +126,7 @@ static void mutate(pcg64 & generator, void * src, size_t length)
|
||||
std::swap(pos[1], pos[2]);
|
||||
std::swap(pos[0], pos[1]);
|
||||
}
|
||||
if (res == 3)
|
||||
else if (res == 3)
|
||||
{
|
||||
std::swap(pos[3], pos[2]);
|
||||
std::swap(pos[4], pos[3]);
|
||||
|
Loading…
Reference in New Issue
Block a user