List:Commits« Previous MessageNext Message »
From:Jorgen Loland Date:April 1 2011 12:43pm
Subject:bzr commit into mysql-trunk branch (jorgen.loland:3351) Bug#11882131
View as plain text  
#At file:///export/home/jl208045/mysql/mysql-trunk-11882131/ based on revid:mattias.jonsson@stripped

 3351 Jorgen Loland	2011-04-01
      BUG#11882131: OPTIMIZER CHOOSES FILESORT WHEN REVERSE 
                    INDEX SCAN COULD BE USED
      
      Consider the following case:
      
      CREATE TABLE t1 (a INT,KEY (a));
      INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
      SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC;
      
      This query could have been resolved by GROUP range access
      if it hadn't been for the descending ordering [1]. 
      
      To access this table, covering index scan is first chosen. 
      Later an attempt to avoid sorting is made by calling 
      test_if_skip_sort_order(). Range analysis now decides that
      GROUP range access is the most efficient access method, but
      since this access method cannot produce records in 
      descending order, it is scrapped by 
      test_if_skip_sort_order() before concluding that filesort 
      is required after all. 
      
      In this case, test_if_skip_sort_order() fails to check if 
      the descending ordering can be resolved by scanning the 
      covering index in reverse order instead. Because of this, 
      the resulting execution plan is to 1) scan the index and 2) 
      sort the result instead of simply do 1) scan the index in 
      reverse order.
      
      This patch adds an interesting_order parameter to 
      test_quick_select(). This parameter ensures that only range 
      access plans that can produce rows in requested order are 
      considered.
      
      The gains from this change include:
       1) Optimizer will not spend time to calculate whether or not
          an unusable range access plan is cheap.
       2) Before, if two range access plans P1 and P2 were considered, 
          and P1 could produce the requested ordering but P2 could not,
          P2 would still be returned from test_quick_select() if it was
          cheaper than P1. test_if_skip_sort_order() would then discard
          the range access plan as not usable. With this patch, P2 will
          not be considered, so test_quick_select() will instead return
          the best *usable* plan P1.
       3) Due to #2, the aforementioned deficiency in 
          test_if_skip_sort_order() is no longer an issue: If 
          test_quick_select() returns a range access plan, that plan 
          will be able to resolve the requested ordering.
     @ mysql-test/r/order_by_icp_mrr.result
        BUG#11882131: Changed test output
     @ mysql-test/r/order_by_none.result
        BUG#11882131: Changed test output
     @ sql/item_sum.cc
        Struct ORDER variable "bool asc" replaced with "enum_order order"
     @ sql/opt_range.cc
        Add parameter "interesting_order" to test_quick_select() and make the method only consider range access plans that are able to produce rows in requested order. Also add variable PARAM::order, defining whether the range access plan needs to produce ASC/DESC ordering (or no order)
     @ sql/opt_range.h
        Add method QUICK_SELECT_I::reverse_sort_possible().
     @ sql/sql_lex.cc
        Struct ORDER variable "bool asc" replaced with "enum_order order"
     @ sql/sql_parse.cc
        Struct ORDER variable "bool asc" replaced with "enum_order order"
     @ sql/sql_select.cc
        When calling test_quick_select(): define whether ascending/descending ordering will be required.
     @ sql/sql_update.cc
        Struct ORDER variable "bool asc" replaced with "enum_order order"
     @ sql/table.h
        Struct ORDER variable "bool asc" replaced with "enum_order order"

    modified:
      mysql-test/r/order_by_icp_mrr.result
      mysql-test/r/order_by_none.result
      sql/item_sum.cc
      sql/opt_range.cc
      sql/opt_range.h
      sql/sql_lex.cc
      sql/sql_parse.cc
      sql/sql_select.cc
      sql/sql_update.cc
      sql/table.h
