diff options
Diffstat (limited to 'sql/sql_class.cc')
-rw-r--r-- | sql/sql_class.cc | 326 |
1 files changed, 162 insertions, 164 deletions
diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b1e416257ec..eb4d353db81 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -100,8 +100,8 @@ extern "C" void free_user_var(user_var_entry *entry) { char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry)); if (entry->value && entry->value != pos) - my_free(entry->value, MYF(0)); - my_free((char*) entry,MYF(0)); + my_free(entry->value); + my_free(entry); } bool Key_part_spec::operator==(const Key_part_spec& other) const @@ -493,7 +493,9 @@ THD::THD() rli_fake(0), lock_id(&main_lock_id), user_time(0), in_sub_stmt(0), - binlog_unsafe_warning_flags(0), binlog_table_maps(0), + binlog_unsafe_warning_flags(0), + stmt_accessed_table_flag(0), + binlog_table_maps(0), table_map_for_update(0), arg_of_last_insert_id_function(FALSE), first_successful_insert_id_in_prev_stmt(0), @@ -537,7 +539,7 @@ THD::THD() count_cuted_fields= CHECK_FIELD_IGNORE; killed= NOT_KILLED; col_access=0; - is_slave_error= thread_specific_used= thread_temporary_used= FALSE; + is_slave_error= thread_specific_used= FALSE; my_hash_clear(&handler_tables_hash); tmp_table=0; used_tables=0; @@ -1123,7 +1125,8 @@ THD::~THD() DBUG_PRINT("info", ("freeing security context")); main_security_ctx.destroy(); - safeFree(db); + my_free(db); + db= NULL; free_root(&transaction.mem_root,MYF(0)); mysys_var=0; // Safety (shouldn't be needed) mysql_mutex_destroy(&LOCK_thd_data); @@ -2962,10 +2965,18 @@ void Security_context::destroy() { // If not pointer to constant if (host != my_localhost) - safeFree(host); + { + my_free(host); + host= NULL; + } if (user != delayed_user) - safeFree(user); - safeFree(ip); + { + my_free(user); + user= NULL; + } + + my_free(ip); + ip= NULL; } @@ -2981,7 +2992,7 @@ void Security_context::skip_grants() bool Security_context::set_user(char *user_arg) { - safeFree(user); + my_free(user); user= my_strdup(user_arg, MYF(0)); return user == 0; } @@ -3473,7 +3484,7 @@ uchar *xid_get_hash_key(const uchar *ptr, size_t *length, void xid_free_hash(void *ptr) { if (!((XID_STATE*)ptr)->in_thd) - my_free((uchar*)ptr, MYF(0)); + my_free(ptr); } #ifdef HAVE_PSI_INTERFACE @@ -3689,7 +3700,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) capabilities. */ handler::Table_flags flags_write_some_set= 0; - handler::Table_flags flags_some_set= 0; + handler::Table_flags flags_access_some_set= 0; handler::Table_flags flags_write_all_set= HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE; @@ -3704,17 +3715,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) Innodb and Falcon; Innodb and MyIsam. */ my_bool multi_access_engine= FALSE; - /* - If non-transactional and transactional engines are about - to be accessed and any of them is about to be updated. - For example: Innodb and MyIsam. - */ - my_bool trans_non_trans_access_engines= FALSE; - /* - If all engines that are about to be updated are - transactional. - */ - my_bool all_trans_write_engines= TRUE; + TABLE* prev_write_table= NULL; TABLE* prev_access_table= NULL; @@ -3738,9 +3739,12 @@ int THD::decide_logging_format(TABLE_LIST *tables) { if (table->placeholder()) continue; + if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE) lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE); + handler::Table_flags const flags= table->table->file->ha_table_flags(); + DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx", table->table_name, flags)); if (table->lock_type >= TL_WRITE_ALLOW_WRITE) @@ -3748,177 +3752,171 @@ int THD::decide_logging_format(TABLE_LIST *tables) if (prev_write_table && prev_write_table->file->ht != table->table->file->ht) multi_write_engine= TRUE; - /* - Every temporary table must be always written to the binary - log in transaction boundaries and as such we artificially - classify them as transactional. - Indirectly, this avoids classifying a temporary table created - on a non-transactional engine as unsafe when it is modified - after any transactional table: + my_bool trans= table->table->file->has_transactions(); - BEGIN; - INSERT INTO innodb_t VALUES (1); - INSERT INTO myisam_t_temp VALUES (1); - COMMIT; - - BINARY LOG: + if (table->table->s->tmp_table) + set_stmt_accessed_table(trans ? STMT_WRITES_TEMP_TRANS_TABLE : + STMT_WRITES_TEMP_NON_TRANS_TABLE); + else + set_stmt_accessed_table(trans ? STMT_WRITES_TRANS_TABLE : + STMT_WRITES_NON_TRANS_TABLE); - BEGIN; - INSERT INTO innodb_t VALUES (1); - INSERT INTO myisam_t_temp VALUES (1); - COMMIT; - */ - all_trans_write_engines= all_trans_write_engines && - (table->table->file->has_transactions() || - table->table->s->tmp_table); - prev_write_table= table->table; flags_write_all_set &= flags; flags_write_some_set |= flags; - } - flags_some_set |= flags; - /* - The mixture of non-transactional and transactional tables must - identified and classified as unsafe. However, a temporary table - must be always handled as a transactional table. Based on that, - we have the following statements classified as mixed and by - consequence as unsafe: - - 1: INSERT INTO myisam_t SELECT * FROM innodb_t; - - 2: INSERT INTO innodb_t SELECT * FROM myisam_t; - - 3: INSERT INTO myisam_t SELECT * FROM myisam_t_temp; - - 4: INSERT INTO myisam_t_temp SELECT * FROM myisam_t; - 5: CREATE TEMPORARY TABLE myisam_t_temp SELECT * FROM mysiam_t; - - The following statements are not considered mixed and as such - are safe: - - 1: INSERT INTO innodb_t SELECT * FROM myisam_t_temp; + prev_write_table= table->table; + } + flags_access_some_set |= flags; - 2: INSERT INTO myisam_t_temp SELECT * FROM innodb_t_temp; - */ - if (!trans_non_trans_access_engines && prev_access_table && - (lex->sql_command != SQLCOM_CREATE_TABLE || + if (lex->sql_command != SQLCOM_CREATE_TABLE || (lex->sql_command == SQLCOM_CREATE_TABLE && - (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)))) + (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))) { - my_bool prev_trans; - my_bool act_trans; - if (prev_access_table->s->tmp_table || table->table->s->tmp_table) - { - prev_trans= prev_access_table->s->tmp_table ? TRUE : - prev_access_table->file->has_transactions(); - act_trans= table->table->s->tmp_table ? TRUE : - table->table->file->has_transactions(); - } + my_bool trans= table->table->file->has_transactions(); + + if (table->table->s->tmp_table) + set_stmt_accessed_table(trans ? STMT_READS_TEMP_TRANS_TABLE : + STMT_READS_TEMP_NON_TRANS_TABLE); else - { - prev_trans= prev_access_table->file->has_transactions(); - act_trans= table->table->file->has_transactions(); - } - trans_non_trans_access_engines= (prev_trans != act_trans); - multi_access_engine= TRUE; + set_stmt_accessed_table(trans ? STMT_READS_TRANS_TABLE : + STMT_READS_NON_TRANS_TABLE); } - thread_temporary_used |= table->table->s->tmp_table; + + if (prev_access_table && prev_access_table->file->ht != + table->table->file->ht) + multi_access_engine= TRUE; + prev_access_table= table->table; } DBUG_PRINT("info", ("flags_write_all_set: 0x%llx", flags_write_all_set)); DBUG_PRINT("info", ("flags_write_some_set: 0x%llx", flags_write_some_set)); - DBUG_PRINT("info", ("flags_some_set: 0x%llx", flags_some_set)); + DBUG_PRINT("info", ("flags_access_some_set: 0x%llx", flags_access_some_set)); DBUG_PRINT("info", ("multi_write_engine: %d", multi_write_engine)); DBUG_PRINT("info", ("multi_access_engine: %d", multi_access_engine)); - DBUG_PRINT("info", ("trans_non_trans_access_engines: %d", - trans_non_trans_access_engines)); int error= 0; int unsafe_flags; /* - Set the statement as unsafe if: - - . it is a mixed statement, i.e. access transactional and non-transactional - tables, and update any of them; - - or: - - . an early statement updated a transactional table; - . and, the current statement updates a non-transactional table. - - Any mixed statement is classified as unsafe to ensure that mixed mode is - completely safe. Consider the following example to understand why we - decided to do this: - - Note that mixed statements such as - - 1: INSERT INTO myisam_t SELECT * FROM innodb_t; - - 2: INSERT INTO innodb_t SELECT * FROM myisam_t; - - 3: INSERT INTO myisam_t SELECT * FROM myisam_t_temp; - - 4: INSERT INTO myisam_t_temp SELECT * FROM myisam_t; - - 5: CREATE TEMPORARY TABLE myisam_t_temp SELECT * FROM mysiam_t; + Classify a statement as unsafe when there is a mixed statement and an + on-going transaction at any point of the execution if: - are classified as unsafe to ensure that in mixed mode the execution is - completely safe and equivalent to the row mode. Consider the following - statements and sessions (connections) to understand the reason: + 1. The mixed statement is about to update a transactional table and + a non-transactional table. - con1: INSERT INTO innodb_t VALUES (1); - con1: INSERT INTO innodb_t VALUES (100); + 2. The mixed statement is about to update a temporary transactional + table and a non-transactional table. + + 3. The mixed statement is about to update a transactional table and + read from a non-transactional table. - con1: BEGIN - con2: INSERT INTO myisam_t SELECT * FROM innodb_t; - con1: INSERT INTO innodb_t VALUES (200); - con1: COMMIT; + 4. The mixed statement is about to update a temporary transactional + table and read from a non-transactional table. - The point is that the concurrent statements may be written into the binary log - in a way different from the execution. For example, + 5. The mixed statement is about to update a non-transactional table + and read from a transactional table when the isolation level is + lower than repeatable read. - BINARY LOG: + After updating a transactional table if: - con2: BEGIN; - con2: INSERT INTO myisam_t SELECT * FROM innodb_t; - con2: COMMIT; - con1: BEGIN - con1: INSERT INTO innodb_t VALUES (200); - con1: COMMIT; - - .... - - or - - BINARY LOG: - - con1: BEGIN - con1: INSERT INTO innodb_t VALUES (200); - con1: COMMIT; - con2: BEGIN; - con2: INSERT INTO myisam_t SELECT * FROM innodb_t; - con2: COMMIT; - - Clearly, this may become a problem in STMT mode and setting the statement - as unsafe will make rows to be written into the binary log in MIXED mode - and as such the problem will not stand. - - In STMT mode, although such statement is classified as unsafe, i.e. + 6. The mixed statement is about to update a non-transactional table + and read from a temporary transactional table. + + 7. The mixed statement is about to update a non-transactional table + and read from a temporary transactional table. + + 8. The mixed statement is about to update a non-transactionala table + and read from a temporary non-transactional table. + + 9. The mixed statement is about to update a temporary non-transactional + table and update a non-transactional table. + + 10. The mixed statement is about to update a temporary non-transactional + table and read from a non-transactional table. + + 11. A statement is about to update a non-transactional table and the + option variables.binlog_direct_non_trans_update is OFF. + + The reason for this is that locks acquired may not protected a concurrent + transaction of interfering in the current execution and by consequence in + the result. In particular, if there is an on-going transaction and a + transactional table was already updated, a temporary table must be written + to the binary log in the boundaries of the on-going transaction and as + such we artificially classify them as transactional. + */ + if (in_multi_stmt_transaction_mode()) + { + my_bool mixed_unsafe= FALSE; + my_bool non_trans_unsafe= FALSE; + + /* Case 1. */ + if (stmt_accessed_table(STMT_WRITES_TRANS_TABLE) && + stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 2. */ + else if (stmt_accessed_table(STMT_WRITES_TEMP_TRANS_TABLE) && + stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 3. */ + else if (stmt_accessed_table(STMT_WRITES_TRANS_TABLE) && + stmt_accessed_table(STMT_READS_NON_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 4. */ + else if (stmt_accessed_table(STMT_WRITES_TEMP_TRANS_TABLE) && + stmt_accessed_table(STMT_READS_NON_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 5. */ + else if (stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE) && + stmt_accessed_table(STMT_READS_TRANS_TABLE) && + tx_isolation < ISO_REPEATABLE_READ) + /* + By default, InnoDB operates in REPEATABLE READ and with the option + --innodb-locks-unsafe-for-binlog disabled. In this case, InnoDB uses + next-key locks for searches and index scans, which prevents phantom + rows. + + This is scenario is safe for Innodb. However, there are no means to + transparently get this information. Therefore, we need to improve this + and change the storage engines to report somehow when an execution is + safe under an isolation level & binary logging format. + */ + mixed_unsafe= TRUE; - INSERT INTO myisam_t SELECT * FROM innodb_t; + if (trans_has_updated_trans_table(this)) + { + /* Case 6. */ + if (stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE) && + stmt_accessed_table(STMT_READS_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 7. */ + else if (stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE) && + stmt_accessed_table(STMT_READS_TEMP_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 8. */ + else if (stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE) && + stmt_accessed_table(STMT_READS_TEMP_NON_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 9. */ + else if (stmt_accessed_table(STMT_WRITES_TEMP_NON_TRANS_TABLE) && + stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 10. */ + else if (stmt_accessed_table(STMT_WRITES_TEMP_NON_TRANS_TABLE) && + stmt_accessed_table(STMT_READS_NON_TRANS_TABLE)) + mixed_unsafe= TRUE; + /* Case 11. */ + else if (!variables.binlog_direct_non_trans_update && + stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE)) + non_trans_unsafe= TRUE; + } - there is no enough information to avoid writing it outside the boundaries - of a transaction. This is not a problem if we are considering snapshot - isolation level but if we have pure repeatable read or serializable the - lock history on the slave will be different from the master. - */ - if (trans_non_trans_access_engines) - lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT); - else if (trans_has_updated_trans_table(this) && !all_trans_write_engines) - lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS); + if (mixed_unsafe) + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT); + else if (non_trans_unsafe) + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS); + } /* If more than one engine is involved in the statement and at @@ -3930,7 +3928,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) (flags_write_some_set & HA_HAS_OWN_BINLOGGING)) my_error((error= ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE), MYF(0)); - else if (multi_access_engine && flags_some_set & HA_HAS_OWN_BINLOGGING) + else if (multi_access_engine && flags_access_some_set & HA_HAS_OWN_BINLOGGING) lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE); /* both statement-only and row-only engines involved */ @@ -4293,7 +4291,7 @@ CPP_UNNAMED_NS_START ~Row_data_memory() { if (m_memory != 0 && m_release_memory_on_destruction) - my_free((uchar*) m_memory, MYF(MY_WME)); + my_free(m_memory); } /** |