From e2664ee8362a94335df06edfc86936ff263d6dc0 Mon Sep 17 00:00:00 2001 From: Monty Date: Mon, 26 Mar 2018 17:22:41 +0300 Subject: [PATCH] MDEV-14275 Improving memory utilization for information schema SELECT queries on information_schema table will now not store data in the temporary table for columns that are not used in the query. This can drastically reduce memory when there are many rows involved in a query, like using SELECT table_name FROM information_schema.table on a system with many tables. The difference for the above query is about 14K per existing table. The code works this way: - Create a bitmap for all information_schema fields used in the query - For BLOB and VARCHAR fields that are not use, create a Field_null field. --- sql/sql_select.cc | 12 +++-- sql/sql_show.cc | 95 ++++++++++++++++++++++++++++++--- storage/innobase/handler/i_s.cc | 3 +- 3 files changed, 97 insertions(+), 13 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0b706f78452..4390ea4d713 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16685,11 +16685,15 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table) { Field *field; if (max_length > MAX_FIELD_VARCHARLENGTH) - field= new Field_blob(max_length, maybe_null, &name, - collation.collation); + field= new (thd->mem_root) Field_blob(max_length, maybe_null, &name, + collation.collation); + else if (max_length > 0) + field= new (thd->mem_root) Field_varstring(max_length, maybe_null, &name, + table->s, + collation.collation); else - field= new Field_varstring(max_length, maybe_null, &name, - table->s, collation.collation); + field= new Field_null((uchar*) 0, 0, Field::NONE, &name, + collation.collation); if (field) field->init(table); return field; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a64b8cbf18a..895a974eb02 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -8071,6 +8071,50 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx) return &schema_tables[schema_table_idx]; } +static void +mark_all_fields_used_in_query(THD *thd, + ST_FIELD_INFO *schema_fields, + MY_BITMAP *bitmap, + Item *all_items) +{ + Item *item; + DBUG_ENTER("mark_all_fields_used_in_query"); + + /* If not SELECT command, return all columns */ + if (thd->lex->sql_command != SQLCOM_SELECT && + thd->lex->sql_command != SQLCOM_SET_OPTION) + { + bitmap_set_all(bitmap); + DBUG_VOID_RETURN; + } + + for (item= all_items ; item ; item= item->next) + { + if (item->type() == Item::FIELD_ITEM) + { + ST_FIELD_INFO *fields= schema_fields; + uint count; + Item_field *item_field= (Item_field*) item; + + /* item_field can be '*' as this function is called before fix_fields */ + if (item_field->field_name.str == star_clex_str.str) + { + bitmap_set_all(bitmap); + break; + } + for (count=0; fields->field_name; fields++, count++) + { + if (!my_strcasecmp(system_charset_info, fields->field_name, + item_field->field_name.str)) + { + bitmap_set_bit(bitmap, count); + break; + } + } + } + } + DBUG_VOID_RETURN; +} /** Create information_schema table using schema_table data. @@ -8095,17 +8139,34 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx) TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) { - int field_count= 0; - Item *item; + uint field_count; + Item *item, *all_items; TABLE *table; List field_list; ST_SCHEMA_TABLE *schema_table= table_list->schema_table; ST_FIELD_INFO *fields_info= schema_table->fields_info; + ST_FIELD_INFO *fields; CHARSET_INFO *cs= system_charset_info; MEM_ROOT *mem_root= thd->mem_root; + MY_BITMAP bitmap; + my_bitmap_map *buf; DBUG_ENTER("create_schema_table"); - for (; fields_info->field_name; fields_info++) + for (field_count= 0, fields= fields_info; fields->field_name; fields++) + field_count++; + if (!(buf= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)))) + DBUG_RETURN(NULL); + my_bitmap_init(&bitmap, buf, field_count, 0); + + if (!thd->stmt_arena->is_conventional() && + thd->mem_root != thd->stmt_arena->mem_root) + all_items= thd->stmt_arena->free_list; + else + all_items= thd->free_list; + + mark_all_fields_used_in_query(thd, fields_info, &bitmap, all_items); + + for (field_count=0; fields_info->field_name; fields_info++) { size_t field_name_length= strlen(fields_info->field_name); switch (fields_info->field_type) { @@ -8182,19 +8243,36 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: - if (!(item= new (mem_root) - Item_blob(thd, fields_info->field_name, - fields_info->field_length))) + if (bitmap_is_set(&bitmap, field_count)) { - DBUG_RETURN(0); + if (!(item= new (mem_root) + Item_blob(thd, fields_info->field_name, + fields_info->field_length))) + { + DBUG_RETURN(0); + } + } + else + { + if (!(item= new (mem_root) + Item_empty_string(thd, "", 0, cs))) + { + DBUG_RETURN(0); + } + item->set_name(thd, fields_info->field_name, + field_name_length, cs); } break; default: + { + bool show_field; /* Don't let unimplemented types pass through. Could be a grave error. */ DBUG_ASSERT(fields_info->field_type == MYSQL_TYPE_STRING); + show_field= bitmap_is_set(&bitmap, field_count); if (!(item= new (mem_root) - Item_empty_string(thd, "", fields_info->field_length, cs))) + Item_empty_string(thd, "", + show_field ? fields_info->field_length : 0, cs))) { DBUG_RETURN(0); } @@ -8202,6 +8280,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) field_name_length, cs); break; } + } field_list.push_back(item, thd->mem_root); item->maybe_null= (fields_info->field_flags & MY_I_S_MAYBE_NULL); field_count++; diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index e9b33e556c8..f03bd054d09 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -294,7 +294,8 @@ field_store_index_name( int ret; ut_ad(index_name != NULL); - ut_ad(field->real_type() == MYSQL_TYPE_VARCHAR); + ut_ad(field->real_type() == MYSQL_TYPE_VARCHAR || + field->real_type() == MYSQL_TYPE_NULL); /* Since TEMP_INDEX_PREFIX is not a valid UTF8, we need to convert it to something else. */