Drizzled Public API Documentation

quick_range_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 
00022 #include <drizzled/session.h>
00023 #include <drizzled/optimizer/quick_range.h>
00024 #include <drizzled/optimizer/quick_range_select.h>
00025 #include <drizzled/internal/m_string.h>
00026 #include <drizzled/current_session.h>
00027 #include <drizzled/key.h>
00028 #include <drizzled/table.h>
00029 #include <drizzled/util/test.h>
00030 #include <drizzled/system_variables.h>
00031 
00032 #include <fcntl.h>
00033 
00034 using namespace std;
00035 
00036 namespace drizzled {
00037 
00038 
00039 optimizer::QuickRangeSelect::QuickRangeSelect(Session *session,
00040                                               Table *table,
00041                                               uint32_t key_nr,
00042                                               bool no_alloc,
00043                                               memory::Root *parent_alloc)
00044   :
00045     cursor(NULL),
00046     ranges(),
00047     in_ror_merged_scan(false),
00048     column_bitmap(NULL),
00049     save_read_set(NULL),
00050     save_write_set(NULL),
00051     free_file(false),
00052     cur_range(NULL),
00053     last_range(NULL),
00054     qr_traversal_ctx(),
00055     mrr_buf_size(0),
00056     key_parts(NULL),
00057     dont_free(false),
00058     mrr_flags(0),
00059     alloc()
00060 {
00061   sorted= 0;
00062   index= key_nr;
00063   head= table;
00064   key_part_info= head->key_info[index].key_part;
00065   ranges.init(sizeof(optimizer::QuickRange*), 16, 16);
00066 
00067   /* 'session' is not accessible in QuickRangeSelect::reset(). */
00068   mrr_buf_size= session->variables.read_rnd_buff_size;
00069 
00070   if (! no_alloc && ! parent_alloc)
00071   {
00072     // Allocates everything through the internal memroot
00073     alloc.init(session->variables.range_alloc_block_size);
00074     session->mem_root= &alloc;
00075   }
00076   else
00077   {
00078     memset(&alloc, 0, sizeof(alloc));
00079   }
00080 
00081   cursor= head->cursor;
00082   record= head->record[0];
00083   save_read_set= head->read_set;
00084   save_write_set= head->write_set;
00085   column_bitmap= new boost::dynamic_bitset<>(table->getShare()->sizeFields());
00086 }
00087 
00088 
00089 int optimizer::QuickRangeSelect::init()
00090 {
00091   if (cursor->inited != Cursor::NONE)
00092     cursor->ha_index_or_rnd_end();
00093   return (cursor->startIndexScan(index, 1));
00094 }
00095 
00096 
00097 void optimizer::QuickRangeSelect::range_end()
00098 {
00099   if (cursor->inited != Cursor::NONE)
00100     cursor->ha_index_or_rnd_end();
00101 }
00102 
00103 
00104 optimizer::QuickRangeSelect::~QuickRangeSelect()
00105 {
00106   if (! dont_free)
00107   {
00108     /* cursor is NULL for CPK scan on covering ROR-intersection */
00109     if (cursor)
00110     {
00111       range_end();
00112       if (head->key_read)
00113       {
00114         head->key_read= 0;
00115         cursor->extra(HA_EXTRA_NO_KEYREAD);
00116       }
00117       if (free_file)
00118       {
00119         cursor->ha_external_lock(current_session, F_UNLCK);
00120         cursor->close();
00121         delete cursor;
00122       }
00123     }
00124     ranges.free(); /* ranges are allocated in alloc */
00125     delete column_bitmap;
00126     alloc.free_root(MYF(0));
00127   }
00128   head->column_bitmaps_set(*save_read_set, *save_write_set);
00129 }
00130 
00131 
00132 int optimizer::QuickRangeSelect::init_ror_merged_scan(bool reuse_handler)
00133 {
00134   Cursor* org_file;
00135   Cursor* save_file= cursor;
00136   Session* session;
00137 
00138   in_ror_merged_scan= 1;
00139   if (reuse_handler)
00140   {
00141     if (init() || reset())
00142     {
00143       return 0;
00144     }
00145     head->column_bitmaps_set(*column_bitmap, *column_bitmap);
00146     goto end;
00147   }
00148 
00149   /* Create a separate Cursor object for this quick select */
00150   if (free_file)
00151   {
00152     /* already have own 'Cursor' object. */
00153     return 0;
00154   }
00155 
00156   session= head->in_use;
00157   if (not (cursor= head->cursor->clone(session->mem_root)))
00158   {
00159     /*
00160       Manually set the error flag. Note: there seems to be quite a few
00161       places where a failure could cause the server to "hang" the client by
00162       sending no response to a query. ATM those are not real errors because
00163       the storage engine calls in question happen to never fail with the
00164       existing storage engines.
00165     */
00166     my_error(ER_OUT_OF_RESOURCES, MYF(0));
00167     /* Caller will free the memory */
00168     goto failure;
00169   }
00170 
00171   head->column_bitmaps_set(*column_bitmap, *column_bitmap);
00172 
00173   if (cursor->ha_external_lock(session, F_RDLCK))
00174     goto failure;
00175 
00176   if (init() || reset())
00177   {
00178     cursor->ha_external_lock(session, F_UNLCK);
00179     cursor->close();
00180     goto failure;
00181   }
00182   free_file= true;
00183   last_rowid= cursor->ref;
00184 
00185 end:
00186   /*
00187     We are only going to read key fields and call position() on 'cursor'
00188     The following sets head->tmp_set to only use this key and then updates
00189     head->read_set and head->write_set to use this bitmap.
00190     The now bitmap is stored in 'column_bitmap' which is used in ::get_next()
00191   */
00192   org_file= head->cursor;
00193   head->cursor= cursor;
00194   /* We don't have to set 'head->keyread' here as the 'cursor' is unique */
00195   if (not head->no_keyread)
00196   {
00197     head->key_read= 1;
00198     head->mark_columns_used_by_index(index);
00199   }
00200   head->prepare_for_position();
00201   head->cursor= org_file;
00202   *column_bitmap|= *head->read_set;
00203   head->column_bitmaps_set(*column_bitmap, *column_bitmap);
00204 
00205   return 0;
00206 
00207 failure:
00208   head->column_bitmaps_set(*save_read_set, *save_write_set);
00209   delete cursor;
00210   cursor= save_file;
00211   return 0;
00212 }
00213 
00214 
00215 void optimizer::QuickRangeSelect::save_last_pos()
00216 {
00217   cursor->position(record);
00218 }
00219 
00220 
00221 bool optimizer::QuickRangeSelect::unique_key_range() const
00222 {
00223   if (ranges.size() == 1)
00224   {
00225     optimizer::QuickRange *tmp= *((optimizer::QuickRange**)ranges.buffer);
00226     if ((tmp->flag & (EQ_RANGE | NULL_RANGE)) == EQ_RANGE)
00227     {
00228       KeyInfo *key=head->key_info+index;
00229       return ((key->flags & (HA_NOSAME)) == HA_NOSAME &&
00230         key->key_length == tmp->min_length);
00231     }
00232   }
00233   return false;
00234 }
00235 
00236 
00237 int optimizer::QuickRangeSelect::reset()
00238 {
00239   int error= 0;
00240   last_range= NULL;
00241   cur_range= (optimizer::QuickRange**) ranges.buffer;
00242 
00243   if (cursor->inited == Cursor::NONE && (error= cursor->startIndexScan(index, 1)))
00244   {
00245     return error;
00246   }
00247 
00248   /*
00249     (in the past) Allocate buffer if we need one but haven't allocated it yet 
00250     There is a later assert in th code that hoped to catch random free() that might
00251     have done this.
00252   */
00253   assert(not (mrr_buf_size));
00254 
00255   if (sorted)
00256   {
00257      mrr_flags|= HA_MRR_SORTED;
00258   }
00259   RANGE_SEQ_IF seq_funcs= {
00260     optimizer::quick_range_seq_init,
00261     optimizer::quick_range_seq_next
00262   };
00263   error= cursor->multi_range_read_init(&seq_funcs,
00264                                        (void*) this,
00265                                        ranges.size(),
00266                                        mrr_flags);
00267   return error;
00268 }
00269 
00270 
00271 int optimizer::QuickRangeSelect::get_next()
00272 {
00273   char *dummy= NULL;
00274   if (in_ror_merged_scan)
00275   {
00276     /*
00277       We don't need to signal the bitmap change as the bitmap is always the
00278       same for this head->cursor
00279     */
00280     head->column_bitmaps_set(*column_bitmap, *column_bitmap);
00281   }
00282 
00283   int result= cursor->multi_range_read_next(&dummy);
00284 
00285   if (in_ror_merged_scan)
00286   {
00287     /* Restore bitmaps set on entry */
00288     head->column_bitmaps_set(*save_read_set, *save_write_set);
00289   }
00290   return result;
00291 }
00292 
00293 
00294 int optimizer::QuickRangeSelect::get_next_prefix(uint32_t prefix_length,
00295                                                  key_part_map keypart_map,
00296                                                  unsigned char *cur_prefix)
00297 {
00298   for (;;)
00299   {
00300     int result;
00301     key_range start_key, end_key;
00302     if (last_range)
00303     {
00304       /* Read the next record in the same range with prefix after cur_prefix. */
00305       assert(cur_prefix != 0);
00306       result= cursor->index_read_map(record,
00307                                      cur_prefix,
00308                                      keypart_map,
00309                                      HA_READ_AFTER_KEY);
00310       if (result || (cursor->compare_key(cursor->end_range) <= 0))
00311         return result;
00312     }
00313 
00314     uint32_t count= ranges.size() - (cur_range - (optimizer::QuickRange**) ranges.buffer);
00315     if (count == 0)
00316     {
00317       /* Ranges have already been used up before. None is left for read. */
00318       last_range= 0;
00319       return HA_ERR_END_OF_FILE;
00320     }
00321     last_range= *(cur_range++);
00322 
00323     start_key.key= (const unsigned char*) last_range->min_key;
00324     start_key.length= min(last_range->min_length, (uint16_t)prefix_length);
00325     start_key.keypart_map= last_range->min_keypart_map & keypart_map;
00326     start_key.flag= ((last_range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY :
00327                                                     (last_range->flag & EQ_RANGE) ?
00328                                                     HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT);
00329     end_key.key= (const unsigned char*) last_range->max_key;
00330     end_key.length= min(last_range->max_length, (uint16_t)prefix_length);
00331     end_key.keypart_map= last_range->max_keypart_map & keypart_map;
00332     /*
00333       We use READ_AFTER_KEY here because if we are reading on a key
00334       prefix we want to find all keys with this prefix
00335     */
00336     end_key.flag= (last_range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY :
00337                                                  HA_READ_AFTER_KEY);
00338 
00339     result= cursor->read_range_first(last_range->min_keypart_map ? &start_key : 0,
00340                                      last_range->max_keypart_map ? &end_key : 0,
00341                                      test(last_range->flag & EQ_RANGE),
00342                                      sorted);
00343     if (last_range->flag == (UNIQUE_RANGE | EQ_RANGE))
00344       last_range= 0; // Stop searching
00345 
00346     if (result != HA_ERR_END_OF_FILE)
00347       return result;
00348     last_range= 0; // No matching rows; go to next range
00349   }
00350 }
00351 
00352 
00353 bool optimizer::QuickRangeSelect::row_in_ranges()
00354 {
00355   optimizer::QuickRange *res= NULL;
00356   uint32_t min= 0;
00357   uint32_t max= ranges.size() - 1;
00358   uint32_t mid= (max + min) / 2;
00359 
00360   while (min != max)
00361   {
00362     if (cmp_next(reinterpret_cast<optimizer::QuickRange**>(ranges.buffer)[mid]))
00363     {
00364       /* current row value > mid->max */
00365       min= mid + 1;
00366     }
00367     else
00368       max= mid;
00369     mid= (min + max) / 2;
00370   }
00371   res= reinterpret_cast<optimizer::QuickRange**>(ranges.buffer)[mid];
00372   return not cmp_next(res) && not cmp_prev(res);
00373 }
00374 
00375 
00376 int optimizer::QuickRangeSelect::cmp_next(optimizer::QuickRange *range_arg)
00377 {
00378   if (range_arg->flag & NO_MAX_RANGE)
00379     return 0;                                   /* key can't be to large */
00380 
00381   KEY_PART *key_part= key_parts;
00382   uint32_t store_length;
00383 
00384   for (unsigned char *key=range_arg->max_key, *end=key+range_arg->max_length;
00385        key < end;
00386        key+= store_length, key_part++)
00387   {
00388     int cmp;
00389     store_length= key_part->store_length;
00390     if (key_part->null_bit)
00391     {
00392       if (*key)
00393       {
00394         if (! key_part->field->is_null())
00395           return 1;
00396         continue;
00397       }
00398       else if (key_part->field->is_null())
00399         return 0;
00400       key++;          // Skip null byte
00401       store_length--;
00402     }
00403     if ((cmp= key_part->field->key_cmp(key, key_part->length)) < 0)
00404       return 0;
00405     if (cmp > 0)
00406       return 1;
00407   }
00408   return (range_arg->flag & NEAR_MAX) ? 1 : 0;          // Exact match
00409 }
00410 
00411 
00412 int optimizer::QuickRangeSelect::cmp_prev(optimizer::QuickRange *range_arg)
00413 {
00414   if (range_arg->flag & NO_MIN_RANGE)
00415     return 0; /* key can't be to small */
00416 
00417   int cmp= key_cmp(key_part_info,
00418                    range_arg->min_key,
00419                    range_arg->min_length);
00420   if (cmp > 0 || (cmp == 0 && (range_arg->flag & NEAR_MIN) == false))
00421     return 0;
00422   return 1; // outside of range
00423 }
00424 
00425 
00426 void optimizer::QuickRangeSelect::add_info_string(string *str)
00427 {
00428   KeyInfo *key_info= head->key_info + index;
00429   str->append(key_info->name);
00430 }
00431 
00432 
00433 void optimizer::QuickRangeSelect::add_keys_and_lengths(string *key_names,
00434                                                        string *used_lengths)
00435 {
00436   char buf[64];
00437   uint32_t length;
00438   KeyInfo *key_info= head->key_info + index;
00439   key_names->append(key_info->name);
00440   length= internal::int64_t2str(max_used_key_length, buf, 10) - buf;
00441   used_lengths->append(buf, length);
00442 }
00443 
00444 
00445 /*
00446   This is a hack: we inherit from QUICK_SELECT so that we can use the
00447   get_next() interface, but we have to hold a pointer to the original
00448   QUICK_SELECT because its data are used all over the place.  What
00449   should be done is to factor out the data that is needed into a base
00450   class (QUICK_SELECT), and then have two subclasses (_ASC and _DESC)
00451   which handle the ranges and implement the get_next() function.  But
00452   for now, this seems to work right at least.
00453  */
00454 optimizer::QuickSelectDescending::QuickSelectDescending(optimizer::QuickRangeSelect *q, uint32_t, bool *)
00455   :
00456     optimizer::QuickRangeSelect(*q)
00457 {
00458   optimizer::QuickRange **pr= (optimizer::QuickRange**) ranges.buffer;
00459   optimizer::QuickRange **end_range= pr + ranges.size();
00460   for (; pr != end_range; pr++)
00461   {
00462     rev_ranges.push_back(*pr);
00463   }
00464   rev_it= rev_ranges.begin();
00465 
00466   /* Remove EQ_RANGE flag for keys that are not using the full key */
00467   BOOST_FOREACH(QuickRange* it, rev_ranges)
00468   {
00469     if ((it->flag & EQ_RANGE) && head->key_info[index].key_length != it->max_length)
00470     {
00471       it->flag&= ~EQ_RANGE;
00472     }
00473   }
00474   q->dont_free= 1; // Don't free shared mem
00475   delete q;
00476 }
00477 
00478 
00479 int optimizer::QuickSelectDescending::get_next()
00480 {
00481   /* The max key is handled as follows:
00482    *   - if there is NO_MAX_RANGE, start at the end and move backwards
00483    *   - if it is an EQ_RANGE, which means that max key covers the entire
00484    *     key, go directly to the key and read through it (sorting backwards is
00485    *     same as sorting forwards)
00486    *   - if it is NEAR_MAX, go to the key or next, step back once, and
00487    *     move backwards
00488    *   - otherwise (not NEAR_MAX == include the key), go after the key,
00489    *     step back once, and move backwards
00490    */
00491   for (;;)
00492   {
00493     int result;
00494     if (last_range)
00495     {           // Already read through key
00496       result= ((last_range->flag & EQ_RANGE) ?
00497                cursor->index_next_same(record, last_range->min_key,
00498                                        last_range->min_length) :
00499                cursor->index_prev(record));
00500       if (! result)
00501       {
00502           if (cmp_prev(*(rev_it - 1)) == 0)
00503             return 0;
00504       }
00505       else if (result != HA_ERR_END_OF_FILE)
00506         return result;
00507     }
00508 
00509     if (rev_it == rev_ranges.end())
00510     {
00511       return HA_ERR_END_OF_FILE; // All ranges used
00512     }
00513     last_range= *rev_it;
00514     ++rev_it;
00515 
00516     if (last_range->flag & NO_MAX_RANGE)        // Read last record
00517     {
00518       int local_error;
00519       if ((local_error= cursor->index_last(record)))
00520         return local_error; // Empty table
00521       if (cmp_prev(last_range) == 0)
00522         return 0;
00523       last_range= 0; // No match; go to next range
00524       continue;
00525     }
00526 
00527     if (last_range->flag & EQ_RANGE)
00528     {
00529       result = cursor->index_read_map(record,
00530                                       last_range->max_key,
00531                                       last_range->max_keypart_map,
00532                                       HA_READ_KEY_EXACT);
00533     }
00534     else
00535     {
00536       assert(last_range->flag & NEAR_MAX ||
00537              range_reads_after_key(last_range));
00538       result= cursor->index_read_map(record,
00539                                      last_range->max_key,
00540                                      last_range->max_keypart_map,
00541                                      ((last_range->flag & NEAR_MAX) ?
00542                                       HA_READ_BEFORE_KEY :
00543                                       HA_READ_PREFIX_LAST_OR_PREV));
00544     }
00545     if (result)
00546     {
00547       if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE)
00548         return result;
00549       last_range= 0;                            // Not found, to next range
00550       continue;
00551     }
00552     if (cmp_prev(last_range) == 0)
00553     {
00554       if (last_range->flag == (UNIQUE_RANGE | EQ_RANGE))
00555         last_range= 0;        // Stop searching
00556       return 0;       // Found key is in range
00557     }
00558     last_range= 0;                              // To next range
00559   }
00560 }
00561 
00562 
00563 /*
00564  * true if this range will require using HA_READ_AFTER_KEY
00565    See comment in get_next() about this
00566  */
00567 bool optimizer::QuickSelectDescending::range_reads_after_key(optimizer::QuickRange *range_arg)
00568 {
00569   return ((range_arg->flag & (NO_MAX_RANGE | NEAR_MAX)) ||
00570           ! (range_arg->flag & EQ_RANGE) ||
00571           head->key_info[index].key_length != range_arg->max_length) ? 1 : 0;
00572 }
00573 
00574 
00575 } /* namespace drizzled */