diff --git a/.travis.yml b/.travis.yml index d850a0d681e..24ccd522868 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ addons: - libpam0g-dev - libreadline-gplv2-dev - libssl-dev + - libnuma-dev - lsb-release - perl - po-debconf diff --git a/CMakeLists.txt b/CMakeLists.txt index e461141acc8..d0e5cf87475 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,6 +162,7 @@ INCLUDE(install_macros) INCLUDE(systemd) INCLUDE(mysql_add_executable) INCLUDE(crc32-vpmsum) +INCLUDE(numa) # Handle options OPTION(DISABLE_SHARED @@ -356,6 +357,7 @@ IF(WITH_UNIT_TESTS) ADD_SUBDIRECTORY(unittest/examples) ADD_SUBDIRECTORY(unittest/mysys) ADD_SUBDIRECTORY(unittest/my_decimal) + ADD_SUBDIRECTORY(unittest/json_lib) IF(NOT WITHOUT_SERVER) ADD_SUBDIRECTORY(unittest/sql) ENDIF() diff --git a/cmake/numa.cmake b/cmake/numa.cmake new file mode 100644 index 00000000000..d5234a5ef4f --- /dev/null +++ b/cmake/numa.cmake @@ -0,0 +1,38 @@ +MACRO (MYSQL_CHECK_NUMA) + + CHECK_INCLUDE_FILES(numa.h HAVE_NUMA_H) + CHECK_INCLUDE_FILES(numaif.h HAVE_NUMAIF_H) + + IF(HAVE_NUMA_H AND HAVE_NUMAIF_H) + OPTION(WITH_NUMA "Explicitly set NUMA memory allocation policy" ON) + ELSE() + OPTION(WITH_NUMA "Explicitly set NUMA memory allocation policy" OFF) + ENDIF() + + IF(WITH_NUMA AND HAVE_NUMA_H AND HAVE_NUMAIF_H) + SET(SAVE_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} numa) + CHECK_C_SOURCE_COMPILES( + " + #include + #include + int main() + { + struct bitmask *all_nodes= numa_all_nodes_ptr; + set_mempolicy(MPOL_DEFAULT, 0, 0); + return all_nodes != NULL; + }" + HAVE_LIBNUMA) + SET(CMAKE_REQUIRED_LIBRARIES ${SAVE_CMAKE_REQUIRED_LIBRARIES}) + ELSE() + SET(HAVE_LIBNUMA 0) + ENDIF() + + IF(WITH_NUMA AND NOT HAVE_LIBNUMA) + # Forget it in cache, abort the build. + UNSET(WITH_NUMA CACHE) + MESSAGE(FATAL_ERROR "Could not find numa headers/libraries") + ENDIF() + +ENDMACRO() + diff --git a/config.h.cmake b/config.h.cmake index 00aa03483ee..08b0422a520 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -106,6 +106,7 @@ #cmakedefine HAVE_LIBWRAP 1 #cmakedefine HAVE_SYSTEMD 1 #cmakedefine HAVE_CRC32_VPMSUM 1 +#cmakedefine HAVE_LIBNUMA 1 /* Does "struct timespec" have a "sec" and "nsec" field? */ #cmakedefine HAVE_TIMESPEC_TS_SEC 1 diff --git a/debian/control b/debian/control index 0c34ae61b7e..1941b84c148 100644 --- a/debian/control +++ b/debian/control @@ -19,6 +19,7 @@ Build-Depends: bison, libreadline-gplv2-dev, libssl-dev, libxml2-dev, + libnuma-dev, lsb-release, perl (>= 5.6.0), po-debconf, diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 8ebd72b9c58..e6de8515fdc 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -61,6 +61,7 @@ SET(HEADERS my_compiler.h handler_state.h handler_ername.h + json_lib.h ) INSTALL(FILES ${HEADERS} DESTINATION ${INSTALL_INCLUDEDIR} COMPONENT Development) diff --git a/include/json_lib.h b/include/json_lib.h new file mode 100644 index 00000000000..d9e3bb7c624 --- /dev/null +++ b/include/json_lib.h @@ -0,0 +1,356 @@ +#ifndef JSON_LIB_INCLUDED +#define JSON_LIB_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_DEPTH_LIMIT 32 + +/* + When error happens, the c_next of the JSON engine contains the + character that caused the error, and the c_str is the position + in string where the error occurs. +*/ +enum json_errors { + JE_BAD_CHR= -1, /* Invalid character, charset handler cannot read it. */ + + JE_NOT_JSON_CHR= -2, /* Character met not used in JSON. */ + /* ASCII 00-08 for instance. */ + + JE_EOS= -3, /* Unexpected end of string. */ + + JE_SYN= -4, /* The next character breaks the JSON syntax. */ + + JE_STRING_CONST= -5, /* Character disallowed in string constant. */ + + JE_ESCAPING= -6, /* Error in the escaping. */ + + JE_DEPTH= -7, /* The limit on the JSON depth was overrun. */ +}; + + +typedef struct st_json_string_t +{ + const uchar *c_str; /* Current position in JSON string */ + const uchar *str_end; /* The end on the string. */ + my_wc_t c_next; /* UNICODE of the last read character */ + int error; /* error code. */ + + CHARSET_INFO *cs; /* Character set of the JSON string. */ + + my_charset_conv_mb_wc wc; /* UNICODE conversion function. */ + /* It's taken out of the cs just to speed calls. */ +} json_string_t; + + +void json_string_set_cs(json_string_t *s, CHARSET_INFO *i_cs); +void json_string_set_str(json_string_t *s, + const uchar *str, const uchar *end); +#define json_next_char(j) \ + (j)->wc((j)->cs, &(j)->c_next, (j)->c_str, (j)->str_end) +#define json_eos(j) ((j)->c_str >= (j)->str_end) +/* + read_string_const_chr() reads the next character of the string constant + and saves it to the js->c_next. + It takes into account possible escapings, so if for instance + the string is '\b', the read_string_const_chr() sets 8. +*/ +int json_read_string_const_chr(json_string_t *js); + + +/* + Various JSON-related operations expect JSON path as a parameter. + The path is a string like this "$.keyA[2].*" + The path itself is a number of steps specifying either a key or a position + in an array. Some of them can be wildcards. + So the representation of the JSON path is the json_path_t class + containing an array of json_path_step_t objects. +*/ + + +enum json_path_step_types +{ + JSON_PATH_KEY=0, + JSON_PATH_ARRAY=1 +}; + + +typedef struct st_json_path_step_t +{ + enum json_path_step_types type; /* The type of the step - KEY or ARRAY */ + int wild; /* If the step is a wildcard */ + const uchar *key; /* Pointer to the beginning of the key. */ + const uchar *key_end; /* Pointer to the end of the key. */ + uint n_item; /* Item number in an array. No meaning for the key step. */ +} json_path_step_t; + + +typedef struct st_json_path_t +{ + json_string_t s; /* The string to be parsed. */ + json_path_step_t steps[JSON_DEPTH_LIMIT]; /* Steps of the path. */ + json_path_step_t *last_step; /* Points to the last step. */ + + int mode_strict; /* TRUE if the path specified as 'strict' */ +} json_path_t; + + +int json_path_setup(json_path_t *p, + CHARSET_INFO *i_cs, const uchar *str, const uchar *end); + + +/* + The set of functions and structures below provides interface + to the JSON text parser. + Running the parser normally goes like this: + + json_engine_t j_eng; // structure keeps parser's data + json_scan_start(j_eng) // begin the parsing + + do + { + // The parser has read next piece of JSON + // and set fields of j_eng structure accordingly. + // So let's see what we have: + switch (j_eng.state) + { + case JST_KEY: + // Handle key name. See the json_read_keyname_chr() + // Probably compare it with the keyname we're looking for + case JST_VALUE: + // Handle value. It is either value of the key or an array item. + // see the json_read_value() + case JST_OBJ_START: + // parser found an object (the '{' in JSON) + case JST_OBJ_END: + // parser found the end of the object (the '}' in JSON) + case JST_ARRAY_START: + // parser found an array (the '[' in JSON) + case JST_ARRAY_END: + // parser found the end of the array (the ']' in JSON) + + }; + } while (json_scan_next() == 0); // parse next structure + + + if (j_eng.s.error) // we need to check why the loop ended. + // Did we get to the end of JSON, or came upon error. + { + signal_error_in_JSON() + } + + + Parts of JSON can be quickly skipped. If we are not interested + in a particular key, we can just skip it with json_skip_key() call. + Similarly json_skip_level() goes right to the end of an object + or an array. +*/ + + +/* These are JSON parser states that user can expect and handle. */ +enum json_states { + JST_VALUE, /* value found */ + JST_KEY, /* key found */ + JST_OBJ_START, /* object */ + JST_OBJ_END, /* object ended */ + JST_ARRAY_START, /* array */ + JST_ARRAY_END, /* array ended */ + NR_JSON_USER_STATES +}; + + +enum json_value_types +{ + JSON_VALUE_OBJECT=0, + JSON_VALUE_ARRAY=1, + JSON_VALUE_STRING, + JSON_VALUE_NUMBER, + JSON_VALUE_TRUE, + JSON_VALUE_FALSE, + JSON_VALUE_NULL +}; + + +typedef struct st_json_engine_t +{ + json_string_t s; /* String to parse. */ + int sav_c_len; /* Length of the current character. + Can be more than 1 for multibyte charsets */ + + int state; /* The state of the parser. One of 'enum json_states'. + It tells us what construction of JSON we've just read. */ + + /* These values are only set after the json_read_value() call. */ + enum json_value_types value_type; /* type of the value.*/ + const uchar *value; /* Points to the value. */ + const uchar *value_begin;/* Points to where the value starts in the JSON. */ + + /* + In most cases the 'value' and 'value_begin' are equal. + They only differ if the value is a string constants. Then 'value_begin' + points to the starting quotation mark, while the 'value' - to + the first character of the string. + */ + + const uchar *value_end; /* Points to the next character after the value. */ + int value_len; /* The length of the value. Does not count quotations for */ + /* string constants. */ + + int stack[JSON_DEPTH_LIMIT]; /* Keeps the stack of nested JSON structures. */ + int *stack_p; /* The 'stack' pointer. */ +} json_engine_t; + + +int json_scan_start(json_engine_t *je, + CHARSET_INFO *i_cs, const uchar *str, const uchar *end); +int json_scan_next(json_engine_t *j); + + +/* + json_read_keyname_chr() function assists parsing the name of an JSON key. + It only can be called when the json_engine is in JST_KEY. + The json_read_keyname_chr() reads one character of the name of the key, + and puts it in j_eng.s.next_c. + Typical usage is like this: + + if (j_eng.state == JST_KEY) + { + while (json_read_keyname_chr(&j) == 0) + { + //handle next character i.e. match it against the pattern + } + } +*/ + +int json_read_keyname_chr(json_engine_t *j); + + +/* + json_read_value() function parses the JSON value syntax, + so that we can handle the value of a key or an array item. + It only returns meaningful result when the engine is in + the JST_VALUE state. + + Typical usage is like this: + + if (j_eng.state == JST_VALUE) + { + json_read_value(&j_eng); + switch(j_eng.value_type) + { + case JSON_VALUE_STRING: + // get the string + str= j_eng.value; + str_length= j_eng.value_len; + case JSON_VALUE_NUMBER: + // get the number + ... etc + } +*/ +int json_read_value(json_engine_t *j); + + +/* + json_skip_key() makes parser skip the content of the current + JSON key quickly. + It can be called only when the json_engine state is JST_KEY. + Typical usage is: + + if (j_eng.state == JST_KEY) + { + if (key_does_not_match(j_eng)) + json_skip_key(j_eng); + } +*/ + +int json_skip_key(json_engine_t *j); + + +/* + json_skip_level() makes parser quickly skip the JSON content + to the end of the current object or array. + It is used when we're not interested in the rest of an array + or the rest of the keys of an object. +*/ +int json_skip_level(json_engine_t *j); + + +#define json_skip_array_item json_skip_key + +/* + Checks if the current value is of scalar type - + not an OBJECT nor ARRAY. +*/ +#define json_value_scalar(je) ((je)->value_type > JSON_VALUE_ARRAY) + +/* + Look for the JSON PATH in the json string. + Function can be called several times with same JSON/PATH to + find multiple matches. + On the first call, the json_engine_t parameter should be + initialized with the JSON string, and the json_path_t with the JSON path + appropriately. The 'p_cur_step' should point at the first + step of the path. + The 'array_counters' is the array of JSON_DEPTH_LIMIT size. + It stores the array counters of the parsed JSON. + If function returns 0, it means it found the match. The position of + the match is je->s.c_str. Then we can call the json_find_path() + with same engine/path/p_cur_step to get the next match. + Non-zero return means no matches found. + Check je->s.error to see if there was an error in JSON. +*/ +int json_find_path(json_engine_t *je, + json_path_t *p, json_path_step_t **p_cur_step, + uint *array_counters); + + +typedef struct st_json_find_paths_t +{ + uint n_paths; + json_path_t *paths; + uint cur_depth; + uint *path_depths; + uint array_counters[JSON_DEPTH_LIMIT]; +} json_find_paths_t; + + +int json_find_paths_first(json_engine_t *je, json_find_paths_t *state, + uint n_paths, json_path_t *paths, uint *path_depths); +int json_find_paths_next(json_engine_t *je, json_find_paths_t *state); + + +/* + Converst JSON string constant into ordinary string constant + which can involve unpacking json escapes and changing character set. + Returns negative integer in the case of an error, + the length of the result otherwise. +*/ +int json_unescape(CHARSET_INFO *json_cs, + const uchar *json_str, const uchar *json_end, + CHARSET_INFO *res_cs, + uchar *res, uchar *res_end); + +/* + Converst ordinary string constant into JSON string constant. + which can involve appropriate escaping and changing character set. + Returns negative integer in the case of an error, + the length of the result otherwise. +*/ +int json_escape(CHARSET_INFO *str_cs, const uchar *str, const uchar *str_end, + CHARSET_INFO *json_cs, uchar *json, uchar *json_end); + + +/* + Appends the ASCII string to the json with the charset conversion. +*/ +int json_append_ascii(CHARSET_INFO *json_cs, + uchar *json, uchar *json_end, + const uchar *ascii, const uchar *ascii_end); + +#ifdef __cplusplus +} +#endif + +#endif /* JSON_LIB_INCLUDED */ + diff --git a/include/my_atomic.h b/include/my_atomic.h index 2034bf48987..9f416208013 100644 --- a/include/my_atomic.h +++ b/include/my_atomic.h @@ -282,8 +282,16 @@ make_atomic_store(ptr) #if SIZEOF_LONG == 4 #define my_atomic_addlong(A,B) my_atomic_add32((int32*) (A), (B)) +#define my_atomic_loadlong(A) my_atomic_load32((int32*) (A)) +#define my_atomic_storelong(A,B) my_atomic_store32((int32*) (A), (B)) +#define my_atomic_faslong(A,B) my_atomic_fas32((int32*) (A), (B)) +#define my_atomic_caslong(A,B,C) my_atomic_cas32((int32*) (A), (int32*) (B), (C)) #else #define my_atomic_addlong(A,B) my_atomic_add64((int64*) (A), (B)) +#define my_atomic_loadlong(A) my_atomic_load64((int64*) (A)) +#define my_atomic_storelong(A,B) my_atomic_store64((int64*) (A), (B)) +#define my_atomic_faslong(A,B) my_atomic_fas64((int64*) (A), (B)) +#define my_atomic_caslong(A,B,C) my_atomic_cas64((int64*) (A), (int64*) (B), (C)) #endif #ifdef _atomic_h_cleanup_ diff --git a/include/mysql.h.pp b/include/mysql.h.pp index 857f5b99a90..714b26bc16a 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -109,6 +109,8 @@ my_bool net_write_command(NET *net,unsigned char command, const unsigned char *packet, size_t len); int net_real_write(NET *net,const unsigned char *packet, size_t len); unsigned long my_net_read_packet(NET *net, my_bool read_from_server); +ulong my_net_read_packet_reallen(NET *net, my_bool read_from_server, + ulong* reallen); struct sockaddr; int my_connect(my_socket s, const struct sockaddr *name, unsigned int namelen, unsigned int timeout); diff --git a/include/mysql_com.h b/include/mysql_com.h index 461800f3ce7..60a3c9f8a9a 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -585,6 +585,8 @@ my_bool net_write_command(NET *net,unsigned char command, const unsigned char *packet, size_t len); int net_real_write(NET *net,const unsigned char *packet, size_t len); unsigned long my_net_read_packet(NET *net, my_bool read_from_server); +ulong my_net_read_packet_reallen(NET *net, my_bool read_from_server, + ulong* reallen); #define my_net_read(A) my_net_read_packet((A), 0) #ifdef MY_GLOBAL_INCLUDED diff --git a/include/sql_common.h b/include/sql_common.h index 49616f6d56c..bbf459e1e55 100644 --- a/include/sql_common.h +++ b/include/sql_common.h @@ -104,6 +104,7 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, const unsigned char *arg, ulong arg_length, my_bool skip_check, MYSQL_STMT *stmt); unsigned long cli_safe_read(MYSQL *mysql); +unsigned long cli_safe_read_reallen(MYSQL *mysql, ulong* reallen); void net_clear_error(NET *net); void set_stmt_errmsg(MYSQL_STMT *stmt, NET *net); void set_stmt_error(MYSQL_STMT *stmt, int errcode, const char *sqlstate, diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index b64348cdd70..62aab628ed2 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -50,7 +50,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/item.cc ../sql/item_create.cc ../sql/item_func.cc ../sql/item_geofunc.cc ../sql/item_row.cc ../sql/item_strfunc.cc ../sql/item_subselect.cc ../sql/item_sum.cc ../sql/item_timefunc.cc - ../sql/item_xmlfunc.cc ../sql/key.cc ../sql/lock.cc ../sql/log.cc + ../sql/item_xmlfunc.cc ../sql/item_jsonfunc.cc + ../sql/key.cc ../sql/lock.cc ../sql/log.cc ../sql/log_event.cc ../sql/mf_iocache.cc ../sql/my_decimal.cc ../sql/net_serv.cc ../sql/opt_range.cc ../sql/opt_sum.cc ../sql/parse_file.cc ../sql/procedure.cc ../sql/protocol.cc diff --git a/mysql-test/extra/rpl_tests/delayed_slave_wait_on_query.inc b/mysql-test/extra/rpl_tests/delayed_slave_wait_on_query.inc new file mode 100644 index 00000000000..ffdcb7f60bb --- /dev/null +++ b/mysql-test/extra/rpl_tests/delayed_slave_wait_on_query.inc @@ -0,0 +1,55 @@ +# ==== Purpose ==== +# +# Auxiliary file used by rpl_delayed_slave.test. This assumes that an +# 'INSERT INTO t1...' query has been executed on the master. It does +# this: +# +# - After half the delay, check the status. It should be delaying and +# the query should not have executed. +# +# - After one and a half delay, check the status. It should not be +# delaying and the query should be executed. +# +# +# ==== Usage ==== +# +# --let $query_number= 4 +# --source extra/rpl_tests/delayed_slave_wait_on_query.inc +# +# Parameters: +# $query_number +# The value of the 'b' column in t1 for the row inserted by the query +# we are waiting for. + +connection master; + +--echo [on slave] +--let $slave_timeout= $time1 +--source include/sync_slave_io_with_master.inc +--echo # sleep 1*T +--sleep $time1 + +--let $assert_text= Query $query_number should not be executed +--let $assert_cond= MAX(b) < $query_number FROM t1 +--source include/rpl_assert.inc + +--let $assert_text= Status should be 'Waiting until MASTER_DELAY...' +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" LIKE "Waiting until MASTER_DELAY%" +--source include/rpl_assert.inc + +--echo # sleep 1*T +--sleep $time1 + +--echo # sync with master (with timeout 1*T) +--source include/sync_with_master.inc + +--let $assert_text= Query $query_number should be executed +--let $assert_cond= MAX(b) = $query_number FROM t1 +--source include/rpl_assert.inc + +--let $assert_text= Status should be 'Has read all relay log...' +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" LIKE "Slave has read all relay log%" +--source include/rpl_assert.inc + + +--source include/check_slave_is_running.inc diff --git a/mysql-test/include/check-testcase.test b/mysql-test/include/check-testcase.test index 083f44ce966..abac3861c66 100644 --- a/mysql-test/include/check-testcase.test +++ b/mysql-test/include/check-testcase.test @@ -67,6 +67,9 @@ if ($tmp) --echo Replicate_Do_Domain_Ids --echo Replicate_Ignore_Domain_Ids --echo Parallel_Mode conservative + --echo SQL_Delay 0 + --echo SQL_Remaining_Delay NULL + --echo Slave_SQL_Running_State } if (!$tmp) { # Note: after WL#5177, fields 13-18 shall not be filtered-out. diff --git a/mysql-test/include/have_numa.inc b/mysql-test/include/have_numa.inc new file mode 100644 index 00000000000..18bca99e04d --- /dev/null +++ b/mysql-test/include/have_numa.inc @@ -0,0 +1,9 @@ +let $numa_support = `SELECT COUNT(VARIABLE_VALUE) = 1 FROM + INFORMATION_SCHEMA.GLOBAL_VARIABLES + WHERE VARIABLE_NAME='innodb_numa_interleave'`; + +if ( $numa_support == 0 ) +{ + --skip Test requires: Binary must be built with NUMA support. +} + diff --git a/mysql-test/include/relocate_binlogs.inc b/mysql-test/include/relocate_binlogs.inc index d5d1135dda3..593ea0e9fbf 100644 --- a/mysql-test/include/relocate_binlogs.inc +++ b/mysql-test/include/relocate_binlogs.inc @@ -101,16 +101,16 @@ if ($relocate_index_file) --eval LOAD DATA INFILE '$relocate_fix_relay_log_info' INTO TABLE tmp (entry) --let $count= `SELECT count(*) FROM tmp` - --let $_curr_entry= `SELECT entry FROM tmp WHERE id=1` + --let $_curr_entry= `SELECT entry FROM tmp WHERE id=2` --let $_curr_entry_basename= `SELECT RIGHT(RTRIM("$_curr_entry"), LOCATE("$_path_separator",REVERSE(RTRIM("$_curr_entry"))) -1)` if ($relocate_is_windows) { - --eval UPDATE tmp SET entry='$_to\$_curr_entry_basename' WHERE id=1 + --eval UPDATE tmp SET entry='$_to\$_curr_entry_basename' WHERE id=2 } if (!$relocate_is_windows) { - --eval UPDATE tmp SET entry='$_to/$_curr_entry_basename' WHERE id=1 + --eval UPDATE tmp SET entry='$_to/$_curr_entry_basename' WHERE id=2 } --remove_file $relocate_fix_relay_log_info diff --git a/mysql-test/include/rpl_assert.inc b/mysql-test/include/rpl_assert.inc new file mode 100644 index 00000000000..d9963e8e782 --- /dev/null +++ b/mysql-test/include/rpl_assert.inc @@ -0,0 +1,118 @@ +# ==== Purpose ==== +# +# Check if a condition holds, fail with debug info if not. +# +# The condition is parsed before executed. The following constructs +# are supported: +# +# [SQL STATEMENT, COLUMN, ROW] +# The square bracket is replaced by the result from SQL STATEMENT, +# in the given COLUMN and ROW. +# +# <1> +# This is a shorthand for the result of the first executed square +# bracket. <2> is a shorthand for the second executed square +# bracket, and so on. +# +# ==== Usage ==== +# +# --let $assert_text= Relay_Log_Pos must be smaller than pos. +# --let $assert_cond= [SHOW SLAVE STATUS, Relay_Log_Pos, 1] >= $min_pos AND <1> <= $max_pos +# [--let $assert_quiet= 1] +# [--let $rpl_debug= 1] +# --source include/rpl_assert.inc +# +# Parameters: +# +# $assert_text +# Text that describes what is being checked. By default, this text +# is written to the query log. +# +# $assert_cond +# Condition to check. See above for details about the format. The +# condition will be executed as `SELECT $assert_cond`. Note: this +# condition is parsed using SQL statements, quoted inside single +# quotes, so it must not contain single quotes itself (use double +# quotes for strings). +# +# $assert_quiet +# Do not print $assert_text to the query log. +# +# $rpl_debug +# Print extra debug info. + + +if ($rpl_debug) +{ + --echo # debug: assert_text='$assert_text' assert_cond='$assert_cond' +} + +# Sanity-check input +if (`SELECT "$assert_text" = ""`) +{ + --die ERROR IN TEST: the mysqltest variable rpl_test must be set +} + +# Evaluate square brackets in cond. +--let $_rpl_assert_substmt_number= 1 +--let $_rpl_interpolated_cond= $assert_cond +--let $_rpl_assert_lbracket= `SELECT LOCATE('[', '$_rpl_interpolated_cond')` +while ($_rpl_assert_lbracket) +{ + # Get position of right bracket + --let $_rpl_assert_rbracket= `SELECT LOCATE(']', '$_rpl_interpolated_cond')` + if (!$_rpl_assert_rbracket) + { + --echo BUG IN TEST: Mismatching square brackets in assert_cond: '$assert_cond' + --die BUG IN TEST: Mismatching square brackets in $assert_cond + } + # Get sub-statement and result of it + --let $_rpl_assert_substmt= `SELECT SUBSTRING('$_rpl_interpolated_cond', $_rpl_assert_lbracket + 1, $_rpl_assert_rbracket - $_rpl_assert_lbracket - 1)` + --let $_rpl_assert_substmt_result= query_get_value($_rpl_assert_substmt) + if ($rpl_debug) + { + --echo # debug: sub-statement='$_rpl_assert_substmt' result='$rpl_assert_result' + } + # Replace sub-statement by its result + --let $_rpl_interpolated_cond= `SELECT REPLACE('$_rpl_interpolated_cond', '[$_rpl_assert_substmt]', '$_rpl_assert_substmt_result')` + # Replace result references by result + --let $_rpl_interpolated_cond= `SELECT REPLACE('$_rpl_interpolated_cond', '<$_rpl_assert_substmt_number>', '$_rpl_assert_substmt_result')` + + --let $_rpl_assert_lbracket= `SELECT LOCATE('[', '$_rpl_interpolated_cond')` + + --inc $_rpl_assert_substmt_number +} + +if ($rpl_debug) +{ + --echo # debug: interpolated_cond='$_rpl_interpolated_cond' +} + +# Execute. +--let $_rpl_assert_result= `SELECT $_rpl_interpolated_cond` + +if ($rpl_debug) +{ + --echo # debug: result='$_rpl_assert_result' +} + +# Check. +if (!$_rpl_assert_result) +{ + --echo ######## Test assertion failed: $assert_text ######## + --echo Dumping debug info: + --source include/show_rpl_debug_info.inc + --echo Assertion text: '$assert_text' + --echo Assertion condition: '$assert_cond' + --echo Assertion condition, interpolated: '$_rpl_interpolated_cond' + --echo Assertion result: '$_rpl_assert_result' + --die Test assertion failed in rpl_assertion.inc +} + +if (!$assert_quiet) +{ + --echo # Asserted this: $assert_text +} + +--let $assert_text= +--let $assert_cond= diff --git a/mysql-test/include/show_delayed_slave_state.inc b/mysql-test/include/show_delayed_slave_state.inc new file mode 100644 index 00000000000..8eb7232a206 --- /dev/null +++ b/mysql-test/include/show_delayed_slave_state.inc @@ -0,0 +1,28 @@ +# ==== Purpose ==== +# +# Display the delay state of the SQL thread. +# +# ==== Usage ==== +# +# --let $verbose_delayed_slave_state= [0|1] +# --source extra/rpl_tests/show_delayed_slave_state.inc +# +# By default, the output is normalized so that it does not depend on +# exact timing or exact binlog positions. If +# $verbose_delayed_slave_state is set, then it outputs exact times and +# binlog positions. This can be useful for debugging. + +--let $_delayed_slave_status= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running_State, 1) + +--let $_delayed_slave_remaining_delay= query_get_value(SHOW SLAVE STATUS, SQL_Remaining_Delay, 1) +--let $_delayed_slave_qualitative_delay= `SELECT CASE WHEN "$_delayed_slave_remaining_delay" = "NULL" THEN "NULL" WHEN "$_delayed_slave_remaining_delay" = "0" THEN "0" ELSE "greater than zero" END` + +--let $_delayed_slave_io_pos= query_get_value(SHOW SLAVE STATUS, Read_Master_Log_Pos, 1) +--let $_delayed_slave_sql_pos= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +--let $_delayed_slave_qualitative_log_pos= `SELECT IF($_delayed_slave_io_pos > $_delayed_slave_sql_pos, "behind", "in sync with")` + +--echo Slave_SQL_Running_State='$_delayed_slave_status'; SQL_Remaining_Delay is $_delayed_slave_qualitative_delay; SQL thread is $_delayed_slave_qualitative_log_pos IO thread + +if ($verbose_delayed_slave_state) { + --echo SQL_Remaining_Delay='$_delayed_slave_remaining_delay'; Read_master_log_pos='$_delayed_slave_io_pos'; Exec_Master_Log_Pos='$_delayed_slave_sql_pos' +} diff --git a/mysql-test/include/sync_with_master.inc b/mysql-test/include/sync_with_master.inc new file mode 100644 index 00000000000..dcb995a3ca9 --- /dev/null +++ b/mysql-test/include/sync_with_master.inc @@ -0,0 +1,26 @@ +# ==== Purpose ==== +# +# This file does the same as the built-in command sync_with_master, +# but can be configured to use a custom timeout. This has the benefit +# that it accepts the same $slave_timeout and $master_connection +# parameters as wait_for_slave_param.inc +# +# +# ==== Usage ==== +# +# --connection master +# --source include/save_master_pos.inc +# --connection slave +# --source include/sync_with_master.inc +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. + +--let $slave_param= Relay_Master_Log_File +--let $slave_param_value= $_master_file +--source include/wait_for_slave_param.inc + +--let $slave_param= Exec_Master_Log_Pos +--let $slave_param_value= $_master_pos +--source include/wait_for_slave_param.inc diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result new file mode 100644 index 00000000000..1a47dd79fe8 --- /dev/null +++ b/mysql-test/r/func_json.result @@ -0,0 +1,123 @@ +select json_valid('[1, 2]'); +json_valid('[1, 2]') +1 +select json_valid('"string"}'); +json_valid('"string"}') +0 +select json_valid('{"key1":1, "key2":[2,3]}'); +json_valid('{"key1":1, "key2":[2,3]}') +1 +select json_valid('[false, true, null]'); +json_valid('[false, true, null]') +1 +select json_value('{"key1":123}', '$.key2'); +json_value('{"key1":123}', '$.key2') +NULL +select json_value('{"key1":123}', '$.key1'); +json_value('{"key1":123}', '$.key1') +123 +select json_value('{"key1":[1,2,3]}', '$.key1'); +json_value('{"key1":[1,2,3]}', '$.key1') +NULL +select json_value('{"key1": [1,2,3], "key1":123}', '$.key1'); +json_value('{"key1": [1,2,3], "key1":123}', '$.key1') +123 +select json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key2'); +json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key2') +NULL +select json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key1'); +json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key1') +{"a":1, "b":[1,2]} +select json_query('{"key1": 1}', '$.key1'); +json_query('{"key1": 1}', '$.key1') +NULL +select json_query('{"key1":123, "key1": [1,2,3]}', '$.key1'); +json_query('{"key1":123, "key1": [1,2,3]}', '$.key1') +[1,2,3] +select json_array(1); +json_array(1) +[1] +select json_array(1, "text", false, null); +json_array(1, "text", false, null) +[1, "text", false, null] +select json_array_append('["a", "b"]', '$', FALSE); +json_array_append('["a", "b"]', '$', FALSE) +["a", "b", false] +select json_array_append('{"k1":1, "k2":["a", "b"]}', '$.k2', 2); +json_array_append('{"k1":1, "k2":["a", "b"]}', '$.k2', 2) +{"k1":1, "k2":["a", "b", 2]} +select json_contains('{"k1":123, "k2":345}', '123', '$.k1'); +json_contains('{"k1":123, "k2":345}', '123', '$.k1') +1 +select json_contains('"you"', '"you"'); +json_contains('"you"', '"you"') +1 +select json_contains('"youth"', '"you"'); +json_contains('"youth"', '"you"') +0 +select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[1]"); +json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[1]") +1 +select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[10]"); +json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[10]") +0 +select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.ma"); +json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.ma") +0 +select json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1"); +json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1") +1 +select json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1", "$.ma"); +json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1", "$.ma") +1 +select json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.ma"); +json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.ma") +0 +select json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.key2"); +json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.key2") +1 +select json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1"); +json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1") +asd +select json_extract('{"key1":"asd", "key2":[2,3]}', "$.keyX", "$.keyY"); +json_extract('{"key1":"asd", "key2":[2,3]}', "$.keyX", "$.keyY") +NULL +select json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1", "$.key2"); +json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1", "$.key2") +["asd", [2,3]] +select json_extract('{"key1":5, "key2":[2,3]}', "$.key1", "$.key2"); +json_extract('{"key1":5, "key2":[2,3]}', "$.key1", "$.key2") +[5, [2,3]] +select json_extract('{"key0":true, "key1":"qwe"}', "$.key1"); +json_extract('{"key0":true, "key1":"qwe"}', "$.key1") +qwe +select json_object("ki", 1, "mi", "ya"); +json_object("ki", 1, "mi", "ya") +{"ki": 1, "mi": "ya"} +select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2"); +json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2") +1 +select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[1]"); +json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[1]") +1 +select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[10]"); +json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[10]") +0 +select json_quote('"string"'); +json_quote('"string"') +\"string\" +select json_merge('string', 123); +json_merge('string', 123) +["string", 123] +select json_type('{"k1":123, "k2":345}'); +json_type('{"k1":123, "k2":345}') +OBJECT +select json_type('[123, "k2", 345]'); +json_type('[123, "k2", 345]') +ARRAY +select json_type("true"); +json_type("true") +BOOLEAN +select json_type('123'); +json_type('123') +NUMBER diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 862113c224a..32d38cd41ef 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -774,6 +774,9 @@ The following options may be given as the first argument: --range-alloc-block-size=# Allocation block size for storing ranges during optimization + --read-binlog-speed-limit=# + Maximum speed(KB/s) to read binlog from master (0 = no + limit) --read-buffer-size=# Each thread that does a sequential scan allocates a buffer of this size for each table it scans. If you do @@ -1399,6 +1402,7 @@ query-cache-type OFF query-cache-wlock-invalidate FALSE query-prealloc-size 24576 range-alloc-block-size 4096 +read-binlog-speed-limit 0 read-buffer-size 131072 read-only FALSE read-rnd-buffer-size 262144 diff --git a/mysql-test/r/null.result b/mysql-test/r/null.result index 222ccc809b1..1686bc7dbd3 100644 --- a/mysql-test/r/null.result +++ b/mysql-test/r/null.result @@ -1584,7 +1584,7 @@ SELECT * FROM t1 WHERE ((c1 IS NOT NULL) >= (NOT TRUE)) IS NOT NULL; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`c1` AS `c1` from `test`.`t1` where (((`test`.`t1`.`c1` is not null) >= ((not(1)))) is not null) +Note 1003 select `test`.`t1`.`c1` AS `c1` from `test`.`t1` where (((`test`.`t1`.`c1` is not null) >= 0) is not null) SELECT * FROM t1 WHERE ((c1 IS NOT NULL) >= (NOT TRUE)) IS NOT NULL; c1 1 diff --git a/mysql-test/suite/multi_source/info_logs.result b/mysql-test/suite/multi_source/info_logs.result index 1e3243df07a..e177c9826a9 100644 --- a/mysql-test/suite/multi_source/info_logs.result +++ b/mysql-test/suite/multi_source/info_logs.result @@ -89,17 +89,17 @@ MASTER 2.2 # EOF # show all slaves status; -Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos - Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_1 60 master-bin.000001 relay.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 1 No conservative 0 1073741824 7 0 60.000 -MASTER 2.2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 relay-master@00202@002e2.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 1073741824 7 0 60.000 +Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos + Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_1 60 master-bin.000001 relay.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 1 No conservative 0 NULL Slave has read all relay log; waiting for the slave I/O thread to update it 0 1073741824 7 0 60.000 +MASTER 2.2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 relay-master@00202@002e2.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 NULL Slave has read all relay log; waiting for the slave I/O thread to update it 0 1073741824 7 0 60.000 include/wait_for_slave_to_start.inc set default_master_connection = 'MASTER 2.2'; include/wait_for_slave_to_start.inc set default_master_connection = ''; show all slaves status; -Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos - Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_1 60 master-bin.000001 relay.000004 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 1 No conservative 0 1073741824 6 0 60.000 -MASTER 2.2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 relay-master@00202@002e2.000004 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 1073741824 6 0 60.000 +Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos + Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_1 60 master-bin.000001 relay.000004 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 1 No conservative 0 NULL Slave has read all relay log; waiting for the slave I/O thread to update it 0 1073741824 6 0 60.000 +MASTER 2.2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 relay-master@00202@002e2.000004 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 NULL Slave has read all relay log; waiting for the slave I/O thread to update it 0 1073741824 6 0 60.000 # # List of files matching '*info*' pattern # after slave server restart diff --git a/mysql-test/suite/multi_source/reset_slave.result b/mysql-test/suite/multi_source/reset_slave.result index 926c603f701..353970ac8ff 100644 --- a/mysql-test/suite/multi_source/reset_slave.result +++ b/mysql-test/suite/multi_source/reset_slave.result @@ -13,15 +13,15 @@ insert into t1 values (1),(2); connection slave; stop slave 'master1'; show slave 'master1' status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode - 127.0.0.1 root MYPORT_1 60 master-bin.000001 mysqld-relay-bin-master1.000002 master-bin.000001 No No 0 0 None 0 No NULL No 0 0 1 No conservative +Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State + 127.0.0.1 root MYPORT_1 60 master-bin.000001 mysqld-relay-bin-master1.000002 master-bin.000001 No No 0 0 None 0 No NULL No 0 0 1 No conservative 0 NULL mysqld-relay-bin-master1.000001 mysqld-relay-bin-master1.000002 mysqld-relay-bin-master1.index reset slave 'master1'; show slave 'master1' status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode - 127.0.0.1 root MYPORT_1 60 4 No No 0 0 0 None 0 No NULL No 0 0 1 No conservative +Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State + 127.0.0.1 root MYPORT_1 60 4 No No 0 0 0 None 0 No NULL No 0 0 1 No conservative 0 NULL reset slave 'master1' all; show slave 'master1' status; ERROR HY000: There is no master connection 'master1' diff --git a/mysql-test/suite/multi_source/simple.result b/mysql-test/suite/multi_source/simple.result index cabad76d7e6..419b9951905 100644 --- a/mysql-test/suite/multi_source/simple.result +++ b/mysql-test/suite/multi_source/simple.result @@ -18,9 +18,9 @@ connection slave; connection master2; connection slave; show all slaves status; -Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos -slave1 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_1 60 master-bin.000001 mysqld-relay-bin-slave1.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 1 No conservative 0 1073741824 7 0 60.000 -slave2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 mysqld-relay-bin-slave2.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 1073741824 7 0 60.000 +Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos +slave1 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_1 60 master-bin.000001 mysqld-relay-bin-slave1.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 1 No conservative 0 NULL Slave has read all relay log; waiting for the slave I/O thread to update it 0 1073741824 7 0 60.000 +slave2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 mysqld-relay-bin-slave2.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 NULL Slave has read all relay log; waiting for the slave I/O thread to update it 0 1073741824 7 0 60.000 start all slaves; stop slave 'slave1'; show slave 'slave1' status; @@ -71,21 +71,24 @@ Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode conservative +SQL_Delay 0 +SQL_Remaining_Delay NULL +Slave_SQL_Running_State reset slave 'slave1'; show all slaves status; -Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos -slave1 127.0.0.1 root MYPORT_1 60 4 No No 0 0 0 None 0 No NULL No 0 0 1 No conservative 0 1073741824 7 0 60.000 -slave2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 mysqld-relay-bin-slave2.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 1073741824 7 0 60.000 +Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos +slave1 127.0.0.1 root MYPORT_1 60 4 No No 0 0 0 None 0 No NULL No 0 0 1 No conservative 0 NULL 0 1073741824 7 0 60.000 +slave2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 mysqld-relay-bin-slave2.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 NULL Slave has read all relay log; waiting for the slave I/O thread to update it 0 1073741824 7 0 60.000 reset slave 'slave1' all; show all slaves status; -Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos -slave2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 mysqld-relay-bin-slave2.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 1073741824 7 0 60.000 +Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos +slave2 Slave has read all relay log; waiting for the slave I/O thread to update it Waiting for master to send event 127.0.0.1 root MYPORT_2 60 master-bin.000001 mysqld-relay-bin-slave2.000002 master-bin.000001 Yes Yes 0 0 None 0 No 0 No 0 0 2 No conservative 0 NULL Slave has read all relay log; waiting for the slave I/O thread to update it 0 1073741824 7 0 60.000 stop all slaves; Warnings: Note 1938 SLAVE 'slave2' stopped show all slaves status; -Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos -slave2 127.0.0.1 root MYPORT_2 60 master-bin.000001 mysqld-relay-bin-slave2.000002 master-bin.000001 No No 0 0 None 0 No NULL No 0 0 2 No conservative 0 1073741824 7 0 60.000 +Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos +slave2 127.0.0.1 root MYPORT_2 60 master-bin.000001 mysqld-relay-bin-slave2.000002 master-bin.000001 No No 0 0 None 0 No NULL No 0 0 2 No conservative 0 NULL 0 1073741824 7 0 60.000 stop all slaves; include/reset_master_slave.inc disconnect slave; diff --git a/mysql-test/suite/multi_source/syntax.result b/mysql-test/suite/multi_source/syntax.result index 7059234472a..a17a61d3e7c 100644 --- a/mysql-test/suite/multi_source/syntax.result +++ b/mysql-test/suite/multi_source/syntax.result @@ -1,11 +1,11 @@ include/master-slave.inc [connection master] show slave status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode +Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State show slave '' status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode +Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State show all slaves status; -Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos +Connection_name Slave_SQL_State Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error Replicate_Ignore_Server_Ids Master_Server_Id Master_SSL_Crl Master_SSL_Crlpath Using_Gtid Gtid_IO_Pos Replicate_Do_Domain_Ids Replicate_Ignore_Domain_Ids Parallel_Mode SQL_Delay SQL_Remaining_Delay Slave_SQL_Running_State Retried_transactions Max_relay_log_size Executed_log_entries Slave_received_heartbeats Slave_heartbeat_period Gtid_Slave_Pos # # Check error handling # diff --git a/mysql-test/suite/rpl/r/rpl_delayed_slave,parallel.rdiff b/mysql-test/suite/rpl/r/rpl_delayed_slave,parallel.rdiff new file mode 100644 index 00000000000..aaadbb28ca3 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_delayed_slave,parallel.rdiff @@ -0,0 +1,17 @@ +--- mysql-test/suite/rpl/r/rpl_delayed_slave.result 2016-10-14 21:14:02.338075590 +0200 ++++ mysql-test/suite/rpl/r/rpl_delayed_slave,parallel.reject 2016-10-14 21:17:51.296986686 +0200 +@@ -45,7 +45,6 @@ + # wait for first query to execute + # sleep 1*T + # Asserted this: Second query executed +-# Asserted this: Status should be executing third query (i.e., 'User sleep') + # sleep 2*T + # Asserted this: Third query executed + # Asserted this: Status should be 'Has read all relay log...' +@@ -167,5 +166,5 @@ + conservative + SELECT @@GLOBAL.slave_parallel_threads; + @@GLOBAL.slave_parallel_threads +-0 ++10 + include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_delayed_slave.result b/mysql-test/suite/rpl/r/rpl_delayed_slave.result new file mode 100644 index 00000000000..bcfd49934b4 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_delayed_slave.result @@ -0,0 +1,192 @@ +include/master-slave.inc +[connection master] +call mtr.add_suppression("Unsafe statement written to the binary log using statement format"); +connection slave; +call mtr.add_suppression("Unsafe statement written to the binary log using statement format"); +connection master; +[on master] +CREATE TABLE t1 (a VARCHAR(100), b INT); +INSERT INTO t1 VALUES ("zero", 0); +==== Normal setup ==== +[on slave] +connection slave; +include/stop_slave.inc +# CHANGE MASTER TO MASTER_DELAY = 2*T +include/start_slave.inc +# Asserted this: SHOW SLAVE STATUS should return the same delay that we set with CHANGE MASTER +[on master] +connection master; +INSERT INTO t1 VALUES ('normal setup', 1); +connection master; +[on slave] +include/sync_slave_io_with_master.inc +# sleep 1*T +# Asserted this: Query 1 should not be executed +# Asserted this: Status should be 'Waiting until MASTER_DELAY...' +# sleep 1*T +# sync with master (with timeout 1*T) +include/wait_for_slave_param.inc [Relay_Master_Log_File] +include/wait_for_slave_param.inc [Exec_Master_Log_Pos] +# Asserted this: Query 1 should be executed +# Asserted this: Status should be 'Has read all relay log...' +include/check_slave_is_running.inc +==== Slave lags "naturally" after master ==== +[on master] +connection master; +# CREATE FUNCTION delay_on_slave(time_units INT) RETURNS INT BEGIN IF @@GLOBAL.server_id = 2 THEN RETURN SLEEP(time_units * T); ELSE RETURN 0; END IF; END +INSERT INTO t1 SELECT delay_on_slave(3), 2; +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave +INSERT INTO t1 VALUES ('slave is already lagging: this statement should execute immediately', 3); +INSERT INTO t1 SELECT delay_on_slave(2), 4; +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave +[on slave] +include/sync_slave_io_with_master.inc +# sleep 1*T +# Asserted this: No query executed +# Asserted this: Status should be 'Waiting until MASTER_DELAY...' +# wait for first query to execute +# sleep 1*T +# Asserted this: Second query executed +# Asserted this: Status should be executing third query (i.e., 'User sleep') +# sleep 2*T +# Asserted this: Third query executed +# Asserted this: Status should be 'Has read all relay log...' +==== Seconds_Behind_Master ==== +# Bring slave to sync. +include/stop_slave.inc +CHANGE MASTER TO MASTER_DELAY = 0; +include/start_slave.inc +connection master; +INSERT INTO t1 VALUES ('Syncing slave', 5); +connection slave; +include/stop_slave.inc +# CHANGE MASTER TO MASTER_DELAY = 2*T +include/start_slave.inc +connection master; +INSERT INTO t1 VALUES (delay_on_slave(1), 6); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave +connection slave; +# sleep 1*T +# Asserted this: Seconds_Behind_Master should be between 0 and the 2*T +# sleep 1*T +# Asserted this: Seconds_Behind_Master should be at least 2*T +==== STOP SLAVE / START SLAVE + DML ==== +include/stop_slave.inc +# CHANGE MASTER TO MASTER_DELAY = 3*T +include/start_slave.inc +[on master] +connection master; +INSERT INTO t1 VALUES ('stop slave and start slave: DML', 7); +[on slave] +connection slave; +# sleep 1*T +include/stop_slave.inc +# Asserted this: STOP SLAVE should finish quickly, not wait for the ongoing sleep to finish +# Asserted this: SQL thread position should not increase after STOP SLAVE +# Asserted this: Query should not be executed after STOP SLAVE +# Asserted this: Status should be '' after STOP SLAVE +include/start_slave.inc +# Asserted this: START SLAVE should finish quickly +connection master; +[on slave] +include/sync_slave_io_with_master.inc +# sleep 1*T +# Asserted this: Query 7 should not be executed +# Asserted this: Status should be 'Waiting until MASTER_DELAY...' +# sleep 1*T +# sync with master (with timeout 1*T) +include/wait_for_slave_param.inc [Relay_Master_Log_File] +include/wait_for_slave_param.inc [Exec_Master_Log_Pos] +# Asserted this: Query 7 should be executed +# Asserted this: Status should be 'Has read all relay log...' +include/check_slave_is_running.inc +==== STOP SLAVE / START SLAVE + DDL ==== +This verifies BUG#56442 +[on master] +connection master; +CREATE TABLE t_check_dml_not_executed_prematurely (a INT); +include/save_master_pos.inc +[on slave] +connection slave; +# sleep 1*T +include/stop_slave.inc +# Asserted this: STOP SLAVE should finish quickly, not wait for the ongoing sleep to finish +# Asserted this: SQL thread position should not increase after STOP SLAVE +# Asserted this: Query should not be executed after STOP SLAVE +# Asserted this: Status should be '' after STOP SLAVE +include/start_slave.inc +# Asserted this: START SLAVE should finish quickly +# sleep 1*T +# Asserted this: DDL Query should not be executed after START SLAVE +# Asserted this: Status should be 'Waiting until MASTER_DELAY...' +# sleep 1*T +# sync with master (with timeout 1*T) +include/wait_for_slave_param.inc [Relay_Master_Log_File] +include/wait_for_slave_param.inc [Exec_Master_Log_Pos] +# Asserted this: DDL Query should be executed +# Asserted this: Status should be 'Has read all relay log...' +include/check_slave_is_running.inc +==== Change back to no delay ==== +[on slave] +connection slave; +include/stop_slave.inc +CHANGE MASTER TO MASTER_DELAY = 0; +# Asserted this: Delay should be 0 when we set it to 0 +include/start_slave.inc +[on master] +connection master; +INSERT INTO t1 VALUES ('change back to no delay', 8); +[on slave] +include/sync_slave_io_with_master.inc +# sleep 1*T +# Asserted this: Query should be executed +# Asserted this: Status should be 'Slave has read all relay log...' +==== Reset delay with RESET SLAVE ==== +include/stop_slave.inc +CHANGE MASTER TO MASTER_DELAY = 71; +include/start_slave.inc +# Asserted this: Delay should be 71 when we set it to 71 +include/stop_slave.inc +RESET SLAVE; +[on master] +connection master; +RESET MASTER; +[on slave] +connection slave; +include/start_slave.inc +# Asserted this: Delay should be 0 after RESET SLAVE +==== Set an invalid value for the delay ==== +include/stop_slave.inc +# Expect error for setting negative delay +CHANGE MASTER TO MASTER_DELAY = -1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '-1' at line 1 +# Expect that it's ok to set delay of 2^31-1 +CHANGE MASTER TO MASTER_DELAY = 2147483647; +# Expect error for setting delay between 2^31 and 2^32-1 +CHANGE MASTER TO MASTER_DELAY = 2147483648; +ERROR HY000: The requested value 2147483648 for the master delay exceeds the maximum 2147483647 +# Expect error for setting delay to nonsense +CHANGE MASTER TO MASTER_DELAY = blah; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'blah' at line 1 +CHANGE MASTER TO MASTER_DELAY = 0; +include/start_slave.inc +==== Clean up ==== +[on master] +connection master; +DROP TABLE t1, t_check_dml_not_executed_prematurely; +DROP FUNCTION delay_on_slave; +[on slave] +connection slave; +SELECT @@GLOBAL.slave_parallel_mode; +@@GLOBAL.slave_parallel_mode +conservative +SELECT @@GLOBAL.slave_parallel_threads; +@@GLOBAL.slave_parallel_threads +0 +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_delayed_slave2.result b/mysql-test/suite/rpl/r/rpl_delayed_slave2.result new file mode 100644 index 00000000000..998c7ab85b7 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_delayed_slave2.result @@ -0,0 +1,58 @@ +include/master-slave.inc +[connection master] +connection master; +ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; +CREATE TABLE t1 (a INT PRIMARY KEY, b VARCHAR(100)); +INSERT INTO t1 VALUES (1, "a"); +connection slave; +include/stop_slave.inc +CHANGE MASTER TO master_use_gtid=slave_pos; +SET @old_mode= @@GLOBAL.slave_parallel_mode; +SET GLOBAL slave_parallel_mode=optimistic; +SET @old_threads= @@GLOBAL.slave_parallel_threads; +SET GLOBAL slave_parallel_threads=10; +connection master; +INSERT INTO t1 VALUES (2, "b"); +INSERT INTO t1 VALUES (3, "b"); +INSERT INTO t1 VALUES (4, "b"); +SET timestamp= @@timestamp + 24*60*60; +INSERT INTO t1 VALUES (5, "c"); +INSERT INTO t1 VALUES (6, "c"); +SET timestamp= 0; +include/save_master_gtid.inc +connection slave; +CHANGE MASTER TO master_delay=1; +include/start_slave.inc +SELECT MASTER_GTID_WAIT('GTID1'); +MASTER_GTID_WAIT('GTID1') +0 +SELECT MASTER_GTID_WAIT('GTID2', 2); +MASTER_GTID_WAIT('GTID2', 2) +-1 +include/stop_slave.inc +SELECT * FROM t1 ORDER BY a; +a b +1 a +2 b +3 b +4 b +CHANGE MASTER TO master_delay=0; +include/start_slave.inc +include/sync_with_master_gtid.inc +SELECT * FROM t1 ORDER BY a; +a b +1 a +2 b +3 b +4 b +5 c +6 c +connection slave; +include/stop_slave.inc +CHANGE MASTER TO master_use_gtid=no, master_delay=0; +SET GLOBAL slave_parallel_mode=@old_mode; +SET GLOBAL slave_parallel_threads=@old_threads; +include/start_slave.inc +connection master; +DROP TABLE t1; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_delayed_slave.combinations b/mysql-test/suite/rpl/t/rpl_delayed_slave.combinations new file mode 100644 index 00000000000..8adc75e834f --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_delayed_slave.combinations @@ -0,0 +1,5 @@ +[nonparallel] + +[parallel] +--slave-parallel-mode=conservative +--slave-parallel-threads=10 diff --git a/mysql-test/suite/rpl/t/rpl_delayed_slave.test b/mysql-test/suite/rpl/t/rpl_delayed_slave.test new file mode 100644 index 00000000000..2400a821e2b --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_delayed_slave.test @@ -0,0 +1,424 @@ +# ==== Purpose ==== +# +# Test the time-delayed replication feature, i.e., +# CHANGE MASTER TO MASTER_DELAY=X: +# +# - Verify that slave has executed the events after but not before the +# delay timeout. +# +# - Verify that delay is correct when slave is already lagging +# due to slow queries. +# +# - Verify that Seconds_Behind_Master is greater than or equal to the +# delay if the slave still has unprocessed events in the relay log +# and more time than the delay has elapsed since the last event was +# executed on the master. +# +# - Verify that STOP SLAVE works instantly even during a delay, and +# that it does not cause the waited-for event to be executed too +# early on slave. +# +# - Verify that changing back to no delay works. +# +# - Verify that RESET SLAVE sets the delay to 0. +# +# - Verify that setting a bad value for the delay gives an error. +# +# ==== Implementation ==== +# +# We run the slave with 10 seconds lag. +# +# In general, to test that a query has not been executed by the slave +# before this time, we wait until the slave IO thread has received the +# event, and then 5 seconds more, and check that the table has not +# been updated. To test that a query has been executed after this +# time, we wait 10 seconds more. +# +# To simulate that the slave lags due to slow queries, we invoke a +# stored function that executes SLEEP if @@gloval.server_id==2. This +# requires that we run with binlog_format=STATEMENT. +# +# ==== Related Bugs and Worklogs ==== +# +# WL#344: Time-delayed replication +# BUG#28760: Simulating a replication lag +# [duplicate] BUG#22072: configurable delayed replication +# [duplicate] BUG#21639: Add Replication Delay parameter +# BUG#56442: Slave executes delayed statements when STOP SLAVE is issued +# +# ==== Issues with this Test Case ==== +# +# The test is inherently timing-sensitive (i.e., contains races) and +# is likely to fail sporadically on a loaded host. +# +# The test takes a long time; it sleeps for around 20*10 seconds. + +--source include/big_test.inc +--source include/not_valgrind.inc +--source include/master-slave.inc +# Needed so that sleeps get executed in the slave SQL thread. +--source include/have_binlog_format_statement.inc + + +call mtr.add_suppression("Unsafe statement written to the binary log using statement format"); +--connection slave +call mtr.add_suppression("Unsafe statement written to the binary log using statement format"); +--connection master + + +# We assume that any simple operation takes zero time, with an error +# margin of $time1 seconds. Hence, if we run with a delay of $time2 +# seconds, we expect that: +# - If we execute a query on master and wait $time1 seconds, then the +# query has been copied to slave but not yet executed. +# - If we execute a query on master and wait $time3 seconds, then the +# query has been executed. +--let $time1= 10 +if (`SELECT '$max_query_execution_time' > 0`) { + --let $time1= $max_query_execution_time +} +--let $time2= `SELECT 2 * $time1` +--let $time3= `SELECT 3 * $time1` + + +--echo [on master] +CREATE TABLE t1 (a VARCHAR(100), b INT); +INSERT INTO t1 VALUES ("zero", 0); + + +--echo ==== Normal setup ==== + +--echo [on slave] +--sync_slave_with_master + +--source include/stop_slave.inc + +--echo # CHANGE MASTER TO MASTER_DELAY = 2*T +--disable_query_log +eval CHANGE MASTER TO MASTER_DELAY = $time2; +--enable_query_log + +--source include/start_slave.inc + +--let $assert_text= SHOW SLAVE STATUS should return the same delay that we set with CHANGE MASTER +--let $assert_cond= [SHOW SLAVE STATUS, SQL_Delay, 1] = $time2 +--source include/rpl_assert.inc + +--echo [on master] +--connection master +INSERT INTO t1 VALUES ('normal setup', 1); + +--let $query_number= 1 +--source extra/rpl_tests/delayed_slave_wait_on_query.inc + + +--echo ==== Slave lags "naturally" after master ==== + +--echo [on master] +--connection master + +--disable_query_log +--echo # CREATE FUNCTION delay_on_slave(time_units INT) RETURNS INT BEGIN IF @@GLOBAL.server_id = 2 THEN RETURN SLEEP(time_units * T); ELSE RETURN 0; END IF; END +--eval CREATE FUNCTION delay_on_slave(time_units INT) RETURNS INT BEGIN IF @@GLOBAL.server_id = 2 THEN RETURN SLEEP(time_units * $time1); ELSE RETURN 0; END IF; END +--enable_query_log + +INSERT INTO t1 SELECT delay_on_slave(3), 2; + +--save_master_pos +INSERT INTO t1 VALUES ('slave is already lagging: this statement should execute immediately', 3); +INSERT INTO t1 SELECT delay_on_slave(2), 4; + +--echo [on slave] +--source include/sync_slave_io_with_master.inc +--echo # sleep 1*T +--sleep $time1 + +--let $assert_text= No query executed +--let $assert_cond= MAX(b) = 1 FROM t1 +--source include/rpl_assert.inc + +--let $assert_text= Status should be 'Waiting until MASTER_DELAY...' +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" LIKE "Waiting until MASTER_DELAY%" +--source include/rpl_assert.inc + +--echo # wait for first query to execute +--sync_with_master + +--echo # sleep 1*T +--sleep $time1 + +--let $assert_text= Second query executed +--let $assert_cond= MAX(b) = 3 FROM t1 +--source include/rpl_assert.inc + +let $parallel= `SELECT @@GLOBAL.slave_parallel_threads`; +if (!$parallel) +{ + let $assert_text= Status should be executing third query (i.e., 'User sleep'); + let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" = "User sleep"; + source include/rpl_assert.inc; +} + +--echo # sleep 2*T +--sleep $time2 + +--let $assert_text= Third query executed +--let $assert_cond= MAX(b) = 4 FROM t1 +--source include/rpl_assert.inc + +--let $assert_text= Status should be 'Has read all relay log...' +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" LIKE "Slave has read all relay log%" +--source include/rpl_assert.inc + + +--echo ==== Seconds_Behind_Master ==== + +--echo # Bring slave to sync. +--source include/stop_slave.inc +CHANGE MASTER TO MASTER_DELAY = 0; +--source include/start_slave.inc + +--connection master +INSERT INTO t1 VALUES ('Syncing slave', 5); +--sync_slave_with_master + +--source include/stop_slave.inc +--echo # CHANGE MASTER TO MASTER_DELAY = 2*T +--disable_query_log +eval CHANGE MASTER TO MASTER_DELAY = $time2; +--enable_query_log +--source include/start_slave.inc + +--connection master +INSERT INTO t1 VALUES (delay_on_slave(1), 6); +--save_master_pos +--connection slave + +--echo # sleep 1*T +--sleep $time1 + +--let $assert_cond= [SHOW SLAVE STATUS, Seconds_Behind_Master, 1] >= 0 AND <1> < $time2 +--let $assert_text= Seconds_Behind_Master should be between 0 and the 2*T +--source include/rpl_assert.inc + +--echo # sleep 1*T +--sleep $time1 + +--let $assert_cond= [SHOW SLAVE STATUS, Seconds_Behind_Master, 1] >= $time2 +--let $assert_text= Seconds_Behind_Master should be at least 2*T +--source include/rpl_assert.inc + +--sync_with_master + + +--echo ==== STOP SLAVE / START SLAVE + DML ==== + +# Set up a longer delay. +--source include/stop_slave.inc + +--echo # CHANGE MASTER TO MASTER_DELAY = 3*T +--disable_query_log +eval CHANGE MASTER TO MASTER_DELAY = $time3; +--enable_query_log + +--source include/start_slave.inc + +--echo [on master] +--connection master +INSERT INTO t1 VALUES ('stop slave and start slave: DML', 7); + +--echo [on slave] +--connection slave +--echo # sleep 1*T +--sleep $time1 +--let $timestamp_before_stop= `SELECT UNIX_TIMESTAMP()` +--let $relay_log_pos_before_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) +--source include/stop_slave.inc + +--let $assert_text= STOP SLAVE should finish quickly, not wait for the ongoing sleep to finish +--let $assert_cond= UNIX_TIMESTAMP() - $timestamp_before_stop < $time1 +--source include/rpl_assert.inc + +--let $assert_text= SQL thread position should not increase after STOP SLAVE +--let $assert_cond= [SHOW SLAVE STATUS, Relay_Log_Pos, 1] = $relay_log_pos_before_stop +--source include/rpl_assert.inc + +--let $assert_text= Query should not be executed after STOP SLAVE +--let $assert_cond= MAX(b) = 6 FROM t1 +--source include/rpl_assert.inc + +--let $assert_text= Status should be '' after STOP SLAVE +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" = "" +--source include/rpl_assert.inc + +--source include/start_slave.inc + +--let $assert_text= START SLAVE should finish quickly +--let $assert_cond= UNIX_TIMESTAMP() - $timestamp_before_stop < $time1 +--source include/rpl_assert.inc + +--let $query_number= 7 +--source extra/rpl_tests/delayed_slave_wait_on_query.inc + + +--echo ==== STOP SLAVE / START SLAVE + DDL ==== + +--echo This verifies BUG#56442 + +--echo [on master] +--connection master +CREATE TABLE t_check_dml_not_executed_prematurely (a INT); +--source include/save_master_pos.inc + +--echo [on slave] +--connection slave +--echo # sleep 1*T +--sleep $time1 + +--let $timestamp_before_stop= `SELECT UNIX_TIMESTAMP()` +--let $relay_log_pos_before_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) +--source include/stop_slave.inc + +--let $assert_text= STOP SLAVE should finish quickly, not wait for the ongoing sleep to finish +--let $assert_cond= UNIX_TIMESTAMP() - $timestamp_before_stop < $time1 +--source include/rpl_assert.inc + +--let $assert_text= SQL thread position should not increase after STOP SLAVE +--let $assert_cond= [SHOW SLAVE STATUS, Relay_Log_Pos, 1] = $relay_log_pos_before_stop +--source include/rpl_assert.inc + +--let $assert_text= Query should not be executed after STOP SLAVE +--let $assert_cond= COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = "t_check_dml_not_executed_prematurely" +--source include/rpl_assert.inc + +--let $assert_text= Status should be '' after STOP SLAVE +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" = "" +--source include/rpl_assert.inc + +--source include/start_slave.inc + +--let $assert_text= START SLAVE should finish quickly +--let $assert_cond= UNIX_TIMESTAMP() - $timestamp_before_stop < $time1 +--source include/rpl_assert.inc + +--echo # sleep 1*T +--sleep $time1 + +--let $assert_text= DDL Query should not be executed after START SLAVE +--let $assert_cond= COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = "t_check_dml_not_executed_prematurely" +--source include/rpl_assert.inc + +--let $assert_text= Status should be 'Waiting until MASTER_DELAY...' +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" LIKE "Waiting until MASTER_DELAY%" +--source include/rpl_assert.inc + +--echo # sleep 1*T +--sleep $time1 + +--echo # sync with master (with timeout 1*T) +--source include/sync_with_master.inc + +--let $assert_text= DDL Query should be executed +--let $assert_cond= COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = "t_check_dml_not_executed_prematurely" +--source include/rpl_assert.inc + +--let $assert_text= Status should be 'Has read all relay log...' +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" LIKE "Slave has read all relay log%" +--source include/rpl_assert.inc + +--source include/check_slave_is_running.inc + + +--echo ==== Change back to no delay ==== + +--echo [on slave] +--connection slave +--source include/stop_slave.inc +CHANGE MASTER TO MASTER_DELAY = 0; + +--let $assert_text= Delay should be 0 when we set it to 0 +--let $assert_cond= [SHOW SLAVE STATUS, SQL_Delay, 1] = 0 +--source include/rpl_assert.inc + +--source include/start_slave.inc + +--echo [on master] +--connection master +INSERT INTO t1 VALUES ('change back to no delay', 8); + +--echo [on slave] +--source include/sync_slave_io_with_master.inc +--echo # sleep 1*T +--sleep $time1 + +--let $assert_text= Query should be executed +--let $assert_cond= MAX(b) = 8 FROM t1 +--source include/rpl_assert.inc + +--let $assert_text= Status should be 'Slave has read all relay log...' +--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]" Like "Slave has read all relay log%" +--source include/rpl_assert.inc + + +--echo ==== Reset delay with RESET SLAVE ==== + +--source include/stop_slave.inc +CHANGE MASTER TO MASTER_DELAY = 71; +--source include/start_slave.inc + +--let $assert_text= Delay should be 71 when we set it to 71 +--let $assert_cond= [SHOW SLAVE STATUS, SQL_Delay, 1] = 71 +--source include/rpl_assert.inc + +--source include/stop_slave.inc +RESET SLAVE; +--echo [on master] +--connection master +RESET MASTER; +--echo [on slave] +--connection slave +--source include/start_slave.inc + +--let $assert_text= Delay should be 0 after RESET SLAVE +--let $assert_cond= [SHOW SLAVE STATUS, SQL_Delay, 1] = 0 +--source include/rpl_assert.inc + + +--echo ==== Set an invalid value for the delay ==== + +--source include/stop_slave.inc + +--echo # Expect error for setting negative delay +--error ER_PARSE_ERROR +CHANGE MASTER TO MASTER_DELAY = -1; + +--echo # Expect that it's ok to set delay of 2^31-1 +CHANGE MASTER TO MASTER_DELAY = 2147483647; +--echo # Expect error for setting delay between 2^31 and 2^32-1 +--error ER_MASTER_DELAY_VALUE_OUT_OF_RANGE +CHANGE MASTER TO MASTER_DELAY = 2147483648; + +--echo # Expect error for setting delay to nonsense +--error ER_PARSE_ERROR +CHANGE MASTER TO MASTER_DELAY = blah; + +# todo: CHANGE MASTER TO MASTER_DELAY = 999999999999999999999999999 +# should give error + +CHANGE MASTER TO MASTER_DELAY = 0; +--source include/start_slave.inc + + +--echo ==== Clean up ==== + +--echo [on master] +--connection master +DROP TABLE t1, t_check_dml_not_executed_prematurely; +DROP FUNCTION delay_on_slave; + +--echo [on slave] +--sync_slave_with_master +SELECT @@GLOBAL.slave_parallel_mode; +SELECT @@GLOBAL.slave_parallel_threads; + +--source include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_delayed_slave2.test b/mysql-test/suite/rpl/t/rpl_delayed_slave2.test new file mode 100644 index 00000000000..68e8f8e1c46 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_delayed_slave2.test @@ -0,0 +1,65 @@ +--source include/have_innodb.inc +--source include/master-slave.inc + +# This test file tests delayed slave for parallel replication (and GTID). +# Uses a different approach from rpl_delayed_slave.test, setting @@timestamp +# to simulate events logged on master at different times. + +--connection master +ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; +CREATE TABLE t1 (a INT PRIMARY KEY, b VARCHAR(100)); +INSERT INTO t1 VALUES (1, "a"); +--save_master_pos + +--connection slave +--sync_with_master +--source include/stop_slave.inc +CHANGE MASTER TO master_use_gtid=slave_pos; +SET @old_mode= @@GLOBAL.slave_parallel_mode; +SET GLOBAL slave_parallel_mode=optimistic; +SET @old_threads= @@GLOBAL.slave_parallel_threads; +SET GLOBAL slave_parallel_threads=10; + +--connection master +INSERT INTO t1 VALUES (2, "b"); +INSERT INTO t1 VALUES (3, "b"); +INSERT INTO t1 VALUES (4, "b"); +--let $gtid1= `SELECT @@gtid_binlog_pos` +# Simulate an event a days in the future, for delayed slave to wait on. +SET timestamp= @@timestamp + 24*60*60; +INSERT INTO t1 VALUES (5, "c"); +INSERT INTO t1 VALUES (6, "c"); +SET timestamp= 0; +--let $gtid2= `SELECT @@gtid_binlog_pos` +--source include/save_master_gtid.inc + +--connection slave +CHANGE MASTER TO master_delay=1; +--source include/start_slave.inc +--replace_result $gtid1 GTID1 +# First sync halfways, to avoid timing-dependent test failures. +eval SELECT MASTER_GTID_WAIT('$gtid1'); +# Try to sync up, should timeout because slave is waiting for one day. +--replace_result $gtid2 GTID2 +eval SELECT MASTER_GTID_WAIT('$gtid2', 2); + +# Check that we can stop slave while delaying. +--source include/stop_slave.inc +SELECT * FROM t1 ORDER BY a; +CHANGE MASTER TO master_delay=0; +--source include/start_slave.inc +--source include/sync_with_master_gtid.inc +SELECT * FROM t1 ORDER BY a; + + +--connection slave +--source include/stop_slave.inc +CHANGE MASTER TO master_use_gtid=no, master_delay=0; +SET GLOBAL slave_parallel_mode=@old_mode; +SET GLOBAL slave_parallel_threads=@old_threads; +--source include/start_slave.inc + +--connection master +DROP TABLE t1; + +--source include/rpl_end.inc diff --git a/mysql-test/suite/sys_vars/r/innodb_numa_interleave_basic.result b/mysql-test/suite/sys_vars/r/innodb_numa_interleave_basic.result new file mode 100644 index 00000000000..21ed16c1dab --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_numa_interleave_basic.result @@ -0,0 +1,10 @@ +SELECT @@GLOBAL.innodb_numa_interleave; +@@GLOBAL.innodb_numa_interleave +1 +SET @@GLOBAL.innodb_numa_interleave=off; +ERROR HY000: Variable 'innodb_numa_interleave' is a read only variable +SELECT @@GLOBAL.innodb_numa_interleave; +@@GLOBAL.innodb_numa_interleave +1 +SELECT @@SESSION.innodb_numa_interleave; +ERROR HY000: Variable 'innodb_numa_interleave' is a GLOBAL variable diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index f586b7b294a..8e364e6fd50 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -1447,6 +1447,20 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST OFF,ON READ_ONLY YES COMMAND_LINE_ARGUMENT NONE +VARIABLE_NAME INNODB_LOCK_SCHEDULE_ALGORITHM +SESSION_VALUE NULL +GLOBAL_VALUE vats +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE vats +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE ENUM +VARIABLE_COMMENT The algorithm Innodb uses for deciding which locks to grant next when a lock is released. Possible values are FCFS grant the locks in First-Come-First-Served order; VATS use the Variance-Aware-Transaction-Scheduling algorithm, which uses an Eldest-Transaction-First heuristic. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST fcfs,vats +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME INNODB_LOCK_WAIT_TIMEOUT SESSION_VALUE 50 GLOBAL_VALUE 50 diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded,32bit.rdiff b/mysql-test/suite/sys_vars/r/sysvars_server_embedded,32bit.rdiff index 588d38d9c73..e79ce413e71 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded,32bit.rdiff +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded,32bit.rdiff @@ -894,7 +894,7 @@ NUMERIC_MAX_VALUE 256 @@ -2829,7 +2829,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME - DEFAULT_VALUE 184 + DEFAULT_VALUE 185 VARIABLE_SCOPE GLOBAL -VARIABLE_TYPE BIGINT UNSIGNED +VARIABLE_TYPE INT UNSIGNED diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 98c846f0b57..335716e3df3 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -2825,9 +2825,9 @@ READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME PERFORMANCE_SCHEMA_MAX_STATEMENT_CLASSES SESSION_VALUE NULL -GLOBAL_VALUE 184 +GLOBAL_VALUE 185 GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE 184 +DEFAULT_VALUE 185 VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT Maximum number of statement instruments. diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded,32bit.rdiff b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded,32bit.rdiff index 82b5b7bf38f..7ed7f1a7c10 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded,32bit.rdiff +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded,32bit.rdiff @@ -1,5 +1,5 @@ ---- suite/sys_vars/r/sysvars_server_notembedded.result -+++ suite/sys_vars/r/sysvars_server_notembedded.reject +--- suite/sys_vars/r/sysvars_server_notembedded.result 2016-10-21 18:47:47.000000000 +0300 ++++ suite/sys_vars/r/sysvars_server_notembedded.reject 2016-10-24 02:02:03.000000000 +0300 @@ -57,7 +57,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE 1 @@ -449,7 +449,7 @@ VARIABLE_COMMENT Maximum number of prepared statements in the server NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 1048576 -@@ -2171,7 +2171,7 @@ +@@ -2185,7 +2185,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 4294967295 VARIABLE_SCOPE SESSION @@ -458,7 +458,7 @@ VARIABLE_COMMENT Maximum number of iterations when executing recursive queries NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 -@@ -2199,7 +2199,7 @@ +@@ -2213,7 +2213,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 4294967295 VARIABLE_SCOPE SESSION @@ -467,7 +467,7 @@ VARIABLE_COMMENT Limit assumed max number of seeks when looking up rows based on a key NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 4294967295 -@@ -2213,7 +2213,7 @@ +@@ -2227,7 +2227,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1024 VARIABLE_SCOPE SESSION @@ -476,7 +476,7 @@ VARIABLE_COMMENT The number of bytes to use when sorting BLOB or TEXT values (only the first max_sort_length bytes of each value are used; the rest are ignored) NUMERIC_MIN_VALUE 4 NUMERIC_MAX_VALUE 8388608 -@@ -2227,7 +2227,7 @@ +@@ -2241,7 +2241,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 0 VARIABLE_SCOPE SESSION @@ -485,7 +485,7 @@ VARIABLE_COMMENT Maximum stored procedure recursion depth NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 255 -@@ -2255,7 +2255,7 @@ +@@ -2269,7 +2269,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 32 VARIABLE_SCOPE SESSION @@ -494,7 +494,7 @@ VARIABLE_COMMENT Unused, will be removed. NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 4294967295 -@@ -2283,7 +2283,7 @@ +@@ -2297,7 +2297,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 4294967295 VARIABLE_SCOPE GLOBAL @@ -503,7 +503,7 @@ VARIABLE_COMMENT After this many write locks, allow some read locks to run in between NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 4294967295 -@@ -2297,7 +2297,7 @@ +@@ -2311,7 +2311,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1024 VARIABLE_SCOPE GLOBAL @@ -512,7 +512,7 @@ VARIABLE_COMMENT Unused NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 1048576 -@@ -2311,7 +2311,7 @@ +@@ -2325,7 +2325,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 8 VARIABLE_SCOPE GLOBAL @@ -521,7 +521,7 @@ VARIABLE_COMMENT Unused NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 1024 -@@ -2325,7 +2325,7 @@ +@@ -2339,7 +2339,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 0 VARIABLE_SCOPE SESSION @@ -530,7 +530,7 @@ VARIABLE_COMMENT Don't write queries to slow log that examine fewer rows than that NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 -@@ -2339,7 +2339,7 @@ +@@ -2353,7 +2353,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 262144 VARIABLE_SCOPE SESSION @@ -539,7 +539,7 @@ VARIABLE_COMMENT Size of buffer to use when using MRR with range access NUMERIC_MIN_VALUE 8192 NUMERIC_MAX_VALUE 2147483647 -@@ -2353,10 +2353,10 @@ +@@ -2367,10 +2367,10 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 256 VARIABLE_SCOPE SESSION @@ -552,7 +552,7 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -2367,7 +2367,7 @@ +@@ -2381,7 +2381,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1024 VARIABLE_SCOPE GLOBAL @@ -561,7 +561,7 @@ VARIABLE_COMMENT Block size to be used for MyISAM index pages NUMERIC_MIN_VALUE 1024 NUMERIC_MAX_VALUE 16384 -@@ -2381,7 +2381,7 @@ +@@ -2395,7 +2395,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 6 VARIABLE_SCOPE GLOBAL @@ -570,7 +570,7 @@ VARIABLE_COMMENT Default pointer size to be used for MyISAM tables NUMERIC_MIN_VALUE 2 NUMERIC_MAX_VALUE 7 -@@ -2391,9 +2391,9 @@ +@@ -2405,9 +2405,9 @@ COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME MYISAM_MAX_SORT_FILE_SIZE SESSION_VALUE NULL @@ -582,7 +582,7 @@ VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT Don't use the fast sort index method to created index if the temporary file would get bigger than this -@@ -2405,14 +2405,14 @@ +@@ -2419,14 +2419,14 @@ COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME MYISAM_MMAP_SIZE SESSION_VALUE NULL @@ -600,7 +600,7 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY YES -@@ -2437,10 +2437,10 @@ +@@ -2451,10 +2451,10 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1 VARIABLE_SCOPE SESSION @@ -613,7 +613,7 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -2454,7 +2454,7 @@ +@@ -2468,7 +2468,7 @@ VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE NUMERIC_MIN_VALUE 4096 @@ -622,7 +622,7 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -2507,7 +2507,7 @@ +@@ -2521,7 +2521,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 16384 VARIABLE_SCOPE SESSION @@ -631,7 +631,7 @@ VARIABLE_COMMENT Buffer length for TCP/IP and socket communication NUMERIC_MIN_VALUE 1024 NUMERIC_MAX_VALUE 1048576 -@@ -2521,7 +2521,7 @@ +@@ -2535,7 +2535,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 30 VARIABLE_SCOPE SESSION @@ -640,7 +640,7 @@ VARIABLE_COMMENT Number of seconds to wait for more data from a connection before aborting the read NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 31536000 -@@ -2535,7 +2535,7 @@ +@@ -2549,7 +2549,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 10 VARIABLE_SCOPE SESSION @@ -649,7 +649,7 @@ VARIABLE_COMMENT If a read on a communication port is interrupted, retry this many times before giving up NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 4294967295 -@@ -2549,7 +2549,7 @@ +@@ -2563,7 +2563,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 60 VARIABLE_SCOPE SESSION @@ -658,7 +658,7 @@ VARIABLE_COMMENT Number of seconds to wait for a block to be written to a connection before aborting the write NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 31536000 -@@ -2619,7 +2619,7 @@ +@@ -2633,7 +2633,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1 VARIABLE_SCOPE SESSION @@ -667,7 +667,7 @@ VARIABLE_COMMENT Controls the heuristic(s) applied during query optimization to prune less-promising partial plans from the optimizer search space. Meaning: 0 - do not apply any heuristic, thus perform exhaustive search; 1 - prune plans based on number of retrieved rows NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 1 -@@ -2633,7 +2633,7 @@ +@@ -2647,7 +2647,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 62 VARIABLE_SCOPE SESSION @@ -676,7 +676,7 @@ VARIABLE_COMMENT Maximum depth of search performed by the query optimizer. Values larger than the number of relations in a query result in better query plans, but take longer to compile a query. Values smaller than the number of tables in a relation result in faster optimization, but may produce very bad query plans. If set to 0, the system will automatically pick a reasonable value NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 62 -@@ -2647,7 +2647,7 @@ +@@ -2661,7 +2661,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 100 VARIABLE_SCOPE SESSION @@ -685,7 +685,7 @@ VARIABLE_COMMENT Controls number of record samples to check condition selectivity NUMERIC_MIN_VALUE 10 NUMERIC_MAX_VALUE 4294967295 -@@ -2675,7 +2675,7 @@ +@@ -2689,7 +2689,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1 VARIABLE_SCOPE SESSION @@ -694,7 +694,7 @@ VARIABLE_COMMENT Controls selectivity of which conditions the optimizer takes into account to calculate cardinality of a partial join when it searches for the best execution plan Meaning: 1 - use selectivity of index backed range conditions to calculate the cardinality of a partial join if the last joined table is accessed by full table scan or an index scan, 2 - use selectivity of index backed range conditions to calculate the cardinality of a partial join in any case, 3 - additionally always use selectivity of range conditions that are not backed by any index to calculate the cardinality of a partial join, 4 - use histograms to calculate selectivity of range conditions that are not backed by any index to calculate the cardinality of a partial join.5 - additionally use selectivity of certain non-range predicates calculated on record samples NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 5 -@@ -2703,7 +2703,7 @@ +@@ -2717,7 +2717,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -703,7 +703,7 @@ VARIABLE_COMMENT Maximum number of instrumented user@host accounts. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -2717,7 +2717,7 @@ +@@ -2731,7 +2731,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -712,7 +712,7 @@ VARIABLE_COMMENT Size of the statement digest. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 200 -@@ -2731,7 +2731,7 @@ +@@ -2745,7 +2745,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -721,7 +721,7 @@ VARIABLE_COMMENT Number of rows in EVENTS_STAGES_HISTORY_LONG. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -2745,7 +2745,7 @@ +@@ -2759,7 +2759,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -730,7 +730,7 @@ VARIABLE_COMMENT Number of rows per thread in EVENTS_STAGES_HISTORY. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1024 -@@ -2759,7 +2759,7 @@ +@@ -2773,7 +2773,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -739,7 +739,7 @@ VARIABLE_COMMENT Number of rows in EVENTS_STATEMENTS_HISTORY_LONG. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -2773,7 +2773,7 @@ +@@ -2787,7 +2787,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -748,7 +748,7 @@ VARIABLE_COMMENT Number of rows per thread in EVENTS_STATEMENTS_HISTORY. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1024 -@@ -2787,7 +2787,7 @@ +@@ -2801,7 +2801,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -757,7 +757,7 @@ VARIABLE_COMMENT Number of rows in EVENTS_WAITS_HISTORY_LONG. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -2801,7 +2801,7 @@ +@@ -2815,7 +2815,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -766,7 +766,7 @@ VARIABLE_COMMENT Number of rows per thread in EVENTS_WAITS_HISTORY. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1024 -@@ -2815,7 +2815,7 @@ +@@ -2829,7 +2829,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -775,7 +775,7 @@ VARIABLE_COMMENT Maximum number of instrumented hosts. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -2829,7 +2829,7 @@ +@@ -2843,7 +2843,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 80 VARIABLE_SCOPE GLOBAL @@ -784,7 +784,7 @@ VARIABLE_COMMENT Maximum number of condition instruments. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 256 -@@ -2843,7 +2843,7 @@ +@@ -2857,7 +2857,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -793,7 +793,7 @@ VARIABLE_COMMENT Maximum number of instrumented condition objects. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -2857,7 +2857,7 @@ +@@ -2871,7 +2871,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1024 VARIABLE_SCOPE GLOBAL @@ -802,7 +802,7 @@ VARIABLE_COMMENT Maximum length considered for digest text, when stored in performance_schema tables. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 1048576 -@@ -2871,7 +2871,7 @@ +@@ -2885,7 +2885,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 50 VARIABLE_SCOPE GLOBAL @@ -811,7 +811,7 @@ VARIABLE_COMMENT Maximum number of file instruments. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 256 -@@ -2885,7 +2885,7 @@ +@@ -2899,7 +2899,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 32768 VARIABLE_SCOPE GLOBAL @@ -820,7 +820,7 @@ VARIABLE_COMMENT Maximum number of opened instrumented files. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 1048576 -@@ -2899,7 +2899,7 @@ +@@ -2913,7 +2913,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -829,7 +829,7 @@ VARIABLE_COMMENT Maximum number of instrumented files. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -2913,7 +2913,7 @@ +@@ -2927,7 +2927,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 200 VARIABLE_SCOPE GLOBAL @@ -838,7 +838,7 @@ VARIABLE_COMMENT Maximum number of mutex instruments. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 256 -@@ -2927,7 +2927,7 @@ +@@ -2941,7 +2941,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -847,7 +847,7 @@ VARIABLE_COMMENT Maximum number of instrumented MUTEX objects. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 104857600 -@@ -2941,7 +2941,7 @@ +@@ -2955,7 +2955,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 40 VARIABLE_SCOPE GLOBAL @@ -856,7 +856,7 @@ VARIABLE_COMMENT Maximum number of rwlock instruments. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 256 -@@ -2955,7 +2955,7 @@ +@@ -2969,7 +2969,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -865,7 +865,7 @@ VARIABLE_COMMENT Maximum number of instrumented RWLOCK objects. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 104857600 -@@ -2969,7 +2969,7 @@ +@@ -2983,7 +2983,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 10 VARIABLE_SCOPE GLOBAL @@ -874,7 +874,7 @@ VARIABLE_COMMENT Maximum number of socket instruments. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 256 -@@ -2983,7 +2983,7 @@ +@@ -2997,7 +2997,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -883,7 +883,7 @@ VARIABLE_COMMENT Maximum number of opened instrumented sockets. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -2997,7 +2997,7 @@ +@@ -3011,7 +3011,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 150 VARIABLE_SCOPE GLOBAL @@ -892,16 +892,16 @@ VARIABLE_COMMENT Maximum number of stage instruments. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 256 -@@ -3011,7 +3011,7 @@ +@@ -3025,7 +3025,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME - DEFAULT_VALUE 181 + DEFAULT_VALUE 185 VARIABLE_SCOPE GLOBAL -VARIABLE_TYPE BIGINT UNSIGNED +VARIABLE_TYPE INT UNSIGNED VARIABLE_COMMENT Maximum number of statement instruments. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 256 -@@ -3025,7 +3025,7 @@ +@@ -3039,7 +3039,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -910,7 +910,7 @@ VARIABLE_COMMENT Maximum number of opened instrumented tables. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -3039,7 +3039,7 @@ +@@ -3053,7 +3053,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -919,7 +919,7 @@ VARIABLE_COMMENT Maximum number of instrumented tables. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -3053,7 +3053,7 @@ +@@ -3067,7 +3067,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 50 VARIABLE_SCOPE GLOBAL @@ -928,7 +928,7 @@ VARIABLE_COMMENT Maximum number of thread instruments. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 256 -@@ -3067,7 +3067,7 @@ +@@ -3081,7 +3081,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -937,7 +937,7 @@ VARIABLE_COMMENT Maximum number of instrumented threads. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -3081,7 +3081,7 @@ +@@ -3095,7 +3095,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -946,7 +946,7 @@ VARIABLE_COMMENT Size of session attribute string buffer per thread. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -3095,7 +3095,7 @@ +@@ -3109,7 +3109,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 100 VARIABLE_SCOPE GLOBAL @@ -955,7 +955,7 @@ VARIABLE_COMMENT Maximum number of rows in SETUP_ACTORS. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 1024 -@@ -3109,7 +3109,7 @@ +@@ -3123,7 +3123,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 100 VARIABLE_SCOPE GLOBAL @@ -964,7 +964,7 @@ VARIABLE_COMMENT Maximum number of rows in SETUP_OBJECTS. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 1048576 -@@ -3123,7 +3123,7 @@ +@@ -3137,7 +3137,7 @@ GLOBAL_VALUE_ORIGIN CONFIG DEFAULT_VALUE -1 VARIABLE_SCOPE GLOBAL @@ -973,7 +973,7 @@ VARIABLE_COMMENT Maximum number of instrumented users. Use 0 to disable, -1 for automated sizing. NUMERIC_MIN_VALUE -1 NUMERIC_MAX_VALUE 1048576 -@@ -3193,7 +3193,7 @@ +@@ -3207,7 +3207,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 32768 VARIABLE_SCOPE SESSION @@ -982,7 +982,7 @@ VARIABLE_COMMENT The size of the buffer that is allocated when preloading indexes NUMERIC_MIN_VALUE 1024 NUMERIC_MAX_VALUE 1073741824 -@@ -3221,7 +3221,7 @@ +@@ -3235,7 +3235,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 15 VARIABLE_SCOPE SESSION @@ -991,7 +991,7 @@ VARIABLE_COMMENT Limit of query profiling memory NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 100 -@@ -3235,7 +3235,7 @@ +@@ -3249,7 +3249,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 5 VARIABLE_SCOPE SESSION @@ -1000,7 +1000,7 @@ VARIABLE_COMMENT Seconds between sending progress reports to the client for time-consuming statements. Set to 0 to disable progress reporting. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 -@@ -3305,7 +3305,7 @@ +@@ -3319,7 +3319,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 16384 VARIABLE_SCOPE SESSION @@ -1009,7 +1009,7 @@ VARIABLE_COMMENT Allocation block size for query parsing and execution NUMERIC_MIN_VALUE 1024 NUMERIC_MAX_VALUE 4294967295 -@@ -3319,7 +3319,7 @@ +@@ -3333,7 +3333,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1048576 VARIABLE_SCOPE GLOBAL @@ -1018,7 +1018,7 @@ VARIABLE_COMMENT Don't cache results that are bigger than this NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 -@@ -3333,7 +3333,7 @@ +@@ -3347,7 +3347,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 4096 VARIABLE_SCOPE GLOBAL @@ -1027,7 +1027,7 @@ VARIABLE_COMMENT The minimum size for blocks allocated by the query cache NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 -@@ -3350,7 +3350,7 @@ +@@ -3364,7 +3364,7 @@ VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT The memory allocated to store results from old queries NUMERIC_MIN_VALUE 0 @@ -1036,7 +1036,7 @@ NUMERIC_BLOCK_SIZE 1024 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -3403,7 +3403,7 @@ +@@ -3417,7 +3417,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 24576 VARIABLE_SCOPE SESSION @@ -1045,7 +1045,7 @@ VARIABLE_COMMENT Persistent buffer for query parsing and execution NUMERIC_MIN_VALUE 1024 NUMERIC_MAX_VALUE 4294967295 -@@ -3417,7 +3417,7 @@ +@@ -3431,7 +3431,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 4096 VARIABLE_SCOPE SESSION @@ -1054,7 +1054,16 @@ VARIABLE_COMMENT Allocation block size for storing ranges during optimization NUMERIC_MIN_VALUE 4096 NUMERIC_MAX_VALUE 4294967295 -@@ -3431,7 +3431,7 @@ +@@ -3448,7 +3448,7 @@ + VARIABLE_TYPE BIGINT UNSIGNED + VARIABLE_COMMENT Maximum speed(KB/s) to read binlog from master (0 = no limit) + NUMERIC_MIN_VALUE 0 +-NUMERIC_MAX_VALUE 18446744073709551615 ++NUMERIC_MAX_VALUE 4294967295 + NUMERIC_BLOCK_SIZE 1 + ENUM_VALUE_LIST NULL + READ_ONLY NO +@@ -3459,7 +3459,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 131072 VARIABLE_SCOPE SESSION @@ -1063,7 +1072,7 @@ VARIABLE_COMMENT Each thread that does a sequential scan allocates a buffer of this size for each table it scans. If you do many sequential scans, you may want to increase this value NUMERIC_MIN_VALUE 8192 NUMERIC_MAX_VALUE 2147483647 -@@ -3459,7 +3459,7 @@ +@@ -3487,7 +3487,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 262144 VARIABLE_SCOPE SESSION @@ -1072,7 +1081,7 @@ VARIABLE_COMMENT When reading rows in sorted order after a sort, the rows are read through this buffer to avoid a disk seeks NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 2147483647 -@@ -3739,10 +3739,10 @@ +@@ -3767,10 +3767,10 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 8388608 VARIABLE_SCOPE SESSION @@ -1085,16 +1094,16 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -3781,7 +3781,7 @@ +@@ -3809,7 +3809,7 @@ GLOBAL_VALUE_ORIGIN CONFIG - DEFAULT_VALUE 0 + DEFAULT_VALUE 1 VARIABLE_SCOPE SESSION -VARIABLE_TYPE BIGINT UNSIGNED +VARIABLE_TYPE INT UNSIGNED VARIABLE_COMMENT Uniquely identifies the server instance in the community of replication partners - NUMERIC_MIN_VALUE 0 + NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 4294967295 -@@ -3907,7 +3907,7 @@ +@@ -3991,7 +3991,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 0 VARIABLE_SCOPE GLOBAL @@ -1103,7 +1112,7 @@ VARIABLE_COMMENT Maximum number of parallel threads to use on slave for events in a single replication domain. When using multiple domains, this can be used to limit a single domain from grabbing all threads and thus stalling other domains. The default of 0 means to allow a domain to grab as many threads as it wants, up to the value of slave_parallel_threads. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 16383 -@@ -3949,7 +3949,7 @@ +@@ -4033,7 +4033,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 1073741824 VARIABLE_SCOPE GLOBAL @@ -1112,7 +1121,7 @@ VARIABLE_COMMENT The maximum packet length to sent successfully from the master to slave. NUMERIC_MIN_VALUE 1024 NUMERIC_MAX_VALUE 1073741824 -@@ -3977,7 +3977,7 @@ +@@ -4061,7 +4061,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 131072 VARIABLE_SCOPE GLOBAL @@ -1121,7 +1130,7 @@ VARIABLE_COMMENT Limit on how much memory SQL threads should use per parallel replication thread when reading ahead in the relay log looking for opportunities for parallel replication. Only used when --slave-parallel-threads > 0. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 2147483647 -@@ -4005,7 +4005,7 @@ +@@ -4089,7 +4089,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 0 VARIABLE_SCOPE GLOBAL @@ -1130,7 +1139,7 @@ VARIABLE_COMMENT If non-zero, number of threads to spawn to apply in parallel events on the slave that were group-committed on the master or were logged with GTID in different replication domains. Note that these threads are in addition to the IO and SQL threads, which are always created by a replication slave NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 16383 -@@ -4019,7 +4019,7 @@ +@@ -4103,7 +4103,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 0 VARIABLE_SCOPE GLOBAL @@ -1139,7 +1148,7 @@ VARIABLE_COMMENT Alias for slave_parallel_threads NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 16383 -@@ -4075,7 +4075,7 @@ +@@ -4159,7 +4159,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 10 VARIABLE_SCOPE GLOBAL @@ -1148,7 +1157,7 @@ VARIABLE_COMMENT Number of times the slave SQL thread will retry a transaction in case it failed with a deadlock or elapsed lock wait timeout, before giving up and stopping NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 -@@ -4103,7 +4103,7 @@ +@@ -4187,7 +4187,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 2 VARIABLE_SCOPE GLOBAL @@ -1157,7 +1166,7 @@ VARIABLE_COMMENT If creating the thread takes longer than this value (in seconds), the Slow_launch_threads counter will be incremented NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 31536000 -@@ -4162,7 +4162,7 @@ +@@ -4246,7 +4246,7 @@ VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT Each thread that needs to do a sort allocates a buffer of this size NUMERIC_MIN_VALUE 1024 @@ -1166,7 +1175,7 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -4453,7 +4453,7 @@ +@@ -4551,7 +4551,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 256 VARIABLE_SCOPE GLOBAL @@ -1175,7 +1184,7 @@ VARIABLE_COMMENT The soft upper limit for number of cached stored routines for one connection. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 524288 -@@ -4551,7 +4551,7 @@ +@@ -4649,7 +4649,7 @@ GLOBAL_VALUE_ORIGIN AUTO DEFAULT_VALUE 400 VARIABLE_SCOPE GLOBAL @@ -1184,7 +1193,7 @@ VARIABLE_COMMENT The number of cached table definitions NUMERIC_MIN_VALUE 400 NUMERIC_MAX_VALUE 524288 -@@ -4565,7 +4565,7 @@ +@@ -4663,7 +4663,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 2000 VARIABLE_SCOPE GLOBAL @@ -1193,7 +1202,7 @@ VARIABLE_COMMENT The number of cached open tables NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 524288 -@@ -4579,7 +4579,7 @@ +@@ -4691,7 +4691,7 @@ GLOBAL_VALUE_ORIGIN AUTO DEFAULT_VALUE 256 VARIABLE_SCOPE GLOBAL @@ -1202,7 +1211,7 @@ VARIABLE_COMMENT How many threads we should keep in a cache for reuse. These are freed after 5 minutes of idle time NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 16384 -@@ -4593,7 +4593,7 @@ +@@ -4705,7 +4705,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 10 VARIABLE_SCOPE GLOBAL @@ -1211,7 +1220,7 @@ VARIABLE_COMMENT Permits the application to give the threads system a hint for the desired number of threads that should be run at the same time.This variable has no effect, and is deprecated. It will be removed in a future release. NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 512 -@@ -4778,7 +4778,7 @@ +@@ -4918,7 +4918,7 @@ VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT If an internal in-memory temporary table exceeds this size, MySQL will automatically convert it to an on-disk MyISAM or Aria table NUMERIC_MIN_VALUE 1024 @@ -1220,7 +1229,7 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -4789,7 +4789,7 @@ +@@ -4929,7 +4929,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 8192 VARIABLE_SCOPE SESSION @@ -1229,7 +1238,7 @@ VARIABLE_COMMENT Allocation block size for transactions to be stored in binary log NUMERIC_MIN_VALUE 1024 NUMERIC_MAX_VALUE 134217728 -@@ -4803,7 +4803,7 @@ +@@ -4943,7 +4943,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 4096 VARIABLE_SCOPE SESSION @@ -1238,7 +1247,7 @@ VARIABLE_COMMENT Persistent buffer for transactions to be stored in binary log NUMERIC_MIN_VALUE 1024 NUMERIC_MAX_VALUE 134217728 -@@ -4901,7 +4901,7 @@ +@@ -5041,7 +5041,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE 28800 VARIABLE_SCOPE SESSION @@ -1247,7 +1256,7 @@ VARIABLE_COMMENT The number of seconds the server waits for activity on a connection before closing it NUMERIC_MIN_VALUE 1 NUMERIC_MAX_VALUE 31536000 -@@ -5089,7 +5089,7 @@ +@@ -5145,7 +5145,7 @@ COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME OPEN_FILES_LIMIT VARIABLE_SCOPE GLOBAL @@ -1256,7 +1265,7 @@ VARIABLE_COMMENT If this is not 0, then mysqld will use this value to reserve file descriptors to use with setrlimit(). If this value is 0 then mysqld will reserve max_connections*5 or max_connections + table_cache*2 (whichever is larger) number of file descriptors NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 -@@ -5102,7 +5102,7 @@ +@@ -5158,7 +5158,7 @@ VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT Sets the internal state of the RAND() generator for replication purposes NUMERIC_MIN_VALUE 0 @@ -1265,7 +1274,7 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -5112,7 +5112,7 @@ +@@ -5168,7 +5168,7 @@ VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT Sets the internal state of the RAND() generator for replication purposes NUMERIC_MIN_VALUE 0 @@ -1274,7 +1283,7 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO -@@ -5197,7 +5197,7 @@ +@@ -5253,7 +5253,7 @@ VARIABLE_NAME LOG_TC_SIZE GLOBAL_VALUE_ORIGIN AUTO VARIABLE_SCOPE GLOBAL diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 2a6c24653e1..6cc43cdc1dc 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3439,6 +3439,20 @@ NUMERIC_BLOCK_SIZE 1024 ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME READ_BINLOG_SPEED_LIMIT +SESSION_VALUE NULL +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE BIGINT UNSIGNED +VARIABLE_COMMENT Maximum speed(KB/s) to read binlog from master (0 = no limit) +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 18446744073709551615 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME READ_BUFFER_SIZE SESSION_VALUE 131072 GLOBAL_VALUE 131072 diff --git a/mysql-test/suite/sys_vars/t/innodb_numa_interleave_basic-master.opt b/mysql-test/suite/sys_vars/t/innodb_numa_interleave_basic-master.opt new file mode 100644 index 00000000000..c1c2bb26b8a --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_numa_interleave_basic-master.opt @@ -0,0 +1 @@ +--loose-innodb_numa_interleave=1 diff --git a/mysql-test/suite/sys_vars/t/innodb_numa_interleave_basic.test b/mysql-test/suite/sys_vars/t/innodb_numa_interleave_basic.test new file mode 100644 index 00000000000..518b5ebba17 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_numa_interleave_basic.test @@ -0,0 +1,14 @@ +--source include/have_innodb.inc +--source include/have_numa.inc +--source include/have_64bit.inc + +SELECT @@GLOBAL.innodb_numa_interleave; + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@GLOBAL.innodb_numa_interleave=off; + +SELECT @@GLOBAL.innodb_numa_interleave; + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@SESSION.innodb_numa_interleave; + diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test new file mode 100644 index 00000000000..3990ff24fc4 --- /dev/null +++ b/mysql-test/t/func_json.test @@ -0,0 +1,54 @@ +select json_valid('[1, 2]'); +select json_valid('"string"}'); +select json_valid('{"key1":1, "key2":[2,3]}'); +select json_valid('[false, true, null]'); + +select json_value('{"key1":123}', '$.key2'); +select json_value('{"key1":123}', '$.key1'); +select json_value('{"key1":[1,2,3]}', '$.key1'); +select json_value('{"key1": [1,2,3], "key1":123}', '$.key1'); + +select json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key2'); +select json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key1'); +select json_query('{"key1": 1}', '$.key1'); +select json_query('{"key1":123, "key1": [1,2,3]}', '$.key1'); + +select json_array(1); +select json_array(1, "text", false, null); + +select json_array_append('["a", "b"]', '$', FALSE); +select json_array_append('{"k1":1, "k2":["a", "b"]}', '$.k2', 2); + +select json_contains('{"k1":123, "k2":345}', '123', '$.k1'); +select json_contains('"you"', '"you"'); +select json_contains('"youth"', '"you"'); + +select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[1]"); +select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[10]"); +select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.ma"); +select json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1"); +select json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1", "$.ma"); +select json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.ma"); +select json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.key2"); + +select json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1"); +select json_extract('{"key1":"asd", "key2":[2,3]}', "$.keyX", "$.keyY"); +select json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1", "$.key2"); +select json_extract('{"key1":5, "key2":[2,3]}', "$.key1", "$.key2"); +select json_extract('{"key0":true, "key1":"qwe"}', "$.key1"); + +select json_object("ki", 1, "mi", "ya"); + +select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2"); +select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[1]"); +select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[10]"); + +select json_quote('"string"'); + +select json_merge('string', 123); + +select json_type('{"k1":123, "k2":345}'); +select json_type('[123, "k2", 345]'); +select json_type("true"); +select json_type('123'); + diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index b2b264e17fa..cfd7f23ed2b 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1451,3 +1451,123 @@ g codership/mysql-wsrep/issues#176 fun:start_thread fun:clone } + +# +# MDEV-11061: OpenSSL 0.9.8 problems +# + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Cond + obj:*/libz.so* + ... + obj:*/libcrypto.so.0.9.8 + ... + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Value8 + obj:*/libz.so* + ... + obj:*/libcrypto.so.0.9.8 + ... + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Cond + obj:*/libcrypto.so.0.9.8 + ... + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Value8 + obj:*/libcrypto.so.0.9.8 + ... + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Cond + obj:*/libssl.so.0.9.8 + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Value8 + obj:*/libssl.so.0.9.8 + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Cond + fun:memcpy + obj:*/libcrypto.so.0.9.8 + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Value8 + fun:memcpy + obj:*/libcrypto.so.0.9.8 + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Cond + fun:is_overlap + fun:memcpy + obj:*/libcrypto.so.0.9.8 + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Cond + fun:memset + obj:*/libcrypto.so.0.9.8 + ... + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Value8 + fun:memset + obj:*/libcrypto.so.0.9.8 + ... + obj:*/libssl.so.0.9.8 + ... +} + +{ + MDEV-11061: OpenSSL 0.9.8 + Memcheck:Param + write(buf) + obj:*/libpthread-2.9.so* + obj:*/libcrypto.so.0.9.8 + ... + obj:*/libssl.so.0.9.8 + ... +} + diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index 395659238b3..e22fb4162ec 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -38,13 +38,13 @@ static char *heap_start; -#ifdef HAVE_BSS_START +#if(defined HAVE_BSS_START) && !(defined __linux__) extern char *__bss_start; #endif void my_init_stacktrace() { -#ifdef HAVE_BSS_START +#if(defined HAVE_BSS_START) && !(defined __linux__) heap_start = (char*) &__bss_start; #endif } diff --git a/sql-common/client.c b/sql-common/client.c index 858e9ec4b5b..fc76fa976e0 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -569,16 +569,22 @@ err: Error message is set. @retval */ - ulong cli_safe_read(MYSQL *mysql) +{ + ulong reallen = 0; + return cli_safe_read_reallen(mysql, &reallen); +} + +ulong +cli_safe_read_reallen(MYSQL *mysql, ulong* reallen) { NET *net= &mysql->net; ulong len=0; restart: if (net->vio != 0) - len= my_net_read_packet(net, 0); + len= my_net_read_packet_reallen(net, 0, reallen); if (len == packet_error || len == 0) { diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 28072375bbc..d011b1a07d2 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -136,7 +136,7 @@ SET (SQL_SOURCE opt_table_elimination.cc sql_expression_cache.cc gcalc_slicescan.cc gcalc_tools.cc threadpool_common.cc ../sql-common/mysql_async.c - my_apc.cc my_apc.h mf_iocache_encr.cc + my_apc.cc my_apc.h mf_iocache_encr.cc item_jsonfunc.cc my_json_writer.cc my_json_writer.h rpl_gtid.cc rpl_parallel.cc sql_type.cc sql_type.h diff --git a/sql/item.cc b/sql/item.cc index 448e34b89e3..2388679e424 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2900,6 +2900,14 @@ void Item_int::print(String *str, enum_query_type query_type) } +Item *Item_bool::neg_transformer(THD *thd) +{ + value= !value; + name= 0; + return this; +} + + Item_uint::Item_uint(THD *thd, const char *str_arg, uint length): Item_int(thd, str_arg, length) { diff --git a/sql/item.h b/sql/item.h index 28a14454c0b..ab70fdb7dc1 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2997,6 +2997,21 @@ public: }; +/* + We sometimes need to distinguish a number from a boolean: + a[1] and a[true] are different things in XPath. + Also in JSON boolean values should be treated differently. +*/ +class Item_bool :public Item_int +{ +public: + Item_bool(THD *thd, const char *str_arg, longlong i): + Item_int(thd, str_arg, i, 1) {} + bool is_bool_type() { return true; } + Item *neg_transformer(THD *thd); +}; + + class Item_uint :public Item_int { public: @@ -4750,6 +4765,7 @@ public: #include "item_timefunc.h" #include "item_subselect.h" #include "item_xmlfunc.h" +#include "item_jsonfunc.h" #include "item_create.h" #endif diff --git a/sql/item_create.cc b/sql/item_create.cc index 7f0d4144177..cf6f24eddb7 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -1708,6 +1708,201 @@ protected: #endif +class Create_func_json_exists : public Create_func_arg2 +{ +public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_json_exists s_singleton; + +protected: + Create_func_json_exists() {} + virtual ~Create_func_json_exists() {} +}; + + +class Create_func_json_valid : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_json_valid s_singleton; + +protected: + Create_func_json_valid() {} + virtual ~Create_func_json_valid() {} +}; + + +class Create_func_json_type : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_json_type s_singleton; + +protected: + Create_func_json_type() {} + virtual ~Create_func_json_type() {} +}; + + +class Create_func_json_depth : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_json_depth s_singleton; + +protected: + Create_func_json_depth() {} + virtual ~Create_func_json_depth() {} +}; + + +class Create_func_json_value : public Create_func_arg2 +{ +public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_json_value s_singleton; + +protected: + Create_func_json_value() {} + virtual ~Create_func_json_value() {} +}; + + +class Create_func_json_query : public Create_func_arg2 +{ +public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_json_query s_singleton; + +protected: + Create_func_json_query() {} + virtual ~Create_func_json_query() {} +}; + + +class Create_func_json_contains: public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_json_contains s_singleton; + +protected: + Create_func_json_contains() {} + virtual ~Create_func_json_contains() {} +}; + + +class Create_func_json_contains_path : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_json_contains_path s_singleton; + +protected: + Create_func_json_contains_path() {} + virtual ~Create_func_json_contains_path() {} +}; + + +class Create_func_json_extract : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_json_extract s_singleton; + +protected: + Create_func_json_extract() {} + virtual ~Create_func_json_extract() {} +}; + + +class Create_func_json_array : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_json_array s_singleton; + +protected: + Create_func_json_array() {} + virtual ~Create_func_json_array() {} +}; + + +class Create_func_json_array_append : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_json_array_append s_singleton; + +protected: + Create_func_json_array_append() {} + virtual ~Create_func_json_array_append() {} +}; + + +class Create_func_json_object : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_json_object s_singleton; + +protected: + Create_func_json_object() {} + virtual ~Create_func_json_object() {} +}; + + +class Create_func_json_length : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_json_length s_singleton; + +protected: + Create_func_json_length() {} + virtual ~Create_func_json_length() {} +}; + + +class Create_func_json_merge : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_json_merge s_singleton; + +protected: + Create_func_json_merge() {} + virtual ~Create_func_json_merge() {} +}; + + +class Create_func_json_quote : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_json_quote s_singleton; + +protected: + Create_func_json_quote() {} + virtual ~Create_func_json_quote() {} +}; + + class Create_func_last_day : public Create_func_arg1 { public: @@ -4572,6 +4767,69 @@ Create_func_issimple::create_1_arg(THD *thd, Item *arg1) #endif +Create_func_json_exists Create_func_json_exists::s_singleton; + +Item* +Create_func_json_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_json_exists(thd, arg1, arg2); +} + + +Create_func_json_valid Create_func_json_valid::s_singleton; + +Item* +Create_func_json_valid::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_json_valid(thd, arg1); +} + + +Create_func_json_type Create_func_json_type::s_singleton; + +Item* +Create_func_json_type::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_json_type(thd, arg1); +} + + +Create_func_json_depth Create_func_json_depth::s_singleton; + +Item* +Create_func_json_depth::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_json_depth(thd, arg1); +} + + +Create_func_json_value Create_func_json_value::s_singleton; + +Item* +Create_func_json_value::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_json_value(thd, arg1, arg2); +} + + +Create_func_json_query Create_func_json_query::s_singleton; + +Item* +Create_func_json_query::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_json_query(thd, arg1, arg2); +} + + +Create_func_json_quote Create_func_json_quote::s_singleton; + +Item* +Create_func_json_quote::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_json_quote(thd, arg1); +} + + Create_func_last_day Create_func_last_day::s_singleton; Item* @@ -4581,6 +4839,207 @@ Create_func_last_day::create_1_arg(THD *thd, Item *arg1) } +Create_func_json_array Create_func_json_array::s_singleton; + +Item* +Create_func_json_array::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func; + + if (item_list != NULL) + { + func= new (thd->mem_root) Item_func_json_array(thd, *item_list); + } + else + { + func= new (thd->mem_root) Item_func_json_array(thd); + } + + return func; +} + + +Create_func_json_array_append Create_func_json_array_append::s_singleton; + +Item* +Create_func_json_array_append::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + } + else + { + func= new (thd->mem_root) Item_func_json_array_append(thd, *item_list); + } + + return func; +} + + +Create_func_json_object Create_func_json_object::s_singleton; + +Item* +Create_func_json_object::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func; + int arg_count; + + if (item_list != NULL) + { + arg_count= item_list->elements; + if ((arg_count & 1) != 0 /*is odd*/) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + func= NULL; + } + else + { + func= new (thd->mem_root) Item_func_json_object(thd, *item_list); + } + } + else + { + arg_count= 0; + func= new (thd->mem_root) Item_func_json_object(thd); + } + + return func; +} + + +Create_func_json_length Create_func_json_length::s_singleton; + +Item* +Create_func_json_length::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func; + int arg_count; + + if (item_list == NULL || + (arg_count= item_list->elements) == 0) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + func= NULL; + } + else + { + func= new (thd->mem_root) Item_func_json_length(thd, *item_list); + } + + return func; +} + + +Create_func_json_merge Create_func_json_merge::s_singleton; + +Item* +Create_func_json_merge::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func; + int arg_count; + + if (item_list == NULL || + (arg_count= item_list->elements) == 0) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + func= NULL; + } + else + { + func= new (thd->mem_root) Item_func_json_merge(thd, *item_list); + } + + return func; +} + + +Create_func_json_contains Create_func_json_contains::s_singleton; + +Item* +Create_func_json_contains::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 2 /* json_doc, val, [path]...*/) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + } + else + { + func= new (thd->mem_root) Item_func_json_contains(thd, *item_list); + } + + return func; +} + + +Create_func_json_contains_path Create_func_json_contains_path::s_singleton; + +Item* +Create_func_json_contains_path::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 3 /* json_doc, one_or_all, path, [path]...*/) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + } + else + { + func= new (thd->mem_root) Item_func_json_contains_path(thd, *item_list); + } + + return func; +} + + +Create_func_json_extract Create_func_json_extract::s_singleton; + +Item* +Create_func_json_extract::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 2 /* json_doc, path, [path]...*/) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + } + else + { + func= new (thd->mem_root) Item_func_json_extract(thd, *item_list); + } + + return func; +} + + Create_func_last_insert_id Create_func_last_insert_id::s_singleton; Item* @@ -5852,6 +6311,21 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)}, { { C_STRING_WITH_LEN("IS_FREE_LOCK") }, BUILDER(Create_func_is_free_lock)}, { { C_STRING_WITH_LEN("IS_USED_LOCK") }, BUILDER(Create_func_is_used_lock)}, + { { C_STRING_WITH_LEN("JSON_ARRAY") }, BUILDER(Create_func_json_array)}, + { { C_STRING_WITH_LEN("JSON_ARRAY_APPEND") }, BUILDER(Create_func_json_array_append)}, + { { C_STRING_WITH_LEN("JSON_CONTAINS") }, BUILDER(Create_func_json_contains)}, + { { C_STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)}, + { { C_STRING_WITH_LEN("JSON_DEPTH") }, BUILDER(Create_func_json_depth)}, + { { C_STRING_WITH_LEN("JSON_EXISTS") }, BUILDER(Create_func_json_exists)}, + { { C_STRING_WITH_LEN("JSON_EXTRACT") }, BUILDER(Create_func_json_extract)}, + { { C_STRING_WITH_LEN("JSON_LENGTH") }, BUILDER(Create_func_json_length)}, + { { C_STRING_WITH_LEN("JSON_MERGE") }, BUILDER(Create_func_json_merge)}, + { { C_STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)}, + { { C_STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)}, + { { C_STRING_WITH_LEN("JSON_OBJECT") }, BUILDER(Create_func_json_object)}, + { { C_STRING_WITH_LEN("JSON_TYPE") }, BUILDER(Create_func_json_type)}, + { { C_STRING_WITH_LEN("JSON_VALID") }, BUILDER(Create_func_json_valid)}, + { { C_STRING_WITH_LEN("JSON_VALUE") }, BUILDER(Create_func_json_value)}, { { C_STRING_WITH_LEN("LAST_DAY") }, BUILDER(Create_func_last_day)}, { { C_STRING_WITH_LEN("LAST_INSERT_ID") }, BUILDER(Create_func_last_insert_id)}, { { C_STRING_WITH_LEN("LCASE") }, BUILDER(Create_func_lcase)}, diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc new file mode 100644 index 00000000000..80713710927 --- /dev/null +++ b/sql/item_jsonfunc.cc @@ -0,0 +1,1109 @@ +/* Copyright (c) 2016, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#include +#include "sql_priv.h" +#include "sql_class.h" +#include "item.h" + + +/* + Compare ASCII string against the string with the specified + character set. + Only compares the equality, case insencitive. +*/ +static bool eq_ascii_string(const CHARSET_INFO *cs, + const char *ascii, + const char *s, uint32 s_len) +{ + const char *s_end= s + s_len; + + while (*ascii && s < s_end) + { + my_wc_t wc; + int wc_len; + + wc_len= cs->cset->mb_wc(cs, &wc, (uchar *) s, (uchar *) s_end); + if (wc_len <= 0 || (wc | 0x20) != (my_wc_t) *ascii) + return 0; + + ascii++; + s+= wc_len; + } + + return *ascii == 0 && s >= s_end; +} + + +/* + Appends arbitrary String to the JSON string taking charsets in + consideration. +*/ +static int st_append_escaped(String *s, const String *a) +{ + /* + In the worst case one character from the 'a' string + turns into '\uXXXX\uXXXX' which is 12. + */ + int str_len= a->length() * 12 * s->charset()->mbmaxlen / + a->charset()->mbminlen; + if (!s->reserve(str_len, 1024) && + (str_len= + json_escape(a->charset(), (uchar *) a->ptr(), (uchar *)a->end(), + s->charset(), + (uchar *) s->end(), (uchar *)s->end() + str_len)) > 0) + { + s->length(s->length() + str_len); + return 0; + } + + return a->length(); +} + + +longlong Item_func_json_valid::val_int() +{ + String *js= args[0]->val_str(&tmp_value); + json_engine_t je; + + if ((null_value= args[0]->null_value) || js == NULL) + return 0; + + json_scan_start(&je, js->charset(), (const uchar *) js->ptr(), + (const uchar *) js->ptr()+js->length()); + + while (json_scan_next(&je) == 0) {} + + return je.s.error == 0; +} + + +void Item_func_json_exists::fix_length_and_dec() +{ + Item_int_func::fix_length_and_dec(); + maybe_null= 1; + path.set_constant_flag(args[1]->const_item()); +} + + +longlong Item_func_json_exists::val_int() +{ + json_engine_t je; + uint array_counters[JSON_DEPTH_LIMIT]; + + String *js= args[0]->val_str(&tmp_js); + + if (!path.parsed) + { + String *s_p= args[1]->val_str(&tmp_path); + if (s_p && + json_path_setup(&path.p, s_p->charset(), (const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + goto err_return; + path.parsed= path.constant; + } + + if ((null_value= args[0]->null_value || args[1]->null_value)) + { + null_value= 1; + return 0; + } + + null_value= 0; + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + path.cur_step= path.p.steps; + if (json_find_path(&je, &path.p, &path.cur_step, array_counters)) + { + if (je.s.error) + goto err_return; + return 0; + } + + return 1; + +err_return: + null_value= 1; + return 0; +} + + +void Item_func_json_value::fix_length_and_dec() +{ + collation.set(args[0]->collation); + max_length= args[0]->max_length; + path.set_constant_flag(args[1]->const_item()); +} + + +/* + Returns NULL, not an error if the found value + is not a scalar. +*/ +String *Item_func_json_value::val_str(String *str) +{ + json_engine_t je; + String *js= args[0]->val_str(&tmp_js); + int error= 0; + uint array_counters[JSON_DEPTH_LIMIT]; + + if (!path.parsed) + { + String *s_p= args[1]->val_str(&tmp_path); + if (s_p && + json_path_setup(&path.p, s_p->charset(), (const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + goto err_return; + path.parsed= path.constant; + } + + if ((null_value= args[0]->null_value || args[1]->null_value)) + return NULL; + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + path.cur_step= path.p.steps; +continue_search: + if (json_find_path(&je, &path.p, &path.cur_step, array_counters)) + { + if (je.s.error) + goto err_return; + + null_value= 1; + return 0; + } + + if (json_read_value(&je)) + goto err_return; + + if (check_and_get_value(&je, str, &error)) + { + if (error) + goto err_return; + goto continue_search; + } + + return str; + +err_return: + null_value= 1; + return 0; +} + + +bool Item_func_json_value::check_and_get_value(json_engine_t *je, String *res, + int *error) +{ + if (!json_value_scalar(je)) + { + /* We only look for scalar values! */ + if (json_skip_level(je) || json_scan_next(je)) + *error= 1; + return true; + } + + res->set((const char *) je->value, je->value_len, je->s.cs); + return false; +} + + +bool Item_func_json_query::check_and_get_value(json_engine_t *je, String *res, + int *error) +{ + const uchar *value; + if (json_value_scalar(je)) + { + /* We skip scalar values. */ + if (json_scan_next(je)) + *error= 1; + return true; + } + + value= je->value; + if (json_skip_level(je)) + { + *error= 1; + return true; + } + + res->set((const char *) je->value, je->s.c_str - value, je->s.cs); + return false; +} + + +void Item_func_json_quote::fix_length_and_dec() +{ + collation.set(args[0]->collation); + /* + Odd but realistic worst case is when all characters + of the argument turn into '\uXXXX\uXXXX', which is 12. + */ + max_length= args[0]->max_length * 12; +} + + +String *Item_func_json_quote::val_str(String *str) +{ + String *s= args[0]->val_str(&tmp_s); + + if ((null_value= args[0]->null_value)) + return NULL; + + str->length(0); + str->set_charset(s->charset()); + + if (st_append_escaped(str, s)) + { + /* Report an error. */ + null_value= 1; + return 0; + } + + return str; +} + + +static int alloc_tmp_paths(THD *thd, uint n_paths, + json_path_with_flags **paths,String **tmp_paths) +{ + if (n_paths > 0) + { + *paths= (json_path_with_flags *) alloc_root(thd->mem_root, + sizeof(json_path_with_flags) * n_paths); + *tmp_paths= (String *) alloc_root(thd->mem_root, sizeof(String) * n_paths); + if (*paths == 0 || *tmp_paths == 0) + return 1; + + bzero(*tmp_paths, sizeof(String) * n_paths); + + return 0; + } + + /* n_paths == 0 */ + *paths= 0; + *tmp_paths= 0; + return 0; +} + + +static void mark_constant_paths(json_path_with_flags *p, + Item** args, uint n_args) +{ + uint n; + for (n= 0; n < n_args; n++) + p[n].set_constant_flag(args[n]->const_item()); +} + + +bool Item_json_str_multipath::fix_fields(THD *thd, Item **ref) +{ + return alloc_tmp_paths(thd, get_n_paths(), &paths, &tmp_paths) || + Item_str_func::fix_fields(thd, ref); +} + + +void Item_json_str_multipath::cleanup() +{ + if (tmp_paths) + { + for (uint i= get_n_paths(); i>0; i--) + tmp_paths[i-1].free(); + tmp_paths= 0; + } + Item_str_func::cleanup(); +} + + +void Item_func_json_extract::fix_length_and_dec() +{ + collation.set(args[0]->collation); + max_length= args[0]->max_length * (arg_count - 1); + + mark_constant_paths(paths, args+1, arg_count-1); +} + + +String *Item_func_json_extract::val_str(String *str) +{ + String *js= args[0]->val_str(&tmp_js); + json_engine_t je; + bool multiple_values_found= FALSE; + const uchar *value; + const char *first_value= NULL, *first_p_value; + uint n_arg, v_len, first_len, first_p_len; + uint array_counters[JSON_DEPTH_LIMIT]; + + if ((null_value= args[0]->null_value)) + return 0; + + str->set_charset(js->charset()); + str->length(0); + + for (n_arg=1; n_arg < arg_count; n_arg++) + { + json_path_with_flags *c_path= paths + n_arg - 1; + if (!c_path->parsed) + { + String *s_p= args[n_arg]->val_str(tmp_paths + (n_arg-1)); + if (s_p && + json_path_setup(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + goto error; + c_path->parsed= c_path->constant; + } + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + c_path->cur_step= c_path->p.steps; + + if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + { + /* Path wasn't found. */ + if (je.s.error) + goto error; + + continue; + } + + if (json_read_value(&je)) + goto error; + + value= je.value_begin; + if (json_value_scalar(&je)) + v_len= je.value_end - value; + else + { + if (json_skip_level(&je)) + goto error; + v_len= je.s.c_str - value; + } + + if (!multiple_values_found) + { + if (first_value == NULL) + { + /* + Just remember the first value as we don't know yet + if we need to create an array out of it or not. + */ + first_value= (const char *) value; + first_len= v_len; + /* + We need this as we have to preserve quotes around string + constants if we use the value to create an array. Otherwise + we get the value without the quotes. + */ + first_p_value= (const char *) je.value; + first_p_len= je.value_len; + continue; + } + else + { + multiple_values_found= TRUE; /* We have to make an JSON array. */ + if (str->append("[", 1) || + str->append(first_value, first_len)) + goto error; /* Out of memory. */ + } + + } + if (str->append(", ", 2) || + str->append((const char *) value, v_len)) + goto error; /* Out of memory. */ + } + + if (first_value == NULL) + { + /* Nothing was found. */ + null_value= 1; + return 0; + } + + if (multiple_values_found ? + str->append("]") : + str->append(first_p_value, first_p_len)) + goto error; /* Out of memory. */ + + return str; + +error: + /* TODO: launch error messages. */ + null_value= 1; + return 0; +} + + +longlong Item_func_json_extract::val_int() +{ + String *js= args[0]->val_str(&tmp_js); + json_engine_t je; + uint n_arg; + uint array_counters[JSON_DEPTH_LIMIT]; + + if ((null_value= args[0]->null_value)) + return 0; + + for (n_arg=1; n_arg < arg_count; n_arg++) + { + json_path_with_flags *c_path= paths + n_arg - 1; + if (!c_path->parsed) + { + String *s_p= args[n_arg]->val_str(tmp_paths+(n_arg-1)); + if (s_p && + json_path_setup(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + goto error; + c_path->parsed= c_path->constant; + } + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + c_path->cur_step= c_path->p.steps; + + if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + { + /* Path wasn't found. */ + if (je.s.error) + goto error; + + continue; + } + + if (json_read_value(&je)) + goto error; + + if (json_value_scalar(&je)) + { + int err; + char *v_end= (char *) je.value_end; + return (je.s.cs->cset->strtoll10)(je.s.cs, (const char *) je.value_begin, + &v_end, &err); + } + else + break; + } + + /* Nothing was found. */ + null_value= 1; + return 0; + +error: + /* TODO: launch error messages. */ + null_value= 1; + return 0; +} + + +bool Item_func_json_contains::fix_fields(THD *thd, Item **ref) +{ + return alloc_tmp_paths(thd, arg_count-2, &paths, &tmp_paths) || + Item_int_func::fix_fields(thd, ref); +} + + +void Item_func_json_contains::fix_length_and_dec() +{ + a2_constant= args[1]->const_item(); + a2_parsed= FALSE; + mark_constant_paths(paths, args+2, arg_count-2); + Item_int_func::fix_length_and_dec(); +} + + +void Item_func_json_contains::cleanup() +{ + if (tmp_paths) + { + for (uint i= arg_count-2; i>0; i--) + tmp_paths[i-1].free(); + tmp_paths= 0; + } + Item_int_func::cleanup(); +} + + +longlong Item_func_json_contains::val_int() +{ + String *js= args[0]->val_str(&tmp_js); + json_engine_t je; + uint n_arg; + + if ((null_value= args[0]->null_value)) + return 0; + + if (!a2_parsed) + { + val= args[1]->val_str(&tmp_val); + a2_parsed= a2_constant; + } + + if (val == 0) + { + null_value= 1; + return 0; + } + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + if (arg_count<3) /* No path specified. */ + { + if (json_read_value(&je)) + goto error; + String jv_str((const char *)je.value_begin, + je.value_end - je.value_begin, js->charset()); + return val->eq(&jv_str, js->charset()); + } + + for (n_arg=2; n_arg < arg_count; n_arg++) + { + uint array_counters[JSON_DEPTH_LIMIT]; + json_path_with_flags *c_path= paths + n_arg - 2; + if (!c_path->parsed) + { + String *s_p= args[n_arg]->val_str(tmp_paths+(n_arg-2)); + if (s_p && + json_path_setup(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + goto error; + c_path->parsed= c_path->constant; + } + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + c_path->cur_step= c_path->p.steps; + if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + { + /* Path wasn't found. */ + if (je.s.error) + goto error; + continue; + } + + if (json_read_value(&je)) + goto error; + String jv_str((const char *)je.value_begin, + je.value_end - je.value_begin, js->charset()); + if (val->eq(&jv_str, js->charset())) + return 1; + } + + + return 0; + +error: + null_value= 1; + return 0; +} + + +bool Item_func_json_contains_path::fix_fields(THD *thd, Item **ref) +{ + return alloc_tmp_paths(thd, arg_count-2, &paths, &tmp_paths) || + Item_int_func::fix_fields(thd, ref); +} + + +void Item_func_json_contains_path::fix_length_and_dec() +{ + ooa_constant= args[1]->const_item(); + ooa_parsed= FALSE; + mark_constant_paths(paths, args+2, arg_count-2); + Item_int_func::fix_length_and_dec(); +} + + +void Item_func_json_contains_path::cleanup() +{ + if (tmp_paths) + { + for (uint i= arg_count-2; i>0; i--) + tmp_paths[i-1].free(); + tmp_paths= 0; + } + Item_int_func::cleanup(); +} + + +longlong Item_func_json_contains_path::val_int() +{ + String *js= args[0]->val_str(&tmp_js); + json_engine_t je; + uint n_arg; + longlong result; + + if ((null_value= args[0]->null_value)) + return 0; + + if (!ooa_parsed) + { + char buff[20]; + String *res, tmp(buff, sizeof(buff), &my_charset_bin); + res= args[1]->val_str(&tmp); + mode_one=eq_ascii_string(res->charset(), "one", + res->ptr(), res->length()); + if (!mode_one) + { + if (!eq_ascii_string(res->charset(), "all", res->ptr(), res->length())) + goto error; + } + ooa_parsed= ooa_constant; + } + + result= !mode_one; + for (n_arg=2; n_arg < arg_count; n_arg++) + { + uint array_counters[JSON_DEPTH_LIMIT]; + json_path_with_flags *c_path= paths + n_arg - 2; + if (!c_path->parsed) + { + String *s_p= args[n_arg]->val_str(tmp_paths+(n_arg-2)); + if (s_p && + json_path_setup(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + goto error; + c_path->parsed= c_path->constant; + } + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + c_path->cur_step= c_path->p.steps; + if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + { + /* Path wasn't found. */ + if (je.s.error) + goto error; + + if (!mode_one) + { + result= 0; + break; + } + } + else if (mode_one) + { + result= 1; + break; + } + } + + + return result; + +error: + null_value= 1; + return 0; +} + + +static int append_json_value(String *str, Item *item, String *tmp_val) +{ + if (item->is_bool_type()) + { + longlong v_int= item->val_int(); + const char *t_f; + int t_f_len; + + if (item->null_value) + goto append_null; + + if (v_int) + { + t_f= "true"; + t_f_len= 4; + } + else + { + t_f= "false"; + t_f_len= 5; + } + + return str->append(t_f, t_f_len); + } + { + String *sv= item->val_str(tmp_val); + if (item->null_value) + goto append_null; + if (item->result_type() == STRING_RESULT) + { + return str->append("\"", 1) || + st_append_escaped(str, sv) || + str->append("\"", 1); + } + return st_append_escaped(str, sv); + } + +append_null: + return str->append("null", 4); +} + + +static int append_json_keyname(String *str, Item *item, String *tmp_val) +{ + String *sv= item->val_str(tmp_val); + if (item->null_value) + goto append_null; + + return str->append("\"", 1) || + st_append_escaped(str, sv) || + str->append("\": ", 3); + +append_null: + return str->append("\"\": ", 4); +} + + +void Item_func_json_array::fix_length_and_dec() +{ + ulonglong char_length= 4; + uint n_arg; + + if (agg_arg_charsets_for_string_result(collation, args, arg_count)) + return; + + for (n_arg=0 ; n_arg < arg_count ; n_arg++) + char_length+= args[n_arg]->max_char_length() + 2; + + fix_char_length_ulonglong(char_length); + tmp_val.set_charset(collation.collation); +} + + +String *Item_func_json_array::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + uint n_arg; + + str->length(0); + + if (str->append("[", 1) || + ((arg_count > 0) && append_json_value(str, args[0],&tmp_val))) + goto err_return; + + for (n_arg=1; n_arg < arg_count; n_arg++) + { + if (str->append(", ", 2) || + append_json_value(str, args[n_arg], &tmp_val)) + goto err_return; + } + + if (str->append("]", 1)) + goto err_return; + + return str; + +err_return: + /*TODO: Launch out of memory error. */ + null_value= 1; + return NULL; +} + + +void Item_func_json_array_append::fix_length_and_dec() +{ + uint n_arg; + ulonglong char_length; + + collation.set(args[0]->collation); + char_length= args[0]->max_char_length(); + + for (n_arg= 1; n_arg < arg_count; n_arg+= 2) + { + paths[n_arg-1].set_constant_flag(args[n_arg]->const_item()); + char_length+= args[n_arg+1]->max_char_length() + 4; + } + + fix_char_length_ulonglong(char_length); +} + + +String *Item_func_json_array_append::val_str(String *str) +{ + json_engine_t je; + String *js= args[0]->val_str(&tmp_js); + uint n_arg, n_path, str_rest_len; + const uchar *ar_end; + + DBUG_ASSERT(fixed == 1); + + if ((null_value= args[0]->null_value)) + return 0; + + for (n_arg=1, n_path=0; n_arg < arg_count; n_arg+=2, n_path++) + { + uint array_counters[JSON_DEPTH_LIMIT]; + json_path_with_flags *c_path= paths + n_path; + if (!c_path->parsed) + { + String *s_p= args[n_arg]->val_str(tmp_paths+n_path); + if (s_p && + json_path_setup(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + goto error; + c_path->parsed= c_path->constant; + } + if (args[n_arg]->null_value) + { + null_value= 1; + return 0; + } + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + c_path->cur_step= c_path->p.steps; + + if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + { + if (je.s.error) + goto error; + null_value= 1; + return 0; + } + + if (json_read_value(&je)) + goto error; + + if (je.value_type != JSON_VALUE_ARRAY) + { + /* Must be an array. */ + goto error; + } + + if (json_skip_level(&je)) + goto error; + + str->length(0); + str->set_charset(js->charset()); + if (str->reserve(js->length() + 8, 1024)) + goto error; /* Out of memory. */ + ar_end= je.s.c_str - je.sav_c_len; + str_rest_len= js->length() - (ar_end - (const uchar *) js->ptr()); + str->q_append(js->ptr(), ar_end-(const uchar *) js->ptr()); + str->append(", ", 2); + if (append_json_value(str, args[n_arg+1], &tmp_val)) + goto error; /* Out of memory. */ + + if (str->reserve(str_rest_len, 1024)) + goto error; /* Out of memory. */ + str->q_append((const char *) ar_end, str_rest_len); + } + + return str; + +error: + null_value= 1; + return 0; +} + + +String *Item_func_json_object::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + uint n_arg; + + str->length(0); + + if (str->append("{", 1) || + (arg_count > 0 && + (append_json_keyname(str, args[0], &tmp_val) || + append_json_value(str, args[1], &tmp_val)))) + goto err_return; + + for (n_arg=2; n_arg < arg_count; n_arg+=2) + { + if (str->append(", ", 2) || + append_json_keyname(str, args[n_arg], &tmp_val) || + append_json_value(str, args[n_arg+1], &tmp_val)) + goto err_return; + } + + if (str->append("}", 1)) + goto err_return; + + return str; + +err_return: + /*TODO: Launch out of memory error. */ + null_value= 1; + return NULL; +} + + +String *Item_func_json_merge::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + uint n_arg; + + str->length(0); + + if (str->append("[", 1) || + ((arg_count > 0) && append_json_value(str, args[0], &tmp_val))) + goto err_return; + + for (n_arg=1; n_arg < arg_count; n_arg++) + { + if (str->append(", ", 2) || + append_json_value(str, args[n_arg], &tmp_val)) + goto err_return; + } + + if (str->append("]", 1)) + goto err_return; + + return str; + +err_return: + /*TODO: Launch out of memory error. */ + null_value= 1; + return NULL; +} + + +longlong Item_func_json_length::val_int() +{ + String *js= args[0]->val_str(&tmp_js); + json_engine_t je; + uint length= 0; + + if ((null_value= args[0]->null_value)) + return 0; + + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + do + { + if (je.state == JST_VALUE) + length++; + } while (json_scan_next(&je) == 0); + + if (je.s.error) + { + null_value= 1; + return 0; + } + + return length; + +} + + +longlong Item_func_json_depth::val_int() +{ + String *js= args[0]->val_str(&tmp_js); + json_engine_t je; + uint depth= 0; + bool inc_depth= TRUE; + + if ((null_value= args[0]->null_value)) + return 0; + + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + do + { + switch (je.state) + { + case JST_VALUE: + if (inc_depth) + { + depth++; + inc_depth= FALSE; + } + break; + case JST_OBJ_START: + case JST_ARRAY_START: + inc_depth= TRUE; + break; + default: + break; + } + } while (json_scan_next(&je) == 0); + + if (je.s.error) + { + null_value= 1; + return 0; + } + + return depth; +} + + +void Item_func_json_type::fix_length_and_dec() +{ + collation.set(&my_charset_utf8_general_ci); + max_length= 12; +} + + +String *Item_func_json_type::val_str(String *str) +{ + String *js= args[0]->val_str(&tmp_js); + json_engine_t je; + const char *type; + + if ((null_value= args[0]->null_value)) + return 0; + + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + if (json_read_value(&je)) + goto error; + + switch (je.value_type) + { + case JSON_VALUE_OBJECT: + type= "OBJECT"; + break; + case JSON_VALUE_ARRAY: + type= "ARRAY"; + break; + case JSON_VALUE_STRING: + type= "STRING"; + break; + case JSON_VALUE_NUMBER: + type= "NUMBER"; + break; + case JSON_VALUE_TRUE: + case JSON_VALUE_FALSE: + type= "BOOLEAN"; + break; + default: + type= "NULL"; + break; + } + + str->set(type, strlen(type), &my_charset_utf8_general_ci); + return str; + +error: + null_value= 1; + return 0; +} + + diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h new file mode 100644 index 00000000000..54da67b5ab9 --- /dev/null +++ b/sql/item_jsonfunc.h @@ -0,0 +1,304 @@ +#ifndef ITEM_JSONFUNC_INCLUDED +#define ITEM_JSONFUNC_INCLUDED + +/* Copyright (c) 2016, 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 + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +/* This file defines all JSON functions */ + + +#include +#include "item_cmpfunc.h" // Item_bool_func +#include "item_strfunc.h" // Item_str_func + + +class json_path_with_flags +{ +public: + json_path_t p; + bool constant; + bool parsed; + json_path_step_t *cur_step; + void set_constant_flag(bool s_constant) + { + constant= s_constant; + parsed= FALSE; + } +}; + + +class Item_func_json_valid: public Item_int_func +{ +protected: + String tmp_value; + +public: + Item_func_json_valid(THD *thd, Item *json) : Item_int_func(thd, json) {} + longlong val_int(); + const char *func_name() const { return "json_valid"; } + void fix_length_and_dec() + { + Item_int_func::fix_length_and_dec(); + maybe_null= 1; + } + bool is_bool_type() { return true; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_exists: public Item_int_func +{ +protected: + json_path_with_flags path; + String tmp_js, tmp_path; + +public: + Item_func_json_exists(THD *thd, Item *js, Item *path): + Item_int_func(thd, js, path) {} + const char *func_name() const { return "json_exists"; } + bool is_bool_type() { return true; } + void fix_length_and_dec(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } + longlong val_int(); +}; + + +class Item_func_json_value: public Item_str_func +{ +protected: + json_path_with_flags path; + String tmp_js, tmp_path; + +public: + Item_func_json_value(THD *thd, Item *js, Item *path): + Item_str_func(thd, js, path) {} + const char *func_name() const { return "json_value"; } + void fix_length_and_dec(); + String *val_str(String *); + virtual bool check_and_get_value(json_engine_t *je, String *res, int *error); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_query: public Item_func_json_value +{ +public: + Item_func_json_query(THD *thd, Item *js, Item *path): + Item_func_json_value(thd, js, path) {} + const char *func_name() const { return "json_query"; } + bool check_and_get_value(json_engine_t *je, String *res, int *error); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_quote: public Item_str_func +{ +protected: + String tmp_s; + +public: + Item_func_json_quote(THD *thd, Item *s): Item_str_func(thd, s) {} + const char *func_name() const { return "json_quote"; } + void fix_length_and_dec(); + String *val_str(String *); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_json_str_multipath: public Item_str_func +{ +protected: + json_path_with_flags *paths; + String *tmp_paths; +public: + Item_json_str_multipath(THD *thd, List &list): + Item_str_func(thd, list), tmp_paths(0) {} + bool fix_fields(THD *thd, Item **ref); + void cleanup(); + virtual uint get_n_paths() const = 0; +}; + + +class Item_func_json_extract: public Item_json_str_multipath +{ +protected: + String tmp_js; +public: + Item_func_json_extract(THD *thd, List &list): + Item_json_str_multipath(thd, list) {} + const char *func_name() const { return "json_extract"; } + void fix_length_and_dec(); + String *val_str(String *); + longlong val_int(); + uint get_n_paths() const { return arg_count - 1; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_contains: public Item_int_func +{ +protected: + String tmp_js; + json_path_with_flags *paths; + String *tmp_paths; + bool a2_constant, a2_parsed; + String tmp_val, *val; +public: + Item_func_json_contains(THD *thd, List &list): + Item_int_func(thd, list), tmp_paths(0) {} + const char *func_name() const { return "json_contains"; } + bool fix_fields(THD *thd, Item **ref); + void fix_length_and_dec(); + void cleanup(); + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_contains_path: public Item_int_func +{ +protected: + String tmp_js; + json_path_with_flags *paths; + String *tmp_paths; + bool mode_one; + bool ooa_constant, ooa_parsed; + +public: + Item_func_json_contains_path(THD *thd, List &list): + Item_int_func(thd, list), tmp_paths(0) {} + const char *func_name() const { return "json_contains_path"; } + bool fix_fields(THD *thd, Item **ref); + void fix_length_and_dec(); + void cleanup(); + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_array: public Item_str_func +{ +protected: + String tmp_val; +public: + Item_func_json_array(THD *thd): + Item_str_func(thd) {} + Item_func_json_array(THD *thd, List &list): + Item_str_func(thd, list) {} + String *val_str(String *); + void fix_length_and_dec(); + const char *func_name() const { return "json_array"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_array_append: public Item_json_str_multipath +{ +protected: + String tmp_js; + String tmp_val; +public: + Item_func_json_array_append(THD *thd, List &list): + Item_json_str_multipath(thd, list) {} + void fix_length_and_dec(); + String *val_str(String *); + uint get_n_paths() const { return arg_count/2; } + const char *func_name() const { return "json_array_append"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_object: public Item_func_json_array +{ +public: + Item_func_json_object(THD *thd): + Item_func_json_array(thd) {} + Item_func_json_object(THD *thd, List &list): + Item_func_json_array(thd, list) {} + String *val_str(String *); + const char *func_name() const { return "json_object"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_merge: public Item_func_json_array +{ +protected: + String tmp_val; +public: + Item_func_json_merge(THD *thd, List &list): + Item_func_json_array(thd, list) {} + String *val_str(String *); + const char *func_name() const { return "json_merge"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_length: public Item_int_func +{ +protected: + String tmp_js; + String tmp_path; +public: + Item_func_json_length(THD *thd, List &list): + Item_int_func(thd, list) {} + const char *func_name() const { return "json_length"; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_depth: public Item_int_func +{ +protected: + String tmp_js; +public: + Item_func_json_depth(THD *thd, Item *js): Item_int_func(thd, js) {} + const char *func_name() const { return "json_depth"; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +class Item_func_json_type: public Item_str_func +{ +protected: + String tmp_js; +public: + Item_func_json_type(THD *thd, Item *js): Item_str_func(thd, js) {} + const char *func_name() const { return "json_type"; } + void fix_length_and_dec(); + String *val_str(String *); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +#endif /* ITEM_JSONFUNC_INCLUDED */ diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index cbbdeea0205..4d848a0f737 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -13,10 +13,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef __GNUC__ -#pragma implementation -#endif - #include #include "sql_priv.h" /* @@ -406,19 +402,6 @@ public: }; -/* - We need to distinguish a number from a boolean: - a[1] and a[true] are different things in XPath. -*/ -class Item_bool :public Item_int -{ -public: - Item_bool(THD *thd, int32 i): Item_int(thd, i) {} - const char *func_name() const { return "xpath_bool"; } - bool is_bool_type() { return true; } -}; - - /* Converts its argument into a boolean value. * a number is true if it is non-zero @@ -1214,13 +1197,13 @@ my_xpath_keyword(MY_XPATH *x, static Item *create_func_true(MY_XPATH *xpath, Item **args, uint nargs) { - return new (xpath->thd->mem_root) Item_bool(xpath->thd, 1); + return new (xpath->thd->mem_root) Item_bool(xpath->thd, "xpath_bool", 1); } static Item *create_func_false(MY_XPATH *xpath, Item **args, uint nargs) { - return new (xpath->thd->mem_root) Item_bool(xpath->thd, 0); + return new (xpath->thd->mem_root) Item_bool(xpath->thd, "xpath_bool", 0); } diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index 3c58955c96a..3c071b897e2 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -21,11 +21,6 @@ /* This file defines all XML functions */ -#ifdef USE_PRAGMA_INTERFACE -#pragma interface /* gcc class implementation */ -#endif - - typedef struct my_xml_node_st MY_XML_NODE; diff --git a/sql/lex.h b/sql/lex.h index dfb874e3463..527ddb81c4b 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -343,6 +343,7 @@ static SYMBOL symbols[] = { { "LOW_PRIORITY", SYM(LOW_PRIORITY)}, { "MASTER", SYM(MASTER_SYM)}, { "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM)}, + { "MASTER_DELAY", SYM(MASTER_DELAY_SYM)}, { "MASTER_GTID_POS", SYM(MASTER_GTID_POS_SYM)}, { "MASTER_HOST", SYM(MASTER_HOST_SYM)}, { "MASTER_LOG_FILE", SYM(MASTER_LOG_FILE_SYM)}, diff --git a/sql/log.cc b/sql/log.cc index 569942ac485..2c290715741 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -4304,6 +4304,10 @@ void MYSQL_BIN_LOG::wait_for_last_checkpoint_event() relay log. IMPLEMENTATION + + - You must hold rli->data_lock before calling this function, since + it writes group_relay_log_pos and similar fields of + Relay_log_info. - Protects index file with LOCK_index - Delete relevant relay log files - Copy all file names after these ones to the front of the index file @@ -4317,7 +4321,7 @@ void MYSQL_BIN_LOG::wait_for_last_checkpoint_event() read by the SQL slave thread are deleted). @note - - This is only called from the slave-execute thread when it has read + - This is only called from the slave SQL thread when it has read all commands from a relay log and want to switch to a new relay log. - When this happens, we can be in an active transaction as a transaction can span over two relay logs @@ -4348,6 +4352,8 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) DBUG_ASSERT(rli->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT); DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name)); + mysql_mutex_assert_owner(&rli->data_lock); + mysql_mutex_lock(&LOCK_index); ir= rli->inuse_relaylog_list; @@ -4406,7 +4412,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) } /* Store where we are in the new file for the execution thread */ - flush_relay_log_info(rli); + rli->flush(); DBUG_EXECUTE_IF("crash_before_purge_logs", DBUG_SUICIDE();); diff --git a/sql/log_event.cc b/sql/log_event.cc index 60d63a416e3..01bb5e7a561 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -966,6 +966,7 @@ int Log_event::do_update_pos(rpl_group_info *rgi) Relay_log_info *rli= rgi->rli; DBUG_ENTER("Log_event::do_update_pos"); + DBUG_ASSERT(!rli->belongs_to_client()); /* rli is null when (as far as I (Guilhem) know) the caller is Load_log_event::do_apply_event *and* that one is called from @@ -6403,6 +6404,9 @@ bool Rotate_log_event::write() in a A -> B -> A setup. The NOTES below is a wrong comment which will disappear when 4.1 is merged. + This must only be called from the Slave SQL thread, since it calls + Relay_log_info::flush(). + @retval 0 ok */ @@ -6456,7 +6460,7 @@ int Rotate_log_event::do_update_pos(rpl_group_info *rgi) (ulong) rli->group_master_log_pos)); mysql_mutex_unlock(&rli->data_lock); rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi); - flush_relay_log_info(rli); + rli->flush(); /* Reset thd->variables.option_bits and sql_mode etc, because this could @@ -8225,6 +8229,9 @@ void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) were we must do this cleaning is in Start_log_event_v3::do_apply_event(), not here. Because if we come here, the master was sane. + + This must only be called from the Slave SQL thread, since it calls + Relay_log_info::flush(). */ int Stop_log_event::do_update_pos(rpl_group_info *rgi) @@ -8244,7 +8251,7 @@ int Stop_log_event::do_update_pos(rpl_group_info *rgi) { rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi); rli->inc_group_relay_log_pos(0, rgi); - flush_relay_log_info(rli); + rli->flush(); } DBUG_RETURN(0); } diff --git a/sql/net_serv.cc b/sql/net_serv.cc index fccc947f3f1..581bc12b35b 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1133,15 +1133,22 @@ ulong my_net_read(NET *net) The function returns the length of the found packet or packet_error. net->read_pos points to the read data. */ +ulong +my_net_read_packet(NET *net, my_bool read_from_server) +{ + ulong reallen = 0; + return my_net_read_packet_reallen(net, read_from_server, &reallen); +} ulong -my_net_read_packet(NET *net, my_bool read_from_server) +my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen) { size_t len, complen; MYSQL_NET_READ_START(); + *reallen = 0; #ifdef HAVE_COMPRESS if (!net->compress) { @@ -1164,7 +1171,10 @@ my_net_read_packet(NET *net, my_bool read_from_server) } net->read_pos = net->buff + net->where_b; if (len != packet_error) + { net->read_pos[len]=0; /* Safeguard for mysql_use_result */ + *reallen = len; + } MYSQL_NET_READ_DONE(0, len); return len; #ifdef HAVE_COMPRESS @@ -1265,6 +1275,7 @@ my_net_read_packet(NET *net, my_bool read_from_server) return packet_error; } buf_length+= complen; + *reallen += packet_len; } net->read_pos= net->buff+ first_packet_offset + NET_HEADER_SIZE; diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 6048d26998b..fd04e233d35 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -18,7 +18,7 @@ #include "sql_priv.h" #include #include "rpl_mi.h" -#include "slave.h" // SLAVE_MAX_HEARTBEAT_PERIOD +#include "slave.h" #include "strfunc.h" #include "sql_repl.h" @@ -647,7 +647,7 @@ file '%s')", fname); (ulong) mi->master_log_pos)); mi->rli.mi= mi; - if (init_relay_log_info(&mi->rli, slave_info_fname)) + if (mi->rli.init(slave_info_fname)) goto err; mi->inited = 1; diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 8cd7474cb73..eeb96d8608a 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -47,9 +47,7 @@ rpt_handle_event(rpl_parallel_thread::queued_event *qev, if (!(ev->is_artificial_event() || ev->is_relay_log_event() || (ev->when == 0))) rgi->last_master_timestamp= ev->when + (time_t)ev->exec_time; - mysql_mutex_lock(&rli->data_lock); - /* Mutex will be released in apply_event_and_update_pos(). */ - err= apply_event_and_update_pos(ev, thd, rgi, rpt); + err= apply_event_and_update_pos_for_parallel(ev, thd, rgi); thread_safe_increment64(&rli->executed_entries); /* ToDo: error handling. */ @@ -2426,8 +2424,17 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, !(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); + /* Note: rli->data_lock is released by sql_delay_event(). */ + if (sql_delay_event(ev, rli->sql_driver_thd, serial_rgi)) + { + /* + If sql_delay_event() returns non-zero, it means that the wait timed out + due to slave stop. We should not queue the event in this case, it must + not be applied yet. + */ + delete ev; + return 1; + } if (unlikely(typ == FORMAT_DESCRIPTION_EVENT)) { diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index de6e37aecaf..9e2977c8bc5 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -29,6 +29,7 @@ #include "rpl_utility.h" #include "transaction.h" #include "sql_parse.h" // end_trans, ROLLBACK +#include "slave.h" #include #include @@ -42,30 +43,24 @@ rpl_slave_state *rpl_global_gtid_slave_state; /* Object used for MASTER_GTID_WAIT(). */ gtid_waiting rpl_global_gtid_waiting; - -// Defined in slave.cc -int init_intvar_from_file(int* var, IO_CACHE* f, int default_val); -int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, - const char *default_val); +const char *const Relay_log_info::state_delaying_string = "Waiting until MASTER_DELAY seconds after master executed event"; Relay_log_info::Relay_log_info(bool is_slave_recovery) :Slave_reporting_capability("SQL"), - no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id), + replicate_same_server_id(::replicate_same_server_id), info_fd(-1), cur_log_fd(-1), relay_log(&sync_relaylog_period), sync_counter(0), is_relay_log_recovery(is_slave_recovery), save_temporary_tables(0), mi(0), inuse_relaylog_list(0), last_inuse_relaylog(0), cur_log_old_open_count(0), group_relay_log_pos(0), event_relay_log_pos(0), -#if HAVE_valgrind - is_fake(FALSE), -#endif 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(), gtid_skip_flag(GTID_SKIP_NOT), inited(0), abort_slave(0), stop_for_until(0), slave_running(MYSQL_SLAVE_NOT_RUN), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), executed_entries(0), + sql_delay(0), sql_delay_end(0), m_flags(0) { DBUG_ENTER("Relay_log_info::Relay_log_info"); @@ -117,39 +112,42 @@ Relay_log_info::~Relay_log_info() } -int init_relay_log_info(Relay_log_info* rli, - const char* info_fname) +/** + Read the relay_log.info file. + + @param info_fname The name of the file to read from. + @retval 0 success + @retval 1 failure +*/ +int Relay_log_info::init(const char* info_fname) { char fname[FN_REFLEN+128]; - int info_fd; const char* msg = 0; int error = 0; - DBUG_ENTER("init_relay_log_info"); - DBUG_ASSERT(!rli->no_storage); // Don't init if there is no storage + DBUG_ENTER("Relay_log_info::init"); - if (rli->inited) // Set if this function called + if (inited) // Set if this function called DBUG_RETURN(0); fn_format(fname, info_fname, mysql_data_home, "", 4+32); - mysql_mutex_lock(&rli->data_lock); - info_fd = rli->info_fd; - rli->cur_log_fd = -1; - rli->slave_skip_counter=0; - rli->abort_pos_wait=0; - rli->log_space_limit= relay_log_space_limit; - rli->log_space_total= 0; + mysql_mutex_lock(&data_lock); + cur_log_fd = -1; + slave_skip_counter=0; + abort_pos_wait=0; + log_space_limit= relay_log_space_limit; + log_space_total= 0; char pattern[FN_REFLEN]; (void) my_realpath(pattern, slave_load_tmpdir, 0); if (fn_format(pattern, PREFIX_SQL_LOAD, pattern, "", MY_SAFE_PATH | MY_RETURN_REAL_PATH) == NullS) { - mysql_mutex_unlock(&rli->data_lock); + mysql_mutex_unlock(&data_lock); sql_print_error("Unable to use slave's temporary directory %s", slave_load_tmpdir); DBUG_RETURN(1); } - unpack_filename(rli->slave_patternload_file, pattern); - rli->slave_patternload_file_size= strlen(rli->slave_patternload_file); + unpack_filename(slave_patternload_file, pattern); + slave_patternload_file_size= strlen(slave_patternload_file); /* The relay log will now be opened, as a SEQ_READ_APPEND IO_CACHE. @@ -163,7 +161,7 @@ int init_relay_log_info(Relay_log_info* rli, if (opt_relay_logname && opt_relay_logname[strlen(opt_relay_logname) - 1] == FN_LIBCHAR) { - mysql_mutex_unlock(&rli->data_lock); + mysql_mutex_unlock(&data_lock); sql_print_error("Path '%s' is a directory name, please specify \ a file name for --relay-log option", opt_relay_logname); DBUG_RETURN(1); @@ -175,7 +173,7 @@ a file name for --relay-log option", opt_relay_logname); opt_relaylog_index_name[strlen(opt_relaylog_index_name) - 1] == FN_LIBCHAR) { - mysql_mutex_unlock(&rli->data_lock); + mysql_mutex_unlock(&data_lock); sql_print_error("Path '%s' is a directory name, please specify \ a file name for --relay-log-index option", opt_relaylog_index_name); DBUG_RETURN(1); @@ -184,7 +182,7 @@ a file name for --relay-log-index option", opt_relaylog_index_name); char buf[FN_REFLEN]; const char *ln; static bool name_warning_sent= 0; - ln= rli->relay_log.generate_name(opt_relay_logname, "-relay-bin", + ln= relay_log.generate_name(opt_relay_logname, "-relay-bin", 1, buf); /* We send the warning only at startup, not after every RESET SLAVE */ if (!opt_relay_logname && !opt_relaylog_index_name && !name_warning_sent && @@ -207,7 +205,6 @@ a file name for --relay-log-index option", opt_relaylog_index_name); } /* For multimaster, add connection name to relay log filenames */ - Master_info* mi= rli->mi; char buf_relay_logname[FN_REFLEN], buf_relaylog_index_name_buff[FN_REFLEN]; char *buf_relaylog_index_name= opt_relaylog_index_name; @@ -229,12 +226,12 @@ a file name for --relay-log-index option", opt_relaylog_index_name); note, that if open() fails, we'll still have index file open but a destructor will take care of that */ - if (rli->relay_log.open_index_file(buf_relaylog_index_name, ln, TRUE) || - rli->relay_log.open(ln, LOG_BIN, 0, 0, SEQ_READ_APPEND, - mi->rli.max_relay_log_size, 1, TRUE)) + if (relay_log.open_index_file(buf_relaylog_index_name, ln, TRUE) || + relay_log.open(ln, LOG_BIN, 0, 0, SEQ_READ_APPEND, + max_relay_log_size, 1, TRUE)) { - mysql_mutex_unlock(&rli->data_lock); - sql_print_error("Failed when trying to open logs for '%s' in init_relay_log_info(). Error: %M", ln, my_errno); + mysql_mutex_unlock(&data_lock); + sql_print_error("Failed when trying to open logs for '%s' in Relay_log_info::init(). Error: %M", ln, my_errno); DBUG_RETURN(1); } } @@ -256,7 +253,7 @@ file '%s', errno %d)", fname, my_errno); msg= current_thd->get_stmt_da()->message(); goto err; } - if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, + if (init_io_cache(&info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, MYF(MY_WME))) { sql_print_error("Failed to create a cache on relay log info file '%s'", @@ -266,20 +263,19 @@ file '%s', errno %d)", fname, my_errno); } /* Init relay log with first entry in the relay index file */ - if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, + if (init_relay_log_pos(this,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, &msg, 0)) { sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)"); goto err; } - rli->group_master_log_name[0]= 0; - rli->group_master_log_pos= 0; - rli->info_fd= info_fd; + group_master_log_name[0]= 0; + group_master_log_pos= 0; } else // file exists { if (info_fd >= 0) - reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0); + reinit_io_cache(&info_file, READ_CACHE, 0L,0,0); else { int error=0; @@ -291,7 +287,7 @@ Failed to open the existing relay log info file '%s' (errno %d)", fname, my_errno); error= 1; } - else if (init_io_cache(&rli->info_file, info_fd, + else if (init_io_cache(&info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME))) { sql_print_error("Failed to create a cache on relay log info file '%s'", @@ -302,24 +298,15 @@ Failed to open the existing relay log info file '%s' (errno %d)", { if (info_fd >= 0) mysql_file_close(info_fd, MYF(0)); - rli->info_fd= -1; - rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); - mysql_mutex_unlock(&rli->data_lock); + info_fd= -1; + relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); + mysql_mutex_unlock(&data_lock); DBUG_RETURN(1); } } - rli->info_fd = info_fd; int relay_log_pos, master_log_pos, lines; char *first_non_digit; - /* - In MySQL 5.6, there is a MASTER_DELAY option to CHANGE MASTER. This is - not yet merged into MariaDB (as of 10.0.13). However, we detect the - presense of the new option in relay-log.info, as a placeholder for - possible later merge of the feature, and to maintain file format - compatibility with MySQL 5.6+. - */ - int dummy_sql_delay; /* Starting from MySQL 5.6.x, relay-log.info has a new format. @@ -344,25 +331,25 @@ Failed to open the existing relay log info file '%s' (errno %d)", it is line count and not binlog name (new format) it will be overwritten by the second row later. */ - if (init_strvar_from_file(rli->group_relay_log_name, - sizeof(rli->group_relay_log_name), - &rli->info_file, "")) + if (init_strvar_from_file(group_relay_log_name, + sizeof(group_relay_log_name), + &info_file, "")) { msg="Error reading slave log configuration"; goto err; } - lines= strtoul(rli->group_relay_log_name, &first_non_digit, 10); + lines= strtoul(group_relay_log_name, &first_non_digit, 10); - if (rli->group_relay_log_name[0] != '\0' && + if (group_relay_log_name[0] != '\0' && *first_non_digit == '\0' && lines >= LINES_IN_RELAY_LOG_INFO_WITH_DELAY) { DBUG_PRINT("info", ("relay_log_info file is in new format.")); /* Seems to be new format => read relay log name from next line */ - if (init_strvar_from_file(rli->group_relay_log_name, - sizeof(rli->group_relay_log_name), - &rli->info_file, "")) + if (init_strvar_from_file(group_relay_log_name, + sizeof(group_relay_log_name), + &info_file, "")) { msg="Error reading slave log configuration"; goto err; @@ -372,70 +359,70 @@ Failed to open the existing relay log info file '%s' (errno %d)", DBUG_PRINT("info", ("relay_log_info file is in old format.")); if (init_intvar_from_file(&relay_log_pos, - &rli->info_file, BIN_LOG_HEADER_SIZE) || - init_strvar_from_file(rli->group_master_log_name, - sizeof(rli->group_master_log_name), - &rli->info_file, "") || - init_intvar_from_file(&master_log_pos, &rli->info_file, 0) || + &info_file, BIN_LOG_HEADER_SIZE) || + init_strvar_from_file(group_master_log_name, + sizeof(group_master_log_name), + &info_file, "") || + init_intvar_from_file(&master_log_pos, &info_file, 0) || (lines >= LINES_IN_RELAY_LOG_INFO_WITH_DELAY && - init_intvar_from_file(&dummy_sql_delay, &rli->info_file, 0))) + init_intvar_from_file(&sql_delay, &info_file, 0))) { msg="Error reading slave log configuration"; goto err; } - strmake_buf(rli->event_relay_log_name,rli->group_relay_log_name); - rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos; - rli->group_master_log_pos= master_log_pos; + strmake_buf(event_relay_log_name,group_relay_log_name); + group_relay_log_pos= event_relay_log_pos= relay_log_pos; + group_master_log_pos= master_log_pos; - if (rli->is_relay_log_recovery && init_recovery(rli->mi, &msg)) + if (is_relay_log_recovery && init_recovery(mi, &msg)) goto err; - rli->relay_log_state.load(rpl_global_gtid_slave_state); - if (init_relay_log_pos(rli, - rli->group_relay_log_name, - rli->group_relay_log_pos, + relay_log_state.load(rpl_global_gtid_slave_state); + if (init_relay_log_pos(this, + group_relay_log_name, + group_relay_log_pos, 0 /* no data lock*/, &msg, 0)) { sql_print_error("Failed to open the relay log '%s' (relay_log_pos %llu)", - rli->group_relay_log_name, rli->group_relay_log_pos); + group_relay_log_name, group_relay_log_pos); goto err; } } - DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%llu rli->event_relay_log_pos=%llu", - my_b_tell(rli->cur_log), rli->event_relay_log_pos)); - DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); + DBUG_PRINT("info", ("my_b_tell(cur_log)=%llu event_relay_log_pos=%llu", + my_b_tell(cur_log), event_relay_log_pos)); + DBUG_ASSERT(event_relay_log_pos >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(cur_log) == event_relay_log_pos); /* Now change the cache from READ to WRITE - must do this - before flush_relay_log_info + before Relay_log_info::flush() */ - reinit_io_cache(&rli->info_file, WRITE_CACHE,0L,0,1); - if ((error= flush_relay_log_info(rli))) + reinit_io_cache(&info_file, WRITE_CACHE,0L,0,1); + if ((error= flush())) { msg= "Failed to flush relay log info file"; goto err; } - if (count_relay_log_space(rli)) + if (count_relay_log_space(this)) { msg="Error counting relay log space"; goto err; } - rli->inited= 1; - mysql_mutex_unlock(&rli->data_lock); + inited= 1; + mysql_mutex_unlock(&data_lock); DBUG_RETURN(error); err: sql_print_error("%s", msg); - end_io_cache(&rli->info_file); + end_io_cache(&info_file); if (info_fd >= 0) mysql_file_close(info_fd, MYF(0)); - rli->info_fd= -1; - rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); - mysql_mutex_unlock(&rli->data_lock); + info_fd= -1; + relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); + mysql_mutex_unlock(&data_lock); DBUG_RETURN(1); } @@ -752,6 +739,8 @@ err: if (!rli->relay_log.description_event_for_exec->is_valid() && !*errmsg) *errmsg= "Invalid Format_description log event; could be out of memory"; + DBUG_PRINT("info", ("Returning %d from init_relay_log_pos", (*errmsg)?1:0)); + DBUG_RETURN ((*errmsg) ? 1 : 0); } @@ -969,8 +958,11 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, { DBUG_ENTER("Relay_log_info::inc_group_relay_log_pos"); - if (!skip_lock) + if (skip_lock) + mysql_mutex_assert_owner(&data_lock); + else mysql_mutex_lock(&data_lock); + rgi->inc_event_relay_log_pos(); DBUG_PRINT("info", ("log_pos: %lu group_master_log_pos: %lu", (long) log_pos, (long) group_master_log_pos)); @@ -1134,10 +1126,10 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, Indeed, rli->inited==0 does not imply that they already are empty. It could be that slave's info initialization partly succeeded : for example if relay-log.info existed but *relay-bin*.* - have been manually removed, init_relay_log_info reads the old - relay-log.info and fills rli->master_log_*, then init_relay_log_info + have been manually removed, Relay_log_info::init() reads the old + relay-log.info and fills rli->master_log_*, then Relay_log_info::init() checks for the existence of the relay log, this fails and - init_relay_log_info leaves rli->inited to 0. + Relay_log_info::init() leaves rli->inited to 0. In that pathological case, rli->master_log_pos* will be properly reinited at the next START SLAVE (as RESET SLAVE or CHANGE MASTER, the callers of purge_relay_logs, will delete bogus *.info files @@ -1325,6 +1317,7 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd, { DBUG_ENTER("Relay_log_info::stmt_done"); + DBUG_ASSERT(!belongs_to_client()); DBUG_ASSERT(rgi->rli == this); /* If in a transaction, and if the slave supports transactions, just @@ -1374,7 +1367,7 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd, } DBUG_EXECUTE_IF("inject_crash_before_flush_rli", DBUG_SUICIDE();); if (mi->using_gtid == Master_info::USE_GTID_NO) - flush_relay_log_info(this); + flush(); DBUG_EXECUTE_IF("inject_crash_after_flush_rli", DBUG_SUICIDE();); } DBUG_VOID_RETURN; @@ -2037,4 +2030,79 @@ bool rpl_sql_thread_info::cached_charset_compare(char *charset) const DBUG_RETURN(0); } + +/** + Store the file and position where the slave's SQL thread are in the + relay log. + + Notes: + + - This function should be called either from the slave SQL thread, + or when the slave thread is not running. (It reads the + group_{relay|master}_log_{pos|name} and delay fields in the rli + object. These may only be modified by the slave SQL thread or by + a client thread when the slave SQL thread is not running.) + + - If there is an active transaction, then we do not update the + position in the relay log. This is to ensure that we re-execute + statements if we die in the middle of an transaction that was + rolled back. + + - As a transaction never spans binary logs, we don't have to handle + the case where we do a relay-log-rotation in the middle of the + transaction. If transactions could span several binlogs, we would + have to ensure that we do not delete the relay log file where the + transaction started before switching to a new relay log file. + + - Error can happen if writing to file fails or if flushing the file + fails. + + @param rli The object representing the Relay_log_info. + + @todo Change the log file information to a binary format to avoid + calling longlong2str. + + @return 0 on success, 1 on error. +*/ +bool Relay_log_info::flush() +{ + bool error=0; + + DBUG_ENTER("Relay_log_info::flush()"); + + IO_CACHE *file = &info_file; + // 2*file name, 2*long long, 2*unsigned long, 6*'\n' + char buff[FN_REFLEN * 2 + 22 * 2 + 10 * 2 + 6], *pos; + my_b_seek(file, 0L); + pos= longlong10_to_str(LINES_IN_RELAY_LOG_INFO_WITH_DELAY, buff, 10); + *pos++='\n'; + pos=strmov(pos, group_relay_log_name); + *pos++='\n'; + pos=longlong10_to_str(group_relay_log_pos, pos, 10); + *pos++='\n'; + pos=strmov(pos, group_master_log_name); + *pos++='\n'; + pos=longlong10_to_str(group_master_log_pos, pos, 10); + *pos++='\n'; + pos= longlong10_to_str(sql_delay, pos, 10); + *pos++= '\n'; + if (my_b_write(file, (uchar*) buff, (size_t) (pos-buff))) + error=1; + if (flush_io_cache(file)) + error=1; + if (sync_relayloginfo_period && + !error && + ++sync_counter >= sync_relayloginfo_period) + { + if (my_sync(info_fd, MYF(MY_WME))) + error=1; + sync_counter= 0; + } + /* + Flushing the relay log is done by the slave I/O thread + or by the user on STOP SLAVE. + */ + DBUG_RETURN(error); +} + #endif diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index c8673db7f73..5c637702d04 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -29,11 +29,6 @@ class Master_info; class Rpl_filter; -enum { - LINES_IN_RELAY_LOG_INFO_WITH_DELAY= 5 -}; - - /**************************************************************************** Replication SQL Thread @@ -47,7 +42,7 @@ enum { Relay_log_info is initialized from the slave.info file if such exists. Otherwise, data members are intialized with defaults. The - initialization is done with init_relay_log_info() call. + initialization is done with Relay_log_info::init() call. The format of slave.info file: @@ -78,11 +73,17 @@ public: }; /* - If flag set, then rli does not store its state in any info file. - This is the case only when we execute BINLOG SQL commands inside - a client, non-replication thread. + The SQL thread owns one Relay_log_info, and each client that has + executed a BINLOG statement owns one Relay_log_info. This function + returns zero for the Relay_log_info object that belongs to the SQL + thread and nonzero for Relay_log_info objects that belong to + clients. */ - bool no_storage; + inline bool belongs_to_client() + { + DBUG_ASSERT(sql_driver_thd); + return !sql_driver_thd->slave_thread; + } /* If true, events with the same server id should be replicated. This @@ -194,6 +195,11 @@ public: relay log and finishing (commiting) on another relay log. Case which can happen when, for example, the relay log gets rotated because of max_binlog_size. + + Note: group_relay_log_name, group_relay_log_pos must only be + written from the thread owning the Relay_log_info (SQL thread if + !belongs_to_client(); client thread executing BINLOG statement if + belongs_to_client()). */ char group_relay_log_name[FN_REFLEN]; ulonglong group_relay_log_pos; @@ -205,16 +211,17 @@ public: */ char future_event_master_log_name[FN_REFLEN]; -#ifdef HAVE_valgrind - bool is_fake; /* Mark that this is a fake relay log info structure */ -#endif - /* Original log name and position of the group we're currently executing (whose coordinates are group_relay_log_name/pos in the relay log) in the master's binlog. These concern the *group*, because in the master's binlog the log_pos that comes with each event is the position of the beginning of the group. + + Note: group_master_log_name, group_master_log_pos must only be + written from the thread owning the Relay_log_info (SQL thread if + !belongs_to_client(); client thread executing BINLOG statement if + belongs_to_client()). */ char group_master_log_name[FN_REFLEN]; volatile my_off_t group_master_log_pos; @@ -244,6 +251,15 @@ public: bool sql_thread_caught_up; void clear_until_condition(); + /** + Reset the delay. + This is used by RESET SLAVE to clear the delay. + */ + void clear_sql_delay() + { + sql_delay= 0; + } + /* Needed for problems when slave stops and we want to restart it @@ -475,8 +491,72 @@ public: m_flags&= ~flag; } + /** + Text used in THD::proc_info when the slave SQL thread is delaying. + */ + static const char *const state_delaying_string; + + bool flush(); + + /** + Reads the relay_log.info file. + */ + int init(const char* info_filename); + + /** + Indicate that a delay starts. + + This does not actually sleep; it only sets the state of this + Relay_log_info object to delaying so that the correct state can be + reported by SHOW SLAVE STATUS and SHOW PROCESSLIST. + + Requires rli->data_lock. + + @param delay_end The time when the delay shall end. + */ + void start_sql_delay(time_t delay_end) + { + mysql_mutex_assert_owner(&data_lock); + sql_delay_end= delay_end; + thd_proc_info(sql_driver_thd, state_delaying_string); + } + + int32 get_sql_delay() { return sql_delay; } + void set_sql_delay(time_t _sql_delay) { sql_delay= _sql_delay; } + time_t get_sql_delay_end() { return sql_delay_end; } + private: + + /** + Delay slave SQL thread by this amount, compared to master (in + seconds). This is set with CHANGE MASTER TO MASTER_DELAY=X. + + Guarded by data_lock. Initialized by the client thread executing + START SLAVE. Written by client threads executing CHANGE MASTER TO + MASTER_DELAY=X. Read by SQL thread and by client threads + executing SHOW SLAVE STATUS. Note: must not be written while the + slave SQL thread is running, since the SQL thread reads it without + a lock when executing Relay_log_info::flush(). + */ + int sql_delay; + + /** + During a delay, specifies the point in time when the delay ends. + + This is used for the SQL_Remaining_Delay column in SHOW SLAVE STATUS. + + Guarded by data_lock. Written by the sql thread. Read by client + threads executing SHOW SLAVE STATUS. + */ + time_t sql_delay_end; + + /* + Before the MASTER_DELAY parameter was added (WL#344), + relay_log.info had 4 lines. Now it has 5 lines. + */ + static const int LINES_IN_RELAY_LOG_INFO_WITH_DELAY= 5; + /* Holds the state of the data in the relay log. We need this to ensure that we are not in the middle of a @@ -875,10 +955,6 @@ public: }; -// Defined in rpl_rli.cc -int init_relay_log_info(Relay_log_info* rli, const char* info_fname); - - extern struct rpl_slave_state *rpl_global_gtid_slave_state; extern gtid_waiting rpl_global_gtid_waiting; diff --git a/sql/slave.cc b/sql/slave.cc index 08cbf9acb6a..3a6517f54c4 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -77,6 +77,7 @@ Master_info *active_mi= 0; Master_info_index *master_info_index; my_bool replicate_same_server_id; ulonglong relay_log_space_limit = 0; +ulonglong opt_read_binlog_speed_limit = 0; const char *relay_log_index= 0; const char *relay_log_basename= 0; @@ -735,7 +736,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) DBUG_PRINT("info",("Flushing relay-log info file.")); if (current_thd) THD_STAGE_INFO(current_thd, stage_flushing_relay_log_info_file); - if (flush_relay_log_info(&mi->rli)) + if (mi->rli.flush()) DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); if (my_sync(mi->rli.info_fd, MYF(MY_WME))) @@ -1631,8 +1632,10 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) (master_res= mysql_store_result(mysql)) && (master_row= mysql_fetch_row(master_res))) { + mysql_mutex_lock(&mi->data_lock); mi->clock_diff_with_master= (long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10)); + mysql_mutex_unlock(&mi->data_lock); } else if (check_io_slave_killed(mi, NULL)) goto slave_killed_err; @@ -1644,7 +1647,9 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) } else { + mysql_mutex_lock(&mi->data_lock); mi->clock_diff_with_master= 0; /* The "most sensible" value */ + mysql_mutex_unlock(&mi->data_lock); sql_print_warning("\"SELECT UNIX_TIMESTAMP()\" failed on master, " "do not trust column Seconds_Behind_Master of SHOW " "SLAVE STATUS. Error: %s (%d)", @@ -2794,6 +2799,15 @@ void show_master_info_get_fields(THD *thd, List *field_list, Item_empty_string(thd, "Parallel_Mode", sizeof("conservative")-1), mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "SQL_Delay", 10, + MYSQL_TYPE_LONG)); + field_list->push_back(new (mem_root) + Item_return_int(thd, "SQL_Remaining_Delay", 8, + MYSQL_TYPE_LONG)); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Slave_SQL_Running_State", + 20)); if (full) { field_list->push_back(new (mem_root) @@ -2983,6 +2997,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, prot_store_ids(thd, &mi->ignore_server_ids); // Master_Server_id protocol->store((uint32) mi->master_id); + // SQL_Delay // Master_Ssl_Crl protocol->store(mi->ssl_ca, &my_charset_bin); // Master_Ssl_Crlpath @@ -3005,6 +3020,22 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, protocol->store(mode_name, strlen(mode_name), &my_charset_bin); } + protocol->store((uint32) mi->rli.get_sql_delay()); + // SQL_Remaining_Delay + // THD::proc_info is not protected by any lock, so we read it once + // to ensure that we use the same value throughout this function. + const char *slave_sql_running_state= + mi->rli.sql_driver_thd ? mi->rli.sql_driver_thd->proc_info : ""; + if (slave_sql_running_state == Relay_log_info::state_delaying_string) + { + time_t t= my_time(0), sql_delay_end= mi->rli.get_sql_delay_end(); + protocol->store((uint32)(t < sql_delay_end ? sql_delay_end - t : 0)); + } + else + protocol->store_null(); + // Slave_SQL_Running_State + protocol->store(slave_sql_running_state, &my_charset_bin); + if (full) { protocol->store((uint32) mi->rli.retried_trans); @@ -3277,13 +3308,15 @@ static int request_dump(THD *thd, MYSQL* mysql, Master_info* mi, try a reconnect. We do not want to print anything to the error log in this case because this a anormal event in an idle server. + network_read_len get the real network read length in VIO, especially using compressed protocol RETURN VALUES 'packet_error' Error number Length of packet */ -static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings) +static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings, + ulong* network_read_len) { ulong len; DBUG_ENTER("read_event"); @@ -3298,7 +3331,7 @@ static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings) DBUG_RETURN(packet_error); #endif - len = cli_safe_read(mysql); + len = cli_safe_read_reallen(mysql, network_read_len); if (len == packet_error || (long) len < 1) { if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) @@ -3369,38 +3402,83 @@ has_temporary_error(THD *thd) /** - Applies the given event and advances the relay log position. + If this is a lagging slave (specified with CHANGE MASTER TO MASTER_DELAY = X), delays accordingly. Also unlocks rli->data_lock. - In essence, this function does: + Design note: this is the place to unlock rli->data_lock. The lock + must be held when reading delay info from rli, but it should not be + held while sleeping. - @code - ev->apply_event(rli); - ev->update_pos(rli); - @endcode + @param ev Event that is about to be executed. - But it also does some maintainance, such as skipping events if - needed and reporting errors. + @param thd The sql thread's THD object. - If the @c skip flag is set, then it is tested whether the event - should be skipped, by looking at the slave_skip_counter and the - server id. The skip flag should be set when calling this from a - replication thread but not set when executing an explicit BINLOG - statement. + @param rli The sql thread's Relay_log_info structure. - @retval 0 OK. + @retval 0 If the delay timed out and the event shall be executed. - @retval 1 Error calling ev->apply_event(). - - @retval 2 No error calling ev->apply_event(), but error calling - ev->update_pos(). + @retval nonzero If the delay was interrupted and the event shall be skipped. */ -int apply_event_and_update_pos(Log_event* ev, THD* thd, - rpl_group_info *rgi, - rpl_parallel_thread *rpt) +int +sql_delay_event(Log_event *ev, THD *thd, rpl_group_info *rgi) { - int exec_res= 0; Relay_log_info* rli= rgi->rli; - DBUG_ENTER("apply_event_and_update_pos"); + long sql_delay= rli->get_sql_delay(); + + DBUG_ENTER("sql_delay_event"); + mysql_mutex_assert_owner(&rli->data_lock); + DBUG_ASSERT(!rli->belongs_to_client()); + + int type= ev->get_type_code(); + if (sql_delay && type != ROTATE_EVENT && + type != FORMAT_DESCRIPTION_EVENT && type != START_EVENT_V3) + { + // The time when we should execute the event. + time_t sql_delay_end= + ev->when + rli->mi->clock_diff_with_master + sql_delay; + // The current time. + time_t now= my_time(0); + // The time we will have to sleep before executing the event. + unsigned long nap_time= 0; + if (sql_delay_end > now) + nap_time= sql_delay_end - now; + + DBUG_PRINT("info", ("sql_delay= %lu " + "ev->when= %lu " + "rli->mi->clock_diff_with_master= %lu " + "now= %ld " + "sql_delay_end= %lu " + "nap_time= %ld", + sql_delay, (long)ev->when, + rli->mi->clock_diff_with_master, + (long)now, sql_delay_end, (long)nap_time)); + + if (sql_delay_end > now) + { + DBUG_PRINT("info", ("delaying replication event %lu secs", + nap_time)); + rli->start_sql_delay(sql_delay_end); + mysql_mutex_unlock(&rli->data_lock); + DBUG_RETURN(slave_sleep(thd, nap_time, sql_slave_killed, rgi)); + } + } + + mysql_mutex_unlock(&rli->data_lock); + + DBUG_RETURN(0); +} + + +/* + First half of apply_event_and_update_pos(), see below. + Setup some THD variables for applying the event. + + Split out so that it can run with rli->data_lock held in non-parallel + replication, but without the mutex held in the parallel case. +*/ +static int +apply_event_and_update_pos_setup(Log_event* ev, THD* thd, rpl_group_info *rgi) +{ + DBUG_ENTER("apply_event_and_update_pos_setup"); DBUG_PRINT("exec_event",("%s(type_code: %d; server_id: %d)", ev->get_type_str(), ev->get_type_code(), @@ -3450,13 +3528,23 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, (ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0); ev->thd = thd; // because up to this point, ev->thd == 0 - int reason= ev->shall_skip(rgi); - if (reason == Log_event::EVENT_SKIP_COUNT) - { - DBUG_ASSERT(rli->slave_skip_counter > 0); - rli->slave_skip_counter--; - } - mysql_mutex_unlock(&rli->data_lock); + DBUG_RETURN(ev->shall_skip(rgi)); +} + + +/* + Second half of apply_event_and_update_pos(), see below. + + Do the actual event apply (or skip), and position update. + */ +static int +apply_event_and_update_pos_apply(Log_event* ev, THD* thd, rpl_group_info *rgi, + int reason) +{ + int exec_res= 0; + Relay_log_info* rli= rgi->rli; + + DBUG_ENTER("apply_event_and_update_pos_apply"); DBUG_EXECUTE_IF("inject_slave_sql_before_apply_event", { DBUG_ASSERT(!debug_sync_set_action @@ -3503,16 +3591,16 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, if (exec_res == 0) { int error= ev->update_pos(rgi); -#ifdef HAVE_valgrind - if (!rli->is_fake) -#endif + #ifndef DBUG_OFF + DBUG_PRINT("info", ("update_pos error = %d", error)); + if (!rli->belongs_to_client()) { - DBUG_PRINT("info", ("update_pos error = %d", error)); DBUG_PRINT("info", ("group %llu %s", rli->group_relay_log_pos, rli->group_relay_log_name)); DBUG_PRINT("info", ("event %llu %s", rli->event_relay_log_pos, rli->event_relay_log_name)); } +#endif /* The update should not fail, so print an error message and return an error code. @@ -3544,6 +3632,103 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, } +/** + Applies the given event and advances the relay log position. + + This is needed by the sql thread to execute events from the binlog, + and by clients executing BINLOG statements. Conceptually, this + function does: + + @code + ev->apply_event(rli); + ev->update_pos(rli); + @endcode + + It also does the following maintainance: + + - Initializes the thread's server_id and time; and the event's + thread. + + - If !rli->belongs_to_client() (i.e., if it belongs to the slave + sql thread instead of being used for executing BINLOG + statements), it does the following things: (1) skips events if it + is needed according to the server id or slave_skip_counter; (2) + unlocks rli->data_lock; (3) sleeps if required by 'CHANGE MASTER + TO MASTER_DELAY=X'; (4) maintains the running state of the sql + thread (rli->thread_state). + + - Reports errors as needed. + + @param ev The event to apply. + + @param thd The client thread that executes the event (i.e., the + slave sql thread if called from a replication slave, or the client + thread if called to execute a BINLOG statement). + + @param rli The relay log info (i.e., the slave's rli if called from + a replication slave, or the client's thd->rli_fake if called to + execute a BINLOG statement). + + @retval 0 OK. + + @retval 1 Error calling ev->apply_event(). + + @retval 2 No error calling ev->apply_event(), but error calling + ev->update_pos(). + + This function is only used in non-parallel replication, where it is called + with rli->data_lock held; this lock is released during this function. +*/ +int +apply_event_and_update_pos(Log_event* ev, THD* thd, rpl_group_info *rgi) +{ + Relay_log_info* rli= rgi->rli; + mysql_mutex_assert_owner(&rli->data_lock); + int reason= apply_event_and_update_pos_setup(ev, thd, rgi); + if (reason == Log_event::EVENT_SKIP_COUNT) + { + DBUG_ASSERT(rli->slave_skip_counter > 0); + rli->slave_skip_counter--; + } + + if (reason == Log_event::EVENT_SKIP_NOT) + { + // Sleeps if needed, and unlocks rli->data_lock. + if (sql_delay_event(ev, thd, rgi)) + return 0; + } + else + mysql_mutex_unlock(&rli->data_lock); + + return apply_event_and_update_pos_apply(ev, thd, rgi, reason); +} + + +/* + The version of above apply_event_and_update_pos() used in parallel + replication. Unlike the non-parallel case, this function is called without + rli->data_lock held. +*/ +int +apply_event_and_update_pos_for_parallel(Log_event* ev, THD* thd, + rpl_group_info *rgi) +{ + Relay_log_info* rli= rgi->rli; + mysql_mutex_assert_not_owner(&rli->data_lock); + int reason= apply_event_and_update_pos_setup(ev, thd, rgi); + /* + In parallel replication, sql_slave_skip_counter is handled in the SQL + driver thread, so 23 should never see EVENT_SKIP_COUNT here. + */ + DBUG_ASSERT(reason != Log_event::EVENT_SKIP_COUNT); + /* + Calling sql_delay_event() was handled in the SQL driver thread when + doing parallel replication. + */ + return apply_event_and_update_pos_apply(ev, thd, rgi, reason); +} + + /** Keep the relay log transaction state up to date. @@ -3619,7 +3804,8 @@ inline void update_state_of_relay_log(Relay_log_info *rli, Log_event *ev) /** - Top-level function for executing the next event from the relay log. + Top-level function for executing the next event in the relay log. + This is called from the SQL thread. This function reads the event from the relay log, executes it, and advances the relay log position. It also handles errors, etc. @@ -3794,7 +3980,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, serial_rgi->future_event_relay_log_pos= rli->future_event_relay_log_pos; serial_rgi->event_relay_log_name= rli->event_relay_log_name; serial_rgi->event_relay_log_pos= rli->event_relay_log_pos; - exec_res= apply_event_and_update_pos(ev, thd, serial_rgi, NULL); + exec_res= apply_event_and_update_pos(ev, thd, serial_rgi); #ifdef WITH_WSREP WSREP_DEBUG("apply_event_and_update_pos() result: %d", exec_res); @@ -4164,8 +4350,10 @@ connected: };); #endif - // TODO: the assignment below should be under mutex (5.0) + mysql_mutex_lock(&mi->run_lock); mi->slave_running= MYSQL_SLAVE_RUN_CONNECT; + mysql_mutex_unlock(&mi->run_lock); + thd->slave_net = &mysql->net; THD_STAGE_INFO(thd, stage_checking_master_version); ret= get_master_version_and_clock(mysql, mi); @@ -4230,9 +4418,11 @@ connected: mi->slave_running= MYSQL_SLAVE_RUN_READING; DBUG_ASSERT(mi->last_error().number == 0); + ulonglong lastchecktime = my_hrtime().val; + ulonglong tokenamount = opt_read_binlog_speed_limit*1024; while (!io_slave_killed(mi)) { - ulong event_len; + ulong event_len, network_read_len = 0; /* We say "waiting" because read_event() will wait if there's nothing to read. But if there's something to read, it will not wait. The @@ -4240,7 +4430,7 @@ connected: we're in fact receiving nothing. */ THD_STAGE_INFO(thd, stage_waiting_for_master_to_send_event); - event_len= read_event(mysql, mi, &suppress_warnings); + event_len= read_event(mysql, mi, &suppress_warnings, &network_read_len); if (check_io_slave_killed(mi, NullS)) goto err; @@ -4288,6 +4478,47 @@ Stopping slave I/O thread due to out-of-memory error from master"); goto err; } + /* Control the binlog read speed of master + when read_binlog_speed_limit is non-zero + */ + ulonglong speed_limit_in_bytes = opt_read_binlog_speed_limit * 1024; + if (speed_limit_in_bytes) + { + /* Prevent the tokenamount become a large value, + for example, the IO thread doesn't work for a long time + */ + if (tokenamount > speed_limit_in_bytes * 2) + { + lastchecktime = my_hrtime().val; + tokenamount = speed_limit_in_bytes * 2; + } + + do + { + ulonglong currenttime = my_hrtime().val; + tokenamount += (currenttime - lastchecktime) * speed_limit_in_bytes / (1000*1000); + lastchecktime = currenttime; + if(tokenamount < network_read_len) + { + ulonglong micro_time = 1000*1000 * (network_read_len - tokenamount) / speed_limit_in_bytes ; + ulonglong second_time = micro_time / (1000 * 1000); + micro_time = micro_time % (1000 * 1000); + + // at least sleep 1000 micro second + my_sleep(micro_time > 1000 ? micro_time : 1000); + + /* + If it sleep more than one second, + it should use slave_sleep() to avoid the STOP SLAVE hang. + */ + if (second_time) + slave_sleep(thd, second_time, io_slave_killed, mi); + + } + }while(tokenamount < network_read_len); + tokenamount -= network_read_len; + } + /* XXX: 'synced' should be updated by queue_event to indicate whether event has been synced to disk */ bool synced= 0; @@ -4928,7 +5159,7 @@ pthread_handler_t handle_slave_sql(void *arg) { ulong domain_count; - flush_relay_log_info(rli); + rli->flush(); if (mi->using_parallel()) { /* @@ -6516,75 +6747,6 @@ MYSQL *rpl_connect_master(MYSQL *mysql) } #endif -/* - Store the file and position where the execute-slave thread are in the - relay log. - - SYNOPSIS - flush_relay_log_info() - rli Relay log information - - NOTES - - As this is only called by the slave thread or on STOP SLAVE, with the - log_lock grabbed and the slave thread stopped, we don't need to have - a lock here. - - If there is an active transaction, then we don't update the position - in the relay log. This is to ensure that we re-execute statements - if we die in the middle of an transaction that was rolled back. - - As a transaction never spans binary logs, we don't have to handle the - case where we do a relay-log-rotation in the middle of the transaction. - If this would not be the case, we would have to ensure that we - don't delete the relay log file where the transaction started when - we switch to a new relay log file. - - TODO - - Change the log file information to a binary format to avoid calling - longlong2str. - - RETURN VALUES - 0 ok - 1 write error -*/ - -bool flush_relay_log_info(Relay_log_info* rli) -{ - bool error=0; - DBUG_ENTER("flush_relay_log_info"); - - if (unlikely(rli->no_storage)) - DBUG_RETURN(0); - - IO_CACHE *file = &rli->info_file; - char buff[FN_REFLEN*2+22*2+4], *pos; - - my_b_seek(file, 0L); - pos=strmov(buff, rli->group_relay_log_name); - *pos++='\n'; - pos= longlong10_to_str(rli->group_relay_log_pos, pos, 10); - *pos++='\n'; - pos=strmov(pos, rli->group_master_log_name); - *pos++='\n'; - pos=longlong10_to_str(rli->group_master_log_pos, pos, 10); - *pos='\n'; - if (my_b_write(file, (uchar*) buff, (size_t) (pos-buff)+1)) - error=1; - if (flush_io_cache(file)) - error=1; - if (sync_relayloginfo_period && - !error && - ++(rli->sync_counter) >= sync_relayloginfo_period) - { - if (my_sync(rli->info_fd, MYF(MY_WME))) - error=1; - rli->sync_counter= 0; - } - /* - Flushing the relay log is done by the slave I/O thread - or by the user on STOP SLAVE. - */ - DBUG_RETURN(error); -} - /* Called when we notice that the current "hot" log got rotated under our feet. @@ -6941,7 +7103,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) } rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE; strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name); - flush_relay_log_info(rli); + rli->flush(); } /* diff --git a/sql/slave.h b/sql/slave.h index e8a925ce560..b2a1e308aba 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -17,6 +17,14 @@ #ifndef SLAVE_H #define SLAVE_H +/** + MASTER_DELAY can be at most (1 << 31) - 1. +*/ +#define MASTER_DELAY_MAX (0x7FFFFFFF) +#if INT_MAX < 0x7FFFFFFF +#error "don't support platforms where INT_MAX < 0x7FFFFFFF" +#endif + /** @defgroup Replication Replication @{ @@ -102,12 +110,14 @@ int init_dynarray_intvar_from_file(DYNAMIC_ARRAY* arr, IO_CACHE* f); In Master_info: run_lock, data_lock run_lock protects all information about the run state: slave_running, thd - and the existence of the I/O thread to stop/start it, you need this mutex). + and the existence of the I/O thread (to stop/start it, you need this mutex). data_lock protects some moving members of the struct: counters (log name, position) and relay log (MYSQL_BIN_LOG object). In Relay_log_info: run_lock, data_lock see Master_info + However, note that run_lock does not protect + Relay_log_info.run_state; that is protected by data_lock. Order of acquisition: if you want to have LOCK_active_mi and a run_lock, you must acquire LOCK_active_mi first. @@ -130,6 +140,7 @@ extern my_bool opt_log_slave_updates; extern char *opt_slave_skip_errors; extern my_bool opt_replicate_annotate_row_events; extern ulonglong relay_log_space_limit; +extern ulonglong opt_read_binlog_speed_limit; extern ulonglong slave_skipped_errors; extern const char *relay_log_index; extern const char *relay_log_basename; @@ -173,7 +184,6 @@ extern const char *relay_log_basename; int init_slave(); int init_recovery(Master_info* mi, const char** errmsg); void init_slave_skip_errors(const char* arg); -bool flush_relay_log_info(Relay_log_info* rli); int register_slave_on_master(MYSQL* mysql); int terminate_slave_threads(Master_info* mi, int thread_mask, bool skip_lock = 0); @@ -242,9 +252,17 @@ void set_slave_thread_options(THD* thd); void set_slave_thread_default_charset(THD *thd, rpl_group_info *rgi); int rotate_relay_log(Master_info* mi); int has_temporary_error(THD *thd); +int sql_delay_event(Log_event *ev, THD *thd, rpl_group_info *rgi); int apply_event_and_update_pos(Log_event* ev, THD* thd, - struct rpl_group_info *rgi, - rpl_parallel_thread *rpt); + struct rpl_group_info *rgi); +int apply_event_and_update_pos_for_parallel(Log_event* ev, THD* thd, + struct rpl_group_info *rgi); + +int init_intvar_from_file(int* var, IO_CACHE* f, int default_val); +int init_floatvar_from_file(float* var, IO_CACHE* f, float default_val); +int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, + const char *default_val); +int init_dynarray_intvar_from_file(DYNAMIC_ARRAY* arr, IO_CACHE* f); pthread_handler_t handle_slave_io(void *arg); void slave_output_error_info(rpl_group_info *rgi, THD *thd); diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 1967b74e737..d92ac15822f 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -17,18 +17,99 @@ #include #include "sql_priv.h" #include "sql_binlog.h" -#include "sql_parse.h" // check_global_access -#include "sql_acl.h" // *_ACL +#include "sql_parse.h" +#include "sql_acl.h" #include "rpl_rli.h" #include "base64.h" -#include "slave.h" // apply_event_and_update_pos -#include "log_event.h" // Format_description_log_event, - // EVENT_LEN_OFFSET, - // EVENT_TYPE_OFFSET, - // FORMAT_DESCRIPTION_LOG_EVENT, - // START_EVENT_V3, - // Log_event_type, - // Log_event +#include "slave.h" +#include "log_event.h" + + +/** + Check if the event type is allowed in a BINLOG statement. + + @retval 0 if the event type is ok. + @retval 1 if the event type is not ok. +*/ +static int check_event_type(int type, Relay_log_info *rli) +{ + Format_description_log_event *fd_event= + rli->relay_log.description_event_for_exec; + + /* + Convert event type id of certain old versions (see comment in + Format_description_log_event::Format_description_log_event(char*,...)). + */ + if (fd_event && fd_event->event_type_permutation) + { + IF_DBUG({ + int new_type= fd_event->event_type_permutation[type]; + DBUG_PRINT("info", + ("converting event type %d to %d (%s)", + type, new_type, + Log_event::get_type_str((Log_event_type)new_type))); + }, + (void)0); + type= fd_event->event_type_permutation[type]; + } + + switch (type) + { + case START_EVENT_V3: + case FORMAT_DESCRIPTION_EVENT: + /* + We need a preliminary FD event in order to parse the FD event, + if we don't already have one. + */ + if (!fd_event) + if (!(rli->relay_log.description_event_for_exec= + new Format_description_log_event(4))) + { + my_error(ER_OUTOFMEMORY, MYF(0), 1); + return 1; + } + + /* It is always allowed to execute FD events. */ + return 0; + + case TABLE_MAP_EVENT: + case WRITE_ROWS_EVENT_V1: + case UPDATE_ROWS_EVENT_V1: + case DELETE_ROWS_EVENT_V1: + case WRITE_ROWS_EVENT: + case UPDATE_ROWS_EVENT: + case DELETE_ROWS_EVENT: + case PRE_GA_WRITE_ROWS_EVENT: + case PRE_GA_UPDATE_ROWS_EVENT: + case PRE_GA_DELETE_ROWS_EVENT: + /* + Row events are only allowed if a Format_description_event has + already been seen. + */ + if (fd_event) + return 0; + else + { + my_error(ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT, + MYF(0), Log_event::get_type_str((Log_event_type)type)); + return 1; + } + break; + + default: + /* + It is not meaningful to execute other events than row-events and + FD events. It would even be dangerous to execute Stop_log_event + and Rotate_log_event since they call Relay_log_info::flush(), which + is not allowed to call by other threads than the slave SQL + thread when the slave SQL thread is running. + */ + my_error(ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT, + MYF(0), Log_event::get_type_str((Log_event_type)type)); + return 1; + } +} + /** Execute a BINLOG statement. @@ -73,31 +154,13 @@ void mysql_client_binlog_statement(THD* thd) Allocation */ - /* - If we do not have a Format_description_event, we create a dummy - one here. In this case, the first event we read must be a - Format_description_event. - */ - my_bool have_fd_event= TRUE; int err; Relay_log_info *rli; rpl_group_info *rgi; rli= thd->rli_fake; - if (!rli) - { - rli= thd->rli_fake= new Relay_log_info(FALSE); -#ifdef HAVE_valgrind - rli->is_fake= TRUE; -#endif - have_fd_event= FALSE; - } - if (rli && !rli->relay_log.description_event_for_exec) - { - rli->relay_log.description_event_for_exec= - new Format_description_log_event(4); - have_fd_event= FALSE; - } + if (!rli && (rli= thd->rli_fake= new Relay_log_info(FALSE))) + rli->sql_driver_thd= thd; if (!(rgi= thd->rgi_fake)) rgi= thd->rgi_fake= new rpl_group_info(rli); rgi->thd= thd; @@ -109,16 +172,13 @@ void mysql_client_binlog_statement(THD* thd) /* Out of memory check */ - if (!(rli && - rli->relay_log.description_event_for_exec && - buf)) + if (!(rli && buf)) { my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1); /* needed 1 bytes */ goto end; } - rli->sql_driver_thd= thd; - rli->no_storage= TRUE; + DBUG_ASSERT(rli->belongs_to_client()); for (char const *strptr= thd->lex->comment.str ; strptr < thd->lex->comment.str + thd->lex->comment.length ; ) @@ -185,23 +245,8 @@ void mysql_client_binlog_statement(THD* thd) DBUG_PRINT("info", ("event_len=%lu, bytes_decoded=%d", event_len, bytes_decoded)); - /* - If we have not seen any Format_description_event, then we must - see one; it is the only statement that can be read in base64 - without a prior Format_description_event. - */ - if (!have_fd_event) - { - int type = (uchar)bufptr[EVENT_TYPE_OFFSET]; - if (type == FORMAT_DESCRIPTION_EVENT || type == START_EVENT_V3) - have_fd_event= TRUE; - else - { - my_error(ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT, - MYF(0), Log_event::get_type_str((Log_event_type)type)); - goto end; - } - } + if (check_event_type(bufptr[EVENT_TYPE_OFFSET], rli)) + goto end; ev= Log_event::read_log_event(bufptr, event_len, &error, rli->relay_log.description_event_for_exec, @@ -212,7 +257,7 @@ void mysql_client_binlog_statement(THD* thd) { /* This could actually be an out-of-memory, but it is more likely - causes by a bad statement + caused by a bad statement */ my_error(ER_SYNTAX_ERROR, MYF(0)); goto end; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6433786a079..1af3b9a9cca 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -4598,19 +4598,20 @@ extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd) } /* - This function can optionally be called to check if thd_report_wait_for() + This function can optionally be called to check if thd_rpl_deadlock_check() needs to be called for waits done by a given transaction. - If this function returns false for a given thd, there is no need to do any - calls to thd_report_wait_for() on that thd. + If this function returns false for a given thd, there is no need to do + any calls to thd_rpl_deadlock_check() on that thd. - This call is optional; it is safe to call thd_report_wait_for() in any case. - This call can be used to save some redundant calls to thd_report_wait_for() - if desired. (This is unlikely to matter much unless there are _lots_ of - waits to report, as the overhead of thd_report_wait_for() is small). + This call is optional; it is safe to call thd_rpl_deadlock_check() in + any case. This call can be used to save some redundant calls to + thd_rpl_deadlock_check() if desired. (This is unlikely to matter much + unless there are _lots_ of waits to report, as the overhead of + thd_rpl_deadlock_check() is small). */ extern "C" int -thd_need_wait_for(const MYSQL_THD thd) +thd_need_wait_reports(const MYSQL_THD thd) { rpl_group_info *rgi; @@ -4625,75 +4626,9 @@ thd_need_wait_for(const MYSQL_THD thd) } /* - Used by InnoDB/XtraDB to report that one transaction THD is about to go to - wait for a transactional lock held by another transactions OTHER_THD. - - This is used for parallel replication, where transactions are required to - commit in the same order on the slave as they did on the master. If the - transactions on the slave encounters lock conflicts on the slave that did - not exist on the master, this can cause deadlocks. - - Normally, such conflicts will not occur, because the same conflict would - have prevented the two transactions from committing in parallel on the - master, thus preventing them from running in parallel on the slave in the - first place. However, it is possible in case when the optimizer chooses a - different plan on the slave than on the master (eg. table scan instead of - index scan). - - InnoDB/XtraDB reports lock waits using this call. If a lock wait causes a - deadlock with the pre-determined commit order, we kill the later transaction, - and later re-try it, to resolve the deadlock. - - This call need only receive reports about waits for locks that will remain - until the holding transaction commits. InnoDB/XtraDB auto-increment locks - are released earlier, and so need not be reported. (Such false positives are - not harmful, but could lead to unnecessary kill and retry, so best avoided). -*/ -extern "C" void -thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd) -{ - rpl_group_info *rgi; - rpl_group_info *other_rgi; - - if (!thd) - return; - DEBUG_SYNC(thd, "thd_report_wait_for"); - thd->transaction.stmt.mark_trans_did_wait(); - if (!other_thd) - return; - binlog_report_wait_for(thd, other_thd); - rgi= thd->rgi_slave; - other_rgi= other_thd->rgi_slave; - if (!rgi || !other_rgi) - return; - if (!rgi->is_parallel_exec) - return; - if (rgi->rli != other_rgi->rli) - return; - if (!rgi->gtid_sub_id || !other_rgi->gtid_sub_id) - return; - if (rgi->current_gtid.domain_id != other_rgi->current_gtid.domain_id) - return; - if (rgi->gtid_sub_id > other_rgi->gtid_sub_id) - return; - /* - This transaction is about to wait for another transaction that is required - by replication binlog order to commit after. This would cause a deadlock. - - So send a kill to the other transaction, with a temporary error; this will - cause replication to rollback (and later re-try) the other transaction, - releasing the lock for this transaction so replication can proceed. - */ - other_rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED; - mysql_mutex_lock(&other_thd->LOCK_thd_data); - other_thd->awake(KILL_CONNECTION); - mysql_mutex_unlock(&other_thd->LOCK_thd_data); -} - -/* - Used by storage engines (currently TokuDB) to report that one transaction - THD is about to go to wait for a transactional lock held by another - transactions OTHER_THD. + Used by storage engines (currently TokuDB and InnoDB/XtraDB) to report that + one transaction THD is about to go to wait for a transactional lock held by + another transactions OTHER_THD. This is used for parallel replication, where transactions are required to commit in the same order on the slave as they did on the master. If the @@ -4708,9 +4643,9 @@ thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd) chooses a different plan on the slave than on the master (eg. table scan instead of index scan). - InnoDB/XtraDB reports lock waits using this call. If a lock wait causes a - deadlock with the pre-determined commit order, we kill the later transaction, - and later re-try it, to resolve the deadlock. + Storage engines report lock waits using this call. If a lock wait causes a + deadlock with the pre-determined commit order, we kill the later + transaction, and later re-try it, to resolve the deadlock. This call need only receive reports about waits for locks that will remain until the holding transaction commits. InnoDB/XtraDB auto-increment locks, @@ -4801,8 +4736,8 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd) Calling this function is just an optimisation to avoid unnecessary deadlocks. If it was not used, a gap lock would be set that could eventually - cause a deadlock; the deadlock would be caught by thd_report_wait_for() and - the transaction T2 killed and rolled back (and later re-tried). + cause a deadlock; the deadlock would be caught by thd_rpl_deadlock_check() + and the transaction T2 killed and rolled back (and later re-tried). */ extern "C" int thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index c53daf7d92d..19e7668790e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2151,7 +2151,6 @@ void st_select_lex::init_select() in_sum_expr= with_wild= 0; options= 0; sql_cache= SQL_CACHE_UNSPECIFIED; - interval_list.empty(); ftfunc_list_alloc.empty(); inner_sum_func_list= 0; ftfunc_list= &ftfunc_list_alloc; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 63feef92445..b044ef42b97 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -246,11 +246,12 @@ struct LEX_MASTER_INFO ulong server_id; uint port, connect_retry; float heartbeat_period; + int sql_delay; /* Enum is used for making it possible to detect if the user changed variable or if it should be left at old value */ - enum {LEX_MI_UNCHANGED, LEX_MI_DISABLE, LEX_MI_ENABLE} + enum {LEX_MI_UNCHANGED= 0, LEX_MI_DISABLE, LEX_MI_ENABLE} ssl, ssl_verify_server_cert, heartbeat_opt, repl_ignore_server_ids_opt, repl_do_domain_ids_opt, repl_ignore_domain_ids_opt; enum { @@ -266,6 +267,7 @@ struct LEX_MASTER_INFO sizeof(ulong), 0, 16, MYF(0)); my_init_dynamic_array(&repl_ignore_domain_ids, sizeof(ulong), 0, 16, MYF(0)); + sql_delay= -1; } void reset(bool is_change_master) { @@ -286,6 +288,7 @@ struct LEX_MASTER_INFO repl_ignore_domain_ids_opt= LEX_MI_UNCHANGED; gtid_pos_str= null_lex_str; use_gtid_opt= LEX_GTID_UNCHANGED; + sql_delay= -1; } }; @@ -785,7 +788,6 @@ public: Group_list_ptrs *group_list_ptrs; List item_list; /* list of fields & expressions */ - List interval_list; bool is_item_list_lookup; /* Usualy it is pointer to ftfunc_list_alloc, but in union used to create fake diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 6ece9b793c9..bdf90f7caf6 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -495,7 +495,7 @@ static enum enum_binlog_checksum_alg get_binlog_checksum_value_at_connect(THD * TODO - Inform the slave threads that they should sync the position - in the binary log file with flush_relay_log_info. + in the binary log file with Relay_log_info::flush(). Now they sync is done for next read. */ @@ -3304,6 +3304,7 @@ int reset_slave(THD *thd, Master_info* mi) mi->clear_error(); mi->rli.clear_error(); mi->rli.clear_until_condition(); + mi->rli.clear_sql_delay(); mi->rli.slave_skip_counter= 0; // close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 @@ -3613,6 +3614,9 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) if (lex_mi->ssl != LEX_MASTER_INFO::LEX_MI_UNCHANGED) mi->ssl= (lex_mi->ssl == LEX_MASTER_INFO::LEX_MI_ENABLE); + if (lex_mi->sql_delay != -1) + mi->rli.set_sql_delay(lex_mi->sql_delay); + if (lex_mi->ssl_verify_server_cert != LEX_MASTER_INFO::LEX_MI_UNCHANGED) mi->ssl_verify_server_cert= (lex_mi->ssl_verify_server_cert == LEX_MASTER_INFO::LEX_MI_ENABLE); @@ -3797,7 +3801,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) in-memory value at restart (thus causing errors, as the old relay log does not exist anymore). */ - flush_relay_log_info(&mi->rli); + mi->rli.flush(); mysql_cond_broadcast(&mi->data_cond); mysql_mutex_unlock(&mi->rli.data_lock); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b8810311b51..e17a514a391 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1359,6 +1359,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token LOOP_SYM %token LOW_PRIORITY %token MASTER_CONNECT_RETRY_SYM +%token MASTER_DELAY_SYM %token MASTER_GTID_POS_SYM %token MASTER_HOST_SYM %token MASTER_LOG_FILE_SYM @@ -2338,6 +2339,16 @@ master_def: { Lex->mi.connect_retry = $3; } + | MASTER_DELAY_SYM '=' ulong_num + { + if ($3 > MASTER_DELAY_MAX) + { + my_error(ER_MASTER_DELAY_VALUE_OUT_OF_RANGE, MYF(0), + $3, MASTER_DELAY_MAX); + } + else + Lex->mi.sql_delay = $3; + } | MASTER_SSL_SYM '=' ulong_num { Lex->mi.ssl= $3 ? @@ -7850,6 +7861,7 @@ slave: LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_ALL_START; lex->type = 0; + /* If you change this code don't forget to update STOP SLAVE too */ } {} | STOP_SYM SLAVE optional_connection_name slave_thread_opts @@ -13857,13 +13869,13 @@ literal: } | FALSE_SYM { - $$= new (thd->mem_root) Item_int(thd, (char*) "FALSE",0,1); + $$= new (thd->mem_root) Item_bool(thd, (char*) "FALSE",0); if ($$ == NULL) MYSQL_YYABORT; } | TRUE_SYM { - $$= new (thd->mem_root) Item_int(thd, (char*) "TRUE",1,1); + $$= new (thd->mem_root) Item_bool(thd, (char*) "TRUE",1); if ($$ == NULL) MYSQL_YYABORT; } @@ -14744,6 +14756,7 @@ keyword_sp: | MASTER_PASSWORD_SYM {} | MASTER_SERVER_ID_SYM {} | MASTER_CONNECT_RETRY_SYM {} + | MASTER_DELAY_SYM {} | MASTER_SSL_SYM {} | MASTER_SSL_CA_SYM {} | MASTER_SSL_CAPATH_SYM {} diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 3e6a06c3ab0..b6359ffbd48 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -4630,6 +4630,12 @@ static Sys_var_charptr Sys_slave_skip_errors( READ_ONLY GLOBAL_VAR(opt_slave_skip_errors), CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, DEFAULT(0)); +static Sys_var_ulonglong Sys_read_binlog_speed_limit( + "read_binlog_speed_limit", "Maximum speed(KB/s) to read binlog from" + " master (0 = no limit)", + GLOBAL_VAR(opt_read_binlog_speed_limit), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1)); + static Sys_var_ulonglong Sys_relay_log_space_limit( "relay_log_space_limit", "Maximum space to use for all relay logs", READ_ONLY GLOBAL_VAR(relay_log_space_limit), CMD_LINE(REQUIRED_ARG), diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index a810a5a44ae..06846e7dd6a 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -97,7 +97,6 @@ static rpl_group_info* wsrep_relay_group_init(const char* log_fname) { Relay_log_info* rli= new Relay_log_info(false); - rli->no_storage= true; if (!rli->relay_log.description_event_for_exec) { rli->relay_log.description_event_for_exec= diff --git a/storage/connect/mysql-test/connect/disabled.def b/storage/connect/mysql-test/connect/disabled.def index 4e07b5c0576..a97ba67d73e 100644 --- a/storage/connect/mysql-test/connect/disabled.def +++ b/storage/connect/mysql-test/connect/disabled.def @@ -13,3 +13,5 @@ jdbc : Variable settings depend on machine configuration jdbc_new : Variable settings depend on machine configuration jdbc_oracle : Variable settings depend on machine configuration jdbc_postgresql : Variable settings depend on machine configuration +json_udf : conflicts with the server JSON functions +json_udf_bin : conflicts with the server JSON functions diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 446e971aacf..d9b50935d27 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -30,6 +30,7 @@ MYSQL_CHECK_LZO() MYSQL_CHECK_LZMA() MYSQL_CHECK_BZIP2() MYSQL_CHECK_SNAPPY() +MYSQL_CHECK_NUMA() INCLUDE(innodb.cmake) @@ -173,11 +174,20 @@ IF(WITH_INNODB) SET(WITH_INNOBASE_STORAGE_ENGINE TRUE) ENDIF() +UNSET(NUMA_LIBRARY) +IF(HAVE_LIBNUMA) + SET(NUMA_LIBRARY "numa") +ENDIF() + MYSQL_ADD_PLUGIN(innobase ${INNOBASE_SOURCES} STORAGE_ENGINE # MODULE_ONLY # MODULE_OUTPUT_NAME ha_innodb DEFAULT RECOMPILE_FOR_EMBEDDED - LINK_LIBRARIES ${ZLIB_LIBRARY} ${CRC32_VPMSUM_LIBRARY} ${LINKER_SCRIPT}) + LINK_LIBRARIES + ${ZLIB_LIBRARY} + ${CRC32_VPMSUM_LIBRARY} + ${NUMA_LIBRARY} + ${LINKER_SCRIPT}) IF(WITH_INNOBASE_STORAGE_ENGINE) ADD_DEPENDENCIES(innobase GenError) diff --git a/storage/innobase/api/api0api.cc b/storage/innobase/api/api0api.cc index 403d6e4a28d..c6947b6f054 100644 --- a/storage/innobase/api/api0api.cc +++ b/storage/innobase/api/api0api.cc @@ -3425,9 +3425,9 @@ ib_cursor_set_memcached_sync( } if (flag) { - os_atomic_increment_lint(&table->memcached_sync_count, 1); + my_atomic_addlint(&table->memcached_sync_count, 1); } else { - os_atomic_decrement_lint(&table->memcached_sync_count, 1); + my_atomic_addlint(&table->memcached_sync_count, -1); ut_a(table->memcached_sync_count >= 0); } } else { diff --git a/storage/innobase/btr/btr0defragment.cc b/storage/innobase/btr/btr0defragment.cc index a62351f2954..71a93744ef4 100644 --- a/storage/innobase/btr/btr0defragment.cc +++ b/storage/innobase/btr/btr0defragment.cc @@ -499,7 +499,7 @@ btr_defragment_merge_pages( // n_recs_to_move number of records to to_page. We try to reduce // the targeted data size on the to_page by // BTR_DEFRAGMENT_PAGE_REDUCTION_STEP_SIZE and try again. - os_atomic_increment_ulint( + my_atomic_addlint( &btr_defragment_compression_failures, 1); max_ins_size_to_use = move_size > BTR_DEFRAGMENT_PAGE_REDUCTION_STEP_SIZE @@ -722,10 +722,10 @@ btr_defragment_n_pages( } mem_heap_free(heap); n_defragmented ++; - os_atomic_increment_ulint( + my_atomic_addlint( &btr_defragment_count, 1); if (n_pages == n_defragmented) { - os_atomic_increment_ulint( + my_atomic_addlint( &btr_defragment_failures, 1); } else { index->stat_defrag_n_pages_freed += (n_pages - n_defragmented); diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index dbe774e7e6b..12d073bf10f 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -96,11 +96,12 @@ struct set_numa_interleave_t { if (srv_numa_interleave) { + struct bitmask *numa_mems_allowed = numa_get_mems_allowed(); ib::info() << "Setting NUMA memory policy to" " MPOL_INTERLEAVE"; if (set_mempolicy(MPOL_INTERLEAVE, - numa_all_nodes_ptr->maskp, - numa_all_nodes_ptr->size) != 0) { + numa_mems_allowed->maskp, + numa_mems_allowed->size) != 0) { ib::warn() << "Failed to set NUMA memory" " policy to MPOL_INTERLEAVE: " @@ -1555,10 +1556,11 @@ buf_chunk_init( #if defined(HAVE_LIBNUMA) && defined(WITH_NUMA) if (srv_numa_interleave) { + struct bitmask *numa_mems_allowed = numa_get_mems_allowed(); int st = mbind(chunk->mem, chunk->mem_size(), MPOL_INTERLEAVE, - numa_all_nodes_ptr->maskp, - numa_all_nodes_ptr->size, + numa_mems_allowed->maskp, + numa_mems_allowed->size, MPOL_MF_MOVE); if (st != 0) { ib::warn() << "Failed to set NUMA memory policy of" @@ -2504,7 +2506,6 @@ buf_pool_withdraw_blocks( /* retry is not needed */ ++buf_withdraw_clock; - os_wmb; return(false); } @@ -4292,7 +4293,7 @@ loop: mutex_enter(pmutex); ut_ad(buf_pool->n_pend_reads > 0); - os_atomic_decrement_ulint(&buf_pool->n_pend_reads, 1); + my_atomic_addlint(&buf_pool->n_pend_reads, -1); buf_page_set_io_fix(bpage, BUF_IO_NONE); mutex_exit(pmutex); buf_LRU_free_page(bpage, true); @@ -4339,7 +4340,7 @@ loop: mutex_enter(pmutex); ut_ad(buf_pool->n_pend_reads > 0); - os_atomic_decrement_ulint(&buf_pool->n_pend_reads, 1); + my_atomic_addlint(&buf_pool->n_pend_reads, -1); buf_page_set_io_fix(bpage, BUF_IO_NONE); mutex_exit(pmutex); buf_LRU_free_page(bpage, true); @@ -5199,8 +5200,7 @@ buf_page_init( ut_a(buf_fix_count > 0); - os_atomic_increment_uint32(&block->page.buf_fix_count, - buf_fix_count); + my_atomic_add32((int32*) &block->page.buf_fix_count, buf_fix_count); buf_pool_watch_remove(buf_pool, hash_page); } else { @@ -5439,8 +5439,7 @@ buf_page_init_for_read( ut_a(buf_fix_count > 0); - os_atomic_increment_uint32( - &bpage->buf_fix_count, buf_fix_count); + my_atomic_add32((int32*) &bpage->buf_fix_count, buf_fix_count); ut_ad(buf_pool_watch_is_sentinel(buf_pool, watch_page)); buf_pool_watch_remove(buf_pool, watch_page); diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 6a33de63b69..3d527c6b017 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -7334,7 +7334,7 @@ dict_index_zip_pad_update( /* Use atomics even though we have the mutex. This is to ensure that we are able to read info->pad atomically. */ - os_atomic_increment_ulint(&info->pad, ZIP_PAD_INCR); + my_atomic_addlint(&info->pad, ZIP_PAD_INCR); MONITOR_INC(MONITOR_PAD_INCREMENTS); } @@ -7356,7 +7356,7 @@ dict_index_zip_pad_update( /* Use atomics even though we have the mutex. This is to ensure that we are able to read info->pad atomically. */ - os_atomic_decrement_ulint(&info->pad, ZIP_PAD_INCR); + my_atomic_addlint(&info->pad, -ZIP_PAD_INCR); info->n_rounds = 0; @@ -7430,10 +7430,7 @@ dict_index_zip_pad_optimal_page_size( return(UNIV_PAGE_SIZE); } - /* We use atomics to read index->zip_pad.pad. Here we use zero - as increment as are not changing the value of the 'pad'. */ - - pad = os_atomic_increment_ulint(&index->zip_pad.pad, 0); + pad = my_atomic_loadlint(&index->zip_pad.pad); ut_ad(pad < UNIV_PAGE_SIZE); sz = UNIV_PAGE_SIZE - pad; diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index b0d679d4619..a8f8e345728 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -1045,7 +1045,7 @@ dict_mem_create_temporary_tablename( size_t dblen = dbend - dbtab + 1; /* Increment a randomly initialized number for each temp file. */ - os_atomic_increment_uint32(&dict_temp_file_num, 1); + my_atomic_add32((int32*) &dict_temp_file_num, 1); size = dblen + (sizeof(TEMP_FILE_PREFIX) + 3 + 20 + 1 + 10); name = static_cast(mem_heap_alloc(heap, size)); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 604beec6f78..30765ddd357 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -379,6 +379,22 @@ static TYPELIB innodb_default_row_format_typelib = { NULL }; +/** Possible values of the parameter innodb_lock_schedule_algorithm */ +static const char* innodb_lock_schedule_algorithm_names[] = { + "fcfs", + "vats", + NullS +}; + +/** Used to define an enumerate type of the system variable +innodb_lock_schedule_algorithm. */ +static TYPELIB innodb_lock_schedule_algorithm_typelib = { + array_elements(innodb_lock_schedule_algorithm_names) - 1, + "innodb_lock_schedule_algorithm_typelib", + innodb_lock_schedule_algorithm_names, + NULL +}; + /* The following counter is used to convey information to InnoDB about server activity: in case of normal DML ops it is not sensible to call srv_active_wake_master_thread after each @@ -1671,7 +1687,7 @@ thd_is_replication_slave_thread( /*============================*/ THD* thd) /*!< in: thread handle */ { - return((ibool) thd_slave_thread(thd)); + return thd && ((ibool) thd_slave_thread(thd)); } /******************************************************************//** @@ -5685,8 +5701,7 @@ innobase_kill_query( wsrep_thd_is_BF(current_thd, FALSE)); } - if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) && - trx->abort_type == TRX_SERVER_ABORT) { + if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) { ut_ad(!lock_mutex_own()); lock_mutex_enter(); lock_mutex_taken = true; @@ -17080,7 +17095,7 @@ ha_innobase::check( && !dict_index_is_corrupted(index)) { /* Enlarge the fatal lock wait timeout during CHECK TABLE. */ - os_atomic_increment_ulint( + my_atomic_addlint( &srv_fatal_semaphore_wait_threshold, SRV_SEMAPHORE_WAIT_EXTENSION); @@ -17089,9 +17104,9 @@ ha_innobase::check( /* Restore the fatal lock wait timeout after CHECK TABLE. */ - os_atomic_decrement_ulint( + my_atomic_addlint( &srv_fatal_semaphore_wait_threshold, - SRV_SEMAPHORE_WAIT_EXTENSION); + -SRV_SEMAPHORE_WAIT_EXTENSION); if (err != DB_SUCCESS) { is_ok = false; @@ -22784,6 +22799,18 @@ static MYSQL_SYSVAR_ULONG(doublewrite_batch_size, srv_doublewrite_batch_size, NULL, NULL, 120, 1, 127, 0); #endif /* defined UNIV_DEBUG || defined UNIV_PERF_DEBUG */ +static MYSQL_SYSVAR_ENUM(lock_schedule_algorithm, innodb_lock_schedule_algorithm, + PLUGIN_VAR_RQCMDARG, + "The algorithm Innodb uses for deciding which locks to grant next when" + " a lock is released. Possible values are" + " FCFS" + " grant the locks in First-Come-First-Served order;" + " VATS" + " use the Variance-Aware-Transaction-Scheduling algorithm, which" + " uses an Eldest-Transaction-First heuristic.", + NULL, NULL, INNODB_LOCK_SCHEDULE_ALGORITHM_VATS, + &innodb_lock_schedule_algorithm_typelib); + static MYSQL_SYSVAR_ULONG(buffer_pool_instances, srv_buf_pool_instances, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "Number of buffer pool instances, set to higher value on high-end machines to increase scalability", @@ -23657,6 +23684,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(ft_sort_pll_degree), MYSQL_SYSVAR(large_prefix), MYSQL_SYSVAR(force_load_corrupted), + MYSQL_SYSVAR(lock_schedule_algorithm), MYSQL_SYSVAR(locks_unsafe_for_binlog), MYSQL_SYSVAR(lock_wait_timeout), MYSQL_SYSVAR(page_size), diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index 71aa7be3ef7..e9ad1a9d8c0 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -1447,7 +1447,7 @@ ibuf_add_ops( ulint i; for (i = 0; i < IBUF_OP_COUNT; i++) { - os_atomic_increment_ulint(&arr[i], ops[i]); + my_atomic_addlint(&arr[i], ops[i]); } } @@ -4788,7 +4788,7 @@ reset_bit: btr_pcur_close(&pcur); mem_heap_free(heap); - os_atomic_increment_ulint(&ibuf->n_merges, 1); + my_atomic_addlint(&ibuf->n_merges, 1); ibuf_add_ops(ibuf->n_merged_ops, mops); ibuf_add_ops(ibuf->n_discarded_ops, dops); if (space != NULL) { diff --git a/storage/innobase/include/buf0buf.ic b/storage/innobase/include/buf0buf.ic index bf7799774c6..a912f3eb127 100644 --- a/storage/innobase/include/buf0buf.ic +++ b/storage/innobase/include/buf0buf.ic @@ -968,7 +968,7 @@ ulint buf_block_fix( buf_page_t* bpage) { - return(os_atomic_increment_uint32(&bpage->buf_fix_count, 1)); + return(my_atomic_add32((int32*) &bpage->buf_fix_count, 1) + 1); } /** Increments the bufferfix count. @@ -1016,7 +1016,7 @@ ulint buf_block_unfix( buf_page_t* bpage) { - ulint count = os_atomic_decrement_uint32(&bpage->buf_fix_count, 1); + ulint count = my_atomic_add32((int32*) &bpage->buf_fix_count, -1) - 1; ut_ad(count + 1 != 0); return(count); } diff --git a/storage/innobase/include/ib0mutex.h b/storage/innobase/include/ib0mutex.h index 3ea0687da43..9f4ad8424f3 100644 --- a/storage/innobase/include/ib0mutex.h +++ b/storage/innobase/include/ib0mutex.h @@ -31,6 +31,7 @@ Created 2013-03-26 Sunny Bains. #include "ut0ut.h" #include "ut0rnd.h" #include "os0event.h" +#include "sync0arr.h" /** OS mutex for tracking lock/unlock for debugging */ template