# Basic test for optimizer trace

--source include/have_optimizer_trace.inc

# This test gives different results when ps-protocol is enabled
# because in ps-protocol, we read OPTIMIZER_TRACE after
# 'EXECUTE stmt'; for subqueries the IN->EXISTS transformation is done
# at PREPARE time, and not re-done at EXECUTE time, so in normal
# protocol the trace talks about IN->EXISTS, but in ps-protocol it
# doesn't (as it's the EXECUTE trace and IN->EXISTS was done once for
# all at PREPARE; we see IN->EXISTS if we look at the trace of
# PREPARE).
# That's why we have two result files.
# This test is skipped in view-protocol because this changes queries
# submitted to the optimizer (from 'SELECT x FROM ... WHERE ... etc'
# to 'SELECT x FROM mysqltest_tmp_v') so changes trace.
# It's also skipped in sp-protocol for the same reason (SELECT becomes
# CALL).

# When adding tests here, try to have some of them use SELECT and
# others use EXPLAIN SELECT, to verify that both ways give sufficient
# tracing.

# should be empty
select * from information_schema.OPTIMIZER_TRACE;

let $DEFAULT_TRACE_MEM_SIZE=1048576; # 1MB
eval set optimizer_trace_max_mem_size=$DEFAULT_TRACE_MEM_SIZE;
set @@session.optimizer_trace="enabled=on";

select * from information_schema.OPTIMIZER_TRACE;

CREATE TABLE t5 (c int);
INSERT INTO t5 VALUES (NULL);

CREATE TABLE t6 (d int , KEY (d));
INSERT INTO t6 VALUES (NULL),(NULL);

SELECT (SELECT 1 FROM t6 WHERE d = c) AS RESULT FROM t5 ;

