diff options
Diffstat (limited to 'sql/protocol.cc')
-rw-r--r-- | sql/protocol.cc | 461 |
1 files changed, 332 insertions, 129 deletions
diff --git a/sql/protocol.cc b/sql/protocol.cc index af4324b4166..eb9c3e34dbf 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2011, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,7 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file @@ -24,17 +25,21 @@ #pragma implementation // gcc: Class implementation #endif -#include "mysql_priv.h" +#include "sql_priv.h" +#include "unireg.h" // REQUIRED: for other includes +#include "protocol.h" +#include "sql_class.h" // THD #include <stdarg.h> static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; /* Declared non-static only because of the embedded library. */ -bool net_send_error_packet(THD *thd, uint sql_errno, const char *err); -bool net_send_ok(THD *, uint, uint, ha_rows, ulonglong, const char *); -bool net_send_eof(THD *thd, uint server_status, uint total_warn_count); +bool net_send_error_packet(THD *, uint, const char *, const char *); +/* Declared non-static only because of the embedded library. */ +bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *); +/* Declared non-static only because of the embedded library. */ +bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count); #ifndef EMBEDDED_LIBRARY -static bool write_eof_packet(THD *thd, NET *net, - uint server_status, uint total_warn_count); +static bool write_eof_packet(THD *, NET *, uint, uint); #endif #ifndef EMBEDDED_LIBRARY @@ -66,7 +71,7 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length) exactly one byte to store length. It allows not to use the "convert" member as a temporary buffer, conversion is done directly to the "packet" member. - The limit 251 is good enough to optimize send_fields() + The limit 251 is good enough to optimize send_result_set_metadata() because column, table, database names fit into this limit. */ @@ -76,9 +81,7 @@ bool Protocol::net_store_data(const uchar *from, size_t length, { uint dummy_errors; /* Calculate maxumum possible result length */ - size_t conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen; - ulong packet_length, new_length; - char *length_pos, *to; + uint conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen; if (conv_length > 250) { @@ -93,19 +96,19 @@ bool Protocol::net_store_data(const uchar *from, size_t length, Thus conversion directly to "packet" is not worthy. Let's use "convert" as a temporary buffer. */ - return (convert->copy((const char*) from, length, from_cs, to_cs, - &dummy_errors) || + return (convert->copy((const char*) from, length, from_cs, + to_cs, &dummy_errors) || net_store_data((const uchar*) convert->ptr(), convert->length())); } - packet_length= packet->length(); - new_length= packet_length + conv_length + 1; + ulong packet_length= packet->length(); + ulong new_length= packet_length + conv_length + 1; if (new_length > packet->alloced_length() && packet->realloc(new_length)) return 1; - length_pos= (char*) packet->ptr() + packet_length; - to= length_pos + 1; + char *length_pos= (char*) packet->ptr() + packet_length; + char *to= length_pos + 1; to+= copy_and_convert(to, conv_length, to_cs, (const char*) from, length, from_cs, &dummy_errors); @@ -139,29 +142,33 @@ bool Protocol::net_store_data(const uchar *from, size_t length, @retval TRUE An error occurred and the message wasn't sent properly */ -bool net_send_error(THD *thd, uint sql_errno, const char *err) +bool net_send_error(THD *thd, uint sql_errno, const char *err, + const char* sqlstate) { + bool error; DBUG_ENTER("net_send_error"); DBUG_ASSERT(!thd->spcont); DBUG_ASSERT(sql_errno); - DBUG_ASSERT(err && err[0]); + DBUG_ASSERT(err); DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err)); - bool error; + + if (sqlstate == NULL) + sqlstate= mysql_errno_to_sqlstate(sql_errno); /* It's one case when we can push an error even though there is an OK or EOF already. */ - thd->main_da.can_overwrite_status= TRUE; + thd->stmt_da->can_overwrite_status= TRUE; /* Abort multi-result sets */ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; - error= net_send_error_packet(thd, sql_errno, err); + error= net_send_error_packet(thd, sql_errno, err, sqlstate); - thd->main_da.can_overwrite_status= FALSE; + thd->stmt_da->can_overwrite_status= FALSE; DBUG_RETURN(error); } @@ -183,7 +190,7 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err) @param thd Thread handler @param server_status The server status - @param total_warn_count Total number of warnings + @param statement_warn_count Total number of warnings @param affected_rows Number of rows changed by statement @param id Auto_increment id for first row (if used) @param message Message to send to the client (Used by mysql_status) @@ -197,8 +204,8 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err) #ifndef EMBEDDED_LIBRARY bool net_send_ok(THD *thd, - uint server_status, uint total_warn_count, - ha_rows affected_rows, ulonglong id, const char *message) + uint server_status, uint statement_warn_count, + ulonglong affected_rows, ulonglong id, const char *message) { NET *net= &thd->net; uchar buff[MYSQL_ERRMSG_SIZE+10],*pos; @@ -221,12 +228,12 @@ net_send_ok(THD *thd, (ulong) affected_rows, (ulong) id, (uint) (server_status & 0xffff), - (uint) total_warn_count)); + (uint) statement_warn_count)); int2store(pos, server_status); pos+=2; /* We can only return up to 65535 warnings in two bytes */ - uint tmp= min(total_warn_count, 65535); + uint tmp= min(statement_warn_count, 65535); int2store(pos, tmp); pos+= 2; } @@ -235,7 +242,7 @@ net_send_ok(THD *thd, int2store(pos, server_status); pos+=2; } - thd->main_da.can_overwrite_status= TRUE; + thd->stmt_da->can_overwrite_status= TRUE; if (message && message[0]) pos= net_store_data(pos, (uchar*) message, strlen(message)); @@ -243,7 +250,8 @@ net_send_ok(THD *thd, if (!error) error= net_flush(net); - thd->main_da.can_overwrite_status= FALSE; + + thd->stmt_da->can_overwrite_status= FALSE; DBUG_PRINT("info", ("OK sent, so no more error sending allowed")); DBUG_RETURN(error); @@ -267,7 +275,7 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */ @param thd Thread handler @param server_status The server status - @param total_warn_count Total number of warnings + @param statement_warn_count Total number of warnings @return @retval FALSE The message was successfully sent @@ -275,7 +283,7 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */ */ bool -net_send_eof(THD *thd, uint server_status, uint total_warn_count) +net_send_eof(THD *thd, uint server_status, uint statement_warn_count) { NET *net= &thd->net; bool error= FALSE; @@ -283,11 +291,11 @@ net_send_eof(THD *thd, uint server_status, uint total_warn_count) /* Set to TRUE if no active vio, to work well in case of --init-file */ if (net->vio != 0) { - thd->main_da.can_overwrite_status= TRUE; - error= write_eof_packet(thd, net, server_status, total_warn_count); + thd->stmt_da->can_overwrite_status= TRUE; + error= write_eof_packet(thd, net, server_status, statement_warn_count); if (!error) error= net_flush(net); - thd->main_da.can_overwrite_status= FALSE; + thd->stmt_da->can_overwrite_status= FALSE; DBUG_PRINT("info", ("EOF sent, so no more error sending allowed")); } DBUG_RETURN(error); @@ -301,7 +309,7 @@ net_send_eof(THD *thd, uint server_status, uint total_warn_count) @param thd The thread handler @param net The network handler @param server_status The server status - @param total_warn_count The number of warnings + @param statement_warn_count The number of warnings @return @@ -311,7 +319,7 @@ net_send_eof(THD *thd, uint server_status, uint total_warn_count) static bool write_eof_packet(THD *thd, NET *net, uint server_status, - uint total_warn_count) + uint statement_warn_count) { bool error; if (thd->client_capabilities & CLIENT_PROTOCOL_41) @@ -321,7 +329,7 @@ static bool write_eof_packet(THD *thd, NET *net, Don't send warn count during SP execution, as the warn_list is cleared between substatements, and mysqltest gets confused */ - uint tmp= min(total_warn_count, 65535); + uint tmp= min(statement_warn_count, 65535); buff[0]= 254; int2store(buff+1, tmp); /* @@ -350,14 +358,18 @@ static bool write_eof_packet(THD *thd, NET *net, @retval TRUE An error occurred and the messages wasn't sent properly */ -bool net_send_error_packet(THD *thd, uint sql_errno, const char *err) +bool net_send_error_packet(THD *thd, uint sql_errno, const char *err, + const char* sqlstate) + { NET *net= &thd->net; uint length; /* buff[]: sql_errno:2 + ('#':1 + SQLSTATE_LENGTH:5) + MYSQL_ERRMSG_SIZE:512 */ - uchar buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *pos; + uint error; + char converted_err[MYSQL_ERRMSG_SIZE]; + char buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *pos; DBUG_ENTER("send_error_packet"); @@ -371,27 +383,23 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err) DBUG_RETURN(FALSE); } - if (net->return_errno) - { // new client code; Add errno before message - int2store(buff,sql_errno); - pos= buff+2; - if (thd->client_capabilities & CLIENT_PROTOCOL_41) - { - /* The first # is to make the protocol backward compatible */ - buff[2]= '#'; - pos= (uchar*) strmov((char*) buff+3, mysql_errno_to_sqlstate(sql_errno)); - } - length= (uint) (strmake((char*) pos, err, MYSQL_ERRMSG_SIZE-1) - - (char*) buff); - err= (char*) buff; - } - else + int2store(buff,sql_errno); + pos= buff+2; + if (thd->client_capabilities & CLIENT_PROTOCOL_41) { - length=(uint) strlen(err); - set_if_smaller(length,MYSQL_ERRMSG_SIZE-1); + /* The first # is to make the protocol backward compatible */ + buff[2]= '#'; + pos= strmov(buff+3, sqlstate); } - DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err, - length)); + + 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. */ + length= (uint) (strmake(pos, converted_err, MYSQL_ERRMSG_SIZE - 1) - buff); + + DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) buff, + length)); } #endif /* EMBEDDED_LIBRARY */ @@ -455,6 +463,12 @@ static uchar *net_store_length_fast(uchar *packet, uint length) packet is "buffered" in the diagnostics area and sent to the client in the end of statement. + @note This method defines a template, but delegates actual + sending of data to virtual Protocol::send_{ok,eof,error}. This + allows for implementation of protocols that "intercept" ok/eof/error + messages, and store them in memory, etc, instead of sending to + the client. + @pre The diagnostics area is assigned or disabled. It can not be empty -- we assume that every SQL statement or COM_* command generates OK, ERROR, or EOF status. @@ -469,51 +483,96 @@ static uchar *net_store_length_fast(uchar *packet, uint length) Diagnostics_area::is_sent is set for debugging purposes only. */ -void net_end_statement(THD *thd) +void Protocol::end_statement() { - DBUG_ENTER("net_end_statement"); - DBUG_ASSERT(! thd->main_da.is_sent); + DBUG_ENTER("Protocol::end_statement"); + DBUG_ASSERT(! thd->stmt_da->is_sent); + bool error= FALSE; /* Can not be true, but do not take chances in production. */ - if (thd->main_da.is_sent) - return; - - bool error= FALSE; + if (thd->stmt_da->is_sent) + DBUG_VOID_RETURN; - switch (thd->main_da.status()) { + switch (thd->stmt_da->status()) { case Diagnostics_area::DA_ERROR: /* The query failed, send error to log and abort bootstrap. */ - error= net_send_error(thd, - thd->main_da.sql_errno(), - thd->main_da.message()); + error= send_error(thd->stmt_da->sql_errno(), + thd->stmt_da->message(), + thd->stmt_da->get_sqlstate()); break; case Diagnostics_area::DA_EOF: - error= net_send_eof(thd, - thd->main_da.server_status(), - thd->main_da.total_warn_count()); + error= send_eof(thd->server_status, + thd->stmt_da->statement_warn_count()); break; case Diagnostics_area::DA_OK: - error= net_send_ok(thd, - thd->main_da.server_status(), - thd->main_da.total_warn_count(), - thd->main_da.affected_rows(), - thd->main_da.last_insert_id(), - thd->main_da.message()); + error= send_ok(thd->server_status, + thd->stmt_da->statement_warn_count(), + thd->stmt_da->affected_rows(), + thd->stmt_da->last_insert_id(), + thd->stmt_da->message()); break; case Diagnostics_area::DA_DISABLED: break; case Diagnostics_area::DA_EMPTY: default: DBUG_ASSERT(0); - error= net_send_ok(thd, thd->server_status, thd->total_warn_count, - 0, 0, NULL); + error= send_ok(thd->server_status, 0, 0, 0, NULL); break; } if (!error) - thd->main_da.is_sent= TRUE; + thd->stmt_da->is_sent= TRUE; DBUG_VOID_RETURN; } +/** + A default implementation of "OK" packet response to the client. + + Currently this implementation is re-used by both network-oriented + protocols -- the binary and text one. They do not differ + in their OK packet format, which allows for a significant simplification + on client side. +*/ + +bool Protocol::send_ok(uint server_status, uint statement_warn_count, + ulonglong affected_rows, ulonglong last_insert_id, + const char *message) +{ + DBUG_ENTER("Protocol::send_ok"); + const bool retval= + net_send_ok(thd, server_status, statement_warn_count, + affected_rows, last_insert_id, message); + DBUG_RETURN(retval); +} + + +/** + A default implementation of "EOF" packet response to the client. + + Binary and text protocol do not differ in their EOF packet format. +*/ + +bool Protocol::send_eof(uint server_status, uint statement_warn_count) +{ + DBUG_ENTER("Protocol::send_eof"); + const bool retval= net_send_eof(thd, server_status, statement_warn_count); + DBUG_RETURN(retval); +} + + +/** + A default implementation of "ERROR" packet response to the client. + + Binary and text protocol do not differ in ERROR packet format. +*/ + +bool Protocol::send_error(uint sql_errno, const char *err_msg, + const char *sql_state) +{ + DBUG_ENTER("Protocol::send_error"); + const bool retval= net_send_error_packet(thd, sql_errno, err_msg, sql_state); + DBUG_RETURN(retval); +} + /** Send a progress report to the client @@ -629,9 +688,9 @@ bool Protocol::flush() { #ifndef EMBEDDED_LIBRARY bool error; - thd->main_da.can_overwrite_status= TRUE; + thd->stmt_da->can_overwrite_status= TRUE; error= net_flush(&thd->net); - thd->main_da.can_overwrite_status= FALSE; + thd->stmt_da->can_overwrite_status= FALSE; return error; #else return 0; @@ -658,16 +717,16 @@ bool Protocol::flush() 1 Error (Note that in this case the error is not sent to the client) */ -bool Protocol::send_fields(List<Item> *list, uint flags) +bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) { List_iterator_fast<Item> it(*list); Item *item; - uchar buff[80]; + uchar buff[MAX_FIELD_WIDTH]; String tmp((char*) buff,sizeof(buff),&my_charset_bin); Protocol_text prot(thd); String *local_packet= prot.storage_packet(); CHARSET_INFO *thd_charset= thd->variables.character_set_results; - DBUG_ENTER("Protocol::send_fields"); + DBUG_ENTER("Protocol::send_result_set_metadata"); if (flags & SEND_NUM_ROWS) { // Packet with number of elements @@ -716,17 +775,16 @@ bool Protocol::send_fields(List<Item> *list, uint flags) /* Store fixed length fields */ pos= (char*) local_packet->ptr()+local_packet->length(); *pos++= 12; // Length of packed fields - if (item->collation.collation == &my_charset_bin || thd_charset == NULL) + if (item->charset_for_protocol() == &my_charset_bin || thd_charset == NULL) { /* No conversion */ - int2store(pos, field.charsetnr); + int2store(pos, item->charset_for_protocol()->number); int4store(pos+2, field.length); } else { /* With conversion */ - ulonglong max_length; - uint32 field_length; + uint32 field_length, max_length; int2store(pos, thd_charset->number); /* For TEXT/BLOB columns, field_length describes the maximum data @@ -749,9 +807,8 @@ bool Protocol::send_fields(List<Item> *list, uint flags) field.type <= MYSQL_TYPE_BLOB) ? field.length / item->collation.collation->mbminlen : field.length / item->collation.collation->mbmaxlen; - max_length*= thd_charset->mbmaxlen; - field_length= (max_length > UINT_MAX32) ? - UINT_MAX32 : (uint32) max_length; + field_length= char_to_byte_length_safe(max_length, + thd_charset->mbmaxlen); int4store(pos + 2, field_length); } pos[6]= field.type; @@ -770,31 +827,14 @@ bool Protocol::send_fields(List<Item> *list, uint flags) local_packet->realloc(local_packet->length()+10)) goto err; pos= (char*) local_packet->ptr()+local_packet->length(); - -#ifdef TO_BE_DELETED_IN_6 - if (!(thd->client_capabilities & CLIENT_LONG_FLAG)) - { - pos[0]=3; - int3store(pos+1,field.length); - pos[4]=1; - pos[5]=field.type; - pos[6]=2; - pos[7]= (char) field.flags; - pos[8]= (char) field.decimals; - pos+= 9; - } - else -#endif - { - pos[0]=3; - int3store(pos+1,field.length); - pos[4]=1; - pos[5]=field.type; - pos[6]=3; - int2store(pos+7,field.flags); - pos[9]= (char) field.decimals; - pos+= 10; - } + pos[0]=3; + int3store(pos+1,field.length); + pos[4]=1; + pos[5]=field.type; + pos[6]=3; + int2store(pos+7,field.flags); + pos[9]= (char) field.decimals; + pos+= 10; } local_packet->length((uint) (pos - local_packet->ptr())); if (flags & SEND_DEFAULTS) @@ -814,10 +854,10 @@ bool Protocol::send_fields(List<Item> *list, uint flags) Send no warning information, as it will be sent at statement end. */ if (write_eof_packet(thd, &thd->net, thd->server_status, - thd->total_warn_count)) + thd->warning_info->statement_warn_count())) DBUG_RETURN(1); } - DBUG_RETURN(prepare_for_send(list)); + DBUG_RETURN(prepare_for_send(list->elements)); err: my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), @@ -836,6 +876,47 @@ bool Protocol::write() /** + Send one result set row. + + @param row_items a collection of column values for that row + + @return Error status. + @retval TRUE Error. + @retval FALSE Success. +*/ + +bool Protocol::send_result_set_row(List<Item> *row_items) +{ + char buffer[MAX_FIELD_WIDTH]; + String str_buffer(buffer, sizeof (buffer), &my_charset_bin); + List_iterator_fast<Item> it(*row_items); + + DBUG_ENTER("Protocol::send_result_set_row"); + + for (Item *item= it++; item; item= it++) + { + if (item->send(this, &str_buffer)) + { + // If we're out of memory, reclaim some, to help us recover. + this->free(); + DBUG_RETURN(TRUE); + } + /* Item::send() may generate an error. If so, abort the loop. */ + if (thd->is_error()) + DBUG_RETURN(TRUE); + + /* + Reset str_buffer to its original state, as it may have been altered in + Item::send(). + */ + str_buffer.set(buffer, sizeof(buffer), &my_charset_bin); + } + + DBUG_RETURN(FALSE); +} + + +/** Send \\0 end terminated string. @param from NullS or \\0 terminated string @@ -881,7 +962,6 @@ bool Protocol::store(I_List<i_string>* str_list) return store((char*) tmp.ptr(), len, tmp.charset()); } - /**************************************************************************** Functions to handle the simple (default) protocol where everything is This protocol is the one that is used by default between the MySQL server @@ -954,7 +1034,7 @@ bool Protocol_text::store(const char *from, size_t length, CHARSET_INFO *tocs= this->thd->variables.character_set_results; #ifndef DBUG_OFF DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %.*s", field_pos, - field_count, (int) length, (length == 0? "" : from))); + field_count, (int) length, (length == 0 ? "" : from))); DBUG_ASSERT(field_pos < field_count); DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || @@ -1130,6 +1210,53 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals) return net_store_data((uchar*) buff, length); } +/** + Assign OUT-parameters to user variables. + + @param sp_params List of PS/SP parameters (both input and output). + + @return Error status. + @retval FALSE Success. + @retval TRUE Error. +*/ + +bool Protocol_text::send_out_parameters(List<Item_param> *sp_params) +{ + DBUG_ASSERT(sp_params->elements == + thd->lex->prepared_stmt_params.elements); + + List_iterator_fast<Item_param> item_param_it(*sp_params); + List_iterator_fast<LEX_STRING> user_var_name_it(thd->lex->prepared_stmt_params); + + while (true) + { + Item_param *item_param= item_param_it++; + LEX_STRING *user_var_name= user_var_name_it++; + + if (!item_param || !user_var_name) + break; + + if (!item_param->get_out_param_info()) + continue; // It's an IN-parameter. + + Item_func_set_user_var *suv= + new Item_func_set_user_var(*user_var_name, item_param); + /* + Item_func_set_user_var is not fixed after construction, call + fix_fields(). + */ + if (suv->fix_fields(thd, NULL)) + return TRUE; + + if (suv->check(FALSE)) + return TRUE; + + if (suv->update()) + return TRUE; + } + + return FALSE; +} /**************************************************************************** Functions to handle the binary protocol used with prepared statements @@ -1150,14 +1277,13 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals) [..]..[[length]data] data ****************************************************************************/ -bool Protocol_binary::prepare_for_send(List<Item> *item_list) +bool Protocol_binary::prepare_for_send(uint num_columns) { - Protocol::prepare_for_send(item_list); + Protocol::prepare_for_send(num_columns); bit_fields= (field_count+9)/8; - if (packet->alloc(bit_fields+1)) - return 1; + return packet->alloc(bit_fields+1); + /* prepare_for_resend will be called after this one */ - return 0; } @@ -1352,3 +1478,80 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm, int decimals) buff[0]=(char) length; // Length is stored first return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC); } + +/** + Send a result set with OUT-parameter values by means of PS-protocol. + + @param sp_params List of PS/SP parameters (both input and output). + + @return Error status. + @retval FALSE Success. + @retval TRUE Error. +*/ + +bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params) +{ + if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS)) + { + /* The client does not support OUT-parameters. */ + return FALSE; + } + + List<Item> out_param_lst; + + { + List_iterator_fast<Item_param> item_param_it(*sp_params); + + while (true) + { + Item_param *item_param= item_param_it++; + + if (!item_param) + break; + + if (!item_param->get_out_param_info()) + continue; // It's an IN-parameter. + + if (out_param_lst.push_back(item_param)) + return TRUE; + } + } + + if (!out_param_lst.elements) + return FALSE; + + /* + We have to set SERVER_PS_OUT_PARAMS in THD::server_status, because it + is used in send_result_set_metadata(). + */ + + thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS; + + /* Send meta-data. */ + if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF)) + return TRUE; + + /* Send data. */ + + prepare_for_resend(); + + if (send_result_set_row(&out_param_lst)) + return TRUE; + + if (write()) + return TRUE; + + /* Restore THD::server_status. */ + thd->server_status&= ~SERVER_PS_OUT_PARAMS; + + /* + Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet + for sure. + */ + thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; + + /* Send EOF-packet. */ + net_send_eof(thd, thd->server_status, 0); + + return FALSE; +} |