diff --git a/dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.cpp b/dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.cpp index 7b70ae1f55f..98eb74ec53b 100644 --- a/dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.cpp +++ b/dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,6 @@ #include #include - namespace DB { namespace ErrorCodes @@ -923,7 +923,7 @@ void DAGExpressionAnalyzer::appendJoin( std::pair DAGExpressionAnalyzer::buildJoinKey( const ExpressionActionsPtr & actions, const google::protobuf::RepeatedPtrField & keys, - const DataTypes & key_types, + const JoinKeyTypes & join_key_types, bool left, bool is_right_out_join) { @@ -939,10 +939,13 @@ std::pair DAGExpressionAnalyzer::buildJoinKey( String key_name = getActions(key, actions); DataTypePtr current_type = actions->getSampleBlock().getByName(key_name).type; - if (!removeNullable(current_type)->equals(*removeNullable(key_types[i]))) + const auto & join_key_type = join_key_types[i]; + if (!removeNullable(current_type)->equals(*removeNullable(join_key_type.key_type))) { /// need to convert to key type - key_name = appendCast(key_types[i], actions, key_name); + key_name = join_key_type.is_incompatible_decimal + ? applyFunction("formatDecimal", {key_name}, actions, nullptr) + : appendCast(join_key_type.key_type, actions, key_name); has_actions = true; } if (!has_actions && (!left || is_right_out_join)) @@ -986,7 +989,7 @@ std::pair DAGExpressionAnalyzer::buildJoinKey( bool DAGExpressionAnalyzer::appendJoinKeyAndJoinFilters( ExpressionActionsChain & chain, const google::protobuf::RepeatedPtrField & keys, - const DataTypes & key_types, + const JoinKeyTypes & join_key_types, Names & key_names, bool left, bool is_right_out_join, @@ -997,7 +1000,7 @@ bool DAGExpressionAnalyzer::appendJoinKeyAndJoinFilters( ExpressionActionsPtr actions = chain.getLastActions(); bool ret = false; - std::tie(ret, key_names) = buildJoinKey(actions, keys, key_types, left, is_right_out_join); + std::tie(ret, key_names) = buildJoinKey(actions, keys, join_key_types, left, is_right_out_join); if (!filters.empty()) { diff --git a/dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.h b/dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.h index 0a8cd05749f..11155e88d88 100644 --- a/dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.h +++ b/dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.h @@ -43,6 +43,9 @@ enum class ExtraCastAfterTSMode AppendDurationCast }; +struct JoinKeyType; +using JoinKeyTypes = std::vector; + class DAGExpressionAnalyzerHelper; /** Transforms an expression from DAG expression into a sequence of actions to execute it. */ @@ -157,7 +160,7 @@ class DAGExpressionAnalyzer : private boost::noncopyable bool appendJoinKeyAndJoinFilters( ExpressionActionsChain & chain, const google::protobuf::RepeatedPtrField & keys, - const DataTypes & key_types, + const JoinKeyTypes & join_key_types, Names & key_names, bool left, bool is_right_out_join, @@ -288,7 +291,7 @@ class DAGExpressionAnalyzer : private boost::noncopyable std::pair buildJoinKey( const ExpressionActionsPtr & actions, const google::protobuf::RepeatedPtrField & keys, - const DataTypes & key_types, + const JoinKeyTypes & join_key_types, bool left, bool is_right_out_join); diff --git a/dbms/src/Flash/Coprocessor/JoinInterpreterHelper.cpp b/dbms/src/Flash/Coprocessor/JoinInterpreterHelper.cpp index 2fda680d9e1..63cee5859ff 100644 --- a/dbms/src/Flash/Coprocessor/JoinInterpreterHelper.cpp +++ b/dbms/src/Flash/Coprocessor/JoinInterpreterHelper.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,14 @@ #include -namespace DB::JoinInterpreterHelper +namespace DB +{ +namespace ErrorCodes +{ +extern const int NO_COMMON_TYPE; +} // namespace ErrorCodes + +namespace JoinInterpreterHelper { namespace { @@ -99,32 +107,59 @@ std::pair getJoinKindAndBuildSideIndex(const tipb::J return {kind, build_side_index}; } -DataTypes getJoinKeyTypes(const tipb::Join & join) +JoinKeyType geCommonTypeForJoinOn(const DataTypePtr & left_type, const DataTypePtr & right_type) +{ + try + { + return {getLeastSupertype({left_type, right_type}), false}; + } + catch (DB::Exception & e) + { + if (e.code() == ErrorCodes::NO_COMMON_TYPE + && removeNullable(left_type)->isDecimal() + && removeNullable(right_type)->isDecimal()) + { + // fix https://github.com/pingcap/tiflash/issues/4519 + // String is the common type for all types, it is always safe to choose String. + // But then we need to use `FunctionFormatDecimal` to format decimal. + // For example 0.1000000000 is equal to 0.10000000000000000000, but the original strings are not equal. + RUNTIME_ASSERT(!left_type->onlyNull() || !right_type->onlyNull()); + auto fall_back_type = std::make_shared(); + bool make_nullable = left_type->isNullable() || right_type->isNullable(); + return {make_nullable ? makeNullable(fall_back_type) : fall_back_type, true}; + } + else + { + throw; + } + } +} + +JoinKeyTypes getJoinKeyTypes(const tipb::Join & join) { if (unlikely(join.left_join_keys_size() != join.right_join_keys_size())) throw TiFlashException("size of join.left_join_keys != size of join.right_join_keys", Errors::Coprocessor::BadRequest); - DataTypes key_types; + JoinKeyTypes join_key_types; for (int i = 0; i < join.left_join_keys_size(); ++i) { if (unlikely(!exprHasValidFieldType(join.left_join_keys(i)) || !exprHasValidFieldType(join.right_join_keys(i)))) throw TiFlashException("Join key without field type", Errors::Coprocessor::BadRequest); - DataTypes types; - types.emplace_back(getDataTypeByFieldTypeForComputingLayer(join.left_join_keys(i).field_type())); - types.emplace_back(getDataTypeByFieldTypeForComputingLayer(join.right_join_keys(i).field_type())); - DataTypePtr common_type = getLeastSupertype(types); - key_types.emplace_back(common_type); + auto left_type = getDataTypeByFieldTypeForComputingLayer(join.left_join_keys(i).field_type()); + auto right_type = getDataTypeByFieldTypeForComputingLayer(join.right_join_keys(i).field_type()); + join_key_types.emplace_back(geCommonTypeForJoinOn(left_type, right_type)); } - return key_types; + return join_key_types; } -TiDB::TiDBCollators getJoinKeyCollators(const tipb::Join & join, const DataTypes & join_key_types) +TiDB::TiDBCollators getJoinKeyCollators(const tipb::Join & join, const JoinKeyTypes & join_key_types) { TiDB::TiDBCollators collators; size_t join_key_size = join_key_types.size(); if (join.probe_types_size() == static_cast(join_key_size) && join.build_types_size() == join.probe_types_size()) for (size_t i = 0; i < join_key_size; ++i) { - if (removeNullable(join_key_types[i])->isString()) + // Don't need to check the collate for decimal format string. + if (removeNullable(join_key_types[i].key_type)->isString() && !join_key_types[i].is_incompatible_decimal) { if (unlikely(join.probe_types(i).collate() != join.build_types(i).collate())) throw TiFlashException("Join with different collators on the join key", Errors::Coprocessor::BadRequest); @@ -330,7 +365,7 @@ std::tuple prepareJoin( const Context & context, const Block & input_header, const google::protobuf::RepeatedPtrField & keys, - const DataTypes & key_types, + const JoinKeyTypes & join_key_types, bool left, bool is_right_out_join, const google::protobuf::RepeatedPtrField & filters) @@ -342,7 +377,8 @@ std::tuple prepareJoin( ExpressionActionsChain chain; Names key_names; String filter_column_name; - dag_analyzer.appendJoinKeyAndJoinFilters(chain, keys, key_types, key_names, left, is_right_out_join, filters, filter_column_name); + dag_analyzer.appendJoinKeyAndJoinFilters(chain, keys, join_key_types, key_names, left, is_right_out_join, filters, filter_column_name); return {chain.getLastActions(), std::move(key_names), std::move(filter_column_name)}; } -} // namespace DB::JoinInterpreterHelper \ No newline at end of file +} // namespace JoinInterpreterHelper +} // namespace DB diff --git a/dbms/src/Flash/Coprocessor/JoinInterpreterHelper.h b/dbms/src/Flash/Coprocessor/JoinInterpreterHelper.h index 7a1d1a84efc..8f669db9c94 100644 --- a/dbms/src/Flash/Coprocessor/JoinInterpreterHelper.h +++ b/dbms/src/Flash/Coprocessor/JoinInterpreterHelper.h @@ -29,6 +29,13 @@ namespace DB { class Context; +struct JoinKeyType +{ + DataTypePtr key_type; + bool is_incompatible_decimal; +}; +using JoinKeyTypes = std::vector; + namespace JoinInterpreterHelper { struct TiFlashJoin @@ -40,7 +47,7 @@ struct TiFlashJoin ASTTableJoin::Kind kind; size_t build_side_index = 0; - DataTypes join_key_types; + JoinKeyTypes join_key_types; TiDB::TiDBCollators join_key_collators; ASTTableJoin::Strictness strictness; @@ -123,7 +130,7 @@ std::tuple prepareJoin( const Context & context, const Block & input_header, const google::protobuf::RepeatedPtrField & keys, - const DataTypes & key_types, + const JoinKeyTypes & join_key_types, bool left, bool is_right_out_join, const google::protobuf::RepeatedPtrField & filters); diff --git a/dbms/src/Functions/FunctionsString.cpp b/dbms/src/Functions/FunctionsString.cpp index e0c41857c66..196001e4698 100644 --- a/dbms/src/Functions/FunctionsString.cpp +++ b/dbms/src/Functions/FunctionsString.cpp @@ -325,7 +325,6 @@ struct ReverseUTF8Impl } }; - template 'fed.cba' +// `-abc.def` ==> '-fed.cba' +// `abc.def0000` ==> 'fed.cba' +// `-abc.def0000` ==> '-fed.cba' +// `abc.de0000f` ==> 'f0000ed.cba' +// `-abc.de0000f` ==> '-f0000ed.cba' +// `abc.de0000f0000` ==> 'f0000ed.cba' +// `-abc.de0000f0000` ==> '-f0000ed.cba' +// `0.def` ==> 'fed.' +// `-0.def` ==> '-fed.' +// `0.def0000` ==> 'fed.' +// `-0.def0000` ==> '-fed.' +// `0.de0000f` ==> 'f0000ed.' +// `-0.de0000f` ==> '-f0000ed.' +// `0.de0000f0000` ==> 'f0000ed.' +// `-0.de0000f0000` ==> '-f0000ed.' +// `abc` ==> 'cba' +// `-abc` ==> '-cba' +// `abc.00` ==> 'cba' +// `-abc.00` ==> '-cba' +// `abc0000` ==> '0000cba' +// `-abc0000` ==> '-0000cba' +// `abc0000.00` ==> '0000cba' +// `-abc0000.00` ==> '-0000cba' +// `0` ==> '' +// `0.00` ==> '' +class FunctionFormatDecimal : public IFunction +{ +public: + static constexpr auto name = "formatDecimal"; + + static FunctionPtr create(const Context & /*context*/) + { + return std::make_shared(); + } + + String getName() const override { return name; } + size_t getNumberOfArguments() const override { return 1; } + + bool useDefaultImplementationForConstants() const override { return true; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!arguments[0]->isDecimal()) + throw Exception( + fmt::format("Illegal type {} of first argument of function {}", arguments[0]->getName(), getName()), + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + return std::make_shared(); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) const override + { + const auto & base_type = block.getByPosition(arguments[0]).type; + bool is_types_valid = getDecimalType(base_type, [&](const auto & decimal_type, bool) { + using DecimalType = std::decay_t; + using DecimalFieldType = typename DecimalType::FieldType; + static_assert(IsDecimal); + using IntType = typename DecimalFieldType::NativeType; + using DecimalColVec = ColumnDecimal; + + const auto & col_arg = block.getByPosition(arguments[0]); + if (const auto * col = checkAndGetColumn(col_arg.column.get())) + { + auto precision = maxDecimalPrecision(); + auto scale = decimal_type.getScale(); + auto col_res = ColumnString::create(); + format(col, precision, scale, col_res->getChars(), col_res->getOffsets()); + block.getByPosition(result).column = std::move(col_res); + return true; + } + else + { + return false; + } + }); + + if (!is_types_valid) + throw Exception( + fmt::format("Illegal types {} arguments of function {}", base_type->getName(), getName()), + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + +private: + template + static bool getDecimalType(DataTypePtr type, F && f) + { + return castTypeToEither< + DataTypeDecimal32, + DataTypeDecimal64, + DataTypeDecimal128, + DataTypeDecimal256>(type.get(), std::forward(f)); + } + + template + static void format( + const DecimalColVec * col, + PrecType precision, + ScaleType scale, + ColumnString::Chars_t & res_data, + ColumnString::Offsets & res_offsets) + { + auto & data = col->getData(); + size_t size = data.size(); + res_data.resize(size * (precision + 3)); + res_offsets.resize(size); + + ColumnString::Offset cur_offset = 0; + for (size_t i = 0; i < size; ++i) + { + const auto & decimal = data[i]; + if (decimal.value == std::numeric_limits::min()) + // for IntType::min, `value = -value` may cause overflow, so use Int256 here. + doFormat(decimal.value, scale, cur_offset, res_data, res_offsets[i]); + else + doFormat(decimal.value, scale, cur_offset, res_data, res_offsets[i]); + } + } + + template + static void doFormat( + IntType value, + ScaleType scale, + ColumnString::Offset & cur_offset, + ColumnString::Chars_t & res_data, + ColumnString::Offset & res_offset) + { + if (value < 0) + { + res_data[cur_offset++] = '-'; + value = -value; + } + // fill decimal part + if (scale > 0 && value > 0) + { + size_t scale_i = 0; + // return false if the decimal part is all 0. + auto remove_tailing_zero = [&]() { + while (value > 0 && scale_i < scale) + { + int d = static_cast(value % 10); + value /= 10; + ++scale_i; + if (d != 0) + { + res_data[cur_offset++] = d + '0'; + return true; + } + } + return false; + }; + auto fill_decimal_part = [&]() { + for (; value > 0 && scale_i < scale; ++scale_i) + { + int d = static_cast(value % 10); + value /= 10; + res_data[cur_offset++] = d + '0'; + } + res_data[cur_offset++] = '.'; + }; + if (remove_tailing_zero()) + fill_decimal_part(); + } + // fill integer part + while (value > 0) + { + int d = static_cast(value % 10); + value = value / 10; + res_data[cur_offset++] = d + '0'; + } + res_data[cur_offset++] = 0; + res_offset = cur_offset; + } +}; + // clang-format off struct NameEmpty { static constexpr auto name = "empty"; }; struct NameNotEmpty { static constexpr auto name = "notEmpty"; }; @@ -5872,5 +6046,6 @@ void registerFunctionsString(FunctionFactory & factory) factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); + factory.registerFunction(); } } // namespace DB diff --git a/dbms/src/Functions/tests/gtest_strings_format_decimal.cpp b/dbms/src/Functions/tests/gtest_strings_format_decimal.cpp new file mode 100644 index 00000000000..4638a586fed --- /dev/null +++ b/dbms/src/Functions/tests/gtest_strings_format_decimal.cpp @@ -0,0 +1,234 @@ +// Copyright 2022 PingCAP, Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include +#include + +namespace DB +{ +namespace tests +{ +class FormatDecimal : public DB::tests::FunctionTest +{ +public: + // `abc.def` ==> 'fed.cba' + // `-abc.def` ==> '-fed.cba' + // `abc.def0000` ==> 'fed.cba' + // `-abc.def0000` ==> '-fed.cba' + // `abc.de0000f` ==> 'f0000ed.cba' + // `-abc.de0000f` ==> '-f0000ed.cba' + // `abc.de0000f0000` ==> 'f0000ed.cba' + // `-abc.de0000f0000` ==> '-f0000ed.cba' + // `0.def` ==> 'fed.' + // `-0.def` ==> '-fed.' + // `0.def0000` ==> 'fed.' + // `-0.def0000` ==> '-fed.' + // `0.de0000f` ==> 'f0000ed.' + // `-0.de0000f` ==> '-f0000ed.' + // `0.de0000f0000` ==> 'f0000ed.' + // `-0.de0000f0000` ==> '-f0000ed.' + // `abc` ==> 'cba' + // `-abc` ==> '-cba' + // `abc.00` ==> 'cba' + // `-abc.00` ==> '-cba' + // `abc0000` ==> '0000cba' + // `-abc0000` ==> '-0000cba' + // `abc0000.00` ==> '0000cba' + // `-abc0000.00` ==> '-0000cba' + // `0` ==> '' + // `0.00` ==> '' + template + void formatTest() + { + static const std::string func_name = "formatDecimal"; + using Native = typename Decimal::NativeType; + using FieldType = DecimalField; + using NullableDecimal = Nullable; + auto precision = maxDecimalPrecision(); + + auto execute_func = [&](const ColumnWithTypeAndName & column) { + return executeFunction(func_name, {column}, {}, true); + }; + + ASSERT_COLUMN_EQ( + createColumn>({"654.321", "-654.321"}), + execute_func(createColumn( + std::make_tuple(precision, 3), + { + FieldType(static_cast(123456), 3), // 123.456 + FieldType(static_cast(-123456), 3), // -123.456 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"654.321", "-654.321", "600054.321", "-600054.321"}), + execute_func(createColumn( + std::make_tuple(precision, 6), + { + FieldType(static_cast(123456000), 6), // 123.456000 + FieldType(static_cast(-123456000), 6), // -123.456000 + FieldType(static_cast(123450006), 6), // -123.450006 + FieldType(static_cast(-123450006), 6), // -123.450006 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"60054.321", "-60054.321"}), + execute_func(createColumn( + std::make_tuple(precision, 7), + { + FieldType(static_cast(1234500600), 7), // 123.4500600 + FieldType(static_cast(-1234500600), 7), // -123.4500600 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"321.", "-321."}), + execute_func(createColumn( + std::make_tuple(precision, 3), + { + FieldType(static_cast(123), 3), // 0.123 + FieldType(static_cast(-123), 3), // -0.123 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"321.", "-321.", "400321.", "-400321."}), + execute_func(createColumn( + std::make_tuple(precision, 6), + { + FieldType(static_cast(123000), 6), // 0.123000 + FieldType(static_cast(-123000), 6), // -0.123000 + FieldType(static_cast(123004), 6), // 0.123004 + FieldType(static_cast(-123004), 6), // -0.123004 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"400321.", "-400321."}), + execute_func(createColumn( + std::make_tuple(precision, 8), + { + FieldType(static_cast(12300400), 8), // 0.12300400 + FieldType(static_cast(-12300400), 8), // -0.12300400 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"321", "-321"}), + execute_func(createColumn( + std::make_tuple(precision, 0), + { + FieldType(static_cast(123), 0), // 123 + FieldType(static_cast(-123), 0), // -123 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"321", "-321"}), + execute_func(createColumn( + std::make_tuple(precision, 2), + { + FieldType(static_cast(12300), 2), // 123.00 + FieldType(static_cast(-12300), 2), // -123.00 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"00321", "-00321"}), + execute_func(createColumn( + std::make_tuple(precision, 0), + { + FieldType(static_cast(12300), 0), // 12300 + FieldType(static_cast(-12300), 0), // -12300 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({"00321", "-00321"}), + execute_func(createColumn( + std::make_tuple(precision, 2), + { + FieldType(static_cast(1230000), 2), // 12300.00 + FieldType(static_cast(-1230000), 2), // -12300.00 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({""}), + execute_func(createColumn( + std::make_tuple(precision, 0), + { + FieldType(static_cast(0), 0), // 0 + }))); + + ASSERT_COLUMN_EQ( + createColumn>({""}), + execute_func(createColumn( + std::make_tuple(precision, 2), + { + FieldType(static_cast(0), 2), // 0.00 + }))); + + auto test_for_min_max = [&](const ColumnWithTypeAndName & expected) { + ASSERT_COLUMN_EQ( + expected, + execute_func(createColumn( + std::make_tuple(precision, 2), + { + FieldType(static_cast(std::numeric_limits::max()), 2), + FieldType(static_cast(std::numeric_limits::max() - 1), 2), + FieldType(static_cast(std::numeric_limits::min()), 2), + FieldType(static_cast(std::numeric_limits::min() + 1), 2), + }))); + }; + if constexpr (std::is_same_v) + { + test_for_min_max(createColumn>({"74.63847412", + "64.63847412", + "-84.63847412", + "-74.63847412"})); + } + else if constexpr (std::is_same_v) + { + test_for_min_max(createColumn>({"70.85774586302733229", + "60.85774586302733229", + "-80.85774586302733229", + "-70.85774586302733229"})); + } + else if constexpr (std::is_same_v) + { + test_for_min_max(createColumn>({"72.7501488517303786137132964064381141071", + "62.7501488517303786137132964064381141071", + "-82.7501488517303786137132964064381141071", + "-72.7501488517303786137132964064381141071"})); + } + else + { + static_assert(std::is_same_v); + test_for_min_max(createColumn>({"53.9936921319700485754930465046566489962358709786800589075324591613732980297511", + "43.9936921319700485754930465046566489962358709786800589075324591613732980297511", + "-53.9936921319700485754930465046566489962358709786800589075324591613732980297511", + "-43.9936921319700485754930465046566489962358709786800589075324591613732980297511"})); + } + } +}; + +TEST_F(FormatDecimal, test) +try +{ + formatTest(); + formatTest(); + formatTest(); + formatTest(); +} +CATCH + +} // namespace tests +} // namespace DB diff --git a/tests/fullstack-test/mpp/issue_4519.test b/tests/fullstack-test/mpp/issue_4519.test new file mode 100644 index 00000000000..1ec87c59af4 --- /dev/null +++ b/tests/fullstack-test/mpp/issue_4519.test @@ -0,0 +1,45 @@ +# Copyright 2022 PingCAP, Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Preparation. +=> DBGInvoke __init_fail_point() + +mysql> drop table if exists test.test +mysql> create table test.test (col1 decimal(65, 10), col2 decimal(20, 20), col3 decimal(65, 0)) +mysql> insert into test.test values(0.1,0.1,1),(1.0,0.1,1),(0,0,0),(null,null,null),(99.9,0.99,99) + +mysql> alter table test.test set tiflash replica 1 + +func> wait_table test test + +mysql> use test; set tidb_allow_mpp=1;set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select t1.col1, t2.col2 from test t1 join test t2 on t1.col1 = t2.col2; ++--------------+------------------------+ +| col1 | col2 | ++--------------+------------------------+ +| 0.1000000000 | 0.10000000000000000000 | +| 0.1000000000 | 0.10000000000000000000 | +| 0.0000000000 | 0.00000000000000000000 | ++--------------+------------------------+ + +mysql> use test; set tidb_allow_mpp=1;set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select t1.col1, t2.col3 from test t1 join test t2 on t1.col1 = t2.col3; ++--------------+------+ +| col1 | col3 | ++--------------+------+ +| 1.0000000000 | 1 | +| 1.0000000000 | 1 | +| 0.0000000000 | 0 | ++--------------+------+ + +# Clean up. +mysql> drop table if exists test.test