mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
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.
123 lines
3.4 KiB
C++
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();
|
|
}
|
|
|