1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00
Files
mariadb/sql/derived_handler.cc
bsrikanth-mariadb 999b11bd02 MDEV-41243, MDEV-41273 Don't freeze mem_root after engine pushdown
When a statement is pushed down to an engine, the once-per-statement
part of the optimization (e.g. the first_cond_optimization part of
JOIN::optimize(), or the whole of JOIN::optimize() for a pushed down
UNION) is skipped. Re-executing the same prepared statement or stored
routine without pushdown then allocated from a mem_root already marked
ROOT_FLAG_READ_ONLY, hitting an assertion in alloc_root().

Add LEX::dont_freeze_mem_root, set by the pushdown_handler and
derived_handler constructors so it covers every engine implementing
them. Prepared_statement::execute_loop(), sp_head::execute() (through
sp_head::dont_freeze_mem_root) and the SP instruction reparse path no
longer freeze the mem_root of such statements. The flag is only
declared and used in PROTECT_STATEMENT_MEMROOT builds.

The flag is per execution for prepared statements:
Prepared_statement::execute_loop() clears it after each execution, so
the mem_root is frozen again after the first later execution that is
not pushed down. For a stored routine the instruction's LEX keeps the
flag, so once one instruction has been pushed down the routine's
mem_root stays unfrozen, even if later executions are not pushed down,
unless the routine is re-parsed into a new LEX.

Add tests for prepared SELECT, UNION, UPDATE and DELETE, prepared
EXPLAIN of a pushed UNION, derived tables, stored procedures,
functions, triggers, cursors and metadata-invalidation reparse, with
pushdown switched on and off. Most prepared statement and routine
tests check the statements received by the remote server, or EXPLAIN,
to verify that pushdown actually happened.
2026-10-21 10:15:30 +05:30

123 lines
3.4 KiB
C++

/*
Copyright (c) 2018, 2019 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 02111-1301 USA */
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "derived_handler.h"
derived_handler::derived_handler(THD *thd_arg, handlerton *ht_arg)
: thd(thd_arg), ht(ht_arg), derived(0), table(0), tmp_table_param(0),
unit(0), select(0)
{
/* Pushdown skips optimization, so don't freeze the mem_root */
thd_arg->lex->mark_pushed_down();
}
/**
The methods of the Pushdown_derived class.
The objects of this class are used for pushdown of the derived tables
into engines. The main method of the class is Pushdown_derived::execute()
that initiates execution of the query specifying a derived by a foreign
engine, receives the rows of the result set and put them in a temporary
table on the server side.
The method uses only the functions of the derived_handle interface to do
this. The constructor of the class gets this interface as a parameter.
Currently a derived tables pushed into an engine is always materialized.
It could be changed if the cases when the tables is used as driving table.
*/
Pushdown_derived::Pushdown_derived(TABLE_LIST *tbl, derived_handler *h)
: derived(tbl), handler(h)
{
}
int Pushdown_derived::execute()
{
int err;
THD *thd= handler->thd;
TABLE *table= handler->table;
TMP_TABLE_PARAM *tmp_table_param= handler->tmp_table_param;
DBUG_ENTER("Pushdown_derived::execute");
if ((err= handler->init_scan()))
goto error;
while (!(err= handler->next_row()))
{
if (unlikely(thd->check_killed()))
{
handler->end_scan();
DBUG_RETURN(-1);
}
if ((err= table->file->ha_write_tmp_row(table->record[0])))
{
bool is_duplicate;
if (likely(!table->file->is_fatal_error(err, HA_CHECK_DUP)))
continue; // Distinct elimination
if (create_internal_tmp_table_from_heap(thd, table,
tmp_table_param->start_recinfo,
&tmp_table_param->recinfo,
err, 1, &is_duplicate))
DBUG_RETURN(1);
if (is_duplicate)
continue;
}
}
if (err != 0 && err != HA_ERR_END_OF_FILE)
goto error;
if ((err= handler->end_scan()))
goto error_2;
DBUG_RETURN(0);
error:
handler->end_scan();
error_2:
handler->print_error(err, MYF(0));
DBUG_RETURN(-1); // Error not sent to client
}
void derived_handler::print_error(int error, myf errflag)
{
my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
}
void derived_handler::set_derived(TABLE_LIST *tbl)
{
derived= tbl;
table= tbl->table;
unit= tbl->derived;
select= unit->first_select();
tmp_table_param= ((select_unit *)(unit->result))->get_tmp_table_param();
}