diff --git a/dbms/include/DB/Functions/FunctionsComparison.h b/dbms/include/DB/Functions/FunctionsComparison.h index 3c5c171b046..34f33ea75fc 100644 --- a/dbms/include/DB/Functions/FunctionsComparison.h +++ b/dbms/include/DB/Functions/FunctionsComparison.h @@ -10,7 +10,9 @@ #include #include #include +#include +#include #include @@ -384,6 +386,14 @@ template struct StringComparisonImpl> : StringEqualsImpl {}; +struct NameEquals { static constexpr auto name = "equals"; }; +struct NameNotEquals { static constexpr auto name = "notEquals"; }; +struct NameLess { static constexpr auto name = "less"; }; +struct NameGreater { static constexpr auto name = "greater"; }; +struct NameLessOrEquals { static constexpr auto name = "lessOrEquals"; }; +struct NameGreaterOrEquals { static constexpr auto name = "greaterOrEquals"; }; + + template < template class Op, typename Name> @@ -620,6 +630,108 @@ private: } } + void executeTuple(Block & block, size_t result, const IColumn * c0, const IColumn * c1) + { + /** Сравнивать кортежи будем лексикографически. Это делается следующим образом: + * x == y : x1 == y1 && x2 == y2 ... + * x != y : x1 != y1 || x2 != y2 ... + * + * x < y: x1 < y1 || (x1 == y1 && (x2 < y2 || (x2 == y2 ... && xn < yn)) + * x > y: x1 > y1 || (x1 == y1 && (x2 > y2 || (x2 == y2 ... && xn > yn)) + * x <= y: x1 < y1 || (x1 == y1 && (x2 < y2 || (x2 == y2 ... && xn <= yn)) + * + * Рекурсивная запись: + * x <= y: x1 < y1 || (x1 == y1 && x_tail <= y_tail) + * + * x >= y: x1 > y1 || (x1 == y1 && (x2 > y2 || (x2 == y2 ... && xn >= yn)) + */ + + const size_t tuple_size = x->getData().columns(); + + if (0 == tuple_size) + throw Exception("Comparison of zero-sized tuples is not implemented.", ErrorCodes::NOT_IMPLEMENTED); + + auto x = static_cast(c0); + auto y = static_cast(c1); + executeTupleImpl(block, result, x, y, tuple_size); + } + + void executeTupleImpl(Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size); + + template + void executeTupleEqualityImpl(Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size) + { + ComparisonFunction func_compare; + ConvolutionFunction func_convolution; + + Block tmp_block; + for (size_t i = 0; i < tuple_size; ++i) + { + tmp_block.insert(x->getData().getByPosition(i)); + tmp_block.insert(y->getData().getByPosition(i)); + + /// Сравнение элементов. + tmp_block.insert({ nullptr, new DataTypeUInt8, "" }); + func_compare.execute(tmp_block, {i * 3, i * 3 + 1}, i * 3 + 2); + } + + /// Логическая свёртка. + tmp_block.insert({ nullptr, new DataTypeUInt8, "" }); + + ColumnNumbers convolution_args(tuple_size); + for (size_t i = 0; i < tuple_size; ++i) + convolution_args[i] = i * 3 + 2; + + func_convolution.execute(tmp_block, convolution_args, tuple_size * 3); + block.getByPosition(result).column = tmp_block.getByPosition(tuple_size * 3).column; + } + + template + void executeTupleLessGreaterImpl(Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size) + { + HeadComparisonFunction func_compare_head; + TailComparisonFunction func_compare_tail; + FunctionAnd func_and; + FunctionOr func_or; + FunctionComparison func_equals; + + Block tmp_block; + + /// Попарное сравнение на неравенство всех элементов; на равенство всех элементов кроме последнего. + for (size_t i = 0; i < tuple_size; ++i) + { + tmp_block.insert(x->getData().getByPosition(i)); + tmp_block.insert(y->getData().getByPosition(i)); + + tmp_block.insert({ nullptr, new DataTypeUInt8, "" }); + + if (i + 1 != tuple_size) + { + func_compare_head.execute(tmp_block, {i * 4, i * 4 + 1}, i * 4 + 2); + + tmp_block.insert({ nullptr, new DataTypeUInt8, "" }); + func_equals.execute(tmp_block, {i * 4, i * 4 + 1}, i * 4 + 3); + + } + else + func_compare_tail.execute(tmp_block, {i * 4, i * 4 + 1}, i * 4 + 2); + } + + /// Комбинирование. Сложный код - сделайте рисунок. Можно заменить на рекурсивное сравнение кортежей. + size_t i = tuple_size - 1; + while (i > 0) + { + tmp_block.insert({ nullptr, new DataTypeUInt8, "" }); + func_and.execute(tmp_block, { tmp_block.columns() - 2, (i - 1) * 4 + 3 }, tmp_block.columns() - 1); + tmp_block.insert({ nullptr, new DataTypeUInt8, "" }); + func_or.execute(tmp_block, { tmp_block.columns() - 2, (i - 1) * 4 + 2 }, tmp_block.columns() - 1); + --i; + } + + block.getByPosition(result).column = tmp_block.getByPosition(tmp_block.columns() - 1).column; + } + + public: /// Получить имя функции. String getName() const override @@ -639,23 +751,27 @@ public: bool left_is_date_time = false; bool left_is_string = false; bool left_is_fixed_string = false; + const DataTypeTuple * left_tuple = nullptr; false || (left_is_date = typeid_cast(arguments[0].get())) || (left_is_date_time = typeid_cast(arguments[0].get())) || (left_is_string = typeid_cast(arguments[0].get())) - || (left_is_fixed_string = typeid_cast(arguments[0].get())); + || (left_is_fixed_string = typeid_cast(arguments[0].get())) + || (left_tuple = typeid_cast(arguments[0].get())); bool right_is_date = false; bool right_is_date_time = false; bool right_is_string = false; bool right_is_fixed_string = false; + const DataTypeTuple * right_tuple = nullptr; false || (right_is_date = typeid_cast(arguments[1].get())) || (right_is_date_time = typeid_cast(arguments[1].get())) || (right_is_string = typeid_cast(arguments[1].get())) - || (right_is_fixed_string = typeid_cast(arguments[1].get())); + || (right_is_fixed_string = typeid_cast(arguments[1].get())) + || (right_tuple = typeid_cast(arguments[1].get())); if (!( (arguments[0]->behavesAsNumber() && arguments[1]->behavesAsNumber()) || ((left_is_string || left_is_fixed_string) && (right_is_string || right_is_fixed_string)) @@ -664,10 +780,18 @@ public: || (left_is_string && right_is_date) || (left_is_date_time && right_is_date_time) || (left_is_date_time && right_is_string) - || (left_is_string && right_is_date_time))) + || (left_is_string && right_is_date_time) + || (left_tuple && right_tuple && left_tuple->getElements().size() == right_tuple->getElements().size()))) throw Exception("Illegal types of arguments (" + arguments[0]->getName() + ", " + arguments[1]->getName() + ")" " of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + if (left_tuple && right_tuple) + { + size_t size = left_tuple->getElements().size(); + for (size_t i = 0; i < size; ++i) + getReturnType({ left_tuple->getElements()[i], right_tuple->getElements()[i] }); + } + return new DataTypeUInt8; } @@ -696,25 +820,25 @@ public: + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } - else if (!left_is_num && !right_is_num) - { - executeString(block, result, col_left_untyped, col_right_untyped); - } else { - executeDateOrDateTimeWithConstString(block, result, col_left_untyped, col_right_untyped, left_is_num, right_is_num); + if (typeid_cast(col_left_untyped)) + { + executeTuple(block, result, col_left_untyped, col_right_untyped); + } + else if (!left_is_num && !right_is_num) + { + executeString(block, result, col_left_untyped, col_right_untyped); + } + else + { + executeDateOrDateTimeWithConstString(block, result, col_left_untyped, col_right_untyped, left_is_num, right_is_num); + } } } }; -struct NameEquals { static constexpr auto name = "equals"; }; -struct NameNotEquals { static constexpr auto name = "notEquals"; }; -struct NameLess { static constexpr auto name = "less"; }; -struct NameGreater { static constexpr auto name = "greater"; }; -struct NameLessOrEquals { static constexpr auto name = "lessOrEquals"; }; -struct NameGreaterOrEquals { static constexpr auto name = "greaterOrEquals"; }; - typedef FunctionComparison FunctionEquals; typedef FunctionComparison FunctionNotEquals; typedef FunctionComparison FunctionLess; @@ -722,4 +846,56 @@ typedef FunctionComparison FunctionGreater; typedef FunctionComparison FunctionLessOrEquals; typedef FunctionComparison FunctionGreaterOrEquals; + +template <> +void FunctionComparison::executeTupleImpl( + Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size) +{ + return executeTupleEqualityImpl, FunctionAnd>(block, result, x, y, tuple_size); +} + +template <> +void FunctionComparison::executeTupleImpl( + Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size) +{ + return executeTupleEqualityImpl, FunctionOr>(block, result, x, y, tuple_size); +} + +template <> +void FunctionComparison::executeTupleImpl( + Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size) +{ + return executeTupleLessGreaterImpl< + FunctionComparison, + FunctionComparison>(block, result, x, y, tuple_size); +} + +template <> +void FunctionComparison::executeTupleImpl( + Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size) +{ + return executeTupleLessGreaterImpl< + FunctionComparison, + FunctionComparison>(block, result, x, y, tuple_size); +} + +template <> +void FunctionComparison::executeTupleImpl( + Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size) +{ + return executeTupleLessGreaterImpl< + FunctionComparison, + FunctionComparison>(block, result, x, y, tuple_size); +} + +template <> +void FunctionComparison::executeTupleImpl( + Block & block, size_t result, const ColumnTuple * x, const ColumnTuple * y, size_t tuple_size) +{ + return executeTupleLessGreaterImpl< + FunctionComparison, + FunctionComparison>(block, result, x, y, tuple_size); +} + + } diff --git a/dbms/include/DB/Functions/FunctionsHigherOrder.h b/dbms/include/DB/Functions/FunctionsHigherOrder.h index 272714feb76..3a1857caef4 100644 --- a/dbms/include/DB/Functions/FunctionsHigherOrder.h +++ b/dbms/include/DB/Functions/FunctionsHigherOrder.h @@ -8,7 +8,7 @@ #include #include -#include "FunctionsMiscellaneous.h" +#include namespace DB diff --git a/dbms/tests/queries/0_stateless/00250_tuple_comparison.reference b/dbms/tests/queries/0_stateless/00250_tuple_comparison.reference new file mode 100644 index 00000000000..05547aaf6a4 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00250_tuple_comparison.reference @@ -0,0 +1,15 @@ +1 0 0 0 1 1 +0 1 1 0 1 0 +0 1 1 0 1 0 +0 1 1 0 1 0 +0 1 0 1 0 1 +0 1 0 1 0 1 +0 1 0 1 0 1 +1 0 0 0 1 1 +0 1 1 0 1 0 +0 1 1 0 1 0 +0 1 0 1 0 1 +0 1 0 1 0 1 +1 0 0 0 1 1 +0 1 1 0 1 0 +0 1 0 1 0 1 diff --git a/dbms/tests/queries/0_stateless/00250_tuple_comparison.sql b/dbms/tests/queries/0_stateless/00250_tuple_comparison.sql new file mode 100644 index 00000000000..8f5763e2ebb --- /dev/null +++ b/dbms/tests/queries/0_stateless/00250_tuple_comparison.sql @@ -0,0 +1,105 @@ +SELECT + (1, 'Hello', 23) = (1, 'Hello', 23), + (1, 'Hello', 23) != (1, 'Hello', 23), + (1, 'Hello', 23) < (1, 'Hello', 23), + (1, 'Hello', 23) > (1, 'Hello', 23), + (1, 'Hello', 23) <= (1, 'Hello', 23), + (1, 'Hello', 23) >= (1, 'Hello', 23); +SELECT + (1, 'Hello', 23) = (2, 'Hello', 23), + (1, 'Hello', 23) != (2, 'Hello', 23), + (1, 'Hello', 23) < (2, 'Hello', 23), + (1, 'Hello', 23) > (2, 'Hello', 23), + (1, 'Hello', 23) <= (2, 'Hello', 23), + (1, 'Hello', 23) >= (2, 'Hello', 23); +SELECT + (1, 'Hello', 23) = (1, 'World', 23), + (1, 'Hello', 23) != (1, 'World', 23), + (1, 'Hello', 23) < (1, 'World', 23), + (1, 'Hello', 23) > (1, 'World', 23), + (1, 'Hello', 23) <= (1, 'World', 23), + (1, 'Hello', 23) >= (1, 'World', 23); +SELECT + (1, 'Hello', 23) = (1, 'Hello', 24), + (1, 'Hello', 23) != (1, 'Hello', 24), + (1, 'Hello', 23) < (1, 'Hello', 24), + (1, 'Hello', 23) > (1, 'Hello', 24), + (1, 'Hello', 23) <= (1, 'Hello', 24), + (1, 'Hello', 23) >= (1, 'Hello', 24); +SELECT + (2, 'Hello', 23) = (1, 'Hello', 23), + (2, 'Hello', 23) != (1, 'Hello', 23), + (2, 'Hello', 23) < (1, 'Hello', 23), + (2, 'Hello', 23) > (1, 'Hello', 23), + (2, 'Hello', 23) <= (1, 'Hello', 23), + (2, 'Hello', 23) >= (1, 'Hello', 23); +SELECT + (1, 'World', 23) = (1, 'Hello', 23), + (1, 'World', 23) != (1, 'Hello', 23), + (1, 'World', 23) < (1, 'Hello', 23), + (1, 'World', 23) > (1, 'Hello', 23), + (1, 'World', 23) <= (1, 'Hello', 23), + (1, 'World', 23) >= (1, 'Hello', 23); +SELECT + (1, 'Hello', 24) = (1, 'Hello', 23), + (1, 'Hello', 24) != (1, 'Hello', 23), + (1, 'Hello', 24) < (1, 'Hello', 23), + (1, 'Hello', 24) > (1, 'Hello', 23), + (1, 'Hello', 24) <= (1, 'Hello', 23), + (1, 'Hello', 24) >= (1, 'Hello', 23); +SELECT + (1, 'Hello') = (1, 'Hello'), + (1, 'Hello') != (1, 'Hello'), + (1, 'Hello') < (1, 'Hello'), + (1, 'Hello') > (1, 'Hello'), + (1, 'Hello') <= (1, 'Hello'), + (1, 'Hello') >= (1, 'Hello'); +SELECT + (1, 'Hello') = (2, 'Hello'), + (1, 'Hello') != (2, 'Hello'), + (1, 'Hello') < (2, 'Hello'), + (1, 'Hello') > (2, 'Hello'), + (1, 'Hello') <= (2, 'Hello'), + (1, 'Hello') >= (2, 'Hello'); +SELECT + (1, 'Hello') = (1, 'World'), + (1, 'Hello') != (1, 'World'), + (1, 'Hello') < (1, 'World'), + (1, 'Hello') > (1, 'World'), + (1, 'Hello') <= (1, 'World'), + (1, 'Hello') >= (1, 'World'); +SELECT + (2, 'Hello') = (1, 'Hello'), + (2, 'Hello') != (1, 'Hello'), + (2, 'Hello') < (1, 'Hello'), + (2, 'Hello') > (1, 'Hello'), + (2, 'Hello') <= (1, 'Hello'), + (2, 'Hello') >= (1, 'Hello'); +SELECT + (1, 'World') = (1, 'Hello'), + (1, 'World') != (1, 'Hello'), + (1, 'World') < (1, 'Hello'), + (1, 'World') > (1, 'Hello'), + (1, 'World') <= (1, 'Hello'), + (1, 'World') >= (1, 'Hello'); +SELECT + tuple(1) = tuple(1), + tuple(1) != tuple(1), + tuple(1) < tuple(1), + tuple(1) > tuple(1), + tuple(1) <= tuple(1), + tuple(1) >= tuple(1); +SELECT + tuple(1) = tuple(2), + tuple(1) != tuple(2), + tuple(1) < tuple(2), + tuple(1) > tuple(2), + tuple(1) <= tuple(2), + tuple(1) >= tuple(2); +SELECT + tuple(2) = tuple(1), + tuple(2) != tuple(1), + tuple(2) < tuple(1), + tuple(2) > tuple(1), + tuple(2) <= tuple(1), + tuple(2) >= tuple(1);