MDEV-18769 Assertion `fixed == 1' failed in Item_cond_or::val_int

This bug is caused by pushdown from HAVING into WHERE.
    It appears because condition that is pushed wasn't fixed.

    It is also discovered that condition pushdown from HAVING into
    WHERE is done wrong. There is no need to build clones for some
    conditions that can be pushed. They can be simply moved from HAVING
    into WHERE without cloning.
    build_pushable_cond_for_having_pushdown(),
    remove_pushed_top_conjuncts_for_having() methods are changed.

    It is found that there is no transformation made for fields of
    pushed condition.
    field_transformer_for_having_pushdown transformer is added.

    New tests are added. Some comments are changed.
This commit is contained in:
Galina Shalygina 2019-03-23 15:28:22 +03:00
parent 3a3d5ba235
commit ae15f91f22
18 changed files with 4179 additions and 585 deletions

View File

@ -8218,12 +8218,10 @@ EXPLAIN
"query_block": {
"select_id": 1,
"table": {
"table_name": "<subquery2>",
"access_type": "system",
"rows": 1,
"filtered": 100,
"materialized": {
"unique": 1,
"message": "Impossible WHERE"
},
"subqueries": [
{
"query_block": {
"select_id": 2,
"table": {
@ -8231,26 +8229,7 @@ EXPLAIN
}
}
}
},
"table": {
"table_name": "<derived3>",
"access_type": "ALL",
"rows": 2,
"filtered": 100,
"attached_condition": "v1.c = NULL",
"materialized": {
"query_block": {
"select_id": 3,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 2,
"filtered": 100,
"attached_condition": "t1.c = NULL"
}
}
}
}
]
}
}
DROP VIEW v1;

View File

@ -483,7 +483,7 @@ HAVING (table2.f2 = 8);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
Warnings:
Note 1003 select `test`.`table1`.`f1` AS `f1`,7 AS `f2` from `test`.`t1` `table1` join `test`.`t1` `table2` where 0 group by `test`.`table1`.`f1`,7 having multiple equal(8, 7)
Note 1003 select `test`.`table1`.`f1` AS `f1`,7 AS `f2` from `test`.`t1` `table1` join `test`.`t1` `table2` where 0 group by `test`.`table1`.`f1`,7 having 1
DROP TABLE t1;
#
# Bug#52336 Segfault / crash in 5.1 copy_fields (param=0x9872980) at sql_select.cc:15355

File diff suppressed because it is too large Load Diff

View File

@ -486,3 +486,818 @@ EXPLAIN
SELECT a FROM t1 WHERE b = 1 AND b = 2 GROUP BY a HAVING a <= 3;
DROP TABLE t1;
--echo #
--echo # MDEV-18769: unfixed OR condition pushed from HAVING into WHERE
--echo #
CREATE TABLE t1(a INT, b INT, c INT);
CREATE TABLE t3(a INT, b INT, c INT, d INT);
INSERT INTO t1 VALUES (1,14,3), (2,13,2), (1,22,1), (3,13,4), (3,14,2);
INSERT INTO t3 VALUES (1,2,16,1), (1,3,11,2), (2,3,10,2);
--echo # nothing to push
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a
HAVING t1.b = 13 AND MAX(t1.c) > 2;
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a
HAVING t1.b = 13 AND MAX(t1.c) > 2;
eval $no_pushdown explain format=json $query;
--echo # extracted AND formula
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a,t1.b
HAVING (t1.a = 1 OR t1.b > 10) AND (t1.b < 14);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a = 1 OR t1.b > 10) AND (t1.b < 14)
GROUP BY t1.a,t1.b;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a,t1.b
HAVING (t1.a = 1 OR t1.b > 10) AND (t1.b < 14 OR t1.b > 15);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a = 1 OR t1.b > 10) AND (t1.b < 14 OR t1.b > 15)
GROUP BY t1.a,t1.b;
eval $no_pushdown explain format=json $query;
--echo # extracted AND formula : equality in the inner AND formula
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a,t1.b
HAVING (t1.a = 1 OR t1.b > 10) AND (t1.b < 14 OR (t1.b > 15 AND t1.a = 2));
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a = 1 OR t1.b > 10) AND (t1.b < 14 OR (t1.b > 15 AND t1.a = 2))
GROUP BY t1.a,t1.b;
eval $no_pushdown explain format=json $query;
--echo # extracted OR formula
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a,t1.b
HAVING (t1.a < 2) OR (t1.b = 13 AND t1.a > 2);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a < 2) OR (t1.b = 13 AND t1.a > 2)
GROUP BY t1.a,t1.b;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a,t1.b
HAVING (t1.a < 2 AND t1.b = 14) OR (t1.a > 2 AND t1.b = 13);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a < 2 AND t1.b = 14) OR (t1.a > 2 AND t1.b = 13)
GROUP BY t1.a,t1.b;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a,t1.b
HAVING (t1.a < 2 AND t1.b = 14) OR (t1.a > 2 AND (t1.b = 13 OR t1.b = 14));
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a < 2 AND t1.b = 14) OR (t1.a > 2 AND (t1.b = 13 OR t1.b = 14))
GROUP BY t1.a,t1.b;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a
HAVING (t1.a < 2 AND MAX(t1.c) = 2) OR (MAX(t1.c) > 2 AND (t1.a = 1 OR t1.a = 2));
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a < 2) OR (t1.a = 1 OR t1.a = 2)
GROUP BY t1.a
HAVING (t1.a < 2 AND MAX(t1.c) = 2) OR (MAX(t1.c) > 2 AND (t1.a = 1 OR t1.a = 2));
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a
HAVING (t1.a = 2 AND MAX(t1.c) = 2) OR (MAX(t1.c) > 2 AND (t1.a = 1 OR t1.a = 2));
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a = 2) OR (t1.a = 1 OR t1.a = 2)
GROUP BY t1.a
HAVING (t1.a = 2 AND MAX(t1.c) = 2) OR (MAX(t1.c) > 2 AND (t1.a = 1 OR t1.a = 2));
eval $no_pushdown explain format=json $query;
--echo # conjunctive subformula : equality pushdown
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a
HAVING (t1.a = 1) AND (MAX(t1.c) = 3);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a = 1)
GROUP BY t1.a
HAVING (MAX(t1.c) = 3);
eval $no_pushdown explain format=json $query;
--echo # conjunctive subformula : equalities pushdown
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a,t1.c
HAVING (t1.a = 1) AND (t1.c = 3) AND MAX(t1.b = 14);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a = 1) AND (t1.c = 3)
GROUP BY t1.a,t1.c
HAVING (MAX(t1.b) = 14);
eval $no_pushdown explain format=json $query;
--echo # conjunctive subformula : multiple equality consists of
--echo two equalities pushdown
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
GROUP BY t1.a,t1.c
HAVING (t1.a = 1) AND (t1.c = 1) AND MAX(t1.b = 14);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.a = 1) AND (t1.c = 1)
GROUP BY t1.a,t1.c
HAVING (MAX(t1.b) = 14);
eval $no_pushdown explain format=json $query;
--echo #
--echo # Pushdown from HAVING into non-empty WHERE
--echo #
--echo # inequality : inequality in WHERE
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.b > 2)
GROUP BY t1.a
HAVING (t1.a < 3);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.b > 2) AND (t1.a < 3)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # equality : inequality in WHERE
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.b > 2)
GROUP BY t1.a
HAVING (t1.a = 3);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.b > 2) AND (t1.a = 3)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # inequality : equality in WHERE
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.b = 14)
GROUP BY t1.a
HAVING (t1.a < 3);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.b = 14) AND (t1.a < 3)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # equality : equality in WHERE
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.b = 14)
GROUP BY t1.a
HAVING (t1.a = 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,t1.b,MAX(t1.c)
FROM t1
WHERE (t1.b = 14) AND (t1.a = 1)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # equality : equality in WHERE, impossible WHERE
let $query=
SELECT t1.a,MAX(t1.c)
FROM t1
WHERE (t1.a = 3)
GROUP BY t1.a
HAVING (t1.a = 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.c)
FROM t1
WHERE (t1.a = 3) AND (t1.a = 1)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # equality : equality in WHERE (equal through constant)
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.c = 1)
GROUP BY t1.a
HAVING (t1.a = 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.c = 1) AND (t1.a = 1)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # inequality : AND formula in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.c > 0) AND (t1.c < 3)
GROUP BY t1.a
HAVING (t1.a > 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.c > 0) AND (t1.c < 3) AND (t1.a > 1)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # equality : AND formula in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.c > 0) AND (t1.c < 3)
GROUP BY t1.a
HAVING (t1.a = 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.c > 0) AND (t1.c < 3) AND (t1.a = 1)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # equality : AND formula in WHERE, impossible WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 0) AND (t1.c < 3)
GROUP BY t1.a
HAVING (t1.a = 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 0) AND (t1.c < 3) AND (t1.a = 1)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b)
FROM t1
WHERE (t1.a = 0) AND (t1.a = 3)
GROUP BY t1.a
HAVING (t1.a = 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b)
FROM t1
WHERE (t1.a = 0) AND (t1.a = 3) AND (t1.a = 1)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t3.a,t3.b,MAX(t3.c),t3.d
FROM t3
WHERE (t3.b = 2) AND (t3.d = 1)
GROUP BY t3.a,t3.b,t3.d
HAVING (t3.a = 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t3.a,t3.b,MAX(t3.c),t3.d
FROM t3
WHERE (t3.b = 2) AND (t3.d = 1) AND (t3.a = 1)
GROUP BY t3.a,t3.b,t3.d;
eval $no_pushdown explain format=json $query;
--echo # inequality : OR formula in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 1) OR (t1.c < 3)
GROUP BY t1.a
HAVING (t1.a < 2);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE ((t1.a > 1) OR (t1.c < 3)) AND (t1.a < 2)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b)
FROM t1
WHERE (t1.a = 1) OR (t1.a = 3)
GROUP BY t1.a
HAVING (t1.a = 2);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b)
FROM t1
WHERE ((t1.a = 1) OR (t1.a = 3)) AND (t1.a = 2)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # AND formula : inequality in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 1)
GROUP BY t1.a
HAVING (t1.a < 4) AND (t1.a > 0);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 1) AND (t1.a < 4) AND (t1.a > 0)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # AND formula : equality in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1)
GROUP BY t1.a
HAVING (t1.a < 4) AND (t1.a > 0);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.a < 4) AND (t1.a > 0)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # OR formula : inequality in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 1)
GROUP BY t1.a
HAVING (t1.a < 4) OR (t1.a > 0);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 1) AND ((t1.a < 4) OR (t1.a > 0))
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # OR formula : equality in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1)
GROUP BY t1.a
HAVING (t1.a < 4) OR (t1.a > 0);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND ((t1.a < 4) OR (t1.a > 0))
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # AND formula : AND formula in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 1) AND (t1.c < 3)
GROUP BY t1.a
HAVING (t1.a < 4) AND (t1.c > 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE ((t1.a > 1) AND (t1.c < 3)) AND
(t1.a < 4)
GROUP BY t1.a
HAVING (t1.c > 1);
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.c < 3)
GROUP BY t1.a,t1.c
HAVING (t1.a < 4) AND (t1.c > 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE ((t1.a = 1) AND (t1.c < 3)) AND
((t1.a < 4) AND (t1.c > 1))
GROUP BY t1.a,t1.c;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.c = 3)
GROUP BY t1.a,t1.c
HAVING (t1.a < 4) AND (t1.c > 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE ((t1.a = 1) AND (t1.c = 3)) AND
((t1.a < 4) AND (t1.c > 1))
GROUP BY t1.a,t1.c;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t3.a,t3.b,MAX(t3.c),t3.d
FROM t3
WHERE (t3.a = 1) AND (t3.d = 1)
GROUP BY t3.a,t3.b
HAVING (t3.b = 2) AND (t3.d > 0);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t3.a,t3.b,MAX(t3.c),t3.d
FROM t3
WHERE (t3.a = 1) AND (t3.d = 1) AND
(t3.b = 2)
GROUP BY t3.a,t3.b
HAVING (t3.d > 0);
eval $no_pushdown explain format=json $query;
let $query=
SELECT t3.a,t3.b,MAX(t3.c),t3.d
FROM t3
WHERE (t3.a = 1) AND (t3.d = 1)
GROUP BY t3.a,t3.b,t3.d
HAVING (t3.b = 2) AND (t3.d > 0);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t3.a,t3.b,MAX(t3.c),t3.d
FROM t3
WHERE (t3.a = 1) AND (t3.d = 1) AND
(t3.b = 2) AND (t3.d > 0)
GROUP BY t3.a,t3.b,t3.d;
eval $no_pushdown explain format=json $query;
--echo # AND formula : OR formula in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 1) OR (t1.c < 3)
GROUP BY t1.a
HAVING (t1.a < 4) AND (t1.c > 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE ((t1.a > 1) OR (t1.c < 3)) AND
(t1.a < 4)
GROUP BY t1.a
HAVING (t1.c > 1);
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a > 1) OR (t1.c < 3)
GROUP BY t1.a,t1.c
HAVING (t1.a < 4) AND (t1.c > 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE ((t1.a > 1) OR (t1.c < 3)) AND
(t1.a < 4) AND (t1.c > 1)
GROUP BY t1.a,t1.c;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) OR (t1.a = 3)
GROUP BY t1.a,t1.c
HAVING (t1.a = 4) OR (t1.c > 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE ((t1.a = 1) OR (t1.a = 3)) AND
((t1.a = 4) OR (t1.c > 1))
GROUP BY t1.a,t1.c;
eval $no_pushdown explain format=json $query;
--echo # equality : pushdown through equality in WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.a = t1.c)
GROUP BY t1.a
HAVING (t1.c = 1);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.a = t1.c) AND (t1.c = 1)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # OR formula : pushdown through equality
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.a = t1.c)
GROUP BY t1.a
HAVING (t1.c = 1) OR (t1.c = 2);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.a = t1.c) AND
((t1.c = 1) OR (t1.c = 2))
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # OR formula : pushdown through equality, impossible WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.a = t1.c)
GROUP BY t1.a
HAVING (t1.c = 3) OR (t1.c = 2);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.a = t1.c) AND
((t1.c = 3) OR (t1.c = 2))
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # AND formula : pushdown through equality, impossible WHERE
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1)
GROUP BY t1.a,t1.c
HAVING (t1.c = 3) AND (t1.a > 2) AND (t1.a = t1.c);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.c = 3) AND
(t1.a > 2) AND (t1.a = t1.c)
GROUP BY t1.a,t1.c;
eval $no_pushdown explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1)
GROUP BY t1.a
HAVING (t1.c = 3) AND (t1.a > 2) AND (t1.a = t1.c);
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
WHERE (t1.a = 1) AND (t1.c = 3) AND
(t1.a > 2) AND (t1.a = t1.c)
GROUP BY t1.a;
eval $no_pushdown explain format=json $query;
--echo # AND formula with OR subformula : AND condition in WHERE
let $query=
SELECT t3.a,MAX(t3.b),t3.c,t3.d
FROM t3
WHERE (t3.a > 1) AND ((t3.c = 3) OR (t3.c < 2))
GROUP BY t3.a
HAVING (t3.a = t3.d) AND ((t3.d = 1) OR (t3.d > 1));
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t3.a,MAX(t3.c),t3.d
FROM t3
WHERE (t3.a > 1) AND ((t3.c = 3) OR (t3.c < 2)) AND
(t3.a = t3.d) AND ((t3.d = 1) OR (t3.d > 1))
GROUP BY t3.a;
eval $no_pushdown explain format=json $query;
--echo # AND formula with OR subformula : AND condition in WHERE
let $query=
SELECT t3.a,MAX(t3.b),t3.c,t3.d
FROM t3
WHERE (t3.a > 1) AND ((t3.c = 3) OR (t3.c < 2))
GROUP BY t3.a
HAVING (t3.a = t3.d) AND (((t3.d = t3.c) AND (t3.c < 15)) OR (t3.d > 1));
eval $no_pushdown $query;
eval $query;
eval explain $query;
eval explain format=json $query;
let $query=
SELECT t3.a,t3.b,MAX(t3.c),t3.d
FROM t3
WHERE (t3.a > 1) AND ((t3.c = 3) OR (t3.c < 2)) AND
(t3.a = t3.d) AND (((t3.d = t3.c) AND (t3.c < 15)) OR (t3.d > 1))
GROUP BY t3.a;
eval $no_pushdown explain format=json $query;
--echo # prepare statement
PREPARE stmt1 from "
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
GROUP BY t1.a
HAVING (t1.a = 1)
";
execute stmt1;
execute stmt1;
deallocate prepare stmt1;
DROP TABLE t1,t3;

View File

@ -2332,7 +2332,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
2 UNION NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
Warnings:
Note 1003 /* select#1 */ select 1 AS `1`,2 AS `2` union all /* select#2 */ select 1 AS `i`,count(0) AS `COUNT(*)` from `test`.`t2` where 0 group by 1 having multiple equal(10, `i`)
Note 1003 /* select#1 */ select 1 AS `1`,2 AS `2` union all /* select#2 */ select 1 AS `i`,count(0) AS `COUNT(*)` from `test`.`t2` where 0 group by 1 having 1
DROP TABLE t1,t2;
#
# Start of 10.3 tests

View File

@ -2832,8 +2832,6 @@ Item_sp::init_result_field(THD *thd, uint max_length, uint maybe_null,
Item* Item_ref::build_clone(THD *thd)
{
if (thd->having_pushdown)
return real_item()->build_clone(thd);
Item_ref *copy= (Item_ref *) get_copy(thd);
if (unlikely(!copy) ||
unlikely(!(copy->ref= (Item**) alloc_root(thd->mem_root,
@ -7195,8 +7193,9 @@ void Item::check_pushable_cond(Pushdown_checker checker, uchar *arg)
Build condition extractable from this condition for pushdown
@param thd the thread handle
@param checker the checker callback function to be applied to the
equal items of multiple equality items
@param checker the checker callback function to be applied to the nodes
of the tree of the object to check if multiple equality
elements can be used to create equalities
@param arg parameter to be passed to the checker
@details
@ -7300,19 +7299,6 @@ Item *Item::build_pushable_cond(THD *thd,
(equalities.elements == 0))
return 0;
if (thd->having_pushdown)
{
/* Creates multiple equalities from equalities that can be pushed */
Item::cond_result cond_value;
COND_EQUAL *cond_equal= new (thd->mem_root) COND_EQUAL();
new_cond= and_new_conditions_to_optimized_cond(thd, new_cond,
&cond_equal,
equalities,
&cond_value,
false);
return new_cond;
}
switch (equalities.elements)
{
case 0:
@ -7484,15 +7470,6 @@ Item *Item_field::grouping_field_transformer_for_where(THD *thd, uchar *arg)
}
bool Item::pushable_equality_checker_for_having_pushdown(uchar *arg)
{
return (type() == Item::FIELD_ITEM ||
(type() == Item::REF_ITEM &&
((((Item_ref *) this)->ref_type() == Item_ref::VIEW_REF) ||
(((Item_ref *) this)->ref_type() == Item_ref::REF))));
}
Item *
Item_direct_view_ref::grouping_field_transformer_for_where(THD *thd,
uchar *arg)
@ -9079,18 +9056,7 @@ bool Item_direct_view_ref::excl_dep_on_grouping_fields(st_select_lex *sel)
}
bool Item_direct_view_ref::excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
{
if (item_equal)
{
DBUG_ASSERT(real_item()->type() == Item::FIELD_ITEM);
return (find_matching_field_pair(this, sel->grouping_tmp_fields) != NULL);
}
return (*ref)->excl_dep_on_group_fields_for_having_pushdown(sel);
}
bool Item_args::excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
bool Item_args::excl_dep_on_grouping_fields(st_select_lex *sel)
{
for (uint i= 0; i < arg_count; i++)
{
@ -9100,7 +9066,7 @@ bool Item_args::excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
return false;
if (args[i]->const_item())
continue;
if (!args[i]->excl_dep_on_group_fields_for_having_pushdown(sel))
if (!args[i]->excl_dep_on_grouping_fields(sel))
return false;
}
return true;

View File

@ -1870,9 +1870,11 @@ public:
Not to be used for AND/OR formulas.
*/
virtual bool excl_dep_on_table(table_map tab_map) { return false; }
/*
/*
TRUE if the expression depends only on grouping fields of sel
or can be converted to such an exression using equalities.
or can be converted to such an expression using equalities.
It also checks if the expression doesn't contain stored procedures,
subqueries or randomly generated elements.
Not to be used for AND/OR formulas.
*/
virtual bool excl_dep_on_grouping_fields(st_select_lex *sel)
@ -1884,15 +1886,6 @@ public:
*/
virtual bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
{ return false; }
/*
TRUE if the expression depends only on grouping fields of sel
or can be converted to such an expression using equalities.
It also checks if the expression doesn't contain stored procedures,
subqueries or randomly generated elements.
Not to be used for AND/OR formulas.
*/
virtual bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
{ return false; }
virtual bool switch_to_nullable_fields_processor(void *arg) { return 0; }
virtual bool find_function_processor (void *arg) { return 0; }
@ -2089,6 +2082,10 @@ public:
{ return this; }
virtual Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg)
{ return this; }
virtual Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
{ return this; }
virtual Item *multiple_equality_transformer(THD *thd, uchar *arg)
{ return this; }
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const
@ -2269,6 +2266,11 @@ public:
*/
virtual void under_not(Item_func_not * upper
__attribute__((unused))) {};
/*
If Item_field is wrapped in Item_direct_wrep remove this Item_direct_ref
wrapper.
*/
virtual Item *remove_item_direct_ref() { return this; }
void register_in(THD *thd);
@ -2309,24 +2311,6 @@ public:
Checks if this item consists in the left part of arg IN subquery predicate
*/
bool pushable_equality_checker_for_subquery(uchar *arg);
/*
Checks if this item is of the type FIELD_ITEM or REF_ITEM so it
can be pushed as the part of the equality into the WHERE clause.
*/
bool pushable_equality_checker_for_having_pushdown(uchar *arg);
/*
Checks if this item consists in the GROUP BY of the SELECT arg
*/
bool dep_on_grouping_fields_checker(uchar *arg)
{ return excl_dep_on_grouping_fields((st_select_lex *) arg); }
/*
Checks if this item consists in the GROUP BY of the SELECT arg
with respect to the pushdown from HAVING into WHERE clause limitations.
*/
bool dep_on_grouping_fields_checker_for_having_pushdown(uchar *arg)
{
return excl_dep_on_group_fields_for_having_pushdown((st_select_lex *) arg);
}
};
MEM_ROOT *get_thd_memroot(THD *thd);
@ -2504,17 +2488,7 @@ protected:
}
return true;
}
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{
for (uint i= 0; i < arg_count; i++)
{
if (args[i]->const_item())
continue;
if (!args[i]->excl_dep_on_grouping_fields(sel))
return false;
}
return true;
}
bool excl_dep_on_grouping_fields(st_select_lex *sel);
bool eq(const Item_args *other, bool binary_cmp) const
{
for (uint i= 0; i < arg_count ; i++)
@ -2535,7 +2509,6 @@ protected:
}
return true;
}
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel);
public:
Item_args(void)
:args(NULL), arg_count(0)
@ -3450,8 +3423,6 @@ public:
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred);
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
{ return excl_dep_on_grouping_fields(sel); }
bool cleanup_excluding_fields_processor(void *arg)
{ return field ? 0 : cleanup_processor(arg); }
bool cleanup_excluding_const_fields_processor(void *arg)
@ -5342,8 +5313,6 @@ public:
{ return (*ref)->excl_dep_on_grouping_fields(sel); }
bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
{ return (*ref)->excl_dep_on_in_subq_left_part(subq_pred); }
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
{ return (*ref)->excl_dep_on_group_fields_for_having_pushdown(sel); }
bool cleanup_excluding_fields_processor(void *arg)
{
Item *item= real_item();
@ -5362,6 +5331,13 @@ public:
}
bool with_sum_func() const { return m_with_sum_func; }
With_sum_func_cache* get_with_sum_func_cache() { return this; }
Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
{ return (*ref)->field_transformer_for_having_pushdown(thd, arg); }
Item *remove_item_direct_ref()
{
*ref= (*ref)->remove_item_direct_ref();
return this;
}
};
@ -5406,6 +5382,8 @@ public:
virtual Ref_Type ref_type() { return DIRECT_REF; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_direct_ref>(thd, this); }
Item *remove_item_direct_ref()
{ return (*ref)->remove_item_direct_ref(); }
};
@ -5658,7 +5636,6 @@ public:
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred);
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel);
Item *derived_field_transformer_for_having(THD *thd, uchar *arg);
Item *derived_field_transformer_for_where(THD *thd, uchar *arg);
Item *grouping_field_transformer_for_where(THD *thd, uchar *arg);
@ -5754,6 +5731,8 @@ public:
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_direct_view_ref>(thd, this); }
Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
{ return this; }
};

View File

@ -5227,20 +5227,6 @@ bool Item_cond::excl_dep_on_table(table_map tab_map)
bool Item_cond::excl_dep_on_grouping_fields(st_select_lex *sel)
{
List_iterator_fast<Item> li(list);
Item *item;
while ((item= li++))
{
if (!item->excl_dep_on_grouping_fields(sel))
return false;
}
return true;
}
bool
Item_cond::excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
{
if (has_rand_bit())
return false;
@ -5248,7 +5234,7 @@ Item_cond::excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
Item *item;
while ((item= li++))
{
if (!item->excl_dep_on_group_fields_for_having_pushdown(sel))
if (!item->excl_dep_on_grouping_fields(sel))
return false;
}
return true;
@ -7359,14 +7345,14 @@ Item_bool_rowready_func2* Le_creator::create_swap(THD *thd, Item *a, Item *b) co
bool
Item_equal::excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
Item_equal::excl_dep_on_grouping_fields(st_select_lex *sel)
{
Item_equal_fields_iterator it(*this);
Item *item;
while ((item=it++))
{
if (item->excl_dep_on_group_fields_for_having_pushdown(sel))
if (item->excl_dep_on_grouping_fields(sel))
{
set_extraction_flag(FULL_EXTRACTION_FL);
return true;
@ -7378,31 +7364,71 @@ Item_equal::excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
/**
@brief
Create from this multiple equality equalities that can be pushed down
Transform multiple equality into list of equalities
@param thd the thread handle
@param equalities the result list of created equalities
@param equalities the list where created equalities are stored
@param checker the checker callback function to be applied to the nodes
of the tree of the object
of the tree of the object to check if multiple equality
elements can be used to create equalities
@param arg parameter to be passed to the checker
@details
The method traverses this multiple equality trying to create from it
new equalities that can be pushed down. It creates equalities with
the constant used in this multiple equality if it exists or the first
item for which checker returns non-NULL result and all other items
in this multiple equality for which checker returns non-NULL result.
How the method works on examples:
Example:
Example 1:
It takes MULT_EQ(x,a,b) and tries to create from its elements a set of
equalities {(x=a),(x=b)}.
MULT_EQ(1,a,b)
=>
Created equalities: {(1=a),(1=b)}
Example 2:
It takes MULT_EQ(1,a,b) and tries to create from its elements a set of
equalities {(1=a),(1=b)}.
MULT_EQ(a,b,c,d)
=>
Created equalities: {(a=b),(a=c),(a=d)}
How it is done:
1. The method finds the left part of the equalities to be built. It will
be the same for all equalities. It is either:
a. A constant if there is any
b. A first element in the multiple equality that satisfies
checker function
For the example 1 the left element is field 'x'.
For the example 2 it is constant '1'.
2. If the left element is found the rest elements of the multiple equality
are checked with the checker function if they can be right parts
of equalities.
If the element can be a right part of the equality, equality is built.
It is built with the left part element found at the step 1 and
the right part element found at this step (step 2).
Suppose for the example above that both 'a' and 'b' fields can be used
to build equalities:
Example 1:
for 'a' field (x=a) is built
for 'b' field (x=b) is built
Example 2:
for 'a' field (1=a) is built
for 'b' field (1=b) is built
3. As a result we get a set of equalities built with the elements of
this multiple equality. They are saved in the equality list.
Example 1:
{(x=a),(x=b)}
Example 2:
{(1=a),(1=b)}
@note
This method is called for condition pushdown into materialized
derived table/view, and IN subquery, and pushdown from HAVING into WHERE.
When it is called for pushdown from HAVING the empty checker is passed.
It happens because elements of this multiple equality don't need to be
checked if they can be used to build equalities. There are no elements
that can't be used to build equalities.
@retval true if an error occurs
@retval false otherwise
@ -7416,14 +7442,14 @@ bool Item_equal::create_pushable_equalities(THD *thd,
Item *item;
Item_equal_fields_iterator it(*this);
Item *left_item = get_const();
Item *right_item;
if (!left_item)
{
while ((item=it++))
{
left_item= ((item->*checker) (arg)) ? item : NULL;
if (left_item)
break;
left_item= item;
if (checker && !((item->*checker) (arg)))
continue;
break;
}
}
if (!left_item)
@ -7431,8 +7457,7 @@ bool Item_equal::create_pushable_equalities(THD *thd,
while ((item=it++))
{
right_item= ((item->*checker) (arg)) ? item : NULL;
if (!right_item)
if (checker && !((item->*checker) (arg)))
continue;
Item_func_eq *eq= 0;
Item *left_item_clone= left_item->build_clone(thd);
@ -7450,3 +7475,38 @@ bool Item_equal::create_pushable_equalities(THD *thd,
}
return false;
}
/**
Transform multiple equality into the AND condition of equalities.
Example:
MULT_EQ(x,a,b)
=>
(x=a) AND (x=b)
Equalities are built in Item_equal::create_pushable_equalities() method
using elements of this multiple equality. The result of this method is
saved in an equality list.
This method returns the condition where the elements of the equality list
are anded.
*/
Item *Item_equal::multiple_equality_transformer(THD *thd, uchar *arg)
{
List<Item> equalities;
if (create_pushable_equalities(thd, &equalities, 0, 0))
return 0;
switch (equalities.elements)
{
case 0:
return 0;
case 1:
return equalities.head();
break;
default:
return new (thd->mem_root) Item_cond_and(thd, equalities);
break;
}
}

View File

@ -3012,7 +3012,6 @@ public:
Item *build_clone(THD *thd);
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel);
};
template <template<class> class LI, class T> class Item_equal_iterator;
@ -3200,13 +3199,14 @@ public:
return used_tables() & tab_map;
}
bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred);
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
bool create_pushable_equalities(THD *thd, List<Item> *equalities,
Pushdown_checker checker, uchar *arg);
/* Return the number of elements in this multiple equality */
uint elements_count() { return equal_items.elements; }
friend class Item_equal_fields_iterator;
bool count_sargable_conds(void *arg);
Item *multiple_equality_transformer(THD *thd, uchar *arg);
friend class Item_equal_iterator<List_iterator_fast,Item>;
friend class Item_equal_iterator<List_iterator,Item>;
friend Item *eliminate_item_equal(THD *thd, COND *cond,

View File

@ -342,6 +342,8 @@ public:
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{
if (has_rand_bit())
return false;
return Item_args::excl_dep_on_grouping_fields(sel);
}
@ -350,13 +352,6 @@ public:
return Item_args::excl_dep_on_in_subq_left_part(subq_pred);
}
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
{
if (has_rand_bit())
return false;
return Item_args::excl_dep_on_group_fields_for_having_pushdown(sel);
}
/*
We assume the result of any function that has a TIMESTAMP argument to be
timezone-dependent, since a TIMESTAMP value in both numeric and string
@ -2340,8 +2335,6 @@ public:
}
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{ return false; }
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
{ return false;}
};
@ -3239,7 +3232,7 @@ public:
not_null_tables_cache= 0;
return 0;
}
bool excl_dep_on_group_fields_for_having_pushdown(st_select_lex *sel)
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{ return false; }
};

View File

@ -5601,44 +5601,44 @@ int select_value_catcher::send_data(List<Item> &items)
/**
@brief
Conjunct conditions after optimize_cond() call
Attach conditions to already optimized condition
@param thd the thread handle
@param cond the condition where to attach new conditions
@param cond_eq IN/OUT the multiple equalities of cond
@param new_conds IN/OUT the list of conditions needed to add
@param cond_value the returned value of the condition
@param build_cond_equal flag to control if COND_EQUAL elements for
AND-conditions should be built
@param thd the thread handle
@param cond the condition to which add new conditions
@param cond_eq IN/OUT the multiple equalities of cond
@param new_conds the list of conditions to be added
@param cond_value the returned value of the condition
if it can be evaluated
@details
The method creates new condition through conjunction of cond and
The method creates new condition through union of cond and
the conditions from new_conds list.
The method is called after optimize_cond() for cond. The result
of the conjunction should be the same as if it was done before the
of the union should be the same as if it was done before the
the optimize_cond() call.
@retval NULL if an error occurs
@retval otherwise the created condition
@retval NULL if an error occurs
*/
Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond,
COND_EQUAL **cond_eq,
List<Item> &new_conds,
Item::cond_result *cond_value,
bool build_cond_equal)
Item::cond_result *cond_value)
{
COND_EQUAL new_cond_equal;
Item *item;
Item_equal *equality;
Item_equal *mult_eq;
bool is_simplified_cond= false;
/* The list where parts of the new condition are stored. */
List_iterator<Item> li(new_conds);
List_iterator_fast<Item_equal> it(new_cond_equal.current_level);
/*
Creates multiple equalities new_cond_equal from new_conds list
equalities. If multiple equality can't be created or the condition
from new_conds list isn't an equality the method leaves it in new_conds
Create multiple equalities from the equalities of the list new_conds.
Save the created multiple equalities in new_cond_equal.
If multiple equality can't be created or the condition
from new_conds list isn't an equality leave it in new_conds
list.
The equality can't be converted into the multiple equality if it
@ -5664,150 +5664,224 @@ Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond,
((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
{
/*
cond is an AND-condition.
The method conjugates the AND-condition cond, created multiple
equalities new_cond_equal and remain conditions from new_conds.
First, the method disjoins multiple equalities of cond and
merges new_cond_equal multiple equalities with these equalities.
It checks if after the merge the multiple equalities are knowingly
true or false equalities.
It attaches to cond the conditions from new_conds list and the result
of the merge of multiple equalities. The multiple equalities are
attached only to the upper level of AND-condition cond. So they
should be pushed down to the inner levels of cond AND-condition
if needed. It is done by propagate_new_equalities().
Case when cond is an AND-condition.
Union AND-condition cond, created multiple equalities from
new_cond_equal and remaining conditions from new_conds.
*/
COND_EQUAL *cond_equal= &((Item_cond_and *) cond)->m_cond_equal;
List<Item_equal> *cond_equalities= &cond_equal->current_level;
List<Item> *and_args= ((Item_cond_and *)cond)->argument_list();
and_args->disjoin((List<Item> *) cond_equalities);
and_args->append(&new_conds);
while ((equality= it++))
/*
Disjoin multiple equalities of cond.
Merge these multiple equalities with the multiple equalities of
new_cond_equal. Save the result in new_cond_equal.
Check if after the merge some multiple equalities are knowingly
true or false.
*/
and_args->disjoin((List<Item> *) cond_equalities);
while ((mult_eq= it++))
{
equality->upper_levels= 0;
equality->merge_into_list(thd, cond_equalities, false, false);
mult_eq->upper_levels= 0;
mult_eq->merge_into_list(thd, cond_equalities, false, false);
}
List_iterator_fast<Item_equal> ei(*cond_equalities);
while ((equality= ei++))
while ((mult_eq= ei++))
{
if (equality->const_item() && !equality->val_int())
if (mult_eq->const_item() && !mult_eq->val_int())
is_simplified_cond= true;
equality->fixed= 0;
if (equality->fix_fields(thd, NULL))
return NULL;
else
{
mult_eq->unfix_fields();
if (mult_eq->fix_fields(thd, NULL))
return NULL;
}
}
li.rewind();
while ((item=li++))
{
/*
There still can be some equalities at not top level of new_conds
conditions that are not transformed into multiple equalities.
To transform them build_item_equal() is called.
Examples of not top level equalities:
1. (t1.a = 3) OR (t1.b > 5)
(t1.a = 3) - not top level equality.
It is inside OR condition
2. ((t3.d = t3.c) AND (t3.c < 15)) OR (t3.d > 1)
(t1.d = t3.c) - not top level equality.
It is inside AND condition which is a part of OR condition
*/
if (item->type() == Item::COND_ITEM &&
((Item_cond *)item)->functype() == Item_func::COND_OR_FUNC)
{
item= item->build_equal_items(thd,
&((Item_cond_and *) cond)->m_cond_equal,
false, NULL);
}
/*
Check if equalities that can't be transformed into multiple
equalities are knowingly true or false.
*/
if (item->const_item() && !item->val_int())
is_simplified_cond= true;
and_args->push_back(item, thd->mem_root);
}
and_args->append((List<Item> *) cond_equalities);
*cond_eq= &((Item_cond_and *) cond)->m_cond_equal;
propagate_new_equalities(thd, cond, cond_equalities,
cond_equal->upper_levels,
&is_simplified_cond);
cond= cond->propagate_equal_fields(thd,
Item::Context_boolean(),
cond_equal);
}
else
{
/*
cond isn't AND-condition or is NULL.
Case when cond isn't an AND-condition or is NULL.
There can be several cases:
1. cond is a multiple equality.
In this case cond is merged with the multiple equalities of
In this case merge cond with the multiple equalities of
new_cond_equal.
The new condition is created with the conjunction of new_conds
list conditions and the result of merge of multiple equalities.
Create new condition from the created multiple equalities
and new_conds list conditions.
2. cond is NULL
The new condition is created from the conditions of new_conds
list and multiple equalities from new_cond_equal.
Create new condition from new_conds list conditions
and multiple equalities from new_cond_equal.
3. Otherwise
In this case the new condition is created from cond, remain conditions
from new_conds list and created multiple equalities from
new_cond_equal.
Create new condition through union of cond, conditions from new_conds
list and created multiple equalities from new_cond_equal.
*/
List<Item> new_conds_list;
/* Flag is set to true if cond is a multiple equality */
bool is_mult_eq= (cond && cond->type() == Item::FUNC_ITEM &&
((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC);
/*
If cond is non-empty and is not multiple equality save it as
a part of a new condition.
*/
if (cond && !is_mult_eq &&
new_conds_list.push_back(cond, thd->mem_root))
return NULL;
if (new_conds.elements > 0)
{
li.rewind();
while ((item=li++))
{
if (!item->is_fixed() && item->fix_fields(thd, NULL))
return NULL;
if (item->const_item() && !item->val_int())
is_simplified_cond= true;
}
new_conds_list.append(&new_conds);
}
/*
If cond is a multiple equality merge it with new_cond_equal
multiple equalities.
*/
if (is_mult_eq)
{
Item_equal *eq_cond= (Item_equal *)cond;
eq_cond->upper_levels= 0;
eq_cond->merge_into_list(thd, &new_cond_equal.current_level,
false, false);
}
while ((equality= it++))
/**
Fix created multiple equalities and check if they are knowingly
true or false.
*/
List_iterator_fast<Item_equal> ei(new_cond_equal.current_level);
while ((mult_eq=ei++))
{
if (mult_eq->const_item() && !mult_eq->val_int())
is_simplified_cond= true;
else
{
if (equality->const_item() && !equality->val_int())
is_simplified_cond= true;
}
if (new_cond_equal.current_level.elements +
new_conds_list.elements == 1)
{
it.rewind();
equality= it++;
equality->fixed= 0;
if (equality->fix_fields(thd, NULL))
mult_eq->unfix_fields();
if (mult_eq->fix_fields(thd, NULL))
return NULL;
}
(*cond_eq)->copy(new_cond_equal);
}
/*
Create AND condition if new condition will have two or
more elements.
*/
Item_cond_and *and_cond= 0;
COND_EQUAL *inherited= 0;
if (new_conds_list.elements +
new_conds.elements +
new_cond_equal.current_level.elements > 1)
{
and_cond= new (thd->mem_root) Item_cond_and(thd);
and_cond->m_cond_equal.copy(new_cond_equal);
inherited= &and_cond->m_cond_equal;
}
li.rewind();
while ((item=li++))
{
/*
Look for the comment in the case when cond is an
AND condition above the build_equal_items() call.
*/
if (item->type() == Item::COND_ITEM &&
((Item_cond *)item)->functype() == Item_func::COND_OR_FUNC)
{
item= item->build_equal_items(thd, inherited, false, NULL);
}
/*
Check if equalities that can't be transformed into multiple
equalities are knowingly true or false.
*/
if (item->const_item() && !item->val_int())
is_simplified_cond= true;
new_conds_list.push_back(item, thd->mem_root);
}
new_conds_list.append((List<Item> *)&new_cond_equal.current_level);
if (new_conds_list.elements > 1)
if (and_cond)
{
Item_cond_and *and_cond=
new (thd->mem_root) Item_cond_and(thd, new_conds_list);
and_cond->m_cond_equal.copy(new_cond_equal);
and_cond->argument_list()->append(&new_conds_list);
cond= (Item *)and_cond;
*cond_eq= &((Item_cond_and *)cond)->m_cond_equal;
*cond_eq= &((Item_cond_and *) cond)->m_cond_equal;
}
else
{
List_iterator_fast<Item> iter(new_conds_list);
cond= iter++;
if (cond->type() == Item::FUNC_ITEM &&
((Item_func *)cond)->functype() == Item_func::MULT_EQUAL_FUNC)
{
if (!(*cond_eq))
*cond_eq= new COND_EQUAL();
(*cond_eq)->copy(new_cond_equal);
}
else
*cond_eq= 0;
}
}
if (!cond->is_fixed() && cond->fix_fields(thd, NULL))
return NULL;
if (!cond)
return NULL;
if (new_cond_equal.current_level.elements > 0)
cond= cond->propagate_equal_fields(thd,
Item::Context_boolean(),
&new_cond_equal);
if (*cond_eq)
{
/*
The multiple equalities are attached only to the upper level
of AND-condition cond.
Push them down to the bottom levels of cond AND-condition if needed.
*/
propagate_new_equalities(thd, cond,
&(*cond_eq)->current_level,
0,
&is_simplified_cond);
cond= cond->propagate_equal_fields(thd,
Item::Context_boolean(),
*cond_eq);
}
/*
If it was found that some of the created condition parts are knowingly
true or false equalities the method calls removes_eq_cond() to remove them
from cond and set the cond_value to the appropriate value.
If it was found that there are some knowingly true or false equalities
remove them from cond and set cond_value to the appropriate value.
*/
if (is_simplified_cond)
if (cond && is_simplified_cond)
cond= cond->remove_eq_conds(thd, cond_value, true);
if (cond && cond->fix_fields_if_needed(thd, NULL))
return NULL;
return cond;
}
@ -6889,10 +6963,14 @@ bool Item_in_subselect::pushdown_cond_for_in_subquery(THD *thd, Item *cond)
remaining_cond->transform(thd,
&Item::in_subq_field_transformer_for_having,
(uchar *)this);
if (!remaining_cond)
if (!remaining_cond ||
remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor,
0, 0))
goto exit;
sel->mark_or_conds_to_avoid_pushdown(remaining_cond);
mark_or_conds_to_avoid_pushdown(remaining_cond);
sel->cond_pushed_into_having= remaining_cond;
exit:
thd->lex->current_select= save_curr_select;

View File

@ -865,7 +865,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier, bool skip_global_sys_var_lock)
create_tmp_table_for_derived= FALSE;
save_prep_leaf_list= FALSE;
org_charset= 0;
having_pushdown= FALSE;
/* Restore THR_THD */
set_current_thd(old_THR_THD);
}

View File

@ -5032,8 +5032,6 @@ public:
Item *sp_fix_func_item(Item **it_addr);
Item *sp_prepare_func_item(Item **it_addr, uint cols= 1);
bool sp_eval_expr(Field *result_field, Item **expr_item_ptr);
bool having_pushdown;
};
/** A short cut for thd->get_stmt_da()->set_ok_status(). */

View File

@ -47,7 +47,6 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived);
bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived);
dt_processor processors[]=
{
&mysql_derived_init,
@ -1463,7 +1462,13 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
if (!remaining_cond)
continue;
sl->mark_or_conds_to_avoid_pushdown(remaining_cond);
if (remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor,
0, 0))
continue;
mark_or_conds_to_avoid_pushdown(remaining_cond);
sl->cond_pushed_into_having= remaining_cond;
}
thd->lex->current_select= save_curr_select;
DBUG_RETURN(false);

View File

@ -7893,11 +7893,9 @@ bool st_select_lex::collect_grouping_fields(THD *thd)
*/
void
st_select_lex::check_cond_extraction_for_grouping_fields(THD *thd, Item *cond,
Pushdown_checker checker)
st_select_lex::check_cond_extraction_for_grouping_fields(THD *thd, Item *cond)
{
if (thd->having_pushdown &&
cond->get_extraction_flag() == NO_EXTRACTION_FL)
if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
return;
cond->clear_extraction_flag();
if (cond->type() == Item::COND_ITEM)
@ -7913,7 +7911,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(THD *thd, Item *cond,
Item *item;
while ((item=li++))
{
check_cond_extraction_for_grouping_fields(thd, item, checker);
check_cond_extraction_for_grouping_fields(thd, item);
if (item->get_extraction_flag() != NO_EXTRACTION_FL)
{
count++;
@ -7938,7 +7936,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(THD *thd, Item *cond,
}
else
{
int fl= ((cond->*checker) ((uchar *)this)) ?
int fl= cond->excl_dep_on_grouping_fields(this) ?
FULL_EXTRACTION_FL : NO_EXTRACTION_FL;
cond->set_extraction_flag(fl);
}
@ -9569,46 +9567,42 @@ bool LEX::sp_proc_stmt_statement_finalize(THD *thd, bool no_lookahead)
/**
@brief
Extract from given item a condition pushable into WHERE clause
Extract the condition that can be pushed into WHERE clause
@param thd the thread handle
@param cond the item to extract a condition to be pushed
into WHERE
@param remaining_cond the condition that will remain of cond after
the pushdown of its parts into the WHERE clause
@param cond the condition from which to extract a pushed condition
@param remaining_cond IN/OUT the condition that will remain of cond after
the extraction
@param transformer the transformer callback function to be
applied to the condition so it can be pushed
down into the WHERE clause of this select
applied to the fields of the condition so it
can be pushed`
@param arg parameter to be passed to the transformer
@details
This method checks if cond entirely or its parts can be
pushed into the WHERE clause of this select and prepares it for pushing.
This function builds the most restrictive condition depending only on
the fields used in the GROUP BY of this SELECT. These fields were
collected before in grouping_tmp_fields list of this SELECT.
First it checks wherever this select doesn't have any aggregation function
in its projection and GROUP BY clause. If so cond can be entirely
pushed into the WHERE clause of this select but before its fields should
be transformed with transformer_for_where to make it pushable.
First this method checks if this SELECT doesn't have any aggregation
functions and has no GROUP BY clause. If so cond can be entirely pushed
into WHERE.
Otherwise the method checks if there is a condition depending only on
grouping fields that can be extracted from cond.
The condition that can be pushed into WHERE should be transformed.
It is done by transformer.
Otherwise the method checks wherever any condition depending only on
grouping fields can be extracted from cond. If there is any it prepares it
for pushing using grouping_field_transformer_for_where and if it happens to
be a conjunct of cond it removes it from cond. It saves the result of
removal in remaining_cond.
The extracted condition is saved in cond_pushed_into_where of this select.
@note
When looking for pushable condition the method considers only the grouping
fields from the list grouping_tmp_fields whose elements are of the type
Field_pair. This list must be prepared before the call of the
function.
cond can remain un empty after the extraction of the condition that can be
pushed into WHERE. It is saved in remaining_cond.
@note
This method is called for pushdown conditions into materialized
derived tables/views optimization.
Item::derived_field_transformer_for_where is passed as the actual
callback function.
Also it is called for pushdown conditions into materialized IN subqueries.
Also it is called for pushdown into materialized IN subqueries.
Item::in_subq_field_transformer_for_where is passed as the actual
callback function.
*/
@ -9624,8 +9618,7 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
if (have_window_funcs())
{
Item *cond_over_partition_fields;
check_cond_extraction_for_grouping_fields(thd, cond,
&Item::dep_on_grouping_fields_checker);
check_cond_extraction_for_grouping_fields(thd, cond);
cond_over_partition_fields=
build_cond_for_grouping_fields(thd, cond, true);
if (cond_over_partition_fields)
@ -9657,19 +9650,17 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
}
/*
Figure out what can be extracted from cond
that could be pushed into the WHERE clause of this select
Figure out what can be extracted from cond and pushed into
the WHERE clause of this select.
*/
Item *cond_over_grouping_fields;
check_cond_extraction_for_grouping_fields(thd, cond,
&Item::dep_on_grouping_fields_checker);
check_cond_extraction_for_grouping_fields(thd, cond);
cond_over_grouping_fields=
build_cond_for_grouping_fields(thd, cond, true);
/*
Transform the references to the columns from the cond
pushed into the WHERE clause of this select to make them usable in
the new context
Transform references to the columns of condition that can be pushed
into WHERE so it can be pushed.
*/
if (cond_over_grouping_fields)
cond_over_grouping_fields= cond_over_grouping_fields->transform(thd,
@ -9680,7 +9671,7 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
{
/*
In cond remove top conjuncts that has been pushed into the WHERE
Remove top conjuncts in cond that has been pushed into the WHERE
clause of this select
*/
cond= remove_pushed_top_conjuncts(thd, cond);
@ -9698,14 +9689,14 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
@brief
Mark OR-conditions as non-pushable to avoid repeatable pushdown
@param cond The condition that should be marked (or its subformulas)
@param cond the processed condition
@details
In the case when OR-condition can be pushed into the HAVING clause
of the materialized derived table/view/IN subquery and some of
its parts can be pushed into the WHERE clause it can cause
repeatable pushdown in the pushdown from HAVING into WHERE clause.
Example:
Consider pushdown into the materialized derived table/view.
Consider OR condition that can be pushed into HAVING and some
parts of this OR condition that can be pushed into WHERE.
On example:
SELECT *
FROM t1,
@ -9716,8 +9707,15 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
WHERE ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) AND
(t1.a=v1.a);
after the pushdown into the materialized views/derived tables optimization
is done:
Here ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) or1
can be pushed down into the HAVING of the materialized
derived table dt.
(dt.a>2) OR (dt.a<3) part of or1 depends only on grouping fields
of dt and can be pushed into WHERE.
As a result:
SELECT *
FROM t1,
@ -9730,21 +9728,23 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
WHERE ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) AND
(t1.a=v1.a);
In the optimization stage for the select that defines derived table
in the pushdown from HAVING into WHERE optimization
(dt.a>2) OR (dt.a<3) will be again extracted from
((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3))
and pushed into the WHERE clause of the select that defines derived table.
To avoid it after conditions are pushed into the materialized derived
tables/views or IN subqueries OR-conditions that were pushed are marked
with NO_EXTRACTION_FL flag to avoid repeatable pushdown.
Here (dt.a>2) OR (dt.a<3) also remains in HAVING of dt.
When SELECT that defines df is processed HAVING pushdown optimization
is made. In HAVING pushdown optimization it will extract
(dt.a>2) OR (dt.a<3) condition from or1 again and push it into WHERE.
This will cause duplicate conditions in WHERE of dt.
To avoid repeatable pushdown such OR conditions as or1 describen
above are marked with NO_EXTRACTION_FL.
@note
This method is called for pushdown into materialized
derived tables/views/IN subqueries optimization.
*/
void st_select_lex::mark_or_conds_to_avoid_pushdown(Item *cond)
void mark_or_conds_to_avoid_pushdown(Item *cond)
{
cond->walk(&Item::cleanup_excluding_const_fields_processor, 0, 0);
if (cond->type() == Item::COND_ITEM &&
((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
{
@ -9760,37 +9760,55 @@ void st_select_lex::mark_or_conds_to_avoid_pushdown(Item *cond)
else if (cond->type() == Item::COND_ITEM &&
((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC)
cond->set_extraction_flag(NO_EXTRACTION_FL);
cond_pushed_into_having= cond;
}
/**
@brief
Gets conditions that can be pushed down for pushdown from HAVING into WHERE
Get condition that can be pushed from HAVING into WHERE
@param thd The thread handle
@param cond The condition from which the condition depended on grouping
fields is to be extracted
@param checker The checker callback function to be applied to the nodes
of the tree of the object
@param thd the thread handle
@param cond the condition from which to extract the condition
@details
The method finds out what conditions can be extracted from cond depended
only on the grouping fields of this SELECT or fields equal to them.
If the condition that can be pushed is AND-condition it is splitted up
and for each its element it is checked if it can be pushed.
Pushable elements are attached to the attach_to_conds list.
If the condition isn't AND-condition it is entirely pushed into
the attach_to_conds list. If the condition that is extracted is a multiple
equality it is transformed into the set of equalities.
The method collects in attach_to_conds list conditions from cond
that can be pushed from HAVING into WHERE.
attach_to_conds list is created to be passed to
and_new_conditions_to_optimized_cond() method so extracted conditions can
be joined to the already optimized WHERE clause in the right way.
Conditions that can be pushed were marked with FULL_EXTRACTION_FL in
check_cond_extraction_for_grouping_fields() method.
Conditions that can't be pushed were marked with NO_EXTRACTION_FL.
Conditions which parts can be pushed weren't marked.
There are two types of conditions that can be pushed:
1. Condition that can be simply moved from HAVING
(if cond is marked with FULL_EXTRACTION_FL or
cond is an AND condition and some of its parts are marked with
FULL_EXTRACTION_FL)
In this case condition is transformed and pushed into attach_to_conds
list.
2. Part of some other condition c1 that can't be entirely pushed
(if с1 isn't marked with any flag).
For example:
SELECT t1.a,MAX(t1.b),t1.c
FROM t1
GROUP BY t1.a
HAVING ((t1.a > 5) AND (t1.c < 3)) OR (t1.a = 3);
Here (t1.a > 5) OR (t1.a = 3) from HAVING can be pushed into WHERE.
In this case build_pushable_cond() is called for c1.
This method builds a clone of the c1 part that can be pushed.
Transformation mentioned above is made with multiple_equality_transformer
transformer. It transforms all multiple equalities in the extracted
condition into the set of equalities.
@note
The method is similar to st_select_lex::build_cond_for_grouping_fields() and
Item::build_pushable_cond().
Conditions that can be pushed are collected in attach_to_conds in this way:
1. if cond is an AND condition its parts that can be pushed into WHERE
are added to attach_to_conds list separately.
2. in all other cases conditions are pushed into the list entirely.
@retval
true - if an error occurs
@ -9798,101 +9816,107 @@ void st_select_lex::mark_or_conds_to_avoid_pushdown(Item *cond)
*/
bool
st_select_lex::build_pushable_cond_for_having_pushdown(THD *thd,
Item *cond)
st_select_lex::build_pushable_cond_for_having_pushdown(THD *thd, Item *cond)
{
Pushdown_checker checker=
&Item::pushable_equality_checker_for_having_pushdown;
bool is_multiple_equality= cond->type() == Item::FUNC_ITEM &&
((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC;
List<Item> equalities;
/* Condition can't be pushed */
if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
return false;
if (cond->type() == Item::COND_ITEM)
/**
Condition can be pushed entirely.
Transform its multiple equalities and add to attach_to_conds list.
*/
if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
{
Item *result= cond->transform(thd,
&Item::multiple_equality_transformer,
(uchar *)this);
if (!result)
return true;
if (result->type() == Item::COND_ITEM &&
((Item_cond*) result)->functype() == Item_func::COND_AND_FUNC)
{
List_iterator<Item> li(*((Item_cond*) result)->argument_list());
Item *item;
while ((item=li++))
{
if (attach_to_conds.push_back(item, thd->mem_root))
return true;
}
}
else
{
if (attach_to_conds.push_back(result, thd->mem_root))
return true;
}
return false;
}
/**
There is no flag set for this condition. It means that some
part of this condition can be pushed.
*/
if (cond->type() != Item::COND_ITEM)
return false;
if (((Item_cond *)cond)->functype() != Item_cond::COND_AND_FUNC)
{
Item *fix= cond->build_pushable_cond(thd, 0, 0);
if (!fix)
return false;
if (attach_to_conds.push_back(fix, thd->mem_root))
return true;
}
else
{
bool cond_and= false;
if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
cond_and= true;
List<Item> equalities;
List<Item> new_conds;
List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
Item *item;
while ((item=li++))
{
if (item->get_extraction_flag() == NO_EXTRACTION_FL)
continue;
if (item->type() == Item::FUNC_ITEM &&
((Item_func*) item)->functype() == Item_func::MULT_EQUAL_FUNC)
else if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
{
equalities.empty();
if (((Item_equal*) item)->create_pushable_equalities(thd, &equalities,
checker, (uchar *)this))
Item *result= item->transform(thd,
&Item::multiple_equality_transformer,
(uchar *)item);
if (!result)
return true;
if (equalities.elements != 0)
if (result->type() == Item::COND_ITEM &&
((Item_cond*) result)->functype() == Item_func::COND_AND_FUNC)
{
if (cond_and)
new_conds.append(&equalities);
else
List_iterator<Item> li(*((Item_cond*) result)->argument_list());
Item *item;
while ((item=li++))
{
Item_cond_and *new_cond=
new (thd->mem_root) Item_cond_and(thd, equalities);
if (!new_cond || new_conds.push_back(new_cond, thd->mem_root))
if (attach_to_conds.push_back(item, thd->mem_root))
return true;
}
}
else if (!cond_and)
return true;
continue;
else
{
if (attach_to_conds.push_back(result, thd->mem_root))
return true;
}
}
Item *fix= item->build_pushable_cond(thd, checker, (uchar *)this);
if (!fix && !cond_and)
else
{
attach_to_conds.empty();
return false;
Item *fix= item->build_pushable_cond(thd, 0, 0);
if (!fix)
continue;
if (attach_to_conds.push_back(fix, thd->mem_root))
return true;
}
if (!fix)
continue;
if (new_conds.push_back(fix, thd->mem_root))
return true;
}
if (!cond_and)
{
Item_cond_or *new_cond= new (thd->mem_root) Item_cond_or(thd, new_conds);
if (attach_to_conds.push_back(new_cond, thd->mem_root))
return true;
}
else
attach_to_conds.append(&new_conds);
}
else if (is_multiple_equality)
{
List<Item> equalities;
Item_equal *item_equal= (Item_equal *)cond;
if (item_equal->create_pushable_equalities(thd, &equalities,
checker, (uchar *)this))
return true;
attach_to_conds.append(&equalities);
return false;
}
else if (cond->get_extraction_flag() != NO_EXTRACTION_FL)
{
Item *copy= cond->build_clone(thd);
if (attach_to_conds.push_back(copy, thd->mem_root))
return true;
}
return false;
}
/**
Check if the item is equal to some field in Field_pair 'field_pair'
Check if item is equal to some field in Field_pair 'field_pair'
from 'pair_list' and return found 'field_pair' if it exists.
*/
@ -9918,15 +9942,14 @@ Field_pair *get_corresponding_field_pair(Item *item,
/**
@brief
Collect fields in multiple equalities usable for pushdown from having
Collect fields from multiple equalities which are equal to grouping
@param thd The thread handle
@param thd the thread handle
@details
This method looks through the multiple equalities of the WHERE clause
trying to find any of them whose fields are used in the GROUP BY of the
SELECT. Any field from these multiple equality is included into the
the list of fields against which any candidate for pushing is checked.
This method checks if multiple equalities of the WHERE clause contain
fields from GROUP BY of this SELECT. If so all fields of such multiple
equalities are collected in grouping_tmp_fields list without repetitions.
@retval
true - if an error occurs
@ -9967,68 +9990,17 @@ bool st_select_lex::collect_fields_equal_to_grouping(THD *thd)
return false;
}
/**
@brief
Cleanup and fix of the condition that is ready to be pushed down
@param thd The thread handle
@param cond The condition to be processed
@details
This method recursively traverses cond making cleanup and fix
where needed.
There is no need to make cleanup and fix for multiple equalities as
they are created so they can be immediately pushed down.
@retval
true - if an error occurs
false - otherwise
*/
static
bool cleanup_condition_pushed_from_having(THD *thd, Item *cond)
{
if (cond->type() == Item::FUNC_ITEM &&
((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
return false;
if (cond->type() == Item::COND_ITEM)
{
List_iterator_fast<Item> it(*((Item_cond *)cond)->argument_list());
Item *item;
while ((item=it++))
cleanup_condition_pushed_from_having(thd, item);
}
else
{
cond->walk(&Item::cleanup_excluding_const_fields_processor, 0, 0);
if (cond->fix_fields(thd, NULL))
return true;
}
return false;
}
/**
@brief
Remove marked top conjuncts of condition for pushdown from HAVING into WHERE
Remove marked top conjuncts of HAVING for having pushdown
@param thd The thread handle
@param cond The condition which subformulas are to be removed
@param thd the thread handle
@param cond the condition which subformulas are to be removed
@details
The function behavior is similar to remove_pushed_top_conjuncts()
except the case when 'cond' is the AND-condition.
As in the pushdown from HAVING into WHERE conditions are not just cloned
so they can be later pushed down as it is for pushdown into materialized
derived tables/views or IN subqueries, but also should be removed from
the HAVING clause.
The multiple equalities of the HAVING clause are not removed in this
function, but rather marked as to be removed later. Their removal is
done in substitute_for_best_equal_field() called for HAVING at the moment
when all multiple equalities referencing the top level multiple equalities
have been already eliminated.
This method removes from cond all subformulas that can be moved from HAVING
into WHERE.
@retval
condition without removed subformulas
@ -10037,43 +10009,47 @@ bool cleanup_condition_pushed_from_having(THD *thd, Item *cond)
Item *remove_pushed_top_conjuncts_for_having(THD *thd, Item *cond)
{
/* Nothing to extract */
if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
{
cond->clear_extraction_flag();
return cond;
}
/* cond can be pushed in WHERE entirely */
if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
{
cond->clear_extraction_flag();
if (cond->type() == Item::FUNC_ITEM &&
((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
{
cond->set_extraction_flag(DELETION_FL);
return cond;
}
return 0;
}
if (cond->type() != Item::COND_ITEM)
return cond;
if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
/* Some parts of cond can be pushed */
if (cond->type() == Item::COND_ITEM &&
((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
{
List<Item> *cond_arg_list= ((Item_cond_and *)cond)->argument_list();
List_iterator<Item> li(*cond_arg_list);
List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
Item *item;
while ((item= li++))
while ((item=li++))
{
if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
{
if (item->get_extraction_flag() == NO_EXTRACTION_FL)
item->clear_extraction_flag();
else if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
{
if (item->type() == Item::FUNC_ITEM &&
((Item_func*) item)->functype() == Item_func::MULT_EQUAL_FUNC)
item->set_extraction_flag(DELETION_FL);
else
{
item->clear_extraction_flag();
li.remove();
}
}
}
switch (cond_arg_list->elements)
switch (((Item_cond*) cond)->argument_list()->elements)
{
case 0:
return 0;
case 1:
return (cond_arg_list->head());
return (((Item_cond*) cond)->argument_list()->head());
default:
return cond;
}
@ -10084,19 +10060,21 @@ Item *remove_pushed_top_conjuncts_for_having(THD *thd, Item *cond)
/**
@brief
Extract condition that can be pushed from HAVING clause into WHERE clause
Extract condition that can be pushed from HAVING into WHERE
@param thd the thread handle
@param having the HAVING clause of this select
@param having_equal multiple equalities of HAVING
@details
This function builds the most restrictive condition depending only on
the fields used in the GROUP BY of this select (directly or indirectly
through equality) that can be extracted from the HAVING clause of this
select and pushes it into the WHERE clause of this select.
This method builds a set of conditions dependent only on
fields used in the GROUP BY of this select (directly or indirectly
through equalities). These conditions are extracted from the HAVING
clause of this select.
The method saves these conditions into attach_to_conds list and removes
from HAVING conditions that can be entirely pushed into WHERE.
Example of the transformation:
Example of the HAVING pushdown transformation:
SELECT t1.a,MAX(t1.b)
FROM t1
@ -10111,14 +10089,27 @@ Item *remove_pushed_top_conjuncts_for_having(THD *thd, Item *cond)
GROUP BY t1.a
HAVING (MAX(c)>12);
In this method (t1.a>2) is not attached to the WHERE clause.
It is pushed into the attach_to_conds list to be attached to
the WHERE clause later.
In details:
1. Collect fields used in the GROUP BY grouping_fields of this SELECT
2. Collect fields equal to grouping_fields from the WHERE clause
of this SELECT and add them to the grouping_fields list.
3. Extract the most restrictive condition from the HAVING clause of this
select that depends only on the grouping fields (directly or indirectly
through equality). Store it in the attach_to_conds list.
4. Remove pushable conditions from the HAVING clause if it's possible.
through equality).
If the extracted condition is an AND condition it is transformed into a
list of all its conjuncts saved in attach_to_conds. Otherwise,
the condition is put into attach_to_conds as the only its element.
4. Remove conditions from HAVING clause that can be entirely pushed
into WHERE.
Multiple equalities are not removed but marked with DELETION_FL flag.
They will be deleted later in substitite_for_best_equal_field() called
for the HAVING condition.
5. Unwrap fields wrapped in Item_ref wrappers contain in the condition
of attach_to_conds so the condition could be pushed into WHERE.
@note
This method is similar to st_select_lex::pushdown_cond_into_where_clause().
@ -10149,56 +10140,84 @@ Item *st_select_lex::pushdown_from_having_into_where(THD *thd, Item *having)
/*
3. Extract the most restrictive condition from the HAVING clause of this
select that depends only on the grouping fields (directly or indirectly
through equality). Store it in the attach_to_conds list.
through equality).
If the extracted condition is an AND condition it is transformed into a
list of all its conjuncts saved in attach_to_conds. Otherwise,
the condition is put into attach_to_conds as the only its element.
*/
thd->having_pushdown= true;
List_iterator_fast<Item> it(attach_to_conds);
Item *item;
check_cond_extraction_for_grouping_fields(thd, having,
&Item::dep_on_grouping_fields_checker_for_having_pushdown);
check_cond_extraction_for_grouping_fields(thd, having);
if (build_pushable_cond_for_having_pushdown(thd, having))
{
attach_to_conds.empty();
goto exit;
}
if (attach_to_conds.elements != 0)
{
/*
4. Remove pushable conditions from the HAVING clause if it's possible.
*/
having= remove_pushed_top_conjuncts_for_having(thd, having);
if (!attach_to_conds.elements)
goto exit;
it.rewind();
while ((item=it++))
{
if (cleanup_condition_pushed_from_having(thd, item))
{
attach_to_conds.empty();
goto exit;
}
}
/*
Refresh having_equal as some of the multiple equalities of
having can be removed after pushdown.
*/
/*
4. Remove conditions from HAVING clause that can be entirely pushed
into WHERE.
Multiple equalities are not removed but marked with DELETION_FL flag.
They will be deleted later in substitite_for_best_equal_field() called
for the HAVING condition.
*/
having= remove_pushed_top_conjuncts_for_having(thd, having);
/*
Change join->cond_equal which points to the multiple equalities of
the top level of HAVING.
Removal of AND conditions may leave only one conjunct in HAVING.
Example 1:
SELECT *
FROM t1
GROUP BY t1.a
(t1.a < 2) AND (t1.b = 2)
(t1.a < 2) is pushed into WHERE.
join->cond_equal should point on (t1.b = 2) multiple equality now.
Example 2:
SELECT *
FROM t1
GROUP BY t1.a
(t1.a = 2) AND (t1.b < 2)
(t1.a = 2) is pushed into WHERE.
join->cond_equal should be NULL now.
*/
if (having &&
having->type() == Item::FUNC_ITEM &&
((Item_func*) having)->functype() == Item_func::MULT_EQUAL_FUNC)
join->having_equal= new (thd->mem_root) COND_EQUAL((Item_equal *)having,
thd->mem_root);
else if (!having ||
having->type() != Item::COND_ITEM ||
((Item_cond *)having)->functype() != Item_cond::COND_AND_FUNC)
join->having_equal= 0;
if (having)
/*
5. Unwrap fields wrapped in Item_ref wrappers contain in the condition
of attach_to_conds so the condition could be pushed into WHERE.
*/
it.rewind();
while ((item=it++))
{
item= item->transform(thd,
&Item::field_transformer_for_having_pushdown,
(uchar *)this);
if (item->walk(&Item::cleanup_processor, 0, 0) ||
item->fix_fields(thd, NULL))
{
if (having->type() == Item::COND_ITEM &&
((Item_cond*) having)->functype() == Item_func::COND_AND_FUNC)
{
Item_cond_and *and_having= (Item_cond_and *)having;
join->having_equal= &and_having->m_cond_equal;
}
if (having->type() == Item::FUNC_ITEM &&
((Item_func*) having)->functype() == Item_func::MULT_EQUAL_FUNC)
join->having_equal= new (thd->mem_root) COND_EQUAL((Item_equal *)having,
thd->mem_root);
attach_to_conds.empty();
goto exit;
}
}
exit:
thd->lex->current_select= save_curr_select;
thd->having_pushdown= false;
return having;
}

View File

@ -1479,8 +1479,7 @@ public:
void collect_grouping_fields_for_derived(THD *thd, ORDER *grouping_list);
bool collect_grouping_fields(THD *thd);
bool collect_fields_equal_to_grouping(THD *thd);
void check_cond_extraction_for_grouping_fields(THD *thd, Item *cond,
Pushdown_checker excl_dep);
void check_cond_extraction_for_grouping_fields(THD *thd, Item *cond);
Item *build_cond_for_grouping_fields(THD *thd, Item *cond,
bool no_to_clones);
@ -1506,13 +1505,11 @@ public:
bool cond_pushdown_is_allowed() const
{ return !olap && !explicit_limit && !tvc; }
bool build_pushable_cond_for_having_pushdown(THD *thd,
Item *cond);
bool build_pushable_cond_for_having_pushdown(THD *thd, Item *cond);
void pushdown_cond_into_where_clause(THD *thd, Item *extracted_cond,
Item **remaining_cond,
Item_transformer transformer,
uchar *arg);
void mark_or_conds_to_avoid_pushdown(Item *cond);
Item *pushdown_from_having_into_where(THD *thd, Item *having);
select_handler *find_select_handler(THD *thd);
@ -4790,5 +4787,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
void sp_create_assignment_lex(THD *thd, bool no_lookahead);
bool sp_create_assignment_instr(THD *thd, bool no_lookahead);
void mark_or_conds_to_avoid_pushdown(Item *cond);
#endif /* MYSQL_SERVER */
#endif /* SQL_LEX_INCLUDED */

View File

@ -1958,9 +1958,7 @@ JOIN::optimize_inner()
{
conds= and_new_conditions_to_optimized_cond(thd, conds, &cond_equal,
select_lex->attach_to_conds,
&cond_value, true);
if (conds && !conds->is_fixed() && conds->fix_fields(thd, &conds))
DBUG_RETURN(1);
&cond_value);
sel->attach_to_conds.empty();
}
}
@ -1983,48 +1981,13 @@ JOIN::optimize_inner()
if (eq_list.elements != 0)
{
conds= and_new_conditions_to_optimized_cond(thd, conds, &cond_equal,
eq_list, &cond_value, false);
eq_list, &cond_value);
if (!conds &&
cond_value != Item::COND_FALSE && cond_value != Item::COND_TRUE)
DBUG_RETURN(TRUE);
}
{
if (select_lex->where)
{
select_lex->cond_value= cond_value;
if (sel->where != conds && cond_value == Item::COND_OK)
thd->change_item_tree(&sel->where, conds);
}
if (select_lex->having)
{
select_lex->having_value= having_value;
if (sel->having != having && having_value == Item::COND_OK)
thd->change_item_tree(&sel->having, having);
}
if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
(!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
{ /* Impossible cond */
if (unit->select_limit_cnt)
{
DBUG_PRINT("info", (having_value == Item::COND_FALSE ?
"Impossible HAVING" : "Impossible WHERE"));
zero_result_cause= having_value == Item::COND_FALSE ?
"Impossible HAVING" : "Impossible WHERE";
}
else
{
DBUG_PRINT("info", ("Zero limit"));
zero_result_cause= "Zero limit";
}
table_count= top_join_tab_count= 0;
error= 0;
subq_exit_fl= true;
goto setup_subq_exit;
}
}
if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED))
{
TABLE_LIST *tbl;
@ -2062,6 +2025,40 @@ JOIN::optimize_inner()
if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
DBUG_RETURN(1);
}
{
if (select_lex->where)
{
select_lex->cond_value= cond_value;
if (sel->where != conds && cond_value == Item::COND_OK)
thd->change_item_tree(&sel->where, conds);
}
if (select_lex->having)
{
select_lex->having_value= having_value;
if (sel->having != having && having_value == Item::COND_OK)
thd->change_item_tree(&sel->having, having);
}
if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
(!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
{ /* Impossible cond */
if (unit->select_limit_cnt)
{
DBUG_PRINT("info", (having_value == Item::COND_FALSE ?
"Impossible HAVING" : "Impossible WHERE"));
zero_result_cause= having_value == Item::COND_FALSE ?
"Impossible HAVING" : "Impossible WHERE";
}
else
{
DBUG_PRINT("info", ("Zero limit"));
zero_result_cause= "Zero limit";
}
table_count= top_join_tab_count= 0;
error= 0;
subq_exit_fl= true;
goto setup_subq_exit;
}
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
@ -15316,8 +15313,9 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
*/
Item *head_item= (!item_const && current_sjm &&
current_sjm_head != field_item) ? current_sjm_head: head;
eq_item= new (thd->mem_root) Item_func_eq(thd, field_item, head_item);
eq_item= new (thd->mem_root) Item_func_eq(thd,
field_item->remove_item_direct_ref(),
head_item->remove_item_direct_ref());
if (!eq_item || eq_item->set_cmp_func())
return 0;

View File

@ -228,8 +228,7 @@ Item *remove_pushed_top_conjuncts(THD *thd, Item *cond);
Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond,
COND_EQUAL **cond_eq,
List<Item> &new_conds,
Item::cond_result *cond_value,
bool build_cond_equal);
Item::cond_result *cond_value);
#include "sql_explain.h"