Skip to content

Commit 163f881

Browse files
author
Chaithra Gopalareddy
committed
Bug#35634714: Derived condition pushdown return wrong results
When pushing a condition down to a derived table, we clone the condition. While cloning, if the underlying field is a view reference (field from a merged derived table), we strip off the view reference and clone the expression that it references. For the case in bugpage, this underlying expression happens to be a constant expression from a table that is on the inner side of an outer join. A constant expression in such a case cannot be treated as a basic constant because of the "NULLs" that need to be generated and this is handled carefully in Item_view_ref::used_tables(). So when we strip off the view reference, this information is lost and therefore we see wrong results. So the fix is to avoid condition pushdown for such a case. Change-Id: I0d09d73649b7a9b5c22b27156b40dd56027e255d
1 parent 4f930a7 commit 163f881

File tree

3 files changed

+71
-19
lines changed

3 files changed

+71
-19
lines changed

mysql-test/r/derived_condition_pushdown.result

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,3 +2411,20 @@ DEALLOCATE PREPARE ps_select_latin1_on;
24112411
DEALLOCATE PREPARE ps_select_latin1_off;
24122412
DROP VIEW v1;
24132413
DROP TABLE t1, t2;
2414+
#
2415+
# Bug#35634714: Derived condition pushdown return wrong results
2416+
#
2417+
CREATE TABLE t1(f1 INTEGER);
2418+
INSERT INTO t1 VALUES (NULL);
2419+
CREATE TABLE t2(f1 INTEGER);
2420+
SELECT dt3.f1
2421+
FROM t1 JOIN (SELECT f1
2422+
FROM (SELECT dt1.f1
2423+
FROM t1
2424+
LEFT JOIN (SELECT 1 AS f1
2425+
FROM t2) AS dt1
2426+
ON TRUE) AS dt2
2427+
GROUP BY f1) AS dt3
2428+
WHERE dt3.f1 IS NOT NULL;
2429+
f1
2430+
DROP TABLE t1,t2;

mysql-test/t/derived_condition_pushdown.test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,3 +1618,22 @@ DEALLOCATE PREPARE ps_select_latin1_off;
16181618

16191619
DROP VIEW v1;
16201620
DROP TABLE t1, t2;
1621+
--echo #
1622+
--echo # Bug#35634714: Derived condition pushdown return wrong results
1623+
--echo #
1624+
1625+
CREATE TABLE t1(f1 INTEGER);
1626+
INSERT INTO t1 VALUES (NULL);
1627+
CREATE TABLE t2(f1 INTEGER);
1628+
1629+
SELECT dt3.f1
1630+
FROM t1 JOIN (SELECT f1
1631+
FROM (SELECT dt1.f1
1632+
FROM t1
1633+
LEFT JOIN (SELECT 1 AS f1
1634+
FROM t2) AS dt1
1635+
ON TRUE) AS dt2
1636+
GROUP BY f1) AS dt3
1637+
WHERE dt3.f1 IS NOT NULL;
1638+
1639+
DROP TABLE t1,t2;

sql/item.cc

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,15 @@ bool Item_field::is_valid_for_pushdown(uchar *arg) {
960960
// having them.
961961
// Expressions which have system variables in the underlying derived
962962
// table cannot be pushed as of now because Item_func_get_system_var::print
963-
// does not print the original expression which leads to an incorrect clone.
963+
// does not print the original expression which leads to an incorrect
964+
// clone.
965+
// Constant expressions from a merged derived table (Item_view_ref to a
966+
// const item) which is on the inner side of an outer join has special
967+
// handling. However, when such expressions are cloned, they would be
968+
// cloned as basic constants i.e. view references are stripped off after
969+
// cloning. This leads to wrong results as the used_tables information
970+
// (See Item_view_ref::used_tables()) is lost. So we disable condition
971+
// pushdown if we have such expressions.
964972
Query_expression *derived_query_expression =
965973
derived_table->derived_query_expression();
966974
Item_result result_type = this->result_type();
@@ -970,24 +978,32 @@ bool Item_field::is_valid_for_pushdown(uchar *arg) {
970978
if (result_type != item->result_type()) {
971979
return true;
972980
}
973-
bool has_trigger_field = false;
974-
bool has_system_var = false;
975-
WalkItem(item, enum_walk::PREFIX,
976-
[&has_trigger_field, &has_system_var](Item *inner_item) {
977-
if (inner_item->type() == Item::TRIGGER_FIELD_ITEM) {
978-
has_trigger_field = true;
979-
return true;
980-
}
981-
if (inner_item->type() == Item::FUNC_ITEM &&
982-
down_cast<Item_func *>(inner_item)->functype() ==
983-
Item_func::GSYSVAR_FUNC) {
984-
has_system_var = true;
985-
return true;
986-
}
987-
return false;
988-
});
989-
if (item->has_subquery() || item->is_non_deterministic() ||
990-
has_trigger_field || has_system_var)
981+
if (item->has_subquery() || item->is_non_deterministic()) return true;
982+
if (WalkItem(item, enum_walk::PREFIX, [](Item *inner_item) {
983+
if (inner_item->type() == Item::TRIGGER_FIELD_ITEM) {
984+
return true;
985+
}
986+
if (inner_item->type() == Item::FUNC_ITEM &&
987+
down_cast<Item_func *>(inner_item)->functype() ==
988+
Item_func::GSYSVAR_FUNC) {
989+
return true;
990+
}
991+
if (inner_item->type() == Item::REF_ITEM &&
992+
down_cast<Item_ref *>(inner_item)->ref_type() ==
993+
Item_ref::VIEW_REF) {
994+
Item *ref_item = down_cast<Item_ref *>(inner_item);
995+
Item *real_item = ref_item->real_item();
996+
// A const item from a merged derived inner table that
997+
// is part of an outer join may return NULL values and
998+
// is hence not const and cannot be pushed down. This is
999+
// indicated by having the actual field const and the ref
1000+
// item non-const.
1001+
if (real_item->const_item() && !ref_item->const_item()) {
1002+
return true;
1003+
}
1004+
}
1005+
return false;
1006+
}))
9911007
return true;
9921008
}
9931009
return false;

0 commit comments

Comments
 (0)