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.cc1122
1 files changed, 785 insertions, 337 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 9c52aea2931..f4712d37065 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -99,7 +99,6 @@ When one supplies long data for a placeholder:
#include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert
#include "sql_update.h" // mysql_prepare_update
#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
-#include "sql_acl.h" // *_ACL
#include "sql_derived.h" // mysql_derived_prepare,
// mysql_handle_derived
#include "sql_cte.h"
@@ -125,6 +124,7 @@ static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8;
#include "log_event.h" // class Log_event
#include "sql_handler.h"
#include "transaction.h" // trans_rollback_implicit
+#include "mysql/psi/mysql_ps.h" // MYSQL_EXECUTE_PS
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
#include "wsrep_trans_observer.h"
@@ -166,6 +166,7 @@ public:
};
THD *thd;
+ PSI_prepared_stmt* m_prepared_stmt;
Select_fetch_protocol_binary result;
Item_param **param_array;
Server_side_cursor *cursor;
@@ -248,62 +249,6 @@ private:
class Ed_connection;
-/**
- Protocol_local: a helper class to intercept the result
- of the data written to the network.
-*/
-
-class Protocol_local :public Protocol
-{
-public:
- Protocol_local(THD *thd, Ed_connection *ed_connection);
- ~Protocol_local() { free_root(&m_rset_root, MYF(0)); }
-protected:
- virtual void prepare_for_resend();
- virtual bool write();
- virtual bool store_null();
- virtual bool store_tiny(longlong from);
- virtual bool store_short(longlong from);
- virtual bool store_long(longlong from);
- virtual bool store_longlong(longlong from, bool unsigned_flag);
- virtual bool store_decimal(const my_decimal *);
- virtual bool store(const char *from, size_t length, CHARSET_INFO *cs);
- virtual bool store(const char *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
- virtual bool store(MYSQL_TIME *time, int decimals);
- virtual bool store_date(MYSQL_TIME *time);
- virtual bool store_time(MYSQL_TIME *time, int decimals);
- virtual bool store(float value, uint32 decimals, String *buffer);
- virtual bool store(double value, uint32 decimals, String *buffer);
- virtual bool store(Field *field);
-
- virtual bool send_result_set_metadata(List<Item> *list, uint flags);
- virtual bool send_out_parameters(List<Item_param> *sp_params);
-#ifdef EMBEDDED_LIBRARY
- void remove_last_row();
-#endif
- virtual enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
-
- virtual bool send_ok(uint server_status, uint statement_warn_count,
- ulonglong affected_rows, ulonglong last_insert_id,
- const char *message, bool skip_flush);
-
- virtual bool send_eof(uint server_status, uint statement_warn_count);
- virtual bool send_error(uint sql_errno, const char *err_msg, const char* sqlstate);
-private:
- bool store_string(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs);
-
- bool store_column(const void *data, size_t length);
- void opt_add_row_to_rset();
-private:
- Ed_connection *m_connection;
- MEM_ROOT m_rset_root;
- List<Ed_row> *m_rset;
- size_t m_column_count;
- Ed_column *m_current_row;
- Ed_column *m_current_column;
-};
/******************************************************************************
Implementation
@@ -745,6 +690,8 @@ void Item_param::setup_conversion(THD *thd, uchar param_type)
*/
if (!h)
h= &type_handler_string;
+ else if (unsigned_flag)
+ h= h->type_handler_unsigned();
set_handler(h);
h->Item_param_setup_conversion(thd, this);
}
@@ -947,6 +894,9 @@ static bool insert_bulk_params(Prepared_statement *stmt,
case STMT_INDICATOR_IGNORE:
param->set_ignore();
break;
+ default:
+ DBUG_ASSERT(0);
+ DBUG_RETURN(1);
}
}
else
@@ -1369,9 +1319,8 @@ static bool mysql_test_insert(Prepared_statement *stmt,
table_list->table->insert_values=(uchar *)1;
}
- if (mysql_prepare_insert(thd, table_list, table_list->table,
- fields, values, update_fields, update_values,
- duplic, &unused_conds, FALSE))
+ if (mysql_prepare_insert(thd, table_list, fields, values, update_fields,
+ update_values, duplic, &unused_conds, FALSE))
goto error;
value_count= values->elements;
@@ -1432,7 +1381,7 @@ static int mysql_test_update(Prepared_statement *stmt,
TABLE_LIST *update_source_table;
SELECT_LEX *select= stmt->lex->first_select_lex();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- uint want_privilege;
+ privilege_t want_privilege(NO_ACL);
#endif
DBUG_ENTER("mysql_test_update");
@@ -1555,8 +1504,6 @@ static bool mysql_test_delete(Prepared_statement *stmt,
}
DBUG_RETURN(mysql_prepare_delete(thd, table_list,
- lex->first_select_lex()->with_wild,
- lex->first_select_lex()->item_list,
&lex->first_select_lex()->where,
&delete_while_scanning));
error:
@@ -1591,7 +1538,7 @@ static int mysql_test_select(Prepared_statement *stmt,
lex->first_select_lex()->context.resolve_in_select_list= TRUE;
- ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
+ privilege_t privilege(lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL);
if (tables)
{
if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE))
@@ -2013,20 +1960,21 @@ static int mysql_test_show_grants(Prepared_statement *stmt)
TRUE error, error message is set in THD
*/
-static int mysql_test_show_slave_status(Prepared_statement *stmt)
+static int mysql_test_show_slave_status(Prepared_statement *stmt,
+ bool show_all_slaves_stat)
{
DBUG_ENTER("mysql_test_show_slave_status");
THD *thd= stmt->thd;
List<Item> fields;
- show_master_info_get_fields(thd, &fields, thd->lex->verbose, 0);
-
+ show_master_info_get_fields(thd, &fields, show_all_slaves_stat, 0);
+
DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
}
/**
- Validate and prepare for execution SHOW MASTER STATUS statement.
+ Validate and prepare for execution SHOW BINLOG STATUS statement.
@param stmt prepared statement
@@ -2036,9 +1984,9 @@ static int mysql_test_show_slave_status(Prepared_statement *stmt)
TRUE error, error message is set in THD
*/
-static int mysql_test_show_master_status(Prepared_statement *stmt)
+static int mysql_test_show_binlog_status(Prepared_statement *stmt)
{
- DBUG_ENTER("mysql_test_show_master_status");
+ DBUG_ENTER("mysql_test_show_binlog_status");
THD *thd= stmt->thd;
List<Item> fields;
@@ -2234,7 +2182,7 @@ static int mysql_insert_select_prepare_tester(THD *thd)
thd->lex->first_select_lex()->context.first_name_resolution_table=
second_table;
- return mysql_insert_select_prepare(thd);
+ return mysql_insert_select_prepare(thd, NULL);
}
@@ -2474,14 +2422,22 @@ static bool check_prepared_statement(Prepared_statement *stmt)
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
#ifndef EMBEDDED_LIBRARY
case SQLCOM_SHOW_SLAVE_STAT:
- if ((res= mysql_test_show_slave_status(stmt)) == 2)
{
- /* Statement and field info has already been sent */
- DBUG_RETURN(FALSE);
+ DBUG_ASSERT(thd->lex->m_sql_cmd);
+ Sql_cmd_show_slave_status *cmd;
+ cmd= dynamic_cast<Sql_cmd_show_slave_status*>(thd->lex->m_sql_cmd);
+ DBUG_ASSERT(cmd);
+ if ((res= mysql_test_show_slave_status(stmt,
+ cmd->is_show_all_slaves_stat()))
+ == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
}
- break;
- case SQLCOM_SHOW_MASTER_STAT:
- if ((res= mysql_test_show_master_status(stmt)) == 2)
+ case SQLCOM_SHOW_BINLOG_STAT:
+ if ((res= mysql_test_show_binlog_status(stmt)) == 2)
{
/* Statement and field info has already been sent */
DBUG_RETURN(FALSE);
@@ -2746,8 +2702,22 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
thd->protocol= &thd->protocol_binary;
+ /* Create PS table entry, set query text after rewrite. */
+ stmt->m_prepared_stmt= MYSQL_CREATE_PS(stmt, stmt->id,
+ thd->m_statement_psi,
+ stmt->name.str, stmt->name.length);
+
if (stmt->prepare(packet, packet_length))
{
+ /*
+ Prepare failed and stmt will be freed.
+ Now we have to save the query_string in the so the
+ audit plugin later gets the meaningful notification.
+ */
+ if (alloc_query(thd, stmt->query_string.str(), stmt->query_string.length()))
+ {
+ thd->set_query(0, 0);
+ }
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
thd->clear_last_stmt();
@@ -2882,6 +2852,7 @@ bool Lex_prepared_stmt::get_dynamic_sql_string(THD *thd,
void mysql_sql_stmt_prepare(THD *thd)
{
LEX *lex= thd->lex;
+ CSET_STRING orig_query= thd->query_string;
const LEX_CSTRING *name= &lex->prepared_stmt.name();
Prepared_statement *stmt;
LEX_CSTRING query;
@@ -2947,14 +2918,28 @@ void mysql_sql_stmt_prepare(THD *thd)
*/
Item_change_list_savepoint change_list_savepoint(thd);
- if (stmt->prepare(query.str, (uint) query.length))
+ /* Create PS table entry, set query text after rewrite. */
+ stmt->m_prepared_stmt= MYSQL_CREATE_PS(stmt, stmt->id,
+ thd->m_statement_psi,
+ stmt->name.str, stmt->name.length);
+
+ bool res= stmt->prepare(query.str, (uint) query.length);
+ /*
+ stmt->prepare() sets thd->query_string with the prepared
+ query, so the audit plugin gets adequate notification with the
+ mysqld_stmt_* set of functions.
+ But here we should restore the original query so it's mentioned in
+ logs properly.
+ */
+ thd->set_query(orig_query);
+ if (res)
{
/* Statement map deletes the statement on erase */
thd->stmt_map.erase(stmt);
}
else
{
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
my_ok(thd, 0L, 0L, "Statement prepared");
}
change_list_savepoint.rollback(thd);
@@ -2966,6 +2951,7 @@ void mysql_sql_stmt_prepare(THD *thd)
void mysql_sql_stmt_execute_immediate(THD *thd)
{
LEX *lex= thd->lex;
+ CSET_STRING orig_query= thd->query_string;
Prepared_statement *stmt;
LEX_CSTRING query;
DBUG_ENTER("mysql_sql_stmt_execute_immediate");
@@ -3014,6 +3000,14 @@ void mysql_sql_stmt_execute_immediate(THD *thd)
thd->free_items();
thd->free_list= free_list_backup;
+ /*
+ stmt->execute_immediately() sets thd->query_string with the executed
+ query, so the audit plugin gets adequate notification with the
+ mysqld_stmt_* set of functions.
+ But here we should restore the original query so it's mentioned in
+ logs properly.
+ */
+ thd->set_query_inner(orig_query);
stmt->lex->restore_set_statement_var();
delete stmt;
DBUG_VOID_RETURN;
@@ -3429,8 +3423,17 @@ static void mysql_stmt_execute_common(THD *thd,
if (!(stmt= find_prepared_statement(thd, stmt_id)))
{
char llbuf[22];
- my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
- llstr(stmt_id, llbuf), "mysqld_stmt_execute");
+ size_t length;
+ /*
+ Did not find the statement with the provided stmt_id.
+ Set thd->query_string with the stmt_id so the
+ audit plugin gets the meaningful notification.
+ */
+ length= (size_t) (longlong10_to_str(stmt_id, llbuf, 10) - llbuf);
+ if (alloc_query(thd, llbuf, length + 1))
+ thd->set_query(0, 0);
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), (int) length, llbuf,
+ "mysqld_stmt_execute");
DBUG_VOID_RETURN;
}
@@ -3465,6 +3468,8 @@ static void mysql_stmt_execute_common(THD *thd,
open_cursor= MY_TEST(cursor_flags & (ulong) CURSOR_TYPE_READ_ONLY);
thd->protocol= &thd->protocol_binary;
+ MYSQL_EXECUTE_PS(thd->m_statement_psi, stmt->m_prepared_stmt);
+
if (!bulk_op)
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
else
@@ -3574,6 +3579,8 @@ void mysql_sql_stmt_execute(THD *thd)
CALL p1('x');
*/
Item_change_list_savepoint change_list_savepoint(thd);
+ MYSQL_EXECUTE_PS(thd->m_statement_psi, stmt->m_prepared_stmt);
+
(void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL);
change_list_savepoint.rollback(thd);
thd->free_items(); // Free items created by execute_loop()
@@ -3757,7 +3764,7 @@ void mysql_sql_stmt_close(THD *thd)
else
{
stmt->deallocate();
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
my_ok(thd);
}
}
@@ -3976,6 +3983,7 @@ Execute_sql_statement::execute_server_code(THD *thd)
end:
thd->lex->restore_set_statement_var();
+ delete_explain_query(thd->lex);
lex_end(thd->lex);
return error;
@@ -3990,6 +3998,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
STMT_INITIALIZED,
((++thd_arg->statement_id_counter) & STMT_ID_MASK)),
thd(thd_arg),
+ m_prepared_stmt(NULL),
result(thd_arg),
param_array(0),
cursor(0),
@@ -4003,10 +4012,9 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
read_types(0),
m_sql_mode(thd->variables.sql_mode)
{
- init_sql_alloc(&main_mem_root, "Prepared_statement",
- thd_arg->variables.query_alloc_block_size,
- thd_arg->variables.query_prealloc_size,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_prepared_statement_main_mem_root,
+ &main_mem_root, thd_arg->variables.query_alloc_block_size,
+ thd_arg->variables.query_prealloc_size, MYF(MY_THREAD_SPECIFIC));
*last_error= '\0';
}
@@ -4072,6 +4080,9 @@ Prepared_statement::~Prepared_statement()
DBUG_ENTER("Prepared_statement::~Prepared_statement");
DBUG_PRINT("enter",("stmt: %p cursor: %p",
this, cursor));
+
+ MYSQL_DESTROY_PS(m_prepared_stmt);
+
delete cursor;
/*
We have to call free on the items even if cleanup is called as some items,
@@ -4204,6 +4215,19 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
DBUG_RETURN(TRUE);
}
+ /*
+ We'd like to have thd->query to be set to the actual query
+ after the function ends.
+ This value will be sent to audit plugins later.
+ As the statement is created, the query will be stored
+ in statement's arena. Normally the statement lives longer than
+ the end of this query, so we can just set thd->query_string to
+ be the stmt->query_string.
+ Though errors can result in statement to be freed. These cases
+ should be handled appropriately.
+ */
+ stmt_backup.query_string= thd->query_string;
+
old_stmt_arena= thd->stmt_arena;
thd->stmt_arena= this;
@@ -4297,7 +4321,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
lex->unit.cleanup();
/* No need to commit statement transaction, it's not started. */
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ DBUG_ASSERT(thd->transaction->stmt.is_empty());
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
@@ -4332,6 +4356,8 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
state= Query_arena::STMT_PREPARED;
flags&= ~ (uint) IS_IN_USE;
+ MYSQL_SET_PS_TEXT(m_prepared_stmt, query(), query_length());
+
/*
Log COM_EXECUTE to the general log. Note, that in case of SQL
prepared statements this causes two records to be output:
@@ -4564,6 +4590,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
uchar *packet_end_arg)
{
Reprepare_observer reprepare_observer;
+ unsigned char *readbuff= NULL;
bool error= 0;
packet= packet_arg;
packet_end= packet_end_arg;
@@ -4577,24 +4604,37 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
if (state == Query_arena::STMT_ERROR)
{
my_message(last_errno, last_error, MYF(0));
- thd->set_bulk_execution(0);
- return TRUE;
+ goto err;
}
/* Check for non zero parameter count*/
if (param_count == 0)
{
DBUG_PRINT("error", ("Statement with no parameters for bulk execution."));
my_error(ER_UNSUPPORTED_PS, MYF(0));
- thd->set_bulk_execution(0);
- return TRUE;
+ goto err;
}
if (!(sql_command_flags[lex->sql_command] & CF_PS_ARRAY_BINDING_SAFE))
{
DBUG_PRINT("error", ("Command is not supported in bulk execution."));
my_error(ER_UNSUPPORTED_PS, MYF(0));
- thd->set_bulk_execution(0);
- return TRUE;
+ goto err;
+ }
+ /*
+ Here second buffer for not optimized commands,
+ optimized commands do it inside thier internal loop.
+ */
+ if (!(sql_command_flags[lex->sql_command] & CF_PS_ARRAY_BINDING_OPTIMIZED) &&
+ this->lex->has_returning())
+ {
+ // Above check can be true for SELECT in future
+ DBUG_ASSERT(lex->sql_command != SQLCOM_SELECT);
+ readbuff= thd->net.buff; // old buffer
+ if (net_allocate_new_packet(&thd->net, thd, MYF(MY_THREAD_SPECIFIC)))
+ {
+ readbuff= NULL; // failure, net_allocate_new_packet keeps old buffer
+ goto err;
+ }
}
#ifndef EMBEDDED_LIBRARY
@@ -4606,9 +4646,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
{
my_error(ER_WRONG_ARGUMENTS, MYF(0),
"mysqld_stmt_bulk_execute");
- reset_stmt_params(this);
- thd->set_bulk_execution(0);
- return true;
+ goto err;
}
read_types= FALSE;
@@ -4625,8 +4663,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
{
if (set_bulk_parameters(TRUE))
{
- thd->set_bulk_execution(0);
- return true;
+ goto err;
}
}
@@ -4690,8 +4727,16 @@ reexecute:
}
reset_stmt_params(this);
thd->set_bulk_execution(0);
-
+ if (readbuff)
+ my_free(readbuff);
return error;
+
+err:
+ reset_stmt_params(this);
+ thd->set_bulk_execution(0);
+ if (readbuff)
+ my_free(readbuff);
+ return true;
}
@@ -4763,18 +4808,18 @@ Prepared_statement::reprepare()
TRUE, &cur_db_changed)))
return TRUE;
- sql_mode_t save_sql_mode= thd->variables.sql_mode;
- thd->variables.sql_mode= m_sql_mode;
+ Sql_mode_instant_set sms(thd, m_sql_mode);
+
error= ((name.str && copy.set_name(&name)) ||
copy.prepare(query(), query_length()) ||
validate_metadata(&copy));
- thd->variables.sql_mode= save_sql_mode;
if (cur_db_changed)
mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE);
if (likely(!error))
{
+ MYSQL_REPREPARE_PS(m_prepared_stmt);
swap_prepared_statement(&copy);
swap_parameter_array(param_array, copy.param_array, param_count);
#ifdef DBUG_ASSERT_EXISTS
@@ -4789,6 +4834,15 @@ Prepared_statement::reprepare()
*/
thd->get_stmt_da()->clear_warning_info(thd->query_id);
}
+ else
+ {
+ /*
+ Prepare failed and the 'copy' will be freed.
+ Now we have to restore the query_string in the so the
+ audit plugin later gets the meaningful notification.
+ */
+ thd->set_query(query(), query_length());
+ }
return error;
}
@@ -5012,17 +5066,13 @@ 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,
thd->get_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
@@ -5135,7 +5185,11 @@ bool Prepared_statement::execute_immediate(const char *query, uint query_len)
set_sql_prepare();
name= execute_immediate_stmt_name; // for DBUG_PRINT etc
- if (unlikely(prepare(query, query_len)))
+
+ m_prepared_stmt= MYSQL_CREATE_PS(this, id, thd->m_statement_psi,
+ name.str, name.length);
+
+ if (prepare(query, query_len))
DBUG_RETURN(true);
if (param_count != thd->lex->prepared_stmt.param_count())
@@ -5145,6 +5199,7 @@ bool Prepared_statement::execute_immediate(const char *query, uint query_len)
DBUG_RETURN(true);
}
+ MYSQL_EXECUTE_PS(thd->m_statement_psi, m_prepared_stmt);
(void) execute_loop(&expanded_query, FALSE, NULL, NULL);
deallocate_immediate();
DBUG_RETURN(false);
@@ -5265,12 +5320,12 @@ Ed_connection::free_old_result()
*/
bool
-Ed_connection::execute_direct(LEX_STRING sql_text)
+Ed_connection::execute_direct(Protocol *p, LEX_STRING sql_text)
{
Execute_sql_statement execute_sql_statement(sql_text);
DBUG_PRINT("ed_query", ("%s", sql_text.str));
- return execute_direct(&execute_sql_statement);
+ return execute_direct(p, &execute_sql_statement);
}
@@ -5287,10 +5342,9 @@ Ed_connection::execute_direct(LEX_STRING sql_text)
@param server_runnable A code fragment to execute.
*/
-bool Ed_connection::execute_direct(Server_runnable *server_runnable)
+bool Ed_connection::execute_direct(Protocol *p, Server_runnable *server_runnable)
{
bool rc= FALSE;
- 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->get_stmt_da();
@@ -5299,7 +5353,7 @@ bool Ed_connection::execute_direct(Server_runnable *server_runnable)
free_old_result(); /* Delete all data from previous execution, if any */
- m_thd->protocol= &protocol_local;
+ m_thd->protocol= p;
m_thd->set_stmt_da(&m_diagnostics_area);
rc= stmt.execute_server_runnable(server_runnable);
@@ -5386,354 +5440,748 @@ Ed_connection::store_result_set()
return ed_result_set;
}
-/*************************************************************************
-* Protocol_local
-**************************************************************************/
-
-Protocol_local::Protocol_local(THD *thd, Ed_connection *ed_connection)
- :Protocol(thd),
- m_connection(ed_connection),
- m_rset(NULL),
- m_column_count(0),
- m_current_row(NULL),
- m_current_column(NULL)
-{
- clear_alloc_root(&m_rset_root);
-}
+/*
+ MENT-56
+ Protocol_local and service_sql for plugins to enable 'local' SQL query execution.
+*/
-/**
- Called between two result set rows.
+#ifndef EMBEDDED_LIBRARY
+// This part is mostly copied from libmysqld/lib_sql.cc
+// TODO: get rid of code duplications
- Prepare structures to fill result set rows.
- Unfortunately, we can't return an error here. If memory allocation
- fails, we'll have to return an error later. And so is done
- in methods such as @sa store_column().
-*/
+#include <mysql.h>
+#include "../libmysqld/embedded_priv.h"
-void Protocol_local::prepare_for_resend()
+class Protocol_local : public Protocol_text
{
- DBUG_ASSERT(alloc_root_inited(&m_rset_root));
+public:
+ struct st_mysql_data *cur_data;
+ struct st_mysql_data *first_data;
+ struct st_mysql_data **data_tail;
+ void clear_data_list();
+ struct st_mysql_data *alloc_new_dataset();
+ char **next_field;
+ MYSQL_FIELD *next_mysql_field;
+ MEM_ROOT *alloc;
+
+ Protocol_local(THD *thd_arg, ulong prealloc= 0) :
+ Protocol_text(thd_arg, prealloc),
+ cur_data(0), first_data(0), data_tail(&first_data), alloc(0)
+ {}
+
+protected:
+ bool net_store_data(const uchar *from, size_t length);
+ bool net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
+ bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *,
+ bool, bool);
+ bool net_send_error_packet(THD *, uint, const char *, const char *);
+ bool begin_dataset();
+ bool begin_dataset(THD *thd, uint numfields);
+
+ bool write();
+ bool flush();
+
+ bool store_field_metadata(const THD *thd, const Send_field &field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos);
+ bool send_result_set_metadata(List<Item> *list, uint flags);
+ void remove_last_row();
+ bool store_null();
+ void prepare_for_resend();
+ bool send_list_fields(List<Field> *list, const TABLE_LIST *table_list);
+
+ enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
+};
- opt_add_row_to_rset();
- /* Start a new row. */
- m_current_row= (Ed_column *) alloc_root(&m_rset_root,
- sizeof(Ed_column) * m_column_count);
- m_current_column= m_current_row;
+static
+bool
+write_eof_packet_local(THD *thd,
+ Protocol_local *p, uint server_status, uint statement_warn_count)
+{
+// if (!thd->mysql) // bootstrap file handling
+// return FALSE;
+ /*
+ The following test should never be true, but it's better to do it
+ because if 'is_fatal_error' is set the server is not going to execute
+ other queries (see the if test in dispatch_command / COM_QUERY)
+ */
+ if (thd->is_fatal_error)
+ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
+ p->cur_data->embedded_info->server_status= server_status;
+ /*
+ Don't send warn count during SP execution, as the warn_list
+ is cleared between substatements, and mysqltest gets confused
+ */
+ p->cur_data->embedded_info->warning_count=
+ (thd->spcont ? 0 : MY_MIN(statement_warn_count, 65535));
+ return FALSE;
}
-/**
- In "real" protocols this is called to finish a result set row.
- Unused in the local implementation.
-*/
-
-bool Protocol_local::write()
+MYSQL_DATA *Protocol_local::alloc_new_dataset()
{
- return FALSE;
+ MYSQL_DATA *data;
+ struct embedded_query_result *emb_data;
+ if (!my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME | MY_ZEROFILL),
+ &data, sizeof(*data),
+ &emb_data, sizeof(*emb_data),
+ NULL))
+ return NULL;
+
+ emb_data->prev_ptr= &data->data;
+ cur_data= data;
+ *data_tail= data;
+ data_tail= &emb_data->next;
+ data->embedded_info= emb_data;
+ return data;
}
-/**
- A helper function to add the current row to the current result
- set. Called in @sa prepare_for_resend(), when a new row is started,
- and in send_eof(), when the result set is finished.
-*/
-void Protocol_local::opt_add_row_to_rset()
+static char *dup_str_aux(MEM_ROOT *root, const char *from, uint length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
- if (m_current_row)
+ uint32 dummy32;
+ uint dummy_err;
+ char *result;
+
+ /* 'tocs' is set 0 when client issues SET character_set_results=NULL */
+ if (tocs && String::needs_conversion(0, fromcs, tocs, &dummy32))
+ {
+ uint new_len= (tocs->mbmaxlen * length) / fromcs->mbminlen + 1;
+ result= (char *)alloc_root(root, new_len);
+ length= copy_and_convert(result, new_len,
+ tocs, from, length, fromcs, &dummy_err);
+ }
+ else
{
- /* Add the old row to the result set */
- Ed_row *ed_row= new (&m_rset_root) Ed_row(m_current_row, m_column_count);
- if (ed_row)
- m_rset->push_back(ed_row, &m_rset_root);
+ result= (char *)alloc_root(root, length + 1);
+ memcpy(result, from, length);
}
+
+ result[length]= 0;
+ return result;
}
-/**
- Add a NULL column to the current row.
-*/
+static char *dup_str_aux(MEM_ROOT *root, const LEX_CSTRING &from,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+{
+ return dup_str_aux(root, from.str, (uint) from.length, fromcs, tocs);
+}
-bool Protocol_local::store_null()
+
+bool Protocol_local::net_store_data(const uchar *from, size_t length)
{
- if (m_current_column == NULL)
- return TRUE; /* prepare_for_resend() failed to allocate memory. */
+ char *field_buf;
+// if (!thd->mysql) // bootstrap file handling
+// return FALSE;
- bzero(m_current_column, sizeof(*m_current_column));
- ++m_current_column;
+ if (!(field_buf= (char*) alloc_root(alloc, length + sizeof(uint) + 1)))
+ return TRUE;
+ *(uint *)field_buf= (uint) length;
+ *next_field= field_buf + sizeof(uint);
+ memcpy((uchar*) *next_field, from, length);
+ (*next_field)[length]= 0;
+ if (next_mysql_field->max_length < length)
+ next_mysql_field->max_length= (unsigned long) length;
+ ++next_field;
+ ++next_mysql_field;
return FALSE;
}
+bool Protocol_local::net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
+{
+ uint conv_length= (uint) (to_cs->mbmaxlen * length / from_cs->mbminlen);
+ uint dummy_error;
+ char *field_buf;
+// if (!thd->mysql) // bootstrap file handling
+// return false;
+
+ if (!(field_buf= (char*) alloc_root(alloc, conv_length + sizeof(uint) + 1)))
+ return true;
+ *next_field= field_buf + sizeof(uint);
+ length= copy_and_convert(*next_field, conv_length, to_cs,
+ (const char*) from, length, from_cs, &dummy_error);
+ *(uint *) field_buf= (uint) length;
+ (*next_field)[length]= 0;
+ if (next_mysql_field->max_length < length)
+ next_mysql_field->max_length= (unsigned long) length;
+ ++next_field;
+ ++next_mysql_field;
+ return false;
+}
+
+
/**
- A helper method to add any column to the current row
- in its binary form.
+ Embedded library implementation of OK response.
+
+ This function is used by the server to write 'OK' packet to
+ the "network" when the server is compiled as an embedded library.
+ Since there is no network in the embedded configuration,
+ a different implementation is necessary.
+ Instead of marshalling response parameters to a network representation
+ and then writing it to the socket, here we simply copy the data to the
+ corresponding client-side connection structures.
- Allocates memory for the data in the result set memory root.
+ @sa Server implementation of net_send_ok in protocol.cc for
+ description of the arguments.
+
+ @return
+ @retval TRUE An error occurred
+ @retval FALSE Success
*/
-bool Protocol_local::store_column(const void *data, size_t length)
+bool
+Protocol_local::net_send_ok(THD *thd,
+ uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong id, const char *message, bool, bool)
{
- if (m_current_column == NULL)
- return TRUE; /* prepare_for_resend() failed to allocate memory. */
- /*
- alloc_root() automatically aligns memory, so we don't need to
- do any extra alignment if we're pointing to, say, an integer.
- */
- m_current_column->str= (char*) memdup_root(&m_rset_root,
- data,
- length + 1 /* Safety */);
- if (! m_current_column->str)
- return TRUE;
- m_current_column->str[length]= '\0'; /* Safety */
- m_current_column->length= length;
- ++m_current_column;
- return FALSE;
+ DBUG_ENTER("emb_net_send_ok");
+ MYSQL_DATA *data;
+// MYSQL *mysql= thd->mysql;
+
+// if (!mysql) // bootstrap file handling
+// DBUG_RETURN(FALSE);
+ if (!(data= alloc_new_dataset()))
+ DBUG_RETURN(TRUE);
+ data->embedded_info->affected_rows= affected_rows;
+ data->embedded_info->insert_id= id;
+ if (message)
+ strmake_buf(data->embedded_info->info, message);
+
+ bool error= write_eof_packet_local(thd, this,
+ server_status, statement_warn_count);
+ cur_data= 0;
+ DBUG_RETURN(error);
}
/**
- Store a string value in a result set column, optionally
- having converted it to character_set_results.
+ Embedded library implementation of EOF response.
+
+ @sa net_send_ok
+
+ @return
+ @retval TRUE An error occurred
+ @retval FALSE Success
*/
bool
-Protocol_local::store_string(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+Protocol_local::net_send_eof(THD *thd, uint server_status,
+ uint statement_warn_count)
{
- /* Store with conversion */
- uint error_unused;
-
- if (dst_cs && !my_charset_same(src_cs, dst_cs) &&
- src_cs != &my_charset_bin &&
- dst_cs != &my_charset_bin)
- {
- if (unlikely(convert->copy(str, length, src_cs, dst_cs, &error_unused)))
- return TRUE;
- str= convert->ptr();
- length= convert->length();
- }
- return store_column(str, length);
+ bool error= write_eof_packet_local(thd, this, server_status,
+ statement_warn_count);
+ cur_data= 0;
+ return error;
}
-/** Store a tiny int as is (1 byte) in a result set column. */
+bool Protocol_local::net_send_error_packet(THD *thd, uint sql_errno,
+ const char *err, const char *sqlstate)
+{
+ uint error;
+ char converted_err[MYSQL_ERRMSG_SIZE];
+ MYSQL_DATA *data= cur_data;
+ struct embedded_query_result *ei;
+
+// if (!thd->mysql) // bootstrap file handling
+// {
+// fprintf(stderr, "ERROR: %d %s\n", sql_errno, err);
+// return TRUE;
+// }
+ if (!data)
+ data= alloc_new_dataset();
+
+ ei= data->embedded_info;
+ ei->last_errno= sql_errno;
+ convert_error_message(converted_err, sizeof(converted_err),
+ thd->variables.character_set_results,
+ err, strlen(err),
+ system_charset_info, &error);
+ /* Converted error message is always null-terminated. */
+ strmake_buf(ei->info, converted_err);
+ strmov(ei->sqlstate, sqlstate);
+ ei->server_status= thd->server_status;
+ cur_data= 0;
+ return FALSE;
+}
+
-bool Protocol_local::store_tiny(longlong value)
+bool Protocol_local::begin_dataset()
{
- char v= (char) value;
- return store_column(&v, 1);
+ MYSQL_DATA *data= alloc_new_dataset();
+ if (!data)
+ return 1;
+ alloc= &data->alloc;
+ /* Assume rowlength < 8192 */
+ init_alloc_root(PSI_INSTRUMENT_ME, alloc, 8192, 0, MYF(0));
+ alloc->min_malloc= sizeof(MYSQL_ROWS);
+ return 0;
}
-/** Store a short as is (2 bytes, host order) in a result set column. */
-
-bool Protocol_local::store_short(longlong value)
+bool Protocol_local::begin_dataset(THD *thd, uint numfields)
{
- int16 v= (int16) value;
- return store_column(&v, 2);
+ if (begin_dataset())
+ return true;
+ MYSQL_DATA *data= cur_data;
+ data->fields= field_count= numfields;
+ if (!(data->embedded_info->fields_list=
+ (MYSQL_FIELD*)alloc_root(&data->alloc, sizeof(MYSQL_FIELD)*field_count)))
+ return true;
+ return false;
}
-/** Store a "long" as is (4 bytes, host order) in a result set column. */
-
-bool Protocol_local::store_long(longlong value)
+bool Protocol_local::write()
{
- int32 v= (int32) value;
- return store_column(&v, 4);
-}
+// if (!thd->mysql) // bootstrap file handling
+// return false;
+ *next_field= 0;
+ return false;
+}
-/** Store a "longlong" as is (8 bytes, host order) in a result set column. */
-bool Protocol_local::store_longlong(longlong value, bool unsigned_flag)
+bool Protocol_local::flush()
{
- int64 v= (int64) value;
- return store_column(&v, 8);
+ return 0;
}
-/** Store a decimal in string format in a result set column */
+bool Protocol_local::store_field_metadata(const THD * thd,
+ const Send_field &server_field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos)
+{
+ CHARSET_INFO *cs= system_charset_info;
+ CHARSET_INFO *thd_cs= thd->variables.character_set_results;
+ MYSQL_DATA *data= cur_data;
+ MEM_ROOT *field_alloc= &data->alloc;
+ MYSQL_FIELD *client_field= &cur_data->embedded_info->fields_list[pos];
+ DBUG_ASSERT(server_field.is_sane());
+
+ client_field->db= dup_str_aux(field_alloc, server_field.db_name,
+ cs, thd_cs);
+ client_field->table= dup_str_aux(field_alloc, server_field.table_name,
+ cs, thd_cs);
+ client_field->name= dup_str_aux(field_alloc, server_field.col_name,
+ cs, thd_cs);
+ client_field->org_table= dup_str_aux(field_alloc, server_field.org_table_name,
+ cs, thd_cs);
+ client_field->org_name= dup_str_aux(field_alloc, server_field.org_col_name,
+ cs, thd_cs);
+ if (charset_for_protocol == &my_charset_bin || thd_cs == NULL)
+ {
+ /* No conversion */
+ client_field->charsetnr= charset_for_protocol->number;
+ client_field->length= server_field.length;
+ }
+ else
+ {
+ /* With conversion */
+ client_field->charsetnr= thd_cs->number;
+ client_field->length= server_field.max_octet_length(charset_for_protocol,
+ thd_cs);
+ }
+ client_field->type= server_field.type_handler()->type_code_for_protocol();
+ client_field->flags= (uint16) server_field.flags;
+ client_field->decimals= server_field.decimals;
-bool Protocol_local::store_decimal(const my_decimal *value)
-{
- DBUG_ASSERT(0); // This method is not used yet
- StringBuffer<DECIMAL_MAX_STR_LENGTH> str;
- return value->to_string(&str) ? store_column(str.ptr(), str.length()) : true;
-}
+ client_field->db_length= (unsigned int) strlen(client_field->db);
+ client_field->table_length= (unsigned int) strlen(client_field->table);
+ client_field->name_length= (unsigned int) strlen(client_field->name);
+ client_field->org_name_length= (unsigned int) strlen(client_field->org_name);
+ client_field->org_table_length= (unsigned int) strlen(client_field->org_table);
+
+ client_field->catalog= dup_str_aux(field_alloc, "def", 3, cs, thd_cs);
+ client_field->catalog_length= 3;
+
+ if (IS_NUM(client_field->type))
+ client_field->flags|= NUM_FLAG;
+ client_field->max_length= 0;
+ client_field->def= 0;
+ return false;
+}
-/** Convert to cs_results and store a string. */
-bool Protocol_local::store(const char *str, size_t length,
- CHARSET_INFO *src_cs)
+void Protocol_local::remove_last_row()
{
- CHARSET_INFO *dst_cs;
+ MYSQL_DATA *data= cur_data;
+ MYSQL_ROWS **last_row_hook= &data->data;
+ my_ulonglong count= data->rows;
+ DBUG_ENTER("Protocol_text::remove_last_row");
+ while (--count)
+ last_row_hook= &(*last_row_hook)->next;
- dst_cs= m_connection->m_thd->variables.character_set_results;
- return store_string(str, length, src_cs, dst_cs);
-}
+ *last_row_hook= 0;
+ data->embedded_info->prev_ptr= last_row_hook;
+ data->rows--;
+ DBUG_VOID_RETURN;
+}
-/** Store a string. */
-bool Protocol_local::store(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags)
{
- return store_string(str, length, src_cs, dst_cs);
-}
+ List_iterator_fast<Item> it(*list);
+ Item *item;
+// Protocol_local prot(thd);
+ DBUG_ENTER("send_result_set_metadata");
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_RETURN(0);
-/* Store MYSQL_TIME (in binary format) */
+ if (begin_dataset(thd, list->elements))
+ goto err;
-bool Protocol_local::store(MYSQL_TIME *time, int decimals)
-{
- if (decimals != AUTO_SEC_PART_DIGITS)
- my_datetime_trunc(time, decimals);
- return store_column(time, sizeof(MYSQL_TIME));
-}
+ for (uint pos= 0 ; (item= it++); pos++)
+ {
+ if (/*prot.*/store_item_metadata(thd, item, pos))
+ goto err;
+ }
+ if (flags & SEND_EOF)
+ write_eof_packet_local(thd, this, thd->server_status,
+ thd->get_stmt_da()->current_statement_warn_count());
-/** Store MYSQL_TIME (in binary format) */
+ DBUG_RETURN(prepare_for_send(list->elements));
+ err:
+ my_error(ER_OUT_OF_RESOURCES, MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+}
-bool Protocol_local::store_date(MYSQL_TIME *time)
+static void
+list_fields_send_default(THD *thd, Protocol_local *p, Field *fld, uint pos)
{
- return store_column(time, sizeof(MYSQL_TIME));
-}
+ char buff[80];
+ String tmp(buff, sizeof(buff), default_charset_info), *res;
+ MYSQL_FIELD *client_field= &p->cur_data->embedded_info->fields_list[pos];
+ if (fld->is_null() || !(res= fld->val_str(&tmp)))
+ {
+ client_field->def_length= 0;
+ client_field->def= strmake_root(&p->cur_data->alloc, "", 0);
+ }
+ else
+ {
+ client_field->def_length= res->length();
+ client_field->def= strmake_root(&p->cur_data->alloc, res->ptr(),
+ client_field->def_length);
+ }
+}
-/** Store MYSQL_TIME (in binary format) */
-bool Protocol_local::store_time(MYSQL_TIME *time, int decimals)
+bool Protocol_local::send_list_fields(List<Field> *list, const TABLE_LIST *table_list)
{
- if (decimals != AUTO_SEC_PART_DIGITS)
- my_time_trunc(time, decimals);
- return store_column(time, sizeof(MYSQL_TIME));
-}
+ DBUG_ENTER("send_result_set_metadata");
+ Protocol_text prot(thd);
+ List_iterator_fast<Field> it(*list);
+ Field *fld;
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_RETURN(0);
-/* Store a floating point number, as is. */
+ if (begin_dataset(thd, list->elements))
+ goto err;
-bool Protocol_local::store(float value, uint32 decimals, String *buffer)
-{
- return store_column(&value, sizeof(float));
+ for (uint pos= 0 ; (fld= it++); pos++)
+ {
+ if (prot.store_field_metadata_for_list_fields(thd, fld, table_list, pos))
+ goto err;
+ list_fields_send_default(thd, this, fld, pos);
+ }
+
+ DBUG_RETURN(prepare_for_send(list->elements));
+err:
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ DBUG_RETURN(1);
}
-/* Store a double precision number, as is. */
+void Protocol_local::prepare_for_resend()
+{
+ MYSQL_ROWS *cur;
+ MYSQL_DATA *data= cur_data;
+ DBUG_ENTER("send_data");
+
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_VOID_RETURN;
+
+ data->rows++;
+ if (!(cur= (MYSQL_ROWS *)alloc_root(alloc, sizeof(MYSQL_ROWS)+(field_count + 1) * sizeof(char *))))
+ {
+ my_error(ER_OUT_OF_RESOURCES,MYF(0));
+ DBUG_VOID_RETURN;
+ }
+ cur->data= (MYSQL_ROW)(((char *)cur) + sizeof(MYSQL_ROWS));
+
+ *data->embedded_info->prev_ptr= cur;
+ data->embedded_info->prev_ptr= &cur->next;
+ next_field=cur->data;
+ next_mysql_field= data->embedded_info->fields_list;
+#ifndef DBUG_OFF
+ field_pos= 0;
+#endif
+
+ DBUG_VOID_RETURN;
+}
-bool Protocol_local::store(double value, uint32 decimals, String *buffer)
+bool Protocol_local::store_null()
{
- return store_column(&value, sizeof (double));
+ *(next_field++)= NULL;
+ ++next_mysql_field;
+ return false;
}
-/* Store a Field. */
+#include <sql_common.h>
+#include <errmsg.h>
-bool Protocol_local::store(Field *field)
+struct local_results
{
- if (field->is_null())
- return store_null();
- return field->send_binary(this);
-}
+ struct st_mysql_data *cur_data;
+ struct st_mysql_data *first_data;
+ struct st_mysql_data **data_tail;
+ void clear_data_list();
+ struct st_mysql_data *alloc_new_dataset();
+ char **next_field;
+ MYSQL_FIELD *next_mysql_field;
+ MEM_ROOT *alloc;
+};
-/** Called to start a new result set. */
+static void embedded_get_error(MYSQL *mysql, MYSQL_DATA *data)
+{
+ NET *net= &mysql->net;
+ struct embedded_query_result *ei= data->embedded_info;
+ net->last_errno= ei->last_errno;
+ strmake_buf(net->last_error, ei->info);
+ memcpy(net->sqlstate, ei->sqlstate, sizeof(net->sqlstate));
+ mysql->server_status= ei->server_status;
+ my_free(data);
+}
-bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
+
+static my_bool loc_read_query_result(MYSQL *mysql)
{
- DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root));
+ local_results *thd= (local_results *) mysql->thd;
+
+ MYSQL_DATA *res= thd->first_data;
+ DBUG_ASSERT(!thd->cur_data);
+ thd->first_data= res->embedded_info->next;
+ if (res->embedded_info->last_errno &&
+ !res->embedded_info->fields_list)
+ {
+ embedded_get_error(mysql, res);
+ return 1;
+ }
- init_sql_alloc(&m_rset_root, "send_result_set_metadata",
- MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ mysql->warning_count= res->embedded_info->warning_count;
+ mysql->server_status= res->embedded_info->server_status;
+ mysql->field_count= res->fields;
+ if (!(mysql->fields= res->embedded_info->fields_list))
+ {
+ mysql->affected_rows= res->embedded_info->affected_rows;
+ mysql->insert_id= res->embedded_info->insert_id;
+ }
+ net_clear_error(&mysql->net);
+ mysql->info= 0;
- if (! (m_rset= new (&m_rset_root) List<Ed_row>))
- return TRUE;
+ if (res->embedded_info->info[0])
+ {
+ strmake(mysql->info_buffer, res->embedded_info->info, MYSQL_ERRMSG_SIZE-1);
+ mysql->info= mysql->info_buffer;
+ }
- m_column_count= columns->elements;
+ if (res->embedded_info->fields_list)
+ {
+ mysql->status=MYSQL_STATUS_GET_RESULT;
+ thd->cur_data= res;
+ }
+ else
+ my_free(res);
- return FALSE;
+ return 0;
}
-/**
- Normally this is a separate result set with OUT parameters
- of stored procedures. Currently unsupported for the local
- version.
-*/
+static MYSQL_METHODS local_methods=
+{
+ loc_read_query_result, /* read_query_result */
+ NULL/*loc_advanced_command*/, /* advanced_command */
+ NULL/*loc_read_rows*/, /* read_rows */
+ NULL/*loc_use_result*/, /* use_result */
+ NULL/*loc_fetch_lengths*/, /* fetch_lengths */
+ NULL/*loc_flush_use_result*/, /* flush_use_result */
+ NULL/*loc_read_change_user_result*/ /* read_change_user_result */
+};
-bool Protocol_local::send_out_parameters(List<Item_param> *sp_params)
+
+extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql,
+ const char *host, const char *user, const char *passwd, const char *db)
{
- return FALSE;
-}
+ //char name_buff[USERNAME_LENGTH];
+ DBUG_ENTER("mysql_real_connect_local");
-/** Called for statements that don't have a result set, at statement end. */
+ /* Test whether we're already connected */
+ if (mysql->server_version)
+ {
+ set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate);
+ DBUG_RETURN(0);
+ }
-bool
-Protocol_local::send_ok(uint server_status, uint statement_warn_count,
- ulonglong affected_rows, ulonglong last_insert_id,
- const char *message, bool skip_flush)
-{
- /*
- Just make sure nothing is sent to the client, we have grabbed
- the status information in the connection diagnostics area.
- */
- return FALSE;
-}
+ if (!host || !host[0])
+ host= mysql->options.host;
+ mysql->methods= &local_methods;
-/**
- Called at the end of a result set. Append a complete
- result set to the list in Ed_connection.
+ if (!db || !db[0])
+ db=mysql->options.db;
- Don't send anything to the client, but instead finish
- building of the result set at hand.
-*/
+ if (!user || !user[0])
+ user=mysql->options.user;
-bool Protocol_local::send_eof(uint server_status, uint statement_warn_count)
-{
- Ed_result_set *ed_result_set;
+ mysql->user= my_strdup(PSI_INSTRUMENT_ME, user, MYF(0));
- DBUG_ASSERT(m_rset);
- opt_add_row_to_rset();
- m_current_row= 0;
+ mysql->info_buffer= (char *) my_malloc(PSI_INSTRUMENT_ME,
+ MYSQL_ERRMSG_SIZE, MYF(0));
+ //mysql->thd= create_embedded_thd(client_flag);
- ed_result_set= new (&m_rset_root) Ed_result_set(m_rset, m_column_count,
- &m_rset_root);
+ //init_embedded_mysql(mysql, client_flag);
- m_rset= NULL;
+ //if (mysql_init_character_set(mysql))
+ // goto error;
- if (! ed_result_set)
- return TRUE;
+ //if (check_embedded_connection(mysql, db))
+ // goto error;
- /* In case of successful allocation memory ownership was transferred. */
- DBUG_ASSERT(!alloc_root_inited(&m_rset_root));
+ mysql->server_status= SERVER_STATUS_AUTOCOMMIT;
- /*
- Link the created Ed_result_set instance into the list of connection
- result sets. Never fails.
- */
- m_connection->add_result_set(ed_result_set);
- return FALSE;
-}
+ //if (mysql->options.init_commands)
+ //{
+ // DYNAMIC_ARRAY *init_commands= mysql->options.init_commands;
+ // char **ptr= (char**)init_commands->buffer;
+ // char **end= ptr + init_commands->elements;
+//
+ // for (; ptr<end; ptr++)
+ // {
+ // MYSQL_RES *res;
+ // if (mysql_query(mysql,*ptr))
+ // goto error;
+ // if (mysql->fields)
+ // {
+ // if (!(res= (*mysql->methods->use_result)(mysql)))
+ // goto error;
+ // mysql_free_result(res);
+ // }
+ // }
+ //}
+ DBUG_PRINT("exit",("Mysql handler: %p", mysql));
+ DBUG_RETURN(mysql);
-/** Called to send an error to the client at the end of a statement. */
+//error:
+ DBUG_PRINT("error",("message: %u (%s)",
+ mysql->net.last_errno,
+ mysql->net.last_error));
+ {
+ /* Free alloced memory */
+ my_bool free_me=mysql->free_me;
+ free_old_query(mysql);
+ mysql->free_me=0;
+ mysql_close(mysql);
+ mysql->free_me=free_me;
+ }
+ DBUG_RETURN(0);
+}
-bool
-Protocol_local::send_error(uint sql_errno, const char *err_msg, const char*)
+
+extern "C" int execute_sql_command(const char *command,
+ char *hosts, char *names, char *filters)
{
- /*
- Just make sure that nothing is sent to the client (default
- implementation).
- */
- return FALSE;
+ MYSQL_LEX_STRING sql_text;
+ THD *thd= current_thd;
+ THD *new_thd= 0;
+ int result;
+ my_bool qc_save= 0;
+ Reprepare_observer *save_reprepare_observer= nullptr;
+
+ if (!thd)
+ {
+ new_thd= new THD(0);
+ new_thd->thread_stack= (char*) &sql_text;
+ new_thd->store_globals();
+ new_thd->security_ctx->skip_grants();
+ new_thd->query_cache_is_applicable= 0;
+ new_thd->variables.wsrep_on= 0;
+ bzero((char*) &new_thd->net, sizeof(new_thd->net));
+ thd= new_thd;
+ }
+ else
+ {
+ if (thd->lock)
+ /* Doesn't work if the thread opened/locked tables already. */
+ return 2;
+
+ qc_save= thd->query_cache_is_applicable;
+ thd->query_cache_is_applicable= 0;
+ save_reprepare_observer= thd->m_reprepare_observer;
+ thd->m_reprepare_observer= nullptr;
+ }
+ sql_text.str= (char *) command;
+ sql_text.length= strlen(command);
+ {
+ Protocol_local p(thd);
+ Ed_connection con(thd);
+ result= con.execute_direct(&p, sql_text);
+ if (!result && p.first_data)
+ {
+ int nr= (int) p.first_data->rows;
+ MYSQL_ROWS *rows= p.first_data->data;
+
+ while (nr--)
+ {
+ strcpy(hosts, rows->data[0]);
+ hosts+= strlen(hosts) + 1;
+ strcpy(names, rows->data[1]);
+ names+= strlen(names) + 1;
+ if (filters)
+ {
+ strcpy(filters, rows->data[2]);
+ filters+= strlen(filters) + 1;
+ }
+ rows= rows->next;
+ }
+ }
+ if (p.first_data)
+ {
+ if (p.alloc)
+ free_root(p.alloc, MYF(0));
+ my_free(p.first_data);
+ }
+ }
+
+ if (new_thd)
+ delete new_thd;
+ else
+ {
+ thd->query_cache_is_applicable= qc_save;
+ thd->m_reprepare_observer= save_reprepare_observer;
+ }
+
+ *hosts= 0;
+ return result;
}
+#endif /*!EMBEDDED_LIBRARY*/
+
-#ifdef EMBEDDED_LIBRARY
-void Protocol_local::remove_last_row()
-{ }
-#endif