Commit e0d2f525 authored by unknown's avatar unknown
Browse files

Fix for bug#20670 "UPDATE using key and invoking trigger that modifies

this key does not stop" (5.1 version).

UPDATE statement which WHERE clause used key and which invoked trigger
that modified field in this key worked indefinetely.

This problem occured because in cases when UPDATE statement was
executed in update-on-the-fly mode (in which row is updated right
during evaluation of select for WHERE clause) the new version of
the row became visible to select representing WHERE clause and was
updated again and again.
We already solve this problem for UPDATE statements which does not
invoke triggers by detecting the fact that we are going to update
field in key used for scanning and performing update in two steps,
during the first step we gather information about the rows to be
updated and then doing actual updates. We also do this for
MULTI-UPDATE and in its case we even detect situation when such
fields are updated in triggers (actually we simply assume that
we always update fields used in key if we have before update
trigger).

The fix simply extends this check which is done with help of
check_if_key_used()/QUICK_SELECT_I::check_if_keys_used()
routine/method in such way that it also detects cases when
field used in key is updated in trigger. We do this by
changing check_if_key_used() to take field bitmap instead
field list as argument and passing TABLE::write_set
to it (we also have to add info about fields used in
triggers to this bitmap a bit earlier).
As nice side-effect we have more precise and thus more optimal
perfomance-wise check for the MULTI-UPDATE.
Also check_if_key_used() routine and similar method were renamed
to is_key_used()/is_keys_used() in order to better reflect that
it is simple boolean predicate.
Finally, partition_key_modified() routine now also takes field
bitmap instead of field list as argument.


sql/key.cc:
  Now check_if_key_used() takes field bitmap instead of field list as
  argument (bitmaps are also used in its implementation).
  It is no longer responsible for checking if key uses automatically
  updated TIMESTAMP fields, instead callers should properly mark such
  fields in field bitmap.
  Also renamed this function to is_key_used().
sql/mysql_priv.h:
  Renamed check_if_key_used() to is_key_used(), also now this routine
  takes field bitmap instead of field list as argument.
sql/opt_range.cc:
  QUICK_SELECT_I::check_if_keys_used() method and check_if_key_used()
  routine were renamed to is_keys_used()/is_key_used(). Also now they
  take bitmap instead of field list as argument.
sql/opt_range.h:
  QUICK_SELECT_I::check_if_key_used() method was renamed to is_keys_used().
  Now it takes field bitmap instead of field list as argument and no
  longer responsible for checking if key uses automatically updated
  TIMESTAMP fields. Instead callers should properly mark such fields
  in field bitmap.
sql/sql_partition.cc:
  partition_key_modified() now takes field bitmap instead of list
  as argument.
sql/sql_partition.h:
  partition_key_modified() now takes field bitmap as argument
  instead of field list.
sql/sql_update.cc:
  To detect situation in which trigger modifies part of key which is
  going to be used for processing of where clause and thus makes
  processing of this update with update-on-the-fly method unsafe
  we use check_if_key_used() routine and similar method (which were
  renamed to is_key_used()) plus information from TABLE::write_map
  bitmap. Note that we have to call TABLE::mark_columns_needed_for_update()
  method earlier now to fill this bitmap with information about fields
  updated in triggers.
  safe_update_on_fly() routine now uses the same approach and no
  longer needs list of fields as argument.
parent f5e8d19f
Loading
Loading
Loading
Loading
+21 −23
Original line number Diff line number Diff line
@@ -359,31 +359,29 @@ void key_unpack(String *to,TABLE *table,uint idx)


/*
  Return 1 if any field in a list is part of key or the key uses a field
  that is automaticly updated (like a timestamp)
*/
  Check if key uses field that is marked in passed field bitmap.

bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
{
  List_iterator_fast<Item> f(fields);
  KEY_PART_INFO *key_part,*key_part_end;
  for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
	 table->key_info[idx].key_parts ;
       key_part < key_part_end;
       key_part++)
  {
    Item_field *field;

    if (key_part->field == table->timestamp_field)
      return 1;					// Can't be used for update
  SYNOPSIS
    is_key_used()
      table   TABLE object with which keys and fields are associated.
      idx     Key to be checked.
      fields  Bitmap of fields to be checked.

  NOTE
    This function uses TABLE::tmp_set bitmap so the caller should care
    about saving/restoring its state if it also uses this bitmap.

  RETURN VALUE
    TRUE   Key uses field from bitmap
    FALSE  Otherwise
*/

    f.rewind();
    while ((field=(Item_field*) f++))
bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields)
{
      if (key_part->field->eq(field->field))
  bitmap_clear_all(&table->tmp_set);
  table->mark_columns_used_by_index_no_reset(idx, &table->tmp_set);
  if (bitmap_is_overlapping(&table->tmp_set, fields))
    return 1;
    }
  }

  /*
    If table handler has primary key as part of the index, check that primary
@@ -391,7 +389,7 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
  */
  if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY &&
      (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
    return check_if_key_used(table, table->s->primary_key, fields);
    return is_key_used(table, table->s->primary_key, fields);
  return 0;
}

+1 −1
Original line number Diff line number Diff line
@@ -1402,7 +1402,7 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info,
                 uint key_length);
