List:Internals« Previous MessageNext Message »
From:timour Date:December 14 2005 9:48am
Subject:bk commit into 5.1 tree (timour:1.1956)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of timka. When timka does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1956 05/12/14 11:48:34 timour@stripped[timka] +6 -0
  ms01.01.patch

  sql/sql_select.cc
    1.373 05/12/13 03:21:53 timour@stripped[timka] +52 -37
    Import patch ms01.01.patch

  sql/opt_range.h
    1.58 05/12/13 03:21:53 timour@stripped[timka] +3 -2
    Import patch ms01.01.patch

  sql/opt_range.cc
    1.187 05/12/13 03:21:53 timour@stripped[timka] +659 -36
    Import patch ms01.01.patch

  sql/handler.h
    1.170 05/12/13 03:21:53 timour@stripped[timka] +6 -1
    Import patch ms01.01.patch

  sql/ha_partition.h
    1.7 05/12/13 03:21:53 timour@stripped[timka] +1 -0
    Import patch ms01.01.patch

  sql/ha_ndbcluster.h
    1.101 05/12/13 03:21:53 timour@stripped[timka] +1 -0
    Import patch ms01.01.patch

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	timour
# Host:	lamia.home
# Root:	/home/timka/mysql/src/5.1-2985

--- 1.169/sql/handler.h	2005-11-28 21:07:10 +02:00
+++ 1.170/sql/handler.h	2005-12-13 03:21:53 +02:00
@@ -538,9 +538,12 @@
   get_part_id_func get_partition_id;
   get_part_id_func get_part_partition_id;
   get_subpart_id_func get_subpartition_id;
-
+  
+  /* NULL-terminated list of fields used in [sub] partitioned expression */
   Field **part_field_array;
   Field **subpart_field_array;
+
+  /* ?? */
   Field **full_part_field_array;
 
   Item *part_expr;
@@ -1274,6 +1277,8 @@
   virtual ulong partition_flags(void) const { return 0;}
   virtual int get_default_no_partitions(ulonglong max_rows) { return 1;}
   virtual void set_part_info(partition_info *part_info) { return; }
+  /* Return partitioning info or NULL if the table is not partitioned */
+  virtual partition_info *get_part_info() { return NULL; }
 #endif
   virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0;
   virtual ulong index_ddl_flags(KEY *wanted_index) const

--- 1.186/sql/opt_range.cc	2005-11-28 21:07:12 +02:00
+++ 1.187/sql/opt_range.cc	2005-12-13 03:21:53 +02:00
@@ -286,6 +286,13 @@
     return parent->left == this ? &parent->left : &parent->right;
   }
   SEL_ARG *clone_tree();
+
+  /* Return TRUE if this represents "keypartK = const" or "keypartK IS NULL" */
+  bool is_singlepoint()
+  {
+    return !min_flag && !max_flag && 
+           !field->key_cmp((byte*) min_value, (byte*)max_value);
+  }
 };
 
 class SEL_IMERGE;
@@ -319,25 +326,51 @@
   /* Note that #records for each key scan is stored in table->quick_rows */
 };
 
+class RANGE_OPT_PARAM
+{
+public:
+  THD	*thd;   /* Current thread handle */
+  TABLE *table; /* Table being analyzed */
+  COND *cond;   /* Used inside get_mm_tree(). */
+  table_map prev_tables;
+  table_map read_tables;
+  table_map current_table; /* Bit of the table being analyzed */
+
+  /* Array of parts of all keys for which range analysis is performed */
+  KEY_PART *key_parts;
+  KEY_PART *key_parts_end;
+  MEM_ROOT *mem_root; /* Memory that will be freed when range analysis completes */
+  MEM_ROOT *old_root; /* Memory that will last until the query end */
+  /*
+    Number of indexes used in range analysis (In SEL_TREE::keys only first
+    #keys elements are not empty)
+  */
+  uint keys;
+  
+  /* 
+    If true, the index descriptions describe real indexes (and it is ok to
+    call field->optimize_range(real_keynr[...], ...).
+    Otherwise index description describes fake indexes.
+  */
+  bool optimizing_real_indexes;
+  
+  /*
+    used_key_no -> table_key_no translation table. Only makes sense if
+    optimizing_real_indexes==TRUE
+  */
+  uint real_keynr[MAX_KEY];
+};
 
