diff options
author | unknown <mskold/marty@linux.site> | 2007-04-04 17:03:31 +0200 |
---|---|---|
committer | unknown <mskold/marty@linux.site> | 2007-04-04 17:03:31 +0200 |
commit | 7279edc72dd951e356db1cf9842115452a5773a1 (patch) | |
tree | dd37944358fa408c8d0b0528e0d416320718c1e4 | |
parent | 2770bf0bfcc73c78b37320c4662c27c72b9d9e12 (diff) | |
parent | 59a64c2b1604649be464d38141e2345c79d27bd3 (diff) | |
download | mariadb-git-7279edc72dd951e356db1cf9842115452a5773a1.tar.gz |
Merge mysql.com:/windows/Linux_space/MySQL/mysql-5.1
into mysql.com:/windows/Linux_space/MySQL/mysql-5.1-new-ndb
include/my_base.h:
Auto merged
sql/ha_ndbcluster.h:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/sql_delete.cc:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_load.cc:
Auto merged
sql/sql_trigger.h:
Auto merged
sql/sql_update.cc:
Auto merged
sql/ha_ndbcluster.cc:
Merge
-rw-r--r-- | include/my_base.h | 10 | ||||
-rw-r--r-- | mysql-test/r/ndb_trigger.result | 171 | ||||
-rw-r--r-- | mysql-test/t/ndb_trigger.test | 107 | ||||
-rw-r--r-- | sql/ha_ndbcluster.cc | 23 | ||||
-rw-r--r-- | sql/ha_ndbcluster.h | 2 | ||||
-rw-r--r-- | sql/mysql_priv.h | 1 | ||||
-rw-r--r-- | sql/sql_delete.cc | 26 | ||||
-rw-r--r-- | sql/sql_insert.cc | 46 | ||||
-rw-r--r-- | sql/sql_load.cc | 2 | ||||
-rw-r--r-- | sql/sql_trigger.h | 5 | ||||
-rw-r--r-- | sql/sql_update.cc | 26 |
11 files changed, 409 insertions, 10 deletions
diff --git a/include/my_base.h b/include/my_base.h index dd21362e8f7..37110810558 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -172,7 +172,15 @@ enum ha_extra_function { Off by default. */ HA_EXTRA_WRITE_CAN_REPLACE, - HA_EXTRA_WRITE_CANNOT_REPLACE + HA_EXTRA_WRITE_CANNOT_REPLACE, + /* + Inform handler that delete_row()/update_row() cannot batch deletes/updates + and should perform them immediately. This may be needed when table has + AFTER DELETE/UPDATE triggers which access to subject table. + These flags are reset by the handler::extra(HA_EXTRA_RESET) call. + */ + HA_EXTRA_DELETE_CANNOT_BATCH, + HA_EXTRA_UPDATE_CANNOT_BATCH }; /* The following is parameter to ha_panic() */ diff --git a/mysql-test/r/ndb_trigger.result b/mysql-test/r/ndb_trigger.result index 2aeca5db2d3..28f9f9bdc37 100644 --- a/mysql-test/r/ndb_trigger.result +++ b/mysql-test/r/ndb_trigger.result @@ -141,4 +141,175 @@ a b drop trigger t4_au; drop trigger t4_ad; drop table t1, t2, t3, t4, t5; +CREATE TABLE t1 ( +id INT NOT NULL PRIMARY KEY, +xy INT +) ENGINE=ndbcluster; +INSERT INTO t1 VALUES (1, 0); +CREATE TRIGGER t1_update AFTER UPDATE ON t1 FOR EACH ROW BEGIN REPLACE INTO t2 SELECT * FROM t1 WHERE t1.id = NEW.id; END // +CREATE TABLE t2 ( +id INT NOT NULL PRIMARY KEY, +xy INT +) ENGINE=ndbcluster; +INSERT INTO t2 VALUES (2, 0); +CREATE TABLE t3 (id INT NOT NULL PRIMARY KEY) ENGINE=ndbcluster; +INSERT INTO t3 VALUES (1); +CREATE TABLE t4 LIKE t1; +CREATE TRIGGER t4_update AFTER UPDATE ON t4 FOR EACH ROW BEGIN REPLACE INTO t5 SELECT * FROM t4 WHERE t4.id = NEW.id; END // +CREATE TABLE t5 LIKE t2; +UPDATE t1 SET xy = 3 WHERE id = 1; +SELECT xy FROM t1 where id = 1; +xy +3 +SELECT xy FROM t2 where id = 1; +xy +3 +UPDATE t1 SET xy = 4 WHERE id IN (SELECT id FROM t3 WHERE id = 1); +SELECT xy FROM t1 where id = 1; +xy +4 +SELECT xy FROM t2 where id = 1; +xy +4 +INSERT INTO t4 SELECT * FROM t1; +INSERT INTO t5 SELECT * FROM t2; +UPDATE t1,t4 SET t1.xy = 3, t4.xy = 3 WHERE t1.id = 1 AND t4.id = 1; +SELECT xy FROM t1 where id = 1; +xy +3 +SELECT xy FROM t2 where id = 1; +xy +3 +SELECT xy FROM t4 where id = 1; +xy +3 +SELECT xy FROM t5 where id = 1; +xy +3 +UPDATE t1,t4 SET t1.xy = 4, t4.xy = 4 WHERE t1.id IN (SELECT id FROM t3 WHERE id = 1) AND t4.id IN (SELECT id FROM t3 WHERE id = 1); +SELECT xy FROM t1 where id = 1; +xy +4 +SELECT xy FROM t2 where id = 1; +xy +4 +SELECT xy FROM t4 where id = 1; +xy +4 +SELECT xy FROM t5 where id = 1; +xy +4 +INSERT INTO t1 VALUES (1,0) ON DUPLICATE KEY UPDATE xy = 5; +SELECT xy FROM t1 where id = 1; +xy +5 +SELECT xy FROM t2 where id = 1; +xy +5 +DROP TRIGGER t1_update; +DROP TRIGGER t4_update; +CREATE TRIGGER t1_delete AFTER DELETE ON t1 FOR EACH ROW BEGIN REPLACE INTO t2 SELECT * FROM t1 WHERE t1.id > 4; END // +CREATE TRIGGER t4_delete AFTER DELETE ON t4 FOR EACH ROW BEGIN REPLACE INTO t5 SELECT * FROM t4 WHERE t4.id > 4; END // +INSERT INTO t1 VALUES (5, 0),(6,0); +INSERT INTO t2 VALUES (5, 1),(6,1); +INSERT INTO t3 VALUES (5); +SELECT * FROM t1 order by id; +id xy +1 5 +5 0 +6 0 +SELECT * FROM t2 order by id; +id xy +1 5 +2 0 +5 1 +6 1 +DELETE FROM t1 WHERE id IN (SELECT id FROM t3 WHERE id = 5); +SELECT * FROM t1 order by id; +id xy +1 5 +6 0 +SELECT * FROM t2 order by id; +id xy +1 5 +2 0 +5 1 +6 0 +INSERT INTO t1 VALUES (5,0); +UPDATE t2 SET xy = 1 WHERE id = 6; +TRUNCATE t4; +INSERT INTO t4 SELECT * FROM t1; +TRUNCATE t5; +INSERT INTO t5 SELECT * FROM t2; +SELECT * FROM t1 order by id; +id xy +1 5 +5 0 +6 0 +SELECT * FROM t2 order by id; +id xy +1 5 +2 0 +5 1 +6 1 +SELECT * FROM t4 order by id; +id xy +1 5 +5 0 +6 0 +SELECT * FROM t5 order by id; +id xy +1 5 +2 0 +5 1 +6 1 +DELETE FROM t1,t4 USING t1,t3,t4 WHERE t1.id IN (SELECT id FROM t3 WHERE id = 5) AND t4.id IN (SELECT id FROM t3 WHERE id = 5); +SELECT * FROM t1 order by id; +id xy +1 5 +6 0 +SELECT * FROM t2 order by id; +id xy +1 5 +2 0 +5 1 +6 0 +SELECT * FROM t4 order by id; +id xy +1 5 +6 0 +SELECT * FROM t5 order by id; +id xy +1 5 +2 0 +5 1 +6 0 +INSERT INTO t1 VALUES (5, 0); +REPLACE INTO t2 VALUES (6,1); +SELECT * FROM t1 order by id; +id xy +1 5 +5 0 +6 0 +SELECT * FROM t2 order by id; +id xy +1 5 +2 0 +5 1 +6 1 +REPLACE INTO t1 VALUES (5, 1); +SELECT * FROM t1 order by id; +id xy +1 5 +5 1 +6 0 +SELECT * FROM t2 order by id; +id xy +1 5 +2 0 +5 1 +6 0 +DROP TRIGGER t1_delete; +DROP TRIGGER t4_delete; +DROP TABLE t1, t2, t3, t4, t5; End of 5.1 tests diff --git a/mysql-test/t/ndb_trigger.test b/mysql-test/t/ndb_trigger.test index 7a4e58033a9..586fdc0ac97 100644 --- a/mysql-test/t/ndb_trigger.test +++ b/mysql-test/t/ndb_trigger.test @@ -110,5 +110,112 @@ drop trigger t4_au; drop trigger t4_ad; drop table t1, t2, t3, t4, t5; +# Test for bug#26242 +# Verify that AFTER UPDATE/DELETE triggers are executed +# after the change has actually taken place + +CREATE TABLE t1 ( + id INT NOT NULL PRIMARY KEY, + xy INT +) ENGINE=ndbcluster; + +INSERT INTO t1 VALUES (1, 0); + +DELIMITER //; +CREATE TRIGGER t1_update AFTER UPDATE ON t1 FOR EACH ROW BEGIN REPLACE INTO t2 SELECT * FROM t1 WHERE t1.id = NEW.id; END // +DELIMITER ;// + +CREATE TABLE t2 ( + id INT NOT NULL PRIMARY KEY, + xy INT +) ENGINE=ndbcluster; + +INSERT INTO t2 VALUES (2, 0); + +CREATE TABLE t3 (id INT NOT NULL PRIMARY KEY) ENGINE=ndbcluster; + +INSERT INTO t3 VALUES (1); + +CREATE TABLE t4 LIKE t1; + +DELIMITER //; +CREATE TRIGGER t4_update AFTER UPDATE ON t4 FOR EACH ROW BEGIN REPLACE INTO t5 SELECT * FROM t4 WHERE t4.id = NEW.id; END // +DELIMITER ;// + +CREATE TABLE t5 LIKE t2; + +UPDATE t1 SET xy = 3 WHERE id = 1; +SELECT xy FROM t1 where id = 1; +SELECT xy FROM t2 where id = 1; + +UPDATE t1 SET xy = 4 WHERE id IN (SELECT id FROM t3 WHERE id = 1); +SELECT xy FROM t1 where id = 1; +SELECT xy FROM t2 where id = 1; + +INSERT INTO t4 SELECT * FROM t1; +INSERT INTO t5 SELECT * FROM t2; +UPDATE t1,t4 SET t1.xy = 3, t4.xy = 3 WHERE t1.id = 1 AND t4.id = 1; +SELECT xy FROM t1 where id = 1; +SELECT xy FROM t2 where id = 1; +SELECT xy FROM t4 where id = 1; +SELECT xy FROM t5 where id = 1; + +UPDATE t1,t4 SET t1.xy = 4, t4.xy = 4 WHERE t1.id IN (SELECT id FROM t3 WHERE id = 1) AND t4.id IN (SELECT id FROM t3 WHERE id = 1); +SELECT xy FROM t1 where id = 1; +SELECT xy FROM t2 where id = 1; +SELECT xy FROM t4 where id = 1; +SELECT xy FROM t5 where id = 1; + +INSERT INTO t1 VALUES (1,0) ON DUPLICATE KEY UPDATE xy = 5; +SELECT xy FROM t1 where id = 1; +SELECT xy FROM t2 where id = 1; + +DROP TRIGGER t1_update; +DROP TRIGGER t4_update; + +DELIMITER //; +CREATE TRIGGER t1_delete AFTER DELETE ON t1 FOR EACH ROW BEGIN REPLACE INTO t2 SELECT * FROM t1 WHERE t1.id > 4; END // +DELIMITER ;// + +DELIMITER //; +CREATE TRIGGER t4_delete AFTER DELETE ON t4 FOR EACH ROW BEGIN REPLACE INTO t5 SELECT * FROM t4 WHERE t4.id > 4; END // +DELIMITER ;// + +INSERT INTO t1 VALUES (5, 0),(6,0); +INSERT INTO t2 VALUES (5, 1),(6,1); +INSERT INTO t3 VALUES (5); +SELECT * FROM t1 order by id; +SELECT * FROM t2 order by id; +DELETE FROM t1 WHERE id IN (SELECT id FROM t3 WHERE id = 5); +SELECT * FROM t1 order by id; +SELECT * FROM t2 order by id; + +INSERT INTO t1 VALUES (5,0); +UPDATE t2 SET xy = 1 WHERE id = 6; +TRUNCATE t4; +INSERT INTO t4 SELECT * FROM t1; +TRUNCATE t5; +INSERT INTO t5 SELECT * FROM t2; +SELECT * FROM t1 order by id; +SELECT * FROM t2 order by id; +SELECT * FROM t4 order by id; +SELECT * FROM t5 order by id; +DELETE FROM t1,t4 USING t1,t3,t4 WHERE t1.id IN (SELECT id FROM t3 WHERE id = 5) AND t4.id IN (SELECT id FROM t3 WHERE id = 5); +SELECT * FROM t1 order by id; +SELECT * FROM t2 order by id; +SELECT * FROM t4 order by id; +SELECT * FROM t5 order by id; + +INSERT INTO t1 VALUES (5, 0); +REPLACE INTO t2 VALUES (6,1); +SELECT * FROM t1 order by id; +SELECT * FROM t2 order by id; +REPLACE INTO t1 VALUES (5, 1); +SELECT * FROM t1 order by id; +SELECT * FROM t2 order by id; + +DROP TRIGGER t1_delete; +DROP TRIGGER t4_delete; +DROP TABLE t1, t2, t3, t4, t5; --echo End of 5.1 tests diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 5544dbd5fc6..08f29461bd8 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2999,8 +2999,13 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) if (thd->slave_thread) op->setAnyValue(thd->server_id); - // Execute update operation - if (!cursor && execute_no_commit(this,trans,FALSE) != 0) { + /* + Execute update operation if we are not doing a scan for update + and there exist UPDATE AFTER triggers + */ + + if ((!cursor || m_update_cannot_batch) && + execute_no_commit(this,trans,false) != 0) { no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); } @@ -3057,7 +3062,7 @@ int ha_ndbcluster::delete_row(const byte *record) if (thd->slave_thread) ((NdbOperation *)trans->getLastDefinedOperation())->setAnyValue(thd->server_id); - if (!m_primary_key_update) + if (!(m_primary_key_update || m_delete_cannot_batch)) // If deleting from cursor, NoCommit will be handled in next_result DBUG_RETURN(0); } @@ -3902,7 +3907,13 @@ int ha_ndbcluster::extra(enum ha_extra_function operation) DBUG_PRINT("info", ("Turning OFF use of write instead of insert")); m_use_write= FALSE; break; - default: + case HA_EXTRA_DELETE_CANNOT_BATCH: + DBUG_PRINT("info", ("HA_EXTRA_DELETE_CANNOT_BATCH")); + m_delete_cannot_batch= TRUE; + break; + case HA_EXTRA_UPDATE_CANNOT_BATCH: + DBUG_PRINT("info", ("HA_EXTRA_UPDATE_CANNOT_BATCH")); + m_update_cannot_batch= TRUE; break; } @@ -3913,6 +3924,8 @@ int ha_ndbcluster::extra(enum ha_extra_function operation) int ha_ndbcluster::reset() { DBUG_ENTER("ha_ndbcluster::reset"); + m_delete_cannot_batch= FALSE; + m_update_cannot_batch= FALSE; cond_clear(); /* Regular partition pruning will set the bitmap appropriately. @@ -5922,6 +5935,8 @@ ha_ndbcluster::ha_ndbcluster(handlerton *hton, TABLE_SHARE *table_arg): m_bulk_insert_rows((ha_rows) 1024), m_rows_changed((ha_rows) 0), m_bulk_insert_not_flushed(FALSE), + m_delete_cannot_batch(FALSE), + m_update_cannot_batch(FALSE), m_ops_pending(0), m_skip_auto_increment(TRUE), m_blobs_pending(0), diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 98f6efe15d8..fe79135a47d 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -964,6 +964,8 @@ private: ha_rows m_bulk_insert_rows; ha_rows m_rows_changed; bool m_bulk_insert_not_flushed; + bool m_delete_cannot_batch; + bool m_update_cannot_batch; ha_rows m_ops_pending; bool m_skip_auto_increment; bool m_blobs_pending; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2bc8c2071d2..ea3140531db 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1007,6 +1007,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields, bool ignore); int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *table_list); +void prepare_triggers_for_insert_stmt(TABLE *table); bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds); bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ha_rows rows, ulonglong options, diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d300edd6e18..4e865a12b66 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -221,7 +221,20 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, init_ftfuncs(thd, select_lex, 1); thd->proc_info="updating"; - will_batch= !table->file->start_bulk_delete(); + if (table->triggers && + table->triggers->has_triggers(TRG_EVENT_DELETE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER DELETE triggers that might access to subject table + and therefore might need delete to be done immediately. So we turn-off + the batching. + */ + (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH); + will_batch= FALSE; + } + else + will_batch= !table->file->start_bulk_delete(); table->mark_columns_needed_for_delete(); @@ -563,6 +576,17 @@ multi_delete::initialize_tables(JOIN *join) transactional_tables= 1; else normal_tables= 1; + if (tbl->triggers && + tbl->triggers->has_triggers(TRG_EVENT_DELETE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER DELETE triggers that might access to subject + table and therefore might need delete to be done immediately. + So we turn-off the batching. + */ + (void) tbl->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH); + } tbl->prepare_for_position(); tbl->mark_columns_needed_for_delete(); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index c0e0203ed86..9852c71e2b4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -341,6 +341,47 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, return 0; } +/* + Prepare triggers for INSERT-like statement. + + SYNOPSIS + prepare_triggers_for_insert_stmt() + table Table to which insert will happen + + NOTE + Prepare triggers for INSERT-like statement by marking fields + used by triggers and inform handlers that batching of UPDATE/DELETE + cannot be done if there are BEFORE UPDATE/DELETE triggers. +*/ + +void prepare_triggers_for_insert_stmt(TABLE *table) +{ + if (table->triggers) + { + if (table->triggers->has_triggers(TRG_EVENT_DELETE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER DELETE triggers that might access to + subject table and therefore might need delete to be done + immediately. So we turn-off the batching. + */ + (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH); + } + if (table->triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER UPDATE triggers that might access to subject + table and therefore might need update to be done immediately. + So we turn-off the batching. + */ + (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH); + } + } + table->mark_columns_needed_for_insert(); +} + bool mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, @@ -575,7 +616,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES))); - table->mark_columns_needed_for_insert(); + prepare_triggers_for_insert_stmt(table); + if (table_list->prepare_where(thd, 0, TRUE) || table_list->prepare_check_option(thd)) @@ -2646,7 +2688,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) table_list->prepare_check_option(thd)); if (!res) - table->mark_columns_needed_for_insert(); + prepare_triggers_for_insert_stmt(table); DBUG_RETURN(res); } diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 7d2c2281bba..4838474b115 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -226,7 +226,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_RETURN(TRUE); } - table->mark_columns_needed_for_insert(); + prepare_triggers_for_insert_stmt(table); uint tot_length=0; bool use_blobs= 0, use_vars= 0; diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 707fcc4e380..bc4cafe026a 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -110,6 +110,11 @@ public: const char *old_table, const char *new_db, const char *new_table); + bool has_triggers(trg_event_type event_type, + trg_action_time_type action_time) + { + return (bodies[event_type][action_time]); + } bool has_delete_triggers() { return (bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] || diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 0b4632edfbe..19c171b1be0 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -452,7 +452,20 @@ int mysql_update(THD *thd, (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES))); - will_batch= !table->file->start_bulk_update(); + if (table->triggers && + table->triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER UPDATE triggers that might access to subject + table and therefore might need update to be done immediately. + So we turn-off the batching. + */ + (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH); + will_batch= FALSE; + } + else + will_batch= !table->file->start_bulk_update(); /* We can use compare_record() to optimize away updates if @@ -1121,6 +1134,17 @@ int multi_update::prepare(List<Item> ¬_used_values, table->no_keyread=1; table->covering_keys.clear_all(); table->pos_in_table_list= tl; + if (table->triggers && + table->triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER UPDATE triggers that might access to subject + table and therefore might need update to be done immediately. + So we turn-off the batching. + */ + (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH); + } } } |