=== modified file 'mysql-test/r/order_by_icp_mrr.result'
--- a/mysql-test/r/order_by_icp_mrr.result	2011-02-07 09:46:53 +0000
+++ b/mysql-test/r/order_by_icp_mrr.result	2011-04-01 12:43:09 +0000
@@ -2536,7 +2536,7 @@ CREATE TABLE t1 (a INT,KEY (a));
 INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
 EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	a	a	5	NULL	10	Using where; Using index; Using filesort
+1	SIMPLE	t1	index	a	a	5	NULL	10	Using where; Using index
 SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC;
 a	1
 10	1

=== modified file 'mysql-test/r/order_by_none.result'
--- a/mysql-test/r/order_by_none.result	2011-02-07 09:46:53 +0000
+++ b/mysql-test/r/order_by_none.result	2011-04-01 12:43:09 +0000
@@ -2535,7 +2535,7 @@ CREATE TABLE t1 (a INT,KEY (a));
 INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
 EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	a	a	5	NULL	10	Using where; Using index; Using filesort
+1	SIMPLE	t1	index	a	a	5	NULL	10	Using where; Using index
 SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC;
 a	1
 10	1

=== modified file 'sql/item_sum.cc'
--- a/sql/item_sum.cc	2011-03-22 11:44:40 +0000
+++ b/sql/item_sum.cc	2011-04-01 12:43:09 +0000
@@ -2891,7 +2891,8 @@ int group_concat_key_cmp_with_order(cons
       uint offset= (field->offset(field->table->record[0]) -
                     table->s->null_bytes);
       if ((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset)))
-        return (*order_item)->asc ? res : -res;
+        return ((*order_item)->direction == ORDER::ORDER_ASC) ? res : -res;
+          
     }
   }
   /*
@@ -3432,7 +3433,7 @@ void Item_func_group_concat::print(Strin
       if (i)
         str->append(',');
       orig_args[i + arg_count_field]->print(str, query_type);
-      if (order[i]->asc)
+      if (order[i]->direction == ORDER::ORDER_ASC)
         str->append(STRING_WITH_LEN(" ASC"));
       else
         str->append(STRING_WITH_LEN(" DESC"));

=== modified file 'sql/opt_range.cc'
--- a/sql/opt_range.cc	2011-03-22 11:44:40 +0000
+++ b/sql/opt_range.cc	2011-04-01 12:43:09 +0000
@@ -742,6 +742,10 @@ public:
   bool is_ror_scan;
   /* Number of ranges in the last checked tree->key */
   uint n_ranges;
+
+  /* Order direction 
+*/
+  ORDER::enum_order order_direction;
 };
 
 class TABLE_READ_PLAN;
@@ -2142,6 +2146,8 @@ static int fill_used_fields_bitmap(PARAM
       limit             Query limit
       force_quick_range Prefer to use range (instead of full table scan) even
                         if it is more expensive.
+      interesting_order The sort order the range access method must be able
+                        to provide. Three-value logic: asc/desc/don't care
 
   NOTES
     Updates the following in the select parameter:
@@ -2197,9 +2203,9 @@ static int fill_used_fields_bitmap(PARAM
 */
 
 int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
-				  table_map prev_tables,
-				  ha_rows limit, bool force_quick_range, 
-                                  bool ordered_output)
+                                  table_map prev_tables,
+                                  ha_rows limit, bool force_quick_range, 
+                                  const ORDER::enum_order interesting_order)
 {
   uint idx;
   double scan_time;
@@ -2260,7 +2266,8 @@ int SQL_SELECT::test_quick_select(THD *t
     param.imerge_cost_buff_size= 0;
     param.using_real_indexes= TRUE;
     param.remove_jump_scans= TRUE;
-    param.force_default_mrr= ordered_output;
+    param.force_default_mrr= (interesting_order != ORDER::ORDER_NOT_RELEVANT);
+    param.order_direction= interesting_order;
 
     thd->no_errors=1;				// Don't warn about NULL
     init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
@@ -2386,10 +2393,12 @@ int SQL_SELECT::test_quick_select(THD *t
         /*
           Simultaneous key scans and row deletes on several handler
           objects are not allowed so don't use ROR-intersection for
-          table deletes.
+          table deletes. Also, ROR-intersection cannot return rows in
+          descending order
         */
         if ((thd->lex->sql_command != SQLCOM_DELETE) && 
-             thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE))
+            thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE) &&
+            interesting_order != ORDER::ORDER_DESC)
         {
           /*
             Get best non-covering ROR-intersection plan and prepare data for
@@ -2413,7 +2422,9 @@ int SQL_SELECT::test_quick_select(THD *t
       }
       else
       {
-        if (thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE))
+        // Cannot return rows in descending order.
+        if (thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE) &&
+            interesting_order != ORDER::ORDER_DESC)
         {
           /* Try creating index_merge/ROR-union scan. */
           SEL_IMERGE *imerge;
@@ -4576,6 +4587,9 @@ TRP_ROR_INTERSECT *get_best_ror_intersec
       !param->thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT))
     DBUG_RETURN(NULL);
 