-typedef struct st_qsel_param {
-  THD	*thd;
-  TABLE *table;
-  KEY_PART *key_parts,*key_parts_end;
+class PARAM : public RANGE_OPT_PARAM
+{
+public:
   KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
-  MEM_ROOT *mem_root, *old_root;
-  table_map prev_tables,read_tables,current_table;
   uint baseflag, max_key_part, range_count;
 
-  uint keys; /* number of keys used in the query */
-
-  /* used_key_no -> table_key_no translation table */
-  uint real_keynr[MAX_KEY];
 
   char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
     max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
   bool quick;				// Don't calulate possible keys
-  COND *cond;
 
   uint fields_bitmap_size;
   MY_BITMAP needed_fields;    /* bitmask of fields needed by the query */
@@ -349,7 +382,7 @@
 
  /* TRUE if last checked tree->key can be used for ROR-scan */
   bool is_ror_scan;
-} PARAM;
+};
 
 class TABLE_READ_PLAN;
   class TRP_RANGE;
@@ -360,13 +393,13 @@
 
 struct st_ror_scan_info;
 
-static SEL_TREE * get_mm_parts(PARAM *param,COND *cond_func,Field *field,
+static SEL_TREE * get_mm_parts(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
 			       Item_func::Functype type,Item *value,
 			       Item_result cmp_type);
-static SEL_ARG *get_mm_leaf(PARAM *param,COND *cond_func,Field *field,
+static SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
 			    KEY_PART *key_part,
 			    Item_func::Functype type,Item *value);
-static SEL_TREE *get_mm_tree(PARAM *param,COND *cond);
+static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond);
 
 static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
 static ha_rows check_quick_select(PARAM *param,uint index,SEL_ARG *key_tree);
@@ -409,8 +442,8 @@
 static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg);
 #endif
 
-static SEL_TREE *tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
-static SEL_TREE *tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
+static SEL_TREE *tree_and(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
+static SEL_TREE *tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
 static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2);
 static SEL_ARG *key_or(SEL_ARG *key1,SEL_ARG *key2);
 static SEL_ARG *key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag);
@@ -423,7 +456,7 @@
 static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
 static bool null_part_in_key(KEY_PART *key_part, const char *key,
                              uint length);
-bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param);
+bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, RANGE_OPT_PARAM* param);
 
 
 /*
@@ -455,9 +488,9 @@
     trees_next(trees),
     trees_end(trees + PREALLOCED_TREES)
   {}
-  int or_sel_tree(PARAM *param, SEL_TREE *tree);
-  int or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree);
-  int or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge);
+  int or_sel_tree(RANGE_OPT_PARAM *param, SEL_TREE *tree);
+  int or_sel_tree_with_checks(RANGE_OPT_PARAM *param, SEL_TREE *new_tree);
+  int or_sel_imerge_with_checks(RANGE_OPT_PARAM *param, SEL_IMERGE* imerge);
 };
 
 
@@ -473,7 +506,7 @@
     -1 - Out of memory.
 */
 
