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.cc175
1 files changed, 115 insertions, 60 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index dc5c526aadc..7101b5087e0 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2002, 2015, Oracle and/or its affiliates.
- Copyright (c) 2008, 2015, MariaDB
+ Copyright (c) 2008, 2017, 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
@@ -74,7 +74,7 @@ When one supplies long data for a placeholder:
- Server gets the long data in pieces with command type
'COM_STMT_SEND_LONG_DATA'.
- - The packet recieved will have the format as:
+ - The packet received will have the format as:
[COM_STMT_SEND_LONG_DATA:1][STMT_ID:4][parameter_number:2][data]
- data from the packet is appended to the long data value buffer for this
placeholder.
@@ -84,7 +84,7 @@ When one supplies long data for a placeholder:
at statement execute.
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_class.h" // set_var.h: THD
@@ -106,6 +106,7 @@ When one supplies long data for a placeholder:
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
+#include "sql_handler.h" // mysql_ha_rm_tables
#include "probes_mysql.h"
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
@@ -344,7 +345,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
int2store(buff+5, columns);
int2store(buff+7, stmt->param_count);
buff[9]= 0; // Guard against a 4.1 client
- tmp= min(stmt->thd->warning_info->statement_warn_count(), 65535);
+ tmp= MY_MIN(stmt->thd->get_stmt_da()->current_statement_warn_count(), 65535);
int2store(buff+10, tmp);
/*
@@ -361,7 +362,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
if (!error)
/* Flag that a response has already been sent */
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
DBUG_RETURN(error);
}
@@ -374,7 +375,7 @@ static bool send_prep_stmt(Prepared_statement *stmt,
thd->client_stmt_id= stmt->id;
thd->client_param_count= stmt->param_count;
thd->clear_error();
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
return 0;
}
@@ -977,7 +978,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
typecode= sint2korr(read_pos);
read_pos+= 2;
- (**it).unsigned_flag= test(typecode & signed_bit);
+ (**it).unsigned_flag= MY_TEST(typecode & signed_bit);
setup_one_conversion_function(thd, *it, (uchar) (typecode & ~signed_bit));
}
}
@@ -1254,6 +1255,17 @@ static bool mysql_test_insert(Prepared_statement *stmt,
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 (open_temporary_tables(thd, table_list))
+ goto error;
+ }
+
if (insert_precheck(thd, table_list))
goto error;
@@ -1462,7 +1474,10 @@ static bool mysql_test_delete(Prepared_statement *stmt,
goto error;
}
- DBUG_RETURN(mysql_prepare_delete(thd, table_list, &lex->select_lex.where));
+ DBUG_RETURN(mysql_prepare_delete(thd, table_list,
+ lex->select_lex.with_wild,
+ lex->select_lex.item_list,
+ &lex->select_lex.where));
error:
DBUG_RETURN(TRUE);
}
@@ -1764,7 +1779,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
if (select_lex->item_list.elements)
{
/* Base table and temporary table are not in the same name space. */
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ if (!lex->create_info.tmp_table())
create_table->open_type= OT_BASE_ONLY;
if (open_normal_and_derived_tables(stmt->thd, lex->query_tables,
@@ -1806,7 +1821,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
@note This function handles create view commands.
@retval FALSE Operation was a success.
- @retval TRUE An error occured.
+ @retval TRUE An error occurred.
*/
static bool mysql_test_create_view(Prepared_statement *stmt)
@@ -1823,6 +1838,13 @@ static bool mysql_test_create_view(Prepared_statement *stmt)
if (create_view_precheck(thd, tables, view, lex->create_view_mode))
goto err;
+ /*
+ Since we can't pre-open temporary tables for SQLCOM_CREATE_VIEW,
+ (see mysql_create_view) we have to do it here instead.
+ */
+ if (open_temporary_tables(thd, tables))
+ goto err;
+
if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL,
DT_PREPARE))
goto err;
@@ -2058,7 +2080,20 @@ static bool check_prepared_statement(Prepared_statement *stmt)
/* Reset warning count for each query that uses tables */
if (tables)
- thd->warning_info->opt_clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
+
+ if (sql_command_flags[sql_command] & CF_HA_CLOSE)
+ mysql_ha_rm_tables(thd, tables);
+
+ /*
+ Open temporary tables that are known now. Temporary tables added by
+ prelocking will be opened afterwards (during open_tables()).
+ */
+ if (sql_command_flags[sql_command] & CF_PREOPEN_TMP_TABLES)
+ {
+ if (open_temporary_tables(thd, tables))
+ goto error;
+ }
switch (sql_command) {
case SQLCOM_REPLACE:
@@ -2074,7 +2109,6 @@ static bool check_prepared_statement(Prepared_statement *stmt)
/* mysql_test_update returns 2 if we need to switch to multi-update */
if (res != 2)
break;
-
/* fall through */
case SQLCOM_UPDATE_MULTI:
res= mysql_test_multiupdate(stmt, tables, res == 2);
@@ -2147,6 +2181,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
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_RENAME_TABLE:
case SQLCOM_ALTER_TABLE:
@@ -2164,6 +2199,8 @@ static bool check_prepared_statement(Prepared_statement *stmt)
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:
@@ -2178,6 +2215,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_GRANT:
case SQLCOM_REVOKE:
case SQLCOM_KILL:
+ case SQLCOM_SHUTDOWN:
break;
case SQLCOM_PREPARE:
@@ -2488,6 +2526,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
object and because of this can be used in different threads.
*/
lex->thd= thd;
+ DBUG_ASSERT(!lex->explain);
if (lex->empty_field_list_on_rset)
{
@@ -2688,7 +2727,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
DBUG_PRINT("exec_query", ("%s", stmt->query()));
DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt));
- open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY);
+ open_cursor= MY_TEST(flags & (ulong) CURSOR_TYPE_READ_ONLY);
thd->protocol= &thd->protocol_binary;
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
@@ -2852,7 +2891,7 @@ void mysqld_stmt_reset(THD *thd, char *packet)
stmt->state= Query_arena::STMT_PREPARED;
- general_log_print(thd, thd->command, NullS);
+ general_log_print(thd, thd->get_command(), NullS);
my_ok(thd);
@@ -2874,7 +2913,7 @@ void mysqld_stmt_close(THD *thd, char *packet)
Prepared_statement *stmt;
DBUG_ENTER("mysqld_stmt_close");
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
if (!(stmt= find_prepared_statement(thd, stmt_id)))
DBUG_VOID_RETURN;
@@ -2885,7 +2924,7 @@ void mysqld_stmt_close(THD *thd, char *packet)
*/
DBUG_ASSERT(! stmt->is_in_use());
stmt->deallocate();
- general_log_print(thd, thd->command, NullS);
+ general_log_print(thd, thd->get_command(), NullS);
DBUG_VOID_RETURN;
}
@@ -2950,7 +2989,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
status_var_increment(thd->status_var.com_stmt_send_long_data);
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
#ifndef EMBEDDED_LIBRARY
/* Minimal size of long data packet is 6 bytes */
if (packet_length < MYSQL_LONG_DATA_HEADER)
@@ -2979,28 +3018,25 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
param= stmt->param_array[param_number];
- Diagnostics_area new_stmt_da, *save_stmt_da= thd->stmt_da;
- Warning_info new_warnning_info(thd->query_id, false);
- Warning_info *save_warinig_info= thd->warning_info;
+ Diagnostics_area new_stmt_da(thd->query_id, false, true);
+ Diagnostics_area *save_stmt_da= thd->get_stmt_da();
- thd->stmt_da= &new_stmt_da;
- thd->warning_info= &new_warnning_info;
+ thd->set_stmt_da(&new_stmt_da);
#ifndef EMBEDDED_LIBRARY
param->set_longdata(packet, (ulong) (packet_end - packet));
#else
param->set_longdata(thd->extra_data, thd->extra_length);
#endif
- if (thd->stmt_da->is_error())
+ if (thd->get_stmt_da()->is_error())
{
stmt->state= Query_arena::STMT_ERROR;
- stmt->last_errno= thd->stmt_da->sql_errno();
- strncpy(stmt->last_error, thd->stmt_da->message(), MYSQL_ERRMSG_SIZE);
+ stmt->last_errno= thd->get_stmt_da()->sql_errno();
+ strmake_buf(stmt->last_error, thd->get_stmt_da()->message());
}
- thd->stmt_da= save_stmt_da;
- thd->warning_info= save_warinig_info;
+ thd->set_stmt_da(save_stmt_da);
- general_log_print(thd, thd->command, NullS);
+ general_log_print(thd, thd->get_command(), NullS);
DBUG_VOID_RETURN;
}
@@ -3074,8 +3110,7 @@ Reprepare_observer::report_error(THD *thd)
that this thread execution stops and returns to the caller,
backtracking all the way to Prepared_statement::execute_loop().
*/
- thd->stmt_da->set_error_status(thd, ER_NEED_REPREPARE,
- ER(ER_NEED_REPREPARE), "HY000");
+ thd->get_stmt_da()->set_error_status(ER_NEED_REPREPARE);
m_invalidated= TRUE;
return TRUE;
@@ -3110,6 +3145,7 @@ Execute_sql_statement(LEX_STRING sql_text)
bool
Execute_sql_statement::execute_server_code(THD *thd)
{
+ PSI_statement_locker *parent_locker;
bool error;
if (alloc_query(thd, m_sql_text.str, m_sql_text.length))
@@ -3129,7 +3165,10 @@ Execute_sql_statement::execute_server_code(THD *thd)
thd->lex->set_trg_event_type_for_tables();
+ parent_locker= thd->m_statement_psi;
+ thd->m_statement_psi= NULL;
error= mysql_execute_command(thd);
+ thd->m_statement_psi= parent_locker;
/* report error issued during command execution */
if (error == 0 && thd->spcont == NULL)
@@ -3158,7 +3197,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
flags((uint) IS_IN_USE)
{
init_sql_alloc(&main_mem_root, thd_arg->variables.query_alloc_block_size,
- thd_arg->variables.query_prealloc_size);
+ thd_arg->variables.query_prealloc_size, MYF(MY_THREAD_SPECIFIC));
*last_error= '\0';
}
@@ -3554,7 +3593,6 @@ Prepared_statement::execute_loop(String *expanded_query,
Reprepare_observer reprepare_observer;
bool error;
int reprepare_attempt= 0;
- bool need_set_parameters= true;
/* Check if we got an error when sending long data */
if (state == Query_arena::STMT_ERROR)
@@ -3563,20 +3601,19 @@ Prepared_statement::execute_loop(String *expanded_query,
return TRUE;
}
-reexecute:
- if (need_set_parameters &&
- set_parameters(expanded_query, packet, packet_end))
+ if (set_parameters(expanded_query, packet, packet_end))
return TRUE;
- /*
- if set_parameters() has generated warnings,
- we need to repeat it when reexecuting, to recreate these
- warnings.
- */
- need_set_parameters= thd->warning_info->statement_warn_count();
-
- reprepare_observer.reset_reprepare_observer();
+#ifdef NOT_YET_FROM_MYSQL_5_6
+ if (unlikely(thd->security_ctx->password_expired &&
+ !lex->is_change_password))
+ {
+ my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
+ return true;
+ }
+#endif
+reexecute:
/*
If the free_list is not empty, we'll wrongly free some externally
allocated items when cleaning up after validation of the prepared
@@ -3590,22 +3627,24 @@ reexecute:
the observer method will be invoked to push an error into
the error stack.
*/
- if (sql_command_flags[lex->sql_command] &
- CF_REEXECUTION_FRAGILE)
+
+ if (sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE)
{
+ reprepare_observer.reset_reprepare_observer();
DBUG_ASSERT(thd->m_reprepare_observer == NULL);
- thd->m_reprepare_observer = &reprepare_observer;
+ thd->m_reprepare_observer= &reprepare_observer;
}
error= execute(expanded_query, open_cursor) || thd->is_error();
thd->m_reprepare_observer= NULL;
- if (error && !thd->is_fatal_error && !thd->killed &&
+ if ((sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE) &&
+ error && !thd->is_fatal_error && !thd->killed &&
reprepare_observer.is_invalidated() &&
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
{
- DBUG_ASSERT(thd->stmt_da->sql_errno() == ER_NEED_REPREPARE);
+ DBUG_ASSERT(thd->get_stmt_da()->sql_errno() == ER_NEED_REPREPARE);
thd->clear_error();
error= reprepare();
@@ -3707,7 +3746,7 @@ Prepared_statement::reprepare()
Sic: we can't simply silence warnings during reprepare, because if
it's failed, we need to return all the warnings to the user.
*/
- thd->warning_info->clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->clear_warning_info(thd->query_id);
}
return error;
}
@@ -3933,13 +3972,17 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (query_cache_send_result_to_client(thd, thd->query(),
thd->query_length()) <= 0)
{
+ PSI_statement_locker *parent_locker;
MYSQL_QUERY_EXEC_START(thd->query(),
thd->thread_id,
(char *) (thd->db ? thd->db : ""),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip,
1);
+ parent_locker= thd->m_statement_psi;
+ thd->m_statement_psi= NULL;
error= mysql_execute_command(thd);
+ thd->m_statement_psi= parent_locker;
MYSQL_QUERY_EXEC_DONE(error);
}
else
@@ -3967,6 +4010,21 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (! cursor)
cleanup_stmt();
+
+ /*
+ 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
+ executed.
+ */
+ if (!stmt_backup.lex->explain ||
+ !stmt_backup.lex->explain->have_query_plan())
+ {
+ delete_explain_query(stmt_backup.lex);
+ stmt_backup.lex->explain = thd->lex->explain;
+ thd->lex->explain= NULL;
+ }
+ else
+ delete_explain_query(thd->lex);
thd->set_statement(&stmt_backup);
thd->stmt_arena= old_stmt_arena;
@@ -4077,7 +4135,7 @@ Ed_result_set::Ed_result_set(List<Ed_row> *rows_arg,
*/
Ed_connection::Ed_connection(THD *thd)
- :m_warning_info(thd->query_id, false),
+ :m_diagnostics_area(thd->query_id, false, true),
m_thd(thd),
m_rsets(0),
m_current_rset(0)
@@ -4103,7 +4161,7 @@ Ed_connection::free_old_result()
}
m_current_rset= m_rsets;
m_diagnostics_area.reset_diagnostics_area();
- m_warning_info.clear_warning_info(m_thd->query_id);
+ m_diagnostics_area.clear_warning_info(m_thd->query_id);
}
@@ -4140,23 +4198,20 @@ bool Ed_connection::execute_direct(Server_runnable *server_runnable)
Protocol_local protocol_local(m_thd, this);
Prepared_statement stmt(m_thd);
Protocol *save_protocol= m_thd->protocol;
- Diagnostics_area *save_diagnostics_area= m_thd->stmt_da;
- Warning_info *save_warning_info= m_thd->warning_info;
+ Diagnostics_area *save_diagnostics_area= m_thd->get_stmt_da();
DBUG_ENTER("Ed_connection::execute_direct");
free_old_result(); /* Delete all data from previous execution, if any */
m_thd->protocol= &protocol_local;
- m_thd->stmt_da= &m_diagnostics_area;
- m_thd->warning_info= &m_warning_info;
+ m_thd->set_stmt_da(&m_diagnostics_area);
rc= stmt.execute_server_runnable(server_runnable);
m_thd->protocol->end_statement();
m_thd->protocol= save_protocol;
- m_thd->stmt_da= save_diagnostics_area;
- m_thd->warning_info= save_warning_info;
+ m_thd->set_stmt_da(save_diagnostics_area);
/*
Protocol_local makes use of m_current_rset to keep
track of the last result set, while adding result sets to the end.
@@ -4446,7 +4501,7 @@ bool Protocol_local::store(const char *str, size_t length,
bool Protocol_local::store(MYSQL_TIME *time, int decimals)
{
if (decimals != AUTO_SEC_PART_DIGITS)
- time->second_part= sec_part_truncate(time->second_part, decimals);
+ my_time_trunc(time, decimals);
return store_column(time, sizeof(MYSQL_TIME));
}
@@ -4464,7 +4519,7 @@ bool Protocol_local::store_date(MYSQL_TIME *time)
bool Protocol_local::store_time(MYSQL_TIME *time, int decimals)
{
if (decimals != AUTO_SEC_PART_DIGITS)
- time->second_part= sec_part_truncate(time->second_part, decimals);
+ my_time_trunc(time, decimals);
return store_column(time, sizeof(MYSQL_TIME));
}
@@ -4501,7 +4556,7 @@ bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
{
DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root));
- init_sql_alloc(&m_rset_root, MEM_ROOT_BLOCK_SIZE, 0);
+ init_sql_alloc(&m_rset_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (! (m_rset= new (&m_rset_root) List<Ed_row>))
return TRUE;