# some float values might not be identical on all platforms, we will see.
select * from information_schema.OPTIMIZER_TRACE;
# check frequency of spaces
select (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
  from information_schema.OPTIMIZER_TRACE;

# now all on one line
set optimizer_trace="one_line=on";
SELECT (SELECT 1 FROM t6 WHERE d = c) AS RESULT FROM t5 ;
select * from information_schema.OPTIMIZER_TRACE;
# check frequency of spaces
select (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
  from information_schema.OPTIMIZER_TRACE;

# From now on, use end markers, easier to read
set end_markers_in_json=on;
set optimizer_trace="one_line=off";

# trace should be produced by EXPLAIN too
EXPLAIN SELECT (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5 ;
select * from information_schema.OPTIMIZER_TRACE;

SELECT /* should be last */ (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5 ;
select * from information_schema.OPTIMIZER_TRACE;

set @@session.optimizer_trace="enabled=off";
SELECT /* bug if you see this*/ (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5 ;
# should not see last statement but previous
select QUERY from information_schema.OPTIMIZER_TRACE;

set @@session.optimizer_trace="enabled=on";

# union in subquery
SELECT (SELECT 1 FROM t6 WHERE d = ifnull(c,null) UNION SELECT 2 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5 ;
select * from information_schema.OPTIMIZER_TRACE;

# union in subquery in WHERE clause
SELECT * FROM t5 WHERE 5 IN (SELECT 1 FROM t6 WHERE d = ifnull(c,null) UNION SELECT 2 FROM t6 WHERE d = ifnull(c,null));
select * from information_schema.OPTIMIZER_TRACE;

# Test max_mem_size: re-run same query with lower max_mem_size:
# save non-truncated trace but don't output it again
select (@query:=QUERY)+NULL, (@trace:=TRACE)+NULL from information_schema.OPTIMIZER_TRACE;
select length(@trace);
# The concatenation of query and trace above has length:
# - >@max_mem_size in normal mode
# - <@max_mem_size in ps-protocol mode (because IN->EXISTS is done at PREPARE
# and we trace only EXECUTE)
# - So in normal mode, the lines below verify truncation,
# whereas in ps-protocol mode they verify non-truncation.
set @max_mem_size=13900;
set optimizer_trace_max_mem_size=@max_mem_size;
select length(@query)+length(@trace) > @@optimizer_trace_max_mem_size;
SELECT * FROM t5 WHERE 5 IN (SELECT 1 FROM t6 WHERE d = ifnull(c,null) UNION SELECT 2 FROM t6 WHERE d = ifnull(c,null));
select (@missing_bytes:=missing_bytes_beyond_max_mem_size) from information_schema.OPTIMIZER_TRACE;
select (@query2:=QUERY)+NULL,(@trace2:=TRACE)+NULL from information_schema.OPTIMIZER_TRACE;
select length(@trace2),
       (length(@trace2) + @missing_bytes) = length(@trace),
       @query2 = @query;
# If truncated, trace length should be around the maximum, possibly a
# bit higher as we stop tracing after passing the maximum.
select length(@query2) + length(@trace2)
       between (@@optimizer_trace_max_mem_size-200) and (@@optimizer_trace_max_mem_size+200);
# if truncated, trace should be a prefix of non-truncated trace
select instr(@trace, @trace2) = 1;

# Test that if the query is longer than max size, trace is not produced
set optimizer_trace_max_mem_size=1;
select 1;
select * from information_schema.OPTIMIZER_TRACE;
# Test that with max size == 0, even query is not produced
set optimizer_trace_max_mem_size=0;
select 1;
select * from information_schema.OPTIMIZER_TRACE;
eval set optimizer_trace_max_mem_size=$DEFAULT_TRACE_MEM_SIZE;

# semijoin

explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null);
select * from information_schema.OPTIMIZER_TRACE;

# subquery materialization

set @old_opt_switch=@@optimizer_switch;
# The SET below must not be output, because only servers supporting
# semijoin will execute it (would make varying output).
if (`select locate('semijoin', @@optimizer_switch) > 0`)
{
--disable_query_log
  set optimizer_switch="semijoin=off,subquery_materialization_cost_based=off";
--enable_query_log
}
explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null);
select * from information_schema.OPTIMIZER_TRACE;
set optimizer_switch=@old_opt_switch;

# 5.1-style subquery transformations
CREATE TABLE t1 (s1 CHAR(5),
                 s2 CHAR(5));
INSERT INTO t1 VALUES ('z','?'),('y','!');
# > ANY -> > MIN...
explain extended select * from t1 where s1 > any (select s2 from t1);
select * from information_schema.OPTIMIZER_TRACE;
explain extended select * from t1 where s1 > any (select max(s2) from t1);
select * from information_schema.OPTIMIZER_TRACE;
# IN -> EXISTS
if (`select locate('semijoin', @@optimizer_switch) > 0`)
{
--disable_query_log
  set optimizer_switch="semijoin=off,materialization=off";
--enable_query_log
}
explain extended select * from t1 where s1 in (select s2 from t1);
select * from information_schema.OPTIMIZER_TRACE;
explain extended select * from t1 where (s1,s2) in (select s2,s1 from t1);
select * from information_schema.OPTIMIZER_TRACE;
set optimizer_switch=@old_opt_switch;
drop table t1;

# explanation of plan choice
create table t1(a int);
create table t2(a int);
insert into t1 values(1),(2),(3);
insert into t2 values(1),(2);
# print many plans:
set @@session.optimizer_prune_level=0;
explain select * from t1,t2;
select * from information_schema.OPTIMIZER_TRACE;
# don't print plans:
select @@optimizer_trace_features;
set @@optimizer_trace_features="greedy_search=off";
explain select * from t1,t2;
select * from information_schema.OPTIMIZER_TRACE;
set @@optimizer_trace_features=default;
set @@session.optimizer_prune_level=default;
drop table t1, t2;

# test late decision to abandon subquery materialization due to BLOBs
# (from subselect_mat.test)
# force materialization to be always considered
if (`select locate('semijoin', @@optimizer_switch) > 0`)
{
--disable_query_log
  set @@optimizer_switch='semijoin=off';
--enable_query_log
}
set @prefix_len = 6;
# BLOB == 16 (small blobs that could be stored in HEAP tables)
set @blob_len = 16;
set @suffix_len = @blob_len - @prefix_len;
create table t1_16 (a1 blob(16), a2 blob(16));
create table t2_16 (b1 blob(16), b2 blob(16));
create table t3_16 (c1 blob(16), c2 blob(16));
insert into t1_16 values
 (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len)));
insert into t1_16 values
 (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len)));
insert into t1_16 values
 (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len)));
insert into t2_16 values
 (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len)));
insert into t2_16 values
 (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len)));
insert into t2_16 values
 (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len)));
