mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-41146: Merge pseudo_bits_cond into outer_ref_cond
A join of only const tables stored the WHERE conjuncts that refer to a column of an outer query or call rand() in pseudo_bits_cond. The executor read pseudo_bits_cond only when the query needed no temporary table, and read outer_ref_cond otherwise. A window function or DISTINCT with ROLLUP needs a temporary table, so those conjuncts were never checked, and the query returned rows that the WHERE clause rejects. pseudo_bits_cond came from the fix for LP bug 908269 in 2012. For a join of only const tables, that fix also collected conjuncts that call rand(), and it stored them in a new field without stating a reason. The code that filled the two fields and the code that read them chose between them by the same test. The tests have differed since MDEV-8646 in 2016, which added the temporary table condition to the executor's test. Later changes only display the field, in EXPLAIN FORMAT=JSON (MDEV-19714) and in the optimizer trace (MDEV-21095). In MDEV-19714, its author wrote that it was not clear why pseudo_bits_cond was needed in addition to outer_ref_cond. pseudo_bits_cond is removed, and outer_ref_cond holds these conjuncts for every join. The executor checks it on both paths, so where the conjuncts are stored no longer depends on the executor's test. Conjuncts that call rand() are still collected only for a join of only const tables. EXPLAIN FORMAT=JSON shows these conjuncts as outer_ref_condition instead of pseudo_bits_condition. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Dave Gosselin
parent
cfc6a0e733
commit
26af7ea811
@@ -3038,7 +3038,7 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"pseudo_bits_condition": "if(uuid_short(),NULL,1)",
|
||||
"outer_ref_condition": "if(uuid_short(),NULL,1)",
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
@@ -3064,7 +3064,7 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"pseudo_bits_condition": "10 = rand()",
|
||||
"outer_ref_condition": "10 = rand()",
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
|
||||
@@ -905,3 +905,18 @@ NULL
|
||||
NULL
|
||||
DROP TABLE t1;
|
||||
# End of 10.3 Tests
|
||||
#
|
||||
# MDEV-41146: WHERE is not checked when every table is const and
|
||||
# the query needs a temporary table
|
||||
#
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
# rand() < 0 is never true, so the result is empty.
|
||||
select distinct a from t1 where a=1 and rand() < 0 group by a with rollup;
|
||||
a
|
||||
select distinct a from t1 where a=1 and rand() >= 0 group by a with rollup;
|
||||
a
|
||||
1
|
||||
NULL
|
||||
drop table t1;
|
||||
# End of 11.4 tests
|
||||
|
||||
@@ -533,3 +533,19 @@ SELECT NULLIF( CAST( 'foo' AS DATE ), NULL & 'bar' ) AS f FROM t1 GROUP BY f WIT
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo # End of 10.3 Tests
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41146: WHERE is not checked when every table is const and
|
||||
--echo # the query needs a temporary table
|
||||
--echo #
|
||||
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
|
||||
--echo # rand() < 0 is never true, so the result is empty.
|
||||
select distinct a from t1 where a=1 and rand() < 0 group by a with rollup;
|
||||
select distinct a from t1 where a=1 and rand() >= 0 group by a with rollup;
|
||||
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 11.4 tests
|
||||
|
||||
@@ -7668,7 +7668,7 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"pseudo_bits_condition": "1 = t1.a or <in_optimizer>(1,<exists>(subquery#3))",
|
||||
"outer_ref_condition": "1 = t1.a or <in_optimizer>(1,<exists>(subquery#3))",
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
|
||||
@@ -4866,3 +4866,27 @@ DROP TABLE t1;
|
||||
#
|
||||
# End of 10.11 tests
|
||||
#
|
||||
#
|
||||
# MDEV-41146: WHERE is not checked when every table is const and
|
||||
# the query needs a temporary table
|
||||
#
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
create table t2 (b int);
|
||||
insert into t2 values (1),(10);
|
||||
# rand() < 0 is never true, so the result is empty.
|
||||
select a, row_number() over () from t1 where a=1 and rand() < 0;
|
||||
a row_number() over ()
|
||||
select a, row_number() over () from t1 where a=1 and rand() >= 0;
|
||||
a row_number() over ()
|
||||
1 1
|
||||
# t2.b > 5 is true only for the row where t2.b is 10.
|
||||
select b, (select row_number() over () from t1 where t1.a=1 and t2.b > 5) x
|
||||
from t2;
|
||||
b x
|
||||
1 NULL
|
||||
10 1
|
||||
drop table t1, t2;
|
||||
#
|
||||
# End of 11.4 tests
|
||||
#
|
||||
|
||||
@@ -3150,3 +3150,28 @@ DROP TABLE t1;
|
||||
--echo #
|
||||
--echo # End of 10.11 tests
|
||||
--echo #
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41146: WHERE is not checked when every table is const and
|
||||
--echo # the query needs a temporary table
|
||||
--echo #
|
||||
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
create table t2 (b int);
|
||||
insert into t2 values (1),(10);
|
||||
|
||||
--echo # rand() < 0 is never true, so the result is empty.
|
||||
select a, row_number() over () from t1 where a=1 and rand() < 0;
|
||||
select a, row_number() over () from t1 where a=1 and rand() >= 0;
|
||||
|
||||
--echo # t2.b > 5 is true only for the row where t2.b is 10.
|
||||
--sorted_result
|
||||
select b, (select row_number() over () from t1 where t1.a=1 and t2.b > 5) x
|
||||
from t2;
|
||||
|
||||
drop table t1, t2;
|
||||
|
||||
--echo #
|
||||
--echo # End of 11.4 tests
|
||||
--echo #
|
||||
|
||||
@@ -4873,6 +4873,30 @@ DROP TABLE t1;
|
||||
# End of 10.11 tests
|
||||
#
|
||||
#
|
||||
# MDEV-41146: WHERE is not checked when every table is const and
|
||||
# the query needs a temporary table
|
||||
#
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
create table t2 (b int);
|
||||
insert into t2 values (1),(10);
|
||||
# rand() < 0 is never true, so the result is empty.
|
||||
select a, row_number() over () from t1 where a=1 and rand() < 0;
|
||||
a row_number() over ()
|
||||
select a, row_number() over () from t1 where a=1 and rand() >= 0;
|
||||
a row_number() over ()
|
||||
1 1
|
||||
# t2.b > 5 is true only for the row where t2.b is 10.
|
||||
select b, (select row_number() over () from t1 where t1.a=1 and t2.b > 5) x
|
||||
from t2;
|
||||
b x
|
||||
1 NULL
|
||||
10 1
|
||||
drop table t1, t2;
|
||||
#
|
||||
# End of 11.4 tests
|
||||
#
|
||||
#
|
||||
# MDEV-23867: select crash in compute_window_func
|
||||
#
|
||||
set @save_sort_buffer_size=@@sort_buffer_size;
|
||||
|
||||
@@ -1200,11 +1200,6 @@ void Explain_select::print_explain_json(Explain_query *query,
|
||||
writer->add_member("outer_ref_condition");
|
||||
write_item(writer, outer_ref_cond);
|
||||
}
|
||||
if (pseudo_bits_cond)
|
||||
{
|
||||
writer->add_member("pseudo_bits_condition");
|
||||
write_item(writer, pseudo_bits_cond);
|
||||
}
|
||||
|
||||
/* we do not print HAVING which always evaluates to TRUE */
|
||||
if (having || (having_value == Item::COND_FALSE))
|
||||
|
||||
@@ -248,7 +248,6 @@ public:
|
||||
/* Expensive constant condition */
|
||||
Item *exec_const_cond;
|
||||
Item *outer_ref_cond;
|
||||
Item *pseudo_bits_cond;
|
||||
|
||||
/* HAVING condition */
|
||||
Item *having;
|
||||
|
||||
@@ -570,7 +570,7 @@ void JOIN::init(THD *thd_arg, List<Item> &fields_arg,
|
||||
first_select= sub_select;
|
||||
group_sent= 0;
|
||||
|
||||
outer_ref_cond= pseudo_bits_cond= NULL;
|
||||
outer_ref_cond= NULL;
|
||||
in_to_exists_where= NULL;
|
||||
in_to_exists_having= NULL;
|
||||
emb_sjm_nest= NULL;
|
||||
@@ -14588,40 +14588,37 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
join->exec_const_cond= const_cond;
|
||||
}
|
||||
|
||||
if (join->table_count != join->const_tables)
|
||||
{
|
||||
COND *outer_ref_cond= make_cond_for_table(thd, cond,
|
||||
join->const_table_map |
|
||||
OUTER_REF_TABLE_BIT,
|
||||
OUTER_REF_TABLE_BIT,
|
||||
-1, FALSE, FALSE);
|
||||
if (outer_ref_cond)
|
||||
/*
|
||||
Collect the conjuncts that are checked once for each execution
|
||||
of the query, before the join reads a table that is not const.
|
||||
Each refers to at least one pseudo table in pseudo_bits and to
|
||||
no table that is not const.
|
||||
|
||||
A conjunct that refers to a column of an outer query is always
|
||||
collected, because that column has one value for each
|
||||
execution. rand() takes a new value on each call, and a
|
||||
conjunct that calls it is checked for each row of the last top
|
||||
level table that is neither const nor a materialized semijoin
|
||||
nest. A join of only const tables has no such table and
|
||||
returns at most one row, and its rand() conjuncts are collected
|
||||
here.
|
||||
*/
|
||||
table_map pseudo_bits= OUTER_REF_TABLE_BIT;
|
||||
if (join->only_const_tables())
|
||||
pseudo_bits= PSEUDO_TABLE_BITS;
|
||||
COND *pseudo_cond= make_cond_for_table(thd, cond,
|
||||
join->const_table_map |
|
||||
pseudo_bits,
|
||||
pseudo_bits,
|
||||
-1, FALSE, FALSE);
|
||||
if (pseudo_cond)
|
||||
{
|
||||
add_cond_and_fix(thd, &outer_ref_cond, join->outer_ref_cond);
|
||||
join->outer_ref_cond= outer_ref_cond;
|
||||
add_cond_and_fix(thd, &pseudo_cond, join->outer_ref_cond);
|
||||
join->outer_ref_cond= pseudo_cond;
|
||||
|
||||
Json_writer_object trace(thd);
|
||||
trace.add("outer_ref_cond", outer_ref_cond);
|
||||
}
|
||||
else if (thd->is_error())
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
COND *pseudo_bits_cond=
|
||||
make_cond_for_table(thd, cond,
|
||||
join->const_table_map |
|
||||
PSEUDO_TABLE_BITS,
|
||||
PSEUDO_TABLE_BITS,
|
||||
-1, FALSE, FALSE);
|
||||
if (pseudo_bits_cond)
|
||||
{
|
||||
add_cond_and_fix(thd, &pseudo_bits_cond,
|
||||
join->pseudo_bits_cond);
|
||||
join->pseudo_bits_cond= pseudo_bits_cond;
|
||||
|
||||
Json_writer_object trace(thd);
|
||||
trace.add("pseudo_bits_cond", pseudo_bits_cond);
|
||||
trace.add("outer_ref_cond", pseudo_cond);
|
||||
}
|
||||
else if (thd->is_error())
|
||||
DBUG_RETURN(1);
|
||||
@@ -24056,12 +24053,11 @@ do_select(JOIN *join, Procedure *procedure)
|
||||
HAVING will be checked after processing aggregate functions,
|
||||
But WHERE should checked here (we alredy have read tables).
|
||||
Notice that make_join_select() splits all conditions in this case
|
||||
into two groups exec_const_cond and outer_ref_cond.
|
||||
If join->table_count == join->const_tables then it is
|
||||
sufficient to check only the condition pseudo_bits_cond.
|
||||
into two groups exec_const_cond and outer_ref_cond. exec_const_cond
|
||||
is checked before the join starts, and it is sufficient to check
|
||||
only the condition outer_ref_cond.
|
||||
*/
|
||||
DBUG_ASSERT(join->outer_ref_cond == NULL);
|
||||
if (!join->pseudo_bits_cond || join->pseudo_bits_cond->val_bool())
|
||||
if (!join->outer_ref_cond || join->outer_ref_cond->val_bool())
|
||||
{
|
||||
// HAVING will be checked by end_select
|
||||
error= (*end_select)(join, 0, 0);
|
||||
@@ -31667,7 +31663,6 @@ int JOIN::save_explain_data_intern(Explain_query *output,
|
||||
|
||||
xpl_sel->exec_const_cond= exec_const_cond;
|
||||
xpl_sel->outer_ref_cond= outer_ref_cond;
|
||||
xpl_sel->pseudo_bits_cond= pseudo_bits_cond;
|
||||
if (tmp_having)
|
||||
xpl_sel->having= tmp_having;
|
||||
else
|
||||
|
||||
@@ -1648,8 +1648,15 @@ public:
|
||||
ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select
|
||||
COND *conds; // ---"---
|
||||
Item *conds_history; // store WHERE for explain
|
||||
COND *outer_ref_cond; ///<part of conds containing only outer references
|
||||
COND *pseudo_bits_cond; // part of conds containing special bita
|
||||
/*
|
||||
Condition to be checked once for every join execution, before reading any
|
||||
non-const tables. (Do not confuse this with exec_const_cond which is
|
||||
constant across all executions). Normally, this is condition that refers
|
||||
to outer tables only.
|
||||
For degenerate joins that only have constant tables, this includes
|
||||
conditions with RAND_TABLE_BIT.
|
||||
*/
|
||||
Item *outer_ref_cond;
|
||||
TABLE_LIST *tables_list; ///<hold 'tables' parameter of mysql_select
|
||||
List<TABLE_LIST> *join_list; ///< list of joined tables in reverse order
|
||||
COND_EQUAL *cond_equal;
|
||||
|
||||
Reference in New Issue
Block a user