Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: the results of tikv and tiflash are different (#5839) #5876

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion dbms/src/Functions/FunctionsLogical.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,23 @@ struct AssociativeOperationImpl
{
if (Op::isSaturable())
{
UInt8 a = vec[i];
// cast a: UInt8 -> bool -> UInt8 is a trick
// TiFlash converts columns with non-UInt8 type to UInt8 type and sets value to 0 or 1
// which correspond to false or true. However, for columns with UInt8 type,
// no more convertion will be executed on them and the values stored
// in them are 'origin' which means that they won't be converted to 0 or 1.
// For example:
// Input column with non-UInt8 type:
// column_values = {-2, 0, 2}
// then, they will be converted to:
// vec = {1, 0, 1} (here vec stores converted values)
//
// Input column with UInt8 type:
// column_values = {1, 0, 2}
// then, the vec will be:
// vec = {1, 0, 2} (error, we only want 0 or 1)
// See issue: https://github.com/pingcap/tidb/issues/37258
bool a = static_cast<bool>(vec[i]);
return Op::isSaturatedValue(a) ? a : continuation.apply(i);
}
else
Expand Down
169 changes: 169 additions & 0 deletions dbms/src/Functions/tests/gtest_logical.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// 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 <Interpreters/Context.h>
#include <TestUtils/FunctionTestUtils.h>
#include <TestUtils/TiFlashTestBasic.h>

#include <string>
#include <vector>

namespace DB::tests
{
class Logical : public DB::tests::FunctionTest
{
};

TEST_F(Logical, andTest)
try
{
const String & func_name = "and";

// column, column
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({0, 1, 0, 0, {}, 0}),
executeFunction(
func_name,
createColumn<Nullable<UInt8>>({0, 1, 0, 1, {}, 0}),
createColumn<Nullable<UInt8>>({0, 1, 1, 0, 1, {}})));
// column, const
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({1, 0}),
executeFunction(
func_name,
createConstColumn<Nullable<UInt8>>(2, 1),
createColumn<Nullable<UInt8>>({1, 0})));
// const, const
ASSERT_COLUMN_EQ(
createConstColumn<UInt8>(1, 1),
executeFunction(
func_name,
createConstColumn<Nullable<UInt8>>(1, 1),
createConstColumn<Nullable<UInt8>>(1, 1)));
// only null
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({{}, 0}),
executeFunction(
func_name,
createOnlyNullColumnConst(2),
createColumn<Nullable<UInt8>>({1, 0})));
}
CATCH

TEST_F(Logical, orTest)
try
{
const String & func_name = "or";

// column, column
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({0, 1, 1, 1, 1, {}}),
executeFunction(
func_name,
createColumn<Nullable<UInt8>>({0, 1, 0, 1, {}, 0}),
createColumn<Nullable<UInt8>>({0, 1, 1, 0, 1, {}})));
// issue 5849
ASSERT_COLUMN_EQ(
createColumn<UInt8>({0, 1, 1, 1}),
executeFunction(
func_name,
createColumn<UInt8>({0, 123, 0, 41}),
createColumn<Int64>({0, 11, 221, 0})));
// column, const
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({1, 1}),
executeFunction(
func_name,
createConstColumn<Nullable<UInt8>>(2, 1),
createColumn<Nullable<UInt8>>({1, 0})));
// const, const
ASSERT_COLUMN_EQ(
createConstColumn<UInt8>(1, 1),
executeFunction(
func_name,
createConstColumn<Nullable<UInt8>>(1, 1),
createConstColumn<Nullable<UInt8>>(1, 0)));
// only null
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({1, {}}),
executeFunction(
func_name,
createOnlyNullColumnConst(2),
createColumn<Nullable<UInt8>>({1, 0})));
}
CATCH

TEST_F(Logical, xorTest)
try
{
const String & func_name = "xor";

// column, column
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({0, 0, 1, 1, {}, {}}),
executeFunction(
func_name,
createColumn<Nullable<UInt8>>({0, 1, 0, 1, {}, 0}),
createColumn<Nullable<UInt8>>({0, 1, 1, 0, 1, {}})));
// column, const
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({0, 1}),
executeFunction(
func_name,
createConstColumn<Nullable<UInt8>>(2, 1),
createColumn<Nullable<UInt8>>({1, 0})));
// const, const
ASSERT_COLUMN_EQ(
createConstColumn<UInt8>(1, 0),
executeFunction(
func_name,
createConstColumn<Nullable<UInt8>>(1, 1),
createConstColumn<Nullable<UInt8>>(1, 1)));
// only null
ASSERT_COLUMN_EQ(
createOnlyNullColumnConst(2),
executeFunction(
func_name,
createOnlyNullColumnConst(2),
createColumn<Nullable<UInt8>>({1, 0})));
}
CATCH

