[Commits] 0b5d989: EXPLAIN FORMAT=JSON: Add support for non-merged semi-joins

Sergei Petrunia psergey at askmonty.org
Sat Nov 29 00:08:18 EET 2014


revision-id: 0b5d989c894a14ebcb786940fafd025e31523d8b
parent(s): 2ac3b39e68bf6bf1b56e6eafd290c0a78368d0be
committer: Sergei Petrunia
branch nick: 10.1-explain-json-r5
timestamp: 2014-11-29 01:08:18 +0300
message:

EXPLAIN FORMAT=JSON: Add support for non-merged semi-joins

---
 mysql-test/r/explain_json.result |   42 ++++++++++++++++++++++++++++++++++++++
 mysql-test/t/explain_json.test   |    6 ++++++
 sql/sql_explain.cc               |   26 ++++++++++++++++++++++-
 sql/sql_explain.h                |   19 ++++++++++++++---
 sql/sql_lex.cc                   |    7 ++++++-
 sql/sql_select.cc                |    9 ++++++--
 sql/sql_select.h                 |   15 ++++++++++++++
 7 files changed, 117 insertions(+), 7 deletions(-)

diff --git a/mysql-test/r/explain_json.result b/mysql-test/r/explain_json.result
index 416f432..2211c0a 100644
--- a/mysql-test/r/explain_json.result
+++ b/mysql-test/r/explain_json.result
@@ -464,4 +464,46 @@ EXPLAIN
     }
   }
 }
+#
+# Non-merged semi-join (aka JTBM)
+#
+explain format=json 
+select * from t1 where a in (select max(a) from t1 group by b);
+EXPLAIN
+{
+  "query_block": {
+    "select_id": 1,
+    "table": {
+      "table_name": "t1",
+      "access_type": "ALL",
+      "rows": 10,
+      "filtered": 100,
+      "attached_condition": "(t1.a is not null)"
+    },
+    "table": {
+      "table_name": "<subquery2>",
+      "access_type": "eq_ref",
+      "possible_keys": ["distinct_key"],
+      "key": "distinct_key",
+      "key_length": "4",
+      "used_key_parts": ["max(a)"],
+      "ref": ["test.t1.a"],
+      "rows": 1,
+      "filtered": 100,
+      "materialized": {
+        "unique": 1,
+        "query_block": {
+          "select_id": 2,
+          "table": {
+            "table_name": "t1",
+            "access_type": "ALL",
+            "rows": 10,
+            "filtered": 100
+          }
+        }
+      }
+    }
+  }
+}
+drop table t1;
 drop table t0;
diff --git a/mysql-test/t/explain_json.test b/mysql-test/t/explain_json.test
index 8631d85..6e3e874 100644
--- a/mysql-test/t/explain_json.test
+++ b/mysql-test/t/explain_json.test
@@ -93,5 +93,11 @@ explain format=json
 select * from (select a, count(*) as cnt from t1 group by a) as tbl1, t1 as
 tbl2 where cnt=tbl2.a;
 
+--echo #
+--echo # Non-merged semi-join (aka JTBM)
+--echo #
+explain format=json 
+select * from t1 where a in (select max(a) from t1 group by b);
+drop table t1;
 drop table t0;
 
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 37843e8..4dce48e 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -554,6 +554,19 @@ int Explain_node::print_explain_for_children(Explain_query *query,
 }
 
 
+/*
+  This tells whether a child subquery should be printed in JSON output.
+
+  Derived tables and Non-merged semi-joins should not be printed, because they
+  are printed inline in Explain_table_access.
+*/
+bool is_connection_printable_in_json(enum Explain_node::explain_connection_type type)
+{
+  return (type != Explain_node::EXPLAIN_NODE_DERIVED && 
+          type != Explain_node::EXPLAIN_NODE_NON_MERGED_SJ);
+}
+
+
 void Explain_node::print_explain_json_for_children(Explain_query *query, 
                                                   Json_writer *writer,
                                                   bool is_analyze)
