diff --git a/dbms/include/DB/Core/Types.h b/dbms/include/DB/Core/Types.h index 04d26f5e1ae..fac272c7f86 100644 --- a/dbms/include/DB/Core/Types.h +++ b/dbms/include/DB/Core/Types.h @@ -60,7 +60,4 @@ template <> struct TypeName { static std::string get() { return "Float template <> struct TypeName { static std::string get() { return "Float64"; } }; template <> struct TypeName { static std::string get() { return "String"; } }; -/// Эти типы не поддерживаются СУБД. Но используются в других местах. -template <> struct TypeName{ static std::string get() { return "long double"; } }; - } diff --git a/dbms/include/DB/Interpreters/Settings.h b/dbms/include/DB/Interpreters/Settings.h index 259d9256174..a7e0042595d 100644 --- a/dbms/include/DB/Interpreters/Settings.h +++ b/dbms/include/DB/Interpreters/Settings.h @@ -86,9 +86,6 @@ struct Settings M(SettingTotalsMode, totals_mode, TotalsMode::AFTER_HAVING_EXCLUSIVE) \ M(SettingFloat, totals_auto_threshold, 0.5) \ \ - /** Сэмплирование по умолчанию. Если равно 1, то отключено. */ \ - M(SettingFloat, default_sample, 1.0) \ - \ /** Включена ли компиляция запросов. */ \ M(SettingBool, compile, false) \ /** Количество одинаковых по структуре запросов перед тем, как инициируется их компиляция. */ \ diff --git a/dbms/include/DB/Parsers/ASTSampleRatio.h b/dbms/include/DB/Parsers/ASTSampleRatio.h new file mode 100644 index 00000000000..050becadd1f --- /dev/null +++ b/dbms/include/DB/Parsers/ASTSampleRatio.h @@ -0,0 +1,43 @@ +#pragma once + +#include + + +namespace DB +{ + +/** Коэффициент сэмплирования вида 0.1 или 1/10. + * Важно сохранять его как рациональное число без преобразования в IEEE-754. + */ +class ASTSampleRatio : public IAST +{ +public: + using BigNum = __uint128_t; /// Должен вмещать в себя результат перемножения двух UInt64. + + struct Rational + { + BigNum numerator = 0; + BigNum denominator = 1; + }; + + Rational ratio; + + ASTSampleRatio() = default; + ASTSampleRatio(const StringRange range_) : IAST(range_) {} + ASTSampleRatio(const StringRange range_, Rational & ratio_) : IAST(range_), ratio(ratio_) {} + + String getID() const override { return "SampleRatio_" + toString(ratio); } + + ASTPtr clone() const override { return new ASTSampleRatio(*this); } + + static String toString(BigNum num); + static String toString(Rational ratio); + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + settings.ostr << toString(ratio); + } +}; + +} diff --git a/dbms/include/DB/Parsers/ParserSampleRatio.h b/dbms/include/DB/Parsers/ParserSampleRatio.h new file mode 100644 index 00000000000..c5c2f0a9def --- /dev/null +++ b/dbms/include/DB/Parsers/ParserSampleRatio.h @@ -0,0 +1,19 @@ +#pragma once + +#include + + +namespace DB +{ + +/** Коэффициент сэмплирования вида 0.1 или 1/10. + * Парсится как рациональное число без преобразования в IEEE-754. + */ +class ParserSampleRatio : public IParserBase +{ +protected: + const char * getName() const { return "Sample ratio or offset"; } + bool parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_parsed_pos, Expected & expected); +}; + +} diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index deab0d69127..c52cb773274 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -698,12 +698,6 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns() interpreter_subquery->ignoreWithTotals(); } - /// если в настройках установлен default_sample != 1, то все запросы выполняем с сэмплингом - /// если таблица не поддерживает сэмплинг получим исключение - /// поэтому запросы типа SHOW TABLES работать с включенном default_sample не будут - if (!query.sample_size && settings.default_sample != 1) - query.sample_size = new ASTLiteral(StringRange(), Float64(settings.default_sample)); - if (query.sample_size && (!storage || !storage->supportsSampling())) throw Exception("Illegal SAMPLE: table doesn't support sampling", ErrorCodes::SAMPLING_NOT_SUPPORTED); diff --git a/dbms/src/Parsers/ASTSampleRatio.cpp b/dbms/src/Parsers/ASTSampleRatio.cpp new file mode 100644 index 00000000000..2150cf59b22 --- /dev/null +++ b/dbms/src/Parsers/ASTSampleRatio.cpp @@ -0,0 +1,40 @@ +#include + +namespace DB +{ + + +String ASTSampleRatio::toString(BigNum num) +{ + if (num == 0) + return "0"; + + static const size_t MAX_WIDTH = 40; + + char tmp[MAX_WIDTH]; + + char * pos; + for (pos = tmp + MAX_WIDTH - 1; num != 0; --pos) + { + *pos = '0' + num % 10; + num /= 10; + } + + ++pos; + + return String(pos, tmp + MAX_WIDTH - pos); +} + + +String ASTSampleRatio::toString(Rational ratio) +{ + if (ratio.denominator == 1) + return toString(ratio.numerator); + else + return toString(ratio.numerator) + " / " + toString(ratio.denominator); +} + + + + +} diff --git a/dbms/src/Parsers/ParserSampleRatio.cpp b/dbms/src/Parsers/ParserSampleRatio.cpp new file mode 100644 index 00000000000..89bf1c4f029 --- /dev/null +++ b/dbms/src/Parsers/ParserSampleRatio.cpp @@ -0,0 +1,128 @@ +#include +#include +#include +#include + + +namespace DB +{ + + +static bool parseDecimal(IParser::Pos & pos, IParser::Pos end, ASTSampleRatio::Rational & res, IParser::Pos & max_parsed_pos) +{ + ParserWhiteSpaceOrComments ws; + ws.ignore(pos, end); + + UInt64 num_before = 0; + UInt64 num_after = 0; + Int64 exponent = 0; + + IParser::Pos pos_after_first_num = tryReadIntText(num_before, pos, end); + + bool has_num_before_point = pos_after_first_num > pos; + pos = pos_after_first_num; + bool has_point = pos < end && *pos == '.'; + + if (has_point) + ++pos; + + if (!has_num_before_point && !has_point) + return false; + + size_t number_of_digits_after_point = 0; + + if (has_point) + { + IParser::Pos pos_after_second_num = tryReadIntText(num_after, pos, end); + number_of_digits_after_point = pos_after_second_num - pos; + pos = pos_after_second_num; + } + + bool has_exponent = pos < end && (*pos == 'e' || *pos == 'E'); + + if (has_exponent) + { + ++pos; + IParser::Pos pos_after_exponent = tryReadIntText(exponent, pos, end); + + if (pos_after_exponent == pos) + return false; + + pos = pos_after_exponent; + } + + res.numerator = num_before * exp10(number_of_digits_after_point) + num_after; + res.denominator = exp10(number_of_digits_after_point); + + if (exponent > 0) + res.numerator *= exp10(exponent); + if (exponent < 0) + res.denominator *= exp10(-exponent); + + /// NOTE Удаление общих степеней десяти из числителя и знаменателя - не нужно. + return true; +} + + +/** Возможные варианты: + * + * 12345 + * - целое число + * + * 0.12345 + * .12345 + * 0. + * - дробь в обычной десятичной записи + * + * 1.23e-1 + * - дробь в инженерной десятичной записи + * + * 123 / 456 + * - дробь с произвольным знаменателем + * + * На всякий случай, в числителе и знаменателе дроби, поддерживаем предыдущие случаи. + * Пример: + * 123.0 / 456e0 + */ +bool ParserSampleRatio::parseImpl(IParser::Pos & pos, IParser::Pos end, ASTPtr & node, IParser::Pos & max_parsed_pos, Expected & expected) +{ + auto begin = pos; + + ParserWhiteSpaceOrComments ws; + + ASTSampleRatio::Rational numerator; + ASTSampleRatio::Rational denominator; + ASTSampleRatio::Rational res; + + ws.ignore(pos, end); + + if (!parseDecimal(pos, end, numerator, max_parsed_pos)) + return false; + + ws.ignore(pos, end); + + bool has_slash = pos < end && *pos == '/'; + + if (has_slash) + { + ++pos; + ws.ignore(pos, end); + + if (!parseDecimal(pos, end, denominator, max_parsed_pos)) + return false; + + res.numerator = numerator.numerator * denominator.denominator; + res.denominator = numerator.denominator * denominator.numerator; + } + else + { + res = numerator; + } + + ws.ignore(pos, end); + + node = new ASTSampleRatio(StringRange(begin, pos), res); + return true; +} + +} diff --git a/dbms/src/Parsers/ParserSelectQuery.cpp b/dbms/src/Parsers/ParserSelectQuery.cpp index 1ff373af3d0..b82e7de9816 100644 --- a/dbms/src/Parsers/ParserSelectQuery.cpp +++ b/dbms/src/Parsers/ParserSelectQuery.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace DB @@ -156,9 +157,9 @@ bool ParserSelectQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_p { ws.ignore(pos, end); - ParserNumber num; + ParserSampleRatio ratio; - if (!num.parse(pos, end, select_query->sample_size, max_parsed_pos, expected)) + if (!ratio.parse(pos, end, select_query->sample_size, max_parsed_pos, expected)) return false; ws.ignore(pos, end); @@ -168,9 +169,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_p { ws.ignore(pos, end); - ParserNumber num; - - if (!num.parse(pos, end, select_query->sample_offset, max_parsed_pos, expected)) + if (!ratio.parse(pos, end, select_query->sample_offset, max_parsed_pos, expected)) return false; ws.ignore(pos, end); diff --git a/dbms/src/Server/Server.cpp b/dbms/src/Server/Server.cpp index 4a12bc5ab8b..134b5e8e6d8 100644 --- a/dbms/src/Server/Server.cpp +++ b/dbms/src/Server/Server.cpp @@ -210,7 +210,7 @@ public: const MergeTreeSettings & settings = context.getMergeTreeSettings(); - bool ok = true; + bool ok = /*true*/false; std::stringstream message; for (const auto & db : replicated_tables) diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 9b215b6a329..623e8ae64e5 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -1,9 +1,12 @@ +#include /// Для вычислений, связанных с коэффициентами сэмплирования. + #include #include #include #include #include #include +#include #include #include #include @@ -65,10 +68,13 @@ size_t MergeTreeDataSelectExecutor::getApproximateTotalRowsToRead( } -/** Пожалуй, наиболее удобный способ разбить диапазон 64-битных целых чисел на интервалы по их относительной величине - * - использовать для этого long double. Это некроссплатформенно. Надо, чтобы long double содержал хотя бы 64 бита мантиссы. - */ -using RelativeSize = long double; +using RelativeSize = boost::rational; + +static std::ostream & operator<<(std::ostream & ostr, const RelativeSize & x) +{ + ostr << ASTSampleRatio::toString(x.numerator()) << "/" << ASTSampleRatio::toString(x.denominator()); + return ostr; +} /// Переводит размер сэмпла в приблизительном количестве строк (вида SAMPLE 1000000) в относительную величину (вида SAMPLE 0.1). @@ -77,8 +83,10 @@ static RelativeSize convertAbsoluteSampleSizeToRelative(const ASTPtr & node, siz if (approx_total_rows == 0) return 1; - size_t absolute_sample_size = apply_visitor(FieldVisitorConvertToNumber(), typeid_cast(*node).value); - return std::min(RelativeSize(1.0), RelativeSize(absolute_sample_size) / approx_total_rows); + const ASTSampleRatio & node_sample = typeid_cast(*node); + + auto absolute_sample_size = node_sample.ratio.numerator / node_sample.ratio.denominator; + return std::min(RelativeSize(1), RelativeSize(absolute_sample_size) / approx_total_rows); } @@ -166,16 +174,18 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( if (select.sample_size) { - relative_sample_size = apply_visitor(FieldVisitorConvertToNumber(), - typeid_cast(*select.sample_size).value); + relative_sample_size.assign( + typeid_cast(*select.sample_size).ratio.numerator, + typeid_cast(*select.sample_size).ratio.denominator); if (relative_sample_size < 0) throw Exception("Negative sample size", ErrorCodes::ARGUMENT_OUT_OF_BOUND); relative_sample_offset = 0; if (select.sample_offset) - relative_sample_offset = apply_visitor(FieldVisitorConvertToNumber(), - typeid_cast(*select.sample_offset).value); + relative_sample_offset.assign( + typeid_cast(*select.sample_offset).ratio.numerator, + typeid_cast(*select.sample_offset).ratio.denominator); if (relative_sample_offset < 0) throw Exception("Negative sample offset", ErrorCodes::ARGUMENT_OUT_OF_BOUND); @@ -234,10 +244,8 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( * <------> - size * <--><--> - кусочки для разных parallel_replica_offset, выбираем второй. * - * TODO * Очень важно, чтобы интервалы для разных parallel_replica_offset покрывали весь диапазон без пропусков и перекрытий. * Также важно, чтобы весь юнивёрсум можно было покрыть, используя SAMPLE 0.1 OFFSET 0, ... OFFSET 0.9 и похожие десятичные дроби. - * Сейчас это не гарантируется. */ bool use_sampling = relative_sample_size > 0 || settings.parallel_replicas_count > 1; @@ -272,16 +280,16 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( bool has_lower_limit = false; bool has_upper_limit = false; - RelativeSize lower_limit_float = relative_sample_offset * size_of_universum; - RelativeSize upper_limit_float = (relative_sample_offset + relative_sample_size) * size_of_universum; + RelativeSize lower_limit_rational = relative_sample_offset * size_of_universum; + RelativeSize upper_limit_rational = (relative_sample_offset + relative_sample_size) * size_of_universum; - UInt64 lower = lower_limit_float; - UInt64 upper = upper_limit_float; + UInt64 lower = boost::rational_cast(lower_limit_rational); + UInt64 upper = boost::rational_cast(upper_limit_rational); if (lower > 0) has_lower_limit = true; - if (upper_limit_float <= size_of_universum) + if (lower_limit_rational <= size_of_universum) has_upper_limit = true; /* std::cerr << std::fixed << std::setprecision(100)