Skip to content

Commit

Permalink
fix(tianmu): fix query syntax (WHERE NOT IN ) is not supported. (#767)
Browse files Browse the repository at this point in the history
[summary]
1 root cause: sub-select item is the left argument,not the right argument of item_func;
  • Loading branch information
lujiashun committed Nov 15, 2022
1 parent c0c9973 commit 2e10d61
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 33 deletions.
152 changes: 152 additions & 0 deletions mysql-test/suite/tianmu/r/issue767.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use test;
create table t1(val int) ENGINE=tianmu;
create table t2(val2 int) ENGINE=tianmu;
insert into t1 values(0);
insert into t1 values(1);
insert into t1 values(10);
insert into t1 values(11);
insert into t1 values(20);
insert into t1 values(21);
insert into t1 values(42);
insert into t1 values(43);
insert into t2 values(0);
insert into t2 values(1);
insert into t2 values(10);
insert into t2 values(11);
insert into t2 values(20);
insert into t2 values(21);
insert into t2 values(42);
insert into t2 values(43);
select * from t1 where 42 not in (select * from t1 where val > 42);
val
0
1
10
11
20
21
42
43
select * from t1 where 42 not in (select * from t1 where val < 10);
val
0
1
10
11
20
21
42
43
select * from t1 where 42 not in (select * from t1 where val >= 42);
val
select * from t1 where 42 not in (select * from t1 where val <= 10);
val
0
1
10
11
20
21
42
43
insert into t2 values(10);
select * from t1 where val not in (select * from t2 where val2 > 10);
val
0
1
10
select * from t1 where val not in (select * from t2 where val2 >= 10);
val
0
1
select * from t1 where val not in (select * from t2 where val2 < 10);
val
10
11
20
21
42
43
select * from t1 where val not in (select * from t2 where val2 <=10);
val
11
20
21
42
43
select * from t1 where val not in (select * from t2 where val2 > t1.val);
val
0
1
10
11
20
21
42
43
select * from t1 where val not in (select * from t2 where val2 >= t1.val);
val
select * from t1 where val not in (select * from t2 where val2 < t1.val);
val
0
1
10
11
20
21
42
43
select * from t1 where val not in (select * from t2 where val2 <= t1.val);
val
select * from t1 where val not in (select * from t2 where val2 > t1.val and val2 >t1.val +10);
val
0
1
10
11
20
21
42
43
select * from t1 where val not in (select * from t2 where val2 >= t1.val and val2 >= t1.val +10);
val
0
1
10
11
20
21
42
43
select * from t1 where val not in (select * from t2 where val2 < t1.val and val2 <t1.val +10);
val
0
1
10
11
20
21
42
43
select * from t1 where val not in (select * from t2 where val2 <= t1.val and val2 >= t1.val +10);
val
0
1
10
11
20
21
42
43
select * from t1 where val not in (select * from t2 where val2 > t1.val);
val
0
1
10
11
20
21
42
43
drop table t1;
drop table t2;
49 changes: 49 additions & 0 deletions mysql-test/suite/tianmu/t/issue767.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--source include/have_tianmu.inc
use test;
create table t1(val int) ENGINE=tianmu;
create table t2(val2 int) ENGINE=tianmu;

insert into t1 values(0);
insert into t1 values(1);
insert into t1 values(10);
insert into t1 values(11);
insert into t1 values(20);
insert into t1 values(21);
insert into t1 values(42);
insert into t1 values(43);
insert into t2 values(0);
insert into t2 values(1);
insert into t2 values(10);
insert into t2 values(11);
insert into t2 values(20);
insert into t2 values(21);
insert into t2 values(42);
insert into t2 values(43);

#original case(just test not in)
select * from t1 where 42 not in (select * from t1 where val > 42);
select * from t1 where 42 not in (select * from t1 where val < 10);
select * from t1 where 42 not in (select * from t1 where val >= 42);
select * from t1 where 42 not in (select * from t1 where val <= 10);

#independent subquery
insert into t2 values(10);
select * from t1 where val not in (select * from t2 where val2 > 10);
select * from t1 where val not in (select * from t2 where val2 >= 10);
select * from t1 where val not in (select * from t2 where val2 < 10);
select * from t1 where val not in (select * from t2 where val2 <=10);

#dependent subquery
select * from t1 where val not in (select * from t2 where val2 > t1.val);
select * from t1 where val not in (select * from t2 where val2 >= t1.val);
select * from t1 where val not in (select * from t2 where val2 < t1.val);
select * from t1 where val not in (select * from t2 where val2 <= t1.val);

select * from t1 where val not in (select * from t2 where val2 > t1.val and val2 >t1.val +10);
select * from t1 where val not in (select * from t2 where val2 >= t1.val and val2 >= t1.val +10);
select * from t1 where val not in (select * from t2 where val2 < t1.val and val2 <t1.val +10);
select * from t1 where val not in (select * from t2 where val2 <= t1.val and val2 >= t1.val +10);
select * from t1 where val not in (select * from t2 where val2 > t1.val);

drop table t1;
drop table t2;
87 changes: 54 additions & 33 deletions storage/tianmu/core/query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1626,15 +1626,17 @@ bool Query::ClearSubselectTransformation(common::Operator &oper_for_subselect, I
Item *left_expr_for_subselect) {
cond_to_reinsert = nullptr;
list_to_reinsert = nullptr;
Item *cond_removed;
if (having &&
(having->type() == Item::COND_ITEM ||
(having->type() == Item::FUNC_ITEM && ((Item_func *)having)->functype() != Item_func::ISNOTNULLTEST_FUNC &&
(((Item_func *)having)->functype() != Item_func::TRIG_COND_FUNC ||
((Item_func *)having)->arguments()[0]->type() != Item::FUNC_ITEM ||
((Item_func *)((Item_func *)having)->arguments()[0])->functype() != Item_func::ISNOTNULLTEST_FUNC)))) {
Item *cond_removed = nullptr;
Item *left_ref = nullptr;
if (having && (having->type() == Item::COND_ITEM ||
(having->type() == Item::FUNC_ITEM &&
down_cast<Item_func *>(having)->functype() != Item_func::ISNOTNULLTEST_FUNC &&
(down_cast<Item_func *>(having)->functype() != Item_func::TRIG_COND_FUNC ||
down_cast<Item_func *>(having)->arguments()[0]->type() != Item::FUNC_ITEM ||
down_cast<Item_func *>(down_cast<Item_func *>(having)->arguments()[0])->functype() !=
Item_func::ISNOTNULLTEST_FUNC)))) {
if (having->type() == Item::COND_ITEM) {
Item_cond *having_cond = (Item_cond *)having;
Item_cond *having_cond = down_cast<Item_cond *>(having);
// if the condition is a complex formula it must be AND
if (having_cond->functype() != Item_func::COND_AND_FUNC)
return false;
Expand All @@ -1656,48 +1658,53 @@ bool Query::ClearSubselectTransformation(common::Operator &oper_for_subselect, I
cond_removed = UnRef(cond_removed);
// check if the extra condition was wrapped into trigger
if (cond_removed->type() == Item::FUNC_ITEM &&
((Item_func *)cond_removed)->functype() == Item_func::TRIG_COND_FUNC) {
cond_removed = ((Item_func *)cond_removed)->arguments()[0];
down_cast<Item_func *>(cond_removed)->functype() == Item_func::TRIG_COND_FUNC) {
cond_removed = down_cast<Item_func *>(cond_removed)->arguments()[0];
cond_removed = UnRef(cond_removed);
}
// check if the extra condition is a comparison
if (cond_removed->type() != Item::FUNC_ITEM || ((Item_func *)cond_removed)->arg_count != 2)
if (cond_removed->type() != Item::FUNC_ITEM || down_cast<Item_func *>(cond_removed)->arg_count != 2)
return false;
// the right side of equality is the field of the original subselect
if (dynamic_cast<Item_ref_null_helper *>(((Item_func *)cond_removed)->arguments()[1]) == nullptr)
if (dynamic_cast<Item_ref_null_helper *>(down_cast<Item_func *>(cond_removed)->arguments()[1]) == nullptr)
return false;
field_for_subselect = nullptr;
// the left side of equality should be the left side of the original
// expression with subselect
left_ref = down_cast<Item_func *>(cond_removed)->arguments()[0];
} else if (!having || (having->type() == Item::FUNC_ITEM &&
(((Item_func *)having)->functype() == Item_func::ISNOTNULLTEST_FUNC ||
((Item_func *)having)->functype() == Item_func::TRIG_COND_FUNC))) {
(down_cast<Item_func *>(having)->functype() == Item_func::ISNOTNULLTEST_FUNC ||
down_cast<Item_func *>(having)->functype() == Item_func::TRIG_COND_FUNC))) {
if (!conds)
return false;
if (conds->type() == Item::COND_ITEM && ((Item_cond *)conds)->functype() == Item_func::COND_AND_FUNC) {
if (conds->type() == Item::COND_ITEM && down_cast<Item_cond *>(conds)->functype() == Item_func::COND_AND_FUNC) {
// if the condition is a conjunctive formula
// the extra condition should be in the last argument
if (((Item_cond *)conds)->argument_list()->elements < 2)
if (down_cast<Item_cond *>(conds)->argument_list()->elements < 2)
return false;
List_iterator<Item> li(*(((Item_cond *)conds)->argument_list()));

List_iterator<Item> li(*(down_cast<Item_cond *>(conds)->argument_list()));
while (li++ != nullptr) cond_to_reinsert = *li.ref();
li.rewind();
while (*li.ref() != cond_to_reinsert) li++;
li.remove();
list_to_reinsert = ((Item_cond *)conds)->argument_list();
list_to_reinsert = down_cast<Item_cond *>(conds)->argument_list();
cond_removed = cond_to_reinsert;
} else {
// if no conjunctive formula the original condition was empty
cond_removed = conds;
conds = nullptr;
}
if (cond_removed->type() == Item::FUNC_ITEM &&
((Item_func *)cond_removed)->functype() == Item_func::TRIG_COND_FUNC) {
down_cast<Item_func *>(cond_removed)->functype() == Item_func::TRIG_COND_FUNC) {
// Condition was wrapped into trigger
cond_removed = (Item_cond *)((Item_func *)cond_removed)->arguments()[0];
cond_removed = down_cast<Item_cond *>(down_cast<Item_func *>(cond_removed)->arguments()[0]);
}
if (cond_removed->type() == Item::COND_ITEM && ((Item_func *)cond_removed)->functype() == Item_func::COND_OR_FUNC) {
if (cond_removed->type() == Item::COND_ITEM &&
down_cast<Item_func *>(cond_removed)->functype() == Item_func::COND_OR_FUNC) {
// if the subselect field could have null values
// equality condition was OR-ed with IS nullptr condition
Item_cond *cond_cond = (Item_cond *)cond_removed;
Item_cond *cond_cond = down_cast<Item_cond *>(cond_removed);
List_iterator_fast<Item> li(*(cond_cond->argument_list()));
cond_removed = li++;
if (cond_removed == nullptr)
Expand All @@ -1710,24 +1717,38 @@ bool Query::ClearSubselectTransformation(common::Operator &oper_for_subselect, I
having = nullptr;
}
// check if the extra condition is a comparison
if (cond_removed->type() != Item::FUNC_ITEM || ((Item_func *)cond_removed)->arg_count != 2)
if (cond_removed->type() != Item::FUNC_ITEM || down_cast<Item_func *>(cond_removed)->arg_count != 2)
return false;
// the right side of equality is the field of the original subselect
field_for_subselect = ((Item_func *)cond_removed)->arguments()[1];

auto item_func = down_cast<Item_func *>(cond_removed);
if (item_func->arguments()[0]->type() == Item::REF_ITEM) {
// the right side of equality is the field of the original subselect
field_for_subselect = item_func->arguments()[1];
// the left side of equality should be the left side of the original
// expression with subselect
left_ref = item_func->arguments()[0];
} else if (item_func->arguments()[1]->type() == Item::REF_ITEM) {
// ref #767
// the left side of equality is the field of the original subselect
field_for_subselect = item_func->arguments()[0];
// the right side of equality should be the left side of the original
// expression with subselect
left_ref = item_func->arguments()[1];
} else {
return false;
}
} else
return false;
// the left side of equality should be the left side of the original
// expression with subselect
Item *left_ref = ((Item_func *)cond_removed)->arguments()[0];

if (dynamic_cast<Item_int_with_ref *>(left_ref) != nullptr)
left_ref = ((Item_int_with_ref *)left_ref)->real_item();
if (left_ref->type() != Item::REF_ITEM || ((Item_ref *)left_ref)->ref_type() != Item_ref::DIRECT_REF ||
((Item_ref *)left_ref)->real_item() != left_expr_for_subselect)
left_ref = down_cast<Item_int_with_ref *>(left_ref)->real_item();
if (left_ref->type() != Item::REF_ITEM || down_cast<Item_ref *>(left_ref)->ref_type() != Item_ref::DIRECT_REF ||
down_cast<Item_ref *>(left_ref)->real_item() != left_expr_for_subselect)
return false;
// set the operation type
switch (((Item_func *)cond_removed)->functype()) {
switch (down_cast<Item_func *>(cond_removed)->functype()) {
case Item_func::EQ_FUNC:
oper_for_subselect = common::Operator::O_IN; /*common::Operator::common::Operator::O_IN;*/
oper_for_subselect = common::Operator::O_IN;
break;
case Item_func::NE_FUNC:
oper_for_subselect = common::Operator::O_NOT_EQ;
Expand Down

0 comments on commit 2e10d61

Please sign in to comment.