Skip to content

Commit

Permalink
Add variable to disable full table/index scans
Browse files Browse the repository at this point in the history
Summary:
There are cases where it is always unacceptable for a client to be doing full table scans. To prevent this from happening, add a new variable optimizer_full_scan. When it is turned off, it will return a new error code ER_FULL_SCAN_DISABLED.

For best results, use with optimizer_force_index_for_range. Currently, it does not fallback to alternative plans because we check at the end of query planning.

Reviewed By: hermanlee

Differential Revision: D7528820

fbshipit-source-id: 94fcd07
  • Loading branch information
lth authored and facebook-github-bot committed Apr 18, 2018
1 parent 7eab374 commit b90e801
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 0 deletions.
4 changes: 4 additions & 0 deletions mysql-test/r/mysqld--help-notwin-profiling.result
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,9 @@ The following options may be given as the first argument:
--optimizer-force-index-for-range
If enabled, FORCE INDEX will also try to force a range
plan.
--optimizer-full-scan
Enable full table and index scans.
(Defaults to on; use --skip-optimizer-full-scan to disable.)
--optimizer-low-limit-heuristic
Enable low limit heuristic.
(Defaults to on; use --skip-optimizer-low-limit-heuristic to disable.)
Expand Down Expand Up @@ -2037,6 +2040,7 @@ old-alter-table FALSE
old-passwords 0
old-style-user-limits FALSE
optimizer-force-index-for-range FALSE
optimizer-full-scan TRUE
optimizer-low-limit-heuristic TRUE
optimizer-prune-level 1
optimizer-search-depth 62
Expand Down
4 changes: 4 additions & 0 deletions mysql-test/r/mysqld--help-notwin.result
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,9 @@ The following options may be given as the first argument:
--optimizer-force-index-for-range
If enabled, FORCE INDEX will also try to force a range
plan.
--optimizer-full-scan
Enable full table and index scans.
(Defaults to on; use --skip-optimizer-full-scan to disable.)
--optimizer-low-limit-heuristic
Enable low limit heuristic.
(Defaults to on; use --skip-optimizer-low-limit-heuristic to disable.)
Expand Down Expand Up @@ -2035,6 +2038,7 @@ old-alter-table FALSE
old-passwords 0
old-style-user-limits FALSE
optimizer-force-index-for-range FALSE
optimizer-full-scan TRUE
optimizer-low-limit-heuristic TRUE
optimizer-prune-level 1
optimizer-search-depth 62
Expand Down
99 changes: 99 additions & 0 deletions mysql-test/r/optimizer_full_scan.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
set @orig_optimizer_full_scan = @@optimizer_full_scan;
set @orig_optimizer_force_index_for_range = @@optimizer_force_index_for_range;
create table t (i int, j int, key(i));
insert into t values (1, 1);
insert into t values (1, 2);
insert into t values (2, 1);
insert into t values (2, 2);
insert into t values (3, 1);
insert into t values (3, 2);
analyze table t;
Table Op Msg_type Msg_text
test.t analyze status OK
# Basic tests
explain select * from t;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t ALL NULL NULL NULL NULL # NULL
set optimizer_full_scan = off;
select * from t;
ERROR HY000: Full table/index scan is disabled
set optimizer_full_scan = on;
select * from t;
i j
1 1
1 2
2 1
2 2
3 1
3 2
explain select * from t force index (i);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t ALL NULL NULL NULL NULL # NULL
set optimizer_full_scan = off;
select i from t force index (i);
ERROR HY000: Full table/index scan is disabled
set optimizer_full_scan = on;
select i from t force index (i);
i
1
1
2
2
3
3
explain select * from t a, t b where a.i = b.i;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE a ALL i NULL NULL NULL # NULL
1 SIMPLE b ALL i NULL NULL NULL # Using where; Using join buffer (Block Nested Loop)
set optimizer_full_scan = off;
select * from t a, t b where a.i = b.i;
ERROR HY000: Full table/index scan is disabled
set optimizer_full_scan = on;
select * from t a, t b where a.i = b.i;
i j i j
1 1 1 1
1 2 1 1
1 1 1 2
1 2 1 2
2 1 2 1
2 2 2 1
2 1 2 2
2 2 2 2
3 1 3 1
3 2 3 1
3 1 3 2
3 2 3 2
explain select * from t a straight_join t b where a.i = 10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE a ref i i 5 const # NULL
1 SIMPLE b ALL NULL NULL NULL NULL # Using join buffer (Block Nested Loop)
set optimizer_full_scan = off;
select * from t a straight_join t b where a.i = 10;
ERROR HY000: Full table/index scan is disabled
set optimizer_full_scan = on;
select * from t a straight_join t b where a.i = 10;
i j i j
# Test integration with optimizer_force_index_for_range
alter table t drop index i, add primary key (i, j);
# Test range plans
set optimizer_force_index_for_range = on;
set optimizer_full_scan = off;
explain select i from t where i in (1, 2, 3) and j in (1, 2);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t index PRIMARY PRIMARY 8 NULL # Using where; Using index
select i from t where i in (1, 2, 3) and j in (1, 2);
ERROR HY000: Full table/index scan is disabled
explain select i from t force index (primary) where i in (1, 2, 3) and j in (1, 2);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t range PRIMARY PRIMARY 8 NULL # Using where; Using index
select i from t force index (primary) where i in (1, 2, 3) and j in (1, 2);
i
1
1
2
2
3
3
drop table t;
set optimizer_full_scan = @orig_optimizer_full_scan;
set optimizer_force_index_for_range = @orig_optimizer_force_index_for_range;
93 changes: 93 additions & 0 deletions mysql-test/suite/sys_vars/r/optimizer_full_scan_basic.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
SET @session_start_value = @@session.optimizer_full_scan;
SELECT @session_start_value;
@session_start_value
1
SET @global_start_value = @@global.optimizer_full_scan;
SELECT @global_start_value;
@global_start_value
1
SET @@session.optimizer_full_scan = 0;
SET @@session.optimizer_full_scan = DEFAULT;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
1
SET @@session.optimizer_full_scan = 1;
SET @@session.optimizer_full_scan = DEFAULT;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
1
SET optimizer_full_scan = 1;
SELECT @@optimizer_full_scan;
@@optimizer_full_scan
1
SELECT session.optimizer_full_scan;
ERROR 42S02: Unknown table 'session' in field list
SELECT local.optimizer_full_scan;
ERROR 42S02: Unknown table 'local' in field list
SET session optimizer_full_scan = 0;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
0
SET @@session.optimizer_full_scan = 0;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
0
SET @@session.optimizer_full_scan = 1;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
1
SET @@session.optimizer_full_scan = -1;
ERROR 42000: Variable 'optimizer_full_scan' can't be set to the value of '-1'
SET @@session.optimizer_full_scan = 2;
ERROR 42000: Variable 'optimizer_full_scan' can't be set to the value of '2'
SET @@session.optimizer_full_scan = "T";
ERROR 42000: Variable 'optimizer_full_scan' can't be set to the value of 'T'
SET @@session.optimizer_full_scan = "Y";
ERROR 42000: Variable 'optimizer_full_scan' can't be set to the value of 'Y'
SET @@session.optimizer_full_scan = NO;
ERROR 42000: Variable 'optimizer_full_scan' can't be set to the value of 'NO'
SET @@global.optimizer_full_scan = 1;
SELECT @@global.optimizer_full_scan;
@@global.optimizer_full_scan
1
SET @@global.optimizer_full_scan = 0;
SELECT count(VARIABLE_VALUE) FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME='optimizer_full_scan';
count(VARIABLE_VALUE)
1
SELECT IF(@@session.optimizer_full_scan, "ON", "OFF") = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='optimizer_full_scan';
IF(@@session.optimizer_full_scan, "ON", "OFF") = VARIABLE_VALUE
1
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
1
SELECT VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='optimizer_full_scan';
VARIABLE_VALUE
ON
SET @@session.optimizer_full_scan = OFF;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
0
SET @@session.optimizer_full_scan = ON;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
1
SET @@session.optimizer_full_scan = TRUE;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
1
SET @@session.optimizer_full_scan = FALSE;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
0
SET @@session.optimizer_full_scan = @session_start_value;
SELECT @@session.optimizer_full_scan;
@@session.optimizer_full_scan
1
SET @@global.optimizer_full_scan = @global_start_value;
SELECT @@global.optimizer_full_scan;
@@global.optimizer_full_scan
1
102 changes: 102 additions & 0 deletions mysql-test/suite/sys_vars/t/optimizer_full_scan_basic.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
--source include/load_sysvars.inc


