diff --git a/mysql-test/main/cset_narrowing.result b/mysql-test/main/cset_narrowing.result index 797401c67d89b..e14d6c8cc256b 100644 --- a/mysql-test/main/cset_narrowing.result +++ b/mysql-test/main/cset_narrowing.result @@ -303,7 +303,7 @@ EXPLAIN "rows": 4, "cost": "COST_REPLACED", "filtered": 100, - "index_condition": "t1.mb3 = '????' or t1.mb3 = 'hello'" + "index_condition": "t1.mb3 = '😊' or t1.mb3 = 'hello'" } } ] @@ -338,7 +338,7 @@ EXPLAIN "rows": 10002, "cost": "COST_REPLACED", "filtered": 100, - "attached_condition": "convert(t1.mb3 using utf8mb4) > '????'" + "attached_condition": "convert(t1.mb3 using utf8mb4) > '😊'" } } ] @@ -384,7 +384,7 @@ EXPLAIN "rows": 3, "cost": "COST_REPLACED", "filtered": 100, - "index_condition": "t2.mb4 > '????'" + "index_condition": "t2.mb4 > '😊'" } } ] diff --git a/mysql-test/main/explain_json.result b/mysql-test/main/explain_json.result index 84cffd18424b9..81409cfbc1590 100644 --- a/mysql-test/main/explain_json.result +++ b/mysql-test/main/explain_json.result @@ -2305,3 +2305,64 @@ SET optimizer_trace = 'enabled=on'; SELECT * FROM t1 WHERE a IN ( SELECT b FROM t2 INNER JOIN t1 ON (a = pk) ); a DROP TABLE t1, t2; +# +# MDEV-16462: EXPLAIN FORMAT=JSON produces illegal json text in the attached_condition field +# +set names utf8mb4; +create table t1 ( +id int(11) not null auto_increment, +oid binary(16) not null, +primary key (id, oid) +); +insert into t1 values +(1,0x02697732663011E39AE220CF3068F21D), +(2,0x02697732663011E39AE220CF3068F21D); +create table t2 ( +id int(11) not null, +oid binary(16) not null, +primary key (id) +); +insert into t2 values +(1,0x02697732663011E39AE220CF3068F21D), +(2,0x02697732663011E39AE220CF3068F21D); +explain format=json +select t1.id from t1, t2 where t1.oid=t2.oid and t2.id=1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.006067082, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "const", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["id"], + "ref": ["const"], + "rows": 1, + "filtered": 100 + } + }, + { + "table": { + "table_name": "t1", + "access_type": "index", + "key": "PRIMARY", + "key_length": "20", + "used_key_parts": ["id", "oid"], + "loops": 1, + "rows": 2, + "cost": 0.006067082, + "filtered": 100, + "attached_condition": "t1.oid = 0x02697732663011E39AE220CF3068F21D", + "using_index": true + } + } + ] + } +} +drop table t1, t2; +set names default; diff --git a/mysql-test/main/explain_json.test b/mysql-test/main/explain_json.test index 0d34b95070426..39d44b08544d4 100644 --- a/mysql-test/main/explain_json.test +++ b/mysql-test/main/explain_json.test @@ -500,3 +500,33 @@ INSERT INTO t2 VALUES SET optimizer_trace = 'enabled=on'; SELECT * FROM t1 WHERE a IN ( SELECT b FROM t2 INNER JOIN t1 ON (a = pk) ); DROP TABLE t1, t2; + +--echo # +--echo # MDEV-16462: EXPLAIN FORMAT=JSON produces illegal json text in the attached_condition field +--echo # +set names utf8mb4; +create table t1 ( + id int(11) not null auto_increment, + oid binary(16) not null, + primary key (id, oid) +); + +insert into t1 values +(1,0x02697732663011E39AE220CF3068F21D), +(2,0x02697732663011E39AE220CF3068F21D); + +create table t2 ( + id int(11) not null, + oid binary(16) not null, + primary key (id) +); + +insert into t2 values +(1,0x02697732663011E39AE220CF3068F21D), +(2,0x02697732663011E39AE220CF3068F21D); + +explain format=json +select t1.id from t1, t2 where t1.oid=t2.oid and t2.id=1; + +drop table t1, t2; +set names default; diff --git a/sql/item.cc b/sql/item.cc index 3bdd7f82da327..e9bf4c61af0c8 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -555,6 +555,16 @@ void Item::print_value(String *str) { switch (cmp_type()) { case STRING_RESULT: + if (ptr->charset() == &my_charset_bin && ptr->length()) + { + StringBuffer<64> escaped_val; + escaped_val.set_hex(ptr->ptr(), ptr->length()); + str->append(STRING_WITH_LEN("0x")); + str->append(escaped_val); + } + else + append_unescaped(str, ptr->ptr(), ptr->length()); + break; case TIME_RESULT: append_unescaped(str, ptr->ptr(), ptr->length()); break; diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 77aab40092f65..288dd0637c786 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -211,15 +211,15 @@ int Explain_query::send_explain(THD *thd, bool extended) if (extended) { char buff[1024]; - String str(buff,(uint32) sizeof(buff), system_charset_info); - str.length(0); - /* - The warnings system requires input in utf8, @see - mysqld_show_warnings(). - */ - lex->unit.print(&str, QT_EXPLAIN_EXTENDED); - push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_YES, str.c_ptr_safe()); + String str(buff, (uint32) sizeof(buff), &my_charset_utf8mb4_bin); + str.length(0); + /* + The warnings system requires input in utf8, @see + mysqld_show_warnings(). + */ + lex->unit.print(&str, QT_EXPLAIN_EXTENDED); + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_YES, + str.c_ptr_safe()); } } if (res) @@ -330,7 +330,7 @@ bool Explain_query::print_query_blocks_json(Json_writer *writer, const bool is_a void Explain_query::send_explain_json_to_output(Json_writer *writer, select_result_sink *output) { - CHARSET_INFO *cs= system_charset_info; + CHARSET_INFO *cs= &my_charset_utf8mb4_bin; List item_list; const String *buf= writer->output.get_string(); THD *thd= output->thd; @@ -1287,7 +1287,7 @@ void Explain_aggr_filesort::print_json_members(Json_writer *writer, bool is_analyze) { char item_buf[256]; - String str(item_buf, sizeof(item_buf), &my_charset_bin); + String str(item_buf, sizeof(item_buf), &my_charset_utf8mb4_bin); str.length(0); List_iterator_fast it(sort_items); @@ -1389,7 +1389,7 @@ void Explain_table_access::push_extra(enum explain_extra_tag extra_tag) void Explain_table_access::fill_key_str(String *key_str, bool is_json) const { - CHARSET_INFO *cs= system_charset_info; + CHARSET_INFO *cs= &my_charset_utf8mb4_bin; bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT || type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE); LEX_CSTRING hash_key_prefix= { STRING_WITH_LEN("#hash#") }; @@ -1775,7 +1775,7 @@ static void write_item(Json_writer *writer, Item *item) { THD *thd= current_thd; char item_buf[256]; - String str(item_buf, sizeof(item_buf), &my_charset_bin); + String str(item_buf, sizeof(item_buf), &my_charset_utf8mb4_bin); str.length(0); ulonglong save_option_bits= thd->variables.option_bits; @@ -1784,7 +1784,7 @@ static void write_item(Json_writer *writer, Item *item) item->print(&str, QT_EXPLAIN); thd->variables.option_bits= save_option_bits; - writer->add_str(str.c_ptr_safe()); + writer->add_str(str.c_ptr_safe(), str.length()); } static void append_item_to_str(String *out, Item *item) @@ -2739,10 +2739,10 @@ int Explain_update::print_explain(Explain_query *query, else if (key.get_key_name()) { const char *name= key.get_key_name(); - key_buf.set(name, strlen(name), &my_charset_bin); + key_buf.set(name, strlen(name), &my_charset_utf8mb4_bin); char buf[64]; size_t length= longlong10_to_str(key.get_key_len(), buf, 10) - buf; - key_len_buf.copy(buf, length, &my_charset_bin); + key_len_buf.copy(buf, length, &my_charset_utf8mb4_bin); } if (using_where)