opt_sum.cc 26.9 KB
Newer Older
1
/* Copyright (C) 2000-2003 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16 17
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/*
  Optimising of MIN(), MAX() and COUNT(*) queries without 'group by' clause
  by replacing the aggregate expression with a constant.  

  Given a table with a compound key on columns (a,b,c), the following
  types of queries are optimised (assuming the table handler supports
  the required methods)

  SELECT COUNT(*) FROM t1[,t2,t3,...]
  SELECT MIN(b) FROM t1 WHERE a=const
  SELECT MAX(c) FROM t1 WHERE a=const AND b=const
  SELECT MAX(b) FROM t1 WHERE a=const AND b<const
  SELECT MIN(b) FROM t1 WHERE a=const AND b>const
  SELECT MIN(b) FROM t1 WHERE a=const AND b BETWEEN const AND const
  SELECT MAX(b) FROM t1 WHERE a=const AND b BETWEEN const AND const

  Instead of '<' one can use '<=', '>', '>=' and '=' as well.
  Instead of 'a=const' the condition 'a IS NULL' can be used.

  If all selected fields are replaced then we will also remove all
  involved tables and return the answer without any join. Thus, the
  following query will be replaced with a row of two constants:
  SELECT MAX(b), MIN(d) FROM t1,t2 
    WHERE a=const AND b<const AND d>const
  (assuming a index for column d of table t2 is defined)

*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
45 46 47 48

#include "mysql_priv.h"
#include "sql_select.h"

49 50 51
static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, Field* field,
                                COND *cond, uint *range_fl,
                                uint *key_prefix_length);
52 53 54 55
static int reckey_in_range(bool max_fl, TABLE_REF *ref, Field* field,
                            COND *cond, uint range_fl, uint prefix_len);
static int maxmin_in_range(bool max_fl, Field* field, COND *cond);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
56

57 58 59 60 61
/*
  Substitutes constants for some COUNT(), MIN() and MAX() functions.

  SYNOPSIS
    opt_sum_query()
62 63 64
    tables                list of leaves of join table tree
    all_fields            All fields to be returned
    conds                 WHERE clause
65 66 67 68 69

  NOTE:
    This function is only called for queries with sum functions and no
    GROUP BY part.

70
  RETURN VALUES
71
    0 No errors
72 73 74 75
    1 if all items were resolved
   -1 on impossible conditions
    OR an error number from my_base.h HA_ERR_... if a deadlock or a lock
       wait timeout happens, for example
76
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
77 78 79

int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
{
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
80
  List_iterator_fast<Item> it(all_fields);
81 82
  int const_result= 1;
  bool recalc_const_item= 0;
83 84
  longlong count= 1;
  bool is_exact_count= TRUE;
85 86
  table_map removed_tables= 0, outer_tables= 0, used_tables= 0;
  table_map where_tables= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
87
  Item *item;
88
  int error;
89

90 91 92
  if (conds)
    where_tables= conds->used_tables();

93 94 95 96
  /*
    Analyze outer join dependencies, and, if possible, compute the number
    of returned rows.
  */
97
  for (TABLE_LIST *tl= tables; tl; tl= tl->next_leaf)
98
  {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
99 100 101 102 103 104 105
    TABLE_LIST *embedded;
    for (embedded= tl ; embedded; embedded= embedded->embedding)
    {
      if (embedded->on_expr)
        break;
    }
    if (embedded)
106
    /* Don't replace expression on a table that is part of an outer join */
107 108 109 110
    {
      outer_tables|= tl->table->map;

      /*
111 112
        We can't optimise LEFT JOIN in cases where the WHERE condition
        restricts the table that is used, like in:
113
          SELECT MAX(t1.a) FROM t1 LEFT JOIN t2 join-condition
114
          WHERE t2.field IS NULL;
115 116
      */
      if (tl->table->map & where_tables)
117
        return 0;
118
    }
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
119 120
    else
      used_tables|= tl->table->map;
121 122 123 124 125 126 127

    /*
      If the storage manager of 'tl' gives exact row count, compute the total
      number of rows. If there are no outer table dependencies, this count
      may be used as the real count.
    */
    if (tl->table->file->table_flags() & HA_NOT_EXACT_COUNT)
128
    {
129
      is_exact_count= FALSE;
130 131
      count= 1;                                 // ensure count != 0
    }
132 133 134 135 136
    else
    {
      tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
      count*= tl->table->file->records;
    }
137 138 139
  }

  /*
140 141
    Iterate through all items in the SELECT clause and replace
    COUNT(), MIN() and MAX() with constants (if possible).
142
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
143 144 145 146 147 148 149 150

  while ((item= it++))
  {
    if (item->type() == Item::SUM_FUNC_ITEM)
    {
      Item_sum *item_sum= (((Item_sum*) item));
      switch (item_sum->sum_func()) {
      case Item_sum::COUNT_FUNC:
151 152
        /*
          If the expr in count(expr) can never be null we can change this
153 154
          to the number of rows in the tables if this number is exact and
          there are no outer joins.
155
        */
