diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index e861d48bedf..00000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,18 +0,0 @@ - -## [1.1.54189](https://github.com/yandex/Clickhouse/tree/v1.1.54189-testing) (2017-03-17) -[Full Changelog](https://github.com/yandex/Clickhouse/compare/v1.1.54188-stable...v1.1.54189-testing) - -- Config: Allow define several graphite blocks, graphite.interval=60 option added. use_graphite option deleted. -- Configuration elements can now be loaded from ZooKeeper (see [documentation](https://clickhouse.yandex/reference_en.html#Configuration%20files)) - - -## [1.1.54181](https://github.com/yandex/Clickhouse/tree/v1.1.54181-testing) (2017-03-10) -[Full Changelog](https://github.com/yandex/Clickhouse/compare/v1.1.54165-stable...v1.1.54181-testing) - -- https server: - to enable: get/generate keys, uncomment in config.xml: `8443` and tune `` section - - -- listen_host by default changed to ::1 and 127.0.0.1. - If you want use connections from other computers write to config.xml: `::` - diff --git a/contrib/libbtrie/src/btrie.c b/contrib/libbtrie/src/btrie.c index e959d3f1786..24821da5653 100644 --- a/contrib/libbtrie/src/btrie.c +++ b/contrib/libbtrie/src/btrie.c @@ -255,7 +255,7 @@ btrie_insert_a6(btrie_t *tree, const uint8_t *key, const uint8_t *mask, uintptr_t value) { uint8_t bit; - uint i; + unsigned int i; btrie_node_t *node, *next; i = 0; @@ -337,7 +337,7 @@ int btrie_delete_a6(btrie_t *tree, const uint8_t *key, const uint8_t *mask) { uint8_t bit; - uint i; + unsigned int i; btrie_node_t *node; i = 0; @@ -411,7 +411,7 @@ btrie_find_a6(btrie_t *tree, const uint8_t *key) { uint8_t bit; uintptr_t value; - uint i; + unsigned int i; btrie_node_t *node; i = 0; diff --git a/dbms/cmake/version.cmake b/dbms/cmake/version.cmake index 248c11bf89e..5b7bf569041 100644 --- a/dbms/cmake/version.cmake +++ b/dbms/cmake/version.cmake @@ -1,6 +1,6 @@ #This strings autochanged from release_lib.sh : -set(VERSION_DESCRIBE v1.1.54234-testing) -set(VERSION_REVISION 54234) +set(VERSION_DESCRIBE v1.1.54235-testing) +set(VERSION_REVISION 54235) #===end of autochange set (VERSION_MAJOR 1) diff --git a/dbms/src/Analyzers/tests/analyze_columns.cpp b/dbms/src/Analyzers/tests/analyze_columns.cpp index ae93cec1fab..bc23b6ec9b4 100644 --- a/dbms/src/Analyzers/tests/analyze_columns.cpp +++ b/dbms/src/Analyzers/tests/analyze_columns.cpp @@ -35,8 +35,8 @@ try auto system_database = std::make_shared("system"); context.addDatabase("system", system_database); - system_database->attachTable("one", StorageSystemOne::create("one")); - system_database->attachTable("numbers", StorageSystemNumbers::create("numbers")); + system_database->attachTable("one", StorageSystemOne::create("one")); + system_database->attachTable("numbers", StorageSystemNumbers::create("numbers")); context.setCurrentDatabase("system"); AnalyzeLambdas analyze_lambdas; diff --git a/dbms/src/DataStreams/CastTypeBlockInputStream.cpp b/dbms/src/DataStreams/CastTypeBlockInputStream.cpp index c8c7696fb16..975841bbb5f 100644 --- a/dbms/src/DataStreams/CastTypeBlockInputStream.cpp +++ b/dbms/src/DataStreams/CastTypeBlockInputStream.cpp @@ -12,14 +12,11 @@ namespace DB CastTypeBlockInputStream::CastTypeBlockInputStream( const Context & context_, - BlockInputStreamPtr input_, - const Block & in_sample_, - const Block & out_sample_) - : context(context_) + const BlockInputStreamPtr & input_, + const Block & reference_definition_) + : context(context_), ref_defenition(reference_definition_) { - collectDifferent(in_sample_, out_sample_); - cast_functions.resize(in_sample_.columns()); - children.push_back(input_); + children.emplace_back(input_); } String CastTypeBlockInputStream::getName() const @@ -29,98 +26,91 @@ String CastTypeBlockInputStream::getName() const String CastTypeBlockInputStream::getID() const { - std::stringstream res; - res << "CastType(" << children.back()->getID() << ")"; - return res.str(); + return "CastType(" + children.back()->getID() + ")"; } Block CastTypeBlockInputStream::readImpl() { Block block = children.back()->read(); - if (!block || cast_types.empty()) + if (!block) return block; - size_t block_size = block.columns(); - - if (block_size != cast_types.size()) + if (!initialized) { - LOG_ERROR(log, "Number of columns do not match, skipping cast"); - return block; + initialized = true; + initialize(block); } + if (cast_description.empty()) + return block; + + size_t num_columns = block.columns(); Block res; - for (size_t i = 0; i < block_size; ++i) + for (size_t col = 0; col < num_columns; ++col) { - const auto & elem = block.getByPosition(i); + const auto & src_column = block.getByPosition(col); + auto it = cast_description.find(col); - if (bool(cast_types[i])) + if (it == cast_description.end()) { - const auto & type = cast_types[i]->type; - Block temporary_block - { - { - elem.column, - elem.type, - elem.name - }, - { - std::make_shared(1, type->getName()), - std::make_shared(), - "" - }, - { - nullptr, - cast_types[i]->type, - "" - } - }; - - FunctionPtr & cast_function = cast_functions[i]; - - /// Initialize function. - if (!cast_function) - { - cast_function = FunctionFactory::instance().get("CAST", context); - - DataTypePtr unused_return_type; - ColumnsWithTypeAndName arguments{ temporary_block.getByPosition(0), temporary_block.getByPosition(1) }; - std::vector unused_prerequisites; - - /// Prepares function to execution. TODO It is not obvious. - cast_function->getReturnTypeAndPrerequisites(arguments, unused_return_type, unused_prerequisites); - } - - cast_function->execute(temporary_block, {0, 1}, 2); - - res.insert({ - temporary_block.getByPosition(2).column, - cast_types[i]->type, - cast_types[i]->name}); + res.insert(src_column); } else { - res.insert(elem); + CastElement & cast_element = it->second; + + size_t tmp_col = cast_element.tmp_col_offset; + ColumnNumbers arguments{tmp_col, tmp_col + 1}; + tmp_conversion_block.getByPosition(tmp_col).column = src_column.column; + + cast_element.function->execute(tmp_conversion_block, arguments, tmp_col + 2); + res.insert(tmp_conversion_block.getByPosition(tmp_col + 2)); } } return res; } -void CastTypeBlockInputStream::collectDifferent(const Block & in_sample, const Block & out_sample) + +CastTypeBlockInputStream::CastElement::CastElement(std::shared_ptr && function_, size_t tmp_col_offset_) + : function(std::move(function_)), tmp_col_offset(tmp_col_offset_) {} + + +void CastTypeBlockInputStream::initialize(const Block & src_block) { - size_t in_size = in_sample.columns(); - cast_types.resize(in_size); - for (size_t i = 0; i < in_size; ++i) + for (size_t src_col = 0; src_col < src_block.columns(); ++src_col) { - const auto & in_elem = in_sample.getByPosition(i); - const auto & out_elem = out_sample.getByPosition(i); + const auto & src_column = src_block.getByPosition(src_col); + + /// Skip, if it is a problem, it will be detected on the next pipeline stage + if (!ref_defenition.has(src_column.name)) + continue; + + const auto & ref_column = ref_defenition.getByName(src_column.name); /// Force conversion if source and destination types is different. - if (!out_elem.type->equals(*in_elem.type)) + if (!ref_column.type->equals(*src_column.type)) { - cast_types[i] = NameAndTypePair(out_elem.name, out_elem.type); + ColumnWithTypeAndName src_columnn_copy = src_column.cloneEmpty(); + ColumnWithTypeAndName alias_column(std::make_shared(1, ref_column.type->getName()), std::make_shared(), ""); + ColumnWithTypeAndName result_column(nullptr, ref_column.type->clone(), src_column.name); + + DataTypePtr unused_return_type; + std::vector unused_prerequisites; + ColumnsWithTypeAndName arguments{src_columnn_copy, alias_column}; + + /// Prepares function to execution. TODO It is not obvious. + auto cast_function = FunctionFactory::instance().get("CAST", context); + cast_function->getReturnTypeAndPrerequisites(arguments, unused_return_type, unused_prerequisites); + + tmp_conversion_block.insert(src_column); + tmp_conversion_block.insert(alias_column); + tmp_conversion_block.insert(result_column); + + size_t tmp_col_offset = cast_description.size() * 3; + cast_description.emplace(src_col, CastElement(std::move(cast_function), tmp_col_offset)); } } } diff --git a/dbms/src/DataStreams/CastTypeBlockInputStream.h b/dbms/src/DataStreams/CastTypeBlockInputStream.h index 2f8c6adfb31..b100c4bee35 100644 --- a/dbms/src/DataStreams/CastTypeBlockInputStream.h +++ b/dbms/src/DataStreams/CastTypeBlockInputStream.h @@ -2,10 +2,6 @@ #include -#include -#include -#include - namespace DB { @@ -16,10 +12,9 @@ class IFunction; class CastTypeBlockInputStream : public IProfilingBlockInputStream { public: - CastTypeBlockInputStream(const Context & context_, - BlockInputStreamPtr input_, - const Block & in_sample_, - const Block & out_sample_); + CastTypeBlockInputStream(const Context & context, + const BlockInputStreamPtr & input, + const Block & reference_definition); String getName() const override; @@ -28,14 +23,25 @@ public: protected: Block readImpl() override; -private: - void collectDifferent(const Block & in_sample, const Block & out_sample); - private: const Context & context; - std::vector> cast_types; - std::vector> cast_functions; /// Used to perform type conversions. - Logger * log = &Logger::get("CastTypeBlockInputStream"); + Block ref_defenition; + + void initialize(const Block & src_block); + bool initialized = false; + + struct CastElement + { + std::shared_ptr function; + size_t tmp_col_offset; + + CastElement(std::shared_ptr && function_, size_t tmp_col_offset_); + }; + + /// Describes required conversions on source block + std::map cast_description; + /// Auxiliary block, stores arguments and results of required CAST calls + Block tmp_conversion_block; }; } diff --git a/dbms/src/DataStreams/GraphiteRollupSortedBlockInputStream.cpp b/dbms/src/DataStreams/GraphiteRollupSortedBlockInputStream.cpp index ac362992e8c..b0693cf00fe 100644 --- a/dbms/src/DataStreams/GraphiteRollupSortedBlockInputStream.cpp +++ b/dbms/src/DataStreams/GraphiteRollupSortedBlockInputStream.cpp @@ -94,8 +94,8 @@ Block GraphiteRollupSortedBlockInputStream::readImpl() if (i != time_column_num && i != value_column_num && i != version_column_num) unmodified_column_numbers.push_back(i); - if (selected_row.empty()) - selected_row.columns.resize(num_columns); + if (current_selected_row.empty()) + current_selected_row.columns.resize(num_columns); } if (has_collation) @@ -112,111 +112,114 @@ void GraphiteRollupSortedBlockInputStream::merge(ColumnPlainPtrs & merged_column { const DateLUTImpl & date_lut = DateLUT::instance(); - size_t merged_rows = 0; + size_t started_rows = 0; /// Number of times startNextRow() has been called. + + /// Take rows in needed order and put them into `merged_block` until we get `max_block_size` rows. + /// + /// Variables starting with current_* refer to the rows previously popped from the queue that will + /// contribute towards current output row. + /// Variables starting with next_* refer to the row at the top of the queue. - /// Take rows in needed order and put them into `merged_block` until rows no more than `max_block_size` while (!queue.empty()) { - TSortCursor current = queue.top(); + TSortCursor next_cursor = queue.top(); - next_path = current->all_columns[path_column_num]->getDataAt(current->pos); - next_time = current->all_columns[time_column_num]->get64(current->pos); - - auto prev_pattern = current_pattern; + StringRef next_path = next_cursor->all_columns[path_column_num]->getDataAt(next_cursor->pos); bool path_differs = is_first || next_path != current_path; + is_first = false; + time_t next_time = next_cursor->all_columns[time_column_num]->get64(next_cursor->pos); /// Is new key before rounding. bool is_new_key = path_differs || next_time != current_time; - UInt64 current_version = current->all_columns[version_column_num]->get64(current->pos); - if (is_new_key) { - current_path = next_path; - current_time = next_time; - - /// For previous group of rows with same key, accumulate a row that has maximum version. - if (merged_rows) - accumulateRow(selected_row); + /// Accumulate the row that has maximum version in the previous group of rows wit the same key: + if (started_rows) + accumulateRow(current_selected_row); + const Graphite::Pattern * next_pattern = current_pattern; if (path_differs) - current_pattern = selectPatternForPath(next_path); + next_pattern = selectPatternForPath(next_path); - if (current_pattern) + time_t next_time_rounded; + if (next_pattern) { - UInt32 precision = selectPrecision(current_pattern->retentions, next_time); + UInt32 precision = selectPrecision(next_pattern->retentions, next_time); next_time_rounded = roundTimeToPrecision(date_lut, next_time, precision); } - /// If no patterns has matched - it means that no need to do rounding. + else + { + /// If no pattern has matched - take the value as-is. + next_time_rounded = next_time; + } /// Key will be new after rounding. It means new result row. bool will_be_new_key = path_differs || next_time_rounded != current_time_rounded; if (will_be_new_key) { - /// This is not the first row in block. - if (merged_rows) + if (started_rows) { finishCurrentRow(merged_columns); - /// if we have enough rows - if (merged_rows >= max_block_size) + /// We have enough rows - return, but don't advance the loop. At the beginning of the + /// next call to merge() the same next_cursor will be processed once more and + /// the next output row will be created from it. + if (started_rows >= max_block_size) return; } - startNextRow(merged_columns, current); + /// At this point previous row has been fully processed, so we can advance the loop + /// (substitute current_* values for next_*, advance the cursor). + + startNextRow(merged_columns, next_cursor, next_pattern); + ++started_rows; current_time_rounded = next_time_rounded; - - if (prev_pattern) - prev_pattern->function->destroy(place_for_aggregate_state.data()); - if (current_pattern) - current_pattern->function->create(place_for_aggregate_state.data()); - - ++merged_rows; } + + current_path = next_path; + current_time = next_time; } /// Within all rows with same key, we should leave only one row with maximum version; - /// and for rows with same maximum version - only last row. - if (is_new_key || current_version >= current_max_version) + /// and for rows with same maximum version - only last row. + UInt64 next_version = next_cursor->all_columns[version_column_num]->get64(next_cursor->pos); + if (is_new_key || next_version >= current_max_version) { - current_max_version = current_version; - setRowRef(selected_row, current); + current_max_version = next_version; + setRowRef(current_selected_row, next_cursor); } queue.pop(); - if (!current->isLast()) + if (!next_cursor->isLast()) { - current->next(); - queue.push(current); + next_cursor->next(); + queue.push(next_cursor); } else { /// We get the next block from the appropriate source, if there is one. - fetchNextBlock(current, queue); + fetchNextBlock(next_cursor, queue); } } /// Write result row for the last group. - ++merged_rows; - accumulateRow(selected_row); - finishCurrentRow(merged_columns); + if (started_rows) + { + accumulateRow(current_selected_row); + finishCurrentRow(merged_columns); + } finished = true; - - if (current_pattern) - { - current_pattern->function->destroy(place_for_aggregate_state.data()); - current_pattern = nullptr; - } } template -void GraphiteRollupSortedBlockInputStream::startNextRow(ColumnPlainPtrs & merged_columns, TSortCursor & cursor) +void GraphiteRollupSortedBlockInputStream::startNextRow(ColumnPlainPtrs & merged_columns, TSortCursor & cursor, const Graphite::Pattern * next_pattern) { /// Copy unmodified column values. for (size_t i = 0, size = unmodified_column_numbers.size(); i < size; ++i) @@ -225,8 +228,13 @@ void GraphiteRollupSortedBlockInputStream::startNextRow(ColumnPlainPtrs & merged merged_columns[j]->insertFrom(*cursor->all_columns[j], cursor->pos); } - if (!current_pattern) - merged_columns[value_column_num]->insertFrom(*cursor->all_columns[value_column_num], cursor->pos); + if (next_pattern) + { + next_pattern->function->create(place_for_aggregate_state.data()); + aggregate_state_created = true; + } + + current_pattern = next_pattern; } @@ -236,14 +244,21 @@ void GraphiteRollupSortedBlockInputStream::finishCurrentRow(ColumnPlainPtrs & me merged_columns[time_column_num]->insert(UInt64(current_time_rounded)); merged_columns[version_column_num]->insert(current_max_version); - if (current_pattern) + if (aggregate_state_created) + { current_pattern->function->insertResultInto(place_for_aggregate_state.data(), *merged_columns[value_column_num]); + current_pattern->function->destroy(place_for_aggregate_state.data()); + aggregate_state_created = false; + } + else + merged_columns[value_column_num]->insertFrom( + *current_selected_row.columns[value_column_num], current_selected_row.row_num); } void GraphiteRollupSortedBlockInputStream::accumulateRow(RowRef & row) { - if (current_pattern) + if (aggregate_state_created) current_pattern->function->add(place_for_aggregate_state.data(), &row.columns[value_column_num], row.row_num, nullptr); } diff --git a/dbms/src/DataStreams/GraphiteRollupSortedBlockInputStream.h b/dbms/src/DataStreams/GraphiteRollupSortedBlockInputStream.h index 0c289cb025d..7a6b4f49e28 100644 --- a/dbms/src/DataStreams/GraphiteRollupSortedBlockInputStream.h +++ b/dbms/src/DataStreams/GraphiteRollupSortedBlockInputStream.h @@ -154,7 +154,7 @@ public: ~GraphiteRollupSortedBlockInputStream() { - if (current_pattern) + if (aggregate_state_created) current_pattern->function->destroy(place_for_aggregate_state.data()); } @@ -179,19 +179,17 @@ private: /// All data has been read. bool finished = false; - RowRef selected_row; /// Last row with maximum version for current primary key. + RowRef current_selected_row; /// Last row with maximum version for current primary key. UInt64 current_max_version = 0; bool is_first = true; StringRef current_path; time_t current_time = 0; time_t current_time_rounded = 0; - StringRef next_path; - time_t next_time = 0; - time_t next_time_rounded = 0; const Graphite::Pattern * current_pattern = nullptr; std::vector place_for_aggregate_state; + bool aggregate_state_created = false; /// Invariant: if true then current_pattern is not NULL. const Graphite::Pattern * selectPatternForPath(StringRef path) const; UInt32 selectPrecision(const Graphite::Retentions & retentions, time_t time) const; @@ -202,7 +200,7 @@ private: /// Insert the values into the resulting columns, which will not be changed in the future. template - void startNextRow(ColumnPlainPtrs & merged_columns, TSortCursor & cursor); + void startNextRow(ColumnPlainPtrs & merged_columns, TSortCursor & cursor, const Graphite::Pattern * next_pattern); /// Insert the calculated `time`, `value`, `version` values into the resulting columns by the last group of rows. void finishCurrentRow(ColumnPlainPtrs & merged_columns); diff --git a/dbms/src/DataTypes/DataTypeNumberBase.cpp b/dbms/src/DataTypes/DataTypeNumberBase.cpp index 0a3b6caf5de..7977f0d04db 100644 --- a/dbms/src/DataTypes/DataTypeNumberBase.cpp +++ b/dbms/src/DataTypes/DataTypeNumberBase.cpp @@ -82,6 +82,9 @@ void DataTypeNumberBase::serializeTextJSON(const IColumn & column, size_t row template void DataTypeNumberBase::deserializeTextJSON(IColumn & column, ReadBuffer & istr) const { + static constexpr bool is_uint8 = std::is_same::value; + static constexpr bool is_int8 = std::is_same::value; + bool has_quote = false; if (!istr.eof() && *istr.position() == '"') /// We understand the number both in quotes and without. { @@ -101,7 +104,24 @@ void DataTypeNumberBase::deserializeTextJSON(IColumn & column, ReadBuffer & i } else { - readText(x, istr); + if (is_uint8 || is_int8) + { + // extra conditions to parse true/false strings into 1/0 + if (istr.eof()) + throwReadAfterEOF(); + if (*istr.position() == 't' || *istr.position() == 'f') + { + bool tmp = false; + readBoolTextWord(tmp, istr); + x = tmp; + } + else + readText(x, istr); + } + else + { + readText(x, istr); + } if (has_quote) assertChar('"', istr); diff --git a/dbms/src/Functions/FunctionsCoding.cpp b/dbms/src/Functions/FunctionsCoding.cpp index 12f3f7c315f..f8d089bb774 100644 --- a/dbms/src/Functions/FunctionsCoding.cpp +++ b/dbms/src/Functions/FunctionsCoding.cpp @@ -16,6 +16,7 @@ void registerFunctionsCoding(FunctionFactory & factory) factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); + factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); diff --git a/dbms/src/Functions/FunctionsCoding.h b/dbms/src/Functions/FunctionsCoding.h index 3258608ad9c..afa4df73083 100644 --- a/dbms/src/Functions/FunctionsCoding.h +++ b/dbms/src/Functions/FunctionsCoding.h @@ -1189,6 +1189,103 @@ public: } }; +class FunctionMACStringToOUI : public IFunction +{ +public: + static constexpr auto name = "MACStringToOUI"; + static FunctionPtr create(const Context & context) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 1; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!typeid_cast(&*arguments[0])) + throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + return std::make_shared(); + } + + static UInt64 parseMAC(const char * pos) + { + + /// get integer value for a hexademical char digit, or -1 + const auto number_by_char = [] (const char ch) + { + if ('A' <= ch && ch <= 'F') + return 10 + ch - 'A'; + + if ('a' <= ch && ch <= 'f') + return 10 + ch - 'a'; + + if ('0' <= ch && ch <= '9') + return ch - '0'; + + return -1; + }; + + UInt64 res = 0; + for (int offset = 40; offset >= 0; offset -= 8) + { + UInt64 value = 0; + size_t len = 0; + int val = 0; + while ((val = number_by_char(*pos)) >= 0 && len <= 2) + { + value = value * 16 + val; + ++len; + ++pos; + } + if (len == 0 || value > 255 || (offset > 0 && *pos != ':')) + return 0; + res |= value << offset; + ++pos; + } + if (*(pos - 1) != '\0') + return 0; + res = res >> 24; + return res; + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) override + { + const ColumnPtr & column = block.safeGetByPosition(arguments[0]).column; + + if (const ColumnString * col = typeid_cast(column.get())) + { + auto col_res = std::make_shared(); + block.safeGetByPosition(result).column = col_res; + + ColumnUInt64::Container_t & vec_res = col_res->getData(); + vec_res.resize(col->size()); + + const ColumnString::Chars_t & vec_src = col->getChars(); + const ColumnString::Offsets_t & offsets_src = col->getOffsets(); + size_t prev_offset = 0; + + for (size_t i = 0; i < vec_res.size(); ++i) + { + vec_res[i] = parseMAC(reinterpret_cast(&vec_src[prev_offset])); + prev_offset = offsets_src[i]; + } + } + else if (const ColumnConstString * col = typeid_cast(column.get())) + { + auto col_res = std::make_shared>(col->size(), parseMAC(col->getData().c_str())); + block.safeGetByPosition(result).column = col_res; + } + else + throw Exception("Illegal column " + block.safeGetByPosition(arguments[0]).column->getName() + + " of argument of function " + getName(), + ErrorCodes::ILLEGAL_COLUMN); + } +}; + class FunctionUUIDNumToString : public IFunction { diff --git a/dbms/src/Functions/FunctionsMiscellaneous.cpp b/dbms/src/Functions/FunctionsMiscellaneous.cpp index 1a130ca8a8d..ebef13e405b 100644 --- a/dbms/src/Functions/FunctionsMiscellaneous.cpp +++ b/dbms/src/Functions/FunctionsMiscellaneous.cpp @@ -255,6 +255,44 @@ public: }; +/// Returns global default value for type of passed argument (example: 0 for numeric types, '' for String). +class FunctionDefaultValueOfArgumentType : public IFunction +{ +public: + static constexpr auto name = "defaultValueOfArgumentType"; + static FunctionPtr create(const Context & context) + { + return std::make_shared(); + } + + String getName() const override + { + return name; + } + + bool hasSpecialSupportForNulls() const override + { + return true; + } + + size_t getNumberOfArguments() const override + { + return 1; + } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + return arguments[0]->clone(); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) override + { + IDataType & type = *block.getByPosition(arguments[0]).type; + block.getByPosition(result).column = type.createConstColumn(block.rows(), type.getDefault()); + } +}; + + class FunctionBlockSize : public IFunction { public: @@ -1762,6 +1800,7 @@ void registerFunctionsMiscellaneous(FunctionFactory & factory) factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); + factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); diff --git a/dbms/src/Functions/FunctionsRound.h b/dbms/src/Functions/FunctionsRound.h index 8051380ac87..6bb176d5ac9 100644 --- a/dbms/src/Functions/FunctionsRound.h +++ b/dbms/src/Functions/FunctionsRound.h @@ -18,7 +18,7 @@ namespace DB /** Функции округления: * roundToExp2 - вниз до ближайшей степени двойки; * roundDuration - вниз до ближайшего из: 0, 1, 10, 30, 60, 120, 180, 240, 300, 600, 1200, 1800, 3600, 7200, 18000, 36000; - * roundAge - вниз до ближайшего из: 0, 18, 25, 35, 45. + * roundAge - вниз до ближайшего из: 0, 18, 25, 35, 45, 55. * * round(x, N) - арифметическое округление (N = 0 по умолчанию). * ceil(x, N) - наименьшее число, которое не меньше x (N = 0 по умолчанию). @@ -101,7 +101,8 @@ struct RoundAgeImpl : (x < 25 ? 18 : (x < 35 ? 25 : (x < 45 ? 35 - : 45)))); + : (x < 55 ? 45 + : 55))))); } }; diff --git a/dbms/src/IO/ReadHelpers.h b/dbms/src/IO/ReadHelpers.h index d4e4c38558c..aed9dddb538 100644 --- a/dbms/src/IO/ReadHelpers.h +++ b/dbms/src/IO/ReadHelpers.h @@ -216,6 +216,23 @@ inline void readBoolText(bool & x, ReadBuffer & buf) x = tmp != '0'; } +inline void readBoolTextWord(bool & x, ReadBuffer & buf) +{ + if (buf.eof()) + throwReadAfterEOF(); + + if (*buf.position() == 't') + { + assertString("true", buf); + x = true; + } + else + { + assertString("false", buf); + x = false; + } +} + template ReturnType readIntTextImpl(T & x, ReadBuffer & buf) { diff --git a/dbms/src/Interpreters/InterpreterAlterQuery.cpp b/dbms/src/Interpreters/InterpreterAlterQuery.cpp index c515e2ed96a..a7929d9b6d7 100644 --- a/dbms/src/Interpreters/InterpreterAlterQuery.cpp +++ b/dbms/src/Interpreters/InterpreterAlterQuery.cpp @@ -70,7 +70,7 @@ BlockIO InterpreterAlterQuery::execute() case PartitionCommand::RESHARD_PARTITION: table->reshardPartitions(query_ptr, database_name, command.partition, command.last_partition, command.weighted_zookeeper_paths, command.sharding_key_expr, command.do_copy, - command.coordinator, context.getSettingsRef()); + command.coordinator, context); break; case PartitionCommand::DROP_COLUMN: diff --git a/dbms/src/Interpreters/InterpreterInsertQuery.cpp b/dbms/src/Interpreters/InterpreterInsertQuery.cpp index 5f943f94a36..b4739e74d8a 100644 --- a/dbms/src/Interpreters/InterpreterInsertQuery.cpp +++ b/dbms/src/Interpreters/InterpreterInsertQuery.cpp @@ -33,7 +33,7 @@ namespace ErrorCodes } -InterpreterInsertQuery::InterpreterInsertQuery(ASTPtr query_ptr_, Context & context_) +InterpreterInsertQuery::InterpreterInsertQuery(const ASTPtr & query_ptr_, const Context & context_) : query_ptr(query_ptr_), context(context_) { ProfileEvents::increment(ProfileEvents::InsertQuery); @@ -124,7 +124,7 @@ BlockIO InterpreterInsertQuery::execute() res.in = interpreter_select.execute().in; res.in = std::make_shared(res.in, res.in_sample, res.out_sample); - res.in = std::make_shared(context, res.in, res.in_sample, res.out_sample); + res.in = std::make_shared(context, res.in, res.out_sample); res.in = std::make_shared(res.in, out); } diff --git a/dbms/src/Interpreters/InterpreterInsertQuery.h b/dbms/src/Interpreters/InterpreterInsertQuery.h index 4f2e5c465e8..4f7695ffb60 100644 --- a/dbms/src/Interpreters/InterpreterInsertQuery.h +++ b/dbms/src/Interpreters/InterpreterInsertQuery.h @@ -15,7 +15,7 @@ namespace DB class InterpreterInsertQuery : public IInterpreter { public: - InterpreterInsertQuery(ASTPtr query_ptr_, Context & context_); + InterpreterInsertQuery(const ASTPtr & query_ptr_, const Context & context_); /** Подготовить запрос к выполнению. Вернуть потоки блоков * - поток, в который можно писать данные для выполнения запроса, если INSERT; diff --git a/dbms/src/Storages/IStorage.h b/dbms/src/Storages/IStorage.h index 5773204ea20..e8e007f52e1 100644 --- a/dbms/src/Storages/IStorage.h +++ b/dbms/src/Storages/IStorage.h @@ -185,7 +185,7 @@ public: * It is guaranteed that the table structure will not change over the lifetime of the returned streams (that is, there will not be ALTER, RENAME and DROP). */ virtual BlockOutputStreamPtr write( - ASTPtr query, + const ASTPtr & query, const Settings & settings) { throw Exception("Method write is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); @@ -251,11 +251,12 @@ public: /** Run the RESHARD PARTITION query. */ - virtual void reshardPartitions(ASTPtr query, const String & database_name, + virtual void reshardPartitions( + const ASTPtr & query, const String & database_name, const Field & first_partition, const Field & last_partition, const WeightedZooKeeperPaths & weighted_zookeeper_paths, const ASTPtr & sharding_key_expr, bool do_copy, const Field & coordinator, - const Settings & settings) + Context & context) { throw Exception("Method reshardPartition is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); } diff --git a/dbms/src/Storages/MergeTree/MergeTreeBlockInputStream.cpp b/dbms/src/Storages/MergeTree/MergeTreeBlockInputStream.cpp index 2eeb2ac0f71..3242bff0b9b 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeBlockInputStream.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeBlockInputStream.cpp @@ -150,11 +150,11 @@ try std::reverse(remaining_mark_ranges.begin(), remaining_mark_ranges.end()); auto size_predictor = (preferred_block_size_bytes == 0) ? nullptr - : std::make_shared(data_part, columns, pre_columns); + : std::make_unique(data_part, ordered_names, data_part->storage.getSampleBlock()); task = std::make_unique(data_part, remaining_mark_ranges, part_index_in_query, ordered_names, column_name_set, columns, pre_columns, remove_prewhere_column, should_reorder, - size_predictor); + std::move(size_predictor)); if (!reader) { diff --git a/dbms/src/Storages/MergeTree/MergeTreeBlockReadUtils.cpp b/dbms/src/Storages/MergeTree/MergeTreeBlockReadUtils.cpp index aebdd42f831..dd785ec742d 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeBlockReadUtils.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeBlockReadUtils.cpp @@ -67,30 +67,29 @@ MergeTreeReadTask::MergeTreeReadTask( const MergeTreeData::DataPartPtr & data_part, const MarkRanges & mark_ranges, const std::size_t part_index_in_query, const Names & ordered_names, const NameSet & column_name_set, const NamesAndTypesList & columns, const NamesAndTypesList & pre_columns, const bool remove_prewhere_column, const bool should_reorder, - const MergeTreeBlockSizePredictorPtr & size_predictor) + MergeTreeBlockSizePredictorPtr && size_predictor) : data_part{data_part}, mark_ranges{mark_ranges}, part_index_in_query{part_index_in_query}, ordered_names{ordered_names}, column_name_set{column_name_set}, columns{columns}, pre_columns{pre_columns}, -remove_prewhere_column{remove_prewhere_column}, should_reorder{should_reorder}, size_predictor{size_predictor} +remove_prewhere_column{remove_prewhere_column}, should_reorder{should_reorder}, size_predictor{std::move(size_predictor)} {} MergeTreeReadTask::~MergeTreeReadTask() = default; MergeTreeBlockSizePredictor::MergeTreeBlockSizePredictor( - const MergeTreeData::DataPartPtr & data_part_, - const NamesAndTypesList & columns, - const NamesAndTypesList & pre_columns) + const MergeTreeData::DataPartPtr & data_part_, const Names & columns, const Block & sample_block) : data_part(data_part_) { - auto add_column = [&] (const NameAndTypePair & column) + for (const String & column_name : columns) { - ColumnPtr column_data = column.type->createColumn(); - const auto column_checksum = data_part->tryGetBinChecksum(column.name); + const auto column_checksum = data_part->tryGetBinChecksum(column_name); - /// There are no data files, column will be const - if (!column_checksum) + /// There are no column data files, column will be const + if (!column_checksum || !data_part->hasColumnFiles(column_name)) return; + const ColumnPtr & column_data = sample_block.getByName(column_name).column; + if (column_data->isFixed()) { fixed_columns_bytes_per_row += column_data->sizeOfField(); @@ -98,19 +97,13 @@ MergeTreeBlockSizePredictor::MergeTreeBlockSizePredictor( else { ColumnInfo info; - info.name = column.name; + info.name = column_name; info.bytes_per_row_global = column_checksum->uncompressed_size; dynamic_columns_infos.emplace_back(info); } }; - for (const NameAndTypePair & column : pre_columns) - add_column(column); - - for (const NameAndTypePair & column : columns) - add_column(column); - size_t rows_approx = data_part->getExactSizeRows(); bytes_per_row_global = fixed_columns_bytes_per_row; @@ -133,7 +126,7 @@ void MergeTreeBlockSizePredictor::startBlock() } -/// FIXME: add last_read_row_in_part parameter to take into account gaps between adjacent ranges +/// TODO: add last_read_row_in_part parameter to take into account gaps between adjacent ranges void MergeTreeBlockSizePredictor::update(const Block & block, double decay) { size_t new_rows = block.rows(); diff --git a/dbms/src/Storages/MergeTree/MergeTreeBlockReadUtils.h b/dbms/src/Storages/MergeTree/MergeTreeBlockReadUtils.h index bb6fa5261a0..3cd552f6065 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeBlockReadUtils.h +++ b/dbms/src/Storages/MergeTree/MergeTreeBlockReadUtils.h @@ -10,7 +10,7 @@ struct MergeTreeReadTask; struct MergeTreeBlockSizePredictor; using MergeTreeReadTaskPtr = std::unique_ptr; -using MergeTreeBlockSizePredictorPtr = std::shared_ptr; +using MergeTreeBlockSizePredictorPtr = std::unique_ptr; /** If some of the requested columns are not in the part, @@ -50,7 +50,7 @@ struct MergeTreeReadTask const MergeTreeData::DataPartPtr & data_part, const MarkRanges & mark_ranges, const std::size_t part_index_in_query, const Names & ordered_names, const NameSet & column_name_set, const NamesAndTypesList & columns, const NamesAndTypesList & pre_columns, const bool remove_prewhere_column, const bool should_reorder, - const MergeTreeBlockSizePredictorPtr & size_predictor); + MergeTreeBlockSizePredictorPtr && size_predictor); virtual ~MergeTreeReadTask(); }; @@ -58,10 +58,7 @@ struct MergeTreeReadTask struct MergeTreeBlockSizePredictor { - MergeTreeBlockSizePredictor( - const MergeTreeData::DataPartPtr & data_part_, - const NamesAndTypesList & columns, - const NamesAndTypesList & pre_columns); + MergeTreeBlockSizePredictor(const MergeTreeData::DataPartPtr & data_part_, const Names & columns, const Block & sample_block); /// Reset some values for correct statistics calculating void startBlock(); diff --git a/dbms/src/Storages/MergeTree/MergeTreeReadPool.cpp b/dbms/src/Storages/MergeTree/MergeTreeReadPool.cpp index 823d40f01c3..2b2774feb22 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeReadPool.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeReadPool.cpp @@ -108,12 +108,12 @@ MergeTreeReadTaskPtr MergeTreeReadPool::getTask(const std::size_t min_marks_to_r } auto curr_task_size_predictor = !per_part_size_predictor[part_idx] ? nullptr - : std::make_shared(*per_part_size_predictor[part_idx]); /// make a copy + : std::make_unique(*per_part_size_predictor[part_idx]); /// make a copy return std::make_unique( part.data_part, ranges_to_get_from_part, part.part_index_in_query, column_names, per_part_column_name_set[part_idx], per_part_columns[part_idx], per_part_pre_columns[part_idx], - per_part_remove_prewhere_column[part_idx], per_part_should_reorder[part_idx], curr_task_size_predictor); + per_part_remove_prewhere_column[part_idx], per_part_should_reorder[part_idx], std::move(curr_task_size_predictor)); } @@ -163,6 +163,7 @@ std::vector MergeTreeReadPool::fillPerPartInfo( const bool check_columns) { std::vector per_part_sum_marks; + Block sample_block = data.getSampleBlock(); for (const auto i : ext::range(0, parts.size())) { @@ -247,8 +248,8 @@ std::vector MergeTreeReadPool::fillPerPartInfo( if (predict_block_size_bytes) { - per_part_size_predictor.emplace_back(std::make_shared( - part.data_part, per_part_columns.back(), per_part_pre_columns.back())); + per_part_size_predictor.emplace_back(std::make_unique( + part.data_part, column_names, sample_block)); } else per_part_size_predictor.emplace_back(nullptr); diff --git a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeLogEntry.h b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeLogEntry.h index 077be41a693..a68288eafe9 100644 --- a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeLogEntry.h +++ b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeLogEntry.h @@ -31,11 +31,11 @@ struct ReplicatedMergeTreeLogEntryData { enum Type { - EMPTY, /// Not used. - GET_PART, /// Get the part from another replica. - MERGE_PARTS, /// Merge the parts. - DROP_RANGE, /// Delete the parts in the specified month in the specified number range. - ATTACH_PART, /// Move a part from the `detached` or `unreplicated` directory. + EMPTY, /// Not used. + GET_PART, /// Get the part from another replica. + MERGE_PARTS, /// Merge the parts. + DROP_RANGE, /// Delete the parts in the specified month in the specified number range. + ATTACH_PART, /// Move a part from the `detached` or `unreplicated` directory. }; String typeToString() const @@ -43,9 +43,9 @@ struct ReplicatedMergeTreeLogEntryData switch (type) { case ReplicatedMergeTreeLogEntryData::GET_PART: return "GET_PART"; - case ReplicatedMergeTreeLogEntryData::MERGE_PARTS: return "MERGE_PARTS"; - case ReplicatedMergeTreeLogEntryData::DROP_RANGE: return "DROP_RANGE"; - case ReplicatedMergeTreeLogEntryData::ATTACH_PART: return "ATTACH_PART"; + case ReplicatedMergeTreeLogEntryData::MERGE_PARTS: return "MERGE_PARTS"; + case ReplicatedMergeTreeLogEntryData::DROP_RANGE: return "DROP_RANGE"; + case ReplicatedMergeTreeLogEntryData::ATTACH_PART: return "ATTACH_PART"; default: throw Exception("Unknown log entry type: " + DB::toString(type), ErrorCodes::LOGICAL_ERROR); } @@ -63,7 +63,8 @@ struct ReplicatedMergeTreeLogEntryData /// The name of resulting part. /// For DROP_RANGE, the name of a non-existent part. You need to remove all the parts covered by it. String new_part_name; - String block_id; /// For parts of level zero, the block identifier for deduplication (node name in /blocks/). + String block_id; /// For parts of level zero, the block identifier for deduplication (node name in /blocks/). + mutable String actual_new_part_name; /// GET_PART could actually fetch a part covering 'new_part_name'. Strings parts_to_merge; bool deduplicate = false; /// Do deduplicate on merge diff --git a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp index e55235e3732..32e551388ff 100644 --- a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp +++ b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp @@ -448,6 +448,60 @@ void ReplicatedMergeTreeQueue::removeGetsAndMergesInRange(zkutil::ZooKeeperPtr z } +bool ReplicatedMergeTreeQueue::isNotCoveredByFuturePartsImpl(const String & new_part_name, String & out_reason) +{ + /// mutex should been already acquired + + /// Let's check if the same part is now being created by another action. + if (future_parts.count(new_part_name)) + { + out_reason = "Not executing log entry for part " + new_part_name + + " because another log entry for the same part is being processed. This shouldn't happen often."; + return false; + + /** When the corresponding action is completed, then `isNotCoveredByFuturePart` next time, will succeed, + * and queue element will be processed. + * Immediately in the `executeLogEntry` function it will be found that we already have a part, + * and queue element will be immediately treated as processed. + */ + } + + /// A more complex check is whether another part is currently created by other action that will cover this part. + /// NOTE The above is redundant, but left for a more convenient message in the log. + ActiveDataPartSet::Part result_part; + ActiveDataPartSet::parsePartName(new_part_name, result_part); + + /// It can slow down when the size of `future_parts` is large. But it can not be large, since `BackgroundProcessingPool` is limited. + for (const auto & future_part_name : future_parts) + { + ActiveDataPartSet::Part future_part; + ActiveDataPartSet::parsePartName(future_part_name, future_part); + + if (future_part.contains(result_part)) + { + out_reason = "Not executing log entry for part " + new_part_name + + " because another log entry for covering part " + future_part_name + " is being processed."; + return false; + } + } + + return true; +} + +bool ReplicatedMergeTreeQueue::addFuturePartIfNotCoveredByThem(const String & part_name, const LogEntry & entry, String & reject_reason) +{ + std::lock_guard lock(mutex); + + if (isNotCoveredByFuturePartsImpl(part_name, reject_reason)) + { + CurrentlyExecuting::setActualPartName(entry, part_name, *this); + return true; + } + + return false; +} + + bool ReplicatedMergeTreeQueue::shouldExecuteLogEntry( const LogEntry & entry, String & out_postpone_reason, @@ -458,41 +512,10 @@ bool ReplicatedMergeTreeQueue::shouldExecuteLogEntry( if (entry.type == LogEntry::MERGE_PARTS || entry.type == LogEntry::GET_PART || entry.type == LogEntry::ATTACH_PART) { - /// Let's check if the same part is now being created by another action. - if (future_parts.count(entry.new_part_name)) + if (!isNotCoveredByFuturePartsImpl(entry.new_part_name, out_postpone_reason)) { - String reason = "Not executing log entry for part " + entry.new_part_name - + " because another log entry for the same part is being processed. This shouldn't happen often."; - LOG_DEBUG(log, reason); - out_postpone_reason = reason; + LOG_DEBUG(log, out_postpone_reason); return false; - - /** When the corresponding action is completed, then `shouldExecuteLogEntry` next time, will succeed, - * and queue element will be processed. - * Immediately in the `executeLogEntry` function it will be found that we already have a part, - * and queue element will be immediately treated as processed. - */ - } - - /// A more complex check is whether another part is currently created by other action that will cover this part. - /// NOTE The above is redundant, but left for a more convenient message in the log. - ActiveDataPartSet::Part result_part; - ActiveDataPartSet::parsePartName(entry.new_part_name, result_part); - - /// It can slow down when the size of `future_parts` is large. But it can not be large, since `BackgroundProcessingPool` is limited. - for (const auto & future_part_name : future_parts) - { - ActiveDataPartSet::Part future_part; - ActiveDataPartSet::parsePartName(future_part_name, future_part); - - if (future_part.contains(result_part)) - { - String reason = "Not executing log entry for part " + entry.new_part_name - + " because another log entry for covering part " + future_part_name + " is being processed."; - LOG_DEBUG(log, reason); - out_postpone_reason = reason; - return false; - } } } @@ -560,6 +583,24 @@ ReplicatedMergeTreeQueue::CurrentlyExecuting::CurrentlyExecuting(ReplicatedMerge throw Exception("Tagging already tagged future part " + entry->new_part_name + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); } + +void ReplicatedMergeTreeQueue::CurrentlyExecuting::setActualPartName(const ReplicatedMergeTreeLogEntry & entry, + const String & actual_part_name, ReplicatedMergeTreeQueue & queue) +{ + if (!entry.actual_new_part_name.empty()) + throw Exception("Entry actual part isn't empty yet. This is a bug.", ErrorCodes::LOGICAL_ERROR); + + entry.actual_new_part_name = actual_part_name; + + /// Check if it is the same (and already added) part. + if (entry.actual_new_part_name == entry.new_part_name) + return; + + if (!queue.future_parts.insert(entry.actual_new_part_name).second) + throw Exception("Attaching already exsisting future part " + entry.actual_new_part_name + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); +} + + ReplicatedMergeTreeQueue::CurrentlyExecuting::~CurrentlyExecuting() { std::lock_guard lock(queue.mutex); @@ -569,6 +610,14 @@ ReplicatedMergeTreeQueue::CurrentlyExecuting::~CurrentlyExecuting() if (!queue.future_parts.erase(entry->new_part_name)) LOG_ERROR(queue.log, "Untagging already untagged future part " + entry->new_part_name + ". This is a bug."); + + if (!entry->actual_new_part_name.empty()) + { + if (entry->actual_new_part_name != entry->new_part_name && !queue.future_parts.erase(entry->actual_new_part_name)) + LOG_ERROR(queue.log, "Untagging already untagged future part " + entry->actual_new_part_name + ". This is a bug."); + + entry->actual_new_part_name.clear(); + } } @@ -578,7 +627,7 @@ ReplicatedMergeTreeQueue::SelectedEntry ReplicatedMergeTreeQueue::selectEntryToP LogEntryPtr entry; - for (Queue::iterator it = queue.begin(); it != queue.end(); ++it) + for (auto it = queue.begin(); it != queue.end(); ++it) { if ((*it)->currently_executing) continue; diff --git a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h index 65be4e3d24b..f1054a67e80 100644 --- a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h +++ b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h @@ -88,6 +88,11 @@ private: */ bool shouldExecuteLogEntry(const LogEntry & entry, String & out_postpone_reason, MergeTreeDataMerger & merger, MergeTreeData & data); + /** Check that part isn't in currently generating parts and isn't covered by them. + * Should be called under queue's mutex. + */ + bool isNotCoveredByFuturePartsImpl(const String & new_part_name, String & out_reason); + /// After removing the queue element, update the insertion times in the RAM. Running under queue_mutex. /// Returns information about what times have changed - this information can be passed to updateTimesInZooKeeper. void updateTimesOnRemoval(const LogEntryPtr & entry, bool & min_unprocessed_insert_time_changed, bool & max_processed_insert_time_changed); @@ -107,6 +112,10 @@ private: /// Created only in the selectEntryToProcess function. It is called under mutex. CurrentlyExecuting(ReplicatedMergeTreeQueue::LogEntryPtr & entry, ReplicatedMergeTreeQueue & queue); + + /// In case of fetch, we determine actual part during the execution, so we need to update entry. It is called under mutex. + static void setActualPartName(const ReplicatedMergeTreeLogEntry & entry, const String & actual_part_name, + ReplicatedMergeTreeQueue & queue); public: ~CurrentlyExecuting(); }; @@ -165,6 +174,11 @@ public: /// Prohibit merges in the specified range. void disableMergesInRange(const String & part_name); + /** Check that part isn't in currently generating parts and isn't covered by them and add it to future_parts. + * Locks queue's mutex. + */ + bool addFuturePartIfNotCoveredByThem(const String & part_name, const LogEntry & entry, String & reject_reason); + /// Count the number of merges in the queue. size_t countMerges(); diff --git a/dbms/src/Storages/StorageBuffer.cpp b/dbms/src/Storages/StorageBuffer.cpp index f6759c0f594..454c646e6ac 100644 --- a/dbms/src/Storages/StorageBuffer.cpp +++ b/dbms/src/Storages/StorageBuffer.cpp @@ -347,7 +347,7 @@ private: }; -BlockOutputStreamPtr StorageBuffer::write(ASTPtr query, const Settings & settings) +BlockOutputStreamPtr StorageBuffer::write(const ASTPtr & query, const Settings & settings) { return std::make_shared(*this); } diff --git a/dbms/src/Storages/StorageBuffer.h b/dbms/src/Storages/StorageBuffer.h index 90608253dec..3b229e8af12 100644 --- a/dbms/src/Storages/StorageBuffer.h +++ b/dbms/src/Storages/StorageBuffer.h @@ -76,7 +76,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; /// Resets all buffers to the subordinate table. void shutdown() override; diff --git a/dbms/src/Storages/StorageDistributed.cpp b/dbms/src/Storages/StorageDistributed.cpp index 3b2093be68b..42d315f14b3 100644 --- a/dbms/src/Storages/StorageDistributed.cpp +++ b/dbms/src/Storages/StorageDistributed.cpp @@ -253,7 +253,7 @@ BlockInputStreams StorageDistributed::read( } -BlockOutputStreamPtr StorageDistributed::write(ASTPtr query, const Settings & settings) +BlockOutputStreamPtr StorageDistributed::write(const ASTPtr & query, const Settings & settings) { auto cluster = context.getCluster(cluster_name); @@ -293,11 +293,12 @@ void StorageDistributed::shutdown() } -void StorageDistributed::reshardPartitions(ASTPtr query, const String & database_name, +void StorageDistributed::reshardPartitions( + const ASTPtr & query, const String & database_name, const Field & first_partition, const Field & last_partition, const WeightedZooKeeperPaths & weighted_zookeeper_paths, const ASTPtr & sharding_key_expr, bool do_copy, const Field & coordinator, - const Settings & settings) + Context & context) { auto & resharding_worker = context.getReshardingWorker(); if (!resharding_worker.isStarted()) @@ -377,7 +378,7 @@ void StorageDistributed::reshardPartitions(ASTPtr query, const String & database ClusterProxy::AlterQueryConstructor alter_query_constructor; BlockInputStreams streams = ClusterProxy::Query{alter_query_constructor, cluster, alter_query_ptr, - context, settings, enable_shard_multiplexing}.execute(); + context, context.getSettingsRef(), enable_shard_multiplexing}.execute(); /// This callback is called if an exception has occurred while attempting to read /// a block from a shard. This is to avoid a potential deadlock if other shards are @@ -399,7 +400,7 @@ void StorageDistributed::reshardPartitions(ASTPtr query, const String & database }; streams[0] = std::make_shared>( - streams, nullptr, settings.max_distributed_connections, exception_callback); + streams, nullptr, context.getSettingsRef().max_distributed_connections, exception_callback); streams.resize(1); auto stream_ptr = dynamic_cast(&*streams[0]); diff --git a/dbms/src/Storages/StorageDistributed.h b/dbms/src/Storages/StorageDistributed.h index 4cac100568b..592220ea479 100644 --- a/dbms/src/Storages/StorageDistributed.h +++ b/dbms/src/Storages/StorageDistributed.h @@ -75,7 +75,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; void drop() override {} void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name) override { name = new_table_name; } @@ -85,11 +85,12 @@ public: void shutdown() override; - void reshardPartitions(ASTPtr query, const String & database_name, + void reshardPartitions( + const ASTPtr & query, const String & database_name, const Field & first_partition, const Field & last_partition, const WeightedZooKeeperPaths & weighted_zookeeper_paths, const ASTPtr & sharding_key_expr, bool do_copy, const Field & coordinator, - const Settings & settings) override; + Context & context) override; /// From each replica, get a description of the corresponding local table. BlockInputStreams describe(const Context & context, const Settings & settings); @@ -145,7 +146,7 @@ private: String remote_database; String remote_table; - Context & context; + const Context & context; Logger * log = &Logger::get("StorageDistributed"); /// Used to implement TableFunctionRemote. diff --git a/dbms/src/Storages/StorageFile.cpp b/dbms/src/Storages/StorageFile.cpp index e18bcd3b1d9..931d4d3475e 100644 --- a/dbms/src/Storages/StorageFile.cpp +++ b/dbms/src/Storages/StorageFile.cpp @@ -218,7 +218,7 @@ private: }; BlockOutputStreamPtr StorageFile::write( - ASTPtr query, + const ASTPtr & query, const Settings & settings) { return std::make_shared(*this); diff --git a/dbms/src/Storages/StorageFile.h b/dbms/src/Storages/StorageFile.h index ecc1a9726fc..27f3040bfdf 100644 --- a/dbms/src/Storages/StorageFile.h +++ b/dbms/src/Storages/StorageFile.h @@ -82,7 +82,7 @@ public: unsigned threads = 1) override; BlockOutputStreamPtr write( - ASTPtr query, + const ASTPtr & query, const Settings & settings) override; void drop() override; diff --git a/dbms/src/Storages/StorageLog.cpp b/dbms/src/Storages/StorageLog.cpp index 344aa234258..a685cd9fd69 100644 --- a/dbms/src/Storages/StorageLog.cpp +++ b/dbms/src/Storages/StorageLog.cpp @@ -919,7 +919,7 @@ BlockInputStreams StorageLog::read( BlockOutputStreamPtr StorageLog::write( - ASTPtr query, const Settings & settings) + const ASTPtr & query, const Settings & settings) { loadMarks(); return std::make_shared(*this); diff --git a/dbms/src/Storages/StorageLog.h b/dbms/src/Storages/StorageLog.h index 0479230a583..6a64a23e69e 100644 --- a/dbms/src/Storages/StorageLog.h +++ b/dbms/src/Storages/StorageLog.h @@ -78,7 +78,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name) override; diff --git a/dbms/src/Storages/StorageMaterializedView.cpp b/dbms/src/Storages/StorageMaterializedView.cpp index 7fa4d357e7c..5e9846bb6aa 100644 --- a/dbms/src/Storages/StorageMaterializedView.cpp +++ b/dbms/src/Storages/StorageMaterializedView.cpp @@ -119,7 +119,7 @@ BlockInputStreams StorageMaterializedView::read( return getInnerTable()->read(column_names, query, context, settings, processed_stage, max_block_size, threads); } -BlockOutputStreamPtr StorageMaterializedView::write(ASTPtr query, const Settings & settings) +BlockOutputStreamPtr StorageMaterializedView::write(const ASTPtr & query, const Settings & settings) { return getInnerTable()->write(query, settings); } diff --git a/dbms/src/Storages/StorageMaterializedView.h b/dbms/src/Storages/StorageMaterializedView.h index 25489d0fab6..c538e35f67e 100644 --- a/dbms/src/Storages/StorageMaterializedView.h +++ b/dbms/src/Storages/StorageMaterializedView.h @@ -37,7 +37,7 @@ public: bool supportsParallelReplicas() const override { return getInnerTable()->supportsParallelReplicas(); } bool supportsIndexForIn() const override { return getInnerTable()->supportsIndexForIn(); } - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; void drop() override; bool optimize(const String & partition, bool final, bool deduplicate, const Settings & settings) override; diff --git a/dbms/src/Storages/StorageMemory.cpp b/dbms/src/Storages/StorageMemory.cpp index baa707b7fb5..b72d0e78f8e 100644 --- a/dbms/src/Storages/StorageMemory.cpp +++ b/dbms/src/Storages/StorageMemory.cpp @@ -149,7 +149,7 @@ BlockInputStreams StorageMemory::read( BlockOutputStreamPtr StorageMemory::write( - ASTPtr query, const Settings & settings) + const ASTPtr & query, const Settings & settings) { return std::make_shared(*this); } diff --git a/dbms/src/Storages/StorageMemory.h b/dbms/src/Storages/StorageMemory.h index e75dde8aec1..c024f0428bc 100644 --- a/dbms/src/Storages/StorageMemory.h +++ b/dbms/src/Storages/StorageMemory.h @@ -54,7 +54,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; void drop() override; void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name) override { name = new_table_name; } diff --git a/dbms/src/Storages/StorageMerge.cpp b/dbms/src/Storages/StorageMerge.cpp index b1a6e1a1298..82f3ad99b32 100644 --- a/dbms/src/Storages/StorageMerge.cpp +++ b/dbms/src/Storages/StorageMerge.cpp @@ -183,7 +183,7 @@ BlockInputStreams StorageMerge::read( for (auto & stream : source_streams) { /// will throw if some columns not convertible - stream = std::make_shared(context, stream, table->getSampleBlock(), getSampleBlock()); + stream = std::make_shared(context, stream, getSampleBlock()); } } else @@ -212,7 +212,7 @@ BlockInputStreams StorageMerge::read( if (!streams.empty()) { /// will throw if some columns not convertible - stream = std::make_shared(context, stream, table->getSampleBlock(), getSampleBlock()); + stream = std::make_shared(context, stream, getSampleBlock()); } return stream; })); diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index c2e8e3037df..ddb6ccc59a9 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -124,7 +124,7 @@ BlockInputStreams StorageMergeTree::read( return reader.read(column_names, query, context, settings, processed_stage, max_block_size, threads, nullptr, 0); } -BlockOutputStreamPtr StorageMergeTree::write(ASTPtr query, const Settings & settings) +BlockOutputStreamPtr StorageMergeTree::write(const ASTPtr & query, const Settings & settings) { return std::make_shared(*this); } diff --git a/dbms/src/Storages/StorageMergeTree.h b/dbms/src/Storages/StorageMergeTree.h index 6d4ab228f00..725ba3a0272 100644 --- a/dbms/src/Storages/StorageMergeTree.h +++ b/dbms/src/Storages/StorageMergeTree.h @@ -83,7 +83,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; /** Perform the next step in combining the parts. */ diff --git a/dbms/src/Storages/StorageNull.h b/dbms/src/Storages/StorageNull.h index b11405b6434..1778a05c29b 100644 --- a/dbms/src/Storages/StorageNull.h +++ b/dbms/src/Storages/StorageNull.h @@ -46,7 +46,7 @@ public: return { std::make_shared() }; } - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override { return std::make_shared(); } diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 4a2bbc0801e..c75a56815bc 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -1251,8 +1251,7 @@ bool StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) if (do_fetch) { - String covering_part; - String replica = findReplicaHavingCoveringPart(entry.new_part_name, true, covering_part); + String replica = findReplicaHavingCoveringPart(entry, true); static std::atomic_uint total_fetches {0}; if (data.settings.replicated_max_parallel_fetches && total_fetches >= data.settings.replicated_max_parallel_fetches) @@ -1422,7 +1421,7 @@ bool StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) try { - if (!fetchPart(covering_part, zookeeper_path + "/replicas/" + replica, false, entry.quorum)) + if (!fetchPart(entry.actual_new_part_name, zookeeper_path + "/replicas/" + replica, false, entry.quorum)) return false; } catch (Exception & e) @@ -1565,13 +1564,19 @@ void StorageReplicatedMergeTree::queueUpdatingThread() { setThreadName("ReplMTQueueUpd"); + bool update_in_progress = false; while (!shutdown_called) { - last_queue_update_attempt_time.store(time(nullptr)); + if (!update_in_progress) + { + last_queue_update_start_time.store(time(nullptr)); + update_in_progress = true; + } try { pullLogsToQueue(queue_updating_event); - last_successful_queue_update_attempt_time.store(time(nullptr)); + last_queue_update_finish_time.store(time(nullptr)); + update_in_progress = false; queue_updating_event->wait(); } catch (const zkutil::KeeperException & e) @@ -1965,7 +1970,7 @@ String StorageReplicatedMergeTree::findReplicaHavingPart(const String & part_nam } -String StorageReplicatedMergeTree::findReplicaHavingCoveringPart(const String & part_name, bool active, String & out_covering_part_name) +String StorageReplicatedMergeTree::findReplicaHavingCoveringPart(const LogEntry & entry, bool active) { auto zookeeper = getZooKeeper(); Strings replicas = zookeeper->getChildren(zookeeper_path + "/replicas"); @@ -1985,10 +1990,9 @@ String StorageReplicatedMergeTree::findReplicaHavingCoveringPart(const String & Strings parts = zookeeper->getChildren(zookeeper_path + "/replicas/" + replica + "/parts"); for (const String & part_on_replica : parts) { - if (part_on_replica == part_name || ActiveDataPartSet::contains(part_on_replica, part_name)) + if (part_on_replica == entry.new_part_name || ActiveDataPartSet::contains(part_on_replica, entry.new_part_name)) { - if (largest_part_found.empty() - || ActiveDataPartSet::contains(part_on_replica, largest_part_found)) + if (largest_part_found.empty() || ActiveDataPartSet::contains(part_on_replica, largest_part_found)) { largest_part_found = part_on_replica; } @@ -1997,7 +2001,23 @@ String StorageReplicatedMergeTree::findReplicaHavingCoveringPart(const String & if (!largest_part_found.empty()) { - out_covering_part_name = largest_part_found; + bool the_same_part = largest_part_found == entry.new_part_name; + + /// Make a check in case if selected part differs from source part + if (!the_same_part) + { + String reject_reason; + if (!queue.addFuturePartIfNotCoveredByThem(largest_part_found, entry, reject_reason)) + { + LOG_INFO(log, "Will not fetch part " << largest_part_found << " covering " << entry.new_part_name << ". " << reject_reason); + return {}; + } + } + else + { + entry.actual_new_part_name = entry.new_part_name; + } + return replica; } } @@ -2389,7 +2409,7 @@ void StorageReplicatedMergeTree::assertNotReadonly() const } -BlockOutputStreamPtr StorageReplicatedMergeTree::write(ASTPtr query, const Settings & settings) +BlockOutputStreamPtr StorageReplicatedMergeTree::write(const ASTPtr & query, const Settings & settings) { assertNotReadonly(); @@ -3261,16 +3281,16 @@ time_t StorageReplicatedMergeTree::getAbsoluteDelay() const time_t max_processed_insert_time = 0; queue.getInsertTimes(min_unprocessed_insert_time, max_processed_insert_time); - /// Load in reverse order to preserve consistency (successful update time must be after update start time). - /// Probably doesn't matter because pullLogsToQueue() acts as a barrier. - time_t successful_queue_update_time = last_successful_queue_update_attempt_time.load(); - time_t queue_update_time = last_queue_update_attempt_time.load(); + /// Load start time, then finish time to avoid reporting false delay when start time is updated + /// between loading of two variables. + time_t queue_update_start_time = last_queue_update_start_time.load(); + time_t queue_update_finish_time = last_queue_update_finish_time.load(); time_t current_time = time(nullptr); - if (!queue_update_time) + if (!queue_update_finish_time) { - /// We have not even tried to update queue yet (perhaps replica is readonly). + /// We have not updated queue even once yet (perhaps replica is readonly). /// As we have no info about the current state of replication log, return effectively infinite delay. return current_time; } @@ -3279,12 +3299,12 @@ time_t StorageReplicatedMergeTree::getAbsoluteDelay() const /// There are some unprocessed insert entries in queue. return (current_time > min_unprocessed_insert_time) ? (current_time - min_unprocessed_insert_time) : 0; } - else if (queue_update_time > successful_queue_update_time) + else if (queue_update_start_time > queue_update_finish_time) { /// Queue is empty, but there are some in-flight or failed queue update attempts /// (likely because of problems with connecting to ZooKeeper). /// Return the time passed since last attempt. - return (current_time > queue_update_time) ? (current_time - queue_update_time) : 0; + return (current_time > queue_update_start_time) ? (current_time - queue_update_start_time) : 0; } else { @@ -3523,11 +3543,12 @@ void StorageReplicatedMergeTree::freezePartition(const Field & partition, const } -void StorageReplicatedMergeTree::reshardPartitions(ASTPtr query, const String & database_name, +void StorageReplicatedMergeTree::reshardPartitions( + const ASTPtr & query, const String & database_name, const Field & first_partition, const Field & last_partition, const WeightedZooKeeperPaths & weighted_zookeeper_paths, const ASTPtr & sharding_key_expr, bool do_copy, const Field & coordinator, - const Settings & settings) + Context & context) { auto & resharding_worker = context.getReshardingWorker(); if (!resharding_worker.isStarted()) diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.h b/dbms/src/Storages/StorageReplicatedMergeTree.h index 2fc4a322ca9..c07ce8e580b 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.h +++ b/dbms/src/Storages/StorageReplicatedMergeTree.h @@ -131,7 +131,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; bool optimize(const String & partition, bool final, bool deduplicate, const Settings & settings) override; @@ -142,11 +142,12 @@ public: void fetchPartition(const Field & partition, const String & from, const Settings & settings) override; void freezePartition(const Field & partition, const String & with_name, const Settings & settings) override; - void reshardPartitions(ASTPtr query, const String & database_name, + void reshardPartitions( + const ASTPtr & query, const String & database_name, const Field & first_partition, const Field & last_partition, const WeightedZooKeeperPaths & weighted_zookeeper_paths, const ASTPtr & sharding_key_expr, bool do_copy, const Field & coordinator, - const Settings & settings) override; + Context & context) override; /** Removes a replica from ZooKeeper. If there are no other replicas, it deletes the entire table from ZooKeeper. */ @@ -245,8 +246,8 @@ private: * In ZK entries in chronological order. Here it is not necessary. */ ReplicatedMergeTreeQueue queue; - std::atomic last_queue_update_attempt_time{0}; - std::atomic last_successful_queue_update_attempt_time{0}; + std::atomic last_queue_update_start_time{0}; + std::atomic last_queue_update_finish_time{0}; /** /replicas/me/is_active. */ @@ -445,10 +446,10 @@ private: /** Find replica having specified part or any part that covers it. * If active = true, consider only active replicas. - * If found, returns replica name and set 'out_covering_part_name' to name of found largest covering part. + * If found, returns replica name and set 'entry->actual_new_part_name' to name of found largest covering part. * If not found, returns empty string. */ - String findReplicaHavingCoveringPart(const String & part_name, bool active, String & out_covering_part_name); + String findReplicaHavingCoveringPart(const LogEntry & entry, bool active); /** Download the specified part from the specified replica. * If `to_detached`, the part is placed in the `detached` directory. @@ -457,11 +458,11 @@ private: */ bool fetchPart(const String & part_name, const String & replica_path, bool to_detached, size_t quorum); + /// Required only to avoid races between executeLogEntry and fetchPartition std::unordered_set currently_fetching_parts; std::mutex currently_fetching_parts_mutex; - /** With the quorum being tracked, add a replica to the quorum for the part. - */ + /// With the quorum being tracked, add a replica to the quorum for the part. void updateQuorum(const String & part_name); AbandonableLockInZooKeeper allocateBlockNumber(const String & month_name); diff --git a/dbms/src/Storages/StorageSet.cpp b/dbms/src/Storages/StorageSet.cpp index 0697e2fe090..063e40ecef1 100644 --- a/dbms/src/Storages/StorageSet.cpp +++ b/dbms/src/Storages/StorageSet.cpp @@ -66,7 +66,7 @@ void SetOrJoinBlockOutputStream::writeSuffix() -BlockOutputStreamPtr StorageSetOrJoinBase::write(ASTPtr query, const Settings & settings) +BlockOutputStreamPtr StorageSetOrJoinBase::write(const ASTPtr & query, const Settings & settings) { ++increment; return std::make_shared(*this, path, path + "tmp/", toString(increment) + ".bin"); diff --git a/dbms/src/Storages/StorageSet.h b/dbms/src/Storages/StorageSet.h index 1732044bccf..f01bacfea07 100644 --- a/dbms/src/Storages/StorageSet.h +++ b/dbms/src/Storages/StorageSet.h @@ -25,7 +25,7 @@ public: void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; protected: StorageSetOrJoinBase( diff --git a/dbms/src/Storages/StorageStripeLog.cpp b/dbms/src/Storages/StorageStripeLog.cpp index 9b367d76775..d887b595a55 100644 --- a/dbms/src/Storages/StorageStripeLog.cpp +++ b/dbms/src/Storages/StorageStripeLog.cpp @@ -283,7 +283,7 @@ BlockInputStreams StorageStripeLog::read( BlockOutputStreamPtr StorageStripeLog::write( - ASTPtr query, const Settings & settings) + const ASTPtr & query, const Settings & settings) { return std::make_shared(*this); } diff --git a/dbms/src/Storages/StorageStripeLog.h b/dbms/src/Storages/StorageStripeLog.h index 4265ab36abe..5681da2f9e0 100644 --- a/dbms/src/Storages/StorageStripeLog.h +++ b/dbms/src/Storages/StorageStripeLog.h @@ -53,7 +53,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name) override; diff --git a/dbms/src/Storages/StorageTinyLog.cpp b/dbms/src/Storages/StorageTinyLog.cpp index a3e67142179..916a81f4437 100644 --- a/dbms/src/Storages/StorageTinyLog.cpp +++ b/dbms/src/Storages/StorageTinyLog.cpp @@ -363,7 +363,7 @@ void TinyLogBlockOutputStream::addStream(const String & name, const IDataType & void TinyLogBlockOutputStream::writeData(const String & name, const IDataType & type, const IColumn & column, - OffsetColumns & offset_columns, size_t level) + OffsetColumns & offset_columns, size_t level) { if (type.isNullable()) { @@ -557,7 +557,7 @@ BlockInputStreams StorageTinyLog::read( BlockOutputStreamPtr StorageTinyLog::write( - ASTPtr query, const Settings & settings) + const ASTPtr & query, const Settings & settings) { return std::make_shared(*this); } diff --git a/dbms/src/Storages/StorageTinyLog.h b/dbms/src/Storages/StorageTinyLog.h index 842bce6ef2e..8d5141bdaec 100644 --- a/dbms/src/Storages/StorageTinyLog.h +++ b/dbms/src/Storages/StorageTinyLog.h @@ -53,7 +53,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query, const Settings & settings) override; + BlockOutputStreamPtr write(const ASTPtr & query, const Settings & settings) override; void drop() override; diff --git a/dbms/src/TableFunctions/TableFunctionMerge.cpp b/dbms/src/TableFunctions/TableFunctionMerge.cpp index 1b1fa5159ba..64521f96e57 100644 --- a/dbms/src/TableFunctions/TableFunctionMerge.cpp +++ b/dbms/src/TableFunctions/TableFunctionMerge.cpp @@ -72,8 +72,8 @@ StoragePtr TableFunctionMerge::execute(ASTPtr ast_function, Context & context) c args[0] = evaluateConstantExpressionOrIdentidierAsLiteral(args[0], context); args[1] = evaluateConstantExpressionAsLiteral(args[1], context); - String source_database = static_cast(*args[0]).value.safeGet(); - String table_name_regexp = static_cast(*args[1]).value.safeGet(); + String source_database = static_cast(*args[0]).value.safeGet(); + String table_name_regexp = static_cast(*args[1]).value.safeGet(); return StorageMerge::create( getName(), diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 5cb094333fe..0c60b99d36d 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -30,213 +30,213 @@ MSG_SKIPPED = OP_SQUARE_BRACKET + colored(" SKIPPED ", "cyan", attrs=['bold']) + def main(args): - SERVER_DIED = False + SERVER_DIED = False - def is_data_present(): - proc = Popen(args.client, stdin=PIPE, stdout=PIPE, stderr=PIPE) - (stdout, stderr) = proc.communicate("EXISTS TABLE test.hits") - if proc.returncode != 0: - raise CalledProcessError(proc.returncode, args.client, stderr) + def is_data_present(): + proc = Popen(args.client, stdin=PIPE, stdout=PIPE, stderr=PIPE) + (stdout, stderr) = proc.communicate("EXISTS TABLE test.hits") + if proc.returncode != 0: + raise CalledProcessError(proc.returncode, args.client, stderr) - return stdout.startswith('1') + return stdout.startswith('1') - def dump_report(destination, suite, test_case, report): - if destination is not None: - destination_file = os.path.join(destination, suite, test_case + ".xml") - destination_dir = os.path.dirname(destination_file) - if not os.path.exists(destination_dir): - os.makedirs(destination_dir) - with open(destination_file, 'w') as report_file: - report_root = et.Element("testsuites", attrib = {'name': 'ClickHouse Tests'}) - report_suite = et.Element("testsuite", attrib = {"name": suite}) - report_suite.append(report) - report_root.append(report_suite) - report_file.write(et.tostring(report_root, encoding = "UTF-8", xml_declaration=True, pretty_print=True)) + def dump_report(destination, suite, test_case, report): + if destination is not None: + destination_file = os.path.join(destination, suite, test_case + ".xml") + destination_dir = os.path.dirname(destination_file) + if not os.path.exists(destination_dir): + os.makedirs(destination_dir) + with open(destination_file, 'w') as report_file: + report_root = et.Element("testsuites", attrib = {'name': 'ClickHouse Tests'}) + report_suite = et.Element("testsuite", attrib = {"name": suite}) + report_suite.append(report) + report_root.append(report_suite) + report_file.write(et.tostring(report_root, encoding = "UTF-8", xml_declaration=True, pretty_print=True)) - if args.zookeeper is None: - try: - check_call(['grep', '-q', '::"', '/etc/clickhouse-server/config-preprocessed.xml'], ) - args.shard = True - except CalledProcessError: - # TODO: false here after setting ::1 - args.shard = True + if args.shard is None: + try: + check_call(['grep', '-qE', '"127.0.0.2|::"', '/etc/clickhouse-server/config-preprocessed.xml'], ) + args.shard = True + except CalledProcessError: + # TODO: false here after setting ::1 + args.shard = True - base_dir = os.path.abspath(args.queries) + base_dir = os.path.abspath(args.queries) - failures_total = 0 + failures_total = 0 - for suite in sorted(os.listdir(base_dir)): - if SERVER_DIED: - break + for suite in sorted(os.listdir(base_dir)): + if SERVER_DIED: + break - suite_dir = os.path.join(base_dir, suite) - suite_re_obj = re.search('^[0-9]+_(.*)$', suite) - if not suite_re_obj: #skip .gitignore and so on - continue - suite = suite_re_obj.group(1) - if os.path.isdir(suite_dir): - print("\nRunning {} tests.\n".format(suite)) + suite_dir = os.path.join(base_dir, suite) + suite_re_obj = re.search('^[0-9]+_(.*)$', suite) + if not suite_re_obj: #skip .gitignore and so on + continue + suite = suite_re_obj.group(1) + if os.path.isdir(suite_dir): + print("\nRunning {} tests.\n".format(suite)) - failures = 0 - if 'stateful' in suite and not is_data_present(): - print("Won't run stateful tests because test data wasn't loaded. See README.txt.") - continue + failures = 0 + if 'stateful' in suite and not is_data_present(): + print("Won't run stateful tests because test data wasn't loaded. See README.txt.") + continue - for case in sorted(filter(lambda case: re.search(args.test, case) if args.test else True, os.listdir(suite_dir))): - if SERVER_DIED: - break + for case in sorted(filter(lambda case: re.search(args.test, case) if args.test else True, os.listdir(suite_dir))): + if SERVER_DIED: + break - case_file = os.path.join(suite_dir, case) - if os.path.isfile(case_file) and (case.endswith('.sh') or case.endswith('.py') or case.endswith('.sql')): - (name, ext) = os.path.splitext(case) - report_testcase = et.Element("testcase", attrib = {"name": name}) + case_file = os.path.join(suite_dir, case) + if os.path.isfile(case_file) and (case.endswith('.sh') or case.endswith('.py') or case.endswith('.sql')): + (name, ext) = os.path.splitext(case) + report_testcase = et.Element("testcase", attrib = {"name": name}) - print "{0:70}".format(name + ": "), - sys.stdout.flush() + print "{0:70}".format(name + ": "), + sys.stdout.flush() - if not args.zookeeper and 'zookeeper' in name: - report_testcase.append(et.Element("skipped", attrib = {"message": "no zookeeper"})) - print(MSG_SKIPPED + " - no zookeeper") - elif not args.shard and 'shard' in name: - report_testcase.append(et.Element("skipped", attrib = {"message": "no shard"})) - print(MSG_SKIPPED + " - no shard") - else: - reference_file = os.path.join(suite_dir, name) + '.reference' - stdout_file = os.path.join(suite_dir, name) + '.stdout' - stderr_file = os.path.join(suite_dir, name) + '.stderr' + if not args.zookeeper and 'zookeeper' in name: + report_testcase.append(et.Element("skipped", attrib = {"message": "no zookeeper"})) + print(MSG_SKIPPED + " - no zookeeper") + elif not args.shard and 'shard' in name: + report_testcase.append(et.Element("skipped", attrib = {"message": "no shard"})) + print(MSG_SKIPPED + " - no shard") + else: + reference_file = os.path.join(suite_dir, name) + '.reference' + stdout_file = os.path.join(suite_dir, name) + '.stdout' + stderr_file = os.path.join(suite_dir, name) + '.stderr' - if ext == '.sql': - command = "{0} --multiquery < {1} > {2} 2> {3}".format(args.client, case_file, stdout_file, stderr_file) - else: - command = "{0} > {1} 2> {2}".format(case_file, stdout_file, stderr_file) + if ext == '.sql': + command = "{0} --multiquery < {1} > {2} 2> {3}".format(args.client, case_file, stdout_file, stderr_file) + else: + command = "{0} > {1} 2> {2}".format(case_file, stdout_file, stderr_file) - proc = Popen(command, shell = True) - start_time = datetime.now() - while (datetime.now() - start_time).total_seconds() < args.timeout and proc.poll() is None: - sleep(0) + proc = Popen(command, shell = True) + start_time = datetime.now() + while (datetime.now() - start_time).total_seconds() < args.timeout and proc.poll() is None: + sleep(0) - if proc.returncode is None: - try: - proc.kill() - except OSError as e: - if e.errno != ESRCH: - raise + if proc.returncode is None: + try: + proc.kill() + except OSError as e: + if e.errno != ESRCH: + raise - failure = et.Element("failure", attrib = {"message": "Timeout"}) - report_testcase.append(failure) + failure = et.Element("failure", attrib = {"message": "Timeout"}) + report_testcase.append(failure) - failures = failures + 1 - print("{0} - Timeout!".format(MSG_FAIL)) - else: - stdout = open(stdout_file, 'r').read() if os.path.exists(stdout_file) else '' - stdout = unicode(stdout, errors='replace', encoding='utf-8') - stderr = open(stderr_file, 'r').read() if os.path.exists(stderr_file) else '' - stderr = unicode(stderr, errors='replace', encoding='utf-8') + failures = failures + 1 + print("{0} - Timeout!".format(MSG_FAIL)) + else: + stdout = open(stdout_file, 'r').read() if os.path.exists(stdout_file) else '' + stdout = unicode(stdout, errors='replace', encoding='utf-8') + stderr = open(stderr_file, 'r').read() if os.path.exists(stderr_file) else '' + stderr = unicode(stderr, errors='replace', encoding='utf-8') - if proc.returncode != 0: - failure = et.Element("failure", attrib = {"message": "return code {}".format(proc.returncode)}) - report_testcase.append(failure) + if proc.returncode != 0: + failure = et.Element("failure", attrib = {"message": "return code {}".format(proc.returncode)}) + report_testcase.append(failure) - stdout_element = et.Element("system-out") - stdout_element.text = et.CDATA(stdout) - report_testcase.append(stdout_element) + stdout_element = et.Element("system-out") + stdout_element.text = et.CDATA(stdout) + report_testcase.append(stdout_element) - failures = failures + 1 - print("{0} - return code {1}".format(MSG_FAIL, proc.returncode)) + failures = failures + 1 + print("{0} - return code {1}".format(MSG_FAIL, proc.returncode)) - if stderr: - stderr_element = et.Element("system-err") - stderr_element.text = et.CDATA(stderr) - report_testcase.append(stderr_element) - print(stderr) + if stderr: + stderr_element = et.Element("system-err") + stderr_element.text = et.CDATA(stderr) + report_testcase.append(stderr_element) + print(stderr) - if args.stop and ('Connection refused' in stderr or 'Attempt to read after eof' in stderr) and not 'Received exception from server' in stderr: - SERVER_DIED = True + if args.stop and ('Connection refused' in stderr or 'Attempt to read after eof' in stderr) and not 'Received exception from server' in stderr: + SERVER_DIED = True - elif stderr: - failure = et.Element("failure", attrib = {"message": "having stderror"}) - report_testcase.append(failure) + elif stderr: + failure = et.Element("failure", attrib = {"message": "having stderror"}) + report_testcase.append(failure) - stderr_element = et.Element("system-err") - stderr_element.text = et.CDATA(stderr) - report_testcase.append(stderr_element) + stderr_element = et.Element("system-err") + stderr_element.text = et.CDATA(stderr) + report_testcase.append(stderr_element) - failures = failures + 1 - print("{0} - having stderror:\n{1}".format(MSG_FAIL, stderr.encode('utf-8'))) - elif 'Exception' in stdout: - failure = et.Element("error", attrib = {"message": "having exception"}) - report_testcase.append(failure) + failures = failures + 1 + print("{0} - having stderror:\n{1}".format(MSG_FAIL, stderr.encode('utf-8'))) + elif 'Exception' in stdout: + failure = et.Element("error", attrib = {"message": "having exception"}) + report_testcase.append(failure) - stdout_element = et.Element("system-out") - stdout_element.text = et.CDATA(stdout) - report_testcase.append(stdout_element) + stdout_element = et.Element("system-out") + stdout_element.text = et.CDATA(stdout) + report_testcase.append(stdout_element) - failures = failures + 1 - print("{0} - having exception:\n{1}".format(MSG_FAIL, stdout.encode('utf-8'))) - elif not os.path.isfile(reference_file): - skipped = et.Element("skipped", attrib = {"message": "no reference file"}) - report_testcase.append(skipped) - print("{0} - no reference file".format(MSG_UNKNOWN)) - else: - result_is_different = subprocess.call(['cmp', '-s', reference_file, stdout_file], stdout = PIPE) + failures = failures + 1 + print("{0} - having exception:\n{1}".format(MSG_FAIL, stdout.encode('utf-8'))) + elif not os.path.isfile(reference_file): + skipped = et.Element("skipped", attrib = {"message": "no reference file"}) + report_testcase.append(skipped) + print("{0} - no reference file".format(MSG_UNKNOWN)) + else: + result_is_different = subprocess.call(['cmp', '-s', reference_file, stdout_file], stdout = PIPE) - if result_is_different: - (diff, _) = Popen(['diff', '--side-by-side', reference_file, stdout_file], stdout = PIPE).communicate() - diff = unicode(diff, errors='replace', encoding='utf-8') + if result_is_different: + (diff, _) = Popen(['diff', '--side-by-side', reference_file, stdout_file], stdout = PIPE).communicate() + diff = unicode(diff, errors='replace', encoding='utf-8') - failure = et.Element("failure", attrib = {"message": "result differs with reference"}) - report_testcase.append(failure) + failure = et.Element("failure", attrib = {"message": "result differs with reference"}) + report_testcase.append(failure) - stdout_element = et.Element("system-out") - stdout_element.text = et.CDATA(diff) - report_testcase.append(stdout_element) + stdout_element = et.Element("system-out") + stdout_element.text = et.CDATA(diff) + report_testcase.append(stdout_element) - failures = failures + 1 - print("{0} - result differs with reference:\n{1}".format(MSG_FAIL, diff.encode('utf-8'))) - else: - print(MSG_OK) - if os.path.exists(stdout_file): - os.remove(stdout_file) - if os.path.exists(stderr_file): - os.remove(stderr_file) + failures = failures + 1 + print("{0} - result differs with reference:\n{1}".format(MSG_FAIL, diff.encode('utf-8'))) + else: + print(MSG_OK) + if os.path.exists(stdout_file): + os.remove(stdout_file) + if os.path.exists(stderr_file): + os.remove(stderr_file) - dump_report(args.output, suite, name, report_testcase) + dump_report(args.output, suite, name, report_testcase) - failures_total = failures_total + failures + failures_total = failures_total + failures - if failures_total > 0: - print(colored("\nHaving {0} errors!".format(failures_total), "red", attrs=["bold"])) - sys.exit(1) - else: - print(colored("\nAll tests passed.", "green", attrs=["bold"])) - sys.exit(0) + if failures_total > 0: + print(colored("\nHaving {0} errors!".format(failures_total), "red", attrs=["bold"])) + sys.exit(1) + else: + print(colored("\nAll tests passed.", "green", attrs=["bold"])) + sys.exit(0) if __name__ == '__main__': - parser = ArgumentParser(description = 'ClickHouse functional tests') - parser.add_argument('-q', '--queries', default = 'queries', help = 'Path to queries dir') - parser.add_argument('-c', '--client', default = 'clickhouse-client', help = 'Client program') - parser.add_argument('-o', '--output', help = 'Output xUnit compliant test report directory') - parser.add_argument('-t', '--timeout', type = int, default = 600, help = 'Timeout for each test case in seconds') - parser.add_argument('test', nargs = '?', help = 'Optional test case name regex') + parser = ArgumentParser(description = 'ClickHouse functional tests') + parser.add_argument('-q', '--queries', default = 'queries', help = 'Path to queries dir') + parser.add_argument('-c', '--client', default = 'clickhouse-client', help = 'Client program') + parser.add_argument('-o', '--output', help = 'Output xUnit compliant test report directory') + parser.add_argument('-t', '--timeout', type = int, default = 600, help = 'Timeout for each test case in seconds') + parser.add_argument('test', nargs = '?', help = 'Optional test case name regex') - group = parser.add_mutually_exclusive_group(required = False) - group.add_argument('--zookeeper', action = 'store_true', default = None, dest = 'zookeeper', help = 'Run zookeeper related tests') - group.add_argument('--no-zookeeper', action = 'store_false', default = None, dest = 'zookeeper', help = 'Do not run zookeeper related tests') - group.add_argument('--shard', action = 'store_true', default = None, dest = 'shard', help = 'Run sharding related tests (required to clickhouse-server listen 127.0.0.2 127.0.0.3)') - group.add_argument('--no-shard', action = 'store_false', default = None, dest = 'shard', help = 'Do not run shard related tests') - group.add_argument('--stop', action = 'store_true', default = None, dest = 'stop', help = 'Stop on network errors ') + group = parser.add_mutually_exclusive_group(required = False) + group.add_argument('--zookeeper', action = 'store_true', default = None, dest = 'zookeeper', help = 'Run zookeeper related tests') + group.add_argument('--no-zookeeper', action = 'store_false', default = None, dest = 'zookeeper', help = 'Do not run zookeeper related tests') + group.add_argument('--shard', action = 'store_true', default = None, dest = 'shard', help = 'Run sharding related tests (required to clickhouse-server listen 127.0.0.2 127.0.0.3)') + group.add_argument('--no-shard', action = 'store_false', default = None, dest = 'shard', help = 'Do not run shard related tests') + group.add_argument('--stop', action = 'store_true', default = None, dest = 'stop', help = 'Stop on network errors ') - args = parser.parse_args() + args = parser.parse_args() - main(args) + main(args) diff --git a/dbms/tests/queries/0_stateless/00443_preferred_block_size_bytes.reference b/dbms/tests/queries/0_stateless/00443_preferred_block_size_bytes.reference index 8bd0b03103c..3f8ae43dbed 100644 --- a/dbms/tests/queries/0_stateless/00443_preferred_block_size_bytes.reference +++ b/dbms/tests/queries/0_stateless/00443_preferred_block_size_bytes.reference @@ -1,6 +1,8 @@ 1 0 2 0 45 0 -PASSED -PASSED +1000 +2000 250500 250500 1000 +PASSED +PASSED diff --git a/dbms/tests/queries/0_stateless/00443_preferred_block_size_bytes.sh b/dbms/tests/queries/0_stateless/00443_preferred_block_size_bytes.sh index f99d4865aeb..18742d94021 100755 --- a/dbms/tests/queries/0_stateless/00443_preferred_block_size_bytes.sh +++ b/dbms/tests/queries/0_stateless/00443_preferred_block_size_bytes.sh @@ -10,6 +10,25 @@ clickhouse-client --preferred_block_size_bytes=52 -q "SELECT DISTINCT blockSize( clickhouse-client --preferred_block_size_bytes=90 -q "SELECT DISTINCT blockSize(), ignore(p) FROM test.preferred_block_size_bytes" clickhouse-client -q "DROP TABLE IF EXISTS test.preferred_block_size_bytes" +# PREWHERE using empty column + +clickhouse-client -q "DROP TABLE IF EXISTS test.pbs" +clickhouse-client -q "CREATE TABLE test.pbs (p Date, i UInt64, sa Array(String)) ENGINE = MergeTree(p, p, 100)" +clickhouse-client -q "INSERT INTO test.pbs (p, i, sa) SELECT toDate(i % 30) AS p, number AS i, ['a'] AS sa FROM system.numbers LIMIT 1000" +clickhouse-client -q "ALTER TABLE test.pbs ADD COLUMN s UInt8 DEFAULT 0" +clickhouse-client --preferred_block_size_bytes=100000 -q "SELECT count() FROM test.pbs PREWHERE s = 0" +clickhouse-client -q "INSERT INTO test.pbs (p, i, sa) SELECT toDate(i % 30) AS p, number AS i, ['a'] AS sa FROM system.numbers LIMIT 1000" +clickhouse-client --preferred_block_size_bytes=100000 -q "SELECT count() FROM test.pbs PREWHERE s = 0" +clickhouse-client -q "DROP TABLE test.pbs" + +# Nullable PREWHERE + +clickhouse-client -q "DROP TABLE IF EXISTS test.nullable_prewhere" +clickhouse-client -q "CREATE TABLE test.nullable_prewhere (p Date, f Nullable(UInt64), d UInt64) ENGINE = MergeTree(p, p, 8)" +clickhouse-client -q "INSERT INTO test.nullable_prewhere SELECT toDate(0) AS p, if(number % 2 = 0, CAST(number AS Nullable(UInt64)), CAST(NULL AS Nullable(UInt64))) AS f, number as d FROM system.numbers LIMIT 1001" +clickhouse-client -q "SELECT sum(d), sum(f), max(d) FROM test.nullable_prewhere PREWHERE NOT isNull(f)" +clickhouse-client -q "DROP TABLE IF EXISTS test.nullable_prewhere" + # Depend on 00282_merging test pushd `dirname $0` > /dev/null @@ -25,11 +44,3 @@ cat "$SCRIPTDIR"/00282_merging.sql | clickhouse-client --preferred_block_size_by cmp "$SCRIPTDIR"/00282_merging.reference preferred_block_size_bytes.stdout && echo PASSED || echo FAILED rm preferred_block_size_bytes.stdout - -# Nullable PREWHERE - -clickhouse-client -q "DROP TABLE IF EXISTS test.nullable_prewhere" -clickhouse-client -q "CREATE TABLE test.nullable_prewhere (p Date, f Nullable(UInt64), d UInt64) ENGINE = MergeTree(p, p, 8)" -clickhouse-client -q "INSERT INTO test.nullable_prewhere SELECT toDate(0) AS p, if(number % 2 = 0, CAST(number AS Nullable(UInt64)), CAST(NULL AS Nullable(UInt64))) AS f, number as d FROM system.numbers LIMIT 1001" -clickhouse-client -q "SELECT sum(d), sum(f), max(d) FROM test.nullable_prewhere PREWHERE NOT isNull(f)" -clickhouse-client -q "DROP TABLE IF EXISTS test.nullable_prewhere" diff --git a/dbms/tests/queries/0_stateless/00458_merge_type_cast.reference b/dbms/tests/queries/0_stateless/00458_merge_type_cast.reference index 196ab90f4a9..dc8784b555e 100644 --- a/dbms/tests/queries/0_stateless/00458_merge_type_cast.reference +++ b/dbms/tests/queries/0_stateless/00458_merge_type_cast.reference @@ -26,8 +26,8 @@ 1 1 Array(UInt32) | Array(UInt64) -[1] -[1] -[4294967290] -[4294967290] -[4294967299] +[1] [0] +[1] [0] +[4294967290] [4294967290] +[4294967290] [4294967290] +[4294967299] [4294967299] diff --git a/dbms/tests/queries/0_stateless/00458_merge_type_cast.sql b/dbms/tests/queries/0_stateless/00458_merge_type_cast.sql index 6c4a7bd7661..d1359cad93f 100644 --- a/dbms/tests/queries/0_stateless/00458_merge_type_cast.sql +++ b/dbms/tests/queries/0_stateless/00458_merge_type_cast.sql @@ -5,18 +5,18 @@ DROP TABLE IF EXISTS test.u32; DROP TABLE IF EXISTS test.u64; DROP TABLE IF EXISTS test.merge_32_64; -CREATE TABLE test.u32 (x UInt32) ENGINE = Memory; -CREATE TABLE test.u64 (x UInt64) ENGINE = Memory; +CREATE TABLE test.u32 (x UInt32, y UInt32 DEFAULT x) ENGINE = Memory; +CREATE TABLE test.u64 (x UInt64, y UInt64 DEFAULT x) ENGINE = Memory; CREATE TABLE test.merge_32_64 (x UInt64) ENGINE = Merge(test, 'u32|u64'); -INSERT INTO test.u32 VALUES (1); -INSERT INTO test.u64 VALUES (1); +INSERT INTO test.u32 (x) VALUES (1); +INSERT INTO test.u64 (x) VALUES (1); -INSERT INTO test.u32 VALUES (4294967290); -INSERT INTO test.u64 VALUES (4294967290); +INSERT INTO test.u32 (x) VALUES (4294967290); +INSERT INTO test.u64 (x) VALUES (4294967290); --now inserts 3. maybe need out of range check? --INSERT INTO test.u32 VALUES (4294967299); -INSERT INTO test.u64 VALUES (4294967299); +INSERT INTO test.u64 (x) VALUES (4294967299); select ' = 1:'; SELECT x FROM test.merge_32_64 WHERE x = 1; @@ -117,20 +117,20 @@ DROP TABLE IF EXISTS test.one; DROP TABLE IF EXISTS test.two; DROP TABLE IF EXISTS test.merge_one_two; -CREATE TABLE test.one (x Array(UInt32)) ENGINE = Memory; -CREATE TABLE test.two (x Array(UInt64)) ENGINE = Memory; -CREATE TABLE test.merge_one_two (x Array(UInt64)) ENGINE = Merge(test, 'one|two'); +CREATE TABLE test.one (x Array(UInt32), z String DEFAULT '', y Array(UInt32)) ENGINE = Memory; +CREATE TABLE test.two (x Array(UInt64), z String DEFAULT '', y Array(UInt64)) ENGINE = Memory; +CREATE TABLE test.merge_one_two (x Array(UInt64), z String, y Array(UInt64)) ENGINE = Merge(test, 'one|two'); -INSERT INTO test.one VALUES ([1]); -INSERT INTO test.two VALUES ([1]); -INSERT INTO test.one VALUES ([4294967290]); -INSERT INTO test.two VALUES ([4294967290]); -INSERT INTO test.one VALUES ([4294967299]); -INSERT INTO test.two VALUES ([4294967299]); +INSERT INTO test.one (x, y) VALUES ([1], [0]); +INSERT INTO test.two (x, y) VALUES ([1], [0]); +INSERT INTO test.one (x, y) VALUES ([4294967290], [4294967290]); +INSERT INTO test.two (x, y) VALUES ([4294967290], [4294967290]); +INSERT INTO test.one (x, y) VALUES ([4294967299], [4294967299]); +INSERT INTO test.two (x, y) VALUES ([4294967299], [4294967299]); -SELECT x FROM test.merge_one_two WHERE x IN (1); -SELECT x FROM test.merge_one_two WHERE x IN (4294967290); -SELECT x FROM test.merge_one_two WHERE x IN (4294967299); +SELECT x, y FROM test.merge_one_two WHERE x IN (1); +SELECT x, y FROM test.merge_one_two WHERE x IN (4294967290); +SELECT x, y FROM test.merge_one_two WHERE x IN (4294967299); DROP TABLE IF EXISTS test.one; DROP TABLE IF EXISTS test.two; diff --git a/doc/administration/zookeeper.md b/doc/administration/zookeeper.md new file mode 100644 index 00000000000..9a69467d04f --- /dev/null +++ b/doc/administration/zookeeper.md @@ -0,0 +1,138 @@ +As of 2017-05-20, we have the following configuration in production: + +ZooKeeper version is 3.5.1. + +zoo.cfg: + +``` +# http://hadoop.apache.org/zookeeper/docs/current/zookeeperAdmin.html + +# The number of milliseconds of each tick +tickTime=2000 +# The number of ticks that the initial +# synchronization phase can take +initLimit=30000 +# The number of ticks that can pass between +# sending a request and getting an acknowledgement +syncLimit=10 + +maxClientCnxns=2000 + +maxSessionTimeout=60000000 +# the directory where the snapshot is stored. +dataDir=/opt/zookeeper/{{ cluster['name'] }}/data +# Place the dataLogDir to a separate physical disc for better performance +dataLogDir=/opt/zookeeper/{{ cluster['name'] }}/logs + +autopurge.snapRetainCount=10 +autopurge.purgeInterval=1 + + +# To avoid seeks ZooKeeper allocates space in the transaction log file in +# blocks of preAllocSize kilobytes. The default block size is 64M. One reason +# for changing the size of the blocks is to reduce the block size if snapshots +# are taken more often. (Also, see snapCount). +preAllocSize=131072 + +# Clients can submit requests faster than ZooKeeper can process them, +# especially if there are a lot of clients. To prevent ZooKeeper from running +# out of memory due to queued requests, ZooKeeper will throttle clients so that +# there is no more than globalOutstandingLimit outstanding requests in the +# system. The default limit is 1,000.ZooKeeper logs transactions to a +# transaction log. After snapCount transactions are written to a log file a +# snapshot is started and a new transaction log file is started. The default +# snapCount is 10,000. +snapCount=3000000 + +# If this option is defined, requests will be will logged to a trace file named +# traceFile.year.month.day. +#traceFile= + +# Leader accepts client connections. Default value is "yes". The leader machine +# coordinates updates. For higher update throughput at thes slight expense of +# read throughput the leader can be configured to not accept clients and focus +# on coordination. +leaderServes=yes + +standaloneEnabled=false +dynamicConfigFile=/etc/zookeeper-{{ cluster['name'] }}/conf/zoo.cfg.dynamic +``` + +Java version: + +``` +Java(TM) SE Runtime Environment (build 1.8.0_25-b17) +Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode) +``` + +Java parameters: + +``` +NAME=zookeeper-{{ cluster['name'] }} +ZOOCFGDIR=/etc/$NAME/conf + +# TODO this is really ugly +# How to find out, which jars are needed? +# seems, that log4j requires the log4j.properties file to be in the classpath +CLASSPATH="$ZOOCFGDIR:/usr/build/classes:/usr/build/lib/*.jar:/usr/share/zookeeper/zookeeper-3.5.1-metrika.jar:/usr/share/zookeeper/slf4j-log4j12-1.7.5.jar:/usr/share/zookeeper/slf4j-api-1.7.5.jar:/usr/share/zookeeper/servlet-api-2.5-20081211.jar:/usr/share/zookeeper/netty-3.7.0.Final.jar:/usr/share/zookeeper/log4j-1.2.16.jar:/usr/share/zookeeper/jline-2.11.jar:/usr/share/zookeeper/jetty-util-6.1.26.jar:/usr/share/zookeeper/jetty-6.1.26.jar:/usr/share/zookeeper/javacc.jar:/usr/share/zookeeper/jackson-mapper-asl-1.9.11.jar:/usr/share/zookeeper/jackson-core-asl-1.9.11.jar:/usr/share/zookeeper/commons-cli-1.2.jar:/usr/src/java/lib/*.jar:/usr/etc/zookeeper" + +ZOOCFG="$ZOOCFGDIR/zoo.cfg" +ZOO_LOG_DIR=/var/log/$NAME +USER=zookeeper +GROUP=zookeeper +PIDDIR=/var/run/$NAME +PIDFILE=$PIDDIR/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME +JAVA=/usr/bin/java +ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain" +ZOO_LOG4J_PROP="INFO,ROLLINGFILE" +JMXLOCALONLY=false +JAVA_OPTS="-Xms{{ cluster.get('xms','128M') }} \ + -Xmx{{ cluster.get('xmx','1G') }} \ + -Xloggc:/var/log/$NAME/zookeeper-gc.log \ + -XX:+UseGCLogFileRotation \ + -XX:NumberOfGCLogFiles=16 \ + -XX:GCLogFileSize=16M \ + -verbose:gc \ + -XX:+PrintGCTimeStamps \ + -XX:+PrintGCDateStamps \ + -XX:+PrintGCDetails + -XX:+PrintTenuringDistribution \ + -XX:+PrintGCApplicationStoppedTime \ + -XX:+PrintGCApplicationConcurrentTime \ + -XX:+PrintSafepointStatistics \ + -XX:+UseParNewGC \ + -XX:+UseConcMarkSweepGC \ +-XX:+CMSParallelRemarkEnabled" +``` + +Salt init: + +``` +description "zookeeper-{{ cluster['name'] }} centralized coordination service" + +start on runlevel [2345] +stop on runlevel [!2345] + +respawn + +limit nofile 8192 8192 + +pre-start script + [ -r "/etc/zookeeper-{{ cluster['name'] }}/conf/environment" ] || exit 0 + . /etc/zookeeper-{{ cluster['name'] }}/conf/environment + [ -d $ZOO_LOG_DIR ] || mkdir -p $ZOO_LOG_DIR + chown $USER:$GROUP $ZOO_LOG_DIR +end script + +script + . /etc/zookeeper-{{ cluster['name'] }}/conf/environment + [ -r /etc/default/zookeeper ] && . /etc/default/zookeeper + if [ -z "$JMXDISABLE" ]; then + JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY" + fi + exec start-stop-daemon --start -c $USER --exec $JAVA --name zookeeper-{{ cluster['name'] }} \ + -- -cp $CLASSPATH $JAVA_OPTS -Dzookeeper.log.dir=${ZOO_LOG_DIR} \ + -Dzookeeper.root.logger=${ZOO_LOG4J_PROP} $ZOOMAIN $ZOOCFG +end script +``` diff --git a/doc/build.md b/doc/build.md index 6a23b1dd48f..256848e4082 100644 --- a/doc/build.md +++ b/doc/build.md @@ -133,7 +133,7 @@ For production releases, GCC is used. ### Run release script. ``` rm -f ../clickhouse*.deb -./release --standalone +./release ``` You will find built packages in parent directory. diff --git a/doc/getting_started/getting_started_en.md b/doc/getting_started/getting_started_en.md index 8210b98d5b4..5b9c8208780 100644 --- a/doc/getting_started/getting_started_en.md +++ b/doc/getting_started/getting_started_en.md @@ -33,7 +33,7 @@ https://github.com/yandex/ClickHouse/blob/master/doc/build.md Install required packages. After that let's run the following command from directory with source code of ClickHouse: ``` -~/ClickHouse$ ./release --standalone +~/ClickHouse$ ./release ``` The build successfully completed: diff --git a/doc/getting_started/getting_started_ru.md b/doc/getting_started/getting_started_ru.md index 776e6672c07..e31fbba54d9 100644 --- a/doc/getting_started/getting_started_ru.md +++ b/doc/getting_started/getting_started_ru.md @@ -33,7 +33,7 @@ https://github.com/yandex/ClickHouse/blob/master/doc/build.md Установим необходимые пакеты. После этого выполним следующую команду из директории с исходными кодами ClickHouse: ``` -~/ClickHouse$ ./release --standalone +~/ClickHouse$ ./release ``` Сборка успешно завершена: diff --git a/doc/presentations b/doc/presentations new file mode 160000 index 00000000000..b0b67a13a5a --- /dev/null +++ b/doc/presentations @@ -0,0 +1 @@ +Subproject commit b0b67a13a5ab20eac11496393acbe1e6c4f9c925 diff --git a/docker/client/Dockerfile b/docker/client/Dockerfile index e28997ecef8..aab26246d29 100644 --- a/docker/client/Dockerfile +++ b/docker/client/Dockerfile @@ -4,7 +4,7 @@ ARG repository="deb https://repo.yandex.ru/clickhouse/xenial/ dists/stable/main/ ARG version=\* RUN apt-get update && \ - apt-get install -y apt-transport-https && \ + apt-get install -y apt-transport-https tzdata && \ mkdir -p /etc/apt/sources.list.d && \ echo $repository | tee /etc/apt/sources.list.d/clickhouse.list && \ apt-get update && \ diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 599e944d0ff..531e727360d 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -4,7 +4,7 @@ ARG repository="deb https://repo.yandex.ru/clickhouse/xenial/ dists/stable/main/ ARG version=\* RUN apt-get update && \ - apt-get install -y apt-transport-https && \ + apt-get install -y apt-transport-https tzdata && \ mkdir -p /etc/apt/sources.list.d && \ echo $repository | tee /etc/apt/sources.list.d/clickhouse.list && \ apt-get update && \ diff --git a/docs/Makefile b/docs/Makefile index 8d8aca5abd2..95802e63d01 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build @@ -15,11 +15,17 @@ endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) ru +ALLSPHINXOPTS = $(SPHINXOPTS) en # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) ru -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext +.PHONY: default help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +default: + $(SPHINXBUILD) -b html $(SPHINXOPTS) ru $(BUILDDIR)/docs/ru + $(SPHINXBUILD) -b html $(SPHINXOPTS) en $(BUILDDIR)/docs/en + SPHINX_TEMPLATES=../_singlehtml_templates $(SPHINXBUILD) -b singlehtml $(SPHINXOPTS) ru $(BUILDDIR)/docs/ru/single + SPHINX_TEMPLATES=../_singlehtml_templates $(SPHINXBUILD) -b singlehtml $(SPHINXOPTS) en $(BUILDDIR)/docs/en/single help: @echo "Please use \`make ' where is one of" @@ -175,3 +181,4 @@ pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + diff --git a/docs/_singlehtml_templates/navigation.html b/docs/_singlehtml_templates/navigation.html new file mode 100644 index 00000000000..f98fc4161f7 --- /dev/null +++ b/docs/_singlehtml_templates/navigation.html @@ -0,0 +1,10 @@ +