# Saving initial value of optimizer_full_scan in a temporary variable

SET @session_start_value = @@session.optimizer_full_scan;
SELECT @session_start_value;
SET @global_start_value = @@global.optimizer_full_scan;
SELECT @global_start_value;

# Display the DEFAULT value of optimizer_full_scan

SET @@session.optimizer_full_scan = 0;
SET @@session.optimizer_full_scan = DEFAULT;
SELECT @@session.optimizer_full_scan;

SET @@session.optimizer_full_scan = 1;
SET @@session.optimizer_full_scan = DEFAULT;
SELECT @@session.optimizer_full_scan;


# Check if optimizer_full_scan can be accessed with and without @@ sign

SET optimizer_full_scan = 1;
SELECT @@optimizer_full_scan;

--Error ER_UNKNOWN_TABLE
SELECT session.optimizer_full_scan;

--Error ER_UNKNOWN_TABLE
SELECT local.optimizer_full_scan;

SET session optimizer_full_scan = 0;
SELECT @@session.optimizer_full_scan;

# change the value of optimizer_full_scan to a valid value

SET @@session.optimizer_full_scan = 0;
SELECT @@session.optimizer_full_scan;
SET @@session.optimizer_full_scan = 1;
SELECT @@session.optimizer_full_scan;


# Change the value of optimizer_full_scan to invalid value

--Error ER_WRONG_VALUE_FOR_VAR
SET @@session.optimizer_full_scan = -1;
--Error ER_WRONG_VALUE_FOR_VAR
SET @@session.optimizer_full_scan = 2;
--Error ER_WRONG_VALUE_FOR_VAR
SET @@session.optimizer_full_scan = "T";
--Error ER_WRONG_VALUE_FOR_VAR
SET @@session.optimizer_full_scan = "Y";
--Error ER_WRONG_VALUE_FOR_VAR
SET @@session.optimizer_full_scan = NO;


# Test if accessing global optimizer_full_scan gives error

SET @@global.optimizer_full_scan = 1;
SELECT @@global.optimizer_full_scan;
SET @@global.optimizer_full_scan = 0;


# Check if the value in GLOBAL Table contains variable value

SELECT count(VARIABLE_VALUE) FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME='optimizer_full_scan';


# Check if the value in GLOBAL Table matches value in variable

SELECT IF(@@session.optimizer_full_scan, "ON", "OFF") = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='optimizer_full_scan';
SELECT @@session.optimizer_full_scan;
SELECT VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='optimizer_full_scan';


# Check if ON and OFF values can be used on variable

SET @@session.optimizer_full_scan = OFF;
SELECT @@session.optimizer_full_scan;
SET @@session.optimizer_full_scan = ON;
SELECT @@session.optimizer_full_scan;


# Check if TRUE and FALSE values can be used on variable

SET @@session.optimizer_full_scan = TRUE;
SELECT @@session.optimizer_full_scan;
SET @@session.optimizer_full_scan = FALSE;
SELECT @@session.optimizer_full_scan;


# Restore initial value

SET @@session.optimizer_full_scan = @session_start_value;
SELECT @@session.optimizer_full_scan;
SET @@global.optimizer_full_scan = @global_start_value;
SELECT @@global.optimizer_full_scan;
65 changes: 65 additions & 0 deletions mysql-test/t/optimizer_full_scan.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
set @orig_optimizer_full_scan = @@optimizer_full_scan;
set @orig_optimizer_force_index_for_range = @@optimizer_force_index_for_range;

create table t (i int, j int, key(i));
insert into t values (1, 1);
insert into t values (1, 2);
insert into t values (2, 1);
insert into t values (2, 2);
insert into t values (3, 1);
insert into t values (3, 2);

analyze table t;

--echo # Basic tests
--replace_column 9 #
explain select * from t;
set optimizer_full_scan = off;
--error ER_FULL_SCAN_DISABLED
select * from t;
set optimizer_full_scan = on;
select * from t;

--replace_column 9 #
explain select * from t force index (i);
set optimizer_full_scan = off;
--error ER_FULL_SCAN_DISABLED
select i from t force index (i);
set optimizer_full_scan = on;
select i from t force index (i);

--replace_column 9 #
explain select * from t a, t b where a.i = b.i;
set optimizer_full_scan = off;
--error ER_FULL_SCAN_DISABLED
select * from t a, t b where a.i = b.i;
set optimizer_full_scan = on;
select * from t a, t b where a.i = b.i;

--replace_column 9 #
explain select * from t a straight_join t b where a.i = 10;
set optimizer_full_scan = off;
--error ER_FULL_SCAN_DISABLED
select * from t a straight_join t b where a.i = 10;
set optimizer_full_scan = on;
select * from t a straight_join t b where a.i = 10;

--echo # Test integration with optimizer_force_index_for_range
alter table t drop index i, add primary key (i, j);

--echo # Test range plans
set optimizer_force_index_for_range = on;
set optimizer_full_scan = off;
--replace_column 9 #
explain select i from t where i in (1, 2, 3) and j in (1, 2);
--error ER_FULL_SCAN_DISABLED
select i from t where i in (1, 2, 3) and j in (1, 2);
--replace_column 9 #
explain select i from t force index (primary) where i in (1, 2, 3) and j in (1, 2);
select i from t force index (primary) where i in (1, 2, 3) and j in (1, 2);

drop table t;

set optimizer_full_scan = @orig_optimizer_full_scan;
set optimizer_force_index_for_range = @orig_optimizer_force_index_for_range;

Loading

0 comments on commit b90e801

Please sign in to comment.