156 157
        if (!conds && !((Item_sum_count*) item)->args[0]->maybe_null &&
            !outer_tables && is_exact_count)
158 159 160
        {
          longlong count= 1;
          TABLE_LIST *table;
161
          for (table= tables; table; table= table->next_leaf)
162 163
          {
            if (outer_tables || (table->table->file->table_flags() &
164
                                 HA_NOT_EXACT_COUNT) || table->schema_table)
165
            {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
166
              const_result= 0;			// Can't optimize left join
167 168 169 170 171 172 173 174 175 176 177 178 179 180
              break;
            }
            tables->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
            count*= table->table->file->records;
          }
          if (!table)
          {
            ((Item_sum_count*) item)->make_const(count);
            recalc_const_item= 1;
          }
        }
        else
          const_result= 0;
        break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
181 182
      case Item_sum::MIN_FUNC:
      {
183 184 185 186 187 188
        /*
          If MIN(expr) is the first part of a key or if all previous
          parts of the key is found in the COND, then we can use
          indexes to find the key.
        */
        Item *expr=item_sum->args[0];
189
        if (expr->real_item()->type() == Item::FIELD_ITEM)
190 191 192 193 194 195
        {
          byte key_buff[MAX_KEY_LENGTH];
          TABLE_REF ref;
          uint range_fl, prefix_len;

          ref.key_buff= key_buff;
196
          Item_field *item_field= (Item_field*) (expr->real_item());
197 198 199 200 201 202
          TABLE *table= item_field->field->table;

          /* 
            Look for a partial key that can be used for optimization.
            If we succeed, ref.key_length will contain the length of
            this key, while prefix_len will contain the length of 
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
203 204 205
            the beginning of this key without field used in MIN(). 
            Type of range for the key part for this field will be
            returned in range_fl.
206 207 208 209 210 211 212 213
          */
          if ((outer_tables & table->map) ||
              !find_key_for_maxmin(0, &ref, item_field->field, conds,
                                   &range_fl, &prefix_len))
          {
            const_result= 0;
            break;
          }
214
          error= table->file->ha_index_init((uint) ref.key, 1);
215 216

          if (!ref.key_length)
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
217
            error= table->file->index_first(table->record[0]);
218
          else
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
219 220 221 222 223
	    error= table->file->index_read(table->record[0],key_buff,
					   ref.key_length,
					   range_fl & NEAR_MIN ?
					   HA_READ_AFTER_KEY :
					   HA_READ_KEY_OR_NEXT);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
224 225
	  if (!error && reckey_in_range(0, &ref, item_field->field, 
			                conds, range_fl, prefix_len))
226
	    error= HA_ERR_KEY_NOT_FOUND;
227 228 229 230 231
          if (table->key_read)
          {
            table->key_read= 0;
            table->file->extra(HA_EXTRA_NO_KEYREAD);
          }
232
          table->file->ha_index_end();
233
          if (error)
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
234
	  {
235 236
	    if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE)
	      return -1;		       // No rows matching WHERE
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
237 238 239
	    /* HA_ERR_LOCK_DEADLOCK or some other error */
 	    table->file->print_error(error, MYF(0));
            return(error);
240
	  }
241 242
          removed_tables|= table->map;
        }
243
        else if (!expr->const_item() || !is_exact_count)
