diff options
author | kostja@bodhi.(none) <> | 2007-07-31 23:47:38 +0400 |
---|---|---|
committer | kostja@bodhi.(none) <> | 2007-07-31 23:47:38 +0400 |
commit | 623876328186c4b5e3e484d0de7a55bbeabad96c (patch) | |
tree | 3aa1ba7703851e4f51c6bce20c1be35c0e039b74 /sql/lock.cc | |
parent | 78da884eecdeb408924881d5add13c2409977dd1 (diff) | |
parent | 4ebf43b0543d99896efa6378d2fe11d4a4f598e8 (diff) | |
download | mariadb-git-623876328186c4b5e3e484d0de7a55bbeabad96c.tar.gz |
Merge bk-internal.mysql.com:/home/bk/mysql-5.1
into bodhi.(none):/opt/local/work/mysql-5.1-runtime
Diffstat (limited to 'sql/lock.cc')
-rw-r--r-- | sql/lock.cc | 125 |
1 files changed, 109 insertions, 16 deletions
diff --git a/sql/lock.cc b/sql/lock.cc index 63d0807b975..08ff90ce983 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -116,16 +116,90 @@ static void print_lock_error(int error, const char *); static int thr_lock_errno_to_mysql[]= { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; +/** + Perform semantic checks for mysql_lock_tables. + @param thd The current thread + @param tables The tables to lock + @param count The number of tables to lock + @param flags Lock flags + @return 0 if all the check passed, non zero if a check failed. +*/ +int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags) +{ + bool log_table_write_query; + uint system_count; + uint i; + + DBUG_ENTER("mysql_lock_tables_check"); + + system_count= 0; + log_table_write_query= (is_log_table_write_query(thd->lex->sql_command) + || ((flags & MYSQL_LOCK_PERF_SCHEMA) != 0)); + + for (i=0 ; i<count; i++) + { + TABLE *t= tables[i]; + + /* Protect against 'fake' partially initialized TABLE_SHARE */ + DBUG_ASSERT(t->s->table_category != TABLE_UNKNOWN_CATEGORY); + + /* + Table I/O to performance schema tables is performed + only internally by the server implementation. + When a user is requesting a lock, the following + constraints are enforced: + */ + if (t->s->require_write_privileges() && + ! log_table_write_query) + { + /* + A user should not be able to prevent writes, + or hold any type of lock in a session, + since this would be a DOS attack. + */ + if ((t->reginfo.lock_type >= TL_READ_NO_INSERT) + || (thd->lex->sql_command == SQLCOM_LOCK_TABLES)) + { + my_error(ER_CANT_LOCK_LOG_TABLE, MYF(0)); + DBUG_RETURN(1); + } + } + + if ((t->s->table_category == TABLE_CATEGORY_SYSTEM) && + (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE)) + { + system_count++; + } + } + + /* + Locking of system tables is restricted: + locking a mix of system and non-system tables in the same lock + is prohibited, to prevent contention. + */ + if ((system_count > 0) && (system_count < count)) + { + my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0)); + DBUG_RETURN(1); + } + + DBUG_RETURN(0); +} + MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags, bool *need_reopen) { MYSQL_LOCK *sql_lock; TABLE *write_lock_used; int rc; + DBUG_ENTER("mysql_lock_tables"); *need_reopen= FALSE; + if (mysql_lock_tables_check(thd, tables, count, flags)) + DBUG_RETURN (NULL); + for (;;) { if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS, @@ -175,7 +249,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, thd->proc_info="System lock"; DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info)); - 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); @@ -271,6 +346,7 @@ static int lock_external(THD *thd, TABLE **tables, uint count) ((*tables)->reginfo.lock_type >= TL_READ && (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT)) lock_type=F_RDLCK; + if ((error=(*tables)->file->ha_external_lock(thd,lock_type))) { print_lock_error(error, (*tables)->file->table_type()); @@ -379,10 +455,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. + + @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) +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; @@ -396,6 +490,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; @@ -445,7 +543,8 @@ void mysql_lock_downgrade_write(THD *thd, TABLE *table, { MYSQL_LOCK *locked; TABLE *write_lock_used; - if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used))) + if ((locked = get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK, + &write_lock_used))) { for (uint i=0; i < locked->lock_count; i++) thr_downgrade_write_lock(locked->locks[i], new_lock_type); @@ -704,25 +803,19 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, TABLE **to, **table_buf; DBUG_ENTER("get_lock_data"); + DBUG_ASSERT((flags == GET_LOCK_UNLOCK) || (flags == GET_LOCK_STORE_LOCKS)); + DBUG_PRINT("info", ("count %d", count)); *write_lock_used=0; - uint system_count= 0; for (i=tables=lock_count=0 ; i < count ; i++) { - if (table_ptr[i]->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE) + TABLE *t= table_ptr[i]; + + if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE) { - tables+=table_ptr[i]->file->lock_count(); + tables+= t->file->lock_count(); lock_count++; } - /* - Check if we can lock the table. For some tables we cannot do that - beacause of handler-specific locking issues. - */ - if (!table_ptr[i]-> file-> - check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type, - table_ptr[i], count, i, &system_count, - logger.is_privileged_thread(thd))) - DBUG_RETURN(0); } /* |