Drizzled Public API Documentation

explain_plan.cc
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008-2009 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include <config.h>
00021 
00022 #include <drizzled/session.h>
00023 #include <drizzled/item/uint.h>
00024 #include <drizzled/item/float.h>
00025 #include <drizzled/item/string.h>
00026 #include <drizzled/optimizer/explain_plan.h>
00027 #include <drizzled/optimizer/position.h>
00028 #include <drizzled/optimizer/quick_ror_intersect_select.h>
00029 #include <drizzled/optimizer/range.h>
00030 #include <drizzled/sql_select.h>
00031 #include <drizzled/join.h>
00032 #include <drizzled/internal/m_string.h>
00033 #include <drizzled/select_result.h>
00034 #include <drizzled/sql_lex.h>
00035 
00036 #include <cstdio>
00037 #include <string>
00038 #include <sstream>
00039 #include <bitset>
00040 
00041 using namespace std;
00042 
00043 namespace drizzled
00044 {
00045 
00046 static const string access_method_str[]=
00047 {
00048   "UNKNOWN",
00049   "system",
00050   "const",
00051   "eq_ref",
00052   "ref",
00053   "MAYBE_REF",
00054   "ALL",
00055   "range",
00056   "index",
00057   "ref_or_null",
00058   "unique_subquery",
00059   "index_subquery",
00060   "index_merge"
00061 };
00062 
00063 static const string select_type_str[]=
00064 {
00065   "PRIMARY",
00066   "SIMPLE",
00067   "DERIVED",
00068   "DEPENDENT SUBQUERY",
00069   "UNCACHEABLE SUBQUERY",
00070   "SUBQUERY",
00071   "DEPENDENT UNION",
00072   "UNCACHEABLE_UNION",
00073   "UNION",
00074   "UNION RESULT"
00075 };
00076 
00077 void optimizer::ExplainPlan::printPlan()
00078 {
00079   List<Item> item_list;
00080   Session *session= join->session;
00081   select_result *result= join->result;
00082   Item *item_null= new Item_null();
00083   const charset_info_st * const cs= system_charset_info;
00084   int quick_type;
00085   /* Don't log this into the slow query log */
00086   session->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
00087   join->unit->offset_limit_cnt= 0;
00088 
00089   /*
00090    NOTE: the number/types of items pushed into item_list must be in sync with
00091    EXPLAIN column types as they're "defined" in Session::send_explain_fields()
00092    */
00093   if (message)
00094   {
00095     item_list.push_back(new Item_int((int32_t)join->select_lex->select_number));
00096     item_list.push_back(new Item_string(select_type_str[join->select_lex->type], cs));
00097     for (uint32_t i= 0; i < 7; i++)
00098       item_list.push_back(item_null);
00099 
00100     if (join->session->lex().describe & DESCRIBE_EXTENDED)
00101       item_list.push_back(item_null);
00102 
00103     item_list.push_back(new Item_string(str_ref(message), cs));
00104     if (result->send_data(item_list))
00105       join->error= 1;
00106   }
00107   else if (join->select_lex == join->unit->fake_select_lex)
00108   {
00109     /*
00110        here we assume that the query will return at least two rows, so we
00111        show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
00112        and no filesort will be actually done, but executing all selects in
00113        the UNION to provide precise EXPLAIN information will hardly be
00114        appreciated :)
00115      */
00116     char table_name_buffer[NAME_LEN];
00117     item_list.clear();
00118     /* id */
00119     item_list.push_back(new Item_null);
00120     /* select_type */
00121     item_list.push_back(new Item_string(select_type_str[join->select_lex->type], cs));
00122     /* table */
00123     {
00124       Select_Lex *sl= join->unit->first_select();
00125       uint32_t len= 6, lastop= 0;
00126       memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
00127       for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
00128       {
00129         len+= lastop;
00130         lastop= snprintf(table_name_buffer + len, NAME_LEN - len,
00131             "%u,", sl->select_number);
00132       }
00133       if (sl || len + lastop >= NAME_LEN)
00134       {
00135         memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
00136         len+= 4;
00137       }
00138       else
00139       {
00140         len+= lastop;
00141         table_name_buffer[len - 1]= '>';  // change ',' to '>'
00142       }
00143       item_list.push_back(new Item_string(table_name_buffer, len, cs));
00144     }
00145     /* type */
00146     item_list.push_back(new Item_string(access_method_str[AM_ALL], cs));
00147     /* possible_keys */
00148     item_list.push_back(item_null);
00149     /* key*/
00150     item_list.push_back(item_null);
00151     /* key_len */
00152     item_list.push_back(item_null);
00153     /* ref */
00154     item_list.push_back(item_null);
00155     /* in_rows */
00156     if (join->session->lex().describe & DESCRIBE_EXTENDED)
00157       item_list.push_back(item_null);
00158     /* rows */
00159     item_list.push_back(item_null);
00160     /* extra */
00161     if (join->unit->global_parameters->order_list.first)
00162       item_list.push_back(new Item_string(str_ref("Using filesort"), cs));
00163     else
00164       item_list.push_back(new Item_string(str_ref(""), cs));
00165 
00166     if (result->send_data(item_list))
00167       join->error= 1;
00168   }
00169   else
00170   {
00171     table_map used_tables= 0;
00172     for (uint32_t i= 0; i < join->tables; i++)
00173     {
00174       JoinTable *tab= join->join_tab + i;
00175       Table *table= tab->table;
00176       char keylen_str_buf[64];
00177       string extra;
00178       char table_name_buffer[NAME_LEN];
00179       string tmp1;
00180       string tmp2;
00181       string tmp3;
00182 
00183       quick_type= -1;
00184       item_list.clear();
00185       /* id */
00186       item_list.push_back(new Item_uint((uint32_t)join->select_lex->select_number));
00187       /* select_type */
00188       item_list.push_back(new Item_string(select_type_str[join->select_lex->type], cs));
00189       if (tab->type == AM_ALL && tab->select && tab->select->quick)
00190       {
00191         quick_type= tab->select->quick->get_type();
00192         if ((quick_type == optimizer::QuickSelectInterface::QS_TYPE_INDEX_MERGE) ||
00193             (quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_INTERSECT) ||
00194             (quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_UNION))
00195           tab->type = AM_INDEX_MERGE;
00196         else
00197           tab->type = AM_RANGE;
00198       }
00199       /* table */
00200       if (table->derived_select_number)
00201       {
00202         /* Derived table name generation */
00203         int len= snprintf(table_name_buffer, 
00204                           sizeof(table_name_buffer)-1,
00205                           "<derived%u>",
00206                           table->derived_select_number);
00207         item_list.push_back(new Item_string(table_name_buffer, len, cs));
00208       }
00209       else
00210       {
00211         TableList *real_table= table->pos_in_table_list;
00212         item_list.push_back(new Item_string(str_ref(real_table->alias), cs));
00213       }
00214       /* "type" column */
00215       item_list.push_back(new Item_string(access_method_str[tab->type], cs));
00216       /* Build "possible_keys" value and add it to item_list */
00217       if (tab->keys.any())
00218       {
00219         for (uint32_t j= 0; j < table->getShare()->sizeKeys(); j++)
00220         {
00221           if (tab->keys.test(j))
00222           {
00223             if (tmp1.length())
00224               tmp1.append(",");
00225             tmp1.append(table->key_info[j].name,
00226                         strlen(table->key_info[j].name));
00227           }
00228         }
00229       }
00230       if (tmp1.length())
00231         item_list.push_back(new Item_string(tmp1, cs));
00232       else
00233         item_list.push_back(item_null);
00234 
00235       /* Build "key", "key_len", and "ref" values and add them to item_list */
00236       if (tab->ref.key_parts)
00237       {
00238         KeyInfo *key_info= table->key_info+ tab->ref.key;
00239         item_list.push_back(new Item_string(str_ref(key_info->name), system_charset_info));
00240         uint32_t length= internal::int64_t2str(tab->ref.key_length, keylen_str_buf, 10) - keylen_str_buf;
00241         item_list.push_back(new Item_string(keylen_str_buf, length, system_charset_info));
00242         for (StoredKey **ref= tab->ref.key_copy; *ref; ref++)
00243         {
00244           if (tmp2.length())
00245             tmp2.append(",");
00246           tmp2.append((*ref)->name(),
00247                       strlen((*ref)->name()));
00248         }
00249         item_list.push_back(new Item_string(tmp2, cs));
00250       }
00251       else if (tab->type == AM_NEXT)
00252       {
00253         KeyInfo *key_info=table->key_info+ tab->index;
00254         item_list.push_back(new Item_string(str_ref(key_info->name),cs));
00255         uint32_t length= internal::int64_t2str(key_info->key_length, keylen_str_buf, 10) - keylen_str_buf;
00256         item_list.push_back(new Item_string(keylen_str_buf, length, system_charset_info));
00257         item_list.push_back(item_null);
00258       }
00259       else if (tab->select && tab->select->quick)
00260       {
00261         tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
00262         item_list.push_back(new Item_string(tmp2, cs));
00263         item_list.push_back(new Item_string(tmp3, cs));
00264         item_list.push_back(item_null);
00265       }
00266       else
00267       {
00268         item_list.push_back(item_null);
00269         item_list.push_back(item_null);
00270         item_list.push_back(item_null);
00271       }
00272 
00273       /* Add "rows" field to item_list. */
00274       double examined_rows;
00275       if (tab->select && tab->select->quick)
00276       {
00277         examined_rows= tab->select->quick->records;
00278       }
00279       else if (tab->type == AM_NEXT || tab->type == AM_ALL)
00280       {
00281         examined_rows= tab->limit ? tab->limit : tab->table->cursor->records();
00282       }
00283       else
00284       {
00285         optimizer::Position cur_pos= join->getPosFromOptimalPlan(i);
00286         examined_rows= cur_pos.getFanout();
00287       }
00288 
00289       item_list.push_back(new Item_int((int64_t) (uint64_t) examined_rows,
00290                                        MY_INT64_NUM_DECIMAL_DIGITS));
00291 
00292       /* Add "filtered" field to item_list. */
00293       if (join->session->lex().describe & DESCRIBE_EXTENDED)
00294       {
00295         float f= 0.0;
00296         if (examined_rows)
00297         {
00298           optimizer::Position cur_pos= join->getPosFromOptimalPlan(i);
00299           f= static_cast<float>(100.0 * cur_pos.getFanout() / examined_rows);
00300         }
00301         item_list.push_back(new Item_float(f, 2));
00302       }
00303 
00304       /* Build "Extra" field and add it to item_list. */
00305       bool key_read= table->key_read;
00306       if ((tab->type == AM_NEXT || tab->type == AM_CONST) &&
00307           table->covering_keys.test(tab->index))
00308         key_read= 1;
00309       if (quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_INTERSECT &&
00310           ! ((optimizer::QuickRorIntersectSelect *) tab->select->quick)->need_to_fetch_row)
00311         key_read= 1;
00312 
00313       if (tab->info)
00314         item_list.push_back(new Item_string(str_ref(tab->info),cs));
00315       else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
00316       {
00317         if (tab->packed_info & TAB_INFO_USING_INDEX)
00318           extra.append("; Using index");
00319         if (tab->packed_info & TAB_INFO_USING_WHERE)
00320           extra.append("; Using where");
00321         if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
00322           extra.append("; Full scan on NULL key");
00323         if (extra.length())
00324           extra.erase(0, 2);        /* Skip initial "; "*/
00325         item_list.push_back(new Item_string(extra, cs));
00326       }
00327       else
00328       {
00329         if (quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_UNION ||
00330             quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_INTERSECT ||
00331             quick_type == optimizer::QuickSelectInterface::QS_TYPE_INDEX_MERGE)
00332         {
00333           extra.append("; Using ");
00334           tab->select->quick->add_info_string(&extra);
00335         }
00336         if (tab->select)
00337         {
00338           if (tab->use_quick == 2)
00339           {
00340             /*
00341              * To print out the bitset in tab->keys, we go through
00342              * it 32 bits at a time. We need to do this to ensure
00343              * that the to_ulong() method will not throw an
00344              * out_of_range exception at runtime which would happen
00345              * if the bitset we were working with was larger than 64
00346              * bits on a 64-bit platform (for example).
00347              */
00348             stringstream s, w;
00349             string str;
00350             w << tab->keys;
00351             w >> str;
00352             for (uint32_t pos= 0; pos < tab->keys.size(); pos+= 32)
00353             {
00354               bitset<32> tmp(str, pos, 32);
00355               if (tmp.any())
00356                 s << uppercase << hex << tmp.to_ulong();
00357             }
00358             extra.append("; Range checked for each record (index map: 0x");
00359             extra.append(s.str().c_str());
00360             extra.append(")");
00361           }
00362           else if (tab->select->cond)
00363           {
00364             extra.append("; Using where");
00365           }
00366         }
00367         if (key_read)
00368         {
00369           if (quick_type == optimizer::QuickSelectInterface::QS_TYPE_GROUP_MIN_MAX)
00370             extra.append("; Using index for group-by");
00371           else
00372             extra.append("; Using index");
00373         }
00374         if (table->reginfo.not_exists_optimize)
00375           extra.append("; Not exists");
00376 
00377         if (need_tmp_table)
00378         {
00379           need_tmp_table=0;
00380           extra.append("; Using temporary");
00381         }
00382         if (need_order)
00383         {
00384           need_order=0;
00385           extra.append("; Using filesort");
00386         }
00387         if (distinct & test_all_bits(used_tables,session->used_tables))
00388           extra.append("; Distinct");
00389 
00390         if (tab->insideout_match_tab)
00391         {
00392           extra.append("; LooseScan");
00393         }
00394 
00395         for (uint32_t part= 0; part < tab->ref.key_parts; part++)
00396         {
00397           if (tab->ref.cond_guards[part])
00398           {
00399             extra.append("; Full scan on NULL key");
00400             break;
00401           }
00402         }
00403 
00404         if (i > 0 && tab[-1].next_select == sub_select_cache)
00405           extra.append("; Using join buffer");
00406 
00407         if (extra.length())
00408           extra.erase(0, 2);
00409         item_list.push_back(new Item_string(extra, cs));
00410       }
00411       // For next iteration
00412       used_tables|=table->map;
00413       if (result->send_data(item_list))
00414         join->error= 1;
00415     }
00416   }
00417   for (Select_Lex_Unit *unit= join->select_lex->first_inner_unit();
00418       unit;
00419       unit= unit->next_unit())
00420   {
00421     if (explainUnion(session, unit, result))
00422       return;
00423   }
00424   return;
00425 }
00426 
00427 bool optimizer::ExplainPlan::explainUnion(Session *session,
00428                                           Select_Lex_Unit *unit,
00429                                           select_result *result)
00430 {
00431   bool res= false;
00432   Select_Lex *first= unit->first_select();
00433 
00434   for (Select_Lex *sl= first;
00435        sl;
00436        sl= sl->next_select())
00437   {
00438     // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only
00439     sl->uncacheable.reset(UNCACHEABLE_EXPLAIN);
00440     if (&session->lex().select_lex == sl)
00441     {
00442       if (sl->first_inner_unit() || sl->next_select())
00443       {
00444         sl->type= optimizer::ST_PRIMARY;
00445       }
00446       else
00447       {
00448         sl->type= optimizer::ST_SIMPLE;
00449       }
00450     }
00451     else
00452     {
00453       if (sl == first)
00454       {
00455         if (sl->linkage == DERIVED_TABLE_TYPE)
00456         {
00457           sl->type= optimizer::ST_DERIVED;
00458         }
00459         else
00460         {
00461           if (sl->uncacheable.test(UNCACHEABLE_DEPENDENT))
00462           {
00463             sl->type= optimizer::ST_DEPENDENT_SUBQUERY;
00464           }
00465           else
00466           {
00467             if (sl->uncacheable.any())
00468             {
00469               sl->type= optimizer::ST_UNCACHEABLE_SUBQUERY;
00470             }
00471             else
00472             {
00473               sl->type= optimizer::ST_SUBQUERY;
00474             }
00475           }
00476         }
00477       }
00478       else
00479       {
00480         if (sl->uncacheable.test(UNCACHEABLE_DEPENDENT))
00481         {
00482           sl->type= optimizer::ST_DEPENDENT_UNION;
00483         }
00484         else
00485         {
00486           if (sl->uncacheable.any())
00487           {
00488             sl->type= optimizer::ST_UNCACHEABLE_UNION;
00489           }
00490           else
00491           {
00492             sl->type= optimizer::ST_UNION;
00493           }
00494         }
00495       }
00496     }
00497     sl->options|= SELECT_DESCRIBE;
00498   }
00499 
00500   if (unit->is_union())
00501   {
00502     unit->fake_select_lex->select_number= UINT_MAX; // just for initialization
00503     unit->fake_select_lex->type= optimizer::ST_UNION_RESULT;
00504     unit->fake_select_lex->options|= SELECT_DESCRIBE;
00505     if (! (res= unit->prepare(session, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
00506     {
00507       res= unit->exec();
00508     }
00509     res|= unit->cleanup();
00510   }
00511   else
00512   {
00513     session->lex().current_select= first;
00514     unit->set_limit(unit->global_parameters);
00515     res= select_query(session, 
00516                       &first->ref_pointer_array,
00517                       (TableList*) first->table_list.first,
00518                       first->with_wild, 
00519                       first->item_list,
00520                       first->where,
00521                       first->order_list.elements + first->group_list.elements,
00522                       (Order*) first->order_list.first,
00523                       (Order*) first->group_list.first,
00524                       first->having,
00525                       first->options | session->options | SELECT_DESCRIBE,
00526                       result, 
00527                       unit, 
00528                       first);
00529   }
00530   return (res || session->is_error());
00531 }
00532 
00533 } /* namespace drizzled */