summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/ha_innodb.cc199
-rw-r--r--sql/ha_ndbcluster.cc10
-rw-r--r--sql/ha_ndbcluster.h2
-rw-r--r--sql/ha_partition.cc1
-rw-r--r--sql/handler.cc174
-rw-r--r--sql/item_xmlfunc.cc12
-rw-r--r--sql/log.cc207
-rw-r--r--sql/log.h19
-rw-r--r--sql/log_event.cc37
-rw-r--r--sql/mysql_priv.h11
-rw-r--r--sql/mysqld.cc45
-rw-r--r--sql/set_var.cc241
-rw-r--r--sql/set_var.h45
-rw-r--r--sql/share/errmsg.txt4
-rw-r--r--sql/sql_base.cc36
-rw-r--r--sql/sql_class.cc31
-rw-r--r--sql/sql_class.h40
-rw-r--r--sql/sql_delete.cc47
-rw-r--r--sql/sql_insert.cc110
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_parse.cc2
-rw-r--r--sql/sql_show.cc7
-rw-r--r--sql/sql_table.cc70
-rw-r--r--sql/sql_update.cc9
-rw-r--r--sql/table.cc11
-rw-r--r--sql/unireg.cc1
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> &not_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 */