summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorTatiana A. Nurnberg <azundris@mysql.com>2009-07-31 21:58:40 +0200
committerTatiana A. Nurnberg <azundris@mysql.com>2009-07-31 21:58:40 +0200
commite26350e000e1967592030270b6cd90d470043ce1 (patch)
tree9ef8cb8d8fe75f8505b899addc69d86dbdfa56eb /sql
parent717d6054f516baa35e14ac11f0a06c630b9e9fd9 (diff)
parent4e95179af9c2f2589093ea9f94472d8d047892c7 (diff)
downloadmariadb-git-e26350e000e1967592030270b6cd90d470043ce1.tar.gz
auto-merge
Diffstat (limited to 'sql')
-rw-r--r--sql/event_data_objects.cc20
-rw-r--r--sql/event_scheduler.cc7
-rw-r--r--sql/events.cc49
-rw-r--r--sql/field.cc39
-rw-r--r--sql/ha_ndbcluster_binlog.cc6
-rw-r--r--sql/ha_partition.cc51
-rw-r--r--sql/handler.cc33
-rw-r--r--sql/handler.h11
-rw-r--r--sql/item.cc1
-rw-r--r--sql/item_func.cc7
-rw-r--r--sql/item_func.h1
-rw-r--r--sql/item_subselect.cc4
-rw-r--r--sql/item_sum.cc5
-rw-r--r--sql/lex.h2
-rw-r--r--sql/log.cc50
-rw-r--r--sql/log_event.cc16
-rw-r--r--sql/mysql_priv.h1
-rw-r--r--sql/mysqld.cc38
-rw-r--r--sql/net_serv.cc13
-rw-r--r--sql/protocol.cc130
-rw-r--r--sql/protocol.h2
-rw-r--r--sql/rpl_rli.cc1
-rw-r--r--sql/share/errmsg.txt5
-rw-r--r--sql/slave.cc218
-rw-r--r--sql/sp.cc3
-rw-r--r--sql/sp.h3
-rw-r--r--sql/sp_head.cc10
-rw-r--r--sql/sql_base.cc8
-rw-r--r--sql/sql_class.cc94
-rw-r--r--sql/sql_class.h57
-rw-r--r--sql/sql_delete.cc10
-rw-r--r--sql/sql_handler.cc29
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_load.cc11
-rw-r--r--sql/sql_parse.cc51
-rw-r--r--sql/sql_prepare.cc43
-rw-r--r--sql/sql_repl.cc4
-rw-r--r--sql/sql_select.cc39
-rw-r--r--sql/sql_select.h3
-rw-r--r--sql/sql_show.cc35
-rw-r--r--sql/sql_string.cc73
-rw-r--r--sql/sql_string.h16
-rw-r--r--sql/sql_table.cc77
-rw-r--r--sql/sql_update.cc11
-rw-r--r--sql/sql_yacc.yy49
-rw-r--r--sql/table.cc9
-rw-r--r--sql/table.h2
47 files changed, 954 insertions, 395 deletions
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index 0eba357b632..dba32cac6b2 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -1430,13 +1430,7 @@ Event_job_data::execute(THD *thd, bool drop)
thd->variables.sql_mode= sql_mode;
thd->variables.time_zone= time_zone;
- /*
- Peculiar initialization order is a crutch to avoid races in SHOW
- PROCESSLIST which reads thd->{query/query_length} without a mutex.
- */
- thd->query_length= 0;
- thd->query= sp_sql.c_ptr_safe();
- thd->query_length= sp_sql.length();
+ thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
{
Parser_state parser_state(thd, thd->query, thd->query_length);
@@ -1497,13 +1491,8 @@ end_no_lex_start:
else
{
ulong saved_master_access;
- /*
- Peculiar initialization order is a crutch to avoid races in SHOW
- PROCESSLIST which reads thd->{query/query_length} without a mutex.
- */
- thd->query_length= 0;
- thd->query= sp_sql.c_ptr_safe();
- thd->query_length= sp_sql.length();
+
+ thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
/*
NOTE: even if we run in read-only mode, we should be able to lock
@@ -1528,8 +1517,7 @@ end_no_lex_start:
thd->end_statement();
thd->cleanup_after_query();
/* Avoid races with SHOW PROCESSLIST */
- thd->query_length= 0;
- thd->query= NULL;
+ thd->set_query(NULL, 0);
DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index d9d010783e8..8c0025f9ed4 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -158,6 +158,7 @@ deinit_event_thread(THD *thd)
thread_count--;
thread_running--;
delete thd;
+ pthread_cond_broadcast(&COND_thread_count);
pthread_mutex_unlock(&LOCK_thread_count);
}
@@ -418,6 +419,7 @@ Event_scheduler::start()
thread_count--;
thread_running--;
delete new_thd;
+ pthread_cond_broadcast(&COND_thread_count);
pthread_mutex_unlock(&LOCK_thread_count);
}
end:
@@ -550,6 +552,7 @@ error:
thread_count--;
thread_running--;
delete new_thd;
+ pthread_cond_broadcast(&COND_thread_count);
pthread_mutex_unlock(&LOCK_thread_count);
}
delete event_name;
@@ -625,13 +628,13 @@ Event_scheduler::stop()
DBUG_PRINT("info", ("Scheduler thread has id %lu",
scheduler_thd->thread_id));
/* Lock from delete */
- pthread_mutex_lock(&scheduler_thd->LOCK_delete);
+ pthread_mutex_lock(&scheduler_thd->LOCK_thd_data);
/* This will wake up the thread if it waits on Queue's conditional */
sql_print_information("Event Scheduler: Killing the scheduler thread, "
"thread id %lu",
scheduler_thd->thread_id);
scheduler_thd->awake(THD::KILL_CONNECTION);
- pthread_mutex_unlock(&scheduler_thd->LOCK_delete);
+ pthread_mutex_unlock(&scheduler_thd->LOCK_thd_data);
/* thd could be 0x0, when shutting down */
sql_print_information("Event Scheduler: "
diff --git a/sql/events.cc b/sql/events.cc
index ea935e67bd3..10edfff2402 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -852,22 +852,23 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
}
-/*
- Inits the scheduler's structures.
+/**
+ Initializes the scheduler's structures.
- SYNOPSIS
- Events::init()
+ @param opt_noacl_or_bootstrap
+ TRUE if there is --skip-grant-tables or --bootstrap
+ option. In that case we disable the event scheduler.
- NOTES
- This function is not synchronized.
+ @note This function is not synchronized.
- RETURN VALUE
- FALSE OK
- TRUE Error in case the scheduler can't start
+ @retval FALSE Perhaps there was an error, and the event scheduler
+ is disabled. But the error is not fatal and the
+ server start up can continue.
+ @retval TRUE Fatal error. Startup must terminate (call unireg_abort()).
*/
bool
-Events::init(my_bool opt_noacl)
+Events::init(my_bool opt_noacl_or_bootstrap)
{
THD *thd;
@@ -875,11 +876,6 @@ Events::init(my_bool opt_noacl)
DBUG_ENTER("Events::init");
- /* Disable the scheduler if running with --skip-grant-tables */
- if (opt_noacl)
- opt_event_scheduler= EVENTS_DISABLED;
-
-
/* We need a temporary THD during boot */
if (!(thd= new THD()))
{
@@ -908,23 +904,30 @@ Events::init(my_bool opt_noacl)
/*
Since we allow event DDL even if the scheduler is disabled,
check the system tables, as we might need them.
+
+ If run with --skip-grant-tables or --bootstrap, don't try to do the
+ check of system tables and don't complain: in these modes the tables
+ are most likely not there and we're going to disable the event
+ scheduler anyway.
*/
- if (Event_db_repository::check_system_tables(thd))
+ if (opt_noacl_or_bootstrap || Event_db_repository::check_system_tables(thd))
{
- sql_print_error("Event Scheduler: An error occurred when initializing "
- "system tables.%s",
- opt_event_scheduler == EVENTS_DISABLED ?
- "" : " Disabling the Event Scheduler.");
+ if (! opt_noacl_or_bootstrap)
+ {
+ sql_print_error("Event Scheduler: An error occurred when initializing "
+ "system tables. Disabling the Event Scheduler.");
+ check_system_tables_error= TRUE;
+ }
/* Disable the scheduler since the system tables are not up to date */
opt_event_scheduler= EVENTS_DISABLED;
- check_system_tables_error= TRUE;
goto end;
}
/*
Was disabled explicitly from the command line, or because we're running
- with --skip-grant-tables, or because we have no system tables.
+ with --skip-grant-tables, or --bootstrap, or because we have no system
+ tables.
*/
if (opt_event_scheduler == Events::EVENTS_DISABLED)
goto end;
@@ -941,7 +944,7 @@ Events::init(my_bool opt_noacl)
}
if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
- opt_event_scheduler == EVENTS_ON && scheduler->start())
+ (opt_event_scheduler == EVENTS_ON && scheduler->start()))
{
sql_print_error("Event Scheduler: Error while loading from disk.");
res= TRUE; /* fatal error: request unireg_abort */
diff --git a/sql/field.cc b/sql/field.cc
index ed085de1db3..452dfc3ae55 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -6271,48 +6271,15 @@ check_string_copy_error(Field_str *field,
const char *end,
CHARSET_INFO *cs)
{
- const char *pos, *end_orig;
- char tmp[64], *t;
+ const char *pos;
+ char tmp[32];
if (!(pos= well_formed_error_pos) &&
!(pos= cannot_convert_error_pos))
return FALSE;
- end_orig= end;
- set_if_smaller(end, pos + 6);
+ convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
- for (t= tmp; pos < end; pos++)
- {
- /*
- If the source string is ASCII compatible (mbminlen==1)
- and the source character is in ASCII printable range (0x20..0x7F),
- then display the character as is.
-
- Otherwise, if the source string is not ASCII compatible (e.g. UCS2),
- or the source character is not in the printable range,
- then print the character using HEX notation.
- */
- if (((unsigned char) *pos) >= 0x20 &&
- ((unsigned char) *pos) <= 0x7F &&
- cs->mbminlen == 1)
- {
- *t++= *pos;
- }
- else
- {
- *t++= '\\';
- *t++= 'x';
- *t++= _dig_vec_upper[((unsigned char) *pos) >> 4];
- *t++= _dig_vec_upper[((unsigned char) *pos) & 15];
- }
- }
- if (end_orig > end)
- {
- *t++= '.';
- *t++= '.';
- *t++= '.';
- }
- *t= '\0';
push_warning_printf(field->table->in_use,
field->table->in_use->abort_on_warning ?
MYSQL_ERROR::WARN_LEVEL_ERROR :
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index f705af8bf47..a3adbe4a952 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -253,8 +253,7 @@ static void run_query(THD *thd, char *buf, char *end,
const char* found_semicolon= NULL;
bzero((char*) &thd->net, sizeof(NET));
- thd->query_length= end - buf;
- thd->query= buf;
+ thd->set_query(buf, (uint) (end - buf));
thd->variables.pseudo_thread_id= thread_id;
thd->transaction.stmt.modified_non_trans_table= FALSE;
if (disable_binlog)
@@ -297,8 +296,7 @@ static void run_query(THD *thd, char *buf, char *end,
thd->main_da.reset_diagnostics_area();
thd->options= save_thd_options;
- thd->query_length= save_thd_query_length;
- thd->query= save_thd_query;
+ thd->set_query(save_thd_query, save_thd_query_length);
thd->variables.pseudo_thread_id= save_thread_id;
thd->status_var= save_thd_status_var;
thd->transaction.all= save_thd_transaction_all;
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 97ecd70d6f8..562716a7db7 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -423,12 +423,9 @@ bool ha_partition::initialize_partition(MEM_ROOT *mem_root)
int ha_partition::delete_table(const char *name)
{
- int error;
DBUG_ENTER("ha_partition::delete_table");
- if ((error= del_ren_cre_table(name, NULL, NULL, NULL)))
- DBUG_RETURN(error);
- DBUG_RETURN(handler::delete_table(name));
+ DBUG_RETURN(del_ren_cre_table(name, NULL, NULL, NULL));
}
@@ -456,12 +453,9 @@ int ha_partition::delete_table(const char *name)
int ha_partition::rename_table(const char *from, const char *to)
{
- int error;
DBUG_ENTER("ha_partition::rename_table");
- if ((error= del_ren_cre_table(from, to, NULL, NULL)))
- DBUG_RETURN(error);
- DBUG_RETURN(handler::rename_table(from, to));
+ DBUG_RETURN(del_ren_cre_table(from, to, NULL, NULL));
}
@@ -1807,6 +1801,15 @@ uint ha_partition::del_ren_cre_table(const char *from,
DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to));
name_buffer_ptr= m_name_buffer_ptr;
file= m_file;
+ if (to == NULL && table_arg == NULL)
+ {
+ /*
+ Delete table, start by delete the .par file. If error, break, otherwise
+ delete as much as possible.
+ */
+ if ((error= handler::delete_table(from)))
+ DBUG_RETURN(error);
+ }
/*
Since ha_partition has HA_FILE_BASED, it must alter underlying table names
if they do not have HA_FILE_BASED and lower_case_table_names == 2.
@@ -1828,6 +1831,8 @@ uint ha_partition::del_ren_cre_table(const char *from,
create_partition_name(to_buff, to_path, name_buffer_ptr,
NORMAL_PART_NAME, FALSE);
error= (*file)->ha_rename_table(from_buff, to_buff);
+ if (error)
+ goto rename_error;
}
else if (table_arg == NULL) // delete branch
error= (*file)->ha_delete_table(from_buff);
@@ -1843,6 +1848,15 @@ uint ha_partition::del_ren_cre_table(const char *from,
save_error= error;
i++;
} while (*(++file));
+ if (to != NULL)
+ {
+ if ((error= handler::rename_table(from, to)))
+ {
+ /* Try to revert everything, ignore errors */
+ (void) handler::rename_table(to, from);
+ goto rename_error;
+ }
+ }
DBUG_RETURN(save_error);
create_error:
name_buffer_ptr= m_name_buffer_ptr;
@@ -1850,7 +1864,21 @@ create_error:
{
create_partition_name(from_buff, from_path, name_buffer_ptr, NORMAL_PART_NAME,
FALSE);
- VOID((*file)->ha_delete_table((const char*) from_buff));
+ (void) (*file)->ha_delete_table((const char*) from_buff);
+ name_buffer_ptr= strend(name_buffer_ptr) + 1;
+ }
+ DBUG_RETURN(error);
+rename_error:
+ name_buffer_ptr= m_name_buffer_ptr;
+ for (abort_file= file, file= m_file; file < abort_file; file++)
+ {
+ /* Revert the rename, back from 'to' to the original 'from' */
+ create_partition_name(from_buff, from_path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE);
+ create_partition_name(to_buff, to_path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE);
+ /* Ignore error here */
+ (void) (*file)->ha_rename_table(to_buff, from_buff);
name_buffer_ptr= strend(name_buffer_ptr) + 1;
}
DBUG_RETURN(error);
@@ -3179,6 +3207,7 @@ int ha_partition::delete_row(const uchar *buf)
int ha_partition::delete_all_rows()
{
int error;
+ bool truncate= FALSE;
handler **file;
THD *thd= ha_thd();
DBUG_ENTER("ha_partition::delete_all_rows");
@@ -3190,12 +3219,16 @@ int ha_partition::delete_all_rows()
ha_data->next_auto_inc_val= 0;
ha_data->auto_inc_initialized= FALSE;
unlock_auto_increment();
+ truncate= TRUE;
}
file= m_file;
do
{
if ((error= (*file)->ha_delete_all_rows()))
DBUG_RETURN(error);
+ /* Ignore the error */
+ if (truncate)
+ (void) (*file)->ha_reset_auto_increment(0);
} while (*(++file));
DBUG_RETURN(0);
}
diff --git a/sql/handler.cc b/sql/handler.cc
index e65ceba4181..e5c64452aaf 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -62,7 +62,9 @@ static const LEX_STRING sys_table_aliases[]=
};
const char *ha_row_type[] = {
- "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", "PAGE", "?","?","?"
+ "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT",
+ /* Reserved to be "PAGE" in future versions */ "?",
+ "?","?","?"
};
const char *tx_isolation_names[] =
@@ -342,6 +344,7 @@ int ha_init_errors(void)
SETMSG(HA_ERR_TABLE_READONLY, ER(ER_OPEN_AS_READONLY));
SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER(ER_AUTOINC_READ_FAILED));
SETMSG(HA_ERR_AUTOINC_ERANGE, ER(ER_WARN_DATA_OUT_OF_RANGE));
+ SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER(ER_TOO_MANY_CONCURRENT_TRXS));
/* Register the error messages for use with my_error(). */
return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST);
@@ -2747,6 +2750,9 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_AUTOINC_ERANGE:
textno= ER_WARN_DATA_OUT_OF_RANGE;
break;
+ case HA_ERR_TOO_MANY_CONCURRENT_TRXS:
+ textno= ER_TOO_MANY_CONCURRENT_TRXS;
+ break;
default:
{
/* The error was "unknown" to this function.
@@ -2973,6 +2979,7 @@ uint handler::get_dup_key(int error)
*/
int handler::delete_table(const char *name)
{
+ int saved_error= 0;
int error= 0;
int enoent_or_zero= ENOENT; // Error if no file was deleted
char buff[FN_REFLEN];
@@ -2982,21 +2989,31 @@ int handler::delete_table(const char *name)
fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT);
if (my_delete_with_symlink(buff, MYF(0)))
{
- if ((error= my_errno) != ENOENT)
- break;
+ if (my_errno != ENOENT)
+ {
+ /*
+ If error on the first existing file, return the error.
+ Otherwise delete as much as possible.
+ */
+ if (enoent_or_zero)
+ return my_errno;
+ saved_error= my_errno;
+ }
}
else
enoent_or_zero= 0; // No error for ENOENT
error= enoent_or_zero;
}
- return error;
+ return saved_error ? saved_error : error;
}
int handler::rename_table(const char * from, const char * to)
{
int error= 0;
- for (const char **ext= bas_ext(); *ext ; ext++)
+ const char **ext, **start_ext;
+ start_ext= bas_ext();
+ for (ext= start_ext; *ext ; ext++)
{
if (rename_file_ext(from, to, *ext))
{
@@ -3005,6 +3022,12 @@ int handler::rename_table(const char * from, const char * to)
error= 0;
}
}
+ if (error)
+ {
+ /* Try to revert the rename. Ignore errors. */
+ for (; ext >= start_ext; ext--)
+ rename_file_ext(to, from, *ext);
+ }
return error;
}
diff --git a/sql/handler.h b/sql/handler.h
index 5c7cfa4d58b..01f673ecd51 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -280,7 +280,9 @@ enum legacy_db_type
enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED,
ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED,
- ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT, ROW_TYPE_PAGE };
+ ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT,
+ /** Unused. Reserved for future versions. */
+ ROW_TYPE_PAGE };
enum enum_binlog_func {
BFN_RESET_LOGS= 1,
@@ -323,7 +325,9 @@ enum enum_binlog_command {
#define HA_CREATE_USED_PASSWORD (1L << 17)
#define HA_CREATE_USED_CONNECTION (1L << 18)
#define HA_CREATE_USED_KEY_BLOCK_SIZE (1L << 19)
+/** Unused. Reserved for future versions. */
#define HA_CREATE_USED_TRANSACTIONAL (1L << 20)
+/** Unused. Reserved for future versions. */
#define HA_CREATE_USED_PAGE_CHECKSUM (1L << 21)
typedef ulonglong my_xid; // this line is the same as in log_event.h
@@ -914,13 +918,14 @@ typedef struct st_ha_create_information
uint options; /* OR of HA_CREATE_ options */
uint merge_insert_method;
uint extra_size; /* length of extra data segment */
- /* 0 not used, 1 if not transactional, 2 if transactional */
+ /** Transactional or not. Unused; reserved for future versions. */
enum ha_choice transactional;
bool table_existed; /* 1 in create if table existed */
bool frm_only; /* 1 if no ha_create_table() */
bool varchar; /* 1 if table has a VARCHAR */
enum ha_storage_media storage_media; /* DEFAULT, DISK or MEMORY */
- enum ha_choice page_checksum; /* If we have page_checksums */
+ /** Per-page checksums or not. Unused; reserved for future versions. */
+ enum ha_choice page_checksum;
} HA_CREATE_INFO;
diff --git a/sql/item.cc b/sql/item.cc
index 4d9004fff26..2640b74851b 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -599,6 +599,7 @@ bool Item_ident::remove_dependence_processor(uchar * arg)
DBUG_ENTER("Item_ident::remove_dependence_processor");
if (depended_from == (st_select_lex *) arg)
depended_from= 0;
+ context= &((st_select_lex *) arg)->context;
DBUG_RETURN(0);
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 0af3c4954cd..55d4b37ddb0 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -5990,6 +5990,9 @@ Item_func_sp::execute_impl(THD *thd)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx= thd->security_ctx;
#endif
+ enum enum_sp_data_access access=
+ (m_sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ?
+ SP_DEFAULT_ACCESS_MAPPING : m_sp->m_chistics->daccess;
DBUG_ENTER("Item_func_sp::execute_impl");
@@ -6007,11 +6010,13 @@ Item_func_sp::execute_impl(THD *thd)
Throw an error if a non-deterministic function is called while
statement-based replication (SBR) is active.
*/
+
if (!m_sp->m_chistics->detistic && !trust_function_creators &&
+ (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) &&
(mysql_bin_log.is_open() &&
thd->variables.binlog_format == BINLOG_FORMAT_STMT))
{
- my_error(ER_BINLOG_ROW_RBR_TO_SBR, MYF(0));
+ my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0));
goto error;
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 514f93a39ea..025ac12fe07 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1519,6 +1519,7 @@ public:
ft_handler->please->close_search(ft_handler);
ft_handler= 0;
concat_ws= 0;
+ table= 0; // required by Item_func_match::eq()
DBUG_VOID_RETURN;
}
enum Functype functype() const { return FT_FUNC; }
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 00c09679737..cdb091fa07e 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1227,6 +1227,10 @@ Item_in_subselect::single_value_transformer(JOIN *join,
else
{
// it is single select without tables => possible optimization
+ // remove the dependence mark since the item is moved to upper
+ // select and is not outer anymore.
+ item->walk(&Item::remove_dependence_processor, 0,
+ (uchar *) select_lex->outer_select());
item= func->create(left_expr, item);
// fix_field of item will be done in time of substituting
substitution= item;
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 46a58351872..38251294053 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -3327,8 +3327,13 @@ bool Item_func_group_concat::add()
TREE_ELEMENT *el= 0; // Only for safety
if (row_eligible && tree)
+ {
el= tree_insert(tree, table->record[0] + table->s->null_bytes, 0,
tree->custom_arg);
+ /* check if there was enough memory to insert the row */
+ if (!el)
+ return 1;
+ }
/*
If the row is not a duplicate (el->count == 1)
we can dump the row here in case of GROUP_CONCAT(DISTINCT...)
diff --git a/sql/lex.h b/sql/lex.h
index acb81dcf717..0a85824f6f7 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -387,7 +387,6 @@ static SYMBOL symbols[] = {
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
{ "PARSER", SYM(PARSER_SYM)},
{ "PAGE", SYM(PAGE_SYM)},
- { "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)},
{ "PARTIAL", SYM(PARTIAL)},
{ "PARTITION", SYM(PARTITION_SYM)},
{ "PARTITIONING", SYM(PARTITIONING_SYM)},
@@ -543,7 +542,6 @@ static SYMBOL symbols[] = {
{ "TO", SYM(TO_SYM)},
{ "TRAILING", SYM(TRAILING)},
{ "TRANSACTION", SYM(TRANSACTION_SYM)},
- { "TRANSACTIONAL", SYM(TRANSACTIONAL_SYM)},
{ "TRIGGER", SYM(TRIGGER_SYM)},
{ "TRIGGERS", SYM(TRIGGERS_SYM)},
{ "TRUE", SYM(TRUE_SYM)},
diff --git a/sql/log.cc b/sql/log.cc
index 8bb6ba8e9c6..bb81d0c723e 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1564,25 +1564,15 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
YESNO(all),
YESNO(thd->transaction.all.modified_non_trans_table),
YESNO(thd->transaction.stmt.modified_non_trans_table)));
- if ((all && thd->transaction.all.modified_non_trans_table) ||
- (!all && thd->transaction.stmt.modified_non_trans_table &&
- !mysql_bin_log.check_write_error(thd)) ||
- ((thd->options & OPTION_KEEP_LOG) &&
- !mysql_bin_log.check_write_error(thd)))
+ if (mysql_bin_log.check_write_error(thd))
{
/*
- We write the transaction cache with a rollback last if we have
- modified any non-transactional table. We do this even if we are
- committing a single statement that has modified a
- non-transactional table since it can have modified a
- transactional table in that statement as well, which needs to be
- rolled back on the slave.
+ "all == true" means that a "rollback statement" triggered the error and
+ this function was called. However, this must not happen as a rollback
+ is written directly to the binary log. And in auto-commit mode, a single
+ statement that is rolled back has the flag all == false.
*/
- Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, TRUE, 0);
- error= binlog_end_trans(thd, trx_data, &qev, all);
- }
- else
- {
+ DBUG_ASSERT(!all);
/*
We reach this point if either only transactional tables were modified or
the effect of a statement that did not get into the binlog needs to be
@@ -1592,13 +1582,39 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
on the master did not get into the binlog and slaves will be inconsistent.
On the other hand, if a statement is transactional, we just safely roll it
back.
- */
+ */
if ((thd->transaction.stmt.modified_non_trans_table ||
(thd->options & OPTION_KEEP_LOG)) &&
mysql_bin_log.check_write_error(thd))
trx_data->set_incident();
error= binlog_end_trans(thd, trx_data, 0, all);
}
+ else
+ {
+ /*
+ We flush the cache with a rollback, wrapped in a beging/rollback if:
+ . aborting a transcation that modified a non-transactional table or;
+ . aborting a statement that modified both transactional and
+ non-transctional tables but which is not in the boundaries of any
+ transaction;
+ . the OPTION_KEEP_LOG is activate.
+ */
+ if ((all && thd->transaction.all.modified_non_trans_table) ||
+ (!all && thd->transaction.stmt.modified_non_trans_table &&
+ !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) ||
+ ((thd->options & OPTION_KEEP_LOG)))
+ {
+ Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, TRUE, 0);
+ error= binlog_end_trans(thd, trx_data, &qev, all);
+ }
+ /*
+ Otherwise, we simply truncate the cache as there is no change on
+ non-transactional tables as follows.
+ */
+ else if ((all && !thd->transaction.all.modified_non_trans_table) ||
+ (!all && !thd->transaction.stmt.modified_non_trans_table))
+ error= binlog_end_trans(thd, trx_data, 0, all);
+ }
if (!all)
trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt rollback
DBUG_RETURN(error);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 5f77ab3dcc4..e7cbbaba38e 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -3025,8 +3025,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
rpl_filter->db_ok(thd->db))
{
thd->set_time((time_t)when);
- thd->query_length= q_len_arg;
- thd->query= (char*)query_arg;
+ thd->set_query((char*)query_arg, q_len_arg);
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_id = next_query_id();
VOID(pthread_mutex_unlock(&LOCK_thread_count));
@@ -3231,7 +3230,6 @@ Default database: '%s'. Query: '%s'",
} /* End of if (db_ok(... */
end:
- VOID(pthread_mutex_lock(&LOCK_thread_count));
/*
Probably we have set thd->query, thd->db, thd->catalog to point to places
in the data_buf of this event. Now the event is going to be deleted
@@ -3244,10 +3242,8 @@ end:
*/
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
+ thd->set_query(NULL, 0);
DBUG_PRINT("info", ("end: query= 0"));
- thd->query= 0; // just to be sure
- thd->query_length= 0;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
close_thread_tables(thd);
/*
As a disk space optimization, future masters will not log an event for
@@ -4557,8 +4553,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
print_query(FALSE, load_data_query, &end, (char **)&thd->lex->fname_start,
(char **)&thd->lex->fname_end);
*end= 0;
- thd->query_length= end - load_data_query;
- thd->query= load_data_query;
+ thd->set_query(load_data_query, (uint) (end - load_data_query));
if (sql_ex.opt_flags & REPLACE_FLAG)
{
@@ -4664,12 +4659,9 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
error:
thd->net.vio = 0;
const char *remember_db= thd->db;
- VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
- thd->query= 0;
- thd->query_length= 0;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->set_query(NULL, 0);
close_thread_tables(thd);
DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index de02abc48e3..03c366a45bf 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1976,6 +1976,7 @@ extern bool opt_disable_networking, opt_skip_show_db;
extern bool opt_ignore_builtin_innodb;
extern my_bool opt_character_set_client_handshake;
extern bool volatile abort_loop, shutdown_in_progress;
+extern bool in_bootstrap;
extern uint volatile thread_count, thread_running, global_read_lock;
extern uint connection_count;
extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index f7ee7b025f9..49729b5eaea 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -416,6 +416,21 @@ my_bool locked_in_memory;
bool opt_using_transactions;
bool volatile abort_loop;
bool volatile shutdown_in_progress;
+/*
+ True if the bootstrap thread is running. Protected by LOCK_thread_count,
+ just like thread_count.
+ Used in bootstrap() function to determine if the bootstrap thread
+ has completed. Note, that we can't use 'thread_count' instead,
+ since in 5.1, in presence of the Event Scheduler, there may be
+ event threads running in parallel, so it's impossible to know
+ what value of 'thread_count' is a sign of completion of the
+ bootstrap thread.
+
+ At the same time, we can't start the event scheduler after
+ bootstrap either, since we want to be able to process event-related
+ SQL commands in the init file and in --bootstrap mode.
+*/
+bool in_bootstrap= FALSE;
/**
@brief 'grant_option' is used to indicate if privileges needs
to be checked, in which case the lock, LOCK_grant, is used
@@ -3504,7 +3519,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_mysql_create_db,MY_MUTEX_INIT_SLOW);
(void) pthread_mutex_init(&LOCK_lock_db,MY_MUTEX_INIT_SLOW);
(void) pthread_mutex_init(&LOCK_Acl,MY_MUTEX_INIT_SLOW);
- (void) pthread_mutex_init(&LOCK_open, NULL);
+ (void) pthread_mutex_init(&LOCK_open, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_mapped_file,MY_MUTEX_INIT_SLOW);
(void) pthread_mutex_init(&LOCK_status,MY_MUTEX_INIT_FAST);
@@ -3651,14 +3666,17 @@ static void init_ssl()
#ifdef HAVE_OPENSSL
if (opt_use_ssl)
{
+ enum enum_ssl_init_error error= SSL_INITERR_NOERROR;
+
/* having ssl_acceptor_fd != 0 signals the use of SSL */
ssl_acceptor_fd= new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert,
opt_ssl_ca, opt_ssl_capath,
- opt_ssl_cipher);
+ opt_ssl_cipher, &error);
DBUG_PRINT("info",("ssl_acceptor_fd: 0x%lx", (long) ssl_acceptor_fd));
if (!ssl_acceptor_fd)
{
sql_print_warning("Failed to setup SSL");
+ sql_print_warning("SSL error: %s", sslGetErrString(error));
opt_use_ssl = 0;
have_ssl= SHOW_OPTION_DISABLED;
}
@@ -4300,7 +4318,6 @@ int main(int argc, char **argv)
select_thread=pthread_self();
select_thread_in_use=1;
- init_ssl();
#ifdef HAVE_LIBWRAP
libwrapName= my_progname+dirname_length(my_progname);
@@ -4355,6 +4372,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
if (init_server_components())
unireg_abort(1);
+ init_ssl();
network_init();
#ifdef __WIN__
@@ -4423,6 +4441,11 @@ we force server id to 2, but this MySQL server will not act as a slave.");
unireg_abort(1);
}
+ execute_ddl_log_recovery();
+
+ if (Events::init(opt_noacl || opt_bootstrap))
+ unireg_abort(1);
+
if (opt_bootstrap)
{
select_thread_in_use= 0; // Allow 'kill' to work
@@ -4434,14 +4457,10 @@ we force server id to 2, but this MySQL server will not act as a slave.");
if (read_init_file(opt_init_file))
unireg_abort(1);
}
- execute_ddl_log_recovery();
create_shutdown_thread();
start_handle_manager();
- if (Events::init(opt_noacl))
- unireg_abort(1);
-
sql_print_information(ER(ER_STARTUP),my_progname,server_version,
((unix_sock == INVALID_SOCKET) ? (char*) ""
: mysqld_unix_port),
@@ -4723,6 +4742,7 @@ static void bootstrap(FILE *file)
thd->security_ctx->master_access= ~(ulong)0;
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
thread_count++;
+ in_bootstrap= TRUE;
bootstrap_file=file;
#ifndef EMBEDDED_LIBRARY // TODO: Enable this
@@ -4735,7 +4755,7 @@ static void bootstrap(FILE *file)
}
/* Wait for thread to die */
(void) pthread_mutex_lock(&LOCK_thread_count);
- while (thread_count)
+ while (in_bootstrap)
{
(void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
@@ -6621,7 +6641,7 @@ The minimum value for this variable is 4096.",
"Joins that are probably going to read more than max_join_size records return an error.",
(uchar**) &global_system_variables.max_join_size,
(uchar**) &max_system_variables.max_join_size, 0, GET_HA_ROWS, REQUIRED_ARG,
- ~0L, 1, ~0L, 0, 1, 0},
+ HA_POS_ERROR, 1, HA_POS_ERROR, 0, 1, 0},
{"max_length_for_sort_data", OPT_MAX_LENGTH_FOR_SORT_DATA,
"Max number of bytes in sorted records.",
(uchar**) &global_system_variables.max_length_for_sort_data,
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 0a8720bae64..73892f31ccf 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -186,10 +186,12 @@ my_bool net_realloc(NET *net, size_t length)
pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
/*
We must allocate some extra bytes for the end 0 and to be able to
- read big compressed blocks
+ read big compressed blocks + 1 safety byte since uint3korr() in
+ my_real_read() may actually read 4 bytes depending on build flags and
+ platform.
*/
if (!(buff= (uchar*) my_realloc((char*) net->buff, pkt_length +
- NET_HEADER_SIZE + COMP_HEADER_SIZE,
+ NET_HEADER_SIZE + COMP_HEADER_SIZE + 1,
MYF(MY_WME))))
{
/* @todo: 1 and 2 codes are identical. */
@@ -921,6 +923,13 @@ my_real_read(NET *net, size_t *complen)
#ifdef HAVE_COMPRESS
if (net->compress)
{
+ /*
+ The following uint3korr() may read 4 bytes, so make sure we don't
+ read unallocated or uninitialized memory. The right-hand expression
+ must match the size of the buffer allocated in net_realloc().
+ */
+ DBUG_ASSERT(net->where_b + NET_HEADER_SIZE + sizeof(uint32) <=
+ net->max_packet + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1);
/*
If the packet is compressed then complen > 0 and contains the
number of bytes in the uncompressed packet
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 16975c68a54..4f69a0fdb52 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -29,11 +29,11 @@
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
/* Declared non-static only because of the embedded library. */
-void net_send_error_packet(THD *thd, uint sql_errno, const char *err);
-void net_send_ok(THD *, uint, uint, ha_rows, ulonglong, const char *);
-void net_send_eof(THD *thd, uint server_status, uint total_warn_count);
+bool net_send_error_packet(THD *thd, uint sql_errno, const char *err);
+bool net_send_ok(THD *, uint, uint, ha_rows, ulonglong, const char *);
+bool net_send_eof(THD *thd, uint server_status, uint total_warn_count);
#ifndef EMBEDDED_LIBRARY
-static void write_eof_packet(THD *thd, NET *net,
+static bool write_eof_packet(THD *thd, NET *net,
uint server_status, uint total_warn_count);
#endif
@@ -70,8 +70,17 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length)
For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
critical that every error that can be intercepted is issued in one
place only, my_message_sql.
+
+ @param thd Thread handler
+ @param sql_errno The error code to send
+ @param err A pointer to the error message
+
+ @return
+ @retval FALSE The message was sent to the client
+ @retval TRUE An error occurred and the message wasn't sent properly
*/
-void net_send_error(THD *thd, uint sql_errno, const char *err)
+
+bool net_send_error(THD *thd, uint sql_errno, const char *err)
{
DBUG_ENTER("net_send_error");
@@ -80,6 +89,7 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
DBUG_ASSERT(err && err[0]);
DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err));
+ bool error;
/*
It's one case when we can push an error even though there
@@ -90,11 +100,11 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
/* Abort multi-result sets */
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
- net_send_error_packet(thd, sql_errno, err);
+ error= net_send_error_packet(thd, sql_errno, err);
thd->main_da.can_overwrite_status= FALSE;
- DBUG_VOID_RETURN;
+ DBUG_RETURN(error);
}
/**
@@ -113,25 +123,33 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
Is not stored if no message.
@param thd Thread handler
+ @param server_status The server status
+ @param total_warn_count Total number of warnings
@param affected_rows Number of rows changed by statement
@param id Auto_increment id for first row (if used)
@param message Message to send to the client (Used by mysql_status)
+
+ @return
+ @retval FALSE The message was successfully sent
+ @retval TRUE An error occurred and the messages wasn't sent properly
+
*/
#ifndef EMBEDDED_LIBRARY
-void
+bool
net_send_ok(THD *thd,
uint server_status, uint total_warn_count,
ha_rows affected_rows, ulonglong id, const char *message)
{
NET *net= &thd->net;
uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
+ bool error= FALSE;
DBUG_ENTER("my_ok");
if (! net->vio) // hack for re-parsing queries
{
DBUG_PRINT("info", ("vio present: NO"));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(FALSE);
}
buff[0]=0; // No fields
@@ -162,13 +180,14 @@ net_send_ok(THD *thd,
if (message && message[0])
pos= net_store_data(pos, (uchar*) message, strlen(message));
- VOID(my_net_write(net, buff, (size_t) (pos-buff)));
- VOID(net_flush(net));
+ error= my_net_write(net, buff, (size_t) (pos-buff));
+ if (!error)
+ error= net_flush(net);
thd->main_da.can_overwrite_status= FALSE;
DBUG_PRINT("info", ("OK sent, so no more error sending allowed"));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(error);
}
static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
@@ -188,37 +207,54 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
client.
@param thd Thread handler
- @param no_flush Set to 1 if there will be more data to the client,
- like in send_fields().
+ @param server_status The server status
+ @param total_warn_count Total number of warnings
+
+ @return
+ @retval FALSE The message was successfully sent
+ @retval TRUE An error occurred and the message wasn't sent properly
*/
-void
+bool
net_send_eof(THD *thd, uint server_status, uint total_warn_count)
{
NET *net= &thd->net;
+ bool error= FALSE;
DBUG_ENTER("net_send_eof");
/* Set to TRUE if no active vio, to work well in case of --init-file */
if (net->vio != 0)
{
thd->main_da.can_overwrite_status= TRUE;
- write_eof_packet(thd, net, server_status, total_warn_count);
- VOID(net_flush(net));
+ error= write_eof_packet(thd, net, server_status, total_warn_count);
+ if (!error)
+ error= net_flush(net);
thd->main_da.can_overwrite_status= FALSE;
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
}
- DBUG_VOID_RETURN;
+ DBUG_RETURN(error);
}
/**
Format EOF packet according to the current protocol and
write it to the network output buffer.
+
+ @param thd The thread handler
+ @param net The network handler
+ @param server_status The server status
+ @param total_warn_count The number of warnings
+
+
+ @return
+ @retval FALSE The message was sent successfully
+ @retval TRUE An error occurred and the messages wasn't sent properly
*/
-static void write_eof_packet(THD *thd, NET *net,
+static bool write_eof_packet(THD *thd, NET *net,
uint server_status,
uint total_warn_count)
{
+ bool error;
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{
uchar buff[5];
@@ -237,10 +273,12 @@ static void write_eof_packet(THD *thd, NET *net,
if (thd->is_fatal_error)
server_status&= ~SERVER_MORE_RESULTS_EXISTS;
int2store(buff + 3, server_status);
- VOID(my_net_write(net, buff, 5));
+ error= my_net_write(net, buff, 5);
}
else
- VOID(my_net_write(net, eof_buff, 1));
+ error= my_net_write(net, eof_buff, 1);
+
+ return error;
}
/**
@@ -261,7 +299,17 @@ bool send_old_password_request(THD *thd)
}
-void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
+/**
+ @param thd Thread handler
+ @param sql_errno The error code to send
+ @param err A pointer to the error message
+
+ @return
+ @retval FALSE The message was successfully sent
+ @retval TRUE An error occurred and the messages wasn't sent properly
+*/
+
+bool net_send_error_packet(THD *thd, uint sql_errno, const char *err)
{
NET *net= &thd->net;
uint length;
@@ -279,7 +327,7 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
/* In bootstrap it's ok to print on stderr */
fprintf(stderr,"ERROR: %d %s\n",sql_errno,err);
}
- DBUG_VOID_RETURN;
+ DBUG_RETURN(FALSE);
}
if (net->return_errno)
@@ -301,9 +349,8 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
length=(uint) strlen(err);
set_if_smaller(length,MYSQL_ERRMSG_SIZE-1);
}
- VOID(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err,
+ DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err,
length));
- DBUG_VOID_RETURN;
}
#endif /* EMBEDDED_LIBRARY */
@@ -389,36 +436,39 @@ void net_end_statement(THD *thd)
if (thd->main_da.is_sent)
return;
+ bool error= FALSE;
+
switch (thd->main_da.status()) {
case Diagnostics_area::DA_ERROR:
/* The query failed, send error to log and abort bootstrap. */
- net_send_error(thd,
- thd->main_da.sql_errno(),
- thd->main_da.message());
+ error= net_send_error(thd,
+ thd->main_da.sql_errno(),
+ thd->main_da.message());
break;
case Diagnostics_area::DA_EOF:
- net_send_eof(thd,
- thd->main_da.server_status(),
- thd->main_da.total_warn_count());
+ error= net_send_eof(thd,
+ thd->main_da.server_status(),
+ thd->main_da.total_warn_count());
break;
case Diagnostics_area::DA_OK:
- net_send_ok(thd,
- thd->main_da.server_status(),
- thd->main_da.total_warn_count(),
- thd->main_da.affected_rows(),
- thd->main_da.last_insert_id(),
- thd->main_da.message());
+ error= net_send_ok(thd,
+ thd->main_da.server_status(),
+ thd->main_da.total_warn_count(),
+ thd->main_da.affected_rows(),
+ thd->main_da.last_insert_id(),
+ thd->main_da.message());
break;
case Diagnostics_area::DA_DISABLED:
break;
case Diagnostics_area::DA_EMPTY:
default:
DBUG_ASSERT(0);
- net_send_ok(thd, thd->server_status, thd->total_warn_count,
- 0, 0, NULL);
+ error= net_send_ok(thd, thd->server_status, thd->total_warn_count,
+ 0, 0, NULL);
break;
}
- thd->main_da.is_sent= TRUE;
+ if (!error)
+ thd->main_da.is_sent= TRUE;
}
diff --git a/sql/protocol.h b/sql/protocol.h
index a4770e9b6e3..251ba6fbc33 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -173,7 +173,7 @@ public:
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
-void net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
+bool net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
void net_end_statement(THD *thd);
bool send_old_password_request(THD *thd);
uchar *net_store_data(uchar *to,const uchar *from, size_t length);
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 45c0efb10c2..18fbae9bb9d 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -947,6 +947,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
if (count_relay_log_space(rli))
{
*errmsg= "Error counting relay log space";
+ error=1;
goto err;
}
if (!just_reset)
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 42bca02984d..5531ee71620 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -6201,3 +6201,8 @@ ER_TEMPORARY_NAME
ER_RENAMED_NAME
eng "Renamed"
swe "Namnändrad"
+ER_TOO_MANY_CONCURRENT_TRXS
+ eng "Too many active concurrent transactions"
+
+WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED
+ eng "Non-ASCII separator arguments are not fully supported"
diff --git a/sql/slave.cc b/sql/slave.cc
index 81be7064f89..3b64e23ece5 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -38,6 +38,7 @@
#include <my_dir.h>
#include <sql_common.h>
#include <errmsg.h>
+#include <mysqld_error.h>
#include <mysys_err.h>
#ifdef HAVE_REPLICATION
@@ -511,7 +512,7 @@ terminate_slave_thread(THD *thd,
int error;
DBUG_PRINT("loop", ("killing slave thread"));
- pthread_mutex_lock(&thd->LOCK_delete);
+ pthread_mutex_lock(&thd->LOCK_thd_data);
#ifndef DONT_USE_THR_ALARM
/*
Error codes from pthread_kill are:
@@ -522,7 +523,7 @@ terminate_slave_thread(THD *thd,
DBUG_ASSERT(err != EINVAL);
#endif
thd->awake(THD::NOT_KILLED);
- pthread_mutex_unlock(&thd->LOCK_delete);
+ pthread_mutex_unlock(&thd->LOCK_thd_data);
/*
There is a small chance that slave thread might miss the first
@@ -859,6 +860,29 @@ int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
DBUG_RETURN(1);
}
+
+/*
+ Check if the error is caused by network.
+ @param[in] errorno Number of the error.
+ RETURNS:
+ TRUE network error
+ FALSE not network error
+*/
+
+bool is_network_error(uint errorno)
+{
+ if (errorno == CR_CONNECTION_ERROR ||
+ errorno == CR_CONN_HOST_ERROR ||
+ errorno == CR_SERVER_GONE_ERROR ||
+ errorno == CR_SERVER_LOST ||
+ errorno == ER_CON_COUNT_ERROR ||
+ errorno == ER_SERVER_SHUTDOWN)
+ return TRUE;
+
+ return FALSE;
+}
+
+
/*
Note that we rely on the master's version (3.23, 4.0.14 etc) instead of
relying on the binlog's version. This is not perfect: imagine an upgrade
@@ -871,6 +895,7 @@ int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
RETURNS
0 ok
1 error
+ 2 transient network problem, the caller should try to reconnect
*/
static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
@@ -956,6 +981,8 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
unavailable (very old master not supporting UNIX_TIMESTAMP()?).
*/
+ DBUG_SYNC_POINT("debug_lock.before_get_UNIX_TIMESTAMP", 10);
+ master_res= NULL;
if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) &&
(master_res= mysql_store_result(mysql)) &&
(master_row= mysql_fetch_row(master_res)))
@@ -963,7 +990,13 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
mi->clock_diff_with_master=
(long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
}
- else if (!check_io_slave_killed(mi->io_thd, mi, NULL))
+ else if (is_network_error(mysql_errno(mysql)))
+ {
+ mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ "Get master clock failed with error: %s", mysql_error(mysql));
+ goto network_err;
+ }
+ else
{
mi->clock_diff_with_master= 0; /* The "most sensible" value */
sql_print_warning("\"SELECT UNIX_TIMESTAMP()\" failed on master, "
@@ -972,7 +1005,10 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
mysql_error(mysql), mysql_errno(mysql));
}
if (master_res)
+ {
mysql_free_result(master_res);
+ master_res= NULL;
+ }
/*
Check that the master's server id and ours are different. Because if they
@@ -984,12 +1020,15 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
Note: we could have put a @@SERVER_ID in the previous SELECT
UNIX_TIMESTAMP() instead, but this would not have worked on 3.23 masters.
*/
+ DBUG_SYNC_POINT("debug_lock.before_get_SERVER_ID", 10);
+ master_res= NULL;
+ master_row= NULL;
if (!mysql_real_query(mysql,
STRING_WITH_LEN("SHOW VARIABLES LIKE 'SERVER_ID'")) &&
- (master_res= mysql_store_result(mysql)))
+ (master_res= mysql_store_result(mysql)) &&
+ (master_row= mysql_fetch_row(master_res)))
{
- if ((master_row= mysql_fetch_row(master_res)) &&
- (::server_id == strtoul(master_row[1], 0, 10)) &&
+ if ((::server_id == strtoul(master_row[1], 0, 10)) &&
!mi->rli.replicate_same_server_id)
{
errmsg= "The slave I/O thread stops because master and slave have equal \
@@ -998,10 +1037,34 @@ the --replicate-same-server-id option must be used on slave but this does \
not always make sense; please check the manual before using it).";
err_code= ER_SLAVE_FATAL_ERROR;
sprintf(err_buff, ER(err_code), errmsg);
+ goto err;
}
+ }
+ else if (mysql_errno(mysql))
+ {
+ if (is_network_error(mysql_errno(mysql)))
+ {
+ mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ "Get master SERVER_ID failed with error: %s", mysql_error(mysql));
+ goto network_err;
+ }
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is encountered \
+when it try to get the value of SERVER_ID variable from master.";
+ err_code= mysql_errno(mysql);
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ else if (!master_row && master_res)
+ {
+ mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE,
+ "Unknown system variable 'SERVER_ID' on master, \
+maybe it is a *VERY OLD MASTER*.");
+ }
+ if (master_res)
+ {
mysql_free_result(master_res);
- if (errmsg)
- goto err;
+ master_res= NULL;
}
/*
@@ -1025,23 +1088,50 @@ not always make sense; please check the manual before using it).";
if (*mysql->server_version == '3')
goto err;
- if ((*mysql->server_version == '4') &&
- !mysql_real_query(mysql,
- STRING_WITH_LEN("SELECT @@GLOBAL.COLLATION_SERVER")) &&
- (master_res= mysql_store_result(mysql)))
+ if (*mysql->server_version == '4')
{
- if ((master_row= mysql_fetch_row(master_res)) &&
- strcmp(master_row[0], global_system_variables.collation_server->name))
+ master_res= NULL;
+ if (!mysql_real_query(mysql,
+ STRING_WITH_LEN("SELECT @@GLOBAL.COLLATION_SERVER")) &&
+ (master_res= mysql_store_result(mysql)) &&
+ (master_row= mysql_fetch_row(master_res)))
{
- errmsg= "The slave I/O thread stops because master and slave have \
+ if (strcmp(master_row[0], global_system_variables.collation_server->name))
+ {
+ errmsg= "The slave I/O thread stops because master and slave have \
different values for the COLLATION_SERVER global variable. The values must \
-be equal for replication to work";
- err_code= ER_SLAVE_FATAL_ERROR;
- sprintf(err_buff, ER(err_code), errmsg);
+be equal for the Statement-format replication to work";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ goto err;
+ }
}
- mysql_free_result(master_res);
- if (errmsg)
+ else if (is_network_error(mysql_errno(mysql)))
+ {
+ mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ "Get master COLLATION_SERVER failed with error: %s", mysql_error(mysql));
+ goto network_err;
+ }
+ else if (mysql_errno(mysql) != ER_UNKNOWN_SYSTEM_VARIABLE)
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is encountered \
+when it try to get the value of COLLATION_SERVER global variable from master.";
+ err_code= mysql_errno(mysql);
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
goto err;
+ }
+ else
+ mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE,
+ "Unknown system variable 'COLLATION_SERVER' on master, \
+maybe it is a *VERY OLD MASTER*. *NOTE*: slave may experience \
+inconsistency if replicated data deals with collation.");
+
+ if (master_res)
+ {
+ mysql_free_result(master_res);
+ master_res= NULL;
+ }
}
/*
@@ -1059,35 +1149,62 @@ be equal for replication to work";
This check is only necessary for 4.x masters (and < 5.0.4 masters but
those were alpha).
*/
- if ((*mysql->server_version == '4') &&
- !mysql_real_query(mysql, STRING_WITH_LEN("SELECT @@GLOBAL.TIME_ZONE")) &&
- (master_res= mysql_store_result(mysql)))
+ if (*mysql->server_version == '4')
{
- if ((master_row= mysql_fetch_row(master_res)) &&
- strcmp(master_row[0],
- global_system_variables.time_zone->get_name()->ptr()))
+ master_res= NULL;
+ if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT @@GLOBAL.TIME_ZONE")) &&
+ (master_res= mysql_store_result(mysql)) &&
+ (master_row= mysql_fetch_row(master_res)))
{
- errmsg= "The slave I/O thread stops because master and slave have \
+ if (strcmp(master_row[0],
+ global_system_variables.time_zone->get_name()->ptr()))
+ {
+ errmsg= "The slave I/O thread stops because master and slave have \
different values for the TIME_ZONE global variable. The values must \
-be equal for replication to work";
- err_code= ER_SLAVE_FATAL_ERROR;
- sprintf(err_buff, ER(err_code), errmsg);
+be equal for the Statement-format replication to work";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ goto err;
+ }
}
- mysql_free_result(master_res);
-
- if (errmsg)
+ else if (is_network_error(mysql_errno(mysql)))
+ {
+ mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ "Get master TIME_ZONE failed with error: %s", mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is encountered \
+when it try to get the value of TIME_ZONE global variable from master.";
+ err_code= mysql_errno(mysql);
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
goto err;
+ }
+ if (master_res)
+ {
+ mysql_free_result(master_res);
+ master_res= NULL;
+ }
}
err:
if (errmsg)
{
+ if (master_res)
+ mysql_free_result(master_res);
DBUG_ASSERT(err_code != 0);
mi->report(ERROR_LEVEL, err_code, err_buff);
DBUG_RETURN(1);
}
DBUG_RETURN(0);
+
+network_err:
+ if (master_res)
+ mysql_free_result(master_res);
+ DBUG_RETURN(2);
}
/*
@@ -1133,15 +1250,13 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
DBUG_RETURN(1);
}
thd->command = COM_TABLE_DUMP;
- thd->query_length= packet_len;
- /* Note that we should not set thd->query until the area is initalized */
if (!(query = thd->strmake((char*) net->read_pos, packet_len)))
{
sql_print_error("create_table_from_dump: out of memory");
my_message(ER_GET_ERRNO, "Out of memory", MYF(0));
DBUG_RETURN(1);
}
- thd->query= query;
+ thd->set_query(query, packet_len);
thd->is_slave_error = 0;
bzero((char*) &tables,sizeof(tables));
@@ -2374,6 +2489,7 @@ pthread_handler_t handle_slave_io(void *arg)
char llbuff[22];
uint retry_count;
bool suppress_warnings;
+ int ret;
#ifndef DBUG_OFF
uint retry_count_reg= 0, retry_count_dump= 0, retry_count_event= 0;
#endif
@@ -2454,8 +2570,23 @@ connected:
mi->slave_running= MYSQL_SLAVE_RUN_CONNECT;
thd->slave_net = &mysql->net;
thd_proc_info(thd, "Checking master version");
- if (get_master_version_and_clock(mysql, mi))
+ ret= get_master_version_and_clock(mysql, mi);
+ if (ret == 1)
+ /* Fatal error */
goto err;
+
+ if (ret == 2)
+ {
+ if (check_io_slave_killed(mi->io_thd, mi, "Slave I/O thread killed"
+ "while calling get_master_version_and_clock(...)"))
+ goto err;
+ suppress_warnings= FALSE;
+ /* Try to reconnect because the error was caused by a transient network problem */
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_REG]))
+ goto err;
+ goto connected;
+ }
if (mi->rli.relay_log.description_event_for_queue->binlog_version > 1)
{
@@ -2622,10 +2753,8 @@ 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));
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query = thd->db = 0; // extra safety
- thd->query_length= thd->db_length= 0;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->set_query(NULL, 0);
+ thd->reset_db(NULL, 0);
if (mysql)
{
/*
@@ -2977,15 +3106,14 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
must "proactively" clear playgrounds:
*/
rli->cleanup_context(thd, 1);
- VOID(pthread_mutex_lock(&LOCK_thread_count));
/*
Some extra safety, which should not been needed (normally, event deletion
should already have done these assignments (each event which sets these
variables is supposed to set them to 0 before terminating)).
*/
- thd->query= thd->db= thd->catalog= 0;
- thd->query_length= thd->db_length= 0;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->catalog= 0;
+ thd->set_query(NULL, 0);
+ thd->reset_db(NULL, 0);
thd_proc_info(thd, "Waiting for slave mutex on exit");
pthread_mutex_lock(&rli->run_lock);
/* We need data_lock, at least to wake up any waiting master_pos_wait() */
diff --git a/sql/sp.cc b/sql/sp.cc
index 29e228f5e45..4d840f53e2f 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -70,9 +70,6 @@ enum
MYSQL_PROC_FIELD_COUNT
};
-/* Tells what SP_DEFAULT_ACCESS should be mapped to */
-#define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL
-
/*************************************************************************/
/**
diff --git a/sql/sp.h b/sql/sp.h
index 75c6856f64b..5a190c5480e 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -17,6 +17,9 @@
#ifndef _SP_H_
#define _SP_H_
+/* Tells what SP_DEFAULT_ACCESS should be mapped to */
+#define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL
+
// Return codes from sp_create_*, sp_drop_*, and sp_show_*:
#define SP_OK 0
#define SP_KEY_NOT_FOUND -1
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 8a8a5b06cc1..0736e5fc2a8 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1012,8 +1012,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
else
DBUG_RETURN(TRUE);
- thd->query= pbuf;
- thd->query_length= qbuf.length();
+ thd->set_query(pbuf, qbuf.length());
DBUG_RETURN(FALSE);
}
@@ -1249,7 +1248,7 @@ sp_head::execute(THD *thd)
*/
if (thd->prelocked_mode == NON_PRELOCKED)
thd->user_var_events_alloc= thd->mem_root;
-
+
err_status= i->execute(thd, &ip);
if (i->free_list)
@@ -2858,14 +2857,13 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
}
else
*nextp= m_ip+1;
- thd->query= query;
- thd->query_length= query_length;
+ thd->set_query(query, query_length);
thd->query_name_consts= 0;
if (!thd->is_error())
thd->main_da.reset_diagnostics_area();
}
- DBUG_RETURN(res);
+ DBUG_RETURN(res || thd->is_error());
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 88e1620b152..b81070000b3 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -7375,7 +7375,13 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
/* make * substituting permanent */
SELECT_LEX *select_lex= thd->lex->current_select;
select_lex->with_wild= 0;
- select_lex->item_list= fields;
+ /*
+ The assignment below is translated to memcpy() call (at least on some
+ platforms). memcpy() expects that source and destination areas do not
+ overlap. That problem was detected by valgrind.
+ */
+ if (&select_lex->item_list != &fields)
+ select_lex->item_list= fields;
thd->restore_active_arena(arena, &backup);
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 48ddb42f0d8..a9c399e15de 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -627,7 +627,7 @@ THD::THD()
#ifdef SIGNAL_WITH_VIO_CLOSE
active_vio = 0;
#endif
- pthread_mutex_init(&LOCK_delete, MY_MUTEX_INIT_FAST);
+ pthread_mutex_init(&LOCK_thd_data, MY_MUTEX_INIT_FAST);
/* Variables with default values */
proc_info="login";
@@ -911,8 +911,8 @@ THD::~THD()
THD_CHECK_SENTRY(this);
DBUG_ENTER("~THD()");
/* Ensure that no one is using THD */
- pthread_mutex_lock(&LOCK_delete);
- pthread_mutex_unlock(&LOCK_delete);
+ pthread_mutex_lock(&LOCK_thd_data);
+ pthread_mutex_unlock(&LOCK_thd_data);
add_to_status(&global_status_var, &status_var);
/* Close connection */
@@ -939,7 +939,7 @@ THD::~THD()
free_root(&transaction.mem_root,MYF(0));
#endif
mysys_var=0; // Safety (shouldn't be needed)
- pthread_mutex_destroy(&LOCK_delete);
+ pthread_mutex_destroy(&LOCK_thd_data);
#ifndef DBUG_OFF
dbug_sentry= THD_SENTRY_GONE;
#endif
@@ -1012,7 +1012,7 @@ void THD::awake(THD::killed_state state_to_set)
DBUG_ENTER("THD::awake");
DBUG_PRINT("enter", ("this: 0x%lx", (long) this));
THD_CHECK_SENTRY(this);
- safe_mutex_assert_owner(&LOCK_delete);
+ safe_mutex_assert_owner(&LOCK_thd_data);
killed= state_to_set;
if (state_to_set != THD::KILL_QUERY)
@@ -1409,7 +1409,7 @@ int THD::send_explain_fields(select_result *result)
void THD::close_active_vio()
{
DBUG_ENTER("close_active_vio");
- safe_mutex_assert_owner(&LOCK_delete);
+ safe_mutex_assert_owner(&LOCK_thd_data);
#ifndef EMBEDDED_LIBRARY
if (active_vio)
{
@@ -1790,6 +1790,8 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
if ((uint) strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN)
strmake(path,exchange->file_name,FN_REFLEN-1);
+ write_cs= exchange->cs ? exchange->cs : &my_charset_bin;
+
if ((file= create_file(thd, path, exchange, &cache)) < 0)
return 1;
/* Check if there is any blobs in data */
@@ -1809,6 +1811,31 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
non_string_results= TRUE;
}
}
+ if (exchange->escaped->numchars() > 1 || exchange->enclosed->numchars() > 1)
+ {
+ my_error(ER_WRONG_FIELD_TERMINATORS, MYF(0));
+ return TRUE;
+ }
+ if (exchange->escaped->length() > 1 || exchange->enclosed->length() > 1 ||
+ !my_isascii(exchange->escaped->ptr()[0]) ||
+ !my_isascii(exchange->enclosed->ptr()[0]) ||
+ !exchange->field_term->is_ascii() || !exchange->line_term->is_ascii() ||
+ !exchange->line_start->is_ascii())
+ {
+ /*
+ Current LOAD DATA INFILE recognizes field/line separators "as is" without
+ converting from client charset to data file charset. So, it is supposed,
+ that input file of LOAD DATA INFILE consists of data in one charset and
+ separators in other charset. For the compatibility with that [buggy]
+ behaviour SELECT INTO OUTFILE implementation has been saved "as is" too,
+ but the new warning message has been added:
+
+ Non-ASCII separator arguments are not fully supported
+ */
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED,
+ ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
+ }
field_term_length=exchange->field_term->length();
field_term_char= field_term_length ?
(int) (uchar) (*exchange->field_term)[0] : INT_MAX;
@@ -1858,6 +1885,8 @@ bool select_export::send_data(List<Item> &items)
DBUG_ENTER("select_export::send_data");
char buff[MAX_FIELD_WIDTH],null_buff[2],space[MAX_FIELD_WIDTH];
+ char cvt_buff[MAX_FIELD_WIDTH];
+ String cvt_str(cvt_buff, sizeof(cvt_buff), write_cs);
bool space_inited=0;
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
tmp.length(0);
@@ -1881,6 +1910,37 @@ bool select_export::send_data(List<Item> &items)
bool enclosed = (exchange->enclosed->length() &&
(!exchange->opt_enclosed || result_type == STRING_RESULT));
res=item->str_result(&tmp);
+ if (res && !my_charset_same(write_cs, res->charset()) &&
+ !my_charset_same(write_cs, &my_charset_bin))
+ {
+ const char *well_formed_error_pos;
+ const char *cannot_convert_error_pos;
+ const char *from_end_pos;
+ const char *error_pos;
+ uint32 bytes;
+ bytes= well_formed_copy_nchars(write_cs, cvt_buff, sizeof(cvt_buff),
+ res->charset(), res->ptr(), res->length(),
+ sizeof(cvt_buff),
+ &well_formed_error_pos,
+ &cannot_convert_error_pos,
+ &from_end_pos);
+ error_pos= well_formed_error_pos ? well_formed_error_pos
+ : cannot_convert_error_pos;
+ if (error_pos)
+ {
+ char printable_buff[32];
+ convert_to_printable(printable_buff, sizeof(printable_buff),
+ error_pos, res->ptr() + res->length() - error_pos,
+ res->charset(), 6);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
+ ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
+ "string", printable_buff,
+ item->name, row_count);
+ }
+ cvt_str.length(bytes);
+ res= &cvt_str;
+ }
if (res && enclosed)
{
if (my_b_write(&cache,(uchar*) exchange->enclosed->ptr(),
@@ -3055,6 +3115,25 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
}
+void THD::set_statement(Statement *stmt)
+{
+ pthread_mutex_lock(&LOCK_thd_data);
+ Statement::set_statement(stmt);
+ pthread_mutex_unlock(&LOCK_thd_data);
+}
+
+
+/** Assign a new value to thd->query. */
+
+void THD::set_query(char *query_arg, uint32 query_length_arg)
+{
+ pthread_mutex_lock(&LOCK_thd_data);
+ query= query_arg;
+ query_length= query_length_arg;
+ pthread_mutex_unlock(&LOCK_thd_data);
+}
+
+
/**
Mark transaction to rollback and mark error as fatal to a sub-statement.
@@ -3695,7 +3774,8 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
push_warning(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_BINLOG_UNSAFE_STATEMENT,
ER(ER_BINLOG_UNSAFE_STATEMENT));
- if (!(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED))
+ if (global_system_variables.log_warnings &&
+ !(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED))
{
sql_print_warning("%s Statement: %.*s",
ER(ER_BINLOG_UNSAFE_STATEMENT),
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 36e696f2da6..09841edc480 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -631,22 +631,16 @@ public:
we need to declare it char * because all table handlers are written
in C and need to point to it.
- Note that (A) if we set query = NULL, we must at the same time set
- query_length = 0, and protect the whole operation with the
- LOCK_thread_count mutex. And (B) we are ONLY allowed to set query to a
- non-NULL value if its previous value is NULL. We do not need to protect
- operation (B) with any mutex. To avoid crashes in races, if we do not
- know that thd->query cannot change at the moment, one should print
+ Note that if we set query = NULL, we must at the same time set
+ query_length = 0, and protect the whole operation with
+ LOCK_thd_data mutex. To avoid crashes in races, if we do not
+ know that thd->query cannot change at the moment, we should print
thd->query like this:
- (1) reserve the LOCK_thread_count mutex;
- (2) check if thd->query is NULL;
- (3) if not NULL, then print at most thd->query_length characters from
- it. We will see the query_length field as either 0, or the right value
- for it.
- Assuming that the write and read of an n-bit memory field in an n-bit
- computer is atomic, we can avoid races in the above way.
- This printing is needed at least in SHOW PROCESSLIST and SHOW INNODB
- STATUS.
+ (1) reserve the LOCK_thd_data mutex;
+ (2) print or copy the value of query and query_length
+ (3) release LOCK_thd_data mutex.
+ This printing is needed at least in SHOW PROCESSLIST and SHOW
+ ENGINE INNODB STATUS.
*/
char *query;
uint32 query_length; // current query length
@@ -678,7 +672,7 @@ public:
virtual ~Statement();
/* Assign execution context (note: not all members) of given stmt to self */
- void set_statement(Statement *stmt);
+ virtual void set_statement(Statement *stmt);
void set_n_backup_statement(Statement *stmt, Statement *backup);
void restore_backup_statement(Statement *stmt, Statement *backup);
/* return class type */
@@ -1298,7 +1292,15 @@ public:
THR_LOCK_OWNER main_lock_id; // To use for conventional queries
THR_LOCK_OWNER *lock_id; // If not main_lock_id, points to
// the lock_id of a cursor.
- pthread_mutex_t LOCK_delete; // Locked before thd is deleted
+ /**
+ Protects THD data accessed from other threads:
+ - thd->query and thd->query_length (used by SHOW ENGINE
+ INNODB STATUS and SHOW PROCESSLIST
+ - thd->mysys_var (used by KILL statement and shutdown).
+ Is locked when THD is deleted.
+ */
+ pthread_mutex_t LOCK_thd_data;
+
/* all prepared statements and cursors of this connection */
Statement_map stmt_map;
/*
@@ -1335,6 +1337,10 @@ public:
Set it using the thd_proc_info(THD *thread, const char *message)
macro/function.
+
+ This member is accessed and assigned without any synchronization.
+ Therefore, it may point only to constant (statically
+ allocated) strings, which memory won't go away over time.
*/
const char *proc_info;
@@ -1886,15 +1892,15 @@ public:
#ifdef SIGNAL_WITH_VIO_CLOSE
inline void set_active_vio(Vio* vio)
{
- pthread_mutex_lock(&LOCK_delete);
+ pthread_mutex_lock(&LOCK_thd_data);
active_vio = vio;
- pthread_mutex_unlock(&LOCK_delete);
+ pthread_mutex_unlock(&LOCK_thd_data);
}
inline void clear_active_vio()
{
- pthread_mutex_lock(&LOCK_delete);
+ pthread_mutex_lock(&LOCK_thd_data);
active_vio = 0;
- pthread_mutex_unlock(&LOCK_delete);
+ pthread_mutex_unlock(&LOCK_thd_data);
}
void close_active_vio();
#endif
@@ -2267,6 +2273,14 @@ public:
*/
void pop_internal_handler();
+ /** Overloaded to guard query/query_length fields */
+ virtual void set_statement(Statement *stmt);
+
+ /**
+ Assign a new value to thd->query.
+ Protected with LOCK_thd_data mutex.
+ */
+ void set_query(char *query_arg, uint32 query_length_arg);
private:
/** The current internal error handler for this thread, or NULL. */
Internal_error_handler *m_internal_handler;
@@ -2479,6 +2493,7 @@ class select_export :public select_to_file {
*/
bool is_unsafe_field_sep;
bool fixed_row_size;
+ CHARSET_INFO *write_cs; // output charset
public:
select_export(sql_exchange *ex) :select_to_file(ex) {}
/**
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 677098d275a..0d4bf3f0b8f 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -187,6 +187,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete select;
free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0;
+ /*
+ Error was already created by quick select evaluation (check_quick()).
+ TODO: Add error code output parameter to Item::val_xxx() methods.
+ Currently they rely on the user checking DA for
+ errors when unwinding the stack after calling Item::val_xxx().
+ */
+ if (thd->is_error())
+ DBUG_RETURN(TRUE);
my_ok(thd, (ha_rows) thd->row_count_func);
/*
We don't need to call reset_auto_increment in this case, because
@@ -490,7 +498,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
if (select_lex->inner_refs_list.elements &&
fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
select_lex->fix_prepare_information(thd, conds, &fake_conds);
DBUG_RETURN(FALSE);
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 16810e29343..1e92d95573a 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -252,14 +252,37 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
/* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE;
+ /*
+ We use open_tables() here, rather than, say,
+ open_ltable() or open_table() because we would like to be able
+ to open a temporary table.
+ */
error= open_tables(thd, &tables, &counter, 0);
- /* restore the state and merge the opened table into handler_tables list */
if (thd->open_tables)
{
- thd->open_tables->next= thd->handler_tables;
- thd->handler_tables= thd->open_tables;
+ if (thd->open_tables->next)
+ {
+ /*
+ We opened something that is more than a single table.
+ This happens with MERGE engine. Don't try to link
+ this mess into thd->handler_tables list, close it
+ and report an error. We must do it right away
+ because mysql_ha_close_table(), called down the road,
+ can close a single table only.
+ */
+ close_thread_tables(thd);
+ my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
+ error= 1;
+ }
+ else
+ {
+ /* Merge the opened table into handler_tables list. */
+ thd->open_tables->next= thd->handler_tables;
+ thd->handler_tables= thd->open_tables;
+ }
}
+ /* Restore the state. */
thd->open_tables= backup_open_tables;
if (error)
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 104c81ca984..7d581f8c3a9 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1909,7 +1909,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
thread_count++;
pthread_mutex_unlock(&LOCK_thread_count);
di->thd.set_db(table_list->db, (uint) strlen(table_list->db));
- di->thd.query= my_strdup(table_list->table_name, MYF(MY_WME));
+ di->thd.set_query(my_strdup(table_list->table_name, MYF(MY_WME)), 0);
if (di->thd.db == NULL || di->thd.query == NULL)
{
/* The error is reported */
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 1758a6df5f9..b7f33d51335 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -148,6 +148,17 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
MYF(0));
DBUG_RETURN(TRUE);
}
+
+ /* Report problems with non-ascii separators */
+ if (!escaped->is_ascii() || !enclosed->is_ascii() ||
+ !field_term->is_ascii() ||
+ !ex->line_term->is_ascii() || !ex->line_start->is_ascii())
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED,
+ ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
+ }
+
if (open_and_lock_tables(thd, table_list))
DBUG_RETURN(TRUE);
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 542ce992537..fb5d58b63c4 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -457,6 +457,7 @@ pthread_handler_t handle_bootstrap(void *arg)
thd->init_for_queries();
while (fgets(buff, thd->net.max_packet, file))
{
+ char *query;
/* strlen() can't be deleted because fgets() doesn't return length */
ulong length= (ulong) strlen(buff);
while (buff[length-1] != '\n' && !feof(file))
@@ -489,11 +490,10 @@ pthread_handler_t handle_bootstrap(void *arg)
if (strncmp(buff, STRING_WITH_LEN("delimiter")) == 0)
continue;
- thd->query_length=length;
- thd->query= (char*) thd->memdup_w_gap(buff, length+1,
- thd->db_length+1+
- QUERY_CACHE_FLAGS_SIZE);
- thd->query[length] = '\0';
+ query= (char *) thd->memdup_w_gap(buff, length + 1,
+ thd->db_length + 1 +
+ QUERY_CACHE_FLAGS_SIZE);
+ thd->set_query(query, length);
DBUG_PRINT("query",("%-.4096s",thd->query));
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.start_new_query();
@@ -533,8 +533,9 @@ end:
#ifndef EMBEDDED_LIBRARY
(void) pthread_mutex_lock(&LOCK_thread_count);
thread_count--;
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ in_bootstrap= FALSE;
(void) pthread_cond_broadcast(&COND_thread_count);
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
my_thread_end();
pthread_exit(0);
#endif
@@ -658,8 +659,7 @@ int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name)
if (check_one_table_access(thd, SELECT_ACL, table_list))
goto err;
thd->free_list = 0;
- thd->query_length=(uint) strlen(tbl_name);
- thd->query = tbl_name;
+ thd->set_query(tbl_name, (uint) strlen(tbl_name));
if ((error = mysqld_dump_create_info(thd, table_list, -1)))
{
my_error(ER_GET_ERRNO, MYF(0), my_errno);
@@ -1239,9 +1239,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif
+ thd->set_query(beginning_of_next_stmt, length);
VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_length= length;
- thd->query= beginning_of_next_stmt;
/*
Count each statement from the client.
*/
@@ -1294,9 +1293,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
table_list.schema_table= schema_table;
}
- thd->query_length= (uint) (packet_end - packet); // Don't count end \0
- if (!(thd->query=fields= (char*) thd->memdup(packet,thd->query_length+1)))
+ uint query_length= (uint) (packet_end - packet); // Don't count end \0
+ if (!(fields= (char *) thd->memdup(packet, query_length + 1)))
break;
+ thd->set_query(fields, query_length);
general_log_print(thd, command, "%s %s", table_list.table_name, fields);
if (lower_case_table_names)
my_casedn_str(files_charset_info, table_list.table_name);
@@ -1589,13 +1589,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
log_slow_statement(thd);
thd_proc_info(thd, "cleaning up");
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
- thd_proc_info(thd, 0);
+ thd->set_query(NULL, 0);
thd->command=COM_SLEEP;
- thd->query=0;
- thd->query_length=0;
+ VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd_proc_info(thd, 0);
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
@@ -1788,6 +1787,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
bool alloc_query(THD *thd, const char *packet, uint packet_length)
{
+ char *query;
/* Remove garbage at start and end of query */
while (packet_length > 0 && my_isspace(thd->charset(), packet[0]))
{
@@ -1802,14 +1802,13 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length)
packet_length--;
}
/* We must allocate some extra memory for query cache */
- thd->query_length= 0; // Extra safety: Avoid races
- if (!(thd->query= (char*) thd->memdup_w_gap((uchar*) (packet),
- packet_length,
- thd->db_length+ 1 +
- QUERY_CACHE_FLAGS_SIZE)))
- return TRUE;
- thd->query[packet_length]=0;
- thd->query_length= packet_length;
+ if (! (query= (char*) thd->memdup_w_gap(packet,
+ packet_length,
+ 1 + thd->db_length +
+ QUERY_CACHE_FLAGS_SIZE)))
+ return TRUE;
+ query[packet_length]= '\0';
+ thd->set_query(query, packet_length);
/* Reclaim some memory */
thd->packet.shrink(thd->variables.net_buffer_length);
@@ -6950,7 +6949,7 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
continue;
if (tmp->thread_id == id)
{
- pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete
+ pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
@@ -6983,7 +6982,7 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
}
else
error=ER_KILL_DENIED_ERROR;
- pthread_mutex_unlock(&tmp->LOCK_delete);
+ pthread_mutex_unlock(&tmp->LOCK_thd_data);
}
DBUG_PRINT("exit", ("%d", error));
DBUG_RETURN(error);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index e7e821dd4ae..c1839b7220f 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -127,12 +127,12 @@ class Prepared_statement: public Statement
public:
enum flag_values
{
- IS_IN_USE= 1
+ IS_IN_USE= 1,
+ IS_SQL_PREPARE= 2
};
THD *thd;
Select_fetch_protocol_binary result;
- Protocol *protocol;
Item_param **param_array;
uint param_count;
uint last_errno;
@@ -148,7 +148,7 @@ public:
List<LEX_STRING>& varnames,
String *expanded_query);
public:
- Prepared_statement(THD *thd_arg, Protocol *protocol_arg);
+ Prepared_statement(THD *thd_arg);
virtual ~Prepared_statement();
void setup_set_params();
virtual Query_arena::Type type() const;
@@ -156,7 +156,8 @@ public:
bool set_name(LEX_STRING *name);
inline void close_cursor() { delete cursor; cursor= 0; }
inline bool is_in_use() { return flags & (uint) IS_IN_USE; }
- inline bool is_protocol_text() const { return protocol == &thd->protocol_text; }
+ inline bool is_sql_prepare() const { return flags & (uint) IS_SQL_PREPARE; }
+ void set_sql_prepare() { flags|= (uint) IS_SQL_PREPARE; }
bool prepare(const char *packet, uint packet_length);
bool execute_loop(String *expanded_query,
bool open_cursor,
@@ -1358,7 +1359,7 @@ static int mysql_test_select(Prepared_statement *stmt,
*/
if (unit->prepare(thd, 0, 0))
goto error;
- if (!lex->describe && !stmt->is_protocol_text())
+ if (!lex->describe && !stmt->is_sql_prepare())
{
/* Make copy of item list, as change_columns may change it */
List<Item> fields(lex->select_lex.item_list);
@@ -1988,7 +1989,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
break;
}
if (res == 0)
- DBUG_RETURN(stmt->is_protocol_text() ?
+ DBUG_RETURN(stmt->is_sql_prepare() ?
FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush()));
error:
DBUG_RETURN(TRUE);
@@ -2058,6 +2059,7 @@ static bool init_param_array(Prepared_statement *stmt)
void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
{
+ Protocol *save_protocol= thd->protocol;
Prepared_statement *stmt;
bool error;
DBUG_ENTER("mysqld_stmt_prepare");
@@ -2067,7 +2069,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
- if (! (stmt= new Prepared_statement(thd, &thd->protocol_binary)))
+ if (! (stmt= new Prepared_statement(thd)))
DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
if (thd->stmt_map.insert(thd, stmt))
@@ -2084,6 +2086,8 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
sp_cache_flush_obsolete(&thd->sp_proc_cache);
sp_cache_flush_obsolete(&thd->sp_func_cache);
+ thd->protocol= &thd->protocol_binary;
+
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
@@ -2097,6 +2101,9 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
}
+
+ thd->protocol= save_protocol;
+
/* check_prepared_statemnt sends the metadata packet in case of success */
DBUG_VOID_RETURN;
}
@@ -2229,7 +2236,6 @@ void mysql_sql_stmt_prepare(THD *thd)
const char *query;
uint query_len= 0;
DBUG_ENTER("mysql_sql_stmt_prepare");
- DBUG_ASSERT(thd->protocol == &thd->protocol_text);
if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
{
@@ -2247,11 +2253,13 @@ void mysql_sql_stmt_prepare(THD *thd)
}
if (! (query= get_dynamic_sql_string(lex, &query_len)) ||
- ! (stmt= new Prepared_statement(thd, &thd->protocol_text)))
+ ! (stmt= new Prepared_statement(thd)))
{
DBUG_VOID_RETURN; /* out of memory */
}
+ stmt->set_sql_prepare();
+
/* Set the name first, insert should know that this statement has a name */
if (stmt->set_name(name))
{
@@ -2431,6 +2439,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
String expanded_query;
uchar *packet_end= packet + packet_length;
Prepared_statement *stmt;
+ Protocol *save_protocol= thd->protocol;
bool open_cursor;
DBUG_ENTER("mysqld_stmt_execute");
@@ -2458,7 +2467,9 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY);
+ thd->protocol= &thd->protocol_binary;
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
+ thd->protocol= save_protocol;
/* Close connection socket; for use with client testing (Bug#43560). */
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
@@ -2814,12 +2825,11 @@ Select_fetch_protocol_binary::send_data(List<Item> &fields)
Prepared_statement
****************************************************************************/
-Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
+Prepared_statement::Prepared_statement(THD *thd_arg)
:Statement(NULL, &main_mem_root,
INITIALIZED, ++thd_arg->statement_id_counter),
thd(thd_arg),
result(thd_arg),
- protocol(protocol_arg),
param_array(0),
param_count(0),
last_errno(0),
@@ -3288,7 +3298,9 @@ Prepared_statement::reprepare()
bool cur_db_changed;
bool error;
- Prepared_statement copy(thd, &thd->protocol_text);
+ Prepared_statement copy(thd);
+
+ copy.set_sql_prepare(); /* To suppress sending metadata to the client. */
status_var_increment(thd->status_var.com_stmt_reprepare);
@@ -3346,7 +3358,7 @@ bool Prepared_statement::validate_metadata(Prepared_statement *copy)
return FALSE -- the metadata of the original SELECT,
if any, has not been sent to the client.
*/
- if (is_protocol_text() || lex->describe)
+ if (is_sql_prepare() || lex->describe)
return FALSE;
if (lex->select_lex.item_list.elements !=
@@ -3409,7 +3421,6 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy)
DBUG_ASSERT(thd == copy->thd);
last_error[0]= '\0';
last_errno= 0;
- /* Do not swap protocols, the copy always has protocol_text */
}
@@ -3550,8 +3561,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
thd->stmt_arena= this;
reinit_stmt_before_use(thd, lex);
- thd->protocol= protocol; /* activate stmt protocol */
-
/* Go! */
if (open_cursor)
@@ -3582,8 +3591,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (cur_db_changed)
mysql_change_db(thd, &saved_cur_db_name, TRUE);
- thd->protocol= &thd->protocol_text; /* use normal protocol */
-
/* Assert that if an error, no cursor is open */
DBUG_ASSERT(! (error && cursor));
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 476c8aaaefb..0ec8d91214c 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1102,7 +1102,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
if (tmp->command == COM_BINLOG_DUMP &&
tmp->server_id == slave_server_id)
{
- pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete
+ pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
@@ -1115,7 +1115,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
again. We just to do kill the thread ourselves.
*/
tmp->awake(THD::KILL_QUERY);
- pthread_mutex_unlock(&tmp->LOCK_delete);
+ pthread_mutex_unlock(&tmp->LOCK_thd_data);
}
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 37721660184..c900588212c 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1807,7 +1807,8 @@ JOIN::exec()
curr_join->having= curr_join->tmp_having= 0; // Allready done
/* Change sum_fields reference to calculated fields in tmp_table */
- curr_join->all_fields= *curr_all_fields;
+ if (curr_join != this)
+ curr_join->all_fields= *curr_all_fields;
if (!items1)
{
items1= items0 + all_fields.elements;
@@ -1826,8 +1827,11 @@ JOIN::exec()
fields_list.elements, all_fields))
DBUG_VOID_RETURN;
}
- curr_join->tmp_all_fields1= tmp_all_fields1;
- curr_join->tmp_fields_list1= tmp_fields_list1;
+ if (curr_join != this)
+ {
+ curr_join->tmp_all_fields1= tmp_all_fields1;
+ curr_join->tmp_fields_list1= tmp_fields_list1;
+ }
curr_join->items1= items1;
}
curr_all_fields= &tmp_all_fields1;
@@ -1975,8 +1979,11 @@ JOIN::exec()
tmp_fields_list2, tmp_all_fields2,
fields_list.elements, tmp_all_fields1))
DBUG_VOID_RETURN;
- curr_join->tmp_fields_list2= tmp_fields_list2;
- curr_join->tmp_all_fields2= tmp_all_fields2;
+ if (curr_join != this)
+ {
+ curr_join->tmp_fields_list2= tmp_fields_list2;
+ curr_join->tmp_all_fields2= tmp_all_fields2;
+ }
}
curr_fields_list= &curr_join->tmp_fields_list2;
curr_all_fields= &curr_join->tmp_all_fields2;
@@ -2031,8 +2038,11 @@ JOIN::exec()
tmp_table_param.save_copy_field= curr_join->tmp_table_param.copy_field;
tmp_table_param.save_copy_field_end=
curr_join->tmp_table_param.copy_field_end;
- curr_join->tmp_all_fields3= tmp_all_fields3;
- curr_join->tmp_fields_list3= tmp_fields_list3;
+ if (curr_join != this)
+ {
+ curr_join->tmp_all_fields3= tmp_all_fields3;
+ curr_join->tmp_fields_list3= tmp_fields_list3;
+ }
}
else
{
@@ -10652,6 +10662,11 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
if (table->s->db_type() != heap_hton ||
error != HA_ERR_RECORD_FILE_FULL)
{
+ /*
+ We don't want this error to be converted to a warning, e.g. in case of
+ INSERT IGNORE ... SELECT.
+ */
+ thd->fatal_error();
table->file->print_error(error,MYF(0));
DBUG_RETURN(1);
}
@@ -13132,9 +13147,17 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
for (nr=0; nr < table->s->keys ; nr++)
{
int direction;
+
if (keys.is_set(nr) &&
(direction= test_if_order_by_key(order, table, nr, &used_key_parts)))
{
+ /*
+ At this point we are sure that ref_key is a non-ordering
+ key (where "ordering key" is a key that will return rows
+ in the order required by ORDER BY).
+ */
+ DBUG_ASSERT (ref_key != (int) nr);
+
bool is_covering= table->covering_keys.is_set(nr) ||
(nr == table->s->primary_key &&
table->file->primary_key_is_clustered());
@@ -13215,7 +13238,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
*/
index_scan_time= select_limit/rec_per_key *
min(rec_per_key, table->file->scan_time());
- if (is_covering ||
+ if ((ref_key < 0 && is_covering) ||
(ref_key < 0 && (group || table->force_index)) ||
index_scan_time < read_time)
{
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 353c2b1d610..a0366d47149 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -466,7 +466,8 @@ public:
group_optimized_away= 0;
all_fields= fields_arg;
- fields_list= fields_arg;
+ if (&fields_list != &fields_arg) /* Avoid valgrind-warning */
+ fields_list= fields_arg;
bzero((char*) &keyuse,sizeof(keyuse));
tmp_table_param.init();
tmp_table_param.end_write_records= HA_POS_ERROR;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index d07e951bfd1..ae75609e2b6 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -74,9 +74,6 @@ static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **),
grant_names, NULL};
#endif
-/* Match the values of enum ha_choice */
-static const char *ha_choice_values[] = {"", "0", "1"};
-
static void store_key_options(THD *thd, String *packet, TABLE *table,
KEY *key_info);
@@ -1151,7 +1148,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
{
const LEX_STRING *const db=
table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db;
- if (strcmp(db->str, thd->db) != 0)
+ if (!thd->db || strcmp(db->str, thd->db))
{
append_identifier(thd, packet, db->str, db->length);
packet->append(STRING_WITH_LEN("."));
@@ -1428,11 +1425,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
/* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
if (share->db_create_options & HA_OPTION_CHECKSUM)
packet->append(STRING_WITH_LEN(" CHECKSUM=1"));
- if (share->page_checksum != HA_CHOICE_UNDEF)
- {
- packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM="));
- packet->append(ha_choice_values[(uint) share->page_checksum], 1);
- }
if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1"));
if (create_info.row_type != ROW_TYPE_DEFAULT)
@@ -1440,11 +1432,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" ROW_FORMAT="));
packet->append(ha_row_type[(uint) create_info.row_type]);
}
- if (share->transactional != HA_CHOICE_UNDEF)
- {
- packet->append(STRING_WITH_LEN(" TRANSACTIONAL="));
- packet->append(ha_choice_values[(uint) share->transactional], 1);
- }
if (table->s->key_block_size)
{
char *end;
@@ -1768,16 +1755,14 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thd_info->start_time= tmp->start_time;
thd_info->query=0;
+ /* Lock THD mutex that protects its data when looking at it. */
+ pthread_mutex_lock(&tmp->LOCK_thd_data);
if (tmp->query)
{
- /*
- query_length is always set to 0 when we set query = NULL; see
- the comment in sql_class.h why this prevents crashes in possible
- races with query_length
- */
uint length= min(max_query_length, tmp->query_length);
thd_info->query=(char*) thd->strmake(tmp->query,length);
}
+ pthread_mutex_unlock(&tmp->LOCK_thd_data);
thread_infos.append(thd_info);
}
}
@@ -3593,21 +3578,12 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
/* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
if (share->db_create_options & HA_OPTION_CHECKSUM)
ptr=strmov(ptr," checksum=1");
- if (share->page_checksum != HA_CHOICE_UNDEF)
- ptr= strxmov(ptr, " page_checksum=",
- ha_choice_values[(uint) share->page_checksum], NullS);
if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
ptr=strmov(ptr," delay_key_write=1");
if (share->row_type != ROW_TYPE_DEFAULT)
ptr=strxmov(ptr, " row_format=",
ha_row_type[(uint) share->row_type],
NullS);
- if (share->transactional != HA_CHOICE_UNDEF)
- {
- ptr= strxmov(ptr, " TRANSACTIONAL=",
- (share->transactional == HA_CHOICE_YES ? "1" : "0"),
- NullS);
- }
if (share->key_block_size)
{
ptr= strmov(ptr, " KEY_BLOCK_SIZE=");
@@ -3617,9 +3593,6 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
if (is_partitioned)
ptr= strmov(ptr, " partitioned");
#endif
- if (share->transactional != HA_CHOICE_UNDEF)
- ptr= strxmov(ptr, " transactional=",
- ha_choice_values[(uint) share->transactional], NullS);
table->field[19]->store(option_buff+1,
(ptr == option_buff ? 0 :
(uint) (ptr-option_buff)-1), cs);
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 7759985ba85..7c9793b273b 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1119,3 +1119,76 @@ void String::swap(String &s)
swap_variables(bool, alloced, s.alloced);
swap_variables(CHARSET_INFO*, str_charset, s.str_charset);
}
+
+
+/**
+ Convert string to printable ASCII string
+
+ @details This function converts input string "from" replacing non-ASCII bytes
+ with hexadecimal sequences ("\xXX") optionally appending "..." to the end of
+ the resulting string.
+ This function used in the ER_TRUNCATED_WRONG_VALUE_FOR_FIELD error messages,
+ e.g. when a string cannot be converted to a result charset.
+
+
+ @param to output buffer
+ @param to_len size of the output buffer (8 bytes or greater)
+ @param from input string
+ @param from_len size of the input string
+ @param from_cs input charset
+ @param nbytes maximal number of bytes to convert (from_len if 0)
+
+ @return number of bytes in the output string
+*/
+
+uint convert_to_printable(char *to, size_t to_len,
+ const char *from, size_t from_len,
+ CHARSET_INFO *from_cs, size_t nbytes /*= 0*/)
+{
+ /* needs at least 8 bytes for '\xXX...' and zero byte */
+ DBUG_ASSERT(to_len >= 8);
+
+ char *t= to;
+ char *t_end= to + to_len - 1; // '- 1' is for the '\0' at the end
+ const char *f= from;
+ const char *f_end= from + (nbytes ? min(from_len, nbytes) : from_len);
+ char *dots= to; // last safe place to append '...'
+
+ if (!f || t == t_end)
+ return 0;
+
+ for (; t < t_end && f < f_end; f++)
+ {
+ /*
+ If the source string is ASCII compatible (mbminlen==1)
+ and the source character is in ASCII printable range (0x20..0x7F),
+ then display the character as is.
+
+ Otherwise, if the source string is not ASCII compatible (e.g. UCS2),
+ or the source character is not in the printable range,
+ then print the character using HEX notation.
+ */
+ if (((unsigned char) *f) >= 0x20 &&
+ ((unsigned char) *f) <= 0x7F &&
+ from_cs->mbminlen == 1)
+ {
+ *t++= *f;
+ }
+ else
+ {
+ if (t_end - t < 4) // \xXX
+ break;
+ *t++= '\\';
+ *t++= 'x';
+ *t++= _dig_vec_upper[((unsigned char) *f) >> 4];
+ *t++= _dig_vec_upper[((unsigned char) *f) & 0x0F];
+ }
+ if (t_end - t >= 3) // '...'
+ dots= t;
+ }
+ if (f < from + from_len)
+ memcpy(dots, STRING_WITH_LEN("...\0"));
+ else
+ *t= '\0';
+ return t - to;
+}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index be11fea70dc..d62908e5d66 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -40,6 +40,9 @@ uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs,
size_t my_copy_with_hex_escaping(CHARSET_INFO *cs,
char *dst, size_t dstlen,
const char *src, size_t srclen);
+uint convert_to_printable(char *to, size_t to_len,
+ const char *from, size_t from_len,
+ CHARSET_INFO *from_cs, size_t nbytes= 0);
class String
{
@@ -366,6 +369,19 @@ public:
{
return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
}
+ bool is_ascii() const
+ {
+ if (length() == 0)
+ return TRUE;
+ if (charset()->mbminlen > 1)
+ return FALSE;
+ for (const char *c= ptr(), *end= c + length(); c < end; c++)
+ {
+ if (!my_isascii(*c))
+ return FALSE;
+ }
+ return TRUE;
+ }
};
static inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str,
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index e752421223a..0066c66eb59 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1799,6 +1799,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
int non_temp_tables_count= 0;
bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
String built_query;
+ String built_tmp_query;
DBUG_ENTER("mysql_rm_table_part2");
LINT_INIT(alias);
@@ -1866,6 +1867,25 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
case 0:
// removed temporary table
tmp_table_deleted= 1;
+ if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
+ thd->current_stmt_binlog_row_based)
+ {
+ if (built_tmp_query.is_empty())
+ {
+ built_tmp_query.set_charset(system_charset_info);
+ built_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS ");
+ }
+
+ built_tmp_query.append("`");
+ if (thd->db == NULL || strcmp(db,thd->db) != 0)
+ {
+ built_tmp_query.append(db);
+ built_tmp_query.append("`.`");
+ }
+ built_tmp_query.append(table->table_name);
+ built_tmp_query.append("`,");
+ }
+
continue;
case -1:
DBUG_ASSERT(thd->in_sub_stmt);
@@ -2023,29 +2043,52 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
write_bin_log(thd, !error, thd->query, thd->query_length);
}
else if (thd->current_stmt_binlog_row_based &&
- non_temp_tables_count > 0 &&
tmp_table_deleted)
{
+ if (non_temp_tables_count > 0)
+ {
+ /*
+ In this case we have deleted both temporary and
+ non-temporary tables, so:
+ - since we have deleted a non-temporary table we have to
+ binlog the statement, but
+ - since we have deleted a temporary table we cannot binlog
+ the statement (since the table may have not been created on the
+ slave - check "if" branch below, this might cause the slave to
+ stop).
+
+ Instead, we write a built statement, only containing the
+ non-temporary tables, to the binary log
+ */
+ built_query.chop(); // Chop of the last comma
+ built_query.append(" /* generated by server */");
+ write_bin_log(thd, !error, built_query.ptr(), built_query.length());
+ }
+
/*
- In this case we have deleted both temporary and
- non-temporary tables, so:
- - since we have deleted a non-temporary table we have to
- binlog the statement, but
- - since we have deleted a temporary table we cannot binlog
- the statement (since the table has not been created on the
- slave, this might cause the slave to stop).
-
- Instead, we write a built statement, only containing the
- non-temporary tables, to the binary log
+ One needs to always log any temporary table drop, if:
+ 1. thread logging format is mixed mode; AND
+ 2. current statement logging format is set to row.
*/
- built_query.chop(); // Chop of the last comma
- built_query.append(" /* generated by server */");
- write_bin_log(thd, !error, built_query.ptr(), built_query.length());
+ if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED)
+ {
+ /*
+ In this case we have deleted some temporary tables but we are using
+ row based logging for the statement. However, thread uses mixed mode
+ format, thence we need to log the dropping as we cannot tell for
+ sure whether the create was logged as statement previously or not, ie,
+ before switching to row mode.
+ */
+ built_tmp_query.chop(); // Chop of the last comma
+ built_tmp_query.append(" /* generated by server */");
+ write_bin_log(thd, !error, built_tmp_query.ptr(), built_tmp_query.length());
+ }
}
+
/*
The remaining cases are:
- - no tables where deleted and
- - only temporary tables where deleted and row-based
+ - no tables were deleted and
+ - only temporary tables were deleted and row-based
replication is used.
In both these cases, nothing should be written to the binary
log.
@@ -5885,8 +5928,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
create_info->key_block_size= table->s->key_block_size;
- if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL))
- create_info->transactional= table->s->transactional;
if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
{
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 17cc683e14a..6884f863326 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -292,7 +292,7 @@ int mysql_update(THD *thd,
if (select_lex->inner_refs_list.elements &&
fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
- DBUG_RETURN(-1);
+ DBUG_RETURN(1);
if (conds)
{
@@ -332,7 +332,14 @@ int mysql_update(THD *thd,
{
delete select;
free_underlaid_joins(thd, select_lex);
- if (error)
+ /*
+ There was an error or the error was already sent by
+ the quick select evaluation.
+ TODO: Add error code output parameter to Item::val_xxx() methods.
+ Currently they rely on the user checking DA for
+ errors when unwinding the stack after calling Item::val_xxx().
+ */
+ if (error || thd->is_error())
{
DBUG_RETURN(1); // Error in where
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 57a828a690b..320b43c8e5c 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -47,6 +47,12 @@
#include <myisam.h>
#include <myisammrg.h>
+/* this is to get the bison compilation windows warnings out */
+#ifdef _MSC_VER
+/* warning C4065: switch statement contains 'default' but no 'case' labels */
+#pragma warning (disable : 4065)
+#endif
+
int yylex(void *yylval, void *yythd);
const LEX_STRING null_lex_str= {0,0};
@@ -883,7 +889,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token OWNER_SYM
%token PACK_KEYS_SYM
%token PAGE_SYM
-%token PAGE_CHECKSUM_SYM
%token PARAM_MARKER
%token PARSER_SYM
%token PARTIAL /* SQL-2003-N */
@@ -1042,7 +1047,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token TO_SYM /* SQL-2003-R */
%token TRAILING /* SQL-2003-R */
%token TRANSACTION_SYM
-%token TRANSACTIONAL_SYM
%token TRIGGERS_SYM
%token TRIGGER_SYM /* SQL-2003-R */
%token TRIM /* SQL-2003-N */
@@ -4202,6 +4206,10 @@ opt_sub_partition:
if (Lex->part_info->no_subparts != 0 &&
!Lex->part_info->use_default_subpartitions)
{
+ /*
+ We come here when we have defined subpartitions on the first
+ partition but not on all the subsequent partitions.
+ */
my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
MYSQL_YYABORT;
}
@@ -4244,6 +4252,23 @@ sub_part_definition:
partition_info *part_info= lex->part_info;
partition_element *curr_part= part_info->current_partition;
partition_element *sub_p_elem= new partition_element(curr_part);
+ if (part_info->use_default_subpartitions &&
+ part_info->partitions.elements >= 2)
+ {
+ /*
+ create table t1 (a int)
+ partition by list (a) subpartition by hash (a)
+ (partition p0 values in (1),
+ partition p1 values in (2) subpartition sp11);
+ causes use to arrive since we are on the second
+ partition, but still use_default_subpartitions
+ is set. When we come here we're processing at least
+ the second partition (the current partition processed
+ have already been put into the partitions list.
+ */
+ my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
+ MYSQL_YYABORT;
+ }
if (!sub_p_elem ||
curr_part->subpartitions.push_back(sub_p_elem))
{
@@ -4460,11 +4485,6 @@ create_table_option:
Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM;
Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM;
}
- | PAGE_CHECKSUM_SYM opt_equal choice
- {
- Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM;
- Lex->create_info.page_checksum= $3;
- }
| DELAY_KEY_WRITE_SYM opt_equal ulong_num
{
Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE;
@@ -4524,11 +4544,6 @@ create_table_option:
Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE;
Lex->create_info.key_block_size= $3;
}
- | TRANSACTIONAL_SYM opt_equal choice
- {
- Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL;
- Lex->create_info.transactional= $3;
- }
;
default_charset:
@@ -4610,7 +4625,6 @@ row_types:
| COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; }
| REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; }
| COMPACT_SYM { $$= ROW_TYPE_COMPACT; }
- | PAGE_SYM { $$= ROW_TYPE_PAGE; }
;
merge_insert_types:
@@ -8493,6 +8507,7 @@ table_factor:
MYSQL_YYABORT;
sel->add_joined_table($$);
lex->pop_context();
+ lex->nest_level--;
}
else if ($4 || $6)
{
@@ -8501,7 +8516,11 @@ table_factor:
MYSQL_YYABORT;
}
else
+ {
+ /* nested join: FROM (t1 JOIN t2 ...),
+ nest_level is the same as in the outer query */
$$= $3;
+ }
}
;
@@ -9194,6 +9213,8 @@ into_destination:
!(lex->result= new select_export(lex->exchange, lex->nest_level)))
MYSQL_YYABORT;
}
+ opt_load_data_charset
+ { Lex->exchange->cs= $4; }
opt_field_term opt_line_term
| DUMPFILE TEXT_STRING_filesystem
{
@@ -11548,7 +11569,6 @@ keyword_sp:
| ONE_SYM {}
| PACK_KEYS_SYM {}
| PAGE_SYM {}
- | PAGE_CHECKSUM_SYM {}
| PARTIAL {}
| PARTITIONING_SYM {}
| PARTITIONS_SYM {}
@@ -11625,7 +11645,6 @@ keyword_sp:
| TEXT_SYM {}
| THAN_SYM {}
| TRANSACTION_SYM {}
- | TRANSACTIONAL_SYM {}
| TRIGGERS_SYM {}
| TIMESTAMP {}
| TIMESTAMP_ADD {}
diff --git a/sql/table.cc b/sql/table.cc
index 60a27e136b1..c1d79bdcdd3 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -724,8 +724,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (!head[32]) // New frm file in 3.23
{
share->avg_row_length= uint4korr(head+34);
- share->transactional= (ha_choice) (head[39] & 3);
- share->page_checksum= (ha_choice) ((head[39] >> 2) & 3);
share->row_type= (row_type) head[40];
share->table_charset= get_charset((uint) head[38],MYF(0));
share->null_field_first= 1;
@@ -2492,8 +2490,11 @@ File create_frm(THD *thd, const char *name, const char *db,
int4store(fileinfo+34,create_info->avg_row_length);
fileinfo[38]= (create_info->default_table_charset ?
create_info->default_table_charset->number : 0);
- fileinfo[39]= (uchar) ((uint) create_info->transactional |
- ((uint) create_info->page_checksum << 2));
+ /*
+ In future versions, we will store in fileinfo[39] the values of the
+ TRANSACTIONAL and PAGE_CHECKSUM clauses of CREATE TABLE.
+ */
+ fileinfo[39]= 0;
fileinfo[40]= (uchar) create_info->row_type;
/* Next few bytes where for RAID support */
fileinfo[41]= 0;
diff --git a/sql/table.h b/sql/table.h
index cb53013cd59..98ede52cd99 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -361,7 +361,9 @@ typedef struct st_table_share
}
enum row_type row_type; /* How rows are stored */
enum tmp_table_type tmp_table;
+ /** Transactional or not. Unused; reserved for future versions. */
enum ha_choice transactional;
+ /** Per-page checksums or not. Unused; reserved for future versions. */
enum ha_choice page_checksum;
uint ref_count; /* How many TABLE objects uses this */