insert into t3_16 values
 (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len)));
insert into t3_16 values
 (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len)));
insert into t3_16 values
 (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len)));
insert into t3_16 values
 (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len)));
# single value transformer
explain extended select left(a1,7), left(a2,7)
from t1_16
where a1 in (select b1 from t2_16 where b1 > '0');
select * from information_schema.OPTIMIZER_TRACE;
drop table t1_16,t2_16,t3_16;
set @@optimizer_switch=@old_opt_switch;

# test of outer-join preventing semijoin
# from subselect.test

CREATE table t1 ( c1 integer );
INSERT INTO t1 VALUES ( 1 );
INSERT INTO t1 VALUES ( 2 );
INSERT INTO t1 VALUES ( 3 );

CREATE TABLE t2 ( c2 integer );
INSERT INTO t2 VALUES ( 1 );
INSERT INTO t2 VALUES ( 4 );
INSERT INTO t2 VALUES ( 5 );

SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2
  WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2 IN ( 1 ) );
select * from information_schema.OPTIMIZER_TRACE;

# union in top-query

SELECT * FROM t1 WHERE c1=5 UNION SELECT * FROM t2 WHERE c2=5;
select * from information_schema.OPTIMIZER_TRACE;

# two subqueries in top query

if (`select locate('semijoin', @@optimizer_switch) > 0`)
{
--disable_query_log
  set optimizer_switch="semijoin=off";
--enable_query_log
}

explain
select * from t1
where concat(c1,'x') IN
      (select left(c2,8) from t2)
and
concat(c1,'y') IN
      (select left(c2,9) from t2);

select * from information_schema.OPTIMIZER_TRACE;

set optimizer_switch=@old_opt_switch;

DROP TABLE t1,t2;

# two constant tables

create table t1 (a int);
insert into t1 values(1);
create table t2 (a int);
insert into t2 values(1);
select * from t1,t2;
select * from information_schema.OPTIMIZER_TRACE;

# semijoin pullout

create table t3 (a int, b int);
create table t4 (a int primary key);
insert into t4 values(1),(2);
# we do it with prepared statements; by reading the expanded query we
# can see that
# - subquery->semijoin transformation is done at first EXECUTE
# - same for semijoin table pullout
# - both are reused by the second EXECUTE (where expanded_query is a
# pure join)
prepare stmt from 'select * from t3 where (a,a,b) in (select * from t1,t2,t4)';
select trace from information_schema.OPTIMIZER_TRACE;
execute stmt;
select trace from information_schema.OPTIMIZER_TRACE;
execute stmt;
select trace from information_schema.OPTIMIZER_TRACE;
DROP TABLE t1,t2,t3,t4;

# Trig_cond printing

create table t1(a int);
insert into t1 values(1),(1);
create table t2(a int);
insert into t2 values(1),(1);
# guarded pushed down conds for WHERE and ON in LEFT JOIN
select * from t1 left join t2 on t2.a=500 where t2.a is NULL;
select * from information_schema.OPTIMIZER_TRACE;
drop table t1,t2;
# guarded added conds for IN->EXISTS:
create table t1(a int, b int);
insert into t1 values(1,NULL),(NULL,2);
create table t2(c int, d int);
insert into t2 values(1,1),(2,2);
select * from t1 where (t1.a,t1.b) not in (select c,d from t2 where c>0);
select * from information_schema.OPTIMIZER_TRACE;

# HAVING
select t1.a,avg(t2.c) as moyenne from t1, t2 where t2.c>-1
  group by t1.a having moyenne<>0;
--replace_regex /("sort_buffer_size":) [0-9]+/\1 "NNN"/
select trace from information_schema.OPTIMIZER_TRACE;
# impossible HAVING
select t1.a,avg(t2.c) as moyenne from t1, t2 where t2.c>-1
  group by t1.a having 4=5;
select trace from information_schema.OPTIMIZER_TRACE;
drop table t1,t2;

# HAVING


# test non-SELECT statements

update t6 set d=5 where d is NULL;
select * from information_schema.OPTIMIZER_TRACE;

delete from t6 where d=5;
select * from information_schema.OPTIMIZER_TRACE;

insert into t6 values(6),(7),(8);
select * from information_schema.OPTIMIZER_TRACE;