bool key_cmp_if_same(TABLE *form,const byte *key,uint index,uint key_length);
void key_unpack(String *to,TABLE *form,uint index);
bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields);
bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields);
int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length);
int key_rec_cmp(void *key_info, byte *a, byte *b);

+8 −8
Original line number Diff line number Diff line
@@ -7418,42 +7418,42 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
}


bool QUICK_SELECT_I::check_if_keys_used(List<Item> *fields)
bool QUICK_SELECT_I::is_keys_used(const MY_BITMAP *fields)
{
  return check_if_key_used(head, index, *fields);
  return is_key_used(head, index, fields);
}

bool QUICK_INDEX_MERGE_SELECT::check_if_keys_used(List<Item> *fields)
bool QUICK_INDEX_MERGE_SELECT::is_keys_used(const MY_BITMAP *fields)
{
  QUICK_RANGE_SELECT *quick;
  List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
  while ((quick= it++))
  {
    if (check_if_key_used(head, quick->index, *fields))
    if (is_key_used(head, quick->index, fields))
      return 1;
  }
  return 0;
}

bool QUICK_ROR_INTERSECT_SELECT::check_if_keys_used(List<Item> *fields)
bool QUICK_ROR_INTERSECT_SELECT::is_keys_used(const MY_BITMAP *fields)
{
  QUICK_RANGE_SELECT *quick;
  List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
  while ((quick= it++))
  {
    if (check_if_key_used(head, quick->index, *fields))
    if (is_key_used(head, quick->index, fields))
      return 1;
  }
  return 0;
}

bool QUICK_ROR_UNION_SELECT::check_if_keys_used(List<Item> *fields)
bool QUICK_ROR_UNION_SELECT::is_keys_used(const MY_BITMAP *fields)
{
  QUICK_SELECT_I *quick;
  List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
  while ((quick= it++))
  {
    if (quick->check_if_keys_used(fields))
    if (quick->is_keys_used(fields))
      return 1;
  }
  return 0;
+5 −6
Original line number Diff line number Diff line
@@ -224,10 +224,9 @@ class QUICK_SELECT_I
  virtual void add_info_string(String *str) {};
  /*
    Return 1 if any index used by this quick select
     a) uses field that is listed in passed field list or
     b) is automatically updated (like a timestamp)
    uses field which is marked in passed bitmap.
  */
  virtual bool check_if_keys_used(List<Item> *fields);
  virtual bool is_keys_used(const MY_BITMAP *fields);

  /*
    rowid of last row retrieved by this quick select. This is used only when
@@ -425,7 +424,7 @@ class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I
  int get_type() { return QS_TYPE_INDEX_MERGE; }
  void add_keys_and_lengths(String *key_names, String *used_lengths);
  void add_info_string(String *str);
  bool check_if_keys_used(List<Item> *fields);
  bool is_keys_used(const MY_BITMAP *fields);
#ifndef DBUG_OFF
  void dbug_dump(int indent, bool verbose);
#endif
@@ -484,7 +483,7 @@ class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I
  int get_type() { return QS_TYPE_ROR_INTERSECT; }
  void add_keys_and_lengths(String *key_names, String *used_lengths);
  void add_info_string(String *str);
  bool check_if_keys_used(List<Item> *fields);
  bool is_keys_used(const MY_BITMAP *fields);
#ifndef DBUG_OFF
  void dbug_dump(int indent, bool verbose);
#endif
@@ -538,7 +537,7 @@ class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I
  int get_type() { return QS_TYPE_ROR_UNION; }
  void add_keys_and_lengths(String *key_names, String *used_lengths);
  void add_info_string(String *str);
  bool check_if_keys_used(List<Item> *fields);
  bool is_keys_used(const MY_BITMAP *fields);
#ifndef DBUG_OFF
  void dbug_dump(int indent, bool verbose);
#endif
+5 −7
Original line number Diff line number Diff line
@@ -2021,18 +2021,17 @@ char *generate_partition_syntax(partition_info *part_info,
  SYNOPSIS
    partition_key_modified
    table                TABLE object for which partition fields are set-up
    fields               A list of the to be modifed
    fields               Bitmap representing fields to be modified

  RETURN VALUES
    TRUE                 Need special handling of UPDATE
    FALSE                Normal UPDATE handling is ok
*/

bool partition_key_modified(TABLE *table, List<Item> &fields)
bool partition_key_modified(TABLE *table, const MY_BITMAP *fields)
{
  List_iterator_fast<Item> f(fields);
  Field **fld;
  partition_info *part_info= table->part_info;
  Item_field *item_field;
  DBUG_ENTER("partition_key_modified");

  if (!part_info)
@@ -2040,9 +2039,8 @@ bool partition_key_modified(TABLE *table, List<Item> &fields)
  if (table->s->db_type->partition_flags &&
      (table->s->db_type->partition_flags() & HA_CAN_UPDATE_PARTITION_KEY))
    DBUG_RETURN(FALSE);
  f.rewind();
  while ((item_field=(Item_field*) f++))
    if (item_field->field->flags & FIELD_IN_PART_FUNC_FLAG)
  for (fld= part_info->full_part_field_array; *fld; fld++)
    if (bitmap_is_set(fields, (*fld)->field_index))
      DBUG_RETURN(TRUE);
  DBUG_RETURN(FALSE);
}
Loading