TEST_F(Logical, notTest)
try
{
const String & func_name = "not";

// column
ASSERT_COLUMN_EQ(
createColumn<Nullable<UInt8>>({1, 0, {}}),
executeFunction(
func_name,
createColumn<Nullable<UInt8>>({0, 1, {}})));
// const
ASSERT_COLUMN_EQ(
createConstColumn<UInt8>(1, 0),
executeFunction(
func_name,
createConstColumn<Nullable<UInt8>>(1, 1)));
// only null
ASSERT_COLUMN_EQ(
createOnlyNullColumnConst(1),
executeFunction(
func_name,
createOnlyNullColumnConst(1)));
}
CATCH

} // namespace DB::tests
78 changes: 78 additions & 0 deletions tests/fullstack-test/expr/logical_op.test
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mysql> drop table if exists test.t1;
mysql> drop table if exists test.t2;
<<<<<<< HEAD
mysql> create table test.t1(a char(20),b double);
mysql> create table test.t2(a char(20));
mysql> insert into test.t1 values(1,null),('j',0),(1,12.991),(0,0),(0,0),('they',1.009),('can',-99),(0,12.991),(1,-9.183),(null,1);
Expand All @@ -9,6 +10,27 @@ mysql> alter table test.t2 set tiflash replica 1;

func> wait_table test t1
func> wait_table test t2
=======
mysql> drop table if exists test.t3;
mysql> drop table if exists test.t4;
mysql> create table test.t1(a char(20),b double);
mysql> create table test.t2(a char(20));
mysql> create table test.t3(a int);
mysql> create table test.t4(a tinyint(45) unsigned NOT NULL, b bigint(20) NOT NULL);
mysql> insert into test.t1 values(1,null),('j',0),(1,12.991),(0,0),(0,0),('they',1.009),('can',-99),(0,12.991),(1,-9.183),(null,1);
mysql> insert into test.t2 values(0),(0),(0),(0),(0),(0),(1),('with'),('see'),(null);
mysql> insert into test.t3 values(0),(1);
mysql> insert into test.t4 values(65, 1),(66, 2), (67, 3), (0, 0);
mysql> alter table test.t1 set tiflash replica 1;
mysql> alter table test.t2 set tiflash replica 1;
mysql> alter table test.t3 set tiflash replica 1;
mysql> alter table test.t4 set tiflash replica 1;

func> wait_table test t1
func> wait_table test t2
func> wait_table test t3
func> wait_table test t4
>>>>>>> 624a10ac01 (fix: the results of tikv and tiflash are different (#5839))

mysql> set session tidb_isolation_read_engines='tiflash'; select count(*) from test.t1 where (b between null and 100) is null;
+----------+
Expand All @@ -24,5 +46,61 @@ mysql> set session tidb_isolation_read_engines='tiflash'; select count(*) from t
| 10 |
+----------+

<<<<<<< HEAD
#mysql> drop table if exists test.t1;
#mysql> drop table if exists test.t2;
=======
mysql> set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select null and a > 0, a from test.t3;
+----------------+------+
| null and a > 0 | a |
+----------------+------+
| 0 | 0 |
| NULL | 1 |
+----------------+------+

mysql> set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select null or a > 0, a from test.t3;
+---------------+------+
| null or a > 0 | a |
+---------------+------+
| NULL | 0 |
| 1 | 1 |
+---------------+------+

mysql> set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select null xor a > 0, a from test.t3;
+----------------+------+
| null xor a > 0 | a |
+----------------+------+
| NULL | 0 |
| NULL | 1 |
+----------------+------+

mysql> set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select !null, a from test.t3;
+-------+------+
| !null | a |
+-------+------+
| NULL | 0 |
| NULL | 1 |
+-------+------+

mysql> set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select count(*) from test.t3 group by a having min(null) and a > 0;
# empty

mysql> set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select count(*) from test.t3 group by a having ifnull(null,count(*)) and min(null);
# empty

# issue 5849
mysql> set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select a or b from test.t4;
+--------+
| a or b |
+--------+
| 1 |
| 1 |
| 1 |
| 0 |
+--------+

#mysql> drop table if exists test.t1;
#mysql> drop table if exists test.t2;
#mysql> drop table if exists test.t3;
#mysql> drop table if exists test.t4;
>>>>>>> 624a10ac01 (fix: the results of tikv and tiflash are different (#5839))