Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 192 additions & 1 deletion mysql-test/main/opt_trace.result
Original file line number Diff line number Diff line change
Expand Up @@ -11581,7 +11581,7 @@ SELECT 'a\0' LIMIT 0 {
"select_id": 1,
"steps": [
{
"expanded_query": "select 'a\0' AS `a\x00` limit 0"
"expanded_query": "select 'a\\0' AS `a\\x00` limit 0"
}
]
}
Expand Down Expand Up @@ -13862,3 +13862,194 @@ set @@optimizer_switch= @save_optimizer_switch;
set @@use_stat_tables= @save_use_stat_tables;
set @@histogram_size= @save_histogram_size;
set @@optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity;
#
# MDEV-30354: Fix JSON escaping in optimizer trace output
#
set optimizer_trace='enabled=on';
# Test 1: Simple double quotes
SELECT LENGTH("a");
LENGTH("a")
1
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;
valid_json trace
1 {
"steps": [
{
"join_preparation": {
"select_id": 1,
"steps": [
{
"expanded_query": "select octet_length('a') AS `LENGTH(\"a\")`"
}
]
}
},
{
"join_optimization": {
"select_id": 1,
"steps": []
}
}
]
}
# Test 2: Nested quotes
SELECT LENGTH("a\"b");
LENGTH("a\"b")
3
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;
valid_json trace
1 {
"steps": [
{
"join_preparation": {
"select_id": 1,
"steps": [
{
"expanded_query": "select octet_length('a\"b') AS `LENGTH(\"a\\\"b\")`"
}
]
}
},
{
"join_optimization": {
"select_id": 1,
"steps": []
}
}
]
}
# Test 3: Special characters with quotes
SELECT LENGTH("a\n\"b\t\"c");
LENGTH("a\n\"b\t\"c")
7
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;
valid_json trace
1 {
"steps": [
{
"join_preparation": {
"select_id": 1,
"steps": [
{
"expanded_query": "select octet_length('a\\n\"b\\t\"c') AS `LENGTH(\"a\\n\\\"b\\t\\\"c\")`"
}
]
}
},
{
"join_optimization": {
"select_id": 1,
"steps": []
}
}
]
}
# Test 4: New line
SELECT COLUMN_JSON(COLUMN_CREATE("foo", "New
line" AS CHAR));
COLUMN_JSON(COLUMN_CREATE("foo", "New
line" AS CHAR))
{"foo":"New\u000Aline"}
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;
valid_json trace
1 {
"steps": [
{
"join_preparation": {
"select_id": 1,
"steps": [
{
"expanded_query": "select column_json(column_create('foo','New\\nline' AS char charset latin1 collate latin1_swedish_ci )) AS `COLUMN_JSON(COLUMN_CREATE(\"foo\", \"New\nline\" AS CHAR))`"
}
]
}
},
{
"join_optimization": {
"select_id": 1,
"steps": []
}
}
]
}
# Test 5: Backslashes
SELECT LENGTH("a\\b");
LENGTH("a\\b")
3
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;
valid_json trace
1 {
"steps": [
{
"join_preparation": {
"select_id": 1,
"steps": [
{
"expanded_query": "select octet_length('a\\\\b') AS `LENGTH(\"a\\\\b\")`"
}
]
}
},
{
"join_optimization": {
"select_id": 1,
"steps": []
}
}
]
}
# Test 6: JSON functions
SELECT JSON_EXTRACT('{"key":"value"}', "$.key");
JSON_EXTRACT('{"key":"value"}', "$.key")
"value"
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;
valid_json trace
1 {
"steps": [
{
"join_preparation": {
"select_id": 1,
"steps": [
{
"expanded_query": "select json_extract('{\"key\":\"value\"}','$.key') AS `JSON_EXTRACT('{\"key\":\"value\"}', \"$.key\")`"
}
]
}
},
{
"join_optimization": {
"select_id": 1,
"steps": []
}
}
]
}
# Test 7: Failed escape
SET NAMES utf8mb4;
SELECT "𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞" AS x;
x
𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;
valid_json trace
1 {
"steps": [
{
"join_preparation": {
"select_id": 1,
"steps": [
{
"expanded_query": "Error: json_escape() failed to escape query string"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... why did this happen? You should have allocated enough, right? Was it due to "illegal symbol"?

}
]
}
},
{
"join_optimization": {
"select_id": 1,
"steps": []
}
}
]
}
SET NAMES DEFAULT;
SET optimizer_trace='enabled=off';
39 changes: 39 additions & 0 deletions mysql-test/main/opt_trace.test
Original file line number Diff line number Diff line change
Expand Up @@ -1344,3 +1344,42 @@ set @@optimizer_switch= @save_optimizer_switch;
set @@use_stat_tables= @save_use_stat_tables;
set @@histogram_size= @save_histogram_size;
set @@optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity;

--echo #
--echo # MDEV-30354: Fix JSON escaping in optimizer trace output
--echo #

set optimizer_trace='enabled=on';

--echo # Test 1: Simple double quotes
SELECT LENGTH("a");
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;

--echo # Test 2: Nested quotes
SELECT LENGTH("a\"b");
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;

--echo # Test 3: Special characters with quotes
SELECT LENGTH("a\n\"b\t\"c");
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;

--echo # Test 4: New line
SELECT COLUMN_JSON(COLUMN_CREATE("foo", "New
line" AS CHAR));
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;

--echo # Test 5: Backslashes
SELECT LENGTH("a\\b");
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;

--echo # Test 6: JSON functions
SELECT JSON_EXTRACT('{"key":"value"}', "$.key");
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;

--echo # Test 7: Failed escape
SET NAMES utf8mb4;
SELECT "𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞" AS x;
SELECT json_valid(trace) AS valid_json, trace FROM information_schema.optimizer_trace;

SET NAMES DEFAULT;
SET optimizer_trace='enabled=off';
20 changes: 19 additions & 1 deletion sql/opt_trace.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,25 @@ void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
The output is not very pretty lots of back-ticks, the output
is as the one in explain extended , lets try to improved it here.
*/
writer->add("expanded_query", str.c_ptr_safe(), str.length());

StringBuffer<1024> escaped_str(system_charset_info);
escaped_str.alloc(str.length() * 6);
int result= json_escape(str.charset(),
(const uchar*) str.ptr(),
(const uchar*) str.ptr() + str.length(),
&my_charset_utf8mb4_bin,
(uchar*) escaped_str.ptr(),
(uchar*) escaped_str.ptr() +
escaped_str.alloced_length());

if (result >= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coding style:

if ()
{
  ...
}
else
{
   ...
}

escaped_str.length(result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you, but you don't have to do this. Just pass result directly to writer->add().

writer->add("expanded_query", escaped_str.c_ptr_safe(),
escaped_str.length());
} else {
writer->add("expanded_query",
"Error: json_escape() failed to escape query string");
}
}

void opt_trace_disable_if_no_security_context_access(THD *thd)
Expand Down