Drizzled Public API Documentation

quick_index_merge_select.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 #include <drizzled/session.h>
00022 #include <drizzled/records.h>
00023 #include <drizzled/util/functors.h>
00024 #include <drizzled/optimizer/quick_range_select.h>
00025 #include <drizzled/optimizer/quick_index_merge_select.h>
00026 #include <drizzled/internal/m_string.h>
00027 #include <drizzled/unique.h>
00028 #include <drizzled/key.h>
00029 #include <drizzled/table.h>
00030 
00031 #include <vector>
00032 
00033 using namespace std;
00034 
00035 namespace drizzled
00036 {
00037 
00038 static int refpos_order_cmp(void *arg, const void *a, const void *b)
00039 {
00040   Cursor *cursor= (Cursor*)arg;
00041   return cursor->cmp_ref((const unsigned char *) a, (const unsigned char *) b);
00042 }
00043 
00044 optimizer::QuickIndexMergeSelect::QuickIndexMergeSelect(Session *session_param,
00045                                                         Table *table)
00046   :
00047     pk_quick_select(NULL),
00048     session(session_param)
00049 {
00050   index= MAX_KEY;
00051   head= table;
00052   memset(&read_record, 0, sizeof(read_record));
00053   memory::init_sql_alloc(&alloc, session->variables.range_alloc_block_size, 0);
00054   return;
00055 }
00056 
00057 int optimizer::QuickIndexMergeSelect::init()
00058 {
00059   return 0;
00060 }
00061 
00062 int optimizer::QuickIndexMergeSelect::reset()
00063 {
00064   return (read_keys_and_merge());
00065 }
00066 
00067 bool
00068 optimizer::QuickIndexMergeSelect::push_quick_back(optimizer::QuickRangeSelect *quick_sel_range)
00069 {
00070   /*
00071     Save quick_select that does scan on clustered primary key as it will be
00072     processed separately.
00073   */
00074   if (head->cursor->primary_key_is_clustered() &&
00075       quick_sel_range->index == head->getShare()->getPrimaryKey())
00076   {
00077     pk_quick_select= quick_sel_range;
00078   }
00079   else
00080   {
00081     quick_selects.push_back(quick_sel_range);
00082   }
00083   return false;
00084 }
00085 
00086 optimizer::QuickIndexMergeSelect::~QuickIndexMergeSelect()
00087 {
00088   for (vector<optimizer::QuickRangeSelect *>::iterator it= quick_selects.begin();
00089        it != quick_selects.end();
00090        ++it)
00091   {
00092     (*it)->cursor= NULL;
00093   }
00094   for_each(quick_selects.begin(),
00095            quick_selects.end(),
00096            DeletePtr());
00097   quick_selects.clear();
00098   delete pk_quick_select;
00099   alloc.free_root(MYF(0));
00100   return;
00101 }
00102 
00103 
00104 int optimizer::QuickIndexMergeSelect::read_keys_and_merge()
00105 {
00106   vector<optimizer::QuickRangeSelect *>::iterator it= quick_selects.begin();
00107   optimizer::QuickRangeSelect *cur_quick= NULL;
00108   int result;
00109   Unique *unique= NULL;
00110   Cursor *cursor= head->cursor;
00111 
00112   cursor->extra(HA_EXTRA_KEYREAD);
00113   head->prepare_for_position();
00114 
00115   cur_quick= *it;
00116   ++it;
00117   assert(cur_quick != 0);
00118 
00119   /*
00120     We reuse the same instance of Cursor so we need to call both init and
00121     reset here.
00122   */
00123   if (cur_quick->init() || cur_quick->reset())
00124     return 0;
00125 
00126   unique= new Unique(refpos_order_cmp,
00127                      (void *) cursor,
00128                      cursor->ref_length,
00129                      session->variables.sortbuff_size);
00130   if (! unique)
00131     return 0;
00132   for (;;)
00133   {
00134     while ((result= cur_quick->get_next()) == HA_ERR_END_OF_FILE)
00135     {
00136       cur_quick->range_end();
00137       if (it == quick_selects.end())
00138       {
00139         break;
00140       }
00141       cur_quick= *it;
00142       ++it;
00143       if (! cur_quick)
00144         break;
00145 
00146       if (cur_quick->cursor->inited != Cursor::NONE)
00147         cur_quick->cursor->endIndexScan();
00148       if (cur_quick->init() || cur_quick->reset())
00149         return 0;
00150     }
00151 
00152     if (result)
00153     {
00154       if (result != HA_ERR_END_OF_FILE)
00155       {
00156         cur_quick->range_end();
00157         return result;
00158       }
00159       break;
00160     }
00161 
00162     if (session->getKilled())
00163       return 0;
00164 
00165     /* skip row if it will be retrieved by clustered PK scan */
00166     if (pk_quick_select && pk_quick_select->row_in_ranges())
00167       continue;
00168 
00169     cur_quick->cursor->position(cur_quick->record);
00170     result= unique->unique_add((char*) cur_quick->cursor->ref);
00171     if (result)
00172       return 0;
00173 
00174   }
00175 
00176   /* ok, all row ids are in Unique */
00177   result= unique->get(head);
00178   delete unique;
00179   doing_pk_scan= false;
00180   /* index_merge currently doesn't support "using index" at all */
00181   cursor->extra(HA_EXTRA_NO_KEYREAD);
00182   /* start table scan */
00183   if ((result= read_record.init_read_record(session, head, (optimizer::SqlSelect*) 0, 1, 1)))
00184   {
00185     head->print_error(result, MYF(0));
00186     return 0;
00187   }
00188   return result;
00189 }
00190 
00191 
00192 int optimizer::QuickIndexMergeSelect::get_next()
00193 {
00194   int result;
00195 
00196   if (doing_pk_scan)
00197     return(pk_quick_select->get_next());
00198 
00199   if ((result= read_record.read_record(&read_record)) == -1)
00200   {
00201     result= HA_ERR_END_OF_FILE;
00202     read_record.end_read_record();
00203     /* All rows from Unique have been retrieved, do a clustered PK scan */
00204     if (pk_quick_select)
00205     {
00206       doing_pk_scan= true;
00207       if ((result= pk_quick_select->init()) ||
00208           (result= pk_quick_select->reset()))
00209         return result;
00210       return(pk_quick_select->get_next());
00211     }
00212   }
00213 
00214   return result;
00215 }
00216 
00217 bool optimizer::QuickIndexMergeSelect::is_keys_used(const boost::dynamic_bitset<>& fields)
00218 {
00219   for (vector<optimizer::QuickRangeSelect *>::iterator it= quick_selects.begin();
00220        it != quick_selects.end();
00221        ++it)
00222   {
00223     if (is_key_used(head, (*it)->index, fields))
00224     {
00225       return 1;
00226     }
00227   }
00228   return 0;
00229 }
00230 
00231 
00232 void optimizer::QuickIndexMergeSelect::add_info_string(string *str)
00233 {
00234   bool first= true;
00235   str->append("sort_union(");
00236   for (vector<optimizer::QuickRangeSelect *>::iterator it= quick_selects.begin();
00237        it != quick_selects.end();
00238        ++it)
00239   {
00240     if (! first)
00241       str->append(",");
00242     else
00243       first= false;
00244     (*it)->add_info_string(str);
00245   }
00246   if (pk_quick_select)
00247   {
00248     str->append(",");
00249     pk_quick_select->add_info_string(str);
00250   }
00251   str->append(")");
00252 }
00253 
00254 
00255 void optimizer::QuickIndexMergeSelect::add_keys_and_lengths(string *key_names,
00256                                                             string *used_lengths)
00257 {
00258   char buf[64];
00259   uint32_t length= 0;
00260   bool first= true;
00261 
00262   for (vector<optimizer::QuickRangeSelect *>::iterator it= quick_selects.begin();
00263        it != quick_selects.end();
00264        ++it)
00265   {
00266     if (first)
00267       first= false;
00268     else
00269     {
00270       key_names->append(",");
00271       used_lengths->append(",");
00272     }
00273 
00274     KeyInfo *key_info= head->key_info + (*it)->index;
00275     key_names->append(key_info->name);
00276     length= internal::int64_t2str((*it)->max_used_key_length, buf, 10) - buf;
00277     used_lengths->append(buf, length);
00278   }
00279   if (pk_quick_select)
00280   {
00281     KeyInfo *key_info= head->key_info + pk_quick_select->index;
00282     key_names->append(",");
00283     key_names->append(key_info->name);
00284     length= internal::int64_t2str(pk_quick_select->max_used_key_length, buf, 10) - buf;
00285     used_lengths->append(",");
00286     used_lengths->append(buf, length);
00287   }
00288 }
00289 
00290 
00291 } /* namespace drizzled */