diff options
Diffstat (limited to 'sql/protocol.cc')
-rw-r--r-- | sql/protocol.cc | 331 |
1 files changed, 171 insertions, 160 deletions
diff --git a/sql/protocol.cc b/sql/protocol.cc index 02c95a42695..8c7eeaec90c 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -562,8 +562,26 @@ static uchar *net_store_length_fast(uchar *packet, size_t length) void Protocol::end_statement() { - /* sanity check*/ - DBUG_ASSERT_IF_WSREP(!(WSREP(thd) && thd->wsrep_conflict_state == REPLAYING)); +#ifdef WITH_WSREP + /* + Commented out: This sanity check does not hold in general. + Thd->LOCK_thd_data() must be unlocked before sending response + to client, so BF abort may sneak in here. + DBUG_ASSERT(!WSREP(thd) || thd->wsrep_conflict_state() == NO_CONFLICT); + */ + + /* + sanity check, don't send end statement while replaying + */ + DBUG_ASSERT(thd->wsrep_trx().state() != wsrep::transaction::s_replaying); + if (WSREP(thd) && thd->wsrep_trx().state() == + wsrep::transaction::s_replaying) + { + WSREP_ERROR("attempting net_end_statement while replaying"); + return; + } +#endif /* WITH_WSREP */ + DBUG_ENTER("Protocol::end_statement"); DBUG_ASSERT(! thd->get_stmt_da()->is_sent()); bool error= FALSE; @@ -749,7 +767,7 @@ void Protocol::init(THD *thd_arg) packet= &thd->packet; convert= &thd->convert_buffer; #ifndef DBUG_OFF - field_types= 0; + field_handlers= 0; #endif } @@ -781,6 +799,73 @@ bool Protocol::flush() #ifndef EMBEDDED_LIBRARY +bool Protocol_text::store_field_metadata(const THD * thd, + const Send_field &field, + CHARSET_INFO *charset_for_protocol, + uint fieldnr) +{ + CHARSET_INFO *thd_charset= thd->variables.character_set_results; + char *pos; + CHARSET_INFO *cs= system_charset_info; + DBUG_ASSERT(field.is_sane()); + + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + { + if (store(STRING_WITH_LEN("def"), cs, thd_charset) || + store_str(field.db_name, cs, thd_charset) || + store_str(field.table_name, cs, thd_charset) || + store_str(field.org_table_name, cs, thd_charset) || + store_str(field.col_name, cs, thd_charset) || + store_str(field.org_col_name, cs, thd_charset) || + packet->realloc(packet->length() + 12)) + return true; + /* Store fixed length fields */ + pos= (char*) packet->end(); + *pos++= 12; // Length of packed fields + /* inject a NULL to test the client */ + DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= (char) 0xfb;); + if (charset_for_protocol == &my_charset_bin || thd_charset == NULL) + { + /* No conversion */ + int2store(pos, charset_for_protocol->number); + int4store(pos + 2, field.length); + } + else + { + /* With conversion */ + int2store(pos, thd_charset->number); + uint32 field_length= field.max_octet_length(charset_for_protocol, + thd_charset); + int4store(pos + 2, field_length); + } + pos[6]= field.type_handler()->type_code_for_protocol(); + int2store(pos + 7, field.flags); + pos[9]= (char) field.decimals; + pos[10]= 0; // For the future + pos[11]= 0; // For the future + pos+= 12; + } + else + { + if (store_str(field.table_name, cs, thd_charset) || + store_str(field.col_name, cs, thd_charset) || + packet->realloc(packet->length() + 10)) + return true; + pos= (char*) packet->end(); + pos[0]= 3; + int3store(pos + 1, field.length); + pos[4]= 1; + pos[5]= field.type_handler()->type_code_for_protocol(); + pos[6]= 3; + int2store(pos + 7, field.flags); + pos[9]= (char) field.decimals; + pos+= 10; + } + packet->length((uint) (pos - packet->ptr())); + return false; +} + + /** Send name and type of result to client. @@ -803,10 +888,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) { List_iterator_fast<Item> it(*list); Item *item; - ValueBuffer<MAX_FIELD_WIDTH> tmp; - Protocol_text prot(thd); - String *local_packet= prot.storage_packet(); - CHARSET_INFO *thd_charset= thd->variables.character_set_results; + Protocol_text prot(thd, thd->variables.net_buffer_length); DBUG_ENTER("Protocol::send_result_set_metadata"); if (flags & SEND_NUM_ROWS) @@ -819,119 +901,19 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) } #ifndef DBUG_OFF - field_types= (enum_field_types*) thd->alloc(sizeof(field_types) * - list->elements); - uint count= 0; + field_handlers= (const Type_handler**) thd->alloc(sizeof(field_handlers[0]) * + list->elements); #endif - /* We have to reallocate it here as a stored procedure may have reset it */ - (void) local_packet->alloc(thd->variables.net_buffer_length); - - while ((item=it++)) + for (uint pos= 0; (item=it++); pos++) { - char *pos; - CHARSET_INFO *cs= system_charset_info; - Send_field field; - item->make_send_field(thd, &field); - - /* limit number of decimals for float and double */ - if (field.type == MYSQL_TYPE_FLOAT || field.type == MYSQL_TYPE_DOUBLE) - set_if_smaller(field.decimals, FLOATING_POINT_DECIMALS); - - /* Keep things compatible for old clients */ - if (field.type == MYSQL_TYPE_VARCHAR) - field.type= MYSQL_TYPE_VAR_STRING; - prot.prepare_for_resend(); - - if (thd->client_capabilities & CLIENT_PROTOCOL_41) - { - if (prot.store(STRING_WITH_LEN("def"), cs, thd_charset) || - prot.store(field.db_name, (uint) strlen(field.db_name), - cs, thd_charset) || - prot.store(field.table_name, (uint) strlen(field.table_name), - cs, thd_charset) || - prot.store(field.org_table_name, (uint) strlen(field.org_table_name), - cs, thd_charset) || - prot.store(field.col_name.str, (uint) field.col_name.length, - cs, thd_charset) || - prot.store(field.org_col_name.str, (uint) field.org_col_name.length, - cs, thd_charset) || - local_packet->realloc(local_packet->length()+12)) - goto err; - /* Store fixed length fields */ - pos= (char*) local_packet->ptr()+local_packet->length(); - *pos++= 12; // Length of packed fields - /* inject a NULL to test the client */ - DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= (char) 0xfb;); - if (item->charset_for_protocol() == &my_charset_bin || thd_charset == NULL) - { - /* No conversion */ - int2store(pos, item->charset_for_protocol()->number); - int4store(pos+2, field.length); - } - else - { - /* With conversion */ - uint32 field_length, max_length; - int2store(pos, thd_charset->number); - /* - For TEXT/BLOB columns, field_length describes the maximum data - length in bytes. There is no limit to the number of characters - that a TEXT column can store, as long as the data fits into - the designated space. - For the rest of textual columns, field_length is evaluated as - char_count * mbmaxlen, where character count is taken from the - definition of the column. In other words, the maximum number - of characters here is limited by the column definition. - - When one has a LONG TEXT column with a single-byte - character set, and the connection character set is multi-byte, the - client may get fields longer than UINT_MAX32, due to - <character set column> -> <character set connection> conversion. - In that case column max length does not fit into the 4 bytes - reserved for it in the protocol. - */ - max_length= (field.type >= MYSQL_TYPE_TINY_BLOB && - field.type <= MYSQL_TYPE_BLOB) ? - field.length / item->collation.collation->mbminlen : - field.length / item->collation.collation->mbmaxlen; - field_length= char_to_byte_length_safe(max_length, - thd_charset->mbmaxlen); - int4store(pos + 2, field_length); - } - pos[6]= field.type; - int2store(pos+7,field.flags); - pos[9]= (char) field.decimals; - pos[10]= 0; // For the future - pos[11]= 0; // For the future - pos+= 12; - } - else - { - if (prot.store(field.table_name, (uint) strlen(field.table_name), - cs, thd_charset) || - prot.store(field.col_name.str, (uint) field.col_name.length, - cs, thd_charset) || - local_packet->realloc(local_packet->length()+10)) - goto err; - pos= (char*) local_packet->ptr()+local_packet->length(); - 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) - item->send(&prot, &tmp); // Send default value + if (prot.store_field_metadata(thd, item, pos)) + goto err; if (prot.write()) DBUG_RETURN(1); #ifndef DBUG_OFF - field_types[count++]= field.type; + field_handlers[pos]= item->type_handler(); #endif } @@ -960,6 +942,43 @@ err: } +bool Protocol::send_list_fields(List<Field> *list, const TABLE_LIST *table_list) +{ + DBUG_ENTER("Protocol::send_list_fields"); + List_iterator_fast<Field> it(*list); + Field *fld; + Protocol_text prot(thd, thd->variables.net_buffer_length); + +#ifndef DBUG_OFF + field_handlers= (const Type_handler **) thd->alloc(sizeof(field_handlers[0]) * + list->elements); +#endif + + for (uint pos= 0; (fld= it++); pos++) + { + prot.prepare_for_resend(); + if (prot.store_field_metadata_for_list_fields(thd, fld, table_list, pos)) + goto err; + prot.store(fld); // Send default value + if (prot.write()) + DBUG_RETURN(1); +#ifndef DBUG_OFF + /* + Historically all BLOB variant Fields are displayed as + MYSQL_TYPE_BLOB in metadata. + See Field_blob::make_send_field() for more comments. + */ + field_handlers[pos]= Send_field(fld).type_handler(); +#endif + } + DBUG_RETURN(prepare_for_send(list->elements)); + +err: + my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES), MYF(0)); + DBUG_RETURN(1); +} + + bool Protocol::write() { DBUG_ENTER("Protocol::write"); @@ -969,6 +988,25 @@ bool Protocol::write() #endif /* EMBEDDED_LIBRARY */ +bool Protocol_text::store_field_metadata(THD *thd, Item *item, uint pos) +{ + Send_field field(thd, item); + return store_field_metadata(thd, field, item->charset_for_protocol(), pos); +} + + +bool Protocol_text::store_field_metadata_for_list_fields(const THD *thd, + Field *fld, + const TABLE_LIST *tl, + uint pos) +{ + Send_field field= tl->view ? + Send_field(fld, tl->view_db.str, tl->view_name.str) : + Send_field(fld); + return store_field_metadata(thd, field, fld->charset_for_protocol(), pos); +} + + /** Send one result set row. @@ -1109,12 +1147,7 @@ bool Protocol_text::store(const char *from, size_t length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DECIMAL || - field_types[field_pos] == MYSQL_TYPE_BIT || - field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL || - (field_types[field_pos] >= MYSQL_TYPE_ENUM && - field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_STRING)); field_pos++; #endif return store_string_aux(from, length, fromcs, tocs); @@ -1128,14 +1161,8 @@ bool Protocol_text::store(const char *from, size_t length, #ifndef DBUG_OFF DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %.*s", field_pos, field_count, (int) length, (length == 0 ? "" : from))); - DBUG_ASSERT(field_types == 0 || field_pos < field_count); - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DECIMAL || - field_types[field_pos] == MYSQL_TYPE_BIT || - field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL || - field_types[field_pos] == MYSQL_TYPE_NEWDATE || - (field_types[field_pos] >= MYSQL_TYPE_ENUM && - field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); + DBUG_ASSERT(field_handlers == 0 || field_pos < field_count); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_STRING)); field_pos++; #endif return store_string_aux(from, length, fromcs, tocs); @@ -1145,7 +1172,7 @@ bool Protocol_text::store(const char *from, size_t length, bool Protocol_text::store_tiny(longlong from) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_TINY)); field_pos++; #endif char buff[22]; @@ -1157,9 +1184,7 @@ bool Protocol_text::store_tiny(longlong from) bool Protocol_text::store_short(longlong from) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_YEAR || - field_types[field_pos] == MYSQL_TYPE_SHORT); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_SHORT)); field_pos++; #endif char buff[22]; @@ -1172,9 +1197,7 @@ bool Protocol_text::store_short(longlong from) bool Protocol_text::store_long(longlong from) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_INT24 || - field_types[field_pos] == MYSQL_TYPE_LONG); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_LONG)); field_pos++; #endif char buff[22]; @@ -1187,8 +1210,7 @@ bool Protocol_text::store_long(longlong from) bool Protocol_text::store_longlong(longlong from, bool unsigned_flag) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_LONGLONG); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_LONGLONG)); field_pos++; #endif char buff[22]; @@ -1202,13 +1224,11 @@ bool Protocol_text::store_longlong(longlong from, bool unsigned_flag) bool Protocol_text::store_decimal(const my_decimal *d) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL); + DBUG_ASSERT(0); // This method is not used yet field_pos++; #endif - char buff[DECIMAL_MAX_STR_LENGTH]; - String str(buff, sizeof(buff), &my_charset_bin); - (void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str); + StringBuffer<DECIMAL_MAX_STR_LENGTH> str; + (void) d->to_string(&str); return net_store_data((uchar*) str.ptr(), str.length()); } @@ -1216,8 +1236,7 @@ bool Protocol_text::store_decimal(const my_decimal *d) bool Protocol_text::store(float from, uint32 decimals, String *buffer) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_FLOAT); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_FLOAT)); field_pos++; #endif Float(from).to_string(buffer, decimals); @@ -1228,8 +1247,7 @@ bool Protocol_text::store(float from, uint32 decimals, String *buffer) bool Protocol_text::store(double from, uint32 decimals, String *buffer) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DOUBLE); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_DOUBLE)); field_pos++; #endif buffer->set_real(from, decimals, thd->charset()); @@ -1267,9 +1285,7 @@ bool Protocol_text::store(Field *field) bool Protocol_text::store(MYSQL_TIME *tm, int decimals) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DATETIME || - field_types[field_pos] == MYSQL_TYPE_TIMESTAMP); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_DATETIME)); field_pos++; #endif char buff[MAX_DATE_STRING_REP_LENGTH]; @@ -1281,8 +1297,7 @@ bool Protocol_text::store(MYSQL_TIME *tm, int decimals) bool Protocol_text::store_date(MYSQL_TIME *tm) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DATE); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_DATE)); field_pos++; #endif char buff[MAX_DATE_STRING_REP_LENGTH]; @@ -1294,8 +1309,7 @@ bool Protocol_text::store_date(MYSQL_TIME *tm) bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_TIME); + DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_TIME)); field_pos++; #endif char buff[MAX_DATE_STRING_REP_LENGTH]; @@ -1315,11 +1329,10 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals) bool Protocol_text::send_out_parameters(List<Item_param> *sp_params) { - DBUG_ASSERT(sp_params->elements == - thd->lex->prepared_stmt_params.elements); + DBUG_ASSERT(sp_params->elements == thd->lex->prepared_stmt.param_count()); List_iterator_fast<Item_param> item_param_it(*sp_params); - List_iterator_fast<Item> param_it(thd->lex->prepared_stmt_params); + List_iterator_fast<Item> param_it(thd->lex->prepared_stmt.params()); while (true) { @@ -1453,13 +1466,11 @@ bool Protocol_binary::store_longlong(longlong from, bool unsigned_flag) bool Protocol_binary::store_decimal(const my_decimal *d) { #ifndef DBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL); + DBUG_ASSERT(0); // This method is not used yet field_pos++; #endif - char buff[DECIMAL_MAX_STR_LENGTH]; - String str(buff, sizeof(buff), &my_charset_bin); - (void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str); + StringBuffer<DECIMAL_MAX_STR_LENGTH> str; + (void) d->to_string(&str); return store(str.ptr(), str.length(), str.charset()); } |