Commit 3aa89efc authored by unknown's avatar unknown
Browse files

Manual merge of Bug#9096

parents 9724795f b883aeba
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -587,3 +587,30 @@ deallocate prepare stmt|
drop procedure p1|
drop table t1|
delimiter ;|

#
# Bug#9096 "select doesn't return all matched records if prepared statements
# is used"
# The bug was is bad co-operation of the optimizer's algorithm which determines
# which keys can be used to execute a query, constants propagation
# part of the optimizer and parameter markers used by prepared statements.

drop table if exists t1;
create table t1 (c1 int(11) not null, c2 int(11) not null,
             primary key  (c1,c2), key c2 (c2), key c1 (c1));

insert into t1 values (200887, 860);
insert into t1 values (200887, 200887);

select * from t1 where (c1=200887 and c2=200887) or c2=860;

prepare stmt from
"select * from t1 where (c1=200887 and c2=200887) or c2=860";
execute stmt;
prepare stmt from
"select * from t1 where (c1=200887 and c2=?) or c2=?";
set @a=200887, @b=860;
# this query did not return all matching rows
execute stmt using @a, @b;
deallocate prepare stmt;
+114 −1
Original line number Diff line number Diff line
@@ -557,6 +557,11 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)

bool Item::eq(const Item *item, bool binary_cmp) const
{
  /*
    Note, that this is never TRUE if item is a Item_param:
    for all basic constants we have special checks, and Item_param's
    type() can be only among basic constant types.
  */
  return type() == item->type() && name && item->name &&
    !my_strcasecmp(system_charset_info,name,item->name);
}
@@ -601,7 +606,7 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)

bool Item_string::eq(const Item *item, bool binary_cmp) const
{
  if (type() == item->type())
  if (type() == item->type() && item->basic_const_item())
  {
    if (binary_cmp)
      return !stringcmp(&str_value, &item->str_value);
@@ -2194,6 +2199,72 @@ bool Item_param::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
}


bool Item_param::basic_const_item() const
{
  if (state == NO_VALUE || state == TIME_VALUE)
    return FALSE;
  return TRUE;
}


Item *
Item_param::new_item()
{
  /* see comments in the header file */
  switch (state) {
  case NULL_VALUE:
    return new Item_null(name);
  case INT_VALUE:
    return new Item_int(name, value.integer, max_length);
  case REAL_VALUE:
    return new Item_real(name, value.real, decimals, max_length);
  case STRING_VALUE:
  case LONG_DATA_VALUE:
    return new Item_string(name, str_value.c_ptr_quick(), str_value.length(),
                           str_value.charset());
  case TIME_VALUE:
    break;
  case NO_VALUE:
  default:
    DBUG_ASSERT(0);
  };
  return 0;
}


bool
Item_param::eq(const Item *arg, bool binary_cmp) const
{
  Item *item;
  if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type())
    return FALSE;
  /*
    We need to cast off const to call val_int(). This should be OK for
    a basic constant.
  */
  item= (Item*) arg;

  switch (state) {
  case NULL_VALUE:
    return TRUE;
  case INT_VALUE:
    return value.integer == item->val_int() &&
           unsigned_flag == item->unsigned_flag;
  case REAL_VALUE:
    return value.real == item->val();
  case STRING_VALUE:
  case LONG_DATA_VALUE:
    if (binary_cmp)
      return !stringcmp(&str_value, &item->str_value);
    return !sortcmp(&str_value, &item->str_value, collation.collation);
  default:
    break;
  }
  return FALSE;
}

/* End of Item_param related */

void Item_param::print(String *str)
{
  if (state == NO_VALUE)
@@ -3346,6 +3417,22 @@ int Item_decimal::save_in_field(Field *field, bool no_conversions)
}


bool Item_int::eq(const Item *arg, bool binary_cmp) const
{
  /* No need to check for null value as basic constant can't be NULL */
  if (arg->basic_const_item() && arg->type() == type())
  {
    /*
      We need to cast off const to call val_int(). This should be OK for
      a basic constant.
    */
    Item *item= (Item*) arg;
    return item->val_int() == value && item->unsigned_flag == unsigned_flag;
  }
  return FALSE;
}


