diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/ha_innodb.cc | 199 | ||||
-rw-r--r-- | sql/ha_ndbcluster.cc | 10 | ||||
-rw-r--r-- | sql/ha_ndbcluster.h | 2 | ||||
-rw-r--r-- | sql/ha_partition.cc | 1 | ||||
-rw-r--r-- | sql/handler.cc | 174 | ||||
-rw-r--r-- | sql/item_xmlfunc.cc | 12 | ||||
-rw-r--r-- | sql/log.cc | 207 | ||||
-rw-r--r-- | sql/log.h | 19 | ||||
-rw-r--r-- | sql/log_event.cc | 37 | ||||
-rw-r--r-- | sql/mysql_priv.h | 11 | ||||
-rw-r--r-- | sql/mysqld.cc | 45 | ||||
-rw-r--r-- | sql/set_var.cc | 241 | ||||
-rw-r--r-- | sql/set_var.h | 45 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 4 | ||||
-rw-r--r-- | sql/sql_base.cc | 36 | ||||
-rw-r--r-- | sql/sql_class.cc | 31 | ||||
-rw-r--r-- | sql/sql_class.h | 40 | ||||
-rw-r--r-- | sql/sql_delete.cc | 47 | ||||
-rw-r--r-- | sql/sql_insert.cc | 110 | ||||
-rw-r--r-- | sql/sql_lex.cc | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 7 | ||||
-rw-r--r-- | sql/sql_table.cc | 70 | ||||
-rw-r--r-- | sql/sql_update.cc | 9 | ||||
-rw-r--r-- | sql/table.cc | 11 | ||||
-rw-r--r-- | sql/unireg.cc | 1 |
26 files changed, 1073 insertions, 300 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 742f9ce7631..ec504cc1266 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -135,6 +135,7 @@ extern "C" { #include "../storage/innobase/include/fil0fil.h" #include "../storage/innobase/include/trx0xa.h" #include "../storage/innobase/include/thr0loc.h" +#include "../storage/innobase/include/ha_prototypes.h" } #define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */ @@ -668,6 +669,61 @@ innobase_get_cset_width( } /********************************************************************** +Converts an identifier to a table name. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +void +innobase_convert_from_table_id( +/*===========================*/ + char* to, /* out: converted identifier */ + const char* from, /* in: identifier to convert */ + ulint len) /* in: length of 'to', in bytes */ +{ + uint errors; + + strconvert(current_thd->charset(), from, + &my_charset_filename, to, len, &errors); +} + +/********************************************************************** +Converts an identifier to UTF-8. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +void +innobase_convert_from_id( +/*=====================*/ + char* to, /* out: converted identifier */ + const char* from, /* in: identifier to convert */ + ulint len) /* in: length of 'to', in bytes */ +{ + uint errors; + + strconvert(current_thd->charset(), from, + system_charset_info, to, len, &errors); +} + +/********************************************************************** +Removes the filename encoding of a table or database name. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +void +innobase_convert_from_filename( +/*===========================*/ + char* s) /* in: identifier; out: decoded identifier */ +{ + uint errors; + + strconvert(&my_charset_filename, s, + system_charset_info, s, strlen(s), &errors); +} + +/********************************************************************** Compares NUL-terminated UTF-8 strings case insensitively. NOTE that the exact prototype of this function has to be in @@ -697,6 +753,21 @@ innobase_casedn_str( my_casedn_str(system_charset_info, a); } +/************************************************************************** +Determines the connection character set. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +struct charset_info_st* +innobase_get_charset( +/*=================*/ + /* out: connection character set */ + void* mysql_thd) /* in: MySQL thread handle */ +{ + return(((THD*) mysql_thd)->charset()); +} + /************************************************************************* Creates a temporary file. */ extern "C" @@ -743,6 +814,25 @@ innobase_mysql_tmpfile(void) } /************************************************************************* +Wrapper around MySQL's copy_and_convert function, see it for +documentation. */ +extern "C" +ulint +innobase_convert_string( +/*====================*/ + void* to, + ulint to_length, + CHARSET_INFO* to_cs, + const void* from, + ulint from_length, + CHARSET_INFO* from_cs, + uint* errors) +{ + return(copy_and_convert((char*)to, to_length, to_cs, + (const char*)from, from_length, from_cs, errors)); +} + +/************************************************************************* Gets the InnoDB transaction handle for a MySQL handler object, creates an InnoDB transaction struct if the corresponding MySQL thread struct still lacks one. */ @@ -1076,23 +1166,69 @@ innobase_invalidate_query_cache( } /********************************************************************* -Get the quote character to be used in SQL identifiers. +Display an SQL identifier. This definition must match the one in innobase/ut/ut0ut.c! */ extern "C" -int -mysql_get_identifier_quote_char( -/*============================*/ - /* out: quote character to be - used in SQL identifiers; EOF if none */ +void +innobase_print_identifier( +/*======================*/ + FILE* f, /* in: output stream */ trx_t* trx, /* in: transaction */ + ibool table_id,/* in: TRUE=decode table name */ const char* name, /* in: name to print */ ulint namelen)/* in: length of name */ { + const char* s = name; + char* qname = NULL; + int q; + + if (table_id) { + /* Decode the table name. The filename_to_tablename() + function expects a NUL-terminated string. The input and + output strings buffers must not be shared. The function + only produces more output when the name contains other + characters than [0-9A-Z_a-z]. */ + char* temp_name = my_malloc(namelen + 1, MYF(MY_WME)); + uint qnamelen = namelen + + (1 + sizeof srv_mysql50_table_name_prefix); + + if (temp_name) { + qname = my_malloc(qnamelen, MYF(MY_WME)); + if (qname) { + memcpy(temp_name, name, namelen); + temp_name[namelen] = 0; + s = qname; + namelen = filename_to_tablename(temp_name, + qname, qnamelen); + } + my_free(temp_name, MYF(0)); + } + } + if (!trx || !trx->mysql_thd) { - return(EOF); + + q = '"'; + } else { + q = get_quote_char_for_identifier((THD*) trx->mysql_thd, + s, (int) namelen); + } + + if (q == EOF) { + fwrite(s, 1, namelen, f); + } else { + const char* e = s + namelen; + putc(q, f); + while (s < e) { + int c = *s++; + if (c == q) { + putc(c, f); + } + putc(c, f); + } + putc(q, f); } - return(get_quote_char_for_identifier((THD*) trx->mysql_thd, - name, (int) namelen)); + + my_free(qname, MYF(MY_ALLOW_ZERO_PTR)); } /************************************************************************** @@ -1232,6 +1368,24 @@ innobase_init(void) ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR); +#ifdef UNIV_DEBUG + static const char test_filename[] = "-@"; + char test_tablename[sizeof test_filename + + sizeof srv_mysql50_table_name_prefix]; + if ((sizeof test_tablename) - 1 + != filename_to_tablename(test_filename, test_tablename, + sizeof test_tablename) + || strncmp(test_tablename, + srv_mysql50_table_name_prefix, + sizeof srv_mysql50_table_name_prefix) + || strcmp(test_tablename + + sizeof srv_mysql50_table_name_prefix, + test_filename)) { + sql_print_error("tablename encoding has been changed"); + goto error; + } +#endif /* UNIV_DEBUG */ + /* Check that values don't overflow on 32-bit systems. */ if (sizeof(ulint) == 4) { if (innobase_buffer_pool_size > UINT_MAX32) { @@ -2200,8 +2354,7 @@ ha_innobase::open( /* Get pointer to a table object in InnoDB dictionary cache */ - ib_table = dict_table_get_and_increment_handle_count( - norm_name, NULL); + ib_table = dict_table_get_and_increment_handle_count(norm_name); if (NULL == ib_table) { ut_print_timestamp(stderr); @@ -4116,6 +4269,9 @@ ha_innobase::index_prev( mysql_byte* buf) /* in/out: buffer for previous row in MySQL format */ { + statistic_increment(current_thd->status_var.ha_read_prev_count, + &LOCK_status); + return(general_fetch(buf, ROW_SEL_PREV, 0)); } @@ -4652,7 +4808,7 @@ ha_innobase::create( /* Get the transaction associated with the current thd, or create one if not yet created */ - parent_trx = check_trx_exists(current_thd); + parent_trx = check_trx_exists(thd); /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads */ @@ -4748,20 +4904,9 @@ ha_innobase::create( } } - if (current_thd->query != NULL) { - LEX_STRING q; - - if (thd->convert_string(&q, system_charset_info, - current_thd->query, - current_thd->query_length, - current_thd->charset())) { - error = HA_ERR_OUT_OF_MEM; - - goto cleanup; - } - + if (thd->query != NULL) { error = row_table_add_foreign_constraints(trx, - q.str, norm_name, + thd->query, norm_name, create_info->options & HA_LEX_CREATE_TMP_TABLE); error = convert_error_code_to_mysql(error, NULL); @@ -4781,7 +4926,7 @@ ha_innobase::create( log_buffer_flush_to_disk(); - innobase_table = dict_table_get(norm_name, NULL); + innobase_table = dict_table_get(norm_name); DBUG_ASSERT(innobase_table != 0); @@ -4917,7 +5062,7 @@ ha_innobase::delete_table( /* Get the transaction associated with the current thd, or create one if not yet created */ - parent_trx = check_trx_exists(current_thd); + parent_trx = check_trx_exists(thd); /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads */ diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 2d623702670..7afa2852e1a 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -735,7 +735,7 @@ int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg) ha_ndbcluster *ha= (ha_ndbcluster *)arg; int ret= get_ndb_blobs_value(ha->table, ha->m_value, ha->m_blobs_buffer, ha->m_blobs_buffer_size, - 0); + ha->m_blobs_offset); DBUG_RETURN(ret); } @@ -864,6 +864,7 @@ int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field, if (ndb_blob != NULL) { // Set callback + m_blobs_offset= buf - (byte*) table->record[0]; void *arg= (void *)this; DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0); } @@ -5477,6 +5478,7 @@ ha_ndbcluster::ha_ndbcluster(TABLE_SHARE *table_arg): m_ops_pending(0), m_skip_auto_increment(TRUE), m_blobs_pending(0), + m_blobs_offset(0), m_blobs_buffer(0), m_blobs_buffer_size(0), m_dupkey((uint) -1), @@ -10364,7 +10366,7 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, table->field[c++]->set_null(); // DELETED_ROWS table->field[c++]->set_null(); // UPDATE_COUNT table->field[c++]->store(lfg.getUndoFreeWords()); // FREE_EXTENTS - table->field[c++]->store(lfg.getUndoBufferSize()); // TOTAL_EXTENTS + table->field[c++]->store(uf.getSize()/4); // TOTAL_EXTENTS table->field[c++]->store(4); // EXTENT_SIZE table->field[c++]->store(uf.getSize()); // INITIAL_SIZE @@ -10394,8 +10396,8 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, table->field[c++]->store("NORMAL", 6, system_charset_info); - char extra[30]; - int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u",id); + char extra[100]; + int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u;UNDO_BUFFER_SIZE=%lu",id,lfg.getUndoBufferSize()); table->field[c]->store(extra, len, system_charset_info); schema_table_store_record(thd, table); } diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 9bcc549cb60..df3c5791713 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -798,7 +798,6 @@ private: int get_ndb_value(NdbOperation*, Field *field, uint fieldnr, byte*); int get_ndb_partition_id(NdbOperation *); friend int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg); - int get_ndb_blobs_value(NdbBlob *last_ndb_blob); int set_primary_key(NdbOperation *op, const byte *key); int set_primary_key_from_record(NdbOperation *op, const byte *record); int set_index_key_from_record(NdbOperation *op, const byte *record, @@ -883,6 +882,7 @@ private: ha_rows m_ops_pending; bool m_skip_auto_increment; bool m_blobs_pending; + my_ptrdiff_t m_blobs_offset; // memory for blobs in one tuple char *m_blobs_buffer; uint32 m_blobs_buffer_size; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 93fb6409f9f..7929257d608 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1565,6 +1565,7 @@ error: void ha_partition::update_create_info(HA_CREATE_INFO *create_info) { + m_file[0]->update_create_info(create_info); return; } diff --git a/sql/handler.cc b/sql/handler.cc index 47bcf1caba1..0895c6cf454 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -53,6 +53,8 @@ st_plugin_int *hton2plugin[MAX_HA]; static handlerton *installed_htons[128]; +#define BITMAP_STACKBUF_SIZE (128/8) + KEY_CREATE_INFO default_key_create_info= { HA_KEY_ALG_UNDEF, 0, {NullS,0} }; /* static functions defined in this file */ @@ -3234,35 +3236,128 @@ namespace { } } -template<class RowsEventT> int binlog_log_row(TABLE *table, - const byte *before_record, - const byte *after_record) +/* + Write table maps for all (manually or automatically) locked tables + to the binary log. + + SYNOPSIS + write_locked_table_maps() + thd Pointer to THD structure + + DESCRIPTION + This function will generate and write table maps for all tables + that are locked by the thread 'thd'. Either manually locked + (stored in THD::locked_tables) and automatically locked (stored + in THD::lock) are considered. + + RETURN VALUE + 0 All OK + 1 Failed to write all table maps + + SEE ALSO + THD::lock + THD::locked_tables + */ +namespace { - if (table->file->is_injective()) - return 0; - bool error= 0; + int write_locked_table_maps(THD *thd) + { + DBUG_ENTER("write_locked_table_maps"); + DBUG_PRINT("enter", ("thd=%p, thd->lock=%p, thd->locked_tables=%p", + thd, thd->lock, thd->locked_tables)); + + if (thd->get_binlog_table_maps() == 0) + { + /* + Exactly one table has to be locked, otherwise this code is not + guaranteed to work. + */ + DBUG_ASSERT((thd->lock != NULL) + (thd->locked_tables != NULL) == 1); + + MYSQL_LOCK *lock= thd->lock ? thd->lock : thd->locked_tables; + DBUG_ASSERT(lock->table_count > 0); + TABLE **const end_ptr= lock->table + lock->table_count; + for (TABLE **table_ptr= lock->table ; + table_ptr != end_ptr ; + ++table_ptr) + { + TABLE *const table= *table_ptr; + DBUG_PRINT("info", ("Checking table %s", table->s->table_name)); + if (table->current_lock == F_WRLCK && + check_table_binlog_row_based(thd, table)) + { + int const has_trans= table->file->has_transactions(); + int const error= thd->binlog_write_table_map(table, has_trans); + /* + If an error occurs, it is the responsibility of the caller to + roll back the transaction. + */ + if (unlikely(error)) + DBUG_RETURN(1); + } + } + } + DBUG_RETURN(0); + } - if (check_table_binlog_row_based(table->in_use, table)) + template<class RowsEventT> int + binlog_log_row(TABLE* table, + const byte *before_record, + const byte *after_record) { - error= - RowsEventT::binlog_row_logging_function(table->in_use, table, - table->file->has_transactions(), - &table->s->all_set, - table->s->fields, - before_record, after_record); + if (table->file->is_injective()) + return 0; + bool error= 0; + THD *const thd= table->in_use; + + if (check_table_binlog_row_based(thd, table)) + { + MY_BITMAP cols; + /* Potential buffer on the stack for the bitmap */ + uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)]; + uint n_fields= table->s->fields; + my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8; + + /* + If there are no table maps written to the binary log, this is + the first row handled in this statement. In that case, we need + to write table maps for all locked tables to the binary log. + */ + if (likely(!(error= bitmap_init(&cols, + use_bitbuf ? bitbuf : NULL, + (n_fields + 7) & ~7UL, + false)))) + { + bitmap_set_all(&cols); + if (likely(!(error= write_locked_table_maps(thd)))) + { + error= + RowsEventT::binlog_row_logging_function(thd, table, + table->file->has_transactions(), + &cols, table->s->fields, + before_record, after_record); + } + if (!use_bitbuf) + bitmap_free(&cols); + } + } + return error ? HA_ERR_RBR_LOGGING_FAILED : 0; } - return error ? HA_ERR_RBR_LOGGING_FAILED : 0; -} + /* + Instantiate the versions we need for the above template function, + because we have -fno-implicit-template as compiling option. + */ -/* - Instantiate the versions we need for the above template function, because we - have -fno-implicit-template as compiling option. -*/ + template int + binlog_log_row<Write_rows_log_event>(TABLE *, const byte *, const byte *); + + template int + binlog_log_row<Delete_rows_log_event>(TABLE *, const byte *, const byte *); -template int binlog_log_row<Write_rows_log_event>(TABLE *, const byte *, const byte *); -template int binlog_log_row<Delete_rows_log_event>(TABLE *, const byte *, const byte *); -template int binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const byte *); + template int + binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const byte *); +} #endif /* HAVE_ROW_BASED_REPLICATION */ @@ -3272,41 +3367,6 @@ int handler::ha_external_lock(THD *thd, int lock_type) int error; if (unlikely(error= external_lock(thd, lock_type))) DBUG_RETURN(error); -#ifdef HAVE_ROW_BASED_REPLICATION - if (table->file->is_injective()) - DBUG_RETURN(0); - - /* - There is a number of statements that are logged statement-based - but call external lock. For these, we do not need to generate a - table map. - - TODO: The need for this switch is an indication that the model for - locking combined with row-based replication needs to be looked - over. Ideally, no such special handling should be needed. - */ - switch (thd->lex->sql_command) { - case SQLCOM_TRUNCATE: - case SQLCOM_ALTER_TABLE: - case SQLCOM_OPTIMIZE: - case SQLCOM_REPAIR: - DBUG_RETURN(0); - default: - break; - } - - /* - If we are locking a table for writing, we generate a table map. - For all other kinds of locks, we don't do anything. - */ - if (lock_type == F_WRLCK && check_table_binlog_row_based(thd, table)) - { - int const has_trans= table->file->has_transactions(); - error= thd->binlog_write_table_map(table, has_trans); - if (unlikely(error)) - DBUG_RETURN(error); - } -#endif DBUG_RETURN(0); } diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 17e8db90dc7..fb5ca083eab 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -2601,7 +2601,17 @@ String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf) xml_enter(&p, raw_xml->ptr(), 0); /* Execute XML parser */ - rc= my_xml_parse(&p, raw_xml->ptr(), raw_xml->length()); + if ((rc= my_xml_parse(&p, raw_xml->ptr(), raw_xml->length())) != MY_XML_OK) + { + char buf[128]; + my_snprintf(buf, sizeof(buf)-1, "parse error at line %d pos %d: %s", + my_xml_error_lineno(&p) + 1, + my_xml_error_pos(&p) + 1, + my_xml_error_string(&p)); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WRONG_VALUE, + ER(ER_WRONG_VALUE), "XML", buf); + } my_xml_parser_free(&p); return rc == MY_XML_OK ? parsed_xml_buf : 0; diff --git a/sql/log.cc b/sql/log.cc index d05a8e4202f..7168f7e2da7 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -37,9 +37,6 @@ #define MAX_USER_HOST_SIZE 512 #define MAX_TIME_SIZE 32 -/* we need this for log files intialization */ -extern char *opt_logname, *opt_slow_logname; - LOGGER logger; MYSQL_BIN_LOG mysql_bin_log; @@ -63,6 +60,13 @@ sql_print_message_func sql_print_message_handlers[3] = }; +char *make_default_log_name(char *buff,const char* log_ext) +{ + strmake(buff, glob_hostname, FN_REFLEN-5); + return fn_format(buff, buff, mysql_data_home, log_ext, + MYF(MY_UNPACK_FILENAME|MY_APPEND_EXT)); +} + /* This is a POD. Please keep it that way! @@ -71,11 +75,17 @@ sql_print_message_func sql_print_message_handlers[3] = struct binlog_trx_data { bool empty() const { +#ifdef HAVE_ROW_BASED_REPLICATION return pending == NULL && my_b_tell(&trans_log) == 0; +#else + return my_b_tell(&trans_log) == 0; +#endif } binlog_trx_data() {} IO_CACHE trans_log; // The transaction cache +#ifdef HAVE_ROW_BASED_REPLICATION Rows_log_event *pending; // The pending binrows event +#endif }; handlerton binlog_hton; @@ -250,8 +260,10 @@ bool Log_to_csv_event_handler::reopen_log_table(uint log_table_type) void Log_to_csv_event_handler::cleanup() { - close_log_table(QUERY_LOG_GENERAL, FALSE); - close_log_table(QUERY_LOG_SLOW, FALSE); + if (opt_log) + close_log_table(QUERY_LOG_GENERAL, FALSE); + if (opt_slow_log) + close_log_table(QUERY_LOG_SLOW, FALSE); logger.is_log_tables_initialized= FALSE; } @@ -505,10 +517,10 @@ bool Log_to_file_event_handler::init() if (!is_initialized) { if (opt_slow_log) - mysql_slow_log.open_slow_log(opt_slow_logname); + mysql_slow_log.open_slow_log(sys_var_slow_log_path.value); if (opt_log) - mysql_log.open_query_log(opt_logname); + mysql_log.open_query_log(sys_var_general_log_path.value); is_initialized= TRUE; } @@ -525,8 +537,11 @@ void Log_to_file_event_handler::cleanup() void Log_to_file_event_handler::flush() { - mysql_log.reopen_file(); - mysql_slow_log.reopen_file(); + /* reopen log files */ + if (opt_log) + mysql_log.reopen_file(); + if (opt_slow_log) + mysql_slow_log.reopen_file(); } /* @@ -646,10 +661,7 @@ bool LOGGER::flush_logs(THD *thd) close_general_log.db= (char*) "mysql"; close_general_log.db_length= 5; - /* reopen log files */ - file_log_handler->flush(); - - /* flush tables, in the case they are enabled */ + /* lock tables, in the case they are enabled */ if (logger.is_log_tables_initialized) { /* @@ -662,23 +674,29 @@ bool LOGGER::flush_logs(THD *thd) Here we use one of the logger handler THD's. Simply because it seems appropriate. */ - lock_and_wait_for_table_name(table_log_handler->general_log_thd, - &close_slow_log); - lock_and_wait_for_table_name(table_log_handler->general_log_thd, - &close_general_log); + if (opt_slow_log) + lock_and_wait_for_table_name(table_log_handler->general_log_thd, + &close_slow_log); + if (opt_log) + lock_and_wait_for_table_name(table_log_handler->general_log_thd, + &close_general_log); + } - /* - Deny others from logging to general and slow log, - while reopening tables. - */ - logger.lock(); + /* + Deny others from logging to general and slow log, + while reopening tables. + */ + logger.lock(); + + /* reopen log files */ + file_log_handler->flush(); + /* flush tables, in the case they are enabled */ + if (logger.is_log_tables_initialized) table_log_handler->flush(table_log_handler->general_log_thd, &close_slow_log, &close_general_log); - - /* end of log tables flush */ - logger.unlock(); - } + /* end of log flush */ + logger.unlock(); return FALSE; } @@ -726,6 +744,11 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, return 0; lock(); + if (!opt_slow_log) + { + unlock(); + return 0; + } /* fill in user_host value: the format is "%s[%s] @ %s [%s]" */ user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE, @@ -800,6 +823,11 @@ bool LOGGER::general_log_print(THD *thd, enum enum_server_command command, id=0; /* Log from connect handler */ lock(); + if (!opt_log) + { + unlock(); + return 0; + } time_t current_time= time(NULL); user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE, @@ -903,26 +931,126 @@ void LOGGER::init_general_log(uint general_log_printer) } +bool LOGGER::activate_log_handler(THD* thd, uint log_type) +{ + bool res= 0; + lock(); + switch (log_type) { + case QUERY_LOG_SLOW: + if (!opt_slow_log) + { + if ((res= reopen_log_table(log_type))) + goto err; + file_log_handler->get_mysql_slow_log()-> + open_slow_log(sys_var_slow_log_path.value); + init_slow_log(log_output_options); + opt_slow_log= TRUE; + } + break; + case QUERY_LOG_GENERAL: + if (!opt_log) + { + if ((res= reopen_log_table(log_type))) + goto err; + file_log_handler->get_mysql_log()-> + open_query_log(sys_var_general_log_path.value); + init_general_log(log_output_options); + opt_log= TRUE; + } + break; + default: + DBUG_ASSERT(0); + } +err: + unlock(); + return res; +} + + +void LOGGER::deactivate_log_handler(THD *thd, uint log_type) +{ + TABLE_LIST *table_list; + my_bool *tmp_opt= 0; + MYSQL_LOG *file_log; + THD *log_thd; + + switch (log_type) { + case QUERY_LOG_SLOW: + table_list= &table_log_handler->slow_log; + tmp_opt= &opt_slow_log; + file_log= file_log_handler->get_mysql_slow_log(); + log_thd= table_log_handler->slow_log_thd; + break; + case QUERY_LOG_GENERAL: + table_list= &table_log_handler->general_log; + tmp_opt= &opt_log; + file_log= file_log_handler->get_mysql_log(); + log_thd= table_log_handler->general_log_thd; + break; + default: + DBUG_ASSERT(0); + } + + if (!(*tmp_opt)) + return; + + if (is_log_tables_initialized) + lock_and_wait_for_table_name(log_thd, table_list); + lock(); + + if (is_log_tables_initialized) + { + VOID(pthread_mutex_lock(&LOCK_open)); + close_log_table(log_type, TRUE); + table_list->table= 0; + query_cache_invalidate3(log_thd, table_list, 0); + unlock_table_name(log_thd, table_list); + VOID(pthread_mutex_unlock(&LOCK_open)); + } + file_log->close(0); + *tmp_opt= FALSE; + unlock(); +} + + bool Log_to_csv_event_handler::flush(THD *thd, TABLE_LIST *close_slow_log, TABLE_LIST *close_general_log) { VOID(pthread_mutex_lock(&LOCK_open)); - close_log_table(QUERY_LOG_GENERAL, TRUE); - close_log_table(QUERY_LOG_SLOW, TRUE); - close_general_log->next_local= close_slow_log; - query_cache_invalidate3(thd, close_general_log, 0); - unlock_table_name(thd, close_slow_log); - unlock_table_name(thd, close_general_log); + if (opt_log) + { + close_log_table(QUERY_LOG_GENERAL, TRUE); + query_cache_invalidate3(thd, close_general_log, 0); + unlock_table_name(thd, close_general_log); + } + if (opt_slow_log) + { + close_log_table(QUERY_LOG_SLOW, TRUE); + query_cache_invalidate3(thd, close_slow_log, 0); + unlock_table_name(thd, close_slow_log); + } VOID(pthread_mutex_unlock(&LOCK_open)); - return reopen_log_table(QUERY_LOG_SLOW) || - reopen_log_table(QUERY_LOG_GENERAL); + /* + we use | and not || here, to ensure that both reopen_log_table + are called, even if the first one fails + */ + if ((opt_slow_log && reopen_log_table(QUERY_LOG_SLOW)) | + (opt_log && reopen_log_table(QUERY_LOG_GENERAL))) + return 1; + return 0; } /* the parameters are unused for the log tables */ bool Log_to_csv_event_handler::init() { - /* we always open log tables. even if the logging is disabled */ - return (open_log_table(QUERY_LOG_GENERAL) || open_log_table(QUERY_LOG_SLOW)); + /* + we use | and not || here, to ensure that both open_log_table + are called, even if the first one fails + */ + if ((opt_log && open_log_table(QUERY_LOG_GENERAL)) | + (opt_slow_log && open_log_table(QUERY_LOG_SLOW))) + return 1; + return 0; } int LOGGER::set_handlers(uint error_log_printer, @@ -1073,6 +1201,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, Log_event *end_ev) #endif error= mysql_bin_log.write(thd, trans_log, end_ev); } +#ifdef HAVE_ROW_BASED_REPLICATION else { #ifdef HAVE_ROW_BASED_REPLICATION @@ -1092,6 +1221,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, Log_event *end_ev) transaction cache. */ mysql_bin_log.update_table_map_version(); +#endif statistic_increment(binlog_cache_use, &LOCK_status); if (trans_log->disk_writes != 0) @@ -2919,7 +3049,6 @@ bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) } -#ifdef HAVE_ROW_BASED_REPLICATION /* These functions are placed in this file since they need access to binlog_hton, which has internal linkage. @@ -2955,6 +3084,7 @@ int THD::binlog_setup_trx_data() engine has registered for the transaction. */ +#ifdef HAVE_ROW_BASED_REPLICATION int THD::binlog_write_table_map(TABLE *table, bool is_trans) { int error; @@ -3133,9 +3263,9 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) we are inside a stored function, we do not end the statement since this will close all tables on the slave. */ +#ifdef HAVE_ROW_BASED_REPLICATION bool const end_stmt= thd->prelocked_mode && thd->lex->requires_prelocking(); -#ifdef HAVE_ROW_BASED_REPLICATION thd->binlog_flush_pending_rows_event(end_stmt); #endif /*HAVE_ROW_BASED_REPLICATION*/ @@ -3186,7 +3316,6 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) (binlog_trx_data*) thd->ha_data[binlog_hton.slot]; IO_CACHE *trans_log= &trx_data->trans_log; bool trans_log_in_use= my_b_tell(trans_log) != 0; - if (event_info->get_cache_stmt() && !trans_log_in_use) trans_register_ha(thd, (thd->options & diff --git a/sql/log.h b/sql/log.h index 8a41cd9053d..b4818a370d7 100644 --- a/sql/log.h +++ b/sql/log.h @@ -467,6 +467,8 @@ public: CHARSET_INFO *client_cs); void flush(); void init_pthread_objects(); + MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; } + MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; } }; @@ -539,8 +541,21 @@ public: void init_error_log(uint error_log_printer); void init_slow_log(uint slow_log_printer); void init_general_log(uint general_log_printer); - }; - + void deactivate_log_handler(THD* thd, uint log_type); + bool activate_log_handler(THD* thd, uint log_type); + MYSQL_QUERY_LOG *get_slow_log_file_handler() + { + if (file_log_handler) + return file_log_handler->get_mysql_slow_log(); + return NULL; + } + MYSQL_QUERY_LOG *get_log_file_handler() + { + if (file_log_handler) + return file_log_handler->get_mysql_log(); + return NULL; + } +}; enum enum_binlog_format { BINLOG_FORMAT_STMT= 0, // statement-based diff --git a/sql/log_event.cc b/sql/log_event.cc index 78ab54aeb79..bf876366879 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5595,6 +5595,7 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) bool Rows_log_event::write_data_header(IO_CACHE *file) { byte buf[ROWS_HEADER_LEN]; // No need to init the buffer + DBUG_ASSERT(m_table_id != ~0UL); DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { int4store(buf + 0, m_table_id); @@ -5845,9 +5846,7 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) /* Open the table if it is not already open and add the table to table map. - If the table should not be replicated, we don't bother to do anything. - The table map will return NULL and the row-level event will effectively - be a no-op. + Note that for any table that should not be replicated, a filter is needed. */ uint count; /* @@ -5863,34 +5862,14 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) /* Error reporting borrowed from Query_log_event with many excessive simplifications (we don't honour --slave-skip-errors) - - BUG: There can be extreneous table maps in the binary log, - so in case we fail to open the table, we just generate a - warning and do not add the table to the list of tables to - open and lock. */ uint actual_error= thd->net.last_errno; - switch (actual_error) - { - case ER_NO_SUCH_TABLE: - slave_print_msg(WARNING_LEVEL, rli, actual_error, - thd->net.last_error ? - thd->net.last_error : - "<no message>"); - clear_all_errors(thd, rli); - rli->inc_event_relay_log_pos(); // Continue with next event - error= 0; - break; - - default: - slave_print_msg(ERROR_LEVEL, rli, actual_error, - "Error '%s' on opening table `%s`.`%s`", - (actual_error ? thd->net.last_error : - "unexpected success or fatal error"), - table_list->db, table_list->table_name); - thd->query_error= 1; - break; - } + slave_print_msg(ERROR_LEVEL, rli, actual_error, + "Error '%s' on opening table `%s`.`%s`", + (actual_error ? thd->net.last_error : + "unexpected success or fatal error"), + table_list->db, table_list->table_name); + thd->query_error= 1; } DBUG_RETURN(error); } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 6f10e812f3e..4db622cc541 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -862,7 +862,8 @@ int prepare_create_field(create_field *sql_field, bool mysql_create_table(THD *thd,const char *db, const char *table_name, HA_CREATE_INFO *create_info, List<create_field> &fields, List<Key> &keys, - bool tmp_table, uint select_field_count); + bool tmp_table, uint select_field_count, + bool use_copy_create_info); bool mysql_alter_table(THD *thd, char *new_db, char *new_name, HA_CREATE_INFO *create_info, @@ -1167,6 +1168,7 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db, void remove_db_from_cache(const char *db); void flush_tables(); bool is_equal(const LEX_STRING *a, const LEX_STRING *b); +char *make_default_log_name(char *buff,const char* log_ext); #ifdef WITH_PARTITION_STORAGE_ENGINE uint fast_alter_partition_table(THD *thd, TABLE *table, @@ -1514,7 +1516,9 @@ extern bool opt_endinfo, using_udf_functions; extern my_bool locked_in_memory; extern bool opt_using_transactions, mysqld_embedded; extern bool using_update_log, opt_large_files, server_id_supplied; -extern bool opt_log, opt_update_log, opt_bin_log, opt_slow_log, opt_error_log; +extern bool opt_update_log, opt_bin_log, opt_error_log; +extern my_bool opt_log, opt_slow_log; +extern uint log_output_options; extern my_bool opt_log_queries_not_using_indexes; extern bool opt_disable_networking, opt_skip_show_db; extern my_bool opt_character_set_client_handshake; @@ -1536,6 +1540,8 @@ extern my_bool opt_enable_shared_memory; extern char *default_tz_name; extern my_bool opt_large_pages; extern uint opt_large_page_size; +extern char *opt_logname, *opt_slow_logname; +extern const char *log_output_str; extern MYSQL_BIN_LOG mysql_bin_log; extern LOGGER logger; @@ -1582,6 +1588,7 @@ extern TABLE *unused_tables; extern const char* any_db; extern struct my_option my_long_options[]; extern const LEX_STRING view_type; +extern TYPELIB log_output_typelib; /* optional things, have_* variables */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f2d7191f130..67c25e65b66 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -302,16 +302,15 @@ arg_cmp_func Arg_comparator::comparator_matrix[5][2] = {&Arg_comparator::compare_row, &Arg_comparator::compare_e_row}, {&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal}}; -const char *log_output_names[] = -{ "NONE", "FILE", "TABLE", NullS}; +const char *log_output_names[] = { "NONE", "FILE", "TABLE", NullS}; +static const unsigned int log_output_names_len[]= { 4, 4, 5, 0 }; TYPELIB log_output_typelib= {array_elements(log_output_names)-1,"", - log_output_names, NULL}; + log_output_names, + (unsigned int *) log_output_names_len}; /* static variables */ /* the default log output is log tables */ -static const char *log_output_str= "TABLE"; -static ulong log_output_options= LOG_TABLE; static bool lower_case_table_names_used= 0; static bool volatile select_thread_in_use, signal_thread_in_use; static bool volatile ready_to_exit; @@ -342,7 +341,9 @@ static my_bool opt_sync_bdb_logs; /* Global variables */ -bool opt_log, opt_update_log, opt_bin_log, opt_slow_log; +bool opt_update_log, opt_bin_log; +my_bool opt_log, opt_slow_log; +uint log_output_options; my_bool opt_log_queries_not_using_indexes= 0; bool opt_error_log= IF_WIN(1,0); bool opt_disable_networking=0, opt_skip_show_db=0; @@ -520,6 +521,7 @@ ulong thread_id=1L,current_pid; ulong slow_launch_threads = 0, sync_binlog_period; ulong expire_logs_days = 0; ulong rpl_recovery_rank=0; +const char *log_output_str= "TABLE"; double log_10[32]; /* 10 potences */ time_t start_time; @@ -1229,6 +1231,8 @@ void clean_up(bool print_message) free_defaults(defaults_argv); my_free(sys_init_connect.value, MYF(MY_ALLOW_ZERO_PTR)); my_free(sys_init_slave.value, MYF(MY_ALLOW_ZERO_PTR)); + my_free(sys_var_general_log_path.value, MYF(MY_ALLOW_ZERO_PTR)); + my_free(sys_var_slow_log_path.value, MYF(MY_ALLOW_ZERO_PTR)); free_tmpdir(&mysql_tmpdir_list); #ifdef HAVE_REPLICATION my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); @@ -2607,6 +2611,7 @@ static bool init_global_datetime_format(timestamp_type format_type, static int init_common_variables(const char *conf_file_name, int argc, char **argv, const char **groups) { + char buff[FN_REFLEN]; umask(((~my_umask) & 0666)); my_decimal_set_zero(&decimal_zero); // set decimal_zero constant; tzset(); // Set tzname @@ -2763,6 +2768,16 @@ static int init_common_variables(const char *conf_file_name, int argc, else sys_init_slave.value=my_strdup("",MYF(0)); + if (!opt_logname) + opt_logname= make_default_log_name(buff, ".log"); + sys_var_general_log_path.value= my_strdup(opt_logname, MYF(0)); + sys_var_general_log_path.value_length= strlen(opt_logname); + + if (!opt_slow_logname) + opt_slow_logname= make_default_log_name(buff, "-slow.log"); + sys_var_slow_log_path.value= my_strdup(opt_slow_logname, MYF(0)); + sys_var_slow_log_path.value_length= strlen(opt_slow_logname); + if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1)) return 1; if (my_database_names_init()) @@ -4816,7 +4831,9 @@ enum options_mysqld OPT_TABLE_LOCK_WAIT_TIMEOUT, OPT_PLUGIN_DIR, OPT_LOG_OUTPUT, - OPT_PORT_OPEN_TIMEOUT + OPT_PORT_OPEN_TIMEOUT, + OPT_GENERAL_LOG, + OPT_SLOW_LOG }; @@ -5046,6 +5063,9 @@ Disable with --skip-bdb (will save memory).", "Set up signals usable for debugging", (gptr*) &opt_debugging, (gptr*) &opt_debugging, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"general-log", OPT_GENERAL_LOG, + "Enable|disable general log", (gptr*) &opt_log, + (gptr*) &opt_log, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, #ifdef HAVE_LARGE_PAGES {"large-pages", OPT_ENABLE_LARGE_PAGES, "Enable support for large pages. \ Disable with --skip-large-pages.", @@ -5621,6 +5641,9 @@ replicating a LOAD DATA INFILE command.", "Tells the slave thread to continue replication when a query returns an error from the provided list.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif + {"slow-query-log", OPT_SLOW_LOG, + "Enable|disable slow query log", (gptr*) &opt_slow_log, + (gptr*) &opt_slow_log, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"socket", OPT_SOCKET, "Socket file to use for connection.", (gptr*) &mysqld_unix_port, (gptr*) &mysqld_unix_port, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -6156,7 +6179,8 @@ The minimum value for this variable is 4096.", "Each thread that does a sequential scan allocates a buffer of this size for each table it scans. If you do many sequential scans, you may want to increase this value.", (gptr*) &global_system_variables.read_buff_size, (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG, - 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, SSIZE_MAX, MALLOC_OVERHEAD, IO_SIZE, + 0}, {"read_only", OPT_READONLY, "Make all non-temporary tables read-only, with the exception for replication (slave) threads and users with the SUPER privilege", (gptr*) &opt_readonly, @@ -6167,12 +6191,12 @@ The minimum value for this variable is 4096.", (gptr*) &global_system_variables.read_rnd_buff_size, (gptr*) &max_system_variables.read_rnd_buff_size, 0, GET_ULONG, REQUIRED_ARG, 256*1024L, IO_SIZE*2+MALLOC_OVERHEAD, - ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + SSIZE_MAX, MALLOC_OVERHEAD, IO_SIZE, 0}, {"record_buffer", OPT_RECORD_BUFFER, "Alias for read_buffer_size", (gptr*) &global_system_variables.read_buff_size, (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG, - 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, SSIZE_MAX, MALLOC_OVERHEAD, IO_SIZE, 0}, #ifdef HAVE_REPLICATION {"relay_log_purge", OPT_RELAY_LOG_PURGE, "0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.", @@ -6930,6 +6954,7 @@ static void mysql_init_variables(void) opt_skip_slave_start= opt_reckless_slave = 0; mysql_home[0]= pidfile_name[0]= log_error_file[0]= 0; opt_log= opt_update_log= opt_slow_log= 0; + log_output_options= find_bit_type(log_output_str, &log_output_typelib); opt_bin_log= 0; opt_disable_networking= opt_skip_show_db=0; opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname= 0; diff --git a/sql/set_var.cc b/sql/set_var.cc index a44395c74ca..e6b0625f097 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -55,6 +55,7 @@ #include <my_getopt.h> #include <thr_alarm.h> #include <myisam.h> +#include <my_dir.h> #include "event_scheduler.h" @@ -164,6 +165,11 @@ static byte *get_error_count(THD *thd); static byte *get_warning_count(THD *thd); static byte *get_prepared_stmt_count(THD *thd); static byte *get_tmpdir(THD *thd); +static int sys_check_log_path(THD *thd, set_var *var); +static bool sys_update_general_log_path(THD *thd, set_var * var); +static void sys_default_general_log_path(THD *thd, enum_var_type type); +static bool sys_update_slow_log_path(THD *thd, set_var * var); +static void sys_default_slow_log_path(THD *thd, enum_var_type type); /* Variable definition list @@ -681,6 +687,22 @@ sys_var_have_variable sys_have_row_based_replication("have_row_based_replication /* Global read-only variable describing server license */ sys_var_const_str sys_license("license", STRINGIFY_ARG(LICENSE)); +/* Global variables which enable|disable logging */ +sys_var_log_state sys_var_general_log("general_log", &opt_log, + QUERY_LOG_GENERAL); +sys_var_log_state sys_var_slow_query_log("slow_query_log", &opt_slow_log, + QUERY_LOG_SLOW); +sys_var_str sys_var_general_log_path("general_log_file", sys_check_log_path, + sys_update_general_log_path, + sys_default_general_log_path, + opt_logname); +sys_var_str sys_var_slow_log_path("slow_query_log_file", sys_check_log_path, + sys_update_slow_log_path, + sys_default_slow_log_path, + opt_slow_logname); +sys_var_log_output sys_var_log_output_state("log_output", &log_output_options, + &log_output_typelib, 0); + #ifdef HAVE_REPLICATION static int show_slave_skip_errors(THD *thd, SHOW_VAR *var, char *buff) { @@ -779,6 +801,8 @@ SHOW_VAR init_vars[]= { {"ft_min_word_len", (char*) &ft_min_word_len, SHOW_LONG}, {"ft_query_expansion_limit",(char*) &ft_query_expansion_limit, SHOW_LONG}, {"ft_stopword_file", (char*) &ft_stopword_file, SHOW_CHAR_PTR}, + {sys_var_general_log.name, (char*) &opt_log, SHOW_MY_BOOL}, + {sys_var_general_log_path.name, (char*) &sys_var_general_log_path, SHOW_SYS}, {sys_group_concat_max_len.name, (char*) &sys_group_concat_max_len, SHOW_SYS}, {sys_have_archive_db.name, (char*) &have_archive_db, SHOW_HAVE}, {sys_have_berkeley_db.name, (char*) &have_berkeley_db, SHOW_HAVE}, @@ -856,6 +880,7 @@ SHOW_VAR init_vars[]= { {"log_bin", (char*) &opt_bin_log, SHOW_BOOL}, {sys_trust_function_creators.name,(char*) &sys_trust_function_creators, SHOW_SYS}, {"log_error", (char*) log_error_file, SHOW_CHAR}, + {sys_var_log_output_state.name, (char*) &sys_var_log_output_state, SHOW_SYS}, {sys_log_queries_not_using_indexes.name, (char*) &sys_log_queries_not_using_indexes, SHOW_SYS}, #ifdef HAVE_REPLICATION @@ -981,6 +1006,8 @@ SHOW_VAR init_vars[]= { {sys_slave_trans_retries.name,(char*) &sys_slave_trans_retries, SHOW_SYS}, #endif {sys_slow_launch_time.name, (char*) &sys_slow_launch_time, SHOW_SYS}, + {sys_var_slow_query_log.name, (char*) &opt_slow_log, SHOW_MY_BOOL}, + {sys_var_slow_log_path.name, (char*) &sys_var_slow_log_path, SHOW_SYS}, #ifdef HAVE_SYS_UN_H {"socket", (char*) &mysqld_unix_port, SHOW_CHAR_PTR}, #endif @@ -1301,15 +1328,20 @@ bool sys_var_thd_binlog_format::is_readonly() const if global or not here. And this test will also prevent switching from RBR to RBR (a no-op which should not happen too often). + + If we don't have row-based replication compiled in, the variable + is always read-only. */ -#ifdef HAVE_ROW_BASED_REPLICATION +#ifndef HAVE_ROW_BASED_REPLICATION + my_error(ER_RBR_NOT_AVAILABLE, MYF(0)); + return 1; +#else if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW) && thd->temporary_tables) { my_error(ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR, MYF(0)); return 1; } -#endif /*HAVE_ROW_BASED_REPLICATION*/ /* if in a stored function, it's too late to change mode */ @@ -1327,10 +1359,12 @@ bool sys_var_thd_binlog_format::is_readonly() const my_error(ER_NDB_CANT_SWITCH_BINLOG_FORMAT, MYF(0)); return 1; } -#endif +#endif /* HAVE_NDB_BINLOG */ +#endif /* HAVE_ROW_BASED_REPLICATION */ return sys_var_thd_enum::is_readonly(); } + void fix_binlog_format_after_update(THD *thd, enum_var_type type) { #ifdef HAVE_ROW_BASED_REPLICATION @@ -1338,6 +1372,7 @@ void fix_binlog_format_after_update(THD *thd, enum_var_type type) #endif /*HAVE_ROW_BASED_REPLICATION*/ } + static void fix_max_binlog_size(THD *thd, enum_var_type type) { DBUG_ENTER("fix_max_binlog_size"); @@ -2513,6 +2548,206 @@ end: } +bool sys_var_log_state::update(THD *thd, set_var *var) +{ + bool res= 0; + pthread_mutex_lock(&LOCK_global_system_variables); + if (!var->save_result.ulong_value) + logger.deactivate_log_handler(thd, log_type); + else + { + if ((res= logger.activate_log_handler(thd, log_type))) + { + my_error(ER_CANT_ACTIVATE_LOG, MYF(0), + log_type == QUERY_LOG_GENERAL ? "general" : + "slow query"); + goto err; + } + } +err: + pthread_mutex_unlock(&LOCK_global_system_variables); + return res; +} + +void sys_var_log_state::set_default(THD *thd, enum_var_type type) +{ + pthread_mutex_lock(&LOCK_global_system_variables); + logger.deactivate_log_handler(thd, log_type); + pthread_mutex_unlock(&LOCK_global_system_variables); +} + + +static int sys_check_log_path(THD *thd, set_var *var) +{ + char path[FN_REFLEN]; + MY_STAT f_stat; + const char *var_path= var->value->str_value.ptr(); + bzero(&f_stat, sizeof(MY_STAT)); + + (void) unpack_filename(path, var_path); + if (my_stat(path, &f_stat, MYF(0))) + { + /* Check if argument is a file and we have 'write' permission */ + if (!MY_S_ISREG(f_stat.st_mode) || + !(f_stat.st_mode & MY_S_IWRITE)) + return -1; + } + else + { + /* + Check if directory exists and + we have permission to create file & write to file + */ + (void) dirname_part(path, var_path); + if (my_access(path, (F_OK|W_OK))) + return -1; + } + return 0; +} + + +bool update_sys_var_str_path(THD *thd, sys_var_str *var_str, + set_var *var, const char *log_ext, + bool log_state, uint log_type) +{ + MYSQL_QUERY_LOG *file_log; + char buff[FN_REFLEN]; + char *res= 0, *old_value=(char *)(var ? var->value->str_value.ptr() : 0); + bool result= 0; + uint str_length= (var ? var->value->str_value.length() : 0); + + switch (log_type) { + case QUERY_LOG_SLOW: + file_log= logger.get_slow_log_file_handler(); + break; + case QUERY_LOG_GENERAL: + file_log= logger.get_log_file_handler(); + break; + default: + DBUG_ASSERT(0); + } + + if (!old_value) + { + old_value= make_default_log_name(buff, log_ext); + str_length= strlen(old_value); + } + if (!(res= my_strndup((byte*)old_value, str_length, MYF(MY_FAE+MY_WME)))) + { + result= 1; + goto err; + } + + pthread_mutex_lock(&LOCK_global_system_variables); + logger.lock(); + + if (file_log && log_state) + file_log->close(0); + old_value= var_str->value; + var_str->value= res; + var_str->value_length= str_length; + my_free(old_value, MYF(MY_ALLOW_ZERO_PTR)); + if (file_log && log_state) + { + switch (log_type) { + case QUERY_LOG_SLOW: + file_log->open_slow_log(sys_var_general_log_path.value); + break; + case QUERY_LOG_GENERAL: + file_log->open_query_log(sys_var_general_log_path.value); + break; + default: + DBUG_ASSERT(0); + } + } + + logger.unlock(); + pthread_mutex_unlock(&LOCK_global_system_variables); + +err: + return result; +} + + +static bool sys_update_general_log_path(THD *thd, set_var * var) +{ + return update_sys_var_str_path(thd, &sys_var_general_log_path, + var, ".log", opt_log, QUERY_LOG_GENERAL); +} + + +static void sys_default_general_log_path(THD *thd, enum_var_type type) +{ + (void) update_sys_var_str_path(thd, &sys_var_general_log_path, + 0, ".log", opt_log, QUERY_LOG_GENERAL); +} + + +static bool sys_update_slow_log_path(THD *thd, set_var * var) +{ + return update_sys_var_str_path(thd, &sys_var_slow_log_path, + var, "-slow.log", opt_slow_log, + QUERY_LOG_SLOW); +} + + +static void sys_default_slow_log_path(THD *thd, enum_var_type type) +{ + (void) update_sys_var_str_path(thd, &sys_var_slow_log_path, + 0, "-slow.log", opt_slow_log, + QUERY_LOG_SLOW); +} + + +bool sys_var_log_output::update(THD *thd, set_var *var) +{ + pthread_mutex_lock(&LOCK_global_system_variables); + logger.lock(); + logger.init_slow_log(var->save_result.ulong_value); + logger.init_general_log(var->save_result.ulong_value); + *value= var->save_result.ulong_value; + logger.unlock(); + pthread_mutex_unlock(&LOCK_global_system_variables); + return 0; +} + + +void sys_var_log_output::set_default(THD *thd, enum_var_type type) +{ + pthread_mutex_lock(&LOCK_global_system_variables); + logger.lock(); + logger.init_slow_log(LOG_TABLE); + logger.init_general_log(LOG_TABLE); + *value= LOG_TABLE; + logger.unlock(); + pthread_mutex_unlock(&LOCK_global_system_variables); +} + + +byte *sys_var_log_output::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) +{ + char buff[256]; + String tmp(buff, sizeof(buff), &my_charset_latin1); + ulong length; + ulong val= *value; + + tmp.length(0); + for (uint i= 0; val; val>>= 1, i++) + { + if (val & 1) + { + tmp.append(log_output_typelib.type_names[i], + log_output_typelib.type_lengths[i]); + tmp.append(','); + } + } + + if ((length= tmp.length())) + length--; + return (byte*) thd->strmake(tmp.ptr(), length); +} + + /***************************************************************************** Functions to handle SET NAMES and SET CHARACTER SET *****************************************************************************/ diff --git a/sql/set_var.h b/sql/set_var.h index 1049b154d47..719a47906d7 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -262,7 +262,7 @@ public: class sys_var_enum :public sys_var { - uint *value; + uint *value; TYPELIB *enum_names; public: sys_var_enum(const char *name_arg, uint *value_arg, @@ -772,6 +772,38 @@ public: }; +class sys_var_log_state :public sys_var_bool_ptr +{ + uint log_type; +public: + sys_var_log_state(const char *name_arg, my_bool *value_arg, uint log_type_arg) + :sys_var_bool_ptr(name_arg, value_arg), log_type(log_type_arg) {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); +}; + + +class sys_var_log_output :public sys_var +{ + uint *value; + TYPELIB *enum_names; +public: + sys_var_log_output(const char *name_arg, uint *value_arg, + TYPELIB *typelib, sys_after_update_func func) + :sys_var(name_arg,func), value(value_arg), enum_names(typelib) + {} + bool check(THD *thd, set_var *var) + { + return check_set(thd, var, enum_names); + } + bool update(THD *thd, set_var *var); + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); + bool check_update_type(Item_result type) { return 0; } + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_CHAR; } +}; + + /* Variable that you can only read from */ class sys_var_readonly: public sys_var @@ -881,15 +913,20 @@ public: byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; +#ifdef HAVE_ROW_BASED_REPLICATION extern void fix_binlog_format_after_update(THD *thd, enum_var_type type); +#endif class sys_var_thd_binlog_format :public sys_var_thd_enum { public: sys_var_thd_binlog_format(const char *name_arg, ulong SV::*offset_arg) :sys_var_thd_enum(name_arg, offset_arg, - &binlog_format_typelib, - fix_binlog_format_after_update) + &binlog_format_typelib +#ifdef HAVE_ROW_BASED_REPLICATION + , fix_binlog_format_after_update +#endif + ) {}; bool is_readonly() const; }; @@ -1068,6 +1105,8 @@ CHARSET_INFO *get_old_charset_by_name(const char *old_name); gptr find_named(I_List<NAMED_LIST> *list, const char *name, uint length, NAMED_LIST **found); +extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path; + /* key_cache functions */ KEY_CACHE *get_key_cache(LEX_STRING *cache_name); KEY_CACHE *get_or_create_key_cache(const char *name, uint length); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 421da4d40b5..476bc2f2f02 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5835,3 +5835,7 @@ ER_EVENT_SET_VAR_ERROR ER_PARTITION_MERGE_ERROR eng "%s handler cannot be used in partitioned tables" swe "%s kan inte användas i en partitionerad tabell" +ER_CANT_ACTIVATE_LOG + eng "Cannot activate '%-.64s' log." +ER_RBR_NOT_AVAILABLE + eng "The server was not built with row-based replication" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1b448a3ba18..f8ec4531995 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1526,17 +1526,37 @@ bool close_temporary_table(THD *thd, TABLE_LIST *table_list) } /* - Close temporary table and unlink from thd->temporary tables + unlink from thd->temporary tables and close temporary table */ void close_temporary_table(THD *thd, TABLE *table, bool free_share, bool delete_table) { - TABLE **prev= table->open_prev; - if ((*table->open_prev= table->next)) - table->next->open_prev= prev; + if (table->prev) + { + table->prev->next= table->next; + if (table->prev->next) + table->next->prev= table->prev; + } + else + { + /* removing the item from the list */ + DBUG_ASSERT(table == thd->temporary_tables); + /* + slave must reset its temporary list pointer to zero to exclude + passing non-zero value to end_slave via rli->save_temporary_tables + when no temp tables opened, see an invariant below. + */ + thd->temporary_tables= table->next; + if (thd->temporary_tables) + table->next->prev= 0; + } if (thd->slave_thread) + { + /* natural invariant of temporary_tables */ + DBUG_ASSERT(slave_open_temp_tables || !thd->temporary_tables); slave_open_temp_tables--; + } close_temporary(table, free_share, delete_table); } @@ -3504,10 +3524,12 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, if (link_in_list) { - tmp_table->open_prev= &thd->temporary_tables; - if ((tmp_table->next= thd->temporary_tables)) - thd->temporary_tables->open_prev= &tmp_table->next; + /* growing temp list at the head */ + tmp_table->next= thd->temporary_tables; + if (tmp_table->next) + tmp_table->next->prev= tmp_table; thd->temporary_tables= tmp_table; + thd->temporary_tables->prev= 0; if (thd->slave_thread) slave_open_temp_tables++; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0a9e6472d9f..23603afc038 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2134,7 +2134,9 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) && !current_stmt_binlog_row_based) + { options&= ~OPTION_BIN_LOG; + } /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; @@ -2642,31 +2644,6 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end) error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0); } - else if (stmt_end && binlog_table_maps > 0) - { /* there is no pending event at this point */ - /* - If pending is null and we are going to end the statement, we - have to write an extra, empty, binrow event so that the slave - knows to discard the tables it has received. Otherwise, the - table maps written this far will be included in the table maps - for the following statement. - - TODO: Remove the need for a dummy event altogether. It can be - fixed if we can write table maps to a memory buffer before - writing the first binrow event. We can then flush and clear the - memory buffer with table map events before writing the first - binrow event. In the event of a crash, nothing is lost since - the table maps are only needed if there are binrow events. - */ - - Rows_log_event *ev= - new Write_rows_log_event(this, 0, ~0UL, 0, FALSE); - ev->set_flags(Rows_log_event::STMT_END_F); - binlog_set_pending_rows_event(ev); - - error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0); - binlog_table_maps= 0; - } DBUG_RETURN(error); } @@ -2724,6 +2701,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, to how you treat this. */ case THD::ROW_QUERY_TYPE: +#ifdef HAVE_ROW_BASED_REPLICATION if (current_stmt_binlog_row_based) { /* @@ -2744,6 +2722,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, #endif /*HAVE_ROW_BASED_REPLICATION*/ DBUG_RETURN(0); } +#endif /* Otherwise, we fall through */ case THD::STMT_QUERY_TYPE: /* @@ -2752,7 +2731,9 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, */ { Query_log_event qinfo(this, query, query_len, is_trans, suppress_use); +#ifdef HAVE_ROW_BASED_REPLICATION qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F; +#endif /* Binlog table maps will be irrelevant after a Query_log_event (they are just removed on the slave side) so after the query diff --git a/sql/sql_class.h b/sql/sql_class.h index 723dad715bd..b6283b6d174 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -908,8 +908,10 @@ public: /* container for handler's private per-connection data */ void *ha_data[MAX_HA]; -#ifdef HAVE_ROW_BASED_REPLICATION #ifndef MYSQL_CLIENT + int binlog_setup_trx_data(); + +#ifdef HAVE_ROW_BASED_REPLICATION /* Public interface to write RBR events to the binlog @@ -939,7 +941,6 @@ public: RowsEventT* hint); Rows_log_event* binlog_get_pending_rows_event() const; void binlog_set_pending_rows_event(Rows_log_event* ev); - int binlog_setup_trx_data(); my_size_t max_row_length_blob(TABLE* table, const byte *data) const; my_size_t max_row_length(TABLE* table, const byte *data) const @@ -960,12 +961,15 @@ public: private: uint binlog_table_maps; // Number of table maps currently in the binlog - public: - -#endif + uint get_binlog_table_maps() const { + return binlog_table_maps; + } #endif /* HAVE_ROW_BASED_REPLICATION */ +#endif /* MYSQL_CLIENT */ + #ifndef MYSQL_CLIENT +public: enum enum_binlog_query_type { /* The query can be logged row-based or statement-based @@ -1403,18 +1407,26 @@ public: inline void set_current_stmt_binlog_row_based_if_mixed() { if (variables.binlog_format == BINLOG_FORMAT_MIXED) - current_stmt_binlog_row_based= 1; + current_stmt_binlog_row_based= TRUE; } inline void set_current_stmt_binlog_row_based() { - current_stmt_binlog_row_based= 1; + current_stmt_binlog_row_based= TRUE; + } + inline void clear_current_stmt_binlog_row_based() + { + current_stmt_binlog_row_based= FALSE; } +#endif inline void reset_current_stmt_binlog_row_based() { - current_stmt_binlog_row_based= test(variables.binlog_format == - BINLOG_FORMAT_ROW); +#ifdef HAVE_ROW_BASED_REPLICATION + current_stmt_binlog_row_based= + test(variables.binlog_format == BINLOG_FORMAT_ROW); +#else + current_stmt_binlog_row_based= FALSE; +#endif } -#endif /*HAVE_ROW_BASED_REPLICATION*/ }; @@ -1579,6 +1591,9 @@ class select_insert :public select_result_interceptor { bool send_eof(); /* not implemented: select_insert is never re-used in prepared statements */ void cleanup(); + +protected: + MYSQL_LOCK *lock; }; @@ -1588,7 +1603,6 @@ class select_create: public select_insert { List<create_field> *extra_fields; List<Key> *keys; HA_CREATE_INFO *create_info; - MYSQL_LOCK *lock; Field **field; public: select_create (TABLE_LIST *table, @@ -1598,10 +1612,10 @@ public: List<Item> &select_fields,enum_duplicates duplic, bool ignore) :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), create_table(table), extra_fields(&fields_par),keys(&keys_par), - create_info(create_info_par), - lock(0) + create_info(create_info_par) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); + void binlog_show_create_table(TABLE **tables, uint count); void store_values(List<Item> &values); void send_error(uint errcode,const char *err); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index dd4748bc15c..b9ce1a53aaf 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -42,8 +42,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ha_rows deleted= 0; uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; - bool ha_delete_all_rows= 0; - ulonglong const saved_options= thd->options; DBUG_ENTER("mysql_delete"); if (open_and_lock_tables(thd, table_list)) @@ -75,20 +73,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, Test if the user wants to delete all rows and deletion doesn't have any side-effects (because of triggers), so we can use optimized handler::delete_all_rows() method. + + If row-based replication is used, we also delete the table row by + row. */ if (!using_limit && const_cond && (!conds || conds->val_int()) && !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && - !(table->triggers && table->triggers->has_delete_triggers())) + !(table->triggers && table->triggers->has_delete_triggers()) && + !thd->current_stmt_binlog_row_based) { /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); ha_rows const maybe_deleted= table->file->stats.records; - /* - If all rows shall be deleted, we (almost) always log this - statement-based (see [binlog], below), so we set this flag and - test it below. - */ - ha_delete_all_rows= 1; + DBUG_PRINT("debug", ("Trying to use delete_all_rows()")); if (!(error=table->file->delete_all_rows())) { error= -1; // ok @@ -218,13 +215,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, thd->proc_info="updating"; will_batch= !table->file->start_bulk_delete(); - /* - We saved the thread options above before clearing the - OPTION_BIN_LOG, and will restore below, effectively disabling the - binary log (unless it was already disabled, of course). - */ - if (ha_delete_all_rows) - thd->options&= ~static_cast<ulonglong>(OPTION_BIN_LOG); table->mark_columns_needed_for_delete(); @@ -318,12 +308,6 @@ cleanup: delete select; transactional_table= table->file->has_transactions(); - /* - Restore the saved value of the OPTION_BIN_LOG bit in the thread - options before executing binlog_query() below. - */ - thd->options|= (saved_options & OPTION_BIN_LOG); - /* See similar binlogging code in sql_update.cc, for comments */ if ((error < 0) || (deleted && !transactional_table)) { @@ -338,11 +322,7 @@ cleanup: statement-based; otherwise, 'ha_delete_row()' was used to delete specific rows which we might log row-based. */ - THD::enum_binlog_query_type const - query_type(ha_delete_all_rows && !table->file->is_injective() ? - THD::STMT_QUERY_TYPE : - THD::ROW_QUERY_TYPE); - int log_result= thd->binlog_query(query_type, + int log_result= thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, transactional_table, FALSE); @@ -931,7 +911,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) /* close log tables in use */ if (!my_strcasecmp(system_charset_info, table_list->db, "mysql")) { - if (!my_strcasecmp(system_charset_info, table_list->table_name, + if (opt_log && + !my_strcasecmp(system_charset_info, table_list->table_name, "general_log")) { lock_logger= 1; @@ -940,7 +921,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) closed_log_tables= closed_log_tables | QUERY_LOG_GENERAL; } else - if (!my_strcasecmp(system_charset_info, table_list->table_name, + if (opt_slow_log && + !my_strcasecmp(system_charset_info, table_list->table_name, "slow_log")) { lock_logger= 1; @@ -981,10 +963,10 @@ end: unlock_table_name(thd, table_list); VOID(pthread_mutex_unlock(&LOCK_open)); - if (closed_log_tables & QUERY_LOG_SLOW) + if (opt_slow_log && (closed_log_tables & QUERY_LOG_SLOW)) logger.reopen_log_table(QUERY_LOG_SLOW); - if (closed_log_tables & QUERY_LOG_GENERAL) + if (opt_log && (closed_log_tables & QUERY_LOG_GENERAL)) logger.reopen_log_table(QUERY_LOG_GENERAL); if (lock_logger) logger.unlock(); @@ -1004,6 +986,9 @@ trunc_by_del: thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT); ha_enable_transaction(thd, FALSE); mysql_init_select(thd->lex); +#ifdef HAVE_ROW_BASED_REPLICATION + thd->clear_current_stmt_binlog_row_based(); +#endif error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, HA_POS_ERROR, LL(0), TRUE); ha_enable_transaction(thd, TRUE); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f1f97400283..f1616e12d42 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1083,16 +1083,19 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) to convert the latter operation internally to an UPDATE. We also should not perform this conversion if we have timestamp field with ON UPDATE which is different from DEFAULT. + Another case when conversion should not be performed is when + we have ON DELETE trigger on table so user may notice that + we cheat here. Note that it is ok to do such conversion for + tables which have ON UPDATE but have no ON DELETE triggers, + we just should not expose this fact to users by invoking + ON UPDATE triggers. */ if (last_uniq_key(table,key_nr) && !table->file->referenced_by_foreign_key() && (table->timestamp_field_type == TIMESTAMP_NO_AUTO_SET || - table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH)) + table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH) && + (!table->triggers || !table->triggers->has_delete_triggers())) { - if (table->triggers && - table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, - TRG_ACTION_BEFORE, TRUE)) - goto before_trg_err; if (thd->clear_next_insert_id) { /* Reset auto-increment cacheing if we do an update */ @@ -1103,13 +1106,11 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) table->record[0]))) goto err; info->deleted++; - trg_error= (table->triggers && - table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, - TRG_ACTION_AFTER, - TRUE)); - /* Update logfile and count */ - info->copied++; - goto ok_or_after_trg_err; + /* + Since we pretend that we have done insert we should call + its after triggers. + */ + goto after_trg_n_copied_inc; } else { @@ -1133,10 +1134,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } } } - info->copied++; - trg_error= (table->triggers && - table->triggers->process_triggers(thd, TRG_EVENT_INSERT, - TRG_ACTION_AFTER, TRUE)); /* Restore column maps if they where replaced during an duplicate key problem. @@ -1151,15 +1148,15 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE)) goto err; table->file->restore_auto_increment(); - } - else - { - info->copied++; - trg_error= (table->triggers && - table->triggers->process_triggers(thd, TRG_EVENT_INSERT, - TRG_ACTION_AFTER, TRUE)); + goto ok_or_after_trg_err; } +after_trg_n_copied_inc: + info->copied++; + trg_error= (table->triggers && + table->triggers->process_triggers(thd, TRG_EVENT_INSERT, + TRG_ACTION_AFTER, TRUE)); + ok_or_after_trg_err: if (key) my_safe_afree(key,table->s->max_unique_length,MAX_KEY_LENGTH); @@ -2191,6 +2188,7 @@ select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par, bool ignore_check_option_errors) :table_list(table_list_par), table(table_par), fields(fields_par), last_insert_id(0), + lock(0), insert_into_view(table_list_par && table_list_par->view != 0) { bzero((char*) &info,sizeof(info)); @@ -2378,7 +2376,36 @@ bool select_insert::send_data(List<Item> &values) DBUG_RETURN(1); } } - if (!(error= write_record(thd, table, &info))) + + /* + The thd->lock lock contain the locks for the select part of the + statement and the 'lock' variable contain the write lock for the + currently locked table that is being created or inserted + into. However, the row-based replication will investigate the + thd->lock to decide what table maps are to be written, so this one + has to contain the tables locked for writing. To be able to write + table map for the table being created, we temporarily set + THD::lock to select_insert::lock while writing the record to the + storage engine. We cannot set this elsewhere, since the execution + of a stored function inside the select expression might cause the + lock structures to be NULL. + */ + + { + MYSQL_LOCK *saved_lock= NULL; + if (lock) + { + saved_lock= thd->lock; + thd->lock= lock; + } + + error= write_record(thd, table, &info); + + if (lock) + thd->lock= saved_lock; + } + + if (!error) { if (table->triggers || info.handle_duplicates == DUP_UPDATE) { @@ -2657,7 +2684,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, tmp_disable_binlog(thd); if (!mysql_create_table(thd, create_table->db, create_table->table_name, create_info, *extra_fields, *keys, 0, - select_field_count)) + select_field_count, 0)) { /* If we are here in prelocked mode we either create temporary table @@ -2723,15 +2750,34 @@ private: }; -int select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) +int +select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { - MY_HOOKS hooks(this); DBUG_ENTER("select_create::prepare"); + TABLEOP_HOOKS *hook_ptr= NULL; +#ifdef HAVE_ROW_BASED_REPLICATION + class MY_HOOKS : public TABLEOP_HOOKS { + public: + MY_HOOKS(select_create *x) : ptr(x) { } + virtual void do_prelock(TABLE **tables, uint count) + { + if (ptr->get_thd()->current_stmt_binlog_row_based) + ptr->binlog_show_create_table(tables, count); + } + + private: + select_create *ptr; + }; + + MY_HOOKS hooks(this); + hook_ptr= &hooks; +#endif + unit= u; if (!(table= create_table_from_items(thd, create_info, create_table, extra_fields, keys, &values, &lock, - &hooks))) + hook_ptr))) DBUG_RETURN(-1); // abort() deletes table if (table->s->fields < values.elements) @@ -2767,7 +2813,9 @@ int select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) } -void select_create::binlog_show_create_table(TABLE **tables, uint count) +#ifdef HAVE_ROW_BASED_REPLICATION +void +select_create::binlog_show_create_table(TABLE **tables, uint count) { /* Note 1: In RBR mode, we generate a CREATE TABLE statement for the @@ -2786,9 +2834,7 @@ void select_create::binlog_show_create_table(TABLE **tables, uint count) schema that will do a close_thread_tables(), destroying the statement transaction cache. */ -#ifdef HAVE_ROW_BASED_REPLICATION DBUG_ASSERT(thd->current_stmt_binlog_row_based); -#endif DBUG_ASSERT(tables && *tables && count > 0); char buf[2048]; @@ -2808,7 +2854,7 @@ void select_create::binlog_show_create_table(TABLE **tables, uint count) /* is_trans */ TRUE, /* suppress_use */ FALSE); } - +#endif // HAVE_ROW_BASED_REPLICATION void select_create::store_values(List<Item> &values) { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d45f4369095..6811f120c3f 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -777,8 +777,6 @@ int MYSQLlex(void *arg, void *yythd) int length; if ((length= my_mbcharlen(cs, c)) == 1) { - if (c == (uchar) NAMES_SEP_CHAR) - break; /* Old .frm format can't handle this char */ if (c == quote_char) { if (yyPeek() != quote_char) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 00aacd7b67b..a77f321a437 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2943,7 +2943,7 @@ mysql_execute_command(THD *thd) res= mysql_create_table(thd, create_table->db, create_table->table_name, &lex->create_info, lex->create_list, - lex->key_list, 0, 0); + lex->key_list, 0, 0, 1); } if (!res) send_ok(thd); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index dc598b786d5..d522318648d 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -438,6 +438,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, uint col_access=thd->col_access; #endif TABLE_LIST table_list; + char tbbuff[FN_REFLEN]; DBUG_ENTER("mysql_find_files"); if (wild && !wild[0]) @@ -454,6 +455,8 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, DBUG_RETURN(-1); } + VOID(tablename_to_filename(tmp_file_prefix, tbbuff, sizeof(tbbuff))); + for (i=0 ; i < (uint) dirp->number_off_files ; i++) { char uname[NAME_LEN*3+1]; /* Unencoded name */ @@ -491,7 +494,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, { // Return only .frm files which aren't temp files. if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),reg_ext) || - is_prefix(file->name,tmp_file_prefix)) + is_prefix(file->name,tbbuff)) continue; *ext=0; VOID(filename_to_tablename(file->name, uname, sizeof(uname))); @@ -1340,7 +1343,9 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, FALSE, show_table_options)))) { + packet->append(STRING_WITH_LEN(" /*!50100")); packet->append(part_syntax, part_syntax_len); + packet->append(STRING_WITH_LEN(" */")); my_free(part_syntax, MYF(0)); } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 41f345ad9f0..ab928058ec6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3002,6 +3002,31 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field) /* + Copy HA_CREATE_INFO struct + SYNOPSIS + copy_create_info() + lex_create_info The create_info struct setup by parser + RETURN VALUES + > 0 A pointer to a copy of the lex_create_info + 0 Memory allocation error + DESCRIPTION + Allocate memory for copy of HA_CREATE_INFO structure from parser + to ensure we can reuse the parser struct in stored procedures + and prepared statements. +*/ + +static HA_CREATE_INFO *copy_create_info(HA_CREATE_INFO *lex_create_info) +{ + HA_CREATE_INFO *create_info; + if (!(create_info= (HA_CREATE_INFO*)sql_alloc(sizeof(HA_CREATE_INFO)))) + mem_alloc_error(sizeof(HA_CREATE_INFO)); + else + memcpy((void*)create_info, (void*)lex_create_info, sizeof(HA_CREATE_INFO)); + return create_info; +} + + +/* Create a table SYNOPSIS @@ -3009,11 +3034,15 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field) thd Thread object db Database table_name Table name - create_info Create information (like MAX_ROWS) + lex_create_info Create information (like MAX_ROWS) fields List of fields to create keys List of keys to create internal_tmp_table Set to 1 if this is an internal temporary table (From ALTER TABLE) + select_field_count + use_copy_create_info Should we make a copy of create info (we do this + when this is called from sql_parse.cc where we + want to ensure lex object isn't manipulated. DESCRIPTION If one creates a temporary table, this is automatically opened @@ -3030,20 +3059,32 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field) bool mysql_create_table_internal(THD *thd, const char *db, const char *table_name, - HA_CREATE_INFO *create_info, + HA_CREATE_INFO *lex_create_info, List<create_field> &fields, List<Key> &keys,bool internal_tmp_table, - uint select_field_count) + uint select_field_count, + bool use_copy_create_info) { char path[FN_REFLEN]; uint path_length; const char *alias; uint db_options, key_count; KEY *key_info_buffer; + HA_CREATE_INFO *create_info; handler *file; bool error= TRUE; DBUG_ENTER("mysql_create_table_internal"); + if (use_copy_create_info) + { + if (!(create_info= copy_create_info(lex_create_info))) + { + DBUG_RETURN(TRUE); + } + } + else + create_info= lex_create_info; + /* Check for duplicate fields and check type of table to create */ if (!fields.elements) { @@ -3358,7 +3399,8 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, HA_CREATE_INFO *create_info, List<create_field> &fields, List<Key> &keys,bool internal_tmp_table, - uint select_field_count) + uint select_field_count, + bool use_copy_create_info) { bool result; DBUG_ENTER("mysql_create_table"); @@ -3382,7 +3424,8 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, result= mysql_create_table_internal(thd, db, table_name, create_info, fields, keys, internal_tmp_table, - select_field_count); + select_field_count, + use_copy_create_info); pthread_mutex_lock(&LOCK_lock_db); if (!--creating_table && creating_database) @@ -4328,7 +4371,7 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables) */ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, - HA_CREATE_INFO *create_info, + HA_CREATE_INFO *lex_create_info, Table_ident *table_ident) { TABLE *tmp_table; @@ -4341,9 +4384,15 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, int err; bool res= TRUE; enum legacy_db_type not_used; + HA_CREATE_INFO *create_info; TABLE_LIST src_tables_list; DBUG_ENTER("mysql_create_like_table"); + + if (!(create_info= copy_create_info(lex_create_info))) + { + DBUG_RETURN(TRUE); + } src_db= table_ident->db.str ? table_ident->db.str : thd->db; /* @@ -4889,7 +4938,7 @@ static uint compare_tables(TABLE *table, List<create_field> *create_list, */ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, - HA_CREATE_INFO *create_info, + HA_CREATE_INFO *lex_create_info, TABLE_LIST *table_list, List<create_field> &fields, List<Key> &keys, uint order_num, ORDER *order, @@ -4907,6 +4956,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ulonglong next_insert_id; uint db_create_options, used_fields; handlerton *old_db_type, *new_db_type; + HA_CREATE_INFO *create_info; uint need_copy_table= 0; bool no_table_reopen= FALSE, varchar= FALSE; #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -4932,6 +4982,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, LINT_INIT(index_drop_buffer); thd->proc_info="init"; + if (!(create_info= copy_create_info(lex_create_info))) + { + DBUG_RETURN(TRUE); + } table_name=table_list->table_name; alias= (lower_case_table_names == 2) ? table_list->alias : table_name; db=table_list->db; @@ -5686,7 +5740,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, */ tmp_disable_binlog(thd); error= mysql_create_table(thd, new_db, tmp_name, - create_info,create_list,key_list,1,0); + create_info,create_list,key_list,1,0,0); reenable_binlog(thd); if (error) DBUG_RETURN(error); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index e917c1358ef..da529cc0070 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -985,7 +985,12 @@ reopen_tables: } } } - + /* + Set exclude_from_table_unique_test value back to FALSE. It is needed for + further check in multi_update::prepare whether to use record cache. + */ + lex->select_lex.exclude_from_table_unique_test= FALSE; + if (thd->fill_derived_tables() && mysql_handle_derived(lex, &mysql_derived_filling)) DBUG_RETURN(TRUE); @@ -1164,7 +1169,7 @@ int multi_update::prepare(List<Item> ¬_used_values, for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf) { TABLE *table=table_ref->table; - if (!(tables_to_update & table->map) && + if ((tables_to_update & table->map) && unique_table(thd, table_ref, update_tables)) table->no_cache= 1; // Disable row cache } diff --git a/sql/table.cc b/sql/table.cc index ab1bd49ba48..a96ca0da881 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1484,7 +1484,18 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, tmp= fix_partition_func(thd, outparam, is_create_table); *root_ptr= old_root; if (tmp) + { + if (is_create_table) + { + /* + During CREATE/ALTER TABLE it is ok to receive errors here. + It is not ok if it happens during the opening of an frm + file as part of a normal query. + */ + error_reported= TRUE; + } goto err; + } } #endif diff --git a/sql/unireg.cc b/sql/unireg.cc index 11aa73bb502..42518e7b9b7 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -339,6 +339,7 @@ int rea_create_table(THD *thd, const char *path, DBUG_RETURN(0); err_handler: + VOID(file->create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info)); my_delete(frm_name, MYF(0)); DBUG_RETURN(1); } /* rea_create_table */ |