summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt41
-rw-r--r--sql/event_scheduler.cc5
-rw-r--r--sql/field.cc281
-rw-r--r--sql/field.h75
-rw-r--r--sql/filesort.cc4
-rw-r--r--sql/gen_lex_token.cc353
-rw-r--r--sql/ha_ndbcluster_binlog.cc6
-rw-r--r--sql/handler.cc118
-rw-r--r--sql/handler.h27
-rw-r--r--sql/item.cc71
-rw-r--r--sql/item.h40
-rw-r--r--sql/item_cmpfunc.cc65
-rw-r--r--sql/item_cmpfunc.h9
-rw-r--r--sql/item_func.cc12
-rw-r--r--sql/item_geofunc.cc8
-rw-r--r--sql/item_geofunc.h4
-rw-r--r--sql/item_strfunc.cc10
-rw-r--r--sql/item_strfunc.h3
-rw-r--r--sql/item_sum.cc18
-rw-r--r--sql/lex.h4
-rw-r--r--sql/log.cc220
-rw-r--r--sql/log.h13
-rw-r--r--sql/log_event.cc63
-rw-r--r--sql/log_event_old.cc2
-rw-r--r--sql/message.rc4
-rw-r--r--sql/mysqld.cc99
-rw-r--r--sql/mysqld.h2
-rw-r--r--sql/net_serv.cc2
-rw-r--r--sql/opt_subselect.cc62
-rw-r--r--sql/opt_subselect.h4
-rw-r--r--sql/parse_file.cc2
-rw-r--r--sql/rpl_gtid.cc75
-rw-r--r--sql/rpl_gtid.h2
-rw-r--r--sql/rpl_mi.cc45
-rw-r--r--sql/rpl_mi.h1
-rw-r--r--sql/rpl_parallel.cc375
-rw-r--r--sql/rpl_parallel.h15
-rw-r--r--sql/rpl_rli.cc96
-rw-r--r--sql/rpl_rli.h35
-rw-r--r--sql/rpl_utility.cc2
-rw-r--r--sql/share/errmsg-utf8.txt2
-rw-r--r--sql/slave.cc183
-rw-r--r--sql/sp.cc6
-rw-r--r--sql/sp_head.cc5
-rw-r--r--sql/sql_acl.cc162
-rw-r--r--sql/sql_admin.cc57
-rw-r--r--sql/sql_base.cc160
-rw-r--r--sql/sql_base.h4
-rw-r--r--sql/sql_class.cc47
-rw-r--r--sql/sql_class.h44
-rw-r--r--sql/sql_connect.cc13
-rw-r--r--sql/sql_cursor.cc4
-rw-r--r--sql/sql_db.cc6
-rw-r--r--sql/sql_delete.cc8
-rw-r--r--sql/sql_derived.cc26
-rw-r--r--sql/sql_derived.h1
-rw-r--r--sql/sql_digest.cc683
-rw-r--r--sql/sql_digest.h130
-rw-r--r--sql/sql_digest_stream.h51
-rw-r--r--sql/sql_insert.cc27
-rw-r--r--sql/sql_lex.cc79
-rw-r--r--sql/sql_lex.h37
-rw-r--r--sql/sql_parse.cc62
-rw-r--r--sql/sql_partition.cc1
-rw-r--r--sql/sql_partition_admin.cc11
-rw-r--r--sql/sql_plugin.cc41
-rw-r--r--sql/sql_rename.cc9
-rw-r--r--sql/sql_repl.cc57
-rw-r--r--sql/sql_select.cc123
-rw-r--r--sql/sql_select.h20
-rw-r--r--sql/sql_servers.cc3
-rw-r--r--sql/sql_show.cc19
-rw-r--r--sql/sql_table.cc94
-rw-r--r--sql/sql_test.cc2
-rw-r--r--sql/sql_truncate.cc10
-rw-r--r--sql/sql_update.cc4
-rw-r--r--sql/sql_view.cc167
-rw-r--r--sql/sql_view.h5
-rw-r--r--sql/sql_yacc.yy96
-rw-r--r--sql/structs.h1
-rw-r--r--sql/sys_vars.cc22
-rw-r--r--sql/table.cc108
-rw-r--r--sql/table.h26
-rw-r--r--sql/transaction.cc2
-rw-r--r--sql/tztime.cc3
-rw-r--r--sql/udf_example.c5
-rw-r--r--sql/uniques.cc14
-rw-r--r--sql/winservice.h80
88 files changed, 3935 insertions, 1058 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index c59ae3b7846..88ec26eb6f6 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+# Copyright (c) 2006, 2014, Oracle and/or its affiliates.
+# Copyright (c) 2010, 2015, MariaDB
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -27,8 +28,26 @@ ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h
${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
)
+SET(GEN_DIGEST_SOURCES
+ ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
+)
+
+SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES}
+ ${GEN_DIGEST_SOURCES}
+ PROPERTIES GENERATED 1)
+
+# Gen_lex_token
+# Make sure sql_yacc.h is generated before compiling gen_lex_token
+IF(NOT CMAKE_CROSSCOMPILING)
+ ADD_EXECUTABLE(gen_lex_token gen_lex_token.cc)
+ ADD_DEPENDENCIES(gen_lex_token GenServerSource)
+ENDIF()
-SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED 1)
+ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
+ COMMAND gen_lex_token > lex_token.h
+ DEPENDS gen_lex_token
+)
ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER)
@@ -58,7 +77,8 @@ SET (SQL_SOURCE
slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc
sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc
sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h
- sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc sql_do.cc
+ sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc
+ sql_digest.cc sql_do.cc
sql_error.cc sql_handler.cc sql_get_diagnostics.cc
sql_help.cc sql_insert.cc sql_lex.cc
sql_list.cc sql_load.cc sql_manager.cc
@@ -95,6 +115,7 @@ SET (SQL_SOURCE
table_cache.cc
${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
${GEN_SOURCES}
+ ${GEN_DIGEST_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)
@@ -115,6 +136,7 @@ RECOMPILE_FOR_EMBEDDED)
ADD_LIBRARY(sql STATIC ${SQL_SOURCE})
ADD_DEPENDENCIES(sql GenServerSource)
+ADD_DEPENDENCIES(sql GenDigestServerSource)
DTRACE_INSTRUMENT(sql)
TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS}
mysys mysys_ssl dbug strings vio pcre ${LIBJEMALLOC}
@@ -244,6 +266,11 @@ ADD_CUSTOM_TARGET(
DEPENDS ${GEN_SOURCES}
)
+ADD_CUSTOM_TARGET(
+ GenDigestServerSource
+ DEPENDS ${GEN_DIGEST_SOURCES}
+)
+
#Need this only for embedded
SET_TARGET_PROPERTIES(GenServerSource PROPERTIES EXCLUDE_FROM_ALL TRUE)
@@ -263,14 +290,15 @@ IF(WIN32 OR HAVE_DLOPEN AND NOT DISABLE_SHARED)
ENDIF()
FOREACH(tool glibtoolize libtoolize aclocal autoconf autoheader automake gtar
- tar bzr)
+ tar git)
STRING(TOUPPER ${tool} TOOL)
FIND_PROGRAM(${TOOL}_EXECUTABLE ${tool} DOC "path to the executable")
MARK_AS_ADVANCED(${TOOL}_EXECUTABLE)
ENDFOREACH()
CONFIGURE_FILE(
- ${CMAKE_SOURCE_DIR}/cmake/make_dist.cmake.in ${CMAKE_BINARY_DIR}/make_dist.cmake @ONLY)
+ ${CMAKE_SOURCE_DIR}/cmake/make_dist.cmake.in
+ ${CMAKE_BINARY_DIR}/make_dist.cmake @ONLY)
ADD_CUSTOM_TARGET(dist
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/make_dist.cmake
@@ -280,8 +308,7 @@ ADD_CUSTOM_TARGET(dist
ADD_CUSTOM_TARGET(distclean
COMMAND ${CMAKE_COMMAND} -E echo WARNING: distclean target is not functional
- COMMAND ${CMAKE_COMMAND} -E echo Use 'bzr clean-tree' with --unknown and/or
- --ignored parameter instead
+ COMMAND ${CMAKE_COMMAND} -E echo Use 'git clean -fdx' instead
VERBATIM
)
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index f2b3a77f414..5c4926c830c 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -301,6 +301,9 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
Event_job_data job_data;
bool res;
+ DBUG_ASSERT(thd->m_digest == NULL);
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
+
thd->thread_stack= &my_stack; // remember where our stack is
res= post_init_event_thread(thd);
@@ -329,6 +332,8 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
job_data.definer.str,
job_data.dbname.str, job_data.name.str);
end:
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
+ DBUG_ASSERT(thd->m_digest == NULL);
DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
event->name.str));
diff --git a/sql/field.cc b/sql/field.cc
index e7e046a8458..c5319ae0b72 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1307,18 +1307,13 @@ int Field_num::check_int(CHARSET_INFO *cs, const char *str, int length,
if (str == int_end || error == MY_ERRNO_EDOM)
{
ErrConvString err(str, length, cs);
- push_warning_printf(get_thd(), Sql_condition::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "integer", err.ptr(), field_name,
- (ulong) table->in_use->get_stmt_da()->
- current_row_for_warning());
+ set_warning_truncated_wrong_value("integer", err.ptr());
return 1;
}
/* Test if we have garbage at the end of the given string. */
if (test_if_important_data(cs, int_end, str + length))
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
return 2;
}
return 0;
@@ -1387,7 +1382,7 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len,
return 0;
out_of_range:
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
@@ -1408,12 +1403,12 @@ int Field::warn_if_overflow(int op_result)
{
if (op_result == E_DEC_OVERFLOW)
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
if (op_result == E_DEC_TRUNCATED)
{
- set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ set_note(WARN_DATA_TRUNCATED, 1);
/* We return 0 here as this is not a critical issue */
}
return 0;
@@ -1739,7 +1734,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val,
{
if (val->sign())
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
i= 0;
*err= 1;
}
@@ -2047,7 +2042,7 @@ void Field_decimal::overflow(bool negative)
uint len=field_length;
uchar *to=ptr, filler= '9';
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
if (negative)
{
if (!unsigned_flag)
@@ -2155,7 +2150,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
from++;
if (from == end)
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
is_cuted_fields_incr=1;
}
else if (*from == '+' || *from == '-') // Found some sign ?
@@ -2231,7 +2226,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
for (;from != end && my_isspace(&my_charset_bin, *from); from++) ;
if (from != end) // If still something left, warn
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
is_cuted_fields_incr=1;
}
}
@@ -2409,8 +2404,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
if (tmp_char != '0') // Losing a non zero digit ?
{
if (!is_cuted_fields_incr)
- set_warning(Sql_condition::WARN_LEVEL_WARN,
- WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
return 0;
}
continue;
@@ -2432,7 +2426,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
This is a note, not a warning, as we don't want to abort
when we cut decimals in strict mode
*/
- set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ set_note(WARN_DATA_TRUNCATED, 1);
}
return 0;
}
@@ -2633,7 +2627,7 @@ void Field_decimal::sql_type(String &res) const
if (dec)
tmp--;
res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
- "decimal(%d,%d)",tmp,dec));
+ "decimal(%d,%d)/*old*/",tmp,dec));
add_zerofill_and_unsigned(res);
}
@@ -2782,7 +2776,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
if (unsigned_flag && decimal_value->sign())
{
DBUG_PRINT("info", ("unsigned overflow"));
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
decimal_value= &decimal_zero;
}
@@ -2826,32 +2820,22 @@ int Field_new_decimal::store(const char *from, uint length,
thd->abort_on_warning)
{
ErrConvString errmsg(from, length, charset_arg);
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "decimal", errmsg.ptr(), field_name,
- static_cast<ulong>(thd->get_stmt_da()->
- current_row_for_warning()));
+ set_warning_truncated_wrong_value("decimal", errmsg.ptr());
DBUG_RETURN(err);
}
switch (err) {
case E_DEC_TRUNCATED:
- set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ set_note(WARN_DATA_TRUNCATED, 1);
break;
case E_DEC_OVERFLOW:
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
set_value_on_overflow(&decimal_value, decimal_value.sign());
break;
case E_DEC_BAD_NUM:
{
ErrConvString errmsg(from, length, charset_arg);
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "decimal", errmsg.ptr(), field_name,
- static_cast<ulong>(thd->get_stmt_da()->
- current_row_for_warning()));
+ set_warning_truncated_wrong_value("decimal", errmsg.ptr());
my_decimal_set_zero(&decimal_value);
break;
}
@@ -3158,13 +3142,13 @@ int Field_tiny::store(double nr)
if (nr < 0.0)
{
*ptr=0;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 255.0)
{
*ptr= (uchar) 255;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3175,13 +3159,13 @@ int Field_tiny::store(double nr)
if (nr < -128.0)
{
*ptr= (uchar) -128;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 127.0)
{
*ptr=127;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3201,13 +3185,13 @@ int Field_tiny::store(longlong nr, bool unsigned_val)
if (nr < 0 && !unsigned_val)
{
*ptr= 0;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if ((ulonglong) nr > (ulonglong) 255)
{
*ptr= (char) 255;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3220,13 +3204,13 @@ int Field_tiny::store(longlong nr, bool unsigned_val)
if (nr < -128)
{
*ptr= (char) -128;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 127)
{
*ptr=127;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3337,13 +3321,13 @@ int Field_short::store(double nr)
if (nr < 0)
{
res=0;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) UINT_MAX16)
{
res=(int16) UINT_MAX16;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3354,13 +3338,13 @@ int Field_short::store(double nr)
if (nr < (double) INT_MIN16)
{
res=INT_MIN16;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) INT_MAX16)
{
res=INT_MAX16;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3382,13 +3366,13 @@ int Field_short::store(longlong nr, bool unsigned_val)
if (nr < 0L && !unsigned_val)
{
res=0;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if ((ulonglong) nr > (ulonglong) UINT_MAX16)
{
res=(int16) UINT_MAX16;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3402,13 +3386,13 @@ int Field_short::store(longlong nr, bool unsigned_val)
if (nr < INT_MIN16)
{
res=INT_MIN16;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (longlong) INT_MAX16)
{
res=INT_MAX16;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3525,14 +3509,14 @@ int Field_medium::store(double nr)
if (nr < 0)
{
int3store(ptr,0);
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr >= (double) (long) (1L << 24))
{
uint32 tmp=(uint32) (1L << 24)-1L;
int3store(ptr,tmp);
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3544,14 +3528,14 @@ int Field_medium::store(double nr)
{
long tmp=(long) INT_MIN24;
int3store(ptr,tmp);
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) INT_MAX24)
{
long tmp=(long) INT_MAX24;
int3store(ptr,tmp);
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3571,14 +3555,14 @@ int Field_medium::store(longlong nr, bool unsigned_val)
if (nr < 0 && !unsigned_val)
{
int3store(ptr,0);
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24))
{
long tmp= (long) (1L << 24)-1L;
int3store(ptr,tmp);
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3593,14 +3577,14 @@ int Field_medium::store(longlong nr, bool unsigned_val)
{
long tmp= (long) INT_MIN24;
int3store(ptr,tmp);
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (longlong) INT_MAX24)
{
long tmp=(long) INT_MAX24;
int3store(ptr,tmp);
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3722,7 +3706,7 @@ int Field_long::store(double nr)
else if (nr > (double) UINT_MAX32)
{
res= UINT_MAX32;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3744,7 +3728,7 @@ int Field_long::store(double nr)
res=(int32) (longlong) nr;
}
if (error)
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int4store(ptr,res);
return error;
@@ -3790,7 +3774,7 @@ int Field_long::store(longlong nr, bool unsigned_val)
res=(int32) nr;
}
if (error)
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int4store(ptr,res);
return error;
@@ -3889,7 +3873,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&error);
if (error == MY_ERRNO_ERANGE)
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (get_thd()->count_cuted_fields &&
@@ -3911,7 +3895,7 @@ int Field_longlong::store(double nr)
res= double_to_longlong(nr, unsigned_flag, &error);
if (error)
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int8store(ptr,res);
return error;
@@ -3932,7 +3916,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val)
if (unsigned_flag != unsigned_val)
{
nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
}
@@ -4046,8 +4030,7 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs)
if (error || (!len || ((uint) (end-from) != len &&
get_thd()->count_cuted_fields)))
{
- set_warning(Sql_condition::WARN_LEVEL_WARN,
- (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1);
+ set_warning(error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED, 1);
error= error ? 1 : 2;
}
Field_float::store(nr);
@@ -4063,7 +4046,7 @@ int Field_float::store(double nr)
unsigned_flag, FLT_MAX);
if (error)
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
if (error < 0) // Wrong double value
{
error= 1;
@@ -4234,8 +4217,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs)
if (error || (!len || ((uint) (end-from) != len &&
get_thd()->count_cuted_fields)))
{
- set_warning(Sql_condition::WARN_LEVEL_WARN,
- (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1);
+ set_warning(error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED, 1);
error= error ? 1 : 2;
}
Field_double::store(nr);
@@ -4251,7 +4233,7 @@ int Field_double::store(double nr)
unsigned_flag, DBL_MAX);
if (error)
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
if (error < 0) // Wrong double value
{
error= 1;
@@ -4612,7 +4594,7 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
if (MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) || !have_smth_to_conv)
{
error= 1;
- set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
+ set_datetime_warning(WARN_DATA_TRUNCATED,
str, MYSQL_TIMESTAMP_DATETIME, 1);
}
else if (MYSQL_TIME_WARN_HAVE_NOTES(was_cut))
@@ -4630,7 +4612,7 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
conversion_error= ER_WARN_DATA_OUT_OF_RANGE;
if (conversion_error)
{
- set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, conversion_error,
+ set_datetime_warning(conversion_error,
str, MYSQL_TIMESTAMP_DATETIME, !error);
error= 1;
}
@@ -5128,7 +5110,7 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED,
str, mysql_type_to_time_type(type()), 1);
if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
- set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
+ set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE,
str, mysql_type_to_time_type(type()), 1);
}
@@ -5656,7 +5638,7 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs)
error == MY_ERRNO_ERANGE)
{
*ptr=0;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
if (get_thd()->count_cuted_fields &&
@@ -5699,7 +5681,7 @@ int Field_year::store(longlong nr, bool unsigned_val)
if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155)
{
*ptr= 0;
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
if (nr != 0 || field_length != 4) // 0000 -> 0; 00 -> 2000
@@ -5720,8 +5702,7 @@ int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec)
if (Field_year::store(ltime->year, 0))
return 1;
- set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
- &str, ltime->time_type, 1);
+ set_datetime_warning(WARN_DATA_TRUNCATED, &str, ltime->time_type, 1);
return 0;
}
@@ -6281,31 +6262,21 @@ bool Field_datetimef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
TRUE - If an error happened
*/
-static bool
-check_string_copy_error(Field_str *field,
- const char *well_formed_error_pos,
- const char *cannot_convert_error_pos,
- const char *end,
- CHARSET_INFO *cs)
+bool
+Field_longstr::check_string_copy_error(const char *well_formed_error_pos,
+ const char *cannot_convert_error_pos,
+ const char *end,
+ CHARSET_INFO *cs)
{
const char *pos;
char tmp[32];
- THD *thd;
-
- thd= field->get_thd();
if (!(pos= well_formed_error_pos) &&
!(pos= cannot_convert_error_pos))
return FALSE;
convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
-
- push_warning_printf(thd,
- Sql_condition::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "string", tmp, field->field_name,
- thd->get_stmt_da()->current_row_for_warning());
+ set_warning_truncated_wrong_value("string", tmp);
return TRUE;
}
@@ -6340,14 +6311,14 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
if (test_if_important_data(field_charset, pstr, end))
{
if (thd->abort_on_warning)
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ set_warning(ER_DATA_TOO_LONG, 1);
else
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
return 2;
}
else if (count_spaces)
{ /* If we lost only spaces then produce a NOTE, not a WARNING */
- set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ set_note(WARN_DATA_TRUNCATED, 1);
return 2;
}
}
@@ -6382,7 +6353,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
field_length-copy_length,
field_charset->pad_char);
- if (check_string_copy_error(this, well_formed_error_pos,
+ if (check_string_copy_error(well_formed_error_pos,
cannot_convert_error_pos, from + length, cs))
return 2;
@@ -6413,9 +6384,9 @@ int Field_str::store(double nr)
if (error)
{
if (get_thd()->abort_on_warning)
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ set_warning(ER_DATA_TOO_LONG, 1);
else
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
}
return store(buff, length, &my_charset_numeric);
}
@@ -6891,7 +6862,7 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
else
int2store(ptr, copy_length);
- if (check_string_copy_error(this, well_formed_error_pos,
+ if (check_string_copy_error(well_formed_error_pos,
cannot_convert_error_pos, from + length, cs))
return 2;
@@ -7417,7 +7388,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
tmp= value.ptr();
bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
- if (check_string_copy_error(this, well_formed_error_pos,
+ if (check_string_copy_error(well_formed_error_pos,
cannot_convert_error_pos, from + length, cs))
return 2;
@@ -7923,6 +7894,14 @@ err_exit:
return -1;
}
+Field::geometry_type Field_geom::geometry_type_merge(geometry_type a,
+ geometry_type b)
+{
+ if (a == b)
+ return a;
+ return Field::GEOM_GEOMETRY;
+}
+
#endif /*HAVE_SPATIAL*/
/****************************************************************************
@@ -7983,13 +7962,13 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
if (err || end != from+length || tmp > typelib->count)
{
tmp=0;
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
}
if (!get_thd()->count_cuted_fields)
err= 0;
}
else
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
}
store_type((ulonglong) tmp);
return err;
@@ -8008,7 +7987,7 @@ int Field_enum::store(longlong nr, bool unsigned_val)
int error= 0;
if ((ulonglong) nr > typelib->count || nr == 0)
{
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
if (nr != 0 || get_thd()->count_cuted_fields)
{
nr= 0;
@@ -8161,11 +8140,11 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1))
{
tmp=0;
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
}
}
else if (got_warning)
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
store_type(tmp);
return err;
}
@@ -8185,7 +8164,7 @@ int Field_set::store(longlong nr, bool unsigned_val)
if ((ulonglong) nr > max_nr)
{
nr&= max_nr;
- set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
error=1;
}
store_type((ulonglong) nr);
@@ -8537,9 +8516,9 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len);
memset(ptr, 0xff, bytes_in_rec);
if (get_thd()->really_abort_on_warning())
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ set_warning(ER_DATA_TOO_LONG, 1);
else
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
/* delta is >= -1 here */
@@ -8974,9 +8953,9 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
if (bits)
*ptr&= ((1 << bits) - 1); /* set first uchar */
if (get_thd()->really_abort_on_warning())
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ set_warning(ER_DATA_TOO_LONG, 1);
else
- set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
bzero(ptr, delta);
@@ -9923,35 +9902,52 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
char_length= length;
/*
- Copy the default value from the column object orig_field, if:
- 1) The column has a constant default value.
- 2) The column type is not a BLOB type.
- 3) The original column (old_field) was properly initialized with a record
- buffer pointer.
- 4) The original column doesn't have a default function to auto-initialize
- the column on INSERT
+ Copy the default (constant/function) from the column object orig_field, if
+ supplied. We do this if all these conditions are met:
+
+ - The column allows a default.
+
+ - The column type is not a BLOB type.
+
+ - The original column (old_field) was properly initialized with a record
+ buffer pointer.
*/
- if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && // 1) 2)
- old_field->ptr && orig_field && // 3)
- !old_field->has_insert_default_function()) // 4)
- {
- char buff[MAX_FIELD_WIDTH];
- String tmp(buff,sizeof(buff), charset);
- my_ptrdiff_t diff;
-
- /* Get the value from default_values */
- diff= (my_ptrdiff_t) (orig_field->table->s->default_values-
- orig_field->table->record[0]);
- orig_field->move_field_offset(diff); // Points now at default_values
- if (!orig_field->is_real_null())
+ if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
+ old_field->ptr != NULL &&
+ orig_field != NULL)
+ {
+ bool default_now= false;
+ if (real_type_with_now_as_default(sql_type))
{
- char buff[MAX_FIELD_WIDTH], *pos;
- String tmp(buff, sizeof(buff), charset), *res;
- res= orig_field->val_str(&tmp);
- pos= (char*) sql_strmake(res->ptr(), res->length());
- def= new Item_string(pos, res->length(), charset);
+ // The SQL type of the new field allows a function default:
+ default_now= orig_field->has_insert_default_function();
+ bool update_now= orig_field->has_update_default_function();
+
+ if (default_now && update_now)
+ unireg_check= Field::TIMESTAMP_DNUN_FIELD;
+ else if (default_now)
+ unireg_check= Field::TIMESTAMP_DN_FIELD;
+ else if (update_now)
+ unireg_check= Field::TIMESTAMP_UN_FIELD;
+ }
+ if (!default_now) // Give a constant default
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff), charset);
+
+ /* Get the value from default_values */
+ my_ptrdiff_t diff= orig_field->table->default_values_offset();
+ orig_field->move_field_offset(diff); // Points now at default_values
+ if (!orig_field->is_real_null())
+ {
+ char buff[MAX_FIELD_WIDTH], *pos;
+ String tmp(buff, sizeof(buff), charset), *res;
+ res= orig_field->val_str(&tmp);
+ pos= (char*) sql_strmake(res->ptr(), res->length());
+ def= new Item_string(pos, res->length(), charset);
+ }
+ orig_field->move_field_offset(-diff); // Back to record[0]
}
- orig_field->move_field_offset(-diff); // Back to record[0]
}
}
@@ -10102,6 +10098,19 @@ void Field::set_datetime_warning(Sql_condition::enum_warning_level level,
}
+void Field::set_warning_truncated_wrong_value(const char *type,
+ const char *value)
+{
+ THD *thd= get_thd();
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
+ ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
+ type, value, field_name,
+ static_cast<ulong>(thd->get_stmt_da()->
+ current_row_for_warning()));
+}
+
+
/*
@brief
Return possible keys for a field
diff --git a/sql/field.h b/sql/field.h
index 0390e95f954..a40e2ef9913 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1,7 +1,7 @@
#ifndef FIELD_INCLUDED
#define FIELD_INCLUDED
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, SkySQL Ab.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -119,6 +119,20 @@ inline bool is_temporal_type_with_date(enum_field_types type)
/**
+ Tests if a field real type can have "DEFAULT CURRENT_TIMESTAMP"
+
+ @param type Field type, as returned by field->real_type().
+ @retval true If field real type can have "DEFAULT CURRENT_TIMESTAMP".
+ @retval false If field real type can not have "DEFAULT CURRENT_TIMESTAMP".
+*/
+inline bool real_type_with_now_as_default(enum_field_types type)
+{
+ return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2 ||
+ type == MYSQL_TYPE_DATETIME || type == MYSQL_TYPE_DATETIME2;
+}
+
+
+/**
Recognizer for concrete data type (called real_type for some reason),
returning true if it is one of the TIMESTAMP types.
*/
@@ -656,36 +670,32 @@ public:
inline bool is_null(my_ptrdiff_t row_offset= 0) const
{
/*
- If the field is NULLable, it returns NULLity based
- on null_ptr[row_offset] value. Otherwise it returns
- NULL flag depending on TABLE::null_row value.
-
The table may have been marked as containing only NULL values
for all fields if it is a NULL-complemented row of an OUTER JOIN
or if the query is an implicitly grouped query (has aggregate
functions but no GROUP BY clause) with no qualifying rows. If
- this is the case (in which TABLE::null_row is true) and the
- field is not nullable, the field is considered to be NULL.
-
- Do not change the order of testing. Fields may be associated
- with a TABLE object without being part of the current row.
- For NULL value check to work for these fields, they must
- have a valid null_ptr, and this pointer must be checked before
- TABLE::null_row.
+ this is the case (in which TABLE::null_row is true), the field
+ is considered to be NULL.
Note that if a table->null_row is set then also all null_bits are
set for the row.
+
+ In the case of the 'result_field' for GROUP BY, table->null_row might
+ refer to the *next* row in the table (when the algorithm is: read the
+ next row, see if any of group column values have changed, send the
+ result - grouped - row to the client if yes). So, table->null_row might
+ be wrong, but such a result_field is always nullable (that's defined by
+ original_field->maybe_null()) and we trust its null bit.
*/
- return real_maybe_null() ?
- MY_TEST(null_ptr[row_offset] & null_bit) : table->null_row;
+ return null_ptr ? null_ptr[row_offset] & null_bit : table->null_row;
}
inline bool is_real_null(my_ptrdiff_t row_offset= 0) const
- { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : 0; }
+ { return null_ptr && (null_ptr[row_offset] & null_bit); }
inline bool is_null_in_record(const uchar *record) const
{
if (!null_ptr)
return 0;
- return MY_TEST(record[(uint) (null_ptr - table->record[0])] & null_bit);
+ return record[(uint) (null_ptr - table->record[0])] & null_bit;
}
inline void set_null(my_ptrdiff_t row_offset= 0)
{ if (null_ptr) null_ptr[row_offset]|= null_bit; }
@@ -875,14 +885,32 @@ public:
virtual int set_time() { return 1; }
bool set_warning(Sql_condition::enum_warning_level, unsigned int code,
int cuted_increment) const;
+protected:
+ bool set_warning(unsigned int code, int cuted_increment) const
+ {
+ return set_warning(Sql_condition::WARN_LEVEL_WARN, code, cuted_increment);
+ }
+ bool set_note(unsigned int code, int cuted_increment) const
+ {
+ return set_warning(Sql_condition::WARN_LEVEL_NOTE, code, cuted_increment);
+ }
void set_datetime_warning(Sql_condition::enum_warning_level, uint code,
const ErrConv *str, timestamp_type ts_type,
int cuted_increment);
+ void set_datetime_warning(uint code,
+ const ErrConv *str, timestamp_type ts_type,
+ int cuted_increment)
+ {
+ set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, code, str, ts_type,
+ cuted_increment);
+ }
+ void set_warning_truncated_wrong_value(const char *type, const char *value);
inline bool check_overflow(int op_result)
{
return (op_result == E_DEC_OVERFLOW);
}
int warn_if_overflow(int op_result);
+public:
void set_table_name(String *alias)
{
table_name= &alias->Ptr;
@@ -1129,6 +1157,10 @@ class Field_longstr :public Field_str
protected:
int report_if_important_data(const char *ptr, const char *end,
bool count_spaces);
+ bool check_string_copy_error(const char *well_formed_error_pos,
+ const char *cannot_convert_error_pos,
+ const char *end,
+ CHARSET_INFO *cs);
public:
Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
@@ -2565,6 +2597,14 @@ public:
int store(longlong nr, bool unsigned_val);
int store_decimal(const my_decimal *);
uint size_of() const { return sizeof(*this); }
+ /**
+ Key length is provided only to support hash joins. (compared byte for byte)
+ Ex: SELECT .. FROM t1,t2 WHERE t1.field_geom1=t2.field_geom2.
+
+ The comparison is not very relevant, as identical geometry might be
+ represented differently, but we need to support it either way.
+ */
+ uint32 key_length() const { return packlength; }
/**
Non-nullable GEOMETRY types cannot have defaults,
@@ -2573,6 +2613,7 @@ public:
int reset(void) { return Field_blob::reset() || !maybe_null(); }
geometry_type get_geometry_type() { return geom_type; };
+ static geometry_type geometry_type_merge(geometry_type, geometry_type);
};
#endif /*HAVE_SPATIAL*/
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 027437fca67..a545bb623c0 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/sql/gen_lex_token.cc b/sql/gen_lex_token.cc
new file mode 100644
index 00000000000..3584dd60c62
--- /dev/null
+++ b/sql/gen_lex_token.cc
@@ -0,0 +1,353 @@
+/*
+ Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include <my_global.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* We only need the tokens here */
+#define YYSTYPE_IS_DECLARED
+#include <sql_yacc.h>
+#include <lex.h>
+
+#include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
+
+/*
+ This is a tool used during build only,
+ so MY_MAX_TOKEN does not need to be exact,
+ only big enough to hold:
+ - 256 character terminal tokens
+ - YYNTOKENS named terminal tokens
+ from bison.
+ See also YYMAXUTOK.
+*/
+#define MY_MAX_TOKEN 1000
+/** Generated token. */
+struct gen_lex_token_string
+{
+ const char *m_token_string;
+ int m_token_length;
+ bool m_append_space;
+ bool m_start_expr;
+};
+
+gen_lex_token_string compiled_token_array[MY_MAX_TOKEN];
+int max_token_seen= 0;
+
+char char_tokens[256];
+
+int tok_generic_value= 0;
+int tok_generic_value_list= 0;
+int tok_row_single_value= 0;
+int tok_row_single_value_list= 0;
+int tok_row_multiple_value= 0;
+int tok_row_multiple_value_list= 0;
+int tok_unused= 0;
+
+void set_token(int tok, const char *str)
+{
+ if (tok <= 0)
+ {
+ fprintf(stderr, "Bad token found\n");
+ exit(1);
+ }
+
+ if (tok > max_token_seen)
+ {
+ max_token_seen= tok;
+ }
+
+ if (max_token_seen >= MY_MAX_TOKEN)
+ {
+ fprintf(stderr, "Added that many new keywords ? Increase MY_MAX_TOKEN\n");
+ exit(1);
+ }
+
+ compiled_token_array[tok].m_token_string= str;
+ compiled_token_array[tok].m_token_length= strlen(str);
+ compiled_token_array[tok].m_append_space= true;
+ compiled_token_array[tok].m_start_expr= false;
+}
+
+void set_start_expr_token(int tok)
+{
+ compiled_token_array[tok].m_start_expr= true;
+}
+
+void compute_tokens()
+{
+ int tok;
+ unsigned int i;
+ char *str;
+
+ /*
+ Default value.
+ */
+ for (tok= 0; tok < MY_MAX_TOKEN; tok++)
+ {
+ compiled_token_array[tok].m_token_string= "(unknown)";
+ compiled_token_array[tok].m_token_length= 9;
+ compiled_token_array[tok].m_append_space= true;
+ compiled_token_array[tok].m_start_expr= false;
+ }
+
+ /*
+ Tokens made of just one terminal character
+ */
+ for (tok=0; tok < 256; tok++)
+ {
+ str= & char_tokens[tok];
+ str[0]= (char) tok;
+ compiled_token_array[tok].m_token_string= str;
+ compiled_token_array[tok].m_token_length= 1;
+ compiled_token_array[tok].m_append_space= true;
+ }
+
+ max_token_seen= 255;
+
+ /*
+ String terminal tokens, used in sql_yacc.yy
+ */
+ set_token(NEG, "~");
+ set_token(TABLE_REF_PRIORITY, "TABLE_REF_PRIORITY");
+
+ /*
+ Tokens hard coded in sql_lex.cc
+ */
+
+ set_token(WITH_CUBE_SYM, "WITH CUBE");
+ set_token(WITH_ROLLUP_SYM, "WITH ROLLUP");
+ set_token(NOT2_SYM, "!");
+ set_token(OR2_SYM, "|");
+ set_token(PARAM_MARKER, "?");
+ set_token(SET_VAR, ":=");
+ set_token(UNDERSCORE_CHARSET, "(_charset)");
+ set_token(END_OF_INPUT, "");
+
+ /*
+ Values.
+ These tokens are all normalized later,
+ so this strings will never be displayed.
+ */
+ set_token(BIN_NUM, "(bin)");
+ set_token(DECIMAL_NUM, "(decimal)");
+ set_token(FLOAT_NUM, "(float)");
+ set_token(HEX_NUM, "(hex)");
+ set_token(LEX_HOSTNAME, "(hostname)");
+ set_token(LONG_NUM, "(long)");
+ set_token(NUM, "(num)");
+ set_token(TEXT_STRING, "(text)");
+ set_token(NCHAR_STRING, "(nchar)");
+ set_token(ULONGLONG_NUM, "(ulonglong)");
+
+ /*
+ Identifiers.
+ */
+ set_token(IDENT, "(id)");
+ set_token(IDENT_QUOTED, "(id_quoted)");
+
+ /*
+ Unused tokens
+ */
+ set_token(LOCATOR_SYM, "LOCATOR");
+ set_token(SERVER_OPTIONS, "SERVER_OPTIONS");
+ set_token(UDF_RETURNS_SYM, "UDF_RETURNS");
+
+ /*
+ See symbols[] in sql/lex.h
+ */
+ for (i= 0; i< sizeof(symbols)/sizeof(symbols[0]); i++)
+ {
+ set_token(symbols[i].tok, symbols[i].name);
+ }
+
+ /*
+ See sql_functions[] in sql/lex.h
+ */
+ for (i= 0; i< sizeof(sql_functions)/sizeof(sql_functions[0]); i++)
+ {
+ set_token(sql_functions[i].tok, sql_functions[i].name);
+ }
+
+ /*
+ Additional FAKE tokens,
+ used internally to normalize a digest text.
+ */
+
+ max_token_seen++;
+ tok_generic_value= max_token_seen;
+ set_token(tok_generic_value, "?");
+
+ max_token_seen++;
+ tok_generic_value_list= max_token_seen;
+ set_token(tok_generic_value_list, "?, ...");
+
+ max_token_seen++;
+ tok_row_single_value= max_token_seen;
+ set_token(tok_row_single_value, "(?)");
+
+ max_token_seen++;
+ tok_row_single_value_list= max_token_seen;
+ set_token(tok_row_single_value_list, "(?) /* , ... */");
+
+ max_token_seen++;
+ tok_row_multiple_value= max_token_seen;
+ set_token(tok_row_multiple_value, "(...)");
+
+ max_token_seen++;
+ tok_row_multiple_value_list= max_token_seen;
+ set_token(tok_row_multiple_value_list, "(...) /* , ... */");
+
+ max_token_seen++;
+ tok_unused= max_token_seen;
+ set_token(tok_unused, "UNUSED");
+
+ /*
+ Fix whitespace for some special tokens.
+ */
+
+ /*
+ The lexer parses "@@variable" as '@', '@', 'variable',
+ returning a token for '@' alone.
+
+ This is incorrect, '@' is not really a token,
+ because the syntax "@ @ variable" (with spaces) is not accepted:
+ The lexer keeps some internal state after the '@' fake token.
+
+ To work around this, digest text are printed as "@@variable".
+ */
+ compiled_token_array[(int) '@'].m_append_space= false;
+
+ /*
+ Define additional properties for tokens.
+
+ List all the token that are followed by an expression.
+ This is needed to differentiate unary from binary
+ '+' and '-' operators, because we want to:
+ - reduce <unary +> <NUM> to <?>,
+ - preserve <...> <binary +> <NUM> as is.
+ */
+ set_start_expr_token('(');
+ set_start_expr_token(',');
+ set_start_expr_token(EVERY_SYM);
+ set_start_expr_token(AT_SYM);
+ set_start_expr_token(STARTS_SYM);
+ set_start_expr_token(ENDS_SYM);
+ set_start_expr_token(DEFAULT);
+ set_start_expr_token(RETURN_SYM);
+ set_start_expr_token(IF);
+ set_start_expr_token(ELSEIF_SYM);
+ set_start_expr_token(CASE_SYM);
+ set_start_expr_token(WHEN_SYM);
+ set_start_expr_token(WHILE_SYM);
+ set_start_expr_token(UNTIL_SYM);
+ set_start_expr_token(SELECT_SYM);
+
+ set_start_expr_token(OR_SYM);
+ set_start_expr_token(OR2_SYM);
+ set_start_expr_token(XOR);
+ set_start_expr_token(AND_SYM);
+ set_start_expr_token(AND_AND_SYM);
+ set_start_expr_token(NOT_SYM);
+ set_start_expr_token(BETWEEN_SYM);
+ set_start_expr_token(LIKE);
+ set_start_expr_token(REGEXP);
+
+ set_start_expr_token('|');
+ set_start_expr_token('&');
+ set_start_expr_token(SHIFT_LEFT);
+ set_start_expr_token(SHIFT_RIGHT);
+ set_start_expr_token('+');
+ set_start_expr_token('-');
+ set_start_expr_token(INTERVAL_SYM);
+ set_start_expr_token('*');
+ set_start_expr_token('/');
+ set_start_expr_token('%');
+ set_start_expr_token(DIV_SYM);
+ set_start_expr_token(MOD_SYM);
+ set_start_expr_token('^');
+}
+
+void print_tokens()
+{
+ int tok;
+
+ printf("#ifdef LEX_TOKEN_WITH_DEFINITION\n");
+ printf("lex_token_string lex_token_array[]=\n");
+ printf("{\n");
+ printf("/* PART 1: character tokens. */\n");
+
+ for (tok= 0; tok<256; tok++)
+ {
+ printf("/* %03d */ { \"\\x%02x\", 1, %s, %s},\n",
+ tok,
+ tok,
+ compiled_token_array[tok].m_append_space ? "true" : "false",
+ compiled_token_array[tok].m_start_expr ? "true" : "false");
+ }
+
+ printf("/* PART 2: named tokens. */\n");
+
+ for (tok= 256; tok<= max_token_seen; tok++)
+ {
+ printf("/* %03d */ { \"%s\", %d, %s, %s},\n",
+ tok,
+ compiled_token_array[tok].m_token_string,
+ compiled_token_array[tok].m_token_length,
+ compiled_token_array[tok].m_append_space ? "true" : "false",
+ compiled_token_array[tok].m_start_expr ? "true" : "false");
+ }
+
+ printf("/* DUMMY */ { \"\", 0, false, false}\n");
+ printf("};\n");
+ printf("#endif /* LEX_TOKEN_WITH_DEFINITION */\n");
+
+ printf("/* DIGEST specific tokens. */\n");
+ printf("#define TOK_GENERIC_VALUE %d\n", tok_generic_value);
+ printf("#define TOK_GENERIC_VALUE_LIST %d\n", tok_generic_value_list);
+ printf("#define TOK_ROW_SINGLE_VALUE %d\n", tok_row_single_value);
+ printf("#define TOK_ROW_SINGLE_VALUE_LIST %d\n", tok_row_single_value_list);
+ printf("#define TOK_ROW_MULTIPLE_VALUE %d\n", tok_row_multiple_value);
+ printf("#define TOK_ROW_MULTIPLE_VALUE_LIST %d\n", tok_row_multiple_value_list);
+ printf("#define TOK_UNUSED %d\n", tok_unused);
+}
+
+int main(int argc,char **argv)
+{
+ puts("/*");
+ puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2011"));
+ puts("*/");
+
+ printf("/*\n");
+ printf(" This file is generated, do not edit.\n");
+ printf(" See file sql/gen_lex_token.cc.\n");
+ printf("*/\n");
+ printf("struct lex_token_string\n");
+ printf("{\n");
+ printf(" const char *m_token_string;\n");
+ printf(" int m_token_length;\n");
+ printf(" bool m_append_space;\n");
+ printf(" bool m_start_expr;\n");
+ printf("};\n");
+ printf("typedef struct lex_token_string lex_token_string;\n");
+
+ compute_tokens();
+ print_tokens();
+
+ return 0;
+}
+
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 73513ac9f40..64ae31ce231 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -1,5 +1,4 @@
-/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
- Copyright (c) 2012, 2013, Monty Proram Ab.
+/* Copyright (c) 2006, 2015, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -2529,7 +2528,8 @@ ndbcluster_check_if_local_tables_in_db(THD *thd, const char *dbname)
char path[FN_REFLEN + 1];
build_table_filename(path, sizeof(path) - 1, dbname, "", "", 0);
- if (find_files(thd, &files, dbname, path, NullS, 0) != FIND_FILES_OK)
+ if (find_files(thd, &files, dbname, path, NullS, 0, NULL) !=
+ FIND_FILES_OK)
{
DBUG_PRINT("info", ("Failed to find files"));
DBUG_RETURN(true);
diff --git a/sql/handler.cc b/sql/handler.cc
index 518ca7b860d..1f8daf3927b 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1445,7 +1445,13 @@ done:
/* Come here if error and we need to rollback. */
err:
error= 1; /* Transaction was rolled back */
- ha_rollback_trans(thd, all);
+ /*
+ In parallel replication, rollback is delayed, as there is extra replication
+ book-keeping to be done before rolling back and allowing a conflicting
+ transaction to continue (MDEV-7458).
+ */
+ if (!(thd->rgi_slave && thd->rgi_slave->is_parallel_exec))
+ ha_rollback_trans(thd, all);
end:
if (rw_trans && mdl_request.ticket)
@@ -1539,7 +1545,10 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
}
/* Free resources and perform other cleanup even for 'empty' transactions. */
if (is_real_trans)
+ {
+ thd->has_waiter= false;
thd->transaction.cleanup();
+ }
DBUG_RETURN(error);
}
@@ -1620,7 +1629,10 @@ int ha_rollback_trans(THD *thd, bool all)
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
+ {
+ thd->has_waiter= false;
thd->transaction.cleanup();
+ }
if (all)
thd->transaction_rollback_request= FALSE;
@@ -2286,9 +2298,11 @@ handle_condition(THD *,
}
-/** @brief
- This should return ENOENT if the file doesn't exists.
- The .frm file will be deleted only if we return 0 or ENOENT
+/** delete a table in the engine
+
+ @note
+ ENOENT and HA_ERR_NO_SUCH_TABLE are not considered errors.
+ The .frm file will be deleted only if we return 0.
*/
int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
const char *db, const char *alias, bool generate_warning)
@@ -2303,47 +2317,66 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
/* table_type is NULL in ALTER TABLE when renaming only .frm files */
if (table_type == NULL || table_type == view_pseudo_hton ||
! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type)))
- DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
+ DBUG_RETURN(0);
bzero((char*) &dummy_table, sizeof(dummy_table));
bzero((char*) &dummy_share, sizeof(dummy_share));
dummy_table.s= &dummy_share;
path= get_canonical_filename(file, path, tmp_path);
- if ((error= file->ha_delete_table(path)) && generate_warning)
+ if ((error= file->ha_delete_table(path)))
{
/*
- Because file->print_error() use my_error() to generate the error message
- we use an internal error handler to intercept it and store the text
- in a temporary buffer. Later the message will be presented to user
- as a warning.
+ it's not an error if the table doesn't exist in the engine.
+ warn the user, but still report DROP being a success
*/
- Ha_delete_table_error_handler ha_delete_table_error_handler;
-
- /* Fill up strucutures that print_error may need */
- dummy_share.path.str= (char*) path;
- dummy_share.path.length= strlen(path);
- dummy_share.normalized_path= dummy_share.path;
- dummy_share.db.str= (char*) db;
- dummy_share.db.length= strlen(db);
- dummy_share.table_name.str= (char*) alias;
- dummy_share.table_name.length= strlen(alias);
- dummy_table.alias.set(alias, dummy_share.table_name.length,
- table_alias_charset);
-
- file->change_table_ptr(&dummy_table, &dummy_share);
+ bool intercept= error == ENOENT || error == HA_ERR_NO_SUCH_TABLE;
- thd->push_internal_handler(&ha_delete_table_error_handler);
- file->print_error(error, 0);
+ if (!intercept || generate_warning)
+ {
+ /*
+ Because file->print_error() use my_error() to generate the error message
+ we use an internal error handler to intercept it and store the text
+ in a temporary buffer. Later the message will be presented to user
+ as a warning.
+ */
+ Ha_delete_table_error_handler ha_delete_table_error_handler;
+
+ /* Fill up strucutures that print_error may need */
+ dummy_share.path.str= (char*) path;
+ dummy_share.path.length= strlen(path);
+ dummy_share.normalized_path= dummy_share.path;
+ dummy_share.db.str= (char*) db;
+ dummy_share.db.length= strlen(db);
+ dummy_share.table_name.str= (char*) alias;
+ dummy_share.table_name.length= strlen(alias);
+ dummy_table.alias.set(alias, dummy_share.table_name.length,
+ table_alias_charset);
+
+ file->change_table_ptr(&dummy_table, &dummy_share);
+
+#if MYSQL_VERSION_ID > 100105
+ // XXX as an ugly 10.0-only hack we intercept HA_ERR_ROW_IS_REFERENCED,
+ // to report it under the old historical error number.
+#error remove HA_ERR_ROW_IS_REFERENCED, use ME_JUST_WARNING instead of a handler
+#endif
+ if (intercept || error == HA_ERR_ROW_IS_REFERENCED)
+ thd->push_internal_handler(&ha_delete_table_error_handler);
- thd->pop_internal_handler();
+ file->print_error(error, 0);
- /*
- XXX: should we convert *all* errors to warnings here?
- What if the error is fatal?
- */
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN, error,
- ha_delete_table_error_handler.buff);
+ if (intercept || error == HA_ERR_ROW_IS_REFERENCED)
+ {
+ thd->pop_internal_handler();
+ if (error == HA_ERR_ROW_IS_REFERENCED)
+ my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
+ else
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, error,
+ ha_delete_table_error_handler.buff);
+ }
+ }
+ if (intercept)
+ error= 0;
}
delete file;
@@ -6290,3 +6323,22 @@ fl_create_iterator(enum handler_iterator_type type,
}
}
#endif /*TRANS_LOG_MGM_EXAMPLE_CODE*/
+
+
+bool HA_CREATE_INFO::check_conflicting_charset_declarations(CHARSET_INFO *cs)
+{
+ if ((used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
+ /* DEFAULT vs explicit, or explicit vs DEFAULT */
+ (((default_table_charset == NULL) != (cs == NULL)) ||
+ /* Two different explicit character sets */
+ (default_table_charset && cs &&
+ !my_charset_same(default_table_charset, cs))))
+ {
+ my_error(ER_CONFLICTING_DECLARATIONS, MYF(0),
+ "CHARACTER SET ", default_table_charset ?
+ default_table_charset->csname : "DEFAULT",
+ "CHARACTER SET ", cs ? cs->csname : "DEFAULT");
+ return true;
+ }
+ return false;
+}
diff --git a/sql/handler.h b/sql/handler.h
index 4222270e9c4..2e219d5f3cc 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1637,6 +1637,33 @@ struct HA_CREATE_INFO
bool table_was_deleted;
bool tmp_table() { return options & HA_LEX_CREATE_TMP_TABLE; }
+ bool check_conflicting_charset_declarations(CHARSET_INFO *cs);
+ bool add_table_option_default_charset(CHARSET_INFO *cs)
+ {
+ // cs can be NULL, e.g.: CREATE TABLE t1 (..) CHARACTER SET DEFAULT;
+ if (check_conflicting_charset_declarations(cs))
+ return true;
+ default_table_charset= cs;
+ used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
+ return false;
+ }
+ bool add_alter_list_item_convert_to_charset(CHARSET_INFO *cs)
+ {
+ /*
+ cs cannot be NULL, as sql_yacc.yy translates
+ CONVERT TO CHARACTER SET DEFAULT
+ to
+ CONVERT TO CHARACTER SET <character-set-of-the-current-database>
+ TODO: Should't we postpone resolution of DEFAULT until the
+ character set of the table owner database is loaded from its db.opt?
+ */
+ DBUG_ASSERT(cs);
+ if (check_conflicting_charset_declarations(cs))
+ return true;
+ table_charset= default_table_charset= cs;
+ used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET);
+ return false;
+ }
};
diff --git a/sql/item.cc b/sql/item.cc
index 132cfa2846a..bffa7e2990d 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2010, 2014, Monty Program Ab.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1179,6 +1179,42 @@ Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
/**
+ Some pieces of the code do not support changing of
+ Item_cache to other Item types.
+
+ Example:
+ Item_singlerow_subselect has "Item_cache **row".
+ Creating of Item_func_conv_charset followed by THD::change_item_tree()
+ should not change row[i] from Item_cache directly to Item_func_conv_charset, because Item_singlerow_subselect
+ because Item_singlerow_subselect later calls Item_cache-specific methods,
+ e.g. row[i]->store() and row[i]->cache_value().
+
+ Let's wrap Item_func_conv_charset in a new Item_cache,
+ so the Item_cache-specific methods can still be used for
+ Item_singlerow_subselect::row[i] safely.
+
+ As a bonus we cache the converted value, instead of converting every time
+
+ TODO: we should eventually check all other use cases of change_item_tree().
+ Perhaps some more potentially dangerous substitution examples exist.
+*/
+Item *Item_cache::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ if (!example)
+ return Item::safe_charset_converter(tocs);
+ Item *conv= example->safe_charset_converter(tocs);
+ if (conv == example)
+ return this;
+ Item_cache *cache;
+ if (!conv || !(cache= new Item_cache_str(conv)))
+ return NULL; // Safe conversion is not possible, or OEM
+ cache->setup(conv);
+ cache->fixed= false; // Make Item::fix_fields() happy
+ return cache;
+}
+
+
+/**
@details
Created mostly for mysql_prepare_table(). Important
when a string ENUM/SET column is described with a numeric default value:
@@ -7931,6 +7967,7 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference)
return TRUE;
if (view->table && view->table->maybe_null)
maybe_null= TRUE;
+ set_null_ref_table();
return FALSE;
}
@@ -9470,6 +9507,11 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
item_decimals= 0;
decimals= MY_MAX(decimals, item_decimals);
}
+
+ if (fld_type == FIELD_TYPE_GEOMETRY)
+ geometry_type=
+ Field_geom::geometry_type_merge(geometry_type, item->get_geometry_type());
+
if (Field::result_merge_type(fld_type) == DECIMAL_RESULT)
{
decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE);
@@ -9775,13 +9817,30 @@ void Item_ref::update_used_tables()
(*ref)->update_used_tables();
}
+void Item_direct_view_ref::update_used_tables()
+{
+ set_null_ref_table();
+ Item_direct_ref::update_used_tables();
+}
+
+
table_map Item_direct_view_ref::used_tables() const
{
- return get_depended_from() ?
- OUTER_REF_TABLE_BIT :
- ((view->is_merged_derived() || view->merged || !view->table) ?
- (*ref)->used_tables() :
- view->table->map);
+ DBUG_ASSERT(null_ref_table);
+
+ if (get_depended_from())
+ return OUTER_REF_TABLE_BIT;
+
+ if (view->is_merged_derived() || view->merged || !view->table)
+ {
+ table_map used= (*ref)->used_tables();
+ return (used ?
+ used :
+ ((null_ref_table != NO_NULL_TABLE) ?
+ null_ref_table->map :
+ (table_map)0 ));
+ }
+ return view->table->map;
}
table_map Item_direct_view_ref::not_null_tables() const
diff --git a/sql/item.h b/sql/item.h
index 13e80639657..171bdb05310 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1,8 +1,8 @@
#ifndef SQL_ITEM_INCLUDED
#define SQL_ITEM_INCLUDED
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013 Monty Program Ab.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -2931,8 +2931,9 @@ public:
{
set_cs_specified(true);
}
- Item_string_with_introducer(const String *str, CHARSET_INFO *tocs)
- :Item_string(str->ptr(), str->length(), tocs)
+ Item_string_with_introducer(const char *name,
+ const char *str, uint length, CHARSET_INFO *tocs)
+ :Item_string(name, str, length, tocs)
{
set_cs_specified(true);
}
@@ -3734,13 +3735,16 @@ class Item_direct_view_ref :public Item_direct_ref
#define NO_NULL_TABLE (reinterpret_cast<TABLE *>(0x1))
+ void set_null_ref_table()
+ {
+ if (!view->is_inner_table_of_outer_join() ||
+ !(null_ref_table= view->get_real_join_table()))
+ null_ref_table= NO_NULL_TABLE;
+ }
+
bool check_null_ref()
{
- if (null_ref_table == NULL)
- {
- if (!(null_ref_table= view->get_real_join_table()))
- null_ref_table= NO_NULL_TABLE;
- }
+ DBUG_ASSERT(null_ref_table);
if (null_ref_table != NO_NULL_TABLE && null_ref_table->null_row)
{
null_value= 1;
@@ -3748,6 +3752,7 @@ class Item_direct_view_ref :public Item_direct_ref
}
return FALSE;
}
+
public:
Item_direct_view_ref(Name_resolution_context *context_arg, Item **item,
const char *table_name_arg,
@@ -3755,7 +3760,11 @@ public:
TABLE_LIST *view_arg)
:Item_direct_ref(context_arg, item, table_name_arg, field_name_arg),
item_equal(0), view(view_arg),
- null_ref_table(NULL) {}
+ null_ref_table(NULL)
+ {
+ if (fixed)
+ set_null_ref_table();
+ }
bool fix_fields(THD *, Item **);
bool eq(const Item *item, bool binary_cmp) const;
@@ -3773,7 +3782,9 @@ public:
Item *equal_fields_propagator(uchar *arg);
Item *replace_equal_field(uchar *arg);
table_map used_tables() const;
+ void update_used_tables();
table_map not_null_tables() const;
+ bool const_item() const { return used_tables() == 0; }
bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
return (*ref)->walk(processor, walk_subquery, arg) ||
@@ -4466,7 +4477,6 @@ class Item_cache: public Item_basic_constant
{
protected:
Item *example;
- table_map used_table_map;
/**
Field that this object will get value from. This is used by
index-based subquery engines to detect and remove the equality injected
@@ -4484,7 +4494,7 @@ protected:
bool value_cached;
public:
Item_cache():
- example(0), used_table_map(0), cached_field(0),
+ example(0), cached_field(0),
cached_field_type(MYSQL_TYPE_STRING),
value_cached(0)
{
@@ -4493,7 +4503,7 @@ public:
null_value= 1;
}
Item_cache(enum_field_types field_type_arg):
- example(0), used_table_map(0), cached_field(0),
+ example(0), cached_field(0),
cached_field_type(field_type_arg),
value_cached(0)
{
@@ -4502,8 +4512,6 @@ public:
null_value= 1;
}
- void set_used_tables(table_map map) { used_table_map= map; }
-
virtual bool allocate(uint i) { return 0; }
virtual bool setup(Item *item)
{
@@ -4520,7 +4528,6 @@ public:
enum_field_types field_type() const { return cached_field_type; }
static Item_cache* get_cache(const Item *item);
static Item_cache* get_cache(const Item* item, const Item_result type);
- table_map used_tables() const { return used_table_map; }
virtual void keep_array() {}
virtual void print(String *str, enum_query_type query_type);
bool eq_def(Field *field)
@@ -4571,6 +4578,7 @@ public:
return TRUE;
return (this->*processor)(arg);
}
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 1f1982ffb80..90eef1ea55c 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -5160,10 +5160,65 @@ bool Regexp_processor_pcre::compile(Item *item, bool send_error)
}
+/**
+ Send a warning explaining an error code returned by pcre_exec().
+*/
+void Regexp_processor_pcre::pcre_exec_warn(int rc) const
+{
+ char buf[64];
+ const char *errmsg= NULL;
+ /*
+ Make a descriptive message only for those pcre_exec() error codes
+ that can actually happen in MariaDB.
+ */
+ switch (rc)
+ {
+ case PCRE_ERROR_NOMEMORY:
+ errmsg= "pcre_exec: Out of memory";
+ break;
+ case PCRE_ERROR_BADUTF8:
+ errmsg= "pcre_exec: Invalid utf8 byte sequence in the subject string";
+ break;
+ case PCRE_ERROR_RECURSELOOP:
+ errmsg= "pcre_exec: Recursion loop detected";
+ break;
+ default:
+ /*
+ As other error codes should normally not happen,
+ we just report the error code without textual description
+ of the code.
+ */
+ my_snprintf(buf, sizeof(buf), "pcre_exec: Internal error (%d)", rc);
+ errmsg= buf;
+ }
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_REGEXP_ERROR, ER(ER_REGEXP_ERROR), errmsg);
+}
+
+
+/**
+ Call pcre_exec() and send a warning if pcre_exec() returned with an error.
+*/
+int Regexp_processor_pcre::pcre_exec_with_warn(const pcre *code,
+ const pcre_extra *extra,
+ const char *subject,
+ int length, int startoffset,
+ int options, int *ovector,
+ int ovecsize)
+{
+ int rc= pcre_exec(code, extra, subject, length,
+ startoffset, options, ovector, ovecsize);
+ DBUG_EXECUTE_IF("pcre_exec_error_123", rc= -123;);
+ if (rc < PCRE_ERROR_NOMATCH)
+ pcre_exec_warn(rc);
+ return rc;
+}
+
+
bool Regexp_processor_pcre::exec(const char *str, int length, int offset)
{
- m_pcre_exec_rc= pcre_exec(m_pcre, NULL, str, length,
- offset, 0, m_SubStrVec, m_subpatterns_needed * 3);
+ m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, NULL, str, length, offset, 0,
+ m_SubStrVec, m_subpatterns_needed * 3);
return false;
}
@@ -5173,8 +5228,10 @@ bool Regexp_processor_pcre::exec(String *str, int offset,
{
if (!(str= convert_if_needed(str, &subject_converter)))
return true;
- m_pcre_exec_rc= pcre_exec(m_pcre, NULL, str->c_ptr_safe(), str->length(),
- offset, 0, m_SubStrVec, m_subpatterns_needed * 3);
+ m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, NULL,
+ str->c_ptr_safe(), str->length(),
+ offset, 0,
+ m_SubStrVec, m_subpatterns_needed * 3);
if (m_pcre_exec_rc > 0)
{
uint i;
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 5d11057228c..c4933e6d7ed 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -734,6 +734,11 @@ public:
void fix_length_and_dec();
const char *func_name() const { return "interval"; }
uint decimal_precision() const { return 2; }
+ void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name());
+ print_args(str, 0, query_type);
+ }
};
@@ -1544,6 +1549,10 @@ class Regexp_processor_pcre
int m_pcre_exec_rc;
int m_SubStrVec[30];
uint m_subpatterns_needed;
+ void pcre_exec_warn(int rc) const;
+ int pcre_exec_with_warn(const pcre *code, const pcre_extra *extra,
+ const char *subject, int length, int startoffset,
+ int options, int *ovector, int ovecsize);
public:
String *convert_if_needed(String *src, String *converter);
String subject_converter;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 92ddd5feada..0dabd06d423 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, SkySQL Ab.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -3659,8 +3659,12 @@ bool udf_handler::get_arguments()
{
f_args.args[i]= (char*) res->ptr();
f_args.lengths[i]= res->length();
- break;
}
+ else
+ {
+ f_args.lengths[i]= 0;
+ }
+ break;
}
case INT_RESULT:
*((longlong*) to) = args[i]->val_int();
@@ -3939,11 +3943,11 @@ longlong Item_master_pos_wait::val_int()
}
#ifdef HAVE_REPLICATION
longlong pos = (ulong)args[1]->val_int();
- longlong timeout = (arg_count==3) ? args[2]->val_int() : 0 ;
+ longlong timeout = (arg_count>=3) ? args[2]->val_int() : 0 ;
String connection_name_buff;
LEX_STRING connection_name;
Master_info *mi;
- if (arg_count == 4)
+ if (arg_count >= 4)
{
String *con;
if (!(con= args[3]->val_str(&connection_name_buff)))
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index 6d7a6a5e6ff..124c9ce6b72 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -1287,6 +1287,13 @@ String *Item_func_buffer::val_str(String *str_value)
if (dist > 0.0)
mbr.buffer(dist);
+ else
+ {
+ /* This happens when dist is too far negative. */
+ if (mbr.xmax + dist < mbr.xmin || mbr.ymax + dist < mbr.ymin)
+ goto return_empty_result;
+ }
+
collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
/*
If the distance given is 0, the Buffer function is in fact NOOP,
@@ -1314,6 +1321,7 @@ String *Item_func_buffer::val_str(String *str_value)
goto mem_error;
+return_empty_result:
str_value->set_charset(&my_charset_bin);
if (str_value->reserve(SRID_SIZE, 512))
goto mem_error;
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 6d52661e5c9..94be38e26ee 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -2,7 +2,7 @@
#define ITEM_GEOFUNC_INCLUDED
/* Copyright (c) 2000, 2010 Oracle and/or its affiliates.
- Copyright (C) 2011 Monty Program Ab.
+ Copyright (C) 2011, 2015 MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -116,7 +116,7 @@ class Item_func_point: public Item_geometry_func
public:
Item_func_point(Item *a, Item *b): Item_geometry_func(a, b) {}
Item_func_point(Item *a, Item *b, Item *srid): Item_geometry_func(a, b, srid) {}
- const char *func_name() const { return "st_point"; }
+ const char *func_name() const { return "point"; }
String *val_str(String *);
Field::geometry_type get_geometry_type() const;
};
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 32a19341895..972ae5afb16 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -5122,6 +5122,16 @@ null:
void Item_dyncol_get::print(String *str, enum_query_type query_type)
{
+ /*
+ Parent cast doesn't exist yet, only print dynamic column name. This happens
+ when called from create_func_cast() / wrong_precision_error().
+ */
+ if (!str->length())
+ {
+ args[1]->print(str, query_type);
+ return;
+ }
+
/* see create_func_dyncol_get */
DBUG_ASSERT(str->length() >= 5);
DBUG_ASSERT(strncmp(str->ptr() + str->length() - 5, "cast(", 5) == 0);
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 8377a20e0a4..2886cb68f9b 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -3,7 +3,7 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013, Monty Program Ab.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -937,7 +937,6 @@ public:
Item_func_conv_charset(Item *a, CHARSET_INFO *cs, bool cache_if_const)
:Item_str_func(a)
{
- DBUG_ASSERT(args[0]->fixed);
conv_charset= cs;
if (cache_if_const && args[0]->const_item() && !args[0]->is_expensive())
{
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 21f14ae8435..1c5682417a5 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, SkySQL Ab.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -3311,7 +3311,19 @@ void Item_func_group_concat::cleanup()
}
DBUG_ASSERT(tree == 0);
}
-
+ /*
+ As the ORDER structures pointed to by the elements of the
+ 'order' array may be modified in find_order_in_list() called
+ from Item_func_group_concat::setup() to point to runtime
+ created objects, we need to reset them back to the original
+ arguments of the function.
+ */
+ ORDER **order_ptr= order;
+ for (uint i= 0; i < arg_count_order; i++)
+ {
+ (*order_ptr)->item= &args[arg_count_field + i];
+ order_ptr++;
+ }
DBUG_VOID_RETURN;
}
diff --git a/sql/lex.h b/sql/lex.h
index 10a52160cf0..a272504c0f2 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -1,7 +1,8 @@
#ifndef LEX_INCLUDED
#define LEX_INCLUDED
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -389,6 +390,7 @@ static SYMBOL symbols[] = {
{ "MULTIPOINT", SYM(MULTIPOINT)},
{ "MULTIPOLYGON", SYM(MULTIPOLYGON)},
{ "MUTEX", SYM(MUTEX_SYM)},
+ { "MYSQL", SYM(MYSQL_SYM)},
{ "MYSQL_ERRNO", SYM(MYSQL_ERRNO_SYM)},
{ "NAME", SYM(NAME_SYM)},
{ "NAMES", SYM(NAMES_SYM)},
diff --git a/sql/log.cc b/sql/log.cc
index 38fe1066896..99d3fb69b18 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, SkySQL Ab.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -95,6 +95,9 @@ mysql_mutex_t LOCK_commit_ordered;
static ulonglong binlog_status_var_num_commits;
static ulonglong binlog_status_var_num_group_commits;
+static ulonglong binlog_status_group_commit_trigger_count;
+static ulonglong binlog_status_group_commit_trigger_lock_wait;
+static ulonglong binlog_status_group_commit_trigger_timeout;
static char binlog_snapshot_file[FN_REFLEN];
static ulonglong binlog_snapshot_position;
@@ -104,6 +107,12 @@ static SHOW_VAR binlog_status_vars_detail[]=
(char *)&binlog_status_var_num_commits, SHOW_LONGLONG},
{"group_commits",
(char *)&binlog_status_var_num_group_commits, SHOW_LONGLONG},
+ {"group_commit_trigger_count",
+ (char *)&binlog_status_group_commit_trigger_count, SHOW_LONGLONG},
+ {"group_commit_trigger_lock_wait",
+ (char *)&binlog_status_group_commit_trigger_lock_wait, SHOW_LONGLONG},
+ {"group_commit_trigger_timeout",
+ (char *)&binlog_status_group_commit_trigger_timeout, SHOW_LONGLONG},
{"snapshot_file",
(char *)&binlog_snapshot_file, SHOW_CHAR},
{"snapshot_position",
@@ -2484,6 +2493,8 @@ bool MYSQL_LOG::open(
char buff[FN_REFLEN];
MY_STAT f_stat;
File file= -1;
+ my_off_t seek_offset;
+ bool is_fifo = false;
int open_flags= O_CREAT | O_BINARY;
DBUG_ENTER("MYSQL_LOG::open");
DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg));
@@ -2500,15 +2511,17 @@ bool MYSQL_LOG::open(
log_type_arg, io_cache_type_arg))
goto err;
- /* File is regular writable file */
- if (my_stat(log_file_name, &f_stat, MYF(0)) && !MY_S_ISREG(f_stat.st_mode))
- goto err;
+ is_fifo = my_stat(log_file_name, &f_stat, MYF(0)) &&
+ MY_S_ISFIFO(f_stat.st_mode);
if (io_cache_type == SEQ_READ_APPEND)
open_flags |= O_RDWR | O_APPEND;
else
open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND);
+ if (is_fifo)
+ open_flags |= O_NONBLOCK;
+
db[0]= 0;
#ifdef HAVE_PSI_INTERFACE
@@ -2516,11 +2529,16 @@ bool MYSQL_LOG::open(
m_log_file_key= log_file_key;
#endif
- if ((file= mysql_file_open(log_file_key,
- log_file_name, open_flags,
- MYF(MY_WME | ME_WAITTANG))) < 0 ||
- init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
- mysql_file_tell(file, MYF(MY_WME)), 0,
+ if ((file= mysql_file_open(log_file_key, log_file_name, open_flags,
+ MYF(MY_WME | ME_WAITTANG))) < 0)
+ goto err;
+
+ if (is_fifo)
+ seek_offset= 0;
+ else if ((seek_offset= mysql_file_tell(file, MYF(MY_WME))))
+ goto err;
+
+ if (init_io_cache(&log_file, file, IO_SIZE, io_cache_type, seek_offset, 0,
MYF(MY_WME | MY_NABP |
((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
goto err;
@@ -2609,17 +2627,17 @@ void MYSQL_LOG::close(uint exiting)
{
end_io_cache(&log_file);
- if (mysql_file_sync(log_file.file, MYF(MY_WME)) && ! write_error)
+ if (log_type == LOG_BIN && mysql_file_sync(log_file.file, MYF(MY_WME)) && ! write_error)
{
write_error= 1;
- sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ sql_print_error(ER_THD_OR_DEFAULT(current_thd, ER_ERROR_ON_WRITE), name, errno);
}
if (!(exiting & LOG_CLOSE_DELAYED_CLOSE) &&
mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error)
{
write_error= 1;
- sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ sql_print_error(ER_THD_OR_DEFAULT(current_thd, ER_ERROR_ON_WRITE), name, errno);
}
}
@@ -3035,6 +3053,8 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
bytes_written(0), file_id(1), open_count(1),
group_commit_queue(0), group_commit_queue_busy(FALSE),
num_commits(0), num_group_commits(0),
+ group_commit_trigger_count(0), group_commit_trigger_timeout(0),
+ group_commit_trigger_lock_wait(0),
sync_period_ptr(sync_period), sync_counter(0),
state_file_deleted(false), binlog_state_recover_done(false),
is_relay_log(0), signal_cnt(0),
@@ -4134,8 +4154,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
included= 1;
to_purge_if_included= my_strdup(ir->name, MYF(0));
}
- my_atomic_rwlock_destroy(&ir->inuse_relaylog_atomic_lock);
- my_free(ir);
+ rli->free_inuse_relaylog(ir);
ir= next;
}
rli->inuse_relaylog_list= ir;
@@ -5653,6 +5672,14 @@ end:
}
+/*
+ Initialize the binlog state from the master-bin.state file, at server startup.
+
+ Returns:
+ 0 for success.
+ 2 for when .state file did not exist.
+ 1 for other error.
+*/
int
MYSQL_BIN_LOG::read_state_from_file()
{
@@ -5680,7 +5707,7 @@ MYSQL_BIN_LOG::read_state_from_file()
with GTID enabled. So initialize to empty state.
*/
rpl_global_gtid_binlog_state.reset();
- err= 0;
+ err= 2;
goto end;
}
}
@@ -5850,6 +5877,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
if (direct)
{
int res;
+ uint64 commit_id= 0;
DBUG_PRINT("info", ("direct is set"));
if ((res= thd->wait_for_prior_commit()))
DBUG_RETURN(res);
@@ -5857,7 +5885,16 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
my_org_b_tell= my_b_tell(file);
mysql_mutex_lock(&LOCK_log);
prev_binlog_id= current_binlog_id;
- if (write_gtid_event(thd, true, using_trans, 0))
+ DBUG_EXECUTE_IF("binlog_force_commit_id",
+ {
+ const LEX_STRING name= { C_STRING_WITH_LEN("commit_id") };
+ bool null_value;
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars,
+ (uchar*) name.str, name.length);
+ commit_id= entry->val_int(&null_value);
+ });
+ if (write_gtid_event(thd, true, using_trans, commit_id))
goto err;
}
else
@@ -6637,6 +6674,10 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
if (check_purge)
checkpoint_and_purge(prev_binlog_id);
}
+ else
+ {
+ mysql_mutex_unlock(&LOCK_log);
+ }
DBUG_RETURN(error);
}
@@ -7022,6 +7063,14 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
}
}
+ /*
+ Handle the heuristics that if another transaction is waiting for this
+ transaction (or if it does so later), then we want to trigger group
+ commit immediately, without waiting for the binlog_commit_wait_usec
+ timeout to expire.
+ */
+ entry->thd->waiting_on_group_commit= true;
+
/* Add the entry to the group commit queue. */
next_entry= entry->next;
entry->next= group_commit_queue;
@@ -7037,7 +7086,7 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
cur= entry->thd->wait_for_commit_ptr;
}
- if (opt_binlog_commit_wait_count > 0)
+ if (opt_binlog_commit_wait_count > 0 && orig_queue != NULL)
mysql_cond_signal(&COND_prepare_ordered);
mysql_mutex_unlock(&LOCK_prepare_ordered);
DEBUG_SYNC(orig_entry->thd, "commit_after_release_LOCK_prepare_ordered");
@@ -7211,6 +7260,11 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
while (current)
{
group_commit_entry *next= current->next;
+ /*
+ Now that group commit is started, we can clear the flag; there is no
+ longer any use in waiters on this commit trying to trigger it early.
+ */
+ current->thd->waiting_on_group_commit= false;
current->next= queue;
queue= current;
current= next;
@@ -7224,6 +7278,15 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
if (likely(is_open())) // Should always be true
{
commit_id= (last_in_queue == leader ? 0 : (uint64)leader->thd->query_id);
+ DBUG_EXECUTE_IF("binlog_force_commit_id",
+ {
+ const LEX_STRING name= { C_STRING_WITH_LEN("commit_id") };
+ bool null_value;
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&leader->thd->user_vars,
+ (uchar*) name.str, name.length);
+ commit_id= entry->val_int(&null_value);
+ });
/*
Commit every transaction in the queue.
@@ -7340,6 +7403,8 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
my_error(ER_ERROR_ON_WRITE, MYF(ME_NOREFRESH), name, errno);
check_purge= false;
}
+ /* In case of binlog rotate, update the correct current binlog offset. */
+ commit_offset= my_b_write_tell(&log_file);
}
DEBUG_SYNC(leader->thd, "commit_before_get_LOCK_commit_ordered");
@@ -7521,8 +7586,18 @@ MYSQL_BIN_LOG::wait_for_sufficient_commits()
mysql_mutex_assert_owner(&LOCK_prepare_ordered);
for (e= last_head= group_commit_queue, count= 0; e; e= e->next)
+ {
if (++count >= opt_binlog_commit_wait_count)
+ {
+ group_commit_trigger_count++;
+ return;
+ }
+ if (unlikely(e->thd->has_waiter))
+ {
+ group_commit_trigger_lock_wait++;
return;
+ }
+ }
mysql_mutex_unlock(&LOCK_log);
set_timespec_nsec(wait_until, (ulonglong)1000*opt_binlog_commit_wait_usec);
@@ -7535,14 +7610,33 @@ MYSQL_BIN_LOG::wait_for_sufficient_commits()
err= mysql_cond_timedwait(&COND_prepare_ordered, &LOCK_prepare_ordered,
&wait_until);
if (err == ETIMEDOUT)
+ {
+ group_commit_trigger_timeout++;
break;
+ }
+ if (unlikely(last_head->thd->has_waiter))
+ {
+ group_commit_trigger_lock_wait++;
+ break;
+ }
head= group_commit_queue;
for (e= head; e && e != last_head; e= e->next)
+ {
++count;
+ if (unlikely(e->thd->has_waiter))
+ {
+ group_commit_trigger_lock_wait++;
+ goto after_loop;
+ }
+ }
if (count >= opt_binlog_commit_wait_count)
+ {
+ group_commit_trigger_count++;
break;
+ }
last_head= head;
}
+after_loop:
/*
We must not wait for LOCK_log while holding LOCK_prepare_ordered.
@@ -7566,6 +7660,42 @@ MYSQL_BIN_LOG::wait_for_sufficient_commits()
}
+void
+MYSQL_BIN_LOG::binlog_trigger_immediate_group_commit()
+{
+ group_commit_entry *head;
+ mysql_mutex_lock(&LOCK_prepare_ordered);
+ head= group_commit_queue;
+ if (head)
+ {
+ head->thd->has_waiter= true;
+ mysql_cond_signal(&COND_prepare_ordered);
+ }
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
+}
+
+
+/*
+ This function is called when a transaction T1 goes to wait for another
+ transaction T2. It is used to cut short any binlog group commit delay from
+ --binlog-commit-wait-count in the case where another transaction is stalled
+ on the wait due to conflicting row locks.
+
+ If T2 is already ready to group commit, any waiting group commit will be
+ signalled to proceed immediately. Otherwise, a flag will be set in T2, and
+ when T2 later becomes ready, immediate group commit will be triggered.
+*/
+void
+binlog_report_wait_for(THD *thd1, THD *thd2)
+{
+ if (opt_binlog_commit_wait_count == 0)
+ return;
+ thd2->has_waiter= true;
+ if (thd2->waiting_on_group_commit)
+ mysql_bin_log.binlog_trigger_immediate_group_commit();
+}
+
+
/**
Wait until we get a signal that the relay log has been updated.
@@ -9444,7 +9574,17 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
if (error != LOG_INFO_EOF)
sql_print_error("find_log_pos() failed (error: %d)", error);
else
+ {
error= read_state_from_file();
+ if (error == 2)
+ {
+ /*
+ No binlog files and no binlog state is not an error (eg. just initial
+ server start after fresh installation).
+ */
+ error= 0;
+ }
+ }
return error;
}
@@ -9470,15 +9610,42 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
if ((ev= Log_event::read_log_event(&log, 0, &fdle,
opt_master_verify_checksum)) &&
- ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
- ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
+ ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
{
- sql_print_information("Recovering after a crash using %s", opt_name);
- error= recover(&log_info, log_name, &log,
- (Format_description_log_event *)ev, do_xa_recovery);
+ if (ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
+ {
+ sql_print_information("Recovering after a crash using %s", opt_name);
+ error= recover(&log_info, log_name, &log,
+ (Format_description_log_event *)ev, do_xa_recovery);
+ }
+ else
+ {
+ error= read_state_from_file();
+ if (error == 2)
+ {
+ /*
+ The binlog exists, but the .state file is missing. This is normal if
+ this is the first master start after a major upgrade to 10.0 (with
+ GTID support).
+
+ However, it could also be that the .state file was lost somehow, and
+ in this case it could be a serious issue, as we would set the wrong
+ binlog state in the next binlog file to be created, and GTID
+ processing would be corrupted. A common way would be copying files
+ from an old server to a new one and forgetting the .state file.
+
+ So in this case, we want to try to recover the binlog state by
+ scanning the last binlog file (but we do not need any XA recovery).
+
+ ToDo: We could avoid one scan at first start after major upgrade, by
+ detecting that there is no GTID_LIST event at the start of the
+ binlog file, and stopping the scan in that case.
+ */
+ error= recover(&log_info, log_name, &log,
+ (Format_description_log_event *)ev, false);
+ }
+ }
}
- else
- error= read_state_from_file();
delete ev;
end_io_cache(&log);
@@ -9650,6 +9817,11 @@ TC_LOG_BINLOG::set_status_variables(THD *thd)
binlog_snapshot_position= last_commit_pos_offset;
}
mysql_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_lock(&LOCK_prepare_ordered);
+ binlog_status_group_commit_trigger_count= this->group_commit_trigger_count;
+ binlog_status_group_commit_trigger_timeout= this->group_commit_trigger_timeout;
+ binlog_status_group_commit_trigger_lock_wait= this->group_commit_trigger_lock_wait;
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
if (have_snapshot)
{
diff --git a/sql/log.h b/sql/log.h
index a93e2e1c0b9..48970f7452a 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -515,6 +515,9 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
ulonglong num_commits;
/* Number of group commits done. */
ulonglong num_group_commits;
+ /* The reason why the group commit was grouped */
+ ulonglong group_commit_trigger_count, group_commit_trigger_timeout;
+ ulonglong group_commit_trigger_lock_wait;
/* pointer to the sync period variable, for binlog this will be
sync_binlog_period, for relay log this will be
@@ -690,6 +693,7 @@ public:
void set_max_size(ulong max_size_arg);
void signal_update();
void wait_for_sufficient_commits();
+ void binlog_trigger_immediate_group_commit();
void wait_for_update_relay_log(THD* thd);
int wait_for_update_bin_log(THD* thd, const struct timespec * timeout);
void init(ulong max_size);
@@ -986,11 +990,9 @@ uint purge_log_get_error_code(int res);
int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
void sql_print_error(const char *format, ...);
-void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
-void sql_print_information(const char *format, ...)
- ATTRIBUTE_FORMAT(printf, 1, 2);
-typedef void (*sql_print_message_func)(const char *format, ...)
- ATTRIBUTE_FORMAT_FPTR(printf, 1, 2);
+void sql_print_warning(const char *format, ...);
+void sql_print_information(const char *format, ...);
+typedef void (*sql_print_message_func)(const char *format, ...);
extern sql_print_message_func sql_print_message_handlers[];
int error_log_print(enum loglevel level, const char *format,
@@ -1005,6 +1007,7 @@ bool general_log_print(THD *thd, enum enum_server_command command,
bool general_log_write(THD *thd, enum enum_server_command command,
const char *query, uint query_length);
+void binlog_report_wait_for(THD *thd, THD *other_thd);
void sql_perror(const char *message);
bool flush_error_log();
diff --git a/sql/log_event.cc b/sql/log_event.cc
index dd272ac52cd..69b981f3f47 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -49,9 +49,12 @@
#include <base64.h>
#include <my_bitmap.h>
#include "rpl_utility.h"
+#include "sql_digest.h"
#define my_b_write_string(A, B) my_b_write((A), (B), (uint) (sizeof(B) - 1))
+using std::max;
+
/**
BINLOG_CHECKSUM variable.
*/
@@ -335,10 +338,6 @@ private:
flag_set m_flags;
};
-#ifndef DBUG_OFF
-uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation
-#endif
-
/*
pretty_print_str()
*/
@@ -877,7 +876,7 @@ Log_event::Log_event()
Log_event::Log_event(const char* buf,
const Format_description_log_event* description_event)
- :temp_buf(0), cache_type(Log_event::EVENT_INVALID_CACHE),
+ :temp_buf(0), exec_time(0), cache_type(Log_event::EVENT_INVALID_CACHE),
crc(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
{
#ifndef MYSQL_CLIENT
@@ -968,29 +967,12 @@ int Log_event::do_update_pos(rpl_group_info *rgi)
if (rli)
{
/*
- bug#29309 simulation: resetting the flag to force
- wrong behaviour of artificial event to update
- rli->last_master_timestamp for only one time -
- the first FLUSH LOGS in the test.
- */
- DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
- if (debug_not_change_ts_if_art_event == 1
- && is_artificial_event())
- debug_not_change_ts_if_art_event= 0; );
- /*
In parallel execution, delay position update for the events that are
not part of event groups (format description, rotate, and such) until
the actual event execution reaches that point.
*/
if (!rgi->is_parallel_exec || is_group_event(get_type_code()))
- rli->stmt_done(log_pos,
- (is_artificial_event() &&
- IF_DBUG(debug_not_change_ts_if_art_event > 0, 1) ?
- 0 : when),
- thd, rgi);
- DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
- if (debug_not_change_ts_if_art_event == 0)
- debug_not_change_ts_if_art_event= 2; );
+ rli->stmt_done(log_pos, thd, rgi);
}
DBUG_RETURN(0); // Cannot fail currently
}
@@ -1326,9 +1308,10 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
}
data_len= uint4korr(buf + EVENT_LEN_OFFSET);
if (data_len < LOG_EVENT_MINIMAL_HEADER_LEN ||
- data_len > current_thd->variables.max_allowed_packet)
+ data_len > max(current_thd->variables.max_allowed_packet,
+ opt_binlog_rows_event_max_size + MAX_LOG_EVENT_HEADER))
{
- DBUG_PRINT("error",("data_len: %ld", data_len));
+ DBUG_PRINT("error",("data_len: %lu", data_len));
result= ((data_len < LOG_EVENT_MINIMAL_HEADER_LEN) ? LOG_READ_BOGUS :
LOG_READ_TOO_LARGE);
goto end;
@@ -1449,7 +1432,7 @@ failed my_b_read"));
*/
DBUG_RETURN(0);
}
- uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
+ ulong data_len = uint4korr(head + EVENT_LEN_OFFSET);
char *buf= 0;
const char *error= 0;
Log_event *res= 0;
@@ -1458,7 +1441,8 @@ failed my_b_read"));
uint max_allowed_packet= thd ? slave_max_allowed_packet:~(uint)0;
#endif
- if (data_len > max_allowed_packet)
+ if (data_len > max<ulong>(max_allowed_packet,
+ opt_binlog_rows_event_max_size + MAX_LOG_EVENT_HEADER))
{
error = "Event too big";
goto err;
@@ -1492,7 +1476,7 @@ err:
{
DBUG_ASSERT(error != 0);
sql_print_error("Error in Log_event::read_log_event(): "
- "'%s', data_len: %d, event_type: %d",
+ "'%s', data_len: %lu, event_type: %d",
error,data_len,(uchar)(head[EVENT_TYPE_OFFSET]));
my_free(buf);
/*
@@ -2422,6 +2406,12 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
my_snprintf(typestr, typestr_length, "STRING(%d)", length);
return my_b_write_quoted_with_length(file, ptr, length);
+ case MYSQL_TYPE_DECIMAL:
+ my_b_printf(file,
+ "!! Old DECIMAL (mysql-4.1 or earlier). "
+ "Not enough metadata to display the value. ");
+ break;
+
default:
{
char tmp[5];
@@ -4299,12 +4289,17 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
Parser_state parser_state;
if (!parser_state.init(thd, thd->query(), thd->query_length()))
{
+ DBUG_ASSERT(thd->m_digest == NULL);
+ thd->m_digest= & thd->m_digest_state;
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
stmt_info_rpl.m_key,
thd->db, thd->db_length,
thd->charset());
THD_STAGE_INFO(thd, stage_init);
MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(), thd->query_length());
+ if (thd->m_digest != NULL)
+ thd->m_digest->reset(thd->m_token_array, max_digest_length);
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
/* Finalize server status flags after executing a statement. */
@@ -4396,11 +4391,10 @@ compare_errors:
!ignored_error_code(expected_error))
{
rli->report(ERROR_LEVEL, 0, rgi->gtid_info(),
- "\
-Query caused different errors on master and slave. \
-Error on master: message (format)='%s' error code=%d ; \
-Error on slave: actual message='%s', error code=%d. \
-Default database: '%s'. Query: '%s'",
+ "Query caused different errors on master and slave. "
+ "Error on master: message (format)='%s' error code=%d ; "
+ "Error on slave: actual message='%s', error code=%d. "
+ "Default database: '%s'. Query: '%s'",
ER_SAFE(expected_error),
expected_error,
actual_error ? thd->get_stmt_da()->message() : "no error",
@@ -4496,6 +4490,7 @@ end:
/* Mark the statement completed. */
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
/*
As a disk space optimization, future masters will not log an event for
@@ -10103,7 +10098,7 @@ Rows_log_event::do_update_pos(rpl_group_info *rgi)
Step the group log position if we are not in a transaction,
otherwise increase the event log position.
*/
- rli->stmt_done(log_pos, when, thd, rgi);
+ rli->stmt_done(log_pos, thd, rgi);
/*
Clear any errors in thd->net.last_err*. It is not known if this is
needed or not. It is believed that any errors that may exist in
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 181d950bf59..e6c05aeb849 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -1830,7 +1830,7 @@ Old_rows_log_event::do_update_pos(rpl_group_info *rgi)
Step the group log position if we are not in a transaction,
otherwise increase the event log position.
*/
- rli->stmt_done(log_pos, when, thd, rgi);
+ rli->stmt_done(log_pos, thd, rgi);
/*
Clear any errors in thd->net.last_err*. It is not known if this is
needed or not. It is believed that any errors that may exist in
diff --git a/sql/message.rc b/sql/message.rc
index 116522b7d48..0885a897e6f 100644
--- a/sql/message.rc
+++ b/sql/message.rc
@@ -1,2 +1,2 @@
-LANGUAGE 0x9,0x1
-1 11 MSG00001.bin
+LANGUAGE 0x9,0x1
+1 11 MSG00001.bin
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 7138027ff0a..c9682e15eae 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, SkySQL Ab.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -515,6 +515,7 @@ ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
ulong max_connections, max_connect_errors;
ulong extra_max_connections;
+ulong max_digest_length= 0;
ulong slave_retried_transactions;
ulong feature_files_opened_with_delayed_keys;
ulonglong denied_connections;
@@ -2251,7 +2252,8 @@ static struct passwd *check_user(const char *user)
{
if (!opt_bootstrap && !opt_help)
{
- sql_print_error("Fatal error: Please read \"Security\" section of the manual to find out how to run mysqld as root!\n");
+ sql_print_error("Fatal error: Please consult the Knowledge Base "
+ "to find out how to run mysqld as root!\n");
unireg_abort(1);
}
return NULL;
@@ -2421,10 +2423,11 @@ static MYSQL_SOCKET activate_tcp_port(uint port)
if (mysql_socket_getfd(ip_sock) == INVALID_SOCKET)
{
- sql_print_error("Failed to create a socket for %s '%s': errno: %d.",
- (a->ai_family == AF_INET) ? "IPv4" : "IPv6",
- (const char *) ip_addr,
- (int) socket_errno);
+ sql_print_message_func func= real_bind_addr_str ? sql_print_error
+ : sql_print_warning;
+ func("Failed to create a socket for %s '%s': errno: %d.",
+ (a->ai_family == AF_INET) ? "IPv4" : "IPv6",
+ (const char *) ip_addr, (int) socket_errno);
}
else
{
@@ -4068,6 +4071,10 @@ static int init_common_variables()
return 1;
set_server_version();
+ if (!opt_help)
+ sql_print_information("%s (mysqld %s) starting as process %lu ...",
+ my_progname, server_version, (ulong) getpid());
+
#ifndef EMBEDDED_LIBRARY
if (opt_abort && !opt_verbose)
unireg_abort(0);
@@ -4338,11 +4345,12 @@ static int init_common_variables()
if (lower_case_table_names_used)
{
if (global_system_variables.log_warnings)
- sql_print_warning("\
-You have forced lower_case_table_names to 0 through a command-line \
-option, even though your file system '%s' is case insensitive. This means \
-that you can corrupt a MyISAM table by accessing it with different cases. \
-You should consider changing lower_case_table_names to 1 or 2",
+ sql_print_warning("You have forced lower_case_table_names to 0 through "
+ "a command-line option, even though your file system "
+ "'%s' is case insensitive. This means that you can "
+ "corrupt a MyISAM table by accessing it with "
+ "different cases. You should consider changing "
+ "lower_case_table_names to 1 or 2",
mysql_real_data_home);
}
else
@@ -4556,7 +4564,6 @@ static void init_ssl()
opt_ssl_cipher, &error,
opt_ssl_crl, opt_ssl_crlpath);
DBUG_PRINT("info",("ssl_acceptor_fd: 0x%lx", (long) ssl_acceptor_fd));
- ERR_remove_state(0);
if (!ssl_acceptor_fd)
{
sql_print_warning("Failed to setup SSL");
@@ -4564,6 +4571,14 @@ static void init_ssl()
opt_use_ssl = 0;
have_ssl= SHOW_OPTION_DISABLED;
}
+ if (global_system_variables.log_warnings > 0)
+ {
+ ulong err;
+ while ((err= ERR_get_error()))
+ sql_print_warning("SSL error: %s", ERR_error_string(err, NULL));
+ }
+ else
+ ERR_remove_state(0);
}
else
{
@@ -4738,15 +4753,16 @@ static int init_server_components()
{
if (opt_bin_log)
{
- sql_print_error("using --replicate-same-server-id in conjunction with \
---log-slave-updates is impossible, it would lead to infinite loops in this \
-server.");
+ sql_print_error("using --replicate-same-server-id in conjunction with "
+ "--log-slave-updates is impossible, it would lead to "
+ "infinite loops in this server.");
unireg_abort(1);
}
else
- sql_print_warning("using --replicate-same-server-id in conjunction with \
---log-slave-updates would lead to infinite loops in this server. However this \
-will be ignored as the --log-bin option is not defined.");
+ sql_print_warning("using --replicate-same-server-id in conjunction with "
+ "--log-slave-updates would lead to infinite loops in "
+ "this server. However this will be ignored as the "
+ "--log-bin option is not defined.");
}
#endif
@@ -4759,8 +4775,8 @@ will be ignored as the --log-bin option is not defined.");
if (opt_bin_logname[0] &&
opt_bin_logname[strlen(opt_bin_logname) - 1] == FN_LIBCHAR)
{
- sql_print_error("Path '%s' is a directory name, please specify \
-a file name for --log-bin option", opt_bin_logname);
+ sql_print_error("Path '%s' is a directory name, please specify "
+ "a file name for --log-bin option", opt_bin_logname);
unireg_abort(1);
}
@@ -4770,8 +4786,9 @@ a file name for --log-bin option", opt_bin_logname);
opt_binlog_index_name[strlen(opt_binlog_index_name) - 1]
== FN_LIBCHAR)
{
- sql_print_error("Path '%s' is a directory name, please specify \
-a file name for --log-bin-index option", opt_binlog_index_name);
+ sql_print_error("Path '%s' is a directory name, please specify "
+ "a file name for --log-bin-index option",
+ opt_binlog_index_name);
unireg_abort(1);
}
@@ -5010,6 +5027,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
init_global_client_stats();
if (!opt_bootstrap)
servers_init(0);
+ init_status_vars();
DBUG_RETURN(0);
}
@@ -5258,6 +5276,8 @@ int mysqld_main(int argc, char **argv)
pfs_param.m_hints.m_table_open_cache= tc_size;
pfs_param.m_hints.m_max_connections= max_connections;
pfs_param.m_hints.m_open_files_limit= open_files_limit;
+ /* the performance schema digest size is the same as the SQL layer */
+ pfs_param.m_max_digest_length= max_digest_length;
PSI_hook= initialize_performance_schema(&pfs_param);
if (PSI_hook == NULL)
{
@@ -5458,7 +5478,6 @@ int mysqld_main(int argc, char **argv)
#endif
}
- init_status_vars();
if (opt_bootstrap) /* If running with bootstrap, do not start replication. */
opt_skip_slave_start= 1;
@@ -5538,6 +5557,8 @@ int mysqld_main(int argc, char **argv)
mysql_cond_signal(&COND_server_started);
mysql_mutex_unlock(&LOCK_server_started);
+ MYSQL_SET_STAGE(0 ,__FILE__, __LINE__);
+
#if defined(_WIN32) || defined(HAVE_SMEM)
handle_connections_methods();
#else
@@ -8080,16 +8101,15 @@ static void usage(void)
else
{
#ifdef __WIN__
- puts("NT and Win32 specific options:\n\
- --install Install the default service (NT).\n\
- --install-manual Install the default service started manually (NT).\n\
- --install service_name Install an optional service (NT).\n\
- --install-manual service_name Install an optional service started manually (NT).\n\
- --remove Remove the default service from the service list (NT).\n\
- --remove service_name Remove the service_name from the service list (NT).\n\
- --enable-named-pipe Only to be used for the default server (NT).\n\
- --standalone Dummy option to start as a standalone server (NT).\
-");
+ puts("NT and Win32 specific options:\n"
+ " --install Install the default service (NT).\n"
+ " --install-manual Install the default service started manually (NT).\n"
+ " --install service_name Install an optional service (NT).\n"
+ " --install-manual service_name Install an optional service started manually (NT).\n"
+ " --remove Remove the default service from the service list (NT).\n"
+ " --remove service_name Remove the service_name from the service list (NT).\n"
+ " --enable-named-pipe Only to be used for the default server (NT).\n"
+ " --standalone Dummy option to start as a standalone server (NT).");
puts("");
#endif
print_defaults(MYSQL_CONFIG_NAME,load_default_groups);
@@ -8101,14 +8121,12 @@ static void usage(void)
if (! plugins_are_initialized)
{
- puts("\n\
-Plugins have parameters that are not reflected in this list\n\
-because execution stopped before plugins were initialized.");
+ puts("\nPlugins have parameters that are not reflected in this list"
+ "\nbecause execution stopped before plugins were initialized.");
}
- puts("\n\
-To see what values a running MySQL server is using, type\n\
-'mysqladmin variables' instead of 'mysqld --verbose --help'.");
+ puts("\nTo see what values a running MySQL server is using, type"
+ "\n'mysqladmin variables' instead of 'mysqld --verbose --help'.");
}
DBUG_VOID_RETURN;
}
@@ -9498,6 +9516,7 @@ PSI_stage_info stage_waiting_for_work_from_sql_thread= { 0, "Waiting for work fr
PSI_stage_info stage_waiting_for_prior_transaction_to_commit= { 0, "Waiting for prior transaction to commit", 0};
PSI_stage_info stage_waiting_for_prior_transaction_to_start_commit= { 0, "Waiting for prior transaction to start commit before starting next transaction", 0};
PSI_stage_info stage_waiting_for_room_in_worker_thread= { 0, "Waiting for room in worker thread event queue", 0};
+PSI_stage_info stage_waiting_for_workers_idle= { 0, "Waiting for worker threads to be idle", 0};
PSI_stage_info stage_master_gtid_wait_primary= { 0, "Waiting in MASTER_GTID_WAIT() (primary waiter)", 0};
PSI_stage_info stage_master_gtid_wait= { 0, "Waiting in MASTER_GTID_WAIT()", 0};
PSI_stage_info stage_gtid_wait_other_connection= { 0, "Waiting for other master connection to process GTID received on multiple master connections", 0};
diff --git a/sql/mysqld.h b/sql/mysqld.h
index f6d9dbea48a..156e7f99bb5 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -167,6 +167,7 @@ extern ulong query_cache_limit;
extern ulong query_cache_min_res_unit;
extern ulong slow_launch_threads, slow_launch_time;
extern MYSQL_PLUGIN_IMPORT ulong max_connections;
+extern ulong max_digest_length;
extern ulong max_connect_errors, connect_timeout;
extern my_bool slave_allow_batching;
extern my_bool allow_slave_start;
@@ -452,6 +453,7 @@ extern PSI_stage_info stage_waiting_for_work_from_sql_thread;
extern PSI_stage_info stage_waiting_for_prior_transaction_to_commit;
extern PSI_stage_info stage_waiting_for_prior_transaction_to_start_commit;
extern PSI_stage_info stage_waiting_for_room_in_worker_thread;
+extern PSI_stage_info stage_waiting_for_workers_idle;
extern PSI_stage_info stage_master_gtid_wait_primary;
extern PSI_stage_info stage_master_gtid_wait;
extern PSI_stage_info stage_gtid_wait_other_connection;
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 29b8417c698..0ce0fa93f99 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -917,7 +917,7 @@ my_real_read(NET *net, size_t *complen,
my_progname,vio_errno(net->vio));
}
#ifndef MYSQL_SERVER
- if (vio_errno(net->vio) == SOCKET_EINTR)
+ if (length != 0 && vio_errno(net->vio) == SOCKET_EINTR)
{
DBUG_PRINT("warning",("Interrupted read. Retrying..."));
continue;
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 7d1f6dfbf9d..0ad90e2ef3d 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2010, 2012, Monty Program Ab
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -513,8 +513,6 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
Subquery !contains {GROUP BY, ORDER BY [LIMIT],
aggregate functions}) && subquery predicate is not under "NOT IN"))
- (*) The subquery must be part of a SELECT or CREATE TABLE ... SELECT statement.
- The current condition also excludes multi-table update statements.
A note about prepared statements: we want the if-branch to be taken on
PREPARE and each EXECUTE. The rewrites are only done once, but we need
select_lex->sj_subselects list to be populated for every EXECUTE.
@@ -523,9 +521,7 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0
!child_select->is_part_of_union() && // 1
parent_unit->first_select()->leaf_tables.elements && // 2
- (thd->lex->sql_command == SQLCOM_SELECT || // *
- thd->lex->sql_command == SQLCOM_CREATE_TABLE) && // *
- child_select->outer_select()->leaf_tables.elements && // 2A
+ child_select->outer_select()->leaf_tables.elements && // 2A
subquery_types_allow_materialization(in_subs) &&
(in_subs->is_top_level_item() || //3
optimizer_flag(thd,
@@ -1615,9 +1611,20 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
sj_nest->sj_on_expr= and_items(sj_nest->sj_on_expr, item_eq);
}
}
- /* Fix the created equality and AND */
- if (!sj_nest->sj_on_expr->fixed)
- sj_nest->sj_on_expr->fix_fields(parent_join->thd, &sj_nest->sj_on_expr);
+ /*
+ Fix the created equality and AND
+
+ Note that fix_fields() can actually fail in a meaningful way here. One
+ example is when the IN-equality is not valid, because it compares columns
+ with incompatible collations. (One can argue it would be more appropriate
+ to check for this at name resolution stage, but as a legacy of IN->EXISTS
+ we have in here).
+ */
+ if (!sj_nest->sj_on_expr->fixed &&
+ sj_nest->sj_on_expr->fix_fields(parent_join->thd, &sj_nest->sj_on_expr))
+ {
+ DBUG_RETURN(TRUE);
+ }
/*
Walk through sj nest's WHERE and ON expressions and call
@@ -1636,12 +1643,15 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
/* Inject sj_on_expr into the parent's WHERE or ON */
if (emb_tbl_nest)
{
- emb_tbl_nest->on_expr= and_items(emb_tbl_nest->on_expr,
+ emb_tbl_nest->on_expr= and_items(emb_tbl_nest->on_expr,
sj_nest->sj_on_expr);
emb_tbl_nest->on_expr->top_level_item();
- if (!emb_tbl_nest->on_expr->fixed)
- emb_tbl_nest->on_expr->fix_fields(parent_join->thd,
- &emb_tbl_nest->on_expr);
+ if (!emb_tbl_nest->on_expr->fixed &&
+ emb_tbl_nest->on_expr->fix_fields(parent_join->thd,
+ &emb_tbl_nest->on_expr))
+ {
+ DBUG_RETURN(TRUE);
+ }
}
else
{
@@ -1654,8 +1664,12 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
*/
save_lex= thd->lex->current_select;
thd->lex->current_select=parent_join->select_lex;
- if (!parent_join->conds->fixed)
- parent_join->conds->fix_fields(parent_join->thd, &parent_join->conds);
+ if (!parent_join->conds->fixed &&
+ parent_join->conds->fix_fields(parent_join->thd,
+ &parent_join->conds))
+ {
+ DBUG_RETURN(1);
+ }
thd->lex->current_select=save_lex;
parent_join->select_lex->where= parent_join->conds;
}
@@ -2512,10 +2526,16 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
LooseScan detector in best_access_path)
*/
remaining_tables &= ~new_join_tab->table->map;
- pos->prefix_dups_producing_tables= join->cur_dups_producing_tables;
+ table_map dups_producing_tables;
+
+ if (idx == join->const_tables)
+ dups_producing_tables= 0;
+ else
+ dups_producing_tables= pos[-1].dups_producing_tables;
+
TABLE_LIST *emb_sj_nest;
if ((emb_sj_nest= new_join_tab->emb_sj_nest))
- join->cur_dups_producing_tables |= emb_sj_nest->sj_inner_tables;
+ dups_producing_tables |= emb_sj_nest->sj_inner_tables;
Semi_join_strategy_picker **strategy;
if (idx == join->const_tables)
@@ -2568,7 +2588,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
fanout from semijoin X.
3. We have no clue what to do about fanount of semi-join Y.
*/
- if ((join->cur_dups_producing_tables & handled_fanout) ||
+ if ((dups_producing_tables & handled_fanout) ||
(read_time < *current_read_time &&
!(handled_fanout & pos->inner_tables_handled_with_other_sjs)))
{
@@ -2581,7 +2601,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
join->sjm_lookup_tables &= ~handled_fanout;
*current_read_time= read_time;
*current_record_count= rec_count;
- join->cur_dups_producing_tables &= ~handled_fanout;
+ dups_producing_tables &= ~handled_fanout;
//TODO: update bitmap of semi-joins that were handled together with
// others.
if (is_multiple_semi_joins(join, join->positions, idx, handled_fanout))
@@ -2608,6 +2628,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
pos->prefix_cost.convert_from_cost(*current_read_time);
pos->prefix_record_count= *current_record_count;
+ pos->dups_producing_tables= dups_producing_tables;
}
@@ -3119,8 +3140,6 @@ void restore_prev_sj_state(const table_map remaining_tables,
tab->join->cur_sj_inner_tables &= ~emb_sj_nest->sj_inner_tables;
}
}
- POSITION *pos= tab->join->positions + idx;
- tab->join->cur_dups_producing_tables= pos->prefix_dups_producing_tables;
}
@@ -3894,7 +3913,6 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
/*
STEP 1: Get temporary table name
*/
- thd->inc_status_created_tmp_tables();
if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES))
temp_pool_slot = bitmap_lock_set_next(&temp_pool);
diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h
index acfbebed6b3..3da94d05521 100644
--- a/sql/opt_subselect.h
+++ b/sql/opt_subselect.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2010, 2012, Monty Program Ab
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -299,7 +299,7 @@ public:
};
-extern void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
+void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
double *current_record_count, double *current_read_time,
POSITION *loose_scan_pos);
void restore_prev_sj_state(const table_map remaining_tables,
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
index ee031c1bbc2..197f7c97fda 100644
--- a/sql/parse_file.cc
+++ b/sql/parse_file.cc
@@ -282,7 +282,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
path[path_end+1]= '\0';
if ((handler= mysql_file_create(key_file_fileparser,
path, CREATE_MODE, O_RDWR | O_TRUNC,
- MYF(MY_WME))) <= 0)
+ MYF(MY_WME))) < 0)
{
DBUG_RETURN(TRUE);
}
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index e5620ec41a2..6e67a75b989 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -515,6 +515,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
element *elem;
ulonglong thd_saved_option= thd->variables.option_bits;
Query_tables_list lex_backup;
+ wait_for_commit* suspended_wfc;
DBUG_ENTER("record_gtid");
if (unlikely(!loaded))
@@ -538,6 +539,28 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
DBUG_RETURN(1);
} );
+ /*
+ If we are applying a non-transactional event group, we will be committing
+ here a transaction, but that does not imply that the event group has
+ completed or has been binlogged. So we should not trigger
+ wakeup_subsequent_commits() here.
+
+ Note: An alternative here could be to put a call to mark_start_commit() in
+ stmt_done() before the call to record_and_update_gtid(). This would
+ prevent later calling mark_start_commit() after we have run
+ wakeup_subsequent_commits() from committing the GTID update transaction
+ (which must be avoided to avoid accessing freed group_commit_orderer
+ object). It would also allow following event groups to start slightly
+ earlier. And in the cases where record_gtid() is called without an active
+ transaction, the current statement should have been binlogged already, so
+ binlog order is preserved.
+
+ But this is rather subtle, and potentially fragile. And it does not really
+ seem worth it; non-transactional loads are unlikely to benefit much from
+ parallel replication in any case. So for now, we go with the simple
+ suspend/resume of wakeup_subsequent_commits() here in record_gtid().
+ */
+ suspended_wfc= thd->suspend_subsequent_commits();
thd->lex->reset_n_backup_query_tables_list(&lex_backup);
tlist.init_one_table(STRING_WITH_LEN("mysql"),
rpl_gtid_slave_state_table_name.str,
@@ -689,6 +712,12 @@ end:
}
thd->lex->restore_backup_query_tables_list(&lex_backup);
thd->variables.option_bits= thd_saved_option;
+ thd->resume_subsequent_commits(suspended_wfc);
+ DBUG_EXECUTE_IF("inject_record_gtid_serverid_100_sleep",
+ {
+ if (gtid->server_id == 100)
+ my_sleep(500000);
+ });
DBUG_RETURN(err);
}
@@ -1089,6 +1118,27 @@ rpl_binlog_state::load(struct rpl_gtid *list, uint32 count)
}
+static int rpl_binlog_state_load_cb(rpl_gtid *gtid, void *data)
+{
+ rpl_binlog_state *self= (rpl_binlog_state *)data;
+ return self->update_nolock(gtid, false);
+}
+
+
+bool
+rpl_binlog_state::load(rpl_slave_state *slave_pos)
+{
+ bool res= false;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ reset_nolock();
+ if (slave_pos->iterate(rpl_binlog_state_load_cb, this, NULL, 0))
+ res= true;
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
rpl_binlog_state::~rpl_binlog_state()
{
free();
@@ -1849,6 +1899,31 @@ slave_connection_state::get_gtid_list(rpl_gtid *gtid_list, uint32 list_size)
/*
+ Check if the GTID position has been reached, for mysql_binlog_send().
+
+ The position has not been reached if we have anything in the state, unless
+ it has either the START_ON_EMPTY_DOMAIN flag set (which means it does not
+ belong to this master at all), or the START_OWN_SLAVE_POS (which means that
+ we start on an old position from when the server was a slave with
+ --log-slave-updates=0).
+*/
+bool
+slave_connection_state::is_pos_reached()
+{
+ uint32 i;
+
+ for (i= 0; i < hash.records; ++i)
+ {
+ entry *e= (entry *)my_hash_element(&hash, i);
+ if (!(e->flags & (START_OWN_SLAVE_POS|START_ON_EMPTY_DOMAIN)))
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
Execute a MASTER_GTID_WAIT().
The position to wait for is in gtid_str in string form.
The timeout in microseconds is in timeout_us, zero means no timeout.
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index 3e9e2fce25f..997540728a5 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -235,6 +235,7 @@ struct rpl_binlog_state
void reset();
void free();
bool load(struct rpl_gtid *list, uint32 count);
+ bool load(rpl_slave_state *slave_pos);
int update_nolock(const struct rpl_gtid *gtid, bool strict);
int update(const struct rpl_gtid *gtid, bool strict);
int update_with_next_gtid(uint32 domain_id, uint32 server_id,
@@ -287,6 +288,7 @@ struct slave_connection_state
int to_string(String *out_str);
int append_to_string(String *out_str);
int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size);
+ bool is_pos_reached();
};
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 78218e0418e..47490648a43 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -36,9 +36,9 @@ Master_info::Master_info(LEX_STRING *connection_name_arg,
rli(is_slave_recovery), port(MYSQL_PORT),
checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF),
connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0),
- slave_running(0), slave_run_id(0), sync_counter(0),
- heartbeat_period(0), received_heartbeats(0), master_id(0),
- prev_master_id(0),
+ slave_running(0), slave_run_id(0), clock_diff_with_master(0),
+ sync_counter(0), heartbeat_period(0), received_heartbeats(0),
+ master_id(0), prev_master_id(0),
using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0),
gtid_reconnect_event_skip_count(0), gtid_event_seen(false)
{
@@ -864,6 +864,7 @@ Master_info_index::Master_info_index()
memcpy(index_file_name + dir_length, "multi-", 6);
bzero((char*) &index_file, sizeof(index_file));
+ index_file.file= -1;
}
Master_info_index::~Master_info_index()
@@ -871,7 +872,7 @@ Master_info_index::~Master_info_index()
/* This will close connection for all objects in the cache */
my_hash_free(&master_info_hash);
end_io_cache(&index_file);
- if (index_file.file > 0)
+ if (index_file.file >= 0)
my_close(index_file.file, MYF(MY_WME));
}
@@ -965,7 +966,7 @@ bool Master_info_index::init_all_master_info()
{
/* Master_info is not in HASH; Add it */
if (master_info_index->add_master_info(mi, FALSE))
- return 1;
+ DBUG_RETURN(1);
succ_num++;
unlock_slave_threads(mi);
}
@@ -998,7 +999,7 @@ bool Master_info_index::init_all_master_info()
/* Master_info was not registered; add it */
if (master_info_index->add_master_info(mi, FALSE))
- return 1;
+ DBUG_RETURN(1);
succ_num++;
unlock_slave_threads(mi);
@@ -1095,7 +1096,7 @@ Master_info_index::get_master_info(LEX_STRING *connection_name,
mysql_mutex_assert_owner(&LOCK_active_mi);
if (!this) // master_info_index is set to NULL on server shutdown
- return NULL;
+ DBUG_RETURN(NULL);
/* Make name lower case for comparison */
res= strmake(buff, connection_name->str, connection_name->length);
@@ -1247,10 +1248,10 @@ bool Master_info_index::remove_master_info(LEX_STRING *name)
bool Master_info_index::give_error_if_slave_running()
{
- DBUG_ENTER("warn_if_slave_running");
+ DBUG_ENTER("give_error_if_slave_running");
mysql_mutex_assert_owner(&LOCK_active_mi);
if (!this) // master_info_index is set to NULL on server shutdown
- return TRUE;
+ DBUG_RETURN(TRUE);
for (uint i= 0; i< master_info_hash.records; ++i)
{
@@ -1268,6 +1269,32 @@ bool Master_info_index::give_error_if_slave_running()
/**
+ Master_info_index::any_slave_sql_running()
+
+ The LOCK_active_mi must be held while calling this function.
+
+ @return
+ TRUE If some slave SQL thread is running.
+ FALSE No slave SQL thread is running
+*/
+
+bool Master_info_index::any_slave_sql_running()
+{
+ DBUG_ENTER("any_slave_sql_running");
+ if (!this) // master_info_index is set to NULL on server shutdown
+ DBUG_RETURN(TRUE);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ Master_info *mi= (Master_info *)my_hash_element(&master_info_hash, i);
+ if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Master_info_index::start_all_slaves()
Start all slaves that was not running.
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index ebb1b541728..2b0b40feb3d 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -218,6 +218,7 @@ public:
Master_info *get_master_info(LEX_STRING *connection_name,
Sql_condition::enum_warning_level warning);
bool give_error_if_slave_running();
+ bool any_slave_sql_running();
bool start_all_slaves(THD *thd);
bool stop_all_slaves(THD *thd);
};
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 46c3e4aaaf4..305e8053032 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -2,6 +2,7 @@
#include "rpl_parallel.h"
#include "slave.h"
#include "rpl_mi.h"
+#include "sql_parse.h"
#include "debug_sync.h"
/*
@@ -113,6 +114,7 @@ finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
wait_for_commit *wfc= &rgi->commit_orderer;
int err;
+ thd->get_stmt_da()->set_overwrite_status(true);
/*
Remove any left-over registration to wait for a prior commit to
complete. Normally, such wait would already have been removed at
@@ -129,14 +131,14 @@ finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
for us to complete and rely on this also ensuring that any other
event in the group has completed.
- But in the error case, we have to abort anyway, and it seems best
- to just complete as quickly as possible with unregister. Anyone
- waiting for us will in any case receive the error back from their
- wait_for_prior_commit() call.
+ And in the error case, correct GCO lifetime relies on the fact that once
+ the last event group in the GCO has executed wait_for_prior_commit(),
+ all earlier event groups have also committed; this way no more
+ mark_start_commit() calls can be made and it is safe to de-allocate
+ the GCO.
*/
- if (rgi->worker_error)
- wfc->unregister_wait_for_prior_commit();
- else if ((err= wfc->wait_for_prior_commit(thd)))
+ err= wfc->wait_for_prior_commit(thd);
+ if (unlikely(err) && !rgi->worker_error)
signal_error_to_sql_driver_thread(thd, rgi, err);
thd->wait_for_commit_ptr= NULL;
@@ -166,12 +168,30 @@ finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
done and also no longer need waiting for.
*/
entry->last_committed_sub_id= sub_id;
+ if (entry->need_sub_id_signal)
+ mysql_cond_broadcast(&entry->COND_parallel_entry);
/* Now free any GCOs in which all transactions have committed. */
group_commit_orderer *tmp_gco= rgi->gco;
while (tmp_gco &&
- (!tmp_gco->next_gco || tmp_gco->last_sub_id > sub_id))
+ (!tmp_gco->next_gco || tmp_gco->last_sub_id > sub_id ||
+ tmp_gco->next_gco->wait_count > entry->count_committing_event_groups))
+ {
+ /*
+ We must not free a GCO before the wait_count of the following GCO has
+ been reached and wakeup has been sent. Otherwise we will lose the
+ wakeup and hang (there were several such bugs in the past).
+
+ The intention is that this is ensured already since we only free when
+ the last event group in the GCO has committed
+ (tmp_gco->last_sub_id <= sub_id). However, if we have a bug, we have
+ extra check on next_gco->wait_count to hopefully avoid hanging; we
+ have here an assertion in debug builds that this check does not in
+ fact trigger.
+ */
+ DBUG_ASSERT(!tmp_gco->next_gco || tmp_gco->last_sub_id > sub_id);
tmp_gco= tmp_gco->prev_gco;
+ }
while (tmp_gco)
{
group_commit_orderer *prev_gco= tmp_gco->prev_gco;
@@ -193,6 +213,10 @@ finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
thd->clear_error();
thd->reset_killed();
+ /*
+ Would do thd->get_stmt_da()->set_overwrite_status(false) here, but
+ reset_diagnostics_area() already does that.
+ */
thd->get_stmt_da()->reset_diagnostics_area();
wfc->wakeup_subsequent_commits(rgi->worker_error);
}
@@ -305,7 +329,7 @@ retry_event_group(rpl_group_info *rgi, rpl_parallel_thread *rpt,
IO_CACHE rlog;
LOG_INFO linfo;
File fd= (File)-1;
- const char *errmsg= NULL;
+ const char *errmsg;
inuse_relaylog *ir= rgi->relay_log;
uint64 event_count;
uint64 events_to_execute= rgi->retry_event_count;
@@ -321,6 +345,7 @@ retry_event_group(rpl_group_info *rgi, rpl_parallel_thread *rpt,
do_retry:
event_count= 0;
err= 0;
+ errmsg= NULL;
/*
If we already started committing before getting the deadlock (or other
@@ -356,7 +381,16 @@ do_retry:
*/
if(thd->wait_for_commit_ptr)
thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
+ DBUG_EXECUTE_IF("inject_mdev8031", {
+ /* Simulate that we get deadlock killed at this exact point. */
+ rgi->killed_for_retry= true;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->killed= KILL_CONNECTION;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ });
rgi->cleanup_context(thd, 1);
+ thd->reset_killed();
+ thd->clear_error();
/*
If we retry due to a deadlock kill that occured during the commit step, we
@@ -372,9 +406,46 @@ do_retry:
statistic_increment(slave_retried_transactions, LOCK_status);
mysql_mutex_unlock(&rli->data_lock);
- mysql_mutex_lock(&entry->LOCK_parallel_entry);
- register_wait_for_prior_event_group_commit(rgi, entry);
- mysql_mutex_unlock(&entry->LOCK_parallel_entry);
+ for (;;)
+ {
+ mysql_mutex_lock(&entry->LOCK_parallel_entry);
+ register_wait_for_prior_event_group_commit(rgi, entry);
+ mysql_mutex_unlock(&entry->LOCK_parallel_entry);
+
+ /*
+ Let us wait for all prior transactions to complete before trying again.
+ This way, we avoid repeatedly conflicting with and getting deadlock
+ killed by the same earlier transaction.
+ */
+ if (!(err= thd->wait_for_prior_commit()))
+ break;
+
+ convert_kill_to_deadlock_error(rgi);
+ if (!has_temporary_error(thd))
+ goto err;
+ /*
+ If we get a temporary error such as a deadlock kill, we can safely
+ ignore it, as we already rolled back.
+
+ But we still want to retry the wait for the prior transaction to
+ complete its commit.
+ */
+ thd->clear_error();
+ thd->reset_killed();
+ if(thd->wait_for_commit_ptr)
+ thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
+ DBUG_EXECUTE_IF("inject_mdev8031", {
+ /* Inject a small sleep to give prior transaction a chance to commit. */
+ my_sleep(100000);
+ });
+ }
+
+ /*
+ Let us clear any lingering deadlock kill one more time, here after
+ wait_for_prior_commit() has completed. This should rule out any
+ possibility of an old deadlock kill lingering on beyond this point.
+ */
+ thd->reset_killed();
strmake_buf(log_name, ir->name);
if ((fd= open_binlog(&rlog, log_name, &errmsg)) <0)
@@ -391,6 +462,14 @@ do_retry:
err= 1;
goto err;
}
+ DBUG_EXECUTE_IF("inject_mdev8031", {
+ /* Simulate pending KILL caught in read_relay_log_description_event(). */
+ if (thd->check_killed()) {
+ thd->send_kill_message();
+ err= 1;
+ goto err;
+ }
+ });
my_b_seek(&rlog, cur_offset);
do
@@ -413,7 +492,7 @@ do_retry:
{
errmsg= "slave SQL thread aborted because of I/O error";
err= 1;
- goto err;
+ goto check_retry;
}
if (rlog.error > 0)
{
@@ -442,10 +521,25 @@ do_retry:
}
strmake_buf(log_name ,linfo.log_file_name);
+ DBUG_EXECUTE_IF("inject_retry_event_group_open_binlog_kill", {
+ if (retries < 2)
+ {
+ /* Simulate that we get deadlock killed during open_binlog(). */
+ mysql_reset_thd_for_next_command(thd);
+ rgi->killed_for_retry= true;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->killed= KILL_CONNECTION;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ thd->send_kill_message();
+ fd= (File)-1;
+ err= 1;
+ goto check_retry;
+ }
+ });
if ((fd= open_binlog(&rlog, log_name, &errmsg)) <0)
{
err= 1;
- goto err;
+ goto check_retry;
}
/* Loop to try again on the new log file. */
}
@@ -488,26 +582,31 @@ do_retry:
if (retries == 0) err= dbug_simulate_tmp_error(rgi, thd););
DBUG_EXECUTE_IF("rpl_parallel_simulate_infinite_temp_err_gtid_0_x_100",
err= dbug_simulate_tmp_error(rgi, thd););
- if (err)
+ if (!err)
+ continue;
+
+check_retry:
+ convert_kill_to_deadlock_error(rgi);
+ if (has_temporary_error(thd))
{
- convert_kill_to_deadlock_error(rgi);
- if (has_temporary_error(thd))
+ ++retries;
+ if (retries < slave_trans_retries)
{
- ++retries;
- if (retries < slave_trans_retries)
+ if (fd >= 0)
{
end_io_cache(&rlog);
mysql_file_close(fd, MYF(MY_WME));
fd= (File)-1;
- goto do_retry;
}
- sql_print_error("Slave worker thread retried transaction %lu time(s) "
- "in vain, giving up. Consider raising the value of "
- "the slave_transaction_retries variable.",
- slave_trans_retries);
+ goto do_retry;
}
- goto err;
+ sql_print_error("Slave worker thread retried transaction %lu time(s) "
+ "in vain, giving up. Consider raising the value of "
+ "the slave_transaction_retries variable.",
+ slave_trans_retries);
}
+ goto err;
+
} while (event_count < events_to_execute);
err:
@@ -640,7 +739,7 @@ handle_rpl_parallel_thread(void *arg)
}
DBUG_ASSERT(qev->typ==rpl_parallel_thread::queued_event::QUEUED_EVENT);
- thd->rgi_slave= group_rgi= rgi;
+ thd->rgi_slave= rgi;
gco= rgi->gco;
/* Handle a new event group, which will be initiated by a GTID event. */
if ((event_type= qev->ev->get_type_code()) == GTID_EVENT)
@@ -657,6 +756,21 @@ handle_rpl_parallel_thread(void *arg)
}
});
+ if(unlikely(thd->wait_for_commit_ptr) && group_rgi != NULL)
+ {
+ /*
+ This indicates that we get a new GTID event in the middle of
+ a not completed event group. This is corrupt binlog (the master
+ will never write such binlog), so it does not happen unless
+ someone tries to inject wrong crafted binlog, but let us still
+ try to handle it somewhat nicely.
+ */
+ group_rgi->cleanup_context(thd, true);
+ finish_event_group(rpt, group_rgi->gtid_sub_id,
+ group_rgi->parallel_entry, group_rgi);
+ rpt->loc_free_rgi(group_rgi);
+ }
+
in_event_group= true;
/*
If the standalone flag is set, then this event group consists of a
@@ -736,25 +850,11 @@ handle_rpl_parallel_thread(void *arg)
if (unlikely(entry->stop_on_error_sub_id <= rgi->wait_commit_sub_id))
skip_event_group= true;
- else
- register_wait_for_prior_event_group_commit(rgi, entry);
+ register_wait_for_prior_event_group_commit(rgi, entry);
unlock_or_exit_cond(thd, &entry->LOCK_parallel_entry,
&did_enter_cond, &old_stage);
- if(thd->wait_for_commit_ptr)
- {
- /*
- This indicates that we get a new GTID event in the middle of
- a not completed event group. This is corrupt binlog (the master
- will never write such binlog), so it does not happen unless
- someone tries to inject wrong crafted binlog, but let us still
- try to handle it somewhat nicely.
- */
- rgi->cleanup_context(thd, true);
- thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
- thd->wait_for_commit_ptr->wakeup_subsequent_commits(rgi->worker_error);
- }
thd->wait_for_commit_ptr= &rgi->commit_orderer;
if (opt_gtid_ignore_duplicates)
@@ -780,6 +880,7 @@ handle_rpl_parallel_thread(void *arg)
}
}
+ group_rgi= rgi;
group_ending= is_group_ending(qev->ev, event_type);
if (group_ending && likely(!rgi->worker_error))
{
@@ -824,7 +925,9 @@ handle_rpl_parallel_thread(void *arg)
else
{
delete qev->ev;
+ thd->get_stmt_da()->set_overwrite_status(true);
err= thd->wait_for_prior_commit();
+ thd->get_stmt_da()->set_overwrite_status(false);
}
end_of_group=
@@ -941,9 +1044,9 @@ dealloc_gco(group_commit_orderer *gco)
}
-int
+static int
rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
- uint32 new_count, bool skip_check)
+ uint32 new_count)
{
uint32 i;
rpl_parallel_thread **new_list= NULL;
@@ -988,24 +1091,6 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
new_free_list= new_list[i];
}
- if (!skip_check)
- {
- mysql_mutex_lock(&LOCK_active_mi);
- if (master_info_index->give_error_if_slave_running())
- {
- mysql_mutex_unlock(&LOCK_active_mi);
- goto err;
- }
- if (pool->changing)
- {
- mysql_mutex_unlock(&LOCK_active_mi);
- my_error(ER_CHANGE_SLAVE_PARALLEL_THREADS_ACTIVE, MYF(0));
- goto err;
- }
- pool->changing= true;
- mysql_mutex_unlock(&LOCK_active_mi);
- }
-
/*
Grab each old thread in turn, and signal it to stop.
@@ -1065,13 +1150,6 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
mysql_mutex_unlock(&pool->threads[i]->LOCK_rpl_thread);
}
- if (!skip_check)
- {
- mysql_mutex_lock(&LOCK_active_mi);
- pool->changing= false;
- mysql_mutex_unlock(&LOCK_active_mi);
- }
-
mysql_mutex_lock(&pool->LOCK_rpl_thread_pool);
mysql_cond_broadcast(&pool->COND_rpl_thread_pool);
mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool);
@@ -1098,16 +1176,26 @@ err:
}
my_free(new_list);
}
- if (!skip_check)
- {
- mysql_mutex_lock(&LOCK_active_mi);
- pool->changing= false;
- mysql_mutex_unlock(&LOCK_active_mi);
- }
return 1;
}
+int
+rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool)
+{
+ if (!pool->count)
+ return rpl_parallel_change_thread_count(pool, opt_slave_parallel_threads);
+ return 0;
+}
+
+
+int
+rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool)
+{
+ return rpl_parallel_change_thread_count(pool, 0);
+}
+
+
void
rpl_parallel_thread::batch_free()
{
@@ -1351,7 +1439,7 @@ rpl_parallel_thread::loc_free_gco(group_commit_orderer *gco)
rpl_parallel_thread_pool::rpl_parallel_thread_pool()
- : count(0), threads(0), free_list(0), changing(false), inited(false)
+ : count(0), threads(0), free_list(0), inited(false)
{
}
@@ -1366,10 +1454,14 @@ rpl_parallel_thread_pool::init(uint32 size)
mysql_mutex_init(key_LOCK_rpl_thread_pool, &LOCK_rpl_thread_pool,
MY_MUTEX_INIT_SLOW);
mysql_cond_init(key_COND_rpl_thread_pool, &COND_rpl_thread_pool, NULL);
- changing= false;
inited= true;
- return rpl_parallel_change_thread_count(this, size, true);
+ /*
+ The pool is initially empty. Threads will be spawned when a slave SQL
+ thread is started.
+ */
+
+ return 0;
}
@@ -1378,7 +1470,7 @@ rpl_parallel_thread_pool::destroy()
{
if (!inited)
return;
- rpl_parallel_change_thread_count(this, 0, true);
+ rpl_parallel_change_thread_count(this, 0);
mysql_mutex_destroy(&LOCK_rpl_thread_pool);
mysql_cond_destroy(&COND_rpl_thread_pool);
inited= false;
@@ -1804,28 +1896,66 @@ rpl_parallel::wait_for_workers_idle(THD *thd)
max_i= domain_hash.records;
for (i= 0; i < max_i; ++i)
{
- bool active;
- wait_for_commit my_orderer;
+ PSI_stage_info old_stage;
struct rpl_parallel_entry *e;
+ int err= 0;
e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i);
mysql_mutex_lock(&e->LOCK_parallel_entry);
- if ((active= (e->current_sub_id > e->last_committed_sub_id)))
+ e->need_sub_id_signal= true;
+ thd->ENTER_COND(&e->COND_parallel_entry, &e->LOCK_parallel_entry,
+ &stage_waiting_for_workers_idle, &old_stage);
+ while (e->current_sub_id > e->last_committed_sub_id)
{
- wait_for_commit *waitee= &e->current_group_info->commit_orderer;
- my_orderer.register_wait_for_prior_commit(waitee);
- thd->wait_for_commit_ptr= &my_orderer;
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ err= 1;
+ break;
+ }
+ mysql_cond_wait(&e->COND_parallel_entry, &e->LOCK_parallel_entry);
}
- mysql_mutex_unlock(&e->LOCK_parallel_entry);
- if (active)
+ e->need_sub_id_signal= false;
+ thd->EXIT_COND(&old_stage);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/*
+ Handle seeing a GTID during slave restart in GTID mode. If we stopped with
+ different replication domains having reached different positions in the relay
+ log, we need to skip event groups in domains that are further progressed.
+
+ Updates the state with the seen GTID, and returns true if this GTID should
+ be skipped, false otherwise.
+*/
+bool
+process_gtid_for_restart_pos(Relay_log_info *rli, rpl_gtid *gtid)
+{
+ slave_connection_state::entry *gtid_entry;
+ slave_connection_state *state= &rli->restart_gtid_pos;
+
+ if (likely(state->count() == 0) ||
+ !(gtid_entry= state->find_entry(gtid->domain_id)))
+ return false;
+ if (gtid->server_id == gtid_entry->gtid.server_id)
+ {
+ uint64 seq_no= gtid_entry->gtid.seq_no;
+ if (gtid->seq_no >= seq_no)
{
- int err= my_orderer.wait_for_prior_commit(thd);
- thd->wait_for_commit_ptr= NULL;
- if (err)
- return err;
+ /*
+ This domain has reached its start position. So remove it, so that
+ further events will be processed normally.
+ */
+ state->remove(&gtid_entry->gtid);
}
+ return gtid->seq_no <= seq_no;
}
- return 0;
+ else
+ return true;
}
@@ -1890,13 +2020,15 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
return -1;
/* Execute pre-10.0 event, which have no GTID, in single-threaded mode. */
- if (unlikely(!current) && typ != GTID_EVENT)
+ is_group_event= Log_event::is_group_event(typ);
+ if (unlikely(!current) && typ != GTID_EVENT &&
+ !(unlikely(rli->gtid_skip_flag != GTID_SKIP_NOT) && is_group_event))
return -1;
/* ToDo: what to do with this lock?!? */
mysql_mutex_unlock(&rli->data_lock);
- if (typ == FORMAT_DESCRIPTION_EVENT)
+ if (unlikely(typ == FORMAT_DESCRIPTION_EVENT))
{
Format_description_log_event *fdev=
static_cast<Format_description_log_event *>(ev);
@@ -1922,6 +2054,19 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
}
}
}
+ else if (unlikely(typ == GTID_LIST_EVENT))
+ {
+ Gtid_list_log_event *glev= static_cast<Gtid_list_log_event *>(ev);
+ rpl_gtid *list= glev->list;
+ uint32 count= glev->count;
+ rli->update_relay_log_state(list, count);
+ while (count)
+ {
+ process_gtid_for_restart_pos(rli, list);
+ ++list;
+ --count;
+ }
+ }
/*
Stop queueing additional event groups once the SQL thread is requested to
@@ -1931,7 +2076,6 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
been partially queued, but after that we will just ignore any further
events the SQL driver thread may try to queue, and eventually it will stop.
*/
- is_group_event= Log_event::is_group_event(typ);
if ((typ == GTID_EVENT || !is_group_event) && rli->abort_slave)
sql_thread_stopping= true;
if (sql_thread_stopping)
@@ -1944,8 +2088,34 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
return 0;
}
+ if (unlikely(rli->gtid_skip_flag != GTID_SKIP_NOT) && is_group_event)
+ {
+ if (typ == GTID_EVENT)
+ rli->gtid_skip_flag= GTID_SKIP_NOT;
+ else
+ {
+ if (rli->gtid_skip_flag == GTID_SKIP_STANDALONE)
+ {
+ if (!Log_event::is_part_of_group(typ))
+ rli->gtid_skip_flag= GTID_SKIP_NOT;
+ }
+ else
+ {
+ DBUG_ASSERT(rli->gtid_skip_flag == GTID_SKIP_TRANSACTION);
+ if (typ == XID_EVENT ||
+ (typ == QUERY_EVENT &&
+ (((Query_log_event *)ev)->is_commit() ||
+ ((Query_log_event *)ev)->is_rollback())))
+ rli->gtid_skip_flag= GTID_SKIP_NOT;
+ }
+ delete_or_keep_event_post_apply(serial_rgi, typ, ev);
+ return 0;
+ }
+ }
+
if (typ == GTID_EVENT)
{
+ rpl_gtid gtid;
Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev);
uint32 domain_id= (rli->mi->using_gtid == Master_info::USE_GTID_NO ?
0 : gtid_ev->domain_id);
@@ -1956,6 +2126,23 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
return 1;
}
current= e;
+
+ gtid.domain_id= gtid_ev->domain_id;
+ gtid.server_id= gtid_ev->server_id;
+ gtid.seq_no= gtid_ev->seq_no;
+ rli->update_relay_log_state(&gtid, 1);
+ if (process_gtid_for_restart_pos(rli, &gtid))
+ {
+ /*
+ This domain has progressed further into the relay log before the last
+ SQL thread restart. So we need to skip this event group to not doubly
+ apply it.
+ */
+ rli->gtid_skip_flag= ((gtid_ev->flags2 & Gtid_log_event::FL_STANDALONE) ?
+ GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION);
+ delete_or_keep_event_post_apply(serial_rgi, typ, ev);
+ return 0;
+ }
}
else
e= current;
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index 2604cd98527..0c2e4270646 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -53,7 +53,7 @@ struct group_commit_orderer {
group_commit_orderer *prev_gco;
group_commit_orderer *next_gco;
/*
- The sub_id of last event group in this the previous GCO.
+ The sub_id of last event group in the previous GCO.
Only valid if prev_gco != NULL.
*/
uint64 prior_sub_id;
@@ -204,7 +204,6 @@ struct rpl_parallel_thread_pool {
struct rpl_parallel_thread *free_list;
mysql_mutex_t LOCK_rpl_thread_pool;
mysql_cond_t COND_rpl_thread_pool;
- bool changing;
bool inited;
rpl_parallel_thread_pool();
@@ -229,6 +228,12 @@ struct rpl_parallel_entry {
*/
bool force_abort;
/*
+ Set in wait_for_workers_idle() to show that it is waiting, so that
+ finish_event_group knows to signal it when last_committed_sub_id is
+ increased.
+ */
+ bool need_sub_id_signal;
+ /*
At STOP SLAVE (force_abort=true), we do not want to process all events in
the queue (which could unnecessarily delay stop, if a lot of events happen
to be queued). The stop_count provides a safe point at which to stop, so
@@ -314,8 +319,8 @@ struct rpl_parallel {
extern struct rpl_parallel_thread_pool global_rpl_thread_pool;
-extern int rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
- uint32 new_count,
- bool skip_check= false);
+extern int rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool);
+extern int rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool);
+extern bool process_gtid_for_restart_pos(Relay_log_info *rli, rpl_gtid *gtid);
#endif /* RPL_PARALLEL_H */
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index a751dd16650..9bd0ca55b01 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -62,7 +62,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0),
last_master_timestamp(0), sql_thread_caught_up(true), slave_skip_counter(0),
abort_pos_wait(0), slave_run_id(0), sql_driver_thd(),
- inited(0), abort_slave(0), stop_for_until(0),
+ gtid_skip_flag(GTID_SKIP_NOT), inited(0), abort_slave(0), stop_for_until(0),
slave_running(0), until_condition(UNTIL_NONE),
until_log_pos(0), retried_trans(0), executed_entries(0),
m_flags(0)
@@ -100,18 +100,9 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
Relay_log_info::~Relay_log_info()
{
- inuse_relaylog *cur;
DBUG_ENTER("Relay_log_info::~Relay_log_info");
- cur= inuse_relaylog_list;
- while (cur)
- {
- DBUG_ASSERT(cur->queued_count == cur->dequeued_count);
- inuse_relaylog *next= cur->next;
- my_atomic_rwlock_destroy(&cur->inuse_relaylog_atomic_lock);
- my_free(cur);
- cur= next;
- }
+ reset_inuse_relaylog();
mysql_mutex_destroy(&run_lock);
mysql_mutex_destroy(&data_lock);
mysql_mutex_destroy(&log_space_lock);
@@ -1299,13 +1290,9 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
}
-void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
- time_t event_creation_time, THD *thd,
+void Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd,
rpl_group_info *rgi)
{
-#ifndef DBUG_OFF
- extern uint debug_not_change_ts_if_art_event;
-#endif
DBUG_ENTER("Relay_log_info::stmt_done");
DBUG_ASSERT(rgi->rli == this);
@@ -1359,22 +1346,6 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
if (mi->using_gtid == Master_info::USE_GTID_NO)
flush_relay_log_info(this);
DBUG_EXECUTE_IF("inject_crash_after_flush_rli", DBUG_SUICIDE(););
- /*
- Note that Rotate_log_event::do_apply_event() does not call this
- function, so there is no chance that a fake rotate event resets
- last_master_timestamp. Note that we update without mutex
- (probably ok - except in some very rare cases, only consequence
- is that value may take some time to display in
- Seconds_Behind_Master - not critical).
-
- In parallel replication, we take care to not set last_master_timestamp
- backwards, in case of out-of-order calls here.
- */
- if (!(event_creation_time == 0 &&
- IF_DBUG(debug_not_change_ts_if_art_event > 0, 1)) &&
- !(rgi->is_parallel_exec && event_creation_time <= last_master_timestamp)
- )
- last_master_timestamp= event_creation_time;
}
DBUG_VOID_RETURN;
}
@@ -1384,14 +1355,34 @@ int
Relay_log_info::alloc_inuse_relaylog(const char *name)
{
inuse_relaylog *ir;
+ uint32 gtid_count;
+ rpl_gtid *gtid_list;
if (!(ir= (inuse_relaylog *)my_malloc(sizeof(*ir), MYF(MY_WME|MY_ZEROFILL))))
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*ir));
return 1;
}
+ gtid_count= relay_log_state.count();
+ if (!(gtid_list= (rpl_gtid *)my_malloc(sizeof(*gtid_list)*gtid_count,
+ MYF(MY_WME))))
+ {
+ my_free(ir);
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*gtid_list)*gtid_count);
+ return 1;
+ }
+ if (relay_log_state.get_gtid_list(gtid_list, gtid_count))
+ {
+ my_free(gtid_list);
+ my_free(ir);
+ DBUG_ASSERT(0 /* Should not be possible as we allocated correct length */);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return 1;
+ }
ir->rli= this;
strmake_buf(ir->name, name);
+ ir->relay_log_state= gtid_list;
+ ir->relay_log_state_count= gtid_count;
if (!inuse_relaylog_list)
inuse_relaylog_list= ir;
@@ -1407,6 +1398,45 @@ Relay_log_info::alloc_inuse_relaylog(const char *name)
}
+void
+Relay_log_info::free_inuse_relaylog(inuse_relaylog *ir)
+{
+ my_free(ir->relay_log_state);
+ my_atomic_rwlock_destroy(&ir->inuse_relaylog_atomic_lock);
+ my_free(ir);
+}
+
+
+void
+Relay_log_info::reset_inuse_relaylog()
+{
+ inuse_relaylog *cur= inuse_relaylog_list;
+ while (cur)
+ {
+ DBUG_ASSERT(cur->queued_count == cur->dequeued_count);
+ inuse_relaylog *next= cur->next;
+ free_inuse_relaylog(cur);
+ cur= next;
+ }
+ inuse_relaylog_list= last_inuse_relaylog= NULL;
+}
+
+
+int
+Relay_log_info::update_relay_log_state(rpl_gtid *gtid_list, uint32 count)
+{
+ int res= 0;
+ while (count)
+ {
+ if (relay_log_state.update_nolock(gtid_list, false))
+ res= 1;
+ ++gtid_list;
+ --count;
+ }
+ return res;
+}
+
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int
rpl_load_gtid_slave_state(THD *thd)
@@ -1719,7 +1749,7 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
trans_rollback(thd); // if a "real transaction"
/*
Now that we have rolled back the transaction, make sure we do not
- errorneously update the GTID position.
+ erroneously update the GTID position.
*/
gtid_pending= false;
}
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index fb4e3261468..2d92f384ef3 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -269,6 +269,8 @@ public:
int events_till_abort;
#endif
+ enum_gtid_skip_type gtid_skip_flag;
+
/*
inited changes its value within LOCK_active_mi-guarded critical
sections at times of start_slave_threads() (0->1) and end_slave() (1->0).
@@ -344,6 +346,21 @@ public:
size_t slave_patternload_file_size;
rpl_parallel parallel;
+ /*
+ The relay_log_state keeps track of the current binlog state of the execution
+ of the relay log. This is used to know where to resume current GTID position
+ if the slave thread is stopped and restarted.
+ It is only accessed from the SQL thread, so it does not need any locking.
+ */
+ rpl_binlog_state relay_log_state;
+ /*
+ The restart_gtid_state is used when the SQL thread restarts on a relay log
+ in GTID mode. In multi-domain parallel replication, each domain may have a
+ separat position, so some events in more progressed domains may need to be
+ skipped. This keeps track of the domains that have not yet reached their
+ starting event.
+ */
+ slave_connection_state restart_gtid_pos;
Relay_log_info(bool is_slave_recovery);
~Relay_log_info();
@@ -398,16 +415,12 @@ public:
Master log position of the event. The position is recorded in the
relay log info and used to produce information for <code>SHOW
SLAVE STATUS</code>.
-
- @param event_creation_time
- Timestamp for the creation of the event on the master side. The
- time stamp is recorded in the relay log info and used to compute
- the <code>Seconds_behind_master</code> field.
*/
- void stmt_done(my_off_t event_log_pos,
- time_t event_creation_time, THD *thd,
- rpl_group_info *rgi);
+ void stmt_done(my_off_t event_log_pos, THD *thd, rpl_group_info *rgi);
int alloc_inuse_relaylog(const char *name);
+ void free_inuse_relaylog(inuse_relaylog *ir);
+ void reset_inuse_relaylog();
+ int update_relay_log_state(rpl_gtid *gtid_list, uint32 count);
/**
Is the replication inside a group?
@@ -497,6 +510,12 @@ private:
struct inuse_relaylog {
inuse_relaylog *next;
Relay_log_info *rli;
+ /*
+ relay_log_state holds the binlog state corresponding to the start of this
+ relay log file. It is an array with relay_log_state_count elements.
+ */
+ rpl_gtid *relay_log_state;
+ uint32 relay_log_state_count;
/* Number of events in this relay log queued for worker threads. */
int64 queued_count;
/* Number of events completed by worker threads. */
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index 9067f1e4253..146bf3b0c0e 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -452,7 +452,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_
CHARSET_INFO *cs= str->charset();
uint32 length=
cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "decimal(%d,?)", metadata);
+ "decimal(%d,?)/*old*/", metadata);
str->length(length);
}
break;
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 4980ba06604..6954170e86c 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7080,7 +7080,7 @@ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO
ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO
eng "Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a stored function or trigger"
ER_GTID_POSITION_NOT_FOUND_IN_BINLOG2
- eng "Connecting slave requested to start from GTID %u-%u-%llu, which is not in the master's binlog. Since the master's binlog contains GTIDs with higher sequence numbers, it probably means that the slave has diverged due to executing extra errorneous transactions"
+ eng "Connecting slave requested to start from GTID %u-%u-%llu, which is not in the master's binlog. Since the master's binlog contains GTIDs with higher sequence numbers, it probably means that the slave has diverged due to executing extra erroneous transactions"
ER_BINLOG_MUST_BE_EMPTY
eng "This operation is not allowed if any GTID has been logged to the binary log. Run RESET MASTER first to erase the log"
ER_NO_SUCH_QUERY
diff --git a/sql/slave.cc b/sql/slave.cc
index c83f09c7510..4129c4cbc98 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, SkySQL Ab.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -148,26 +148,18 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev);
static bool wait_for_relay_log_space(Relay_log_info* rli);
static bool io_slave_killed(Master_info* mi);
static bool sql_slave_killed(rpl_group_info *rgi);
-static int init_slave_thread(THD* thd, Master_info *mi,
- SLAVE_THD_TYPE thd_type);
+static int init_slave_thread(THD*, Master_info *, SLAVE_THD_TYPE);
static void print_slave_skip_errors(void);
static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi);
-static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
- bool suppress_warnings);
-static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
- bool reconnect, bool suppress_warnings);
+static int safe_reconnect(THD*, MYSQL*, Master_info*, bool);
+static int connect_to_master(THD*, MYSQL*, Master_info*, bool, bool);
static Log_event* next_event(rpl_group_info* rgi, ulonglong *event_size);
static int queue_event(Master_info* mi,const char* buf,ulong event_len);
-static int terminate_slave_thread(THD *thd,
- mysql_mutex_t *term_lock,
- mysql_cond_t *term_cond,
- volatile uint *slave_running,
- bool skip_lock);
+static int terminate_slave_thread(THD *, mysql_mutex_t *, mysql_cond_t *,
+ volatile uint *, bool);
static bool check_io_slave_killed(Master_info *mi, const char *info);
-static bool send_show_master_info_header(THD *thd, bool full,
- size_t gtid_pos_length);
-static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
- String *gtid_pos);
+static bool send_show_master_info_header(THD *, bool, size_t);
+static bool send_show_master_info_data(THD *, Master_info *, bool, String *);
/*
Function to set the slave's max_allowed_packet based on the value
of slave_max_allowed_packet.
@@ -371,6 +363,9 @@ int init_slave()
if (run_slave_init_thread())
return 1;
+ if (global_rpl_thread_pool.init(opt_slave_parallel_threads))
+ return 1;
+
/*
This is called when mysqld starts. Before client connections are
accepted. However bootstrap may conflict with us if it does START SLAVE.
@@ -404,9 +399,6 @@ int init_slave()
goto err;
}
- if (global_rpl_thread_pool.init(opt_slave_parallel_threads))
- return 1;
-
/*
If --slave-skip-errors=... was not used, the string value for the
system variable has not been set up yet. Do it now.
@@ -653,6 +645,9 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
mysql_mutex_unlock(log_lock);
}
+ if (opt_slave_parallel_threads > 0 &&
+ !master_info_index->any_slave_sql_running())
+ rpl_parallel_inactivate_pool(&global_rpl_thread_pool);
if (thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL))
{
DBUG_PRINT("info",("Terminating IO thread"));
@@ -943,6 +938,8 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
Master_info::USE_GTID_CURRENT_POS);
mi->events_queued_since_last_gtid= 0;
mi->gtid_reconnect_event_skip_count= 0;
+
+ mi->rli.restart_gtid_pos.reset();
}
if (!error && (thread_mask & SLAVE_IO))
@@ -956,7 +953,10 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
mi);
if (!error && (thread_mask & SLAVE_SQL))
{
- error= start_slave_thread(
+ if (opt_slave_parallel_threads > 0)
+ error= rpl_parallel_activate_pool(&global_rpl_thread_pool);
+ if (!error)
+ error= start_slave_thread(
#ifdef HAVE_PSI_INTERFACE
key_thread_slave_sql,
#endif
@@ -2002,11 +2002,21 @@ after_set_capability:
!(master_row= mysql_fetch_row(master_res)))
{
err_code= mysql_errno(mysql);
- errmsg= "The slave I/O thread stops because master does not support "
- "MariaDB global transaction id. A fatal error is encountered when "
- "it tries to SELECT @@GLOBAL.gtid_domain_id.";
- sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
- goto err;
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code, NULL,
+ "Get master @@GLOBAL.gtid_domain_id failed with error: %s",
+ mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ errmsg= "The slave I/O thread stops because master does not support "
+ "MariaDB global transaction id. A fatal error is encountered when "
+ "it tries to SELECT @@GLOBAL.gtid_domain_id.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
}
mysql_free_result(master_res);
master_res= NULL;
@@ -3343,7 +3353,7 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd,
else
{
/*
- Make sure we do not errorneously update gtid_slave_pos with a lingering
+ Make sure we do not erroneously update gtid_slave_pos with a lingering
GTID from this failed event group (MDEV-4906).
*/
rgi->gtid_pending= false;
@@ -3483,6 +3493,21 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
Log_event_type typ= ev->get_type_code();
/*
+ Even if we don't execute this event, we keep the master timestamp,
+ so that seconds behind master shows correct delta (there are events
+ that are not replayed, so we keep falling behind).
+
+ If it is an artificial event, or a relay log event (IO thread generated
+ event) or ev->when is set to 0, we don't update the
+ last_master_timestamp.
+ */
+ if (!(ev->is_artificial_event() || ev->is_relay_log_event() || (ev->when == 0)))
+ {
+ rli->last_master_timestamp= ev->when + (time_t) ev->exec_time;
+ DBUG_ASSERT(rli->last_master_timestamp >= 0);
+ }
+
+ /*
This tests if the position of the beginning of the current event
hits the UNTIL barrier.
*/
@@ -4504,6 +4529,16 @@ pthread_handler_t handle_slave_sql(void *arg)
serial_rgi->gtid_sub_id= 0;
serial_rgi->gtid_pending= false;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ /*
+ We initialize the relay log state from the know starting position.
+ It will then be updated as required by GTID and GTID_LIST events found
+ while applying events read from relay logs.
+ */
+ rli->relay_log_state.load(&rpl_global_gtid_slave_state);
+ }
+ rli->gtid_skip_flag = GTID_SKIP_NOT;
if (init_relay_log_pos(rli,
rli->group_relay_log_name,
rli->group_relay_log_pos,
@@ -4514,6 +4549,7 @@ pthread_handler_t handle_slave_sql(void *arg)
"Error initializing relay log position: %s", errmsg);
goto err;
}
+ rli->reset_inuse_relaylog();
if (rli->alloc_inuse_relaylog(rli->group_relay_log_name))
goto err;
@@ -4718,7 +4754,49 @@ log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME,
thd->reset_query();
thd->reset_db(NULL, 0);
if (rli->mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ ulong domain_count;
+
flush_relay_log_info(rli);
+ if (opt_slave_parallel_threads > 0)
+ {
+ /*
+ In parallel replication GTID mode, we may stop with different domains
+ at different positions in the relay log.
+
+ To handle this when we restart the SQL thread, mark the current
+ per-domain position in the Relay_log_info.
+ */
+ mysql_mutex_lock(&rpl_global_gtid_slave_state.LOCK_slave_state);
+ domain_count= rpl_global_gtid_slave_state.count();
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state);
+ if (domain_count > 1)
+ {
+ inuse_relaylog *ir;
+
+ /*
+ Load the starting GTID position, so that we can skip already applied
+ GTIDs when we restart the SQL thread. And set the start position in
+ the relay log back to a known safe place to start (prior to any not
+ yet applied transaction in any domain).
+ */
+ rli->restart_gtid_pos.load(&rpl_global_gtid_slave_state, NULL, 0);
+ if ((ir= rli->inuse_relaylog_list))
+ {
+ rpl_gtid *gtid= ir->relay_log_state;
+ uint32 count= ir->relay_log_state_count;
+ while (count > 0)
+ {
+ process_gtid_for_restart_pos(rli, gtid);
+ ++gtid;
+ --count;
+ }
+ strmake_buf(rli->group_relay_log_name, ir->name);
+ rli->group_relay_log_pos= BIN_LOG_HEADER_SIZE;
+ }
+ }
+ }
+ }
THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
thd->add_status_to_global();
mysql_mutex_lock(&rli->run_lock);
@@ -4731,6 +4809,7 @@ err_during_init:
/* Forget the relay log's format */
delete rli->relay_log.description_event_for_exec;
rli->relay_log.description_event_for_exec= 0;
+ rli->reset_inuse_relaylog();
/* Wake up master_pos_wait() */
mysql_mutex_unlock(&rli->data_lock);
DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions"));
@@ -4745,12 +4824,10 @@ err_during_init:
*/
thd->temporary_tables = 0; // remove tempation from destructor to close them
THD_CHECK_SENTRY(thd);
- serial_rgi->thd= rli->sql_driver_thd= 0;
+ rli->sql_driver_thd= 0;
mysql_mutex_lock(&LOCK_thread_count);
- THD_CHECK_SENTRY(thd);
thd->rgi_fake= thd->rgi_slave= NULL;
delete serial_rgi;
- delete thd;
mysql_mutex_unlock(&LOCK_thread_count);
/*
Note: the order of the broadcast and unlock calls below (first broadcast, then unlock)
@@ -4761,6 +4838,22 @@ err_during_init:
DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5););
mysql_mutex_unlock(&rli->run_lock); // tell the world we are done
+ /*
+ Deactivate the parallel replication thread pool, if there are now no more
+ SQL threads running. Do this here, when we have released all locks, but
+ while our THD (and current_thd) is still valid.
+ */
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (opt_slave_parallel_threads > 0 &&
+ !master_info_index->any_slave_sql_running())
+ rpl_parallel_inactivate_pool(&global_rpl_thread_pool);
+ mysql_mutex_unlock(&LOCK_active_mi);
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ THD_CHECK_SENTRY(thd);
+ delete thd;
+ mysql_mutex_unlock(&LOCK_thread_count);
+
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
#ifdef HAVE_OPENSSL
@@ -5648,6 +5741,18 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
}
break;
+#ifndef DBUG_OFF
+ case XID_EVENT:
+ DBUG_EXECUTE_IF("slave_discard_xid_for_gtid_0_x_1000",
+ {
+ /* Inject an event group that is missing its XID commit event. */
+ if (mi->last_queued_gtid.domain_id == 0 &&
+ mi->last_queued_gtid.seq_no == 1000)
+ goto skip_relay_logging;
+ });
+ /* Fall through to default case ... */
+#endif
+
default:
default_action:
if (mi->using_gtid != Master_info::USE_GTID_NO && mi->gtid_event_seen)
@@ -5969,7 +6074,23 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
}
#endif
- mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset_info->csname);
+ /*
+ If server's default charset is not supported (like utf16, utf32) as client
+ charset, then set client charset to 'latin1' (default client charset).
+ */
+ if (is_supported_parser_charset(default_charset_info))
+ mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset_info->csname);
+ else
+ {
+ sql_print_information("'%s' can not be used as client character set. "
+ "'%s' will be used as default client character set "
+ "while connecting to master.",
+ default_charset_info->csname,
+ default_client_charset_info->csname);
+ mysql_options(mysql, MYSQL_SET_CHARSET_NAME,
+ default_client_charset_info->csname);
+ }
+
/* This one is not strictly needed but we have it here for completeness */
mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir);
diff --git a/sql/sp.cc b/sql/sp.cc
index 35155cf031a..334b5e12ba3 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2002, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1476,6 +1477,9 @@ bool lock_db_routines(THD *thd, char *db)
{
char *sp_name= get_field(thd->mem_root,
table->field[MYSQL_PROC_FIELD_NAME]);
+ if (sp_name == NULL) // skip invalid sp names (hand-edited mysql.proc?)
+ continue;
+
longlong sp_type= table->field[MYSQL_PROC_MYSQL_TYPE]->val_int();
MDL_request *mdl_request= new (thd->mem_root) MDL_request;
mdl_request->init(sp_type == TYPE_ENUM_FUNCTION ?
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index d59e0aec541..e181e14611b 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1365,8 +1365,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
thd->user_var_events_alloc= thd->mem_root;
+ sql_digest_state *parent_digest= thd->m_digest;
+ thd->m_digest= NULL;
+
err_status= i->execute(thd, &ip);
+ thd->m_digest= parent_digest;
+
if (i->free_list)
cleanup_items(i->free_list);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 4b90209a472..9cc9efae6f8 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -863,32 +863,30 @@ static char *fix_plugin_ptr(char *name)
}
/**
- Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
+ Fix a LEX_STRING *plugin pointer to point to a hard-coded string,
+ if appropriate
Make sure that if ACL_USER's plugin is a built-in, then it points
to a hard coded string, not to an allocated copy. Run-time, for
authentication, we want to be able to detect built-ins by comparing
pointers, not strings.
- Additionally - update the salt if the plugin is built-in.
-
@retval 0 the pointers were fixed
@retval 1 this ACL_USER uses a not built-in plugin
*/
-static bool fix_user_plugin_ptr(ACL_USER *user)
+static bool fix_user_plugin_ptr(LEX_STRING *plugin_ptr)
{
- if (my_strcasecmp(system_charset_info, user->plugin.str,
+ DBUG_ASSERT(plugin_ptr);
+ if (my_strcasecmp(system_charset_info, plugin_ptr->str,
native_password_plugin_name.str) == 0)
- user->plugin= native_password_plugin_name;
+ *plugin_ptr= native_password_plugin_name;
else
- if (my_strcasecmp(system_charset_info, user->plugin.str,
+ if (my_strcasecmp(system_charset_info, plugin_ptr->str,
old_password_plugin_name.str) == 0)
- user->plugin= old_password_plugin_name;
+ *plugin_ptr= old_password_plugin_name;
else
return true;
- if (user->auth_string.length)
- set_user_salt(user, user->auth_string.str, user->auth_string.length);
return false;
}
@@ -967,6 +965,23 @@ my_bool acl_init(bool dont_read_acl_tables)
}
/**
+ Check if the password length provided matches supported native formats.
+*/
+static bool password_length_valid(int password_length)
+{
+ switch (password_length)
+ {
+ case 0: /* no password */
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH:
+ return TRUE;
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/**
Choose from either native or old password plugins when assigning a password
*/
@@ -1257,22 +1272,43 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
char *tmpstr= get_field(&acl_memroot, table->field[next_field++]);
if (tmpstr)
{
+ LEX_STRING auth;
+ auth.str = safe_str(get_field(&acl_memroot, table->field[next_field++]));
+ auth.length = strlen(auth.str);
user.plugin.str= tmpstr;
user.plugin.length= strlen(user.plugin.str);
- user.auth_string.str=
- safe_str(get_field(&acl_memroot, table->field[next_field++]));
- user.auth_string.length= strlen(user.auth_string.str);
-
- if (user.auth_string.length && password_len)
+ if (fix_user_plugin_ptr(&user.plugin)) // Non native plugin.
{
- sql_print_warning("'user' entry '%s@%s' has both a password "
- "and an authentication plugin specified. The "
- "password will be ignored.",
- safe_str(user.user.str),
- safe_str(user.host.hostname));
+ user.auth_string= auth;
+ if (password_len)
+ {
+ sql_print_warning("'user' entry '%s@%s' has both a password "
+ "and an authentication plugin specified. The "
+ "password will be ignored.",
+ safe_str(user.user.str),
+ safe_str(user.host.hostname));
+ }
+ }
+ else // Native plugin.
+ {
+ /*
+ Password field, if not empty, has precedence over
+ authentication_string field, only for native plugins.
+ See MDEV-6253 and MDEV-7985 for reasoning.
+ */
+ if (!password_len)
+ {
+ user.auth_string = auth;
+ if (!password_length_valid(auth.length))
+ {
+ sql_print_warning("Found invalid password for user: '%s@%s';"
+ " Ignoring user", safe_str(user.user.str),
+ safe_str(user.host.hostname));
+ continue;
+ }
+ set_user_salt(&user, auth.str, auth.length);
+ }
}
-
- fix_user_plugin_ptr(&user);
}
}
}
@@ -1971,8 +2007,13 @@ static void acl_update_user(const char *user, const char *host,
acl_user->auth_string.str= auth->str ?
strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
acl_user->auth_string.length= auth->length;
- if (fix_user_plugin_ptr(acl_user))
+ if (acl_user->plugin.str != native_password_plugin_name.str &&
+ acl_user->plugin.str != old_password_plugin_name.str)
acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
+ else
+ set_user_salt(acl_user, acl_user->auth_string.str,
+ acl_user->auth_string.length);
+
}
else
if (password[0])
@@ -2047,8 +2088,12 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.auth_string.str= auth->str ?
strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
acl_user.auth_string.length= auth->length;
- if (fix_user_plugin_ptr(&acl_user))
+ if (acl_user.plugin.str != native_password_plugin_name.str &&
+ acl_user.plugin.str != old_password_plugin_name.str)
acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
+ else
+ set_user_salt(&acl_user, acl_user.auth_string.str,
+ acl_user.auth_string.length);
}
else
{
@@ -3014,17 +3059,20 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
mysql_mutex_assert_owner(&acl_cache->lock);
+ size_t length_to_check = 0;
+ combo.password = combo.password.str ? combo.password : empty_lex_str;
if (combo.password.str && combo.password.str[0])
+ length_to_check = combo.password.length;
+ else if (!fix_user_plugin_ptr(&combo.plugin))
+ {
+ length_to_check = combo.auth.length;
+ }
+
+ if (!password_length_valid(length_to_check))
{
- if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
- combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
- {
my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
DBUG_RETURN(-1);
- }
}
- else
- combo.password= empty_lex_str;
/* if the user table is not up to date, we can't handle role updates */
if (table->s->fields <= ROLE_ASSIGN_COLUMN_IDX && handle_as_role)
@@ -8802,10 +8850,30 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
if (struct_no == ROLES_MAPPINGS_HASH)
{
const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
- if (user_from->is_role() ? strcmp(user_from->user.str, role) :
- (strcmp(user_from->user.str, user) ||
- my_strcasecmp(system_charset_info, user_from->host.str, host)))
- continue;
+ if (user_from->is_role())
+ {
+ /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
+ to check both the user field as well as the role field for a match.
+
+ It is possible to have a role granted to a role. If we are going
+ to modify the mapping entry, it needs to be done on either on the
+ "user" end (here represented by a role) or the "role" end. At least
+ one part must match.
+
+ If the "user" end has a not-empty host string, it can never match
+ as we are searching for a role here. A role always has an empty host
+ string.
+ */
+ if ((*host || strcmp(user_from->user.str, user)) &&
+ strcmp(user_from->user.str, role))
+ continue;
+ }
+ else
+ {
+ if (strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host))
+ continue;
+ }
}
else
{
@@ -9840,7 +9908,6 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
List<LEX_USER> user_list;
bool result;
ACL_USER *au;
- char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
Dummy_error_handler error_handler;
DBUG_ENTER("sp_grant_privileges");
@@ -9881,33 +9948,10 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
if(au)
{
- if (au->salt_len)
- {
- if (au->salt_len == SCRAMBLE_LENGTH)
- {
- make_password_from_salt(passwd_buff, au->salt);
- combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- }
- else if (au->salt_len == SCRAMBLE_LENGTH_323)
- {
- make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
- combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
- }
- else
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_PASSWD_LENGTH,
- ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH);
- return TRUE;
- }
- combo->password.str= passwd_buff;
- }
-
if (au->plugin.str != native_password_plugin_name.str &&
au->plugin.str != old_password_plugin_name.str)
- {
combo->plugin= au->plugin;
- combo->auth= au->auth_string;
- }
+ combo->auth= au->auth_string;
}
if (user_list.push_back(combo))
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 1d917de0c1a..a6b97ce31fa 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2010, 2014, Oracle and/or its affiliates.
- Copyright (c) 2012, 2014, Monty Program Ab.
+ Copyright (c) 2012, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -309,7 +309,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
HA_CHECK_OPT *),
int (handler::*operator_func)(THD *,
HA_CHECK_OPT *),
- int (view_operator_func)(THD *, TABLE_LIST*))
+ int (view_operator_func)(THD *, TABLE_LIST*,
+ HA_CHECK_OPT *))
{
TABLE_LIST *table;
SELECT_LEX *select= &thd->lex->select_lex;
@@ -320,6 +321,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
int result_code;
int compl_result_code;
bool need_repair_or_alter= 0;
+ wait_for_commit* suspended_wfc;
DBUG_ENTER("mysql_admin_table");
DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options));
@@ -337,6 +339,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
+ /*
+ This function calls trans_commit() during its operation, but that does not
+ imply that the operation is complete or binlogged. So we have to suspend
+ temporarily the wakeup_subsequent_commits() calls (if used).
+ */
+ suspended_wfc= thd->suspend_subsequent_commits();
+
mysql_ha_rm_tables(thd, tables);
/*
@@ -385,7 +394,18 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
lex->query_tables_own_last= 0;
if (view_operator_func == NULL)
+ {
table->required_type=FRMTYPE_TABLE;
+ DBUG_ASSERT(!lex->only_view);
+ }
+ else if (lex->only_view)
+ {
+ table->required_type= FRMTYPE_VIEW;
+ }
+ else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR)
+ {
+ table->required_type= FRMTYPE_TABLE;
+ }
if (lex->sql_command == SQLCOM_CHECK ||
lex->sql_command == SQLCOM_REPAIR ||
@@ -464,7 +484,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (!table->table->part_info)
{
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err2;
}
if (set_part_state(alter_info, table->table->part_info, PART_ADMIN))
{
@@ -513,9 +533,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
}
/*
- CHECK TABLE command is only command where VIEW allowed here and this
- command use only temporary teble method for VIEWs resolving => there
- can't be VIEW tree substitition of join view => if opening table
+ CHECK/REPAIR TABLE command is only command where VIEW allowed here and
+ this command use only temporary table method for VIEWs resolving =>
+ there can't be VIEW tree substitition of join view => if opening table
succeed then table->table will have real TABLE pointer as value (in
case of join view substitution table->table can be 0, but here it is
impossible)
@@ -528,7 +548,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
/* if it was a view will check md5 sum */
if (table->view &&
- view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
+ view_check(thd, table, check_opt) == HA_ADMIN_WRONG_CHECKSUM)
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
if (thd->get_stmt_da()->is_error() &&
@@ -543,7 +563,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (table->view)
{
DBUG_PRINT("admin", ("calling view_operator_func"));
- result_code= (*view_operator_func)(thd, table);
+ result_code= (*view_operator_func)(thd, table, check_opt);
goto send_result;
}
@@ -960,7 +980,16 @@ send_result_message:
size_t length;
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- if (table->table->file->ha_table_flags() & HA_CAN_REPAIR)
+#if MYSQL_VERSION_ID > 100104
+#error fix the error message to take TABLE or VIEW as an argument
+#else
+ if (table->view)
+ length= my_snprintf(buf, sizeof(buf),
+ "Upgrade required. Please do \"REPAIR VIEW %`s\" or dump/reload to fix it!",
+ table->table_name);
+ else
+#endif
+ if (table->table->file->ha_table_flags() & HA_CAN_REPAIR || table->view)
length= my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
table->table_name);
else
@@ -983,7 +1012,7 @@ send_result_message:
break;
}
}
- if (table->table)
+ if (table->table && !table->view)
{
if (table->table->s->tmp_table)
{
@@ -1045,6 +1074,8 @@ send_result_message:
}
my_eof(thd);
+ thd->resume_subsequent_commits(suspended_wfc);
+ DBUG_EXECUTE_IF("inject_analyze_table_sleep", my_sleep(500000););
DBUG_RETURN(FALSE);
err:
@@ -1058,6 +1089,8 @@ err:
}
close_thread_tables(thd); // Shouldn't be needed
thd->mdl_context.release_transactional_locks();
+err2:
+ thd->resume_subsequent_commits(suspended_wfc);
DBUG_RETURN(TRUE);
}
@@ -1177,7 +1210,7 @@ bool Sql_cmd_check_table::execute(THD *thd)
res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "check",
lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0,
- &handler::ha_check, &view_checksum);
+ &handler::ha_check, &view_check);
m_lex->select_lex.table_list.first= first_table;
m_lex->query_tables= first_table;
@@ -1234,7 +1267,7 @@ bool Sql_cmd_repair_table::execute(THD *thd)
TL_WRITE, 1,
MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM),
HA_OPEN_FOR_REPAIR, &prepare_for_repair,
- &handler::ha_repair, 0);
+ &handler::ha_repair, &view_repair);
/* ! we write after unlocking the table */
if (!res && !m_lex->no_write_to_binlog)
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 424f572deca..13b8625ebe6 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2010, 2013 Monty Program Ab
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -653,6 +653,7 @@ bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
static void mark_temp_tables_as_free_for_reuse(THD *thd)
{
+ rpl_group_info *rgi_slave;
DBUG_ENTER("mark_temp_tables_as_free_for_reuse");
if (thd->query_id == 0)
@@ -661,20 +662,25 @@ static void mark_temp_tables_as_free_for_reuse(THD *thd)
DBUG_VOID_RETURN;
}
- thd->lock_temporary_tables();
- for (TABLE *table= thd->temporary_tables ; table ; table= table->next)
- {
- if ((table->query_id == thd->query_id) && ! table->open_by_handler)
- mark_tmp_table_for_reuse(table);
- }
- thd->unlock_temporary_tables();
- if (thd->rgi_slave)
+ rgi_slave=thd->rgi_slave;
+ if ((!rgi_slave && thd->temporary_tables) ||
+ (rgi_slave && unlikely(rgi_slave->rli->save_temporary_tables)))
{
- /*
- Temporary tables are shared with other by sql execution threads.
- As a safety messure, clear the pointer to the common area.
- */
- thd->temporary_tables= 0;
+ thd->lock_temporary_tables();
+ for (TABLE *table= thd->temporary_tables ; table ; table= table->next)
+ {
+ if ((table->query_id == thd->query_id) && ! table->open_by_handler)
+ mark_tmp_table_for_reuse(table);
+ }
+ thd->unlock_temporary_tables();
+ if (rgi_slave)
+ {
+ /*
+ Temporary tables are shared with other by sql execution threads.
+ As a safety messure, clear the pointer to the common area.
+ */
+ thd->temporary_tables= 0;
+ }
}
DBUG_VOID_RETURN;
}
@@ -1434,6 +1440,9 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
bool check_alias)
{
TABLE_LIST *dup;
+
+ table= table->find_table_for_update();
+
if (table->table && table->table->file->ht->db_type == DB_TYPE_MRG_MYISAM)
{
TABLE_LIST *child;
@@ -1544,6 +1553,69 @@ TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl)
}
+static bool
+use_temporary_table(THD *thd, TABLE *table, TABLE **out_table)
+{
+ *out_table= table;
+ if (!table)
+ return false;
+ /*
+ Temporary tables are not safe for parallel replication. They were
+ designed to be visible to one thread only, so have no table locking.
+ Thus there is no protection against two conflicting transactions
+ committing in parallel and things like that.
+
+ So for now, anything that uses temporary tables will be serialised
+ with anything before it, when using parallel replication.
+
+ ToDo: We might be able to introduce a reference count or something
+ on temp tables, and have slave worker threads wait for it to reach
+ zero before being allowed to use the temp table. Might not be worth
+ it though, as statement-based replication using temporary tables is
+ in any case rather fragile.
+ */
+ if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
+ thd->wait_for_prior_commit())
+ return true;
+ /*
+ We need to set the THD as it may be different in case of
+ parallel replication
+ */
+ if (table->in_use != thd)
+ {
+ table->in_use= thd;
+#ifdef REMOVE_AFTER_MERGE_WITH_10
+ if (thd->rgi_slave)
+ {
+ /*
+ We may be stealing an opened temporary tables from one slave
+ thread to another, we need to let the performance schema know that,
+ for aggregates per thread to work properly.
+ */
+ table->file->unbind_psi();
+ table->file->rebind_psi();
+ }
+#endif
+ }
+ return false;
+}
+
+bool
+find_and_use_temporary_table(THD *thd, const char *db, const char *table_name,
+ TABLE **out_table)
+{
+ return use_temporary_table(thd, find_temporary_table(thd, db, table_name),
+ out_table);
+}
+
+
+bool
+find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl, TABLE **out_table)
+{
+ return use_temporary_table(thd, find_temporary_table(thd, tl), out_table);
+}
+
+
/**
Find a temporary table specified by a key in the THD::temporary_tables list.
@@ -1564,26 +1636,6 @@ TABLE *find_temporary_table(THD *thd,
if (table->s->table_cache_key.length == table_key_length &&
!memcmp(table->s->table_cache_key.str, table_key, table_key_length))
{
- /*
- We need to set the THD as it may be different in case of
- parallel replication
- */
- if (table->in_use != thd)
- {
- table->in_use= thd;
-#ifdef REMOVE_AFTER_MERGE_WITH_10
- if (thd->rgi_slave)
- {
- /*
- We may be stealing an opened temporary tables from one slave
- thread to another, we need to let the performance schema know that,
- for aggregates per thread to work properly.
- */
- table->file->unbind_psi();
- table->file->rebind_psi();
- }
-#endif
- }
result= table;
break;
}
@@ -3404,7 +3456,7 @@ request_backoff_action(enum_open_table_action action_arg,
if (action_arg != OT_REOPEN_TABLES && m_has_locks)
{
my_error(ER_LOCK_DEADLOCK, MYF(0));
- mark_transaction_to_rollback(m_thd, true);
+ m_thd->mark_transaction_to_rollback(true);
return TRUE;
}
/*
@@ -5583,6 +5635,14 @@ TABLE *open_table_uncached(THD *thd, handlerton *hton,
(uint) thd->variables.server_id,
(ulong) thd->variables.pseudo_thread_id));
+ if (add_to_temporary_tables_list)
+ {
+ /* Temporary tables are not safe for parallel replication. */
+ if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
+ thd->wait_for_prior_commit())
+ DBUG_RETURN(NULL);
+ }
+
/* Create the cache_key for temporary tables */
key_length= create_tmp_table_def_key(thd, cache_key, db, table_name);
@@ -5808,7 +5868,9 @@ bool open_temporary_table(THD *thd, TABLE_LIST *tl)
DBUG_RETURN(FALSE);
}
- if (!(table= find_temporary_table(thd, tl)))
+ if (find_and_use_temporary_table(thd, tl, &table))
+ DBUG_RETURN(TRUE);
+ if (!table)
{
if (tl->open_type == OT_TEMPORARY_ONLY &&
tl->open_strategy == TABLE_LIST::OPEN_NORMAL)
@@ -5819,6 +5881,25 @@ bool open_temporary_table(THD *thd, TABLE_LIST *tl)
DBUG_RETURN(FALSE);
}
+ /*
+ Temporary tables are not safe for parallel replication. They were
+ designed to be visible to one thread only, so have no table locking.
+ Thus there is no protection against two conflicting transactions
+ committing in parallel and things like that.
+
+ So for now, anything that uses temporary tables will be serialised
+ with anything before it, when using parallel replication.
+
+ ToDo: We might be able to introduce a reference count or something
+ on temp tables, and have slave worker threads wait for it to reach
+ zero before being allowed to use the temp table. Might not be worth
+ it though, as statement-based replication using temporary tables is
+ in any case rather fragile.
+ */
+ if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
+ thd->wait_for_prior_commit())
+ DBUG_RETURN(true);
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (tl->partition_names)
{
@@ -6904,7 +6985,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
Item_field for tables.
*/
Item_ident *item_ref= (Item_ident *) item;
- if (item_ref->name && item_ref->table_name &&
+ if (field_name && item_ref->name && item_ref->table_name &&
!my_strcasecmp(system_charset_info, item_ref->name, field_name) &&
!my_strcasecmp(table_alias_charset, item_ref->table_name,
table_name) &&
@@ -7934,9 +8015,10 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
if (select_lex->first_cond_optimization)
{
leaves.empty();
- if (!select_lex->is_prep_leaf_list_saved)
+ if (select_lex->prep_leaf_list_state != SELECT_LEX::SAVED)
{
make_leaves_list(leaves, tables, full_table_list, first_select_table);
+ select_lex->prep_leaf_list_state= SELECT_LEX::READY;
select_lex->leaf_tables_exec.empty();
}
else
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 8a0a1e42500..a6d90199860 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -148,7 +148,11 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
const char *db_name,
const char *table_name);
TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name);
+bool find_and_use_temporary_table(THD *thd, const char *db,
+ const char *table_name, TABLE **out_table);
TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
+bool find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl,
+ TABLE **out_table);
TABLE *find_temporary_table(THD *thd, const char *table_key,
uint table_key_length);
void close_thread_tables(THD *thd);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 9cdf5cec54d..cf0c4a1b84f 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, SkySQL Ab.
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -874,6 +874,7 @@ THD::THD()
stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE),
m_examined_row_count(0),
accessed_rows_and_keys(0),
+ m_digest(NULL),
m_statement_psi(NULL),
m_idle_psi(NULL),
thread_id(0),
@@ -881,12 +882,13 @@ THD::THD()
failed_com_change_user(0),
is_fatal_error(0),
transaction_rollback_request(0),
- is_fatal_sub_stmt_error(0),
+ is_fatal_sub_stmt_error(false),
rand_used(0),
time_zone_used(0),
in_lock_tables(0),
bootstrap(0),
derived_tables_processing(FALSE),
+ waiting_on_group_commit(FALSE), has_waiter(FALSE),
spcont(NULL),
m_parser_state(NULL),
#if defined(ENABLED_DEBUG_SYNC)
@@ -1043,6 +1045,13 @@ THD::THD()
substitute_null_with_insert_id = FALSE;
thr_lock_info_init(&lock_info); /* safety: will be reset after start */
+ m_token_array= NULL;
+ if (max_digest_length > 0)
+ {
+ m_token_array= (unsigned char*) my_malloc(max_digest_length,
+ MYF(MY_WME|MY_THREAD_SPECIFIC));
+ }
+
m_internal_handler= NULL;
m_binlog_invoker= INVOKER_NONE;
arena_for_cached_items= 0;
@@ -1519,6 +1528,7 @@ void THD::cleanup(void)
mysql_ha_cleanup(this);
locked_tables_list.unlock_locked_tables(this);
+ delete_dynamic(&user_var_events);
close_temporary_tables(this);
transaction.xid_state.xa_state= XA_NOTR;
@@ -1550,7 +1560,6 @@ void THD::cleanup(void)
debug_sync_end_thread(this);
#endif /* defined(ENABLED_DEBUG_SYNC) */
- delete_dynamic(&user_var_events);
my_hash_free(&user_vars);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
@@ -1624,6 +1633,7 @@ THD::~THD()
#endif
free_root(&main_mem_root, MYF(0));
+ my_free(m_token_array);
main_da.free_memory();
if (status_var.memory_used != 0)
{
@@ -4233,6 +4243,8 @@ thd_need_wait_for(const MYSQL_THD thd)
{
rpl_group_info *rgi;
+ if (mysql_bin_log.is_open() && opt_binlog_commit_wait_count > 0)
+ return true;
if (!thd)
return false;
rgi= thd->rgi_slave;
@@ -4267,13 +4279,14 @@ thd_need_wait_for(const MYSQL_THD thd)
not harmful, but could lead to unnecessary kill and retry, so best avoided).
*/
extern "C" void
-thd_report_wait_for(const MYSQL_THD thd, MYSQL_THD other_thd)
+thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd)
{
rpl_group_info *rgi;
rpl_group_info *other_rgi;
if (!thd || !other_thd)
return;
+ binlog_report_wait_for(thd, other_thd);
rgi= thd->rgi_slave;
other_rgi= other_thd->rgi_slave;
if (!rgi || !other_rgi)
@@ -4446,7 +4459,8 @@ extern "C" int thd_binlog_format(const MYSQL_THD thd)
extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all)
{
- mark_transaction_to_rollback(thd, all);
+ DBUG_ASSERT(thd);
+ thd->mark_transaction_to_rollback(all);
}
extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
@@ -4687,9 +4701,12 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
If we've left sub-statement mode, reset the fatal error flag.
Otherwise keep the current value, to propagate it up the sub-statement
stack.
+
+ NOTE: is_fatal_sub_stmt_error can be set only if we've been in the
+ sub-statement mode.
*/
if (!in_sub_stmt)
- is_fatal_sub_stmt_error= FALSE;
+ is_fatal_sub_stmt_error= false;
if ((variables.option_bits & OPTION_BIN_LOG) && is_update_query(lex->sql_command) &&
!is_current_stmt_binlog_format_row())
@@ -4931,17 +4948,18 @@ void THD::get_definer(LEX_USER *definer, bool role)
/**
Mark transaction to rollback and mark error as fatal to a sub-statement.
- @param thd Thread handle
@param all TRUE <=> rollback main transaction.
*/
-void mark_transaction_to_rollback(THD *thd, bool all)
+void THD::mark_transaction_to_rollback(bool all)
{
- if (thd)
- {
- thd->is_fatal_sub_stmt_error= TRUE;
- thd->transaction_rollback_request= all;
- }
+ /*
+ There is no point in setting is_fatal_sub_stmt_error unless
+ we are actually in_sub_stmt.
+ */
+ if (in_sub_stmt)
+ is_fatal_sub_stmt_error= true;
+ transaction_rollback_request= all;
}
/***************************************************************************
Handling of XA id cacheing
@@ -6605,6 +6623,7 @@ wait_for_commit::wakeup_subsequent_commits2(int wakeup_error)
a mutex), so no extra explicit barrier is needed here.
*/
wakeup_subsequent_commits_running= false;
+ DBUG_EXECUTE_IF("inject_wakeup_subsequent_commits_sleep", my_sleep(21000););
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 5d64837a2c1..a8d8444571e 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1,5 +1,6 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013, Monty Program Ab.
+/*
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,6 +38,8 @@
#include "violite.h" /* vio_is_connected */
#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA,
THR_LOCK_INFO */
+#include "sql_digest_stream.h" // sql_digest_state
+
#include <mysql/psi/mysql_stage.h>
#include <mysql/psi/mysql_statement.h>
#include <mysql/psi/mysql_idle.h>
@@ -772,9 +775,6 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
STATUS_VAR *dec_var);
-void mark_transaction_to_rollback(THD *thd, bool all);
-
-
/**
Get collation by name, send error to client on failure.
@param name Collation name
@@ -802,7 +802,6 @@ mysqld_collation_get_by_name(const char *name,
return cs;
}
-
#ifdef MYSQL_SERVER
void free_tmp_table(THD *thd, TABLE *entry);
@@ -2508,6 +2507,13 @@ public:
PROFILING profiling;
#endif
+ /** Current statement digest. */
+ sql_digest_state *m_digest;
+ /** Current statement digest token array. */
+ unsigned char *m_token_array;
+ /** Top level statement digest. */
+ sql_digest_state m_digest_state;
+
/** Current statement instrumentation. */
PSI_statement_locker *m_statement_psi;
#ifdef HAVE_PSI_STATEMENT_INTERFACE
@@ -2675,6 +2681,18 @@ public:
*/
bool is_slave_error;
/*
+ True when a transaction is queued up for binlog group commit.
+ Used so that if another transaction needs to wait for a row lock held by
+ this transaction, it can signal to trigger the group commit immediately,
+ skipping the normal --binlog-commit-wait-count wait.
+ */
+ bool waiting_on_group_commit;
+ /*
+ Set true when another transaction goes to wait on a row lock held by this
+ transaction. Used together with waiting_on_group_commit.
+ */
+ bool has_waiter;
+ /*
In case of a slave, set to the error code the master got when executing
the query. 0 if no error on the master.
*/
@@ -3101,6 +3119,8 @@ public:
if (get_stmt_da()->is_error())
get_stmt_da()->reset_diagnostics_area();
is_slave_error= 0;
+ if (killed == KILL_BAD_DATA)
+ killed= NOT_KILLED; // KILL_BAD_DATA can be reset w/o a mutex
DBUG_VOID_RETURN;
}
#ifndef EMBEDDED_LIBRARY
@@ -3682,7 +3702,17 @@ public:
if (wait_for_commit_ptr)
wait_for_commit_ptr->wakeup_subsequent_commits(wakeup_error);
}
+ wait_for_commit *suspend_subsequent_commits() {
+ wait_for_commit *suspended= wait_for_commit_ptr;
+ wait_for_commit_ptr= NULL;
+ return suspended;
+ }
+ void resume_subsequent_commits(wait_for_commit *suspended) {
+ DBUG_ASSERT(!wait_for_commit_ptr);
+ wait_for_commit_ptr= suspended;
+ }
+ void mark_transaction_to_rollback(bool all);
private:
/** The current internal error handler for this thread, or NULL. */
@@ -4864,8 +4894,6 @@ public:
*/
#define CF_SKIP_QUESTIONS (1U << 1)
-void mark_transaction_to_rollback(THD *thd, bool all);
-
/* Inline functions */
inline bool add_item_to_list(THD *thd, Item *item)
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 807b028a4b1..0202b4493ac 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1122,7 +1122,7 @@ bool setup_connection_thread_globals(THD *thd)
bool login_connection(THD *thd)
{
NET *net= &thd->net;
- int error;
+ int error= 0;
DBUG_ENTER("login_connection");
DBUG_PRINT("info", ("login_connection called by thread %lu",
thd->thread_id));
@@ -1141,7 +1141,8 @@ bool login_connection(THD *thd)
my_sleep(1000); /* must wait after eof() */
#endif
statistic_increment(aborted_connects,&LOCK_status);
- DBUG_RETURN(1);
+ error=1;
+ goto exit;
}
/* Connect completed, set read/write timeouts back to default */
my_net_set_read_timeout(net, thd->variables.net_read_timeout);
@@ -1151,10 +1152,13 @@ bool login_connection(THD *thd)
if (increment_connection_count(thd, TRUE))
{
my_error(ER_OUTOFMEMORY, MYF(0), 2*sizeof(USER_STATS));
- DBUG_RETURN(1);
+ error= 1;
+ goto exit;
}
- DBUG_RETURN(0);
+exit:
+ mysql_audit_notify_connection_connect(thd);
+ DBUG_RETURN(error);
}
@@ -1295,7 +1299,6 @@ bool thd_prepare_connection(THD *thd)
bool rc;
lex_start(thd);
rc= login_connection(thd);
- mysql_audit_notify_connection_connect(thd);
if (rc)
return rc;
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 99b7b1e58d0..c09f3269d7a 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -98,6 +98,7 @@ public:
int mysql_open_cursor(THD *thd, select_result *result,
Server_side_cursor **pcursor)
{
+ sql_digest_state *parent_digest;
PSI_statement_locker *parent_locker;
select_result *save_result;
Select_materialize *result_materialize;
@@ -117,11 +118,14 @@ int mysql_open_cursor(THD *thd, select_result *result,
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip,
2);
+ parent_digest= thd->m_digest;
parent_locker= thd->m_statement_psi;
+ thd->m_digest= NULL;
thd->m_statement_psi= NULL;
/* Mark that we can't use query cache with cursors */
thd->query_cache_is_applicable= 0;
rc= mysql_execute_command(thd);
+ thd->m_digest= parent_digest;
thd->m_statement_psi= parent_locker;
MYSQL_QUERY_EXEC_DONE(rc);
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 06e6d738db1..e89c3d9e745 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013, Monty Program Ab.
+ Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -952,7 +952,7 @@ update_binlog:
if (!(query= (char*) thd->alloc(MAX_DROP_TABLE_Q_LEN)))
goto exit; /* not much else we can do */
- query_pos= query_data_start= strmov(query,"DROP TABLE ");
+ query_pos= query_data_start= strmov(query,"DROP TABLE IF EXISTS ");
query_end= query + MAX_DROP_TABLE_Q_LEN;
db_len= strlen(db);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index d776077c4c3..5292b964576 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -850,7 +851,7 @@ int mysql_multi_delete_prepare(THD *thd)
*/
lex->select_lex.exclude_from_table_unique_test= FALSE;
- if (lex->select_lex.save_prep_leaf_tables(thd))
+ if (lex->save_prep_leaf_tables())
DBUG_RETURN(TRUE);
DBUG_RETURN(FALSE);
@@ -897,9 +898,10 @@ multi_delete::initialize_tables(JOIN *join)
delete_while_scanning= 1;
for (walk= delete_tables; walk; walk= walk->next_local)
{
- tables_to_delete_from|= walk->table->map;
+ TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update();
+ tables_to_delete_from|= tbl->table->map;
if (delete_while_scanning &&
- unique_table(thd, walk, join->tables_list, false))
+ unique_table(thd, tbl, join->tables_list, false))
{
/*
If the table we are going to delete from appears
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 14e26fbefa6..fdc615d0fae 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -169,7 +170,9 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
uint8 allowed_phases= (derived->is_merged_derived() ? DT_PHASES_MERGE :
DT_PHASES_MATERIALIZE);
DBUG_ENTER("mysql_handle_single_derived");
- DBUG_PRINT("enter", ("phases: 0x%x allowed: 0x%x", phases, allowed_phases));
+ DBUG_PRINT("enter", ("phases: 0x%x allowed: 0x%x alias: '%s'",
+ phases, allowed_phases,
+ (derived->alias ? derived->alias : "<NULL>")));
if (!lex->derived_tables)
DBUG_RETURN(FALSE);
@@ -503,10 +506,22 @@ unconditional_materialization:
bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_merge_for_insert");
+ DBUG_PRINT("enter", ("derived: %p", derived));
+ DBUG_PRINT("info", ("merged_for_insert: %d is_materialized_derived: %d "
+ "is_multitable: %d single_table_updatable: %d "
+ "merge_underlying_list: %d",
+ derived->merged_for_insert,
+ derived->is_materialized_derived(),
+ derived->is_multitable(),
+ derived->single_table_updatable(),
+ derived->merge_underlying_list != 0));
if (derived->merged_for_insert)
DBUG_RETURN(FALSE);
if (derived->is_materialized_derived())
DBUG_RETURN(mysql_derived_prepare(thd, lex, derived));
+ if ((thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI))
+ DBUG_RETURN(FALSE);
if (!derived->is_multitable())
{
if (!derived->single_table_updatable())
@@ -516,8 +531,9 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
derived->table= derived->merge_underlying_list->table;
derived->schema_table= derived->merge_underlying_list->schema_table;
derived->merged_for_insert= TRUE;
+ DBUG_ASSERT(derived->table);
}
- }
+ }
DBUG_RETURN(FALSE);
}
@@ -544,6 +560,7 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX_UNIT *unit= derived->get_unit();
DBUG_ENTER("mysql_derived_init");
+ DBUG_PRINT("enter", ("derived: %p", derived));
// Skip already prepared views/DT
if (!unit || unit->prepared)
@@ -689,6 +706,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
thd->create_tmp_table_for_derived= FALSE;
derived->table= derived->derived_result->table;
+ DBUG_ASSERT(derived->table);
if (derived->is_derived() && derived->is_merged_derived())
first_select->mark_as_belong_to_derived(derived);
@@ -956,8 +974,7 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
DBUG_ENTER("mysql_derived_reinit");
st_select_lex_unit *unit= derived->get_unit();
- if (derived->table)
- derived->merged_for_insert= FALSE;
+ derived->merged_for_insert= FALSE;
unit->unclean();
unit->types.empty();
/* for derived tables & PS (which can't be reset by Item_subquery) */
@@ -965,4 +982,3 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
unit->set_thd(thd);
DBUG_RETURN(FALSE);
}
-
diff --git a/sql/sql_derived.h b/sql/sql_derived.h
index f232445879e..1dffef7235b 100644
--- a/sql/sql_derived.h
+++ b/sql/sql_derived.h
@@ -23,6 +23,7 @@ struct LEX;
bool mysql_handle_derived(LEX *lex, uint phases);
bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases);
bool mysql_handle_list_of_derived(LEX *lex, TABLE_LIST *dt_list, uint phases);
+bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
/**
Cleans up the SELECT_LEX_UNIT for the derived table (if any).
diff --git a/sql/sql_digest.cc b/sql/sql_digest.cc
new file mode 100644
index 00000000000..324f2fbd428
--- /dev/null
+++ b/sql/sql_digest.cc
@@ -0,0 +1,683 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+/*
+ This code needs extra visibility in the lexer structures
+*/
+
+#include "my_global.h"
+#include "my_md5.h"
+#include "mysqld_error.h"
+
+#include "sql_string.h"
+#include "sql_class.h"
+#include "sql_lex.h"
+#include "sql_digest.h"
+#include "sql_digest_stream.h"
+
+#include "sql_get_diagnostics.h"
+
+#ifdef NEVER
+#include "my_sys.h"
+#include "sql_signal.h"
+#endif
+
+/* Generated code */
+#include "sql_yacc.h"
+#define LEX_TOKEN_WITH_DEFINITION
+#include "lex_token.h"
+
+/* Name pollution from sql/sql_lex.h */
+#ifdef LEX_YYSTYPE
+#undef LEX_YYSTYPE
+#endif
+
+#define LEX_YYSTYPE YYSTYPE*
+
+#define SIZE_OF_A_TOKEN 2
+
+/**
+ Read a single token from token array.
+*/
+inline uint read_token(const sql_digest_storage *digest_storage,
+ uint index, uint *tok)
+{
+ uint safe_byte_count= digest_storage->m_byte_count;
+
+ if (index + SIZE_OF_A_TOKEN <= safe_byte_count &&
+ safe_byte_count <= digest_storage->m_token_array_length)
+ {
+ const unsigned char *src= & digest_storage->m_token_array[index];
+ *tok= src[0] | (src[1] << 8);
+ return index + SIZE_OF_A_TOKEN;
+ }
+
+ /* The input byte stream is exhausted. */
+ *tok= 0;
+ return MAX_DIGEST_STORAGE_SIZE + 1;
+}
+
+/**
+ Store a single token in token array.
+*/
+inline void store_token(sql_digest_storage* digest_storage, uint token)
+{
+ DBUG_ASSERT(digest_storage->m_byte_count <= digest_storage->m_token_array_length);
+
+ if (digest_storage->m_byte_count + SIZE_OF_A_TOKEN <= digest_storage->m_token_array_length)
+ {
+ unsigned char* dest= & digest_storage->m_token_array[digest_storage->m_byte_count];
+ dest[0]= token & 0xff;
+ dest[1]= (token >> 8) & 0xff;
+ digest_storage->m_byte_count+= SIZE_OF_A_TOKEN;
+ }
+ else
+ {
+ digest_storage->m_full= true;
+ }
+}
+
+/**
+ Read an identifier from token array.
+*/
+inline uint read_identifier(const sql_digest_storage* digest_storage,
+ uint index, char ** id_string, int *id_length)
+{
+ uint new_index;
+ uint safe_byte_count= digest_storage->m_byte_count;
+
+ DBUG_ASSERT(index <= safe_byte_count);
+ DBUG_ASSERT(safe_byte_count <= digest_storage->m_token_array_length);
+
+ /*
+ token + length + string are written in an atomic way,
+ so we do always expect a length + string here
+ */
+
+ uint bytes_needed= SIZE_OF_A_TOKEN;
+ /* If we can read token and identifier length */
+ if ((index + bytes_needed) <= safe_byte_count)
+ {
+ const unsigned char *src= & digest_storage->m_token_array[index];
+ /* Read the length of identifier */
+ uint length= src[0] | (src[1] << 8);
+ bytes_needed+= length;
+ /* If we can read entire identifier from token array */
+ if ((index + bytes_needed) <= safe_byte_count)
+ {
+ *id_string= (char *) (src + 2);
+ *id_length= length;
+
+ new_index= index + bytes_needed;
+ DBUG_ASSERT(new_index <= safe_byte_count);
+ return new_index;
+ }
+ }
+
+ /* The input byte stream is exhausted. */
+ return MAX_DIGEST_STORAGE_SIZE + 1;
+}
+
+/**
+ Store an identifier in token array.
+*/
+inline void store_token_identifier(sql_digest_storage* digest_storage,
+ uint token,
+ size_t id_length, const char *id_name)
+{
+ DBUG_ASSERT(digest_storage->m_byte_count <= digest_storage->m_token_array_length);
+
+ size_t bytes_needed= 2 * SIZE_OF_A_TOKEN + id_length;
+ if (digest_storage->m_byte_count + bytes_needed <= (unsigned int)digest_storage->m_token_array_length)
+ {
+ unsigned char* dest= & digest_storage->m_token_array[digest_storage->m_byte_count];
+ /* Write the token */
+ dest[0]= token & 0xff;
+ dest[1]= (token >> 8) & 0xff;
+ /* Write the string length */
+ dest[2]= id_length & 0xff;
+ dest[3]= (id_length >> 8) & 0xff;
+ /* Write the string data */
+ if (id_length > 0)
+ memcpy((char *)(dest + 4), id_name, id_length);
+ digest_storage->m_byte_count+= bytes_needed;
+ }
+ else
+ {
+ digest_storage->m_full= true;
+ }
+}
+
+void compute_digest_md5(const sql_digest_storage *digest_storage, unsigned char *md5)
+{
+ compute_md5_hash((char *) md5,
+ (const char *) digest_storage->m_token_array,
+ digest_storage->m_byte_count);
+}
+
+/*
+ Iterate token array and updates digest_text.
+*/
+void compute_digest_text(const sql_digest_storage* digest_storage,
+ String *digest_text)
+{
+ DBUG_ASSERT(digest_storage != NULL);
+ uint byte_count= digest_storage->m_byte_count;
+ String *digest_output= digest_text;
+ uint tok= 0;
+ uint current_byte= 0;
+ lex_token_string *tok_data;
+
+ /* Reset existing data */
+ digest_output->length(0);
+
+ if (byte_count > digest_storage->m_token_array_length)
+ {
+ digest_output->append("\0", 1);
+ return;
+ }
+
+ /* Convert text to utf8 */
+ const CHARSET_INFO *from_cs= get_charset(digest_storage->m_charset_number, MYF(0));
+ const CHARSET_INFO *to_cs= &my_charset_utf8_bin;
+
+ if (from_cs == NULL)
+ {
+ /*
+ Can happen, as we do dirty reads on digest_storage,
+ which can be written to in another thread.
+ */
+ digest_output->append("\0", 1);
+ return;
+ }
+
+ char id_buffer[NAME_LEN + 1]= {'\0'};
+ char *id_string;
+ size_t id_length;
+ bool convert_text= !my_charset_same(from_cs, to_cs);
+
+ while (current_byte < byte_count)
+ {
+ current_byte= read_token(digest_storage, current_byte, &tok);
+
+ if (tok <= 0 || tok >= array_elements(lex_token_array)
+ || current_byte > max_digest_length)
+ return;
+
+ tok_data= &lex_token_array[tok];
+
+ switch (tok)
+ {
+ /* All identifiers are printed with their name. */
+ case IDENT:
+ case IDENT_QUOTED:
+ {
+ char *id_ptr= NULL;
+ int id_len= 0;
+ uint err_cs= 0;
+
+ /* Get the next identifier from the storage buffer. */
+ current_byte= read_identifier(digest_storage, current_byte,
+ &id_ptr, &id_len);
+ if (current_byte > max_digest_length)
+ return;
+
+ if (convert_text)
+ {
+ /* Verify that the converted text will fit. */
+ if (to_cs->mbmaxlen*id_len > NAME_LEN)
+ {
+ digest_output->append("...", 3);
+ break;
+ }
+ /* Convert identifier string into the storage character set. */
+ id_length= my_convert(id_buffer, NAME_LEN, to_cs,
+ id_ptr, id_len, from_cs, &err_cs);
+ id_string= id_buffer;
+ }
+ else
+ {
+ id_string= id_ptr;
+ id_length= id_len;
+ }
+
+ if (id_length == 0 || err_cs != 0)
+ {
+ break;
+ }
+ /* Copy the converted identifier into the digest string. */
+ if (tok == IDENT_QUOTED)
+ digest_output->append("`", 1);
+ if (id_length > 0)
+ digest_output->append(id_string, id_length);
+ if (tok == IDENT_QUOTED)
+ digest_output->append("`", 1);
+ digest_output->append(" ", 1);
+ }
+ break;
+
+ /* Everything else is printed as is. */
+ default:
+ /*
+ Make sure not to overflow digest_text buffer.
+ +1 is to make sure extra space for ' '.
+ */
+ int tok_length= tok_data->m_token_length;
+
+ digest_output->append(tok_data->m_token_string, tok_length);
+ if (tok_data->m_append_space)
+ digest_output->append(" ", 1);
+ break;
+ }
+ }
+}
+
+static inline uint peek_token(const sql_digest_storage *digest, uint index)
+{
+ uint token;
+ DBUG_ASSERT(index + SIZE_OF_A_TOKEN <= digest->m_byte_count);
+ DBUG_ASSERT(digest->m_byte_count <= digest->m_token_array_length);
+
+ token= ((digest->m_token_array[index + 1])<<8) | digest->m_token_array[index];
+ return token;
+}
+
+/**
+ Function to read last two tokens from token array. If an identifier
+ is found, do not look for token before that.
+*/
+static inline void peek_last_two_tokens(const sql_digest_storage* digest_storage,
+ uint last_id_index, uint *t1, uint *t2)
+{
+ uint byte_count= digest_storage->m_byte_count;
+ uint peek_index= byte_count;
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take last token. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t1= peek_token(digest_storage, peek_index);
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take 2nd token from last. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t2= peek_token(digest_storage, peek_index);
+ }
+ else
+ {
+ *t2= TOK_UNUSED;
+ }
+ }
+ else
+ {
+ *t1= TOK_UNUSED;
+ *t2= TOK_UNUSED;
+ }
+}
+
+/**
+ Function to read last three tokens from token array. If an identifier
+ is found, do not look for token before that.
+*/
+static inline void peek_last_three_tokens(const sql_digest_storage* digest_storage,
+ uint last_id_index, uint *t1, uint *t2, uint *t3)
+{
+ uint byte_count= digest_storage->m_byte_count;
+ uint peek_index= byte_count;
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take last token. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t1= peek_token(digest_storage, peek_index);
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take 2nd token from last. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t2= peek_token(digest_storage, peek_index);
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take 3rd token from last. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t3= peek_token(digest_storage, peek_index);
+ }
+ else
+ {
+ *t3= TOK_UNUSED;
+ }
+ }
+ else
+ {
+ *t2= TOK_UNUSED;
+ *t3= TOK_UNUSED;
+ }
+ }
+ else
+ {
+ *t1= TOK_UNUSED;
+ *t2= TOK_UNUSED;
+ *t3= TOK_UNUSED;
+ }
+}
+
+sql_digest_state* digest_add_token(sql_digest_state *state,
+ uint token,
+ LEX_YYSTYPE yylval)
+{
+ sql_digest_storage *digest_storage= NULL;
+
+ digest_storage= &state->m_digest_storage;
+
+ /*
+ Stop collecting further tokens if digest storage is full or
+ if END token is received.
+ */
+ if (digest_storage->m_full || token == END_OF_INPUT)
+ return NULL;
+
+ /*
+ Take last_token 2 tokens collected till now. These tokens will be used
+ in reduce for normalisation. Make sure not to consider ID tokens in reduce.
+ */
+ uint last_token;
+ uint last_token2;
+
+ switch (token)
+ {
+ case NUM:
+ case LONG_NUM:
+ case ULONGLONG_NUM:
+ case DECIMAL_NUM:
+ case FLOAT_NUM:
+ case BIN_NUM:
+ case HEX_NUM:
+ {
+ bool found_unary;
+ do
+ {
+ found_unary= false;
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if ((last_token == '-') || (last_token == '+'))
+ {
+ /*
+ We need to differentiate:
+ - a <unary minus> operator
+ - a <unary plus> operator
+ from
+ - a <binary minus> operator
+ - a <binary plus> operator
+ to only reduce "a = -1" to "a = ?", and not change "b - 1" to "b ?"
+
+ Binary operators are found inside an expression,
+ while unary operators are found at the beginning of an expression, or after operators.
+
+ To achieve this, every token that is followed by an <expr> expression
+ in the SQL grammar is flagged.
+ See sql/sql_yacc.yy
+ See sql/gen_lex_token.cc
+
+ For example,
+ "(-1)" is parsed as "(", "-", NUM, ")", and lex_token_array["("].m_start_expr is true,
+ so reduction of the "-" NUM is done, the result is "(?)".
+ "(a-1)" is parsed as "(", ID, "-", NUM, ")", and lex_token_array[ID].m_start_expr is false,
+ so the operator is binary, no reduction is done, and the result is "(a-?)".
+ */
+ if (lex_token_array[last_token2].m_start_expr)
+ {
+ /*
+ REDUCE:
+ TOK_GENERIC_VALUE := (UNARY_PLUS | UNARY_MINUS) (NUM | LOG_NUM | ... | FLOAT_NUM)
+
+ REDUCE:
+ TOK_GENERIC_VALUE := (UNARY_PLUS | UNARY_MINUS) TOK_GENERIC_VALUE
+ */
+ token= TOK_GENERIC_VALUE;
+ digest_storage->m_byte_count-= SIZE_OF_A_TOKEN;
+ found_unary= true;
+ }
+ }
+ } while (found_unary);
+ }
+ /* fall through, for case NULL_SYM below */
+ case LEX_HOSTNAME:
+ case TEXT_STRING:
+ case NCHAR_STRING:
+ case PARAM_MARKER:
+ {
+ /*
+ REDUCE:
+ TOK_GENERIC_VALUE := BIN_NUM | DECIMAL_NUM | ... | ULONGLONG_NUM
+ */
+ token= TOK_GENERIC_VALUE;
+
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if ((last_token2 == TOK_GENERIC_VALUE ||
+ last_token2 == TOK_GENERIC_VALUE_LIST) &&
+ (last_token == ','))
+ {
+ /*
+ REDUCE:
+ TOK_GENERIC_VALUE_LIST :=
+ TOK_GENERIC_VALUE ',' TOK_GENERIC_VALUE
+
+ REDUCE:
+ TOK_GENERIC_VALUE_LIST :=
+ TOK_GENERIC_VALUE_LIST ',' TOK_GENERIC_VALUE
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_GENERIC_VALUE_LIST;
+ }
+ /*
+ Add this token or the resulting reduce to digest storage.
+ */
+ store_token(digest_storage, token);
+ break;
+ }
+ case ')':
+ {
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if (last_token == TOK_GENERIC_VALUE &&
+ last_token2 == '(')
+ {
+ /*
+ REDUCE:
+ TOK_ROW_SINGLE_VALUE :=
+ '(' TOK_GENERIC_VALUE ')'
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_ROW_SINGLE_VALUE;
+
+ /* Read last two tokens again */
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if ((last_token2 == TOK_ROW_SINGLE_VALUE ||
+ last_token2 == TOK_ROW_SINGLE_VALUE_LIST) &&
+ (last_token == ','))
+ {
+ /*
+ REDUCE:
+ TOK_ROW_SINGLE_VALUE_LIST :=
+ TOK_ROW_SINGLE_VALUE ',' TOK_ROW_SINGLE_VALUE
+
+ REDUCE:
+ TOK_ROW_SINGLE_VALUE_LIST :=
+ TOK_ROW_SINGLE_VALUE_LIST ',' TOK_ROW_SINGLE_VALUE
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_ROW_SINGLE_VALUE_LIST;
+ }
+ }
+ else if (last_token == TOK_GENERIC_VALUE_LIST &&
+ last_token2 == '(')
+ {
+ /*
+ REDUCE:
+ TOK_ROW_MULTIPLE_VALUE :=
+ '(' TOK_GENERIC_VALUE_LIST ')'
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_ROW_MULTIPLE_VALUE;
+
+ /* Read last two tokens again */
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if ((last_token2 == TOK_ROW_MULTIPLE_VALUE ||
+ last_token2 == TOK_ROW_MULTIPLE_VALUE_LIST) &&
+ (last_token == ','))
+ {
+ /*
+ REDUCE:
+ TOK_ROW_MULTIPLE_VALUE_LIST :=
+ TOK_ROW_MULTIPLE_VALUE ',' TOK_ROW_MULTIPLE_VALUE
+
+ REDUCE:
+ TOK_ROW_MULTIPLE_VALUE_LIST :=
+ TOK_ROW_MULTIPLE_VALUE_LIST ',' TOK_ROW_MULTIPLE_VALUE
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_ROW_MULTIPLE_VALUE_LIST;
+ }
+ }
+ /*
+ Add this token or the resulting reduce to digest storage.
+ */
+ store_token(digest_storage, token);
+ break;
+ }
+ case IDENT:
+ case IDENT_QUOTED:
+ {
+ YYSTYPE *lex_token= yylval;
+ char *yytext= lex_token->lex_str.str;
+ size_t yylen= lex_token->lex_str.length;
+
+ /* Add this token and identifier string to digest storage. */
+ store_token_identifier(digest_storage, token, yylen, yytext);
+
+ /* Update the index of last identifier found. */
+ state->m_last_id_index= digest_storage->m_byte_count;
+ break;
+ }
+ default:
+ {
+ /* Add this token to digest storage. */
+ store_token(digest_storage, token);
+ break;
+ }
+ }
+
+ return state;
+}
+
+sql_digest_state* digest_reduce_token(sql_digest_state *state,
+ uint token_left, uint token_right)
+{
+ sql_digest_storage *digest_storage= NULL;
+
+ digest_storage= &state->m_digest_storage;
+
+ /*
+ Stop collecting further tokens if digest storage is full.
+ */
+ if (digest_storage->m_full)
+ return NULL;
+
+ uint last_token;
+ uint last_token2;
+ uint last_token3;
+ uint token_to_push= TOK_UNUSED;
+
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ /*
+ There is only one caller of digest_reduce_token(),
+ see sql/sql_yacc.yy, rule literal := NULL_SYM.
+ REDUCE:
+ token_left := token_right
+ Used for:
+ TOK_GENERIC_VALUE := NULL_SYM
+ */
+
+ if (last_token == token_right)
+ {
+ /*
+ Current stream is like:
+ TOKEN_X TOKEN_RIGHT .
+ REDUCE to
+ TOKEN_X TOKEN_LEFT .
+ */
+ digest_storage->m_byte_count-= SIZE_OF_A_TOKEN;
+ store_token(digest_storage, token_left);
+ }
+ else
+ {
+ /*
+ Current stream is like:
+ TOKEN_X TOKEN_RIGHT TOKEN_Y .
+ Pop TOKEN_Y
+ TOKEN_X TOKEN_RIGHT . TOKEN_Y
+ REDUCE to
+ TOKEN_X TOKEN_LEFT . TOKEN_Y
+ */
+ DBUG_ASSERT(last_token2 == token_right);
+ digest_storage->m_byte_count-= 2 * SIZE_OF_A_TOKEN;
+ store_token(digest_storage, token_left);
+ token_to_push= last_token;
+ }
+
+ peek_last_three_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2, &last_token3);
+
+ if ((last_token3 == TOK_GENERIC_VALUE ||
+ last_token3 == TOK_GENERIC_VALUE_LIST) &&
+ (last_token2 == ',') &&
+ (last_token == TOK_GENERIC_VALUE))
+ {
+ /*
+ REDUCE:
+ TOK_GENERIC_VALUE_LIST :=
+ TOK_GENERIC_VALUE ',' TOK_GENERIC_VALUE
+
+ REDUCE:
+ TOK_GENERIC_VALUE_LIST :=
+ TOK_GENERIC_VALUE_LIST ',' TOK_GENERIC_VALUE
+ */
+ digest_storage->m_byte_count-= 3*SIZE_OF_A_TOKEN;
+ store_token(digest_storage, TOK_GENERIC_VALUE_LIST);
+ }
+
+ if (token_to_push != TOK_UNUSED)
+ {
+ /*
+ Push TOKEN_Y
+ */
+ store_token(digest_storage, token_to_push);
+ }
+
+ return state;
+}
+
diff --git a/sql/sql_digest.h b/sql/sql_digest.h
new file mode 100644
index 00000000000..ce159283d4d
--- /dev/null
+++ b/sql/sql_digest.h
@@ -0,0 +1,130 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef SQL_DIGEST_H
+#define SQL_DIGEST_H
+
+#include <string.h>
+class String;
+#include "my_md5.h"
+
+#define MAX_DIGEST_STORAGE_SIZE (1024*1024)
+
+/**
+ Structure to store token count/array for a statement
+ on which digest is to be calculated.
+*/
+struct sql_digest_storage
+{
+ bool m_full;
+ uint m_byte_count;
+ unsigned char m_md5[MD5_HASH_SIZE];
+ /** Character set number. */
+ uint m_charset_number;
+ /**
+ Token array.
+ Token array is an array of bytes to store tokens received during parsing.
+ Following is the way token array is formed.
+ ... &lt;non-id-token&gt; &lt;non-id-token&gt; &lt;id-token&gt; &lt;id_len&gt; &lt;id_text&gt; ...
+ For Example:
+ SELECT * FROM T1;
+ &lt;SELECT_TOKEN&gt; &lt;*&gt; &lt;FROM_TOKEN&gt; &lt;ID_TOKEN&gt; &lt;2&gt; &lt;T1&gt;
+ */
+ unsigned char *m_token_array;
+ /* Length of the token array to be considered for DIGEST_TEXT calculation. */
+ uint m_token_array_length;
+
+ sql_digest_storage()
+ {
+ reset(NULL, 0);
+ }
+
+ inline void reset(unsigned char *token_array, uint length)
+ {
+ m_token_array= token_array;
+ m_token_array_length= length;
+ reset();
+ }
+
+ inline void reset()
+ {
+ m_full= false;
+ m_byte_count= 0;
+ m_charset_number= 0;
+ if (m_token_array_length > 0)
+ {
+ memset(m_token_array, 0, m_token_array_length);
+ }
+ memset(m_md5, 0, MD5_HASH_SIZE);
+ }
+
+ inline bool is_empty()
+ {
+ return (m_byte_count == 0);
+ }
+
+ inline void copy(const sql_digest_storage *from)
+ {
+ /*
+ Keep in mind this is a dirty copy of something that may change,
+ as the thread producing the digest is executing concurrently,
+ without any lock enforced.
+ */
+ uint byte_count_copy= m_token_array_length < from->m_byte_count ?
+ m_token_array_length : from->m_byte_count;
+
+ if (byte_count_copy > 0)
+ {
+ m_full= from->m_full;
+ m_byte_count= byte_count_copy;
+ m_charset_number= from->m_charset_number;
+ memcpy(m_token_array, from->m_token_array, m_byte_count);
+ memcpy(m_md5, from->m_md5, MD5_HASH_SIZE);
+ }
+ else
+ {
+ m_full= false;
+ m_byte_count= 0;
+ m_charset_number= 0;
+ }
+ }
+};
+typedef struct sql_digest_storage sql_digest_storage;
+
+/**
+ Compute a digest hash.
+ @param digest_storage The digest
+ @param [out] md5 The computed digest hash. This parameter is a buffer of size @c MD5_HASH_SIZE.
+*/
+void compute_digest_md5(const sql_digest_storage *digest_storage, unsigned char *md5);
+
+/**
+ Compute a digest text.
+ A 'digest text' is a textual representation of a query,
+ where:
+ - comments are removed,
+ - non significant spaces are removed,
+ - literal values are replaced with a special '?' marker,
+ - lists of values are collapsed using a shorter notation
+ @param digest_storage The digest
+ @param [out] digest_text
+ @param digest_text_length Size of @c digest_text.
+ @param [out] truncated true if the text representation was truncated
+*/
+void compute_digest_text(const sql_digest_storage *digest_storage,
+ String *digest_text);
+
+#endif
+
diff --git a/sql/sql_digest_stream.h b/sql/sql_digest_stream.h
new file mode 100644
index 00000000000..55f7e2293c6
--- /dev/null
+++ b/sql/sql_digest_stream.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef SQL_DIGEST_STREAM_H
+#define SQL_DIGEST_STREAM_H
+
+#include "sql_digest.h"
+
+/**
+ State data storage for @c digest_start, @c digest_add_token.
+ This structure extends the @c sql_digest_storage structure
+ with temporary state used only during parsing.
+*/
+struct sql_digest_state
+{
+ /**
+ Index, in the digest token array, of the last identifier seen.
+ Reduce rules used in the digest computation can not
+ apply to tokens seen before an identifier.
+ @sa digest_add_token
+ */
+ int m_last_id_index;
+ sql_digest_storage m_digest_storage;
+
+ inline void reset(unsigned char *token_array, uint length)
+ {
+ m_last_id_index= 0;
+ m_digest_storage.reset(token_array, length);
+ }
+
+ inline bool is_empty()
+ {
+ return m_digest_storage.is_empty();
+ }
+};
+typedef struct sql_digest_state sql_digest_state;
+
+#endif
+
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 11f2d6b3dbc..1ec33a0a0ac 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -148,9 +148,11 @@ bool check_view_single_update(List<Item> &fields, List<Item> *values,
if (view->check_single_table(&tbl, tables, view) || tbl == 0)
goto error;
+ /* view->table should have been set in mysql_derived_merge_for_insert */
+ DBUG_ASSERT(view->table);
+
/*
- A buffer for the insert values was allocated for the merged view.
- Use it.
+ Use buffer for the insert values that was allocated for the merged view.
*/
tbl->table->insert_values= view->table->insert_values;
view->table= tbl->table;
@@ -195,11 +197,12 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
table_map *map)
{
TABLE *table= table_list->table;
+ DBUG_ENTER("check_insert_fields");
if (!table_list->single_table_updatable())
{
my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT");
- return -1;
+ DBUG_RETURN(-1);
}
if (fields.elements == 0 && values.elements != 0)
@@ -208,18 +211,18 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
{
my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0),
table_list->view_db.str, table_list->view_name.str);
- return -1;
+ DBUG_RETURN(-1);
}
if (values.elements != table->s->fields)
{
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
- return -1;
+ DBUG_RETURN(-1);
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Field_iterator_table_ref field_it;
field_it.set(table_list);
if (check_grant_all_columns(thd, INSERT_ACL, &field_it))
- return -1;
+ DBUG_RETURN(-1);
#endif
/*
No fields are provided so all fields must be provided in the values.
@@ -237,7 +240,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
if (fields.elements != values.elements)
{
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
- return -1;
+ DBUG_RETURN(-1);
}
thd->dup_field= 0;
@@ -263,7 +266,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
thd->lex->select_lex.no_wrap_view_item= FALSE;
if (res)
- return -1;
+ DBUG_RETURN(-1);
if (table_list->is_view() && table_list->is_merged_derived())
{
@@ -271,14 +274,14 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
fields_and_values_from_different_maps ?
(List<Item>*) 0 : &values,
table_list, map, true))
- return -1;
+ DBUG_RETURN(-1);
table= table_list->table;
}
if (check_unique && thd->dup_field)
{
my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name);
- return -1;
+ DBUG_RETURN(-1);
}
if (table->default_field)
table->mark_default_fields_for_write();
@@ -296,10 +299,10 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
check_view_insertability(thd, table_list)))
{
my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT");
- return -1;
+ DBUG_RETURN(-1);
}
- return 0;
+ DBUG_RETURN(0);
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 697988a6bf8..2026d5f5059 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -436,6 +436,21 @@ void Lex_input_stream::body_utf8_append_literal(THD *thd,
m_cpp_utf8_processed_ptr= end_ptr;
}
+void Lex_input_stream::add_digest_token(uint token, LEX_YYSTYPE yylval)
+{
+ if (m_digest != NULL)
+ {
+ m_digest= digest_add_token(m_digest, token, yylval);
+ }
+}
+
+void Lex_input_stream::reduce_digest_token(uint token_left, uint token_right)
+{
+ if (m_digest != NULL)
+ {
+ m_digest= digest_reduce_token(m_digest, token_left, token_right);
+ }
+}
/*
This is called before every query that is to be parsed.
@@ -541,6 +556,7 @@ void lex_start(THD *thd)
lex->is_lex_started= TRUE;
lex->used_tables= 0;
+ lex->only_view= FALSE;
lex->reset_slave_info.all= false;
lex->limit_rows_examined= 0;
lex->limit_rows_examined_cnt= ULONGLONG_MAX;
@@ -982,7 +998,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
lip->lookahead_token= -1;
*yylval= *(lip->lookahead_yylval);
lip->lookahead_yylval= NULL;
- lip->m_digest_psi= MYSQL_ADD_TOKEN(lip->m_digest_psi, token, yylval);
+ lip->add_digest_token(token, yylval);
return token;
}
@@ -1000,12 +1016,10 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
token= lex_one_token(yylval, thd);
switch(token) {
case CUBE_SYM:
- lip->m_digest_psi= MYSQL_ADD_TOKEN(lip->m_digest_psi, WITH_CUBE_SYM,
- yylval);
+ lip->add_digest_token(WITH_CUBE_SYM, yylval);
return WITH_CUBE_SYM;
case ROLLUP_SYM:
- lip->m_digest_psi= MYSQL_ADD_TOKEN(lip->m_digest_psi, WITH_ROLLUP_SYM,
- yylval);
+ lip->add_digest_token(WITH_ROLLUP_SYM, yylval);
return WITH_ROLLUP_SYM;
default:
/*
@@ -1014,7 +1028,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
lip->lookahead_yylval= lip->yylval;
lip->yylval= NULL;
lip->lookahead_token= token;
- lip->m_digest_psi= MYSQL_ADD_TOKEN(lip->m_digest_psi, WITH, yylval);
+ lip->add_digest_token(WITH, yylval);
return WITH;
}
break;
@@ -1022,7 +1036,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
break;
}
- lip->m_digest_psi= MYSQL_ADD_TOKEN(lip->m_digest_psi, token, yylval);
+ lip->add_digest_token(token, yylval);
return token;
}
@@ -1860,7 +1874,7 @@ void st_select_lex::init_query()
exclude_from_table_unique_test= no_wrap_view_item= FALSE;
nest_level= 0;
link_next= 0;
- is_prep_leaf_list_saved= FALSE;
+ prep_leaf_list_state= UNINIT;
have_merged_subqueries= FALSE;
bzero((char*) expr_cache_may_be_used, sizeof(expr_cache_may_be_used));
m_non_agg_field_used= false;
@@ -4113,27 +4127,58 @@ bool st_select_lex::save_leaf_tables(THD *thd)
}
-bool st_select_lex::save_prep_leaf_tables(THD *thd)
+bool LEX::save_prep_leaf_tables()
{
if (!thd->save_prep_leaf_list)
- return 0;
+ return FALSE;
Query_arena *arena= thd->stmt_arena, backup;
arena= thd->activate_stmt_arena_if_needed(&backup);
+ //It is used for DETETE/UPDATE so top level has only one SELECT
+ DBUG_ASSERT(select_lex.next_select() == NULL);
+ bool res= select_lex.save_prep_leaf_tables(thd);
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ if (res)
+ return TRUE;
+
+ thd->save_prep_leaf_list= FALSE;
+ return FALSE;
+}
+
+bool st_select_lex::save_prep_leaf_tables(THD *thd)
+{
List_iterator_fast<TABLE_LIST> li(leaf_tables);
TABLE_LIST *table;
+
+ /*
+ Check that the SELECT_LEX was really prepared and so tables are setup.
+
+ It can be subquery in SET clause of UPDATE which was not prepared yet, so
+ its tables are not yet setup and ready for storing.
+ */
+ if (prep_leaf_list_state != READY)
+ return FALSE;
+
while ((table= li++))
{
if (leaf_tables_prep.push_back(table))
- return 1;
+ return TRUE;
+ }
+ prep_leaf_list_state= SAVED;
+ for (SELECT_LEX_UNIT *u= first_inner_unit(); u; u= u->next_unit())
+ {
+ for (SELECT_LEX *sl= u->first_select(); sl; sl= sl->next_select())
+ {
+ if (sl->save_prep_leaf_tables(thd))
+ return TRUE;
+ }
}
- thd->lex->select_lex.is_prep_leaf_list_saved= TRUE;
- thd->save_prep_leaf_list= FALSE;
- if (arena)
- thd->restore_active_arena(arena, &backup);
- return 0;
+ return FALSE;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 88491743d39..dcd8ddfce91 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2010, 2014, Monty Program Ab.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -47,6 +47,7 @@ class sys_var;
class Item_func_match;
class File_parser;
class Key_part_spec;
+struct sql_digest_state;
#ifdef MYSQL_SERVER
/*
@@ -732,7 +733,8 @@ public:
List<TABLE_LIST> leaf_tables;
List<TABLE_LIST> leaf_tables_exec;
List<TABLE_LIST> leaf_tables_prep;
- bool is_prep_leaf_list_saved;
+ enum leaf_list_state {UNINIT, READY, SAVED};
+ enum leaf_list_state prep_leaf_list_state;
uint insert_tables;
st_select_lex *merged_into; /* select which this select is merged into */
/* (not 0 only for views/derived tables) */
@@ -2048,6 +2050,10 @@ public:
/** LALR(2) resolution, value of the look ahead token.*/
LEX_YYSTYPE lookahead_yylval;
+ void add_digest_token(uint token, LEX_YYSTYPE yylval);
+
+ void reduce_digest_token(uint token_left, uint token_right);
+
private:
/** Pointer to the current position in the raw input stream. */
char *m_ptr;
@@ -2167,7 +2173,7 @@ public:
/**
Current statement digest instrumentation.
*/
- PSI_digest_locker* m_digest_psi;
+ sql_digest_state* m_digest;
};
/**
@@ -2743,6 +2749,8 @@ struct LEX: public Query_tables_list
return FALSE;
}
+ bool save_prep_leaf_tables();
+
int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything);
};
@@ -2861,6 +2869,18 @@ public:
};
/**
+ Input parameters to the parser.
+*/
+struct Parser_input
+{
+ bool m_compute_digest;
+
+ Parser_input()
+ : m_compute_digest(false)
+ {}
+};
+
+/**
Internal state of the parser.
The complete state consist of:
- state data used during lexical parsing,
@@ -2887,9 +2907,15 @@ public:
~Parser_state()
{}
+ Parser_input m_input;
Lex_input_stream m_lip;
Yacc_state m_yacc;
+ /**
+ Current performance digest instrumentation.
+ */
+ PSI_digest_locker* m_digest_psi;
+
void reset(char *found_semicolon, unsigned int length)
{
m_lip.reset(found_semicolon, length);
@@ -2897,6 +2923,11 @@ public:
}
};
+extern sql_digest_state *
+digest_add_token(sql_digest_state *state, uint token, LEX_YYSTYPE yylval);
+
+extern sql_digest_state *
+digest_reduce_token(sql_digest_state *state, uint token_left, uint token_right);
struct st_lex_local: public LEX
{
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 96e7bbc822e..5635e9ad4b7 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -83,6 +83,8 @@
#include "rpl_handler.h"
#include "rpl_mi.h"
+#include "sql_digest.h"
+
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
@@ -949,6 +951,7 @@ bool do_command(THD *thd)
/* Mark the statement completed. */
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
if (net->error != 3)
{
@@ -998,6 +1001,7 @@ bool do_command(THD *thd)
out:
/* The statement instrumentation must be closed in all cases. */
+ DBUG_ASSERT(thd->m_digest == NULL);
DBUG_ASSERT(thd->m_statement_psi == NULL);
DBUG_RETURN(return_value);
}
@@ -1278,6 +1282,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
case COM_QUERY:
{
+ DBUG_ASSERT(thd->m_digest == NULL);
+ thd->m_digest= & thd->m_digest_state;
+ thd->m_digest->reset(thd->m_token_array, max_digest_length);
+
if (alloc_query(thd, packet, packet_length))
break; // fatal error is set
MYSQL_QUERY_START(thd->query(), thd->thread_id,
@@ -1337,6 +1345,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* PSI end */
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
/* DTRACE end */
if (MYSQL_QUERY_DONE_ENABLED())
@@ -1357,6 +1366,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
(char *) thd->security_ctx->host_or_ip);
/* PSI begin */
+ thd->m_digest= & thd->m_digest_state;
+
thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
com_statement_info[command].m_key,
thd->db, thd->db_length,
@@ -1741,6 +1752,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* Performance Schema Interface instrumentation, end */
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
thd->set_time();
dec_thread_running();
@@ -2283,11 +2295,15 @@ mysql_execute_command(THD *thd)
according to slave filtering rules.
Returning success without producing any errors in this case.
*/
- DBUG_RETURN(0);
+ if (!thd->lex->check_exists)
+ DBUG_RETURN(0);
+ /*
+ DROP TRIGGER IF NOT EXISTS will return without an error later
+ after possibly writing the query to a binlog
+ */
}
-
- // force searching in slave.cc:tables_ok()
- all_tables->updating= 1;
+ else // force searching in slave.cc:tables_ok()
+ all_tables->updating= 1;
}
/*
@@ -8547,11 +8563,27 @@ bool parse_sql(THD *thd, Parser_state *parser_state,
thd->m_parser_state= parser_state;
-#ifdef HAVE_PSI_STATEMENT_DIGEST_INTERFACE
- /* Start Digest */
- thd->m_parser_state->m_lip.m_digest_psi=
- MYSQL_DIGEST_START(do_pfs_digest ? thd->m_statement_psi : NULL);
-#endif
+ parser_state->m_digest_psi= NULL;
+ parser_state->m_lip.m_digest= NULL;
+
+ if (do_pfs_digest)
+ {
+ /* Start Digest */
+ parser_state->m_digest_psi= MYSQL_DIGEST_START(thd->m_statement_psi);
+
+ if (parser_state->m_input.m_compute_digest ||
+ (parser_state->m_digest_psi != NULL))
+ {
+ /*
+ If either:
+ - the caller wants to compute a digest
+ - the performance schema wants to compute a digest
+ set the digest listener in the lexer.
+ */
+ parser_state->m_lip.m_digest= thd->m_digest;
+ parser_state->m_lip.m_digest->m_digest_storage.m_charset_number= thd->charset()->number;
+ }
+ }
/* Parse the query. */
@@ -8584,6 +8616,18 @@ bool parse_sql(THD *thd, Parser_state *parser_state,
/* That's it. */
ret_value= mysql_parse_status || thd->is_fatal_error;
+
+ if ((ret_value == 0) && (parser_state->m_digest_psi != NULL))
+ {
+ /*
+ On parsing success, record the digest in the performance schema.
+ */
+ DBUG_ASSERT(do_pfs_digest);
+ DBUG_ASSERT(thd->m_digest != NULL);
+ MYSQL_DIGEST_END(parser_state->m_digest_psi,
+ & thd->m_digest->m_digest_storage);
+ }
+
MYSQL_QUERY_PARSE_DONE(ret_value);
DBUG_RETURN(ret_value);
}
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index 239d793c633..f0fde223984 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -57,7 +57,6 @@
#include "lock.h" // mysql_lock_remove
#include "sql_show.h" // append_identifier
#include <m_ctype.h>
-#include "my_md5.h"
#include "transaction.h"
#include "debug_sync.h"
diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
index 8c59febeb77..87803c220ea 100644
--- a/sql/sql_partition_admin.cc
+++ b/sql/sql_partition_admin.cc
@@ -824,9 +824,16 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
log. The exception is a unimplemented truncate method or failure
before any call to handler::truncate() is done.
Also, it is logged in statement format, regardless of the binlog format.
+
+ Since we've changed data within the table, we also have to invalidate
+ the query cache for it.
*/
- if (error != HA_ERR_WRONG_COMMAND && binlog_stmt)
- error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
+ if (error != HA_ERR_WRONG_COMMAND)
+ {
+ query_cache_invalidate3(thd, first_table, FALSE);
+ if (binlog_stmt)
+ error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
+ }
/*
A locked table ticket was upgraded to a exclusive lock. After the
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 4227641859d..dc408703da4 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -264,13 +264,14 @@ public:
{ TRASH(ptr_arg, size); }
sys_var_pluginvar(sys_var_chain *chain, const char *name_arg,
- struct st_mysql_sys_var *plugin_var_arg)
+ struct st_mysql_sys_var *plugin_var_arg,
+ struct st_plugin_int *plugin_arg)
:sys_var(chain, name_arg, plugin_var_arg->comment,
(plugin_var_arg->flags & PLUGIN_VAR_THDLOCAL ? SESSION : GLOBAL) |
(plugin_var_arg->flags & PLUGIN_VAR_READONLY ? READONLY : 0),
0, -1, NO_ARG, pluginvar_show_type(plugin_var_arg), 0, 0,
VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL),
- plugin_var(plugin_var_arg)
+ plugin(plugin_arg), plugin_var(plugin_var_arg)
{ plugin_var->name= name_arg; }
sys_var_pluginvar *cast_pluginvar() { return this; }
bool check_update_type(Item_result type);
@@ -1413,22 +1414,6 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin,
goto err;
}
- /*
- set the plugin attribute of plugin's sys vars so they are pointing
- to the active plugin
- */
- if (plugin->system_vars)
- {
- sys_var_pluginvar *var= plugin->system_vars->cast_pluginvar();
- for (;;)
- {
- var->plugin= plugin;
- if (!var->next)
- break;
- var= var->next->cast_pluginvar();
- }
- }
-
ret= 0;
err:
@@ -2085,14 +2070,8 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
char **argv=orig_argv;
DBUG_ENTER("mysql_install_plugin");
- if (opt_noacl)
- {
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
- DBUG_RETURN(TRUE);
- }
-
tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
- if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
+ if (!opt_noacl && check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
/* need to open before acquiring LOCK_plugin or it will deadlock */
@@ -2227,15 +2206,9 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
bool error= false;
DBUG_ENTER("mysql_uninstall_plugin");
- if (opt_noacl)
- {
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
- DBUG_RETURN(TRUE);
- }
-
tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
- if (check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
+ if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
/* need to open before acquiring LOCK_plugin or it will deadlock */
@@ -3973,7 +3946,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
continue;
tmp_backup[tmp->nbackups++].save(&o->name);
if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
- v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o);
+ v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o, tmp);
else
{
len= plugin_name.length + strlen(o->name) + 2;
@@ -3981,7 +3954,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
strxmov(varname, plugin_name.str, "-", o->name, NullS);
my_casedn_str(&my_charset_latin1, varname);
convert_dash_to_underscore(varname, len-1);
- v= new (mem_root) sys_var_pluginvar(&chain, varname, o);
+ v= new (mem_root) sys_var_pluginvar(&chain, varname, o, tmp);
}
DBUG_ASSERT(v); /* check that an object was actually constructed */
} /* end for */
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 2c17898f07c..6496e1895fb 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -238,7 +238,6 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
{
int rc= 1;
handlerton *hton;
- bool new_exists, old_exists;
const char *new_alias, *old_alias;
DBUG_ENTER("do_rename");
@@ -254,17 +253,13 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
}
DBUG_ASSERT(new_alias);
- new_exists= ha_table_exists(thd, new_db, new_alias);
-
- if (new_exists)
+ if (ha_table_exists(thd, new_db, new_alias))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
DBUG_RETURN(1); // This can't be skipped
}
- old_exists= ha_table_exists(thd, ren_table->db, old_alias, &hton);
-
- if (old_exists)
+ if (ha_table_exists(thd, ren_table->db, old_alias, &hton) && hton)
{
DBUG_ASSERT(!thd->locked_tables_mode);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 68535938484..4055c8ea8af 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -943,7 +943,7 @@ give_error_start_pos_missing_in_binlog(int *err, const char **errormsg,
binlog_gtid.seq_no >= error_gtid->seq_no)
{
*errormsg= "Requested slave GTID state not found in binlog. The slave has "
- "probably diverged due to executing errorneous transactions";
+ "probably diverged due to executing erroneous transactions";
*err= ER_GTID_POSITION_NOT_FOUND_IN_BINLOG2;
}
else
@@ -1948,6 +1948,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
slave_connection_state until_gtid_state_obj;
rpl_gtid error_gtid;
binlog_send_info info(thd, packet, flags, log_file_name);
+ bool has_transmit_started= false;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
@@ -2007,16 +2008,6 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
DBUG_SET("+d,corrupt_read_log_event2");
});
- if (global_system_variables.log_warnings > 1)
- sql_print_information("Start binlog_dump to slave_server(%lu), pos(%s, %lu)",
- thd->variables.server_id, log_ident, (ulong)pos);
- if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos)))
- {
- errmsg= "Failed to run hook 'transmit_start'";
- my_errno= ER_UNKNOWN_ERROR;
- goto err;
- }
-
#ifndef DBUG_OFF
if (opt_sporadic_binlog_dump_fail && (binlog_dump_count++ % 2))
{
@@ -2113,6 +2104,17 @@ impossible position";
goto err;
}
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Start binlog_dump to slave_server(%lu), pos(%s, %lu)",
+ thd->variables.server_id, log_ident, (ulong)pos);
+ if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos)))
+ {
+ errmsg= "Failed to run hook 'transmit_start'";
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ has_transmit_started= true;
+
/* reset transmit packet for the fake rotate event below */
if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
goto err;
@@ -2375,6 +2377,31 @@ impossible position";
info.fdev= tmp;
(*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
+
+ if (info.using_gtid_state)
+ {
+ /*
+ If this event has the field `created' set, then it will cause the
+ slave to delete all active temporary tables. This must not happen
+ if the slave received any later GTIDs in a previous connect, as
+ those GTIDs might have created new temporary tables that are still
+ needed.
+
+ So here, we check if the starting GTID position was already
+ reached before this format description event. If not, we clear the
+ `created' flag to preserve temporary tables on the slave. (If the
+ slave connects at a position past this event, it means that it
+ already received and handled it in a previous connect).
+ */
+ if (!info.gtid_state.is_pos_reached())
+ {
+ int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+
+ ST_CREATED_OFFSET+ev_offset, (ulong) 0);
+ if (info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ fix_checksum(packet, ev_offset);
+ }
+ }
}
#ifndef DBUG_OFF
@@ -2680,7 +2707,8 @@ end:
end_io_cache(&log);
mysql_file_close(file, MYF(MY_WME));
- RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
+ if (has_transmit_started)
+ RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
my_eof(thd);
THD_STAGE_INFO(thd, stage_waiting_to_finalize_termination);
mysql_mutex_lock(&LOCK_thread_count);
@@ -2723,7 +2751,7 @@ err:
"%u-%u-%llu, which is not in the master's binlog. Since the "
"master's binlog contains GTIDs with higher sequence numbers, "
"it probably means that the slave has diverged due to "
- "executing extra errorneous transactions",
+ "executing extra erroneous transactions",
error_gtid.domain_id, error_gtid.server_id, error_gtid.seq_no);
/* Use this error code so slave will know not to try reconnect. */
my_errno = ER_MASTER_FATAL_ERROR_READING_BINLOG;
@@ -2749,7 +2777,8 @@ err:
else
strcpy(error_text, errmsg);
end_io_cache(&log);
- RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
+ if (has_transmit_started)
+ RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
/*
Exclude iteration through thread list
this is needed for purge_logs() - it will iterate through
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index ed56191c32b..e0f560f0b3a 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2013 Oracle and/or its affiliates.
- Copyright (c) 2009, 2013 Monty Program Ab.
+/* Copyright (c) 2000, 2015 Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015 MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -709,9 +709,7 @@ JOIN::prepare(Item ***rref_pointer_array,
if (!(select_options & OPTION_SETUP_TABLES_DONE) &&
setup_tables_and_check_access(thd, &select_lex->context, join_list,
tables_list, select_lex->leaf_tables,
- FALSE, SELECT_ACL, SELECT_ACL,
- (thd->lex->sql_command ==
- SQLCOM_UPDATE_MULTI)))
+ FALSE, SELECT_ACL, SELECT_ACL, FALSE))
DBUG_RETURN(-1);
/*
@@ -4958,7 +4956,18 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field)
}
-#define FT_KEYPART (MAX_REF_PARTS+10)
+/*
+ A key part number that means we're using a fulltext scan.
+
+ In order not to confuse it with regular equalities, we need to pick
+ a number that's greater than MAX_REF_PARTS.
+
+ Hash Join code stores field->field_index in KEYUSE::keypart, so the
+ number needs to be bigger than MAX_FIELDS, also.
+
+ CAUTION: sql_test.cc has its own definition of FT_KEYPART.
+*/
+#define FT_KEYPART (MAX_FIELDS+10)
static bool
add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
@@ -6455,7 +6464,6 @@ choose_plan(JOIN *join, table_map join_tables)
DBUG_ENTER("choose_plan");
join->cur_embedding_map= 0;
- join->cur_dups_producing_tables= 0;
reset_nj_counters(join, join->join_list);
qsort2_cmp jtab_sort_func;
@@ -7451,8 +7459,12 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
else
fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1;
if (keyuse->val->const_item())
- {
- sel /= table->field[fldno]->cond_selectivity;
+ {
+ if (table->field[fldno]->cond_selectivity > 0)
+ {
+ sel /= table->field[fldno]->cond_selectivity;
+ set_if_smaller(sel, 1.0);
+ }
/*
TODO: we could do better here:
1. cond_selectivity might be =1 (the default) because quick
@@ -7506,7 +7518,10 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
if (!(next_field->table->map & rem_tables) && next_field->table != table)
{
if (field->cond_selectivity > 0)
+ {
sel/= field->cond_selectivity;
+ set_if_smaller(sel, 1.0);
+ }
break;
}
}
@@ -8660,7 +8675,8 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
{
Field *field= table->field[keyuse->keypart];
uint fieldnr= keyuse->keypart+1;
- table->create_key_part_by_field(keyinfo, key_part_info, field, fieldnr);
+ table->create_key_part_by_field(key_part_info, field, fieldnr);
+ keyinfo->key_length += key_part_info->store_length;
key_part_info++;
}
}
@@ -9720,10 +9736,24 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (!sel->quick_keys.is_subset(tab->checked_keys) ||
!sel->needed_reg.is_subset(tab->checked_keys))
{
+ /*
+ "Range checked for each record" is a "last resort" access method
+ that should only be used when the other option is a cross-product
+ join.
+
+ We use the following condition (it's approximate):
+ 1. There are potential keys for (sel->needed_reg)
+ 2. There were no possible ways to construct a quick select, or
+ the quick select would be more expensive than the full table
+ scan.
+ */
tab->use_quick= (!sel->needed_reg.is_clear_all() &&
(sel->quick_keys.is_clear_all() ||
- (sel->quick &&
- (sel->quick->records >= 100L)))) ?
+ (sel->quick &&
+ sel->quick->read_time >
+ tab->table->file->scan_time() +
+ tab->table->file->stats.records/TIME_FOR_COMPARE
+ ))) ?
2 : 1;
sel->read_tables= used_tables & ~current_map;
sel->quick_keys.clear_all();
@@ -15791,7 +15821,6 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
(int) distinct, (int) save_sum_fields,
(ulong) rows_limit, MY_TEST(group)));
- thd->inc_status_created_tmp_tables();
thd->query_plan_flags|= QPLAN_TMP_TABLE;
if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES))
@@ -16745,14 +16774,19 @@ bool open_tmp_table(TABLE *table)
HA_OPEN_TMP_TABLE |
HA_OPEN_INTERNAL_TABLE)))
{
- table->file->print_error(error,MYF(0)); /* purecov: inspected */
- table->db_stat=0;
- return(1);
+ table->file->print_error(error, MYF(0)); /* purecov: inspected */
+ table->db_stat= 0;
+ return 1;
}
table->db_stat= HA_OPEN_KEYFILE+HA_OPEN_RNDFILE;
- (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */
- table->created= TRUE;
- return(0);
+ (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */
+ if (!table->created)
+ {
+ table->created= TRUE;
+ table->in_use->inc_status_created_tmp_tables();
+ }
+
+ return 0;
}
@@ -16809,7 +16843,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
goto err;
bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts);
- if (keyinfo->key_length >= table->file->max_key_length() ||
+ if (keyinfo->key_length > table->file->max_key_length() ||
keyinfo->user_defined_key_parts > table->file->max_key_parts() ||
share->uniques)
{
@@ -16919,8 +16953,10 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
goto err;
}
table->in_use->inc_status_created_tmp_disk_tables();
+ table->in_use->inc_status_created_tmp_tables();
table->in_use->query_plan_flags|= QPLAN_TMP_DISK;
share->db_record_offset= 1;
+ table->created= TRUE;
DBUG_RETURN(0);
err:
DBUG_RETURN(1);
@@ -16980,7 +17016,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
goto err;
bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts);
- if (keyinfo->key_length >= table->file->max_key_length() ||
+ if (keyinfo->key_length > table->file->max_key_length() ||
keyinfo->user_defined_key_parts > table->file->max_key_parts() ||
share->uniques)
{
@@ -17065,6 +17101,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
goto err;
}
table->in_use->inc_status_created_tmp_disk_tables();
+ table->in_use->inc_status_created_tmp_tables();
table->in_use->query_plan_flags|= QPLAN_TMP_DISK;
share->db_record_offset= 1;
table->created= TRUE;
@@ -21123,18 +21160,33 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
for (;order;order=order->next,pos++)
{
- Item *item= order->item[0]->real_item();
+ Item *const item= order->item[0], *const real_item= item->real_item();
pos->field= 0; pos->item= 0;
- if (item->type() == Item::FIELD_ITEM)
- pos->field= ((Item_field*) item)->field;
- else if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item())
- pos->field= ((Item_sum*) item)->get_tmp_table_field();
- else if (item->type() == Item::COPY_STR_ITEM)
- { // Blob patch
- pos->item= ((Item_copy*) item)->get_item();
+ if (real_item->type() == Item::FIELD_ITEM)
+ {
+ // Could be a field, or Item_direct_view_ref wrapping a field
+ DBUG_ASSERT(item->type() == Item::FIELD_ITEM ||
+ (item->type() == Item::REF_ITEM &&
+ static_cast<Item_ref*>(item)->ref_type() ==
+ Item_ref::VIEW_REF));
+ pos->field= static_cast<Item_field*>(real_item)->field;
+ }
+ else if (real_item->type() == Item::SUM_FUNC_ITEM &&
+ !real_item->const_item())
+ {
+ // Aggregate, or Item_aggregate_ref
+ DBUG_ASSERT(item->type() == Item::SUM_FUNC_ITEM ||
+ (item->type() == Item::REF_ITEM &&
+ static_cast<Item_ref*>(item)->ref_type() ==
+ Item_ref::AGGREGATE_REF));
+ pos->field= item->get_tmp_table_field();
+ }
+ else if (real_item->type() == Item::COPY_STR_ITEM)
+ { // Blob patch
+ pos->item= static_cast<Item_copy*>(real_item)->get_item();
}
else
- pos->item= *order->item;
+ pos->item= item;
pos->reverse=! order->asc;
DBUG_ASSERT(pos->field != NULL || pos->item != NULL);
}
@@ -21378,6 +21430,17 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
uint el= all_fields.elements;
all_fields.push_front(order_item); /* Add new field to field list. */
ref_pointer_array[el]= order_item;
+ /*
+ If the order_item is a SUM_FUNC_ITEM, when fix_fields is called
+ ref_by is set to order->item which is the address of order_item.
+ But this needs to be address of order_item in the all_fields list.
+ As a result, when it gets replaced with Item_aggregate_ref
+ object in Item::split_sum_func2, we will be able to retrieve the
+ newly created object.
+ */
+ if (order_item->type() == Item::SUM_FUNC_ITEM)
+ ((Item_sum *)order_item)->ref_by= all_fields.head_ref();
+
order->item= ref_pointer_array + el;
return FALSE;
}
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 7d53731b558..5aa29715dc3 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -2,7 +2,7 @@
#define SQL_SELECT_INCLUDED
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2013, Monty Program Ab.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -825,7 +825,12 @@ typedef struct st_position :public Sql_alloc
*/
uint n_sj_tables;
- table_map prefix_dups_producing_tables;
+ /*
+ Bitmap of semi-join inner tables that are in the join prefix and for
+ which there's no provision for how to eliminate semi-join duplicates
+ they produce.
+ */
+ table_map dups_producing_tables;
table_map inner_tables_handled_with_other_sjs;
@@ -1046,13 +1051,6 @@ public:
*/
table_map cur_sj_inner_tables;
- /*
- Bitmap of semi-join inner tables that are in the join prefix and for
- which there's no provision for how to eliminate semi-join duplicates
- they produce.
- */
- table_map cur_dups_producing_tables;
-
/* We also maintain a stack of join optimization states in * join->positions[] */
/******* Join optimization state members end *******/
@@ -1788,10 +1786,6 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
bool table_cant_handle_bit_fields,
bool make_copy_field,
uint convert_blob_length);
-bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
- TMP_ENGINE_COLUMNDEF *start_recinfo,
- TMP_ENGINE_COLUMNDEF **recinfo,
- ulonglong options, my_bool big_tables);
/*
General routine to change field->ptr of a NULL-terminated array of Field
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index 2b0576ffba9..8d5bb2b596d 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -326,7 +326,8 @@ get_server_from_table_to_cache(TABLE *table)
table->use_all_columns();
/* get each field into the server struct ptr */
- server->server_name= get_field(&mem, table->field[0]);
+ ptr= get_field(&mem, table->field[0]);
+ server->server_name= ptr ? ptr : blank;
server->server_name_length= (uint) strlen(server->server_name);
ptr= get_field(&mem, table->field[1]);
server->host= ptr ? ptr : blank;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 6dabd349214..2413cb5ba53 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, SkySQL Ab.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -2301,7 +2301,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
Security_context *tmp_sctx= tmp->security_ctx;
struct st_my_thread_var *mysys_var;
if ((tmp->vio_ok() || tmp->system_thread) &&
- (!user || (tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
+ (!user || (!tmp->system_thread &&
+ tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
{
thread_info *thd_info= new thread_info;
@@ -2679,7 +2680,8 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
ulonglong max_counter;
if ((!tmp->vio_ok() && !tmp->system_thread) ||
- (user && (!tmp_sctx->user || strcmp(tmp_sctx->user, user))))
+ (user && (tmp->system_thread || !tmp_sctx->user ||
+ strcmp(tmp_sctx->user, user))))
continue;
restore_record(table, s->default_values);
@@ -2895,7 +2897,7 @@ void reset_status_vars()
catch-all cleanup function, cleans up everything no matter what
DESCRIPTION
- This function is not strictly required if all add_to_status/
+ This function is not strictly required if all add_status_vars/
remove_status_vars are properly paired, but it's a safety measure that
deletes everything from the all_status_vars[] even if some
remove_status_vars were forgotten
@@ -8099,15 +8101,20 @@ bool get_schema_tables_result(JOIN *join,
TABLE_LIST *table_list= tab->table->pos_in_table_list;
if (table_list->schema_table && thd->fill_information_schema_tables())
{
+#if MYSQL_VERSION_ID > 100105
+#error I_S tables only need to be re-populated if make_cond_for_info_schema() will preserve outer fields
bool is_subselect= (&lex->unit != lex->current_select->master_unit() &&
lex->current_select->master_unit()->item);
+#else
+#define is_subselect false
+#endif
/* A value of 0 indicates a dummy implementation */
if (table_list->schema_table->fill_table == 0)
continue;
/* skip I_S optimizations specific to get_all_tables */
- if (thd->lex->describe &&
+ if (lex->describe &&
(table_list->schema_table->fill_table != get_all_tables))
continue;
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 48aac779215..20cfccaa3d9 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2010, 2014, SkySQL Ab.
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,7 +28,6 @@
#include "sql_base.h" // open_table_uncached, lock_table_names
#include "lock.h" // mysql_unlock_tables
#include "strfunc.h" // find_type2, find_set
-#include "sql_view.h" // view_checksum
#include "sql_truncate.h" // regenerate_locked_table
#include "sql_partition.h" // mem_alloc_error,
// generate_partition_syntax,
@@ -1673,6 +1672,7 @@ void execute_ddl_log_recovery()
(void) mysql_file_delete(key_file_global_ddl_log, file_name, MYF(0));
global_ddl_log.recovery_phase= FALSE;
mysql_mutex_unlock(&LOCK_gdl);
+ thd->reset_query();
delete thd;
/* Remember that we don't have a THD */
set_current_thd(0);
@@ -2201,15 +2201,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
char path[FN_REFLEN + 1], wrong_tables_buff[160], *alias= NULL;
String wrong_tables(wrong_tables_buff, sizeof(wrong_tables_buff)-1,
system_charset_info);
- uint path_length= 0;
+ uint path_length= 0, errors= 0;
int error= 0;
int non_temp_tables_count= 0;
- bool foreign_key_error=0;
bool non_tmp_error= 0;
bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0;
bool non_tmp_table_deleted= 0;
bool is_drop_tmp_if_exists_added= 0;
- bool one_table= tables->next_local == 0;
bool was_view= 0;
String built_query;
String built_trans_tmp_query, built_non_trans_tmp_query;
@@ -2495,12 +2493,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
error= ha_delete_table(thd, table_type, path, db, table->table_name,
!dont_log_query);
- if (error == HA_ERR_ROW_IS_REFERENCED)
- {
- /* the table is referenced by a foreign key constraint */
- foreign_key_error= 1;
- }
- if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
+ if (!error)
{
int frm_delete_error, trigger_drop_error= 0;
/* Delete the table definition file */
@@ -2518,11 +2511,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
if (trigger_drop_error ||
(frm_delete_error && frm_delete_error != ENOENT))
error= 1;
- else if (!frm_delete_error || !error || if_exists)
- {
- error= 0;
+ else if (frm_delete_error && if_exists)
thd->clear_error();
- }
}
non_tmp_error= error ? TRUE : non_tmp_error;
}
@@ -2533,6 +2523,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
wrong_tables.append(db);
wrong_tables.append('.');
wrong_tables.append(table->table_name);
+ errors++;
}
else
{
@@ -2556,14 +2547,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
err:
if (wrong_tables.length())
{
- if (one_table && was_view)
+ DBUG_ASSERT(errors);
+ if (errors == 1 && was_view)
my_printf_error(ER_IT_IS_A_VIEW, ER(ER_IT_IS_A_VIEW), MYF(0),
wrong_tables.c_ptr_safe());
- else if (!foreign_key_error)
+ else if (errors > 1 || !thd->is_error())
my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
wrong_tables.c_ptr_safe());
- else
- my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
error= 1;
}
@@ -2614,8 +2604,8 @@ err:
/* Chop of the last comma */
built_query.chop();
built_query.append(" /* generated by server */");
- int error_code = (non_tmp_error ?
- (foreign_key_error ? ER_ROW_IS_REFERENCED : ER_BAD_TABLE_ERROR) : 0);
+ int error_code = non_tmp_error ? thd->get_stmt_da()->sql_errno()
+ : 0;
error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
built_query.ptr(),
built_query.length(),
@@ -4687,7 +4677,9 @@ int create_table_impl(THD *thd,
if (create_info->tmp_table())
{
TABLE *tmp_table;
- if ((tmp_table= find_temporary_table(thd, db, table_name)))
+ if (find_and_use_temporary_table(thd, db, table_name, &tmp_table))
+ goto err;
+ if (tmp_table)
{
bool table_creation_was_logged= tmp_table->s->table_creation_was_logged;
if (create_info->options & HA_LEX_CREATE_REPLACE)
@@ -5148,6 +5140,7 @@ mysql_rename_table(handlerton *base, const char *old_db,
ulonglong save_bits= thd->variables.option_bits;
int length;
DBUG_ENTER("mysql_rename_table");
+ DBUG_ASSERT(base);
DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'",
old_db, old_name, new_db, new_name));
@@ -5155,8 +5148,7 @@ mysql_rename_table(handlerton *base, const char *old_db,
if (flags & NO_FK_CHECKS)
thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
- file= (base == NULL ? 0 :
- get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));
+ file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base);
build_table_filename(from, sizeof(from) - 1, old_db, old_name, "",
flags & FN_FROM_IS_TMP);
@@ -5477,7 +5469,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
if (!table->view)
{
int result __attribute__((unused))=
- show_create_table(thd, table, &query, create_info, WITHOUT_DB_NAME);
+ show_create_table(thd, table, &query, create_info, WITH_DB_NAME);
DBUG_ASSERT(result == 0); // show_create_table() always return 0
do_logging= FALSE;
@@ -5858,12 +5850,17 @@ drop_create_field:
/* let us check the name of the first key part. */
if ((keyname= key->name.str) == NULL)
{
- List_iterator<Key_part_spec> part_it(key->columns);
- Key_part_spec *kp;
- if ((kp= part_it++))
- keyname= kp->field_name.str;
- if (keyname == NULL)
- continue;
+ if (key->type == Key::PRIMARY)
+ keyname= primary_key_name;
+ else
+ {
+ List_iterator<Key_part_spec> part_it(key->columns);
+ Key_part_spec *kp;
+ if ((kp= part_it++))
+ keyname= kp->field_name.str;
+ if (keyname == NULL)
+ continue;
+ }
}
if (key->type != Key::FOREIGN_KEY)
{
@@ -8427,6 +8424,23 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
mysql_audit_alter_table(thd, table_list);
THD_STAGE_INFO(thd, stage_setup);
+
+ handle_if_exists_options(thd, table, alter_info);
+
+ /*
+ Look if we have to do anything at all.
+ ALTER can become NOOP after handling
+ the IF (NOT) EXISTS options.
+ */
+ if (alter_info->flags == 0)
+ {
+ my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name),
+ ER(ER_INSERT_INFO), 0L, 0L,
+ thd->get_stmt_da()->current_statement_warn_count());
+ my_ok(thd, 0L, 0L, alter_ctx.tmp_name);
+ DBUG_RETURN(false);
+ }
+
if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME |
Alter_info::ALTER_KEYS_ONOFF)) &&
alter_info->requested_algorithm !=
@@ -8447,22 +8461,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
DBUG_RETURN(res);
}
- handle_if_exists_options(thd, table, alter_info);
-
- /*
- Look if we have to do anything at all.
- Normally ALTER can become NOOP only after handling
- the IF (NOT) EXISTS options.
- */
- if (alter_info->flags == 0)
- {
- my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name),
- ER(ER_INSERT_INFO), 0L, 0L,
- thd->get_stmt_da()->current_statement_warn_count());
- my_ok(thd, 0L, 0L, alter_ctx.tmp_name);
- DBUG_RETURN(false);
- }
-
/* We have to do full alter table. */
#ifdef WITH_PARTITION_STORAGE_ENGINE
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 8992ff24a1e..82abc861ec4 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -220,7 +220,7 @@ TEST_join(JOIN *join)
}
-#define FT_KEYPART (MAX_REF_PARTS+10)
+#define FT_KEYPART (MAX_FIELDS+10)
void print_keyuse(KEYUSE *keyuse)
{
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index fe2ea02a8e2..05869b70c8f 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2010, 2014, Oracle and/or its affiliates.
- Copyright (c) 2013, 2014, SkySQL Ab.
+/* Copyright (c) 2010, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2013, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -283,6 +283,12 @@ static bool recreate_temporary_table(THD *thd, TABLE *table)
table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
+ /*
+ If LOCK TABLES list is not empty and contains this table
+ then unlock the table and remove it from this list.
+ */
+ mysql_lock_remove(thd, thd->lock, table);
+
/* Don't free share. */
close_temporary_table(thd, table, FALSE, FALSE);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 6c4d864e75a..f616549097b 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2011, 2013, Monty Program Ab.
+ Copyright (c) 2011, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1520,7 +1520,7 @@ int mysql_multi_update_prepare(THD *thd)
*/
lex->select_lex.exclude_from_table_unique_test= FALSE;
- if (lex->select_lex.save_prep_leaf_tables(thd))
+ if (lex->save_prep_leaf_tables())
DBUG_RETURN(TRUE);
DBUG_RETURN (FALSE);
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 20a16e3eae1..a63d8a51a86 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2004, 2013, Oracle and/or its affiliates.
- Copyright (c) 2011, 2014, SkySQL Ab.
+ Copyright (c) 2011, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -34,6 +34,7 @@
#include "sp.h"
#include "sp_cache.h"
#include "datadict.h" // dd_frm_is_view()
+#include "sql_derived.h"
#define MD5_BUFF_LENGTH 33
@@ -224,7 +225,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view)
view->definer.user= decoy.definer.user;
lex->definer= &view->definer;
}
- if (lex->create_view_algorithm == DTYPE_ALGORITHM_UNDEFINED)
+ if (lex->create_view_algorithm == VIEW_ALGORITHM_INHERIT)
lex->create_view_algorithm= (uint8) decoy.algorithm;
if (lex->create_view_suid == VIEW_SUID_DEFAULT)
lex->create_view_suid= decoy.view_suid ?
@@ -684,6 +685,26 @@ err:
}
+static void make_view_filename(LEX_STRING *dir, char *dir_buff,
+ size_t dir_buff_len,
+ LEX_STRING *path, char *path_buff,
+ size_t path_buff_len,
+ LEX_STRING *file,
+ TABLE_LIST *view)
+{
+ /* print file name */
+ dir->length= build_table_filename(dir_buff, dir_buff_len - 1,
+ view->db, "", "", 0);
+ dir->str= dir_buff;
+
+ path->length= build_table_filename(path_buff, path_buff_len - 1,
+ view->db, view->table_name, reg_ext, 0);
+ path->str= path_buff;
+
+ file->str= path->str + dir->length;
+ file->length= path->length - dir->length;
+}
+
/* number of required parameters for making view */
static const int required_view_parameters= 15;
@@ -746,6 +767,67 @@ static File_option view_parameters[]=
static LEX_STRING view_file_type[]= {{(char*) STRING_WITH_LEN("VIEW") }};
+int mariadb_fix_view(THD *thd, TABLE_LIST *view, bool wrong_checksum,
+ bool swap_alg)
+{
+ char dir_buff[FN_REFLEN + 1], path_buff[FN_REFLEN + 1];
+ LEX_STRING dir, file, path;
+ DBUG_ENTER("mariadb_fix_view");
+
+ if (!wrong_checksum && view->mariadb_version)
+ DBUG_RETURN(HA_ADMIN_OK);
+
+ make_view_filename(&dir, dir_buff, sizeof(dir_buff),
+ &path, path_buff, sizeof(path_buff),
+ &file, view);
+ /* init timestamp */
+ if (!view->timestamp.str)
+ view->timestamp.str= view->timestamp_buffer;
+
+ if (swap_alg && view->algorithm != VIEW_ALGORITHM_UNDEFINED)
+ {
+ DBUG_ASSERT(view->algorithm == VIEW_ALGORITHM_MERGE ||
+ view->algorithm == VIEW_ALGORITHM_TMPTABLE);
+ if (view->algorithm == VIEW_ALGORITHM_MERGE)
+ view->algorithm= VIEW_ALGORITHM_TMPTABLE;
+ else
+ view->algorithm= VIEW_ALGORITHM_MERGE;
+ }
+ else
+ swap_alg= 0;
+ if (wrong_checksum)
+ {
+ if (view->md5.length != 32)
+ {
+ if ((view->md5.str= (char *)thd->alloc(32 + 1)) == NULL)
+ DBUG_RETURN(HA_ADMIN_FAILED);
+ }
+ view->calc_md5(view->md5.str);
+ view->md5.length= 32;
+ }
+ view->mariadb_version= MYSQL_VERSION_ID;
+
+ if (sql_create_definition_file(&dir, &file, view_file_type,
+ (uchar*)view, view_parameters))
+ {
+ sql_print_error("View '%-.192s'.'%-.192s': algorithm swap error.",
+ view->db, view->table_name);
+ DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR);
+ }
+ sql_print_information("View %`s.%`s: the version is set to %llu%s%s",
+ view->db, view->table_name, view->mariadb_version,
+ (wrong_checksum ? ", checksum corrected" : ""),
+ (swap_alg ?
+ ((view->algorithm == VIEW_ALGORITHM_MERGE) ?
+ ", algorithm restored to be MERGE"
+ : ", algorithm restored to be TEMPTABLE")
+ : ""));
+
+
+ DBUG_RETURN(HA_ADMIN_OK);
+}
+
+
/*
Register VIEW (write .frm & process .frm's history backups)
@@ -886,17 +968,9 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
}
loop_out:
/* print file name */
- dir.length= build_table_filename(dir_buff, sizeof(dir_buff) - 1,
- view->db, "", "", 0);
- dir.str= dir_buff;
-
- path.length= build_table_filename(path_buff, sizeof(path_buff) - 1,
- view->db, view->table_name, reg_ext, 0);
- path.str= path_buff;
-
- file.str= path.str + dir.length;
- file.length= path.length - dir.length;
-
+ make_view_filename(&dir, dir_buff, sizeof(dir_buff),
+ &path, path_buff, sizeof(path_buff),
+ &file, view);
/* init timestamp */
if (!view->timestamp.str)
view->timestamp.str= view->timestamp_buffer;
@@ -1022,7 +1096,7 @@ err:
SYNOPSIS
mysql_make_view()
- thd Thread handler
+ thd Thread handle
parser parser object
table TABLE_LIST structure for filling
flags flags
@@ -1063,6 +1137,15 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
DBUG_PRINT("info",
("VIEW %s.%s is already processed on previous PS/SP execution",
table->view_db.str, table->view_name.str));
+
+ /*
+ Clear old variables in the TABLE_LIST that could be left from an old view
+ This is only needed if there was an error at last usage of view,
+ in which case the reinit call wasn't done.
+ See MDEV-6668 for details.
+ */
+ mysql_derived_reinit(thd, NULL, table);
+
DBUG_RETURN(0);
}
@@ -1599,7 +1682,7 @@ err:
SYNOPSIS
mysql_drop_view()
- thd - thread handler
+ thd - thread handle
views - views to delete
drop_mode - cascade/check
@@ -1716,7 +1799,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
SYNOPSIS
check_key_in_view()
- thd thread handler
+ thd thread handle
view view for check with opened table
DESCRIPTION
@@ -1902,6 +1985,58 @@ int view_checksum(THD *thd, TABLE_LIST *view)
HA_ADMIN_OK);
}
+/**
+ Check view
+
+ @param thd thread handle
+ @param view view for check
+ @param check_opt check options
+
+ @retval HA_ADMIN_OK OK
+ @retval HA_ADMIN_NOT_IMPLEMENTED it is not VIEW
+ @retval HA_ADMIN_WRONG_CHECKSUM check sum is wrong
+*/
+int view_check(THD *thd, TABLE_LIST *view, HA_CHECK_OPT *check_opt)
+{
+ DBUG_ENTER("view_check");
+
+ int res= view_checksum(thd, view);
+ if (res != HA_ADMIN_OK)
+ DBUG_RETURN(res);
+
+ if (((check_opt->sql_flags & TT_FOR_UPGRADE) && !view->mariadb_version))
+ DBUG_RETURN(HA_ADMIN_NEEDS_UPGRADE);
+
+ DBUG_RETURN(HA_ADMIN_OK);
+}
+
+
+/**
+ Repair view
+
+ @param thd thread handle
+ @param view view for check
+ @param check_opt check options
+
+ @retval HA_ADMIN_OK OK
+ @retval HA_ADMIN_NOT_IMPLEMENTED it is not VIEW
+ @retval HA_ADMIN_WRONG_CHECKSUM check sum is wrong
+*/
+
+int view_repair(THD *thd, TABLE_LIST *view, HA_CHECK_OPT *check_opt)
+{
+ DBUG_ENTER("view_repair");
+ bool swap_alg= (check_opt->sql_flags & TT_FROM_MYSQL);
+ bool wrong_checksum= view_checksum(thd, view) != HA_ADMIN_OK;
+ int ret;
+ if (wrong_checksum || swap_alg || (!view->mariadb_version))
+ {
+ ret= mariadb_fix_view(thd, view, wrong_checksum, swap_alg);
+ DBUG_RETURN(ret);
+ }
+ DBUG_RETURN(HA_ADMIN_OK);
+}
+
/*
rename view
diff --git a/sql/sql_view.h b/sql/sql_view.h
index abe95c63e6e..8d733a1867c 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -2,7 +2,8 @@
#define SQL_VIEW_INCLUDED
/* -*- C++ -*- */
-/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2004, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,6 +46,8 @@ bool check_key_in_view(THD *thd, TABLE_LIST * view);
bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view);
int view_checksum(THD *thd, TABLE_LIST *view);
+int view_check(THD *thd, TABLE_LIST *view, HA_CHECK_OPT *check_opt);
+int view_repair(THD *thd, TABLE_LIST *view, HA_CHECK_OPT *check_opt);
extern TYPELIB updatable_views_with_limit_typelib;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 74426f88a69..d980de7e1a5 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2010, 2014, Monty Program Ab.
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -63,6 +63,7 @@
#include "keycaches.h"
#include "set_var.h"
#include "rpl_mi.h"
+#include "lex_token.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER
@@ -1331,6 +1332,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MULTIPOINT
%token MULTIPOLYGON
%token MUTEX_SYM
+%token MYSQL_SYM
%token MYSQL_ERRNO_SYM
%token NAMES_SYM /* SQL-2003-N */
%token NAME_SYM /* SQL-2003-N */
@@ -5803,18 +5805,8 @@ create_table_option:
default_charset:
opt_default charset opt_equal charset_name_or_default
{
- HA_CREATE_INFO *cinfo= &Lex->create_info;
- if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
- cinfo->default_table_charset && $4 &&
- !my_charset_same(cinfo->default_table_charset,$4))
- {
- my_error(ER_CONFLICTING_DECLARATIONS, MYF(0),
- "CHARACTER SET ", cinfo->default_table_charset->csname,
- "CHARACTER SET ", $4->csname);
+ if (Lex->create_info.add_table_option_default_charset($4))
MYSQL_YYABORT;
- }
- Lex->create_info.default_table_charset= $4;
- Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
}
;
@@ -7028,6 +7020,7 @@ alter:
{
Lex->name.str= 0;
Lex->name.length= 0;
+ Lex->only_view= FALSE;
Lex->sql_command= SQLCOM_ALTER_TABLE;
Lex->duplicates= DUP_ERROR;
Lex->col_list.empty();
@@ -7149,7 +7142,7 @@ alter:
my_error(ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW");
MYSQL_YYABORT;
}
- lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED;
+ lex->create_view_algorithm= VIEW_ALGORITHM_INHERIT;
lex->create_view_mode= VIEW_ALTER;
}
view_tail
@@ -7646,10 +7639,8 @@ alter_list_item:
MYSQL_YYABORT;
}
LEX *lex= Lex;
- lex->create_info.table_charset=
- lex->create_info.default_table_charset= $5;
- lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET |
- HA_CREATE_USED_DEFAULT_CHARSET);
+ if (lex->create_info.add_alter_list_item_convert_to_charset($5))
+ MYSQL_YYABORT;
lex->alter_info.flags|= Alter_info::ALTER_CONVERT;
}
| create_table_options_space_separated
@@ -7921,8 +7912,13 @@ opt_checksum_type:
| EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; }
;
+repair_table_or_view:
+ table_or_tables table_list opt_mi_repair_type
+ | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_repair_type
+ ;
+
repair:
- REPAIR opt_no_write_to_binlog table_or_tables
+ REPAIR opt_no_write_to_binlog
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_REPAIR;
@@ -7932,7 +7928,7 @@ repair:
/* Will be overriden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
}
- table_list opt_mi_repair_type
+ repair_table_or_view
{
LEX* lex= thd->lex;
DBUG_ASSERT(!lex->m_sql_cmd);
@@ -7958,6 +7954,11 @@ mi_repair_type:
| USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; }
;
+opt_view_repair_type:
+ /* empty */ { }
+ | FROM MYSQL_SYM { Lex->check_opt.sql_flags|= TT_FROM_MYSQL; }
+ ;
+
analyze:
ANALYZE_SYM opt_no_write_to_binlog table_or_tables
{
@@ -8074,25 +8075,29 @@ binlog_base64_event:
}
;
-check:
- CHECK_SYM table_or_tables
+check_view_or_table:
+ table_or_tables table_list opt_mi_check_type
+ | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_check_type
+ ;
+
+check: CHECK_SYM
{
LEX *lex=Lex;
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK");
- MYSQL_YYABORT;
- }
lex->sql_command = SQLCOM_CHECK;
lex->check_opt.init();
lex->alter_info.reset();
/* Will be overriden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
}
- table_list opt_mi_check_type
+ check_view_or_table
{
LEX* lex= thd->lex;
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK");
+ MYSQL_YYABORT;
+ }
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_check_table();
if (lex->m_sql_cmd == NULL)
@@ -8119,6 +8124,11 @@ mi_check_type:
| FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; }
;
+opt_view_check_type:
+ /* empty */ { }
+ | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; }
+ ;
+
optimize:
OPTIMIZE opt_no_write_to_binlog table_or_tables
{
@@ -10105,7 +10115,8 @@ udf_expr:
parse it out. If we hijack the input stream with
remember_name we may get quoted or escaped names.
*/
- else if ($2->type() != Item::FIELD_ITEM)
+ else if ($2->type() != Item::FIELD_ITEM &&
+ $2->type() != Item::REF_ITEM /* For HAVING */ )
$2->set_name($1, (uint) ($3 - $1), thd->charset());
$$= $2;
}
@@ -12529,7 +12540,6 @@ show_param:
lex->sql_command = SQLCOM_SHOW_CREATE;
if (!lex->select_lex.add_table_to_list(thd, $3, NULL,0))
MYSQL_YYABORT;
- lex->only_view= 0;
lex->create_info.storage_media= HA_SM_DEFAULT;
}
| CREATE VIEW_SYM table_ident
@@ -13360,6 +13370,13 @@ literal:
| temporal_literal { $$= $1; }
| NULL_SYM
{
+ /*
+ For the digest computation, in this context only,
+ NULL is considered a literal, hence reduced to '?'
+ REDUCE:
+ TOK_GENERIC_VALUE := NULL_SYM
+ */
+ YYLIP->reduce_digest_token(TOK_GENERIC_VALUE, NULL_SYM);
$$ = new (thd->mem_root) Item_null();
if ($$ == NULL)
MYSQL_YYABORT;
@@ -13398,7 +13415,12 @@ literal:
| UNDERSCORE_CHARSET hex_or_bin_String
{
Item_string_with_introducer *item_str;
- item_str= new (thd->mem_root) Item_string_with_introducer($2, $1);
+ /*
+ Pass NULL as name. Name will be set in the "select_item" rule and
+ will include the introducer and the original hex/bin notation.
+ */
+ item_str= new (thd->mem_root)
+ Item_string_with_introducer(NULL, $2->ptr(), $2->length(), $1);
if (!item_str || !item_str->check_well_formed_result(true))
MYSQL_YYABORT;
@@ -14206,6 +14228,7 @@ keyword_sp:
| MULTIPOINT {}
| MULTIPOLYGON {}
| MUTEX_SYM {}
+ | MYSQL_SYM {}
| MYSQL_ERRNO_SYM {}
| NAME_SYM {}
| NAMES_SYM {}
@@ -14834,8 +14857,8 @@ lock:
;
table_or_tables:
- TABLE_SYM {}
- | TABLES {}
+ TABLE_SYM { }
+ | TABLES { }
;
table_lock_list:
@@ -15739,6 +15762,13 @@ subselect_end:
*/
lex->current_select->select_n_where_fields+=
child->select_n_where_fields;
+
+ /*
+ Aggregate functions in having clause may add fields to an outer
+ select. Count them also.
+ */
+ lex->current_select->select_n_having_items+=
+ child->select_n_having_items;
}
;
diff --git a/sql/structs.h b/sql/structs.h
index 2de7abb666d..ee61b8d3b3a 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -77,6 +77,7 @@ typedef struct st_key_part_info { /* Info about a key part */
*/
uint16 store_length;
uint16 key_type;
+ /* Fieldnr begins counting from 1 */
uint16 fieldnr; /* Fieldnum in UNIREG */
uint16 key_part_flag; /* 0 or HA_REVERSE_SORT */
uint8 type;
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index f1942d08591..94466db5fd9 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2002, 2014, Oracle and/or its affiliates.
- Copyright (c) 2012, 2014, SkySQL Ab.
+/* Copyright (c) 2002, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2012, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1313,6 +1313,12 @@ static Sys_var_ulong Sys_max_connect_errors(
VALID_RANGE(1, UINT_MAX), DEFAULT(MAX_CONNECT_ERRORS),
BLOCK_SIZE(1));
+static Sys_var_long Sys_max_digest_length(
+ "max_digest_length", "Maximum length considered for digest text.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(max_digest_length),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 1024 * 1024), DEFAULT(1024), BLOCK_SIZE(1));
+
static bool check_max_delayed_threads(sys_var *self, THD *thd, set_var *var)
{
return var->type != OPT_GLOBAL &&
@@ -1752,16 +1758,12 @@ check_slave_parallel_threads(sys_var *self, THD *thd, set_var *var)
static bool
fix_slave_parallel_threads(sys_var *self, THD *thd, enum_var_type type)
{
- bool running;
- bool err= false;
+ bool err;
mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- running= master_info_index->give_error_if_slave_running();
+ err= master_info_index->give_error_if_slave_running();
mysql_mutex_unlock(&LOCK_active_mi);
- if (running || rpl_parallel_change_thread_count(&global_rpl_thread_pool,
- opt_slave_parallel_threads))
- err= true;
mysql_mutex_lock(&LOCK_global_system_variables);
return err;
@@ -2528,7 +2530,7 @@ static Sys_var_ulong Sys_trans_alloc_block_size(
"transaction_alloc_block_size",
"Allocation block size for transactions to be stored in binary log",
SESSION_VAR(trans_alloc_block_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1024, UINT_MAX), DEFAULT(QUERY_ALLOC_BLOCK_SIZE),
+ VALID_RANGE(1024, 128 * 1024 * 1024), DEFAULT(QUERY_ALLOC_BLOCK_SIZE),
BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_trans_mem_root));
@@ -2536,7 +2538,7 @@ static Sys_var_ulong Sys_trans_prealloc_size(
"transaction_prealloc_size",
"Persistent buffer for transactions to be stored in binary log",
SESSION_VAR(trans_prealloc_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1024, UINT_MAX), DEFAULT(TRANS_ALLOC_PREALLOC_SIZE),
+ VALID_RANGE(1024, 128 * 1024 * 1024), DEFAULT(TRANS_ALLOC_PREALLOC_SIZE),
BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_trans_mem_root));
diff --git a/sql/table.cc b/sql/table.cc
index 321c2a96b4f..053269ab435 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, SkySQL Ab.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -2927,7 +2927,9 @@ partititon_err:
outparam->no_replicate= FALSE;
}
- thd->status_var.opened_tables++;
+ /* Increment the opened_tables counter, only when open flags set. */
+ if (db_stat)
+ thd->status_var.opened_tables++;
thd->lex->context_analysis_only= save_context_analysis_only;
DBUG_RETURN (OPEN_FRM_OK);
@@ -4728,23 +4730,26 @@ bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg,
bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root)
{
+ DBUG_ENTER("set_insert_values");
if (table)
{
+ DBUG_PRINT("info", ("setting insert_value for table"));
if (!table->insert_values &&
!(table->insert_values= (uchar *)alloc_root(mem_root,
table->s->rec_buff_length)))
- return TRUE;
+ DBUG_RETURN(TRUE);
}
else
{
+ DBUG_PRINT("info", ("setting insert_value for view"));
DBUG_ASSERT(is_view_or_derived() && is_merged_derived());
for (TABLE_LIST *tbl= (TABLE_LIST*)view->select_lex.table_list.first;
tbl;
tbl= tbl->next_local)
if (tbl->set_insert_values(mem_root))
- return TRUE;
+ DBUG_RETURN(TRUE);
}
- return FALSE;
+ DBUG_RETURN(FALSE);
}
@@ -5106,7 +5111,8 @@ TABLE *TABLE_LIST::get_real_join_table()
TABLE_LIST *tbl= this;
while (tbl->table == NULL || tbl->table->reginfo.join_tab == NULL)
{
- if (tbl->view == NULL && tbl->derived == NULL)
+ if ((tbl->view == NULL && tbl->derived == NULL) ||
+ tbl->is_materialized_derived())
break;
/* we do not support merging of union yet */
DBUG_ASSERT(tbl->view == NULL ||
@@ -5115,28 +5121,25 @@ TABLE *TABLE_LIST::get_real_join_table()
tbl->derived->first_select()->next_select() == NULL);
{
- List_iterator_fast<TABLE_LIST> ti;
+ List_iterator_fast<TABLE_LIST>
+ ti(tbl->view != NULL ?
+ tbl->view->select_lex.top_join_list :
+ tbl->derived->first_select()->top_join_list);
+ for (;;)
{
- List_iterator_fast<TABLE_LIST>
- ti(tbl->view != NULL ?
- tbl->view->select_lex.top_join_list :
- tbl->derived->first_select()->top_join_list);
- for (;;)
- {
- tbl= NULL;
- /*
- Find left table in outer join on this level
- (the list is reverted).
- */
- for (TABLE_LIST *t= ti++; t; t= ti++)
- tbl= t;
- if (!tbl)
- return NULL; // view/derived with no tables
- if (!tbl->nested_join)
- break;
- /* go deeper if we've found nested join */
- ti= tbl->nested_join->join_list;
- }
+ tbl= NULL;
+ /*
+ Find left table in outer join on this level
+ (the list is reverted).
+ */
+ for (TABLE_LIST *t= ti++; t; t= ti++)
+ tbl= t;
+ if (!tbl)
+ return NULL; // view/derived with no tables
+ if (!tbl->nested_join)
+ break;
+ /* go deeper if we've found nested join */
+ ti= tbl->nested_join->join_list;
}
}
}
@@ -6085,16 +6088,19 @@ bool TABLE::alloc_keys(uint key_count)
}
-/*
- Given a field, fill key_part_info
- @param keyinfo Key to where key part is added (we will
- only adjust key_length there)
- @param field IN Table field for which key part is needed
- @param key_part_info OUT key part structure to be filled.
- @param fieldnr Field's number.
+/**
+ @brief
+ Populate a KEY_PART_INFO structure with the data related to a field entry.
+
+ @param key_part_info The structure to fill.
+ @param field The field entry that represents the key part.
+ @param fleldnr The number of the field, count starting from 1.
+
+ TODO: This method does not make use of any table specific fields. It
+ could be refactored to act as a constructor for KEY_PART_INFO instead.
*/
-void TABLE::create_key_part_by_field(KEY *keyinfo,
- KEY_PART_INFO *key_part_info,
+
+void TABLE::create_key_part_by_field(KEY_PART_INFO *key_part_info,
Field *field, uint fieldnr)
{
DBUG_ASSERT(field->field_index + 1 == (int)fieldnr);
@@ -6104,8 +6110,11 @@ void TABLE::create_key_part_by_field(KEY *keyinfo,
key_part_info->field= field;
key_part_info->fieldnr= fieldnr;
key_part_info->offset= field->offset(record[0]);
- key_part_info->length= (uint16) field->pack_length();
- keyinfo->key_length+= key_part_info->length;
+ /*
+ field->key_length() accounts for the raw length of the field, excluding
+ any metadata such as length of field or the NULL flag.
+ */
+ key_part_info->length= (uint16) field->key_length();
key_part_info->key_part_flag= 0;
/* TODO:
The below method of computing the key format length of the
@@ -6117,17 +6126,20 @@ void TABLE::create_key_part_by_field(KEY *keyinfo,
*/
key_part_info->store_length= key_part_info->length;
+ /*
+ The total store length of the key part is the raw length of the field +
+ any metadata information, such as its length for strings and/or the null
+ flag.
+ */
if (field->real_maybe_null())
{
key_part_info->store_length+= HA_KEY_NULL_LENGTH;
- keyinfo->key_length+= HA_KEY_NULL_LENGTH;
}
if (field->type() == MYSQL_TYPE_BLOB ||
field->type() == MYSQL_TYPE_GEOMETRY ||
field->real_type() == MYSQL_TYPE_VARCHAR)
{
key_part_info->store_length+= HA_KEY_BLOB_LENGTH;
- keyinfo->key_length+= HA_KEY_BLOB_LENGTH; // ???
key_part_info->key_part_flag|=
field->type() == MYSQL_TYPE_BLOB ? HA_BLOB_PART: HA_VAR_LENGTH_PART;
}
@@ -6253,7 +6265,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
if (key_start)
(*reg_field)->key_start.set_bit(key);
(*reg_field)->part_of_key.set_bit(key);
- create_key_part_by_field(keyinfo, key_part_info, *reg_field, fld_idx+1);
+ create_key_part_by_field(key_part_info, *reg_field, fld_idx+1);
+ keyinfo->key_length += key_part_info->store_length;
(*reg_field)->flags|= PART_KEY_FLAG;
key_start= FALSE;
key_part_info++;
@@ -6897,15 +6910,16 @@ void TABLE_LIST::reset_const_table()
bool TABLE_LIST::handle_derived(LEX *lex, uint phases)
{
- SELECT_LEX_UNIT *unit= get_unit();
- if (unit)
+ SELECT_LEX_UNIT *unit;
+ DBUG_ENTER("handle_derived");
+ if ((unit= get_unit()))
{
for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
if (sl->handle_derived(lex, phases))
- return TRUE;
- return mysql_handle_single_derived(lex, this, phases);
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(mysql_handle_single_derived(lex, this, phases));
}
- return FALSE;
+ DBUG_RETURN(FALSE);
}
diff --git a/sql/table.h b/sql/table.h
index b978484158b..39faa8b9765 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1340,7 +1340,7 @@ public:
bool add_tmp_key(uint key, uint key_parts,
uint (*next_field_no) (uchar *), uchar *arg,
bool unique);
- void create_key_part_by_field(KEY *keyinfo, KEY_PART_INFO *key_part_info,
+ void create_key_part_by_field(KEY_PART_INFO *key_part_info,
Field *field, uint fieldnr);
void use_index(int key_to_save);
void set_table_map(table_map map_arg, uint tablenr_arg)
@@ -1374,6 +1374,10 @@ public:
}
bool update_const_key_parts(COND *conds);
+
+ my_ptrdiff_t default_values_offset() const
+ { return (my_ptrdiff_t) (s->default_values - record[0]); }
+
uint actual_n_key_parts(KEY *keyinfo);
ulong actual_key_flags(KEY *keyinfo);
int update_default_fields();
@@ -1537,6 +1541,8 @@ typedef struct st_schema_table
#define DT_PHASES_MATERIALIZE (DT_COMMON | DT_MATERIALIZE)
#define VIEW_ALGORITHM_UNDEFINED 0
+/* Special value for ALTER VIEW: inherit original algorithm. */
+#define VIEW_ALGORITHM_INHERIT DTYPE_VIEW
#define VIEW_ALGORITHM_MERGE (DTYPE_VIEW | DTYPE_MERGE)
#define VIEW_ALGORITHM_TMPTABLE (DTYPE_VIEW | DTYPE_MATERIALIZE)
@@ -2079,6 +2085,24 @@ struct TABLE_LIST
TABLE_LIST *find_underlying_table(TABLE *table);
TABLE_LIST *first_leaf_for_name_resolution();
TABLE_LIST *last_leaf_for_name_resolution();
+ /**
+ @brief
+ Find the bottom in the chain of embedded table VIEWs.
+
+ @detail
+ This is used for single-table UPDATE/DELETE when they are modifying a
+ single-table VIEW.
+ */
+ TABLE_LIST *find_table_for_update()
+ {
+ TABLE_LIST *tbl= this;
+ while(!tbl->is_multitable() && tbl->single_table_updatable() &&
+ tbl->merge_underlying_list)
+ {
+ tbl= tbl->merge_underlying_list;
+ }
+ return tbl;
+ }
TABLE *get_real_join_table();
bool is_leaf_for_name_resolution();
inline TABLE_LIST *top_table()
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 51d8a08e981..a70c075e142 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -149,6 +149,8 @@ bool trans_begin(THD *thd, uint flags)
when we come here. We should at some point change this to an assert.
*/
thd->transaction.all.modified_non_trans_table= FALSE;
+ thd->has_waiter= false;
+ thd->waiting_on_group_commit= false;
if (res)
DBUG_RETURN(TRUE);
diff --git a/sql/tztime.cc b/sql/tztime.cc
index d3b4fec6335..6486e9b2018 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -2523,7 +2523,8 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level, uint verbose)
for (i= 0; i < cur_dir->number_of_files; i++)
{
- if (cur_dir->dir_entry[i].name[0] != '.')
+ if (cur_dir->dir_entry[i].name[0] != '.' &&
+ strcmp(cur_dir->dir_entry[i].name, "Factory"))
{
name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
FN_REFLEN - (name_end - fullname));
diff --git a/sql/udf_example.c b/sql/udf_example.c
index 36a5eafb704..a48801d1c4a 100644
--- a/sql/udf_example.c
+++ b/sql/udf_example.c
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2014, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -296,9 +296,12 @@ char *metaphon(UDF_INIT *initid __attribute__((unused)),
if (!word) /* Null argument */
{
+ /* The length is expected to be zero when the argument is NULL. */
+ assert(args->lengths[0] == 0);
*is_null=1;
return 0;
}
+
w_end=word+args->lengths[0];
org_result=result;
diff --git a/sql/uniques.cc b/sql/uniques.cc
index 912a38f8927..c755293035b 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2001, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -100,7 +101,7 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
max_elements= (ulong) (max_in_memory_size /
ALIGN_SIZE(sizeof(TREE_ELEMENT)+size));
(void) open_cached_file(&file, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE,
- MYF(MY_WME));
+ MYF(MY_WME));
}
@@ -641,8 +642,8 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
return 1;
if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0))
return 1;
- ulong buff_sz= (max_in_memory_size / full_size + 1) * full_size;
- if (!(merge_buffer= (uchar *) my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC))))
+ size_t buff_sz= (max_in_memory_size / full_size + 1) * full_size;
+ if (!(merge_buffer = (uchar *)my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC|MY_WME))))
return 1;
if (buff_sz < full_size * (file_ptrs.elements + 1UL))
res= merge(table, merge_buffer, buff_sz >= full_size * MERGEBUFF2) ;
@@ -773,9 +774,8 @@ bool Unique::get(TABLE *table)
/* Not enough memory; Save the result to file && free memory used by tree */
if (flush())
return 1;
-
- ulong buff_sz= (max_in_memory_size / full_size + 1) * full_size;
- if (!(sort_buffer= (uchar*) my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC))))
+ size_t buff_sz= (max_in_memory_size / full_size + 1) * full_size;
+ if (!(sort_buffer= (uchar*) my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC|MY_WME))))
return 1;
if (merge(table, sort_buffer, FALSE))
diff --git a/sql/winservice.h b/sql/winservice.h
index fca7b129de5..c3e2bfe1b20 100644
--- a/sql/winservice.h
+++ b/sql/winservice.h
@@ -1,40 +1,40 @@
-/*
- Copyright (c) 2011, 2012, Monty Program Ab
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/*
- Extract properties of a windows service binary path
-*/
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <windows.h>
-typedef struct mysqld_service_properties_st
-{
- char mysqld_exe[MAX_PATH];
- char inifile[MAX_PATH];
- char datadir[MAX_PATH];
- int version_major;
- int version_minor;
- int version_patch;
-} mysqld_service_properties;
-
-extern int get_mysql_service_properties(const wchar_t *bin_path,
- mysqld_service_properties *props);
-
-#ifdef __cplusplus
-}
-#endif
+/*
+ Copyright (c) 2011, 2012, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Extract properties of a windows service binary path
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <windows.h>
+typedef struct mysqld_service_properties_st
+{
+ char mysqld_exe[MAX_PATH];
+ char inifile[MAX_PATH];
+ char datadir[MAX_PATH];
+ int version_major;
+ int version_minor;
+ int version_patch;
+} mysqld_service_properties;
+
+extern int get_mysql_service_properties(const wchar_t *bin_path,
+ mysqld_service_properties *props);
+
+#ifdef __cplusplus
+}
+#endif