244
        {
245 246 247 248 249 250 251 252 253
          /*
            The optimization is not applicable in both cases:
            (a) 'expr' is a non-constant expression. Then we can't
            replace 'expr' by a constant.
            (b) 'expr' is a costant. According to ANSI, MIN/MAX must return
            NULL if the query does not return any rows. Thus, if we are not
            able to determine if the query returns any rows, we can't apply
            the optimization and replace MIN/MAX with a constant.
          */
254 255 256
          const_result= 0;
          break;
        }
257 258
        if (!count)
        {
259
          /* If count == 0, then we know that is_exact_count == TRUE. */
260 261 262 263
          ((Item_sum_min*) item_sum)->clear(); /* Set to NULL. */
        }
        else
          ((Item_sum_min*) item_sum)->reset(); /* Set to the constant value. */
264 265 266
        ((Item_sum_min*) item_sum)->make_const();
        recalc_const_item= 1;
        break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
267 268 269
      }
      case Item_sum::MAX_FUNC:
      {
270 271 272 273 274 275
        /*
          If MAX(expr) is the first part of a key or if all previous
          parts of the key is found in the COND, then we can use
          indexes to find the key.
        */
        Item *expr=item_sum->args[0];
276
        if (expr->real_item()->type() == Item::FIELD_ITEM)
277 278 279
        {
          byte key_buff[MAX_KEY_LENGTH];
          TABLE_REF ref;
280
          uint range_fl, prefix_len;
281 282

          ref.key_buff= key_buff;
283
          Item_field *item_field= (Item_field*) (expr->real_item());
284 285 286 287 288 289
          TABLE *table= item_field->field->table;

          /* 
            Look for a partial key that can be used for optimization.
            If we succeed, ref.key_length will contain the length of
            this key, while prefix_len will contain the length of 
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
290 291 292
            the beginning of this key without field used in MAX().
            Type of range for the key part for this field will be
            returned in range_fl.
293 294 295 296 297 298 299 300
          */
          if ((outer_tables & table->map) ||
	          !find_key_for_maxmin(1, &ref, item_field->field, conds,
				                   &range_fl, &prefix_len))
          {
            const_result= 0;
            break;
          }
301
          error= table->file->ha_index_init((uint) ref.key, 1);
302 303

          if (!ref.key_length)
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
304
            error= table->file->index_last(table->record[0]);
305
          else
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
306 307 308 309 310
	    error= table->file->index_read(table->record[0], key_buff,
					   ref.key_length,
					   range_fl & NEAR_MAX ?
					   HA_READ_BEFORE_KEY :
					   HA_READ_PREFIX_LAST_OR_PREV);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
311 312
	  if (!error && reckey_in_range(1, &ref, item_field->field, 
			                conds, range_fl, prefix_len))
313
	    error= HA_ERR_KEY_NOT_FOUND;
314 315 316 317 318
          if (table->key_read)
          {
            table->key_read=0;
            table->file->extra(HA_EXTRA_NO_KEYREAD);
          }
319
          table->file->ha_index_end();
320
          if (error)
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
321
          {
322
	    if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE)
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
323 324 325 326
	      return -1;		       // No rows matching WHERE
	    /* HA_ERR_LOCK_DEADLOCK or some other error */
 	    table->file->print_error(error, MYF(0));
            return(error);
327
	  }
328 329
          removed_tables|= table->map;
        }
330
        else if (!expr->const_item() || !is_exact_count)
331
        {
332 333 334 335 336 337 338 339 340
          /*
            The optimization is not applicable in both cases:
            (a) 'expr' is a non-constant expression. Then we can't
            replace 'expr' by a constant.
            (b) 'expr' is a costant. According to ANSI, MIN/MAX must return
            NULL if the query does not return any rows. Thus, if we are not
            able to determine if the query returns any rows, we can't apply
            the optimization and replace MIN/MAX with a constant.
          */
341 342 343
          const_result= 0;
          break;
        }
344 345 346 347 348 349 350 351
        if (!count)
        {
          /* If count != 1, then we know that is_exact_count == TRUE. */
          ((Item_sum_max*) item_sum)->clear(); /* Set to NULL. */
        }
        else
          ((Item_sum_max*) item_sum)->reset(); /* Set to the constant value. */
        ((Item_sum_max*) item_sum)->make_const();
352 353
        recalc_const_item= 1;
        break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
354 355
      }
      default:
