Drizzled Public API Documentation

quick_group_min_max_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/sql_select.h>
00023 #include <drizzled/join.h>
00024 #include <drizzled/optimizer/range.h>
00025 #include <drizzled/optimizer/quick_group_min_max_select.h>
00026 #include <drizzled/optimizer/quick_range.h>
00027 #include <drizzled/optimizer/quick_range_select.h>
00028 #include <drizzled/optimizer/sel_arg.h>
00029 #include <drizzled/internal/m_string.h>
00030 #include <drizzled/util/functors.h>
00031 #include <drizzled/key.h>
00032 #include <drizzled/table.h>
00033 #include <drizzled/system_variables.h>
00034 
00035 #include <vector>
00036 
00037 using namespace std;
00038 
00039 namespace drizzled {
00040 namespace optimizer {
00041 
00042 QuickGroupMinMaxSelect::QuickGroupMinMaxSelect(Table *table,
00043                        Join *join_arg,
00044                        bool have_min_arg,
00045                        bool have_max_arg,
00046                        KeyPartInfo *min_max_arg_part_arg,
00047                        uint32_t group_prefix_len_arg,
00048                        uint32_t group_key_parts_arg,
00049                        uint32_t used_key_parts_arg,
00050                        KeyInfo *index_info_arg,
00051                        uint32_t use_index,
00052                        double read_cost_arg,
00053                        ha_rows records_arg,
00054                        uint32_t key_infix_len_arg,
00055                        unsigned char *key_infix_arg,
00056                        memory::Root *parent_alloc)
00057   :
00058     join(join_arg),
00059     index_info(index_info_arg),
00060     group_prefix_len(group_prefix_len_arg),
00061     group_key_parts(group_key_parts_arg),
00062     have_min(have_min_arg),
00063     have_max(have_max_arg),
00064     seen_first_key(false),
00065     min_max_arg_part(min_max_arg_part_arg),
00066     key_infix(key_infix_arg),
00067     key_infix_len(key_infix_len_arg),
00068     min_functions_it(NULL),
00069     max_functions_it(NULL)
00070 {
00071   head= table;
00072   cursor= head->cursor;
00073   index= use_index;
00074   record= head->record[0];
00075   tmp_record= head->getUpdateRecord();
00076   read_time= read_cost_arg;
00077   records= records_arg;
00078   used_key_parts= used_key_parts_arg;
00079   real_key_parts= used_key_parts_arg;
00080   real_prefix_len= group_prefix_len + key_infix_len;
00081   group_prefix= NULL;
00082   min_max_arg_len= min_max_arg_part ? min_max_arg_part->store_length : 0;
00083 
00084   /*
00085     We can't have parent_alloc set as the init function can't handle this case
00086     yet.
00087   */
00088   assert(! parent_alloc);
00089   if (! parent_alloc)
00090   {
00091     alloc.init(join->session->variables.range_alloc_block_size);
00092     join->session->mem_root= &alloc;
00093   }
00094   else
00095     memset(&alloc, 0, sizeof(memory::Root));  // ensure that it's not used
00096 }
00097 
00098 
00099 int QuickGroupMinMaxSelect::init()
00100 {
00101   if (group_prefix) /* Already initialized. */
00102     return 0;
00103 
00104   last_prefix= alloc.alloc(group_prefix_len);
00105   /*
00106     We may use group_prefix to store keys with all select fields, so allocate
00107     enough space for it.
00108   */
00109   group_prefix= alloc.alloc(real_prefix_len + min_max_arg_len);
00110 
00111   if (key_infix_len > 0)
00112   {
00113     /*
00114       The memory location pointed to by key_infix will be deleted soon, so
00115       allocate a new buffer and copy the key_infix into it.
00116     */
00117     unsigned char *tmp_key_infix= alloc.alloc(key_infix_len);
00118     memcpy(tmp_key_infix, this->key_infix, key_infix_len);
00119     this->key_infix= tmp_key_infix;
00120   }
00121 
00122   if (min_max_arg_part)
00123   {
00124     min_functions= have_min ? new List<Item_sum> : NULL;
00125     max_functions= have_max ? new List<Item_sum> : NULL;
00126     Item_sum **func_ptr= join->sum_funcs;
00127     while (Item_sum* min_max_item= *(func_ptr++))
00128     {
00129       if (have_min && (min_max_item->sum_func() == Item_sum::MIN_FUNC))
00130         min_functions->push_back(min_max_item);
00131       else if (have_max && (min_max_item->sum_func() == Item_sum::MAX_FUNC))
00132         max_functions->push_back(min_max_item);
00133     }
00134 
00135     if (have_min)
00136       min_functions_it= new List<Item_sum>::iterator(min_functions->begin());
00137     if (have_max)
00138       max_functions_it= new List<Item_sum>::iterator(max_functions->begin());
00139   }
00140   return 0;
00141 }
00142 
00143 QuickGroupMinMaxSelect::~QuickGroupMinMaxSelect()
00144 {
00145   if (cursor->inited != Cursor::NONE)
00146   {
00147     cursor->endIndexScan();
00148   }
00149   if (min_max_arg_part)
00150   {
00151     for_each(min_max_ranges.begin(),
00152              min_max_ranges.end(),
00153              DeletePtr());
00154   }
00155   min_max_ranges.clear();
00156   alloc.free_root(MYF(0));
00157   delete min_functions_it;
00158   delete max_functions_it;
00159   delete quick_prefix_select;
00160 }
00161 
00162 
00163 bool QuickGroupMinMaxSelect::add_range(SEL_ARG *sel_range)
00164 {
00165   QuickRange *range= NULL;
00166   uint32_t range_flag= sel_range->min_flag | sel_range->max_flag;
00167 
00168   /* Skip (-inf,+inf) ranges, e.g. (x < 5 or x > 4). */
00169   if ((range_flag & NO_MIN_RANGE) && (range_flag & NO_MAX_RANGE))
00170     return false;
00171 
00172   if (! (sel_range->min_flag & NO_MIN_RANGE) &&
00173       ! (sel_range->max_flag & NO_MAX_RANGE))
00174   {
00175     if (sel_range->maybe_null &&
00176         sel_range->min_value[0] && sel_range->max_value[0])
00177       range_flag|= NULL_RANGE; /* IS NULL condition */
00178     else if (memcmp(sel_range->min_value, sel_range->max_value,
00179                     min_max_arg_len) == 0)
00180       range_flag|= EQ_RANGE;  /* equality condition */
00181   }
00182   range= new QuickRange(sel_range->min_value,
00183                                    min_max_arg_len,
00184                                    make_keypart_map(sel_range->part),
00185                                    sel_range->max_value,
00186                                    min_max_arg_len,
00187                                    make_keypart_map(sel_range->part),
00188                                    range_flag);
00189   if (! range)
00190     return true;
00191   min_max_ranges.push_back(range);
00192   return false;
00193 }
00194 
00195 
00196 void QuickGroupMinMaxSelect::adjust_prefix_ranges()
00197 {
00198   if (quick_prefix_select &&
00199       group_prefix_len < quick_prefix_select->max_used_key_length)
00200   {
00201     DYNAMIC_ARRAY& arr= quick_prefix_select->ranges;
00202     for (size_t inx= 0; inx < arr.size(); inx++)
00203       reinterpret_cast<QuickRange**>(arr.buffer)[inx]->flag &= ~(NEAR_MIN | NEAR_MAX);
00204   }
00205 }
00206 
00207 
00208 void QuickGroupMinMaxSelect::update_key_stat()
00209 {
00210   max_used_key_length= real_prefix_len;
00211   if (! min_max_ranges.empty())
00212   {
00213     QuickRange *cur_range= NULL;
00214     if (have_min)
00215     { /* Check if the right-most range has a lower boundary. */
00216       cur_range= min_max_ranges.back();
00217       if (! (cur_range->flag & NO_MIN_RANGE))
00218       {
00219         max_used_key_length+= min_max_arg_len;
00220         used_key_parts++;
00221         return;
00222       }
00223     }
00224     if (have_max)
00225     { /* Check if the left-most range has an upper boundary. */
00226       cur_range= min_max_ranges.front();
00227       if (! (cur_range->flag & NO_MAX_RANGE))
00228       {
00229         max_used_key_length+= min_max_arg_len;
00230         used_key_parts++;
00231         return;
00232       }
00233     }
00234   }
00235   else if (have_min && min_max_arg_part &&
00236            min_max_arg_part->field->real_maybe_null())
00237   {
00238     /*
00239       If a MIN/MAX argument value is NULL, we can quickly determine
00240       that we're in the beginning of the next group, because NULLs
00241       are always < any other value. This allows us to quickly
00242       determine the end of the current group and jump to the next
00243       group (see next_min()) and thus effectively increases the
00244       usable key length.
00245     */
00246     max_used_key_length+= min_max_arg_len;
00247     used_key_parts++;
00248   }
00249 }
00250 
00251 
00252 int QuickGroupMinMaxSelect::reset(void)
00253 {
00254   int result;
00255 
00256   cursor->extra(HA_EXTRA_KEYREAD); /* We need only the key attributes */
00257   if ((result= cursor->startIndexScan(index,1)))
00258     return result;
00259   if (quick_prefix_select && quick_prefix_select->reset())
00260     return 0;
00261   result= cursor->index_last(record);
00262   if (result == HA_ERR_END_OF_FILE)
00263     return 0;
00264   /* Save the prefix of the last group. */
00265   key_copy(last_prefix, record, index_info, group_prefix_len);
00266 
00267   return 0;
00268 }
00269 
00270 
00271 int QuickGroupMinMaxSelect::get_next()
00272 {
00273   int min_res= 0;
00274   int max_res= 0;
00275   int result= 0;
00276   int is_last_prefix= 0;
00277 
00278   /*
00279     Loop until a group is found that satisfies all query conditions or the last
00280     group is reached.
00281   */
00282   do
00283   {
00284     result= next_prefix();
00285     /*
00286       Check if this is the last group prefix. Notice that at this point
00287       this->record contains the current prefix in record format.
00288     */
00289     if (! result)
00290     {
00291       is_last_prefix= key_cmp(index_info->key_part, last_prefix,
00292                               group_prefix_len);
00293       assert(is_last_prefix <= 0);
00294     }
00295     else
00296     {
00297       if (result == HA_ERR_KEY_NOT_FOUND)
00298         continue;
00299       break;
00300     }
00301 
00302     if (have_min)
00303     {
00304       min_res= next_min();
00305       if (min_res == 0)
00306         update_min_result();
00307     }
00308     /* If there is no MIN in the group, there is no MAX either. */
00309     if ((have_max && !have_min) ||
00310         (have_max && have_min && (min_res == 0)))
00311     {
00312       max_res= next_max();
00313       if (max_res == 0)
00314         update_max_result();
00315       /* If a MIN was found, a MAX must have been found as well. */
00316       assert(((have_max && !have_min) ||
00317                   (have_max && have_min && (max_res == 0))));
00318     }
00319     /*
00320       If this is just a GROUP BY or DISTINCT without MIN or MAX and there
00321       are equality predicates for the key parts after the group, find the
00322       first sub-group with the extended prefix.
00323     */
00324     if (! have_min && ! have_max && key_infix_len > 0)
00325       result= cursor->index_read_map(record,
00326                                      group_prefix,
00327                                      make_prev_keypart_map(real_key_parts),
00328                                      HA_READ_KEY_EXACT);
00329 
00330     result= have_min ? min_res : have_max ? max_res : result;
00331   } while ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
00332            is_last_prefix != 0);
00333 
00334   if (result == 0)
00335   {
00336     /*
00337       Partially mimic the behavior of end_select_send. Copy the
00338       field data from Item_field::field into Item_field::result_field
00339       of each non-aggregated field (the group fields, and optionally
00340       other fields in non-ANSI SQL mode).
00341     */
00342     copy_fields(&join->tmp_table_param);
00343   }
00344   else if (result == HA_ERR_KEY_NOT_FOUND)
00345     result= HA_ERR_END_OF_FILE;
00346 
00347   return result;
00348 }
00349 
00350 
00351 int QuickGroupMinMaxSelect::next_min()
00352 {
00353   int result= 0;
00354 
00355   /* Find the MIN key using the eventually extended group prefix. */
00356   if (! min_max_ranges.empty())
00357   {
00358     if ((result= next_min_in_range()))
00359       return result;
00360   }
00361   else
00362   {
00363     /* Apply the constant equality conditions to the non-group select fields */
00364     if (key_infix_len > 0)
00365     {
00366       if ((result= cursor->index_read_map(record,
00367                                           group_prefix,
00368                                           make_prev_keypart_map(real_key_parts),
00369                                           HA_READ_KEY_EXACT)))
00370         return result;
00371     }
00372 
00373     /*
00374       If the min/max argument field is NULL, skip subsequent rows in the same
00375       group with NULL in it. Notice that:
00376       - if the first row in a group doesn't have a NULL in the field, no row
00377       in the same group has (because NULL < any other value),
00378       - min_max_arg_part->field->ptr points to some place in 'record'.
00379     */
00380     if (min_max_arg_part && min_max_arg_part->field->is_null())
00381     {
00382       /* Find the first subsequent record without NULL in the MIN/MAX field. */
00383       key_copy(tmp_record, record, index_info, 0);
00384       result= cursor->index_read_map(record,
00385                                      tmp_record,
00386                                      make_keypart_map(real_key_parts),
00387                                      HA_READ_AFTER_KEY);
00388       /*
00389         Check if the new record belongs to the current group by comparing its
00390         prefix with the group's prefix. If it is from the next group, then the
00391         whole group has NULLs in the MIN/MAX field, so use the first record in
00392         the group as a result.
00393         TODO:
00394         It is possible to reuse this new record as the result candidate for the
00395         next call to next_min(), and to save one lookup in the next call. For
00396         this add a new member 'this->next_group_prefix'.
00397       */
00398       if (! result)
00399       {
00400         if (key_cmp(index_info->key_part, group_prefix, real_prefix_len))
00401           key_restore(record, tmp_record, index_info, 0);
00402       }
00403       else if (result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE)
00404         result= 0; /* There is a result in any case. */
00405     }
00406   }
00407 
00408   /*
00409     If the MIN attribute is non-nullable, this->record already contains the
00410     MIN key in the group, so just return.
00411   */
00412   return result;
00413 }
00414 
00415 
00416 int QuickGroupMinMaxSelect::next_max()
00417 {
00418   /* Get the last key in the (possibly extended) group. */
00419   return min_max_ranges.empty()
00420     ? cursor->index_read_map(record, group_prefix, make_prev_keypart_map(real_key_parts), HA_READ_PREFIX_LAST)
00421     : next_max_in_range();
00422 }
00423 
00424 
00425 int QuickGroupMinMaxSelect::next_prefix()
00426 {
00427   int result= 0;
00428 
00429   if (quick_prefix_select)
00430   {
00431     unsigned char *cur_prefix= seen_first_key ? group_prefix : NULL;
00432     if ((result= quick_prefix_select->get_next_prefix(group_prefix_len,
00433                                                       make_prev_keypart_map(group_key_parts),
00434                                                       cur_prefix)))
00435       return result;
00436     seen_first_key= true;
00437   }
00438   else
00439   {
00440     if (! seen_first_key)
00441     {
00442       result= cursor->index_first(record);
00443       if (result)
00444         return result;
00445       seen_first_key= true;
00446     }
00447     else
00448     {
00449       /* Load the first key in this group into record. */
00450       result= cursor->index_read_map(record,
00451                                      group_prefix,
00452                                      make_prev_keypart_map(group_key_parts),
00453                                      HA_READ_AFTER_KEY);
00454       if (result)
00455         return result;
00456     }
00457   }
00458 
00459   /* Save the prefix of this group for subsequent calls. */
00460   key_copy(group_prefix, record, index_info, group_prefix_len);
00461   /* Append key_infix to group_prefix. */
00462   if (key_infix_len > 0)
00463     memcpy(group_prefix + group_prefix_len,
00464            key_infix,
00465            key_infix_len);
00466 
00467   return 0;
00468 }
00469 
00470 
00471 int QuickGroupMinMaxSelect::next_min_in_range()
00472 {
00473   ha_rkey_function find_flag;
00474   key_part_map keypart_map;
00475   QuickRange *cur_range= NULL;
00476   bool found_null= false;
00477   int result= HA_ERR_KEY_NOT_FOUND;
00478   basic_string<unsigned char> max_key;
00479 
00480   max_key.reserve(real_prefix_len + min_max_arg_len);
00481 
00482   assert(! min_max_ranges.empty());
00483 
00484   for (vector<QuickRange *>::iterator it= min_max_ranges.begin(); it != min_max_ranges.end(); ++it)
00485   { /* Search from the left-most range to the right. */
00486     cur_range= *it;
00487 
00488     /*
00489       If the current value for the min/max argument is bigger than the right
00490       boundary of cur_range, there is no need to check this range.
00491     */
00492     if (it != min_max_ranges.begin() && 
00493         ! (cur_range->flag & NO_MAX_RANGE) &&
00494         (key_cmp(min_max_arg_part,
00495                  (const unsigned char*) cur_range->max_key,
00496                  min_max_arg_len) == 1))
00497       continue;
00498 
00499     if (cur_range->flag & NO_MIN_RANGE)
00500     {
00501       keypart_map= make_prev_keypart_map(real_key_parts);
00502       find_flag= HA_READ_KEY_EXACT;
00503     }
00504     else
00505     {
00506       /* Extend the search key with the lower boundary for this range. */
00507       memcpy(group_prefix + real_prefix_len,
00508              cur_range->min_key,
00509              cur_range->min_length);
00510       keypart_map= make_keypart_map(real_key_parts);
00511       find_flag= (cur_range->flag & (EQ_RANGE | NULL_RANGE)) ?
00512                  HA_READ_KEY_EXACT : (cur_range->flag & NEAR_MIN) ?
00513                  HA_READ_AFTER_KEY : HA_READ_KEY_OR_NEXT;
00514     }
00515 
00516     result= cursor->index_read_map(record, group_prefix, keypart_map, find_flag);
00517     if (result)
00518     {
00519       if ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
00520           (cur_range->flag & (EQ_RANGE | NULL_RANGE)))
00521         continue; /* Check the next range. */
00522 
00523       /*
00524         In all other cases (HA_ERR_*, HA_READ_KEY_EXACT with NO_MIN_RANGE,
00525         HA_READ_AFTER_KEY, HA_READ_KEY_OR_NEXT) if the lookup failed for this
00526         range, it can't succeed for any other subsequent range.
00527       */
00528       break;
00529     }
00530 
00531     /* A key was found. */
00532     if (cur_range->flag & EQ_RANGE)
00533       break; /* No need to perform the checks below for equal keys. */
00534 
00535     if (cur_range->flag & NULL_RANGE)
00536     {
00537       /*
00538         Remember this key, and continue looking for a non-NULL key that
00539         satisfies some other condition.
00540       */
00541       memcpy(tmp_record, record, head->getShare()->rec_buff_length);
00542       found_null= true;
00543       continue;
00544     }
00545 
00546     /* Check if record belongs to the current group. */
00547     if (key_cmp(index_info->key_part, group_prefix, real_prefix_len))
00548     {
00549       result= HA_ERR_KEY_NOT_FOUND;
00550       continue;
00551     }
00552 
00553     /* If there is an upper limit, check if the found key is in the range. */
00554     if (! (cur_range->flag & NO_MAX_RANGE) )
00555     {
00556       /* Compose the MAX key for the range. */
00557       max_key.clear();
00558       max_key.append(group_prefix, real_prefix_len);
00559       max_key.append(cur_range->max_key, cur_range->max_length);
00560       /* Compare the found key with max_key. */
00561       int cmp_res= key_cmp(index_info->key_part,
00562                            max_key.data(),
00563                            real_prefix_len + min_max_arg_len);
00564       if (! (((cur_range->flag & NEAR_MAX) && (cmp_res == -1)) ||
00565           (cmp_res <= 0)))
00566       {
00567         result= HA_ERR_KEY_NOT_FOUND;
00568         continue;
00569       }
00570     }
00571     /* If we got to this point, the current key qualifies as MIN. */
00572     assert(result == 0);
00573     break;
00574   }
00575   /*
00576     If there was a key with NULL in the MIN/MAX field, and there was no other
00577     key without NULL from the same group that satisfies some other condition,
00578     then use the key with the NULL.
00579   */
00580   if (found_null && result)
00581   {
00582     memcpy(record, tmp_record, head->getShare()->rec_buff_length);
00583     result= 0;
00584   }
00585   return result;
00586 }
00587 
00588 
00589 int QuickGroupMinMaxSelect::next_max_in_range()
00590 {
00591   ha_rkey_function find_flag;
00592   key_part_map keypart_map;
00593   QuickRange *cur_range= NULL;
00594   int result= 0;
00595   basic_string<unsigned char> min_key;
00596   min_key.reserve(real_prefix_len + min_max_arg_len);
00597 
00598   assert(! min_max_ranges.empty());
00599 
00600   for (vector<QuickRange *>::reverse_iterator rit= min_max_ranges.rbegin();
00601        rit != min_max_ranges.rend();
00602        ++rit)
00603   { /* Search from the right-most range to the left. */
00604     cur_range= *rit;
00605 
00606     /*
00607       If the current value for the min/max argument is smaller than the left
00608       boundary of cur_range, there is no need to check this range.
00609     */
00610     if (rit != min_max_ranges.rbegin() &&
00611         ! (cur_range->flag & NO_MIN_RANGE) &&
00612         (key_cmp(min_max_arg_part,
00613                  (const unsigned char*) cur_range->min_key,
00614                  min_max_arg_len) == -1))
00615       continue;
00616 
00617     if (cur_range->flag & NO_MAX_RANGE)
00618     {
00619       keypart_map= make_prev_keypart_map(real_key_parts);
00620       find_flag= HA_READ_PREFIX_LAST;
00621     }
00622     else
00623     {
00624       /* Extend the search key with the upper boundary for this range. */
00625       memcpy(group_prefix + real_prefix_len,
00626              cur_range->max_key,
00627              cur_range->max_length);
00628       keypart_map= make_keypart_map(real_key_parts);
00629       find_flag= (cur_range->flag & EQ_RANGE) ?
00630                  HA_READ_KEY_EXACT : (cur_range->flag & NEAR_MAX) ?
00631                  HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV;
00632     }
00633 
00634     result= cursor->index_read_map(record, group_prefix, keypart_map, find_flag);
00635 
00636     if (result)
00637     {
00638       if ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
00639           (cur_range->flag & EQ_RANGE))
00640         continue; /* Check the next range. */
00641 
00642       /*
00643         In no key was found with this upper bound, there certainly are no keys
00644         in the ranges to the left.
00645       */
00646       return result;
00647     }
00648     /* A key was found. */
00649     if (cur_range->flag & EQ_RANGE)
00650       return 0; /* No need to perform the checks below for equal keys. */
00651 
00652     /* Check if record belongs to the current group. */
00653     if (key_cmp(index_info->key_part, group_prefix, real_prefix_len))
00654       continue;                                 // Row not found
00655 
00656     /* If there is a lower limit, check if the found key is in the range. */
00657     if (! (cur_range->flag & NO_MIN_RANGE) )
00658     {
00659       /* Compose the MIN key for the range. */
00660       min_key.clear();
00661       min_key.append(group_prefix, real_prefix_len);
00662       min_key.append(cur_range->min_key, cur_range->min_length);
00663 
00664       /* Compare the found key with min_key. */
00665       int cmp_res= key_cmp(index_info->key_part,
00666                            min_key.data(),
00667                            real_prefix_len + min_max_arg_len);
00668       if (! (((cur_range->flag & NEAR_MIN) && (cmp_res == 1)) ||
00669           (cmp_res >= 0)))
00670         continue;
00671     }
00672     /* If we got to this point, the current key qualifies as MAX. */
00673     return result;
00674   }
00675   return HA_ERR_KEY_NOT_FOUND;
00676 }
00677 
00678 
00679 void QuickGroupMinMaxSelect::update_min_result()
00680 {
00681   *min_functions_it= min_functions->begin();
00682   for (Item_sum *min_func; (min_func= (*min_functions_it)++); )
00683     min_func->reset();
00684 }
00685 
00686 
00687 void QuickGroupMinMaxSelect::update_max_result()
00688 {
00689   *max_functions_it= max_functions->begin();
00690   for (Item_sum *max_func; (max_func= (*max_functions_it)++); )
00691     max_func->reset();
00692 }
00693 
00694 
00695 void QuickGroupMinMaxSelect::add_keys_and_lengths(string *key_names,
00696                                                              string *used_lengths)
00697 {
00698   char buf[64];
00699   key_names->append(index_info->name);
00700   uint32_t length= internal::int64_t2str(max_used_key_length, buf, 10) - buf;
00701   used_lengths->append(buf, length);
00702 }
00703 
00704 }
00705 } /* namespace drizzled */