summaryrefslogtreecommitdiff
path: root/sql/sql_prepare.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_prepare.cc')
-rw-r--r--sql/sql_prepare.cc368
1 files changed, 253 insertions, 115 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 1db04c4656f..22b5e1aa326 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -89,6 +89,7 @@ When one supplies long data for a placeholder:
#include "unireg.h"
#include "sql_class.h" // set_var.h: THD
#include "set_var.h"
+#include "sql_admin.h" // fill_check_table_metadata_fields
#include "sql_prepare.h"
#include "sql_parse.h" // insert_precheck, update_precheck, delete_precheck
#include "sql_base.h" // open_normal_and_derived_tables
@@ -105,6 +106,8 @@ When one supplies long data for a placeholder:
#include "sql_cursor.h"
#include "sql_show.h"
#include "sql_repl.h"
+#include "sql_help.h" // mysqld_help_prepare
+#include "sql_table.h" // fill_checksum_table_metadata_fields
#include "slave.h"
#include "sp_head.h"
#include "sp.h"
@@ -129,6 +132,7 @@ static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8;
#include "wsrep_mysqld.h"
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
+#include "xa.h" // xa_recover_get_fields
/**
A result class used to send cursor rows using the binary protocol.
@@ -179,6 +183,7 @@ public:
my_bool iterations;
my_bool start_param;
my_bool read_types;
+
#ifndef EMBEDDED_LIBRARY
bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
uchar *read_pos, String *expanded_query);
@@ -196,7 +201,7 @@ public:
virtual ~Prepared_statement();
void setup_set_params();
virtual Query_arena::Type type() const;
- virtual void cleanup_stmt();
+ virtual void cleanup_stmt(bool restore_set_statement_vars);
bool set_name(const LEX_CSTRING *name);
inline void close_cursor() { delete cursor; cursor= 0; }
inline bool is_in_use() { return flags & (uint) IS_IN_USE; }
@@ -1255,11 +1260,17 @@ insert_params_from_actual_params_with_log(Prepared_statement *stmt,
DBUG_RETURN(0);
}
-/**
+
+/*
Validate INSERT statement.
@param stmt prepared statement
- @param tables global/local table list
+ @param table_list global/local table list
+ @param fields list of the table's fields to insert values
+ @param values_list values to be inserted into the table
+ @param update_fields the update fields.
+ @param update_values the update values.
+ @param duplic a way to handle duplicates
@retval
FALSE success
@@ -1267,29 +1278,18 @@ insert_params_from_actual_params_with_log(Prepared_statement *stmt,
TRUE error, error message is set in THD
*/
-static bool mysql_test_insert(Prepared_statement *stmt,
- TABLE_LIST *table_list,
- List<Item> &fields,
- List<List_item> &values_list,
- List<Item> &update_fields,
- List<Item> &update_values,
- enum_duplicates duplic)
+static bool mysql_test_insert_common(Prepared_statement *stmt,
+ TABLE_LIST *table_list,
+ List<Item> &fields,
+ List<List_item> &values_list,
+ List<Item> &update_fields,
+ List<Item> &update_values,
+ enum_duplicates duplic)
{
THD *thd= stmt->thd;
List_iterator_fast<List_item> its(values_list);
List_item *values;
- DBUG_ENTER("mysql_test_insert");
-
- /*
- Since INSERT DELAYED doesn't support temporary tables, we could
- not pre-open temporary tables for SQLCOM_INSERT / SQLCOM_REPLACE.
- Open them here instead.
- */
- if (table_list->lock_type != TL_WRITE_DELAYED)
- {
- if (thd->open_temporary_tables(table_list))
- goto error;
- }
+ DBUG_ENTER("mysql_test_insert_common");
if (insert_precheck(thd, table_list))
goto error;
@@ -1357,6 +1357,44 @@ error:
/**
+ Open temporary tables if required and validate INSERT statement.
+
+ @param stmt prepared statement
+ @param tables global/local table list
+
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
+*/
+
+static bool mysql_test_insert(Prepared_statement *stmt,
+ TABLE_LIST *table_list,
+ List<Item> &fields,
+ List<List_item> &values_list,
+ List<Item> &update_fields,
+ List<Item> &update_values,
+ enum_duplicates duplic)
+{
+ THD *thd= stmt->thd;
+
+ /*
+ Since INSERT DELAYED doesn't support temporary tables, we could
+ not pre-open temporary tables for SQLCOM_INSERT / SQLCOM_REPLACE.
+ Open them here instead.
+ */
+ if (table_list->lock_type != TL_WRITE_DELAYED)
+ {
+ if (thd->open_temporary_tables(table_list))
+ return true;
+ }
+
+ return mysql_test_insert_common(stmt, table_list, fields, values_list,
+ update_fields, update_values, duplic);
+}
+
+
+/**
Validate UPDATE statement.
@param stmt prepared statement
@@ -2274,6 +2312,83 @@ static int mysql_test_handler_read(Prepared_statement *stmt,
/**
+ Send metadata to a client on PREPARE phase of XA RECOVER statement
+ processing
+
+ @param stmt prepared statement
+
+ @return 0 on success, 1 on failure, 2 in case metadata was already sent
+*/
+
+static int mysql_test_xa_recover(Prepared_statement *stmt)
+{
+ THD *thd= stmt->thd;
+ List<Item> field_list;
+
+ xa_recover_get_fields(thd, &field_list, nullptr);
+ return send_stmt_metadata(thd, stmt, &field_list);
+}
+
+
+/**
+ Send metadata to a client on PREPARE phase of HELP statement processing
+
+ @param stmt prepared statement
+
+ @return 0 on success, 1 on failure, 2 in case metadata was already sent
+*/
+
+static int mysql_test_help(Prepared_statement *stmt)
+{
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ if (mysqld_help_prepare(thd, stmt->lex->help_arg, &fields))
+ return 1;
+
+ return send_stmt_metadata(thd, stmt, &fields);
+}
+
+
+/**
+ Send metadata to a client on PREPARE phase of admin related statements
+ processing
+
+ @param stmt prepared statement
+
+ @return 0 on success, 1 on failure, 2 in case metadata was already sent
+*/
+
+static int mysql_test_admin_table(Prepared_statement *stmt)
+{
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ fill_check_table_metadata_fields(thd, &fields);
+ return send_stmt_metadata(thd, stmt, &fields);
+}
+
+
+/**
+ Send metadata to a client on PREPARE phase of CHECKSUM TABLE statement
+ processing
+
+ @param stmt prepared statement
+
+ @return 0 on success, 1 on failure, 2 in case metadata was already sent
+*/
+
+static int mysql_test_checksum_table(Prepared_statement *stmt)
+{
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ fill_checksum_table_metadata_fields(thd, &fields);
+ return send_stmt_metadata(thd, stmt, &fields);
+}
+
+
+/**
Perform semantic analysis of the parsed tree and send a response packet
to the client.
@@ -2347,6 +2462,13 @@ static bool check_prepared_statement(Prepared_statement *stmt)
lex->duplicates);
break;
+ case SQLCOM_LOAD:
+ res= mysql_test_insert_common(stmt, tables, lex->field_list,
+ lex->many_values,
+ lex->update_list, lex->value_list,
+ lex->duplicates);
+ break;
+
case SQLCOM_UPDATE:
res= mysql_test_update(stmt, tables);
/* mysql_test_update returns 2 if we need to switch to multi-update */
@@ -2483,11 +2605,6 @@ static bool check_prepared_statement(Prepared_statement *stmt)
}
break;
case SQLCOM_CREATE_VIEW:
- if (lex->create_view->mode == VIEW_ALTER)
- {
- my_message(ER_UNSUPPORTED_PS, ER_THD(thd, ER_UNSUPPORTED_PS), MYF(0));
- goto error;
- }
res= mysql_test_create_view(stmt);
break;
case SQLCOM_DO:
@@ -2515,71 +2632,47 @@ static bool check_prepared_statement(Prepared_statement *stmt)
/* Statement and field info has already been sent */
DBUG_RETURN(res == 1 ? TRUE : FALSE);
- /*
- Note that we don't need to have cases in this list if they are
- marked with CF_STATUS_COMMAND in sql_command_flags
- */
- case SQLCOM_SHOW_EXPLAIN:
- case SQLCOM_DROP_TABLE:
- case SQLCOM_DROP_SEQUENCE:
- case SQLCOM_RENAME_TABLE:
- case SQLCOM_ALTER_TABLE:
- case SQLCOM_ALTER_SEQUENCE:
- case SQLCOM_COMMIT:
- case SQLCOM_CREATE_INDEX:
- case SQLCOM_DROP_INDEX:
- case SQLCOM_ROLLBACK:
- case SQLCOM_ROLLBACK_TO_SAVEPOINT:
- case SQLCOM_TRUNCATE:
- case SQLCOM_DROP_VIEW:
- case SQLCOM_REPAIR:
+ case SQLCOM_XA_RECOVER:
+ res= mysql_test_xa_recover(stmt);
+ if (res == 2)
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(false);
+ break;
+
+ case SQLCOM_HELP:
+ res= mysql_test_help(stmt);
+ if (res == 2)
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(false);
+ break;
+
case SQLCOM_ANALYZE:
- case SQLCOM_OPTIMIZE:
- case SQLCOM_CHANGE_MASTER:
- case SQLCOM_RESET:
- case SQLCOM_FLUSH:
- case SQLCOM_SLAVE_START:
- case SQLCOM_SLAVE_STOP:
- case SQLCOM_SLAVE_ALL_START:
- case SQLCOM_SLAVE_ALL_STOP:
- case SQLCOM_INSTALL_PLUGIN:
- case SQLCOM_UNINSTALL_PLUGIN:
- case SQLCOM_CREATE_DB:
- case SQLCOM_DROP_DB:
- case SQLCOM_ALTER_DB_UPGRADE:
- case SQLCOM_CHECKSUM:
- case SQLCOM_CREATE_USER:
- case SQLCOM_ALTER_USER:
- case SQLCOM_RENAME_USER:
- case SQLCOM_DROP_USER:
- case SQLCOM_CREATE_ROLE:
- case SQLCOM_DROP_ROLE:
case SQLCOM_ASSIGN_TO_KEYCACHE:
+ case SQLCOM_CHECK:
+ case SQLCOM_OPTIMIZE:
case SQLCOM_PRELOAD_KEYS:
- case SQLCOM_GRANT:
- case SQLCOM_GRANT_ROLE:
- case SQLCOM_REVOKE:
- case SQLCOM_REVOKE_ALL:
- case SQLCOM_REVOKE_ROLE:
- case SQLCOM_KILL:
- case SQLCOM_COMPOUND:
- case SQLCOM_SHUTDOWN:
+ case SQLCOM_REPAIR:
+ res= mysql_test_admin_table(stmt);
+ if (res == 2)
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(false);
+ break;
+
+ case SQLCOM_CHECKSUM:
+ res= mysql_test_checksum_table(stmt);
+ if (res == 2)
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(false);
break;
case SQLCOM_PREPARE:
case SQLCOM_EXECUTE:
+ case SQLCOM_EXECUTE_IMMEDIATE:
case SQLCOM_DEALLOCATE_PREPARE:
+ my_message(ER_UNSUPPORTED_PS, ER_THD(thd, ER_UNSUPPORTED_PS), MYF(0));
+ goto error;
+
default:
- /*
- Trivial check of all status commands. This is easier than having
- things in the above case list, as it's less chance for mistakes.
- */
- if (!(sql_command_flags[sql_command] & CF_STATUS_COMMAND))
- {
- /* All other statements are not supported yet. */
- my_message(ER_UNSUPPORTED_PS, ER_THD(thd, ER_UNSUPPORTED_PS), MYF(0));
- goto error;
- }
break;
}
if (res == 0)
@@ -3484,7 +3577,7 @@ static void mysql_stmt_execute_common(THD *thd,
SQLCOM_EXECUTE implementation.
Execute prepared statement using parameter values from
- lex->prepared_stmt_params and send result to the client using
+ lex->prepared_stmt.params() and send result to the client using
text protocol. This is called from mysql_execute_command and
therefore should behave like an ordinary query (e.g. not change
global THD data, such as warning count, server status, etc).
@@ -4100,11 +4193,14 @@ Query_arena::Type Prepared_statement::type() const
}
-void Prepared_statement::cleanup_stmt()
+void Prepared_statement::cleanup_stmt(bool restore_set_statement_vars)
{
DBUG_ENTER("Prepared_statement::cleanup_stmt");
DBUG_PRINT("enter",("stmt: %p", this));
- lex->restore_set_statement_var();
+
+ if (restore_set_statement_vars)
+ lex->restore_set_statement_var();
+
thd->rollback_item_tree_changes();
cleanup_items(free_list);
thd->cleanup_after_query();
@@ -4249,7 +4345,10 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
init_param_array(this));
if (thd->security_ctx->password_expired &&
- lex->sql_command != SQLCOM_SET_OPTION)
+ lex->sql_command != SQLCOM_SET_OPTION &&
+ lex->sql_command != SQLCOM_PREPARE &&
+ lex->sql_command != SQLCOM_EXECUTE &&
+ lex->sql_command != SQLCOM_DEALLOCATE_PREPARE)
{
thd->restore_backup_statement(this, &stmt_backup);
thd->restore_active_arena(this, &stmt_backup);
@@ -4310,12 +4409,6 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_PREPARE;
}
- /*
- Restore original values of variables modified on handling
- SET STATEMENT clause.
- */
- thd->lex->restore_set_statement_var();
-
/* The order is important */
lex->unit.cleanup();
@@ -4344,7 +4437,11 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
if (lex->sql_command != SQLCOM_SET_OPTION)
lex_unlock_plugins(lex);
- cleanup_stmt();
+ /*
+ Pass the value true to restore original values of variables modified
+ on handling SET STATEMENT clause.
+ */
+ cleanup_stmt(true);
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;
thd->cur_stmt= save_cur_stmt;
@@ -5028,6 +5125,25 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
/* Go! */
+ /*
+ Log COM_EXECUTE to the general log. Note, that in case of SQL
+ prepared statements this causes two records to be output:
+
+ Query EXECUTE <statement name>
+ Execute <statement SQL text>
+
+ This is considered user-friendly, since in the
+ second log entry we output values of parameter markers.
+
+ Do not print anything if this is an SQL prepared statement and
+ we're inside a stored procedure (also called Dynamic SQL) --
+ sub-statements inside stored procedures are not logged into
+ the general log.
+ */
+
+ if (thd->spcont == nullptr)
+ general_log_write(thd, COM_STMT_EXECUTE, thd->query(), thd->query_length());
+
if (open_cursor)
error= mysql_open_cursor(thd, &result, &cursor);
else
@@ -5046,8 +5162,9 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip,
1);
- error= mysql_execute_command(thd);
+ error= mysql_execute_command(thd, true);
MYSQL_QUERY_EXEC_DONE(error);
+ thd->update_server_status();
}
else
{
@@ -5073,8 +5190,47 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
DBUG_ASSERT(! (error && cursor));
if (! cursor)
- cleanup_stmt();
-
+ /*
+ Pass the value false to don't restore set statement variables.
+ See the next comment block for more details.
+ */
+ cleanup_stmt(false);
+
+ /*
+ Log the statement to slow query log if it passes filtering.
+ We do it here for prepared statements despite of the fact that the function
+ log_slow_statement() is also called upper the stack from the function
+ dispatch_command(). The reason for logging slow queries here is that
+ the function log_slow_statement() must be called before restoring system
+ variables that could be set on execution of SET STATEMENT clause. Since
+ for prepared statement restoring of system variables set on execution of
+ SET STATEMENT clause is performed on return from the method
+ Prepared_statement::execute(), by the time the function log_slow_statement()
+ be invoked from the function dispatch_command() all variables set by
+ the SET STATEMEN clause would be already reset to their original values
+ that break semantic of the SET STATEMENT clause.
+
+ E.g., lets consider the following statements
+ SET slow_query_log= 1;
+ SET @@long_query_time=0.01;
+ PREPARE stmt FROM 'set statement slow_query_log=0 for select sleep(0.1)';
+ EXECUTE stmt;
+
+ It's expected that the above statements don't write any record
+ to slow query log since the system variable slow_query_log is set to 0
+ during execution of the whole statement
+ 'set statement slow_query_log=0 for select sleep(0.1)'
+
+ However, if the function log_slow_statement wasn't called here the record
+ for the statement would be written to slow query log since the variable
+ slow_query_log is restored to its original value by the time the function
+ log_slow_statement is called from disptach_command() to write a record
+ into slow query log.
+ */
+ log_slow_statement(thd);
+
+ lex->restore_set_statement_var();
+
/*
EXECUTE command has its own dummy "explain data". We don't need it,
instead, we want to keep the query plan of the statement that was
@@ -5116,24 +5272,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
thd->protocol->send_out_parameters(&this->lex->param_list);
}
- /*
- Log COM_EXECUTE to the general log. Note, that in case of SQL
- prepared statements this causes two records to be output:
-
- Query EXECUTE <statement name>
- Execute <statement SQL text>
-
- This is considered user-friendly, since in the
- second log entry we output values of parameter markers.
-
- Do not print anything if this is an SQL prepared statement and
- we're inside a stored procedure (also called Dynamic SQL) --
- sub-statements inside stored procedures are not logged into
- the general log.
- */
- if (likely(error == 0 && thd->spcont == NULL))
- general_log_write(thd, COM_STMT_EXECUTE, thd->query(), thd->query_length());
-
error:
thd->lex->restore_set_statement_var();
flags&= ~ (uint) IS_IN_USE;