@@ -565,7 +578,8 @@ void Explain_node::print_explain_json_for_children(Explain_query *query,
   {
     Explain_node *node= query->get_node(children.at(i));
     /* Derived tables are printed inside Explain_table_access objects */
-    if (node->is_derived_table)
+    
+    if (!is_connection_printable_in_json(node->connection_type))
       continue;
 
     if (!started)
@@ -1189,6 +1203,16 @@ void Explain_table_access::print_explain_json(Explain_query *query,
     node->print_explain_json(query, writer, is_analyze);
     writer->end_object();
   }
+  if (non_merged_sjm_number)
+  {
+    /* This is a non-merged semi-join table. Print its contents here */
+    writer->add_member("materialized").start_object();
+    writer->add_member("unique").add_ll(1);
+    Explain_node *node= query->get_node(non_merged_sjm_number);
+    node->connection_type= Explain_node::EXPLAIN_NODE_NON_MERGED_SJ;
+    node->print_explain_json(query, writer, is_analyze);
+    writer->end_object();
+  }
 
   writer->end_object();
 }
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index 5b2bf9f..a6a0aff 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -77,12 +77,14 @@ class Table_access_tracker
 class Explain_query;
 
 class Json_writer;
+
 /* 
   A node can be either a SELECT, or a UNION.
 */
 class Explain_node : public Sql_alloc
 {
 public:
+  /* A type specifying what kind of node this is */
   enum explain_node_type 
   {
     EXPLAIN_UNION, 
@@ -91,16 +93,24 @@ class Explain_node : public Sql_alloc
     EXPLAIN_DELETE, 
     EXPLAIN_INSERT
   };
+  
+  /* How this node is connected */
+  enum explain_connection_type {
+    EXPLAIN_NODE_OTHER,
+    EXPLAIN_NODE_DERIVED, /* Materialized derived table */
+    EXPLAIN_NODE_NON_MERGED_SJ /* aka JTBM semi-join */
+  };
 
-  Explain_node() : is_derived_table(false) {}
+  Explain_node() : connection_type(EXPLAIN_NODE_OTHER) {}
 
   virtual enum explain_node_type get_type()= 0;
   virtual int get_select_id()= 0;
 
   /*
-    TRUE means this is a derived table. FALSE means otherwise.
+    How this node is connected to its parent.
+    (NOTE: EXPLAIN_NODE_NON_MERGED_SJ is set very late currently)
   */
-  bool is_derived_table;
+  enum explain_connection_type connection_type;
 
   /* 
     A node may have children nodes. When a node's explain structure is 
@@ -502,6 +512,7 @@ class Explain_table_access : public Sql_alloc
 public:
   Explain_table_access() :
     derived_select_number(0),
+    non_merged_sjm_number(0),
     where_cond(NULL),
     cache_cond(NULL),
     pushed_index_cond(NULL)
@@ -525,6 +536,8 @@ class Explain_table_access : public Sql_alloc
     find the query plan for the derived table
   */
   int derived_select_number;
+  /* TODO: join with the previous member. */
+  int non_merged_sjm_number;
 
   enum join_type type;
 
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index de22788..4e6c0ef 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -4268,8 +4268,13 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
 {
   SELECT_LEX *first= first_select();
   Explain_union *eu= new (output->mem_root) Explain_union;
+
   if (derived)
-    eu->is_derived_table= true;
+    eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
+  /* 
+    Note: Non-merged semi-joins cannot be made out of UNIONs currently, so we
+    dont ever set EXPLAIN_NODE_NON_MERGED_SJ.
+  */
 
   for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
     eu->add_select(sl->select_number);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 459c770..f9cdd08 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -23660,8 +23660,13 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
     subselect that used to produce it.
   */
   eta->derived_select_number= table->derived_select_number;
+
+  /* The same for non-merged semi-joins */
+  eta->non_merged_sjm_number = get_non_merged_semijoin_select();
 }
 
+
+
 /*
   Save Query Plan Footprint
 
@@ -23693,7 +23698,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
     xpl_sel->select_type= join->select_lex->type;
     xpl_sel->message= message;
     if (select_lex->master_unit()->derived)
-      xpl_sel->is_derived_table= true;
+      xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
     /* Setting xpl_sel->message means that all other members are invalid */
     output->add_node(xpl_sel);
   }
@@ -23712,7 +23717,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
     xpl_sel->select_id= join->select_lex->select_number;
     xpl_sel->select_type= join->select_lex->type;
     if (select_lex->master_unit()->derived)
-      xpl_sel->is_derived_table= true;
+      xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
 
     JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS);
 
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 9463005..42b2e6b 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -527,6 +527,21 @@ enum sj_strategy_enum
   bool preread_init();
 
   bool is_sjm_nest() { return MY_TEST(bush_children); }
+  
+  /*
+    If this join_tab reads a non-merged semi-join (also called jtbm), return
+    the select's number.  Otherwise, return 0.
+  */
+  int get_non_merged_semijoin_select() const
+  {
+    Item_in_subselect *subq;
+    if (table->pos_in_table_list && 
+        (subq= table->pos_in_table_list->jtbm_subselect))
+    {
+      return subq->unit->first_select()->select_number;
+    }
+    return 0; /* Not a merged semi-join */
+  }
 
   bool access_from_tables_is_allowed(table_map used_tables,
                                      table_map sjm_lookup_tables)


More information about the commits mailing list