diff --git a/mysql-test/r/analyze_format_json.result b/mysql-test/r/analyze_format_json.result index f77db650866..56d52bb557d 100644 --- a/mysql-test/r/analyze_format_json.result +++ b/mysql-test/r/analyze_format_json.result @@ -467,3 +467,116 @@ ANALYZE } } drop table t0, t1; +# +# MDEV-7970: EXPLAIN FORMAT=JSON does not print HAVING +# +create table t0(a int); +insert into t0 values (0),(1),(2),(3); +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2 ( +a int, +b int, +key (a) +); +insert into t2 select A.a*1000 + B.a, A.a*1000 + B.a from t0 A, t1 B; +# normal HAVING +analyze format=json select a, max(b) as TOP from t2 group by a having TOP > a; +ANALYZE +{ + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "having_condition": "(TOP > a)", + "filesort": { + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "r_used_priority_queue": false, + "r_output_rows": 0, + "r_buffer_size": "5Kb", + "temporary_table": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "r_loops": 1, + "rows": 256, + "r_rows": 256, + "r_total_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100 + } + } + } + } +} +# HAVING is always TRUE (not printed) +analyze format=json select a, max(b) as TOP from t2 group by a having 1<>2; +ANALYZE +{ + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "filesort": { + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "r_used_priority_queue": false, + "r_output_rows": 256, + "r_buffer_size": "5Kb", + "temporary_table": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "r_loops": 1, + "rows": 256, + "r_rows": 256, + "r_total_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100 + } + } + } + } +} +# HAVING is always FALSE (intercepted by message) +analyze format=json select a, max(b) as TOP from t2 group by a having 1=2; +ANALYZE +{ + "query_block": { + "select_id": 1, + "table": { + "message": "Impossible HAVING" + } + } +} +# HAVING is absent +analyze format=json select a, max(b) as TOP from t2 group by a; +ANALYZE +{ + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "filesort": { + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "r_used_priority_queue": false, + "r_output_rows": 256, + "r_buffer_size": "5Kb", + "temporary_table": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "r_loops": 1, + "rows": 256, + "r_rows": 256, + "r_total_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100 + } + } + } + } +} +drop table t0, t1, t2; diff --git a/mysql-test/r/explain_json.result b/mysql-test/r/explain_json.result index 07ff72b4208..35c228fda26 100644 --- a/mysql-test/r/explain_json.result +++ b/mysql-test/r/explain_json.result @@ -799,6 +799,7 @@ EXPLAIN { "query_block": { "select_id": 2, + "having_condition": "trigcond((t1.a))", "full-scan-on-null_key": { "table": { "table_name": "t1", @@ -1110,3 +1111,86 @@ EXPLAIN } } DROP TABLE t1; +# +# MDEV-7970: EXPLAIN FORMAT=JSON does not print HAVING +# +create table t0(a int); +insert into t0 values (0),(1),(2),(3); +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2 ( +a int, +b int, +key (a) +); +insert into t2 select A.a*1000 + B.a, A.a*1000 + B.a from t0 A, t1 B; +# normal HAVING +explain format=json select a, max(b) as TOP from t2 group by a having TOP > a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "having_condition": "(TOP > t2.a)", + "filesort": { + "temporary_table": { + "function": "buffer", + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 256, + "filtered": 100 + } + } + } + } +} +# HAVING is always TRUE (not printed) +explain format=json select a, max(b) as TOP from t2 group by a having 1<>2; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "filesort": { + "temporary_table": { + "function": "buffer", + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 256, + "filtered": 100 + } + } + } + } +} +# HAVING is always FALSE (intercepted by message) +explain format=json select a, max(b) as TOP from t2 group by a having 1=2; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "message": "Impossible HAVING" + } + } +} +# HAVING is absent +explain format=json select a, max(b) as TOP from t2 group by a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "filesort": { + "temporary_table": { + "function": "buffer", + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 256, + "filtered": 100 + } + } + } + } +} +drop table t0, t1, t2; diff --git a/mysql-test/t/analyze_format_json.test b/mysql-test/t/analyze_format_json.test index 816d83a62f0..0b55915c047 100644 --- a/mysql-test/t/analyze_format_json.test +++ b/mysql-test/t/analyze_format_json.test @@ -150,3 +150,29 @@ analyze format=json (select * from t1 tbl1 where a<5) union (select * from t1 tb drop table t0, t1; +--echo # +--echo # MDEV-7970: EXPLAIN FORMAT=JSON does not print HAVING +--echo # +create table t0(a int); +insert into t0 values (0),(1),(2),(3); +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2 ( + a int, + b int, + key (a) +); +insert into t2 select A.a*1000 + B.a, A.a*1000 + B.a from t0 A, t1 B; +--echo # normal HAVING +--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/ +analyze format=json select a, max(b) as TOP from t2 group by a having TOP > a; +--echo # HAVING is always TRUE (not printed) +--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/ +analyze format=json select a, max(b) as TOP from t2 group by a having 1<>2; +--echo # HAVING is always FALSE (intercepted by message) +--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/ +analyze format=json select a, max(b) as TOP from t2 group by a having 1=2; +--echo # HAVING is absent +--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/ +analyze format=json select a, max(b) as TOP from t2 group by a; +drop table t0, t1, t2; diff --git a/mysql-test/t/explain_json.test b/mysql-test/t/explain_json.test index 1078222725e..4279d3b4fb4 100644 --- a/mysql-test/t/explain_json.test +++ b/mysql-test/t/explain_json.test @@ -278,7 +278,6 @@ explain format=json select count(distinct a1,a2,b,c) from t1 where (a2 >= 'b') a drop table t1; - --echo # --echo # MDEV-8786 Wrong result for SELECT FORMAT=JSON * FROM t1 WHERE a=_latin1 0xDF --echo # @@ -294,3 +293,26 @@ CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1); INSERT INTO t1 VALUES ('a'),('A'); EXPLAIN FORMAT=JSON SELECT * FROM t1 WHERE NULLIF(a,_utf8'a' COLLATE utf8_bin); DROP TABLE t1; + +--echo # +--echo # MDEV-7970: EXPLAIN FORMAT=JSON does not print HAVING +--echo # +create table t0(a int); +insert into t0 values (0),(1),(2),(3); +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2 ( + a int, + b int, + key (a) +); +insert into t2 select A.a*1000 + B.a, A.a*1000 + B.a from t0 A, t1 B; +--echo # normal HAVING +explain format=json select a, max(b) as TOP from t2 group by a having TOP > a; +--echo # HAVING is always TRUE (not printed) +explain format=json select a, max(b) as TOP from t2 group by a having 1<>2; +--echo # HAVING is always FALSE (intercepted by message) +explain format=json select a, max(b) as TOP from t2 group by a having 1=2; +--echo # HAVING is absent +explain format=json select a, max(b) as TOP from t2 group by a; +drop table t0, t1, t2; diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 15ee67a0318..1d804b15390 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -862,7 +862,20 @@ void Explain_select::print_explain_json(Explain_query *query, writer->add_member("const_condition"); write_item(writer, exec_const_cond); } - + /* we do not print HAVING which always evaluates to TRUE */ + if (having || (having_value == Item::COND_FALSE)) + { + writer->add_member("having_condition"); + if (likely(having)) + write_item(writer, having); + else + { + /* Normally we should not go this branch, left just for safety */ + DBUG_ASSERT(having_value == Item::COND_FALSE); + writer->add_str("0"); + } + } + Filesort_tracker *first_table_sort= NULL; bool first_table_sort_used= false; int started_objects= 0; diff --git a/sql/sql_explain.h b/sql/sql_explain.h index f92a57627d5..434be476723 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -209,6 +209,7 @@ public: Explain_select(MEM_ROOT *root, bool is_analyze) : Explain_basic_join(root), message(NULL), + having(NULL), having_value(Item::COND_UNDEF), using_temporary(false), using_filesort(false), time_tracker(is_analyze), ops_tracker(is_analyze) @@ -231,7 +232,11 @@ public: /* Expensive constant condition */ Item *exec_const_cond; - + + /* HAVING condition */ + COND *having; + Item::cond_result having_value; + /* Global join attributes. In tabular form, they are printed on the first row */ bool using_temporary; bool using_filesort; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f245bd89dba..8973848b8a8 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -24197,6 +24197,11 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table, xpl_sel->using_filesort= true; xpl_sel->exec_const_cond= exec_const_cond; + if (tmp_having) + xpl_sel->having= tmp_having; + else + xpl_sel->having= having; + xpl_sel->having_value= having_value; JOIN_TAB* const first_top_tab= join->first_breadth_first_optimization_tab(); JOIN_TAB* prev_bush_root_tab= NULL;