{{ _('Navigation') }}

+{{ toctree(includehidden=theme_sidebar_includehidden, collapse=False) }} +{% if theme_extra_nav_links %} +
+
    + {% for text, uri in theme_extra_nav_links.items() %} +
  • {{ text }}
  • + {% endfor %} +
+{% endif %} diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 00000000000..59b1ed2463c --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,88 @@ +div.sphinxsidebarwrapper { + padding: 2px 10px; +} + +div.sphinxsidebarwrapper p.logo { + float: left; + text-align: left; + margin: -4px 4px 0 0px; +} + +div.sphinxsidebar a { + border-bottom: none; + color: #000; +} + +pre { + padding: 4px; +} + +input { + display: block; + margin-bottom: 4px; +} + +@font-face { + font-family: 'Yandex Sans Text Web'; + src: url(https://yastatic.net/adv-www/_/yy5JveR58JFkc97waf-xp0i6_jM.eot); + src: url(https://yastatic.net/adv-www/_/yy5JveR58JFkc97waf-xp0i6_jM.eot?#iefix) format('embedded-opentype'), + url(https://yastatic.net/adv-www/_/CYblzLEXzCqQIvrYs7QKQe2omRk.woff2) format('woff2'), + url(https://yastatic.net/adv-www/_/pUcnOdRwl83MvPPzrNomhyletnA.woff) format('woff'), + url(https://yastatic.net/adv-www/_/vNFEmXOcGYKJ4AAidUprHWoXrLU.ttf) format('truetype'), + url(https://yastatic.net/adv-www/_/0w7OcWZM_QLP8x-LQUXFOgXO6dE.svg#YandexSansTextWeb-Bold) format('svg'); + font-weight: 700; + font-style: normal; + font-stretch: normal +} + +@font-face { + font-family: 'Yandex Sans Text Web'; + src: url(https://yastatic.net/adv-www/_/LI6l3L2RqcgxBe2pXmuUha37czQ.eot); + src: url(https://yastatic.net/adv-www/_/LI6l3L2RqcgxBe2pXmuUha37czQ.eot?#iefix) format('embedded-opentype'), + url(https://yastatic.net/adv-www/_/z3MYElcut0R2MF_Iw1RDNrstgYs.woff2) format('woff2'), + url(https://yastatic.net/adv-www/_/1jvKJ_-hCXl3s7gmFl-y_-UHTaI.woff) format('woff'), + url(https://yastatic.net/adv-www/_/9nzjfpCR2QHvK1EzHpDEIoVFGuY.ttf) format('truetype'), + url(https://yastatic.net/adv-www/_/gwyBTpxSwkFCF1looxqs6JokKls.svg#YandexSansTextWeb-Regular) format('svg'); + font-weight: 400; + font-style: normal; + font-stretch: normal +} + +@font-face { + font-family: 'Yandex Sans Text Web'; + src: url(https://yastatic.net/adv-www/_/ayAFYoY8swgBLhq_I56tKj2JftU.eot); + src: url(https://yastatic.net/adv-www/_/ayAFYoY8swgBLhq_I56tKj2JftU.eot?#iefix) format('embedded-opentype'), + url(https://yastatic.net/adv-www/_/lGQcYklLVV0hyvz1HFmFsUTj8_0.woff2) format('woff2'), + url(https://yastatic.net/adv-www/_/f0AAJ9GJ4iiwEmhG-7PWMHk6vUY.woff) format('woff'), + url(https://yastatic.net/adv-www/_/4UDe4nlVvgEJ-VmLWNVq3SxCsA.ttf) format('truetype'), + url(https://yastatic.net/adv-www/_/EKLr1STNokPqxLAQa_RyN82pL98.svg#YandexSansTextWeb-Light) format('svg'); + font-weight: 300; + font-style: normal; + font-stretch: normal +} + +@font-face { + font-family: 'Yandex Sans Display Web'; + src: url(https://yastatic.net/adv-www/_/H63jN0veW07XQUIA2317lr9UIm8.eot); + src: url(https://yastatic.net/adv-www/_/H63jN0veW07XQUIA2317lr9UIm8.eot?#iefix) format('embedded-opentype'), + url(https://yastatic.net/adv-www/_/sUYVCPUAQE7ExrvMS7FoISoO83s.woff2) format('woff2'), + url(https://yastatic.net/adv-www/_/v2Sve_obH3rKm6rKrtSQpf-eB7U.woff) format('woff'), + url(https://yastatic.net/adv-www/_/PzD8hWLMunow5i3RfJ6WQJAL7aI.ttf) format('truetype'), + url(https://yastatic.net/adv-www/_/lF_KG5g4tpQNlYIgA0e77fBSZ5s.svg#YandexSansDisplayWeb-Regular) format('svg'); + font-weight: 400; + font-style: normal; + font-stretch: normal +} + +@font-face { + font-family: 'Yandex Sans Display Web'; + src: url(https://yastatic.net/adv-www/_/g8_MyyKVquSZ3xEL6tarK__V9Vw.eot); + src: url(https://yastatic.net/adv-www/_/g8_MyyKVquSZ3xEL6tarK__V9Vw.eot?#iefix) format('embedded-opentype'), + url(https://yastatic.net/adv-www/_/LGiRvlfqQHlWR9YKLhsw5e7KGNA.woff2) format('woff2'), + url(https://yastatic.net/adv-www/_/40vXwNl4eYYMgteIVgLP49dwmfc.woff) format('woff'), + url(https://yastatic.net/adv-www/_/X6zG5x_wO8-AtwJ-vDLJcKC5228.ttf) format('truetype'), + url(https://yastatic.net/adv-www/_/ZKhaR0m08c8CRRL77GtFKoHcLYA.svg#YandexSansDisplayWeb-Light) format('svg'); + font-weight: 300; + font-style: normal; + font-stretch: normal +} diff --git a/docs/_static/logo.svg b/docs/_static/logo.svg new file mode 120000 index 00000000000..dbc7f998456 --- /dev/null +++ b/docs/_static/logo.svg @@ -0,0 +1 @@ +/Users/blinkov/ClickHouse/website/logo.svg \ No newline at end of file diff --git a/docs/en/conf.py b/docs/en/conf.py index 2f49322f3e5..d37b33208a8 100644 --- a/docs/en/conf.py +++ b/docs/en/conf.py @@ -12,8 +12,9 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys +import collections import os +import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -29,11 +30,14 @@ import os # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.mathjax', + 'alabaster' ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = [ + '../_templates', + os.getenv('SPHINX_TEMPLATES', '') +] # The suffix of source filenames. source_suffix = '.rst' @@ -46,20 +50,20 @@ master_doc = 'index' # General information about the project. project = u'ClickHouse' -copyright = u'2017, Alexey Milovidov' +copyright = u'2016–2017 Yandex LLC' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '1' +version = '' # The full version, including alpha/beta/rc tags. -release = '1' +release = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -100,12 +104,29 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +font_family = '\'Yandex Sans Display Web\', Arial, sans-serif' +html_theme_options = { + 'logo': 'logo.svg', + 'logo_name': True, + 'github_user': 'yandex', + 'github_repo': 'ClickHouse', + 'github_button': False, + 'font_family': font_family, + 'head_font_family': font_family, + 'caption_font_family': font_family, + 'show_powered_by': False, + 'extra_nav_links': collections.OrderedDict([ + ('Documentation in Russian', '/docs/ru/'), + ('Single page documentation', '/docs/en/single/'), + ('Website home', '/'), + ('GitHub', 'https://github.com/yandex/ClickHouse'), + ]) +} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] @@ -129,7 +150,7 @@ html_theme = 'default' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ['../_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -145,7 +166,14 @@ html_static_path = ['_static'] #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', + 'searchbox.html', + ] +} # Additional templates that should be rendered to pages, maps page names to # template names. @@ -161,10 +189,10 @@ html_static_path = ['_static'] #html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True @@ -178,7 +206,7 @@ html_static_path = ['_static'] #html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'ClickHousedoc' +htmlhelp_basename = 'ClickHouse' # -- Options for LaTeX output --------------------------------------------- @@ -199,7 +227,7 @@ latex_elements = { # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'ClickHouse.tex', u'ClickHouse Documentation', - u'Alexey Milovidov', 'manual'), + u'Yandex LLC', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -229,7 +257,7 @@ latex_documents = [ # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'clickhouse', u'ClickHouse Documentation', - [u'Alexey Milovidov'], 1) + [u'Yandex LLC'], 1) ] # If true, show URL addresses after external links. @@ -243,7 +271,7 @@ man_pages = [ # dir menu entry, description, category) texinfo_documents = [ ('index', 'ClickHouse', u'ClickHouse Documentation', - u'Alexey Milovidov', 'ClickHouse', 'One line description of project.', + u'Yandex LLC', 'ClickHouse', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/en/functions/other_functions.rst b/docs/en/functions/other_functions.rst index be5947c9048..04bc62663ab 100644 --- a/docs/en/functions/other_functions.rst +++ b/docs/en/functions/other_functions.rst @@ -259,10 +259,14 @@ Example: └─────────┴─────────────────────┴───────┘ MACNumToString(num) -~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ Takes a UInt64 number. Interprets it as an MAC address in big endian. Returns a string containing the corresponding MAC address in the format AA:BB:CC:DD:EE:FF (colon-separated numbers in hexadecimal form). MACStringToNum(s) -~~~~~~~~ +~~~~~~~~~~~~~~~~~ The reverse function of MACNumToString. If the MAC address has an invalid format, it returns 0. + +MACStringToOUI(s) +~~~~~~~~~~~~~~~~~ +Takes MAC address in the format AA:BB:CC:DD:EE:FF (colon-separated numbers in hexadecimal form). Returns first three octets as UInt64 number. If the MAC address has an invalid format, it returns 0. \ No newline at end of file diff --git a/docs/en/functions/rounding_functions.rst b/docs/en/functions/rounding_functions.rst index b4be1e121e8..b17b40b357b 100644 --- a/docs/en/functions/rounding_functions.rst +++ b/docs/en/functions/rounding_functions.rst @@ -35,4 +35,4 @@ Accepts a number. If the number is less than one, it returns 0. Otherwise, it ro roundAge(num) ~~~~~~~ -Accepts a number. If the number is less than 18, it returns 0. Otherwise, it rounds the number down to numbers from the set: 18, 25, 35, 45. This function is specific to Yandex.Metrica and used for implementing the report on user age. +Accepts a number. If the number is less than 18, it returns 0. Otherwise, it rounds the number down to numbers from the set: 18, 25, 35, 45, 55. This function is specific to Yandex.Metrica and used for implementing the report on user age. diff --git a/docs/ru/access_rights.rst b/docs/ru/access_rights.rst index 0987d4d8465..4078b357252 100644 --- a/docs/ru/access_rights.rst +++ b/docs/ru/access_rights.rst @@ -84,8 +84,14 @@ Для продакшен использования, указывайте только элементы вида ip (IP-адреса и их маски), так как использование host и host_regexp может вызывать лишние задержки. -Далее указывается используемый профиль настроек пользователя (смотрите раздел "Профили настроек"). Вы можете указать профиль по умолчанию - default. Профиль может называться как угодно; один и тот же профиль может быть указан для разных пользователей. Наиболее важная вещь, которую вы можете прописать в профиле настроек - настройку readonly, равную 1, что обеспечивает доступ только на чтение. +Далее указывается используемый профиль настроек пользователя (смотрите раздел "Профили настроек"). Вы можете указать профиль по умолчанию - ``default``. Профиль может называться как угодно; один и тот же профиль может быть указан для разных пользователей. Наиболее важная вещь, которую вы можете прописать в профиле настроек - настройку readonly, равную 1, что обеспечивает доступ только на чтение. Затем указывается используемая квота (смотрите раздел "Квоты"). Вы можете указать квоту по умолчанию - ``default``. Она настроена в конфиге по умолчанию так, что только считает использование ресурсов, но никак их не ограничивает. Квота может называться как угодно; одна и та же квота может быть указана для разных пользователей - в этом случае, подсчёт использования ресурсов делается для каждого пользователя по отдельности. -Также в необязательном разделе ```` можно указать перечень баз, к которым у пользователя будет доступ. По умолчанию пользователю доступны все базы. Можно указать базу данных ``default``, в этом случае пользователь получит доступ к базе данных по умолчанию. +Также в необязательном разделе ```` можно указать перечень баз, к которым у пользователя будет доступ. По умолчанию пользователю доступны все базы. Можно указать базу данных ``default``, в этом случае пользователь получит доступ к базе данных по умолчанию. + +Доступ к БД ``system`` всегда считается разрешённым (так как эта БД используется для выполнения запросов). + +Пользователь может получить список всех БД и таблиц в них с помощью запросов ``SHOW`` или системных таблиц, даже если у него нет доступа к отдельным ДБ. + +Доступ к БД не связан с настройкой :ref:`query_complexity_readonly`. Невозможно дать полный доступ к одной БД и ``readonly`` к другой. diff --git a/docs/ru/conf.py b/docs/ru/conf.py index 2f49322f3e5..4c87cc7b181 100644 --- a/docs/ru/conf.py +++ b/docs/ru/conf.py @@ -12,8 +12,9 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys +import collections import os +import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -29,11 +30,14 @@ import os # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.mathjax', + 'alabaster' ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = [ + '../_templates', + os.getenv('SPHINX_TEMPLATES', '') +] # The suffix of source filenames. source_suffix = '.rst' @@ -46,20 +50,20 @@ master_doc = 'index' # General information about the project. project = u'ClickHouse' -copyright = u'2017, Alexey Milovidov' +copyright = u'2016–2017 Yandex LLC' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '1' +version = '' # The full version, including alpha/beta/rc tags. -release = '1' +release = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +language = 'ru' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -100,12 +104,29 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +font_family = '\'Yandex Sans Display Web\', Arial, sans-serif' +html_theme_options = { + 'logo': 'logo.svg', + 'logo_name': True, + 'github_user': 'yandex', + 'github_repo': 'ClickHouse', + 'github_button': False, + 'font_family': font_family, + 'head_font_family': font_family, + 'caption_font_family': font_family, + 'show_powered_by': False, + 'extra_nav_links': collections.OrderedDict([ + (u'Documentation in English', '/docs/en/'), + (u'Документация на одной странице', '/docs/ru/single/'), + (u'Главная страница сайта', '/'), + (u'GitHub', 'https://github.com/yandex/ClickHouse'), + ]) +} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] @@ -129,7 +150,7 @@ html_theme = 'default' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ['../_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -145,7 +166,14 @@ html_static_path = ['_static'] #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', + 'searchbox.html', + ] +} # Additional templates that should be rendered to pages, maps page names to # template names. @@ -161,10 +189,10 @@ html_static_path = ['_static'] #html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True @@ -178,7 +206,7 @@ html_static_path = ['_static'] #html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'ClickHousedoc' +htmlhelp_basename = 'ClickHouse' # -- Options for LaTeX output --------------------------------------------- @@ -199,7 +227,7 @@ latex_elements = { # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'ClickHouse.tex', u'ClickHouse Documentation', - u'Alexey Milovidov', 'manual'), + u'Yandex LLC', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -229,7 +257,7 @@ latex_documents = [ # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'clickhouse', u'ClickHouse Documentation', - [u'Alexey Milovidov'], 1) + [u'Yandex LLC'], 1) ] # If true, show URL addresses after external links. @@ -243,7 +271,7 @@ man_pages = [ # dir menu entry, description, category) texinfo_documents = [ ('index', 'ClickHouse', u'ClickHouse Documentation', - u'Alexey Milovidov', 'ClickHouse', 'One line description of project.', + u'Yandex LLC', 'ClickHouse', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/ru/configuration_files.rst b/docs/ru/configuration_files.rst index 27e1c321a0a..efe396a89fa 100644 --- a/docs/ru/configuration_files.rst +++ b/docs/ru/configuration_files.rst @@ -1,3 +1,5 @@ +.. _configuration_files: + Конфигурационные файлы ====================== diff --git a/docs/ru/dicts/external_dicts.rst b/docs/ru/dicts/external_dicts.rst index 94dcffa13e5..dce4e0c8d78 100644 --- a/docs/ru/dicts/external_dicts.rst +++ b/docs/ru/dicts/external_dicts.rst @@ -6,7 +6,7 @@ Словарь может полностью храниться в оперативке и периодически обновляться, или быть частично закэшированным в оперативке и динамически подгружать отсутствующие значения. Конфигурация внешних словарей находится в отдельном файле или файлах, указанных в конфигурационном параметре dictionaries_config. -Этот параметр содержит абсолютный или относительный путь к файлу с конфигурацией словарей. Относительный путь - относительно директории с конфигурационным файлом сервера. Путь может содержать wildcard-ы * и ? - тогда рассматриваются все подходящие файлы. Пример: dictionaries/*.xml. +Этот параметр содержит абсолютный или относительный путь к файлу с конфигурацией словарей. Относительный путь - относительно директории с конфигурационным файлом сервера. Путь может содержать wildcard-ы \* и ? - тогда рассматриваются все подходящие файлы. Пример: dictionaries/\*.xml. Конфигурация словарей, а также множество файлов с конфигурацией, может обновляться без перезапуска сервера. Сервер проверяет обновления каждые 5 секунд. То есть, словари могут подключаться динамически. @@ -68,6 +68,18 @@ Для отказоустойчивости, вы можете создать Distributed таблицу на localhost и прописать её. - -> --> + + @@ -139,6 +151,14 @@ true + + + + expr + UInt64 + rand64() + 0 + @@ -167,16 +187,21 @@ range_hashed ------------ В таблице прописаны какие-то данные для диапазонов дат, для каждого ключа. Дать возможность доставать эти данные для заданного ключа, для заданной даты. -Пример: в таблице записаны скидки для каждого рекламодателя в виде: -:: - id рекламодателя дата начала действия скидки дата конца величина - 123 2015-01-01 2015-01-15 0.15 - 123 2015-01-16 2015-01-31 0.25 - 456 2015-01-01 2015-01-15 0.05 +Пример: таблица содержит скидки для каждого рекламодателя в виде: -Добавляем layout = range_hashed. -При использовании такого layout, в structure должны быть элементы range_min, range_max. + +------------------+-----------------------------+------------+----------+ + | id рекламодателя | дата начала действия скидки | дата конца | величина | + +==================+=============================+============+==========+ + | 123 | 2015-01-01 | 2015-01-15 | 0.15 | + +------------------+-----------------------------+------------+----------+ + | 123 | 2015-01-16 | 2015-01-31 | 0.25 | + +------------------+-----------------------------+------------+----------+ + | 456 | 2015-01-01 | 2015-01-15 | 0.05 | + +------------------+-----------------------------+------------+----------+ + +Добавляем ``layout = range_hashed``. +При использовании такого layout, в structure должны быть элементы ``range_min``, ``range_max``. Пример: diff --git a/docs/ru/functions/other_functions.rst b/docs/ru/functions/other_functions.rst index ecaff8f327a..bee2f135e9e 100644 --- a/docs/ru/functions/other_functions.rst +++ b/docs/ru/functions/other_functions.rst @@ -261,10 +261,14 @@ runningDifference(x) └─────────┴─────────────────────┴───────┘ MACNumToString(num) -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ Принимает число типа UInt64. Интерпретирует его, как MAC-адрес в big endian. Возвращает строку, содержащую соответствующий MAC-адрес в формате AA:BB:CC:DD:EE:FF (числа в шестнадцатеричной форме через двоеточие). MACStringToNum(s) -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~ Функция, обратная к MACNumToString. Если MAC адрес в неправильном формате, то возвращает 0. + +MACStringToOUI(s) +~~~~~~~~~~~~~~~~~ +Принимает MAC адрес в формате AA:BB:CC:DD:EE:FF (числа в шестнадцатеричной форме через двоеточие). Возвращает первые три октета как число в формате UInt64. Если MAC адрес в неправильном формате, то возвращает 0. \ No newline at end of file diff --git a/docs/ru/functions/rounding_functions.rst b/docs/ru/functions/rounding_functions.rst index 3d421ab5af2..1503c5b07da 100644 --- a/docs/ru/functions/rounding_functions.rst +++ b/docs/ru/functions/rounding_functions.rst @@ -36,4 +36,4 @@ roundDuration(num) roundAge(num) ~~~~~~~~~~~~~ -Принимает число. Если число меньше 18 - возвращает 0. Иначе округляет число вниз до чисел из набора: 18, 25, 35, 45. Эта функция специфична для Яндекс.Метрики и предназначена для реализации отчёта по возрасту посетителей. +Принимает число. Если число меньше 18 - возвращает 0. Иначе округляет число вниз до чисел из набора: 18, 25, 35, 45, 55. Эта функция специфична для Яндекс.Метрики и предназначена для реализации отчёта по возрасту посетителей. diff --git a/docs/ru/introduction/distinctive_features.rst b/docs/ru/introduction/distinctive_features.rst index 0f160a5880a..5636b6688b4 100644 --- a/docs/ru/introduction/distinctive_features.rst +++ b/docs/ru/introduction/distinctive_features.rst @@ -60,4 +60,4 @@ ClickHouse поддерживает таблицы с первичным клю 14. Репликация данных, поддержка целостности данных на репликах. ---------------------------------------------------------------- Используется асинхронная multimaster репликация. После записи на любую доступную реплику, данные распространяются на все остальные реплики. Система поддерживает полную идентичность данных на разных репликах. Восстановление после сбоя осуществляется автоматически, а в сложных случаях - "по кнопке". -Подробнее смотрите раздел "Репликация данных". +Подробнее смотрите раздел :ref:`table_engines-replication`. diff --git a/docs/ru/query_language/queries.rst b/docs/ru/query_language/queries.rst index db7de94dd05..bd1bbaa1722 100644 --- a/docs/ru/query_language/queries.rst +++ b/docs/ru/query_language/queries.rst @@ -65,9 +65,9 @@ CREATE TABLE ``MATERIALIZED expr`` -Материализованное выражение. Такой столбец не может быть указан при INSERT-е, то есть, он всегда вычисляется. -При INSERT-е без указания списка столбцов, такие столбцы не рассматриваются. -Также этот столбец не подставляется при использовании звёздочки в запросе SELECT - чтобы сохранить инвариант, что дамп, полученный путём SELECT *, можно вставить обратно в таблицу INSERT-ом без указания списка столбцов. +Материализованное выражение. Такой столбец не может быть указан при INSERT, то есть, он всегда вычисляется. +При INSERT без указания списка столбцов, такие столбцы не рассматриваются. +Также этот столбец не подставляется при использовании звёздочки в запросе SELECT - чтобы сохранить инвариант, что дамп, полученный путём ``SELECT *``, можно вставить обратно в таблицу INSERT-ом без указания списка столбцов. ``ALIAS expr`` @@ -675,7 +675,7 @@ SELECT ``ARRAY JOIN`` - это, по сути, ``INNER JOIN`` с массивом. Пример: -.. code-block:: sql +:: :) CREATE TABLE arrays_test (s String, arr Array(UInt8)) ENGINE = Memory @@ -728,7 +728,7 @@ SELECT Для массива в секции ARRAY JOIN может быть указан алиас. В этом случае, элемент массива будет доступен под этим алиасом, а сам массив - под исходным именем. Пример: -.. code-block:: sql +:: :) SELECT s, arr, a FROM arrays_test ARRAY JOIN arr AS a @@ -748,7 +748,7 @@ SELECT В секции ARRAY JOIN может быть указано несколько массивов одинаковых размеров через запятую. В этом случае, JOIN делается с ними одновременно (прямая сумма, а не прямое произведение). Пример: -.. code-block:: sql +:: :) SELECT s, arr, a, num, mapped FROM arrays_test ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num, arrayMap(x -> x + 1, arr) AS mapped @@ -784,7 +784,7 @@ SELECT ARRAY JOIN также работает с вложенными структурами данных. Пример: -.. code-block:: sql +:: :) CREATE TABLE nested_test (s String, nest Nested(x UInt8, y UInt32)) ENGINE = Memory @@ -839,7 +839,7 @@ ARRAY JOIN также работает с вложенными структур При указании имени вложенной структуры данных в ARRAY JOIN, смысл такой же, как ARRAY JOIN со всеми элементами-массивами, из которых она состоит. Пример: -.. code-block:: sql +:: :) SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest.x, nest.y @@ -859,7 +859,7 @@ ARRAY JOIN также работает с вложенными структур Такой вариант тоже имеет смысл: -.. code-block:: sql +:: :) SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest.x @@ -879,7 +879,7 @@ ARRAY JOIN также работает с вложенными структур Алиас для вложенной структуры данных можно использовать, чтобы выбрать как результат JOIN-а, так и исходный массив. Пример: -.. code-block:: sql +:: :) SELECT s, n.x, n.y, nest.x, nest.y FROM nested_test ARRAY JOIN nest AS n @@ -899,7 +899,7 @@ ARRAY JOIN также работает с вложенными структур Пример использования функции arrayEnumerate: -.. code-block:: sql +:: :) SELECT s, n.x, n.y, nest.x, nest.y, num FROM nested_test ARRAY JOIN nest AS n, arrayEnumerate(nest.x) AS num @@ -962,7 +962,7 @@ JOIN-ы бывают нескольких видов: Пример: -.. code-block:: sql +:: SELECT CounterID, @@ -1033,7 +1033,7 @@ PREWHERE имеет смысл использовать, если есть ус Например, полезно писать PREWHERE для запросов, которые вынимают много столбцов, но в которых фильтрация производится лишь по нескольким столбцам. -PREWHERE поддерживается только таблицами семейства *MergeTree. +PREWHERE поддерживается только таблицами семейства ``*MergeTree``. В запросе могут быть одновременно указаны секции PREWHERE и WHERE. В этом случае, PREWHERE идёт перед WHERE. @@ -1289,7 +1289,7 @@ n и m должны быть неотрицательными целыми чи Оператор IN и подзапрос могут встречаться в любой части запроса, в том числе в агрегатных и лямбда функциях. Пример: -.. code-block:: sql +:: SELECT EventDate, @@ -1316,11 +1316,17 @@ n и m должны быть неотрицательными целыми чи за каждый день после 17 марта считаем долю хитов, сделанных посетителями, которые заходили на сайт 17 марта. Подзапрос в секции IN на одном сервере всегда выполняется только один раз. Зависимых подзапросов не существует. + +.. _queries-distributed-subrequests: + Распределённые подзапросы """"""""""""""""""""""""" Существует два варианта IN-ов с подзапросами (аналогично для JOIN-ов): обычный ``IN`` / ``JOIN`` и ``GLOBAL IN`` / ``GLOBAL JOIN``. Они отличаются способом выполнения при распределённой обработке запроса. +.. attention:: + Помните, что алгоритмы, описанные ниже, могут работать иначе в зависимости от :ref:`настройки ` ``distributed_product_mode``. + При использовании обычного IN-а, запрос отправляется на удалённые серверы, и на каждом из них выполняются подзапросы в секциях ``IN`` / ``JOIN``. При использовании ``GLOBAL IN`` / ``GLOBAL JOIN-а``, сначала выполняются все подзапросы для ``GLOBAL IN`` / ``GLOBAL JOIN-ов``, и результаты складываются во временные таблицы. Затем эти временные таблицы передаются на каждый удалённый сервер, и на них выполняются запросы, с использованием этих переданных временных данных. @@ -1343,7 +1349,8 @@ n и m должны быть неотрицательными целыми чи .. code-block:: sql - SELECT uniq(UserID) FROM local_table`` + SELECT uniq(UserID) FROM local_table + , выполнен параллельно на каждом из них до стадии, позволяющей объединить промежуточные результаты; затем промежуточные результаты вернутся на сервер-инициатор запроса, будут на нём объединены, и финальный результат будет отправлен клиенту. diff --git a/docs/ru/settings/index.rst b/docs/ru/settings/index.rst index 65b8816fc38..aa51d89e264 100644 --- a/docs/ru/settings/index.rst +++ b/docs/ru/settings/index.rst @@ -1,7 +1,22 @@ Настройки ========== -Здесь будут рассмотрены настройки, которые можно задать с помощью запроса SET или в конфигурационном файле. Напомню, что эти настройки могут быть выставлены в пределах сессии или глобально. Настройки, которые можно задать только в конфигурационном файле сервера, здесь рассмотрены не будут. +Описанные в разделе настройки могут быть заданы следующими способами: +* Глобально. + + В конфигурационных файлах сервера. + +* Для сессии. + + При запуске консольного клиента ClickHouse в интерактивном режиме отправьте запрос ``SET setting=value``. + +* Для запроса. + + * При запуске консольного клиента ClickHouse в неинтерактивном режиме установите параметр запуска ``--setting=value``. + * При использовании HTTP API передавайте cgi-параметры (``URL?setting_1=value&setting_2=value...``). + + +Настройки, которые можно задать только в конфигурационном файле сервера, в разделе не рассматриваются. .. toctree:: :glob: diff --git a/docs/ru/settings/query_complexity.rst b/docs/ru/settings/query_complexity.rst index 7de814db6a1..d89d8758a86 100644 --- a/docs/ru/settings/query_complexity.rst +++ b/docs/ru/settings/query_complexity.rst @@ -15,6 +15,8 @@ ``any (только для group_by_overflow_mode)`` - продолжить агрегацию по ключам, которые успели войти в набор, но не добавлять новые ключи в набор. +.. _query_complexity_readonly: + readonly -------- При значении 0 можно выполнять любые запросы. @@ -90,7 +92,11 @@ result_overflow_mode Использование break по смыслу похоже на LIMIT. max_execution_time +<<<<<<< HEAD +------------------- +======= ------------------ +>>>>>>> upstream/master Максимальное время выполнения запроса в секундах. На данный момент не проверяется при одной из стадий сортировки а также при слиянии и финализации агрегатных функций. diff --git a/docs/ru/settings/settings.rst b/docs/ru/settings/settings.rst index 022cf1d2656..4d0c54fccd6 100644 --- a/docs/ru/settings/settings.rst +++ b/docs/ru/settings/settings.rst @@ -1,3 +1,47 @@ +.. _settings-distributed_product_mode: + +distributed_product_mode +------------------------ +Изменяет поведение :ref:`распределенных подзапросов `, т.е. в тех случаях, когда запрос содержит произведение распределённых таблиц. + +ClickHouse применяет настройку в том случае, когда в подзапросах на любом уровне встретилась распределенная таблица, которая существует на локальном сервере и имеет больше одного шарда. + +Условия применения: + +* Только подзапросы для IN, JOIN. +* Только если в секции FROM используется распределённая таблица. +* Не используется в случае табличной функции :ref:`remote `. + +Возможные значения: + +.. list-table:: + :widths: 20 80 + :header-rows: 1 + + * - Значение + - Поведение ClickHouse + * - ``deny`` (по умолчанию) + - Генерирует исключение. + * - ``allow`` + - Выполняет запрос без изменения логики. + * - ``global`` + - Преобразует ``IN`` в ``GLOBAL IN``, ``JOIN`` в ``GLOBAL JOIN``. + * - ``local`` + - Преобразует все вхождения Distributed-таблиц в соответствующие им удалённые таблицы. + + +.. _settings-settings-fallback_to_stale_replicas_for_distributed_queries: + +fallback_to_stale_replicas_for_distributed_queries +-------------------------------------------------- +Форсирует запрос в устаревшую реплику в случае, если актуальные данные недоступны. Смотрите :ref:`table_engines-replication`. + +Из устаревших реплик таблицы ClickHouse выбирает наиболее актуальную. + +Используется при выполнении ``SELECT`` из распределенной таблицы, которая указывает на реплицированные таблицы. + +По умолчанию - 1 (включена). + max_block_size -------------- Данные в ClickHouse обрабатываются по блокам (наборам кусочков столбцов). Внутренние циклы обработки одного блока достаточно эффективны, но при этом существуют заметные издержки на каждый блок. ``max_block_size`` - это рекомендация, какого размера блоки (в количестве строк) загружать из таблицы. Размер блока должен быть не слишком маленьким, чтобы издержки на каждый блок оставались незаметными, и не слишком большим, чтобы запрос с LIMIT-ом, который завершается уже после первого блока, выполнялся быстро; чтобы не использовалось слишком много оперативки при вынимании большого количества столбцов в несколько потоков; чтобы оставалась хоть какая-нибудь кэш-локальность. @@ -22,7 +66,21 @@ max_insert_block_size ``По умолчанию - 1 048 576.`` -Это намного больше, чем max_block_size. Это сделано, потому что некоторые движки таблиц (*MergeTree) будут на каждый вставляемый блок формировать кусок данных на диске, что является довольно большой сущностью. Также, в таблицах типа *MergeTree, данные сортируются при вставке, и достаточно большой размер блока позволяет отсортировать больше данных в оперативке. +Это намного больше, чем ``max_block_size``. Это сделано, потому что некоторые движки таблиц (``*MergeTree``) будут на каждый вставляемый блок формировать кусок данных на диске, что является довольно большой сущностью. Также, в таблицах типа ``*MergeTree``, данные сортируются при вставке, и достаточно большой размер блока позволяет отсортировать больше данных в оперативке. + + +.. _settings_settings_max_replica_delay_for_distributed_queries: + +max_replica_delay_for_distributed_queries +------------------------------------------ +Отключает отстающие реплики при распределенных запросах. Смотрите :ref:`table_engines-replication`. + +Устанавливает время в секундах. Если оставание реплики больше установленного значения, то реплика не используется. + +Значение по умолчанию: 0 (отключено). + +Используется при выполнении ``SELECT`` из распределенной таблицы, которая указывает на реплицированные таблицы. + max_threads ----------- @@ -48,7 +106,7 @@ max_compress_block_size min_compress_block_size ----------------------- -Для таблиц типа *MergeTree. В целях уменьшения задержек при обработке запросов, блок сжимается при записи следующей засечки, если его размер не меньше min_compress_block_size. По умолчанию - 65 536. +Для таблиц типа :ref:`MergeTree `. В целях уменьшения задержек при обработке запросов, блок сжимается при записи следующей засечки, если его размер не меньше min_compress_block_size. По умолчанию - 65 536. Реальный размер блока, если несжатых данных меньше max_compress_block_size, будет не меньше этого значения и не меньше объёма данных на одну засечку. @@ -136,6 +194,8 @@ replace_running_query Эта настройка, выставленная в 1, используется в Яндекс.Метрике для реализации suggest-а значений для условий сегментации. После ввода очередного символа, если старый запрос ещё не выполнился, его следует отменить. +.. _settings-load_balancing: + load_balancing -------------- На какие реплики (среди живых реплик) предпочитать отправлять запрос (при первой попытке) при распределённой обработке запроса. diff --git a/docs/ru/table_engines/graphitemergetree.rst b/docs/ru/table_engines/graphitemergetree.rst new file mode 100644 index 00000000000..249ae4dd935 --- /dev/null +++ b/docs/ru/table_engines/graphitemergetree.rst @@ -0,0 +1,91 @@ +GraphiteMergeTree +----------------- + +Движок предназначен для rollup (прореживания и агрегирования/усреднения) данных `Graphite `_. Он может быть интересен разработчикам, которые хотят использовать ClickHouse как хранилище данных для Graphite. + +Graphite хранит в ClickHouse полные данные, а получать их может следующими способами: + +* Без прореживания. + + Используется движок :ref:`MergeTree `. + +* С прореживанием. + + Используется движок ``GraphiteMergeTree``. + +Движок наследует свойства `MergeTree`. Настройки прореживания данных размещаются в :ref:`общей конфигурации ` ClickHouse (config.xml). + +Использование движка +^^^^^^^^^^^^^^^^^^^^ + +Таблица с данными Graphite должна содержать как минимум следующие поля: + +* ``Path`` - имя метрики (сенсора Graphite). +* ``Time`` - время измерения. +* ``Value`` - значение метрики в момент времени Time. +* ``Version`` - настройка, которая определяет какое значение метрики с одинаковыми Path и Time останется в базе. + +Шаблон правил rollup: :: + + pattern + regexp + function + age -> precision + ... + pattern + ... + default + function + age -> precision + ... + +При обработке записи ClickHouse проверит правила в секции ```pattern```. Если имя метрики соответствует шаблону ```regexp```, то применяются правила из ```pattern```, в противном случае из ```default```. + +Поля шаблона правил. + ++---------------+----------------------------------------------------------------------------------------------------------------------------+ +| Поле | Описание | ++===============+============================================================================================================================+ +| ``age`` | Минимальный возраст данных в секундах. | ++---------------+----------------------------------------------------------------------------------------------------------------------------+ +| ``function`` | Имя агрегирующей функции, которую следует применить к данным, чей возраст оказался в интервале ``[age, age + precision]``. | ++---------------+----------------------------------------------------------------------------------------------------------------------------+ +| ``precision`` | Точность определения возраста данных в секундах. | ++---------------+----------------------------------------------------------------------------------------------------------------------------+ +| ``regexp`` | Шаблон имени метрики. | ++---------------+----------------------------------------------------------------------------------------------------------------------------+ + + +Пример настройки: + +.. code-block:: xml + + + + click_cost + any + + 0 + 5 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + diff --git a/docs/ru/table_engines/mergetree.rst b/docs/ru/table_engines/mergetree.rst index 94f0eb3d98e..8c51a1a85f4 100644 --- a/docs/ru/table_engines/mergetree.rst +++ b/docs/ru/table_engines/mergetree.rst @@ -1,3 +1,5 @@ +.. _table_engines-mergetree: + MergeTree --------- diff --git a/docs/ru/table_engines/replication.rst b/docs/ru/table_engines/replication.rst index 0684ddcb600..1d88755a67f 100644 --- a/docs/ru/table_engines/replication.rst +++ b/docs/ru/table_engines/replication.rst @@ -1,3 +1,5 @@ +.. _table_engines-replication: + Репликация данных ----------------- @@ -45,7 +47,7 @@ ReplicatedSummingMergeTree Если в конфигурационном файле не настроен ZooKeeper, то вы не сможете создать реплицируемые таблицы, а уже имеющиеся реплицируемые таблицы будут доступны в режиме только на чтение. -При запросах SELECT, ZooKeeper не используется. То есть, репликация никак не влияет на производительность SELECT-ов - запросы работают так же быстро, как и для нереплицируемых таблиц. +При запросах SELECT, ZooKeeper не используется. То есть, репликация никак не влияет на производительность SELECT-ов - запросы работают так же быстро, как и для нереплицируемых таблиц. При запросах к распределенным реплицированным таблицам поведение ClickHouse регулируется настройками :ref:`settings_settings_max_replica_delay_for_distributed_queries` и :ref:`settings-settings-fallback_to_stale_replicas_for_distributed_queries`. При каждом запросе INSERT (точнее, на каждый вставляемый блок данных; запрос INSERT содержит один блок, или по блоку на каждые max_insert_block_size = 1048576 строк), делается около десятка записей в ZooKeeper в рамках нескольких транзакций. Это приводит к некоторому увеличению задержек при INSERT-е, по сравнению с нереплицируемыми таблицами. Но если придерживаться обычных рекомендаций - вставлять данные пачками не более одного INSERT-а в секунду, то это не составляет проблем. На всём кластере ClickHouse, использующим для координации один кластер ZooKeeper, может быть в совокупности несколько сотен INSERT-ов в секунду. Пропускная способность при вставке данных (количество строчек в секунду) такая же высокая, как для нереплицируемых таблиц. @@ -72,8 +74,7 @@ ReplicatedSummingMergeTree Также добавляются два параметра в начало списка параметров - путь к таблице в ZooKeeper, имя реплики в ZooKeeper. -Пример: -:: +Пример: :: ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/hits', '{replica}', EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID), EventTime), 8192) @@ -124,6 +125,7 @@ ReplicatedSummingMergeTree Если обнаруживается, что локальный набор данных слишком сильно отличается от ожидаемого, то срабатывает защитный механизм - сервер сообщает об этом в лог и отказывается запускаться. Это сделано, так как такой случай может свидетельствовать об ошибке конфигурации - например, если реплика одного шарда была случайно сконфигурирована, как реплика другого шарда. Тем не менее, пороги защитного механизма поставлены довольно низкими, и такая ситуация может возникнуть и при обычном восстановлении после сбоя. В этом случае, восстановление делается полуавтоматически - "по кнопке". Для запуска восстановления, создайте в ZooKeeper узел ``/path_to_table/replica_name/flags/force_restore_data`` с любым содержимым или выполните команду для восстановления всех реплицируемых таблиц: + .. code-block:: bash sudo -u clickhouse touch /var/lib/clickhouse/flags/force_restore_data diff --git a/docs/ru/table_functions/remote.rst b/docs/ru/table_functions/remote.rst index 6901bc53679..b7c67bcf259 100644 --- a/docs/ru/table_functions/remote.rst +++ b/docs/ru/table_functions/remote.rst @@ -1,67 +1,62 @@ +.. _table_functions-remote: + remote ------ -``remote('addresses_expr', db, table[, 'user'[, 'password']])`` +Позволяет обратиться к удалённым серверам без создания таблицы типа ``Distributed``. -или +Сигнатуры: :: -``remote('addresses_expr', db.table[, 'user'[, 'password']])`` + remote('addresses_expr', db, table[, 'user'[, 'password']]) + remote('addresses_expr', db.table[, 'user'[, 'password']]) -- позволяет обратиться к удалённым серверам без создания таблицы типа Distributed. -``addresses_expr`` - выражение, генерирующее адреса удалённых серверов. +``addresses_expr`` - выражение, генерирующее адреса удалённых серверов. Это может быть просто один адрес сервера. Адрес сервера - это ``хост:порт``, или только ``хост``. Хост может быть указан в виде имени сервера, или в виде IPv4 или IPv6 адреса. IPv6 адрес указывается в квадратных скобках. Порт - TCP-порт удалённого сервера. Если порт не указан, используется ``tcp_port`` из конфигурационного файла сервера (по умолчанию - 9000). -Это может быть просто один адрес сервера. Адрес сервера - это хост:порт, или только хост. Хост может быть указан в виде имени сервера, или в виде IPv4 или IPv6 адреса. IPv6 адрес указывается в квадратных скобках. Порт - TCP-порт удалённого сервера. Если порт не указан, используется tcp_port из конфигурационного файла сервера (по умолчанию - 9000). +.. important:: С IPv6-адресом обязательно указывать порт. -Замечание: в качестве исключения, при указании IPv6-адреса, обязательно также указывать порт. +Примеры: :: -Примеры: -:: + example01-01-1 + example01-01-1:9000 + localhost + 127.0.0.1 + [::]:9000 + [2a02:6b8:0:1111::11]:9000 - example01-01-1 - example01-01-1:9000 - localhost - 127.0.0.1 - [::]:9000 - [2a02:6b8:0:1111::11]:9000 +Адреса можно указать через запятую, в этом случае ClickHouse обработает запрос как распределённый, т.е. отправит его по всем указанным адресам как на шарды с разными данными. -Могут быть указаны адреса через запятую - в этом случае, запрос пойдёт на все указанные адреса (как на шарды с разными данными) и будет обработан распределённо. +Пример: :: -Пример: -:: + example01-01-1,example01-02-1 - example01-01-1,example01-02-1 +Часть выражения может быть указана в фигурных скобках. Предыдущий пример может быть записан следующим образом: :: -Часть выражения может быть указана в фигурных скобках. Предыдущий пример может быть записан следующим образом: -:: + example01-0{1,2}-1 - example01-0{1,2}-1 +В фигурных скобках может быть указан диапазон (неотрицательных целых) чисел через две точки. В этом случае, диапазон раскрывается в множество значений, генерирующих адреса шардов. Если запись первого числа начинается с нуля, то значения формируются с таким же выравниванием нулями. Предыдущий пример может быть записан следующим образом: :: -В фигурных скобках может быть указан диапазон (неотрицательных целых) чисел через две точки. В этом случае, диапазон раскрывается в множество значений, генерирующих адреса шардов. Если запись первого числа начинается с нуля, то значения формируются с таким же выравниванием нулями. Предыдущий пример может быть записан следующим образом: -:: - - example01-{01..02}-1 + example01-{01..02}-1 При наличии нескольких пар фигурных скобок, генерируется прямое произведение соответствующих множеств. -Адреса или их фрагменты в фигурных скобках, могут быть указаны через символ |. В этом случае, соответствующие множества адресов понимаются как реплики - запрос будет отправлен на первую живую реплику. При этом, реплики перебираются в порядке, согласно текущей настройке load_balancing. +Адреса или их фрагменты в фигурных скобках можно указать через символ \|. В этом случае, соответствующие множества адресов понимаются как реплики - запрос будет отправлен на первую живую реплику. При этом, реплики перебираются в порядке, согласно текущей настройке :ref:`load_balancing `. -Пример: -:: +Пример: :: - example01-{01..02}-{1|2} + example01-{01..02}-{1|2} В этом примере указано два шарда, в каждом из которых имеется две реплики. -Количество генерируемых адресов ограничено некоторой константой - сейчас это 1000 штук. +Количество генерируемых адресов ограничено константой - сейчас это 1000 штук. -Использование табличной функции remote менее оптимально, чем создание таблицы типа Distributed, так как в этом случае, соединения с серверами устанавливаются заново при каждом запросе, в случае задания имён хостов, делается резолвинг имён, а также не ведётся подсчёт ошибок при работе с разными репликами. При обработке большого количества запросов, всегда создавайте Distributed таблицу заранее, не используйте табличную функцию remote. +Использование табличной функции ``remote`` менее оптимально, чем создание таблицы типа ``Distributed``, так как в этом случае, соединения с серверами устанавливаются заново при каждом запросе, в случае задания имён хостов, делается резолвинг имён, а также не ведётся подсчёт ошибок при работе с разными репликами. При обработке большого количества запросов, всегда создавайте ``Distributed`` таблицу заранее, не используйте табличную функцию ``remote``. -Табличная функция remote может быть полезна для следующих случаев: +Табличная функция ``remote`` может быть полезна для следующих случаях: * обращение на конкретный сервер в целях сравнения данных, отладки и тестирования; * запросы между разными кластерами ClickHouse в целях исследований; * нечастых распределённых запросов, задаваемых вручную; * распределённых запросов, где набор серверов определяется каждый раз заново. -Имя пользователя может быть не задано - тогда используется имя пользователя 'default'. -Пароль может быть не задан - тогда используется пустой пароль. +Если пользователь не задан,то используется ``default``. +Если пароль не задан, то используется пустой пароль. \ No newline at end of file diff --git a/release b/release index 11153458ce7..4c36fcb8c9a 100755 --- a/release +++ b/release @@ -24,6 +24,9 @@ do gen_revision_author git push exit 0 + elif [[ $1 == '--head' ]]; then + REVISION=`git rev-parse HEAD` + shift else echo "Unknown option $1" exit 2 diff --git a/website/404.html b/website/404.html new file mode 100644 index 00000000000..947ad184280 --- /dev/null +++ b/website/404.html @@ -0,0 +1,171 @@ + + + + + ClickHouse — 404 + + + + + +
+ + + + + + + + + + + diff --git a/website/benchmark.html b/website/benchmark.html index 0cd3a79a6a3..eb107c0a6c0 100644 --- a/website/benchmark.html +++ b/website/benchmark.html @@ -226,16 +226,31 @@ function getParams() { - diff --git a/website/gulpfile.js b/website/gulpfile.js index 79a9b6d2e72..512fa0cd064 100644 --- a/website/gulpfile.js +++ b/website/gulpfile.js @@ -8,12 +8,15 @@ var htmlmin = require('gulp-htmlmin'); var minifyInline = require('gulp-minify-inline'); var del = require('del'); var connect = require('gulp-connect'); +var run = require('gulp-run'); var outputDir = 'public'; +var docsDir = '../docs/build/docs'; var paths = { htmls: ['*.html', '!reference_ru.html', '!reference_en.html'], reference: ['reference_ru.html', 'reference_en.html'], + docs: [docsDir + '/**'], scripts: ['*.js', '!gulpfile.js'], styles: ['*.css'], images: ['*.png', '*.ico'], @@ -22,7 +25,7 @@ var paths = { }; gulp.task('clean', function () { - return del([outputDir + '**']); + return del([outputDir + '/**']); }); gulp.task('reference', [], function () { @@ -32,6 +35,13 @@ gulp.task('reference', [], function () { .pipe(connect.reload()) }); +gulp.task('docs', [], function () { + run('cd ' + docsDir + '; make'); + return gulp.src(paths.docs) + .pipe(gulp.dest(outputDir + '/docs')) + .pipe(connect.reload()) +}); + gulp.task('presentations', [], function () { return gulp.src(paths.presentations) .pipe(gulp.dest(outputDir + '/presentations')) @@ -44,7 +54,7 @@ gulp.task('robotstxt', [], function () { .pipe(connect.reload()) }); -gulp.task('htmls', ['reference', 'robotstxt', 'presentations'], function () { +gulp.task('htmls', ['reference', 'docs', 'robotstxt', 'presentations'], function () { return gulp.src(paths.htmls) .pipe(htmlmin({collapseWhitespace: true})) .pipe(minifyInline()) diff --git a/website/index.html b/website/index.html index 3bd7111b220..082f8d49292 100644 --- a/website/index.html +++ b/website/index.html @@ -139,9 +139,8 @@ -moz-transform: rotate(-45deg); -ms-transform: rotate(-45deg); -o-transform: rotate(-45deg); - background: #fc0; + background: #fd0; border: 2px solid #000; - mix-blend-mode: hard-light; } #github_link { @@ -770,12 +769,10 @@
 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv E0C56BD4    # optional
 
-sudo mkdir -p /etc/apt/sources.list.d
-echo "deb http://repo.yandex.ru/clickhouse/trusty stable main" |
-    sudo tee /etc/apt/sources.list.d/clickhouse.list
+sudo apt-add-repository "deb http://repo.yandex.ru/clickhouse/trusty stable main"
 sudo apt-get update
 
-sudo apt-get install clickhouse-server-common clickhouse-client
+sudo apt-get install clickhouse-server-common clickhouse-client -y
 
 sudo service clickhouse-server start
 clickhouse-client
@@ -847,6 +844,7 @@ clickhouse-client
             $('html, body').animate({
                 scrollTop: offset
             }, 500);
+            window.history.replaceState('', document.title, window.location.href.replace(location.hash, '') + this.hash);
         });
 
         var available_distributives = ['xenial', 'trusty', 'precise'];
@@ -862,19 +860,31 @@ clickhouse-client
 
 
 
-