From 59166608b1b46d8419b0cbecf347b82715102ecb Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Tue, 16 Aug 2022 00:12:15 +0300 Subject: [PATCH] MCOL-4715 Mixed inner and outer joins with "null" filter for the table which is not involved into the outer join produces wrong results. --- dbcon/joblist/jlf_common.h | 2 ++ dbcon/joblist/jlf_tuplejoblist.cpp | 31 ++++++++++++++++--- .../columnstore/bugfixes/mcol-4715.result | 30 ++++++++++++++++++ .../columnstore/bugfixes/mcol-4715.test | 28 +++++++++++++++++ 4 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 mysql-test/columnstore/bugfixes/mcol-4715.result create mode 100644 mysql-test/columnstore/bugfixes/mcol-4715.test diff --git a/dbcon/joblist/jlf_common.h b/dbcon/joblist/jlf_common.h index 5ec3e6d66..30a939843 100644 --- a/dbcon/joblist/jlf_common.h +++ b/dbcon/joblist/jlf_common.h @@ -286,6 +286,8 @@ struct JobInfo // bug 2634, 5311 and 5374, outjoin and predicates std::set outerOnTable; + // MCOL-4715. + std::set innerOnTable; std::set tableHasIsNull; JobStepVector outerJoinExpressions; diff --git a/dbcon/joblist/jlf_tuplejoblist.cpp b/dbcon/joblist/jlf_tuplejoblist.cpp index 19cc2774a..857566973 100644 --- a/dbcon/joblist/jlf_tuplejoblist.cpp +++ b/dbcon/joblist/jlf_tuplejoblist.cpp @@ -1576,6 +1576,12 @@ bool addFunctionJoin(vector& joinedTables, JobStepVector& joinSteps, s { m1->second.fTypes.push_back(joinType); m2->second.fTypes.push_back(joinType); + + if (joinType == INNER) + { + jobInfo.innerOnTable.insert(tid1); + jobInfo.innerOnTable.insert(tid2); + } } // need id to keep the join order @@ -2513,12 +2519,21 @@ void spanningTreeCheck(TableInfoMap& tableInfoMap, JobStepVector& joinSteps, Job void outjoinPredicateAdjust(TableInfoMap& tableInfoMap, JobInfo& jobInfo) { - set::iterator i = jobInfo.outerOnTable.begin(); + std::set tables = jobInfo.outerOnTable; + if (!tables.size()) + return; - for (; i != jobInfo.outerOnTable.end(); i++) + // Mixed outer/inner joins and a table with a `null filter`. + for (const auto tableId : jobInfo.innerOnTable) { - // resetTableFilters(tableInfoMap[*i], jobInfo) - TableInfo& tblInfo = tableInfoMap[*i]; + if (jobInfo.tableHasIsNull.find(tableId) != jobInfo.tableHasIsNull.end()) + tables.insert(tableId); + } + + for (const auto tableId : tables) + { + // resetTableFilters(tableInfoMap[tableId], jobInfo) + TableInfo& tblInfo = tableInfoMap[tableId]; if (tblInfo.fTableOid != CNX_VTABLE_ID) { @@ -2597,7 +2612,7 @@ void outjoinPredicateAdjust(TableInfoMap& tableInfoMap, JobInfo& jobInfo) } // Do not apply the primitive filters if there is an "IS NULL" in where clause. - if (jobInfo.tableHasIsNull.find(*i) != jobInfo.tableHasIsNull.end()) + if (jobInfo.tableHasIsNull.find(tableId) != jobInfo.tableHasIsNull.end()) tblInfo.fQuerySteps = onClauseFilterSteps; } @@ -4740,6 +4755,12 @@ void associateTupleJobSteps(JobStepVector& querySteps, JobStepVector& projectSte { m1->second.fTypes.push_back(joinType); m2->second.fTypes.push_back(joinType); + + if (joinType == INNER) + { + jobInfo.innerOnTable.insert(tid1); + jobInfo.innerOnTable.insert(tid2); + } } // need id to keep the join order diff --git a/mysql-test/columnstore/bugfixes/mcol-4715.result b/mysql-test/columnstore/bugfixes/mcol-4715.result new file mode 100644 index 000000000..1509f8b38 --- /dev/null +++ b/mysql-test/columnstore/bugfixes/mcol-4715.result @@ -0,0 +1,30 @@ +DROP DATABASE IF EXISTS mcol_4715; +CREATE DATABASE mcol_4715; +USE mcol_4715; +create table t1 (a int) engine=columnstore; +create table t2 (a int) engine=columnstore; +create table t3 (a int) engine=columnstore; +create table t4 (a int) engine=columnstore; +insert into t1 values (1), (2), (3), (4); +insert into t2 values (2), (3), (4); +insert into t3 values (3), (4); +insert into t4 values (4); +select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t2.a is null order by t1.a; +a a a +1 NULL NULL +2 NULL NULL +select * from t2 inner join t3 on t2.a = t3.a right join t1 on t1.a = t3.a where t2.a is null order by t1.a; +a a a +NULL NULL 1 +NULL NULL 2 +select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t3.a is null order by t1.a; +a a a a +1 NULL NULL NULL +2 2 NULL NULL +3 3 NULL NULL +select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t3.a is null order by t1.a; +a a a a +1 NULL NULL NULL +2 2 NULL NULL +3 3 NULL NULL +DROP DATABASE mcol_4715; diff --git a/mysql-test/columnstore/bugfixes/mcol-4715.test b/mysql-test/columnstore/bugfixes/mcol-4715.test new file mode 100644 index 000000000..109825b2d --- /dev/null +++ b/mysql-test/columnstore/bugfixes/mcol-4715.test @@ -0,0 +1,28 @@ +# +# Mixed inner and outer joins with "null filter" for the table which is not involved into the outer join produces wrong results. +# + +-- source ../include/have_columnstore.inc + +--disable_warnings +DROP DATABASE IF EXISTS mcol_4715; +--enable_warnings + +CREATE DATABASE mcol_4715; +USE mcol_4715; + +create table t1 (a int) engine=columnstore; +create table t2 (a int) engine=columnstore; +create table t3 (a int) engine=columnstore; +create table t4 (a int) engine=columnstore; +insert into t1 values (1), (2), (3), (4); +insert into t2 values (2), (3), (4); +insert into t3 values (3), (4); +insert into t4 values (4); + +select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t2.a is null order by t1.a; +select * from t2 inner join t3 on t2.a = t3.a right join t1 on t1.a = t3.a where t2.a is null order by t1.a; +select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t3.a is null order by t1.a; +select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t3.a is null order by t1.a; + +DROP DATABASE mcol_4715;