mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
This task loads the stats of the tables that are used in a query, from the trace into the optimizer. This feature is controlled by optimizer_replay_context, and points to an user defined variable name, that has the context information. The stats such as num_of_records present in the table, indexes if present, their names, along with the average number of records_per_key with in each index, cost of reading an index are loaded from the trace. Additionally, stats from range analysis i.e. ranges, and the corresponding number of records, along with their costs are also loaded from the trace. The trace context which is in JSON format is firstly set into the user defined session variable, and this variable name is set to optimizer_replay_context system variable. Later, when a user issues a query, the contents of the json context is parsed and an in memory representation is built using the class Optimizer_context_replay. This class is then used by the optimizer to update and save original values of the tables, indexes, and range stats in the methods "set_statistics_for_table()" of sql_statistics.cc, and "check_quick_select()" of opt_range.cc. After the query gets finished, the statistics that were updated in the optimizer are restored back to the saved original values. The entry point for parsing the json structure is in "mysql_execute_command()" of sql_parse.cc, and similarly exit point i.e. to restore the saved stats of the optimizer is at the end of the same method. Two new warning/error messages are introduced: ER_JSON_OPTIMIZER_REPLAY_CONTEXT_PARSE_FAILED, and ER_JSON_OPTIMIZER_REPLAY_CONTEXT_MATCH_FAILED
212 lines
6.1 KiB
C++
212 lines
6.1 KiB
C++
/*
|
|
Copyright (c) 2025, 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 Street, Fifth Floor, Boston, MA 02110-1335
|
|
USA */
|
|
|
|
#include "sql_json_lib.h"
|
|
#include "mysql.h"
|
|
#include "sql_select.h"
|
|
|
|
namespace json_reader
|
|
{
|
|
|
|
/*
|
|
check if the given read_elem_key can be read from the json_engine.
|
|
if not, fill the err_buf with an error message
|
|
@return
|
|
false OK
|
|
true Parse Error
|
|
*/
|
|
static bool check_reading_of_elem_key(json_engine_t *je,
|
|
const char *read_elem_key,
|
|
String *err_buf)
|
|
{
|
|
if (json_read_value(je))
|
|
{
|
|
err_buf->append(STRING_WITH_LEN("error reading "));
|
|
err_buf->append(read_elem_key, strlen(read_elem_key));
|
|
err_buf->append(STRING_WITH_LEN(" value"));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
parse the json to read and put a double into the argument value.
|
|
fill in the err_buf if any error has occurred during parsing
|
|
@return
|
|
false OK
|
|
true Parse Error
|
|
*/
|
|
bool read_double(json_engine_t *je, const char *read_elem_key, String *err_buf,
|
|
double &value)
|
|
{
|
|
if (check_reading_of_elem_key(je, read_elem_key, err_buf))
|
|
return true;
|
|
|
|
const char *size= (const char *) je->value_begin;
|
|
char *size_end= (char *) je->value_end;
|
|
int conv_err;
|
|
value= my_strtod(size, &size_end, &conv_err);
|
|
if (conv_err)
|
|
{
|
|
err_buf->append(read_elem_key, strlen(read_elem_key));
|
|
err_buf->append(STRING_WITH_LEN(" member must be a floating point value"));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
parse the json to read and put a string into the argument value.
|
|
fill in the err_buf if any error has occurred during parsing
|
|
@return
|
|
false OK
|
|
true Parse Error
|
|
*/
|
|
bool read_string(THD *thd, json_engine_t *je, const char *read_elem_key,
|
|
String *err_buf, char *&value)
|
|
{
|
|
if (check_reading_of_elem_key(je, read_elem_key, err_buf))
|
|
return true;
|
|
|
|
StringBuffer<128> val_buf;
|
|
if (json_unescape_to_string((const char *) je->value, je->value_len,
|
|
&val_buf))
|
|
{
|
|
err_buf->append(STRING_WITH_LEN("un-escaping error of "));
|
|
err_buf->append(read_elem_key, strlen(read_elem_key));
|
|
err_buf->append(STRING_WITH_LEN(" element"));
|
|
return true;
|
|
}
|
|
|
|
value= strdup_root(thd->mem_root, val_buf.c_ptr_safe());
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
parse the json to read and put a ha_rows into the argument value.
|
|
fill in the err_buf if any error has occurred either during parsing,
|
|
or string to numeric conversion or during the limit check.
|
|
Remove the quotes around the value if unescape_required is enabled.
|
|
@return
|
|
false OK
|
|
true Error
|
|
*/
|
|
bool read_ha_rows_and_check_limit(json_engine_t *je, const char *read_elem_key,
|
|
String *err_buf, ha_rows &value,
|
|
ha_rows LIMIT_VAL,
|
|
const char *limit_val_type,
|
|
bool unescape_required)
|
|
{
|
|
if (check_reading_of_elem_key(je, read_elem_key, err_buf))
|
|
return true;
|
|
|
|
StringBuffer<32> size_buf;
|
|
char *size;
|
|
char *size_end;
|
|
int conv_err;
|
|
if (unescape_required)
|
|
{
|
|
if (json_unescape_to_string((const char *) je->value, je->value_len,
|
|
&size_buf))
|
|
{
|
|
err_buf->append(
|
|
STRING_WITH_LEN("un-escaping error of rec_per_key element"));
|
|
return true;
|
|
}
|
|
size= size_buf.c_ptr_safe();
|
|
size_end= size + strlen(size);
|
|
}
|
|
else
|
|
{
|
|
size= (char *) je->value_begin;
|
|
size_end= (char *) je->value_end;
|
|
}
|
|
|
|
value= my_strtoll10(size, &size_end, &conv_err);
|
|
|
|
if (conv_err)
|
|
{
|
|
err_buf->append(read_elem_key, strlen(read_elem_key));
|
|
err_buf->append(STRING_WITH_LEN(" member must be a numeric value"));
|
|
return true;
|
|
}
|
|
else if (value > LIMIT_VAL)
|
|
{
|
|
err_buf->append(read_elem_key, strlen(read_elem_key));
|
|
err_buf->append(STRING_WITH_LEN(" is out of range of "));
|
|
err_buf->append(limit_val_type, strlen(limit_val_type));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}; // namespace json_reader
|
|
|
|
/*
|
|
@brief
|
|
Read a JSON object. The members to read are described in *members array.
|
|
|
|
@detail
|
|
members is an array terminated by a member with (char*)NULL as a name.
|
|
Each element describes the element name, type and location where to store
|
|
the read value. See class Read_named_member for details.
|
|
|
|
In the JSON document, object members can come in any order. Non-mandatory
|
|
members may be absent.
|
|
|
|
If JSON document has a member which was not requested to read,
|
|
then we just skip it and not report any error.
|
|
|
|
@seealso
|
|
class Read_named_member
|
|
|
|
@return
|
|
0 OK
|
|
1 An Error occured
|
|
*/
|
|
int json_read_object(json_engine_t *je, Read_named_member *members,
|
|
String *err_buf)
|
|
{
|
|
int rc;
|
|
while (!(rc= json_scan_next(je)) && je->state != JST_OBJ_END)
|
|
{
|
|
Json_saved_parser_state save1(je);
|
|
for (Read_named_member *memb= members; memb->name; memb++)
|
|
{
|
|
Json_string js_name(memb->name);
|
|
if (json_key_matches(je, js_name.get()))
|
|
{
|
|
if (memb->value.read_value(je, memb->name, err_buf))
|
|
return 1;
|
|
memb->value_assigned= true;
|
|
break;
|
|
}
|
|
save1.restore_to(je);
|
|
}
|
|
}
|
|
|
|
/* Check if all members got values */
|
|
for (Read_named_member *memb= members; memb->name; memb++)
|
|
{
|
|
if (!memb->is_optional && !memb->value_assigned)
|
|
{
|
|
err_buf->append(STRING_WITH_LEN("\""));
|
|
err_buf->append(memb->name, strlen(memb->name));
|
|
err_buf->append(STRING_WITH_LEN("\" element not present"));
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|