356 357
        const_result= 0;
        break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
358 359 360 361 362
      }
    }
    else if (const_result)
    {
      if (recalc_const_item)
363
        item->update_used_tables();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
364
      if (!item->const_item())
365
        const_result= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
366 367
    }
  }
368 369 370
  /*
    If we have a where clause, we can only ignore searching in the
    tables if MIN/MAX optimisation replaced all used tables
371
    We do not use replaced values in case of:
372 373 374 375
    SELECT MIN(key) FROM table_1, empty_table
    removed_tables is != 0 if we have used MIN() or MAX().
  */
  if (removed_tables && used_tables != removed_tables)
376
    const_result= 0;                            // We didn't remove all tables
bk@work.mysql.com's avatar
bk@work.mysql.com committed
377 378 379 380
  return const_result;
}


381 382
/*
  Test if the predicate compares a field with constants
bk@work.mysql.com's avatar
bk@work.mysql.com committed
383

384 385
  SYNOPSIS
    simple_pred()
386
    func_item        Predicate item
387
    args        out: Here we store the field followed by constants
388 389
    inv_order   out: Is set to 1 if the predicate is of the form
	             'const op field' 
390 391

  RETURN
392 393
    0        func_item is a simple predicate: a field is compared with
             constants
394 395 396
    1        Otherwise
*/

