From 7a9d15293f60e285337b384fc0923de6c152c07c Mon Sep 17 00:00:00 2001 From: xzhangxian1008 Date: Wed, 14 Sep 2022 11:08:59 +0800 Subject: [PATCH] This is an automated cherry-pick of #5839 Signed-off-by: ti-chi-bot --- dbms/src/Functions/FunctionsLogical.h | 18 ++- dbms/src/Functions/tests/gtest_logical.cpp | 169 +++++++++++++++++++++ tests/fullstack-test/expr/logical_op.test | 78 ++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 dbms/src/Functions/tests/gtest_logical.cpp diff --git a/dbms/src/Functions/FunctionsLogical.h b/dbms/src/Functions/FunctionsLogical.h index 4d8d7b71548..d2e50fe39e9 100644 --- a/dbms/src/Functions/FunctionsLogical.h +++ b/dbms/src/Functions/FunctionsLogical.h @@ -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(vec[i]); return Op::isSaturatedValue(a) ? a : continuation.apply(i); } else diff --git a/dbms/src/Functions/tests/gtest_logical.cpp b/dbms/src/Functions/tests/gtest_logical.cpp new file mode 100644 index 00000000000..53d2e45cc43 --- /dev/null +++ b/dbms/src/Functions/tests/gtest_logical.cpp @@ -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 +#include +#include + +#include +#include + +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>({0, 1, 0, 0, {}, 0}), + executeFunction( + func_name, + createColumn>({0, 1, 0, 1, {}, 0}), + createColumn>({0, 1, 1, 0, 1, {}}))); + // column, const + ASSERT_COLUMN_EQ( + createColumn>({1, 0}), + executeFunction( + func_name, + createConstColumn>(2, 1), + createColumn>({1, 0}))); + // const, const + ASSERT_COLUMN_EQ( + createConstColumn(1, 1), + executeFunction( + func_name, + createConstColumn>(1, 1), + createConstColumn>(1, 1))); + // only null + ASSERT_COLUMN_EQ( + createColumn>({{}, 0}), + executeFunction( + func_name, + createOnlyNullColumnConst(2), + createColumn>({1, 0}))); +} +CATCH + +TEST_F(Logical, orTest) +try +{ + const String & func_name = "or"; + + // column, column + ASSERT_COLUMN_EQ( + createColumn>({0, 1, 1, 1, 1, {}}), + executeFunction( + func_name, + createColumn>({0, 1, 0, 1, {}, 0}), + createColumn>({0, 1, 1, 0, 1, {}}))); + // issue 5849 + ASSERT_COLUMN_EQ( + createColumn({0, 1, 1, 1}), + executeFunction( + func_name, + createColumn({0, 123, 0, 41}), + createColumn({0, 11, 221, 0}))); + // column, const + ASSERT_COLUMN_EQ( + createColumn>({1, 1}), + executeFunction( + func_name, + createConstColumn>(2, 1), + createColumn>({1, 0}))); + // const, const + ASSERT_COLUMN_EQ( + createConstColumn(1, 1), + executeFunction( + func_name, + createConstColumn>(1, 1), + createConstColumn>(1, 0))); + // only null + ASSERT_COLUMN_EQ( + createColumn>({1, {}}), + executeFunction( + func_name, + createOnlyNullColumnConst(2), + createColumn>({1, 0}))); +} +CATCH + +TEST_F(Logical, xorTest) +try +{ + const String & func_name = "xor"; + + // column, column + ASSERT_COLUMN_EQ( + createColumn>({0, 0, 1, 1, {}, {}}), + executeFunction( + func_name, + createColumn>({0, 1, 0, 1, {}, 0}), + createColumn>({0, 1, 1, 0, 1, {}}))); + // column, const + ASSERT_COLUMN_EQ( + createColumn>({0, 1}), + executeFunction( + func_name, + createConstColumn>(2, 1), + createColumn>({1, 0}))); + // const, const + ASSERT_COLUMN_EQ( + createConstColumn(1, 0), + executeFunction( + func_name, + createConstColumn>(1, 1), + createConstColumn>(1, 1))); + // only null + ASSERT_COLUMN_EQ( + createOnlyNullColumnConst(2), + executeFunction( + func_name, + createOnlyNullColumnConst(2), + createColumn>({1, 0}))); +} +CATCH + +TEST_F(Logical, notTest) +try +{ + const String & func_name = "not"; + + // column + ASSERT_COLUMN_EQ( + createColumn>({1, 0, {}}), + executeFunction( + func_name, + createColumn>({0, 1, {}}))); + // const + ASSERT_COLUMN_EQ( + createConstColumn(1, 0), + executeFunction( + func_name, + createConstColumn>(1, 1))); + // only null + ASSERT_COLUMN_EQ( + createOnlyNullColumnConst(1), + executeFunction( + func_name, + createOnlyNullColumnConst(1))); +} +CATCH + +} // namespace DB::tests \ No newline at end of file diff --git a/tests/fullstack-test/expr/logical_op.test b/tests/fullstack-test/expr/logical_op.test index 5f89126dc6f..4fc713f5ba4 100644 --- a/tests/fullstack-test/expr/logical_op.test +++ b/tests/fullstack-test/expr/logical_op.test @@ -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); @@ -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; +----------+ @@ -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))