diff options
author | kostja@bodhi.(none) <> | 2007-07-31 20:03:52 +0400 |
---|---|---|
committer | kostja@bodhi.(none) <> | 2007-07-31 20:03:52 +0400 |
commit | 1bf318b89558b7b18b9cf203fb0071de5b2b2580 (patch) | |
tree | e01a858aa56e916213cf8bdb5392ddf4dce83814 /sql | |
parent | 81905ee14fdfd83a33154103d854e9a773ae738c (diff) | |
parent | 80b48aea438babd21f8db508f610630cbd35075e (diff) | |
download | mariadb-git-1bf318b89558b7b18b9cf203fb0071de5b2b2580.tar.gz |
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into bodhi.(none):/opt/local/work/mysql-5.0-runtime
Diffstat (limited to 'sql')
-rw-r--r-- | sql/handler.h | 39 | ||||
-rw-r--r-- | sql/lock.cc | 35 | ||||
-rw-r--r-- | sql/mysql_priv.h | 3 | ||||
-rw-r--r-- | sql/opt_range.cc | 6 | ||||
-rw-r--r-- | sql/sp.cc | 4 | ||||
-rw-r--r-- | sql/sql_base.cc | 40 | ||||
-rw-r--r-- | sql/sql_class.h | 24 | ||||
-rw-r--r-- | sql/sql_lex.cc | 133 | ||||
-rw-r--r-- | sql/sql_table.cc | 6 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 2 | ||||
-rw-r--r-- | sql/table.cc | 129 | ||||
-rw-r--r-- | sql/table.h | 1 |
12 files changed, 260 insertions, 162 deletions
diff --git a/sql/handler.h b/sql/handler.h index a3767573178..2caf9b20945 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -508,6 +508,29 @@ class handler :public Sql_alloc */ virtual int rnd_init(bool scan) =0; virtual int rnd_end() { return 0; } + /** + Is not invoked for non-transactional temporary tables. + + Tells the storage engine that we intend to read or write data + from the table. This call is prefixed with a call to handler::store_lock() + and is invoked only for those handler instances that stored the lock. + + Calls to rnd_init/index_init are prefixed with this call. When table + IO is complete, we call external_lock(F_UNLCK). + A storage engine writer should expect that each call to + ::external_lock(F_[RD|WR]LOCK is followed by a call to + ::external_lock(F_UNLCK). If it is not, it is a bug in MySQL. + + The name and signature originate from the first implementation + in MyISAM, which would call fcntl to set/clear an advisory + lock on the data file in this method. + + @param lock_type F_RDLCK, F_WRLCK, F_UNLCK + + @return non-0 in case of failure, 0 in case of success. + When lock_type is F_UNLCK, the return value is ignored. + */ + virtual int external_lock(THD *thd, int lock_type) { return 0; } public: const handlerton *ht; /* storage engine of this handler */ @@ -548,6 +571,7 @@ public: uint raid_type,raid_chunks; FT_INFO *ft_handler; enum {NONE=0, INDEX, RND} inited; + bool locked; bool auto_increment_column_changed; bool implicit_emptied; /* Can be !=0 only if HEAP */ const COND *pushed_cond; @@ -560,10 +584,11 @@ public: create_time(0), check_time(0), update_time(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), ref_length(sizeof(my_off_t)), block_size(0), - raid_type(0), ft_handler(0), inited(NONE), implicit_emptied(0), + raid_type(0), ft_handler(0), inited(NONE), + locked(FALSE), implicit_emptied(0), pushed_cond(NULL) {} - virtual ~handler(void) { /* TODO: DBUG_ASSERT(inited == NONE); */ } + virtual ~handler(void) { DBUG_ASSERT(locked == FALSE); /* TODO: DBUG_ASSERT(inited == NONE); */ } virtual handler *clone(MEM_ROOT *mem_root); int ha_open(const char *name, int mode, int test_if_locked); void adjust_next_insert_id_after_explicit_value(ulonglong nr); @@ -597,6 +622,12 @@ public: virtual const char *index_type(uint key_number) { DBUG_ASSERT(0); return "";} + int ha_external_lock(THD *thd, int lock_type) + { + DBUG_ENTER("ha_external_lock"); + locked= lock_type != F_UNLCK; + DBUG_RETURN(external_lock(thd, lock_type)); + } int ha_index_init(uint idx) { DBUG_ENTER("ha_index_init"); @@ -689,7 +720,6 @@ public: virtual int extra_opt(enum ha_extra_function operation, ulong cache_size) { return extra(operation); } virtual int reset() { return extra(HA_EXTRA_RESET); } - virtual int external_lock(THD *thd, int lock_type) { return 0; } virtual void unlock_row() {} virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;} /* @@ -837,6 +867,9 @@ public: /* lock_count() can be more than one if the table is a MERGE */ virtual uint lock_count(void) const { return 1; } + /** + Is not invoked for non-transactional temporary tables. + */ virtual THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type)=0; diff --git a/sql/lock.cc b/sql/lock.cc index 93358e56701..f730ac56d35 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -151,7 +151,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, } thd->proc_info="System lock"; - if (lock_external(thd, tables, count)) + if (sql_lock->table_count && lock_external(thd, sql_lock->table, + sql_lock->table_count)) { /* Clear the lock type of all lock data to avoid reusage. */ reset_lock_data(sql_lock); @@ -246,12 +247,12 @@ static int lock_external(THD *thd, TABLE **tables, uint count) (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT)) lock_type=F_RDLCK; - if ((error=(*tables)->file->external_lock(thd,lock_type))) + if ((error= (*tables)->file->ha_external_lock(thd,lock_type))) { print_lock_error(error, (*tables)->file->table_type()); for (; i-- ; tables--) { - (*tables)->file->external_lock(thd, F_UNLCK); + (*tables)->file->ha_external_lock(thd, F_UNLCK); (*tables)->current_lock=F_UNLCK; } DBUG_RETURN(error); @@ -353,10 +354,28 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock) } +/** + Try to find the table in the list of locked tables. + In case of success, unlock the table and remove it from this list. -void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) + @note This function has a legacy side effect: the table is + unlocked even if it is not found in the locked list. + It's not clear if this side effect is intentional or still + desirable. It might lead to unmatched calls to + unlock_external(). Moreover, a discrepancy can be left + unnoticed by the storage engine, because in + unlock_external() we call handler::external_lock(F_UNLCK) only + if table->current_lock is not F_UNLCK. + + @param always_unlock specify explicitly if the legacy side + effect is desired. +*/ + +void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table, + bool always_unlock) { - mysql_unlock_some_tables(thd, &table,1); + if (always_unlock == TRUE) + mysql_unlock_some_tables(thd, &table, /* table count */ 1); if (locked) { reg1 uint i; @@ -370,6 +389,10 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) DBUG_ASSERT(table->lock_position == i); + /* Unlock if not yet unlocked */ + if (always_unlock == FALSE) + mysql_unlock_some_tables(thd, &table, /* table count */ 1); + /* Decrement table_count in advance, making below expressions easier */ old_tables= --locked->table_count; @@ -623,7 +646,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count) if ((*table)->current_lock != F_UNLCK) { (*table)->current_lock = F_UNLCK; - if ((error=(*table)->file->external_lock(thd, F_UNLCK))) + if ((error= (*table)->file->ha_external_lock(thd, F_UNLCK))) { error_code=error; print_lock_error(error_code, (*table)->file->table_type()); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index d14aab57489..ca6aa8ecab0 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1452,7 +1452,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); -void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); +void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table, + bool always_unlock); void mysql_lock_abort(THD *thd, TABLE *table); bool mysql_lock_abort_for_thread(THD *thd, TABLE *table); MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 247f0eada49..d978c8882ac 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -972,7 +972,7 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() DBUG_PRINT("info", ("Freeing separate handler 0x%lx (free: %d)", (long) file, free_file)); file->reset(); - file->external_lock(current_thd, F_UNLCK); + file->ha_external_lock(current_thd, F_UNLCK); file->close(); } } @@ -1142,7 +1142,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) /* Caller will free the memory */ goto failure; /* purecov: inspected */ } - if (file->external_lock(thd, F_RDLCK)) + if (file->ha_external_lock(thd, F_RDLCK)) goto failure; if (!head->no_keyread) { @@ -1152,7 +1152,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) if (file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) || init() || reset()) { - file->external_lock(thd, F_UNLCK); + file->ha_external_lock(thd, F_UNLCK); file->close(); goto failure; } diff --git a/sql/sp.cc b/sql/sp.cc index c0e7d5e2271..75d6fa4618f 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -273,7 +273,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) goto done; - if (table->s->fields != MYSQL_PROC_FIELD_COUNT) + if (table->s->fields < MYSQL_PROC_FIELD_COUNT) { ret= SP_GET_FIELD_FAILED; goto done; @@ -523,7 +523,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) strxmov(definer, thd->lex->definer->user.str, "@", thd->lex->definer->host.str, NullS); - if (table->s->fields != MYSQL_PROC_FIELD_COUNT) + if (table->s->fields < MYSQL_PROC_FIELD_COUNT) { ret= SP_GET_FIELD_FAILED; goto done; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 911e8d4d1f5..3cd0de922e5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1037,6 +1037,31 @@ TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) return 0; // Not a temporary table } + +/** + Drop a temporary table. + + Try to locate the table in the list of thd->temporary_tables. + If the table is found: + - if the table is in thd->locked_tables, unlock it and + remove it from the list of locked tables. Currently only transactional + temporary tables are present in the locked_tables list. + - Close the temporary table, remove its .FRM + - remove the table from the list of temporary tables + + This function is used to drop user temporary tables, as well as + internal tables created in CREATE TEMPORARY TABLE ... SELECT + or ALTER TABLE. Even though part of the work done by this function + is redundant when the table is internal, as long as we + link both internal and user temporary tables into the same + thd->temporary_tables list, it's impossible to tell here whether + we're dealing with an internal or a user temporary table. + + @retval TRUE the table was not found in the list of temporary tables + of this thread + @retval FALSE the table was found and dropped successfully. +*/ + bool close_temporary_table(THD *thd, const char *db, const char *table_name) { TABLE *table,**prev; @@ -1045,6 +1070,11 @@ bool close_temporary_table(THD *thd, const char *db, const char *table_name) return 1; table= *prev; *prev= table->next; + /* + If LOCK TABLES list is not empty and contains this table, + unlock the table and remove the table from this list. + */ + mysql_lock_remove(thd, thd->locked_tables, table, FALSE); close_temporary(table, 1); if (thd->slave_thread) --slave_open_temp_tables; @@ -1120,7 +1150,7 @@ TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find) !memcmp(list->s->table_cache_key, key, key_length)) { if (thd->locked_tables) - mysql_lock_remove(thd, thd->locked_tables,list); + mysql_lock_remove(thd, thd->locked_tables, list, TRUE); VOID(hash_delete(&open_cache,(byte*) list)); // Close table } else @@ -1151,6 +1181,8 @@ TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find) dropped is already unlocked. In the former case it will also remove lock on the table. But one should not rely on this behaviour as it may change in future. + Currently, however, this function is never called for a + table that was locked with LOCK TABLES. */ void drop_open_table(THD *thd, TABLE *table, const char *db_name, @@ -2099,7 +2131,7 @@ bool close_data_tables(THD *thd,const char *db, const char *table_name) if (!strcmp(table->s->table_name, table_name) && !strcmp(table->s->db, db)) { - mysql_lock_remove(thd, thd->locked_tables,table); + mysql_lock_remove(thd, thd->locked_tables, table, TRUE); table->file->close(); table->db_stat=0; } @@ -2239,7 +2271,7 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, instances of this table. */ mysql_lock_abort(thd, table); - mysql_lock_remove(thd, thd->locked_tables, table); + mysql_lock_remove(thd, thd->locked_tables, table, TRUE); /* We want to protect the table from concurrent DDL operations (like RENAME TABLE) until we will re-open and re-lock it. @@ -2343,7 +2375,7 @@ bool drop_locked_tables(THD *thd,const char *db, const char *table_name) if (!strcmp(table->s->table_name, table_name) && !strcmp(table->s->db, db)) { - mysql_lock_remove(thd, thd->locked_tables,table); + mysql_lock_remove(thd, thd->locked_tables, table, TRUE); VOID(hash_delete(&open_cache,(byte*) table)); found=1; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 058f130d4e7..6ffbd9b4ac7 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -995,13 +995,25 @@ enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1, class Open_tables_state { public: - /* - open_tables - list of regular tables in use by this thread - temporary_tables - list of temp tables in use by this thread - handler_tables - list of tables that were opened with HANDLER OPEN - and are still in use by this thread + /** + List of regular tables in use by this thread. Contains temporary and + base tables that were opened with @see open_tables(). + */ + TABLE *open_tables; + /** + List of temporary tables used by this thread. Contains user-level + temporary tables, created with CREATE TEMPORARY TABLE, and + internal temporary tables, created, e.g., to resolve a SELECT, + or for an intermediate table used in ALTER. + XXX Why are internal temporary tables added to this list? + */ + TABLE *temporary_tables; + /** + List of tables that were opened with HANDLER OPEN and are + still in use by this thread. */ - TABLE *open_tables, *temporary_tables, *handler_tables, *derived_tables; + TABLE *handler_tables; + TABLE *derived_tables; /* During a MySQL session, one can lock tables in two modes: automatic or manual. In automatic mode all necessary tables are locked just before diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index c37d77345b6..a62d8b383a5 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2035,12 +2035,129 @@ void st_select_lex_unit::set_limit(SELECT_LEX *sl) /** - Update the parsed tree with information about triggers that - may be fired when executing this statement. + @brief Set the initial purpose of this TABLE_LIST object in the list of used + tables. + + We need to track this information on table-by-table basis, since when this + table becomes an element of the pre-locked list, it's impossible to identify + which SQL sub-statement it has been originally used in. + + E.g.: + + User request: SELECT * FROM t1 WHERE f1(); + FUNCTION f1(): DELETE FROM t2; RETURN 1; + BEFORE DELETE trigger on t2: INSERT INTO t3 VALUES (old.a); + + For this user request, the pre-locked list will contain t1, t2, t3 + table elements, each needed for different DML. + + The trigger event map is updated to reflect INSERT, UPDATE, DELETE, + REPLACE, LOAD DATA, CREATE TABLE .. SELECT, CREATE TABLE .. + REPLACE SELECT statements, and additionally ON DUPLICATE KEY UPDATE + clause. */ void st_lex::set_trg_event_type_for_tables() { + uint8 new_trg_event_map= 0; + + /* + Some auxiliary operations + (e.g. GRANT processing) create TABLE_LIST instances outside + the parser. Additionally, some commands (e.g. OPTIMIZE) change + the lock type for a table only after parsing is done. Luckily, + these do not fire triggers and do not need to pre-load them. + For these TABLE_LISTs set_trg_event_type is never called, and + trg_event_map is always empty. That means that the pre-locking + algorithm will ignore triggers defined on these tables, if + any, and the execution will either fail with an assert in + sql_trigger.cc or with an error that a used table was not + pre-locked, in case of a production build. + + TODO: this usage pattern creates unnecessary module dependencies + and should be rewritten to go through the parser. + Table list instances created outside the parser in most cases + refer to mysql.* system tables. It is not allowed to have + a trigger on a system table, but keeping track of + initialization provides extra safety in case this limitation + is circumvented. + */ + + switch (sql_command) { + case SQLCOM_LOCK_TABLES: + /* + On a LOCK TABLE, all triggers must be pre-loaded for this TABLE_LIST + when opening an associated TABLE. + */ + new_trg_event_map= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_INSERT)) | + static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_UPDATE)) | + static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_DELETE)); + break; + /* + Basic INSERT. If there is an additional ON DUPLIATE KEY UPDATE + clause, it will be handled later in this method. + */ + case SQLCOM_INSERT: /* fall through */ + case SQLCOM_INSERT_SELECT: + /* + LOAD DATA ... INFILE is expected to fire BEFORE/AFTER INSERT + triggers. + If the statement also has REPLACE clause, it will be + handled later in this method. + */ + case SQLCOM_LOAD: /* fall through */ + /* + REPLACE is semantically equivalent to INSERT. In case + of a primary or unique key conflict, it deletes the old + record and inserts a new one. So we also may need to + fire ON DELETE triggers. This functionality is handled + later in this method. + */ + case SQLCOM_REPLACE: /* fall through */ + case SQLCOM_REPLACE_SELECT: + /* + CREATE TABLE ... SELECT defaults to INSERT if the table or + view already exists. REPLACE option of CREATE TABLE ... + REPLACE SELECT is handled later in this method. + */ + case SQLCOM_CREATE_TABLE: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_INSERT)); + break; + /* Basic update and multi-update */ + case SQLCOM_UPDATE: /* fall through */ + case SQLCOM_UPDATE_MULTI: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_UPDATE)); + break; + /* Basic delete and multi-delete */ + case SQLCOM_DELETE: /* fall through */ + case SQLCOM_DELETE_MULTI: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_DELETE)); + break; + default: + break; + } + + switch (duplicates) { + case DUP_UPDATE: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_UPDATE)); + break; + case DUP_REPLACE: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_DELETE)); + break; + case DUP_ERROR: + default: + break; + } + + /* Do not iterate over sub-selects, only the tables in the outermost SELECT_LEX can be modified, if any. @@ -2049,7 +2166,17 @@ void st_lex::set_trg_event_type_for_tables() while (tables) { - tables->set_trg_event_type(this); + /* + This is a fast check to filter out statements that do + not change data, or tables on the right side, in case of + INSERT .. SELECT, CREATE TABLE .. SELECT and so on. + Here we also filter out OPTIMIZE statement and non-updateable + views, for which lock_type is TL_UNLOCK or TL_READ after + parsing. + */ + if (static_cast<int>(tables->lock_type) >= + static_cast<int>(TL_WRITE_ALLOW_WRITE)) + tables->trg_event_map= new_trg_event_map; tables= tables->next_local; } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7db79543016..cc786c741c1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3811,7 +3811,7 @@ view_err: The following function call will free the new_table pointer, in close_temporary_table(), so we can safely directly jump to err */ - close_temporary_table(thd,new_db,tmp_name); + close_temporary_table(thd, new_db, tmp_name); goto err; } /* Close lock if this is a transactional table */ @@ -4084,7 +4084,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (!(copy= new Copy_field[to->s->fields])) DBUG_RETURN(-1); /* purecov: inspected */ - if (to->file->external_lock(thd, F_WRLCK)) + if (to->file->ha_external_lock(thd, F_WRLCK)) DBUG_RETURN(-1); /* We need external lock before we can disable/enable keys */ @@ -4236,7 +4236,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, free_io_cache(from); *copied= found_count; *deleted=delete_count; - if (to->file->external_lock(thd,F_UNLCK)) + if (to->file->ha_external_lock(thd,F_UNLCK)) error=1; DBUG_RETURN(error > 0 ? -1 : 0); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6fbd521e302..d71e756e91c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5567,7 +5567,7 @@ join_table: so that [INNER | CROSS] JOIN is properly nested as other left-associative joins. */ - table_ref %prec TABLE_REF_PRIORITY normal_join table_ref + table_ref normal_join table_ref %prec TABLE_REF_PRIORITY { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); } | table_ref STRAIGHT_JOIN table_factor { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; } diff --git a/sql/table.cc b/sql/table.cc index ce894e6910f..96ab9cb111e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1781,135 +1781,6 @@ void st_table::reset_item_list(List<Item> *item_list) const } } - -/** - Set the initial purpose of this TABLE_LIST object in the list of - used tables. We need to track this information on table-by- - table basis, since when this table becomes an element of the - pre-locked list, it's impossible to identify which SQL - sub-statement it has been originally used in. - - E.g.: - - User request: SELECT * FROM t1 WHERE f1(); - FUNCTION f1(): DELETE FROM t2; RETURN 1; - BEFORE DELETE trigger on t2: INSERT INTO t3 VALUES (old.a); - - For this user request, the pre-locked list will contain t1, t2, t3 - table elements, each needed for different DML. - - This method is called immediately after parsing for tables - of the table list of the top-level select lex. - - The trigger event map is updated to reflect INSERT, UPDATE, DELETE, - REPLACE, LOAD DATA, CREATE TABLE .. SELECT, CREATE TABLE .. - REPLACE SELECT statements, and additionally ON DUPLICATE KEY UPDATE - clause. -*/ - -void -TABLE_LIST::set_trg_event_type(const st_lex *lex) -{ - enum trg_event_type trg_event; - - /* - Some auxiliary operations - (e.g. GRANT processing) create TABLE_LIST instances outside - the parser. Additionally, some commands (e.g. OPTIMIZE) change - the lock type for a table only after parsing is done. Luckily, - these do not fire triggers and do not need to pre-load them. - For these TABLE_LISTs set_trg_event_type is never called, and - trg_event_map is always empty. That means that the pre-locking - algorithm will ignore triggers defined on these tables, if - any, and the execution will either fail with an assert in - sql_trigger.cc or with an error that a used table was not - pre-locked, in case of a production build. - - TODO: this usage pattern creates unnecessary module dependencies - and should be rewritten to go through the parser. - Table list instances created outside the parser in most cases - refer to mysql.* system tables. It is not allowed to have - a trigger on a system table, but keeping track of - initialization provides extra safety in case this limitation - is circumvented. - */ - - /* - This is a fast check to filter out statements that do - not change data, or tables on the right side, in case of - INSERT .. SELECT, CREATE TABLE .. SELECT and so on. - Here we also filter out OPTIMIZE statement and non-updateable - views, for which lock_type is TL_UNLOCK or TL_READ after - parsing. - */ - if (static_cast<int>(lock_type) < static_cast<int>(TL_WRITE_ALLOW_WRITE)) - return; - - switch (lex->sql_command) { - /* - Basic INSERT. If there is an additional ON DUPLIATE KEY UPDATE - clause, it will be handled later in this method. - */ - case SQLCOM_INSERT: /* fall through */ - case SQLCOM_INSERT_SELECT: - /* - LOAD DATA ... INFILE is expected to fire BEFORE/AFTER INSERT - triggers. - If the statement also has REPLACE clause, it will be - handled later in this method. - */ - case SQLCOM_LOAD: /* fall through */ - /* - REPLACE is semantically equivalent to INSERT. In case - of a primary or unique key conflict, it deletes the old - record and inserts a new one. So we also may need to - fire ON DELETE triggers. This functionality is handled - later in this method. - */ - case SQLCOM_REPLACE: /* fall through */ - case SQLCOM_REPLACE_SELECT: - /* - CREATE TABLE ... SELECT defaults to INSERT if the table or - view already exists. REPLACE option of CREATE TABLE ... - REPLACE SELECT is handled later in this method. - */ - case SQLCOM_CREATE_TABLE: - trg_event= TRG_EVENT_INSERT; - break; - /* Basic update and multi-update */ - case SQLCOM_UPDATE: /* fall through */ - case SQLCOM_UPDATE_MULTI: - trg_event= TRG_EVENT_UPDATE; - break; - /* Basic delete and multi-delete */ - case SQLCOM_DELETE: /* fall through */ - case SQLCOM_DELETE_MULTI: - trg_event= TRG_EVENT_DELETE; - break; - default: - /* - OK to return, since value of 'duplicates' is irrelevant - for non-updating commands. - */ - return; - } - trg_event_map|= static_cast<uint8>(1 << static_cast<int>(trg_event)); - - switch (lex->duplicates) { - case DUP_UPDATE: - trg_event= TRG_EVENT_UPDATE; - break; - case DUP_REPLACE: - trg_event= TRG_EVENT_DELETE; - break; - case DUP_ERROR: - default: - return; - } - trg_event_map|= static_cast<uint8>(1 << static_cast<int>(trg_event)); -} - - /* calculate md5 of query diff --git a/sql/table.h b/sql/table.h index f8f7d7f06b7..f411ce489c4 100644 --- a/sql/table.h +++ b/sql/table.h @@ -770,7 +770,6 @@ struct TABLE_LIST void reinit_before_use(THD *thd); Item_subselect *containing_subselect(); - void set_trg_event_type(const st_lex *lex); private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); |