diff options
Diffstat (limited to 'sql/sql_prepare.cc')
-rw-r--r-- | sql/sql_prepare.cc | 1122 |
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(©)); - 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(©); 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 |