mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
Tentative implementation of
WL#4165 Prepared statements: validation WL#4166 Prepared statements: automatic re-prepare Fixes Bug#27430 Crash in subquery code when in PS and table DDL changed after PREPARE Bug#27690 Re-execution of prepared statement after table was replaced with a view crashes Bug#27420 A combination of PS and view operations cause error + assertion on shutdown The basic idea of the patch is to keep track of table metadata between prepared statement prepare and execute. If some table used in the statement has changed, the prepared statement is re-prepared before execution. See WL#4165 and WL#4166 contents and comments in the code for details of the implementation. include/my_global.h: Remove 'register' keyword to avoid warnings when swapping large structures that don't fit into a register. Any modern compiler is capable of placing a variable in a register when that would benefit performance. mysql-test/r/ps_1general.result: Update test results: since now we re-prepare automatically, more correct results are produced in prepare-ddl-execute scenario. mysql-test/r/query_cache_merge.result: Ensure that the table definition cache is large enough for the test to pass in --ps-protocol mysql-test/r/trigger.result: Update test results to reflect automatic statement reprepare. mysql-test/t/disabled.def: Enable ps_ddl.test, which now passes. mysql-test/t/ps_1general.test: Since now we re-execute prepared statements after DDL successfully, change the test to produce repeatable results. Remove expectancy of an error in one place where now we automatically reprepare the prepared statement. mysql-test/t/query_cache_merge.test: Ensure the table definition cache is large enough for the test to pass in --ps-protocol mysql-test/t/trigger.test: Sinc sql/item.cc: Implement Item_param "copy" functionality, used at re-prepare of a prepared statement. We copy the type of the original parameter, and move the assigned value, if any. Sic, the value is "moved", since it can be quite big -- e.g. in case we deal with a LONG DATA parameter. It's essential to move the value from the old parameter since at the time of re-prepare the client packet with the necessary information may be not available. sql/item.h: Declare a new method used for reprepare. sql/my_decimal.h: Implement "swap()" functionality of class my_decimal to be able to easily swap two decimal values. sql/mysql_priv.h: Declare enum_metadata_type. sql/mysqld.cc: Implement a status variable for the number of reprepared statements. sql/sql_base.cc: Implement metadata version validation. sql/share/errmsg.txt: Add two new error messages: ER_NEED_REPREPARE and ER_PS_REBIND. The first error (theoretically) never reaches the user. It is issued by the metadata validation framework when a metadata version has changed between prepare and execute. Later on it's intercepted and the statement is automatically re-prepared. Only if the error has occurred repeatedly MAX_REPREPARE_ATTEMTS (3) times do we return it to the user. The second error is issued when after re-prepare we discover that the metadata we sent over to the client using the binary protocol differs drammatically from the new result set metadata that the reprepared statement produces (e.g. number of result set columns is different). sql/sql_class.cc: Implement metadata version validation framework. sql/sql_class.h: Declarations for metadata version validation framework. sql/sql_parse.cc: Mark commands for which we must invalidate and reprepare a prepared statement when metadata has changed. sql/sql_prepare.cc: Implement WL#4165 and WL#4166 (limited support of metadata validation and re-prepare). sql/table.h: Implement metadata validation. tests/mysql_client_test.c: Add a test case for WL#4166
This commit is contained in:
@@ -23,6 +23,53 @@
|
||||
#include "log.h"
|
||||
#include "rpl_tblmap.h"
|
||||
|
||||
/**
|
||||
An abstract interface that can be used to take an action when
|
||||
the locking module notices that a table version has changed
|
||||
since the last execution. "Table" here may refer to any kind of
|
||||
table -- a base table, a temporary table, a view or an
|
||||
information schema table.
|
||||
|
||||
When we open and lock tables for execution of a prepared
|
||||
statement, we must verify that they did not change
|
||||
since statement prepare. If some table did change, the statement
|
||||
parse tree *may* be no longer valid, e.g. in case it contains
|
||||
optimizations that depend on table metadata.
|
||||
|
||||
This class provides an abstract interface (a method) that is
|
||||
invoked when such a situation takes place.
|
||||
The implementation of the interface in most cases simply
|
||||
reports an error, but the exact details depend on the nature of
|
||||
the SQL statement.
|
||||
|
||||
At most 1 instance of this class is active at a time, in which
|
||||
case THD::m_metadata_observer is not NULL.
|
||||
|
||||
@sa check_and_update_metadata_version() for details of the
|
||||
version tracking algorithm
|
||||
|
||||
@sa Execute_observer for details of how we detect that
|
||||
a metadata change is fatal and a re-prepare is necessary
|
||||
|
||||
@sa Open_tables_state::m_metadata_observer for the life cycle
|
||||
of metadata observers.
|
||||
*/
|
||||
|
||||
class Metadata_version_observer
|
||||
{
|
||||
protected:
|
||||
virtual ~Metadata_version_observer();
|
||||
public:
|
||||
/**
|
||||
Check if a change of metadata is OK. In future
|
||||
the signature of this method may be extended to accept the old
|
||||
and the new versions, but since currently the check is very
|
||||
simple, we only need the THD to report an error.
|
||||
*/
|
||||
virtual bool check_metadata_change(THD *thd)= 0;
|
||||
};
|
||||
|
||||
|
||||
class Relay_log_info;
|
||||
|
||||
class Query_log_event;
|
||||
@@ -406,6 +453,7 @@ typedef struct system_status_var
|
||||
ulong filesort_scan_count;
|
||||
/* Prepared statements and binary protocol */
|
||||
ulong com_stmt_prepare;
|
||||
ulong com_stmt_reprepare;
|
||||
ulong com_stmt_execute;
|
||||
ulong com_stmt_send_long_data;
|
||||
ulong com_stmt_fetch;
|
||||
@@ -436,7 +484,7 @@ void free_tmp_table(THD *thd, TABLE *entry);
|
||||
|
||||
/* The following macro is to make init of Query_arena simpler */
|
||||
#ifndef DBUG_OFF
|
||||
#define INIT_ARENA_DBUG_INFO is_backup_arena= 0
|
||||
#define INIT_ARENA_DBUG_INFO is_backup_arena= 0; is_reprepared= FALSE;
|
||||
#else
|
||||
#define INIT_ARENA_DBUG_INFO
|
||||
#endif
|
||||
@@ -452,6 +500,7 @@ public:
|
||||
MEM_ROOT *mem_root; // Pointer to current memroot
|
||||
#ifndef DBUG_OFF
|
||||
bool is_backup_arena; /* True if this arena is used for backup. */
|
||||
bool is_reprepared;
|
||||
#endif
|
||||
/*
|
||||
The states relfects three diffrent life cycles for three
|
||||
@@ -788,6 +837,20 @@ enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1,
|
||||
class Open_tables_state
|
||||
{
|
||||
public:
|
||||
/**
|
||||
As part of class THD, this member is set during execution
|
||||
of a prepared statement. When it is set, it is used
|
||||
by the locking subsystem to report a change in table metadata.
|
||||
|
||||
When Open_tables_state part of THD is reset to open
|
||||
a system or INFORMATION_SCHEMA table, the member is cleared
|
||||
to avoid spurious ER_NEED_REPREPARE errors -- system and
|
||||
INFORMATION_SCHEMA tables are not subject to metadata version
|
||||
tracking.
|
||||
@sa check_and_update_metadata_version()
|
||||
*/
|
||||
Metadata_version_observer *m_metadata_observer;
|
||||
|
||||
/**
|
||||
List of regular tables in use by this thread. Contains temporary and
|
||||
base tables that were opened with @see open_tables().
|
||||
@@ -891,6 +954,7 @@ public:
|
||||
extra_lock= lock= locked_tables= 0;
|
||||
prelocked_mode= NON_PRELOCKED;
|
||||
state_flags= 0U;
|
||||
m_metadata_observer= NULL;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -919,6 +983,25 @@ public:
|
||||
bool enable_slow_log;
|
||||
bool last_insert_id_used;
|
||||
SAVEPOINT *savepoints;
|
||||
/**
|
||||
When inside a substatement (a stored function or trigger
|
||||
statement), clear the metadata observer in THD, if any.
|
||||
Remember the value of the observer here, to be able
|
||||
to restore it when leaving the substatement.
|
||||
|
||||
We reset the observer to suppress errors when a substatement
|
||||
uses temporary tables. If a temporary table does not exist
|
||||
at start of the main statement, it's not prelocked
|
||||
and thus is not validated with other prelocked tables.
|
||||
|
||||
Later on, when the temporary table is opened, metadata
|
||||
versions mismatch, expectedly.
|
||||
|
||||
The proper solution for the problem is to re-validate tables
|
||||
of substatements (Bug#12257, Bug#27011, Bug#32868, Bug#33000),
|
||||
but it's not implemented yet.
|
||||
*/
|
||||
Metadata_version_observer *m_metadata_observer;
|
||||
};
|
||||
|
||||
|
||||
@@ -2777,6 +2860,7 @@ public:
|
||||
#define CF_STATUS_COMMAND 4
|
||||
#define CF_SHOW_TABLE_COMMAND 8
|
||||
#define CF_WRITE_LOGS_COMMAND 16
|
||||
#define CF_REEXECUTION_FRAGILE 32
|
||||
|
||||
/* Functions in sql_class.cc */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user