Merge bk-internal.mysql.com:/home/bk/mysql-5.0-runtime
into mockturtle.local:/home/dlenev/src/mysql-5.0-bg20670-2 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_update.cc: Auto merged
This commit is contained in:
commit
8f67eac4a3
@ -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)
|
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
DROP TABLE t2;
|
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
|
End of 5.0 tests
|
||||||
|
@ -1421,4 +1421,23 @@ DROP TABLE t1;
|
|||||||
DROP TABLE t2;
|
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
|
--echo End of 5.0 tests
|
||||||
|
24
sql/key.cc
24
sql/key.cc
@ -18,6 +18,7 @@
|
|||||||
/* Functions to handle keys and fields in forms */
|
/* Functions to handle keys and fields in forms */
|
||||||
|
|
||||||
#include "mysql_priv.h"
|
#include "mysql_priv.h"
|
||||||
|
#include "sql_trigger.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Search after with key field is. If no key starts with field test
|
** Search after with key field is. If no key starts with field test
|
||||||
@ -342,12 +343,24 @@ 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
|
Check if key uses field that is listed in passed field list or is
|
||||||
that is automaticly updated (like a timestamp)
|
automatically updated (like a timestamp) or can be updated by before
|
||||||
|
update trigger defined on the table.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
is_key_used()
|
||||||
|
table TABLE object with which keys and fields are associated.
|
||||||
|
idx Key to be checked.
|
||||||
|
fields List of fields to be checked.
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
TRUE Key uses field which meets one the above conditions
|
||||||
|
FALSE Otherwise
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
|
bool is_key_used(TABLE *table, uint idx, List<Item> &fields)
|
||||||
{
|
{
|
||||||
|
Table_triggers_list *triggers= table->triggers;
|
||||||
List_iterator_fast<Item> f(fields);
|
List_iterator_fast<Item> f(fields);
|
||||||
KEY_PART_INFO *key_part,*key_part_end;
|
KEY_PART_INFO *key_part,*key_part_end;
|
||||||
for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
|
for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
|
||||||
@ -366,6 +379,9 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
|
|||||||
if (key_part->field->eq(field->field))
|
if (key_part->field->eq(field->field))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (triggers &&
|
||||||
|
triggers->is_updated_in_before_update_triggers(key_part->field))
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -374,7 +390,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 &&
|
if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY &&
|
||||||
(table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
|
(table->file->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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1113,7 +1113,7 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info,
|
|||||||
uint key_length);
|
uint key_length);
|
||||||
bool key_cmp_if_same(TABLE *form,const byte *key,uint index,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);
|
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, List<Item> &fields);
|
||||||
int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length);
|
int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length);
|
||||||
|
|
||||||
bool init_errmessage(void);
|
bool init_errmessage(void);
|
||||||
|
@ -6127,42 +6127,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(List<Item> *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(List<Item> *fields)
|
||||||
{
|
{
|
||||||
QUICK_RANGE_SELECT *quick;
|
QUICK_RANGE_SELECT *quick;
|
||||||
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
|
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
|
||||||
while ((quick= it++))
|
while ((quick= it++))
|
||||||
{
|
{
|
||||||
if (check_if_key_used(head, quick->index, *fields))
|
if (is_key_used(head, quick->index, *fields))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QUICK_ROR_INTERSECT_SELECT::check_if_keys_used(List<Item> *fields)
|
bool QUICK_ROR_INTERSECT_SELECT::is_keys_used(List<Item> *fields)
|
||||||
{
|
{
|
||||||
QUICK_RANGE_SELECT *quick;
|
QUICK_RANGE_SELECT *quick;
|
||||||
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
|
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
|
||||||
while ((quick= it++))
|
while ((quick= it++))
|
||||||
{
|
{
|
||||||
if (check_if_key_used(head, quick->index, *fields))
|
if (is_key_used(head, quick->index, *fields))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QUICK_ROR_UNION_SELECT::check_if_keys_used(List<Item> *fields)
|
bool QUICK_ROR_UNION_SELECT::is_keys_used(List<Item> *fields)
|
||||||
{
|
{
|
||||||
QUICK_SELECT_I *quick;
|
QUICK_SELECT_I *quick;
|
||||||
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
|
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
|
||||||
while ((quick= it++))
|
while ((quick= it++))
|
||||||
{
|
{
|
||||||
if (quick->check_if_keys_used(fields))
|
if (quick->is_keys_used(fields))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -225,8 +225,9 @@ public:
|
|||||||
Return 1 if any index used by this quick select
|
Return 1 if any index used by this quick select
|
||||||
a) uses field that is listed in passed field list or
|
a) uses field that is listed in passed field list or
|
||||||
b) is automatically updated (like a timestamp)
|
b) is automatically updated (like a timestamp)
|
||||||
|
c) can be updated by one of before update triggers defined on table
|
||||||
*/
|
*/
|
||||||
virtual bool check_if_keys_used(List<Item> *fields);
|
virtual bool is_keys_used(List<Item> *fields);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
rowid of last row retrieved by this quick select. This is used only when
|
rowid of last row retrieved by this quick select. This is used only when
|
||||||
@ -423,7 +424,7 @@ public:
|
|||||||
int get_type() { return QS_TYPE_INDEX_MERGE; }
|
int get_type() { return QS_TYPE_INDEX_MERGE; }
|
||||||
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
||||||
void add_info_string(String *str);
|
void add_info_string(String *str);
|
||||||
bool check_if_keys_used(List<Item> *fields);
|
bool is_keys_used(List<Item> *fields);
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
void dbug_dump(int indent, bool verbose);
|
void dbug_dump(int indent, bool verbose);
|
||||||
#endif
|
#endif
|
||||||
@ -482,7 +483,7 @@ public:
|
|||||||
int get_type() { return QS_TYPE_ROR_INTERSECT; }
|
int get_type() { return QS_TYPE_ROR_INTERSECT; }
|
||||||
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
||||||
void add_info_string(String *str);
|
void add_info_string(String *str);
|
||||||
bool check_if_keys_used(List<Item> *fields);
|
bool is_keys_used(List<Item> *fields);
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
void dbug_dump(int indent, bool verbose);
|
void dbug_dump(int indent, bool verbose);
|
||||||
#endif
|
#endif
|
||||||
@ -536,7 +537,7 @@ public:
|
|||||||
int get_type() { return QS_TYPE_ROR_UNION; }
|
int get_type() { return QS_TYPE_ROR_UNION; }
|
||||||
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
||||||
void add_info_string(String *str);
|
void add_info_string(String *str);
|
||||||
bool check_if_keys_used(List<Item> *fields);
|
bool is_keys_used(List<Item> *fields);
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
void dbug_dump(int indent, bool verbose);
|
void dbug_dump(int indent, bool verbose);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1547,6 +1547,38 @@ void Table_triggers_list::mark_fields_used(THD *thd, 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
|
Trigger BUG#14090 compatibility hook
|
||||||
|
|
||||||
|
@ -116,15 +116,12 @@ public:
|
|||||||
bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER]);
|
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 set_table(TABLE *new_table);
|
||||||
|
|
||||||
void mark_fields_used(THD *thd, trg_event_type event);
|
void mark_fields_used(THD *thd, trg_event_type event);
|
||||||
|
|
||||||
|
bool is_updated_in_before_update_triggers(Field *fld);
|
||||||
|
|
||||||
friend class Item_trigger_field;
|
friend class Item_trigger_field;
|
||||||
friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
||||||
TABLE_LIST *table);
|
TABLE_LIST *table);
|
||||||
|
@ -274,7 +274,7 @@ int mysql_update(THD *thd,
|
|||||||
{
|
{
|
||||||
used_index= select->quick->index;
|
used_index= select->quick->index;
|
||||||
used_key_is_modified= (!select->quick->unique_key_range() &&
|
used_key_is_modified= (!select->quick->unique_key_range() &&
|
||||||
select->quick->check_if_keys_used(&fields));
|
select->quick->is_keys_used(&fields));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -282,7 +282,7 @@ int mysql_update(THD *thd,
|
|||||||
if (used_index == MAX_KEY) // no index for sort order
|
if (used_index == MAX_KEY) // no index for sort order
|
||||||
used_index= table->file->key_used_on_scan;
|
used_index= table->file->key_used_on_scan;
|
||||||
if (used_index != MAX_KEY)
|
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, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (used_key_is_modified || order)
|
if (used_key_is_modified || order)
|
||||||
@ -1189,21 +1189,15 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
|
|||||||
return TRUE; // At most one matching row
|
return TRUE; // At most one matching row
|
||||||
case JT_REF:
|
case JT_REF:
|
||||||
case JT_REF_OR_NULL:
|
case JT_REF_OR_NULL:
|
||||||
return !check_if_key_used(table, join_tab->ref.key, *fields) &&
|
return !is_key_used(table, join_tab->ref.key, *fields);
|
||||||
!(table->triggers &&
|
|
||||||
table->triggers->has_before_update_triggers());
|
|
||||||
case JT_ALL:
|
case JT_ALL:
|
||||||
/* If range search on index */
|
/* If range search on index */
|
||||||
if (join_tab->quick)
|
if (join_tab->quick)
|
||||||
return !join_tab->quick->check_if_keys_used(fields) &&
|
return !join_tab->quick->is_keys_used(fields);
|
||||||
!(table->triggers &&
|
|
||||||
table->triggers->has_before_update_triggers());
|
|
||||||
/* If scanning in clustered key */
|
/* If scanning in clustered key */
|
||||||
if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
|
if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
|
||||||
table->s->primary_key < MAX_KEY)
|
table->s->primary_key < MAX_KEY)
|
||||||
return !check_if_key_used(table, table->s->primary_key, *fields) &&
|
return !is_key_used(table, table->s->primary_key, *fields);
|
||||||
!(table->triggers &&
|
|
||||||
table->triggers->has_before_update_triggers());
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
default:
|
default:
|
||||||
break; // Avoid compler warning
|
break; // Avoid compler warning
|
||||||
|
Loading…
x
Reference in New Issue
Block a user