Item_num *Item_uint::neg()
{
  Item_decimal *item= new Item_decimal(value, 0);
@@ -3433,6 +3520,21 @@ void Item_float::print(String *str)
  In number context this is a longlong value.
*/

bool Item_real::eq(const Item *arg, bool binary_cmp) const
{
  if (arg->basic_const_item() && arg->type() == type())
  {
    /*
      We need to cast off const to call val_int(). This should be OK for
      a basic constant.
    */
    Item *item= (Item*) arg;
    return item->val() == value;
  }
  return FALSE;
}


inline uint char_val(char X)
{
  return (uint) (X >= '0' && X <= '9' ? X-'0' :
@@ -3503,6 +3605,17 @@ int Item_hex_string::save_in_field(Field *field, bool no_conversions)
}


bool Item_varbinary::eq(const Item *arg, bool binary_cmp) const
{
  if (arg->basic_const_item() && arg->type() == type())
  {
    if (binary_cmp)
      return !stringcmp(&str_value, &arg->str_value);
    return !sortcmp(&str_value, &arg->str_value, collation.collation);
  }
  return FALSE;
}

/*
  bin item.
  In string context this is a binary string.
+23 −4
Original line number Diff line number Diff line
@@ -434,7 +434,7 @@ class Item {
  virtual table_map not_null_tables() const { return used_tables(); }
  /*
    Returns true if this is a simple constant item like an integer, not
    a constant expression
    a constant expression. Used in the optimizer to propagate basic constants.
  */
  virtual bool basic_const_item() const { return 0; }
  /* cloning of constant items (0 if it is not const) */
@@ -914,7 +914,6 @@ class Item_param :public Item

  bool convert_str_value(THD *thd);

  Item *new_item() { return new Item_param(pos_in_query); }
  /*
    If value for parameter was not set we treat it as non-const
    so noone will use parameters value in fix_fields still
@@ -923,12 +922,29 @@ class Item_param :public Item
  virtual table_map used_tables() const
  { return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; }
  void print(String *str);
  /* parameter never equal to other parameter of other item */
  bool eq(const Item *item, bool binary_cmp) const { return 0; }
  bool is_null()
  { DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; }
  bool basic_const_item() const;
  /*
    This method is used to make a copy of a basic constant item when
    propagating constants in the optimizer. The reason to create a new
    item and not use the existing one is not precisely known (2005/04/16).
    Probably we are trying to preserve tree structure of items, in other
    words, avoid pointing at one item from two different nodes of the tree.
    Return a new basic constant item if parameter value is a basic
    constant, assert otherwise. This method is called only if
    basic_const_item returned TRUE.
  */
  Item *new_item();
  /*
    Implement by-value equality evaluation if parameter value
    is set and is a basic constant (integer, real or string).
    Otherwise return FALSE.
  */
  bool eq(const Item *item, bool binary_cmp) const;
};


class Item_int :public Item_num
{
public:
@@ -956,6 +972,7 @@ class Item_int :public Item_num
  void cleanup() {}
  void print(String *str);
  Item_num *neg() { value= -value; return this; }
  bool eq(const Item *, bool binary_cmp) const;
};


@@ -1059,6 +1076,7 @@ class Item_float :public Item_num
  { return new Item_float(name, value, decimals, max_length); }
  Item_num *neg() { value= -value; return this; }
  void print(String *str);
  bool eq(const Item *, bool binary_cmp) const;
};


@@ -1198,6 +1216,7 @@ class Item_hex_string: public Item
  enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
  // to prevent drop fixed flag (no need parent cleanup call)
  void cleanup() {}
  bool eq(const Item *item, bool binary_cmp) const;
};


+3 −2
Original line number Diff line number Diff line
@@ -1395,11 +1395,12 @@ void Item_func_neg::fix_length_and_dec()
  /*
    If this is in integer context keep the context as integer if possible
    (This is how multiplication and other integer functions works)
    Use val() to get value as arg_type doesn't mean that item is
    Item_int or Item_real due to existence of Item_param.
  */
  if (hybrid_type == INT_RESULT &&
      args[0]->type() == INT_ITEM &&
      ((ulonglong) ((Item_uint*) args[0])->value >=
       (ulonglong) LONGLONG_MIN))
      ((ulonglong) args[0]->val_int() >= (ulonglong) LONGLONG_MIN))
  {
    /*
      Ensure that result is converted to DECIMAL, as longlong can't hold