+  if (param->order_direction == ORDER::ORDER_DESC)
+    DBUG_RETURN(NULL);
+
   /*
     Step1: Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of 
     them. Also find and save clustered PK scan if there is one.
@@ -9653,6 +9667,9 @@ get_best_group_min_max(PARAM *param, SEL
     DBUG_RETURN(NULL);
   if (table->s->keys == 0)        /* There are no indexes to use. */
     DBUG_RETURN(NULL);
+  /* Cannot do reverse ordering */
+  if (param->order_direction == ORDER::ORDER_DESC)
+    DBUG_RETURN(NULL);
 
   /* Check (SA1,SA4) and store the only MIN/MAX argument - the C attribute.*/
   if (join->make_sum_func_list(join->all_fields, join->fields_list, 1))

=== modified file 'sql/opt_range.h'
--- a/sql/opt_range.h	2011-03-22 11:44:40 +0000
+++ b/sql/opt_range.h	2011-04-01 12:43:09 +0000
@@ -276,7 +276,15 @@ public:
   /* Range end should be called when we have looped over the whole index */
   virtual void range_end() {}
 
+  /** 
+    Whether the range access method returns records in reverse order.
+  */
   virtual bool reverse_sorted() = 0;
+  /** 
+    Whether the range access method is capable of returning records 
+    in reverse order.
+  */
+  virtual bool reverse_sort_possible() const = 0;
   virtual bool unique_key_range() { return false; }
   virtual bool clustered_pk_range() { return false; }
   
@@ -472,7 +480,8 @@ public:
   void range_end();
   int get_next_prefix(uint prefix_length, uint group_key_parts, 
                       uchar *cur_prefix);
-  bool reverse_sorted() { return 0; }
+  bool reverse_sorted() { return false; }
+  bool reverse_sort_possible() const { return true; }
   bool unique_key_range();
   int init_ror_merged_scan(bool reuse_handler);
   void save_last_pos()
@@ -573,6 +582,7 @@ public:
   int  reset(void);
   int  get_next();
   bool reverse_sorted() { return false; }
+  bool reverse_sort_possible() const { return false; }
   bool unique_key_range() { return false; }
   int get_type() { return QS_TYPE_INDEX_MERGE; }
   void add_keys_and_lengths(String *key_names, String *used_lengths);
@@ -651,6 +661,7 @@ public:
   int  reset(void);
   int  get_next();
   bool reverse_sorted() { return false; }
+  bool reverse_sort_possible() const { return false; }
   bool unique_key_range() { return false; }
   int get_type() { return QS_TYPE_ROR_INTERSECT; }
   void add_keys_and_lengths(String *key_names, String *used_lengths);
@@ -722,6 +733,7 @@ public:
   int  reset(void);
   int  get_next();
   bool reverse_sorted() { return false; }
+  bool reverse_sort_possible() const { return false; }
   bool unique_key_range() { return false; }
   int get_type() { return QS_TYPE_ROR_UNION; }
   void add_keys_and_lengths(String *key_names, String *used_lengths);