397
bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
398 399 400 401
{
  Item *item;
  *inv_order= 0;
  switch (func_item->argument_count()) {
402 403 404 405 406 407 408 409 410 411 412 413
  case 0:
    /* MULT_EQUAL_FUNC */
    {
      Item_equal *item_equal= (Item_equal *) func_item;
      Item_equal_iterator it(*item_equal);
      args[0]= it++;
      if (it++)
        return 0;
      if (!(args[1]= item_equal->get_const()))
        return 0;
    }
    break;
414 415 416 417 418 419 420 421 422 423 424
  case 1:
    /* field IS NULL */
    item= func_item->arguments()[0];
    if (item->type() != Item::FIELD_ITEM)
      return 0;
    args[0]= item;
    break;
  case 2:
    /* 'field op const' or 'const op field' */
    item= func_item->arguments()[0];
    if (item->type() == Item::FIELD_ITEM)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
425
    {
426 427 428 429 430
      args[0]= item;
      item= func_item->arguments()[1];
      if (!item->const_item())
        return 0;
      args[1]= item;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
431
    }
432
    else if (item->const_item())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
433
    {
434 435 436 437 438 439
      args[1]= item;
      item= func_item->arguments()[1];
      if (item->type() != Item::FIELD_ITEM)
        return 0;
      args[0]= item;
      *inv_order= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
440
    }
441 442 443 444 445 446 447
    else
      return 0;
    break;
  case 3:
    /* field BETWEEN const AND const */
    item= func_item->arguments()[0];
    if (item->type() == Item::FIELD_ITEM)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
448
    {
449 450 451 452 453 454 455 456
      args[0]= item;
      for (int i= 1 ; i <= 2; i++)
      {
        item= func_item->arguments()[i];
        if (!item->const_item())
          return 0;
        args[i]= item;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
457
    }
458 459
    else
      return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
460
  }
461
  return 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
462 463 464
}


465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
/* 
   Check whether a condition matches a key to get {MAX|MIN}(field):

   SYNOPSIS
     matching_cond()
     max_fl         in:     Set to 1 if we are optimising MAX()              
     ref            in/out: Reference to the structure we store the key value
     keyinfo        in      Reference to the key info
     field_part     in:     Pointer to the key part for the field
     cond           in      WHERE condition
     key_part_used  in/out: Map of matchings parts
     range_fl       in/out: Says whether including key will be used
     prefix_len     out:    Length of common key part for the range
                            where MAX/MIN is searched for

   DESCRIPTION
     For the index specified by the keyinfo parameter, index that
     contains field as its component (field_part), the function
     checks whether the condition cond is a conjunction and all its
     conjuncts referring to the columns of the same table as column
     field are one of the following forms:
     - f_i= const_i or const_i= f_i or f_i is null,
       where f_i is part of the index
     - field {<|<=|>=|>|=} const or const {<|<=|>=|>|=} field
     - field between const1 and const2

  RETURN
    0        Index can't be used.
    1        We can use index to get MIN/MAX value
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
495

496 497 498 499
static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, 
                          KEY_PART_INFO *field_part, COND *cond,
                          key_part_map *key_part_used, uint *range_fl,
                          uint *prefix_len)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
500
{
501 502 503 504 505 506 507 508
  if (!cond)
    return 1;
  Field *field= field_part->field;
  if (!(cond->used_tables() & field->table->map))
  {
    /* Condition doesn't restrict the used table */
    return 1;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
509 510 511
  if (cond->type() == Item::COND_ITEM)
  {
    if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC)
512
      return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
513

514
    /* AND */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
515
    List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
516
    Item *item;
517
    while ((item= li++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
518
    {
519 520 521
      if (!matching_cond(max_fl, ref, keyinfo, field_part, item,
                         key_part_used, range_fl, prefix_len))
        return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
522
    }
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
    return 1;
  }

  if (cond->type() != Item::FUNC_ITEM)
    return 0;                                 // Not operator, can't optimize

  bool eq_type= 0;                            // =, <=> or IS NULL
  bool noeq_type= 0;                          // < or >  
  bool less_fl= 0;                            // < or <= 
  bool is_null= 0;
  bool between= 0;

  switch (((Item_func*) cond)->functype()) {
  case Item_func::ISNULL_FUNC:
    is_null= 1;     /* fall through */
  case Item_func::EQ_FUNC:
  case Item_func::EQUAL_FUNC:
    eq_type= 1;
    break;
  case Item_func::LT_FUNC:
    noeq_type= 1;   /* fall through */
  case Item_func::LE_FUNC:
    less_fl= 1;      
    break;
  case Item_func::GT_FUNC:
    noeq_type= 1;   /* fall through */
  case Item_func::GE_FUNC:
    break;
  case Item_func::BETWEEN:
    between= 1;
    break;
554 555 556
  case Item_func::MULT_EQUAL_FUNC:
    eq_type= 1;
    break;
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
  default:
    return 0;                                        // Can't optimize function
  }
  
  Item *args[3];
  bool inv;

  /* Test if this is a comparison of a field and constant */
  if (!simple_pred((Item_func*) cond, args, &inv))
    return 0;

  if (inv && !eq_type)
    less_fl= 1-less_fl;                         // Convert '<' -> '>' (etc)

  /* Check if field is part of the tested partial key */
  byte *key_ptr= ref->key_buff;
  KEY_PART_INFO *part;
  for (part= keyinfo->key_part;
       ;
       key_ptr+= part++->store_length)

  {
    if (part > field_part)
      return 0;                     // Field is beyond the tested parts
    if (part->field->eq(((Item_field*) args[0])->field))
      break;                        // Found a part od the key for the field
  }

  bool is_field_part= part == field_part;
  if (!(is_field_part || eq_type))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
587
    return 0;
588 589 590 591 592 593 594 595 596 597 598 599 600 601

  key_part_map org_key_part_used= *key_part_used;
  if (eq_type || between || max_fl == less_fl)
  {
    uint length= (key_ptr-ref->key_buff)+part->store_length;
    if (ref->key_length < length)
    /* Ultimately ref->key_length will contain the length of the search key */
      ref->key_length= length;      
    if (!*prefix_len && part+1 == field_part)       
      *prefix_len= length;
    if (is_field_part && eq_type)
      *prefix_len= ref->key_length;
  
    *key_part_used|= (key_part_map) 1 << (part - keyinfo->key_part);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
602
  }
603 604 605

  if (org_key_part_used != *key_part_used ||
      (is_field_part && 
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
606
       (between || eq_type || max_fl == less_fl) && !cond->val_int()))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
607
  {
608 609 610 611
    /*
      It's the first predicate for this part or a predicate of the
      following form  that moves upper/lower bounds for max/min values:
      - field BETWEEN const AND const
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
612
      - field = const 
613 614 615 616 617
      - field {<|<=} const, when searching for MAX
      - field {>|>=} const, when searching for MIN
    */

    if (is_null)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
618
    {
619 620
      part->field->set_null();
      *key_ptr= (byte) 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
621
    }
622
    else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
623
    {
624 625 626
      store_val_in_field(part->field, args[between && max_fl ? 2 : 1]);
      if (part->null_bit) 
        *key_ptr++= (byte) test(part->field->is_null());
627
      part->field->get_key_image((char*) key_ptr, part->length, Field::itRAW);
628 629 630
    }
    if (is_field_part)
    {
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
631
      if (between || eq_type)
632 633 634 635 636 637 638 639 640
        *range_fl&= ~(NO_MAX_RANGE | NO_MIN_RANGE);
      else
      {
        *range_fl&= ~(max_fl ? NO_MAX_RANGE : NO_MIN_RANGE);
        if (noeq_type)
          *range_fl|=  (max_fl ? NEAR_MAX : NEAR_MIN);
        else
          *range_fl&= ~(max_fl ? NEAR_MAX : NEAR_MIN);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
641 642
    }
  }
643 644 645 646 647 648 649 650 651
  else if (eq_type)
  {
    if (!is_null && !cond->val_int() ||
        is_null && !test(part->field->is_null()))  
     return 0;                       // Impossible test
  }
  else if (is_field_part)
    *range_fl&= ~(max_fl ? NO_MIN_RANGE : NO_MAX_RANGE);
  return 1;  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
652 653 654
}


655 656
/*
  Check whether we can get value for {max|min}(field) by using a key.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
657

658 659 660 661 662 663
  SYNOPSIS
    find_key_for_maxmin()
    max_fl      in:     0 for MIN(field) / 1 for MAX(field)
    ref         in/out  Reference to the structure we store the key value
    field       in:     Field used inside MIN() / MAX()
    cond        in:     WHERE condition
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
664
    range_fl    out:    Bit flags for how to search if key is ok
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
    prefix_len  out:    Length of prefix for the search range

  DESCRIPTION
    If where condition is not a conjunction of 0 or more conjuct the
    function returns false, otherwise it checks whether there is an
    index including field as its k-th component/part such that:

     1. for each previous component f_i there is one and only one conjunct
        of the form: f_i= const_i or const_i= f_i or f_i is null
     2. references to field occur only in conjucts of the form:
        field {<|<=|>=|>|=} const or const {<|<=|>=|>|=} field or 
        field BETWEEN const1 AND const2
     3. all references to the columns from the same table as column field
        occur only in conjucts mentioned above.

     If such an index exists the function through the ref parameter
     returns the key value to find max/min for the field using the index,
     the length of first (k-1) components of the key and flags saying
     how to apply the key for the search max/min value.
     (if we have a condition field = const, prefix_len contains the length
      of the whole search key)

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
687
  NOTE
688 689
   This function may set table->key_read to 1, which must be reset after
   index is used! (This can only happen when function returns 1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
690

691 692 693 694 695 696 697 698 699
  RETURN
    0   Index can not be used to optimize MIN(field)/MAX(field)
    1   Can use key to optimize MIN()/MAX()
        In this case ref, range_fl and prefix_len are updated
*/ 
      
static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
                                Field* field, COND *cond,
                                uint *range_fl, uint *prefix_len)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
700 701
{
  if (!(field->flags & PART_KEY_FLAG))
702
    return 0;                                        // Not key field
703

704 705
  TABLE *table= field->table;
  uint idx= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
706

707
  KEY *keyinfo,*keyinfo_end;
708
  for (keyinfo= table->key_info, keyinfo_end= keyinfo+table->s->keys ;
709 710
       keyinfo != keyinfo_end;
       keyinfo++,idx++)
711
  {
712 713
    KEY_PART_INFO *part,*part_end;
    key_part_map key_part_to_use= 0;
714
    uint jdx= 0;
igor@linux.local's avatar
igor@linux.local committed
715
    *prefix_len= 0;
716 717
    for (part= keyinfo->key_part, part_end= part+keyinfo->key_parts ;
         part != part_end ;
718
         part++, jdx++, key_part_to_use= (key_part_to_use << 1) | 1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
719
    {
720
      if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER))
721 722
        return 0;

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
      if (field->eq(part->field))
      {
        ref->key= idx;
        ref->key_length= 0;
        key_part_map key_part_used= 0;
        *range_fl= NO_MIN_RANGE | NO_MAX_RANGE;
        if (matching_cond(max_fl, ref, keyinfo, part, cond,
                          &key_part_used, range_fl, prefix_len) &&
            !(key_part_to_use & ~key_part_used))
        {
          if (!max_fl && key_part_used == key_part_to_use && part->null_bit)
          {
            /*
              SELECT MIN(key_part2) FROM t1 WHERE key_part1=const
              If key_part2 may be NULL, then we want to find the first row
              that is not null
            */
740 741
            ref->key_buff[ref->key_length]= 1;
            ref->key_length+= part->store_length;
742 743 744 745 746 747 748
            *range_fl&= ~NO_MIN_RANGE;
            *range_fl|= NEAR_MIN;                // > NULL
          }
          /*
            The following test is false when the key in the key tree is
            converted (for example to upper case)
          */
749
          if (field->part_of_key.is_set(idx))
750 751 752 753 754 755 756
          {
            table->key_read= 1;
            table->file->extra(HA_EXTRA_KEYREAD);
          }
          return 1;
        }
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
757 758
    }
  }
759 760 761
  return 0;
}

762

763 764 765 766 767 768 769 770 771 772
/*
  Check whether found key is in range specified by conditions

  SYNOPSIS
    reckey_in_range()
    max_fl      in:     0 for MIN(field) / 1 for MAX(field)
    ref         in:     Reference to the key value and info
    field       in:     Field used the MIN/MAX expression
    cond        in:     WHERE condition
    range_fl    in:     Says whether there is a condition to to be checked
773
    prefix_len  in:     Length of the constant part of the key
774 775 776 777 778 779 780 781 782

  RETURN
    0        ok
    1        WHERE was not true for the found row
*/

static int reckey_in_range(bool max_fl, TABLE_REF *ref, Field* field,
                            COND *cond, uint range_fl, uint prefix_len)
{
783
  if (key_cmp_if_same(field->table, ref->key_buff, ref->key, prefix_len))
784 785
    return 1;
  if (!cond || (range_fl & (max_fl ? NO_MIN_RANGE : NO_MAX_RANGE)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
786
    return 0;
787 788
  return maxmin_in_range(max_fl, field, cond);
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
789

790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807

/*
  Check whether {MAX|MIN}(field) is in range specified by conditions
  SYNOPSIS
    maxmin_in_range()
    max_fl      in:     0 for MIN(field) / 1 for MAX(field)
    field       in:     Field used the MIN/MAX expression
    cond        in:     WHERE condition

  RETURN
    0        ok
    1        WHERE was not true for the found row
*/

static int maxmin_in_range(bool max_fl, Field* field, COND *cond)
{
  /* If AND/OR condition */
  if (cond->type() == Item::COND_ITEM)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
808
  {
809 810 811
    List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list());
    Item *item;
    while ((item= li++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
812
    {
813 814
      if (maxmin_in_range(max_fl, field, item))
        return 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
815
    }
816
    return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
817
  }
818 819 820 821 822 823 824 825 826 827 828 829 830

  if (cond->used_tables() != field->table->map)
    return 0;
  bool less_fl= 0;
  switch (((Item_func*) cond)->functype()) {
  case Item_func::BETWEEN:
    return cond->val_int() == 0;                // Return 1 if WHERE is false
  case Item_func::LT_FUNC:
  case Item_func::LE_FUNC:
    less_fl= 1;
  case Item_func::GT_FUNC:
  case Item_func::GE_FUNC:
  {
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
831 832 833 834 835 836 837 838 839 840 841
    Item *item= ((Item_func*) cond)->arguments()[1];
    /* In case of 'const op item' we have to swap the operator */
    if (!item->const_item())
      less_fl= 1-less_fl;
    /*
      We only have to check the expression if we are using an expression like
      SELECT MAX(b) FROM t1 WHERE a=const AND b>const
      not for
      SELECT MAX(b) FROM t1 WHERE a=const AND b<const
    */
    if (max_fl != less_fl)
842 843 844 845 846 847 848 849 850 851 852
      return cond->val_int() == 0;                // Return 1 if WHERE is false
    return 0;
  }
  case Item_func::EQ_FUNC:
  case Item_func::EQUAL_FUNC:
    break;
  default:                                        // Keep compiler happy
    DBUG_ASSERT(1);                               // Impossible
    break;
  }
  return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
853
}
854