Merge bk-internal.mysql.com:/home/bk/mysql-5.1-runtime
into mockturtle.local:/home/dlenev/src/mysql-5.1-bg20670 mysql-test/r/trigger.result: Auto merged mysql-test/t/trigger.test: Auto merged sql/mysql_priv.h: Auto merged sql/opt_range.cc: Auto merged sql/opt_range.h: Auto merged sql/sql_partition.cc: Auto merged sql/sql_update.cc: Auto merged
This commit is contained in:
commit
72d698e8ed
@ -1173,4 +1173,16 @@ TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW SET @a = 2;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
drop table if exists t1;
|
||||
create table t1 (i int, j int key);
|
||||
insert into t1 values (1,1), (2,2), (3,3);
|
||||
create trigger t1_bu before update on t1 for each row
|
||||
set new.j = new.j + 10;
|
||||
update t1 set i= i+ 10 where j > 2;
|
||||
select * from t1;
|
||||
i j
|
||||
1 1
|
||||
2 2
|
||||
13 13
|
||||
drop table t1;
|
||||
End of 5.0 tests
|
||||
|
@ -1421,4 +1421,23 @@ DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
|
||||
|
||||
#
|
||||
# Bug#20670 "UPDATE using key and invoking trigger that modifies
|
||||
# this key does not stop"
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists t1;
|
||||
--enable_warnings
|
||||
create table t1 (i int, j int key);
|
||||
insert into t1 values (1,1), (2,2), (3,3);
|
||||
create trigger t1_bu before update on t1 for each row
|
||||
set new.j = new.j + 10;
|
||||
# This should not work indefinitely and should cause
|
||||
# expected result
|
||||
update t1 set i= i+ 10 where j > 2;
|
||||
select * from t1;
|
||||
drop table t1;
|
||||
|
||||
|
||||
--echo End of 5.0 tests
|
||||
|
44
sql/key.cc
44
sql/key.cc
@ -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.
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
|
||||
bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *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
|
||||
|
||||
f.rewind();
|
||||
while ((field=(Item_field*) f++))
|
||||
{
|
||||
if (key_part->field->eq(field->field))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1417,7 +1417,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);
|
||||
|
||||
|
@ -7526,42 +7526,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;
|
||||
|
@ -224,10 +224,9 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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
|
||||
|
@ -2037,18 +2037,17 @@ close_file:
|
||||
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)
|
||||
@ -2056,9 +2055,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);
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ bool fix_partition_func(THD *thd, TABLE *table, bool create_table_ind);
|
||||
char *generate_partition_syntax(partition_info *part_info,
|
||||
uint *buf_length, bool use_sql_alloc,
|
||||
bool show_partition_options);
|
||||
bool partition_key_modified(TABLE *table, List<Item> &fields);
|
||||
bool partition_key_modified(TABLE *table, const MY_BITMAP *fields);
|
||||
void get_partition_set(const TABLE *table, byte *buf, const uint index,
|
||||
const key_range *key_spec,
|
||||
part_id_range *part_spec);
|
||||
|
@ -1582,6 +1582,38 @@ void Table_triggers_list::mark_fields_used(trg_event_type event)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check if field of subject table can be changed in before update trigger.
|
||||
|
||||
SYNOPSIS
|
||||
is_updated_in_before_update_triggers()
|
||||
field Field object for field to be checked
|
||||
|
||||
NOTE
|
||||
Field passed to this function should be bound to the same
|
||||
TABLE object as Table_triggers_list.
|
||||
|
||||
RETURN VALUE
|
||||
TRUE Field is changed
|
||||
FALSE Otherwise
|
||||
*/
|
||||
|
||||
bool Table_triggers_list::is_updated_in_before_update_triggers(Field *fld)
|
||||
{
|
||||
Item_trigger_field *trg_fld;
|
||||
for (trg_fld= trigger_fields[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE];
|
||||
trg_fld != 0;
|
||||
trg_fld= trg_fld->next_trg_field)
|
||||
{
|
||||
if (trg_fld->get_settable_routine_parameter() &&
|
||||
trg_fld->field_idx != (uint)-1 &&
|
||||
table->field[trg_fld->field_idx]->eq(fld))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Trigger BUG#14090 compatibility hook
|
||||
|
||||
|
@ -116,11 +116,6 @@ public:
|
||||
bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER]);
|
||||
}
|
||||
|
||||
bool has_before_update_triggers()
|
||||
{
|
||||
return test(bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE]);
|
||||
}
|
||||
|
||||
void set_table(TABLE *new_table);
|
||||
|
||||
void mark_fields_used(trg_event_type event);
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "sp_head.h"
|
||||
#include "sql_trigger.h"
|
||||
|
||||
static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields);
|
||||
static bool safe_update_on_fly(JOIN_TAB *join_tab);
|
||||
|
||||
/* Return 0 if row hasn't changed */
|
||||
|
||||
@ -271,13 +271,16 @@ int mysql_update(THD *thd,
|
||||
}
|
||||
}
|
||||
init_ftfuncs(thd, select_lex, 1);
|
||||
|
||||
table->mark_columns_needed_for_update();
|
||||
|
||||
/* Check if we are modifying a key that we are used to search with */
|
||||
|
||||
if (select && select->quick)
|
||||
{
|
||||
used_index= select->quick->index;
|
||||
used_key_is_modified= (!select->quick->unique_key_range() &&
|
||||
select->quick->check_if_keys_used(&fields));
|
||||
select->quick->is_keys_used(table->write_set));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -285,12 +288,13 @@ int mysql_update(THD *thd,
|
||||
if (used_index == MAX_KEY) // no index for sort order
|
||||
used_index= table->file->key_used_on_scan;
|
||||
if (used_index != MAX_KEY)
|
||||
used_key_is_modified= check_if_key_used(table, used_index, fields);
|
||||
used_key_is_modified= is_key_used(table, used_index, table->write_set);
|
||||
}
|
||||
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
if (used_key_is_modified || order ||
|
||||
partition_key_modified(table, fields))
|
||||
partition_key_modified(table, table->write_set))
|
||||
#else
|
||||
if (used_key_is_modified || order)
|
||||
#endif
|
||||
@ -449,8 +453,6 @@ int mysql_update(THD *thd,
|
||||
MODE_STRICT_ALL_TABLES)));
|
||||
will_batch= !table->file->start_bulk_update();
|
||||
|
||||
table->mark_columns_needed_for_update();
|
||||
|
||||
/*
|
||||
We can use compare_record() to optimize away updates if
|
||||
the table handler is returning all columns OR if
|
||||
@ -1217,7 +1219,7 @@ multi_update::initialize_tables(JOIN *join)
|
||||
table->mark_columns_needed_for_update();
|
||||
if (table == main_table) // First table in join
|
||||
{
|
||||
if (safe_update_on_fly(join->join_tab, &temp_fields))
|
||||
if (safe_update_on_fly(join->join_tab))
|
||||
{
|
||||
table_to_update= main_table; // Update table on the fly
|
||||
continue;
|
||||
@ -1275,7 +1277,6 @@ multi_update::initialize_tables(JOIN *join)
|
||||
SYNOPSIS
|
||||
safe_update_on_fly
|
||||
join_tab How table is used in join
|
||||
fields Fields that are updated
|
||||
|
||||
NOTES
|
||||
We can update the first table in join on the fly if we know that
|
||||
@ -1288,9 +1289,8 @@ multi_update::initialize_tables(JOIN *join)
|
||||
- We are doing a range scan and we don't update the scan key or
|
||||
the primary key for a clustered table handler.
|
||||
|
||||
When checking for above cases we also should take into account that
|
||||
BEFORE UPDATE trigger potentially may change value of any field in row
|
||||
being updated.
|
||||
This function gets information about fields to be updated from
|
||||
the TABLE::write_set bitmap.
|
||||
|
||||
WARNING
|
||||
This code is a bit dependent of how make_join_readinfo() works.
|
||||
@ -1300,7 +1300,7 @@ multi_update::initialize_tables(JOIN *join)
|
||||
1 Safe to update
|
||||
*/
|
||||
|
||||
static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
|
||||
static bool safe_update_on_fly(JOIN_TAB *join_tab)
|
||||
{
|
||||
TABLE *table= join_tab->table;
|
||||
switch (join_tab->type) {
|
||||
@ -1310,21 +1310,15 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
|
||||
return TRUE; // At most one matching row
|
||||
case JT_REF:
|
||||
case JT_REF_OR_NULL:
|
||||
return !check_if_key_used(table, join_tab->ref.key, *fields) &&
|
||||
!(table->triggers &&
|
||||
table->triggers->has_before_update_triggers());
|
||||
return !is_key_used(table, join_tab->ref.key, table->write_set);
|
||||
case JT_ALL:
|
||||
/* If range search on index */
|
||||
if (join_tab->quick)
|
||||
return !join_tab->quick->check_if_keys_used(fields) &&
|
||||
!(table->triggers &&
|
||||
table->triggers->has_before_update_triggers());
|
||||
return !join_tab->quick->is_keys_used(table->write_set);
|
||||
/* If scanning in clustered key */
|
||||
if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
|
||||
table->s->primary_key < MAX_KEY)
|
||||
return !check_if_key_used(table, table->s->primary_key, *fields) &&
|
||||
!(table->triggers &&
|
||||
table->triggers->has_before_update_triggers());
|
||||
return !is_key_used(table, table->s->primary_key, table->write_set);
|
||||
return TRUE;
|
||||
default:
|
||||
break; // Avoid compler warning
|
||||
|
Loading…
x
Reference in New Issue
Block a user