@@ -864,6 +876,7 @@ public:
   int reset();
   int get_next();
   bool reverse_sorted() { return false; }
+  bool reverse_sort_possible() const { return false; }
   bool unique_key_range() { return false; }
   int get_type() { return QS_TYPE_GROUP_MIN_MAX; }
   void add_keys_and_lengths(String *key_names, String *used_lengths);
@@ -885,7 +898,8 @@ public:
   QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts, 
                     bool *create_err);
   int get_next();
-  bool reverse_sorted() { return 1; }
+  bool reverse_sorted() { return true; }
+  bool reverse_sort_possible() const { return true; }
   int get_type() { return QS_TYPE_RANGE_DESC; }
   QUICK_SELECT_I *make_reverse(uint used_key_parts_arg)
   {
@@ -921,7 +935,8 @@ class SQL_SELECT :public Sql_alloc {
   bool check_quick(THD *thd, bool force_quick_range, ha_rows limit)
   {
     key_map tmp(key_map::ALL_BITS);
-    return test_quick_select(thd, tmp, 0, limit, force_quick_range, FALSE) < 0;
+    return test_quick_select(thd, tmp, 0, limit, force_quick_range,
+                             ORDER::ORDER_NOT_RELEVANT) < 0;
   }
   inline bool skip_record(THD *thd, bool *skip_record)
   {
@@ -929,8 +944,8 @@ class SQL_SELECT :public Sql_alloc {
     return thd->is_error();
   }
   int test_quick_select(THD *thd, key_map keys, table_map prev_tables,
-			ha_rows limit, bool force_quick_range, 
-                        bool ordered_output);
+                        ha_rows limit, bool force_quick_range,
+                        const ORDER::enum_order interesting_order);
 };
 
 

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2011-03-11 09:35:38 +0000
+++ b/sql/sql_lex.cc	2011-04-01 12:43:09 +0000
@@ -2196,7 +2196,7 @@ void st_select_lex::print_order(String *
     }
     else
       (*order->item)->print(str, query_type);
-    if (!order->asc)
+    if (order->direction == ORDER::ORDER_DESC)
       str->append(STRING_WITH_LEN(" desc"));
     if (order->next)
       str->append(',');

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2011-03-17 17:39:31 +0000
+++ b/sql/sql_parse.cc	2011-04-01 12:43:09 +0000
@@ -5796,7 +5796,7 @@ bool add_to_list(THD *thd, SQL_I_List<OR
     DBUG_RETURN(1);
   order->item_ptr= item;
   order->item= &order->item_ptr;
-  order->asc = asc;
+  order->direction= (asc ? ORDER::ORDER_ASC : ORDER::ORDER_DESC);
   order->free_me=0;
   order->used=0;
   order->counter_used= 0;

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2011-03-29 07:20:17 +0000
+++ b/sql/sql_select.cc	2011-04-01 12:43:09 +0000
@@ -4516,7 +4516,6 @@ static ha_rows get_quick_record_count(TH
 				      TABLE *table,
 				      const key_map *keys,ha_rows limit)
 {
-  int error;
   DBUG_ENTER("get_quick_record_count");
   uchar buff[STACK_BUFF_ALLOC];
   if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
@@ -4524,8 +4523,13 @@ static ha_rows get_quick_record_count(TH
   if (select)
   {
     select->head=table;
-    if ((error= select->test_quick_select(thd, *(key_map *)keys,(table_map) 0,
-                                          limit, 0, FALSE)) == 1)
+    int error= select->test_quick_select(thd, 
+                                         *keys, 
+                                         0,      //empty table_map
+                                         limit, 
+                                         false,  //don't force quick range
+                                         ORDER::ORDER_NOT_RELEVANT);
+    if (error == 1)
       DBUG_RETURN(select->quick->records);
     if (error == -1)
     {
@@ -9895,13 +9899,14 @@ static bool make_join_select(JOIN *join,
 	    if (sel->cond && !sel->cond->fixed)
 	      sel->cond->quick_fix_field();
 
-	    if (sel->test_quick_select(thd, tab->keys,
-				       used_tables & ~ current_map,
-				       (join->select_options &
-					OPTION_FOUND_ROWS ?
-					HA_POS_ERROR :
-					join->unit->select_limit_cnt), 0,
-                                        FALSE) < 0)
+            if (sel->test_quick_select(thd, tab->keys,
+                                       used_tables & ~ current_map,
+                                       (join->select_options &
+                                        OPTION_FOUND_ROWS ?
+                                        HA_POS_ERROR :
+                                        join->unit->select_limit_cnt),
+                                       false,   // don't force quick range
+                                       ORDER::ORDER_NOT_RELEVANT) < 0)
             {
 	      /*
 		Before reporting "Impossible WHERE" for the whole query
@@ -9914,8 +9919,9 @@ static bool make_join_select(JOIN *join,
                                          (join->select_options &
                                           OPTION_FOUND_ROWS ?
                                           HA_POS_ERROR :
-                                          join->unit->select_limit_cnt),0,
-                                          FALSE) < 0)
+                                          join->unit->select_limit_cnt),
+                                         false,   //don't force quick range
+                                         ORDER::ORDER_NOT_RELEVANT) < 0)
 		DBUG_RETURN(1);			// Impossible WHERE
             }
             else
@@ -18424,9 +18430,12 @@ test_if_quick_select(JOIN_TAB *tab)
 {
   delete tab->select->quick;
   tab->select->quick=0;
-  return tab->select->test_quick_select(tab->join->thd, tab->keys,
-					(table_map) 0, HA_POS_ERROR, 0,
-                                        FALSE);
+  return tab->select->test_quick_select(tab->join->thd, 
+                                        tab->keys,
+                                        0,          // empty table map
+                                        HA_POS_ERROR, 
+                                        false,      // don't force quick range
+                                        ORDER::ORDER_NOT_RELEVANT);
 }
 
 
@@ -19651,9 +19660,11 @@ static int test_if_order_by_key(ORDER *o
     if (key_part->field != field || !field->part_of_sortkey.is_set(idx))
       DBUG_RETURN(0);
 
+    const ORDER::enum_order keypart_order= 
+      (key_part->key_part_flag & HA_REVERSE_SORT) ? 
+      ORDER::ORDER_DESC : ORDER::ORDER_ASC;
     /* set flag to 1 if we can use read-next on key, else to -1 */
-    flag= ((order->asc == !(key_part->key_part_flag & HA_REVERSE_SORT)) ?
-           1 : -1);
+    flag= (order->direction == keypart_order) ? 1 : -1;
     if (reverse && flag != reverse)
       DBUG_RETURN(0);
     reverse=flag;				// Remember if reverse
@@ -20110,13 +20121,15 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR
           new_ref_key_map.set_bit(new_ref_key); // only for new_ref_key.
 
           select->quick= 0;
-          if (select->test_quick_select(tab->join->thd, new_ref_key_map, 0,
+          if (select->test_quick_select(tab->join->thd, 
+                                        new_ref_key_map, 
+                                        0,       // empty table_map
                                         (tab->join->select_options &
                                          OPTION_FOUND_ROWS) ?
                                         HA_POS_ERROR :
-                                        tab->join->unit->select_limit_cnt,0,
-                                        TRUE) <=
-              0)
+                                        tab->join->unit->select_limit_cnt,
+                                        false,   // don't force quick range
+                                        order->direction) <= 0)
             goto use_filesort;
 	}
         ref_key= new_ref_key;
@@ -20161,11 +20174,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR
         key_map map;           // Force the creation of quick select
         map.set_bit(best_key); // only best_key.
         select->quick= 0;
-        select->test_quick_select(join->thd, map, 0,
+        select->test_quick_select(join->thd, 
+                                  map, 
+                                  0,        // empty table_map
                                   join->select_options & OPTION_FOUND_ROWS ?
                                   HA_POS_ERROR :
                                   join->unit->select_limit_cnt,
-                                  TRUE, FALSE);
+                                  true,     // force quick range
+                                  order->direction);
       }
       order_direction= best_key_direction;
       /*
@@ -20195,18 +20211,13 @@ check_reverse_order:                  
       */
       if (select->quick->reverse_sorted())
         goto skipped_filesort;
-      else
-      {
-        int quick_type= select->quick->get_type();
-        if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
-            quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
-            quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
-            quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
-        {
-          tab->limit= 0;
-          goto use_filesort;                   // Use filesort
-        }
-      }
+
+      /*
+        test_quick_select() should not create a quick that cannot do
+        reverse ordering
+      */
+      DBUG_ASSERT((select->quick == save_quick) ||
+                  select->quick->reverse_sort_possible());
     }
   }
 
@@ -20316,7 +20327,7 @@ check_reverse_order:                  
   /*
     Cleanup:
     We may have both a 'select->quick' and 'save_quick' (original)
-    at this point. Delete the one that we wan't use.
+    at this point. Delete the one that we won't use.
   */
 
 skipped_filesort:
@@ -20823,7 +20834,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER 
     }
     else
       pos->item= *order->item;
-    pos->reverse=! order->asc;
+    pos->reverse= (order->direction == ORDER::ORDER_DESC);
     DBUG_ASSERT(pos->field != NULL || pos->item != NULL);
   }
   *length=count;
@@ -21336,7 +21347,7 @@ create_distinct_group(THD *thd, Item **r
         */
         ord->item= ref_pointer_array;
       }
-      ord->asc=1;
+      ord->direction= ORDER::ORDER_ASC;
       *prev=ord;
       prev= &ord->next;
     }
@@ -21413,7 +21424,7 @@ test_if_subpart(ORDER *a,ORDER *b)
   for (; a && b; a=a->next,b=b->next)
   {
     if ((*a->item)->eq(*b->item,1))
-      a->asc=b->asc;
+      a->direction= b->direction;
     else
       return 0;
   }

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2011-02-25 16:41:57 +0000
+++ b/sql/sql_update.cc	2011-04-01 12:43:09 +0000
@@ -1732,7 +1732,7 @@ loop_end:
 
     /* Make an unique key over the first field to avoid duplicated updates */
     bzero((char*) &group, sizeof(group));
-    group.asc= 1;
+    group.direction= ORDER::ORDER_ASC;
     group.item= (Item**) temp_fields.head_ref();
 
     tmp_param->quick_group=1;

=== modified file 'sql/table.h'
--- a/sql/table.h	2011-03-30 11:43:32 +0000
+++ b/sql/table.h	2011-04-01 12:43:09 +0000
@@ -194,7 +194,13 @@ typedef struct st_order {
   Item	 *item_ptr;			/* Storage for initial item */
   int    counter;                       /* position in SELECT list, correct
                                            only if counter_used is true*/
-  bool	 asc;				/* true if ascending */
+  enum enum_order {
+    ORDER_NOT_RELEVANT,
+    ORDER_ASC,
+    ORDER_DESC
+  };
+
+  enum_order direction;                 /* Requested direction of ordering */
   bool	 free_me;			/* true if item isn't shared  */
   bool	 in_field_list;			/* true if in select field list */
   bool   counter_used;                  /* parameter was counter of columns */


Attachment: [text/bzr-bundle] bzr/jorgen.loland@oracle.com-20110401124309-6vi7e4095p14x495.bundle
Thread
bzr commit into mysql-trunk branch (jorgen.loland:3351) Bug#11882131Jorgen Loland1 Apr
  • Re: bzr commit into mysql-trunk branch (jorgen.loland:3351) Bug#11882131Tor Didriksen1 Apr
    • Re: bzr commit into mysql-trunk branch (jorgen.loland:3351) Bug#11882131Jorgen Loland1 Apr
    • Re: bzr commit into mysql-trunk branch (jorgen.loland:3351) Bug#11882131Jorgen Loland6 Apr