insert into t6 select * from t6 where d>7;
select * from information_schema.OPTIMIZER_TRACE;

update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-4 where d>7000;
select * from information_schema.OPTIMIZER_TRACE;

delete t6 from t5, t6 where d>7000;
select * from information_schema.OPTIMIZER_TRACE;

# Test optimizer_trace_offset/limit for consecutive
# non-related statements (as opposed to "for sub-statements"
# which is tested further below).
set optimizer_trace_offset=2,optimizer_trace_limit=2;
# should be empty
select * from information_schema.OPTIMIZER_TRACE;
select 1;
select 2;
select 3;
select 4;
select 5;
# In normal mode, each SELECT is a standalone statement and generates
# one trace: so 3rd and 4th trace means 3rd and 4th SELECT.
# In ps-protocol mode, each SELECT is two statements: PREPARE and
# EXECUTE; each of the two creates one trace; so 3rd and 4th trace
# means 2nd SELECT (preparation) and 2nd SELECT again (execution).
select * from information_schema.OPTIMIZER_TRACE;
set optimizer_trace_offset=-2,optimizer_trace_limit=2;
# should be empty
select * from information_schema.OPTIMIZER_TRACE;
select 1;
select 2;
select 3;
select 4;
select 5;
select * from information_schema.OPTIMIZER_TRACE;
set optimizer_trace_offset=default,optimizer_trace_limit=default;
# should be empty
select * from information_schema.OPTIMIZER_TRACE;

# For stored functions, stored procedures, triggers, with
# offset=-1,limit=1, what we see in the trace is the trace of the last
# statement (i.e. last call to mysql_execute_command()). Thus:
# - for a call to a stored procedure, we will see only the last
# statement of this procedure
# - for a call to a trigger, we will see only the last statement of
# this trigger, and not the caller.
# etc.
# If the user wants to see more, 'offset' should be adjusted.

# Stored functions

create table t1 (
	id   char(16) not null default '',
        data int not null
);
create table t2 (
	s   char(16),
        i   int,
	d   double
);
delimiter |;
insert into t1 values ("a", 1), ("b", 2) |
insert into t2 values ("a", 1, 1.0), ("b", 2, 2.0), ("c", 3, 3.0), ("d", 4, 4.0) |
create function f1() returns int
begin
  declare ret int;
  insert into t1 values("z",0);
  delete from t1 where id="z";
  select sum(data) into ret from t1;
  return ret;
end|
select f1()|
# Here we will see the trace of RETURN.
select * from information_schema.OPTIMIZER_TRACE|
select s, f1() from t2 order by s desc|
select * from information_schema.OPTIMIZER_TRACE|
select * from t6 where d in (select f1() from t2 where s="c")|
select * from information_schema.OPTIMIZER_TRACE|
# Want to see the top and invoked sub-statements; this means 11 traces:
# 1 top statement + two executions of f1() (there is one
# execution inside the range optimizer and one "normal" execution);
# in the stored function we have traces: DECLARE (1 trace), 3 DMLs
# and RETURN (1 trace). 1+2*(1+3+1)=11.
# In ps-protocol mode, we have those 11, plus one for PREPARE of the
# top SELECT.
# We ask for a larger number (60) and will check how many we got.
# Note that when semijoin features are disabled, the subquery is
# treated differently: it is executed, so the order and amount of
# executions of f1() becomes different, which changes the order of
# substatements in the trace.
set @old_opt_switch=@@optimizer_switch;
# Test was created when sj-mat-scan could not handle this query...
set optimizer_switch="materialization=off";
set optimizer_trace_offset=-60, optimizer_trace_limit=60|
select * from t6 where d in (select f1() from t2 where s="c")|
select * from information_schema.OPTIMIZER_TRACE|
select count(*) from information_schema.OPTIMIZER_TRACE|
# Want to see the DELETE (invoked sub-statement):
set optimizer_trace_offset=3, optimizer_trace_limit=1|
select * from t6 where d in (select f1() from t2 where s="c")|
# In normal mode, we have traces for the top SELECT, then the
# function's INSERT then the function's DELETE, so DELETE is the
# third trace, which we see.
# In ps-protocol mode, we also have trace of PREPARE for the top
# SELECT, so we see one trace before the DELETE: the INSERT.
select * from information_schema.OPTIMIZER_TRACE|
set @@optimizer_switch=@old_opt_switch;

