Commit beb9a545 authored by Oleg Smirnov's avatar Oleg Smirnov

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.
parent 9206c1ea
......@@ -626,3 +626,168 @@ DTD_IDENTIFIER ROW
ROUTINE_TYPE PROCEDURE
-------- --------
DROP PROCEDURE p1;
#
# MDEV-20609 Full table scan in INFORMATION_SCHEMA.PARAMETERS/ROUTINES
#
DROP DATABASE IF EXISTS i_s_parameters_test;
CREATE DATABASE i_s_parameters_test;
USE i_s_parameters_test;
CREATE FUNCTION test_func5 (s CHAR(20)) RETURNS VARCHAR(30)
RETURN CONCAT('XYZ, ' ,s);
#
# We cannot use the index due to CONCAT()
FLUSH STATUS;
SELECT * FROM INFORMATION_SCHEMA.PARAMETERS
WHERE CONCAT(SPECIFIC_SCHEMA) = 'i_s_parameters_test'
AND SPECIFIC_NAME = 'test_func5';
SPECIFIC_CATALOG def
SPECIFIC_SCHEMA i_s_parameters_test
SPECIFIC_NAME test_func5
ORDINAL_POSITION 0
PARAMETER_MODE NULL
PARAMETER_NAME NULL
DATA_TYPE varchar
CHARACTER_MAXIMUM_LENGTH 30
CHARACTER_OCTET_LENGTH 30
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER varchar(30)
ROUTINE_TYPE FUNCTION
SPECIFIC_CATALOG def
SPECIFIC_SCHEMA i_s_parameters_test
SPECIFIC_NAME test_func5
ORDINAL_POSITION 1
PARAMETER_MODE IN
PARAMETER_NAME s
DATA_TYPE char
CHARACTER_MAXIMUM_LENGTH 20
CHARACTER_OCTET_LENGTH 20
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER char(20)
ROUTINE_TYPE FUNCTION
SHOW STATUS LIKE 'handler_read%next';
Variable_name Value
Handler_read_next 54
Handler_read_rnd_next 97
#
# Now the index must be used
FLUSH STATUS;
SELECT * FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_SCHEMA = 'i_s_parameters_test'
AND SPECIFIC_NAME = 'test_func5';
SPECIFIC_CATALOG def
SPECIFIC_SCHEMA i_s_parameters_test
SPECIFIC_NAME test_func5
ORDINAL_POSITION 0
PARAMETER_MODE NULL
PARAMETER_NAME NULL
DATA_TYPE varchar
CHARACTER_MAXIMUM_LENGTH 30
CHARACTER_OCTET_LENGTH 30
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER varchar(30)
ROUTINE_TYPE FUNCTION
SPECIFIC_CATALOG def
SPECIFIC_SCHEMA i_s_parameters_test
SPECIFIC_NAME test_func5
ORDINAL_POSITION 1
PARAMETER_MODE IN
PARAMETER_NAME s
DATA_TYPE char
CHARACTER_MAXIMUM_LENGTH 20
CHARACTER_OCTET_LENGTH 20
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER char(20)
ROUTINE_TYPE FUNCTION
SHOW STATUS LIKE 'handler_read%next';
Variable_name Value
Handler_read_next 1
Handler_read_rnd_next 3
#
# Using the first key part of the index
FLUSH STATUS;
SELECT * FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_SCHEMA = 'i_s_parameters_test';
SPECIFIC_CATALOG def
SPECIFIC_SCHEMA i_s_parameters_test
SPECIFIC_NAME test_func5
ORDINAL_POSITION 0
PARAMETER_MODE NULL
PARAMETER_NAME NULL
DATA_TYPE varchar
CHARACTER_MAXIMUM_LENGTH 30
CHARACTER_OCTET_LENGTH 30
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER varchar(30)
ROUTINE_TYPE FUNCTION
SPECIFIC_CATALOG def
SPECIFIC_SCHEMA i_s_parameters_test
SPECIFIC_NAME test_func5
ORDINAL_POSITION 1
PARAMETER_MODE IN
PARAMETER_NAME s
DATA_TYPE char
CHARACTER_MAXIMUM_LENGTH 20
CHARACTER_OCTET_LENGTH 20
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER char(20)
ROUTINE_TYPE FUNCTION
SHOW STATUS LIKE 'handler_read%next';
Variable_name Value
Handler_read_next 1
Handler_read_rnd_next 3
#
# Test non-latin letters in procedure name
SET NAMES koi8r;
CREATE PROCEDURE `процедурка`(a INT) SELECT a;
#
# The index must be used
FLUSH STATUS;
SELECT * FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_SCHEMA = 'i_s_parameters_test'
AND SPECIFIC_NAME = 'процедурка';
SPECIFIC_CATALOG def
SPECIFIC_SCHEMA i_s_parameters_test
SPECIFIC_NAME процедурка
ORDINAL_POSITION 1
PARAMETER_MODE IN
PARAMETER_NAME a
DATA_TYPE int
CHARACTER_MAXIMUM_LENGTH NULL
CHARACTER_OCTET_LENGTH NULL
NUMERIC_PRECISION 10
NUMERIC_SCALE 0
DATETIME_PRECISION NULL
CHARACTER_SET_NAME NULL
COLLATION_NAME NULL
DTD_IDENTIFIER int(11)
ROUTINE_TYPE PROCEDURE
SHOW STATUS LIKE 'handler_read%next';
Variable_name Value
Handler_read_next 1
Handler_read_rnd_next 2
DROP DATABASE i_s_parameters_test;
USE test;
......@@ -276,3 +276,56 @@ DELIMITER ;$$
SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME = 'p1';
--horizontal_results
DROP PROCEDURE p1;
--echo #
--echo # MDEV-20609 Full table scan in INFORMATION_SCHEMA.PARAMETERS/ROUTINES
--echo #
--disable_warnings
DROP DATABASE IF EXISTS i_s_parameters_test;
--enable_warnings
CREATE DATABASE i_s_parameters_test;
USE i_s_parameters_test;
CREATE FUNCTION test_func5 (s CHAR(20)) RETURNS VARCHAR(30)
RETURN CONCAT('XYZ, ' ,s);
--echo #
--echo # We cannot use the index due to CONCAT()
FLUSH STATUS;
query_vertical SELECT * FROM INFORMATION_SCHEMA.PARAMETERS
WHERE CONCAT(SPECIFIC_SCHEMA) = 'i_s_parameters_test'
AND SPECIFIC_NAME = 'test_func5';
SHOW STATUS LIKE 'handler_read%next';
--echo #
--echo # Now the index must be used
FLUSH STATUS;
query_vertical SELECT * FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_SCHEMA = 'i_s_parameters_test'
AND SPECIFIC_NAME = 'test_func5';
SHOW STATUS LIKE 'handler_read%next';
--echo #
--echo # Using the first key part of the index
FLUSH STATUS;
query_vertical SELECT * FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_SCHEMA = 'i_s_parameters_test';
SHOW STATUS LIKE 'handler_read%next';
--echo #
--echo # Test non-latin letters in procedure name
SET NAMES koi8r;
CREATE PROCEDURE `процедурка`(a INT) SELECT a;
--echo #
--echo # The index must be used
FLUSH STATUS;
query_vertical SELECT * FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_SCHEMA = 'i_s_parameters_test'
AND SPECIFIC_NAME = 'процедурка';
SHOW STATUS LIKE 'handler_read%next';
# Cleanup
DROP DATABASE i_s_parameters_test;
USE test;
......@@ -851,3 +851,257 @@ WHERE ROUTINE_SCHEMA = 'i_s_routines_test' AND ROUTINE_NAME = 'test_func5';
SPECIFIC_NAME ROUTINE_CATALOG ROUTINE_SCHEMA ROUTINE_NAME ROUTINE_TYPE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_BODY ROUTINE_DEFINITION EXTERNAL_NAME EXTERNAL_LANGUAGE PARAMETER_STYLE IS_DETERMINISTIC SQL_DATA_ACCESS SQL_PATH SECURITY_TYPE CREATED LAST_ALTERED SQL_MODE ROUTINE_COMMENT DEFINER CHARACTER_SET_CLIENT COLLATION_CONNECTION DATABASE_COLLATION
test_func5 def i_s_routines_test test_func5 FUNCTION varchar 30 90 NULL NULL NULL utf8mb3 utf8mb3_general_ci varchar(30) SQL RETURN CONCAT('XYZ, ' ,s) NULL NULL SQL NO CONTAINS SQL NULL DEFINER <created> <modified> root@localhost latin1 latin1_swedish_ci utf8mb3_general_ci
DROP DATABASE i_s_routines_test;
#
# MDEV-20609 Full table scan in INFORMATION_SCHEMA.PARAMETERS/ROUTINES
#
DROP DATABASE IF EXISTS i_s_routines_test;
CREATE DATABASE i_s_routines_test;
USE i_s_routines_test;
CREATE FUNCTION test_func5 (s CHAR(20)) RETURNS VARCHAR(30)
RETURN CONCAT('XYZ, ' ,s);
#
# We cannot use the index due to CONCAT()
FLUSH STATUS;
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE CONCAT(ROUTINE_SCHEMA) = 'i_s_routines_test'
AND ROUTINE_NAME = 'test_func5';
SPECIFIC_NAME test_func5
ROUTINE_CATALOG def
ROUTINE_SCHEMA i_s_routines_test
ROUTINE_NAME test_func5
ROUTINE_TYPE FUNCTION
DATA_TYPE varchar
CHARACTER_MAXIMUM_LENGTH 30
CHARACTER_OCTET_LENGTH 30
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER varchar(30)
ROUTINE_BODY SQL
ROUTINE_DEFINITION RETURN CONCAT('XYZ, ' ,s)
EXTERNAL_NAME NULL
EXTERNAL_LANGUAGE NULL
PARAMETER_STYLE SQL
IS_DETERMINISTIC NO
SQL_DATA_ACCESS CONTAINS SQL
SQL_PATH NULL
SECURITY_TYPE DEFINER
CREATED <created>
LAST_ALTERED <modified>
SQL_MODE
ROUTINE_COMMENT
DEFINER root@localhost
CHARACTER_SET_CLIENT latin1
COLLATION_CONNECTION latin1_swedish_ci
DATABASE_COLLATION latin1_swedish_ci
SHOW STATUS LIKE 'handler_read%next';
Variable_name Value
Handler_read_next 54
Handler_read_rnd_next 55
#
# Now the index must be used
FLUSH STATUS;
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_SCHEMA = 'i_s_routines_test'
AND ROUTINE_NAME = 'test_func5';
SPECIFIC_NAME test_func5
ROUTINE_CATALOG def
ROUTINE_SCHEMA i_s_routines_test
ROUTINE_NAME test_func5
ROUTINE_TYPE FUNCTION
DATA_TYPE varchar
CHARACTER_MAXIMUM_LENGTH 30
CHARACTER_OCTET_LENGTH 30
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER varchar(30)
ROUTINE_BODY SQL
ROUTINE_DEFINITION RETURN CONCAT('XYZ, ' ,s)
EXTERNAL_NAME NULL
EXTERNAL_LANGUAGE NULL
PARAMETER_STYLE SQL
IS_DETERMINISTIC NO
SQL_DATA_ACCESS CONTAINS SQL
SQL_PATH NULL
SECURITY_TYPE DEFINER
CREATED <created>
LAST_ALTERED <modified>
SQL_MODE
ROUTINE_COMMENT
DEFINER root@localhost
CHARACTER_SET_CLIENT latin1
COLLATION_CONNECTION latin1_swedish_ci
DATABASE_COLLATION latin1_swedish_ci
SHOW STATUS LIKE 'handler_read%next';
Variable_name Value
Handler_read_next 1
Handler_read_rnd_next 2
#
# Using the first key part of the index
FLUSH STATUS;
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_SCHEMA = 'i_s_routines_test';
SPECIFIC_NAME test_func5
ROUTINE_CATALOG def
ROUTINE_SCHEMA i_s_routines_test
ROUTINE_NAME test_func5
ROUTINE_TYPE FUNCTION
DATA_TYPE varchar
CHARACTER_MAXIMUM_LENGTH 30
CHARACTER_OCTET_LENGTH 30
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME latin1
COLLATION_NAME latin1_swedish_ci
DTD_IDENTIFIER varchar(30)
ROUTINE_BODY SQL
ROUTINE_DEFINITION RETURN CONCAT('XYZ, ' ,s)
EXTERNAL_NAME NULL
EXTERNAL_LANGUAGE NULL
PARAMETER_STYLE SQL
IS_DETERMINISTIC NO
SQL_DATA_ACCESS CONTAINS SQL
SQL_PATH NULL
SECURITY_TYPE DEFINER
CREATED <created>
LAST_ALTERED <modified>
SQL_MODE
ROUTINE_COMMENT
DEFINER root@localhost
CHARACTER_SET_CLIENT latin1
COLLATION_CONNECTION latin1_swedish_ci
DATABASE_COLLATION latin1_swedish_ci
SHOW STATUS LIKE 'handler_read%next';
Variable_name Value
Handler_read_next 1
Handler_read_rnd_next 2
#
# Test non-latin letters in procedure name
SET NAMES koi8r;
CREATE PROCEDURE `процедурка`(a INT) SELECT a;
#
# The index must be used
FLUSH STATUS;
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_SCHEMA = 'i_s_routines_test'
AND ROUTINE_NAME = 'процедурка';
SPECIFIC_NAME процедурка
ROUTINE_CATALOG def
ROUTINE_SCHEMA i_s_routines_test
ROUTINE_NAME процедурка
ROUTINE_TYPE PROCEDURE
DATA_TYPE
CHARACTER_MAXIMUM_LENGTH NULL
CHARACTER_OCTET_LENGTH NULL
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME NULL
COLLATION_NAME NULL
DTD_IDENTIFIER NULL
ROUTINE_BODY SQL
ROUTINE_DEFINITION SELECT a
EXTERNAL_NAME NULL
EXTERNAL_LANGUAGE NULL
PARAMETER_STYLE SQL
IS_DETERMINISTIC NO
SQL_DATA_ACCESS CONTAINS SQL
SQL_PATH NULL
SECURITY_TYPE DEFINER
CREATED <created>
LAST_ALTERED <modified>
SQL_MODE
ROUTINE_COMMENT
DEFINER root@localhost
CHARACTER_SET_CLIENT koi8r
COLLATION_CONNECTION koi8r_general_ci
DATABASE_COLLATION latin1_swedish_ci
SHOW STATUS LIKE 'handler_read%next';
Variable_name Value
Handler_read_next 1
Handler_read_rnd_next 2
#
# Test SHOW PROCEDURE STATUS. It's impossible to use the index here
# so don't check Handler_read counters, only the results correctness
SHOW FUNCTION STATUS LIKE 'test_func5';
Db i_s_routines_test
Name test_func5
Type FUNCTION
Definer root@localhost
Modified <modified>
Created <created>
Security_type DEFINER
Comment
character_set_client latin1
collation_connection latin1_swedish_ci
Database Collation latin1_swedish_ci
SHOW FUNCTION STATUS LIKE 'test_%';
Db i_s_routines_test
Name test_func5
Type FUNCTION
Definer root@localhost
Modified <modified>
Created <created>
Security_type DEFINER
Comment
character_set_client latin1
collation_connection latin1_swedish_ci
Database Collation latin1_swedish_ci
SHOW FUNCTION STATUS LIKE '%func%';
Db i_s_routines_test
Name test_func5
Type FUNCTION
Definer root@localhost
Modified <modified>
Created <created>
Security_type DEFINER
Comment
character_set_client latin1
collation_connection latin1_swedish_ci
Database Collation latin1_swedish_ci
SHOW FUNCTION STATUS LIKE 'test';
SHOW PROCEDURE STATUS LIKE 'процедурка';
Db i_s_routines_test
Name процедурка
Type PROCEDURE
Definer root@localhost
Modified <modified>
Created <created>
Security_type DEFINER
Comment
character_set_client koi8r
collation_connection koi8r_general_ci
Database Collation latin1_swedish_ci
SHOW PROCEDURE STATUS LIKE '%оцедурка';
Db i_s_routines_test
Name процедурка
Type PROCEDURE
Definer root@localhost
Modified <modified>
Created <created>
Security_type DEFINER
Comment
character_set_client koi8r
collation_connection koi8r_general_ci
Database Collation latin1_swedish_ci
SHOW PROCEDURE STATUS LIKE '%оцедур%';
Db i_s_routines_test
Name процедурка
Type PROCEDURE
Definer root@localhost
Modified <modified>
Created <created>
Security_type DEFINER
Comment
character_set_client koi8r
collation_connection koi8r_general_ci
Database Collation latin1_swedish_ci
SHOW PROCEDURE STATUS LIKE 'такой_нет';
DROP DATABASE i_s_routines_test;
USE test;
......@@ -249,3 +249,79 @@ WHERE ROUTINE_SCHEMA = 'i_s_routines_test' AND ROUTINE_NAME = 'test_func5';
# final clean up
DROP DATABASE i_s_routines_test;
--echo #
--echo # MDEV-20609 Full table scan in INFORMATION_SCHEMA.PARAMETERS/ROUTINES
--echo #
--disable_warnings
DROP DATABASE IF EXISTS i_s_routines_test;
--enable_warnings
CREATE DATABASE i_s_routines_test;
USE i_s_routines_test;
CREATE FUNCTION test_func5 (s CHAR(20)) RETURNS VARCHAR(30)
RETURN CONCAT('XYZ, ' ,s);
--echo #
--echo # We cannot use the index due to CONCAT()
FLUSH STATUS;
--replace_column 24 <created> 25 <modified>
query_vertical SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE CONCAT(ROUTINE_SCHEMA) = 'i_s_routines_test'
AND ROUTINE_NAME = 'test_func5';
SHOW STATUS LIKE 'handler_read%next';
--echo #
--echo # Now the index must be used
FLUSH STATUS;
--replace_column 24 <created> 25 <modified>
query_vertical SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_SCHEMA = 'i_s_routines_test'
AND ROUTINE_NAME = 'test_func5';
SHOW STATUS LIKE 'handler_read%next';
--echo #
--echo # Using the first key part of the index
FLUSH STATUS;
--replace_column 24 <created> 25 <modified>
query_vertical SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_SCHEMA = 'i_s_routines_test';
SHOW STATUS LIKE 'handler_read%next';
--echo #
--echo # Test non-latin letters in procedure name
SET NAMES koi8r;
CREATE PROCEDURE `процедурка`(a INT) SELECT a;
--echo #
--echo # The index must be used
FLUSH STATUS;
--replace_column 24 <created> 25 <modified>
query_vertical SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_SCHEMA = 'i_s_routines_test'
AND ROUTINE_NAME = 'процедурка';
SHOW STATUS LIKE 'handler_read%next';
--echo #
--echo # Test SHOW PROCEDURE STATUS. It's impossible to use the index here
--echo # so don't check Handler_read counters, only the results correctness
--replace_column 5 <modified> 6 <created>
query_vertical SHOW FUNCTION STATUS LIKE 'test_func5';
--replace_column 5 <modified> 6 <created>
query_vertical SHOW FUNCTION STATUS LIKE 'test_%';
--replace_column 5 <modified> 6 <created>
query_vertical SHOW FUNCTION STATUS LIKE '%func%';
--replace_column 5 <modified> 6 <created>
query_vertical SHOW FUNCTION STATUS LIKE 'test';
--replace_column 5 <modified> 6 <created>
query_vertical SHOW PROCEDURE STATUS LIKE 'процедурка';
--replace_column 5 <modified> 6 <created>
query_vertical SHOW PROCEDURE STATUS LIKE '%оцедурка';
--replace_column 5 <modified> 6 <created>
query_vertical SHOW PROCEDURE STATUS LIKE '%оцедур%';
--replace_column 5 <modified> 6 <created>
query_vertical SHOW PROCEDURE STATUS LIKE 'такой_нет';
# Cleanup
DROP DATABASE i_s_routines_test;
USE test;
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment