mirror of
https://github.com/MariaDB/server.git
synced 2025-09-11 05:52:26 +03:00
Patch for Bug#27863 (excessive memory usage for many small queries in a
multiquery packet). Background: - a query can contain multiple SQL statements; - the server frees resources allocated to process a query when the whole query is handled. In other words, resources allocated to process one SQL statement from a multi-statement query are freed when all SQL statements are handled. The problem was that the parser allocated a buffer of size of the whole query for each SQL statement in a multi-statement query. Thus, if a query had many SQL-statements (so, the query was long), but each SQL statement was short, ther parser tried to allocate huge amount of memory (number of small SQL statements * length of the whole query). The memory was allocated for a so-called "cpp buffer", which is intended to store pre-processed SQL statement -- SQL text without version specific comments. The fix is to allocate memory for the "cpp buffer" once for all SQL statements (once for a query).
This commit is contained in:
@@ -458,7 +458,6 @@ static void handle_bootstrap_impl(THD *thd)
|
||||
{
|
||||
MYSQL_FILE *file= bootstrap_file;
|
||||
char *buff;
|
||||
const char* found_semicolon= NULL;
|
||||
|
||||
DBUG_ENTER("handle_bootstrap");
|
||||
|
||||
@@ -531,7 +530,8 @@ static void handle_bootstrap_impl(THD *thd)
|
||||
mode we have only one thread.
|
||||
*/
|
||||
thd->set_time();
|
||||
mysql_parse(thd, thd->query(), length, & found_semicolon);
|
||||
Parser_state parser_state(thd, thd->query(), length);
|
||||
mysql_parse(thd, thd->query(), length, &parser_state);
|
||||
close_thread_tables(thd); // Free tables
|
||||
|
||||
bootstrap_error= thd->is_error();
|
||||
@@ -1074,19 +1074,21 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
(char *) thd->security_ctx->host_or_ip);
|
||||
char *packet_end= thd->query() + thd->query_length();
|
||||
/* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
|
||||
const char* end_of_stmt= NULL;
|
||||
|
||||
general_log_write(thd, command, thd->query(), thd->query_length());
|
||||
DBUG_PRINT("query",("%-.4096s",thd->query()));
|
||||
#if defined(ENABLED_PROFILING)
|
||||
thd->profiling.set_query_source(thd->query(), thd->query_length());
|
||||
#endif
|
||||
Parser_state parser_state(thd, thd->query(), thd->query_length());
|
||||
|
||||
mysql_parse(thd, thd->query(), thd->query_length(), &end_of_stmt);
|
||||
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
|
||||
|
||||
while (!thd->killed && (end_of_stmt != NULL) && ! thd->is_error())
|
||||
while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
|
||||
! thd->is_error())
|
||||
{
|
||||
char *beginning_of_next_stmt= (char*) end_of_stmt;
|
||||
char *beginning_of_next_stmt= (char*)
|
||||
parser_state.m_lip.found_semicolon;
|
||||
|
||||
thd->protocol->end_statement();
|
||||
query_cache_end_of_result(thd);
|
||||
@@ -1127,8 +1129,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
*/
|
||||
statistic_increment(thd->status_var.questions, &LOCK_status);
|
||||
thd->set_time(); /* Reset the query start time. */
|
||||
parser_state.reset(beginning_of_next_stmt, length);
|
||||
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
|
||||
mysql_parse(thd, beginning_of_next_stmt, length, &end_of_stmt);
|
||||
mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
|
||||
}
|
||||
|
||||
DBUG_PRINT("info",("query ready"));
|
||||
@@ -5721,7 +5724,7 @@ void mysql_init_multi_delete(LEX *lex)
|
||||
*/
|
||||
|
||||
void mysql_parse(THD *thd, const char *inBuf, uint length,
|
||||
const char ** found_semicolon)
|
||||
Parser_state *parser_state)
|
||||
{
|
||||
int error;
|
||||
DBUG_ENTER("mysql_parse");
|
||||
@@ -5751,10 +5754,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
|
||||
Parser_state parser_state(thd, inBuf, length);
|
||||
|
||||
bool err= parse_sql(thd, & parser_state, NULL);
|
||||
*found_semicolon= parser_state.m_lip.found_semicolon;
|
||||
bool err= parse_sql(thd, parser_state, NULL);
|
||||
|
||||
if (!err)
|
||||
{
|
||||
@@ -5769,6 +5769,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
|
||||
{
|
||||
if (! thd->is_error())
|
||||
{
|
||||
const char *found_semicolon= parser_state->m_lip.found_semicolon;
|
||||
/*
|
||||
Binlog logs a string starting from thd->query and having length
|
||||
thd->query_length; so we set thd->query_length correctly (to not
|
||||
@@ -5779,12 +5780,12 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
|
||||
PROCESSLIST.
|
||||
Note that we don't need LOCK_thread_count to modify query_length.
|
||||
*/
|
||||
if (*found_semicolon && (ulong) (*found_semicolon - thd->query()))
|
||||
if (found_semicolon && (ulong) (found_semicolon - thd->query()))
|
||||
thd->set_query_inner(thd->query(),
|
||||
(uint32) (*found_semicolon -
|
||||
(uint32) (found_semicolon -
|
||||
thd->query() - 1));
|
||||
/* Actually execute the query */
|
||||
if (*found_semicolon)
|
||||
if (found_semicolon)
|
||||
{
|
||||
lex->safe_to_cache_query= 0;
|
||||
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
|
||||
@@ -5821,11 +5822,6 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
|
||||
thd->cleanup_after_query();
|
||||
DBUG_ASSERT(thd->change_list.is_empty());
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There are no multi queries in the cache. */
|
||||
*found_semicolon= NULL;
|
||||
}
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
Reference in New Issue
Block a user