From bb71d9abf26aa5a0f8bb703d4541e3c064eed003 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 8 Aug 2017 10:35:26 +0400 Subject: [PATCH 1/9] MDEV-12604 Comparison of JSON_EXTRACT result differs with Mysql. Comparison fixed to take the actual type of JSON value into account. Bug in escaping handling fixed. --- mysql-test/r/func_json.result | 15 +++++ mysql-test/t/func_json.test | 10 +++ sql/item_cmpfunc.cc | 9 +++ sql/item_jsonfunc.cc | 111 ++++++++++++++++++++++++++++++---- sql/item_jsonfunc.h | 4 +- strings/json_lib.c | 2 +- 6 files changed, 138 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index 894e46017f7..d004ebde35b 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -648,3 +648,18 @@ NULL SELECT JSON_EXTRACT( '{"foo":"bar"}', '$[*]' ); JSON_EXTRACT( '{"foo":"bar"}', '$[*]' ) NULL +select JSON_EXTRACT('{"name":"value"}', '$.name') = 'value'; +JSON_EXTRACT('{"name":"value"}', '$.name') = 'value' +1 +select JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = true; +JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = true +1 +select JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = false; +JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = false +0 +select JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = 1; +JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = 1 +1 +select JSON_EXTRACT('{\"input1\":\"\\u00f6\"}', '$.\"input1\"'); +JSON_EXTRACT('{\"input1\":\"\\u00f6\"}', '$.\"input1\"') +"\u00f6" diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 0ce742aac11..3bcdc6a8709 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -302,3 +302,13 @@ DROP TABLE t1; SELECT JSON_EXTRACT( '{"foo":"bar"}', '$[*].*' ); SELECT JSON_EXTRACT( '{"foo":"bar"}', '$[*]' ); +# +# MDEV-12604 Comparison of JSON_EXTRACT result differs with Mysql. +# + +select JSON_EXTRACT('{"name":"value"}', '$.name') = 'value'; +select JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = true; +select JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = false; +select JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = 1; +select JSON_EXTRACT('{\"input1\":\"\\u00f6\"}', '$.\"input1\"'); + diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d203efa6305..efab3da4ac1 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -670,6 +670,15 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, &Arg_comparator::compare_datetime; } + if ((*a)->is_json_type() ^ (*b)->is_json_type()) + { + Item **j_item= (*a)->is_json_type() ? a : b; + Item *uf= new(thd->mem_root) Item_func_json_unquote(thd, *j_item); + if (!uf || uf->fix_fields(thd, &uf)) + return 1; + *j_item= uf; + } + a= cache_converted_constant(thd, a, &a_cache, m_compare_type); b= cache_converted_constant(thd, b, &b_cache, m_compare_type); return set_compare_func(owner_arg, m_compare_type); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index c7639bc2513..3f001771e7c 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -587,24 +587,40 @@ void Item_func_json_unquote::fix_length_and_dec() } -String *Item_func_json_unquote::val_str(String *str) +String *Item_func_json_unquote::read_json(json_engine_t *je) { String *js= args[0]->val_json(&tmp_s); - json_engine_t je; - int c_len; if ((null_value= args[0]->null_value)) - return NULL; + return 0; - json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + json_scan_start(je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.value_type= (enum json_value_types) -1; /* To report errors right. */ + je->value_type= (enum json_value_types) -1; /* To report errors right. */ - if (json_read_value(&je)) + if (json_read_value(je)) goto error; - if (je.value_type != JSON_VALUE_STRING) + return js; + +error: + if (je->value_type == JSON_VALUE_STRING) + report_json_error(js, je, 0); + return js; +} + + +String *Item_func_json_unquote::val_str(String *str) +{ + json_engine_t je; + int c_len; + String *js; + + if (!(js= read_json(&je))) + return NULL; + + if (je.s.error || je.value_type != JSON_VALUE_STRING) return js; str->length(0); @@ -621,13 +637,86 @@ String *Item_func_json_unquote::val_str(String *str) return str; error: - if (je.value_type == JSON_VALUE_STRING) - report_json_error(js, &je, 0); - /* We just return the argument's value in the case of error. */ + report_json_error(js, &je, 0); return js; } +double Item_func_json_unquote::val_real() +{ + json_engine_t je; + double d= 0.0; + String *js; + + if ((js= read_json(&je)) != NULL) + { + switch (je.value_type) + { + case JSON_VALUE_NUMBER: + { + char *end; + int err; + d= my_strntod(je.s.cs, (char *) je.value, je.value_len, &end, &err); + break; + } + case JSON_VALUE_TRUE: + d= 1.0; + break; + case JSON_VALUE_STRING: + { + char *end; + int err; + d= my_strntod(js->charset(), (char *) js->ptr(), js->length(), + &end, &err); + break; + } + default: + break; + }; + } + + return d; +} + + +longlong Item_func_json_unquote::val_int() +{ + json_engine_t je; + longlong i= 0; + String *js; + + if ((js= read_json(&je)) != NULL) + { + switch (je.value_type) + { + case JSON_VALUE_NUMBER: + { + char *end; + int err; + i= my_strntoll(je.s.cs, (char *) je.value, je.value_len, 10, + &end, &err); + break; + } + case JSON_VALUE_TRUE: + i= 1; + break; + case JSON_VALUE_STRING: + { + char *end; + int err; + i= my_strntoll(js->charset(), (char *) js->ptr(), js->length(), 10, + &end, &err); + break; + } + default: + break; + }; + } + + return i; +} + + static int alloc_tmp_paths(THD *thd, uint n_paths, json_path_with_flags **paths,String **tmp_paths) { diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 394ed5f189a..cc089129556 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -127,12 +127,14 @@ class Item_func_json_unquote: public Item_str_func { protected: String tmp_s; - + String *read_json(json_engine_t *je); public: Item_func_json_unquote(THD *thd, Item *s): Item_str_func(thd, s) {} const char *func_name() const { return "json_unquote"; } void fix_length_and_dec(); String *val_str(String *); + double val_real(); + longlong val_int(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy(thd, mem_root, this); } }; diff --git a/strings/json_lib.c b/strings/json_lib.c index 7167b6a2a54..b0c843caec1 100644 --- a/strings/json_lib.c +++ b/strings/json_lib.c @@ -253,7 +253,7 @@ static int read_4_hexdigits(json_string_t *s, uchar *dest) if ((c_len= json_next_char(s)) <= 0) return s->error= json_eos(s) ? JE_EOS : JE_BAD_CHR; - if (s->c_next >= 128 || (t= json_instr_chr_map[s->c_next]) >= S_F) + if (s->c_next >= 128 || (t= json_instr_chr_map[s->c_next]) > S_F) return s->error= JE_SYN; s->c_str+= c_len; From 01a4eb8f761eb669fe2ae5139c73a7434b141a8f Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 8 Aug 2017 13:49:29 +0400 Subject: [PATCH 2/9] MDEV-12732 json.json_no_table fails with valgrind in buildbot and outside. The result_limit variable wasn't always initialized in Item_func_json_array::fix_length_and_dec(). --- sql/item_jsonfunc.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 3f001771e7c..315443fdcd2 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -1485,6 +1485,8 @@ void Item_func_json_array::fix_length_and_dec() ulonglong char_length= 2; uint n_arg; + result_limit= 0; + if (arg_count == 0) { collation.set(&my_charset_utf8_general_ci); @@ -1501,7 +1503,6 @@ void Item_func_json_array::fix_length_and_dec() fix_char_length_ulonglong(char_length); tmp_val.set_charset(collation.collation); - result_limit= 0; } From 4bca34d8a4d6e307ea9ee1d19c0aaf1e54df8837 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 8 Aug 2017 15:40:11 +0400 Subject: [PATCH 3/9] MDEV-12789 JSON_KEYS returns duplicate keys twice. Check for duplicating keys added. --- mysql-test/r/func_json.result | 6 +++++ mysql-test/t/func_json.test | 5 ++++ sql/item_jsonfunc.cc | 50 ++++++++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index d004ebde35b..aa8a2850a58 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -356,6 +356,12 @@ json_keys('foo') NULL Warnings: Warning 4038 Syntax error in JSON text in argument 1 to function 'json_keys' at position 1 +select json_keys('{"a":{"c":1, "d":2}, "b":2, "c":1, "a":3, "b":1, "c":2}'); +json_keys('{"a":{"c":1, "d":2}, "b":2, "c":1, "a":3, "b":1, "c":2}') +["a", "b", "c"] +select json_keys('{"c1": "value 1", "c1": "value 2"}'); +json_keys('{"c1": "value 1", "c1": "value 2"}') +["c1"] SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; select json_search(@j, 'one', 'abc'); json_search(@j, 'one', 'abc') diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 3bcdc6a8709..3bbda0de409 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -138,6 +138,11 @@ select json_keys('{"a":{"c":1, "d":2}, "b":2}'); select json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.a"); select json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.b"); select json_keys('foo'); +# +# mdev-12789 JSON_KEYS returns duplicate keys twice +# +select json_keys('{"a":{"c":1, "d":2}, "b":2, "c":1, "a":3, "b":1, "c":2}'); +select json_keys('{"c1": "value 1", "c1": "value 2"}'); SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; select json_search(@j, 'one', 'abc'); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 315443fdcd2..d871175a3ba 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -2779,6 +2779,41 @@ void Item_func_json_keys::fix_length_and_dec() } +/* + That function is for Item_func_json_keys::val_str exclusively. + It utilizes the fact the resulting string is in specific format: + ["key1", "key2"...] +*/ +static int check_key_in_list(String *res, + const uchar *key, int key_len) +{ + const uchar *c= (const uchar *) res->ptr() + 2; /* beginning '["' */ + const uchar *end= (const uchar *) res->end() - 1; /* ending '"' */ + + while (c < end) + { + int n_char; + for (n_char=0; c[n_char] != '"' && n_char < key_len; n_char++) + { + if (c[n_char] != key[n_char]) + break; + } + if (c[n_char] == '"') + { + if (n_char == key_len) + return 1; + } + else + { + while (c[n_char] != '"') + n_char++; + } + c+= n_char + 4; /* skip ', "' */ + } + return 0; +} + + String *Item_func_json_keys::val_str(String *str) { json_engine_t je; @@ -2835,6 +2870,7 @@ skip_search: while (json_scan_next(&je) == 0 && je.state != JST_OBJ_END) { const uchar *key_start, *key_end; + int key_len; switch (je.state) { @@ -2844,13 +2880,19 @@ skip_search: { key_end= je.s.c_str; } while (json_read_keyname_chr(&je) == 0); - if (je.s.error || - (n_keys > 0 && str->append(", ", 2)) || + if (je.s.error) + goto err_return; + key_len= key_end - key_start; + + if (!check_key_in_list(str, key_start, key_len)) + { + if ((n_keys > 0 && str->append(", ", 2)) || str->append("\"", 1) || - append_simple(str, key_start, key_end - key_start) || + append_simple(str, key_start, key_len) || str->append("\"", 1)) goto err_return; - n_keys++; + n_keys++; + } break; case JST_OBJ_START: case JST_ARRAY_START: From 2152fbdc89671380ceda41e3d6516d1d69c9dd28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 8 Aug 2017 15:25:48 +0300 Subject: [PATCH 4/9] MDEV-13470 DELETE IGNORE should not ignore deadlocks (again) This is basically a duplicate or a reincarnation of MDEV-117. For some reason, the test innodb.mdev-117 started failing in 10.2. It is uncertain when this test started failing. The test is nondeterministic, because there is a race condition between the concurrently executing DELETE IGNORE and DELETE statements. When a deadlock is reported for DELETE IGNORE, the SQL layer would call handler::print_error() but then proceed to the next row, as if no error had happened (which is the purpose of DELETE IGNORE). So, when it proceeded to handler::ha_rnd_next(), InnoDB would hit an assertion failure, because the transaction no longer exists, and we are not executing at the start of a statement. handler::print_error(): If thd_mark_transaction_to_rollback(thd, true) was called, clear the ME_JUST_WARNING and ME_JUST_INFO errflags, so that a note or warning will be promoted to an error if the transaction was aborted by a storage engine. --- sql/handler.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sql/handler.cc b/sql/handler.cc index a336d9916f1..61cc527d8a5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3345,6 +3345,12 @@ void handler::print_error(int error, myf errflag) DBUG_ENTER("handler::print_error"); DBUG_PRINT("enter",("error: %d",error)); + if (ha_thd()->transaction_rollback_request) + { + /* Ensure this becomes a true error */ + errflag&= ~(ME_JUST_WARNING | ME_JUST_INFO); + } + int textno= -1; // impossible value switch (error) { case EACCES: From c3f9fdeaf5b198c87d00764c2ddd0ffc17740b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 8 Aug 2017 15:32:23 +0300 Subject: [PATCH 5/9] Add DBUG "trx" instrumentation that was used for catching MDEV-13470 --- storage/innobase/row/row0mysql.cc | 9 +++++++-- storage/innobase/trx/trx0trx.cc | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 08844bdd133..0b53fb07e26 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -740,6 +740,8 @@ row_mysql_handle_errors( { dberr_t err; + DBUG_ENTER("row_mysql_handle_errors"); + handle_new_error: err = trx->error_state; @@ -747,6 +749,9 @@ handle_new_error: trx->error_state = DB_SUCCESS; + DBUG_LOG("trx", "handle error: " << ut_strerr(err) + << ";id=" << ib::hex(trx->id) << ", " << trx); + switch (err) { case DB_LOCK_WAIT_TIMEOUT: if (row_rollback_on_timeout) { @@ -795,7 +800,7 @@ handle_new_error: *new_err = err; - return(true); + DBUG_RETURN(true); case DB_DEADLOCK: case DB_LOCK_TABLE_FULL: @@ -840,7 +845,7 @@ handle_new_error: trx->error_state = DB_SUCCESS; - return(false); + DBUG_RETURN(false); } /********************************************************************//** diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 55bb2605d7e..2fe13ae7e9d 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -238,6 +238,7 @@ struct TrxFactory { trx_init(trx); + DBUG_LOG("trx", "Init: " << trx); trx->state = TRX_STATE_NOT_STARTED; trx->dict_operation_lock_mode = 0; @@ -452,6 +453,7 @@ trx_create_low() /* Trx state can be TRX_STATE_FORCED_ROLLBACK if the trx was forced to rollback before it's reused.*/ + DBUG_LOG("trx", "Create: " << trx); trx->state = TRX_STATE_NOT_STARTED; heap = mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 8); @@ -630,6 +632,7 @@ trx_free_prepared( ut_d(trx->in_rw_trx_list = FALSE); + DBUG_LOG("trx", "Free prepared: " << trx); trx->state = TRX_STATE_NOT_STARTED; /* Undo trx_resurrect_table_locks(). */ @@ -1753,6 +1756,7 @@ trx_commit_in_memory( ut_ad(!(trx->in_innodb & (TRX_FORCE_ROLLBACK | TRX_FORCE_ROLLBACK_ASYNC))); + DBUG_LOG("trx", "Autocommit in memory: " << trx); trx->state = TRX_STATE_NOT_STARTED; } else { @@ -1888,8 +1892,10 @@ trx_commit_in_memory( if (trx->abort) { trx->abort = false; + DBUG_LOG("trx", "Abort: " << trx); trx->state = TRX_STATE_FORCED_ROLLBACK; } else { + DBUG_LOG("trx", "Commit in memory: " << trx); trx->state = TRX_STATE_NOT_STARTED; } @@ -2061,6 +2067,7 @@ trx_cleanup_at_db_startup( ut_ad(trx->is_recovered); ut_ad(!trx->in_rw_trx_list); ut_ad(!trx->in_mysql_trx_list); + DBUG_LOG("trx", "Cleanup at startup: " << trx); trx->state = TRX_STATE_NOT_STARTED; } From ffa378949522f3dd491306ce81022c7db99378ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 8 Aug 2017 15:34:41 +0300 Subject: [PATCH 6/9] Follow-up to MDEV-11487: Remove InnoDB internal temporary tables row_update_for_mysql(): Remove the wrapper function and rename the function from row_update_for_mysql_using_upd_graph(). Remove the unused parameter mysql_rec. --- storage/innobase/handler/ha_innodb.cc | 4 ++-- storage/innobase/include/row0mysql.h | 5 +---- storage/innobase/row/row0mysql.cc | 23 +++-------------------- 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 28818ed7388..facfe349dd1 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -9417,7 +9417,7 @@ ha_innobase::update_row( innobase_srv_conc_enter_innodb(m_prebuilt); - error = row_update_for_mysql((byte*) old_row, m_prebuilt); + error = row_update_for_mysql(m_prebuilt); if (error == DB_SUCCESS && autoinc) { /* A value for an AUTO_INCREMENT column @@ -9532,7 +9532,7 @@ ha_innobase::delete_row( innobase_srv_conc_enter_innodb(m_prebuilt); - error = row_update_for_mysql((byte*) record, m_prebuilt); + error = row_update_for_mysql(m_prebuilt); innobase_srv_conc_exit_innodb(m_prebuilt); diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 8dfff46e78b..8e93faf7286 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -272,13 +272,10 @@ row_table_got_default_clust_index( const dict_table_t* table); /*!< in: table */ /** Does an update or delete of a row for MySQL. -@param[in] mysql_rec row in the MySQL format @param[in,out] prebuilt prebuilt struct in MySQL handle @return error code or DB_SUCCESS */ dberr_t -row_update_for_mysql( - const byte* mysql_rec, - row_prebuilt_t* prebuilt) +row_update_for_mysql(row_prebuilt_t* prebuilt) MY_ATTRIBUTE((warn_unused_result)); /** This can only be used when srv_locks_unsafe_for_binlog is TRUE or this diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 0b53fb07e26..e7ecca307cc 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1811,14 +1811,10 @@ public: /** Does an update or delete of a row for MySQL. -@param[in] mysql_rec row in the MySQL format @param[in,out] prebuilt prebuilt struct in MySQL handle @return error code or DB_SUCCESS */ -static dberr_t -row_update_for_mysql_using_upd_graph( - const byte* mysql_rec, - row_prebuilt_t* prebuilt) +row_update_for_mysql(row_prebuilt_t* prebuilt) { trx_savept_t savept; dberr_t err; @@ -1834,13 +1830,13 @@ row_update_for_mysql_using_upd_graph( upd_cascade_t* processed_cascades; bool got_s_lock = false; - DBUG_ENTER("row_update_for_mysql_using_upd_graph"); + DBUG_ENTER("row_update_for_mysql"); ut_ad(trx); ut_a(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED); ut_a(prebuilt->magic_n2 == ROW_PREBUILT_ALLOCATED); + ut_a(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); ut_ad(table->stat_initialized); - UT_NOT_USED(mysql_rec); if (!table->is_readable()) { return(row_mysql_get_table_status(table, trx, true)); @@ -2157,19 +2153,6 @@ error: DBUG_RETURN(err); } -/** Does an update or delete of a row for MySQL. -@param[in] mysql_rec row in the MySQL format -@param[in,out] prebuilt prebuilt struct in MySQL handle -@return error code or DB_SUCCESS */ -dberr_t -row_update_for_mysql( - const byte* mysql_rec, - row_prebuilt_t* prebuilt) -{ - ut_a(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); - return(row_update_for_mysql_using_upd_graph(mysql_rec, prebuilt)); -} - /** This can only be used when srv_locks_unsafe_for_binlog is TRUE or this session is using a READ COMMITTED or READ UNCOMMITTED isolation level. Before calling this function row_search_for_mysql() must have From c720e68f5378d27713c86baa6729e6b2b0ee1913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 8 Aug 2017 19:54:12 +0300 Subject: [PATCH 7/9] MDEV-13472 rpl.rpl_semi_sync_wait_point crashes because of thd_destructor_proxy The thd_destructor_proxy detects that no transactions are active and starts srv_shutdown_bg_undo_sources(), but fails to take into account that new transactions can still start, especially be slave but also by other threads. In addition there is no mutex when checking for active transaction so this is not safe. We relax the failing InnoDB debug assertion by allowing the execution of user transactions after the purge thread has been shut down. FIXME: If innodb_fast_shutdown=0, we should somehow guarantee that no new transactions can start after thd_destructor_proxy observed that trx_sys_any_active_transactions() did not hold. --- storage/innobase/trx/trx0purge.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 317087173c5..e22bbd61162 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -293,14 +293,16 @@ trx_purge_add_update_undo_to_history( After the purge thread has been given permission to exit, in fast shutdown, we may roll back transactions (trx->undo_no==0) - in THD::cleanup() invoked from unlink_thd(). */ + in THD::cleanup() invoked from unlink_thd(), and we may also + continue to execute user transactions. */ ut_ad(srv_undo_sources || ((srv_startup_is_before_trx_rollback_phase || trx_rollback_or_clean_is_active) && purge_sys->state == PURGE_STATE_INIT) || (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND && purge_sys->state == PURGE_STATE_DISABLED) - || (trx->undo_no == 0 && srv_fast_shutdown)); + || ((trx->undo_no == 0 || trx->in_mysql_trx_list) + && srv_fast_shutdown)); /* Add the log as the first in the history list */ flst_add_first(rseg_header + TRX_RSEG_HISTORY, From d0c66c87a77fbbb850b15564f35775a85c26fe34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 9 Aug 2017 09:53:24 +0300 Subject: [PATCH 8/9] Fix a random result mismatch of encryption.innodb_encrypt_log Disable change buffering, so that some data that was previously written to the encrypted redo log will not end up being copied to the unencrypted redo log due to change buffer merge. --- mysql-test/suite/encryption/r/innodb_encrypt_log.result | 1 + mysql-test/suite/encryption/t/innodb_encrypt_log.test | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/mysql-test/suite/encryption/r/innodb_encrypt_log.result b/mysql-test/suite/encryption/r/innodb_encrypt_log.result index f8f933be831..14df0012a9c 100644 --- a/mysql-test/suite/encryption/r/innodb_encrypt_log.result +++ b/mysql-test/suite/encryption/r/innodb_encrypt_log.result @@ -25,6 +25,7 @@ CREATE TEMPORARY TABLE t LIKE t0; INSERT INTO t VALUES (NULL,1,1,'private','secret'),(NULL,2,2,'sacred','success'), (NULL,3,3,'story','secure'),(NULL,4,4,'security','sacrament'); +SET GLOBAL innodb_change_buffering=none; SET GLOBAL innodb_flush_log_at_trx_commit=1; INSERT INTO t0 SELECT NULL, t1.col_int, t1.col_int_key, t1.col_char, t1.col_char_key diff --git a/mysql-test/suite/encryption/t/innodb_encrypt_log.test b/mysql-test/suite/encryption/t/innodb_encrypt_log.test index 5f60889600a..82293cc032e 100644 --- a/mysql-test/suite/encryption/t/innodb_encrypt_log.test +++ b/mysql-test/suite/encryption/t/innodb_encrypt_log.test @@ -32,6 +32,11 @@ INSERT INTO t VALUES (NULL,1,1,'private','secret'),(NULL,2,2,'sacred','success'), (NULL,3,3,'story','secure'),(NULL,4,4,'security','sacrament'); +# Prevent change buffering of key(col_char_key), so that +# after the restart, the data ('secret','success','secure','sacrament') +# cannot be emitted to the unencrypted redo log by change buffer merge. +SET GLOBAL innodb_change_buffering=none; + # Force a redo log flush at the next commit. SET GLOBAL innodb_flush_log_at_trx_commit=1; INSERT INTO t0 From 6b14fd6d6d75e09b966ddb0750ccfbb94a74a909 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Wed, 9 Aug 2017 10:42:38 +0300 Subject: [PATCH 9/9] A followup to MDEV-13470: remove the code that is now useless --- sql/handler.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/sql/handler.cc b/sql/handler.cc index 61cc527d8a5..d5d57f3e9a8 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3499,9 +3499,6 @@ void handler::print_error(int error, myf errflag) { String str, full_err_msg(ER_DEFAULT(ER_LOCK_DEADLOCK), system_charset_info); - /* cannot continue. the statement was already aborted in the engine */ - SET_FATAL_ERROR; - get_error_message(error, &str); full_err_msg.append(str); my_printf_error(ER_LOCK_DEADLOCK, "%s", errflag, full_err_msg.c_ptr_safe());