-int SEL_IMERGE::or_sel_tree(PARAM *param, SEL_TREE *tree)
+int SEL_IMERGE::or_sel_tree(RANGE_OPT_PARAM *param, SEL_TREE *tree)
 {
   if (trees_next == trees_end)
   {
@@ -524,7 +557,7 @@
    -1  An error occurred.
 */
 
-int SEL_IMERGE::or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree)
+int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param, SEL_TREE *new_tree)
 {
   for (SEL_TREE** tree = trees;
        tree != trees_next;
@@ -558,7 +591,7 @@
    -1 - An error occurred
 */
 
-int SEL_IMERGE::or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge)
+int SEL_IMERGE::or_sel_imerge_with_checks(RANGE_OPT_PARAM *param, SEL_IMERGE* imerge)
 {
   for (SEL_TREE** tree= imerge->trees;
        tree != imerge->trees_next;
@@ -604,7 +637,7 @@
     other Error, both passed lists are unusable
 */
 
-int imerge_list_or_list(PARAM *param,
+int imerge_list_or_list(RANGE_OPT_PARAM *param,
                         List<SEL_IMERGE> *im1,
                         List<SEL_IMERGE> *im2)
 {
@@ -624,7 +657,7 @@
     other Error
 */
 
-int imerge_list_or_tree(PARAM *param,
+int imerge_list_or_tree(RANGE_OPT_PARAM *param,
                         List<SEL_IMERGE> *im1,
                         SEL_TREE *tree)
 {
@@ -1776,6 +1809,7 @@
     param.old_root= thd->mem_root;
     param.needed_reg= &needed_reg;
     param.imerge_cost_buff_size= 0;
+    param.optimizing_real_indexes= TRUE;
 
     thd->no_errors=1;				// Don't warn about NULL
     init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
@@ -1966,6 +2000,588 @@
   DBUG_RETURN(records ? test(quick) : -1);
 }
 
+/****************************************************************************
+ * Partition pruning starts
+ ****************************************************************************/
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+
+
+/* 
+  Info about partitioning or subpartitioning (direct copies of correspoinding
+  class partition_info members, the point is to have the same code handling
+  both partitions and subpartitions).
+*/
+
+typedef struct st_part_opt_info
+{
+  partition_type part_type;
+  Field **fields;
+  Item *expr;
+} PART_OPT_INFO;
+
+
+/*
+  Partition pruning operation context.
+*/
+typedef struct st_part_prune_param
+{
+  partition_info *part_info; /* Copy of file->get_part_info() */
+
+  RANGE_OPT_PARAM range_param; /* Range optimizer parameters */
+    
+  /*
+    in_part_keyparts[i] == test(keypart #i in partitioning index is a member
+                                used in partitioning)
+    Used for fast check on 'if we have SEL_ARGs for every field'.
+    (psergey-todo: 
+      * MAX_REF_PARTS may be not enough.
+      * think if other representations are better (to be decided once we
+        settle on what to do if same field is used in both partitioning and
+        subpartitioning)
+  */
+  bool in_part_keyparts[MAX_REF_PARTS];
+  
+  /* Same as above for subpartitioning */
+  bool in_subpart_keyparts[MAX_REF_PARTS];
+   
+  /* 
+    Partitioning 'index' description, array of key parts (not NULL-terminated)
+  */
+  KEY_PART *key;
+  
+  /*
+    Number of fields in partitioning index created for partitioning 
+    expr/fields. May be 0.
+  */
+  uint part_fields;
+  uint subpart_fields;
+  
+  /* TRUE <=> PARTITION BY RANGE(unary_asc_func(field)) is used */
+  bool part_type_range_like;
+  
+  /* Copies of info in table->file->get_part_info() */
+  PART_OPT_INFO part_opts[2];
+  
+  /* 
+    All following members are used to store context in find_used_partitions()
+    recursion:
+  */
+  
+  /* "Stack" of SEL_ARGs */
+  SEL_ARG *tuple_parts[MAX_REF_PARTS]; //psergey-todo: this much may be not enough!
+  SEL_ARG **tuple_end; /* Top of the stack */
+  
+  /* 
+    number of partitioninig expr/fields components for which we have a 
+    SEL_ARG* in tuple_parts.
+  */
+  uint cur_part_fields;
+  uint cur_subpart_fields; /* Same as above for subpartitioning */
+  
+} PART_PRUNE_PARAM;
+
+static
+bool create_partition_index_descrition(PART_PRUNE_PARAM *prune_par);
+static 
+int find_used_partitions(PART_PRUNE_PARAM *pprune_par, SEL_ARG *key_tree);
+
+
+/*
+  Save field values from array of SEL_ARG* into table record buffer.
+  SYNOPSIS
+    fill_part_fields()
+      pprune_par  Partition pruning context.
+      start
+      end
+      direction  If 1, start < end,
+*/
+static void fill_part_fields(PART_PRUNE_PARAM *pprune_par, SEL_ARG **start,
+                             SEL_ARG **end, int direction)
+{
+  KEY_PART *parts= pprune_par->range_param.key_parts;
+  for (; start != end; start += direction)
+  {
+    /*
+      Use KEY_PART::length, and not print_length, just like print_key() does.
+    */
+    (*start)->field->set_key_image((char*) ((*start)->min_value),
+                                    parts[(*start)->part].length); 
+  }
+}
+
+/* 
+  Do NULL-aware printout of field value. 
+  NOTE 
+    Later in WL#2985 implementation this will be removed or moved into #ifdef DEBUG
+*/
+void dbug_print_field(Field *field)
+{
+  if (field->is_real_null())
+    fprintf(stderr,"NULL");
+  else
+  {
+    String str;
+    String *pstr;
+    pstr= field->val_str(&str);
+    fprintf(stderr, "'%s'", pstr->c_ptr_safe());
+  }
+}
+
+
+/*
+  Print a partition pruning 'interval'.
+  SYNOPSIS
+    pprune_par  Partition pruning context
+    part        [sub]partitioning description struct (not NULL)
+    next_part   Struct that describes a subpartitioning of the above or
+                NULL.
+ 
+  DESCRIPTION
+    This function prints an interval on the fields used by table
+    partitioning, subpartitioning, or both.
+
+  NOTE 
+    Later in WL#2985 implementation this will be removed or moved into #ifdef DEBUG
+
+  Print field values for partitioning, if there is partitioning function,
+  print its value too.
+*/
+
+static void dbug_print_range(PART_PRUNE_PARAM *pprune_par, PART_OPT_INFO *part, 
+                             PART_OPT_INFO *next_part)
+{
+  String str;
+  String *pstr;
+  Field **field= part->fields;
+  
+  fprintf(stderr, "(");
+  switch (part->part_type) {
+  case RANGE_PARTITION:
+    {
+      SEL_ARG *range= pprune_par->tuple_parts[0];
+      KEY_PART *parts= pprune_par->range_param.key_parts;
+      if (!(range->min_flag & NO_MIN_RANGE))
+      {
+        dbug_print_field(part->fields[0]);
+        if (range->min_flag & NEAR_MIN)
+          fputs(" < ", stderr);
+        else
+          fputs(" <= ", stderr);
+      }
+
+      fprintf(stderr, "%s", part->fields[0]->field_name);
+
+      if (!(range->max_flag & NO_MAX_RANGE))
+      {
+        if (range->max_flag & NEAR_MAX)
+          fputs(" < ", stderr);
+        else
+          fputs(" <= ", stderr);
+        range->field->set_key_image((char*)(range->min_value),
+                                     parts[range->part].length); 
+        dbug_print_field(part->fields[0]);
+      }
+      break;
+    }
+  case HASH_PARTITION:
+  case LIST_PARTITION:
+    {
+      for (field= part->fields; *field; field++)
+      {
+        fprintf(stderr, "%s%s=", (field==part->fields)?"":", ", (*field)->field_name);
+        dbug_print_field(*field);
+      }
+      break;
+    }
+  case NOT_A_PARTITION:
+    DBUG_ASSERT(0);
+  }
+  fprintf(stderr, ")");
+  
+  
+  if (next_part)
+  {
+    fprintf(stderr, ", ");
+    dbug_print_range(pprune_par, next_part, NULL);
+  }
+  else
+    fprintf(stderr, "\n");
+}
+
+
+/*
+  Recursively enumerate partition pruning "intervals" and print them out.
+  
+  SYNOPSIS
+    find_used_partitions()
+      pprune_par Partition pruning context.
+      key_tree   Intervals tree to perform pruning for.
+
+  DESCRIPTION
+    This function recursively walks the SEL_ARG* tree, produces partition
+    pruning "intervals", and prints them out (its future intent is to produce
+    a set of used partitions).
+    
+    WHAT IS CONSIDERED TO BE "INTERVALS"
+    The function tries to construct partitioning "intervals". 
+    A partition pruning "interval" (suggestions for better name are welcome)
+    is equivalent to condition in one of the forms:
+
+    "partition_field1=const1 AND ... partition_fieldN=constN"          (1)
+    "subpartition_field1=const1 AND ... subpartition_fieldN=constN"    (2)
+    "(1) AND (2)"
+    
+    In (1) and (2) all [sub]partitioning fields must be used, and "x=const"
+    includes "x IS NULL". 
+    
+    If partitioning is performed using 
+    PARTITION BY RANGE(unary_monotonic_func(partition_field)),
+    then (1) also can be:
+    
+     "const1 OP1 partition_field"    (1.a)
+     "partition_field OP2 const2"    (1.b)
+     "(1.a) AND (1.b)"
+     
+    where OP1 and OP2 are '<' OR '<='.
+    
+    Everything else is not a partition pruning "interval".
+    
+  RETURN
+    1   The passed SEL_ARG* allowed for inference of partition pruning 
+        "intervals", corresponding partitions are marked as used.
+   -1   Couldn't infer any partition pruning "intervals" from the passed 
+        SEL_ARG* tree.
+*/
+
+static 
+int find_used_partitions(PART_PRUNE_PARAM *pprune_par, SEL_ARG *key_tree)
+{
+  RANGE_OPT_PARAM *param= &(pprune_par->range_param);
+  int res;
+  bool singlepoint_interval;
+  uint partno= key_tree->part;
+  bool process= FALSE;
+
+  if (key_tree->left != &null_element)
+  {
+    res= find_used_partitions(pprune_par,key_tree->left);
+    if (res == -1)
+      return -1;
+  }
+
+  res= 0;
+  singlepoint_interval = key_tree->is_singlepoint();
+  if (partno== 0 && pprune_par->part_type_range_like ||
+      key_tree->type == SEL_ARG::KEY_RANGE && singlepoint_interval)
+  {
+    process= TRUE;
+    //push the SEL_ARG, update counters
+    pprune_par->cur_part_fields+=    pprune_par->in_part_keyparts[partno];
+    pprune_par->cur_subpart_fields+= pprune_par->in_subpart_keyparts[partno];
+    *(pprune_par->tuple_end++) = key_tree;
+  }
+
+  if (key_tree->next_key_part)
+  {
+    find_used_partitions(pprune_par, key_tree->next_key_part);
+  }
+  else
+  {
+    bool limit_part= FALSE; 
+    bool limit_subpart= FALSE;
+    // All key parts collected
+    // check if we have full partition or full subpartition
+    if (pprune_par->part_fields && 
+        pprune_par->part_fields == pprune_par->cur_part_fields)
+    {
+      //Have partitions.
+      limit_part= TRUE;
+      SEL_ARG **start= pprune_par->tuple_parts;
+      if ((*start)->min_flag & NO_MIN_RANGE)
+        start++;
+      fill_part_fields(pprune_par, start,
+                       pprune_par->tuple_parts + pprune_par->part_fields, 1);
+    }
+
+    if (pprune_par->subpart_fields && 
+        pprune_par->subpart_fields == pprune_par->cur_subpart_fields)
+    {
+      //Have subpartitions.
+      limit_subpart= TRUE;
+      fill_part_fields(pprune_par, pprune_par->tuple_end,
+                       pprune_par->tuple_end - pprune_par->subpart_fields, -1);
+    }
+
+    if (limit_part)
+      dbug_print_range(pprune_par, &pprune_par->part_opts[0], 
+                       limit_subpart? &pprune_par->part_opts[1]:NULL);
+    else if (limit_subpart)
+      dbug_print_range(pprune_par, &pprune_par->part_opts[1], NULL);
+    else
+      res= -1;
+  }
+  
+  if (process)
+  {
+    //pop the SEL_ARG, restore counters;
+    pprune_par->tuple_end--;
+    pprune_par->cur_part_fields-=    pprune_par->in_part_keyparts[partno];
+    pprune_par->cur_subpart_fields-= pprune_par->in_subpart_keyparts[partno];
+  }
+  if (res == -1)
+    return res;
+
+  if (key_tree->right != &null_element)
+  {
+    res= find_used_partitions(pprune_par,key_tree->right);
+    if (res == -1)
+      return -1;
+  }
+  return 1; //Found some ranges (or nothing, don't care for now)
+}
+
+
+/*
+  Peform partition pruning for a given table and condition.
+
+  SYNOPSIS
+    prune_partitions()
+      thd           Thread handle
+      table         Table to perform partition pruning for
+      pprune_cond   Condition to use for partition pruning
+      const_tables  
+  
+  DESCRIPTION
+    It is assumed that all table partitions are marked as unused when this
+    function is called.
+    After it finishes, 
+    
+  NOTES
+    This function returns promptly if called for non-partitioned table.
+
+    I didn't check if its ok to call this twice for same table and different
+    conditions (but it is ought to be). For now it is called one time.
+
+  RETURN
+    TRUE   We've inferred that there will be no records that match the query.
+    FALSE  Otherwise
+*/
+
+bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond, 
+                      table_map const_tables)
+{
+  bool res= FALSE;
+  partition_info *part_info = table->file->get_part_info();
+  DBUG_ENTER("prune_partitions");
+
+  if (!part_info || !pprune_cond)
+    DBUG_RETURN(FALSE); /* not a partitioned table */
+  
+  PART_PRUNE_PARAM prune_param;
+  MEM_ROOT alloc;
+  RANGE_OPT_PARAM  *range_par= &prune_param.range_param;
+
+  prune_param.part_info= part_info;
+
+  init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+  range_par->mem_root= &alloc;
+  range_par->old_root= thd->mem_root;
+
+  if (create_partition_index_descrition(&prune_param))
+  {
+    free_root(&alloc,MYF(0));		// Return memory & allocator
+    DBUG_RETURN(FALSE);
+  }
+  //ok, range_param->key_parts_[end] are set.
+  
+  range_par->thd= thd;
+  range_par->table= table;
+  /* range_par->cond doesn't need initialization */
+  range_par->prev_tables= const_tables;
+  range_par->read_tables= const_tables;
+  range_par->current_table= table->map;
+
+  range_par->keys= 1; // one index
+  range_par->optimizing_real_indexes= FALSE;
+  range_par->real_keynr[0]= 0;
+
+  thd->no_errors=1;				// Don't warn about NULL
+  thd->mem_root=&alloc;
+  
+  SEL_TREE *tree;
+  SEL_ARG *arg;
+  if ((tree= get_mm_tree(range_par, pprune_cond)))
+  {
+    if (tree->type == SEL_TREE::IMPOSSIBLE)
+    {
+      res= TRUE;
+      goto end;
+    }
+    if ((tree->type != SEL_TREE::KEY && tree->type != SEL_TREE::KEY_SMALLER))
+      goto end;
+    DBUG_ASSERT(tree->merges.is_empty());
+    arg= tree->keys[0];
+    if (!arg)
+      goto end;
+    if (arg->type == SEL_ARG::IMPOSSIBLE)
+    {
+      res= TRUE; // is this dead code?
+      goto end;
+    }
+
+    prune_param.key= prune_param.range_param.key_parts;
+    prune_param.tuple_end= prune_param.tuple_parts;
+    prune_param.cur_part_fields= 0;
+    prune_param.cur_subpart_fields= 0;
+    find_used_partitions(&prune_param, arg);
+  }
+
+end:
+  thd->no_errors=0;
+  thd->mem_root= range_par->old_root;
+  free_root(&alloc,MYF(0));			// Return memory & allocator
+  DBUG_RETURN(res);
+}
+ 
+
+#ifndef DBUG_OFF
+void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end)
+{
+  DBUG_ENTER("print_partitioning_index");
+  DBUG_LOCK_FILE;
+  fprintf(DBUG_FILE, "partitioning INDEX(");
+  for (KEY_PART *p=parts; p != parts_end; p++)
+  {
+    fprintf(DBUG_FILE, "%s%s", p==parts?"":" ,", p->field->field_name);
+  }
+  fprintf(DBUG_FILE, ");\n");
+  DBUG_UNLOCK_FILE;
+  DBUG_VOID_RETURN;
+}
+#endif
+
+
+/*
+  RETURN 
+    >0 - Number of fields 
+    0  - No fields or fields include fields we can't process.
+*/
+static int get_fields_array_len(Field **pfield)
+{
+  uint i;
+  if (!pfield)
+    return 0;
+  for (i=0; (*pfield); pfield++, i++)
+  {
+    enum_field_types ftype= (*pfield)->real_type();
+    if (ftype == FIELD_TYPE_ENUM || ftype == FIELD_TYPE_GEOMETRY)
+      return 0;
+  }
+  return i;
+}
+
+
+/*
+  Create partition index description and fill related info in the context
+  struct
+
+  SYNOPSIS
+    create_partition_index_descrition()
+      prune_par  INOUT Partition pruning context
+
+  DESCRIPTION
+    Create partition index description. Partition index description is:
+
+      part_index(used_fields_list(part_expr), used_fields_list(subpart_expr))
+
+    If partitioning/sub-partitioning uses BLOB or Geometry fields, then
+    corresponding fields_list(...) is not included into index description
+    and we don't perform partition pruning for partitions/subpartitions.
+
+  RETURN
+    TRUE   Out of memory or can't do partition pruning on such fields.
+    FALSE  OK
+*/
+
+static bool create_partition_index_descrition(PART_PRUNE_PARAM *prune_par)
+{
+  RANGE_OPT_PARAM *range_par= &(prune_par->range_param);
+  partition_info *part_info= prune_par->part_info;
+  uint nparts[2];
+  nparts[0]= get_fields_array_len(part_info->part_field_array);
+  nparts[1]= get_fields_array_len(part_info->subpart_field_array);
+  
+  uint total_parts= nparts[0] + nparts[1];
+
+  prune_par->part_fields= nparts[0];
+  prune_par->subpart_fields= nparts[1];
+  
+  prune_par->part_opts[0].part_type= part_info->part_type;
+  prune_par->part_opts[0].fields=    part_info->part_field_array;
+  prune_par->part_opts[0].expr=      part_info->part_expr;
+
+  prune_par->part_opts[1].part_type= part_info->subpart_type;
+  prune_par->part_opts[1].fields=    part_info->subpart_field_array;
+  prune_par->part_opts[1].expr=      part_info->subpart_expr;
+  
+  prune_par->part_type_range_like= 
+    test(part_info->part_expr && 
+         part_info->part_expr->type() == Item::FIELD_ITEM);
+ 
+  KEY_PART *key_part;
+  if (!total_parts || !(key_part= (KEY_PART*)alloc_root(range_par->mem_root,
+                                                        sizeof(KEY_PART)*
+                                                        total_parts)))
+    return TRUE;
+
+  range_par->key_parts= key_part;
+  Field **field= (nparts[0])? part_info->part_field_array :
+                              part_info->subpart_field_array;
+  bool subpart_fields= FALSE;
+  for (uint part= 0; part < total_parts; part++, key_part++)
+  {
+    key_part->key=          0;
+    key_part->part=	    part;
+    key_part->length=       (*field)->field_length;
+    /* 
+      HA_KEY_NULL_LENGTH is already added if it was needed. We need to add
+      blob length bytes if necessary (doing like it is done open_binary_frm())
+      psergey-todo: check yet again if this is correct for tricky field types,
+        e.g. see "Fix a fatal error in decimal key handling" in 
+        open_binary_frm().
+    */
+    key_part->store_length= (*field)->pack_length();
+    if ((*field)->type() == FIELD_TYPE_BLOB || 
+        (*field)->real_type() == MYSQL_TYPE_VARCHAR)
+      key_part->store_length+=HA_KEY_BLOB_LENGTH;
+
+    key_part->field=        (*field);
+    key_part->image_type =  Field::itRAW;
+    /* We don't set key_parts->null_bit as it will not be used */
+
+    prune_par->in_part_keyparts[part]= !subpart_fields;
+    prune_par->in_subpart_keyparts[part]= subpart_fields;
+ 
+    if (!*(++field))
+    {
+      field= part_info->subpart_field_array;
+      subpart_fields= TRUE;
+    }
+  }
+  
+  range_par->key_parts_end= key_part;
+
+  DBUG_EXECUTE("info", print_partitioning_index(range_par->key_parts,
+                                                range_par->key_parts_end););
+  return FALSE;
+}
+
+
+/****************************************************************************
+ * Partition pruning code ends
+ ****************************************************************************/
+#endif
+
 
 /*
   Get cost of 'sweep' full records retrieval.
@@ -3423,7 +4039,7 @@
     0  on error
 */
 
-static SEL_TREE *get_ne_mm_tree(PARAM *param, Item_func *cond_func, 
+static SEL_TREE *get_ne_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, 
                                 Field *field,
                                 Item *lt_value, Item *gt_value,
                                 Item_result cmp_type)
@@ -3458,7 +4074,7 @@
     Pointer to the tree built tree
 */
 
-static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, 
+static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, 
                                   Field *field, Item *value,
                                   Item_result cmp_type, bool inv)
 {
@@ -3551,7 +4167,7 @@
 
 	/* make a select tree of all keys in condition */
 
-static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
+static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond)
 {
   SEL_TREE *tree=0;
   SEL_TREE *ftree= 0;
@@ -3724,7 +4340,7 @@
 
 
 static SEL_TREE *
-get_mm_parts(PARAM *param, COND *cond_func, Field *field,
+get_mm_parts(RANGE_OPT_PARAM *param, COND *cond_func, Field *field,
 	     Item_func::Functype type,
 	     Item *value, Item_result cmp_type)
 {
@@ -3774,7 +4390,7 @@
 
 
 static SEL_ARG *
-get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
+get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
 	    Item_func::Functype type,Item *value)
 {
   uint maybe_null=(uint) field->real_maybe_null();
@@ -3833,8 +4449,15 @@
       !(conf_func->compare_collation()->state & MY_CS_BINSORT))
     goto end;
 
-  optimize_range= field->optimize_range(param->real_keynr[key_part->key],
-                                        key_part->part);
+  //psergey-todo: check if we should use optimize_range == FALSE
+  //  for HASH partitioning. (that will disable construction of "key < c" 
+  //  intervals.. otoh we might want them to make inferences like 
+  //  "key <= c and key >=c" => "key=c"
+  if (param->optimizing_real_indexes)
+    optimize_range= field->optimize_range(param->real_keynr[key_part->key],
+                                          key_part->part);
+  else
+    optimize_range= TRUE;
 
   if (type == Item_func::LIKE_FUNC)
   {
@@ -4101,7 +4724,7 @@
 
 
 static SEL_TREE *
-tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
+tree_and(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
 {
   DBUG_ENTER("tree_and");
   if (!tree1)
@@ -4171,7 +4794,7 @@
   using index_merge.
 */
 
-bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param)
+bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, RANGE_OPT_PARAM* param)
 {
   key_map common_keys= tree1->keys_map;
   DBUG_ENTER("sel_trees_can_be_ored");
@@ -4198,7 +4821,7 @@
 }
 
 static SEL_TREE *
-tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
+tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
 {
   DBUG_ENTER("tree_or");
   if (!tree1 || !tree2)

--- 1.57/sql/opt_range.h	2005-10-19 00:52:02 +03:00
+++ 1.58/sql/opt_range.h	2005-12-13 03:21:53 +02:00
@@ -249,6 +249,7 @@
 
 
 struct st_qsel_param;
+class PARAM;
 class SEL_ARG;
 
 /*
@@ -283,12 +284,12 @@
   QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
                                                struct st_table_ref *ref,
                                                ha_rows records);
-  friend bool get_quick_keys(struct st_qsel_param *param,
+  friend bool get_quick_keys(PARAM *param,
                              QUICK_RANGE_SELECT *quick,KEY_PART *key,
                              SEL_ARG *key_tree,
                              char *min_key, uint min_key_flag,
                              char *max_key, uint max_key_flag);
-  friend QUICK_RANGE_SELECT *get_quick_select(struct st_qsel_param*,uint idx,
+  friend QUICK_RANGE_SELECT *get_quick_select(PARAM*,uint idx,
                                               SEL_ARG *key_tree,
                                               MEM_ROOT *alloc);
   friend class QUICK_SELECT_DESC;

--- 1.372/sql/sql_select.cc	2005-12-01 12:12:20 +02:00
+++ 1.373/sql/sql_select.cc	2005-12-13 03:21:53 +02:00
@@ -1946,6 +1946,8 @@
   0	ok
   1	Fatal error
 */
+bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond, 
+                      table_map const_tables);
 
 static bool
 make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
@@ -2231,48 +2233,61 @@
     */
     add_group_and_distinct_keys(join, s);
 
-    if (!s->const_keys.is_clear_all() &&
-        !s->table->pos_in_table_list->embedding)
+    if (!s->table->pos_in_table_list->embedding)
     {
-      ha_rows records;
-      SQL_SELECT *select;
-      select= make_select(s->table, found_const_table_map,
-			  found_const_table_map,
-			  *s->on_expr_ref ? *s->on_expr_ref : conds,
-			  1, &error);
-      if (!select)
-        DBUG_RETURN(1);
-      records= get_quick_record_count(join->thd, select, s->table,
-				      &s->const_keys, join->row_limit);
-      s->quick=select->quick;
-      s->needed_reg=select->needed_reg;
-      select->quick=0;
-      if (records == 0 && s->table->reginfo.impossible_range)
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+      Item *pprune_cond= *s->on_expr_ref ? *s->on_expr_ref : conds;
+      s->table->reginfo.impossible_range= FALSE;
+      if (prune_partitions(join->thd, s->table, pprune_cond, 
+                           found_const_table_map))
       {
-	/*
-	  Impossible WHERE or ON expression
-	  In case of ON, we mark that the we match one empty NULL row.
-	  In case of WHERE, don't set found_const_table_map to get the
-	  caller to abort with a zero row result.
-	*/
-	join->const_table_map|= s->table->map;
-	set_position(join,const_count++,s,(KEYUSE*) 0);
-	s->type= JT_CONST;
-	if (*s->on_expr_ref)
-	{
-	  /* Generate empty row */
-	  s->info= "Impossible ON condition";
-	  found_const_table_map|= s->table->map;
-	  s->type= JT_CONST;
-	  mark_as_null_row(s->table);		// All fields are NULL
-	}
+        s->table->reginfo.impossible_range= TRUE;
+        goto impossible_range;  //Dont attempt range optimization
+      }
+#endif
+      if (!s->const_keys.is_clear_all())
+      {
+        ha_rows records;
+        SQL_SELECT *select;
+        select= make_select(s->table, found_const_table_map,
+                            found_const_table_map,
+                            *s->on_expr_ref ? *s->on_expr_ref : conds,
+                            1, &error);
+        if (!select)
+          DBUG_RETURN(1);
+        records= get_quick_record_count(join->thd, select, s->table,
+                                        &s->const_keys, join->row_limit);
+        s->quick=select->quick;
+        s->needed_reg=select->needed_reg;
+        select->quick=0;
+        if (!s->table->reginfo.impossible_range && records != HA_POS_ERROR)
+        {
+          s->found_records=records;
+          s->read_time= (ha_rows) (s->quick ? s->quick->read_time : 0.0);
+        }
+        delete select;
       }
-      if (records != HA_POS_ERROR)
+      if (s->table->reginfo.impossible_range)
       {
-	s->found_records=records;
-	s->read_time= (ha_rows) (s->quick ? s->quick->read_time : 0.0);
+        impossible_range:
+        /*
+          Impossible WHERE or ON expression
+          In case of ON, we mark that the we match one empty NULL row.
+          In case of WHERE, don't set found_const_table_map to get the
+          caller to abort with a zero row result.
+        */
+        join->const_table_map|= s->table->map;
+        set_position(join,const_count++,s,(KEYUSE*) 0);
+        s->type= JT_CONST;
+        if (*s->on_expr_ref)
+        {
+          /* Generate empty row */
+          s->info= "Impossible ON condition";
+          found_const_table_map|= s->table->map;
+          s->type= JT_CONST;
+          mark_as_null_row(s->table);		// All fields are NULL
+        }
       }
-      delete select;
     }
   }
 

--- 1.100/sql/ha_ndbcluster.h	2005-11-25 10:08:29 +02:00
+++ 1.101/sql/ha_ndbcluster.h	2005-12-13 03:21:53 +02:00
@@ -535,6 +535,7 @@
             HA_CAN_PARTITION_UNIQUE);
   }
   void set_part_info(partition_info *part_info);
+  partition_info *get_part_info() { return m_part_info; }
   ulong index_flags(uint idx, uint part, bool all_parts) const;
   uint max_supported_record_length() const;
   uint max_supported_keys() const;

--- 1.6/sql/ha_partition.h	2005-11-25 10:08:30 +02:00
+++ 1.7/sql/ha_partition.h	2005-12-13 03:21:53 +02:00
@@ -127,6 +127,7 @@
      m_part_info= part_info;
      m_is_sub_partitioned= is_sub_partitioned(part_info);
   }
+  partition_info *get_part_info() { return m_part_info; }
   /*
     -------------------------------------------------------------------------
     MODULE create/delete handler object
Thread
bk commit into 5.1 tree (timour:1.1956)timour14 Dec