summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2013-06-06 21:32:29 +0200
committerSergei Golubchik <sergii@pisem.net>2013-06-06 21:32:29 +0200
commit72ba95873a6d99def07a4bfecda44fb942165142 (patch)
tree4bcb11afc568c4894f4cb109d57c14968320a58b /sql
parentfab9a55d077b4f2a511b273d5f51272f1e3dc1ff (diff)
parent4749d40c635634e25e07d28ce1a04e9263bcc375 (diff)
downloadmariadb-git-72ba95873a6d99def07a4bfecda44fb942165142.tar.gz
10.0-base merge
(without InnoDB - all InnoDB changes were ignored)
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt9
-rw-r--r--sql/db.opt2
-rw-r--r--sql/event_db_repository.cc2
-rw-r--r--sql/ha_partition.cc496
-rw-r--r--sql/ha_partition.h20
-rw-r--r--sql/handler.cc35
-rw-r--r--sql/handler.h4
-rw-r--r--sql/item.cc58
-rw-r--r--sql/item.h134
-rw-r--r--sql/item_cmpfunc.cc6
-rw-r--r--sql/item_func.cc6
-rw-r--r--sql/item_func.h19
-rw-r--r--sql/item_strfunc.cc2
-rw-r--r--sql/item_strfunc.h6
-rw-r--r--sql/item_sum.cc21
-rw-r--r--sql/key.cc112
-rw-r--r--sql/key.h4
-rw-r--r--sql/log.cc28
-rw-r--r--sql/log_event.cc31
-rw-r--r--sql/log_event.h31
-rw-r--r--sql/mysql_install_db.cc16
-rw-r--r--sql/mysqld.cc52
-rw-r--r--sql/mysqld.h2
-rw-r--r--sql/opt_range.cc119
-rw-r--r--sql/partition_element.h7
-rw-r--r--sql/partition_info.cc289
-rw-r--r--sql/partition_info.h17
-rw-r--r--sql/rpl_gtid.cc150
-rw-r--r--sql/rpl_gtid.h5
-rw-r--r--sql/rpl_mi.cc24
-rw-r--r--sql/rpl_mi.h39
-rw-r--r--sql/rpl_rli.cc34
-rw-r--r--sql/rpl_utility.cc1
-rw-r--r--sql/set_var.cc18
-rw-r--r--sql/set_var.h10
-rw-r--r--sql/share/errmsg-utf8.txt4
-rw-r--r--sql/slave.cc379
-rw-r--r--sql/sp.cc2
-rw-r--r--sql/sp_head.cc9
-rw-r--r--sql/sql_acl.cc31
-rw-r--r--sql/sql_admin.cc5
-rw-r--r--sql/sql_admin.h4
-rw-r--r--sql/sql_audit.cc32
-rw-r--r--sql/sql_audit.h91
-rw-r--r--sql/sql_base.cc4
-rw-r--r--sql/sql_binlog.cc2
-rw-r--r--sql/sql_cache.cc45
-rw-r--r--sql/sql_class.cc20
-rw-r--r--sql/sql_connect.cc2
-rw-r--r--sql/sql_delete.cc2
-rw-r--r--sql/sql_error.cc8
-rw-r--r--sql/sql_error.h14
-rw-r--r--sql/sql_handler.cc10
-rw-r--r--sql/sql_insert.cc3
-rw-r--r--sql/sql_lex.cc3
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_parse.cc24
-rw-r--r--sql/sql_partition.cc219
-rw-r--r--sql/sql_partition.h6
-rw-r--r--sql/sql_partition_admin.cc4
-rw-r--r--sql/sql_plugin_compat.h4
-rw-r--r--sql/sql_prepare.cc9
-rw-r--r--sql/sql_repl.cc228
-rw-r--r--sql/sql_repl.h3
-rw-r--r--sql/sql_select.cc73
-rw-r--r--sql/sql_show.cc45
-rw-r--r--sql/sql_string.cc2
-rw-r--r--sql/sql_string.h10
-rw-r--r--sql/sql_table.cc17
-rw-r--r--sql/sql_test.cc2
-rw-r--r--sql/sql_view.cc12
-rw-r--r--sql/sql_yacc.yy58
-rw-r--r--sql/sys_vars.cc21
-rw-r--r--sql/threadpool_unix.cc22
-rw-r--r--sql/tztime.cc4
75 files changed, 2659 insertions, 586 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 0b4943851f0..bf1d0bfdc9a 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -275,12 +275,9 @@ ADD_CUSTOM_TARGET(distclean
IF(INSTALL_LAYOUT STREQUAL "STANDALONE")
-# We need to create empty directories (data/test) the installation.
-# This does not work with current CPack due to http://www.cmake.org/Bug/view.php?id=8767
-# Avoid completely empty directories and install dummy file instead.
-SET(DUMMY_FILE ${CMAKE_CURRENT_BINARY_DIR}/db.opt )
-FILE(WRITE ${DUMMY_FILE} "")
-INSTALL(FILES ${DUMMY_FILE} DESTINATION data/test COMPONENT DataFiles)
+# Copy db.opt into data/test/
+SET(DBOPT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/db.opt )
+INSTALL(FILES ${DBOPT_FILE} DESTINATION data/test COMPONENT DataFiles)
# Install initial database on windows
IF(NOT CMAKE_CROSSCOMPILING)
diff --git a/sql/db.opt b/sql/db.opt
new file mode 100644
index 00000000000..d8429c4e0de
--- /dev/null
+++ b/sql/db.opt
@@ -0,0 +1,2 @@
+default-character-set=latin1
+default-collation=latin1_swedish_ci
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index 3b4119347c4..5c77456d907 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -55,7 +55,7 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
},
{
{ C_STRING_WITH_LEN("definer") },
- { C_STRING_WITH_LEN("char(77)") },
+ { C_STRING_WITH_LEN("char(") },
{ C_STRING_WITH_LEN("utf8") }
},
{
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index bd08cd2dc32..09ab6d48eba 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -63,6 +63,8 @@
#include "key.h"
#include "sql_plugin.h"
#include "table.h" /* HA_DATA_PARTITION */
+#include "sql_show.h" // append_identifier
+#include "sql_admin.h" // SQL_ADMIN_MSG_TEXT_SIZE
#include "debug_sync.h"
@@ -287,6 +289,7 @@ void ha_partition::init_handler_variables()
m_rec_length= 0;
m_last_part= 0;
m_rec0= 0;
+ m_err_rec= NULL;
m_curr_key_info[0]= NULL;
m_curr_key_info[1]= NULL;
m_part_func_monotonicity_info= NON_MONOTONIC;
@@ -1046,10 +1049,11 @@ int ha_partition::preload_keys(THD *thd, HA_CHECK_OPT *check_opt)
0 Success
*/
-static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
- handler *file, uint flag)
+int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
+ uint part_id, uint flag)
{
int error;
+ handler *file= m_file[part_id];
DBUG_ENTER("handle_opt_part");
DBUG_PRINT("enter", ("flag = %u", flag));
@@ -1058,9 +1062,27 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
else if (flag == ANALYZE_PARTS)
error= file->ha_analyze(thd, check_opt);
else if (flag == CHECK_PARTS)
+ {
error= file->ha_check(thd, check_opt);
+ if (!error ||
+ error == HA_ADMIN_ALREADY_DONE ||
+ error == HA_ADMIN_NOT_IMPLEMENTED)
+ {
+ if (check_opt->flags & (T_MEDIUM | T_EXTEND))
+ error= check_misplaced_rows(part_id, false);
+ }
+ }
else if (flag == REPAIR_PARTS)
+ {
error= file->ha_repair(thd, check_opt);
+ if (!error ||
+ error == HA_ADMIN_ALREADY_DONE ||
+ error == HA_ADMIN_NOT_IMPLEMENTED)
+ {
+ if (check_opt->flags & (T_MEDIUM | T_EXTEND))
+ error= check_misplaced_rows(part_id, true);
+ }
+ }
else if (flag == ASSIGN_KEYCACHE_PARTS)
error= file->assign_to_keycache(thd, check_opt);
else if (flag == PRELOAD_KEYS_PARTS)
@@ -1082,11 +1104,11 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
TODO: move this into the handler, or rewrite mysql_admin_table.
*/
static bool print_admin_msg(THD* thd, const char* msg_type,
- const char* db_name, const char* table_name,
+ const char* db_name, String &table_name,
const char* op_name, const char *fmt, ...)
ATTRIBUTE_FORMAT(printf, 6, 7);
static bool print_admin_msg(THD* thd, const char* msg_type,
- const char* db_name, const char* table_name,
+ const char* db_name, String &table_name,
const char* op_name, const char *fmt, ...)
{
va_list args;
@@ -1107,7 +1129,7 @@ static bool print_admin_msg(THD* thd, const char* msg_type,
return TRUE;
}
- length=(uint) (strxmov(name, db_name, ".", table_name,NullS) - name);
+ length=(uint) (strxmov(name, db_name, ".", table_name.c_ptr_safe(), NullS) - name);
/*
TODO: switch from protocol to push_warning here. The main reason we didn't
it yet is parallel repair. Due to following trace:
@@ -1179,15 +1201,14 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
part= i * num_subparts + j;
DBUG_PRINT("info", ("Optimize subpartition %u (%s)",
part, sub_elem->partition_name));
- if ((error= handle_opt_part(thd, check_opt, m_file[part], flag)))
+ if ((error= handle_opt_part(thd, check_opt, part, flag)))
{
/* print a line which partition the error belongs to */
if (error != HA_ADMIN_NOT_IMPLEMENTED &&
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, "error", table_share->db.str,
- table->alias.c_ptr(),
+ print_admin_msg(thd, "error", table_share->db.str, table->alias,
opt_op_name[flag],
"Subpartition %s returned error",
sub_elem->partition_name);
@@ -1206,15 +1227,14 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
{
DBUG_PRINT("info", ("Optimize partition %u (%s)", i,
part_elem->partition_name));
- if ((error= handle_opt_part(thd, check_opt, m_file[i], flag)))
+ if ((error= handle_opt_part(thd, check_opt, i, flag)))
{
/* print a line which partition the error belongs to */
if (error != HA_ADMIN_NOT_IMPLEMENTED &&
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, "error", table_share->db.str,
- table->alias.c_ptr(),
+ print_admin_msg(thd, "error", table_share->db.str, table->alias,
opt_op_name[flag], "Partition %s returned error",
part_elem->partition_name);
}
@@ -3583,7 +3603,7 @@ exit:
Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
new_data is always record[0]
- old_data is normally record[1] but may be anything
+ old_data is always record[1]
*/
int ha_partition::update_row(const uchar *old_data, uchar *new_data)
@@ -3593,6 +3613,7 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
int error= 0;
longlong func_value;
DBUG_ENTER("ha_partition::update_row");
+ m_err_rec= NULL;
if ((error= get_parts_for_update(old_data, new_data, table->record[0],
m_part_info, &old_part_id, &new_part_id,
@@ -3601,6 +3622,25 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
m_part_info->err_value= func_value;
goto exit;
}
+ /*
+ The protocol for updating a row is:
+ 1) position the handler (cursor) on the row to be updated,
+ either through the last read row (rnd or index) or by rnd_pos.
+ 2) call update_row with both old and new full records as arguments.
+
+ This means that m_last_part should already be set to actual partition
+ where the row was read from. And if that is not the same as the
+ calculated part_id we found a misplaced row, we return an error to
+ notify the user that something is broken in the row distribution
+ between partitions! Since we don't check all rows on read, we return an
+ error instead of correcting m_last_part, to make the user aware of the
+ problem!
+ */
+ if (old_part_id != m_last_part)
+ {
+ m_err_rec= old_data;
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+ }
m_last_part= new_part_id;
start_part_bulk_insert(thd, new_part_id);
@@ -3705,12 +3745,34 @@ int ha_partition::delete_row(const uchar *buf)
int error;
THD *thd= ha_thd();
DBUG_ENTER("ha_partition::delete_row");
+ m_err_rec= NULL;
if ((error= get_part_for_delete(buf, m_rec0, m_part_info, &part_id)))
{
DBUG_RETURN(error);
}
- m_last_part= part_id;
+ /*
+ The protocol for deleting a row is:
+ 1) position the handler (cursor) on the row to be deleted,
+ either through the last read row (rnd or index) or by rnd_pos.
+ 2) call delete_row with the full record as argument.
+
+ This means that m_last_part should already be set to actual partition
+ where the row was read from. And if that is not the same as the
+ calculated part_id we found a misplaced row, we return an error to
+ notify the user that something is broken in the row distribution
+ between partitions! Since we don't check all rows on read, we return an
+ error instead of forwarding the delete to the correct (m_last_part)
+ partition!
+ TODO: change the assert in InnoDB into an error instead and make this one
+ an assert instead and remove the get_part_for_delete()!
+ */
+ if (part_id != m_last_part)
+ {
+ m_err_rec= buf;
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+ }
+
tmp_disable_binlog(thd);
error= m_file[part_id]->ha_delete_row(buf);
reenable_binlog(thd);
@@ -4557,7 +4619,6 @@ int ha_partition::index_init(uint inx, bool sorted)
file= m_file;
do
{
- /* TODO RONM: Change to index_init() when code is stable */
if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
if ((error= (*file)->ha_index_init(inx, sorted)))
{
@@ -7010,6 +7071,57 @@ enum row_type ha_partition::get_row_type() const
}
+void ha_partition::append_row_to_str(String &str)
+{
+ Field **field_ptr;
+ const uchar *rec;
+ bool is_rec0= !m_err_rec || m_err_rec == table->record[0];
+ if (is_rec0)
+ rec= table->record[0];
+ else
+ rec= m_err_rec;
+ // If PK, use full PK instead of full part field array!
+ if (table->s->primary_key != MAX_KEY)
+ {
+ KEY *key= table->key_info + table->s->primary_key;
+ KEY_PART_INFO *key_part= key->key_part;
+ KEY_PART_INFO *key_part_end= key_part + key->key_parts;
+ if (!is_rec0)
+ set_key_field_ptr(key, rec, table->record[0]);
+ for (; key_part != key_part_end; key_part++)
+ {
+ Field *field= key_part->field;
+ str.append(" ");
+ str.append(field->field_name);
+ str.append(":");
+ field_unpack(&str, field, rec, 0, false);
+ }
+ if (!is_rec0)
+ set_key_field_ptr(key, table->record[0], rec);
+ }
+ else
+ {
+ if (!is_rec0)
+ set_field_ptr(m_part_info->full_part_field_array, rec,
+ table->record[0]);
+ /* No primary key, use full partition field array. */
+ for (field_ptr= m_part_info->full_part_field_array;
+ *field_ptr;
+ field_ptr++)
+ {
+ Field *field= *field_ptr;
+ str.append(" ");
+ str.append(field->field_name);
+ str.append(":");
+ field_unpack(&str, field, rec, 0, false);
+ }
+ if (!is_rec0)
+ set_field_ptr(m_part_info->full_part_field_array, table->record[0],
+ rec);
+ }
+}
+
+
void ha_partition::print_error(int error, myf errflag)
{
THD *thd= ha_thd();
@@ -7018,24 +7130,72 @@ void ha_partition::print_error(int error, myf errflag)
/* Should probably look for my own errors first */
DBUG_PRINT("enter", ("error: %d", error));
- if ((error == HA_ERR_NO_PARTITION_FOUND) &&
- ! (thd->lex->alter_info.flags & ALTER_TRUNCATE_PARTITION))
- m_part_info->print_no_partition_found(table);
- else
+ if (error == HA_ERR_NO_PARTITION_FOUND)
{
- /* In case m_file has not been initialized, like in bug#42438 */
- if (m_file)
+ switch(thd_sql_command(thd))
{
- if (m_last_part >= m_tot_parts)
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ if (m_err_rec)
{
- DBUG_ASSERT(0);
- m_last_part= 0;
+ uint max_length;
+ char buf[MAX_KEY_LENGTH];
+ const char *msg= "Found a row in wrong partition (";
+ String str(buf,sizeof(buf),system_charset_info);
+ uint32 part_id;
+ /* Should only happen on DELETE or UPDATE! */
+ str.length(0);
+ str.append_ulonglong(m_last_part);
+ str.append(" != ");
+ if (!get_part_for_delete(m_err_rec, m_rec0, m_part_info, &part_id))
+ {
+ str.append_ulonglong(part_id);
+ }
+ str.append(")");
+ append_row_to_str(str);
+ /* Log this error, so the DBA can notice it and fix it! */
+ sql_print_error("Table '%-192s' corrupted: %s%s\n"
+ "Please CHECK and REPAIR the table!",
+ table->s->table_name.str, msg, str.c_ptr_safe());
+
+ max_length= (MYSQL_ERRMSG_SIZE-
+ (uint) strlen(msg));
+ if (str.length() >= max_length)
+ {
+ str.length(max_length-4);
+ str.append(STRING_WITH_LEN("..."));
+ }
+ my_printf_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, "%s%s", MYF(0),
+ msg, str.c_ptr_safe());
+ m_err_rec= NULL;
+ DBUG_VOID_RETURN;
+ }
+ default:
+ {
+ if (!(thd->lex->alter_info.flags & ALTER_TRUNCATE_PARTITION))
+ {
+ m_part_info->print_no_partition_found(table);
+ DBUG_VOID_RETURN;
+ }
}
- m_file[m_last_part]->print_error(error, errflag);
+ /* fall through to generic error handling. */
}
- else
- handler::print_error(error, errflag);
}
+
+ /* In case m_file has not been initialized, like in bug#42438 */
+ if (m_file)
+ {
+ if (m_last_part >= m_tot_parts)
+ {
+ DBUG_ASSERT(0);
+ m_last_part= 0;
+ }
+ m_file[m_last_part]->print_error(error, errflag);
+ }
+ else
+ handler::print_error(error, errflag);
DBUG_VOID_RETURN;
}
@@ -7729,6 +7889,288 @@ int ha_partition::indexes_are_disabled(void)
}
+/**
+ Check/fix misplaced rows.
+
+ @param read_part_id Partition to check/fix.
+ @param repair If true, move misplaced rows to correct partition.
+
+ @return Operation status.
+ @retval 0 Success
+ @retval != 0 Error
+*/
+
+int ha_partition::check_misplaced_rows(uint read_part_id, bool repair)
+{
+ int result= 0;
+ uint32 correct_part_id;
+ longlong func_value;
+ longlong num_misplaced_rows= 0;
+
+ DBUG_ENTER("ha_partition::check_misplaced_rows");
+
+ DBUG_ASSERT(m_file);
+
+ if (repair)
+ {
+ /* We must read the full row, if we need to move it! */
+ bitmap_set_all(table->read_set);
+ bitmap_set_all(table->write_set);
+ }
+ else
+ {
+ /* Only need to read the partitioning fields. */
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+ }
+
+ if ((result= m_file[read_part_id]->ha_rnd_init(1)))
+ DBUG_RETURN(result);
+
+ while (true)
+ {
+ if ((result= m_file[read_part_id]->rnd_next(m_rec0)))
+ {
+ if (result == HA_ERR_RECORD_DELETED)
+ continue;
+ if (result != HA_ERR_END_OF_FILE)
+ break;
+
+ if (num_misplaced_rows > 0)
+ {
+ print_admin_msg(ha_thd(), "warning", table_share->db.str, table->alias,
+ opt_op_name[REPAIR_PARTS],
+ "Moved %lld misplaced rows",
+ num_misplaced_rows);
+ }
+ /* End-of-file reached, all rows are now OK, reset result and break. */
+ result= 0;
+ break;
+ }
+
+ result= m_part_info->get_partition_id(m_part_info, &correct_part_id,
+ &func_value);
+ if (result)
+ break;
+
+ if (correct_part_id != read_part_id)
+ {
+ num_misplaced_rows++;
+ if (!repair)
+ {
+ /* Check. */
+ print_admin_msg(ha_thd(), "error", table_share->db.str, table->alias,
+ opt_op_name[CHECK_PARTS],
+ "Found a misplaced row");
+ /* Break on first misplaced row! */
+ result= HA_ADMIN_NEEDS_UPGRADE;
+ break;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Moving row from partition %d to %d",
+ read_part_id, correct_part_id));
+
+ /*
+ Insert row into correct partition. Notice that there are no commit
+ for every N row, so the repair will be one large transaction!
+ */
+ if ((result= m_file[correct_part_id]->ha_write_row(m_rec0)))
+ {
+ /*
+ We have failed to insert a row, it might have been a duplicate!
+ */
+ char buf[MAX_KEY_LENGTH];
+ String str(buf,sizeof(buf),system_charset_info);
+ str.length(0);
+ if (result == HA_ERR_FOUND_DUPP_KEY)
+ {
+ str.append("Duplicate key found, "
+ "please update or delete the record:\n");
+ result= HA_ADMIN_CORRUPT;
+ }
+ m_err_rec= NULL;
+ append_row_to_str(str);
+
+ /*
+ If the engine supports transactions, the failure will be
+ rollbacked.
+ */
+ if (!m_file[correct_part_id]->has_transactions())
+ {
+ /* Log this error, so the DBA can notice it and fix it! */
+ sql_print_error("Table '%-192s' failed to move/insert a row"
+ " from part %d into part %d:\n%s",
+ table->s->table_name.str,
+ read_part_id,
+ correct_part_id,
+ str.c_ptr_safe());
+ }
+ print_admin_msg(ha_thd(), "error", table_share->db.str, table->alias,
+ opt_op_name[REPAIR_PARTS],
+ "Failed to move/insert a row"
+ " from part %d into part %d:\n%s",
+ read_part_id,
+ correct_part_id,
+ str.c_ptr_safe());
+ break;
+ }
+
+ /* Delete row from wrong partition. */
+ if ((result= m_file[read_part_id]->ha_delete_row(m_rec0)))
+ {
+ if (m_file[correct_part_id]->has_transactions())
+ break;
+ /*
+ We have introduced a duplicate, since we failed to remove it
+ from the wrong partition.
+ */
+ char buf[MAX_KEY_LENGTH];
+ String str(buf,sizeof(buf),system_charset_info);
+ str.length(0);
+ m_err_rec= NULL;
+ append_row_to_str(str);
+
+ /* Log this error, so the DBA can notice it and fix it! */
+ sql_print_error("Table '%-192s': Delete from part %d failed with"
+ " error %d. But it was already inserted into"
+ " part %d, when moving the misplaced row!"
+ "\nPlease manually fix the duplicate row:\n%s",
+ table->s->table_name.str,
+ read_part_id,
+ result,
+ correct_part_id,
+ str.c_ptr_safe());
+ break;
+ }
+ }
+ }
+ }
+
+ int tmp_result= m_file[read_part_id]->ha_rnd_end();
+ DBUG_RETURN(result ? result : tmp_result);
+}
+
+
+#define KEY_PARTITIONING_CHANGED_STR \
+ "KEY () partitioning changed, please run:\nALTER TABLE %s.%s %s"
+
+int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt)
+{
+ int error= HA_ADMIN_NEEDS_CHECK;
+ DBUG_ENTER("ha_partition::check_for_upgrade");
+
+ /*
+ This is called even without FOR UPGRADE,
+ if the .frm version is lower than the current version.
+ In that case return that it needs checking!
+ */
+ if (!(check_opt->sql_flags & TT_FOR_UPGRADE))
+ DBUG_RETURN(error);
+
+ /*
+ Partitions will be checked for during their ha_check!
+
+ Check if KEY (sub)partitioning was used and any field's hash calculation
+ differs from 5.1, see bug#14521864.
+ */
+ if (table->s->mysql_version < 50503 && // 5.1 table (<5.5.3)
+ ((m_part_info->part_type == HASH_PARTITION && // KEY partitioned
+ m_part_info->list_of_part_fields) ||
+ (m_is_sub_partitioned && // KEY subpartitioned
+ m_part_info->list_of_subpart_fields)))
+ {
+ Field **field;
+ if (m_is_sub_partitioned)
+ {
+ field= m_part_info->subpart_field_array;
+ }
+ else
+ {
+ field= m_part_info->part_field_array;
+ }
+ for (; *field; field++)
+ {
+ switch ((*field)->real_type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ {
+ THD *thd= ha_thd();
+ char *part_buf;
+ String db_name, table_name;
+ uint part_buf_len;
+ bool skip_generation= false;
+ partition_info::enum_key_algorithm old_algorithm;
+ old_algorithm= m_part_info->key_algorithm;
+ error= HA_ADMIN_FAILED;
+ append_identifier(ha_thd(), &db_name, table_share->db.str,
+ table_share->db.length);
+ append_identifier(ha_thd(), &table_name, table_share->table_name.str,
+ table_share->table_name.length);
+ if (m_part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE)
+ {
+ /*
+ Only possible when someone tampered with .frm files,
+ like during tests :)
+ */
+ skip_generation= true;
+ }
+ m_part_info->key_algorithm= partition_info::KEY_ALGORITHM_51;
+ if (skip_generation ||
+ !(part_buf= generate_partition_syntax(m_part_info,
+ &part_buf_len,
+ true,
+ true,
+ NULL,
+ NULL,
+ NULL)) ||
+ /* Also check that the length is smaller than the output field! */
+ (part_buf_len + db_name.length() + table_name.length()) >=
+ (SQL_ADMIN_MSG_TEXT_SIZE -
+ (strlen(KEY_PARTITIONING_CHANGED_STR) - 3)))
+ {
+ print_admin_msg(thd, "error", table_share->db.str, table->alias,
+ opt_op_name[CHECK_PARTS],
+ KEY_PARTITIONING_CHANGED_STR,
+ db_name.c_ptr_safe(), table_name.c_ptr_safe(),
+ "<old partition clause>, but add ALGORITHM = 1"
+ " between 'KEY' and '(' to change the metadata"
+ " without the need of a full table rebuild.");
+ }
+ else
+ {
+ print_admin_msg(thd, "error", table_share->db.str, table->alias,
+ opt_op_name[CHECK_PARTS],
+ KEY_PARTITIONING_CHANGED_STR,
+ db_name.c_ptr_safe(), table_name.c_ptr_safe(),
+ part_buf);
+ }
+ m_part_info->key_algorithm= old_algorithm;
+ DBUG_RETURN(error);
+ }
+ default:
+ /* Not affected! */
+ ;
+ }
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+
struct st_mysql_storage_engine partition_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index 55f199401f3..fd1056d7b3f 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -2,7 +2,7 @@
#define HA_PARTITION_INCLUDED
/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,13 +25,15 @@
#include "queues.h" /* QUEUE */
enum partition_keywords
-{
+{
PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR,
- PKW_COLUMNS
+ PKW_COLUMNS, PKW_ALGORITHM
};
#define PARTITION_BYTES_IN_POS 2
-#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | HA_REC_NOT_IN_SEQ)
+#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | \
+ HA_REC_NOT_IN_SEQ | \
+ HA_CAN_REPAIR)
#define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \
HA_CAN_FULLTEXT | \
HA_DUPLICATE_POS | \
@@ -84,6 +86,7 @@ private:
*/
KEY *m_curr_key_info[3]; // Current index
uchar *m_rec0; // table->record[0]
+ const uchar *m_err_rec; // record which gave error
QUEUE m_queue; // Prio queue used by sorted read
/*
Since the partition handler is a handler on top of other handlers, it
@@ -1089,9 +1092,18 @@ public:
virtual bool check_and_repair(THD *thd);
virtual bool auto_repair(int error) const;
virtual bool is_crashed() const;
+ virtual int check_for_upgrade(HA_CHECK_OPT *check_opt);
private:
int handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flags);
+ int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, uint part_id,
+ uint flag);
+ /**
+ Check if the rows are placed in the correct partition. If the given
+ argument is true, then move the rows to the correct partition.
+ */
+ int check_misplaced_rows(uint read_part_id, bool repair);
+ void append_row_to_str(String &str);
public:
/*
-------------------------------------------------------------------------
diff --git a/sql/handler.cc b/sql/handler.cc
index 7d97c197da5..867d07f4990 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2012, Monty Program Ab.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@
#include "probes_mysql.h"
#include <mysql/psi/mysql_table.h>
#include "debug_sync.h" // DEBUG_SYNC
+#include "sql_audit.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -272,7 +273,8 @@ handler *get_ha_partition(partition_info *part_info)
}
else
{
- my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(sizeof(ha_partition)));
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ static_cast<int>(sizeof(ha_partition)));
}
DBUG_RETURN(((handler*) partition));
}
@@ -2199,7 +2201,7 @@ handle_condition(THD *,
{
*cond_hdl= NULL;
/* Grab the error message */
- strmake(buff, msg, sizeof(buff)-1);
+ strmake_buf(buff, msg);
return TRUE;
}
@@ -3712,6 +3714,9 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
}
if ((error= check(thd, check_opt)))
return error;
+ /* Skip updating frm version if not main handler. */
+ if (table->file != this)
+ return error;
return update_frm_version(table);
}
@@ -3964,7 +3969,6 @@ int
handler::ha_delete_table(const char *name)
{
mark_trx_read_write();
-
return delete_table(name);
}
@@ -3997,8 +4001,11 @@ int
handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info)
{
mark_trx_read_write();
-
- return create(name, form, info);
+ int error= create(name, form, info);
+ if (!error &&
+ !(info->options & (HA_LEX_CREATE_TMP_TABLE | HA_CREATE_TMP_ALTER)))
+ mysql_audit_create_table(form);
+ return error;
}
@@ -5505,7 +5512,11 @@ int handler::ha_external_lock(THD *thd, int lock_type)
{ error= external_lock(thd, lock_type); })
if (error == 0)
+ {
cached_table_flags= table_flags();
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_audit_external_lock(thd, table_share, lock_type);
+ }
if (MYSQL_HANDLER_RDLOCK_DONE_ENABLED() ||
MYSQL_HANDLER_WRLOCK_DONE_ENABLED() ||
@@ -5560,6 +5571,8 @@ int handler::ha_write_row(uchar *buf)
Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
DBUG_ENTER("handler::ha_write_row");
DEBUG_SYNC_C("ha_write_row_start");
+ DBUG_EXECUTE_IF("inject_error_ha_write_row",
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR); );
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
@@ -5590,6 +5603,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
(and the old record is in record[1]).
*/
DBUG_ASSERT(new_data == table->record[0]);
+ DBUG_ASSERT(old_data == table->record[1]);
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
@@ -5611,6 +5625,13 @@ int handler::ha_delete_row(const uchar *buf)
{
int error;
Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
+ /*
+ Normally table->record[0] is used, but sometimes table->record[1] is used.
+ */
+ DBUG_ASSERT(buf == table->record[0] ||
+ buf == table->record[1]);
+ DBUG_EXECUTE_IF("inject_error_ha_delete_row",
+ return HA_ERR_INTERNAL_ERROR; );
MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
diff --git a/sql/handler.h b/sql/handler.h
index a5fb0d8fd9d..99d75816c68 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -287,7 +287,7 @@
(yes, the sum is deliberately inaccurate)
TODO remove the limit, use dynarrays
*/
-#define MAX_HA 15
+#define MAX_HA 64
/*
Use this instead of 0 as the initial value for the slot number of
@@ -320,7 +320,7 @@
#define HA_LEX_CREATE_TMP_TABLE 1
#define HA_LEX_CREATE_IF_NOT_EXISTS 2
#define HA_LEX_CREATE_TABLE_LIKE 4
-#define HA_CREATE_TMP_ALTER 8
+#define HA_CREATE_TMP_ALTER 8
#define HA_MAX_REC_LENGTH 65535
/* Table caching type */
diff --git a/sql/item.cc b/sql/item.cc
index ecc7917022e..a5d5cf61496 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2010, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
@@ -513,7 +513,7 @@ Item::Item(THD *thd, Item *item):
with_field(item->with_field),
fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name),
- with_subselect(item->with_subselect),
+ with_subselect(item->has_subquery()),
collation(item->collation),
cmp_context(item->cmp_context)
{
@@ -4416,7 +4416,7 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
if (db_name && lower_case_table_names)
{
/* Convert database to lower case for comparison */
- strmake(name_buff, db_name, sizeof(name_buff)-1);
+ strmake_buf(name_buff, db_name);
my_casedn_str(files_charset_info, name_buff);
db_name= name_buff;
}
@@ -6127,17 +6127,8 @@ inline uint char_val(char X)
X-'a'+10);
}
-Item_hex_string::Item_hex_string()
-{
- hex_string_init("", 0);
-}
-
-Item_hex_string::Item_hex_string(const char *str, uint str_length)
-{
- hex_string_init(str, str_length);
-}
-void Item_hex_string::hex_string_init(const char *str, uint str_length)
+void Item_hex_constant::hex_string_init(const char *str, uint str_length)
{
max_length=(str_length+1)/2;
char *ptr=(char*) sql_alloc(max_length+1);
@@ -6161,7 +6152,7 @@ void Item_hex_string::hex_string_init(const char *str, uint str_length)
unsigned_flag= 1;
}
-longlong Item_hex_string::val_int()
+longlong Item_hex_hybrid::val_int()
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
@@ -6175,17 +6166,7 @@ longlong Item_hex_string::val_int()
}
-my_decimal *Item_hex_string::val_decimal(my_decimal *decimal_value)
-{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
- ulonglong value= (ulonglong)val_int();
- int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value);
- return (decimal_value);
-}
-
-
-int Item_hex_string::save_in_field(Field *field, bool no_conversions)
+int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions)
{
field->set_notnull();
if (field->result_type() == STRING_RESULT)
@@ -6218,22 +6199,27 @@ warn:
}
-void Item_hex_string::print(String *str, enum_query_type query_type)
+void Item_hex_hybrid::print(String *str, enum_query_type query_type)
{
- char *end= (char*) str_value.ptr() + str_value.length(),
- *ptr= end - min(str_value.length(), sizeof(longlong));
+ uint32 len= min(str_value.length(), sizeof(longlong));
+ const char *ptr= str_value.ptr() + str_value.length() - len;
str->append("0x");
- for (; ptr != end ; ptr++)
- {
- str->append(_dig_vec_lower[((uchar) *ptr) >> 4]);
- str->append(_dig_vec_lower[((uchar) *ptr) & 0x0F]);
- }
+ str->append_hex(ptr, len);
}
-bool Item_hex_string::eq(const Item *arg, bool binary_cmp) const
+void Item_hex_string::print(String *str, enum_query_type query_type)
{
- if (arg->basic_const_item() && arg->type() == type())
+ str->append("X'");
+ str->append_hex(str_value.ptr(), str_value.length());
+ str->append("'");
+}
+
+
+bool Item_hex_constant::eq(const Item *arg, bool binary_cmp) const
+{
+ if (arg->basic_const_item() && arg->type() == type() &&
+ arg->cast_to_int_type() == cast_to_int_type())
{
if (binary_cmp)
return !stringcmp(&str_value, &arg->str_value);
@@ -6243,7 +6229,7 @@ bool Item_hex_string::eq(const Item *arg, bool binary_cmp) const
}
-Item *Item_hex_string::safe_charset_converter(CHARSET_INFO *tocs)
+Item *Item_hex_constant::safe_charset_converter(CHARSET_INFO *tocs)
{
Item_string *conv;
String tmp, *str= val_str(&tmp);
diff --git a/sql/item.h b/sql/item.h
index b3d08011a39..1a373da9ba5 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1,8 +1,8 @@
#ifndef SQL_ITEM_INCLUDED
#define SQL_ITEM_INCLUDED
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013 Monty Program Ab
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013 Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -384,6 +384,12 @@ struct Name_resolution_context: Sql_alloc
{
(*error_processor)(thd, error_processor_data);
}
+ st_select_lex *outer_select()
+ {
+ return (outer_context ?
+ outer_context->select_lex :
+ NULL);
+ }
};
@@ -1466,6 +1472,12 @@ public:
Return TRUE if the item points to a column of an outer-joined table.
*/
virtual bool is_outer_field() const { DBUG_ASSERT(fixed); return FALSE; }
+
+ /**
+ Checks if this item or any of its decendents contains a subquery.
+ */
+ virtual bool has_subquery() const { return with_subselect; }
+
Item* set_expr_cache(THD *thd);
virtual Item_equal *get_item_equal() { return NULL; }
@@ -2754,36 +2766,112 @@ public:
};
-class Item_hex_string: public Item_basic_constant
+/**
+ Item_hex_constant -- a common class for hex literals: X'HHHH' and 0xHHHH
+*/
+class Item_hex_constant: public Item_basic_constant
{
+private:
+ void hex_string_init(const char *str, uint str_length);
public:
- Item_hex_string();
- Item_hex_string(const char *str,uint str_length);
+ Item_hex_constant()
+ {
+ hex_string_init("", 0);
+ }
+ Item_hex_constant(const char *str, uint str_length)
+ {
+ hex_string_init(str, str_length);
+ }
enum Type type() const { return VARBIN_ITEM; }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
+ bool basic_const_item() const { return 1; }
+ bool eq(const Item *item, bool binary_cmp) const;
+ String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
+};
+
+
+/**
+ Item_hex_hybrid -- is a class implementing 0xHHHH literals, e.g.:
+ SELECT 0x3132;
+ They can behave as numbers and as strings depending on context.
+*/
+class Item_hex_hybrid: public Item_hex_constant
+{
+public:
+ Item_hex_hybrid(): Item_hex_constant() {}
+ Item_hex_hybrid(const char *str, uint str_length):
+ Item_hex_constant(str, str_length) {}
double val_real()
{
DBUG_ASSERT(fixed == 1);
- return (double) (ulonglong) Item_hex_string::val_int();
+ return (double) (ulonglong) Item_hex_hybrid::val_int();
}
longlong val_int();
- bool basic_const_item() const { return 1; }
- String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
- my_decimal *val_decimal(my_decimal *);
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ {
+ // following assert is redundant, because fixed=1 assigned in constructor
+ DBUG_ASSERT(fixed == 1);
+ ulonglong value= (ulonglong) Item_hex_hybrid::val_int();
+ int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value);
+ return decimal_value;
+ }
int save_in_field(Field *field, bool no_conversions);
- enum Item_result result_type () const { return STRING_RESULT; }
enum Item_result cast_to_int_type() const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
- virtual void print(String *str, enum_query_type query_type);
- bool eq(const Item *item, bool binary_cmp) const;
- virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
- bool check_vcol_func_processor(uchar *arg) { return FALSE;}
-private:
- void hex_string_init(const char *str, uint str_length);
+ void print(String *str, enum_query_type query_type);
+};
+
+
+/**
+ Item_hex_string -- is a class implementing X'HHHH' literals, e.g.:
+ SELECT X'3132';
+ Unlike Item_hex_hybrid, X'HHHH' literals behave as strings in all contexts.
+ X'HHHH' are also used in replication of string constants in case of
+ "dangerous" charsets (sjis, cp932, big5, gbk) who can have backslash (0x5C)
+ as the second byte of a multi-byte character, so using '\' escaping for
+ these charsets is not desirable.
+*/
+class Item_hex_string: public Item_hex_constant
+{
+public:
+ Item_hex_string(): Item_hex_constant() {}
+ Item_hex_string(const char *str, uint str_length):
+ Item_hex_constant(str, str_length) {}
+ longlong val_int()
+ {
+ DBUG_ASSERT(fixed == 1);
+ return longlong_from_string_with_check(str_value.charset(),
+ str_value.ptr(),
+ str_value.ptr()+
+ str_value.length());
+ }
+ double val_real()
+ {
+ DBUG_ASSERT(fixed == 1);
+ return double_from_string_with_check(str_value.charset(),
+ str_value.ptr(),
+ str_value.ptr() +
+ str_value.length());
+ }
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ {
+ return val_decimal_from_string(decimal_value);
+ }
+ int save_in_field(Field *field, bool no_conversions)
+ {
+ field->set_notnull();
+ return field->store(str_value.ptr(), str_value.length(),
+ collation.collation);
+ }
+ enum Item_result cast_to_int_type() const { return STRING_RESULT; }
+ void print(String *str, enum_query_type query_type);
};
-class Item_bin_string: public Item_hex_string
+class Item_bin_string: public Item_hex_hybrid
{
public:
Item_bin_string(const char *str,uint str_length);
@@ -2987,6 +3075,14 @@ public:
DBUG_ASSERT(ref);
return (*ref)->is_outer_field();
}
+
+ /**
+ Checks if the item tree that ref points to contains a subquery.
+ */
+ virtual bool has_subquery() const
+ {
+ return (*ref)->has_subquery();
+ }
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 7ca85a72cfa..5f62ee946a5 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -4352,7 +4352,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
with_sum_func= with_sum_func || item->with_sum_func;
with_field= with_field || item->with_field;
- with_subselect|= item->with_subselect;
+ with_subselect|= item->has_subquery();
if (item->maybe_null)
maybe_null=1;
}
@@ -5052,7 +5052,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
return TRUE; /* purecov: inspected */
with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
with_field= args[0]->with_field || args[1]->with_field;
- with_subselect|= args[0]->with_subselect | args[1]->with_subselect;
+ with_subselect= args[0]->has_subquery() || args[1]->has_subquery();
max_length= 1;
decimals= 0;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index d0fca63688a..b150f76aabb 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -225,7 +225,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
with_field= with_field || item->with_field;
used_tables_cache|= item->used_tables();
const_item_cache&= item->const_item();
- with_subselect|= item->with_subselect;
+ with_subselect|= item->has_subquery();
}
}
fix_length_and_dec();
@@ -5466,7 +5466,7 @@ enum Item_result Item_func_get_user_var::result_type() const
void Item_func_get_user_var::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(@"));
- str->append(name.str,name.length);
+ append_identifier(current_thd, str, name.str, name.length);
str->append(')');
}
diff --git a/sql/item_func.h b/sql/item_func.h
index ab6ec706248..bdf91810d81 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -552,12 +552,18 @@ public:
class Item_func_signed :public Item_int_func
{
public:
- Item_func_signed(Item *a) :Item_int_func(a) {}
+ Item_func_signed(Item *a) :Item_int_func(a)
+ {
+ unsigned_flag= 0;
+ }
const char *func_name() const { return "cast_as_signed"; }
longlong val_int();
longlong val_int_from_str(int *error);
void fix_length_and_dec()
- { fix_char_length(args[0]->max_char_length()); unsigned_flag=0; }
+ {
+ fix_char_length(min(args[0]->max_char_length(),
+ MY_INT64_NUM_DECIMAL_DIGITS));
+ }
virtual void print(String *str, enum_query_type query_type);
uint decimal_precision() const { return args[0]->decimal_precision(); }
};
@@ -566,14 +572,11 @@ public:
class Item_func_unsigned :public Item_func_signed
{
public:
- Item_func_unsigned(Item *a) :Item_func_signed(a) {}
- const char *func_name() const { return "cast_as_unsigned"; }
- void fix_length_and_dec()
+ Item_func_unsigned(Item *a) :Item_func_signed(a)
{
- fix_char_length(min(args[0]->max_char_length(),
- DECIMAL_MAX_PRECISION + 2));
- unsigned_flag=1;
+ unsigned_flag= 1;
}
+ const char *func_name() const { return "cast_as_unsigned"; }
longlong val_int();
virtual void print(String *str, enum_query_type query_type);
};
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 5dcd83681c6..5bf53784c26 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -62,6 +62,8 @@ C_MODE_END
#include <sql_repl.h>
#include "sql_statistics.h"
+size_t username_char_length= 16;
+
/**
@todo Remove this. It is not safe to use a shared String object.
*/
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 169da25e826..9daddf94c0b 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -25,6 +25,8 @@
#pragma interface /* gcc class implementation */
#endif
+extern size_t username_char_length;
+
class MY_LOCALE;
class Item_str_func :public Item_func
@@ -510,8 +512,8 @@ public:
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec()
{
- max_length= (USERNAME_LENGTH +
- (HOSTNAME_LENGTH + 1) * SYSTEM_CHARSET_MBMAXLEN);
+ max_length= (username_char_length +
+ HOSTNAME_LENGTH + 1) * SYSTEM_CHARSET_MBMAXLEN;
}
const char *func_name() const { return "user"; }
const char *fully_qualified_func_name() const { return "user()"; }
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 03afd1a4365..d441653c82b 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -65,7 +65,15 @@ ulonglong Item_sum::ram_limitation(THD *thd)
bool Item_sum::init_sum_func_check(THD *thd)
{
- if (!thd->lex->allow_sum_func)
+ SELECT_LEX *curr_sel= thd->lex->current_select;
+ if (!curr_sel->name_visibility_map)
+ {
+ for (SELECT_LEX *sl= curr_sel; sl; sl= sl->context.outer_select())
+ {
+ curr_sel->name_visibility_map|= (1 << sl-> nest_level);
+ }
+ }
+ if (!(thd->lex->allow_sum_func & curr_sel->name_visibility_map))
{
my_message(ER_INVALID_GROUP_FUNC_USE, ER(ER_INVALID_GROUP_FUNC_USE),
MYF(0));
@@ -136,8 +144,11 @@ bool Item_sum::init_sum_func_check(THD *thd)
bool Item_sum::check_sum_func(THD *thd, Item **ref)
{
+ SELECT_LEX *curr_sel= thd->lex->current_select;
+ nesting_map allow_sum_func= (thd->lex->allow_sum_func &
+ curr_sel->name_visibility_map);
bool invalid= FALSE;
- nesting_map allow_sum_func= thd->lex->allow_sum_func;
+ DBUG_ASSERT(curr_sel->name_visibility_map); // should be set already
/*
The value of max_arg_level is updated if an argument of the set function
contains a column reference resolved against a subquery whose level is
@@ -172,7 +183,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
if (!invalid && aggr_level < 0)
{
aggr_level= nest_level;
- aggr_sel= thd->lex->current_select;
+ aggr_sel= curr_sel;
}
/*
By this moment we either found a subquery where the set function is
@@ -309,9 +320,9 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
{
SELECT_LEX *sl;
nesting_map allow_sum_func= thd->lex->allow_sum_func;
- for (sl= thd->lex->current_select->master_unit()->outer_select() ;
+ for (sl= thd->lex->current_select->context.outer_select() ;
sl && sl->nest_level > max_arg_level;
- sl= sl->master_unit()->outer_select() )
+ sl= sl->context.outer_select())
{
if (aggr_level < 0 &&
(allow_sum_func & ((nesting_map)1 << sl->nest_level)))
diff --git a/sql/key.cc b/sql/key.cc
index dd7818119c8..0d3db2d5bf5 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -335,6 +335,70 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
return 0;
}
+
+/**
+ Unpack a field and append it.
+
+ @param[inout] to String to append the field contents to.
+ @param field Field to unpack.
+ @param rec Record which contains the field data.
+ @param max_length Maximum length of field to unpack
+ or 0 for unlimited.
+ @param prefix_key The field is used as a prefix key.
+*/
+
+void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
+ bool prefix_key)
+{
+ String tmp;
+ DBUG_ENTER("field_unpack");
+ if (!max_length)
+ max_length= field->pack_length();
+ if (field)
+ {
+ if (field->is_null())
+ {
+ to->append(STRING_WITH_LEN("NULL"));
+ DBUG_VOID_RETURN;
+ }
+ CHARSET_INFO *cs= field->charset();
+ field->val_str(&tmp);
+ /*
+ For BINARY(N) strip trailing zeroes to make
+ the error message nice-looking
+ */
+ if (field->binary() && field->type() == MYSQL_TYPE_STRING && tmp.length())
+ {
+ const char *tmp_end= tmp.ptr() + tmp.length();
+ while (tmp_end > tmp.ptr() && !*--tmp_end) ;
+ tmp.length(tmp_end - tmp.ptr() + 1);
+ }
+ if (cs->mbmaxlen > 1 && prefix_key)
+ {
+ /*
+ Prefix key, multi-byte charset.
+ For the columns of type CHAR(N), the above val_str()
+ call will return exactly "key_part->length" bytes,
+ which can break a multi-byte characters in the middle.
+ Align, returning not more than "char_length" characters.
+ */
+ uint charpos, char_length= max_length / cs->mbmaxlen;
+ if ((charpos= my_charpos(cs, tmp.ptr(),
+ tmp.ptr() + tmp.length(),
+ char_length)) < tmp.length())
+ tmp.length(charpos);
+ }
+ if (max_length < field->pack_length())
+ tmp.length(min(tmp.length(),max_length));
+ ErrConvString err(&tmp);
+ to->append(err.ptr());
+ }
+ else
+ to->append(STRING_WITH_LEN("???"));
+ DBUG_VOID_RETURN;
+}
+
+
/*
unpack key-fields from record to some buffer.
@@ -352,8 +416,6 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
void key_unpack(String *to,TABLE *table,uint idx)
{
KEY_PART_INFO *key_part,*key_part_end;
- Field *field;
- String tmp;
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
DBUG_ENTER("key_unpack");
@@ -369,47 +431,13 @@ void key_unpack(String *to,TABLE *table,uint idx)
{
if (table->record[0][key_part->null_offset] & key_part->null_bit)
{
- to->append(STRING_WITH_LEN("NULL"));
- continue;
- }
- }
- if ((field=key_part->field))
- {
- CHARSET_INFO *cs= field->charset();
- field->val_str(&tmp);
- /*
- For BINARY(N) strip trailing zeroes to make
- the error message nice-looking
- */
- if (field->binary() && field->type() == MYSQL_TYPE_STRING && tmp.length())
- {
- const char *tmp_end= tmp.ptr() + tmp.length();
- while (tmp_end > tmp.ptr() && !*--tmp_end) ;
- tmp.length(tmp_end - tmp.ptr() + 1);
- }
- if (cs->mbmaxlen > 1 && (key_part->key_part_flag & HA_PART_KEY_SEG))
- {
- /*
- Prefix key, multi-byte charset.
- For the columns of type CHAR(N), the above val_str()
- call will return exactly "key_part->length" bytes,
- which can break a multi-byte characters in the middle.
- Align, returning not more than "char_length" characters.
- */
- uint charpos, char_length= key_part->length / cs->mbmaxlen;
- if ((charpos= my_charpos(cs, tmp.ptr(),
- tmp.ptr() + tmp.length(),
- char_length)) < tmp.length())
- tmp.length(charpos);
+ to->append(STRING_WITH_LEN("NULL"));
+ continue;
}
- if (key_part->length < field->pack_length())
- tmp.length(min(tmp.length(),key_part->length));
- ErrConvString err(&tmp);
- to->append(err.ptr());
}
- else
- to->append(STRING_WITH_LEN("???"));
- }
+ field_unpack(to, key_part->field, table->record[0], key_part->length,
+ test(key_part->key_part_flag & HA_PART_KEY_SEG));
+ }
dbug_tmp_restore_column_map(table->read_set, old_map);
DBUG_VOID_RETURN;
}
diff --git a/sql/key.h b/sql/key.h
index 8bf6f88fa04..0eeda58cd17 100644
--- a/sql/key.h
+++ b/sql/key.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,6 +33,8 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
uint key_length);
bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length);
void key_unpack(String *to,TABLE *form,uint index);
+void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
+ bool prefix_key);
bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields);
int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length);
ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key);
diff --git a/sql/log.cc b/sql/log.cc
index 6726be36e74..69447503c47 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -186,7 +186,7 @@ Silence_log_table_errors::handle_condition(THD *,
MYSQL_ERROR ** cond_hdl)
{
*cond_hdl= NULL;
- strmake(m_message, msg, sizeof(m_message)-1);
+ strmake_buf(m_message, msg);
return TRUE;
}
@@ -3385,8 +3385,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
mysql_file_sync(log_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
goto err;
mysql_mutex_lock(&LOCK_commit_ordered);
- strmake(last_commit_pos_file, log_file_name,
- sizeof(last_commit_pos_file)-1);
+ strmake_buf(last_commit_pos_file, log_file_name);
last_commit_pos_offset= my_b_tell(&log_file);
mysql_mutex_unlock(&LOCK_commit_ordered);
@@ -3474,7 +3473,7 @@ int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo)
int MYSQL_BIN_LOG::raw_get_current_log(LOG_INFO* linfo)
{
- strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name)-1);
+ strmake_buf(linfo->log_file_name, log_file_name);
linfo->pos = my_b_tell(&log_file);
return 0;
}
@@ -3995,8 +3994,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
Reset rli's coordinates to the current log.
*/
rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
- strmake(rli->event_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name);
/*
If we removed the rli->group_relay_log_name file,
@@ -4006,8 +4004,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
if (included)
{
rli->group_relay_log_pos = BIN_LOG_HEADER_SIZE;
- strmake(rli->group_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->group_relay_log_name)-1);
+ strmake_buf(rli->group_relay_log_name,rli->linfo.log_file_name);
rli->notify_group_relay_log_name_update();
}
@@ -4515,9 +4512,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
else
{
if (stat_area.st_mtime < purge_time)
- strmake(to_log,
- log_info.log_file_name,
- sizeof(log_info.log_file_name) - 1);
+ strmake_buf(to_log, log_info.log_file_name);
else
break;
}
@@ -5159,8 +5154,7 @@ binlog_start_consistent_snapshot(handlerton *hton, THD *thd)
binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
/* Server layer calls us with LOCK_commit_ordered locked, so this is safe. */
- strmake(cache_mngr->last_commit_pos_file, mysql_bin_log.last_commit_pos_file,
- sizeof(cache_mngr->last_commit_pos_file)-1);
+ strmake_buf(cache_mngr->last_commit_pos_file, mysql_bin_log.last_commit_pos_file);
cache_mngr->last_commit_pos_offset= mysql_bin_log.last_commit_pos_offset;
trans_register_ha(thd, TRUE, hton);
@@ -6742,8 +6736,8 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
if ((current->error= write_transaction_or_stmt(current)))
current->commit_errno= errno;
- strmake(cache_mngr->last_commit_pos_file, log_file_name,
- sizeof(cache_mngr->last_commit_pos_file)-1);
+
+ strmake_buf(cache_mngr->last_commit_pos_file, log_file_name);
commit_offset= my_b_write_tell(&log_file);
cache_mngr->last_commit_pos_offset= commit_offset;
if (cache_mngr->using_xa && cache_mngr->xa_xid)
@@ -8262,7 +8256,7 @@ int TC_LOG_BINLOG::open(const char *opt_name)
do
{
- strmake(log_name, log_info.log_file_name, sizeof(log_name)-1);
+ strmake_buf(log_name, log_info.log_file_name);
} while (!(error= find_next_log(&log_info, 1)));
if (error != LOG_INFO_EOF)
@@ -8988,7 +8982,7 @@ static void
set_binlog_snapshot_file(const char *src)
{
int dir_len = dirname_length(src);
- strmake(binlog_snapshot_file, src + dir_len, sizeof(binlog_snapshot_file)-1);
+ strmake_buf(binlog_snapshot_file, src + dir_len);
}
/*
diff --git a/sql/log_event.cc b/sql/log_event.cc
index fce0130e8dd..0782e40981e 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -672,16 +672,18 @@ static inline int read_str(const char **buf, const char *buf_end,
/**
- Transforms a string into "" or its expression in 0x... form.
+ Transforms a string into "" or its expression in X'HHHH' form.
*/
char *str_to_hex(char *to, const char *from, uint len)
{
if (len)
{
- *to++= '0';
- *to++= 'x';
+ *to++= 'X';
+ *to++= '\'';
to= octet2hex(to, from, len);
+ *to++= '\'';
+ *to= '\0';
}
else
to= strmov(to, "\"\"");
@@ -702,7 +704,7 @@ append_query_string(THD *thd, CHARSET_INFO *csinfo,
{
char *beg, *ptr;
uint32 const orig_len= to->length();
- if (to->reserve(orig_len + from->length()*2+3))
+ if (to->reserve(orig_len + from->length() * 2 + 4))
return 1;
beg= (char*) to->ptr() + to->length();
@@ -1270,7 +1272,9 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
int Log_event::read_log_event(IO_CACHE* file, String* packet,
mysql_mutex_t* log_lock,
- uint8 checksum_alg_arg)
+ uint8 checksum_alg_arg,
+ const char *log_file_name_arg,
+ bool* is_binlog_active)
{
ulong data_len;
int result=0;
@@ -1280,6 +1284,10 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
if (log_lock)
mysql_mutex_lock(log_lock);
+
+ if (log_file_name_arg)
+ *is_binlog_active= mysql_bin_log.is_active(log_file_name_arg);
+
if (my_b_read(file, (uchar*) buf, sizeof(buf)))
{
/*
@@ -7048,7 +7056,7 @@ void User_var_log_event::pack_info(THD *thd, Protocol* protocol)
buf.append(" "))
return;
old_len= buf.length();
- if (buf.reserve(old_len + val_len*2 + 2 + sizeof(" COLLATE ") +
+ if (buf.reserve(old_len + val_len * 2 + 3 + sizeof(" COLLATE ") +
MY_CS_NAME_SIZE))
return;
beg= const_cast<char *>(buf.ptr()) + old_len;
@@ -7091,10 +7099,9 @@ User_var_log_event(const char* buf, uint event_len,
/*
We don't know yet is_null value, so we must assume that name_len
may have the bigger value possible, is_null= True and there is no
- payload for val.
+ payload for val, or even that name_len is 0.
*/
- if (0 == name_len ||
- !valid_buffer_range<uint>(name_len, buf_start, name,
+ if (!valid_buffer_range<uint>(name_len, buf_start, name,
event_len - UV_VAL_IS_NULL))
{
error= true;
@@ -7317,7 +7324,8 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
char *hex_str;
CHARSET_INFO *cs;
- hex_str= (char *)my_malloc(2*val_len+1+2,MYF(MY_WME)); // 2 hex digits / byte
+ // 2 hex digits / byte
+ hex_str= (char *) my_malloc(2 * val_len + 1 + 3, MYF(MY_WME));
if (!hex_str)
return;
str_to_hex(hex_str, val, val_len);
@@ -10251,6 +10259,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id);
table_list->updating= 1;
+ table_list->required_type= FRMTYPE_TABLE;
DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id));
enum_tbl_map_status tblmap_status= check_table_map(rli, table_list);
if (tblmap_status == OK_TO_PROCESS)
diff --git a/sql/log_event.h b/sql/log_event.h
index b5b488f320d..b54e2028ef2 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -1121,8 +1121,35 @@ public:
const Format_description_log_event
*description_event,
my_bool crc_check);
+
+ /**
+ Reads an event from a binlog or relay log. Used by the dump thread
+ this method reads the event into a raw buffer without parsing it.
+
+ @Note If mutex is 0, the read will proceed without mutex.
+
+ @Note If a log name is given than the method will check if the
+ given binlog is still active.
+
+ @param[in] file log file to be read
+ @param[out] packet packet to hold the event
+ @param[in] lock the lock to be used upon read
+ @param[in] log_file_name_arg the log's file name
+ @param[out] is_binlog_active is the current log still active
+
+ @retval 0 success
+ @retval LOG_READ_EOF end of file, nothing was read
+ @retval LOG_READ_BOGUS malformed event
+ @retval LOG_READ_IO io error while reading
+ @retval LOG_READ_MEM packet memory allocation failed
+ @retval LOG_READ_TRUNC only a partial event could be read
+ @retval LOG_READ_TOO_LARGE event too large
+ */
static int read_log_event(IO_CACHE* file, String* packet,
- mysql_mutex_t* log_lock, uint8 checksum_alg_arg);
+ mysql_mutex_t* log_lock,
+ uint8 checksum_alg_arg,
+ const char *log_file_name_arg = NULL,
+ bool* is_binlog_active = NULL);
/*
init_show_field_list() prepares the column names and types for the
output of SHOW BINLOG EVENTS; it is used only by SHOW BINLOG
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 6a1ca2e09d3..6f28760c055 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -54,6 +54,7 @@ static char *opt_os_password;
static my_bool opt_default_user;
static my_bool opt_allow_remote_root_access;
static my_bool opt_skip_networking;
+static my_bool opt_verbose_bootstrap;
static my_bool verbose_errors;
@@ -83,6 +84,8 @@ static struct my_option my_long_options[]=
0, 0},
{"silent", 's', "Print less information", &opt_silent,
&opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"verbose-bootstrap", 'o', "Include mysqld bootstrap output",&opt_verbose_bootstrap,
+ &opt_verbose_bootstrap, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
@@ -244,11 +247,12 @@ static char *init_bootstrap_command_line(char *cmdline, size_t size)
get_basedir(basedir, sizeof(basedir), mysqld_path);
my_snprintf(cmdline, size-1,
- "\"\"%s\" --no-defaults --bootstrap"
- " \"--language=%s\\share\\english\""
+ "\"\"%s\" --no-defaults %s --bootstrap"
+ " \"--lc-messages-dir=%s/share\""
" --basedir=. --datadir=. --default-storage-engine=myisam"
" --max_allowed_packet=9M "
- " --net-buffer-length=16k\"", mysqld_path, basedir);
+ " --net-buffer-length=16k\"", mysqld_path,
+ opt_verbose_bootstrap?"--console":"", basedir );
return cmdline;
}
@@ -377,7 +381,7 @@ static int register_service()
static void clean_directory(const char *dir)
{
char dir2[MAX_PATH+2];
- *(strmake(dir2, dir, MAX_PATH+1)+1)= 0;
+ *(strmake_buf(dir2, dir)+1)= 0;
SHFILEOPSTRUCT fileop;
fileop.hwnd= NULL; /* no status display */
@@ -552,7 +556,9 @@ static int create_db_instance()
/* Do mysqld --bootstrap. */
init_bootstrap_command_line(cmdline, sizeof(cmdline));
- /* verbose("Executing %s", cmdline); */
+
+ if(opt_verbose_bootstrap)
+ printf("Executing %s\n", cmdline);
in= popen(cmdline, "wt");
if (!in)
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 0a49eb0c7ee..301555fa3c5 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -1004,7 +1004,8 @@ static PSI_cond_info all_server_conds[]=
PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_main,
- key_thread_one_connection, key_thread_signal_hand;
+ key_thread_one_connection, key_thread_signal_hand,
+ key_thread_slave_init;
static PSI_thread_info all_server_threads[]=
{
@@ -1029,7 +1030,8 @@ static PSI_thread_info all_server_threads[]=
{ &key_thread_handle_manager, "manager", PSI_FLAG_GLOBAL},
{ &key_thread_main, "main", PSI_FLAG_GLOBAL},
{ &key_thread_one_connection, "one_connection", 0},
- { &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL}
+ { &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL},
+ { &key_thread_slave_init, "slave_init", PSI_FLAG_GLOBAL}
};
#ifdef HAVE_MMAP
@@ -3416,14 +3418,25 @@ sizeof(load_default_groups)/sizeof(load_default_groups[0]);
#ifndef EMBEDDED_LIBRARY
-static
-int
-check_enough_stack_size()
+/**
+ This function is used to check for stack overrun for pathological
+ cases of regular expressions and 'like' expressions.
+ The call to current_thd is quite expensive, so we try to avoid it
+ for the normal cases.
+ The size of each stack frame for the wildcmp() routines is ~128 bytes,
+ so checking *every* recursive call is not necessary.
+ */
+extern "C" int
+check_enough_stack_size(int recurse_level)
{
uchar stack_top;
+ if (recurse_level % 16 != 0)
+ return 0;
- return check_stack_overrun(current_thd, STACK_MIN_SIZE,
- &stack_top);
+ THD *my_thd= current_thd;
+ if (my_thd != NULL)
+ return check_stack_overrun(my_thd, STACK_MIN_SIZE * 2, &stack_top);
+ return 0;
}
#endif
@@ -3781,7 +3794,7 @@ static int init_common_variables()
WideCharToMultiByte(CP_UTF8,0, wtz_name, -1, system_time_zone,
sizeof(system_time_zone) - 1, NULL, NULL);
#else
- strmake(system_time_zone, tz_name, sizeof(system_time_zone)-1);
+ strmake_buf(system_time_zone, tz_name);
#endif /* _WIN32 */
#endif /* HAVE_TZNAME */
@@ -4047,6 +4060,7 @@ static int init_common_variables()
item_init();
#ifndef EMBEDDED_LIBRARY
my_regex_init(&my_charset_latin1, check_enough_stack_size);
+ my_string_stack_guard= check_enough_stack_size;
#else
my_regex_init(&my_charset_latin1, NULL);
#endif
@@ -4563,11 +4577,13 @@ will be ignored as the --log-bin option is not defined.");
}
#endif
+ DBUG_ASSERT(!opt_bin_log || opt_bin_logname);
+
if (opt_bin_log)
{
/* Reports an error and aborts, if the --log-bin's path
is a directory.*/
- if (opt_bin_logname &&
+ if (opt_bin_logname[0] &&
opt_bin_logname[strlen(opt_bin_logname) - 1] == FN_LIBCHAR)
{
sql_print_error("Path '%s' is a directory name, please specify \
@@ -4589,7 +4605,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
char buf[FN_REFLEN];
const char *ln;
ln= mysql_bin_log.generate_name(opt_bin_logname, "-bin", 1, buf);
- if (!opt_bin_logname && !opt_binlog_index_name)
+ if (!opt_bin_logname[0] && !opt_binlog_index_name)
{
/*
User didn't give us info to name the binlog index file.
@@ -7840,8 +7856,8 @@ static int mysql_init_variables(void)
/* Set directory paths */
mysql_real_data_home_len=
- strmake(mysql_real_data_home, get_relative_path(MYSQL_DATADIR),
- sizeof(mysql_real_data_home)-1) - mysql_real_data_home;
+ strmake_buf(mysql_real_data_home,
+ get_relative_path(MYSQL_DATADIR)) - mysql_real_data_home;
/* Replication parameters */
master_info_file= (char*) "master.info",
relay_log_info_file= (char*) "relay-log.info";
@@ -7950,7 +7966,7 @@ static int mysql_init_variables(void)
const char *tmpenv;
if (!(tmpenv = getenv("MY_BASEDIR_VERSION")))
tmpenv = DEFAULT_MYSQL_HOME;
- (void) strmake(mysql_home, tmpenv, sizeof(mysql_home)-1);
+ strmake_buf(mysql_home, tmpenv);
#endif
return 0;
}
@@ -7989,7 +8005,7 @@ mysqld_get_one_option(int optid,
global_system_variables.tx_isolation= ISO_SERIALIZABLE;
break;
case 'b':
- strmake(mysql_home,argument,sizeof(mysql_home)-1);
+ strmake_buf(mysql_home, argument);
break;
case 'C':
if (default_collation_name == compiled_default_collation_name)
@@ -8000,7 +8016,7 @@ mysqld_get_one_option(int optid,
opt_log=1;
break;
case 'h':
- strmake(mysql_real_data_home,argument, sizeof(mysql_real_data_home)-1);
+ strmake_buf(mysql_real_data_home, argument);
/* Correct pointer set by my_getopt (for embedded library) */
mysql_real_data_home_ptr= mysql_real_data_home;
break;
@@ -8011,7 +8027,7 @@ mysqld_get_one_option(int optid,
sql_print_warning("Ignoring user change to '%s' because the user was set to '%s' earlier on the command line\n", argument, mysqld_user);
break;
case 'L':
- strmake(lc_messages_dir, argument, sizeof(lc_messages_dir)-1);
+ strmake_buf(lc_messages_dir, argument);
break;
case OPT_BINLOG_FORMAT:
binlog_format_used= true;
@@ -8804,7 +8820,7 @@ static int fix_paths(void)
char *sharedir=get_relative_path(SHAREDIR);
if (test_if_hard_path(sharedir))
- strmake(buff,sharedir,sizeof(buff)-1); /* purecov: tested */
+ strmake_buf(buff, sharedir); /* purecov: tested */
else
strxnmov(buff,sizeof(buff)-1,mysql_home,sharedir,NullS);
convert_dirname(buff,buff,NullS);
@@ -8812,7 +8828,7 @@ static int fix_paths(void)
/* If --character-sets-dir isn't given, use shared library dir */
if (charsets_dir)
- strmake(mysql_charsets_dir, charsets_dir, sizeof(mysql_charsets_dir)-1);
+ strmake_buf(mysql_charsets_dir, charsets_dir);
else
strxnmov(mysql_charsets_dir, sizeof(mysql_charsets_dir)-1, buff,
CHARSET_DIR, NullS);
diff --git a/sql/mysqld.h b/sql/mysqld.h
index e07b5d5c41c..eb72b7e2736 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -285,7 +285,7 @@ extern PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_kill_server, key_thread_main,
- key_thread_one_connection, key_thread_signal_hand;
+ key_thread_one_connection, key_thread_signal_hand, key_thread_slave_init;
extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
key_file_dbopt, key_file_des_key_file, key_file_ERRMSG, key_select_to_file,
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 37a7944bbaf..659e63caf6c 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -7689,6 +7689,14 @@ static SEL_TREE *get_full_func_mm_tree(RANGE_OPT_PARAM *param,
param->current_table);
DBUG_ENTER("get_full_func_mm_tree");
+#ifdef HAVE_SPATIAL
+ if (field_item->field->type() == MYSQL_TYPE_GEOMETRY)
+ {
+ /* We have to be able to store all sorts of spatial features here */
+ ((Field_geom*) field_item->field)->geom_type= Field::GEOM_GEOMETRY;
+ }
+#endif /*HAVE_SPATIAL*/
+
for (uint i= 0; i < cond_func->arg_count; i++)
{
Item *arg= cond_func->arguments()[i]->real_item();
@@ -11248,9 +11256,13 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
do
{
+ DBUG_EXECUTE_IF("innodb_quick_report_deadlock",
+ DBUG_SET("+d,innodb_report_deadlock"););
if ((error= quick->get_next()))
{
- quick_with_last_rowid->file->unlock_row();
+ /* On certain errors like deadlock, trx might be rolled back.*/
+ if (!current_thd->transaction_rollback_request)
+ quick_with_last_rowid->file->unlock_row();
DBUG_RETURN(error);
}
quick->file->position(quick->record);
@@ -11276,7 +11288,9 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
quick->file->unlock_row(); /* row not in range; unlock */
if ((error= quick->get_next()))
{
- quick_with_last_rowid->file->unlock_row();
+ /* On certain errors like deadlock, trx might be rolled back.*/
+ if (!current_thd->transaction_rollback_request)
+ quick_with_last_rowid->file->unlock_row();
DBUG_RETURN(error);
}
}
@@ -12161,13 +12175,15 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
NGA1.If in the index I there is a gap between the last GROUP attribute G_k,
and the MIN/MAX attribute C, then NGA must consist of exactly the
index attributes that constitute the gap. As a result there is a
- permutation of NGA that coincides with the gap in the index
- <B_1, ..., B_m>.
+ permutation of NGA, BA=<B_1,...,B_m>, that coincides with the gap
+ in the index.
NGA2.If BA <> {}, then the WHERE clause must contain a conjunction EQ of
equality conditions for all NG_i of the form (NG_i = const) or
(const = NG_i), such that each NG_i is referenced in exactly one
conjunct. Informally, the predicates provide constants to fill the
gap in the index.
+ NGA3.If BA <> {}, there can only be one range. TODO: This is a code
+ limitation and is not strictly needed. See BUG#15947433
WA1. There are no other attributes in the WHERE clause except the ones
referenced in predicates RNG, PA, PC, EQ defined above. Therefore
WA is subset of (GA union NGA union C) for GA,NGA,C that pass the
@@ -12910,6 +12926,74 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
/*
+ Get SEL_ARG tree, if any, for the keypart covering non grouping
+ attribute (NGA) field 'nga_field'.
+
+ This function enforces the NGA3 test: If 'keypart_tree' contains a
+ condition for 'nga_field', there can only be one range. In the
+ opposite case, this function returns with error and 'cur_range'
+ should not be used.
+
+ Note that the NGA1 and NGA2 requirements, like whether or not the
+ range predicate for 'nga_field' is equality, is not tested by this
+ function.
+
+ @param[in] nga_field The NGA field we want the SEL_ARG tree for
+ @param[in] keypart_tree Root node of the SEL_ARG* tree for the index
+ @param[out] cur_range The SEL_ARG tree, if any, for the keypart
+ covering field 'keypart_field'
+ @retval true 'keypart_tree' contained a predicate for 'nga_field' but
+ multiple ranges exists. 'cur_range' should not be used.
+ @retval false otherwise
+*/
+
+static bool
+get_sel_arg_for_keypart(Field *nga_field,
+ SEL_ARG *keypart_tree,
+ SEL_ARG **cur_range)
+{
+ if(keypart_tree == NULL)
+ return false;
+ if(keypart_tree->field->eq(nga_field))
+ {
+ /*
+ Enforce NGA3: If a condition for nga_field has been found, only
+ a single range is allowed.
+ */
+ if (keypart_tree->prev || keypart_tree->next)
+ return true; // There are multiple ranges
+
+ *cur_range= keypart_tree;
+ return false;
+ }
+
+ SEL_ARG *found_tree= NULL;
+ SEL_ARG *first_kp= keypart_tree->first();
+
+ for (SEL_ARG *cur_kp= first_kp; cur_kp && !found_tree;
+ cur_kp= cur_kp->next)
+ {
+ if (cur_kp->next_key_part)
+ {
+ if (get_sel_arg_for_keypart(nga_field,
+ cur_kp->next_key_part,
+ &found_tree))
+ return true;
+
+ }
+ /*
+ Enforce NGA3: If a condition for nga_field has been found,only
+ a single range is allowed.
+ */
+ if (found_tree && first_kp->next)
+ return true; // There are multiple ranges
+ }
+ *cur_range= found_tree;
+ return false;
+}
+
+
+/*
Extract a sequence of constants from a conjunction of equality predicates.
SYNOPSIS
@@ -12923,12 +13007,13 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
key_infix [out] Infix of constants to be used for index lookup
key_infix_len [out] Lenghth of the infix
first_non_infix_part [out] The first keypart after the infix (if any)
-
+
DESCRIPTION
- Test conditions (NGA1, NGA2) from get_best_group_min_max(). Namely,
- for each keypart field NGF_i not in GROUP-BY, check that there is a
- constant equality predicate among conds with the form (NGF_i = const_ci) or
- (const_ci = NGF_i).
+ Test conditions (NGA1, NGA2, NGA3) from get_best_group_min_max(). Namely,
+ for each keypart field NG_i not in GROUP-BY, check that there is exactly one
+ constant equality predicate among conds with the form (NG_i = const_ci) or
+ (const_ci = NG_i).. In addition, there can only be one range when there is
+ such a gap.
Thus all the NGF_i attributes must fill the 'gap' between the last group-by
attribute and the MIN/MAX attribute in the index (if present). If these
conditions hold, copy each constant from its corresponding predicate into
@@ -12957,17 +13042,14 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
uchar *key_ptr= key_infix;
for (cur_part= first_non_group_part; cur_part != end_part; cur_part++)
{
+ cur_range= NULL;
/*
Find the range tree for the current keypart. We assume that
- index_range_tree points to the leftmost keypart in the index.
+ index_range_tree points to the first keypart in the index.
*/
- for (cur_range= index_range_tree;
- cur_range && cur_range->type == SEL_ARG::KEY_RANGE;
- cur_range= cur_range->next_key_part)
- {
- if (cur_range->field->eq(cur_part->field))
- break;
- }
+ if(get_sel_arg_for_keypart(cur_part->field, index_range_tree, &cur_range))
+ return false;
+
if (!cur_range || cur_range->type != SEL_ARG::KEY_RANGE)
{
if (min_max_arg_part)
@@ -12979,9 +13061,6 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
}
}
- /* Check that the current range tree is a single point interval. */
- if (cur_range->prev || cur_range->next)
- return FALSE; /* This is not the only range predicate for the field. */
if ((cur_range->min_flag & NO_MIN_RANGE) ||
(cur_range->max_flag & NO_MAX_RANGE) ||
(cur_range->min_flag & NEAR_MIN) || (cur_range->max_flag & NEAR_MAX))
diff --git a/sql/partition_element.h b/sql/partition_element.h
index 87f3d00e68c..4f03d91035a 100644
--- a/sql/partition_element.h
+++ b/sql/partition_element.h
@@ -1,7 +1,7 @@
#ifndef PARTITION_ELEMENT_INCLUDED
#define PARTITION_ELEMENT_INCLUDED
-/* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -107,9 +107,8 @@ public:
enum partition_state part_state;
uint16 nodegroup_id;
bool has_null_value;
- /* signed_flag and max_value only relevant for subpartitions */
- bool signed_flag;
- bool max_value;
+ bool signed_flag; // Range value signed
+ bool max_value; // MAXVALUE range
partition_element()
: part_max_rows(0), part_min_rows(0), range_value(0),
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index 934f4e970cb..34e47331664 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -2173,9 +2172,36 @@ int partition_info::fix_parser_data(THD *thd)
if (!(part_type == RANGE_PARTITION ||
part_type == LIST_PARTITION))
{
- /* Nothing to do for HASH/KEY partitioning */
+ if (part_type == HASH_PARTITION && list_of_part_fields)
+ {
+ /* KEY partitioning, check ALGORITHM = N. Should not pass the parser! */
+ if (key_algorithm > KEY_ALGORITHM_55)
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(true);
+ }
+ /* If not set, use DEFAULT = 2 for CREATE and ALTER! */
+ if ((thd_sql_command(thd) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(thd) == SQLCOM_ALTER_TABLE) &&
+ key_algorithm == KEY_ALGORITHM_NONE)
+ key_algorithm= KEY_ALGORITHM_55;
+ }
DBUG_RETURN(FALSE);
}
+ if (is_sub_partitioned() && list_of_subpart_fields)
+ {
+ /* KEY subpartitioning, check ALGORITHM = N. Should not pass the parser! */
+ if (key_algorithm > KEY_ALGORITHM_55)
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(true);
+ }
+ /* If not set, use DEFAULT = 2 for CREATE and ALTER! */
+ if ((thd_sql_command(thd) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(thd) == SQLCOM_ALTER_TABLE) &&
+ key_algorithm == KEY_ALGORITHM_NONE)
+ key_algorithm= KEY_ALGORITHM_55;
+ }
do
{
part_elem= it++;
@@ -2224,6 +2250,263 @@ int partition_info::fix_parser_data(THD *thd)
DBUG_RETURN(FALSE);
}
+
+/**
+ helper function to compare strings that can also be
+ a NULL pointer.
+
+ @param a char pointer (can be NULL).
+ @param b char pointer (can be NULL).
+
+ @return false if equal
+ @retval true strings differs
+ @retval false strings is equal
+*/
+
+static bool strcmp_null(const char *a, const char *b)
+{
+ if (!a && !b)
+ return false;
+ if (a && b && !strcmp(a, b))
+ return false;
+ return true;
+}
+
+
+/**
+ Check if the new part_info has the same partitioning.
+
+ @param new_part_info New partition definition to compare with.
+
+ @return True if not considered to have changed the partitioning.
+ @retval true Allowed change (only .frm change, compatible distribution).
+ @retval false Different partitioning, will need redistribution of rows.
+
+ @note Currently only used to allow changing from non-set key_algorithm
+ to a specified key_algorithm, to avoid rebuild when upgrading from 5.1 of
+ such partitioned tables using numeric colums in the partitioning expression.
+ For more info see bug#14521864.
+ Does not check if columns etc has changed, i.e. only for
+ alter_info->flags == ALTER_PARTITION.
+*/
+
+bool partition_info::has_same_partitioning(partition_info *new_part_info)
+{
+ DBUG_ENTER("partition_info::has_same_partitioning");
+
+ DBUG_ASSERT(part_field_array && part_field_array[0]);
+
+ /*
+ Only consider pre 5.5.3 .frm's to have same partitioning as
+ a new one with KEY ALGORITHM = 1 ().
+ */
+
+ if (part_field_array[0]->table->s->mysql_version >= 50503)
+ DBUG_RETURN(false);
+
+ if (!new_part_info ||
+ part_type != new_part_info->part_type ||
+ num_parts != new_part_info->num_parts ||
+ use_default_partitions != new_part_info->use_default_partitions ||
+ new_part_info->is_sub_partitioned() != is_sub_partitioned())
+ DBUG_RETURN(false);
+
+ if (part_type != HASH_PARTITION)
+ {
+ /*
+ RANGE or LIST partitioning, check if KEY subpartitioned.
+ Also COLUMNS partitioning was added in 5.5, so treat that as different.
+ */
+ if (!is_sub_partitioned() ||
+ !new_part_info->is_sub_partitioned() ||
+ column_list ||
+ new_part_info->column_list ||
+ !list_of_subpart_fields ||
+ !new_part_info->list_of_subpart_fields ||
+ new_part_info->num_subparts != num_subparts ||
+ new_part_info->subpart_field_list.elements !=
+ subpart_field_list.elements ||
+ new_part_info->use_default_subpartitions !=
+ use_default_subpartitions)
+ DBUG_RETURN(false);
+ }
+ else
+ {
+ /* Check if KEY partitioned. */
+ if (!new_part_info->list_of_part_fields ||
+ !list_of_part_fields ||
+ new_part_info->part_field_list.elements != part_field_list.elements)
+ DBUG_RETURN(false);
+ }
+
+ /* Check that it will use the same fields in KEY (fields) list. */
+ List_iterator<char> old_field_name_it(part_field_list);
+ List_iterator<char> new_field_name_it(new_part_info->part_field_list);
+ char *old_name, *new_name;
+ while ((old_name= old_field_name_it++))
+ {
+ new_name= new_field_name_it++;
+ if (!new_name || my_strcasecmp(system_charset_info,
+ new_name,
+ old_name))
+ DBUG_RETURN(false);
+ }
+
+ if (is_sub_partitioned())
+ {
+ /* Check that it will use the same fields in KEY subpart fields list. */
+ List_iterator<char> old_field_name_it(subpart_field_list);
+ List_iterator<char> new_field_name_it(new_part_info->subpart_field_list);
+ char *old_name, *new_name;
+ while ((old_name= old_field_name_it++))
+ {
+ new_name= new_field_name_it++;
+ if (!new_name || my_strcasecmp(system_charset_info,
+ new_name,
+ old_name))
+ DBUG_RETURN(false);
+ }
+ }
+
+ if (!use_default_partitions)
+ {
+ /*
+ Loop over partitions/subpartition to verify that they are
+ the same, including state and name.
+ */
+ List_iterator<partition_element> part_it(partitions);
+ List_iterator<partition_element> new_part_it(new_part_info->partitions);
+ uint i= 0;
+ do
+ {
+ partition_element *part_elem= part_it++;
+ partition_element *new_part_elem= new_part_it++;
+ /*
+ The following must match:
+ partition_name, tablespace_name, data_file_name, index_file_name,
+ engine_type, part_max_rows, part_min_rows, nodegroup_id.
+ (max_value, signed_flag, has_null_value only on partition level,
+ RANGE/LIST)
+ The following can differ:
+ - part_comment
+ part_state must be PART_NORMAL!
+ */
+ if (!part_elem || !new_part_elem ||
+ strcmp(part_elem->partition_name,
+ new_part_elem->partition_name) ||
+ part_elem->part_state != PART_NORMAL ||
+ new_part_elem->part_state != PART_NORMAL ||
+ part_elem->max_value != new_part_elem->max_value ||
+ part_elem->signed_flag != new_part_elem->signed_flag ||
+ part_elem->has_null_value != new_part_elem->has_null_value)
+ DBUG_RETURN(false);
+
+ /* new_part_elem may not have engine_type set! */
+ if (new_part_elem->engine_type &&
+ part_elem->engine_type != new_part_elem->engine_type)
+ DBUG_RETURN(false);
+
+ if (is_sub_partitioned())
+ {
+ /*
+ Check that both old and new partition has the same definition
+ (VALUES IN/VALUES LESS THAN) (No COLUMNS partitioning, see above)
+ */
+ if (part_type == LIST_PARTITION)
+ {
+ List_iterator<part_elem_value> list_vals(part_elem->list_val_list);
+ List_iterator<part_elem_value>
+ new_list_vals(new_part_elem->list_val_list);
+ part_elem_value *val;
+ part_elem_value *new_val;
+ while ((val= list_vals++))
+ {
+ new_val= new_list_vals++;
+ if (!new_val)
+ DBUG_RETURN(false);
+ if ((!val->null_value && !new_val->null_value) &&
+ val->value != new_val->value)
+ DBUG_RETURN(false);
+ }
+ if (new_list_vals++)
+ DBUG_RETURN(false);
+ }
+ else
+ {
+ DBUG_ASSERT(part_type == RANGE_PARTITION);
+ if (new_part_elem->range_value != part_elem->range_value)
+ DBUG_RETURN(false);
+ }
+
+ if (!use_default_subpartitions)
+ {
+ List_iterator<partition_element>
+ sub_part_it(part_elem->subpartitions);
+ List_iterator<partition_element>
+ new_sub_part_it(new_part_elem->subpartitions);
+ uint j= 0;
+ do
+ {
+ partition_element *sub_part_elem= sub_part_it++;
+ partition_element *new_sub_part_elem= new_sub_part_it++;
+ /* new_part_elem may not have engine_type set! */
+ if (new_sub_part_elem->engine_type &&
+ sub_part_elem->engine_type != new_part_elem->engine_type)
+ DBUG_RETURN(false);
+
+ if (strcmp(sub_part_elem->partition_name,
+ new_sub_part_elem->partition_name) ||
+ sub_part_elem->part_state != PART_NORMAL ||
+ new_sub_part_elem->part_state != PART_NORMAL ||
+ sub_part_elem->part_min_rows !=
+ new_sub_part_elem->part_min_rows ||
+ sub_part_elem->part_max_rows !=
+ new_sub_part_elem->part_max_rows ||
+ sub_part_elem->nodegroup_id !=
+ new_sub_part_elem->nodegroup_id)
+ DBUG_RETURN(false);
+
+ if (strcmp_null(sub_part_elem->data_file_name,
+ new_sub_part_elem->data_file_name) ||
+ strcmp_null(sub_part_elem->index_file_name,
+ new_sub_part_elem->index_file_name) ||
+ strcmp_null(sub_part_elem->tablespace_name,
+ new_sub_part_elem->tablespace_name))
+ DBUG_RETURN(false);
+
+ } while (++j < num_subparts);
+ }
+ }
+ else
+ {
+ if (part_elem->part_min_rows != new_part_elem->part_min_rows ||
+ part_elem->part_max_rows != new_part_elem->part_max_rows ||
+ part_elem->nodegroup_id != new_part_elem->nodegroup_id)
+ DBUG_RETURN(false);
+
+ if (strcmp_null(part_elem->data_file_name,
+ new_part_elem->data_file_name) ||
+ strcmp_null(part_elem->index_file_name,
+ new_part_elem->index_file_name) ||
+ strcmp_null(part_elem->tablespace_name,
+ new_part_elem->tablespace_name))
+ DBUG_RETURN(false);
+ }
+ } while (++i < num_parts);
+ }
+
+ /*
+ Only if key_algorithm was not specified before and it is now set,
+ consider this as nothing was changed, and allow change without rebuild!
+ */
+ if (key_algorithm != partition_info::KEY_ALGORITHM_NONE ||
+ new_part_info->key_algorithm == partition_info::KEY_ALGORITHM_NONE)
+ DBUG_RETURN(false);
+
+ DBUG_RETURN(true);
+}
+
+
void partition_info::print_debug(const char *str, uint *value)
{
DBUG_ENTER("print_debug");
diff --git a/sql/partition_info.h b/sql/partition_info.h
index 2fbdfff6636..17c9cb383ee 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -1,7 +1,7 @@
#ifndef PARTITION_INFO_INCLUDED
#define PARTITION_INFO_INCLUDED
-/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -192,6 +192,19 @@ public:
but mainly of use to handlers supporting partitioning.
*/
uint16 linear_hash_mask;
+ /*
+ PARTITION BY KEY ALGORITHM=N
+ Which algorithm to use for hashing the fields.
+ N = 1 - Use 5.1 hashing (numeric fields are hashed as binary)
+ N = 2 - Use 5.5 hashing (numeric fields are hashed like latin1 bytes)
+ */
+ enum enum_key_algorithm
+ {
+ KEY_ALGORITHM_NONE= 0,
+ KEY_ALGORITHM_51= 1,
+ KEY_ALGORITHM_55= 2
+ };
+ enum_key_algorithm key_algorithm;
bool use_default_partitions;
bool use_default_num_partitions;
@@ -232,6 +245,7 @@ public:
count_curr_subparts(0), part_error_code(0),
num_list_values(0), num_part_fields(0), num_subpart_fields(0),
num_full_part_fields(0), has_null_part_id(0), linear_hash_mask(0),
+ key_algorithm(KEY_ALGORITHM_NONE),
use_default_partitions(TRUE), use_default_num_partitions(TRUE),
use_default_subpartitions(TRUE), use_default_num_subpartitions(TRUE),
default_partitions_setup(FALSE), defined_max_value(FALSE),
@@ -298,6 +312,7 @@ public:
bool add_column_list_value(THD *thd, Item *item);
void set_show_version_string(String *packet);
void report_part_expr_error(bool use_subpart_expr);
+ bool has_same_partitioning(partition_info *new_part_info);
private:
static int list_part_cmp(const void* a, const void* b);
bool set_up_default_partitions(handler *file, HA_CREATE_INFO *info,
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index b34b890060b..d5e9380296e 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -180,6 +180,22 @@ rpl_slave_state::get_element(uint32 domain_id)
int
+rpl_slave_state::put_back_list(uint32 domain_id, list_element *list)
+{
+ element *e;
+ if (!(e= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0)))
+ return 1;
+ while (list)
+ {
+ list_element *next= list->next;
+ e->add(list);
+ list= next;
+ }
+ return 0;
+}
+
+
+int
rpl_slave_state::truncate_state_table(THD *thd)
{
TABLE_LIST tlist;
@@ -330,7 +346,10 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
table->field[3]->store(gtid->seq_no, true);
DBUG_EXECUTE_IF("inject_crash_before_write_rpl_slave_state", DBUG_SUICIDE(););
if ((err= table->file->ha_write_row(table->record[0])))
- goto end;
+ {
+ table->file->print_error(err, MYF(0));
+ goto end;
+ }
lock();
if ((elem= get_element(gtid->domain_id)) == NULL)
@@ -351,23 +370,42 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
bitmap_set_bit(table->read_set, table->field[1]->field_index);
if ((err= table->file->ha_index_init(0, 0)))
+ {
+ table->file->print_error(err, MYF(0));
goto end;
+ }
while (elist)
{
uchar key_buffer[4+8];
+ DBUG_EXECUTE_IF("gtid_slave_pos_simulate_failed_delete",
+ { err= ENOENT;
+ table->file->print_error(err, MYF(0));
+ /* `break' does not work in DBUG_EXECUTE_IF */
+ goto dbug_break; });
+
next= elist->next;
table->field[1]->store(elist->sub_id, true);
/* domain_id is already set in table->record[0] from write_row() above. */
key_copy(key_buffer, table->record[0], &table->key_info[0], 0, false);
- if ((err= table->file->ha_index_read_map(table->record[1], key_buffer,
- HA_WHOLE_KEY, HA_READ_KEY_EXACT)) ||
- (err= table->file->ha_delete_row(table->record[1])))
- break;
+ if (table->file->ha_index_read_map(table->record[1], key_buffer,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
+ /* We cannot find the row, assume it is already deleted. */
+ ;
+ else if ((err= table->file->ha_delete_row(table->record[1])))
+ table->file->print_error(err, MYF(0));
+ /*
+ In case of error, we still discard the element from the list. We do
+ not want to endlessly error on the same element in case of table
+ corruption or such.
+ */
my_free(elist);
elist= next;
+ if (err)
+ break;
}
+IF_DBUG(dbug_break:, )
table->file->ha_index_end();
if(!err && opt_bin_log &&
@@ -382,9 +420,16 @@ end:
if (err)
{
/*
- ToDo: If error, we need to put any remaining elist back into the HASH so
- we can do another delete attempt later.
+ If error, we need to put any remaining elist back into the HASH so we
+ can do another delete attempt later.
*/
+ if (elist)
+ {
+ lock();
+ put_back_list(gtid->domain_id, elist);
+ unlock();
+ }
+
ha_rollback_trans(thd, FALSE);
close_thread_tables(thd);
}
@@ -437,26 +482,10 @@ rpl_slave_state_tostring_helper(String *dest, const rpl_gtid *gtid, bool *first)
}
-/*
- Prepare the current slave state as a string, suitable for sending to the
- master to request to receive binlog events starting from that GTID state.
-
- The state consists of the most recently applied GTID for each domain_id,
- ie. the one with the highest sub_id within each domain_id.
-
- Optinally, extra_gtids is a list of GTIDs from the binlog. This is used when
- a server was previously a master and now needs to connect to a new master as
- a slave. For each domain_id, if the GTID in the binlog was logged with our
- own server_id _and_ has a higher seq_no than what is in the slave state,
- then this should be used as the position to start replicating at. This
- allows to promote a slave as new master, and connect the old master as a
- slave with MASTER_GTID_POS=AUTO.
-*/
-
int
-rpl_slave_state::tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra)
+rpl_slave_state::iterate(int (*cb)(rpl_gtid *, void *), void *data,
+ rpl_gtid *extra_gtids, uint32 num_extra)
{
- bool first= true;
uint32 i;
HASH gtid_hash;
uchar *rec;
@@ -510,7 +539,7 @@ rpl_slave_state::tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra)
}
}
- if (rpl_slave_state_tostring_helper(dest, &best_gtid, &first))
+ if ((res= (*cb)(&best_gtid, data)))
{
unlock();
goto err;
@@ -523,7 +552,7 @@ rpl_slave_state::tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra)
for (i= 0; i < gtid_hash.records; ++i)
{
gtid= (rpl_gtid *)my_hash_element(&gtid_hash, i);
- if (rpl_slave_state_tostring_helper(dest, gtid, &first))
+ if ((res= (*cb)(gtid, data)))
goto err;
}
@@ -536,6 +565,44 @@ err:
}
+struct rpl_slave_state_tostring_data {
+ String *dest;
+ bool first;
+};
+static int
+rpl_slave_state_tostring_cb(rpl_gtid *gtid, void *data)
+{
+ rpl_slave_state_tostring_data *p= (rpl_slave_state_tostring_data *)data;
+ return rpl_slave_state_tostring_helper(p->dest, gtid, &p->first);
+}
+
+
+/*
+ Prepare the current slave state as a string, suitable for sending to the
+ master to request to receive binlog events starting from that GTID state.
+
+ The state consists of the most recently applied GTID for each domain_id,
+ ie. the one with the highest sub_id within each domain_id.
+
+ Optinally, extra_gtids is a list of GTIDs from the binlog. This is used when
+ a server was previously a master and now needs to connect to a new master as
+ a slave. For each domain_id, if the GTID in the binlog was logged with our
+ own server_id _and_ has a higher seq_no than what is in the slave state,
+ then this should be used as the position to start replicating at. This
+ allows to promote a slave as new master, and connect the old master as a
+ slave with MASTER_GTID_POS=AUTO.
+*/
+int
+rpl_slave_state::tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra)
+{
+ struct rpl_slave_state_tostring_data data;
+ data.first= true;
+ data.dest= dest;
+
+ return iterate(rpl_slave_state_tostring_cb, &data, extra_gtids, num_extra);
+}
+
+
/*
Lookup a domain_id in the current replication slave state.
@@ -581,9 +648,6 @@ rpl_slave_state::domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid)
Parse a GTID at the start of a string, and update the pointer to point
at the first character after the parsed GTID.
- GTID can be in short form with domain_id=0 implied, SERVERID-SEQNO.
- Or long form, DOMAINID-SERVERID-SEQNO.
-
Returns 0 on ok, non-zero on parse error.
*/
static int
@@ -1172,7 +1236,7 @@ slave_connection_state::load(char *slave_request, size_t len)
rpl_gtid *gtid;
const rpl_gtid *gtid2;
- my_hash_reset(&hash);
+ reset();
p= slave_request;
end= slave_request + len;
if (p == end)
@@ -1225,7 +1289,7 @@ slave_connection_state::load(const rpl_gtid *gtid_list, uint32 count)
{
uint32 i;
- my_hash_reset(&hash);
+ reset();
for (i= 0; i < count; ++i)
if (update(&gtid_list[i]))
return 1;
@@ -1233,6 +1297,28 @@ slave_connection_state::load(const rpl_gtid *gtid_list, uint32 count)
}
+static int
+slave_connection_state_load_cb(rpl_gtid *gtid, void *data)
+{
+ slave_connection_state *state= (slave_connection_state *)data;
+ return state->update(gtid);
+}
+
+
+/*
+ Same as rpl_slave_state::tostring(), but populates a slave_connection_state
+ instead.
+*/
+int
+slave_connection_state::load(rpl_slave_state *state,
+ rpl_gtid *extra_gtids, uint32 num_extra)
+{
+ reset();
+ return state->iterate(slave_connection_state_load_cb, this,
+ extra_gtids, num_extra);
+}
+
+
rpl_gtid *
slave_connection_state::find(uint32 domain_id)
{
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index fefce684c2c..4d5302020bf 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -93,6 +93,8 @@ struct rpl_slave_state
int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
bool in_transaction, bool in_statement);
uint64 next_subid(uint32 domain_id);
+ int iterate(int (*cb)(rpl_gtid *, void *), void *data,
+ rpl_gtid *extra_gtids, uint32 num_extra);
int tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra);
bool domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid);
int load(THD *thd, char *state_from_master, size_t len, bool reset,
@@ -103,6 +105,7 @@ struct rpl_slave_state
void unlock() { DBUG_ASSERT(inited); mysql_mutex_unlock(&LOCK_slave_state); }
element *get_element(uint32 domain_id);
+ int put_back_list(uint32 domain_id, list_element *list);
void update_state_hash(uint64 sub_id, rpl_gtid *gtid);
int record_and_update_gtid(THD *thd, Relay_log_info *rli);
@@ -177,8 +180,10 @@ struct slave_connection_state
slave_connection_state();
~slave_connection_state();
+ void reset() { my_hash_reset(&hash); }
int load(char *slave_request, size_t len);
int load(const rpl_gtid *gtid_list, uint32 count);
+ int load(rpl_slave_state *state, rpl_gtid *extra_gtids, uint32 num_extra);
rpl_gtid *find(uint32 domain_id);
int update(const rpl_gtid *in_gtid);
void remove(const rpl_gtid *gtid);
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index a68fd055d74..12bdf722bec 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -38,7 +38,8 @@ Master_info::Master_info(LEX_STRING *connection_name_arg,
connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0),
slave_running(0), slave_run_id(0), sync_counter(0),
heartbeat_period(0), received_heartbeats(0), master_id(0),
- using_gtid(USE_GTID_NO)
+ using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0),
+ gtid_reconnect_event_skip_count(0), gtid_event_seen(false)
{
host[0] = 0; user[0] = 0; password[0] = 0;
ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
@@ -148,6 +149,23 @@ void Master_info::clear_in_memory_info(bool all)
}
}
+
+const char *
+Master_info::using_gtid_astext(enum enum_using_gtid arg)
+{
+ switch (arg)
+ {
+ case USE_GTID_NO:
+ return "No";
+ case USE_GTID_SLAVE_POS:
+ return "Slave_Pos";
+ default:
+ DBUG_ASSERT(arg == USE_GTID_CURRENT_POS);
+ return "Current_Pos";
+ }
+}
+
+
void init_master_log_pos(Master_info* mi)
{
DBUG_ENTER("init_master_log_pos");
@@ -155,6 +173,10 @@ void init_master_log_pos(Master_info* mi)
mi->master_log_name[0] = 0;
mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number
mi->using_gtid= Master_info::USE_GTID_NO;
+ mi->gtid_current_pos.reset();
+ mi->events_queued_since_last_gtid= 0;
+ mi->gtid_reconnect_event_skip_count= 0;
+ mi->gtid_event_seen= false;
/* Intentionally init ssl_verify_server_cert to 0, no option available */
mi->ssl_verify_server_cert= 0;
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 7e3709993ed..ad6c57e21c4 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -61,6 +61,10 @@ typedef struct st_mysql MYSQL;
class Master_info : public Slave_reporting_capability
{
public:
+ enum enum_using_gtid {
+ USE_GTID_NO= 0, USE_GTID_CURRENT_POS= 1, USE_GTID_SLAVE_POS= 2
+ };
+
Master_info(LEX_STRING *connection_name, bool is_slave_recovery);
~Master_info();
bool shall_ignore_server_id(ulong s_id);
@@ -70,11 +74,12 @@ class Master_info : public Slave_reporting_capability
/* If malloc() in initialization failed */
return connection_name.str == 0;
}
+ static const char *using_gtid_astext(enum enum_using_gtid arg);
/* the variables below are needed because we can change masters on the fly */
char master_log_name[FN_REFLEN+6]; /* Room for multi-*/
char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
- char user[USERNAME_LENGTH*+1];
+ char user[USERNAME_LENGTH+1];
char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
LEX_STRING connection_name; /* User supplied connection name */
LEX_STRING cmp_connection_name; /* Connection name in lower case */
@@ -136,9 +141,35 @@ class Master_info : public Slave_reporting_capability
Note that you can not change the numeric values of these, they are used
in master.info.
*/
- enum {
- USE_GTID_NO= 0, USE_GTID_CURRENT_POS= 1, USE_GTID_SLAVE_POS= 2
- } using_gtid;
+ enum enum_using_gtid using_gtid;
+
+ /*
+ This GTID position records how far we have fetched into the relay logs.
+ This is used to continue fetching when the IO thread reconnects to the
+ master.
+
+ (Full slave stop/start does not use it, as it resets the relay logs).
+ */
+ slave_connection_state gtid_current_pos;
+ /*
+ If events_queued_since_last_gtid is non-zero, it is the number of events
+ queued so far in the relaylog of a GTID-prefixed event group.
+ It is zero when no partial event group has been queued at the moment.
+ */
+ uint64 events_queued_since_last_gtid;
+ /*
+ The GTID of the partially-queued event group, when
+ events_queued_since_last_gtid is non-zero.
+ */
+ rpl_gtid last_queued_gtid;
+ /*
+ When slave IO thread needs to reconnect, gtid_reconnect_event_skip_count
+ counts number of events to skip from the first GTID-prefixed event group,
+ to avoid duplicating events in the relay log.
+ */
+ uint64 gtid_reconnect_event_skip_count;
+ /* gtid_event_seen is false until we receive first GTID event from master. */
+ bool gtid_event_seen;
};
int init_master_info(Master_info* mi, const char* master_info_fname,
const char* slave_info_fname,
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 543c6d9fffe..7c8396ceaae 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -327,8 +327,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
msg="Error reading slave log configuration";
goto err;
}
- strmake(rli->event_relay_log_name,rli->group_relay_log_name,
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->event_relay_log_name,rli->group_relay_log_name);
rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos;
rli->group_master_log_pos= master_log_pos;
@@ -546,10 +545,8 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,
*errmsg="Could not find target log during relay log initialization";
goto err;
}
- strmake(rli->group_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->group_relay_log_name)-1);
- strmake(rli->event_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->group_relay_log_name,rli->linfo.log_file_name);
+ strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name);
if (rli->relay_log.is_active(rli->linfo.log_file_name))
{
/*
@@ -889,8 +886,7 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
mysql_mutex_lock(&data_lock);
inc_event_relay_log_pos();
group_relay_log_pos= event_relay_log_pos;
- strmake(group_relay_log_name,event_relay_log_name,
- sizeof(group_relay_log_name)-1);
+ strmake_buf(group_relay_log_name,event_relay_log_name);
notify_group_relay_log_name_update();
@@ -1025,10 +1021,8 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
if (!just_reset)
{
/* Save name of used relay log file */
- strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(),
- sizeof(rli->group_relay_log_name)-1);
- strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(),
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->group_relay_log_name, rli->relay_log.get_log_fname());
+ strmake_buf(rli->event_relay_log_name, rli->relay_log.get_log_fname());
rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
rli->log_space_total= 0;
@@ -1426,7 +1420,10 @@ rpl_load_gtid_slave_state(THD *thd)
bitmap_set_all(table->read_set);
if ((err= table->file->ha_rnd_init_with_error(1)))
+ {
+ table->file->print_error(err, MYF(0));
goto end;
+ }
table_scanned= true;
for (;;)
{
@@ -1441,7 +1438,10 @@ rpl_load_gtid_slave_state(THD *thd)
else if (err == HA_ERR_END_OF_FILE)
break;
else
+ {
+ table->file->print_error(err, MYF(0));
goto end;
+ }
}
domain_id= (ulonglong)table->field[0]->val_int();
sub_id= (ulonglong)table->field[1]->val_int();
@@ -1466,6 +1466,7 @@ rpl_load_gtid_slave_state(THD *thd)
if (!(entry= (struct local_element *)my_malloc(sizeof(*entry),
MYF(MY_WME))))
{
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*entry));
err= 1;
goto end;
}
@@ -1476,12 +1477,18 @@ rpl_load_gtid_slave_state(THD *thd)
if ((err= my_hash_insert(&hash, (uchar *)entry)))
{
my_free(entry);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto end;
}
}
}
rpl_global_gtid_slave_state.lock();
+ if (rpl_global_gtid_slave_state.loaded)
+ {
+ rpl_global_gtid_slave_state.unlock();
+ goto end;
+ }
for (i= 0; i < hash.records; ++i)
{
entry= (struct local_element *)my_hash_element(&hash, i);
@@ -1491,14 +1498,15 @@ rpl_load_gtid_slave_state(THD *thd)
entry->gtid.seq_no)))
{
rpl_global_gtid_slave_state.unlock();
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto end;
}
if (opt_bin_log &&
mysql_bin_log.bump_seq_no_counter_if_needed(entry->gtid.domain_id,
entry->gtid.seq_no))
{
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
rpl_global_gtid_slave_state.unlock();
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto end;
}
}
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index 1be97b34204..6bbe998a624 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -1173,6 +1173,7 @@ void Deferred_log_events::rewind()
Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i);
delete ev;
}
+ last_added= NULL;
if (array.elements > array.max_element)
freeze_size(&array);
reset_dynamic(&array);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 363f03579ef..b2d4e464256 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2002, 2011, Oracle and/or its affiliates.
- Copyright (c) 2008, 2012, Monty Program Ab
+/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -251,16 +251,14 @@ uchar *sys_var::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
return session_value_ptr(thd, base);
}
-bool sys_var::set_default(THD *thd, enum_var_type type)
+bool sys_var::set_default(THD *thd, set_var* var)
{
- set_var var(type, this, &null_lex_str, 0);
-
- if (type == OPT_GLOBAL || scope() == GLOBAL)
- global_save_default(thd, &var);
+ if (var->type == OPT_GLOBAL || scope() == GLOBAL)
+ global_save_default(thd, var);
else
- session_save_default(thd, &var);
+ session_save_default(thd, var);
- return check(thd, &var) || update(thd, &var);
+ return check(thd, var) || update(thd, var);
}
@@ -771,7 +769,7 @@ int set_var::light_check(THD *thd)
*/
int set_var::update(THD *thd)
{
- return value ? var->update(thd, this) : var->set_default(thd, type);
+ return value ? var->update(thd, this) : var->set_default(thd, this);
}
diff --git a/sql/set_var.h b/sql/set_var.h
index 6cb0cd33f87..87a2988bc3d 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -1,6 +1,6 @@
#ifndef SET_VAR_INCLUDED
#define SET_VAR_INCLUDED
-/* Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -105,7 +105,13 @@ public:
bool check(THD *thd, set_var *var);
uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool set_default(THD *thd, enum_var_type type);
+
+ /**
+ Update the system variable with the default value from either
+ session or global scope. The default value is stored in the
+ 'var' argument. Return false when successful.
+ */
+ bool set_default(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
longlong val_int(bool *is_null, THD *thd, enum_var_type type, LEX_STRING *base);
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index ad055273155..5c60de14742 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -6760,7 +6760,7 @@ ER_GTID_OPEN_TABLE_FAILED
ER_GTID_POSITION_NOT_FOUND_IN_BINLOG
eng "Connecting slave requested to start from GTID %u-%u-%llu, which is not in the master's binlog"
ER_CANNOT_LOAD_SLAVE_GTID_STATE
- eng "Failed to load replication slave GTID state from table %s.%s"
+ eng "Failed to load replication slave GTID position from table %s.%s"
ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG
eng "Specified GTID %u-%u-%llu conflicts with the binary log which contains a more recent GTID %u-%u-%llu. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos."
ER_MASTER_GTID_POS_MISSING_DOMAIN
@@ -6771,3 +6771,5 @@ ER_GTID_STRICT_OUT_OF_ORDER
eng "An attempt was made to binlog GTID %u-%u-%llu which would create an out-of-order sequence number with existing GTID %u-%u-%llu, and gtid strict mode is enabled."
ER_GTID_START_FROM_BINLOG_HOLE
eng "The binlog on the master is missing the GTID %u-%u-%llu requested by the slave (even though both a prior and a subsequent sequence number does exist), and GTID strict mode is enabled"
+ER_SLAVE_UNEXPECTED_MASTER_SWITCH
+ eng "Unexpected GTID received from master after reconnect. This normally indicates that the master server was replaced without restarting the slave threads. %s"
diff --git a/sql/slave.cc b/sql/slave.cc
index c7f4dc08096..46a7ddb28f3 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -114,7 +114,7 @@ static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]=
registration on master",
"Reconnecting after a failed registration on master",
"failed registering on master, reconnecting to try again, \
-log '%s' at position %s",
+log '%s' at position %s%s",
"COM_REGISTER_SLAVE",
"Slave I/O thread killed during or after reconnect"
},
@@ -122,7 +122,7 @@ log '%s' at position %s",
"Waiting to reconnect after a failed binlog dump request",
"Slave I/O thread killed while retrying master dump",
"Reconnecting after a failed binlog dump request",
- "failed dump request, reconnecting to try again, log '%s' at position %s",
+ "failed dump request, reconnecting to try again, log '%s' at position %s%s",
"COM_BINLOG_DUMP",
"Slave I/O thread killed during or after reconnect"
},
@@ -131,7 +131,7 @@ log '%s' at position %s",
"Slave I/O thread killed while waiting to reconnect after a failed read",
"Reconnecting after a failed master event read",
"Slave I/O thread: Failed reading log event, reconnecting to retry, \
-log '%s' at position %s",
+log '%s' at position %s%s",
"",
"Slave I/O thread killed during or after a reconnect done to recover from \
failed read"
@@ -253,6 +253,66 @@ static void init_slave_psi_keys(void)
}
#endif /* HAVE_PSI_INTERFACE */
+
+static bool slave_init_thread_running;
+
+
+pthread_handler_t
+handle_slave_init(void *arg __attribute__((unused)))
+{
+ THD *thd;
+
+ my_thread_init();
+ thd= new THD;
+ thd->thread_stack= (char*) &thd; /* Set approximate stack start */
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->thread_id= thread_id++;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ thd->store_globals();
+
+ thd_proc_info(thd, "Loading slave GTID position from table");
+ if (rpl_load_gtid_slave_state(thd))
+ sql_print_warning("Failed to load slave replication state from table "
+ "%s.%s: %u: %s", "mysql",
+ rpl_gtid_slave_state_table_name.str,
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ delete thd;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ my_thread_end();
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ slave_init_thread_running= false;
+ mysql_cond_signal(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+
+ return 0;
+}
+
+
+static int
+run_slave_init_thread()
+{
+ pthread_t th;
+
+ slave_init_thread_running= true;
+ if (mysql_thread_create(key_thread_slave_init, &th, NULL,
+ handle_slave_init, NULL))
+ {
+ sql_print_error("Failed to create thread while initialising slave");
+ return 1;
+ }
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ while (slave_init_thread_running)
+ mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+
+ return 0;
+}
+
+
/* Initialize slave structures */
int init_slave()
@@ -264,6 +324,9 @@ int init_slave()
init_slave_psi_keys();
#endif
+ if (run_slave_init_thread())
+ return 1;
+
/*
This is called when mysqld starts. Before client connections are
accepted. However bootstrap may conflict with us if it does START SLAVE.
@@ -381,16 +444,13 @@ int init_recovery(Master_info* mi, const char** errmsg)
{
mi->master_log_pos= max(BIN_LOG_HEADER_SIZE,
rli->group_master_log_pos);
- strmake(mi->master_log_name, rli->group_master_log_name,
- sizeof(mi->master_log_name)-1);
+ strmake_buf(mi->master_log_name, rli->group_master_log_name);
sql_print_warning("Recovery from master pos %ld and file %s.",
(ulong) mi->master_log_pos, mi->master_log_name);
- strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(),
- sizeof(rli->group_relay_log_name)-1);
- strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(),
- sizeof(mi->rli.event_relay_log_name)-1);
+ strmake_buf(rli->group_relay_log_name, rli->relay_log.get_log_fname());
+ strmake_buf(rli->event_relay_log_name, rli->relay_log.get_log_fname());
rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
}
@@ -830,9 +890,13 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
purge_relay_logs(&mi->rli, NULL, 0, &errmsg);
mi->master_log_name[0]= 0;
mi->master_log_pos= 0;
+ error= rpl_load_gtid_state(&mi->gtid_current_pos, mi->using_gtid ==
+ Master_info::USE_GTID_CURRENT_POS);
+ mi->events_queued_since_last_gtid= 0;
+ mi->gtid_reconnect_event_skip_count= 0;
}
- if (thread_mask & SLAVE_IO)
+ if (!error && (thread_mask & SLAVE_IO))
error= start_slave_thread(
#ifdef HAVE_PSI_INTERFACE
key_thread_slave_io,
@@ -1824,18 +1888,9 @@ past_checksum:
after_set_capability:
#endif
- /*
- Request dump start from slave replication GTID state.
-
- Only request GTID position the first time we connect after CHANGE MASTER
- or after starting both IO or SQL thread.
-
- Otherwise, if the IO thread was ahead of the SQL thread before the
- restart or reconnect, we might end up re-fetching and hence re-applying
- the same event(s) again.
- */
- if (mi->using_gtid != Master_info::USE_GTID_NO && !mi->master_log_name[0])
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
{
+ /* Request dump to start from slave replication GTID state. */
int rc;
char str_buf[256];
String query_str(str_buf, sizeof(str_buf), system_charset_info);
@@ -1864,9 +1919,7 @@ after_set_capability:
query_str.append(STRING_WITH_LEN("SET @slave_connect_state='"),
system_charset_info);
- if (rpl_append_gtid_state(&query_str,
- mi->using_gtid ==
- Master_info::USE_GTID_CURRENT_POS))
+ if (mi->gtid_current_pos.append_to_string(&query_str))
{
err_code= ER_OUTOFMEMORY;
errmsg= "The slave I/O thread stops because a fatal out-of-memory "
@@ -1967,7 +2020,7 @@ after_set_capability:
}
}
}
- if (mi->using_gtid == Master_info::USE_GTID_NO)
+ else
{
/*
If we are not using GTID to connect this time, then instead request
@@ -2548,10 +2601,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
protocol->store(mi->ssl_ca, &my_charset_bin);
// Master_Ssl_Crlpath
protocol->store(mi->ssl_capath, &my_charset_bin);
- protocol->store((mi->using_gtid==Master_info::USE_GTID_NO ? "No" :
- (mi->using_gtid==Master_info::USE_GTID_SLAVE_POS ?
- "Slave_Pos" : "Current_Pos")),
- &my_charset_bin);
+ protocol->store(mi->using_gtid_astext(mi->using_gtid), &my_charset_bin);
if (full)
{
protocol->store((uint32) mi->rli.retried_trans);
@@ -3383,8 +3433,22 @@ static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi,
if (!suppress_warnings)
{
char buf[256], llbuff[22];
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ mi->gtid_current_pos.append_to_string(&tmp);
+ if (mi->events_queued_since_last_gtid == 0)
+ tmp.append(STRING_WITH_LEN("'"));
+ else
+ {
+ tmp.append(STRING_WITH_LEN("', GTID event skip "));
+ tmp.append_ulonglong((ulonglong)mi->events_queued_since_last_gtid);
+ }
+ }
my_snprintf(buf, sizeof(buf), messages[SLAVE_RECON_MSG_FAILED],
- IO_RPL_LOG_NAME, llstr(mi->master_log_pos, llbuff));
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos, llbuff),
+ tmp.c_ptr_safe());
/*
Raise a warining during registering on master/requesting dump.
Log a message reading event.
@@ -3476,6 +3540,16 @@ pthread_handler_t handle_slave_io(void *arg)
/* This must be called before run any binlog_relay_io hooks */
my_pthread_setspecific_ptr(RPL_MASTER_INFO, mi);
+ /* Load the set of seen GTIDs, if we did not already. */
+ if (rpl_load_gtid_slave_state(thd))
+ {
+ mi->report(ERROR_LEVEL, thd->stmt_da->sql_errno(),
+ "Unable to load replication GTID slave state from mysql.%s: %s",
+ rpl_gtid_slave_state_table_name.str, thd->stmt_da->message());
+ goto err;
+ }
+
+
if (RUN_HOOK(binlog_relay_io, thread_start, (thd, mi)))
{
mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
@@ -3494,11 +3568,21 @@ pthread_handler_t handle_slave_io(void *arg)
// we can get killed during safe_connect
if (!safe_connect(thd, mysql, mi))
{
- sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',"
- "replication started in log '%s' at position %s",
- mi->user, mi->host, mi->port,
- IO_RPL_LOG_NAME,
- llstr(mi->master_log_pos,llbuff));
+ if (mi->using_gtid == Master_info::USE_GTID_NO)
+ sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',"
+ "replication started in log '%s' at position %s",
+ mi->user, mi->host, mi->port,
+ IO_RPL_LOG_NAME,
+ llstr(mi->master_log_pos,llbuff));
+ else
+ {
+ String tmp;
+ mi->gtid_current_pos.to_string(&tmp);
+ sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',"
+ "replication starts at GTID position '%s'",
+ mi->user, mi->host, mi->port, tmp.c_ptr_safe());
+ }
+
/*
Adding MAX_LOG_EVENT_HEADER_LEN to the max_packet_size on the I/O
thread, since a replication event can become this much larger than
@@ -3515,6 +3599,25 @@ pthread_handler_t handle_slave_io(void *arg)
connected:
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ /*
+ When the IO thread (re)connects to the master using GTID, it will
+ connect at the start of an event group. But the IO thread may have
+ previously logged part of the following event group to the relay
+ log.
+
+ When the IO and SQL thread are started together, we erase any previous
+ relay logs, but this is not possible/desirable while the SQL thread is
+ running. To avoid duplicating partial event groups in the relay logs in
+ this case, we remember the count of events in any partially logged event
+ group before the reconnect, and then here at connect we set up a counter
+ to skip the already-logged part of the group.
+ */
+ mi->gtid_reconnect_event_skip_count= mi->events_queued_since_last_gtid;
+ mi->gtid_event_seen= false;
+ }
+
#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("dbug.before_get_running_status_yes",
{
@@ -3740,8 +3843,19 @@ log space");
// error = 0;
err:
// print the current replication position
- sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s",
- IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
+ if (mi->using_gtid == Master_info::USE_GTID_NO)
+ sql_print_information("Slave I/O thread exiting, read up to log '%s', "
+ "position %s",
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
+ else
+ {
+ String tmp;
+ mi->gtid_current_pos.to_string(&tmp);
+ sql_print_information("Slave I/O thread exiting, read up to log '%s', "
+ "position %s; GTID position %s",
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff),
+ tmp.c_ptr_safe());
+ }
RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi));
thd->reset_query();
thd->reset_db(NULL, 0);
@@ -4008,10 +4122,20 @@ pthread_handler_t handle_slave_sql(void *arg)
rli->group_master_log_name,
llstr(rli->group_master_log_pos,llbuff)));
if (global_system_variables.log_warnings)
+ {
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ rpl_append_gtid_state(&tmp,
+ mi->using_gtid==Master_info::USE_GTID_CURRENT_POS);
+ tmp.append(STRING_WITH_LEN("'"));
+ }
sql_print_information("Slave SQL thread initialized, starting replication in \
-log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
+log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME,
llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name,
- llstr(rli->group_relay_log_pos,llbuff1));
+ llstr(rli->group_relay_log_pos,llbuff1), tmp.c_ptr_safe());
+ }
if (check_temp_dir(rli->slave_patternload_file))
{
@@ -4049,8 +4173,8 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
mysql_mutex_lock(&rli->data_lock);
if (rli->slave_skip_counter)
{
- strmake(saved_log_name, rli->group_relay_log_name, FN_REFLEN - 1);
- strmake(saved_master_log_name, rli->group_master_log_name, FN_REFLEN - 1);
+ strmake_buf(saved_log_name, rli->group_relay_log_name);
+ strmake_buf(saved_master_log_name, rli->group_master_log_name);
saved_log_pos= rli->group_relay_log_pos;
saved_master_log_pos= rli->group_master_log_pos;
saved_skip= rli->slave_skip_counter;
@@ -4145,16 +4269,35 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
sql_print_warning("Slave: %s Error_code: %d", err->get_message_text(), err->get_sql_errno());
}
if (udf_error)
+ {
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ rpl_append_gtid_state(&tmp, false);
+ tmp.append(STRING_WITH_LEN("'"));
+ }
sql_print_error("Error loading user-defined library, slave SQL "
"thread aborted. Install the missing library, and restart the "
"slave SQL thread with \"SLAVE START\". We stopped at log '%s' "
- "position %s", RPL_LOG_NAME, llstr(rli->group_master_log_pos,
- llbuff));
+ "position %s%s", RPL_LOG_NAME, llstr(rli->group_master_log_pos,
+ llbuff), tmp.c_ptr_safe());
+ }
else
+ {
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ rpl_append_gtid_state(&tmp, false);
+ tmp.append(STRING_WITH_LEN("'"));
+ }
sql_print_error("\
Error running query, slave SQL thread aborted. Fix the problem, and restart \
the slave SQL thread with \"SLAVE START\". We stopped at log \
-'%s' position %s", RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff));
+'%s' position %s%s", RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff),
+ tmp.c_ptr_safe());
+ }
}
goto err;
}
@@ -4162,9 +4305,20 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
}
/* Thread stopped. Print the current replication position to the log */
- sql_print_information("Slave SQL thread exiting, replication stopped in log "
- "'%s' at position %s",
- RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff));
+ {
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ rpl_append_gtid_state(&tmp, false);
+ tmp.append(STRING_WITH_LEN("'"));
+ }
+ sql_print_information("Slave SQL thread exiting, replication stopped in "
+ "log '%s' at position %s%s",
+ RPL_LOG_NAME,
+ llstr(rli->group_master_log_pos,llbuff),
+ tmp.c_ptr_safe());
+ }
err:
@@ -4639,6 +4793,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
mysql_mutex_t *log_lock= rli->relay_log.get_log_lock();
ulong s_id;
bool unlock_data_lock= TRUE;
+ bool gtid_skip_enqueue= false;
+
/*
FD_q must have been prepared for the first R_a event
inside get_master_version_and_clock()
@@ -4826,6 +4982,19 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
mi->rli.relay_log.relay_log_checksum_alg= tmp->checksum_alg;
/*
+ Do not queue any format description event that we receive after a
+ reconnect where we are skipping over a partial event group received
+ before the reconnect.
+
+ (If we queued such an event, and it was the first format_description
+ event after master restart, the slave SQL thread would think that
+ the partial event group before it in the relay log was from a
+ previous master crash and should be rolled back).
+ */
+ if (unlikely(mi->gtid_reconnect_event_skip_count && !mi->gtid_event_seen))
+ gtid_skip_enqueue= true;
+
+ /*
Though this does some conversion to the slave's format, this will
preserve the master's binlog format version, and number of event types.
*/
@@ -4920,18 +5089,113 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
" UNTIL master_gtid_pos %s", str.c_ptr_safe());
mi->abort_slave= true;
}
+ event_pos= glev->log_pos;
delete glev;
/*
- Do not update position for fake Gtid_list event (which has a zero
- end_log_pos).
+ We use fake Gtid_list events to update the old-style position (among
+ other things).
+
+ Early code created fake Gtid_list events with zero log_pos, those should
+ not modify old-style position.
*/
- inc_pos= uint4korr(buf+LOG_POS_OFFSET) ? event_len : 0;
+ if (event_pos == 0 || event_pos <= mi->master_log_pos)
+ inc_pos= 0;
+ else
+ inc_pos= event_pos - mi->master_log_pos;
+ }
+ break;
+
+ case GTID_EVENT:
+ {
+ uchar dummy_flag;
+
+ if (mi->using_gtid == Master_info::USE_GTID_NO)
+ goto default_action;
+ if (unlikely(!mi->gtid_event_seen))
+ {
+ mi->gtid_event_seen= true;
+ if (mi->gtid_reconnect_event_skip_count)
+ {
+ rpl_gtid gtid;
+
+ /*
+ If we are reconnecting, and we need to skip a partial event group
+ already queued to the relay log before the reconnect, then we check
+ that we actually get the same event group (same GTID) as before, so
+ we do not end up with half of one group and half another.
+
+ The only way we should be able to receive a different GTID than what
+ we expect is if the binlog on the master (or more likely the whole
+ master server) was replaced with a different one, one the same IP
+ address, _and_ the new master happens to have domains in a different
+ order so we get the GTID from a different domain first. Still, it is
+ best to protect against this case.
+ */
+ if (Gtid_log_event::peek(buf, event_len, checksum_alg,
+ &gtid.domain_id, &gtid.server_id,
+ &gtid.seq_no, &dummy_flag))
+ {
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ goto err;
+ }
+ if (gtid.domain_id != mi->last_queued_gtid.domain_id ||
+ gtid.server_id != mi->last_queued_gtid.server_id ||
+ gtid.seq_no != mi->last_queued_gtid.seq_no)
+ {
+ bool first;
+ error= ER_SLAVE_UNEXPECTED_MASTER_SWITCH;
+ error_msg.append(STRING_WITH_LEN("Expected: "));
+ first= true;
+ rpl_slave_state_tostring_helper(&error_msg, &mi->last_queued_gtid,
+ &first);
+ error_msg.append(STRING_WITH_LEN(", received: "));
+ first= true;
+ rpl_slave_state_tostring_helper(&error_msg, &gtid, &first);
+ goto err;
+ }
+ }
+ }
+
+ if (unlikely(mi->gtid_reconnect_event_skip_count))
+ {
+ goto default_action;
+ }
+
+ /*
+ We have successfully queued to relay log everything before this GTID, so
+ in case of reconnect we can start from after any previous GTID.
+ */
+ if (mi->events_queued_since_last_gtid)
+ {
+ mi->gtid_current_pos.update(&mi->last_queued_gtid);
+ mi->events_queued_since_last_gtid= 0;
+ }
+ if (Gtid_log_event::peek(buf, event_len, checksum_alg,
+ &mi->last_queued_gtid.domain_id,
+ &mi->last_queued_gtid.server_id,
+ &mi->last_queued_gtid.seq_no, &dummy_flag))
+ {
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ goto err;
+ }
+ ++mi->events_queued_since_last_gtid;
}
break;
default:
default_action:
+ if (mi->using_gtid != Master_info::USE_GTID_NO && mi->gtid_event_seen)
+ {
+ if (unlikely(mi->gtid_reconnect_event_skip_count))
+ {
+ --mi->gtid_reconnect_event_skip_count;
+ gtid_skip_enqueue= true;
+ }
+ else if (mi->events_queued_since_last_gtid)
+ ++mi->events_queued_since_last_gtid;
+ }
+
inc_pos= event_len;
break;
}
@@ -5016,8 +5280,16 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
}
else
{
- /* write the event to the relay log */
- if (likely(!(rli->relay_log.appendv(buf,event_len,0))))
+ /*
+ Write the event to the relay log, unless we reconnected in the middle
+ of an event group and now need to skip the initial part of the group that
+ we already wrote before reconnecting.
+ */
+ if (unlikely(gtid_skip_enqueue))
+ {
+ mi->master_log_pos+= inc_pos;
+ }
+ else if (likely(!(rli->relay_log.appendv(buf,event_len,0))))
{
mi->master_log_pos+= inc_pos;
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
@@ -5755,8 +6027,7 @@ static Log_event* next_event(Relay_log_info* rli)
goto err;
}
rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE;
- strmake(rli->event_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name);
flush_relay_log_info(rli);
}
diff --git a/sql/sp.cc b/sql/sp.cc
index 41a1ae583fc..978d7a2eb13 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -114,7 +114,7 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
},
{
{ C_STRING_WITH_LEN("definer") },
- { C_STRING_WITH_LEN("char(77)") },
+ { C_STRING_WITH_LEN("char(") },
{ C_STRING_WITH_LEN("utf8") }
},
{
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 37f5e97110a..98e807722a3 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1159,8 +1159,9 @@ find_handler_after_execution(THD *thd, sp_rcontext *ctx)
MYSQL_ERROR *err;
while ((err= it++))
{
- if (err->get_level() != MYSQL_ERROR::WARN_LEVEL_WARN &&
- err->get_level() != MYSQL_ERROR::WARN_LEVEL_NOTE)
+ if ((err->get_level() != MYSQL_ERROR::WARN_LEVEL_WARN &&
+ err->get_level() != MYSQL_ERROR::WARN_LEVEL_NOTE) ||
+ err->handled())
continue;
if (ctx->find_handler(thd,
@@ -1169,6 +1170,7 @@ find_handler_after_execution(THD *thd, sp_rcontext *ctx)
err->get_level(),
err->get_message_text()))
{
+ err->mark_handled();
break;
}
}
@@ -1418,6 +1420,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
Will write this SP statement into binlog separately.
TODO: consider changing the condition to "not inside event union".
*/
+ MEM_ROOT *user_var_events_alloc_saved= thd->user_var_events_alloc;
if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
thd->user_var_events_alloc= thd->mem_root;
@@ -1433,7 +1436,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
{
reset_dynamic(&thd->user_var_events);
- thd->user_var_events_alloc= NULL;//DEBUG
+ thd->user_var_events_alloc= user_var_events_alloc_saved;
}
/* we should cleanup free_list and memroot, used by instruction */
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index e776968792c..25548d6b036 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -70,7 +70,7 @@ TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
},
{
{ C_STRING_WITH_LEN("User") },
- { C_STRING_WITH_LEN("char(16)") },
+ { C_STRING_WITH_LEN("char(") },
{NULL, 0}
},
{
@@ -829,6 +829,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
goto end;
table->use_all_columns();
(void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0));
+ username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH);
password_length= table->field[2]->field_length /
table->field[2]->charset()->mbmaxlen;
if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
@@ -1436,12 +1437,12 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
sctx->master_access= acl_user->access;
if (acl_user->user)
- strmake(sctx->priv_user, user, USERNAME_LENGTH);
+ strmake_buf(sctx->priv_user, user);
else
*sctx->priv_user= 0;
if (acl_user->host.hostname)
- strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
+ strmake_buf(sctx->priv_host, acl_user->host.hostname);
else
*sctx->priv_host= 0;
}
@@ -8156,6 +8157,12 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
mysql_mutex_lock(&acl_cache->lock);
+ if (!acl_users.elements)
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ login_failed_error(mpvio->thd);
+ DBUG_RETURN(1);
+ }
uint i= nr1 % acl_users.elements;
ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root);
@@ -8181,10 +8188,9 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
mpvio->auth_info.user_name= sctx->user;
mpvio->auth_info.user_name_length= strlen(sctx->user);
mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
- mpvio->auth_info.auth_string_length=
- (unsigned long) mpvio->acl_user->auth_string.length;
- strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
- mpvio->acl_user->user : "", USERNAME_LENGTH);
+ mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
+ strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
+ mpvio->acl_user->user : "");
DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
"plugin=%s",
@@ -8268,7 +8274,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
/* Clear variables that are allocated */
thd->user_connect= 0;
- strmake(sctx->priv_user, sctx->user, USERNAME_LENGTH);
+ strmake_buf(sctx->priv_user, sctx->user);
if (thd->make_lex_string(&mpvio->db, db_buff, db_len) == 0)
DBUG_RETURN(1); /* The error is set by make_lex_string(). */
@@ -8483,14 +8489,15 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
/*
Clip username to allowed length in characters (not bytes). This is
- mostly for backward compatibility.
+ mostly for backward compatibility (to truncate long usernames, as
+ old 5.1 did)
*/
{
CHARSET_INFO *cs= system_charset_info;
int err;
user_len= (uint) cs->cset->well_formed_len(cs, user, user + user_len,
- USERNAME_CHAR_LENGTH, &err);
+ username_char_length, &err);
user[user_len]= '\0';
}
@@ -9085,12 +9092,12 @@ bool acl_authenticate(THD *thd, uint connect_errors,
sctx->master_access= acl_user->access;
if (acl_user->user)
- strmake(sctx->priv_user, acl_user->user, USERNAME_LENGTH - 1);
+ strmake_buf(sctx->priv_user, acl_user->user);
else
*sctx->priv_user= 0;
if (acl_user->host.hostname)
- strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
+ strmake_buf(sctx->priv_host, acl_user->host.hostname);
else
*sctx->priv_host= 0;
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 9e3ea46f526..0b2a939d0ba 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -329,7 +329,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
item->maybe_null = 1;
field_list.push_back(item = new Item_empty_string("Msg_type", 10));
item->maybe_null = 1;
- field_list.push_back(item = new Item_empty_string("Msg_text", 255));
+ field_list.push_back(item = new Item_empty_string("Msg_text",
+ SQL_ADMIN_MSG_TEXT_SIZE));
item->maybe_null = 1;
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
diff --git a/sql/sql_admin.h b/sql/sql_admin.h
index f7ec76efd5e..5398e3019f1 100644
--- a/sql/sql_admin.h
+++ b/sql/sql_admin.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,6 +16,8 @@
#ifndef SQL_TABLE_MAINTENANCE_H
#define SQL_TABLE_MAINTENANCE_H
+/* Must be able to hold ALTER TABLE t PARTITION BY ... KEY ALGORITHM = 1 ... */
+#define SQL_ADMIN_MSG_TEXT_SIZE 128 * 1024
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
LEX_STRING *key_cache_name);
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index 793eead9869..07a5243e836 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -31,8 +31,7 @@ unsigned long mysql_global_audit_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
static mysql_mutex_t LOCK_audit_mask;
-static void event_class_dispatch(THD *thd, unsigned int event_class,
- const void *event);
+static void event_class_dispatch(THD *, unsigned int, const void *);
static inline
@@ -111,9 +110,36 @@ static void connection_class_handler(THD *thd, uint event_subclass, va_list ap)
}
+static void table_class_handler(THD *thd, uint event_subclass, va_list ap)
+{
+ mysql_event_table event;
+ event.event_subclass= event_subclass;
+ event.read_only= va_arg(ap, int);
+ event.thread_id= va_arg(ap, unsigned long);
+ event.user= va_arg(ap, const char *);
+ event.priv_user= va_arg(ap, const char *);
+ event.priv_host= va_arg(ap, const char *);
+ event.external_user= va_arg(ap, const char *);
+ event.proxy_user= va_arg(ap, const char *);
+ event.host= va_arg(ap, const char *);
+ event.ip= va_arg(ap, const char *);
+ event.database= va_arg(ap, const char *);
+ event.database_length= va_arg(ap, unsigned int);
+ event.table= va_arg(ap, const char *);
+ event.table_length= va_arg(ap, unsigned int);
+ event.new_database= va_arg(ap, const char *);
+ event.new_database_length= va_arg(ap, unsigned int);
+ event.new_table= va_arg(ap, const char *);
+ event.new_table_length= va_arg(ap, unsigned int);
+ event_class_dispatch(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
+}
+
+
static audit_handler_t audit_handlers[] =
{
- general_class_handler, connection_class_handler
+ general_class_handler, connection_class_handler,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* placeholders */
+ table_class_handler
};
static const uint audit_handlers_count=
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index a651ce61374..22fdd221e62 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -48,18 +48,24 @@ static inline bool mysql_audit_connection_enabled()
return mysql_global_audit_mask[0] & MYSQL_AUDIT_CONNECTION_CLASSMASK;
}
+static inline bool mysql_audit_table_enabled()
+{
+ return mysql_global_audit_mask[0] & MYSQL_AUDIT_TABLE_CLASSMASK;
+}
+
#else
static inline void mysql_audit_notify(THD *thd, uint event_class,
uint event_subtype, ...) { }
#define mysql_audit_general_enabled() 0
#define mysql_audit_connection_enabled() 0
+#define mysql_audit_table_enabled() 0
#endif
extern void mysql_audit_release(THD *thd);
#define MAX_USER_HOST_SIZE 512
static inline uint make_user_name(THD *thd, char *buf)
{
- Security_context *sctx= thd->security_ctx;
+ const Security_context *sctx= thd->security_ctx;
return strxnmov(buf, MAX_USER_HOST_SIZE,
sctx->priv_user[0] ? sctx->priv_user : "", "[",
sctx->user ? sctx->user : "", "] @ ",
@@ -197,4 +203,87 @@ void mysql_audit_notify_connection_change_user(THD *thd)
}
}
+static inline
+void mysql_audit_external_lock(THD *thd, TABLE_SHARE *share, int lock)
+{
+ if (lock != F_UNLCK && mysql_audit_table_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_LOCK,
+ (int)(lock == F_RDLCK), (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip, share->db.str, (uint)share->db.length,
+ share->table_name.str, (uint)share->table_name.length,
+ 0,0,0,0);
+ }
+}
+
+static inline
+void mysql_audit_create_table(TABLE *table)
+{
+ if (mysql_audit_table_enabled())
+ {
+ THD *thd= table->in_use;
+ const TABLE_SHARE *share= table->s;
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_CREATE,
+ 0, (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip, share->db.str, (uint)share->db.length,
+ share->table_name.str, (uint)share->table_name.length,
+ 0,0,0,0);
+ }
+}
+
+static inline
+void mysql_audit_drop_table(THD *thd, TABLE_LIST *table)
+{
+ if (mysql_audit_table_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_DROP,
+ 0, (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip, table->db, (uint)table->db_length,
+ table->table_name, (uint)table->table_name_length,
+ 0,0,0,0);
+ }
+}
+
+static inline
+void mysql_audit_rename_table(THD *thd, const char *old_db, const char *old_tb,
+ const char *new_db, const char *new_tb)
+{
+ if (mysql_audit_table_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_RENAME,
+ 0, (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip,
+ old_db, (uint)strlen(old_db), old_tb, (uint)strlen(old_tb),
+ new_db, (uint)strlen(new_db), new_tb, (uint)strlen(new_tb));
+ }
+}
+
+static inline
+void mysql_audit_alter_table(THD *thd, TABLE_LIST *table)
+{
+ if (mysql_audit_table_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_ALTER,
+ 0, (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip, table->db, (uint)table->db_length,
+ table->table_name, (uint)table->table_name_length,
+ 0,0,0,0);
+ }
+}
+
#endif /* SQL_AUDIT_INCLUDED */
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index b4c163f61ee..236d52922d7 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -6844,7 +6844,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
We can't do this in Item_field as this would change the
'name' of the item which may be used in the select list
*/
- strmake(name_buff, db, sizeof(name_buff)-1);
+ strmake_buf(name_buff, db);
my_casedn_str(files_charset_info, name_buff);
db= name_buff;
}
@@ -8417,7 +8417,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
We can't do this in Item_field as this would change the
'name' of the item which may be used in the select list
*/
- strmake(name_buff, db_name, sizeof(name_buff)-1);
+ strmake_buf(name_buff, db_name);
my_casedn_str(files_charset_info, name_buff);
db_name= name_buff;
}
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index a4664da2bf6..3bb5deab406 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -107,7 +107,7 @@ void mysql_client_binlog_statement(THD* thd)
rli->relay_log.description_event_for_exec &&
buf))
{
- my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1); /* needed 1 bytes */
goto end;
}
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index ba204f48978..436f7043c49 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -472,6 +472,8 @@ static void make_base_query(String *new_query,
/* The following is guaranteed by the query_cache interface */
DBUG_ASSERT(query[query_length] == 0);
DBUG_ASSERT(!is_white_space(query[0]));
+ /* We do not support UCS2, UTF16, UTF32 as a client character set */
+ DBUG_ASSERT(current_thd->variables.character_set_client->mbminlen == 1);
new_query->length(0); // Don't copy anything from old buffer
if (new_query->realloc(query_length + additional_length))
@@ -2439,7 +2441,28 @@ void Query_cache::init()
m_cache_status= Query_cache::OK;
m_requests_in_progress= 0;
initialized = 1;
- query_state_map= default_charset_info->state_map;
+ /*
+ Using state_map from latin1 should be fine in all cases:
+ 1. We do not support UCS2, UTF16, UTF32 as a client character set.
+ 2. The other character sets are compatible on the lower ASCII-range
+ 0x00-0x20, and have the following characters marked as spaces:
+
+ 0x09 TAB
+ 0x0A LINE FEED
+ 0x0B VERTICAL TAB
+ 0x0C FORM FEED
+ 0x0D CARRIAGE RETUR
+ 0x20 SPACE
+
+ Additionally, only some of the ASCII-compatible character sets
+ (including latin1) can have 0xA0 mapped to "NON-BREAK SPACE"
+ and thus marked as space.
+ That should not be a problem for those charsets that map 0xA0
+ to something else: the parser will just return syntax error
+ if this character appears straight in the query
+ (i.e. not inside a string literal or comment).
+ */
+ query_state_map= my_charset_latin1.state_map;
/*
If we explicitly turn off query cache from the command line query
cache will be disabled for the reminder of the server life
@@ -3972,6 +3995,18 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
/*
+In non-embedded QC intercepts result in net_real_write
+but if we have no net.vio then net_real_write
+will not be called, so QC can't get results of the query
+*/
+#ifdef EMBEDDED_LIBRARY
+#define qc_is_able_to_intercept_result(T) 1
+#else
+#define qc_is_able_to_intercept_result(T) ((T)->net.vio)
+#endif
+
+
+/*
If query is cacheable return number tables in query
(query without tables are not cached)
*/
@@ -3986,7 +4021,8 @@ Query_cache::is_cacheable(THD *thd, LEX *lex,
if (thd->lex->safe_to_cache_query &&
(thd->variables.query_cache_type == 1 ||
(thd->variables.query_cache_type == 2 && (lex->select_lex.options &
- OPTION_TO_QUERY_CACHE))))
+ OPTION_TO_QUERY_CACHE))) &&
+ qc_is_able_to_intercept_result(thd))
{
DBUG_PRINT("qcache", ("options: %lx %lx type: %u",
(long) OPTION_TO_QUERY_CACHE,
@@ -4008,11 +4044,12 @@ Query_cache::is_cacheable(THD *thd, LEX *lex,
}
DBUG_PRINT("qcache",
- ("not interesting query: %d or not cacheable, options %lx %lx type: %u",
+ ("not interesting query: %d or not cacheable, options %lx %lx type: %u net->vio present: %u",
(int) lex->sql_command,
(long) OPTION_TO_QUERY_CACHE,
(long) lex->select_lex.options,
- (int) thd->variables.query_cache_type));
+ (int) thd->variables.query_cache_type,
+ (uint) test(qc_is_able_to_intercept_result(thd))));
DBUG_RETURN(0);
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 238bf46e528..56560801287 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1209,10 +1209,16 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno,
query_cache_abort(&query_cache_tls);
- /* When simulating OOM, skip writing to error log to avoid mtr errors */
- DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(NULL););
-
- cond= warning_info->push_warning(this, sql_errno, sqlstate, level, msg);
+ /*
+ Avoid pushing a condition for fatal out of memory errors as this will
+ require memory allocation and therefore might fail. Non fatal out of
+ memory errors can occur if raised by SIGNAL/RESIGNAL statement.
+ */
+ if (!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY ||
+ sql_errno == ER_OUTOFMEMORY)))
+ {
+ cond= warning_info->push_warning(this, sql_errno, sqlstate, level, msg);
+ }
DBUG_RETURN(cond);
}
@@ -2124,7 +2130,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
key_length + 1);
if (!new_table)
{
- my_error(EE_OUTOFMEMORY, MYF(ME_BELL),
+ my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_FATALERROR),
ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
killed= KILL_CONNECTION;
return 0;
@@ -2590,7 +2596,7 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
bool string_results= FALSE, non_string_results= FALSE;
unit= u;
if ((uint) strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN)
- strmake(path,exchange->file_name,FN_REFLEN-1);
+ strmake_buf(path,exchange->file_name);
write_cs= exchange->cs ? exchange->cs : &my_charset_bin;
@@ -2728,7 +2734,7 @@ int select_export::send_data(List<Item> &items)
set_if_smaller(estimated_bytes, UINT_MAX32);
if (cvt_str.realloc((uint32) estimated_bytes))
{
- my_error(ER_OUTOFMEMORY, MYF(0), (uint32) estimated_bytes);
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), (uint32) estimated_bytes);
goto err;
}
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index c3f2bb0ca11..19e02cc7dae 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -435,7 +435,7 @@ void init_user_stats(USER_STATS *user_stats,
memcpy(user_stats->user, user, user_length);
user_stats->user[user_length]= 0;
user_stats->user_name_length= user_length;
- strmake(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user)-1);
+ strmake_buf(user_stats->priv_user, priv_user);
user_stats->total_connections= total_connections;
user_stats->concurrent_connections= concurrent_connections;
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index d0a83eac189..300f12c5971 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -682,7 +682,7 @@ multi_delete::initialize_tables(JOIN *join)
tab;
tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
{
- if (tab->table->map & tables_to_delete_from)
+ if (!tab->bush_children && tab->table->map & tables_to_delete_from)
{
/* We are going to delete from this table */
TABLE *tbl=walk->table=tab->table;
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 23a60267737..8cfc304e0e7 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -185,6 +185,7 @@ MYSQL_ERROR::MYSQL_ERROR()
m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin),
m_message_text(),
m_sql_errno(0),
+ m_handled(0),
m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
m_mem_root(NULL)
{
@@ -212,6 +213,7 @@ void MYSQL_ERROR::clear()
m_cursor_name.length(0);
m_message_text.length(0);
m_sql_errno= 0;
+ m_handled= 0;
m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
}
@@ -229,6 +231,7 @@ MYSQL_ERROR::MYSQL_ERROR(MEM_ROOT *mem_root)
m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin),
m_message_text(),
m_sql_errno(0),
+ m_handled(0),
m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
m_mem_root(mem_root)
{
@@ -267,6 +270,7 @@ MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond)
copy_string(m_mem_root, & m_table_name, & cond->m_table_name);
copy_string(m_mem_root, & m_column_name, & cond->m_column_name);
copy_string(m_mem_root, & m_cursor_name, & cond->m_cursor_name);
+ m_handled= cond->m_handled;
}
void
@@ -367,7 +371,7 @@ Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg,
m_affected_rows= affected_rows_arg;
m_last_insert_id= last_insert_id_arg;
if (message_arg)
- strmake(m_message, message_arg, sizeof(m_message) - 1);
+ strmake_buf(m_message, message_arg);
else
m_message[0]= '\0';
m_status= DA_OK;
@@ -435,7 +439,7 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
m_sql_errno= sql_errno_arg;
memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH);
m_sqlstate[SQLSTATE_LENGTH]= '\0';
- strmake(m_message, message_arg, sizeof(m_message)-1);
+ strmake_buf(m_message, message_arg);
m_status= DA_ERROR;
DBUG_VOID_RETURN;
diff --git a/sql/sql_error.h b/sql/sql_error.h
index fadd3b51ec6..ae9ef9f8d47 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -197,6 +197,17 @@ public:
~MYSQL_ERROR()
{}
+ /** check if condition was handled by a condition handler */
+ bool handled() const
+ {
+ return m_handled;
+ }
+ /** mark that condition was handled */
+ void mark_handled()
+ {
+ m_handled= 1;
+ }
+
private:
/*
The interface of MYSQL_ERROR is mostly private, by design,
@@ -310,6 +321,9 @@ private:
/** MySQL extension, MYSQL_ERRNO condition item. */
uint m_sql_errno;
+ /** Marker if error/warning was handled by a continue handler */
+ bool m_handled;
+
/**
SQL RETURNED_SQLSTATE condition item.
This member is always NUL terminated.
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index d1e5d731183..06efaea8a5b 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2001, 2011, Oracle and/or its affiliates.
- Copyright (c) 2011 Monty Program Ab
+/* Copyright (c) 2001, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1010,11 +1010,13 @@ static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables)
for (tables= first; tables; tables= tables->next_local)
{
+ if (tables->is_anonymous_derived_table())
+ continue;
if ((! *tables->db ||
! my_strcasecmp(&my_charset_latin1, hash_tables->db.str,
- tables->db)) &&
+ tables->get_db_name())) &&
! my_strcasecmp(&my_charset_latin1, hash_tables->table_name.str,
- tables->table_name))
+ tables->get_table_name()))
{
/* Link into hash_tables list */
hash_tables->next= head;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 4f025c7c335..b1354ce38bd 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1995,8 +1995,7 @@ public:
DBUG_ENTER("Delayed_insert constructor");
thd.security_ctx->user=(char*) delayed_user;
thd.security_ctx->host=(char*) my_localhost;
- strmake(thd.security_ctx->priv_user, thd.security_ctx->user,
- USERNAME_LENGTH);
+ strmake_buf(thd.security_ctx->priv_user, thd.security_ctx->user);
thd.current_tablenr=0;
thd.set_command(COM_DELAYED_INSERT);
thd.lex->current_select= 0; // for my_message_sql
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index fbe5bcd57f2..c0ebb478056 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1423,7 +1423,7 @@ int lex_one_token(void *arg, void *yythd)
yylval->lex_str=get_token(lip,
2, // skip x'
length-3); // don't count x' and last '
- return (HEX_NUM);
+ return HEX_STRING;
case MY_LEX_BIN_NUMBER: // Found b'bin-string'
lip->yySkip(); // Accept opening '
@@ -1939,6 +1939,7 @@ void st_select_lex::init_select()
merged_into= 0;
m_non_agg_field_used= false;
m_agg_func_used= false;
+ name_visibility_map= 0;
}
/*
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 9cdc8d7d0b6..58cd6316066 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -831,6 +831,9 @@ public:
*/
List<String> *prev_join_using;
+ /* namp of nesting SELECT visibility (for aggregate functions check) */
+ nesting_map name_visibility_map;
+
void init_query();
void init_select();
st_select_lex_unit* master_unit();
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ea0a19fc5f3..a1e8a79513d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1727,7 +1727,8 @@ void log_slow_statement(THD *thd)
/* Follow the slow log filter configuration. */
if (!thd->enable_slow_log ||
- !(thd->variables.log_slow_filter & thd->query_plan_flags))
+ (thd->variables.log_slow_filter
+ && !(thd->variables.log_slow_filter & thd->query_plan_flags)))
DBUG_VOID_RETURN;
if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
@@ -2990,13 +2991,34 @@ end_with_restore_list:
{
LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
Master_info *mi;
+ int load_error;
+
+ load_error= rpl_load_gtid_slave_state(thd);
+
mysql_mutex_lock(&LOCK_active_mi);
if ((mi= (master_info_index->
get_master_info(&lex_mi->connection_name,
MYSQL_ERROR::WARN_LEVEL_ERROR))))
+ {
+ if (load_error)
+ {
+ /*
+ We cannot start a slave using GTID if we cannot load the GTID position
+ from the mysql.gtid_slave_pos table. But we can allow non-GTID
+ replication (useful eg. during upgrade).
+ */
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
+ }
+ else
+ thd->clear_error();
+ }
if (!start_slave(thd, mi, 1 /* net report*/))
my_ok(thd);
+ }
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index b2e2016b47d..2bcefce1212 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2005, 2011, Oracle and/or its affiliates.
+/* Copyright (c) 2005, 2013, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -87,7 +87,9 @@ const LEX_STRING partition_keywords[]=
{ C_STRING_WITH_LEN("KEY") },
{ C_STRING_WITH_LEN("MAXVALUE") },
{ C_STRING_WITH_LEN("LINEAR ") },
- { C_STRING_WITH_LEN(" COLUMNS") }
+ { C_STRING_WITH_LEN(" COLUMNS") },
+ { C_STRING_WITH_LEN("ALGORITHM") }
+
};
static const char *part_str= "PARTITION";
static const char *sub_str= "SUB";
@@ -368,7 +370,7 @@ int get_parts_for_update(const uchar *old_data, uchar *new_data,
longlong old_func_value;
DBUG_ENTER("get_parts_for_update");
- DBUG_ASSERT(new_data == rec0);
+ DBUG_ASSERT(new_data == rec0); // table->record[0]
set_field_ptr(part_field_array, old_data, rec0);
error= part_info->get_partition_id(part_info, old_part_id,
&old_func_value);
@@ -526,12 +528,12 @@ static bool set_up_field_array(TABLE *table,
}
if (num_fields > MAX_REF_PARTS)
{
- char *ptr;
+ char *err_str;
if (is_sub_part)
- ptr= (char*)"subpartition function";
+ err_str= (char*)"subpartition function";
else
- ptr= (char*)"partition function";
- my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), ptr);
+ err_str= (char*)"partition function";
+ my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), err_str);
DBUG_RETURN(TRUE);
}
if (num_fields == 0)
@@ -2339,6 +2341,58 @@ end:
return err;
}
+
+/**
+ Add 'KEY' word, with optional 'ALGORTIHM = N'.
+
+ @param fptr File to write to.
+ @param part_info partition_info holding the used key_algorithm
+ @param current_comment_start NULL, or comment string encapsulating the
+ PARTITION BY clause.
+
+ @return Operation status.
+ @retval 0 Success
+ @retval != 0 Failure
+*/
+
+static int add_key_with_algorithm(File fptr, partition_info *part_info,
+ const char *current_comment_start)
+{
+ int err= 0;
+ err+= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
+
+ /*
+ current_comment_start is given when called from SHOW CREATE TABLE,
+ Then only add ALGORITHM = 1, not the default 2 or non-set 0!
+ For .frm current_comment_start is NULL, then add ALGORITHM if != 0.
+ */
+ if (part_info->key_algorithm == partition_info::KEY_ALGORITHM_51 || // SHOW
+ (!current_comment_start && // .frm
+ (part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE)))
+ {
+ /* If we already are within a comment, end that comment first. */
+ if (current_comment_start)
+ err+= add_string(fptr, "*/ ");
+ err+= add_string(fptr, "/*!50531 ");
+ err+= add_part_key_word(fptr, partition_keywords[PKW_ALGORITHM].str);
+ err+= add_equal(fptr);
+ err+= add_space(fptr);
+ err+= add_int(fptr, part_info->key_algorithm);
+ err+= add_space(fptr);
+ err+= add_string(fptr, "*/ ");
+ if (current_comment_start)
+ {
+ /* Skip new line. */
+ if (current_comment_start[0] == '\n')
+ current_comment_start++;
+ err+= add_string(fptr, current_comment_start);
+ err+= add_space(fptr);
+ }
+ }
+ return err;
+}
+
+
/*
Generate the partition syntax from the partition data structure.
Useful for support of generating defaults, SHOW CREATE TABLES
@@ -2383,7 +2437,8 @@ char *generate_partition_syntax(partition_info *part_info,
bool use_sql_alloc,
bool show_partition_options,
HA_CREATE_INFO *create_info,
- Alter_info *alter_info)
+ Alter_info *alter_info,
+ const char *current_comment_start)
{
uint i,j, tot_num_parts, num_subparts;
partition_element *part_elem;
@@ -2417,7 +2472,8 @@ char *generate_partition_syntax(partition_info *part_info,
err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
if (part_info->list_of_part_fields)
{
- err+= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
+ err+= add_key_with_algorithm(fptr, part_info,
+ current_comment_start);
err+= add_part_field_list(fptr, part_info->part_field_list);
}
else
@@ -2457,8 +2513,9 @@ char *generate_partition_syntax(partition_info *part_info,
err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
if (part_info->list_of_subpart_fields)
{
- add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
- add_part_field_list(fptr, part_info->subpart_field_list);
+ err+= add_key_with_algorithm(fptr, part_info,
+ current_comment_start);
+ err+= add_part_field_list(fptr, part_info->subpart_field_list);
}
else
err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str);
@@ -2667,10 +2724,82 @@ static uint32 calculate_key_value(Field **field_array)
{
ulong nr1= 1;
ulong nr2= 4;
+ bool use_51_hash;
+ use_51_hash= test((*field_array)->table->part_info->key_algorithm ==
+ partition_info::KEY_ALGORITHM_51);
do
{
Field *field= *field_array;
+ if (use_51_hash)
+ {
+ switch (field->real_type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ {
+ if (field->is_null())
+ {
+ nr1^= (nr1 << 1) | 1;
+ continue;
+ }
+ /* Force this to my_hash_sort_bin, which was used in 5.1! */
+ uint len= field->pack_length();
+ my_charset_bin.coll->hash_sort(&my_charset_bin, field->ptr, len,
+ &nr1, &nr2);
+ /* Done with this field, continue with next one. */
+ continue;
+ }
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_BIT:
+ /* Not affected, same in 5.1 and 5.5 */
+ break;
+ /*
+ ENUM/SET uses my_hash_sort_simple in 5.1 (i.e. my_charset_latin1)
+ and my_hash_sort_bin in 5.5!
+ */
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ {
+ if (field->is_null())
+ {
+ nr1^= (nr1 << 1) | 1;
+ continue;
+ }
+ /* Force this to my_hash_sort_bin, which was used in 5.1! */
+ uint len= field->pack_length();
+ my_charset_latin1.coll->hash_sort(&my_charset_latin1, field->ptr,
+ len, &nr1, &nr2);
+ continue;
+ }
+ /* These types should not be allowed for partitioning! */
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_GEOMETRY:
+ /* fall through. */
+ default:
+ DBUG_ASSERT(0); // New type?
+ /* Fall through for default hashing (5.5). */
+ }
+ /* fall through, use collation based hashing. */
+ }
field->hash(&nr1, &nr2);
} while (*(++field_array));
return (uint32) nr1;
@@ -5411,14 +5540,25 @@ the generated partition syntax in a correct manner.
Need to cater for engine types that can handle partition without
using the partition handler.
*/
- if (thd->work_part_info != table->part_info)
+ if (part_info != table->part_info)
{
- DBUG_PRINT("info", ("partition changed"));
- *partition_changed= TRUE;
- if (thd->work_part_info->fix_parser_data(thd))
+ if (part_info->fix_parser_data(thd))
{
goto err;
}
+ /*
+ Compare the old and new part_info. If only key_algorithm
+ change is done, don't consider it as changed partitioning (to avoid
+ rebuild). This is to handle KEY (numeric_cols) partitioned tables
+ created in 5.1. For more info, see bug#14521864.
+ */
+ if (alter_info->flags != ALTER_PARTITION ||
+ !table->part_info ||
+ !table->part_info->has_same_partitioning(part_info))
+ {
+ DBUG_PRINT("info", ("partition changed"));
+ *partition_changed= true;
+ }
}
/*
Set up partition default_engine_type either from the create_info
@@ -6970,7 +7110,8 @@ void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
void mem_alloc_error(size_t size)
{
- my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(size));
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ static_cast<int>(size));
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -7057,12 +7198,12 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str)
definition)
IMPLEMENTATION
- There are two available interval analyzer functions:
- (1) get_part_iter_for_interval_via_mapping
+ There are three available interval analyzer functions:
+ (1) get_part_iter_for_interval_via_mapping
(2) get_part_iter_for_interval_cols_via_map
(3) get_part_iter_for_interval_via_walking
- They both have limited applicability:
+ They all have limited applicability:
(1) is applicable for "PARTITION BY <RANGE|LIST>(func(t.field))", where
func is a monotonic function.
@@ -7435,6 +7576,9 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
get_endpoint_func UNINIT_VAR(get_endpoint);
bool can_match_multiple_values; /* is not '=' */
uint field_len= field->pack_length_in_rec();
+ MYSQL_TIME start_date;
+ bool check_zero_dates= false;
+ bool zero_in_start_date= true;
DBUG_ENTER("get_part_iter_for_interval_via_mapping");
DBUG_ASSERT(!is_subpart);
(void) store_length_array;
@@ -7491,6 +7635,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
{
/* col is NOT NULL, but F(col) can return NULL, add NULL partition */
part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
+ check_zero_dates= true;
}
}
@@ -7534,6 +7679,19 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
DBUG_RETURN(1);
}
part_iter->part_nums.cur= part_iter->part_nums.start;
+ if (check_zero_dates && !part_info->part_expr->null_value)
+ {
+ if (!(flags & NO_MAX_RANGE) &&
+ (field->type() == MYSQL_TYPE_DATE ||
+ field->type() == MYSQL_TYPE_DATETIME))
+ {
+ /* Monotonic, but return NULL for dates with zeros in month/day. */
+ zero_in_start_date= field->get_date(&start_date, 0);
+ DBUG_PRINT("info", ("zero start %u %04d-%02d-%02d",
+ zero_in_start_date, start_date.year,
+ start_date.month, start_date.day));
+ }
+ }
if (part_iter->part_nums.start == max_endpoint_val)
DBUG_RETURN(0); /* No partitions */
}
@@ -7547,6 +7705,29 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
store_key_image_to_rec(field, max_value, field_len);
bool include_endp= !test(flags & NEAR_MAX);
part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp);
+ if (check_zero_dates &&
+ !zero_in_start_date &&
+ !part_info->part_expr->null_value)
+ {
+ MYSQL_TIME end_date;
+ bool zero_in_end_date= field->get_date(&end_date, 0);
+ /*
+ This is an optimization for TO_DAYS()/TO_SECONDS() to avoid scanning
+ the NULL partition for ranges that cannot include a date with 0 as
+ month/day.
+ */
+ DBUG_PRINT("info", ("zero end %u %04d-%02d-%02d",
+ zero_in_end_date,
+ end_date.year, end_date.month, end_date.day));
+ DBUG_ASSERT(!memcmp(((Item_func*) part_info->part_expr)->func_name(),
+ "to_days", 7) ||
+ !memcmp(((Item_func*) part_info->part_expr)->func_name(),
+ "to_seconds", 10));
+ if (!zero_in_end_date &&
+ start_date.month == end_date.month &&
+ start_date.year == end_date.year)
+ part_iter->ret_null_part= part_iter->ret_null_part_orig= false;
+ }
if (part_iter->part_nums.start >= part_iter->part_nums.end &&
!part_iter->ret_null_part)
DBUG_RETURN(0); /* No partitions */
diff --git a/sql/sql_partition.h b/sql/sql_partition.h
index 8db07f836b0..cf532c45c66 100644
--- a/sql/sql_partition.h
+++ b/sql/sql_partition.h
@@ -1,8 +1,7 @@
#ifndef SQL_PARTITION_INCLUDED
#define SQL_PARTITION_INCLUDED
-/*
- Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -267,7 +266,8 @@ char *generate_partition_syntax(partition_info *part_info,
uint *buf_length, bool use_sql_alloc,
bool show_partition_options,
HA_CREATE_INFO *create_info,
- Alter_info *alter_info);
+ Alter_info *alter_info,
+ const char *current_comment_start);
bool partition_key_modified(TABLE *table, const MY_BITMAP *fields);
#else
#define partition_key_modified(X,Y) 0
diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
index 4edd47de855..b9bf3dbc217 100644
--- a/sql/sql_partition_admin.cc
+++ b/sql/sql_partition_admin.cc
@@ -141,7 +141,9 @@ bool Alter_table_truncate_partition_statement::execute(THD *thd)
TODO: Add support for TRUNCATE PARTITION for NDB and other
engines supporting native partitioning.
*/
- if (first_table->table->s->db_type() != partition_hton)
+
+ if (!first_table->table || first_table->view ||
+ first_table->table->s->db_type() != partition_hton)
{
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
diff --git a/sql/sql_plugin_compat.h b/sql/sql_plugin_compat.h
index 8c6014f8dc6..5c7bb620575 100644
--- a/sql/sql_plugin_compat.h
+++ b/sql/sql_plugin_compat.h
@@ -16,9 +16,9 @@
/* old plugin api structures, used for backward compatibility */
#define upgrade_var(X) latest->X= X
-#define upgrade_str(X) strmake(latest->X, X, sizeof(X))
+#define upgrade_str(X) strmake_buf(latest->X, X)
#define downgrade_var(X) X= latest->X
-#define downgrade_str(X) strmake(X, latest->X, sizeof(X)-1)
+#define downgrade_str(X) strmake_buf(X, latest->X)
/**************************************************************/
/* Authentication API, version 0x0100 *************************/
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 025ff8820e6..518b86ee301 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -879,7 +879,7 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
if (param->state == Item_param::NO_VALUE)
DBUG_RETURN(1);
- if (param->limit_clause_param && param->item_type != Item::INT_ITEM)
+ if (param->limit_clause_param)
{
param->set_int(param->val_int(), MY_INT64_NUM_DECIMAL_DIGITS);
param->item_type= Item::INT_ITEM;
@@ -1503,7 +1503,8 @@ static int mysql_test_select(Prepared_statement *stmt,
if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
{
- my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(sizeof(select_send)));
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ static_cast<int>(sizeof(select_send)));
goto error;
}
@@ -1877,7 +1878,7 @@ static bool mysql_test_multidelete(Prepared_statement *stmt,
stmt->thd->lex->current_select= &stmt->thd->lex->select_lex;
if (add_item_to_list(stmt->thd, new Item_null()))
{
- my_error(ER_OUTOFMEMORY, MYF(0), 0);
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0);
goto error;
}
@@ -3867,7 +3868,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
alloc_query(thd, (char*) expanded_query->ptr(),
expanded_query->length()))
{
- my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), expanded_query->length());
goto error;
}
/*
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 9e3da1ce641..0d6c97842d4 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2008, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
@@ -50,7 +50,7 @@ extern TYPELIB binlog_checksum_typelib;
static int
fake_event_header(String* packet, Log_event_type event_type, ulong extra_len,
my_bool *do_checksum, ha_checksum *crc, const char** errmsg,
- uint8 checksum_alg_arg)
+ uint8 checksum_alg_arg, uint32 end_pos)
{
char header[LOG_EVENT_HEADER_LEN];
ulong event_len;
@@ -70,7 +70,7 @@ fake_event_header(String* packet, Log_event_type event_type, ulong extra_len,
int4store(header + EVENT_LEN_OFFSET, event_len);
int2store(header + FLAGS_OFFSET, LOG_EVENT_ARTIFICIAL_F);
// TODO: check what problems this may cause and fix them
- int4store(header + LOG_POS_OFFSET, 0);
+ int4store(header + LOG_POS_OFFSET, end_pos);
if (packet->append(header, sizeof(header)))
{
*errmsg= "Failed due to out-of-memory writing event";
@@ -146,7 +146,7 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
if ((err= fake_event_header(packet, ROTATE_EVENT,
ident_len + ROTATE_HEADER_LEN, &do_checksum, &crc,
- errmsg, checksum_alg_arg)))
+ errmsg, checksum_alg_arg, 0)))
DBUG_RETURN(err);
int8store(buf+R_POS_OFFSET,position);
@@ -169,7 +169,7 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
static int fake_gtid_list_event(NET* net, String* packet,
Gtid_list_log_event *glev, const char** errmsg,
- uint8 checksum_alg_arg)
+ uint8 checksum_alg_arg, uint32 current_pos)
{
my_bool do_checksum;
int err;
@@ -185,7 +185,7 @@ static int fake_gtid_list_event(NET* net, String* packet,
}
if ((err= fake_event_header(packet, GTID_LIST_EVENT,
str.length(), &do_checksum, &crc,
- errmsg, checksum_alg_arg)))
+ errmsg, checksum_alg_arg, current_pos)))
return err;
packet->append(str);
@@ -901,8 +901,6 @@ check_slave_start_position(THD *thd, slave_connection_state *st,
rpl_gtid **delete_list= NULL;
uint32 delete_idx= 0;
bool slave_state_loaded= false;
- uint32 missing_domains= 0;
- rpl_gtid missing_domain_gtid;
for (i= 0; i < st->hash.records; ++i)
{
@@ -943,14 +941,7 @@ check_slave_start_position(THD *thd, slave_connection_state *st,
We do not have anything in this domain, neither in the binlog nor
in the slave state. So we are probably one master in a multi-master
setup, and this domain is served by a different master.
-
- This is not an error, however if we are missing _all_ domains
- requested by the slave, then we still give error (below, after
- the loop).
*/
- if (!missing_domains)
- missing_domain_gtid= *slave_gtid;
- ++missing_domains;
continue;
}
@@ -1043,14 +1034,6 @@ check_slave_start_position(THD *thd, slave_connection_state *st,
}
}
- if (missing_domains == st->hash.records && missing_domains > 0)
- {
- *errormsg= "Requested slave GTID state not found in binlog";
- *error_gtid= missing_domain_gtid;
- err= ER_GTID_POSITION_NOT_FOUND_IN_BINLOG;
- goto end;
- }
-
/* Do any delayed deletes from the hash. */
if (delete_list)
{
@@ -1423,7 +1406,7 @@ is_until_reached(THD *thd, NET *net, String *packet, ulong *ev_offset,
enum_gtid_until_state gtid_until_group,
Log_event_type event_type, uint8 current_checksum_alg,
ushort flags, const char **errmsg,
- rpl_binlog_state *until_binlog_state)
+ rpl_binlog_state *until_binlog_state, uint32 current_pos)
{
switch (gtid_until_group)
{
@@ -1454,7 +1437,8 @@ is_until_reached(THD *thd, NET *net, String *packet, ulong *ev_offset,
return true;
Gtid_list_log_event glev(until_binlog_state,
Gtid_list_log_event::FLAG_UNTIL_REACHED);
- if (fake_gtid_list_event(net, packet, &glev, errmsg, current_checksum_alg))
+ if (fake_gtid_list_event(net, packet, &glev, errmsg, current_checksum_alg,
+ current_pos))
return true;
*errmsg= NULL;
return true;
@@ -1525,6 +1509,19 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
return "Failed to read Gtid_log_event: corrupt binlog";
}
+ DBUG_EXECUTE_IF("gtid_force_reconnect_at_10_1_100",
+ {
+ rpl_gtid *dbug_gtid;
+ if ((dbug_gtid= until_binlog_state->find(10,1)) &&
+ dbug_gtid->seq_no == 100)
+ {
+ DBUG_SET("-d,gtid_force_reconnect_at_10_1_100");
+ DBUG_SET_INITIAL("-d,gtid_force_reconnect_at_10_1_100");
+ my_errno= ER_UNKNOWN_ERROR;
+ return "DBUG-injected forced reconnect";
+ }
+ });
+
if (until_binlog_state->update(&event_gtid, false))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
@@ -1544,19 +1541,31 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
if (event_gtid.server_id == gtid->server_id &&
event_gtid.seq_no >= gtid->seq_no)
{
- /*
- In strict mode, it is an error if the slave requests to start in
- a "hole" in the master's binlog: a GTID that does not exist, even
- though both the prior and subsequent seq_no exists for same
- domain_id and server_id.
- */
- if (slave_gtid_strict_mode && event_gtid.seq_no > gtid->seq_no)
+ if (event_gtid.seq_no > gtid->seq_no)
{
- my_errno= ER_GTID_START_FROM_BINLOG_HOLE;
- *error_gtid= *gtid;
- return "The binlog on the master is missing the GTID requested "
- "by the slave (even though both a prior and a subsequent "
- "sequence number does exist), and GTID strict mode is enabled.";
+ /*
+ In strict mode, it is an error if the slave requests to start
+ in a "hole" in the master's binlog: a GTID that does not
+ exist, even though both the prior and subsequent seq_no exists
+ for same domain_id and server_id.
+ */
+ if (slave_gtid_strict_mode)
+ {
+ my_errno= ER_GTID_START_FROM_BINLOG_HOLE;
+ *error_gtid= *gtid;
+ return "The binlog on the master is missing the GTID requested "
+ "by the slave (even though both a prior and a subsequent "
+ "sequence number does exist), and GTID strict mode is enabled.";
+ }
+ }
+ else
+ {
+ /*
+ Send a fake Gtid_list event to the slave.
+ This allows the slave to update its current binlog position
+ so MASTER_POS_WAIT() and MASTER_GTID_WAIT() can work.
+ */
+// send_fake_gtid_list_event(until_binlog_state);
}
/*
Delete this entry if we have reached slave start position (so we
@@ -1812,8 +1821,10 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
uint8 current_checksum_alg= BINLOG_CHECKSUM_ALG_UNDEF;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
+
#ifndef DBUG_OFF
int left_events = max_binlog_dump_events;
+ uint dbug_reconnect_counter= 0;
#endif
DBUG_ENTER("mysql_binlog_send");
DBUG_PRINT("enter",("log_ident: '%s' pos: %ld", log_ident, (long) pos));
@@ -1847,6 +1858,13 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
until_gtid_state= &until_gtid_state_obj;
}
+ DBUG_EXECUTE_IF("binlog_force_reconnect_after_22_events",
+ {
+ DBUG_SET("-d,binlog_force_reconnect_after_22_events");
+ DBUG_SET_INITIAL("-d,binlog_force_reconnect_after_22_events");
+ dbug_reconnect_counter= 22;
+ });
+
/*
We want to corrupt the first event, in Log_event::read_log_event().
But we do not want the corruption to happen early, eg. when client does
@@ -2130,9 +2148,12 @@ impossible position";
if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
goto err;
+ bool is_active_binlog= false;
while (!(killed= thd->killed) &&
!(error = Log_event::read_log_event(&log, packet, log_lock,
- current_checksum_alg)))
+ current_checksum_alg,
+ log_file_name,
+ &is_active_binlog)))
{
#ifndef DBUG_OFF
if (max_binlog_dump_events && !left_events--)
@@ -2193,6 +2214,19 @@ impossible position";
(*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
}
+#ifndef DBUG_OFF
+ if (dbug_reconnect_counter > 0)
+ {
+ --dbug_reconnect_counter;
+ if (dbug_reconnect_counter == 0)
+ {
+ errmsg= "DBUG-injected forced reconnect";
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ }
+#endif
+
if ((tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
log_file_name, &log,
mariadb_slave_capability, ev_offset,
@@ -2208,7 +2242,7 @@ impossible position";
if (until_gtid_state &&
is_until_reached(thd, net, packet, &ev_offset, gtid_until_group,
event_type, current_checksum_alg, flags, &errmsg,
- &until_binlog_state))
+ &until_binlog_state, my_b_tell(&log)))
{
if (errmsg)
{
@@ -2233,6 +2267,13 @@ impossible position";
if (killed)
goto end;
+ DBUG_EXECUTE_IF("wait_after_binlog_EOF",
+ {
+ const char act[]= "now wait_for signal.rotate_finished";
+ DBUG_ASSERT(!debug_sync_set_action(current_thd,
+ STRING_WITH_LEN(act)));
+ };);
+
/*
TODO: now that we are logging the offset, check to make sure
the recorded offset and the actual match.
@@ -2243,8 +2284,11 @@ impossible position";
if (test_for_non_eof_log_read_errors(error, &errmsg))
goto err;
- if (!(flags & BINLOG_DUMP_NON_BLOCK) &&
- mysql_bin_log.is_active(log_file_name))
+ /*
+ We should only move to the next binlog when the last read event
+ came from a already deactivated binlog.
+ */
+ if (!(flags & BINLOG_DUMP_NON_BLOCK) && is_active_binlog)
{
/*
Block until there is more data in the log
@@ -2298,7 +2342,8 @@ impossible position";
mysql_mutex_unlock(log_lock);
read_packet = 1;
p_coord->pos= uint4korr(packet->ptr() + ev_offset + LOG_POS_OFFSET);
- event_type= (Log_event_type)((*packet)[LOG_EVENT_OFFSET+ev_offset]);
+ event_type=
+ (Log_event_type)((uchar)(*packet)[LOG_EVENT_OFFSET+ev_offset]);
break;
case LOG_READ_EOF:
@@ -2390,7 +2435,7 @@ impossible position";
until_gtid_state &&
is_until_reached(thd, net, packet, &ev_offset, gtid_until_group,
event_type, current_checksum_alg, flags, &errmsg,
- &until_binlog_state))
+ &until_binlog_state, my_b_tell(&log)))
{
if (errmsg)
{
@@ -2628,8 +2673,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
We don't check thd->lex->mi.log_file_name for NULL here
since it is checked in sql_yacc.yy
*/
- strmake(mi->rli.until_log_name, thd->lex->mi.log_file_name,
- sizeof(mi->rli.until_log_name)-1);
+ strmake_buf(mi->rli.until_log_name, thd->lex->mi.log_file_name);
}
else if (thd->lex->mi.relay_log_pos)
{
@@ -2637,8 +2681,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
slave_errno=ER_BAD_SLAVE_UNTIL_COND;
mi->rli.until_condition= Relay_log_info::UNTIL_RELAY_POS;
mi->rli.until_log_pos= thd->lex->mi.relay_log_pos;
- strmake(mi->rli.until_log_name, thd->lex->mi.relay_log_name,
- sizeof(mi->rli.until_log_name)-1);
+ strmake_buf(mi->rli.until_log_name, thd->lex->mi.relay_log_name);
}
else if (thd->lex->mi.gtid_pos_str.str)
{
@@ -2945,14 +2988,15 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
*/
static bool get_string_parameter(char *to, const char *from, size_t length,
- const char *name)
+ const char *name, CHARSET_INFO *cs)
{
if (from) // Empty paramaters allowed
{
- size_t from_length;
- if ((from_length= strlen(from)) > length)
+ size_t from_length= strlen(from);
+ uint from_numchars= cs->cset->numchars(cs, from, from + from_length);
+ if (from_numchars > length / cs->mbmaxlen)
{
- my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name, (int) length);
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name, length / cs->mbmaxlen);
return 1;
}
memcpy(to, from, from_length+1);
@@ -2986,6 +3030,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
char saved_host[HOSTNAME_LENGTH + 1];
uint saved_port;
char saved_log_name[FN_REFLEN];
+ Master_info::enum_using_gtid saved_using_gtid;
char master_info_file_tmp[FN_REFLEN];
char relay_log_info_file_tmp[FN_REFLEN];
my_off_t saved_log_pos;
@@ -3071,10 +3116,11 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
/*
Before processing the command, save the previous state.
*/
- strmake(saved_host, mi->host, HOSTNAME_LENGTH);
+ strmake_buf(saved_host, mi->host);
saved_port= mi->port;
- strmake(saved_log_name, mi->master_log_name, FN_REFLEN - 1);
+ strmake_buf(saved_log_name, mi->master_log_name);
saved_log_pos= mi->master_log_pos;
+ saved_using_gtid= mi->using_gtid;
/*
If the user specified host or port without binlog or position,
@@ -3088,8 +3134,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
}
if (lex_mi->log_file_name)
- strmake(mi->master_log_name, lex_mi->log_file_name,
- sizeof(mi->master_log_name)-1);
+ strmake_buf(mi->master_log_name, lex_mi->log_file_name);
if (lex_mi->pos)
{
mi->master_log_pos= lex_mi->pos;
@@ -3097,11 +3142,12 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
if (get_string_parameter(mi->host, lex_mi->host, sizeof(mi->host)-1,
- "MASTER_HOST") ||
+ "MASTER_HOST", system_charset_info) ||
get_string_parameter(mi->user, lex_mi->user, sizeof(mi->user)-1,
- "MASTER_USER") ||
+ "MASTER_USER", system_charset_info) ||
get_string_parameter(mi->password, lex_mi->password,
- sizeof(mi->password)-1, "MASTER_PASSWORD"))
+ sizeof(mi->password)-1, "MASTER_PASSWORD",
+ &my_charset_bin))
{
ret= TRUE;
goto err;
@@ -3153,19 +3199,19 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
(lex_mi->ssl_verify_server_cert == LEX_MASTER_INFO::LEX_MI_ENABLE);
if (lex_mi->ssl_ca)
- strmake(mi->ssl_ca, lex_mi->ssl_ca, sizeof(mi->ssl_ca)-1);
+ strmake_buf(mi->ssl_ca, lex_mi->ssl_ca);
if (lex_mi->ssl_capath)
- strmake(mi->ssl_capath, lex_mi->ssl_capath, sizeof(mi->ssl_capath)-1);
+ strmake_buf(mi->ssl_capath, lex_mi->ssl_capath);
if (lex_mi->ssl_cert)
- strmake(mi->ssl_cert, lex_mi->ssl_cert, sizeof(mi->ssl_cert)-1);
+ strmake_buf(mi->ssl_cert, lex_mi->ssl_cert);
if (lex_mi->ssl_cipher)
- strmake(mi->ssl_cipher, lex_mi->ssl_cipher, sizeof(mi->ssl_cipher)-1);
+ strmake_buf(mi->ssl_cipher, lex_mi->ssl_cipher);
if (lex_mi->ssl_key)
- strmake(mi->ssl_key, lex_mi->ssl_key, sizeof(mi->ssl_key)-1);
+ strmake_buf(mi->ssl_key, lex_mi->ssl_key);
if (lex_mi->ssl_crl)
- strmake(mi->ssl_crl, lex_mi->ssl_crl, sizeof(mi->ssl_crl)-1);
+ strmake_buf(mi->ssl_crl, lex_mi->ssl_crl);
if (lex_mi->ssl_crlpath)
- strmake(mi->ssl_crlpath, lex_mi->ssl_crlpath, sizeof(mi->ssl_crlpath)-1);
+ strmake_buf(mi->ssl_crlpath, lex_mi->ssl_crlpath);
#ifndef HAVE_OPENSSL
if (lex_mi->ssl || lex_mi->ssl_ca || lex_mi->ssl_capath ||
@@ -3180,10 +3226,8 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
need_relay_log_purge= 0;
char relay_log_name[FN_REFLEN];
mi->rli.relay_log.make_log_name(relay_log_name, lex_mi->relay_log_name);
- strmake(mi->rli.group_relay_log_name, relay_log_name,
- sizeof(mi->rli.group_relay_log_name)-1);
- strmake(mi->rli.event_relay_log_name, relay_log_name,
- sizeof(mi->rli.event_relay_log_name)-1);
+ strmake_buf(mi->rli.group_relay_log_name, relay_log_name);
+ strmake_buf(mi->rli.event_relay_log_name, relay_log_name);
}
if (lex_mi->relay_log_pos)
@@ -3228,8 +3272,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
*/
mi->master_log_pos = max(BIN_LOG_HEADER_SIZE,
mi->rli.group_master_log_pos);
- strmake(mi->master_log_name, mi->rli.group_master_log_name,
- sizeof(mi->master_log_name)-1);
+ strmake_buf(mi->master_log_name, mi->rli.group_master_log_name);
}
/*
@@ -3293,8 +3336,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
*/
mi->rli.group_master_log_pos= mi->master_log_pos;
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
- strmake(mi->rli.group_master_log_name,mi->master_log_name,
- sizeof(mi->rli.group_master_log_name)-1);
+ strmake_buf(mi->rli.group_master_log_name,mi->master_log_name);
if (!mi->rli.group_master_log_name[0]) // uninitialized case
mi->rli.group_master_log_pos=0;
@@ -3312,6 +3354,11 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
"master_log_pos='%ld'.", saved_host, saved_port, saved_log_name,
(ulong) saved_log_pos, mi->host, mi->port, mi->master_log_name,
(ulong) mi->master_log_pos);
+ if (saved_using_gtid != Master_info::USE_GTID_NO ||
+ mi->using_gtid != Master_info::USE_GTID_NO)
+ sql_print_information("Previous Using_Gtid=%s. New Using_Gtid=%s",
+ mi->using_gtid_astext(saved_using_gtid),
+ mi->using_gtid_astext(mi->using_gtid));
/*
If we don't write new coordinates to disk now, then old will remain in
@@ -3773,11 +3820,11 @@ rpl_deinit_gtid_slave_state()
/*
- Format the current GTID state as a string, for use when connecting to a
- master server with GTID, or for returning the value of @@global.gtid_state.
+ Format the current GTID state as a string, for returning the value of
+ @@global.gtid_slave_pos.
If the flag use_binlog is true, then the contents of the binary log (if
- enabled) is merged into the current GTID state.
+ enabled) is merged into the current GTID state (@@global.gtid_current_pos).
*/
int
rpl_append_gtid_state(String *dest, bool use_binlog)
@@ -3790,10 +3837,35 @@ rpl_append_gtid_state(String *dest, bool use_binlog)
(err= mysql_bin_log.get_most_recent_gtid_list(&gtid_list, &num_gtids)))
return err;
- rpl_global_gtid_slave_state.tostring(dest, gtid_list, num_gtids);
+ err= rpl_global_gtid_slave_state.tostring(dest, gtid_list, num_gtids);
my_free(gtid_list);
- return 0;
+ return err;
+}
+
+
+/*
+ Load the current GITD position into a slave_connection_state, for use when
+ connecting to a master server with GTID.
+
+ If the flag use_binlog is true, then the contents of the binary log (if
+ enabled) is merged into the current GTID state (master_use_gtid=current_pos).
+*/
+int
+rpl_load_gtid_state(slave_connection_state *state, bool use_binlog)
+{
+ int err;
+ rpl_gtid *gtid_list= NULL;
+ uint32 num_gtids= 0;
+
+ if (use_binlog && opt_bin_log &&
+ (err= mysql_bin_log.get_most_recent_gtid_list(&gtid_list, &num_gtids)))
+ return err;
+
+ err= state->load(&rpl_global_gtid_slave_state, gtid_list, num_gtids);
+ my_free(gtid_list);
+
+ return err;
}
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index 820ffed0928..917da9b598e 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -32,6 +32,8 @@ typedef struct st_slave_info
THD* thd;
} SLAVE_INFO;
+struct slave_connection_state;
+
extern my_bool opt_show_slave_auth_info;
extern char *master_host, *master_info_file;
extern bool server_id_supplied;
@@ -70,6 +72,7 @@ void rpl_init_gtid_slave_state();
void rpl_deinit_gtid_slave_state();
int gtid_state_from_binlog_pos(const char *name, uint32 pos, String *out_str);
int rpl_append_gtid_state(String *dest, bool use_binlog);
+int rpl_load_gtid_state(slave_connection_state *state, bool use_binlog);
bool rpl_gtid_pos_check(THD *thd, char *str, size_t len);
bool rpl_gtid_pos_update(THD *thd, char *str, size_t len);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index c19c7aa95a7..806017b9ae5 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -2495,7 +2495,12 @@ void JOIN::exec_inner()
List<Item> *curr_all_fields= &all_fields;
List<Item> *curr_fields_list= &fields_list;
TABLE *curr_tmp_table= 0;
- bool tmp_having_used_tables_updated= FALSE;
+ /*
+ curr_join->join_free() will call JOIN::cleanup(full=TRUE). It will not
+ be safe to call update_used_tables() after that.
+ */
+ if (curr_join->tmp_having)
+ curr_join->tmp_having->update_used_tables();
/*
Initialize examined rows here because the values from all join parts
@@ -2751,16 +2756,6 @@ void JOIN::exec_inner()
curr_join->select_distinct=0; /* Each row is unique */
- /*
- curr_join->join_free() will call JOIN::cleanup(full=TRUE). It will not
- be safe to call update_used_tables() after that.
- */
- if (curr_join->tmp_having)
- {
- curr_join->tmp_having->update_used_tables();
- tmp_having_used_tables_updated= TRUE;
- }
-
curr_join->join_free(); /* Free quick selects */
if (curr_join->select_distinct && ! curr_join->group_list)
@@ -2841,9 +2836,6 @@ void JOIN::exec_inner()
if (curr_join->tmp_having && ! curr_join->group_list &&
! curr_join->sort_and_group)
{
- // Some tables may have been const
- if (!tmp_having_used_tables_updated)
- curr_join->tmp_having->update_used_tables();
JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables];
table_map used_tables= (curr_join->const_table_map |
curr_table->table->map);
@@ -3692,9 +3684,16 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
!table->fulltext_searched &&
(!embedding || (embedding->sj_on_expr && !embedding->embedding)))
{
+ key_map base_part, base_const_ref, base_eq_part;
+ base_part.set_prefix(keyinfo->key_parts);
+ base_const_ref= const_ref;
+ base_const_ref.intersect(base_part);
+ base_eq_part= eq_part;
+ base_eq_part.intersect(base_part);
if (table->actual_key_flags(keyinfo) & HA_NOSAME)
{
- if (const_ref == eq_part &&
+
+ if (base_const_ref == base_eq_part &&
!has_expensive_keyparts &&
!((outer_join & table->map) &&
(*s->on_expr_ref)->is_expensive()))
@@ -3720,7 +3719,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
else
found_ref|= refs; // Table is const if all refs are const
}
- else if (const_ref == eq_part)
+ else if (base_const_ref == base_eq_part)
s->const_keys.set_bit(key);
}
}
@@ -11414,7 +11413,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
*simple_order=0; // Must do a temp table to sort
else if (!(order_tables & not_const_tables))
{
- if (order->item[0]->with_subselect)
+ if (order->item[0]->has_subquery())
{
/*
Delay the evaluation of constant ORDER and/or GROUP expressions that
@@ -13734,13 +13733,13 @@ static void restore_prev_nj_state(JOIN_TAB *last)
bool was_fully_covered= nest->is_fully_covered();
+ join->cur_embedding_map|= nest->nj_map;
+
if (--nest->counter == 0)
join->cur_embedding_map&= ~nest->nj_map;
if (!was_fully_covered)
break;
-
- join->cur_embedding_map|= nest->nj_map;
}
}
}
@@ -13960,7 +13959,27 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
if (new_item_and_list->is_empty())
li.remove();
else
+ {
+ Item *list_item;
+ Item *new_list_item;
+ uint cnt= new_item_and_list->elements;
+ List_iterator<Item> it(*new_item_and_list);
+ while ((list_item= it++))
+ {
+ uchar* is_subst_valid= (uchar *) Item::ANY_SUBST;
+ new_list_item=
+ list_item->compile(&Item::subst_argument_checker,
+ &is_subst_valid,
+ &Item::equal_fields_propagator,
+ (uchar *) &cond_and->cond_equal);
+ if (new_list_item != list_item)
+ it.replace(new_list_item);
+ new_list_item->update_used_tables();
+ }
li.replace(*new_item_and_list);
+ for (cnt--; cnt; cnt--)
+ item= li++;
+ }
cond_and_list->concat((List<Item>*) cond_equal_items);
}
else if (new_item->type() == Item::FUNC_ITEM &&
@@ -13980,7 +13999,13 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
if (new_item->type() == Item::COND_ITEM &&
((Item_cond*) new_item)->functype() ==
((Item_cond*) cond)->functype())
- li.replace(*((Item_cond*) new_item)->argument_list());
+ {
+ List<Item> *arg_list= ((Item_cond*) new_item)->argument_list();
+ uint cnt= arg_list->elements;
+ li.replace(*arg_list);
+ for ( cnt--; cnt; cnt--)
+ item= li++;
+ }
else
li.replace(new_item);
}
@@ -19326,7 +19351,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
!(table->file->index_flags(best_key, 0, 1) & HA_CLUSTERED_INDEX)))
goto use_filesort;
- if (table->quick_keys.is_set(best_key) && best_key != ref_key)
+ if (select &&
+ table->quick_keys.is_set(best_key) && best_key != ref_key)
{
key_map map;
map.clear_all(); // Force the creation of quick select
@@ -19904,7 +19930,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
}
if (copy_blobs(first_field))
{
- my_message(ER_OUTOFMEMORY, ER(ER_OUTOFMEMORY), MYF(0));
+ my_message(ER_OUTOFMEMORY, ER(ER_OUTOFMEMORY), MYF(ME_FATALERROR));
error=0;
goto err;
}
@@ -19937,7 +19963,8 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
if (!found)
break; // End of file
/* Restart search on saved row */
- error=file->restart_rnd_next(record);
+ if ((error= file->restart_rnd_next(record)))
+ goto err;
}
file->extra(HA_EXTRA_NO_CACHE);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index ba36c0fc63f..cdb2a117507 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
@@ -1561,6 +1561,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
MODE_MYSQL323 |
MODE_MYSQL40)) != 0;
my_bitmap_map *old_map;
+ int error= 0;
DBUG_ENTER("store_create_info");
DBUG_PRINT("enter",("table: %s", table->s->table_name.str));
@@ -1924,28 +1925,35 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
- /*
- Partition syntax for CREATE TABLE is at the end of the syntax.
- */
- uint part_syntax_len;
- char *part_syntax;
if (table->part_info &&
- (!table->part_info->is_auto_partitioned) &&
- ((part_syntax= generate_partition_syntax(table->part_info,
+ !((table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) &&
+ table->part_info->is_auto_partitioned))
+ {
+ /*
+ Partition syntax for CREATE TABLE is at the end of the syntax.
+ */
+ uint part_syntax_len;
+ char *part_syntax;
+ String comment_start;
+ table->part_info->set_show_version_string(&comment_start);
+ if ((part_syntax= generate_partition_syntax(table->part_info,
&part_syntax_len,
FALSE,
show_table_options,
- NULL, NULL))))
- {
- table->part_info->set_show_version_string(packet);
- packet->append(part_syntax, part_syntax_len);
- packet->append(STRING_WITH_LEN(" */"));
- my_free(part_syntax);
+ NULL, NULL,
+ comment_start.c_ptr())))
+ {
+ packet->append(comment_start);
+ if (packet->append(part_syntax, part_syntax_len) ||
+ packet->append(STRING_WITH_LEN(" */")))
+ error= 1;
+ my_free(part_syntax);
+ }
}
}
#endif
tmp_restore_column_map(table->read_set, old_map);
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
@@ -2185,7 +2193,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
DBUG_ENTER("mysqld_list_processes");
field_list.push_back(new Item_int("Id", 0, MY_INT32_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_empty_string("User",16));
+ field_list.push_back(new Item_empty_string("User", USERNAME_CHAR_LENGTH));
field_list.push_back(new Item_empty_string("Host",LIST_PROCESS_HOST_LEN));
field_list.push_back(field=new Item_empty_string("db",NAME_CHAR_LEN));
field->maybe_null=1;
@@ -7251,7 +7259,7 @@ struct schema_table_ref
ST_FIELD_INFO user_stats_fields_info[]=
{
- {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
+ {"USER", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
{"TOTAL_CONNECTIONS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE},
{"CONCURRENT_CONNECTIONS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE},
{"CONNECTED_TIME", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE},
@@ -8622,7 +8630,8 @@ ST_FIELD_INFO variables_fields_info[]=
ST_FIELD_INFO processlist_fields_info[]=
{
{"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE},
- {"USER", 16, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
+ {"USER", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User",
+ SKIP_OPEN_TABLE},
{"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host",
SKIP_OPEN_TABLE},
{"DB", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db", SKIP_OPEN_TABLE},
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 21748861204..f1cb5e07eca 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -758,7 +758,7 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
{
if (from->Alloced_length >= from_length)
return from;
- if (from->alloced || !to || from == to)
+ if ((from->alloced && (from->Alloced_length != 0)) || !to || from == to)
{
(void) from->realloc(from_length);
return from;
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 2a5f43b35f2..4ce08a5d9e6 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -378,6 +378,16 @@ public:
}
return 0;
}
+ bool append_hex(const char *src, uint32 srclen)
+ {
+ for (const char *end= src + srclen ; src != end ; src++)
+ {
+ if (append(_dig_vec_lower[((uchar) *src) >> 4]) ||
+ append(_dig_vec_lower[((uchar) *src) & 0x0F]))
+ return true;
+ }
+ return false;
+ }
bool fill(uint32 max_length,char fill);
void strip_sp();
friend int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 60fd630001a..f56eedf4c42 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2010, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -53,6 +53,7 @@
#include "sql_parse.h"
#include "sql_show.h"
#include "transaction.h"
+#include "sql_audit.h"
#ifdef __WIN__
#include <io.h>
@@ -1690,7 +1691,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
&syntax_len,
TRUE, TRUE,
lpt->create_info,
- lpt->alter_info)))
+ lpt->alter_info,
+ NULL)))
{
DBUG_RETURN(TRUE);
}
@@ -1793,7 +1795,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
&syntax_len,
TRUE, TRUE,
lpt->create_info,
- lpt->alter_info)))
+ lpt->alter_info,
+ NULL)))
{
error= 1;
goto err;
@@ -2379,6 +2382,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
PSI_CALL_drop_table_share(table->internal_tmp_table,
table->db, table->db_length,
table->table_name, table->table_name_length);
+ mysql_audit_drop_table(thd, table);
}
DBUG_PRINT("table", ("table: 0x%lx s: 0x%lx", (long) table->table,
@@ -4206,7 +4210,8 @@ handler *mysql_create_frm_image(THD *thd,
&syntax_len,
TRUE, TRUE,
create_info,
- alter_info)))
+ alter_info,
+ NULL)))
goto err;
part_info->part_info_string= part_syntax_buf;
part_info->part_info_len= syntax_len;
@@ -4688,6 +4693,8 @@ mysql_rename_table(handlerton *base, const char *old_db,
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
else if (error)
my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
+ else if (!(flags & FN_IS_TMP))
+ mysql_audit_rename_table(thd, old_db, old_name, new_db, new_name);
/*
Remove the old table share from the pfs table share array. The new table
@@ -6355,6 +6362,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
mysql_ha_rm_tables(thd, table_list);
+ mysql_audit_alter_table(thd, table_list);
+
/* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
if (alter_info->tablespace_op != NO_TABLESPACE_OP)
/* Conditionally writes to binlog. */
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 5b3286d77b0..93b35b4918f 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -629,7 +629,7 @@ Max used alarms: %u\n\
Next alarm time: %lu\n",
alarm_info.active_alarms,
alarm_info.max_used_alarms,
- alarm_info.next_alarm_time);
+ (ulong)alarm_info.next_alarm_time);
#endif
display_table_locks();
fflush(stdout);
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 5b5bd0ed2fc..63df46c7094 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1152,9 +1152,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
TODO: when VIEWs will be stored in cache, table mem_root should
be used here
*/
- if (parser->parse((uchar*)table, thd->mem_root, view_parameters,
- required_view_parameters, &file_parser_dummy_hook))
- goto err;
+ if ((result= parser->parse((uchar*)table, thd->mem_root,
+ view_parameters, required_view_parameters,
+ &file_parser_dummy_hook)))
+ goto end;
/*
check old format view .frm
@@ -1217,6 +1218,11 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
now Lex placed in statement memory
*/
table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
+ if (!table->view)
+ {
+ result= true;
+ goto end;
+ }
{
char old_db_buf[SAFE_NAME_LEN+1];
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 6be7c23fb0d..83b5692d606 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2010, 2011 Monty Program Ab
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2011, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1133,6 +1133,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token HAVING /* SQL-2003-R */
%token HELP_SYM
%token HEX_NUM
+%token HEX_STRING
%token HIGH_PRIORITY
%token HOST_SYM
%token HOSTS_SYM
@@ -1566,7 +1567,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%left INTERVAL_SYM
%type <lex_str>
- IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM
+ IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
+ HEX_NUM HEX_STRING hex_num_or_string
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
NCHAR_STRING opt_component key_cache_name
@@ -4591,7 +4593,7 @@ partition:
;
part_type_def:
- opt_linear KEY_SYM '(' part_field_list ')'
+ opt_linear KEY_SYM opt_key_algo '(' part_field_list ')'
{
partition_info *part_info= Lex->part_info;
part_info->list_of_part_fields= TRUE;
@@ -4617,6 +4619,25 @@ opt_linear:
{ Lex->part_info->linear_hash_ind= TRUE;}
;
+opt_key_algo:
+ /* empty */
+ { Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_NONE;}
+ | ALGORITHM_SYM EQ real_ulong_num
+ {
+ switch ($3) {
+ case 1:
+ Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_51;
+ break;
+ case 2:
+ Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_55;
+ break;
+ default:
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
part_field_list:
/* empty */ {}
| part_field_item_list {}
@@ -4698,7 +4719,7 @@ opt_sub_part:
| SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func
{ Lex->part_info->subpart_type= HASH_PARTITION; }
opt_num_subparts {}
- | SUBPARTITION_SYM BY opt_linear KEY_SYM
+ | SUBPARTITION_SYM BY opt_linear KEY_SYM opt_key_algo
'(' sub_part_field_list ')'
{
partition_info *part_info= Lex->part_info;
@@ -6159,6 +6180,11 @@ now_or_signed_literal:
{ $$=$1; }
;
+hex_num_or_string:
+ HEX_NUM {}
+ | HEX_STRING {}
+ ;
+
charset:
CHAR_SYM SET {}
| CHARSET {}
@@ -12638,7 +12664,7 @@ text_string:
}
| HEX_NUM
{
- Item *tmp= new (YYTHD->mem_root) Item_hex_string($1.str, $1.length);
+ Item *tmp= new (YYTHD->mem_root) Item_hex_hybrid($1.str, $1.length);
if (tmp == NULL)
MYSQL_YYABORT;
/*
@@ -12648,6 +12674,14 @@ text_string:
tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
+ | HEX_STRING
+ {
+ Item *tmp= new (YYTHD->mem_root) Item_hex_string($1.str, $1.length);
+ if (tmp == NULL)
+ MYSQL_YYABORT;
+ tmp->quick_fix_field();
+ $$= tmp->val_str((String*) 0);
+ }
| BIN_NUM
{
Item *tmp= new (YYTHD->mem_root) Item_bin_string($1.str, $1.length);
@@ -12717,6 +12751,12 @@ literal:
}
| HEX_NUM
{
+ $$ = new (YYTHD->mem_root) Item_hex_hybrid($1.str, $1.length);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | HEX_STRING
+ {
$$ = new (YYTHD->mem_root) Item_hex_string($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
@@ -12727,7 +12767,7 @@ literal:
if ($$ == NULL)
MYSQL_YYABORT;
}
- | UNDERSCORE_CHARSET HEX_NUM
+ | UNDERSCORE_CHARSET hex_num_or_string
{
Item *tmp= new (YYTHD->mem_root) Item_hex_string($2.str, $2.length);
if (tmp == NULL)
@@ -13283,7 +13323,7 @@ user:
$$->auth= empty_lex_str;
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
- USERNAME_CHAR_LENGTH,
+ username_char_length,
system_charset_info, 0))
MYSQL_YYABORT;
}
@@ -13298,7 +13338,7 @@ user:
$$->auth= empty_lex_str;
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
- USERNAME_CHAR_LENGTH,
+ username_char_length,
system_charset_info, 0) ||
check_host_name(&$$->host))
MYSQL_YYABORT;
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 0b543b8218a..f7d66032ad8 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1392,6 +1392,12 @@ Sys_var_gtid_binlog_pos::global_value_ptr(THD *thd, LEX_STRING *base)
String str(buf, sizeof(buf), system_charset_info);
char *p;
+ if (!rpl_global_gtid_slave_state.loaded)
+ {
+ my_error(ER_CANNOT_LOAD_SLAVE_GTID_STATE, MYF(0), "mysql",
+ rpl_gtid_slave_state_table_name.str);
+ return NULL;
+ }
str.length(0);
if ((opt_bin_log && mysql_bin_log.append_state_pos(&str)) ||
!(p= thd->strmake(str.ptr(), str.length())))
@@ -1438,6 +1444,14 @@ Sys_var_gtid_slave_pos::do_check(THD *thd, set_var *var)
bool running;
DBUG_ASSERT(var->type == OPT_GLOBAL);
+
+ if (!rpl_global_gtid_slave_state.loaded)
+ {
+ my_error(ER_CANNOT_LOAD_SLAVE_GTID_STATE, MYF(0), "mysql",
+ rpl_gtid_slave_state_table_name.str);
+ return true;
+ }
+
mysql_mutex_lock(&LOCK_active_mi);
running= master_info_index->give_error_if_slave_running();
mysql_mutex_unlock(&LOCK_active_mi);
@@ -1496,6 +1510,13 @@ Sys_var_gtid_slave_pos::global_value_ptr(THD *thd, LEX_STRING *base)
String str;
char *p;
+ if (!rpl_global_gtid_slave_state.loaded)
+ {
+ my_error(ER_CANNOT_LOAD_SLAVE_GTID_STATE, MYF(0), "mysql",
+ rpl_gtid_slave_state_table_name.str);
+ return NULL;
+ }
+
str.length(0);
if (rpl_append_gtid_state(&str, false) ||
!(p= thd->strmake(str.ptr(), str.length())))
diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc
index 69df00a2c9a..658a04d877e 100644
--- a/sql/threadpool_unix.cc
+++ b/sql/threadpool_unix.cc
@@ -286,6 +286,20 @@ static void *native_event_get_userdata(native_event *event)
}
#elif defined(HAVE_KQUEUE)
+
+/*
+ NetBSD is incompatible with other BSDs , last parameter in EV_SET macro
+ (udata, user data) needs to be intptr_t, whereas it needs to be void*
+ everywhere else.
+*/
+
+#ifdef __NetBSD__
+#define MY_EV_SET(a, b, c, d, e, f, g) EV_SET(a, b, c, d, e, f, (intptr_t)g)
+#else
+#define MY_EV_SET(a, b, c, d, e, f, g) EV_SET(a, b, c, d, e, f, g)
+#endif
+
+
int io_poll_create()
{
return kqueue();
@@ -294,7 +308,7 @@ int io_poll_create()
int io_poll_start_read(int pollfd, int fd, void *data)
{
struct kevent ke;
- EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
+ MY_EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
0, 0, data);
return kevent(pollfd, &ke, 1, 0, 0, 0);
}
@@ -303,7 +317,7 @@ int io_poll_start_read(int pollfd, int fd, void *data)
int io_poll_associate_fd(int pollfd, int fd, void *data)
{
struct kevent ke;
- EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
+ MY_EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
0, 0, data);
return io_poll_start_read(pollfd,fd, data);
}
@@ -312,7 +326,7 @@ int io_poll_associate_fd(int pollfd, int fd, void *data)
int io_poll_disassociate_fd(int pollfd, int fd)
{
struct kevent ke;
- EV_SET(&ke,fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
+ MY_EV_SET(&ke,fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
return kevent(pollfd, &ke, 1, 0, 0, 0);
}
@@ -337,7 +351,7 @@ int io_poll_wait(int pollfd, struct kevent *events, int maxevents, int timeout_m
static void* native_event_get_userdata(native_event *event)
{
- return event->udata;
+ return (void *)event->udata;
}
#elif defined (__sun)
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 5e1e8bec7b3..f5e9182522e 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -2559,7 +2559,7 @@ main(int argc, char **argv)
if (argc == 2)
{
- root_name_end= strmake(fullname, argv[1], FN_REFLEN);
+ root_name_end= strmake_buf(fullname, argv[1]);
printf("TRUNCATE TABLE time_zone;\n");
printf("TRUNCATE TABLE time_zone_name;\n");
@@ -2713,7 +2713,7 @@ main(int argc, char **argv)
(int)t, (int)t1);
/* Let us load time zone description */
- str_end= strmake(fullname, TZDIR, FN_REFLEN);
+ str_end= strmake_buf(fullname, TZDIR);
strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
if (tz_load(fullname, &tz_info, &tz_storage))