diff options
author | Alexander Barkov <bar@mariadb.com> | 2019-02-21 21:44:44 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.com> | 2019-02-25 12:29:42 +0400 |
commit | b25ad1bc47d2db600ab241d889f9f8f9e775b99d (patch) | |
tree | 52de378bed623d6f90cffb3d5d011ba6ee2c3668 /sql/protocol.cc | |
parent | 1ab2e7573a378144fc120e97e8218081d17cfa89 (diff) | |
download | mariadb-git-b25ad1bc47d2db600ab241d889f9f8f9e775b99d.tar.gz |
MDEV-18408 Assertion `0' failed in Item::val_native_result / Timestamp_or_zero_datetime_native_null::Timestamp_or_zero_datetime_native_null upon mysqld_list_fields after crash recovery
The problem happened because Item_ident_for_show did not implement val_native().
Solution:
- Removing class Item_ident_for_show
- Implementing a new method Protocol::send_list_fields() instead,
which accepts a List<Field> instead of List<Item> as input.
Now no any Item creation is done during mysqld_list_fields().
Adding helper methods, to reuse the code easier:
- Moved a part of Protocol::send_result_set_metadata(),
responsible for sending an individual field metadata,
into a new method Protocol_text::store_field_metadata().
Reusing it in both send_list_fields() and send_result_set_metadata().
- Adding Protocol_text::store_field_metadata()
- Adding Protocol_text::store_field_metadata_for_list_fields()
Note, this patch also automatically fixed another bug:
MDEV-18685 mysql_list_fields() returns DEFAULT 0 instead of DEFAULT NULL for view columns
The reason for this bug was that Item_ident_for_show::val_xxx() and get_date()
did not check field->is_null() before calling field->val_xxx()/get_date().
Now the default value is correctly sent by Protocol_text::store(Field*).
Diffstat (limited to 'sql/protocol.cc')
-rw-r--r-- | sql/protocol.cc | 233 |
1 files changed, 125 insertions, 108 deletions
diff --git a/sql/protocol.cc b/sql/protocol.cc index 38eb8ac99f7..1d5f77fb995 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -788,6 +788,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; + 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; + 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. @@ -810,10 +877,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) @@ -828,117 +892,17 @@ 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; #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_types[pos]= Send_field::protocol_type_code(item->field_type()); #endif } @@ -967,6 +931,38 @@ 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_types= (enum_field_types*) thd->alloc(sizeof(field_types) * + 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 + field_types[pos]= Send_field::protocol_type_code(fld->type()); +#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"); @@ -976,6 +972,27 @@ bool Protocol::write() #endif /* EMBEDDED_LIBRARY */ +bool Protocol_text::store_field_metadata(THD *thd, Item *item, uint pos) +{ + Send_field field; + item->make_send_field(thd, &field); + field.normalize(); + 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. |