diff options
Diffstat (limited to 'sql/protocol.cc')
-rw-r--r-- | sql/protocol.cc | 318 |
1 files changed, 264 insertions, 54 deletions
diff --git a/sql/protocol.cc b/sql/protocol.cc index fcfa14310bb..5990f0f001a 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -28,6 +28,7 @@ #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 *, 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 *); @@ -141,6 +142,7 @@ bool Protocol::net_store_data(const uchar *from, size_t length, 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); @@ -148,7 +150,6 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err, 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); @@ -397,32 +398,24 @@ 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, sqlstate); - } - converted_err_len= convert_error_message((char*)converted_err, - sizeof(converted_err), - thd->variables.character_set_results, - err, strlen(err), - system_charset_info, &error); - length= (uint) (strmake((char*) pos, (char*)converted_err, MYSQL_ERRMSG_SIZE) - - (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= (uchar*) strmov((char*) buff+3, sqlstate); } + converted_err_len= convert_error_message((char*)converted_err, + sizeof(converted_err), + thd->variables.character_set_results, + err, strlen(err), + system_charset_info, &error); + length= (uint) (strmake((char*) pos, (char*)converted_err, MYSQL_ERRMSG_SIZE) - + (char*) buff); + err= (char*) buff; DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err, - length)); + length)); } #endif /* EMBEDDED_LIBRARY */ @@ -486,6 +479,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. @@ -500,47 +499,94 @@ 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("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->stmt_da->is_sent) - return; - - bool error= FALSE; + DBUG_VOID_RETURN; 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->stmt_da->sql_errno(), - thd->stmt_da->message(), - thd->stmt_da->get_sqlstate()); + 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->stmt_da->server_status(), - thd->stmt_da->statement_warn_count()); + error= send_eof(thd->stmt_da->server_status(), + thd->stmt_da->statement_warn_count()); break; case Diagnostics_area::DA_OK: - error= net_send_ok(thd, - thd->stmt_da->server_status(), - thd->stmt_da->statement_warn_count(), - thd->stmt_da->affected_rows(), - thd->stmt_da->last_insert_id(), - thd->stmt_da->message()); + error= send_ok(thd->stmt_da->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, 0, 0, 0, NULL); + error= send_ok(thd->server_status, 0, 0, 0, NULL); break; } if (!error) 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"); + + DBUG_RETURN(net_send_ok(thd, server_status, statement_warn_count, + affected_rows, last_insert_id, message)); +} + + +/** + 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"); + + DBUG_RETURN(net_send_eof(thd, server_status, statement_warn_count)); +} + + +/** + 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"); + + DBUG_RETURN(net_send_error_packet(thd, sql_errno, err_msg, sql_state)); } @@ -597,9 +643,10 @@ void Protocol::init(THD *thd_arg) for the error. */ -void Protocol::end_partial_result_set(THD *thd) +void Protocol::end_partial_result_set(THD *thd_arg) { - net_send_eof(thd, thd->server_status, 0 /* no warnings, we're inside SP */); + net_send_eof(thd_arg, thd_arg->server_status, + 0 /* no warnings, we're inside SP */); } @@ -632,16 +679,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("send_fields"); + DBUG_ENTER("send_result_set_metadata"); if (flags & SEND_NUM_ROWS) { // Packet with number of elements @@ -786,7 +833,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags) write_eof_packet(thd, &thd->net, thd->server_status, thd->warning_info->statement_warn_count()); } - 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), @@ -805,6 +852,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 @@ -850,7 +938,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 @@ -1129,6 +1216,53 @@ bool Protocol_text::store_time(MYSQL_TIME *tm) 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 @@ -1149,14 +1283,13 @@ bool Protocol_text::store_time(MYSQL_TIME *tm) [..]..[[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; } @@ -1344,3 +1477,80 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm) 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; +} |