mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
MDEV-20609 Full table scan in INFORMATION_SCHEMA.PARAMETERS/ROUTINES
Queries to INFORMATION_SCHEMA.PARAMETERS and ROUTINES tables are always performed using full index scan of the mysql.proc primary key on fields (`db`,`name`,`type`). This can be done in a much more effective way if `db` and `name` field values can be derived from the WHERE statement, like here: SELECT * FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_SCHEMA = 'test' AND SPECIFIC_NAME = 'my_func' or here: SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA='test' AND ROUTINE_NAME='my_func'. In such cases index range scan may be employed instead of full index scan. This commit makes the server retrieve lookup field values from the SQL statement and perform index range scan instead of full index scan if possible.
This commit is contained in:
128
sql/sql_show.cc
128
sql/sql_show.cc
@@ -65,7 +65,7 @@
|
||||
#include "transaction.h"
|
||||
#include "opt_trace.h"
|
||||
#include "my_cpu.h"
|
||||
|
||||
#include "key.h"
|
||||
|
||||
#include "lex_symbol.h"
|
||||
#define KEYWORD_SIZE 64
|
||||
@@ -150,7 +150,8 @@ static int show_create_sequence(THD *thd, TABLE_LIST *table_list,
|
||||
|
||||
static const LEX_CSTRING *view_algorithm(TABLE_LIST *table);
|
||||
|
||||
bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *);
|
||||
bool get_lookup_field_values(THD *, COND *, bool, TABLE_LIST *,
|
||||
LOOKUP_FIELD_VALUES *);
|
||||
|
||||
/**
|
||||
Try to lock a mutex, but give up after a short while to not cause deadlocks
|
||||
@@ -340,7 +341,7 @@ int fill_all_plugins(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||
TABLE *table= tables->table;
|
||||
LOOKUP_FIELD_VALUES lookup;
|
||||
|
||||
if (get_lookup_field_values(thd, cond, tables, &lookup))
|
||||
if (get_lookup_field_values(thd, cond, true, tables, &lookup))
|
||||
DBUG_RETURN(0);
|
||||
|
||||
if (lookup.db_value.str && !lookup.db_value.str[0])
|
||||
@@ -4257,7 +4258,8 @@ COND *make_cond_for_info_schema(THD *thd, COND *cond, TABLE_LIST *table)
|
||||
1 error, there can be no matching records for the condition
|
||||
*/
|
||||
|
||||
bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
|
||||
bool get_lookup_field_values(THD *thd, COND *cond, bool fix_table_name_case,
|
||||
TABLE_LIST *tables,
|
||||
LOOKUP_FIELD_VALUES *lookup_field_values)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
@@ -4292,7 +4294,7 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
|
||||
lex->first_select_lex()->db.length);
|
||||
if (wild)
|
||||
{
|
||||
thd->make_lex_string(&lookup_field_values->table_value,
|
||||
thd->make_lex_string(&lookup_field_values->table_value,
|
||||
wild->ptr(), wild->length());
|
||||
lookup_field_values->wild_table_value= 1;
|
||||
}
|
||||
@@ -4315,7 +4317,8 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
|
||||
if (lookup_field_values->db_value.str && lookup_field_values->db_value.str[0])
|
||||
my_casedn_str(system_charset_info,
|
||||
(char*) lookup_field_values->db_value.str);
|
||||
if (lookup_field_values->table_value.str &&
|
||||
if (fix_table_name_case &&
|
||||
lookup_field_values->table_value.str &&
|
||||
lookup_field_values->table_value.str[0])
|
||||
my_casedn_str(system_charset_info,
|
||||
(char*) lookup_field_values->table_value.str);
|
||||
@@ -5470,7 +5473,7 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||
#endif
|
||||
DBUG_ENTER("fill_schema_shemata");
|
||||
|
||||
if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals))
|
||||
if (get_lookup_field_values(thd, cond, true, tables, &lookup_field_vals))
|
||||
DBUG_RETURN(0);
|
||||
DBUG_PRINT("INDEX VALUES",("db_name: %s table_name: %s",
|
||||
lookup_field_vals.db_value.str,
|
||||
@@ -6448,19 +6451,18 @@ static inline void copy_field_as_string(Field *to_field, Field *from_field)
|
||||
@param[in] thd thread handler
|
||||
@param[in] table I_S table
|
||||
@param[in] proc_table 'mysql.proc' table
|
||||
@param[in] wild wild string, not used for now,
|
||||
will be useful
|
||||
if we add 'SHOW PARAMETERs'
|
||||
@param[in] full_access if 1 user has privileges on the routine
|
||||
@param[in] sp_user user in 'user@host' format
|
||||
|
||||
@return Operation status
|
||||
@retval 0 ok
|
||||
@retval 1 error
|
||||
@retval != 0 error / HA_ERR_END_OF_FILE
|
||||
(if there are no more
|
||||
matching records)
|
||||
*/
|
||||
|
||||
bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
|
||||
const char *wild, bool full_access,
|
||||
int store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
|
||||
LOOKUP_FIELD_VALUES *lookup, bool full_access,
|
||||
const char *sp_user)
|
||||
{
|
||||
TABLE_SHARE share;
|
||||
@@ -6482,6 +6484,15 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
|
||||
|
||||
proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db);
|
||||
proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name);
|
||||
|
||||
if (lookup->db_value.str)
|
||||
{
|
||||
if (cmp(lookup->db_value, db))
|
||||
DBUG_RETURN(HA_ERR_END_OF_FILE);
|
||||
if (lookup->table_value.str && cmp(lookup->table_value, name))
|
||||
DBUG_RETURN(HA_ERR_END_OF_FILE);
|
||||
}
|
||||
|
||||
proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer);
|
||||
sql_mode= (sql_mode_t) proc_table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
|
||||
sph= Sp_handler::handler_mysql_proc((enum_sp_type)
|
||||
@@ -6585,16 +6596,33 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
|
||||
}
|
||||
|
||||
|
||||
bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
|
||||
const char *wild, bool full_access, const char *sp_user)
|
||||
int store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
|
||||
LOOKUP_FIELD_VALUES *lookup, bool full_access,
|
||||
const char *sp_user)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
CHARSET_INFO *cs= system_charset_info;
|
||||
const Sp_handler *sph;
|
||||
LEX_CSTRING db, name, definer, returns= empty_clex_str;
|
||||
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
|
||||
|
||||
proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db);
|
||||
proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name);
|
||||
|
||||
if (lookup->db_value.str)
|
||||
{
|
||||
if (cmp(lookup->db_value, db))
|
||||
return HA_ERR_END_OF_FILE;
|
||||
if (lookup->table_value.str)
|
||||
{
|
||||
CHARSET_INFO *cs= proc_table->field[MYSQL_PROC_FIELD_NAME]->charset();
|
||||
if (my_ci_strnncoll(cs, (const uchar*)lookup->table_value.str,
|
||||
lookup->table_value.length,
|
||||
(const uchar*) name.str, name.length, 0))
|
||||
return HA_ERR_END_OF_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer);
|
||||
sph= Sp_handler::handler_mysql_proc((enum_sp_type)
|
||||
proc_table->field[MYSQL_PROC_MYSQL_TYPE]->
|
||||
@@ -6706,7 +6734,6 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||
{
|
||||
TABLE *proc_table;
|
||||
TABLE_LIST proc_tables;
|
||||
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
|
||||
int res= 0;
|
||||
TABLE *table= tables->table;
|
||||
bool full_access;
|
||||
@@ -6726,6 +6753,13 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||
full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE,
|
||||
1, TRUE);
|
||||
|
||||
LOOKUP_FIELD_VALUES lookup;
|
||||
if (get_lookup_field_values(thd, cond, false, tables, &lookup))
|
||||
{
|
||||
// There can be no matching records for the condition
|
||||
return 0;
|
||||
}
|
||||
|
||||
start_new_trans new_trans(thd);
|
||||
|
||||
if (!(proc_table= open_proc_table_for_read(thd)))
|
||||
@@ -6744,34 +6778,51 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((res= proc_table->file->ha_index_first(proc_table->record[0])))
|
||||
if (lookup.db_value.str)
|
||||
{
|
||||
res= (res == HA_ERR_END_OF_FILE) ? 0 : 1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (schema_table_idx == SCH_PROCEDURES ?
|
||||
store_schema_proc(thd, table, proc_table, wild, full_access, definer) :
|
||||
store_schema_params(thd, table, proc_table, wild, full_access, definer))
|
||||
{
|
||||
res= 1;
|
||||
goto err;
|
||||
}
|
||||
while (!proc_table->file->ha_index_next(proc_table->record[0]))
|
||||
{
|
||||
if (schema_table_idx == SCH_PROCEDURES ?
|
||||
store_schema_proc(thd, table, proc_table, wild, full_access, definer):
|
||||
store_schema_params(thd, table, proc_table, wild, full_access, definer))
|
||||
KEY *keyinfo= proc_table->key_info;
|
||||
uint keylen= keyinfo->key_part[0].length;
|
||||
key_part_map keypart_map= 1;
|
||||
enum ha_rkey_function find_flag= HA_READ_PREFIX;
|
||||
uchar keybuf[NAME_CHAR_LEN * 2 * 4 * 64];
|
||||
proc_table->field[0]->store(lookup.db_value.str, lookup.db_value.length,
|
||||
system_charset_info);
|
||||
if (lookup.table_value.str)
|
||||
{
|
||||
res= 1;
|
||||
goto err;
|
||||
proc_table->field[1]->store(lookup.table_value.str,
|
||||
lookup.table_value.length,
|
||||
system_charset_info);
|
||||
keylen+= keyinfo->key_part[1].length;
|
||||
keypart_map= 3;
|
||||
find_flag= HA_READ_KEY_EXACT;
|
||||
}
|
||||
key_copy(keybuf, proc_table->record[0], keyinfo, keylen, 0);
|
||||
res= proc_table->file->ha_index_read_map(proc_table->record[0], keybuf,
|
||||
keypart_map, find_flag);
|
||||
}
|
||||
else
|
||||
res= proc_table->file->ha_index_first(proc_table->record[0]);
|
||||
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
res= schema_table_idx == SCH_PROCEDURES ?
|
||||
store_schema_proc(thd, table, proc_table, &lookup, full_access,definer) :
|
||||
store_schema_params(thd, table, proc_table, &lookup, full_access, definer);
|
||||
while (!res && !proc_table->file->ha_index_next(proc_table->record[0]))
|
||||
{
|
||||
res= schema_table_idx == SCH_PROCEDURES ?
|
||||
store_schema_proc(thd, table, proc_table, &lookup, full_access, definer) :
|
||||
store_schema_params(thd, table, proc_table, &lookup, full_access, definer);
|
||||
}
|
||||
|
||||
err:
|
||||
if (proc_table->file->inited)
|
||||
(void) proc_table->file->ha_index_end();
|
||||
|
||||
if (res == HA_ERR_END_OF_FILE || res == HA_ERR_KEY_NOT_FOUND)
|
||||
res= 0;
|
||||
|
||||
thd->commit_whole_transaction_and_close_tables();
|
||||
new_trans.restore_old_transaction();
|
||||
|
||||
@@ -8669,7 +8720,8 @@ static bool optimize_for_get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (get_lookup_field_values(thd, cond, tables, &plan->lookup_field_vals))
|
||||
if (get_lookup_field_values(thd, cond, true, tables,
|
||||
&plan->lookup_field_vals))
|
||||
{
|
||||
plan->no_rows= true;
|
||||
goto end;
|
||||
@@ -9861,7 +9913,7 @@ ST_SCHEMA_TABLE schema_tables[]=
|
||||
{"OPTIMIZER_TRACE", Show::optimizer_trace_info, 0,
|
||||
fill_optimizer_trace_info, NULL, NULL, -1, -1, false, 0},
|
||||
{"PARAMETERS", Show::parameters_fields_info, 0,
|
||||
fill_schema_proc, 0, 0, -1, -1, 0, 0},
|
||||
fill_schema_proc, 0, 0, 1, 2, 0, 0},
|
||||
{"PARTITIONS", Show::partitions_fields_info, 0,
|
||||
get_all_tables, 0, get_schema_partitions_record, 1, 2, 0,
|
||||
OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
|
||||
@@ -9876,7 +9928,7 @@ ST_SCHEMA_TABLE schema_tables[]=
|
||||
0, get_all_tables, 0, get_referential_constraints_record,
|
||||
1, 9, 0, OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
|
||||
{"ROUTINES", Show::proc_fields_info, 0,
|
||||
fill_schema_proc, make_proc_old_format, 0, -1, -1, 0, 0},
|
||||
fill_schema_proc, make_proc_old_format, 0, 2, 3, 0, 0},
|
||||
{"SCHEMATA", Show::schema_fields_info, 0,
|
||||
fill_schema_schemata, make_schemata_old_format, 0, 1, -1, 0, 0},
|
||||
{"SCHEMA_PRIVILEGES", Show::schema_privileges_fields_info, 0,
|
||||
|
Reference in New Issue
Block a user