# Stored procedures
create procedure p1(arg char(1))
begin
  declare res int;
  select d into res from t6 where d in (select f1() from t2 where s=arg);
  select d+1 into res from t6 where d=res+1;
end|
set @old_opt_switch=@@optimizer_switch;
# Test was created when sj-mat-scan could not handle this query...
set optimizer_switch="materialization=off";
set optimizer_trace_offset=0, optimizer_trace_limit=100;
call p1("c")|
select * from information_schema.OPTIMIZER_TRACE|
set @@optimizer_switch=@old_opt_switch;

# Triggers
create trigger trg1 before insert on t2 for each row
begin
  set new.s=f1();  
end|
set optimizer_trace_offset=0, optimizer_trace_limit=100|
insert into t2 select d,100,200 from t6 where d is not null|
select * from information_schema.OPTIMIZER_TRACE|
select * from t2|
delimiter ;|

# PREPARE/EXECUTE/EXECUTE
prepare stmt from 'select count(*) from t1 where t1.data=?';
set @param="c";
set optimizer_trace_offset=0, optimizer_trace_limit=100;
execute stmt using @param;
select count(*) from information_schema.OPTIMIZER_TRACE;
select TRACE into @trace from information_schema.OPTIMIZER_TRACE;
select @trace;
# second EXECUTE should give same trace
set optimizer_trace_offset=0, optimizer_trace_limit=100;
execute stmt using @param;
select count(*) from information_schema.OPTIMIZER_TRACE;
select TRACE into @trace2 from information_schema.OPTIMIZER_TRACE;
select @trace=@trace2;

# enable/disable tracing in middle of procedure
drop procedure p1;
create temporary table optt like information_schema.OPTIMIZER_TRACE;
delimiter |;
create procedure p1(arg char(1))
begin
  declare res int;
  set optimizer_trace="enabled=off";
  # want to see all of SELECT below
  set optimizer_trace_offset=0, optimizer_trace_limit=100;
  set optimizer_trace="enabled=on";
  select d into res from t6 where d in (select f1() from t2 where s=arg);
  set optimizer_trace="enabled=off"; # and not more
  insert into optt select * from information_schema.OPTIMIZER_TRACE;
  set optimizer_trace_offset=default, optimizer_trace_limit=default;
  select d+1 into res from t6 where d=res+1;
end|
call  p1("c")|
select * from optt|
select @@optimizer_trace|
delimiter ;|

set optimizer_trace="enabled=on";
drop temporary table optt;
drop function f1;
drop procedure p1;
drop trigger trg1;

# Views
# merge-able
create view v1 as select * from t1 where id < "c";
explain select * from v1 where id="b";
select * from information_schema.OPTIMIZER_TRACE;
# insert into view
insert into v1 values("z", 100);
select * from information_schema.OPTIMIZER_TRACE;
delete from v1 where data=100;
select * from information_schema.OPTIMIZER_TRACE;
drop view v1;
# not merge-able
create view v1 as select * from t1 where id < "c" limit 2;
explain select * from v1 where id="b";
select * from information_schema.OPTIMIZER_TRACE;
drop view v1;

# I_S tables
--disable_warnings
select * from information_schema.session_variables where
VARIABLE_NAME="optimizer_trace";
--enable_warnings
select * from information_schema.OPTIMIZER_TRACE;

# test that DUMPFILE does no escaping of \n
# otherwise a JSON parser could not read
set end_markers_in_json=off;
select 1 union select 2;
--let $file=$MYSQLTEST_VARDIR/tmp/optimizer_trace.txt
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval select TRACE into dumpfile '$file' from information_schema.OPTIMIZER_TRACE;
# it has been manually checked that this file's content is JSON-compliant
--cat_file $file
--remove_file $file

# Test for crashing bug
--error ER_WRONG_VALUE_FOR_VAR
set optimizer_switch='default,index_merge=on,index_merge=off,default';
select @@optimizer_switch=@old_opt_switch;

# Test for long query (1070 chars)
--disable_query_log
--disable_result_log
select "abc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111def" as col;
--enable_result_log
--enable_query_log
select * from information_schema.OPTIMIZER_TRACE;

drop table t1,t2;
DROP TABLE t5,t6;
set optimizer_trace=default;
