summaryrefslogtreecommitdiff
path: root/libmysqld
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2019-02-21 21:44:44 +0400
committerAlexander Barkov <bar@mariadb.com>2019-02-25 12:29:42 +0400
commitb25ad1bc47d2db600ab241d889f9f8f9e775b99d (patch)
tree52de378bed623d6f90cffb3d5d011ba6ee2c3668 /libmysqld
parent1ab2e7573a378144fc120e97e8218081d17cfa89 (diff)
downloadmariadb-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 'libmysqld')
-rw-r--r--libmysqld/lib_sql.cc232
1 files changed, 139 insertions, 93 deletions
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index 305f6346c9e..30e40246cb0 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -891,6 +891,20 @@ static char *dup_str_aux(MEM_ROOT *root, const char *from, uint length,
}
+static char *dup_str_aux(MEM_ROOT *root, const char *from,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+{
+ return dup_str_aux(root, from, (uint) strlen(from), fromcs, tocs);
+}
+
+
+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);
+}
+
+
/*
creates new result and hooks it to the list
@@ -969,7 +983,7 @@ write_eof_packet(THD *thd, uint server_status, uint statement_warn_count)
1 if memory allocation failed
*/
-int Protocol::begin_dataset()
+bool Protocol::begin_dataset()
{
MYSQL_DATA *data= thd->alloc_new_dataset();
if (!data)
@@ -982,6 +996,19 @@ int Protocol::begin_dataset()
}
+bool Protocol::begin_dataset(THD *thd, uint numfields)
+{
+ if (begin_dataset())
+ return true;
+ MYSQL_DATA *data= thd->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;
+}
+
+
/*
remove last row of current recordset
@@ -1011,110 +1038,80 @@ void Protocol_text::remove_last_row()
}
+bool Protocol_text::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= thd->cur_data;
+ MEM_ROOT *field_alloc= &data->alloc;
+ MYSQL_FIELD *client_field= &thd->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;
+ client_field->flags= (uint16) server_field.flags;
+ client_field->decimals= server_field.decimals;
+
+ client_field->db_length= strlen(client_field->db);
+ client_field->table_length= strlen(client_field->table);
+ client_field->name_length= strlen(client_field->name);
+ client_field->org_name_length= strlen(client_field->org_name);
+ client_field->org_table_length= 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;
+}
+
+
bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
- MYSQL_FIELD *client_field;
- MEM_ROOT *field_alloc;
- CHARSET_INFO *thd_cs= thd->variables.character_set_results;
- CHARSET_INFO *cs= system_charset_info;
- MYSQL_DATA *data;
+ Protocol_text prot(thd);
DBUG_ENTER("send_result_set_metadata");
if (!thd->mysql) // bootstrap file handling
DBUG_RETURN(0);
- if (begin_dataset())
- goto err;
-
- data= thd->cur_data;
- data->fields= field_count= list->elements;
- field_alloc= &data->alloc;
-
- if (!(client_field= data->embedded_info->fields_list=
- (MYSQL_FIELD*)alloc_root(field_alloc, sizeof(MYSQL_FIELD)*field_count)))
+ if (begin_dataset(thd, list->elements))
goto err;
- while ((item= it++))
+ for (uint pos= 0 ; (item= it++); pos++)
{
- Send_field server_field;
- item->make_send_field(thd, &server_field);
-
- /* Keep things compatible for old clients */
- if (server_field.type == MYSQL_TYPE_VARCHAR)
- server_field.type= MYSQL_TYPE_VAR_STRING;
-
- client_field->db= dup_str_aux(field_alloc, server_field.db_name,
- strlen(server_field.db_name), cs, thd_cs);
- client_field->table= dup_str_aux(field_alloc, server_field.table_name,
- strlen(server_field.table_name), cs, thd_cs);
- client_field->name= dup_str_aux(field_alloc, server_field.col_name.str,
- server_field.col_name.length, cs, thd_cs);
- client_field->org_table= dup_str_aux(field_alloc, server_field.org_table_name,
- strlen(server_field.org_table_name), cs, thd_cs);
- client_field->org_name= dup_str_aux(field_alloc,
- server_field.org_col_name.str,
- server_field.org_col_name.length,
- cs, thd_cs);
- if (item->charset_for_protocol() == &my_charset_bin || thd_cs == NULL)
- {
- /* No conversion */
- client_field->charsetnr= item->charset_for_protocol()->number;
- client_field->length= server_field.length;
- }
- else
- {
- uint max_char_len;
- /* With conversion */
- client_field->charsetnr= thd_cs->number;
- max_char_len= (server_field.type >= (int) MYSQL_TYPE_TINY_BLOB &&
- server_field.type <= (int) MYSQL_TYPE_BLOB) ?
- server_field.length / item->collation.collation->mbminlen :
- server_field.length / item->collation.collation->mbmaxlen;
- client_field->length= char_to_byte_length_safe(max_char_len,
- thd_cs->mbmaxlen);
- }
- client_field->type= server_field.type;
- client_field->flags= (uint16) server_field.flags;
- client_field->decimals= server_field.decimals;
- if (server_field.type == MYSQL_TYPE_FLOAT ||
- server_field.type == MYSQL_TYPE_DOUBLE)
- set_if_smaller(client_field->decimals, FLOATING_POINT_DECIMALS);
-
- client_field->db_length= strlen(client_field->db);
- client_field->table_length= strlen(client_field->table);
- client_field->name_length= strlen(client_field->name);
- client_field->org_name_length= strlen(client_field->org_name);
- client_field->org_table_length= 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;
-
- if (flags & (int) Protocol::SEND_DEFAULTS)
- {
- char buff[80];
- String tmp(buff, sizeof(buff), default_charset_info), *res;
-
- if (!(res=item->val_str(&tmp)))
- {
- client_field->def_length= 0;
- client_field->def= strmake_root(field_alloc, "",0);
- }
- else
- {
- client_field->def_length= res->length();
- client_field->def= strmake_root(field_alloc, res->ptr(),
- client_field->def_length);
- }
- }
- else
- client_field->def=0;
- client_field->max_length= 0;
- ++client_field;
+ if (prot.store_field_metadata(thd, item, pos))
+ goto err;
}
if (flags & SEND_EOF)
@@ -1127,6 +1124,55 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
DBUG_RETURN(1); /* purecov: inspected */
}
+
+static void
+list_fields_send_default(THD *thd, Protocol *p, Field *fld, uint pos)
+{
+ char buff[80];
+ String tmp(buff, sizeof(buff), default_charset_info), *res;
+ MYSQL_FIELD *client_field= &thd->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(&thd->cur_data->alloc, "", 0);
+ }
+ else
+ {
+ client_field->def_length= res->length();
+ client_field->def= strmake_root(&thd->cur_data->alloc, res->ptr(),
+ client_field->def_length);
+ }
+}
+
+
+bool Protocol::send_list_fields(List<Field> *list, const TABLE_LIST *table_list)
+{
+ 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);
+
+ if (begin_dataset(thd, list->elements))
+ goto err;
+
+ 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);
+}
+
+
bool Protocol::write()
{
if (!thd->mysql) // bootstrap file handling