1212#include < Columns/ColumnArray.h>
1313
1414#include < DataTypes/DataTypesNumber.h>
15+ #include < DataTypes/DataTypeNullable.h>
1516#include < DataTypes/DataTypeDateTime.h>
1617#include < DataTypes/DataTypeDateTime64.h>
1718#include < DataTypes/DataTypeDate.h>
@@ -931,16 +932,19 @@ class FunctionComparison : public IFunction
931932 if (0 == tuple_size)
932933 throw Exception (" Comparison of zero-sized tuples is not implemented." , ErrorCodes::NOT_IMPLEMENTED);
933934
935+ ColumnsWithTypeAndName convolution_types (tuple_size);
936+
934937 Block tmp_block;
935938 for (size_t i = 0 ; i < tuple_size; ++i)
936939 {
937940 tmp_block.insert (x[i]);
938941 tmp_block.insert (y[i]);
939942
940943 auto impl = func_compare->build ({x[i], y[i]});
944+ convolution_types[i].type = impl->getReturnType ();
941945
942946 // / Comparison of the elements.
943- tmp_block.insert ({ nullptr , std::make_shared<DataTypeUInt8> (), " " });
947+ tmp_block.insert ({ nullptr , impl-> getReturnType (), " " });
944948 impl->execute (tmp_block, {i * 3 , i * 3 + 1 }, i * 3 + 2 , input_rows_count);
945949 }
946950
@@ -952,14 +956,13 @@ class FunctionComparison : public IFunction
952956 }
953957
954958 // / Logical convolution.
955- tmp_block.insert ({ nullptr , std::make_shared<DataTypeUInt8>(), " " });
956959
957960 ColumnNumbers convolution_args (tuple_size);
958961 for (size_t i = 0 ; i < tuple_size; ++i)
959962 convolution_args[i] = i * 3 + 2 ;
960963
961- ColumnsWithTypeAndName convolution_types (convolution_args.size (), { nullptr , std::make_shared<DataTypeUInt8>(), " " });
962964 auto impl = func_convolution->build (convolution_types);
965+ tmp_block.insert ({ nullptr , impl->getReturnType (), " " });
963966
964967 impl->execute (tmp_block, convolution_args, tuple_size * 3 , input_rows_count);
965968 block.getByPosition (result).column = tmp_block.getByPosition (tuple_size * 3 ).column ;
@@ -978,49 +981,71 @@ class FunctionComparison : public IFunction
978981 size_t tuple_size,
979982 size_t input_rows_count)
980983 {
981- ColumnsWithTypeAndName bin_args = {{ nullptr , std::make_shared<DataTypeUInt8>(), " " },
982- { nullptr , std::make_shared<DataTypeUInt8>(), " " }};
983-
984- auto func_and_adaptor = func_and->build (bin_args);
985- auto func_or_adaptor = func_or->build (bin_args);
986-
987984 Block tmp_block;
988985
989986 // / Pairwise comparison of the inequality of all elements; on the equality of all elements except the last.
987+ // / (x[i], y[i], x[i] < y[i], x[i] == y[i])
990988 for (size_t i = 0 ; i < tuple_size; ++i)
991989 {
992990 tmp_block.insert (x[i]);
993991 tmp_block.insert (y[i]);
994992
995- tmp_block.insert ({ nullptr , std::make_shared<DataTypeUInt8>(), " " });
993+ tmp_block.insert (ColumnWithTypeAndName ()); // pos == i * 4 + 2
996994
997995 if (i + 1 != tuple_size)
998996 {
999997 auto impl_head = func_compare_head->build ({x[i], y[i]});
998+ tmp_block.getByPosition (i * 4 + 2 ).type = impl_head->getReturnType ();
1000999 impl_head->execute (tmp_block, {i * 4 , i * 4 + 1 }, i * 4 + 2 , input_rows_count);
10011000
1002- tmp_block.insert ({ nullptr , std::make_shared<DataTypeUInt8>(), " " });
1001+ tmp_block.insert (ColumnWithTypeAndName ()); // i * 4 + 3
10031002
10041003 auto impl_equals = func_equals->build ({x[i], y[i]});
1004+ tmp_block.getByPosition (i * 4 + 3 ).type = impl_equals->getReturnType ();
10051005 impl_equals->execute (tmp_block, {i * 4 , i * 4 + 1 }, i * 4 + 3 , input_rows_count);
10061006
10071007 }
10081008 else
10091009 {
10101010 auto impl_tail = func_compare_tail->build ({x[i], y[i]});
1011+ tmp_block.getByPosition (i * 4 + 2 ).type = impl_tail->getReturnType ();
10111012 impl_tail->execute (tmp_block, {i * 4 , i * 4 + 1 }, i * 4 + 2 , input_rows_count);
10121013 }
10131014 }
10141015
10151016 // / Combination. Complex code - make a drawing. It can be replaced by a recursive comparison of tuples.
1017+ // / Last column contains intermediate result.
1018+ // / Code is generally equivalent to:
1019+ // / res = `x < y`[tuple_size - 1];
1020+ // / for (int i = tuple_size - 2; i >= 0; --i)
1021+ // / res = (res && `x == y`[i]) || `x < y`[i];
10161022 size_t i = tuple_size - 1 ;
10171023 while (i > 0 )
10181024 {
1019- tmp_block.insert ({ nullptr , std::make_shared<DataTypeUInt8>(), " " });
1020- func_and_adaptor->execute (tmp_block, {tmp_block.columns () - 2 , (i - 1 ) * 4 + 3 }, tmp_block.columns () - 1 , input_rows_count);
1021- tmp_block.insert ({ nullptr , std::make_shared<DataTypeUInt8>(), " " });
1022- func_or_adaptor->execute (tmp_block, {tmp_block.columns () - 2 , (i - 1 ) * 4 + 2 }, tmp_block.columns () - 1 , input_rows_count);
10231025 --i;
1026+
1027+ size_t and_lhs_pos = tmp_block.columns () - 1 ; // res
1028+ size_t and_rhs_pos = i * 4 + 3 ; // `x == y`[i]
1029+ tmp_block.insert (ColumnWithTypeAndName ());
1030+
1031+ ColumnsWithTypeAndName and_args = {{ nullptr , tmp_block.getByPosition (and_lhs_pos).type , " " },
1032+ { nullptr , tmp_block.getByPosition (and_rhs_pos).type , " " }};
1033+
1034+ auto func_and_adaptor = func_and->build (and_args);
1035+ tmp_block.getByPosition (tmp_block.columns () - 1 ).type = func_and_adaptor->getReturnType ();
1036+ func_and_adaptor->execute (tmp_block, {and_lhs_pos, and_rhs_pos}, tmp_block.columns () - 1 , input_rows_count);
1037+
1038+ size_t or_lhs_pos = tmp_block.columns () - 1 ; // (res && `x == y`[i])
1039+ size_t or_rhs_pos = i * 4 + 2 ; // `x < y`[i]
1040+ tmp_block.insert (ColumnWithTypeAndName ());
1041+
1042+ ColumnsWithTypeAndName or_args = {{ nullptr , tmp_block.getByPosition (or_lhs_pos).type , " " },
1043+ { nullptr , tmp_block.getByPosition (or_rhs_pos).type , " " }};
1044+
1045+ auto func_or_adaptor = func_or->build (or_args);
1046+ tmp_block.getByPosition (tmp_block.columns () - 1 ).type = func_or_adaptor->getReturnType ();
1047+ func_or_adaptor->execute (tmp_block, {or_lhs_pos, or_rhs_pos}, tmp_block.columns () - 1 , input_rows_count);
1048+
10241049 }
10251050
10261051 block.getByPosition (result).column = tmp_block.getByPosition (tmp_block.columns () - 1 ).column ;
@@ -1109,13 +1134,20 @@ class FunctionComparison : public IFunction
11091134 auto adaptor = FunctionOverloadResolverAdaptor (std::make_unique<DefaultOverloadResolver>(
11101135 FunctionComparison<Op, Name>::create (context)));
11111136
1137+ bool has_nullable = false ;
1138+
11121139 size_t size = left_tuple->getElements ().size ();
11131140 for (size_t i = 0 ; i < size; ++i)
11141141 {
11151142 ColumnsWithTypeAndName args = {{nullptr , left_tuple->getElements ()[i], " " },
11161143 {nullptr , right_tuple->getElements ()[i], " " }};
1117- adaptor.build (args);
1144+ has_nullable = has_nullable || adaptor.build (args)-> getReturnType ()-> isNullable ( );
11181145 }
1146+
1147+ // / If any element comparison is nullable, return type will also be nullable.
1148+ // / We useDefaultImplementationForNulls, but it doesn't work for tuples.
1149+ if (has_nullable)
1150+ return std::make_shared<DataTypeNullable>(std::make_shared<DataTypeUInt8>());
11191151 }
11201152
11211153 return std::make_shared<DataTypeUInt8>();
@@ -1135,7 +1167,7 @@ class FunctionComparison : public IFunction
11351167 // / NOTE: Nullable types are special case.
11361168 // / (BTW, this function use default implementation for Nullable, so Nullable types cannot be here. Check just in case.)
11371169 // / NOTE: We consider NaN comparison to be implementation specific (and in our implementation NaNs are sometimes equal sometimes not).
1138- if (left_type->equals (*right_type) && !left_type->isNullable () && col_left_untyped == col_right_untyped)
1170+ if (left_type->equals (*right_type) && !left_type->isNullable () && ! isTuple (left_type) && col_left_untyped == col_right_untyped)
11391171 {
11401172 // / Always true: =, <=, >=
11411173 if constexpr (std::is_same_v<Op<int , int >, EqualsOp<int , int >>
0 commit comments