List:Commits« Previous MessageNext Message »
From:Guilhem Bichot Date:May 4 2011 8:53pm
Subject:bzr commit into mysql-trunk branch (guilhem.bichot:3296)
View as plain text  
#At file:///home/mysql_src/bzrrepos_new/mysql-next-mr-opt-backporting-wl4800/ based on revid:guilhem.bichot@oracle.com-20110414130222-8arv4703gu38zi1j

 3296 Guilhem Bichot	2011-05-04
      Security patch for optimizer trace.
      Behaviour changes:
      
      1)
      [ suggested background: http://dev.mysql.com/doc/refman/5.5/en/stored-programs-security.html ]
      Definition of "connected user":
      == the one which opened the connection; <> some SUID user of SQL SECURITY DEFINER objects.
      The idea is that the query's trace exposes more information than the query's result set,
      including information which the connected user has no privilege to see;
      we must prevent the connected user from seeing this information. We do
      it with several measures.
      
      1.1) when using base table or view: disable tracing if connected user doesn't have table-level SELECT
      privilege on base table.
      
      1.2) when using view: disable tracing if connected user doesn't have SHOW VIEW privilege on view or
      doesn't have SELECT privilege on underlying base tables or view (same tests as EXPLAIN already does).
      
      1.3) when using routine: disable tracing if connected user doesn't have privileges to see
      routine's body.
      
      1.4) when using object which has a security context (view, routine): disable tracing if
      this context's user is different from connected user, unless connected user has all
      privileges.
      
      1.5) when reading I_S.OPTIMIZER_TRACE, return an empty result set if the
      current security context is different from the connected user, unless
      the current security context has all privileges.
      "Disable tracing" here means: flag the current statement's trace as "missing a privilege"
      (so in I_S.OPTIMIZER_TRACE the trace will appear as empty and the
      INSUFFICIENT_PRIVILEGES column will be "1"), and recursively disable tracing
      for all children substatements of this statement (they won't even have a row
      in I_S.OPTIMIZER_TRACE, to not expose the number of substatements).
      
      Implementation of all changes above is done in those steps:
      
      1.10) new security-checking functions called each time we start a statement using
      some base tables or views, execute a routine, open a view.
      
      1.11) those checks are always done unless @@optimizer_trace has "enabled=off";
      in particular, they are done even though the statement is outside
      the @@optimizer_trace_offset/limit window.
      
      1.12) a function to disable tracing existed:
      Opt_trace_stmt::disable_I_S_for_this_and_children();
      it had two problems: it could fail if OOM, and it required an Opt_trace_stmt.
      There can now be cases where we want to disable tracing recursively
      even though the current statement has no trace, or even though Opt_trace_context
      hasn't been allocated. Thus:
      
      1.12.1) Opt_trace_context is split in two pieces: most of it moves to
      Opt_trace_context_impl, dynamically allocated; a small piece (a counter and
      a pointer to Opt_trace_context_impl) is left in Opt_trace_context,
      and THD now contains an Opt_trace_context object instead of a pointer.
      
      1.12.2) the counter in Opt_trace_context serves to keep the value
      of "I_S support for any to-be-created traces" (0 means they should
      support I_S):
      1.12.2.1) disable_I_S_for_this_and_children() increments it (affects the to-be-created traces)
      and tells the current statement to disable its own I_S output (affects the current statement)
      1.12.2.2) restore_I_S() does opposite operations.
      
      1.12.3) As I_S support for to-be-created traces and for the current trace are now
      in two different variables, I_S support for the current trace is just
      a yes/no variable (3rd value NO_FOR_THIS_AND_CHILDREN is gone), thus
      it (Opt_trace_stmt::support_I_S) is changed from enum_support_I_S to boolean
      
      1.12.4) Opt_trace_stmt::support_I_S used to vary as we enter and leave Opt_trace_struct
      objects, due to tracable and non-tracable features like greedy search; we used
      to keep track of this in a stack of successive values of Opt_trace_stmt::support_I_S;
      but as disabling I_S in an Opt_trace_struct keeps I_S output disabled
      until leaving this struct, it is possible to change Opt_trace_stmt::support_I_S
      to be a counter (0 means I_S output is enabled); this eliminates a stack,
      its memory consumption and possible OOM failure.
      
      1.12.5) argument 'support_I_S' to constructor of Opt_trace_stmt is deleted,
      instead, creator of the object just calls
      Opt_trace_stmt::disable_I_S() on the new object
      
      1.13) a function to set the "missing privilege" mark for current statement is added:
      Opt_trace_context::missing_privilege(); it sets the mark, disables I_S output for
      future children substatements; I_S output is re-enabled when current statement
      ends.
      
      1.14) that function requires an Opt_trace_stmt (which is where we store
      that I_S output should be re-enabled when statement ends); dbug output
      has the same requirement; thus an argument 'support_dbug_or_support_missing_priv'
      is added to Opt_trace_context::start(); if it's true we must create an Opt_trace_stmt;
      this also simplifies logic in opt_trace_start().
      1.15) a RAII class "Opt_trace_start", replacing opt_trace_start()/opt_trace_end(),
      is added; it simplifies code in sql_parse.cc, sql_prepare.cc.
      
      Other behaviour changes, inspired by review discussions:
      
      2) in routines, we now also trace statements which are not basic SQL but
      still can manipulate subqueries: DECLARE, IF, CASE, RETURN; without that,
      in "IF(subq) THEN ...", subquery was not traced.
      
      3) when a command is not traced because
      sql_command_can_be_traced() returns 'false',
      we used to disable tracing of its substatements, this is not the case anymore.
      Thus we don't need to trace SQL PREPARE and SQL EXECUTE (which
      always produced uninteresting traces), only need to trace their prepared or executed
      statement. Additional benefit: now SQL PREPARE and SQL EXECUTE
      produce one trace each, which is what the C API mysql_stmt_prepare() and
      mysql_stmt_execute() calls already did, so it's consistent.
     @ WL4800_TODO.txt
        questions for reviewers. Organized the todo in sections.
     @ WL4800_validate_json.py
        ability to handle several ".result" files in one command line
     @ mysql-test/include/optimizer_trace.inc
        change (2) influences number of seen traces.
     @ mysql-test/include/optimizer_trace2.inc
        test changes (3) and (2).
        Test that subquery used in parameter of routine call, is traced.
     @ mysql-test/include/optimizer_trace_security.inc
        test change (1): different scenarios of tables/views/triggers/routines,
        to verify that the user sees only what he should.
     @ mysql-test/r/optimizer_trace2_no_prot.result
        effects of (2): more traces in routines. test changes (3) and (2).
     @ mysql-test/r/optimizer_trace2_ps_prot.result
        effects of (2): more traces in routines. test changes (3) and (2).
     @ mysql-test/r/optimizer_trace_no_prot.result
        new column INSUFFICIENT_PRIVILEGES. Change (2) influences number of seen traces.
     @ mysql-test/r/optimizer_trace_ps_prot.result
        new column INSUFFICIENT_PRIVILEGES. Change (2) influences number of seen traces.
     @ mysql-test/r/optimizer_trace_range_no_prot.result
        new column INSUFFICIENT_PRIVILEGES
     @ mysql-test/r/optimizer_trace_range_ps_prot.result
        new column INSUFFICIENT_PRIVILEGES
     @ mysql-test/r/optimizer_trace_subquery_no_prot.result
        new column INSUFFICIENT_PRIVILEGES
     @ mysql-test/r/optimizer_trace_subquery_ps_prot.result
        new column INSUFFICIENT_PRIVILEGES
     @ mysql-test/suite/funcs_1/r/is_columns_is.result
        fix for a rarely run test...
     @ mysql-test/t/optimizer_trace_bugs.test
        empty test, deleted
     @ mysql-test/t/optimizer_trace_debug.test
        Opt_trace_stmt is created more often now, for example in
         set optimizer_trace="enabled=on";
         select * from information_schema.OPTIMIZER_TRACE; # here
        so test is adjusted.
        We test that the case which is important to optimize (i.e. "enabled=off")
        is still optimized.
     @ sql/item_subselect.cc
        THD::opt_trace is object now, not pointer
     @ sql/mysqld.cc
        THD::opt_trace is object now, not pointer
     @ sql/opt_range.cc
        THD::opt_trace is object now, not pointer
     @ sql/opt_trace.cc
        review comment: moving Buffer out of Opt_trace_stmt.
        Implementation of changes 1.12), 1.13), 1.14)
     @ sql/opt_trace.h
        Implementation of changes 1.12), 1.13), 1.14), 1.15).
        One @todo is deleted, things which we don't trace yet are for later WLs, already created.
     @ sql/opt_trace2server.cc
        Implementation of changes 1.1), 1.2), 1.3), 1.4), 1.5), 1.10), 1.11), 1.15), 3).
     @ sql/sp_head.cc
        Implementation of changes 1.10) and 2).
        Removing sp_instr::exec_open_and_lock_tables() and shuffling code
        was ok'd with DmitryL.
     @ sql/sql_class.cc
        THD::opt_trace is object now, not pointer
     @ sql/sql_class.h
        THD::opt_trace is object now, not pointer
     @ sql/sql_delete.cc
        THD::opt_trace is object now, not pointer
     @ sql/sql_help.cc
        THD::opt_trace is object now, not pointer
     @ sql/sql_parse.cc
        Per change (2), SQLCOM_EXECUTE/PREPARE don't need flag CF_OPTIMIZER_TRACE.
        Use of Opt_trace_start (change 1.15) allows not using Opt_trace_struct::end()
        functions (thanks to order of destructor calls), and allows
        the caller to not keep a local 'started_optimizer_trace' bool.
     @ sql/sql_prepare.cc
        Use of Opt_trace_start (change 1.15) allows not using end()
        functions (thanks to order of destructor calls), and allows
        the caller to not keep a local started_optimizer_trace bool.
        Thus some code is reverted to what it is in trunk.
     @ sql/sql_select.cc
        THD::opt_trace is object now, not pointer. One @todo is deleted,
        things which we don't trace yet are for later WLs, already created.
     @ sql/sql_update.cc
        THD::opt_trace is object now, not pointer
     @ sql/sql_view.cc
        Implementation of change 1.10) (views)
     @ sql/sys_vars.cc
        THD::opt_trace is object now, not pointer
     @ sql/table.cc
        Implementation of change 1.10) (security context change by view)
     @ unittest/gunit/opt_trace-t.cc
        - test of new functions, of info.missing_priv property
        - do_check_json_compliance() doesn't accept empty traces
        - don't try to check JSON compliance of traces which
        are known to not be in utf8

    removed:
      mysql-test/r/optimizer_trace_bugs.result
      mysql-test/t/optimizer_trace_bugs.test
    added:
      mysql-test/include/optimizer_trace_security.inc
      mysql-test/r/optimizer_trace_security_no_prot.result
      mysql-test/r/optimizer_trace_security_ps_prot.result
      mysql-test/t/optimizer_trace_security_no_prot.test
      mysql-test/t/optimizer_trace_security_ps_prot.test
    modified:
      WL4800_TODO.txt
      WL4800_validate_json.py
      mysql-test/include/optimizer_trace.inc
      mysql-test/include/optimizer_trace2.inc
      mysql-test/r/optimizer_trace2_no_prot.result
      mysql-test/r/optimizer_trace2_ps_prot.result
      mysql-test/r/optimizer_trace_debug.result
      mysql-test/r/optimizer_trace_no_prot.result
      mysql-test/r/optimizer_trace_ps_prot.result
      mysql-test/r/optimizer_trace_range_no_prot.result
      mysql-test/r/optimizer_trace_range_ps_prot.result
      mysql-test/r/optimizer_trace_subquery_no_prot.result
      mysql-test/r/optimizer_trace_subquery_ps_prot.result
      mysql-test/suite/funcs_1/r/is_columns_is.result
      mysql-test/t/optimizer_trace_debug.test
      sql/item_subselect.cc
      sql/mysqld.cc
      sql/opt_range.cc
      sql/opt_trace.cc
      sql/opt_trace.h
      sql/opt_trace2server.cc
      sql/sp_head.cc
      sql/sp_head.h
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_delete.cc
      sql/sql_help.cc
      sql/sql_parse.cc
      sql/sql_prepare.cc
      sql/sql_select.cc
      sql/sql_update.cc
      sql/sql_view.cc
      sql/sys_vars.cc
      sql/table.cc
      unittest/gunit/opt_trace-t.cc
=== modified file 'WL4800_TODO.txt'
--- a/WL4800_TODO.txt	2011-04-05 07:34:13 +0000
+++ b/WL4800_TODO.txt	2011-05-04 20:53:28 +0000
@@ -1,52 +1,90 @@
 Short-term TODO list to remember for WL#4800
 ============================================
 
-see all added @todo
+A) TO DO BEFORE PUSH
 
-My command to update the HTML Doxygen is:
-rsync -avz /m/doxygen/mysql-wl4800 [URL]:public_html
+Coding style:
+Make sure long code lines are wrapped, and no trailing white space is
+added.
+
+Update copyright header of all changed files to reflect changed in 2011
+
+B) TO DO AFTER PUSH
+
+- in the manual, mention that people should dump their trace with
+SELECT INTO DUMPFILE instead of OUTFILE
+- in changelog, mention the added /*select#*/ in EXPLAIN EXTENDED; in
+the manual, mention use of this to decrypt what "id" is about in
+EXPLAIN output.
 
-fix bugs recorded in optimizer_trace_bugs.test and delete that test.
+C) QUESTIONS FOR REVIEWERS
 
-Open question: should we have a "version" property in the top trace
+C1) should we have a "version" property in the top trace
 object? Assume that at some point in development there is a
 significant overhaul of the trace's organization: new apps will have
 to be able to parse old and new traces; would it help them to have a
 version number in order to distinguish between old/new? Drawback: do
 not forget to bump the version when doing certain changes (and: what
 changes? any little change? maybe).
+Guilhem suggests: don't have a version.
 
-Coding style:
-Make sure long code lines are wrapped, and no trailing white space is
-added.
-
-Misc security issues as discussed already.
+C2) Guilhem to Jorgen: do you still want more precise checks for non-unique keys
+to be bundled in the debug binary? right now, there is a simple check
+that a key is different from the previous key; and there are full
+checks if one uses WL4800_validate_json.py.
 
-Review comments left:
-- in doc, mention DUMPFILE instead of OUTFILE
-- in changelog, mention /*select#*/; in doc, mention use of this (to
-decrypt what "id" is about in EXPLAIN).
-- more checks for non-unique keys?
-
-The optimizer may have second thoughts about which access method to
+C3) Jorgen wrote: The optimizer may have second thoughts about which access method to
 use for a table. This should be traced. See example query (1) where
 the trace says that ref-access is best but we change our mind and use
 range access anyway.
 (1)CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b), KEY b (b));
    INSERT INTO t1 VALUES (1,1),(1,2),(1,0),(1,3);
    SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a;
+Guilhem asks: Can we fix this before push? If not, how to handle it?
 
-Make --opt-trace-protocol dump traces to a separate file so that mtr
+C4) Jorgen wrote: Make --opt-trace-protocol dump traces to a separate file so that mtr
 can run with it without failing all tests.
+Guilhem asks: good idea, but how much is it needed?
+--explain-protocol, --sp-protocol, --view-protocol make all tests
+fail. Maybe I can merely move this as an idea in a comment in mysqltest.cc
+near the definition of opt-trace-protocol?
 
-Update copyright header of all changed files to reflect changed in 2011
-
-should the debug binary really assert(0) if json syntax error? is it a
+C5) should the debug binary really assert(0) if json syntax error? is it a
 good idea at the customer's? On the other hand, how to make sure a
 developer notices a syntax error when running tests?
 sql_print_warning() is an idea.
+Guilhem suggests: keep it as it is (assert(0))
 
-get a review for http://lists.mysql.com/commits/133482
+C6) Should mysql-test/t/optimizer_trace* move into
+mysql-test/suite/optimizer_trace ?
+Guilhem suggests: yes.
+
+C7) Dmitry advised we should trace SET and DO. Right now SET-which-sets-local-routine-vars is
+traced, but SET-which-sets-other-vars is not, this is
+inconsistent. And SET may use a subquery (SET @a=(subq)) which is
+worth tracing.
+Guilhem says: agree. A drawback of this is that
+SET optimizer_trace="enabled=off";
+will be traced, which is a nuisance, as it deletes previous trace.
+But next item below which could eliminate this drawback.
+
+C8) should we limit tracing to statements using
+tables/views? This would decrease the noise when tracing a routine
+(even DECLARE is traced now), and would nicely distinguish between
+  SET @x=(subq); # should be traced
+and
+  SET optimizer_trace="enabled=off"; # tracing is nuisance, as it
+                                     # deletes previous trace
+The worst consequence it could have is that
+  SELECT stored_func();
+would not be traced (but its substatements would still be, if they use
+tables/views).
+
+C9) The entire opt_trace.h is included in sql_class.h which itself is
+included everywhere. Of opt_trace.h, sql_class.h needs only
+Opt_trace_context. Should declaration of Opt_trace_context move to a
+separate smaller include file, to be included in sql_class.h?
+Guilhem suggests: yes.
 
-Move Buffer out of Opt_trace_stmt, in unnamed namespace in
-opt_trace.cc.
+C10) Guilhem to Tor: can we delete opt_notrace-t.cc now? I think I
+replied in some old mail about this file.

=== modified file 'WL4800_validate_json.py'
--- a/WL4800_validate_json.py	2011-02-16 15:06:06 +0000
+++ b/WL4800_validate_json.py	2011-05-04 20:53:28 +0000
@@ -19,21 +19,22 @@ import sys
 
 usage = """
 Usage:
-  %s <a_file>
+  %s <a_file> <another_file> <etc>
 
-It will verify that all optimizer traces of a_file (usually a_file
+It will verify that all optimizer traces of files (usually a_file
 is a .result or .reject file which contains
 SELECT * FROM OPTIMIZER_TRACE; ) are JSON-compliant.
 Exit code is 0 if all ok.
 """ % sys.argv[0]
 
-if len(sys.argv) != 2:
+if len(sys.argv) < 2:
     print usage
     sys.exit(1)
 
 import json, re
 
-retcode=0
+trace_start_re = re.compile("^.*(\t)?{\n")
+trace_end_re = re.compile("^}")
 
 def check(trace, first_trace_line):
     global retcode
@@ -70,28 +71,31 @@ def check(trace, first_trace_line):
         return
     print "ok at line", first_trace_line
 
-all = open(sys.argv[1]).readlines()
-trace_start_re = re.compile("^(.*\t)?{\n")
-trace_end_re = re.compile("^}")
-
-first_trace_line = trace_line = 0
-trace = None
-for l in all:
-    trace_line += 1
-    if trace_start_re.match(l):
-        assert first_trace_line == 0
-        trace = []
-        first_trace_line = trace_line
-        trace.append("{\n")
-        continue
-    if trace_end_re.match(l):
-        assert first_trace_line != 0
-        trace.append("}") # eliminate any following columns of table (OS_MALLOC_ERROR etc)
-        check(trace, first_trace_line)
-        first_trace_line = 0
-    if first_trace_line != 0:
-        # eliminate /* */ (not valid JSON)
-        no_comment = re.sub("/\*.*\*/", "", l)
-        trace.append(no_comment)
+def handle_one_file(name):
+    all = open(name).readlines()
+    first_trace_line = trace_line = 0
+    trace = None
+    for l in all:
+        trace_line += 1
+        if trace_start_re.match(l) and first_trace_line == 0:
+            trace = []
+            first_trace_line = trace_line
+            trace.append("{\n")
+            continue
+        if trace_end_re.match(l):
+            assert first_trace_line != 0
+            trace.append("}") # eliminate any following columns of table (OS_MALLOC_ERROR etc)
+            check(trace, first_trace_line)
+            first_trace_line = 0
+        if first_trace_line != 0:
+            # eliminate /* */ (not valid JSON)
+            no_comment = re.sub("/\*.*\*/", "", l)
+            trace.append(no_comment)
 
+retcode=0
+for f in sys.argv[1:]:
+    print "FILE %s" % f
+    print
+    handle_one_file(f)
+    print
 sys.exit(retcode)

=== modified file 'mysql-test/include/optimizer_trace.inc'
--- a/mysql-test/include/optimizer_trace.inc	2011-02-15 20:53:19 +0000
+++ b/mysql-test/include/optimizer_trace.inc	2011-05-04 20:53:28 +0000
@@ -381,44 +381,42 @@ begin
   return ret;
 end|
 select f1()|
-# Here we will see the trace of SELECT SUM etc;
-# if this SELECT SUM had been the argument of RETURN,
-# SP code would treat it as an expression and not a statement,
-# thus mysql_execute_command() would not be called, thus
-# the trace would rather show DELETE.
+# 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 7 traces:
+# 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);
-# 3 sub-statements in the stored function: 1+2*3=7.
-set optimizer_trace_offset=-7, optimizer_trace_limit=7|
-select @@optimizer_trace_offset, @@optimizer_trace_limit|
+# 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 (20) and will check how many we got.
+set optimizer_trace_offset=-20, optimizer_trace_limit=20|
 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=2, optimizer_trace_limit=1|
+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 have PREPARE and EXECUTE for the top SELECT
-# (two traces); execution will generate the function's INSERT and
-# DELETE; so INSERT 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_trace_offset=default, optimizer_trace_limit=default|
 
 # 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 optimizer_trace_offset=0, optimizer_trace_limit=100;
 call p1("c")|
 select * from information_schema.OPTIMIZER_TRACE|
 
@@ -427,28 +425,30 @@ create trigger trg1 before insert on t2 
 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 'call p1(?)';
-select QUERY from information_schema.OPTIMIZER_TRACE;
+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 table optt like information_schema.OPTIMIZER_TRACE;
+create temporary table optt like information_schema.OPTIMIZER_TRACE;
 delimiter |;
 create procedure p1(arg char(1))
 begin
@@ -469,7 +469,7 @@ select @@optimizer_trace|
 delimiter ;|
 
 set optimizer_trace="enabled=on";
-drop table optt;
+drop temporary table optt;
 drop function f1;
 drop procedure p1;
 drop trigger trg1;

=== modified file 'mysql-test/include/optimizer_trace2.inc'
--- a/mysql-test/include/optimizer_trace2.inc	2011-04-13 15:36:01 +0000
+++ b/mysql-test/include/optimizer_trace2.inc	2011-05-04 20:53:28 +0000
@@ -18,6 +18,8 @@ begin
   select 2 into res from dual;
   return 3;
 end|
+# ps-protocol specific note: as we asked to retain all traces,
+# we see the one of PREPARE too.
 select f1("c")|
 --echo
 # we should not see the trace of "select TRACE+NULL..."
@@ -187,8 +189,7 @@ SELECT POLYGON((SELECT 1 FROM (SELECT 1 
 DROP TABLE t1;
 
 --echo
---echo # Check that SQL PREPARE produces one statement, and
---echo # check that SQL EXECUTE produces two
+--echo # Check that SQL PREPARE and SQL EXECUTE each produce one trace.
 --echo
 set optimizer_trace_offset=0, optimizer_trace_limit=100;
 prepare stmt from "select 1";
@@ -200,11 +201,7 @@ deallocate prepare stmt;
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
 
 --echo
---echo # Test of SELECTs in IF in stored routine. IF is not a real
---echo # command: not in enum_sql_command, does not have a dedicated
---echo # call to mysql_execute_command() for it; IF rather executes as
---echo # a part of the routine's call (CALL, for a procedure). It is
---echo # thus traced or not, as the routine's call.
+--echo # Test of SELECTs in IF in stored routine.
 --echo # Same test for CASE WHEN.
 --echo
 create table t1 (a int);
@@ -228,15 +225,55 @@ begin
 end|
 delimiter ;|
 set optimizer_trace_offset=0, optimizer_trace_limit=100;
+set optimizer_trace_max_mem_size=20000;
 call p1();
-# IF EXISTS() are traced because part of CALL,
-# same for first IF (SELECT) and for
-# CASE (SELECT).
 # SET @a=(SELECT) is not traced because part of SET
 # which is a real command and not traced.
 select * from information_schema.OPTIMIZER_TRACE;
 select * from t1;
 select @a,@b;
+set optimizer_trace_max_mem_size=default;
+drop procedure p1;
+drop table t1;
+
+--echo
+--echo # Test that in a non-traced SQL command, substatements can be
+--echo # traced
+--echo
+
+create table t1(a int);
+insert into t1 values(1),(2);
+delimiter |;
+create function f1() returns int
+begin
+  declare b int;
+  select 48 into b from dual;
+  select a into b from t1 limit 1;
+  insert into t1 values(b*10);
+  return 36;
+end|
+delimiter ;|
+set optimizer_trace_offset=0, optimizer_trace_limit=100;
+do f1(); # DO is never traced
+select * from information_schema.OPTIMIZER_TRACE;
+drop function f1;
+drop table t1;
+
+--echo
+--echo # Test of tracing of subquery used in parameter of routine call
+--echo
+create table t1(a int);
+insert into t1 values(1),(2);
+delimiter |;
+create procedure p1(x int)
+begin
+  declare b int;
+  set b=(select 2+x from dual);
+end|
+delimiter ;|
+set optimizer_trace_offset=0, optimizer_trace_limit=100;
+call p1((select a from t1 limit 1));
+select * from information_schema.OPTIMIZER_TRACE;
 drop procedure p1;
 drop table t1;
 set optimizer_trace_offset=default, optimizer_trace_limit=default;

=== added file 'mysql-test/include/optimizer_trace_security.inc'
--- a/mysql-test/include/optimizer_trace_security.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/optimizer_trace_security.inc	2011-05-04 20:53:28 +0000
@@ -0,0 +1,739 @@
+# Test that trace does not show information forbidden
+# by lack of privileges.
+
+--source include/have_optimizer_trace.inc
+
+connection default;
+let $DEFAULT_TRACE_MEM_SIZE=1048576; # 1MB
+set @old_size = @@global.optimizer_trace_max_mem_size;
+eval set global optimizer_trace_max_mem_size=$DEFAULT_TRACE_MEM_SIZE;
+
+connection default;
+select user();
+create database somedb;
+use somedb;
+create table t1(a varchar(100));
+insert into t1 values("first");
+create table t2(a varchar(100));
+insert into t2 values("first");
+create table t3(a varchar(100));
+insert into t3 values("first");
+delimiter |;
+create procedure p1() sql security definer
+begin
+  declare b int;
+  if (select count(*) from t1)
+  then
+    select 22 into b from dual;
+  end if;
+  select a into b from t1 limit 1;
+  insert into t1 values(current_user());
+end|
+create function f1() returns int sql security definer
+begin
+  declare b int;
+  select 48 into b from dual;
+  select a into b from t1 limit 1;
+  insert into t1 values(current_user());
+  return 36;
+end|
+create trigger trg2 before insert on t2 for each row 
+begin
+  insert into t3 select * from t3;
+end|
+delimiter ;|
+create sql security definer view v1 as select * from t1;
+create user user1@localhost identified by '';
+grant all on *.* to user1@localhost with grant option;
+connect (con_user1, localhost, user1,, somedb);
+
+--echo
+connection con_user1;
+select user();
+set optimizer_trace="enabled=on";
+show grants;
+
+--echo
+--echo # ==========================================================
+--echo # Part A.
+--echo # Test that security context changes are allowed when, and only
+--echo # when, invoker has all global privileges.
+--echo # ==========================================================
+--echo
+
+--echo # Because invoker has all global privileges, all traces are visible:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # this SET always purges all remembered traces
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # Show that really all global privileges are needed: let root
+--echo # revoke just one from user1. Because user1 does not have all global
+--echo # privileges anymore, security context changes are forbidden,
+--echo # thus there is no trace.
+--echo
+
+connection default;
+select user();
+revoke shutdown on *.* from user1@localhost;
+# removing a global privilege never affects an existing connection:
+disconnect con_user1;
+connect (con_user1, localhost, user1,, somedb);
+
+--echo
+connection con_user1;
+select user();
+set optimizer_trace="enabled=on";
+show grants;
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+--echo # In CALL we execute stored procedure and notice a security
+--echo # context change. The context change is probably only relevant
+--echo # for substatements, but we still hide CALL. This is to be
+--echo # consistent with what we do when routine body should not be
+--echo # exposed. And it also feels safer to disable I_S output as
+--echo # soon as possible.
+--echo # Ps-protocol-specific note: mysqltest uses normal protocol for CALL
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # Verify that user1 cannot circumvent security checks by
+--echo # setting @@optimizer_trace_offset so that I_S output is disabled
+--echo # before the object (routine) is checked, and enabled in the
+--echo # middle of object usage, when 'offset' is passed.
+--echo
+
+set optimizer_trace_offset=2,optimizer_trace_limit=1;
+call p1();
+--echo # Even though the routine's execution started before
+--echo # 'offset', it detected the security context changes. So the
+--echo # trace of CALL gets the "missing privilege" mark but we don't
+--echo # see it as CALL was before 'offset'.
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # Finally, verify that if the routine's definer does modify
+--echo # @@optimizer_trace from "enabled=off" to "enabled=on", in the
+--echo # body of the routine, then tracing works. This is no security
+--echo # issue, as it was done by the routine's definer.
+--echo
+
+connection default;
+select user();
+delimiter |;
+create procedure p2() sql security definer
+begin
+  declare b int;
+  set optimizer_trace="enabled=on";
+  select 22 into b from dual;
+end|
+delimiter ;|
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace="enabled=off";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p2();
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Variable is as set by the routine
+select @@optimizer_trace;
+
+--echo
+--echo # ==========================================================
+--echo # Part B.
+--echo # Do same tests but with SQL SECURITY INVOKER objects, to verify that
+--echo # the restriction on security context changes is not present.
+--echo # ==========================================================
+--echo
+
+connection default;
+select user();
+alter procedure p1 sql security invoker;
+alter function f1 sql security invoker;
+alter sql security invoker view v1 as select * from t1;
+--echo # Triggers cannot be SQL SECURITY INVOKER so we don't test
+--echo # them here.
+alter procedure p2 sql security invoker;
+delete from t1 where a<>"first";
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=2,optimizer_trace_limit=1;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace="enabled=off";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p2();
+--echo # SELECT substatement is traced (no security context change)
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+select @@optimizer_trace;
+
+--echo
+--echo # ==========================================================
+--echo # Part C.
+--echo # User1 got traces. Determine the minimum set of privileges he
+--echo # needed for that.
+--echo # ==========================================================
+--echo
+
+connection default;
+drop procedure p2; # p2 is not worth testing more
+select user();
+revoke all privileges, grant option from user1@localhost;
+--echo # Grant minimum privileges to use the routines and views,
+--echo # without considering optimizer trace:
+grant execute on procedure p1 to user1@localhost;
+grant execute on function f1 to user1@localhost;
+grant select (a) on v1 to user1@localhost;
+--echo # Objects above are SQL SECURITY INVOKER, so invoker needs
+--echo # privileges on objects used internally:
+grant select (a) on t1 to user1@localhost;
+grant insert (a) on t1 to user1@localhost;
+delete from t1 where a<>"first";
+disconnect con_user1;
+connect (con_user1, localhost, user1,, somedb);
+
+--echo
+connection con_user1;
+select user();
+set optimizer_trace="enabled=on";
+show grants;
+
+--echo
+--echo # Those privileges are not enough to see traces:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+--echo # In CALL we execute stored procedure and notice that body should
+--echo # not be exposed. The trace of this CALL would not expose the
+--echo # body. Trace of substatements would. But, due to
+--echo # implementation, CALL is hidden.
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+--echo # SELECT is hidden (same reason as for CALL).
+--echo # Ps-protocol-specific note: preparation of SELECT above does not
+--echo # execute f1, so does not risk exposing body, so its trace is
+--echo # visible.
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+--echo # Cannot see anything as it would expose body of view
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # C.0) Add more privileges:
+--echo
+
+connection default;
+select user();
+--echo # - for use of t1 in routines and view:
+grant select on t1 to user1@localhost;
+--echo # - for use of routines:
+grant select on mysql.proc to user1@localhost;
+--echo # - for use of view:
+grant select, show view on v1 to user1@localhost;
+delete from t1 where a<>"first";
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Trace exposed body of routine, and content of t1, which we
+--echo # could see anyway:
+show create procedure p1;
+select * from t1 limit 1;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Trace exposed body of routine, and content of t1, which we
+--echo # could see anyway:
+show create function f1;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Trace exposed body of view, and content of t1, which we
+--echo # could see anyway:
+show create view v1;
+
+--echo
+--echo # Now remove each privilege to verify that it was needed:
+--echo # C.1) remove table-level SELECT privilege on t1
+--echo
+connection default;
+select user();
+revoke select on t1 from user1@localhost;
+grant select (a) on t1 to user1@localhost;
+delete from t1 where a<>"first";
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+--echo # Cannot see those substatements which use t1
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+--echo # Cannot see those substatements which use t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Trace exposed body of routine, which we could see anyway:
+set optimizer_trace="enabled=off";
+show create function f1;
+set optimizer_trace="enabled=on";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+--echo # Cannot see anything as it might expose some data from columns
+--echo # of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # C.2) remove table-level SELECT privilege on mysql.proc
+--echo
+
+connection default;
+select user();
+--echo # Put back privilege removed in C.1
+grant select on t1 to user1@localhost;
+--echo # And remove a next one:
+revoke select on mysql.proc from user1@localhost;
+delete from t1 where a<>"first";
+
+--echo
+connection con_user1;
+select user();
+
+--echo # We have no right to see routines' bodies:
+set optimizer_trace="enabled=off";
+show create procedure p1;
+show create function f1;
+--echo # Verify that optimizer trace does not influence the privilege
+--echo # checking in SHOW CREATE:
+set optimizer_trace="enabled=on";
+show create procedure p1;
+show create function f1;
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+--echo # Cannot see anything as it would expose body of routine
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # C.3) remove table-level SELECT privilege on view
+--echo
+
+connection default;
+select user();
+--echo # Put back privilege removed in C.2
+grant select on mysql.proc to user1@localhost;
+--echo # And remove a next one:
+revoke select on v1 from user1@localhost;
+grant select (a) on v1 to user1@localhost;
+delete from t1 where a<>"first";
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+--echo # Cannot see anything as it might expose some data from columns
+--echo # of v1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # C.4) remove SHOW VIEW privilege on view
+--echo
+
+connection default;
+select user();
+--echo # Put back privilege removed in C.3
+grant select on v1 to user1@localhost;
+--echo # And remove a next one:
+revoke show view on v1 from user1@localhost;
+delete from t1 where a<>"first";
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace="enabled=off";
+--echo # We have no right to see view's body:
+--error ER_TABLEACCESS_DENIED_ERROR
+show create view v1;
+set optimizer_trace="enabled=on";
+--echo # Verify that optimizer trace does not influence the privilege
+--echo # checking in SHOW CREATE:
+--error ER_TABLEACCESS_DENIED_ERROR
+show create view v1;
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+--echo # Cannot see anything as it would expose body of view
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # ==========================================================
+--echo # Part D.
+--echo # Like Part C, but instead of SQL SECURITY INVOKER objects
+--echo # created by root and used by User1, let's have SQL SECURITY
+--echo # DEFINER objects created and used by User1. Determine the
+--echo # minimum set of privileges he needs for that.
+--echo # ==========================================================
+--echo
+
+connection default;
+select user();
+drop procedure p1;
+drop function f1;
+drop view v1;
+drop trigger trg2;
+revoke all privileges, grant option from user1@localhost;
+--echo # Grant minimum privileges to create and use objects,
+--echo # without considering optimizer trace:
+grant create routine on somedb.* to user1@localhost;
+grant trigger on t2 to user1@localhost;
+grant create view on somedb.* to user1@localhost;
+grant select (a) on t1 to user1@localhost;
+grant insert (a) on t1 to user1@localhost;
+grant insert (a) on t2 to user1@localhost;
+grant select (a) on t3 to user1@localhost;
+grant insert (a) on t3 to user1@localhost;
+delete from t1 where a<>"first";
+disconnect con_user1;
+connect (con_user1, localhost, user1,, somedb);
+
+--echo
+connection con_user1;
+select user();
+set optimizer_trace="enabled=on";
+
+delimiter |;
+create procedure p1() sql security definer
+begin
+  declare b int;
+  if (select count(*) from t1)
+  then
+    select 22 into b from dual;
+  end if;
+  select a into b from t1 limit 1;
+  insert into t1 values(current_user());
+end|
+create function f1() returns int sql security definer
+begin
+  declare b int;
+  select 48 into b from dual;
+  select a into b from t1 limit 1;
+  insert into t1 values(current_user());
+  return 36;
+end|
+create trigger trg2 before insert on t2 for each row 
+begin
+  insert into t3 select * from t3;
+end|
+delimiter ;|
+create sql security definer view v1 as select * from t1;
+
+--echo # Creating a view is not enough to be able to SELECT it...
+connection default;
+select user();
+grant select (a) on v1 to user1@localhost;
+
+--echo
+connection con_user1;
+select user();
+
+--echo # Those privileges are not enough to see traces:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+--echo # Can see body of routine (as definer), but not statements using t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+--echo # Can see body of routine (as definer), but not statements using t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+show create function f1;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+--echo # Cannot see anything as it might expose some data from columns
+--echo # of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+--echo # Cannot see anything as it might expose some data from
+--echo # columns of t2
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Also test a query accessing t1 in FROM clause:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select a from (select a from t1 where a like "f%") as tt where a like "fi%";
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # D.0) Add more privileges:
+--echo
+
+connection default;
+select user();
+--echo # - for use of t1 in routines and view:
+grant select on t1 to user1@localhost;
+--echo # - for use of view:
+grant select, show view on v1 to user1@localhost;
+--echo # - for use of trigger
+grant select on t2 to user1@localhost;
+grant select on t3 to user1@localhost;
+delete from t1 where a<>"first";
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Trace exposed body of routine, and content of t1, which we
+--echo # could see anyway:
+show create procedure p1;
+select * from t1 limit 1;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Trace exposed body of routine, and content of t1, which we
+--echo # could see anyway:
+show create function f1;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Trace exposed body of view, and content of t1, which we
+--echo # could see anyway:
+show create view v1;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+--echo # Trace exposed body of trigger, and content of t2/t3, which we
+--echo # could see anyway:
+show create trigger trg2;
+select * from t2, t3 limit 1;
+--echo # Trace exposed content of t1 which we could see anyway:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select a from (select a from t1 where a like "f%") as tt where a like "fi%";
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # For routines, as they only use t1 and we added only one
+--echo # privilege on t1, we have nothing to remove.
+--echo
+--echo # Now remove each privilege to verify that it was needed for
+--echo # the view.
+--echo # D.1) remove table-level SELECT privilege on v1
+--echo
+
+connection default;
+select user();
+
+revoke select on v1 from user1@localhost;
+grant select (a) on v1 to user1@localhost;
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+--echo # Cannot see anything as it might expose some data from columns
+--echo # of v1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # D.2) remove table-level SHOW VIEW privilege on v1
+--echo
+
+connection default;
+select user();
+
+--echo # Put back privilege removed in D.1
+grant select on v1 to user1@localhost;
+--echo # And remove a next one:
+revoke show view on v1 from user1@localhost;
+
+--echo
+connection con_user1;
+select user();
+
+--echo # We have no right to see view's body:
+--error ER_TABLEACCESS_DENIED_ERROR
+show create view v1;
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+--echo # Cannot see anything as it would expose body of view
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # D.3) remove table-level SELECT privilege on t1
+--echo
+
+connection default;
+select user();
+
+--echo # Put back privilege removed in D.2
+grant show view on v1 to user1@localhost;
+--echo # And remove a next one:
+revoke select on t1 from user1@localhost;
+grant select (a) on t1 to user1@localhost;
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+--echo # Cannot see anything as it might expose some data from columns
+--echo # of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # Now remove each privilege to verify that it was needed for
+--echo # the trigger:
+--echo # D.4) remove table-level SELECT privilege on t2
+--echo
+
+connection default;
+select user();
+
+revoke select on t2 from user1@localhost;
+grant select (a) on t2 to user1@localhost;
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+--echo # Cannot see anything as it might expose some data from
+--echo # columns of t2
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # D.5) remove table-level SELECT privilege on t3
+--echo
+
+--echo
+connection default;
+select user();
+
+--echo # Put back privilege removed in D.4
+grant select on t2 to user1@localhost;
+--echo # And remove a next one:
+revoke select on t3 from user1@localhost;
+grant select (a) on t3 to user1@localhost;
+
+--echo
+connection con_user1;
+select user();
+
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+--echo # Cannot see substatement as it might expose some data from
+--echo # columns of t3
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # Cleanup
+connection default;
+select user();
+drop user user1@localhost;
+disconnect con_user1;
+
+--echo
+--echo # ==========================================================
+--echo # Part E.
+--echo # Misc tests.
+--echo # ==========================================================
+--echo
+
+connection default;
+select user();
+drop view v1;
+create sql security definer view v1 as select * from t1 where 'secret';
+create user user1@localhost identified by '';
+grant create, insert, select on somedb.* to user1@localhost;
+grant create routine on somedb.* to user1@localhost;
+connect (con_user1, localhost, user1,, somedb);
+
+--echo
+connection con_user1;
+select user();
+
+--echo user1 cannot see view's body:
+--error 1142
+show create view v1;
+
+--echo user1 creates a procedure
+delimiter |;
+create procedure proc() sql security definer
+begin
+  set optimizer_trace="enabled=on";
+  set optimizer_trace_offset=0,optimizer_trace_limit=100;
+  select * from v1 limit 0;
+  create table leak select * from information_schema.optimizer_trace;
+  set optimizer_trace="enabled=off";
+end|
+delimiter ;|
+
+connection default;
+select user();
+
+--echo root runs procedure, without fear of risk as it is SQL SECURITY DEFINER
+call proc();
+
+--echo
+connection con_user1;
+select user();
+--echo user1 cannot see view's body:
+select * from leak;
+
+--echo
+--echo # Cleanup
+connection default;
+select user();
+drop database somedb;
+drop user user1@localhost;
+set @@global.optimizer_trace_max_mem_size = @old_size;

=== modified file 'mysql-test/r/optimizer_trace2_no_prot.result'
--- a/mysql-test/r/optimizer_trace2_no_prot.result	2011-04-13 15:36:01 +0000
+++ b/mysql-test/r/optimizer_trace2_no_prot.result	2011-05-04 20:53:28 +0000
@@ -17,7 +17,7 @@ f1("c")
 3
 
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select f1("c")	{
   "steps": [
     {
@@ -45,7 +45,15 @@ select f1("c")	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+set res@1 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set dummy@2 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 select 1 into res from dual	{
   "steps": [
     {
@@ -73,7 +81,7 @@ select 1 into res from dual	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select 2 into res from dual	{
   "steps": [
     {
@@ -101,7 +109,11 @@ select 2 into res from dual	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 3	{
+  "steps": [
+  ] /* steps */
+}	0	0
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
 drop function f1;
 # check that if a tracing gets disabled in a routine's  body,
@@ -123,7 +135,7 @@ f1("c")
 3
 
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select f1("c")	{
   "steps": [
     {
@@ -151,7 +163,15 @@ select f1("c")	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+set res@1 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set dummy@2 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
 select @@optimizer_trace;
 @@optimizer_trace
@@ -1163,17 +1183,12 @@ SELECT POLYGON((SELECT 1 FROM (SELECT 1 
 ERROR 22007: Illegal non geometric '(select 1 from (select (1 = group_concat(`test`.`t1`.`f1` separator ',')) AS `1 IN (GROUP_CONCAT(t1.f1))` from `test`.`t1` join `test`.`t1` `t` group by `test`.`t`.`f1`) `d`)' value found during parsing
 DROP TABLE t1;
 
-# Check that SQL PREPARE produces one statement, and
-# check that SQL EXECUTE produces two
+# Check that SQL PREPARE and SQL EXECUTE each produce one trace.
 
 set optimizer_trace_offset=0, optimizer_trace_limit=100;
 prepare stmt from "select 1";
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-prepare stmt from "select 1"	{
-  "steps": [
-  ] /* steps */
-}	0
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 1	{
   "steps": [
     {
@@ -1187,17 +1202,13 @@ select 1	{
       } /* join_preparation */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace_offset=0, optimizer_trace_limit=100;
 execute stmt;
 1
 1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-execute stmt	{
-  "steps": [
-  ] /* steps */
-}	0
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 1	{
   "steps": [
     {
@@ -1225,15 +1236,11 @@ select 1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 deallocate prepare stmt;
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
 
-# Test of SELECTs in IF in stored routine. IF is not a real
-# command: not in enum_sql_command, does not have a dedicated
-# call to mysql_execute_command() for it; IF rather executes as
-# a part of the routine's call (CALL, for a procedure). It is
-# thus traced or not, as the routine's call.
+# Test of SELECTs in IF in stored routine.
 # Same test for CASE WHEN.
 
 create table t1 (a int);
@@ -1255,11 +1262,16 @@ else set @b=3;
 end case;
 end|
 set optimizer_trace_offset=0, optimizer_trace_limit=100;
+set optimizer_trace_max_mem_size=20000;
 call p1();
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 call p1()	{
   "steps": [
+  ] /* steps */
+}	0	0
+jump_if_not 2(2) exists(select 1)	{
+  "steps": [
     {
       "join_preparation": {
         "select#": 2,
@@ -1290,7 +1302,15 @@ call p1()	{
           }
         ] /* steps */
       } /* subselect_execution */
-    },
+    }
+  ] /* steps */
+}	0	0
+insert into t1 values(1)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+jump_if_not 4(4) exists(select 2)	{
+  "steps": [
     {
       "join_preparation": {
         "select#": 3,
@@ -1321,7 +1341,15 @@ call p1()	{
           }
         ] /* steps */
       } /* subselect_execution */
-    },
+    }
+  ] /* steps */
+}	0	0
+insert into t1 values(2)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+jump_if_not 6(6) (select count(0) from `test`.`t1`)	{
+  "steps": [
     {
       "join_preparation": {
         "select#": 4,
@@ -1355,7 +1383,15 @@ call p1()	{
           }
         ] /* steps */
       } /* subselect_execution */
-    },
+    }
+  ] /* steps */
+}	0	0
+insert into t1 values(3)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set_case_expr (15) 0 (select count(`a`) from `test`.`t1` where (`a` > 1))	{
+  "steps": [
     {
       "join_preparation": {
         "select#": 6,
@@ -1469,19 +1505,11 @@ call p1()	{
       } /* subselect_execution */
     }
   ] /* steps */
-}	0
-insert into t1 values(1)	{
+}	0	0
+jump_if_not 11(15) (case_expr@0 = 2)	{
   "steps": [
   ] /* steps */
-}	0
-insert into t1 values(2)	{
-  "steps": [
-  ] /* steps */
-}	0
-insert into t1 values(3)	{
-  "steps": [
-  ] /* steps */
-}	0
+}	0	0
 select * from t1;
 a
 1
@@ -1490,6 +1518,299 @@ a
 select @a,@b;
 @a	@b
 3	2
+set optimizer_trace_max_mem_size=default;
+drop procedure p1;
+drop table t1;
+
+# Test that in a non-traced SQL command, substatements can be
+# traced
+
+create table t1(a int);
+insert into t1 values(1),(2);
+create function f1() returns int
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(b*10);
+return 36;
+end|
+set optimizer_trace_offset=0, optimizer_trace_limit=100;
+do f1();
+select * from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+set b@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+select 48 into b from dual	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select 48 AS `48`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+select a into b from t1 limit 1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` limit 1"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0034,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4034,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+insert into t1 values( NAME_CONST('b',1)*10)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+freturn 3 36	{
+  "steps": [
+  ] /* steps */
+}	0	0
+drop function f1;
+drop table t1;
+
+# Test of tracing of subquery used in parameter of routine call
+
+create table t1(a int);
+insert into t1 values(1),(2);
+create procedure p1(x int)
+begin
+declare b int;
+set b=(select 2+x from dual);
+end|
+set optimizer_trace_offset=0, optimizer_trace_limit=100;
+call p1((select a from t1 limit 1));
+select * from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+call p1((select a from t1 limit 1))	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 2,
+        "steps": [
+          {
+            "expanded_query": "/* select#2 */ select `test`.`t1`.`a` from `test`.`t1` limit 1"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "subselect_execution": {
+        "select#": 2,
+        "steps": [
+          {
+            "join_optimization": {
+              "select#": 2,
+              "steps": [
+                {
+                  "records_estimation": [
+                    {
+                      "database": "test",
+                      "table": "t1",
+                      "table_scan": {
+                        "records": 2,
+                        "cost": 2
+                      } /* table_scan */
+                    }
+                  ] /* records_estimation */
+                },
+                {
+                  "considered_execution_plans": [
+                    {
+                      "database": "test",
+                      "table": "t1",
+                      "best_access_path": {
+                        "considered_access_paths": [
+                          {
+                            "access_type": "scan",
+                            "using_join_cache": true,
+                            "records": 2,
+                            "cost": 2.0034,
+                            "chosen": true
+                          }
+                        ] /* considered_access_paths */
+                      } /* best_access_path */,
+                      "cost_for_plan": 2.4034,
+                      "records_for_plan": 2,
+                      "chosen": true
+                    }
+                  ] /* considered_execution_plans */
+                },
+                {
+                  "attaching_conditions_to_tables": {
+                    "original_condition": null,
+                    "attached_conditions_computation": [
+                    ] /* attached_conditions_computation */,
+                    "attached_conditions_summary": [
+                      {
+                        "database": "test",
+                        "table": "t1",
+                        "attached": null
+                      }
+                    ] /* attached_conditions_summary */
+                  } /* attaching_conditions_to_tables */
+                },
+                {
+                  "refine_plan": [
+                    {
+                      "database": "test",
+                      "table": "t1",
+                      "scan_type": "table"
+                    }
+                  ] /* refine_plan */
+                }
+              ] /* steps */
+            } /* join_optimization */
+          },
+          {
+            "join_execution": {
+              "select#": 2,
+              "steps": [
+              ] /* steps */
+            } /* join_execution */
+          }
+        ] /* steps */
+      } /* subselect_execution */
+    }
+  ] /* steps */
+}	0	0
+set b@1 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set b@1 (select (2 + x@0))	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 3,
+        "steps": [
+          {
+            "expanded_query": "/* select#3 */ select (2 + x@0)"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "subselect_execution": {
+        "select#": 3,
+        "steps": [
+          {
+            "join_optimization": {
+              "select#": 3,
+              "steps": [
+              ] /* steps */
+            } /* join_optimization */
+          },
+          {
+            "join_execution": {
+              "select#": 3,
+              "steps": [
+              ] /* steps */
+            } /* join_execution */
+          }
+        ] /* steps */
+      } /* subselect_execution */
+    }
+  ] /* steps */
+}	0	0
 drop procedure p1;
 drop table t1;
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
@@ -1505,7 +1826,7 @@ concat(concat(_latin1'->',f1),_latin1'<-
 -><-
 -><-
 select * from information_schema.optimizer_trace;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select concat(concat(_latin1'->',f1),_latin1'<-') from t1	{
   "steps": [
     {
@@ -1590,5 +1911,5 @@ select concat(concat(_latin1'->',f1),_la
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 drop table t1;

=== modified file 'mysql-test/r/optimizer_trace2_ps_prot.result'
--- a/mysql-test/r/optimizer_trace2_ps_prot.result	2011-04-13 15:36:01 +0000
+++ b/mysql-test/r/optimizer_trace2_ps_prot.result	2011-05-04 20:53:28 +0000
@@ -17,7 +17,7 @@ f1("c")
 3
 
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select f1("c")	{
   "steps": [
     {
@@ -31,7 +31,7 @@ select f1("c")	{
       } /* join_preparation */
     }
   ] /* steps */
-}	0
+}	0	0
 select f1("c")	{
   "steps": [
     {
@@ -59,7 +59,15 @@ select f1("c")	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+set res@1 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set dummy@2 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 select 1 into res from dual	{
   "steps": [
     {
@@ -87,7 +95,7 @@ select 1 into res from dual	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select 2 into res from dual	{
   "steps": [
     {
@@ -115,7 +123,11 @@ select 2 into res from dual	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 3	{
+  "steps": [
+  ] /* steps */
+}	0	0
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
 drop function f1;
 # check that if a tracing gets disabled in a routine's  body,
@@ -137,7 +149,7 @@ f1("c")
 3
 
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select f1("c")	{
   "steps": [
     {
@@ -151,7 +163,7 @@ select f1("c")	{
       } /* join_preparation */
     }
   ] /* steps */
-}	0
+}	0	0
 select f1("c")	{
   "steps": [
     {
@@ -179,7 +191,15 @@ select f1("c")	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+set res@1 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set dummy@2 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
 select @@optimizer_trace;
 @@optimizer_trace
@@ -1183,17 +1203,12 @@ SELECT POLYGON((SELECT 1 FROM (SELECT 1 
 ERROR 22007: Illegal non geometric '(select 1 from (select (1 = group_concat(`test`.`t1`.`f1` separator ',')) AS `1 IN (GROUP_CONCAT(t1.f1))` from `test`.`t1` join `test`.`t1` `t` group by `test`.`t`.`f1`) `d`)' value found during parsing
 DROP TABLE t1;
 
-# Check that SQL PREPARE produces one statement, and
-# check that SQL EXECUTE produces two
+# Check that SQL PREPARE and SQL EXECUTE each produce one trace.
 
 set optimizer_trace_offset=0, optimizer_trace_limit=100;
 prepare stmt from "select 1";
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-prepare stmt from "select 1"	{
-  "steps": [
-  ] /* steps */
-}	0
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 1	{
   "steps": [
     {
@@ -1207,17 +1222,13 @@ select 1	{
       } /* join_preparation */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace_offset=0, optimizer_trace_limit=100;
 execute stmt;
 1
 1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-execute stmt	{
-  "steps": [
-  ] /* steps */
-}	0
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 1	{
   "steps": [
     {
@@ -1245,15 +1256,11 @@ select 1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 deallocate prepare stmt;
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
 
-# Test of SELECTs in IF in stored routine. IF is not a real
-# command: not in enum_sql_command, does not have a dedicated
-# call to mysql_execute_command() for it; IF rather executes as
-# a part of the routine's call (CALL, for a procedure). It is
-# thus traced or not, as the routine's call.
+# Test of SELECTs in IF in stored routine.
 # Same test for CASE WHEN.
 
 create table t1 (a int);
@@ -1275,11 +1282,16 @@ else set @b=3;
 end case;
 end|
 set optimizer_trace_offset=0, optimizer_trace_limit=100;
+set optimizer_trace_max_mem_size=20000;
 call p1();
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 call p1()	{
   "steps": [
+  ] /* steps */
+}	0	0
+jump_if_not 2(2) exists(select 1)	{
+  "steps": [
     {
       "join_preparation": {
         "select#": 2,
@@ -1310,7 +1322,15 @@ call p1()	{
           }
         ] /* steps */
       } /* subselect_execution */
-    },
+    }
+  ] /* steps */
+}	0	0
+insert into t1 values(1)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+jump_if_not 4(4) exists(select 2)	{
+  "steps": [
     {
       "join_preparation": {
         "select#": 3,
@@ -1341,7 +1361,15 @@ call p1()	{
           }
         ] /* steps */
       } /* subselect_execution */
-    },
+    }
+  ] /* steps */
+}	0	0
+insert into t1 values(2)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+jump_if_not 6(6) (select count(0) from `test`.`t1`)	{
+  "steps": [
     {
       "join_preparation": {
         "select#": 4,
@@ -1375,7 +1403,15 @@ call p1()	{
           }
         ] /* steps */
       } /* subselect_execution */
-    },
+    }
+  ] /* steps */
+}	0	0
+insert into t1 values(3)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set_case_expr (15) 0 (select count(`a`) from `test`.`t1` where (`a` > 1))	{
+  "steps": [
     {
       "join_preparation": {
         "select#": 6,
@@ -1489,19 +1525,11 @@ call p1()	{
       } /* subselect_execution */
     }
   ] /* steps */
-}	0
-insert into t1 values(1)	{
+}	0	0
+jump_if_not 11(15) (case_expr@0 = 2)	{
   "steps": [
   ] /* steps */
-}	0
-insert into t1 values(2)	{
-  "steps": [
-  ] /* steps */
-}	0
-insert into t1 values(3)	{
-  "steps": [
-  ] /* steps */
-}	0
+}	0	0
 select * from t1;
 a
 1
@@ -1510,6 +1538,299 @@ a
 select @a,@b;
 @a	@b
 3	2
+set optimizer_trace_max_mem_size=default;
+drop procedure p1;
+drop table t1;
+
+# Test that in a non-traced SQL command, substatements can be
+# traced
+
+create table t1(a int);
+insert into t1 values(1),(2);
+create function f1() returns int
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(b*10);
+return 36;
+end|
+set optimizer_trace_offset=0, optimizer_trace_limit=100;
+do f1();
+select * from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+set b@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+select 48 into b from dual	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select 48 AS `48`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+select a into b from t1 limit 1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` limit 1"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0034,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4034,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+insert into t1 values( NAME_CONST('b',1)*10)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+freturn 3 36	{
+  "steps": [
+  ] /* steps */
+}	0	0
+drop function f1;
+drop table t1;
+
+# Test of tracing of subquery used in parameter of routine call
+
+create table t1(a int);
+insert into t1 values(1),(2);
+create procedure p1(x int)
+begin
+declare b int;
+set b=(select 2+x from dual);
+end|
+set optimizer_trace_offset=0, optimizer_trace_limit=100;
+call p1((select a from t1 limit 1));
+select * from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+call p1((select a from t1 limit 1))	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 2,
+        "steps": [
+          {
+            "expanded_query": "/* select#2 */ select `test`.`t1`.`a` from `test`.`t1` limit 1"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "subselect_execution": {
+        "select#": 2,
+        "steps": [
+          {
+            "join_optimization": {
+              "select#": 2,
+              "steps": [
+                {
+                  "records_estimation": [
+                    {
+                      "database": "test",
+                      "table": "t1",
+                      "table_scan": {
+                        "records": 2,
+                        "cost": 2
+                      } /* table_scan */
+                    }
+                  ] /* records_estimation */
+                },
+                {
+                  "considered_execution_plans": [
+                    {
+                      "database": "test",
+                      "table": "t1",
+                      "best_access_path": {
+                        "considered_access_paths": [
+                          {
+                            "access_type": "scan",
+                            "using_join_cache": true,
+                            "records": 2,
+                            "cost": 2.0034,
+                            "chosen": true
+                          }
+                        ] /* considered_access_paths */
+                      } /* best_access_path */,
+                      "cost_for_plan": 2.4034,
+                      "records_for_plan": 2,
+                      "chosen": true
+                    }
+                  ] /* considered_execution_plans */
+                },
+                {
+                  "attaching_conditions_to_tables": {
+                    "original_condition": null,
+                    "attached_conditions_computation": [
+                    ] /* attached_conditions_computation */,
+                    "attached_conditions_summary": [
+                      {
+                        "database": "test",
+                        "table": "t1",
+                        "attached": null
+                      }
+                    ] /* attached_conditions_summary */
+                  } /* attaching_conditions_to_tables */
+                },
+                {
+                  "refine_plan": [
+                    {
+                      "database": "test",
+                      "table": "t1",
+                      "scan_type": "table"
+                    }
+                  ] /* refine_plan */
+                }
+              ] /* steps */
+            } /* join_optimization */
+          },
+          {
+            "join_execution": {
+              "select#": 2,
+              "steps": [
+              ] /* steps */
+            } /* join_execution */
+          }
+        ] /* steps */
+      } /* subselect_execution */
+    }
+  ] /* steps */
+}	0	0
+set b@1 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set b@1 (select (2 + x@0))	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 3,
+        "steps": [
+          {
+            "expanded_query": "/* select#3 */ select (2 + x@0)"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "subselect_execution": {
+        "select#": 3,
+        "steps": [
+          {
+            "join_optimization": {
+              "select#": 3,
+              "steps": [
+              ] /* steps */
+            } /* join_optimization */
+          },
+          {
+            "join_execution": {
+              "select#": 3,
+              "steps": [
+              ] /* steps */
+            } /* join_execution */
+          }
+        ] /* steps */
+      } /* subselect_execution */
+    }
+  ] /* steps */
+}	0	0
 drop procedure p1;
 drop table t1;
 set optimizer_trace_offset=default, optimizer_trace_limit=default;
@@ -1525,7 +1846,7 @@ concat(concat(_latin1'->',f1),_latin1'<-
 -><-
 -><-
 select * from information_schema.optimizer_trace;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select concat(concat(_latin1'->',f1),_latin1'<-') from t1	{
   "steps": [
     {
@@ -1610,5 +1931,5 @@ select concat(concat(_latin1'->',f1),_la
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 drop table t1;

=== removed file 'mysql-test/r/optimizer_trace_bugs.result'
--- a/mysql-test/r/optimizer_trace_bugs.result	2010-08-09 15:09:55 +0000
+++ b/mysql-test/r/optimizer_trace_bugs.result	1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-select 1;
-1
-1

=== modified file 'mysql-test/r/optimizer_trace_debug.result'
--- a/mysql-test/r/optimizer_trace_debug.result	2011-03-08 07:18:14 +0000
+++ b/mysql-test/r/optimizer_trace_debug.result	2011-05-04 20:53:28 +0000
@@ -1,27 +1,21 @@
 # We want to make sure that the common case of
-# a connection which has not enabled tracing, or is
-# running SQL commands which are excluded from
-# tracing, or commands which use I_S.OPTIMIZER_TRACE,
-# outside of any tricky stored-routine scenarios tested
-# in optimizer_trace2.test, are optimized, i.e. no trace is
-# created, even a dummy internal one invisible in I_S.
-set debug="d,opt_trace_should_not_start";
+# a connection which has not enabled tracing,
+# is optimized, i.e. neither Opt_trace_context_impl nor
+# Opt_trace_context_stmt is created.
+set debug="d,no_new_opt_trace_stmt";
 select 1;
 1
 1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 set @a=25;
-set optimizer_trace="enabled=on";
-select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
 set debug="default";
+set optimizer_trace="enabled=on";
 select 2;
 2
 2
-set debug="d,opt_trace_should_not_start";
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 2	{
   "steps": [
     {
@@ -49,14 +43,14 @@ select 2	{
       }
     }
   ]
-}	0
-set @a=25;
+}	0	0
 set optimizer_trace="enabled=off";
+set debug="d,no_new_opt_trace_stmt";
 select 3;
 3
 3
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 2	{
   "steps": [
     {
@@ -84,5 +78,4 @@ select 2	{
       }
     }
   ]
-}	0
-set optimizer_trace=default;
+}	0	0

=== modified file 'mysql-test/r/optimizer_trace_no_prot.result'
--- a/mysql-test/r/optimizer_trace_no_prot.result	2011-03-21 17:55:41 +0000
+++ b/mysql-test/r/optimizer_trace_no_prot.result	2011-05-04 20:53:28 +0000
@@ -1,9 +1,9 @@
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 set optimizer_trace_max_mem_size=1048576;
 set @@session.optimizer_trace="enabled=on";
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 CREATE TABLE t5 (c int);
 INSERT INTO t5 VALUES (NULL);
 CREATE TABLE t6 (d int , KEY (d));
@@ -12,7 +12,7 @@ SELECT (SELECT 1 FROM t6 WHERE d = c) AS
 RESULT
 NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT (SELECT 1 FROM t6 WHERE d = c) AS RESULT FROM t5	{
   "steps": [
     {
@@ -167,7 +167,7 @@ SELECT (SELECT 1 FROM t6 WHERE d = c) AS
       }
     }
   ]
-}	0
+}	0	0
 select (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
 from information_schema.OPTIMIZER_TRACE;
 (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
@@ -177,8 +177,8 @@ SELECT (SELECT 1 FROM t6 WHERE d = c) AS
 RESULT
 NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-SELECT (SELECT 1 FROM t6 WHERE d = c) AS RESULT FROM t5	{"steps": [{"join_preparation": {"select#": 1,"steps": [{"join_preparation": {"select#": 2,"steps": [{"expanded_query": "/* select#2 */ select 1 from `test`.`t6` where (`test`.`t6`.`d` = `test`.`t5`.`c`)"}]}},{"expanded_query": "/* select#1 */ select (/* select#2 */ select 1 from `test`.`t6` where (`test`.`t6`.`d` = `test`.`t5`.`c`)) AS `RESULT` from `test`.`t5`"}]}},{"join_optimization": {"select#": 1,"steps": [{"records_estimation": [{"database": "test","table": "t5","records": 1,"cost": 1,"table_type": "system"}]},{"attaching_conditions_to_tables": {"original_condition": null,"attached_conditions_computation": [],"attached_conditions_summary": []}},{"refine_plan": []}]}},{"join_execution": {"select#": 1,"steps": [{"subselect_execution": {"select#": 2,"steps": [{"join_optimization": {"select#": 2,"steps": [{"condition_processing": {"condition": "WHERE","original_condition": "(`test`.`t6`.`d` = NULL)","steps": [{"transformation": "equality_propagation","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"},{"transformation": "constant_propagation","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"},{"transformation": "trivial_condition_removal","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"}]}},{"ref_optimizer_key_uses": [{"database": "test","table": "t6","field": "d","equals": "NULL","null_rejecting": true}]},{"records_estimation": [{"database": "test","table": "t6","range_analysis": {"table_scan": {"records": 2,"cost": 4.5034},"potential_range_indices": [{"index": "d","usable": true,"key_parts": ["d"]}],"best_covering_index_scan": {"index": "d","cost": 1.4233,"chosen": true},"setup_range_conditions": [{"impossible_condition": {"cause": "comparison_with_null_always_false"}}],"impossible_range": true},"records": 0,"cause": "impossible_where_condition"}]}],"empty_result": {"cause": "no matching row in const table"}}},{"join_execution": {"select#": 2,"steps": []}}]}}]}}]}	0
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+SELECT (SELECT 1 FROM t6 WHERE d = c) AS RESULT FROM t5	{"steps": [{"join_preparation": {"select#": 1,"steps": [{"join_preparation": {"select#": 2,"steps": [{"expanded_query": "/* select#2 */ select 1 from `test`.`t6` where (`test`.`t6`.`d` = `test`.`t5`.`c`)"}]}},{"expanded_query": "/* select#1 */ select (/* select#2 */ select 1 from `test`.`t6` where (`test`.`t6`.`d` = `test`.`t5`.`c`)) AS `RESULT` from `test`.`t5`"}]}},{"join_optimization": {"select#": 1,"steps": [{"records_estimation": [{"database": "test","table": "t5","records": 1,"cost": 1,"table_type": "system"}]},{"attaching_conditions_to_tables": {"original_condition": null,"attached_conditions_computation": [],"attached_conditions_summary": []}},{"refine_plan": []}]}},{"join_execution": {"select#": 1,"steps": [{"subselect_execution": {"select#": 2,"steps": [{"join_optimization": {"select#": 2,"steps": [{"condition_processing": {"condition": "WHERE","original_condition": "(`test`.`t6`.`d` = NULL)","steps": [{"transformation": "equality_propagation","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"},{"transformation": "constant_propagation","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"},{"transformation": "trivial_condition_removal","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"}]}},{"ref_optimizer_key_uses": [{"database": "test","table": "t6","field": "d","equals": "NULL","null_rejecting": true}]},{"records_estimation": [{"database": "test","table": "t6","range_analysis": {"table_scan": {"records": 2,"cost": 4.5034},"potential_range_indices": [{"index": "d","usable": true,"key_parts": ["d"]}],"best_covering_index_scan": {"index": "d","cost": 1.4233,"chosen": true},"setup_range_conditions": [{"impossible_condition": {"cause": "comparison_with_null_always_false"}}],"impossible_range": true},"records": 0,"cause": "impossible_where_condition"}]}],"empty_result": {"cause": "no matching row in const table"}}},{"join_execution": {"select#": 2,"steps": []}}]}}]}}]}	0	0
 select (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
 from information_schema.OPTIMIZER_TRACE;
 (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
@@ -189,7 +189,7 @@ id	select_type	table	type	possible_keys	
 1	PRIMARY	t5	system	NULL	NULL	NULL	NULL	1	
 2	DEPENDENT SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5	{
   "steps": [
     {
@@ -337,12 +337,12 @@ EXPLAIN SELECT (SELECT 1 FROM t6 WHERE d
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 SELECT /* should be last */ (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5 ;
 RESULT
 NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT /* should be last */ (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5	{
   "steps": [
     {
@@ -497,7 +497,7 @@ SELECT /* should be last */ (SELECT 1 FR
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 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 ;
 RESULT
@@ -510,7 +510,7 @@ SELECT (SELECT 1 FROM t6 WHERE d = ifnul
 RESULT
 NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT (SELECT 1 FROM t6 WHERE d = ifnull(c,null) UNION SELECT 2 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5	{
   "steps": [
     {
@@ -842,11 +842,11 @@ SELECT (SELECT 1 FROM t6 WHERE d = ifnul
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 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));
 c
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 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))	{
   "steps": [
     {
@@ -1164,7 +1164,7 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select (@query:=QUERY)+NULL, (@trace:=TRACE)+NULL from information_schema.OPTIMIZER_TRACE;
 (@query:=QUERY)+NULL	(@trace:=TRACE)+NULL
 NULL	NULL
@@ -1201,22 +1201,22 @@ select 1;
 1
 1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select 1		521
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+select 1		521	0
 set optimizer_trace_max_mem_size=0;
 select 1;
 1
 1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-		529
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+		529	0
 set optimizer_trace_max_mem_size=1048576;
 explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t5	system	NULL	NULL	NULL	NULL	1	
 1	PRIMARY	t6	ref	d	d	5	const	1	Using where; Using index; FirstMatch(t5)
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null)	{
   "steps": [
     {
@@ -1499,14 +1499,14 @@ explain SELECT c FROM t5 where c+1 in (s
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch="semijoin=off";
 explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t5	system	NULL	NULL	NULL	NULL	1	
 2	SUBQUERY	t6	ref	d	d	5	const	1	Using where; Using index
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null)	{
   "steps": [
     {
@@ -1772,7 +1772,7 @@ explain SELECT c FROM t5 where c+1 in (s
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch=default;
 CREATE TABLE t1 (s1 CHAR(5),
 s2 CHAR(5));
@@ -1784,7 +1784,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,`test`.`t1`.`s2` AS `s2` from `test`.`t1` where <nop>((`test`.`t1`.`s1` > (/* select#2 */ select min(`test`.`t1`.`s2`) from `test`.`t1`)))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select * from t1 where s1 > any (select s2 from t1)	{
   "steps": [
     {
@@ -1988,7 +1988,7 @@ explain extended select * from t1 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 explain extended select * from t1 where s1 > any (select max(s2) from t1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
@@ -1996,7 +1996,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,`test`.`t1`.`s2` AS `s2` from `test`.`t1` where <nop>((`test`.`t1`.`s1` > <min>(/* select#2 */ select max(`test`.`t1`.`s2`) from `test`.`t1`)))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select * from t1 where s1 > any (select max(s2) from t1)	{
   "steps": [
     {
@@ -2200,7 +2200,7 @@ explain extended select * from t1 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch="semijoin=off,materialization=off";
 explain extended select * from t1 where s1 in (select s2 from t1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
@@ -2209,7 +2209,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,`test`.`t1`.`s2` AS `s2` from `test`.`t1` where <in_optimizer>(`test`.`t1`.`s1`,<exists>(/* select#2 */ select 1 from `test`.`t1` where (<cache>(`test`.`t1`.`s1`) = `test`.`t1`.`s2`)))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select * from t1 where s1 in (select s2 from t1)	{
   "steps": [
     {
@@ -2453,7 +2453,7 @@ explain extended select * from t1 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 explain extended select * from t1 where (s1,s2) in (select s2,s1 from t1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
@@ -2461,7 +2461,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,`test`.`t1`.`s2` AS `s2` from `test`.`t1` where <in_optimizer>((`test`.`t1`.`s1`,`test`.`t1`.`s2`),<exists>(/* select#2 */ select `test`.`t1`.`s2`,`test`.`t1`.`s1` from `test`.`t1` where ((<cache>(`test`.`t1`.`s1`) = `test`.`t1`.`s2`) and (<cache>(`test`.`t1`.`s2`) = `test`.`t1`.`s1`))))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select * from t1 where (s1,s2) in (select s2,s1 from t1)	{
   "steps": [
     {
@@ -2705,7 +2705,7 @@ explain extended select * from t1 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch=default;
 drop table t1;
 create table t1(a int);
@@ -2718,7 +2718,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	ALL	NULL	NULL	NULL	NULL	2	
 1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	3	Using join buffer (BNL, incremental buffers)
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain select * from t1,t2	{
   "steps": [
     {
@@ -2877,7 +2877,7 @@ explain select * from t1,t2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select @@optimizer_trace_features;
 @@optimizer_trace_features
 greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on
@@ -2887,7 +2887,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	ALL	NULL	NULL	NULL	NULL	2	
 1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	3	Using join buffer (BNL, incremental buffers)
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain select * from t1,t2	{
   "steps": [
     {
@@ -2971,7 +2971,7 @@ explain select * from t1,t2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set @@optimizer_trace_features=default;
 set @@session.optimizer_prune_level=default;
 drop table t1, t2;
@@ -3011,7 +3011,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` where <in_optimizer>(`test`.`t1_16`.`a1`,<exists>(/* select#2 */ select 1 from `test`.`t2_16` where ((`test`.`t2_16`.`b1` > '0') and (<cache>(`test`.`t1_16`.`a1`) = `test`.`t2_16`.`b1`))))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select left(a1,7), left(a2,7)
 from t1_16
 where a1 in (select b1 from t2_16 where b1 > '0')	{
@@ -3258,7 +3258,7 @@ where a1 in (select b1 from t2_16 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 drop table t1_16,t2_16,t3_16;
 set @@optimizer_switch=default;
 CREATE table t1 ( c1 integer );
@@ -3274,7 +3274,7 @@ WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2
 c1	c2
 1	1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2
 WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2 IN ( 1 ) )	{
   "steps": [
@@ -3610,12 +3610,12 @@ WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 SELECT * FROM t1 WHERE c1=5 UNION SELECT * FROM t2 WHERE c2=5;
 c1
 5
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT * FROM t1 WHERE c1=5 UNION SELECT * FROM t2 WHERE c2=5	{
   "steps": [
     {
@@ -3910,7 +3910,7 @@ SELECT * FROM t1 WHERE c1=5 UNION SELECT
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch="semijoin=off";
 explain
 select * from t1
@@ -3924,7 +3924,7 @@ id	select_type	table	type	possible_keys	
 3	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	
 2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain
 select * from t1
 where concat(c1,'x') IN
@@ -4255,7 +4255,7 @@ concat(c1,'y') IN
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch=default;
 DROP TABLE t1,t2;
 create table t1 (a int);
@@ -4266,7 +4266,7 @@ select * from t1,t2;
 a	a
 1	1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from t1,t2	{
   "steps": [
     {
@@ -4325,7 +4325,7 @@ select * from t1,t2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 create table t3 (a int, b int);
 create table t4 (a int primary key);
 insert into t4 values(1),(2);
@@ -4675,7 +4675,7 @@ a	a
 1	NULL
 1	NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from t1 left join t2 on t2.a=500 where t2.a is NULL	{
   "steps": [
     {
@@ -4820,7 +4820,7 @@ select * from t1 left join t2 on t2.a=50
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 drop table t1,t2;
 create table t1(a int, b int);
 insert into t1 values(1,NULL),(NULL,2);
@@ -4829,7 +4829,7 @@ 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);
 a	b
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from t1 where (t1.a,t1.b) not in (select c,d from t2 where c>0)	{
   "steps": [
     {
@@ -5110,7 +5110,7 @@ select * from t1 where (t1.a,t1.b) not i
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select t1.a,avg(t2.c) as moyenne from t1, t2 where t2.c>-1
 group by t1.a having moyenne<>0;
 a	moyenne
@@ -5374,7 +5374,7 @@ trace
 drop table t1,t2;
 update t6 set d=5 where d is NULL;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 update t6 set d=5 where d is NULL	{
   "steps": [
     {
@@ -5435,10 +5435,10 @@ update t6 set d=5 where d is NULL	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 delete from t6 where d=5;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 delete from t6 where d=5	{
   "steps": [
     {
@@ -5495,17 +5495,17 @@ delete from t6 where d=5	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 insert into t6 values(6),(7),(8);
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 insert into t6 values(6),(7),(8)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 insert into t6 select * from t6 where d>7;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 insert into t6 select * from t6 where d>7	{
   "steps": [
     {
@@ -5656,10 +5656,10 @@ insert into t6 select * from t6 where d>
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-4 where d>7000;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-4 where d>7000	{
   "steps": [
     {
@@ -5823,10 +5823,10 @@ update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 delete t6 from t5, t6 where d>7000;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 delete t6 from t5, t6 where d>7000	{
   "steps": [
     {
@@ -5984,10 +5984,10 @@ delete t6 from t5, t6 where d>7000	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace_offset=2,optimizer_trace_limit=2;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 1;
 1
 1
@@ -6004,7 +6004,7 @@ select 5;
 5
 5
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 3	{
   "steps": [
     {
@@ -6032,7 +6032,7 @@ select 3	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select 4	{
   "steps": [
     {
@@ -6060,10 +6060,10 @@ select 4	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace_offset=-2,optimizer_trace_limit=2;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 1;
 1
 1
@@ -6080,7 +6080,7 @@ select 5;
 5
 5
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 4	{
   "steps": [
     {
@@ -6108,7 +6108,7 @@ select 4	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select 5	{
   "steps": [
     {
@@ -6136,10 +6136,10 @@ select 5	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace_offset=default,optimizer_trace_limit=default;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 create table t1 (
 id   char(16) not null default '',
 data int not null
@@ -6163,281 +6163,35 @@ select f1()|
 f1()
 3
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select sum(data) into ret from t1	{
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+freturn 3 ret@0	{
   "steps": [
-    {
-      "join_preparation": {
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
-          }
-        ] /* steps */
-      } /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        "select#": 1,
-        "steps": [
-          {
-            "records_estimation": [
-              {
-                "database": "test",
-                "table": "t1",
-                "table_scan": {
-                  "records": 2,
-                  "cost": 2
-                } /* table_scan */
-              }
-            ] /* records_estimation */
-          },
-          {
-            "considered_execution_plans": [
-              {
-                "database": "test",
-                "table": "t1",
-                "best_access_path": {
-                  "considered_access_paths": [
-                    {
-                      "access_type": "scan",
-                      "using_join_cache": true,
-                      "records": 2,
-                      "cost": 2.0154,
-                      "chosen": true
-                    }
-                  ] /* considered_access_paths */
-                } /* best_access_path */,
-                "cost_for_plan": 2.4154,
-                "records_for_plan": 2,
-                "chosen": true
-              }
-            ] /* considered_execution_plans */
-          },
-          {
-            "attaching_conditions_to_tables": {
-              "original_condition": null,
-              "attached_conditions_computation": [
-              ] /* attached_conditions_computation */,
-              "attached_conditions_summary": [
-                {
-                  "database": "test",
-                  "table": "t1",
-                  "attached": null
-                }
-              ] /* attached_conditions_summary */
-            } /* attaching_conditions_to_tables */
-          },
-          {
-            "refine_plan": [
-              {
-                "database": "test",
-                "table": "t1",
-                "scan_type": "table"
-              }
-            ] /* refine_plan */
-          }
-        ] /* steps */
-      } /* join_optimization */
-    },
-    {
-      "join_execution": {
-        "select#": 1,
-        "steps": [
-        ] /* steps */
-      } /* join_execution */
-    }
   ] /* steps */
-}	0
+}	0	0
 select s, f1() from t2 order by s desc|
 s	f1()
 c	3
 b	3
 a	3
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select sum(data) into ret from t1	{
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+freturn 3 ret@0	{
   "steps": [
-    {
-      "join_preparation": {
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
-          }
-        ] /* steps */
-      } /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        "select#": 1,
-        "steps": [
-          {
-            "records_estimation": [
-              {
-                "database": "test",
-                "table": "t1",
-                "table_scan": {
-                  "records": 2,
-                  "cost": 2
-                } /* table_scan */
-              }
-            ] /* records_estimation */
-          },
-          {
-            "considered_execution_plans": [
-              {
-                "database": "test",
-                "table": "t1",
-                "best_access_path": {
-                  "considered_access_paths": [
-                    {
-                      "access_type": "scan",
-                      "using_join_cache": true,
-                      "records": 2,
-                      "cost": 2.0154,
-                      "chosen": true
-                    }
-                  ] /* considered_access_paths */
-                } /* best_access_path */,
-                "cost_for_plan": 2.4154,
-                "records_for_plan": 2,
-                "chosen": true
-              }
-            ] /* considered_execution_plans */
-          },
-          {
-            "attaching_conditions_to_tables": {
-              "original_condition": null,
-              "attached_conditions_computation": [
-              ] /* attached_conditions_computation */,
-              "attached_conditions_summary": [
-                {
-                  "database": "test",
-                  "table": "t1",
-                  "attached": null
-                }
-              ] /* attached_conditions_summary */
-            } /* attaching_conditions_to_tables */
-          },
-          {
-            "refine_plan": [
-              {
-                "database": "test",
-                "table": "t1",
-                "scan_type": "table"
-              }
-            ] /* refine_plan */
-          }
-        ] /* steps */
-      } /* join_optimization */
-    },
-    {
-      "join_execution": {
-        "select#": 1,
-        "steps": [
-        ] /* steps */
-      } /* join_execution */
-    }
   ] /* steps */
-}	0
+}	0	0
 select * from t6 where d in (select f1() from t2 where s="c")|
 d
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select sum(data) into ret from t1	{
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+freturn 3 ret@0	{
   "steps": [
-    {
-      "join_preparation": {
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
-          }
-        ] /* steps */
-      } /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        "select#": 1,
-        "steps": [
-          {
-            "records_estimation": [
-              {
-                "database": "test",
-                "table": "t1",
-                "table_scan": {
-                  "records": 2,
-                  "cost": 2
-                } /* table_scan */
-              }
-            ] /* records_estimation */
-          },
-          {
-            "considered_execution_plans": [
-              {
-                "database": "test",
-                "table": "t1",
-                "best_access_path": {
-                  "considered_access_paths": [
-                    {
-                      "access_type": "scan",
-                      "using_join_cache": true,
-                      "records": 2,
-                      "cost": 2.0154,
-                      "chosen": true
-                    }
-                  ] /* considered_access_paths */
-                } /* best_access_path */,
-                "cost_for_plan": 2.4154,
-                "records_for_plan": 2,
-                "chosen": true
-              }
-            ] /* considered_execution_plans */
-          },
-          {
-            "attaching_conditions_to_tables": {
-              "original_condition": null,
-              "attached_conditions_computation": [
-              ] /* attached_conditions_computation */,
-              "attached_conditions_summary": [
-                {
-                  "database": "test",
-                  "table": "t1",
-                  "attached": null
-                }
-              ] /* attached_conditions_summary */
-            } /* attaching_conditions_to_tables */
-          },
-          {
-            "refine_plan": [
-              {
-                "database": "test",
-                "table": "t1",
-                "scan_type": "table"
-              }
-            ] /* refine_plan */
-          }
-        ] /* steps */
-      } /* join_optimization */
-    },
-    {
-      "join_execution": {
-        "select#": 1,
-        "steps": [
-        ] /* steps */
-      } /* join_execution */
-    }
   ] /* steps */
-}	0
-set optimizer_trace_offset=-7, optimizer_trace_limit=7|
-select @@optimizer_trace_offset, @@optimizer_trace_limit|
-@@optimizer_trace_offset	@@optimizer_trace_limit
--7	7
+}	0	0
+set optimizer_trace_offset=-20, optimizer_trace_limit=20|
 select * from t6 where d in (select f1() from t2 where s="c")|
 d
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from t6 where d in (select f1() from t2 where s="c")	{
   "steps": [
     {
@@ -6798,7 +6552,1240 @@ select * from t6 where d in (select f1()
               },
               {
                 "database": "test",
-                "table": "t2",
+                "table": "t2",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+select count(*) from information_schema.OPTIMIZER_TRACE|
+count(*)
+11
+set optimizer_trace_offset=3, optimizer_trace_limit=1|
+select * from t6 where d in (select f1() from t2 where s="c")|
+d
+select * from information_schema.OPTIMIZER_TRACE|
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+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 optimizer_trace_offset=0, optimizer_trace_limit=100;
+call p1("c")|
+Warnings:
+Warning	1329	No data - zero rows fetched, selected, or processed
+select * from information_schema.OPTIMIZER_TRACE|
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+call p1("c")	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set res@1 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+select d into res from t6 where d in (select f1() from t2 where s= NAME_CONST('arg',_latin1'c' COLLATE 'latin1_swedish_ci'))	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "join_preparation": {
+              "select#": 2,
+              "steps": [
+                {
+                  "expanded_query": "/* select#2 */ select `f1`() from `test`.`t2` where (`test`.`t2`.`s` = arg@0)"
+                },
+                {
+                  "transformation": {
+                    "select#": 2,
+                    "from": "IN (SELECT)",
+                    "to": "semijoin",
+                    "chosen": true
+                  } /* transformation */
+                }
+              ] /* steps */
+            } /* join_preparation */
+          },
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t6`.`d` AS `d` from `test`.`t6` where `test`.`t6`.`d` in (/* select#2 */ select `f1`() from `test`.`t2` where (`test`.`t2`.`s` = arg@0))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "transformation": {
+              "select#": 2,
+              "from": "IN (SELECT)",
+              "to": "semijoin",
+              "chosen": true
+            } /* transformation */
+          },
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "(1 and (`test`.`t2`.`s` = arg@0) and (`test`.`t6`.`d` = `f1`()))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "(1 and multiple equal(arg@0, `test`.`t2`.`s`) and multiple equal(`f1`(), `test`.`t6`.`d`))"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "(1 and multiple equal(arg@0, `test`.`t2`.`s`) and multiple equal(`f1`(), `test`.`t6`.`d`))"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "(multiple equal(arg@0, `test`.`t2`.`s`) and multiple equal(`f1`(), `test`.`t6`.`d`))"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "ref_optimizer_key_uses": [
+              {
+                "database": "test",
+                "table": "t6",
+                "field": "d",
+                "equals": "`f1`()",
+                "null_rejecting": false
+              }
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t6",
+                "range_analysis": {
+                  "table_scan": {
+                    "records": 4,
+                    "cost": 4.9068
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "d",
+                      "usable": true,
+                      "key_parts": [
+                        "d"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "d",
+                    "cost": 1.8698,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_single_table"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "d",
+                        "ranges": [
+                          "3 <= d <= 3"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "records": 1,
+                        "cost": 2.21,
+                        "rowid_ordered": true,
+                        "chosen": false,
+                        "cause": "cost"
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */
+                } /* range_analysis */
+              },
+              {
+                "database": "test",
+                "table": "t2",
+                "table_scan": {
+                  "records": 3,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "pulled_out_semijoin_tables": [
+            ] /* pulled_out_semijoin_tables */
+          },
+          {
+            "execution_plan_for_potential_materialization": {
+              "steps": [
+                {
+                  "considered_execution_plans": [
+                    {
+                      "database": "test",
+                      "table": "t2",
+                      "best_access_path": {
+                        "considered_access_paths": [
+                          {
+                            "access_type": "scan",
+                            "using_join_cache": true,
+                            "records": 3,
+                            "cost": 2.0212,
+                            "chosen": true
+                          }
+                        ] /* considered_access_paths */
+                      } /* best_access_path */,
+                      "cost_for_plan": 2.6212,
+                      "records_for_plan": 3,
+                      "semijoin_strategy_choice": [
+                      ] /* semijoin_strategy_choice */,
+                      "chosen": true
+                    }
+                  ] /* considered_execution_plans */
+                }
+              ] /* steps */
+            } /* execution_plan_for_potential_materialization */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t2",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 3,
+                      "cost": 2.0212,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.6212,
+                "records_for_plan": 3,
+                "semijoin_strategy_choice": [
+                ] /* semijoin_strategy_choice */,
+                "rest_of_plan": [
+                  {
+                    "database": "test",
+                    "table": "t6",
+                    "best_access_path": {
+                      "considered_access_paths": [
+                        {
+                          "access_type": "ref",
+                          "index": "d",
+                          "records": 1,
+                          "cost": 3,
+                          "chosen": true
+                        },
+                        {
+                          "access_type": "scan",
+                          "using_join_cache": true,
+                          "records": 1,
+                          "cost": 2.6076,
+                          "chosen": true
+                        }
+                      ] /* considered_access_paths */
+                    } /* best_access_path */,
+                    "cost_for_plan": 5.8289,
+                    "records_for_plan": 3,
+                    "semijoin_strategy_choice": [
+                      {
+                        "strategy": "DuplicatesWeedout",
+                        "cost": 5.2289,
+                        "records": 1,
+                        "duplicate_tables_left": true,
+                        "chosen": true
+                      }
+                    ] /* semijoin_strategy_choice */,
+                    "chosen": true
+                  }
+                ] /* rest_of_plan */
+              },
+              {
+                "database": "test",
+                "table": "t6",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "ref",
+                      "index": "d",
+                      "records": 1,
+                      "cost": 1,
+                      "chosen": true
+                    },
+                    {
+                      "access_type": "scan",
+                      "cost": 2,
+                      "records": 4,
+                      "cause": "cost",
+                      "chosen": false
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 1.2,
+                "records_for_plan": 1,
+                "semijoin_strategy_choice": [
+                ] /* semijoin_strategy_choice */,
+                "rest_of_plan": [
+                  {
+                    "database": "test",
+                    "table": "t2",
+                    "best_access_path": {
+                      "considered_access_paths": [
+                        {
+                          "access_type": "scan",
+                          "using_join_cache": true,
+                          "records": 3,
+                          "cost": 2.0213,
+                          "chosen": true
+                        }
+                      ] /* considered_access_paths */
+                    } /* best_access_path */,
+                    "cost_for_plan": 3.8213,
+                    "records_for_plan": 3,
+                    "semijoin_strategy_choice": [
+                      {
+                        "strategy": "FirstMatch",
+                        "recompute_best_access_paths": {
+                          "cause": "join_buffering_not_possible",
+                          "tables": [
+                            {
+                              "database": "test",
+                              "table": "t2",
+                              "best_access_path": {
+                                "considered_access_paths": [
+                                  {
+                                    "access_type": "scan",
+                                    "using_join_cache": true,
+                                    "records": 3,
+                                    "cost": 2.0213,
+                                    "chosen": true
+                                  }
+                                ] /* considered_access_paths */
+                              } /* best_access_path */
+                            }
+                          ] /* tables */
+                        } /* recompute_best_access_paths */,
+                        "cost": 3.2213,
+                        "records": 1,
+                        "chosen": true
+                      },
+                      {
+                        "strategy": "MaterializationLookup",
+                        "cost": 4.0212,
+                        "records": 1,
+                        "duplicate_tables_left": false,
+                        "chosen": false
+                      },
+                      {
+                        "strategy": "DuplicatesWeedout",
+                        "cost": 3.8213,
+                        "records": 1,
+                        "duplicate_tables_left": false,
+                        "chosen": false
+                      }
+                    ] /* semijoin_strategy_choice */,
+                    "chosen": true
+                  }
+                ] /* rest_of_plan */
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "reconsidering_access_paths_for_semijoin": {
+              "strategy": "FirstMatch",
+              "database": "test",
+              "table": "t2",
+              "best_access_path": {
+                "considered_access_paths": [
+                  {
+                    "access_type": "scan",
+                    "records": 3,
+                    "cost": 2.0212,
+                    "chosen": true
+                  }
+                ] /* considered_access_paths */
+              } /* best_access_path */
+            } /* reconsidering_access_paths_for_semijoin */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "((`test`.`t6`.`d` = `f1`()) and (`test`.`t2`.`s` = arg@0))",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t6",
+                  "attached": null
+                },
+                {
+                  "database": "test",
+                  "table": "t2",
+                  "attached": "((`test`.`t6`.`d` = `f1`()) and (`test`.`t2`.`s` = arg@0))"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t6"
+              },
+              {
+                "database": "test",
+                "table": "t2",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+select d+1 into res from t6 where d= NAME_CONST('res',NULL)+1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select (`test`.`t6`.`d` + 1) AS `d+1` from `test`.`t6` where (`test`.`t6`.`d` = (res@1 + 1))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "(`test`.`t6`.`d` = (res@1 + 1))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "ref_optimizer_key_uses": [
+              {
+                "database": "test",
+                "table": "t6",
+                "field": "d",
+                "equals": "(res@1 + 1)",
+                "null_rejecting": false
+              }
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t6",
+                "range_analysis": {
+                  "table_scan": {
+                    "records": 4,
+                    "cost": 4.9068
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "d",
+                      "usable": true,
+                      "key_parts": [
+                        "d"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "d",
+                    "cost": 1.8698,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                    {
+                      "impossible_condition": {
+                        "cause": "comparison_with_null_always_false"
+                      } /* impossible_condition */
+                    }
+                  ] /* setup_range_conditions */,
+                  "impossible_range": true
+                } /* range_analysis */,
+                "records": 0,
+                "cause": "impossible_where_condition"
+              }
+            ] /* records_estimation */
+          }
+        ] /* steps */,
+        "empty_result": {
+          "cause": "no matching row in const table"
+        } /* empty_result */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+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|
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+insert into t2 select d,100,200 from t6 where d is not null	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t6`.`d` AS `d`,100 AS `100`,200 AS `200` from `test`.`t6` where (`test`.`t6`.`d` is not null)"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "(`test`.`t6`.`d` is not null)",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "(`test`.`t6`.`d` is not null)"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "(`test`.`t6`.`d` is not null)"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "(`test`.`t6`.`d` is not null)"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "ref_optimizer_key_uses": [
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t6",
+                "range_analysis": {
+                  "table_scan": {
+                    "records": 4,
+                    "cost": 4.9068
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "d",
+                      "usable": true,
+                      "key_parts": [
+                        "d"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "d",
+                    "cost": 1.8698,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "d",
+                        "ranges": [
+                          "NULL < d"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "records": 4,
+                        "cost": 1.8798,
+                        "rowid_ordered": false,
+                        "chosen": false,
+                        "cause": "cost"
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */
+                } /* range_analysis */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t6",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 4,
+                      "cost": 2.0068,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.8068,
+                "records_for_plan": 4,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "(`test`.`t6`.`d` is not null)",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t6",
+                  "attached": "(`test`.`t6`.`d` is not null)"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t6",
+                "scan_type": "index"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+set_trigger_field NEW.s:=`f1`()	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
                 "scan_type": "table"
               }
             ] /* refine_plan */
@@ -6814,11 +7801,23 @@ select * from t6 where d in (select f1()
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set_trigger_field NEW.s:=`f1`()	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -6832,7 +7831,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -6917,11 +7916,23 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set_trigger_field NEW.s:=`f1`()	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -6935,7 +7946,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -7020,12 +8031,23 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
-set optimizer_trace_offset=2, optimizer_trace_limit=1|
-select * from t6 where d in (select f1() from t2 where s="c")|
-d
-select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set_trigger_field NEW.s:=`f1`()	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -7039,126 +8061,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
-set optimizer_trace_offset=default, optimizer_trace_limit=default|
-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|
-call p1("c")|
-Warnings:
-Warning	1329	No data - zero rows fetched, selected, or processed
-select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select d+1 into res from t6 where d= NAME_CONST('res',NULL)+1	{
-  "steps": [
-    {
-      "join_preparation": {
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select (`test`.`t6`.`d` + 1) AS `d+1` from `test`.`t6` where (`test`.`t6`.`d` = (res@1 + 1))"
-          }
-        ] /* steps */
-      } /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        "select#": 1,
-        "steps": [
-          {
-            "condition_processing": {
-              "condition": "WHERE",
-              "original_condition": "(`test`.`t6`.`d` = (res@1 + 1))",
-              "steps": [
-                {
-                  "transformation": "equality_propagation",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
-                },
-                {
-                  "transformation": "constant_propagation",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
-                },
-                {
-                  "transformation": "trivial_condition_removal",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
-                }
-              ] /* steps */
-            } /* condition_processing */
-          },
-          {
-            "ref_optimizer_key_uses": [
-              {
-                "database": "test",
-                "table": "t6",
-                "field": "d",
-                "equals": "(res@1 + 1)",
-                "null_rejecting": false
-              }
-            ] /* ref_optimizer_key_uses */
-          },
-          {
-            "records_estimation": [
-              {
-                "database": "test",
-                "table": "t6",
-                "range_analysis": {
-                  "table_scan": {
-                    "records": 4,
-                    "cost": 4.9068
-                  } /* table_scan */,
-                  "potential_range_indices": [
-                    {
-                      "index": "d",
-                      "usable": true,
-                      "key_parts": [
-                        "d"
-                      ] /* key_parts */
-                    }
-                  ] /* potential_range_indices */,
-                  "best_covering_index_scan": {
-                    "index": "d",
-                    "cost": 1.8698,
-                    "chosen": true
-                  } /* best_covering_index_scan */,
-                  "setup_range_conditions": [
-                    {
-                      "impossible_condition": {
-                        "cause": "comparison_with_null_always_false"
-                      } /* impossible_condition */
-                    }
-                  ] /* setup_range_conditions */,
-                  "impossible_range": true
-                } /* range_analysis */,
-                "records": 0,
-                "cause": "impossible_where_condition"
-              }
-            ] /* records_estimation */
-          }
-        ] /* steps */,
-        "empty_result": {
-          "cause": "no matching row in const table"
-        } /* empty_result */
-      } /* join_optimization */
-    },
-    {
-      "join_execution": {
-        "select#": 1,
-        "steps": [
-        ] /* steps */
-      } /* join_execution */
-    }
-  ] /* steps */
-}	0
-create trigger trg1 before insert on t2 for each row
-begin
-set new.s=f1();  
-end|
-insert into t2 select d,100,200 from t6 where d is not null|
-select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -7243,7 +8146,11 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
 select * from t2|
 s	i	d
 a	1	1
@@ -7253,14 +8160,15 @@ c	3	3
 3	100	200
 3	100	200
 3	100	200
-prepare stmt from 'call p1(?)';
-select QUERY from information_schema.OPTIMIZER_TRACE;
-QUERY
-call p1(?)
+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;
-Warnings:
-Warning	1329	No data - zero rows fetched, selected, or processed
+count(*)
+0
+select count(*) from information_schema.OPTIMIZER_TRACE;
+count(*)
+1
 select TRACE into @trace from information_schema.OPTIMIZER_TRACE;
 select @trace;
 @trace
@@ -7271,7 +8179,7 @@ select @trace;
         "select#": 1,
         "steps": [
           {
-            "expanded_query": "/* select#1 */ select (`test`.`t6`.`d` + 1) AS `d+1` from `test`.`t6` where (`test`.`t6`.`d` = (res@1 + 1))"
+            "expanded_query": "/* select#1 */ select count(0) AS `count(*)` from `test`.`t1` where (`test`.`t1`.`data` = 'c')"
           }
         ] /* steps */
       } /* join_preparation */
@@ -7283,76 +8191,85 @@ select @trace;
           {
             "condition_processing": {
               "condition": "WHERE",
-              "original_condition": "(`test`.`t6`.`d` = (res@1 + 1))",
+              "original_condition": "(`test`.`t1`.`data` = 'c')",
               "steps": [
                 {
                   "transformation": "equality_propagation",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                  "resulting_condition": "(`test`.`t1`.`data` = 'c')"
                 },
                 {
                   "transformation": "constant_propagation",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                  "resulting_condition": "(`test`.`t1`.`data` = 'c')"
                 },
                 {
                   "transformation": "trivial_condition_removal",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                  "resulting_condition": "(`test`.`t1`.`data` = 'c')"
                 }
               ] /* steps */
             } /* condition_processing */
           },
           {
             "ref_optimizer_key_uses": [
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "records_estimation": [
               {
                 "database": "test",
-                "table": "t6",
-                "field": "d",
-                "equals": "(res@1 + 1)",
-                "null_rejecting": false
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
               }
-            ] /* ref_optimizer_key_uses */
+            ] /* records_estimation */
           },
           {
-            "records_estimation": [
+            "considered_execution_plans": [
               {
                 "database": "test",
-                "table": "t6",
-                "range_analysis": {
-                  "table_scan": {
-                    "records": 4,
-                    "cost": 4.9068
-                  } /* table_scan */,
-                  "potential_range_indices": [
-                    {
-                      "index": "d",
-                      "usable": true,
-                      "key_parts": [
-                        "d"
-                      ] /* key_parts */
-                    }
-                  ] /* potential_range_indices */,
-                  "best_covering_index_scan": {
-                    "index": "d",
-                    "cost": 1.8698,
-                    "chosen": true
-                  } /* best_covering_index_scan */,
-                  "setup_range_conditions": [
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
                     {
-                      "impossible_condition": {
-                        "cause": "comparison_with_null_always_false"
-                      } /* impossible_condition */
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
                     }
-                  ] /* setup_range_conditions */,
-                  "impossible_range": true
-                } /* range_analysis */,
-                "records": 0,
-                "cause": "impossible_where_condition"
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
               }
-            ] /* records_estimation */
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "(`test`.`t1`.`data` = 'c')",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": "(`test`.`t1`.`data` = 'c')"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
           }
-        ] /* steps */,
-        "empty_result": {
-          "cause": "no matching row in const table"
-        } /* empty_result */
+        ] /* steps */
       } /* join_optimization */
     },
     {
@@ -7364,15 +8281,19 @@ select @trace;
     }
   ] /* steps */
 }
+set optimizer_trace_offset=0, optimizer_trace_limit=100;
 execute stmt using @param;
-Warnings:
-Warning	1329	No data - zero rows fetched, selected, or processed
+count(*)
+0
+select count(*) from information_schema.OPTIMIZER_TRACE;
+count(*)
+1
 select TRACE into @trace2 from information_schema.OPTIMIZER_TRACE;
 select @trace=@trace2;
 @trace=@trace2
 1
 drop procedure p1;
-create table optt like information_schema.OPTIMIZER_TRACE;
+create temporary table optt like information_schema.OPTIMIZER_TRACE;
 create procedure p1(arg char(1))
 begin
 declare res int;
@@ -7390,7 +8311,7 @@ call  p1("c")|
 Warnings:
 Warning	1329	No data - zero rows fetched, selected, or processed
 select * from optt|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select d into res from t6 where d in (select f1() from t2 where s= NAME_CONST('arg',_latin1'c' COLLATE 'latin1_swedish_ci'))	{
   "steps": [
     {
@@ -7732,11 +8653,15 @@ select d into res from t6 where d in (se
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -7750,7 +8675,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -7835,11 +8760,19 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -7853,7 +8786,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -7938,12 +8871,16 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
 select @@optimizer_trace|
 @@optimizer_trace
 enabled=off,end_marker=on,one_line=off
 set optimizer_trace="enabled=on";
-drop table optt;
+drop temporary table optt;
 drop function f1;
 drop procedure p1;
 drop trigger trg1;
@@ -7952,7 +8889,7 @@ explain select * from v1 where id="b";
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain select * from v1 where id="b"	{
   "steps": [
     {
@@ -8070,10 +9007,10 @@ explain select * from v1 where id="b"	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 insert into v1 values("z", 100);
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 insert into v1 values("z", 100)	{
   "steps": [
     {
@@ -8086,10 +9023,10 @@ insert into v1 values("z", 100)	{
       } /* view */
     }
   ] /* steps */
-}	0
+}	0	0
 delete from v1 where data=100;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 delete from v1 where data=100	{
   "steps": [
     {
@@ -8112,7 +9049,7 @@ delete from v1 where data=100	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 drop view v1;
 create view v1 as select * from t1 where id < "c" limit 2;
 explain select * from v1 where id="b";
@@ -8120,7 +9057,7 @@ id	select_type	table	type	possible_keys	
 1	PRIMARY	<derived2>	ALL	NULL	NULL	NULL	NULL	2	Using where
 2	DERIVED	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain select * from v1 where id="b"	{
   "steps": [
     {
@@ -8350,14 +9287,14 @@ explain select * from v1 where id="b"	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 drop view v1;
 select * from information_schema.session_variables where
 VARIABLE_NAME="optimizer_trace";
 VARIABLE_NAME	VARIABLE_VALUE
 OPTIMIZER_TRACE	enabled=on,end_marker=on,one_line=off
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from information_schema.session_variables where
 VARIABLE_NAME="optimizer_trace"	{
   "steps": [
@@ -8467,7 +9404,7 @@ VARIABLE_NAME="optimizer_trace"	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace="end_marker=off";
 select 1 union select 2;
 1
@@ -8612,7 +9549,7 @@ select @@optimizer_switch;
 @@optimizer_switch
 index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,materialization=on,semijoin=on,loosescan=on,firstmatch=on
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select "abc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111def" as col	{
   "steps": [
     {
@@ -8640,7 +9577,7 @@ select "abc11111111111111111111111111111
       }
     }
   ]
-}	0
+}	0	0
 drop table t1,t2;
 DROP TABLE t5,t6;
 set optimizer_trace=default;

=== modified file 'mysql-test/r/optimizer_trace_ps_prot.result'
--- a/mysql-test/r/optimizer_trace_ps_prot.result	2011-03-21 17:55:41 +0000
+++ b/mysql-test/r/optimizer_trace_ps_prot.result	2011-05-04 20:53:28 +0000
@@ -1,9 +1,9 @@
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 set optimizer_trace_max_mem_size=1048576;
 set @@session.optimizer_trace="enabled=on";
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 CREATE TABLE t5 (c int);
 INSERT INTO t5 VALUES (NULL);
 CREATE TABLE t6 (d int , KEY (d));
@@ -12,7 +12,7 @@ SELECT (SELECT 1 FROM t6 WHERE d = c) AS
 RESULT
 NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT (SELECT 1 FROM t6 WHERE d = c) AS RESULT FROM t5	{
   "steps": [
     {
@@ -167,7 +167,7 @@ SELECT (SELECT 1 FROM t6 WHERE d = c) AS
       }
     }
   ]
-}	0
+}	0	0
 select (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
 from information_schema.OPTIMIZER_TRACE;
 (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
@@ -177,8 +177,8 @@ SELECT (SELECT 1 FROM t6 WHERE d = c) AS
 RESULT
 NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-SELECT (SELECT 1 FROM t6 WHERE d = c) AS RESULT FROM t5	{"steps": [{"join_preparation": {"select#": 1,"steps": [{"join_preparation": {"select#": 2,"steps": [{"expanded_query": "/* select#2 */ select 1 from `test`.`t6` where (`test`.`t6`.`d` = `test`.`t5`.`c`)"}]}},{"expanded_query": "/* select#1 */ select (/* select#2 */ select 1 from `test`.`t6` where (`test`.`t6`.`d` = `test`.`t5`.`c`)) AS `RESULT` from `test`.`t5`"}]}},{"join_optimization": {"select#": 1,"steps": [{"records_estimation": [{"database": "test","table": "t5","records": 1,"cost": 1,"table_type": "system"}]},{"attaching_conditions_to_tables": {"original_condition": null,"attached_conditions_computation": [],"attached_conditions_summary": []}},{"refine_plan": []}]}},{"join_execution": {"select#": 1,"steps": [{"subselect_execution": {"select#": 2,"steps": [{"join_optimization": {"select#": 2,"steps": [{"condition_processing": {"condition": "WHERE","original_condition": "(`test`.`t6`.`d` = NULL)","steps": [{"transformation": "equality_propagation","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"},{"transformation": "constant_propagation","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"},{"transformation": "trivial_condition_removal","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"}]}},{"ref_optimizer_key_uses": [{"database": "test","table": "t6","field": "d","equals": "NULL","null_rejecting": true}]},{"records_estimation": [{"database": "test","table": "t6","range_analysis": {"table_scan": {"records": 2,"cost": 4.5034},"potential_range_indices": [{"index": "d","usable": true,"key_parts": ["d"]}],"best_covering_index_scan": {"index": "d","cost": 1.4233,"chosen": true},"setup_range_conditions": [{"impossible_condition": {"cause": "comparison_with_null_always_false"}}],"impossible_range": true},"records": 0,"cause": "impossible_where_condition"}]}],"empty_result": {"cause": "no matching row in const table"}}},{"join_execution": {"select#": 2,"steps": []}}]}}]}}]}	0
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+SELECT (SELECT 1 FROM t6 WHERE d = c) AS RESULT FROM t5	{"steps": [{"join_preparation": {"select#": 1,"steps": [{"join_preparation": {"select#": 2,"steps": [{"expanded_query": "/* select#2 */ select 1 from `test`.`t6` where (`test`.`t6`.`d` = `test`.`t5`.`c`)"}]}},{"expanded_query": "/* select#1 */ select (/* select#2 */ select 1 from `test`.`t6` where (`test`.`t6`.`d` = `test`.`t5`.`c`)) AS `RESULT` from `test`.`t5`"}]}},{"join_optimization": {"select#": 1,"steps": [{"records_estimation": [{"database": "test","table": "t5","records": 1,"cost": 1,"table_type": "system"}]},{"attaching_conditions_to_tables": {"original_condition": null,"attached_conditions_computation": [],"attached_conditions_summary": []}},{"refine_plan": []}]}},{"join_execution": {"select#": 1,"steps": [{"subselect_execution": {"select#": 2,"steps": [{"join_optimization": {"select#": 2,"steps": [{"condition_processing": {"condition": "WHERE","original_condition": "(`test`.`t6`.`d` = NULL)","steps": [{"transformation": "equality_propagation","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"},{"transformation": "constant_propagation","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"},{"transformation": "trivial_condition_removal","resulting_condition": "multiple equal(NULL, `test`.`t6`.`d`)"}]}},{"ref_optimizer_key_uses": [{"database": "test","table": "t6","field": "d","equals": "NULL","null_rejecting": true}]},{"records_estimation": [{"database": "test","table": "t6","range_analysis": {"table_scan": {"records": 2,"cost": 4.5034},"potential_range_indices": [{"index": "d","usable": true,"key_parts": ["d"]}],"best_covering_index_scan": {"index": "d","cost": 1.4233,"chosen": true},"setup_range_conditions": [{"impossible_condition": {"cause": "comparison_with_null_always_false"}}],"impossible_range": true},"records": 0,"cause": "impossible_where_condition"}]}],"empty_result": {"cause": "no matching row in const table"}}},{"join_execution": {"select#": 2,"steps": []}}]}}]}}]}	0	0
 select (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
 from information_schema.OPTIMIZER_TRACE;
 (1-length(replace(TRACE, " ", ""))/length(TRACE))*100
@@ -189,7 +189,7 @@ id	select_type	table	type	possible_keys	
 1	PRIMARY	t5	system	NULL	NULL	NULL	NULL	1	
 2	DEPENDENT SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5	{
   "steps": [
     {
@@ -337,12 +337,12 @@ EXPLAIN SELECT (SELECT 1 FROM t6 WHERE d
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 SELECT /* should be last */ (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5 ;
 RESULT
 NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT /* should be last */ (SELECT 1 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5	{
   "steps": [
     {
@@ -497,7 +497,7 @@ SELECT /* should be last */ (SELECT 1 FR
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 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 ;
 RESULT
@@ -510,7 +510,7 @@ SELECT (SELECT 1 FROM t6 WHERE d = ifnul
 RESULT
 NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT (SELECT 1 FROM t6 WHERE d = ifnull(c,null) UNION SELECT 2 FROM t6 WHERE d = ifnull(c,null)) AS RESULT FROM t5	{
   "steps": [
     {
@@ -842,11 +842,11 @@ SELECT (SELECT 1 FROM t6 WHERE d = ifnul
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 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));
 c
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 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))	{
   "steps": [
     {
@@ -1148,7 +1148,7 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select (@query:=QUERY)+NULL, (@trace:=TRACE)+NULL from information_schema.OPTIMIZER_TRACE;
 (@query:=QUERY)+NULL	(@trace:=TRACE)+NULL
 NULL	NULL
@@ -1185,22 +1185,22 @@ select 1;
 1
 1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select 1		521
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+select 1		521	0
 set optimizer_trace_max_mem_size=0;
 select 1;
 1
 1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-		529
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+		529	0
 set optimizer_trace_max_mem_size=1048576;
 explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t5	system	NULL	NULL	NULL	NULL	1	
 1	PRIMARY	t6	ref	d	d	5	const	1	Using where; Using index; FirstMatch(t5)
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null)	{
   "steps": [
     {
@@ -1483,14 +1483,14 @@ explain SELECT c FROM t5 where c+1 in (s
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch="semijoin=off";
 explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t5	system	NULL	NULL	NULL	NULL	1	
 2	SUBQUERY	t6	ref	d	d	5	const	1	Using where; Using index
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain SELECT c FROM t5 where c+1 in (select d+1 from t6 where d is null)	{
   "steps": [
     {
@@ -1756,7 +1756,7 @@ explain SELECT c FROM t5 where c+1 in (s
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch=default;
 CREATE TABLE t1 (s1 CHAR(5),
 s2 CHAR(5));
@@ -1768,7 +1768,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,`test`.`t1`.`s2` AS `s2` from `test`.`t1` where <nop>((`test`.`t1`.`s1` > (/* select#2 */ select min(`test`.`t1`.`s2`) from `test`.`t1`)))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select * from t1 where s1 > any (select s2 from t1)	{
   "steps": [
     {
@@ -1972,7 +1972,7 @@ explain extended select * from t1 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 explain extended select * from t1 where s1 > any (select max(s2) from t1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
@@ -1980,7 +1980,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,`test`.`t1`.`s2` AS `s2` from `test`.`t1` where <nop>((`test`.`t1`.`s1` > <min>(/* select#2 */ select max(`test`.`t1`.`s2`) from `test`.`t1`)))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select * from t1 where s1 > any (select max(s2) from t1)	{
   "steps": [
     {
@@ -2184,7 +2184,7 @@ explain extended select * from t1 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch="semijoin=off,materialization=off";
 explain extended select * from t1 where s1 in (select s2 from t1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
@@ -2193,7 +2193,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,`test`.`t1`.`s2` AS `s2` from `test`.`t1` where <in_optimizer>(`test`.`t1`.`s1`,<exists>(/* select#2 */ select 1 from `test`.`t1` where (<cache>(`test`.`t1`.`s1`) = `test`.`t1`.`s2`)))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select * from t1 where s1 in (select s2 from t1)	{
   "steps": [
     {
@@ -2437,7 +2437,7 @@ explain extended select * from t1 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 explain extended select * from t1 where (s1,s2) in (select s2,s1 from t1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
@@ -2445,7 +2445,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,`test`.`t1`.`s2` AS `s2` from `test`.`t1` where <in_optimizer>((`test`.`t1`.`s1`,`test`.`t1`.`s2`),<exists>(/* select#2 */ select `test`.`t1`.`s2`,`test`.`t1`.`s1` from `test`.`t1` where ((<cache>(`test`.`t1`.`s1`) = `test`.`t1`.`s2`) and (<cache>(`test`.`t1`.`s2`) = `test`.`t1`.`s1`))))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select * from t1 where (s1,s2) in (select s2,s1 from t1)	{
   "steps": [
     {
@@ -2689,7 +2689,7 @@ explain extended select * from t1 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch=default;
 drop table t1;
 create table t1(a int);
@@ -2702,7 +2702,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	ALL	NULL	NULL	NULL	NULL	2	
 1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	3	Using join buffer (BNL, incremental buffers)
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain select * from t1,t2	{
   "steps": [
     {
@@ -2861,7 +2861,7 @@ explain select * from t1,t2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select @@optimizer_trace_features;
 @@optimizer_trace_features
 greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on
@@ -2871,7 +2871,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	ALL	NULL	NULL	NULL	NULL	2	
 1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	3	Using join buffer (BNL, incremental buffers)
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain select * from t1,t2	{
   "steps": [
     {
@@ -2955,7 +2955,7 @@ explain select * from t1,t2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set @@optimizer_trace_features=default;
 set @@session.optimizer_prune_level=default;
 drop table t1, t2;
@@ -2995,7 +2995,7 @@ id	select_type	table	type	possible_keys	
 Warnings:
 Note	1003	/* select#1 */ select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` where <in_optimizer>(`test`.`t1_16`.`a1`,<exists>(/* select#2 */ select 1 from `test`.`t2_16` where ((`test`.`t2_16`.`b1` > '0') and (<cache>(`test`.`t1_16`.`a1`) = `test`.`t2_16`.`b1`))))
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain extended select left(a1,7), left(a2,7)
 from t1_16
 where a1 in (select b1 from t2_16 where b1 > '0')	{
@@ -3242,7 +3242,7 @@ where a1 in (select b1 from t2_16 where 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 drop table t1_16,t2_16,t3_16;
 set @@optimizer_switch=default;
 CREATE table t1 ( c1 integer );
@@ -3258,7 +3258,7 @@ WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2
 c1	c2
 1	1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2
 WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2 IN ( 1 ) )	{
   "steps": [
@@ -3594,12 +3594,12 @@ WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 SELECT * FROM t1 WHERE c1=5 UNION SELECT * FROM t2 WHERE c2=5;
 c1
 5
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT * FROM t1 WHERE c1=5 UNION SELECT * FROM t2 WHERE c2=5	{
   "steps": [
     {
@@ -3894,7 +3894,7 @@ SELECT * FROM t1 WHERE c1=5 UNION SELECT
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch="semijoin=off";
 explain
 select * from t1
@@ -3908,7 +3908,7 @@ id	select_type	table	type	possible_keys	
 3	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	
 2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain
 select * from t1
 where concat(c1,'x') IN
@@ -4239,7 +4239,7 @@ concat(c1,'y') IN
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_switch=default;
 DROP TABLE t1,t2;
 create table t1 (a int);
@@ -4250,7 +4250,7 @@ select * from t1,t2;
 a	a
 1	1
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from t1,t2	{
   "steps": [
     {
@@ -4309,7 +4309,7 @@ select * from t1,t2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 create table t3 (a int, b int);
 create table t4 (a int primary key);
 insert into t4 values(1),(2);
@@ -4659,7 +4659,7 @@ a	a
 1	NULL
 1	NULL
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from t1 left join t2 on t2.a=500 where t2.a is NULL	{
   "steps": [
     {
@@ -4804,7 +4804,7 @@ select * from t1 left join t2 on t2.a=50
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 drop table t1,t2;
 create table t1(a int, b int);
 insert into t1 values(1,NULL),(NULL,2);
@@ -4813,7 +4813,7 @@ 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);
 a	b
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from t1 where (t1.a,t1.b) not in (select c,d from t2 where c>0)	{
   "steps": [
     {
@@ -5086,7 +5086,7 @@ select * from t1 where (t1.a,t1.b) not i
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 select t1.a,avg(t2.c) as moyenne from t1, t2 where t2.c>-1
 group by t1.a having moyenne<>0;
 a	moyenne
@@ -5350,7 +5350,7 @@ trace
 drop table t1,t2;
 update t6 set d=5 where d is NULL;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 update t6 set d=5 where d is NULL	{
   "steps": [
     {
@@ -5411,10 +5411,10 @@ update t6 set d=5 where d is NULL	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 delete from t6 where d=5;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 delete from t6 where d=5	{
   "steps": [
     {
@@ -5471,17 +5471,17 @@ delete from t6 where d=5	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 insert into t6 values(6),(7),(8);
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 insert into t6 values(6),(7),(8)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 insert into t6 select * from t6 where d>7;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 insert into t6 select * from t6 where d>7	{
   "steps": [
     {
@@ -5632,10 +5632,10 @@ insert into t6 select * from t6 where d>
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-4 where d>7000;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-4 where d>7000	{
   "steps": [
     {
@@ -5799,10 +5799,10 @@ update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 delete t6 from t5, t6 where d>7000;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 delete t6 from t5, t6 where d>7000	{
   "steps": [
     {
@@ -5960,10 +5960,10 @@ delete t6 from t5, t6 where d>7000	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace_offset=2,optimizer_trace_limit=2;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 1;
 1
 1
@@ -5980,7 +5980,7 @@ select 5;
 5
 5
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 2	{
   "steps": [
     {
@@ -5994,7 +5994,7 @@ select 2	{
       } /* join_preparation */
     }
   ] /* steps */
-}	0
+}	0	0
 select 2	{
   "steps": [
     {
@@ -6022,10 +6022,10 @@ select 2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace_offset=-2,optimizer_trace_limit=2;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 1;
 1
 1
@@ -6042,7 +6042,7 @@ select 5;
 5
 5
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select 5	{
   "steps": [
     {
@@ -6056,7 +6056,7 @@ select 5	{
       } /* join_preparation */
     }
   ] /* steps */
-}	0
+}	0	0
 select 5	{
   "steps": [
     {
@@ -6084,10 +6084,10 @@ select 5	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace_offset=default,optimizer_trace_limit=default;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 create table t1 (
 id   char(16) not null default '',
 data int not null
@@ -6111,281 +6111,35 @@ select f1()|
 f1()
 3
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select sum(data) into ret from t1	{
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+freturn 3 ret@0	{
   "steps": [
-    {
-      "join_preparation": {
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
-          }
-        ] /* steps */
-      } /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        "select#": 1,
-        "steps": [
-          {
-            "records_estimation": [
-              {
-                "database": "test",
-                "table": "t1",
-                "table_scan": {
-                  "records": 2,
-                  "cost": 2
-                } /* table_scan */
-              }
-            ] /* records_estimation */
-          },
-          {
-            "considered_execution_plans": [
-              {
-                "database": "test",
-                "table": "t1",
-                "best_access_path": {
-                  "considered_access_paths": [
-                    {
-                      "access_type": "scan",
-                      "using_join_cache": true,
-                      "records": 2,
-                      "cost": 2.0154,
-                      "chosen": true
-                    }
-                  ] /* considered_access_paths */
-                } /* best_access_path */,
-                "cost_for_plan": 2.4154,
-                "records_for_plan": 2,
-                "chosen": true
-              }
-            ] /* considered_execution_plans */
-          },
-          {
-            "attaching_conditions_to_tables": {
-              "original_condition": null,
-              "attached_conditions_computation": [
-              ] /* attached_conditions_computation */,
-              "attached_conditions_summary": [
-                {
-                  "database": "test",
-                  "table": "t1",
-                  "attached": null
-                }
-              ] /* attached_conditions_summary */
-            } /* attaching_conditions_to_tables */
-          },
-          {
-            "refine_plan": [
-              {
-                "database": "test",
-                "table": "t1",
-                "scan_type": "table"
-              }
-            ] /* refine_plan */
-          }
-        ] /* steps */
-      } /* join_optimization */
-    },
-    {
-      "join_execution": {
-        "select#": 1,
-        "steps": [
-        ] /* steps */
-      } /* join_execution */
-    }
   ] /* steps */
-}	0
+}	0	0
 select s, f1() from t2 order by s desc|
 s	f1()
 c	3
 b	3
 a	3
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select sum(data) into ret from t1	{
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+freturn 3 ret@0	{
   "steps": [
-    {
-      "join_preparation": {
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
-          }
-        ] /* steps */
-      } /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        "select#": 1,
-        "steps": [
-          {
-            "records_estimation": [
-              {
-                "database": "test",
-                "table": "t1",
-                "table_scan": {
-                  "records": 2,
-                  "cost": 2
-                } /* table_scan */
-              }
-            ] /* records_estimation */
-          },
-          {
-            "considered_execution_plans": [
-              {
-                "database": "test",
-                "table": "t1",
-                "best_access_path": {
-                  "considered_access_paths": [
-                    {
-                      "access_type": "scan",
-                      "using_join_cache": true,
-                      "records": 2,
-                      "cost": 2.0154,
-                      "chosen": true
-                    }
-                  ] /* considered_access_paths */
-                } /* best_access_path */,
-                "cost_for_plan": 2.4154,
-                "records_for_plan": 2,
-                "chosen": true
-              }
-            ] /* considered_execution_plans */
-          },
-          {
-            "attaching_conditions_to_tables": {
-              "original_condition": null,
-              "attached_conditions_computation": [
-              ] /* attached_conditions_computation */,
-              "attached_conditions_summary": [
-                {
-                  "database": "test",
-                  "table": "t1",
-                  "attached": null
-                }
-              ] /* attached_conditions_summary */
-            } /* attaching_conditions_to_tables */
-          },
-          {
-            "refine_plan": [
-              {
-                "database": "test",
-                "table": "t1",
-                "scan_type": "table"
-              }
-            ] /* refine_plan */
-          }
-        ] /* steps */
-      } /* join_optimization */
-    },
-    {
-      "join_execution": {
-        "select#": 1,
-        "steps": [
-        ] /* steps */
-      } /* join_execution */
-    }
   ] /* steps */
-}	0
+}	0	0
 select * from t6 where d in (select f1() from t2 where s="c")|
 d
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select sum(data) into ret from t1	{
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+freturn 3 ret@0	{
   "steps": [
-    {
-      "join_preparation": {
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
-          }
-        ] /* steps */
-      } /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        "select#": 1,
-        "steps": [
-          {
-            "records_estimation": [
-              {
-                "database": "test",
-                "table": "t1",
-                "table_scan": {
-                  "records": 2,
-                  "cost": 2
-                } /* table_scan */
-              }
-            ] /* records_estimation */
-          },
-          {
-            "considered_execution_plans": [
-              {
-                "database": "test",
-                "table": "t1",
-                "best_access_path": {
-                  "considered_access_paths": [
-                    {
-                      "access_type": "scan",
-                      "using_join_cache": true,
-                      "records": 2,
-                      "cost": 2.0154,
-                      "chosen": true
-                    }
-                  ] /* considered_access_paths */
-                } /* best_access_path */,
-                "cost_for_plan": 2.4154,
-                "records_for_plan": 2,
-                "chosen": true
-              }
-            ] /* considered_execution_plans */
-          },
-          {
-            "attaching_conditions_to_tables": {
-              "original_condition": null,
-              "attached_conditions_computation": [
-              ] /* attached_conditions_computation */,
-              "attached_conditions_summary": [
-                {
-                  "database": "test",
-                  "table": "t1",
-                  "attached": null
-                }
-              ] /* attached_conditions_summary */
-            } /* attaching_conditions_to_tables */
-          },
-          {
-            "refine_plan": [
-              {
-                "database": "test",
-                "table": "t1",
-                "scan_type": "table"
-              }
-            ] /* refine_plan */
-          }
-        ] /* steps */
-      } /* join_optimization */
-    },
-    {
-      "join_execution": {
-        "select#": 1,
-        "steps": [
-        ] /* steps */
-      } /* join_execution */
-    }
   ] /* steps */
-}	0
-set optimizer_trace_offset=-7, optimizer_trace_limit=7|
-select @@optimizer_trace_offset, @@optimizer_trace_limit|
-@@optimizer_trace_offset	@@optimizer_trace_limit
--7	7
+}	0	0
+set optimizer_trace_offset=-20, optimizer_trace_limit=20|
 select * from t6 where d in (select f1() from t2 where s="c")|
 d
 select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from t6 where d in (select f1() from t2 where s="c")	{
   "steps": [
     {
@@ -6415,15 +6169,47 @@ select * from t6 where d in (select f1()
           }
         ] /* steps */
       } /* join_preparation */
-    },
+    }
+  ] /* steps */
+}	0	0
+select * from t6 where d in (select f1() from t2 where s="c")	{
+  "steps": [
     {
-      "join_optimization": {
+      "join_preparation": {
         "select#": 1,
         "steps": [
           {
-            "transformation": {
+            "join_preparation": {
               "select#": 2,
-              "from": "IN (SELECT)",
+              "steps": [
+                {
+                  "expanded_query": "/* select#2 */ select `f1`() from `test`.`t2` where (`test`.`t2`.`s` = 'c')"
+                },
+                {
+                  "transformation": {
+                    "select#": 2,
+                    "from": "IN (SELECT)",
+                    "to": "semijoin",
+                    "chosen": true
+                  } /* transformation */
+                }
+              ] /* steps */
+            } /* join_preparation */
+          },
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t6`.`d` AS `d` from `test`.`t6` where `test`.`t6`.`d` in (/* select#2 */ select `f1`() from `test`.`t2` where (`test`.`t2`.`s` = 'c'))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "transformation": {
+              "select#": 2,
+              "from": "IN (SELECT)",
               "to": "semijoin",
               "chosen": true
             } /* transformation */
@@ -6762,11 +6548,1260 @@ select * from t6 where d in (select f1()
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+select count(*) from information_schema.OPTIMIZER_TRACE|
+count(*)
+12
+set optimizer_trace_offset=3, optimizer_trace_limit=1|
+select * from t6 where d in (select f1() from t2 where s="c")|
+d
+select * from information_schema.OPTIMIZER_TRACE|
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+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 optimizer_trace_offset=0, optimizer_trace_limit=100;
+call p1("c")|
+Warnings:
+Warning	1329	No data - zero rows fetched, selected, or processed
+select * from information_schema.OPTIMIZER_TRACE|
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+call p1("c")	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set res@1 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+select d into res from t6 where d in (select f1() from t2 where s= NAME_CONST('arg',_latin1'c' COLLATE 'latin1_swedish_ci'))	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "join_preparation": {
+              "select#": 2,
+              "steps": [
+                {
+                  "expanded_query": "/* select#2 */ select `f1`() from `test`.`t2` where (`test`.`t2`.`s` = arg@0)"
+                },
+                {
+                  "transformation": {
+                    "select#": 2,
+                    "from": "IN (SELECT)",
+                    "to": "semijoin",
+                    "chosen": true
+                  } /* transformation */
+                }
+              ] /* steps */
+            } /* join_preparation */
+          },
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t6`.`d` AS `d` from `test`.`t6` where `test`.`t6`.`d` in (/* select#2 */ select `f1`() from `test`.`t2` where (`test`.`t2`.`s` = arg@0))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "transformation": {
+              "select#": 2,
+              "from": "IN (SELECT)",
+              "to": "semijoin",
+              "chosen": true
+            } /* transformation */
+          },
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "(1 and (`test`.`t2`.`s` = arg@0) and (`test`.`t6`.`d` = `f1`()))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "(1 and multiple equal(arg@0, `test`.`t2`.`s`) and multiple equal(`f1`(), `test`.`t6`.`d`))"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "(1 and multiple equal(arg@0, `test`.`t2`.`s`) and multiple equal(`f1`(), `test`.`t6`.`d`))"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "(multiple equal(arg@0, `test`.`t2`.`s`) and multiple equal(`f1`(), `test`.`t6`.`d`))"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "ref_optimizer_key_uses": [
+              {
+                "database": "test",
+                "table": "t6",
+                "field": "d",
+                "equals": "`f1`()",
+                "null_rejecting": false
+              }
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t6",
+                "range_analysis": {
+                  "table_scan": {
+                    "records": 4,
+                    "cost": 4.9068
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "d",
+                      "usable": true,
+                      "key_parts": [
+                        "d"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "d",
+                    "cost": 1.8698,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_single_table"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "d",
+                        "ranges": [
+                          "3 <= d <= 3"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "records": 1,
+                        "cost": 2.21,
+                        "rowid_ordered": true,
+                        "chosen": false,
+                        "cause": "cost"
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */
+                } /* range_analysis */
+              },
+              {
+                "database": "test",
+                "table": "t2",
+                "table_scan": {
+                  "records": 3,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "pulled_out_semijoin_tables": [
+            ] /* pulled_out_semijoin_tables */
+          },
+          {
+            "execution_plan_for_potential_materialization": {
+              "steps": [
+                {
+                  "considered_execution_plans": [
+                    {
+                      "database": "test",
+                      "table": "t2",
+                      "best_access_path": {
+                        "considered_access_paths": [
+                          {
+                            "access_type": "scan",
+                            "using_join_cache": true,
+                            "records": 3,
+                            "cost": 2.0212,
+                            "chosen": true
+                          }
+                        ] /* considered_access_paths */
+                      } /* best_access_path */,
+                      "cost_for_plan": 2.6212,
+                      "records_for_plan": 3,
+                      "semijoin_strategy_choice": [
+                      ] /* semijoin_strategy_choice */,
+                      "chosen": true
+                    }
+                  ] /* considered_execution_plans */
+                }
+              ] /* steps */
+            } /* execution_plan_for_potential_materialization */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t2",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 3,
+                      "cost": 2.0212,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.6212,
+                "records_for_plan": 3,
+                "semijoin_strategy_choice": [
+                ] /* semijoin_strategy_choice */,
+                "rest_of_plan": [
+                  {
+                    "database": "test",
+                    "table": "t6",
+                    "best_access_path": {
+                      "considered_access_paths": [
+                        {
+                          "access_type": "ref",
+                          "index": "d",
+                          "records": 1,
+                          "cost": 3,
+                          "chosen": true
+                        },
+                        {
+                          "access_type": "scan",
+                          "using_join_cache": true,
+                          "records": 1,
+                          "cost": 2.6076,
+                          "chosen": true
+                        }
+                      ] /* considered_access_paths */
+                    } /* best_access_path */,
+                    "cost_for_plan": 5.8289,
+                    "records_for_plan": 3,
+                    "semijoin_strategy_choice": [
+                      {
+                        "strategy": "DuplicatesWeedout",
+                        "cost": 5.2289,
+                        "records": 1,
+                        "duplicate_tables_left": true,
+                        "chosen": true
+                      }
+                    ] /* semijoin_strategy_choice */,
+                    "chosen": true
+                  }
+                ] /* rest_of_plan */
+              },
+              {
+                "database": "test",
+                "table": "t6",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "ref",
+                      "index": "d",
+                      "records": 1,
+                      "cost": 1,
+                      "chosen": true
+                    },
+                    {
+                      "access_type": "scan",
+                      "cost": 2,
+                      "records": 4,
+                      "cause": "cost",
+                      "chosen": false
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 1.2,
+                "records_for_plan": 1,
+                "semijoin_strategy_choice": [
+                ] /* semijoin_strategy_choice */,
+                "rest_of_plan": [
+                  {
+                    "database": "test",
+                    "table": "t2",
+                    "best_access_path": {
+                      "considered_access_paths": [
+                        {
+                          "access_type": "scan",
+                          "using_join_cache": true,
+                          "records": 3,
+                          "cost": 2.0213,
+                          "chosen": true
+                        }
+                      ] /* considered_access_paths */
+                    } /* best_access_path */,
+                    "cost_for_plan": 3.8213,
+                    "records_for_plan": 3,
+                    "semijoin_strategy_choice": [
+                      {
+                        "strategy": "FirstMatch",
+                        "recompute_best_access_paths": {
+                          "cause": "join_buffering_not_possible",
+                          "tables": [
+                            {
+                              "database": "test",
+                              "table": "t2",
+                              "best_access_path": {
+                                "considered_access_paths": [
+                                  {
+                                    "access_type": "scan",
+                                    "using_join_cache": true,
+                                    "records": 3,
+                                    "cost": 2.0213,
+                                    "chosen": true
+                                  }
+                                ] /* considered_access_paths */
+                              } /* best_access_path */
+                            }
+                          ] /* tables */
+                        } /* recompute_best_access_paths */,
+                        "cost": 3.2213,
+                        "records": 1,
+                        "chosen": true
+                      },
+                      {
+                        "strategy": "MaterializationLookup",
+                        "cost": 4.0212,
+                        "records": 1,
+                        "duplicate_tables_left": false,
+                        "chosen": false
+                      },
+                      {
+                        "strategy": "DuplicatesWeedout",
+                        "cost": 3.8213,
+                        "records": 1,
+                        "duplicate_tables_left": false,
+                        "chosen": false
+                      }
+                    ] /* semijoin_strategy_choice */,
+                    "chosen": true
+                  }
+                ] /* rest_of_plan */
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "reconsidering_access_paths_for_semijoin": {
+              "strategy": "FirstMatch",
+              "database": "test",
+              "table": "t2",
+              "best_access_path": {
+                "considered_access_paths": [
+                  {
+                    "access_type": "scan",
+                    "records": 3,
+                    "cost": 2.0212,
+                    "chosen": true
+                  }
+                ] /* considered_access_paths */
+              } /* best_access_path */
+            } /* reconsidering_access_paths_for_semijoin */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "((`test`.`t6`.`d` = `f1`()) and (`test`.`t2`.`s` = arg@0))",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t6",
+                  "attached": null
+                },
+                {
+                  "database": "test",
+                  "table": "t2",
+                  "attached": "((`test`.`t6`.`d` = `f1`()) and (`test`.`t2`.`s` = arg@0))"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t6"
+              },
+              {
+                "database": "test",
+                "table": "t2",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+select d+1 into res from t6 where d= NAME_CONST('res',NULL)+1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select (`test`.`t6`.`d` + 1) AS `d+1` from `test`.`t6` where (`test`.`t6`.`d` = (res@1 + 1))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "(`test`.`t6`.`d` = (res@1 + 1))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "ref_optimizer_key_uses": [
+              {
+                "database": "test",
+                "table": "t6",
+                "field": "d",
+                "equals": "(res@1 + 1)",
+                "null_rejecting": false
+              }
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t6",
+                "range_analysis": {
+                  "table_scan": {
+                    "records": 4,
+                    "cost": 4.9068
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "d",
+                      "usable": true,
+                      "key_parts": [
+                        "d"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "d",
+                    "cost": 1.8698,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                    {
+                      "impossible_condition": {
+                        "cause": "comparison_with_null_always_false"
+                      } /* impossible_condition */
+                    }
+                  ] /* setup_range_conditions */,
+                  "impossible_range": true
+                } /* range_analysis */,
+                "records": 0,
+                "cause": "impossible_where_condition"
+              }
+            ] /* records_estimation */
+          }
+        ] /* steps */,
+        "empty_result": {
+          "cause": "no matching row in const table"
+        } /* empty_result */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+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|
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+insert into t2 select d,100,200 from t6 where d is not null	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t6`.`d` AS `d`,100 AS `100`,200 AS `200` from `test`.`t6` where (`test`.`t6`.`d` is not null)"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    }
+  ] /* steps */
+}	0	0
+insert into t2 select d,100,200 from t6 where d is not null	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t6`.`d` AS `d`,100 AS `100`,200 AS `200` from `test`.`t6` where (`test`.`t6`.`d` is not null)"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "(`test`.`t6`.`d` is not null)",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "(`test`.`t6`.`d` is not null)"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "(`test`.`t6`.`d` is not null)"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "(`test`.`t6`.`d` is not null)"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "ref_optimizer_key_uses": [
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t6",
+                "range_analysis": {
+                  "table_scan": {
+                    "records": 4,
+                    "cost": 4.9068
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "d",
+                      "usable": true,
+                      "key_parts": [
+                        "d"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "d",
+                    "cost": 1.8698,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "d",
+                        "ranges": [
+                          "NULL < d"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "records": 4,
+                        "cost": 1.8798,
+                        "rowid_ordered": false,
+                        "chosen": false,
+                        "cause": "cost"
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */
+                } /* range_analysis */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t6",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 4,
+                      "cost": 2.0068,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.8068,
+                "records_for_plan": 4,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "(`test`.`t6`.`d` is not null)",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t6",
+                  "attached": "(`test`.`t6`.`d` is not null)"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t6",
+                "scan_type": "index"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+set_trigger_field NEW.s:=`f1`()	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
+insert into t1 values("z",0)	{
+  "steps": [
+  ] /* steps */
+}	0	0
+delete from t1 where id="z"	{
+  "steps": [
+    {
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
+    }
+  ] /* steps */
+}	0	0
+select sum(data) into ret from t1	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select sum(`test`.`t1`.`data`) AS `sum(data)` from `test`.`t1`"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "records_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
+              }
+            ] /* records_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": null,
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": null
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_execution */
+    }
+  ] /* steps */
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set_trigger_field NEW.s:=`f1`()	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -6780,7 +7815,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -6865,11 +7900,23 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set_trigger_field NEW.s:=`f1`()	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -6883,7 +7930,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -6968,135 +8015,37 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
-set optimizer_trace_offset=2, optimizer_trace_limit=1|
-select * from t6 where d in (select f1() from t2 where s="c")|
-d
-select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set_trigger_field NEW.s:=`f1`()	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
-set optimizer_trace_offset=default, optimizer_trace_limit=default|
-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|
-call p1("c")|
-Warnings:
-Warning	1329	No data - zero rows fetched, selected, or processed
-select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
-select d+1 into res from t6 where d= NAME_CONST('res',NULL)+1	{
+}	0	0
+delete from t1 where id="z"	{
   "steps": [
     {
-      "join_preparation": {
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select (`test`.`t6`.`d` + 1) AS `d+1` from `test`.`t6` where (`test`.`t6`.`d` = (res@1 + 1))"
-          }
-        ] /* steps */
-      } /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        "select#": 1,
-        "steps": [
-          {
-            "condition_processing": {
-              "condition": "WHERE",
-              "original_condition": "(`test`.`t6`.`d` = (res@1 + 1))",
-              "steps": [
-                {
-                  "transformation": "equality_propagation",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
-                },
-                {
-                  "transformation": "constant_propagation",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
-                },
-                {
-                  "transformation": "trivial_condition_removal",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
-                }
-              ] /* steps */
-            } /* condition_processing */
-          },
-          {
-            "ref_optimizer_key_uses": [
-              {
-                "database": "test",
-                "table": "t6",
-                "field": "d",
-                "equals": "(res@1 + 1)",
-                "null_rejecting": false
-              }
-            ] /* ref_optimizer_key_uses */
-          },
-          {
-            "records_estimation": [
-              {
-                "database": "test",
-                "table": "t6",
-                "range_analysis": {
-                  "table_scan": {
-                    "records": 4,
-                    "cost": 4.9068
-                  } /* table_scan */,
-                  "potential_range_indices": [
-                    {
-                      "index": "d",
-                      "usable": true,
-                      "key_parts": [
-                        "d"
-                      ] /* key_parts */
-                    }
-                  ] /* potential_range_indices */,
-                  "best_covering_index_scan": {
-                    "index": "d",
-                    "cost": 1.8698,
-                    "chosen": true
-                  } /* best_covering_index_scan */,
-                  "setup_range_conditions": [
-                    {
-                      "impossible_condition": {
-                        "cause": "comparison_with_null_always_false"
-                      } /* impossible_condition */
-                    }
-                  ] /* setup_range_conditions */,
-                  "impossible_range": true
-                } /* range_analysis */,
-                "records": 0,
-                "cause": "impossible_where_condition"
-              }
-            ] /* records_estimation */
-          }
-        ] /* steps */,
-        "empty_result": {
-          "cause": "no matching row in const table"
-        } /* empty_result */
-      } /* join_optimization */
-    },
-    {
-      "join_execution": {
-        "select#": 1,
-        "steps": [
-        ] /* steps */
-      } /* join_execution */
+      "database": "test",
+      "table": "t1",
+      "range_analysis": {
+        "table_scan": {
+          "records": 3,
+          "cost": 4.7154
+        } /* table_scan */
+      } /* range_analysis */
     }
   ] /* steps */
-}	0
-create trigger trg1 before insert on t2 for each row
-begin
-set new.s=f1();  
-end|
-insert into t2 select d,100,200 from t6 where d is not null|
-select * from information_schema.OPTIMIZER_TRACE|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -7181,7 +8130,11 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
 select * from t2|
 s	i	d
 a	1	1
@@ -7191,14 +8144,15 @@ c	3	3
 3	100	200
 3	100	200
 3	100	200
-prepare stmt from 'call p1(?)';
-select QUERY from information_schema.OPTIMIZER_TRACE;
-QUERY
-call p1(?)
+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;
-Warnings:
-Warning	1329	No data - zero rows fetched, selected, or processed
+count(*)
+0
+select count(*) from information_schema.OPTIMIZER_TRACE;
+count(*)
+1
 select TRACE into @trace from information_schema.OPTIMIZER_TRACE;
 select @trace;
 @trace
@@ -7209,7 +8163,7 @@ select @trace;
         "select#": 1,
         "steps": [
           {
-            "expanded_query": "/* select#1 */ select (`test`.`t6`.`d` + 1) AS `d+1` from `test`.`t6` where (`test`.`t6`.`d` = (res@1 + 1))"
+            "expanded_query": "/* select#1 */ select count(0) AS `count(*)` from `test`.`t1` where (`test`.`t1`.`data` = 'c')"
           }
         ] /* steps */
       } /* join_preparation */
@@ -7221,76 +8175,85 @@ select @trace;
           {
             "condition_processing": {
               "condition": "WHERE",
-              "original_condition": "(`test`.`t6`.`d` = (res@1 + 1))",
+              "original_condition": "(`test`.`t1`.`data` = 'c')",
               "steps": [
                 {
                   "transformation": "equality_propagation",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                  "resulting_condition": "(`test`.`t1`.`data` = 'c')"
                 },
                 {
                   "transformation": "constant_propagation",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                  "resulting_condition": "(`test`.`t1`.`data` = 'c')"
                 },
                 {
                   "transformation": "trivial_condition_removal",
-                  "resulting_condition": "multiple equal((res@1 + 1), `test`.`t6`.`d`)"
+                  "resulting_condition": "(`test`.`t1`.`data` = 'c')"
                 }
               ] /* steps */
             } /* condition_processing */
           },
           {
             "ref_optimizer_key_uses": [
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "records_estimation": [
               {
                 "database": "test",
-                "table": "t6",
-                "field": "d",
-                "equals": "(res@1 + 1)",
-                "null_rejecting": false
+                "table": "t1",
+                "table_scan": {
+                  "records": 2,
+                  "cost": 2
+                } /* table_scan */
               }
-            ] /* ref_optimizer_key_uses */
+            ] /* records_estimation */
           },
           {
-            "records_estimation": [
+            "considered_execution_plans": [
               {
                 "database": "test",
-                "table": "t6",
-                "range_analysis": {
-                  "table_scan": {
-                    "records": 4,
-                    "cost": 4.9068
-                  } /* table_scan */,
-                  "potential_range_indices": [
-                    {
-                      "index": "d",
-                      "usable": true,
-                      "key_parts": [
-                        "d"
-                      ] /* key_parts */
-                    }
-                  ] /* potential_range_indices */,
-                  "best_covering_index_scan": {
-                    "index": "d",
-                    "cost": 1.8698,
-                    "chosen": true
-                  } /* best_covering_index_scan */,
-                  "setup_range_conditions": [
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
                     {
-                      "impossible_condition": {
-                        "cause": "comparison_with_null_always_false"
-                      } /* impossible_condition */
+                      "access_type": "scan",
+                      "using_join_cache": true,
+                      "records": 2,
+                      "cost": 2.0154,
+                      "chosen": true
                     }
-                  ] /* setup_range_conditions */,
-                  "impossible_range": true
-                } /* range_analysis */,
-                "records": 0,
-                "cause": "impossible_where_condition"
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.4154,
+                "records_for_plan": 2,
+                "chosen": true
               }
-            ] /* records_estimation */
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "(`test`.`t1`.`data` = 'c')",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": "(`test`.`t1`.`data` = 'c')"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "scan_type": "table"
+              }
+            ] /* refine_plan */
           }
-        ] /* steps */,
-        "empty_result": {
-          "cause": "no matching row in const table"
-        } /* empty_result */
+        ] /* steps */
       } /* join_optimization */
     },
     {
@@ -7302,15 +8265,19 @@ select @trace;
     }
   ] /* steps */
 }
+set optimizer_trace_offset=0, optimizer_trace_limit=100;
 execute stmt using @param;
-Warnings:
-Warning	1329	No data - zero rows fetched, selected, or processed
+count(*)
+0
+select count(*) from information_schema.OPTIMIZER_TRACE;
+count(*)
+1
 select TRACE into @trace2 from information_schema.OPTIMIZER_TRACE;
 select @trace=@trace2;
 @trace=@trace2
 1
 drop procedure p1;
-create table optt like information_schema.OPTIMIZER_TRACE;
+create temporary table optt like information_schema.OPTIMIZER_TRACE;
 create procedure p1(arg char(1))
 begin
 declare res int;
@@ -7328,7 +8295,7 @@ call  p1("c")|
 Warnings:
 Warning	1329	No data - zero rows fetched, selected, or processed
 select * from optt|
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select d into res from t6 where d in (select f1() from t2 where s= NAME_CONST('arg',_latin1'c' COLLATE 'latin1_swedish_ci'))	{
   "steps": [
     {
@@ -7670,11 +8637,15 @@ select d into res from t6 where d in (se
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -7688,7 +8659,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -7773,11 +8744,19 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
+set ret@0 NULL	{
+  "steps": [
+  ] /* steps */
+}	0	0
 insert into t1 values("z",0)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from t1 where id="z"	{
   "steps": [
     {
@@ -7791,7 +8770,7 @@ delete from t1 where id="z"	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 select sum(data) into ret from t1	{
   "steps": [
     {
@@ -7876,12 +8855,16 @@ select sum(data) into ret from t1	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
+freturn 3 ret@0	{
+  "steps": [
+  ] /* steps */
+}	0	0
 select @@optimizer_trace|
 @@optimizer_trace
 enabled=off,end_marker=on,one_line=off
 set optimizer_trace="enabled=on";
-drop table optt;
+drop temporary table optt;
 drop function f1;
 drop procedure p1;
 drop trigger trg1;
@@ -7890,7 +8873,7 @@ explain select * from v1 where id="b";
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain select * from v1 where id="b"	{
   "steps": [
     {
@@ -8008,17 +8991,17 @@ explain select * from v1 where id="b"	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 insert into v1 values("z", 100);
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 insert into v1 values("z", 100)	{
   "steps": [
   ] /* steps */
-}	0
+}	0	0
 delete from v1 where data=100;
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 delete from v1 where data=100	{
   "steps": [
     {
@@ -8032,7 +9015,7 @@ delete from v1 where data=100	{
       } /* range_analysis */
     }
   ] /* steps */
-}	0
+}	0	0
 drop view v1;
 create view v1 as select * from t1 where id < "c" limit 2;
 explain select * from v1 where id="b";
@@ -8040,7 +9023,7 @@ id	select_type	table	type	possible_keys	
 1	PRIMARY	<derived2>	ALL	NULL	NULL	NULL	NULL	2	Using where
 2	DERIVED	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 explain select * from v1 where id="b"	{
   "steps": [
     {
@@ -8270,14 +9253,14 @@ explain select * from v1 where id="b"	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 drop view v1;
 select * from information_schema.session_variables where
 VARIABLE_NAME="optimizer_trace";
 VARIABLE_NAME	VARIABLE_VALUE
 OPTIMIZER_TRACE	enabled=on,end_marker=on,one_line=off
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select * from information_schema.session_variables where
 VARIABLE_NAME="optimizer_trace"	{
   "steps": [
@@ -8387,7 +9370,7 @@ VARIABLE_NAME="optimizer_trace"	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set optimizer_trace="end_marker=off";
 select 1 union select 2;
 1
@@ -8532,7 +9515,7 @@ select @@optimizer_switch;
 @@optimizer_switch
 index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,materialization=on,semijoin=on,loosescan=on,firstmatch=on
 select * from information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 select "abc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111def" as col	{
   "steps": [
     {
@@ -8560,7 +9543,7 @@ select "abc11111111111111111111111111111
       }
     }
   ]
-}	0
+}	0	0
 drop table t1,t2;
 DROP TABLE t5,t6;
 set optimizer_trace=default;

=== modified file 'mysql-test/r/optimizer_trace_range_no_prot.result'
--- a/mysql-test/r/optimizer_trace_range_no_prot.result	2011-04-06 09:19:23 +0000
+++ b/mysql-test/r/optimizer_trace_range_no_prot.result	2011-05-04 20:53:28 +0000
@@ -40,7 +40,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	range	i2	i2	4	NULL	47	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE key2 < 5 OR key2 > 1020	{
   "steps": [
     {
@@ -234,7 +234,7 @@ EXPLAIN SELECT * FROM t1 WHERE key2 < 5 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set @@optimizer_trace_features="range_optimizer=off";
 
 EXPLAIN SELECT * FROM t1 WHERE key2 < 5 OR key2 > 1020;
@@ -242,7 +242,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	range	i2	i2	4	NULL	47	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE key2 < 5 OR key2 > 1020	{
   "steps": [
     {
@@ -371,7 +371,7 @@ EXPLAIN SELECT * FROM t1 WHERE key2 < 5 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set @@optimizer_trace_features="range_optimizer=on";
 
 EXPLAIN SELECT * FROM t1 WHERE key1 < 3 OR key2 > 1020;
@@ -379,7 +379,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	index_merge	i1,i2	i1,i2	4,4	NULL	45	Using sort_union(i1,i2); Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE key1 < 3 OR key2 > 1020	{
   "steps": [
     {
@@ -611,14 +611,14 @@ EXPLAIN SELECT * FROM t1 WHERE key1 < 3 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT key2, MIN(key2_1) FROM t2 GROUP BY key2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	range	NULL	i2_1	4	NULL	103	Using index for group-by
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT key2, MIN(key2_1) FROM t2 GROUP BY key2	{
   "steps": [
     {
@@ -808,13 +808,13 @@ EXPLAIN SELECT key2, MIN(key2_1) FROM t2
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 EXPLAIN SELECT DISTINCT key2 FROM t2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	range	NULL	i2_1	4	NULL	103	Using index for group-by
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT DISTINCT key2 FROM t2	{
   "steps": [
     {
@@ -1004,7 +1004,7 @@ EXPLAIN SELECT DISTINCT key2 FROM t2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT key2, MIN(key2_1) FROM t2 
 WHERE key2 = 5 or key2 = 4 or key2 = 3 or key2 = 2 or key2 = 1 
@@ -1013,7 +1013,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	range	i2_1,i2_2	i2_1	4	NULL	47	Using where; Using index
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT key2, MIN(key2_1) FROM t2 
 WHERE key2 = 5 or key2 = 4 or key2 = 3 or key2 = 2 or key2 = 1 
 GROUP BY key2	{
@@ -1279,14 +1279,14 @@ GROUP BY key2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE key2 = 1 AND (key2_1 = 1 OR key3 = 5);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ref	i2_1,i2_2	i2_1	4	const	10	Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE key2 = 1 AND (key2_1 = 1 OR key3 = 5)	{
   "steps": [
     {
@@ -1500,14 +1500,14 @@ EXPLAIN SELECT * FROM t2 WHERE key2 = 1 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t1 WHERE key2=10 OR key3=3 OR key4 <=> null;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	index_merge	i2,i3,i4	i2,i3	4,4	NULL	2	Using union(i2,i3); Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE key2=10 OR key3=3 OR key4 <=> null	{
   "steps": [
     {
@@ -1774,14 +1774,14 @@ EXPLAIN SELECT * FROM t1 WHERE key2=10 O
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE key2_1 < 79 OR key2 = 2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ALL	i2_1,i2_2	NULL	NULL	NULL	1024	Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE key2_1 < 79 OR key2 = 2	{
   "steps": [
     {
@@ -1928,14 +1928,14 @@ EXPLAIN SELECT * FROM t2 WHERE key2_1 < 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE key1a = 5 and key1b < 10;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	range	PRIMARY,i1b	PRIMARY	8	NULL	1	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE key1a = 5 and key1b < 10	{
   "steps": [
     {
@@ -2137,7 +2137,7 @@ EXPLAIN SELECT * FROM t2 WHERE key1a = 5
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE (key1a = 5 and key1b < 10 and key1b > 2) or
 (key1a = 4 and key1b < 7 and key1b > 3);
@@ -2145,7 +2145,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	range	PRIMARY,i1b	PRIMARY	8	NULL	2	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE (key1a = 5 and key1b < 10 and key1b > 2) or
 (key1a = 4 and key1b < 7 and key1b > 3)	{
   "steps": [
@@ -2338,7 +2338,7 @@ EXPLAIN SELECT * FROM t2 WHERE (key1a = 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE (key1b < 10 and key1b > 7) and 
 (key1a = 4 or key1a = 5);
@@ -2346,7 +2346,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	range	PRIMARY,i1b	i1b	4	NULL	2	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE (key1b < 10 and key1b > 7) and 
 (key1a = 4 or key1a = 5)	{
   "steps": [
@@ -2537,14 +2537,14 @@ EXPLAIN SELECT * FROM t2 WHERE (key1b < 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t1 WHERE (key1 > 1 OR key2  > 2);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ALL	i1,i2	NULL	NULL	NULL	1024	Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE (key1 > 1 OR key2  > 2)	{
   "steps": [
     {
@@ -2752,7 +2752,7 @@ EXPLAIN SELECT * FROM t1 WHERE (key1 > 1
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT STRAIGHT_JOIN * FROM t1, t2 
 WHERE t1.key1=t2.key1a AND t1.key2 > 1020;
@@ -2761,7 +2761,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	ref	PRIMARY	PRIMARY	4	test.t1.key1	10	
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT STRAIGHT_JOIN * FROM t1, t2 
 WHERE t1.key1=t2.key1a AND t1.key2 > 1020	{
   "steps": [
@@ -3014,7 +3014,7 @@ WHERE t1.key1=t2.key1a AND t1.key2 > 102
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 DROP TABLE t1,t2;
 CREATE TABLE t1 (
 cola char(3) not null, 
@@ -3031,7 +3031,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	index_merge	cola,colb	cola,colb	3,3	NULL	32	Using intersect(cola,colb); Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE cola = 'foo' AND colb = 'bar'	{
   "steps": [
     {
@@ -3274,14 +3274,14 @@ EXPLAIN SELECT * FROM t1 WHERE cola = 'f
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t1 WHERE cola = 'f\no';
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ref	cola	cola	3	const	1	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE cola = 'f\no'	{
   "steps": [
     {
@@ -3455,7 +3455,7 @@ EXPLAIN SELECT * FROM t1 WHERE cola = 'f
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 DROP TABLE t1;
 CREATE TABLE t1(c INT);
 INSERT INTO t1 VALUES (),();
@@ -3470,7 +3470,7 @@ id	select_type	table	type	possible_keys	
 2	DERIVED	t2	ALL	b	NULL	NULL	NULL	3	Range checked for each record (index map: 0x1)
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT 1 FROM 
 (SELECT 1 FROM t2,t1 WHERE b < c GROUP BY 1 LIMIT 1) AS d2	{
   "steps": [
@@ -3751,7 +3751,7 @@ EXPLAIN SELECT 1 FROM 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 SET optimizer_trace_features="greedy_search=off,dynamic_range=off";
 EXPLAIN SELECT 1 FROM 
@@ -3762,7 +3762,7 @@ id	select_type	table	type	possible_keys	
 2	DERIVED	t2	ALL	b	NULL	NULL	NULL	3	Range checked for each record (index map: 0x1)
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT 1 FROM 
 (SELECT 1 FROM t2,t1 WHERE b < c GROUP BY 1 LIMIT 1) AS d2	{
   "steps": [
@@ -4009,7 +4009,7 @@ EXPLAIN SELECT 1 FROM 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 DROP TABLE t1,t2;
 CREATE TABLE `t1` (
@@ -4022,7 +4022,7 @@ SELECT * from t1 where topic = all (SELE
 mot	topic
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT * from t1 where topic = all (SELECT topic FROM t1 GROUP BY topic)	{
   "steps": [
     {
@@ -4264,7 +4264,7 @@ SELECT * from t1 where topic = all (SELE
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 drop table t1;
 CREATE TABLE t1 (
@@ -4280,7 +4280,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	range	k1,k2	k2	5	NULL	2	Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE i1 > '2' ORDER BY i1, i2	{
   "steps": [
     {
@@ -4505,14 +4505,14 @@ EXPLAIN SELECT * FROM t1 WHERE i1 > '2' 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE i1 >= '1' ORDER BY i1 DESC;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	index	k1,k2	k1	5	NULL	2	Using where; Using index
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE i1 >= '1' ORDER BY i1 DESC	{
   "steps": [
     {
@@ -5067,6 +5067,6 @@ EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 DROP TABLE t1;

=== modified file 'mysql-test/r/optimizer_trace_range_ps_prot.result'
--- a/mysql-test/r/optimizer_trace_range_ps_prot.result	2011-04-06 09:19:23 +0000
+++ b/mysql-test/r/optimizer_trace_range_ps_prot.result	2011-05-04 20:53:28 +0000
@@ -40,7 +40,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	range	i2	i2	4	NULL	47	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE key2 < 5 OR key2 > 1020	{
   "steps": [
     {
@@ -234,7 +234,7 @@ EXPLAIN SELECT * FROM t1 WHERE key2 < 5 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set @@optimizer_trace_features="range_optimizer=off";
 
 EXPLAIN SELECT * FROM t1 WHERE key2 < 5 OR key2 > 1020;
@@ -242,7 +242,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	range	i2	i2	4	NULL	47	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE key2 < 5 OR key2 > 1020	{
   "steps": [
     {
@@ -371,7 +371,7 @@ EXPLAIN SELECT * FROM t1 WHERE key2 < 5 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 set @@optimizer_trace_features="range_optimizer=on";
 
 EXPLAIN SELECT * FROM t1 WHERE key1 < 3 OR key2 > 1020;
@@ -379,7 +379,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	index_merge	i1,i2	i1,i2	4,4	NULL	45	Using sort_union(i1,i2); Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE key1 < 3 OR key2 > 1020	{
   "steps": [
     {
@@ -611,14 +611,14 @@ EXPLAIN SELECT * FROM t1 WHERE key1 < 3 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT key2, MIN(key2_1) FROM t2 GROUP BY key2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	range	NULL	i2_1	4	NULL	103	Using index for group-by
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT key2, MIN(key2_1) FROM t2 GROUP BY key2	{
   "steps": [
     {
@@ -808,13 +808,13 @@ EXPLAIN SELECT key2, MIN(key2_1) FROM t2
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 EXPLAIN SELECT DISTINCT key2 FROM t2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	range	NULL	i2_1	4	NULL	103	Using index for group-by
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT DISTINCT key2 FROM t2	{
   "steps": [
     {
@@ -1004,7 +1004,7 @@ EXPLAIN SELECT DISTINCT key2 FROM t2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT key2, MIN(key2_1) FROM t2 
 WHERE key2 = 5 or key2 = 4 or key2 = 3 or key2 = 2 or key2 = 1 
@@ -1013,7 +1013,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	range	i2_1,i2_2	i2_1	4	NULL	47	Using where; Using index
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT key2, MIN(key2_1) FROM t2 
 WHERE key2 = 5 or key2 = 4 or key2 = 3 or key2 = 2 or key2 = 1 
 GROUP BY key2	{
@@ -1279,14 +1279,14 @@ GROUP BY key2	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE key2 = 1 AND (key2_1 = 1 OR key3 = 5);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ref	i2_1,i2_2	i2_1	4	const	10	Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE key2 = 1 AND (key2_1 = 1 OR key3 = 5)	{
   "steps": [
     {
@@ -1500,14 +1500,14 @@ EXPLAIN SELECT * FROM t2 WHERE key2 = 1 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t1 WHERE key2=10 OR key3=3 OR key4 <=> null;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	index_merge	i2,i3,i4	i2,i3	4,4	NULL	2	Using union(i2,i3); Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE key2=10 OR key3=3 OR key4 <=> null	{
   "steps": [
     {
@@ -1774,14 +1774,14 @@ EXPLAIN SELECT * FROM t1 WHERE key2=10 O
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE key2_1 < 79 OR key2 = 2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ALL	i2_1,i2_2	NULL	NULL	NULL	1024	Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE key2_1 < 79 OR key2 = 2	{
   "steps": [
     {
@@ -1928,14 +1928,14 @@ EXPLAIN SELECT * FROM t2 WHERE key2_1 < 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE key1a = 5 and key1b < 10;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	range	PRIMARY,i1b	PRIMARY	8	NULL	1	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE key1a = 5 and key1b < 10	{
   "steps": [
     {
@@ -2137,7 +2137,7 @@ EXPLAIN SELECT * FROM t2 WHERE key1a = 5
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE (key1a = 5 and key1b < 10 and key1b > 2) or
 (key1a = 4 and key1b < 7 and key1b > 3);
@@ -2145,7 +2145,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	range	PRIMARY,i1b	PRIMARY	8	NULL	2	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE (key1a = 5 and key1b < 10 and key1b > 2) or
 (key1a = 4 and key1b < 7 and key1b > 3)	{
   "steps": [
@@ -2338,7 +2338,7 @@ EXPLAIN SELECT * FROM t2 WHERE (key1a = 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t2 WHERE (key1b < 10 and key1b > 7) and 
 (key1a = 4 or key1a = 5);
@@ -2346,7 +2346,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	range	PRIMARY,i1b	i1b	4	NULL	2	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t2 WHERE (key1b < 10 and key1b > 7) and 
 (key1a = 4 or key1a = 5)	{
   "steps": [
@@ -2537,14 +2537,14 @@ EXPLAIN SELECT * FROM t2 WHERE (key1b < 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t1 WHERE (key1 > 1 OR key2  > 2);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ALL	i1,i2	NULL	NULL	NULL	1024	Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE (key1 > 1 OR key2  > 2)	{
   "steps": [
     {
@@ -2752,7 +2752,7 @@ EXPLAIN SELECT * FROM t1 WHERE (key1 > 1
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT STRAIGHT_JOIN * FROM t1, t2 
 WHERE t1.key1=t2.key1a AND t1.key2 > 1020;
@@ -2761,7 +2761,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t2	ref	PRIMARY	PRIMARY	4	test.t1.key1	10	
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT STRAIGHT_JOIN * FROM t1, t2 
 WHERE t1.key1=t2.key1a AND t1.key2 > 1020	{
   "steps": [
@@ -3014,7 +3014,7 @@ WHERE t1.key1=t2.key1a AND t1.key2 > 102
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 DROP TABLE t1,t2;
 CREATE TABLE t1 (
 cola char(3) not null, 
@@ -3031,7 +3031,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	index_merge	cola,colb	cola,colb	3,3	NULL	32	Using intersect(cola,colb); Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE cola = 'foo' AND colb = 'bar'	{
   "steps": [
     {
@@ -3274,14 +3274,14 @@ EXPLAIN SELECT * FROM t1 WHERE cola = 'f
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT * FROM t1 WHERE cola = 'f\no';
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ref	cola	cola	3	const	1	Using index condition
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE cola = 'f\no'	{
   "steps": [
     {
@@ -3455,7 +3455,7 @@ EXPLAIN SELECT * FROM t1 WHERE cola = 'f
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 DROP TABLE t1;
 CREATE TABLE t1(c INT);
 INSERT INTO t1 VALUES (),();
@@ -3470,7 +3470,7 @@ id	select_type	table	type	possible_keys	
 2	DERIVED	t2	ALL	b	NULL	NULL	NULL	3	Range checked for each record (index map: 0x1)
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT 1 FROM 
 (SELECT 1 FROM t2,t1 WHERE b < c GROUP BY 1 LIMIT 1) AS d2	{
   "steps": [
@@ -3751,7 +3751,7 @@ EXPLAIN SELECT 1 FROM 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 SET optimizer_trace_features="greedy_search=off,dynamic_range=off";
 EXPLAIN SELECT 1 FROM 
@@ -3762,7 +3762,7 @@ id	select_type	table	type	possible_keys	
 2	DERIVED	t2	ALL	b	NULL	NULL	NULL	3	Range checked for each record (index map: 0x1)
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT 1 FROM 
 (SELECT 1 FROM t2,t1 WHERE b < c GROUP BY 1 LIMIT 1) AS d2	{
   "steps": [
@@ -4009,7 +4009,7 @@ EXPLAIN SELECT 1 FROM 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 DROP TABLE t1,t2;
 CREATE TABLE `t1` (
@@ -4022,7 +4022,7 @@ SELECT * from t1 where topic = all (SELE
 mot	topic
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT * from t1 where topic = all (SELECT topic FROM t1 GROUP BY topic)	{
   "steps": [
     {
@@ -4256,7 +4256,7 @@ SELECT * from t1 where topic = all (SELE
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 drop table t1;
 CREATE TABLE t1 (
@@ -4272,7 +4272,7 @@ id	select_type	table	type	possible_keys	
 1	SIMPLE	t1	range	k1,k2	k2	5	NULL	2	Using where
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT * FROM t1 WHERE i1 > '2' ORDER BY i1, i2	{
   "steps": [
     {
@@ -4497,14 +4497,14 @@ EXPLAIN SELECT * FROM t1 WHERE i1 > '2' 
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE i1 >= '1' ORDER BY i1 DESC;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	index	k1,k2	k1	5	NULL	2	Using where; Using index
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE i1 >= '1' ORDER BY i1 DESC	{
   "steps": [
     {
@@ -5059,6 +5059,6 @@ EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 DROP TABLE t1;

=== added file 'mysql-test/r/optimizer_trace_security_no_prot.result'
--- a/mysql-test/r/optimizer_trace_security_no_prot.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/optimizer_trace_security_no_prot.result	2011-05-04 20:53:28 +0000
@@ -0,0 +1,1074 @@
+set @old_size = @@global.optimizer_trace_max_mem_size;
+set global optimizer_trace_max_mem_size=1048576;
+select user();
+user()
+root@localhost
+create database somedb;
+use somedb;
+create table t1(a varchar(100));
+insert into t1 values("first");
+create table t2(a varchar(100));
+insert into t2 values("first");
+create table t3(a varchar(100));
+insert into t3 values("first");
+create procedure p1() sql security definer
+begin
+declare b int;
+if (select count(*) from t1)
+then
+select 22 into b from dual;
+end if;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+end|
+create function f1() returns int sql security definer
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end|
+create trigger trg2 before insert on t2 for each row 
+begin
+insert into t3 select * from t3;
+end|
+create sql security definer view v1 as select * from t1;
+create user user1@localhost identified by '';
+grant all on *.* to user1@localhost with grant option;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=on";
+show grants;
+Grants for user1@localhost
+GRANT ALL PRIVILEGES ON *.* TO 'user1'@'localhost' WITH GRANT OPTION
+
+# ==========================================================
+# Part A.
+# Test that security context changes are allowed when, and only
+# when, invoker has all global privileges.
+# ==========================================================
+
+# Because invoker has all global privileges, all traces are visible:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+select 22 into b from dual	407	0
+select a into b from t1 limit 1	1047	0
+insert into t1 values(current_user())	20	0
+# this SET always purges all remembered traces
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+select a into b from t1 limit 1	2010	0
+insert into t1 values(current_user())	20	0
+freturn 3 36	20	0
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+root@localhost
+root@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select * from v1	2163	0
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+insert into t2 values(current_user())	20	0
+insert into t3 select * from t3	1039	0
+
+# Show that really all global privileges are needed: let root
+# revoke just one from user1. Because user1 does not have all global
+# privileges anymore, security context changes are forbidden,
+# thus there is no trace.
+
+select user();
+user()
+root@localhost
+revoke shutdown on *.* from user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=on";
+show grants;
+Grants for user1@localhost
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE ON *.* TO 'user1'@'localhost' WITH GRANT OPTION
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# In CALL we execute stored procedure and notice a security
+# context change. The context change is probably only relevant
+# for substatements, but we still hide CALL. This is to be
+# consistent with what we do when routine body should not be
+# exposed. And it also feels safer to disable I_S output as
+# soon as possible.
+# Ps-protocol-specific note: mysqltest uses normal protocol for CALL
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+		1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+root@localhost
+root@localhost
+root@localhost
+root@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+		1
+
+# Verify that user1 cannot circumvent security checks by
+# setting @@optimizer_trace_offset so that I_S output is disabled
+# before the object (routine) is checked, and enabled in the
+# middle of object usage, when 'offset' is passed.
+
+set optimizer_trace_offset=2,optimizer_trace_limit=1;
+call p1();
+# Even though the routine's execution started before
+# 'offset', it detected the security context changes. So the
+# trace of CALL gets the "missing privilege" mark but we don't
+# see it as CALL was before 'offset'.
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+
+# Finally, verify that if the routine's definer does modify
+# @@optimizer_trace from "enabled=off" to "enabled=on", in the
+# body of the routine, then tracing works. This is no security
+# issue, as it was done by the routine's definer.
+
+select user();
+user()
+root@localhost
+create procedure p2() sql security definer
+begin
+declare b int;
+set optimizer_trace="enabled=on";
+select 22 into b from dual;
+end|
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=off";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p2();
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+select 22 into b from dual	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select 22 AS `22`"
+          }
+        ]
+      }
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    }
+  ]
+}	0
+# Variable is as set by the routine
+select @@optimizer_trace;
+@@optimizer_trace
+enabled=on,end_marker=off,one_line=off
+
+# ==========================================================
+# Part B.
+# Do same tests but with SQL SECURITY INVOKER objects, to verify that
+# the restriction on security context changes is not present.
+# ==========================================================
+
+select user();
+user()
+root@localhost
+alter procedure p1 sql security invoker;
+alter function f1 sql security invoker;
+alter sql security invoker view v1 as select * from t1;
+# Triggers cannot be SQL SECURITY INVOKER so we don't test
+# them here.
+alter procedure p2 sql security invoker;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+select 22 into b from dual	407	0
+select a into b from t1 limit 1	1047	0
+insert into t1 values(current_user())	20	0
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+select a into b from t1 limit 1	2010	0
+insert into t1 values(current_user())	20	0
+freturn 3 36	20	0
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select * from v1	2163	0
+set optimizer_trace_offset=2,optimizer_trace_limit=1;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+set optimizer_trace="enabled=off";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p2();
+# SELECT substatement is traced (no security context change)
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+select 22 into b from dual	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select 22 AS `22`"
+          }
+        ]
+      }
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    }
+  ]
+}	0
+select @@optimizer_trace;
+@@optimizer_trace
+enabled=on,end_marker=off,one_line=off
+
+# ==========================================================
+# Part C.
+# User1 got traces. Determine the minimum set of privileges he
+# needed for that.
+# ==========================================================
+
+drop procedure p2;
+select user();
+user()
+root@localhost
+revoke all privileges, grant option from user1@localhost;
+# Grant minimum privileges to use the routines and views,
+# without considering optimizer trace:
+grant execute on procedure p1 to user1@localhost;
+grant execute on function f1 to user1@localhost;
+grant select (a) on v1 to user1@localhost;
+# Objects above are SQL SECURITY INVOKER, so invoker needs
+# privileges on objects used internally:
+grant select (a) on t1 to user1@localhost;
+grant insert (a) on t1 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=on";
+show grants;
+Grants for user1@localhost
+GRANT USAGE ON *.* TO 'user1'@'localhost'
+GRANT SELECT (a), INSERT (a) ON `somedb`.`t1` TO 'user1'@'localhost'
+GRANT SELECT (a) ON `somedb`.`v1` TO 'user1'@'localhost'
+GRANT EXECUTE ON PROCEDURE `somedb`.`p1` TO 'user1'@'localhost'
+GRANT EXECUTE ON FUNCTION `somedb`.`f1` TO 'user1'@'localhost'
+
+# Those privileges are not enough to see traces:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# In CALL we execute stored procedure and notice that body should
+# not be exposed. The trace of this CALL would not expose the
+# body. Trace of substatements would. But, due to
+# implementation, CALL is hidden.
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+# SELECT is hidden (same reason as for CALL).
+# Ps-protocol-specific note: preparation of SELECT above does not
+# execute f1, so does not risk exposing body, so its trace is
+# visible.
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+		1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it would expose body of view
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+
+# C.0) Add more privileges:
+
+select user();
+user()
+root@localhost
+# - for use of t1 in routines and view:
+grant select on t1 to user1@localhost;
+# - for use of routines:
+grant select on mysql.proc to user1@localhost;
+# - for use of view:
+grant select, show view on v1 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+select 22 into b from dual	407	0
+select a into b from t1 limit 1	1038	0
+insert into t1 values(current_user())	20	0
+# Trace exposed body of routine, and content of t1, which we
+# could see anyway:
+show create procedure p1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+p1		CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
+    SQL SECURITY INVOKER
+begin
+declare b int;
+if (select count(*) from t1)
+then
+select 22 into b from dual;
+end if;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+select * from t1 limit 1;
+a
+first
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+select a into b from t1 limit 1	2010	0
+insert into t1 values(current_user())	20	0
+freturn 3 36	20	0
+# Trace exposed body of routine, and content of t1, which we
+# could see anyway:
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
+    SQL SECURITY INVOKER
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select * from v1	2163	0
+# Trace exposed body of view, and content of t1, which we
+# could see anyway:
+show create view v1;
+View	Create View	character_set_client	collation_connection
+v1	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY INVOKER VIEW `v1` AS select `t1`.`a` AS `a` from `t1`	latin1	latin1_swedish_ci
+
+# Now remove each privilege to verify that it was needed:
+# C.1) remove table-level SELECT privilege on t1
+
+select user();
+user()
+root@localhost
+revoke select on t1 from user1@localhost;
+grant select (a) on t1 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# Cannot see those substatements which use t1
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+call p1()	{
+  "steps": [
+  ]
+}	0
+set b@0 NULL	{
+  "steps": [
+  ]
+}	0
+		1
+select 22 into b from dual	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select 22 AS `22`"
+          }
+        ]
+      }
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    }
+  ]
+}	0
+		1
+		1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+# Cannot see those substatements which use t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+	0	1
+	0	1
+freturn 3 36	20	0
+# Trace exposed body of routine, which we could see anyway:
+set optimizer_trace="enabled=off";
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
+    SQL SECURITY INVOKER
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace="enabled=on";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it might expose some data from columns
+# of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+
+# C.2) remove table-level SELECT privilege on mysql.proc
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in C.1
+grant select on t1 to user1@localhost;
+# And remove a next one:
+revoke select on mysql.proc from user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+# We have no right to see routines' bodies:
+set optimizer_trace="enabled=off";
+show create procedure p1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+p1		NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
+# Verify that optimizer trace does not influence the privilege
+# checking in SHOW CREATE:
+set optimizer_trace="enabled=on";
+show create procedure p1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+p1		NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# Cannot see anything as it would expose body of routine
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+		1
+
+# C.3) remove table-level SELECT privilege on view
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in C.2
+grant select on mysql.proc to user1@localhost;
+# And remove a next one:
+revoke select on v1 from user1@localhost;
+grant select (a) on v1 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+# Cannot see anything as it might expose some data from columns
+# of v1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+
+# C.4) remove SHOW VIEW privilege on view
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in C.3
+grant select on v1 to user1@localhost;
+# And remove a next one:
+revoke show view on v1 from user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=off";
+# We have no right to see view's body:
+show create view v1;
+ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
+set optimizer_trace="enabled=on";
+# Verify that optimizer trace does not influence the privilege
+# checking in SHOW CREATE:
+show create view v1;
+ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+# Cannot see anything as it would expose body of view
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+		1
+
+# ==========================================================
+# Part D.
+# Like Part C, but instead of SQL SECURITY INVOKER objects
+# created by root and used by User1, let's have SQL SECURITY
+# DEFINER objects created and used by User1. Determine the
+# minimum set of privileges he needs for that.
+# ==========================================================
+
+select user();
+user()
+root@localhost
+drop procedure p1;
+drop function f1;
+drop view v1;
+drop trigger trg2;
+revoke all privileges, grant option from user1@localhost;
+# Grant minimum privileges to create and use objects,
+# without considering optimizer trace:
+grant create routine on somedb.* to user1@localhost;
+grant trigger on t2 to user1@localhost;
+grant create view on somedb.* to user1@localhost;
+grant select (a) on t1 to user1@localhost;
+grant insert (a) on t1 to user1@localhost;
+grant insert (a) on t2 to user1@localhost;
+grant select (a) on t3 to user1@localhost;
+grant insert (a) on t3 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=on";
+create procedure p1() sql security definer
+begin
+declare b int;
+if (select count(*) from t1)
+then
+select 22 into b from dual;
+end if;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+end|
+create function f1() returns int sql security definer
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end|
+create trigger trg2 before insert on t2 for each row 
+begin
+insert into t3 select * from t3;
+end|
+create sql security definer view v1 as select * from t1;
+# Creating a view is not enough to be able to SELECT it...
+select user();
+user()
+root@localhost
+grant select (a) on v1 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+# Those privileges are not enough to see traces:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# Can see body of routine (as definer), but not statements using t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+	0	1
+select 22 into b from dual	407	0
+	0	1
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+# Can see body of routine (as definer), but not statements using t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+	0	1
+	0	1
+freturn 3 36	20	0
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		CREATE DEFINER=`user1`@`localhost` FUNCTION `f1`() RETURNS int(11)
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it might expose some data from columns
+# of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+# Cannot see anything as it might expose some data from
+# columns of t2
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+# Also test a query accessing t1 in FROM clause:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select a from (select a from t1 where a like "f%") as tt where a like "fi%";
+a
+first
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+
+# D.0) Add more privileges:
+
+select user();
+user()
+root@localhost
+# - for use of t1 in routines and view:
+grant select on t1 to user1@localhost;
+# - for use of view:
+grant select, show view on v1 to user1@localhost;
+# - for use of trigger
+grant select on t2 to user1@localhost;
+grant select on t3 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+select 22 into b from dual	407	0
+select a into b from t1 limit 1	1038	0
+insert into t1 values(current_user())	20	0
+# Trace exposed body of routine, and content of t1, which we
+# could see anyway:
+show create procedure p1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+p1		CREATE DEFINER=`user1`@`localhost` PROCEDURE `p1`()
+begin
+declare b int;
+if (select count(*) from t1)
+then
+select 22 into b from dual;
+end if;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+select * from t1 limit 1;
+a
+first
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+select a into b from t1 limit 1	2010	0
+insert into t1 values(current_user())	20	0
+freturn 3 36	20	0
+# Trace exposed body of routine, and content of t1, which we
+# could see anyway:
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		CREATE DEFINER=`user1`@`localhost` FUNCTION `f1`() RETURNS int(11)
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select * from v1	2163	0
+# Trace exposed body of view, and content of t1, which we
+# could see anyway:
+show create view v1;
+View	Create View	character_set_client	collation_connection
+v1	CREATE ALGORITHM=UNDEFINED DEFINER=`user1`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1`	latin1	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+insert into t2 values(current_user())	20	0
+insert into t3 select * from t3	2020	0
+# Trace exposed body of trigger, and content of t2/t3, which we
+# could see anyway:
+show create trigger trg2;
+Trigger	sql_mode	SQL Original Statement	character_set_client	collation_connection	Database Collation
+trg2		CREATE DEFINER=`user1`@`localhost` trigger trg2 before insert on t2 for each row 
+begin
+insert into t3 select * from t3;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+select * from t2, t3 limit 1;
+a	a
+first	first
+# Trace exposed content of t1 which we could see anyway:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select a from (select a from t1 where a like "f%") as tt where a like "fi%";
+a
+first
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select a from (select a from t1 where a like "f%") as tt where a like "fi%"	4838	0
+
+# For routines, as they only use t1 and we added only one
+# privilege on t1, we have nothing to remove.
+
+# Now remove each privilege to verify that it was needed for
+# the view.
+# D.1) remove table-level SELECT privilege on v1
+
+select user();
+user()
+root@localhost
+revoke select on v1 from user1@localhost;
+grant select (a) on v1 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it might expose some data from columns
+# of v1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+
+# D.2) remove table-level SHOW VIEW privilege on v1
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in D.1
+grant select on v1 to user1@localhost;
+# And remove a next one:
+revoke show view on v1 from user1@localhost;
+
+select user();
+user()
+user1@localhost
+# We have no right to see view's body:
+show create view v1;
+ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it would expose body of view
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+		1
+
+# D.3) remove table-level SELECT privilege on t1
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in D.2
+grant show view on v1 to user1@localhost;
+# And remove a next one:
+revoke select on t1 from user1@localhost;
+grant select (a) on t1 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it might expose some data from columns
+# of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+
+# Now remove each privilege to verify that it was needed for
+# the trigger:
+# D.4) remove table-level SELECT privilege on t2
+
+select user();
+user()
+root@localhost
+revoke select on t2 from user1@localhost;
+grant select (a) on t2 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+# Cannot see anything as it might expose some data from
+# columns of t2
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+
+# D.5) remove table-level SELECT privilege on t3
+
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in D.4
+grant select on t2 to user1@localhost;
+# And remove a next one:
+revoke select on t3 from user1@localhost;
+grant select (a) on t3 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+# Cannot see substatement as it might expose some data from
+# columns of t3
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+insert into t2 values(current_user())	20	0
+	0	1
+
+# Cleanup
+select user();
+user()
+root@localhost
+drop user user1@localhost;
+
+# ==========================================================
+# Part E.
+# Misc tests.
+# ==========================================================
+
+select user();
+user()
+root@localhost
+drop view v1;
+create sql security definer view v1 as select * from t1 where 'secret';
+create user user1@localhost identified by '';
+grant create, insert, select on somedb.* to user1@localhost;
+grant create routine on somedb.* to user1@localhost;
+
+select user();
+user()
+user1@localhost
+user1 cannot see view's body:
+show create view v1;
+ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
+user1 creates a procedure
+create procedure proc() sql security definer
+begin
+set optimizer_trace="enabled=on";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1 limit 0;
+create table leak select * from information_schema.optimizer_trace;
+set optimizer_trace="enabled=off";
+end|
+select user();
+user()
+root@localhost
+root runs procedure, without fear of risk as it is SQL SECURITY DEFINER
+call proc();
+a
+
+select user();
+user()
+user1@localhost
+user1 cannot see view's body:
+select * from leak;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+
+# Cleanup
+select user();
+user()
+root@localhost
+drop database somedb;
+drop user user1@localhost;
+set @@global.optimizer_trace_max_mem_size = @old_size;

=== added file 'mysql-test/r/optimizer_trace_security_ps_prot.result'
--- a/mysql-test/r/optimizer_trace_security_ps_prot.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/optimizer_trace_security_ps_prot.result	2011-05-04 20:53:28 +0000
@@ -0,0 +1,1146 @@
+set @old_size = @@global.optimizer_trace_max_mem_size;
+set global optimizer_trace_max_mem_size=1048576;
+select user();
+user()
+root@localhost
+create database somedb;
+use somedb;
+create table t1(a varchar(100));
+insert into t1 values("first");
+create table t2(a varchar(100));
+insert into t2 values("first");
+create table t3(a varchar(100));
+insert into t3 values("first");
+create procedure p1() sql security definer
+begin
+declare b int;
+if (select count(*) from t1)
+then
+select 22 into b from dual;
+end if;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+end|
+create function f1() returns int sql security definer
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end|
+create trigger trg2 before insert on t2 for each row 
+begin
+insert into t3 select * from t3;
+end|
+create sql security definer view v1 as select * from t1;
+create user user1@localhost identified by '';
+grant all on *.* to user1@localhost with grant option;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=on";
+show grants;
+Grants for user1@localhost
+GRANT ALL PRIVILEGES ON *.* TO 'user1'@'localhost' WITH GRANT OPTION
+
+# ==========================================================
+# Part A.
+# Test that security context changes are allowed when, and only
+# when, invoker has all global privileges.
+# ==========================================================
+
+# Because invoker has all global privileges, all traces are visible:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+select 22 into b from dual	407	0
+select a into b from t1 limit 1	1047	0
+insert into t1 values(current_user())	20	0
+# this SET always purges all remembered traces
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	214	0
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+select a into b from t1 limit 1	2010	0
+insert into t1 values(current_user())	20	0
+freturn 3 36	20	0
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+root@localhost
+root@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select * from v1	402	0
+select * from v1	2004	0
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+insert into t2 values(current_user())	20	0
+insert into t2 values(current_user())	20	0
+insert into t3 select * from t3	1039	0
+
+# Show that really all global privileges are needed: let root
+# revoke just one from user1. Because user1 does not have all global
+# privileges anymore, security context changes are forbidden,
+# thus there is no trace.
+
+select user();
+user()
+root@localhost
+revoke shutdown on *.* from user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=on";
+show grants;
+Grants for user1@localhost
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE ON *.* TO 'user1'@'localhost' WITH GRANT OPTION
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# In CALL we execute stored procedure and notice a security
+# context change. The context change is probably only relevant
+# for substatements, but we still hide CALL. This is to be
+# consistent with what we do when routine body should not be
+# exposed. And it also feels safer to disable I_S output as
+# soon as possible.
+# Ps-protocol-specific note: mysqltest uses normal protocol for CALL
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+select f1()	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `f1`() AS `f1()`"
+          }
+        ]
+      }
+    }
+  ]
+}	0
+		1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+root@localhost
+root@localhost
+root@localhost
+root@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+insert into t2 values(current_user())	{
+  "steps": [
+  ]
+}	0
+		1
+
+# Verify that user1 cannot circumvent security checks by
+# setting @@optimizer_trace_offset so that I_S output is disabled
+# before the object (routine) is checked, and enabled in the
+# middle of object usage, when 'offset' is passed.
+
+set optimizer_trace_offset=2,optimizer_trace_limit=1;
+call p1();
+# Even though the routine's execution started before
+# 'offset', it detected the security context changes. So the
+# trace of CALL gets the "missing privilege" mark but we don't
+# see it as CALL was before 'offset'.
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+
+# Finally, verify that if the routine's definer does modify
+# @@optimizer_trace from "enabled=off" to "enabled=on", in the
+# body of the routine, then tracing works. This is no security
+# issue, as it was done by the routine's definer.
+
+select user();
+user()
+root@localhost
+create procedure p2() sql security definer
+begin
+declare b int;
+set optimizer_trace="enabled=on";
+select 22 into b from dual;
+end|
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=off";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p2();
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+select 22 into b from dual	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select 22 AS `22`"
+          }
+        ]
+      }
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    }
+  ]
+}	0
+# Variable is as set by the routine
+select @@optimizer_trace;
+@@optimizer_trace
+enabled=on,end_marker=off,one_line=off
+
+# ==========================================================
+# Part B.
+# Do same tests but with SQL SECURITY INVOKER objects, to verify that
+# the restriction on security context changes is not present.
+# ==========================================================
+
+select user();
+user()
+root@localhost
+alter procedure p1 sql security invoker;
+alter function f1 sql security invoker;
+alter sql security invoker view v1 as select * from t1;
+# Triggers cannot be SQL SECURITY INVOKER so we don't test
+# them here.
+alter procedure p2 sql security invoker;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+select 22 into b from dual	407	0
+select a into b from t1 limit 1	1047	0
+insert into t1 values(current_user())	20	0
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	214	0
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+select a into b from t1 limit 1	2010	0
+insert into t1 values(current_user())	20	0
+freturn 3 36	20	0
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select * from v1	402	0
+select * from v1	2004	0
+set optimizer_trace_offset=2,optimizer_trace_limit=1;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+set optimizer_trace="enabled=off";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p2();
+# SELECT substatement is traced (no security context change)
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+select 22 into b from dual	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select 22 AS `22`"
+          }
+        ]
+      }
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    }
+  ]
+}	0
+select @@optimizer_trace;
+@@optimizer_trace
+enabled=on,end_marker=off,one_line=off
+
+# ==========================================================
+# Part C.
+# User1 got traces. Determine the minimum set of privileges he
+# needed for that.
+# ==========================================================
+
+drop procedure p2;
+select user();
+user()
+root@localhost
+revoke all privileges, grant option from user1@localhost;
+# Grant minimum privileges to use the routines and views,
+# without considering optimizer trace:
+grant execute on procedure p1 to user1@localhost;
+grant execute on function f1 to user1@localhost;
+grant select (a) on v1 to user1@localhost;
+# Objects above are SQL SECURITY INVOKER, so invoker needs
+# privileges on objects used internally:
+grant select (a) on t1 to user1@localhost;
+grant insert (a) on t1 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=on";
+show grants;
+Grants for user1@localhost
+GRANT USAGE ON *.* TO 'user1'@'localhost'
+GRANT SELECT (a), INSERT (a) ON `somedb`.`t1` TO 'user1'@'localhost'
+GRANT SELECT (a) ON `somedb`.`v1` TO 'user1'@'localhost'
+GRANT EXECUTE ON PROCEDURE `somedb`.`p1` TO 'user1'@'localhost'
+GRANT EXECUTE ON FUNCTION `somedb`.`f1` TO 'user1'@'localhost'
+
+# Those privileges are not enough to see traces:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# In CALL we execute stored procedure and notice that body should
+# not be exposed. The trace of this CALL would not expose the
+# body. Trace of substatements would. But, due to
+# implementation, CALL is hidden.
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+# SELECT is hidden (same reason as for CALL).
+# Ps-protocol-specific note: preparation of SELECT above does not
+# execute f1, so does not risk exposing body, so its trace is
+# visible.
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+select f1()	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `f1`() AS `f1()`"
+          }
+        ]
+      }
+    }
+  ]
+}	0
+		1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it would expose body of view
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+
+# C.0) Add more privileges:
+
+select user();
+user()
+root@localhost
+# - for use of t1 in routines and view:
+grant select on t1 to user1@localhost;
+# - for use of routines:
+grant select on mysql.proc to user1@localhost;
+# - for use of view:
+grant select, show view on v1 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+select 22 into b from dual	407	0
+select a into b from t1 limit 1	1038	0
+insert into t1 values(current_user())	20	0
+# Trace exposed body of routine, and content of t1, which we
+# could see anyway:
+show create procedure p1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+p1		CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
+    SQL SECURITY INVOKER
+begin
+declare b int;
+if (select count(*) from t1)
+then
+select 22 into b from dual;
+end if;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+select * from t1 limit 1;
+a
+first
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	214	0
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+select a into b from t1 limit 1	2010	0
+insert into t1 values(current_user())	20	0
+freturn 3 36	20	0
+# Trace exposed body of routine, and content of t1, which we
+# could see anyway:
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
+    SQL SECURITY INVOKER
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select * from v1	402	0
+select * from v1	2004	0
+# Trace exposed body of view, and content of t1, which we
+# could see anyway:
+show create view v1;
+View	Create View	character_set_client	collation_connection
+v1	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY INVOKER VIEW `v1` AS select `t1`.`a` AS `a` from `t1`	latin1	latin1_swedish_ci
+
+# Now remove each privilege to verify that it was needed:
+# C.1) remove table-level SELECT privilege on t1
+
+select user();
+user()
+root@localhost
+revoke select on t1 from user1@localhost;
+grant select (a) on t1 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# Cannot see those substatements which use t1
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+call p1()	{
+  "steps": [
+  ]
+}	0
+set b@0 NULL	{
+  "steps": [
+  ]
+}	0
+		1
+select 22 into b from dual	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select 22 AS `22`"
+          }
+        ]
+      }
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    },
+    {
+      "join_execution": {
+        "select#": 1,
+        "steps": [
+        ]
+      }
+    }
+  ]
+}	0
+		1
+		1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+# Cannot see those substatements which use t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	214	0
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+	0	1
+	0	1
+freturn 3 36	20	0
+# Trace exposed body of routine, which we could see anyway:
+set optimizer_trace="enabled=off";
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
+    SQL SECURITY INVOKER
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace="enabled=on";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it might expose some data from columns
+# of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+
+# C.2) remove table-level SELECT privilege on mysql.proc
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in C.1
+grant select on t1 to user1@localhost;
+# And remove a next one:
+revoke select on mysql.proc from user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+# We have no right to see routines' bodies:
+set optimizer_trace="enabled=off";
+show create procedure p1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+p1		NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
+# Verify that optimizer trace does not influence the privilege
+# checking in SHOW CREATE:
+set optimizer_trace="enabled=on";
+show create procedure p1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+p1		NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# Cannot see anything as it would expose body of routine
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+select f1()	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `f1`() AS `f1()`"
+          }
+        ]
+      }
+    }
+  ]
+}	0
+		1
+
+# C.3) remove table-level SELECT privilege on view
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in C.2
+grant select on mysql.proc to user1@localhost;
+# And remove a next one:
+revoke select on v1 from user1@localhost;
+grant select (a) on v1 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+# Cannot see anything as it might expose some data from columns
+# of v1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+
+# C.4) remove SHOW VIEW privilege on view
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in C.3
+grant select on v1 to user1@localhost;
+# And remove a next one:
+revoke show view on v1 from user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=off";
+# We have no right to see view's body:
+show create view v1;
+ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
+set optimizer_trace="enabled=on";
+# Verify that optimizer trace does not influence the privilege
+# checking in SHOW CREATE:
+show create view v1;
+ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+# Cannot see anything as it would expose body of view
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+		1
+		1
+
+# ==========================================================
+# Part D.
+# Like Part C, but instead of SQL SECURITY INVOKER objects
+# created by root and used by User1, let's have SQL SECURITY
+# DEFINER objects created and used by User1. Determine the
+# minimum set of privileges he needs for that.
+# ==========================================================
+
+select user();
+user()
+root@localhost
+drop procedure p1;
+drop function f1;
+drop view v1;
+drop trigger trg2;
+revoke all privileges, grant option from user1@localhost;
+# Grant minimum privileges to create and use objects,
+# without considering optimizer trace:
+grant create routine on somedb.* to user1@localhost;
+grant trigger on t2 to user1@localhost;
+grant create view on somedb.* to user1@localhost;
+grant select (a) on t1 to user1@localhost;
+grant insert (a) on t1 to user1@localhost;
+grant insert (a) on t2 to user1@localhost;
+grant select (a) on t3 to user1@localhost;
+grant insert (a) on t3 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace="enabled=on";
+create procedure p1() sql security definer
+begin
+declare b int;
+if (select count(*) from t1)
+then
+select 22 into b from dual;
+end if;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+end|
+create function f1() returns int sql security definer
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end|
+create trigger trg2 before insert on t2 for each row 
+begin
+insert into t3 select * from t3;
+end|
+create sql security definer view v1 as select * from t1;
+# Creating a view is not enough to be able to SELECT it...
+select user();
+user()
+root@localhost
+grant select (a) on v1 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+# Those privileges are not enough to see traces:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+# Can see body of routine (as definer), but not statements using t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+	0	1
+select 22 into b from dual	407	0
+	0	1
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+# Can see body of routine (as definer), but not statements using t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	214	0
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+	0	1
+	0	1
+freturn 3 36	20	0
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		CREATE DEFINER=`user1`@`localhost` FUNCTION `f1`() RETURNS int(11)
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it might expose some data from columns
+# of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+# Cannot see anything as it might expose some data from
+# columns of t2
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+# Also test a query accessing t1 in FROM clause:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select a from (select a from t1 where a like "f%") as tt where a like "fi%";
+a
+first
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+
+# D.0) Add more privileges:
+
+select user();
+user()
+root@localhost
+# - for use of t1 in routines and view:
+grant select on t1 to user1@localhost;
+# - for use of view:
+grant select, show view on v1 to user1@localhost;
+# - for use of trigger
+grant select on t2 to user1@localhost;
+grant select on t3 to user1@localhost;
+delete from t1 where a<>"first";
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+call p1();
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+call p1()	20	0
+set b@0 NULL	20	0
+jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	715	0
+select 22 into b from dual	407	0
+select a into b from t1 limit 1	1038	0
+insert into t1 values(current_user())	20	0
+# Trace exposed body of routine, and content of t1, which we
+# could see anyway:
+show create procedure p1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+p1		CREATE DEFINER=`user1`@`localhost` PROCEDURE `p1`()
+begin
+declare b int;
+if (select count(*) from t1)
+then
+select 22 into b from dual;
+end if;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+select * from t1 limit 1;
+a
+first
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select f1();
+f1()
+36
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select f1()	214	0
+select f1()	413	0
+set b@0 NULL	20	0
+select 48 into b from dual	407	0
+select a into b from t1 limit 1	2010	0
+insert into t1 values(current_user())	20	0
+freturn 3 36	20	0
+# Trace exposed body of routine, and content of t1, which we
+# could see anyway:
+show create function f1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+f1		CREATE DEFINER=`user1`@`localhost` FUNCTION `f1`() RETURNS int(11)
+begin
+declare b int;
+select 48 into b from dual;
+select a into b from t1 limit 1;
+insert into t1 values(current_user());
+return 36;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select * from v1	402	0
+select * from v1	2004	0
+# Trace exposed body of view, and content of t1, which we
+# could see anyway:
+show create view v1;
+View	Create View	character_set_client	collation_connection
+v1	CREATE ALGORITHM=UNDEFINED DEFINER=`user1`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1`	latin1	latin1_swedish_ci
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+insert into t2 values(current_user())	20	0
+insert into t2 values(current_user())	20	0
+insert into t3 select * from t3	2020	0
+# Trace exposed body of trigger, and content of t2/t3, which we
+# could see anyway:
+show create trigger trg2;
+Trigger	sql_mode	SQL Original Statement	character_set_client	collation_connection	Database Collation
+trg2		CREATE DEFINER=`user1`@`localhost` trigger trg2 before insert on t2 for each row 
+begin
+insert into t3 select * from t3;
+end	latin1	latin1_swedish_ci	latin1_swedish_ci
+select * from t2, t3 limit 1;
+a	a
+first	first
+# Trace exposed content of t1 which we could see anyway:
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select a from (select a from t1 where a like "f%") as tt where a like "fi%";
+a
+first
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+select a from (select a from t1 where a like "f%") as tt where a like "fi%"	613	0
+select a from (select a from t1 where a like "f%") as tt where a like "fi%"	4838	0
+
+# For routines, as they only use t1 and we added only one
+# privilege on t1, we have nothing to remove.
+
+# Now remove each privilege to verify that it was needed for
+# the view.
+# D.1) remove table-level SELECT privilege on v1
+
+select user();
+user()
+root@localhost
+revoke select on v1 from user1@localhost;
+grant select (a) on v1 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it might expose some data from columns
+# of v1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+
+# D.2) remove table-level SHOW VIEW privilege on v1
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in D.1
+grant select on v1 to user1@localhost;
+# And remove a next one:
+revoke show view on v1 from user1@localhost;
+
+select user();
+user()
+user1@localhost
+# We have no right to see view's body:
+show create view v1;
+ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it would expose body of view
+select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	INSUFFICIENT_PRIVILEGES
+		1
+		1
+
+# D.3) remove table-level SELECT privilege on t1
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in D.2
+grant show view on v1 to user1@localhost;
+# And remove a next one:
+revoke select on t1 from user1@localhost;
+grant select (a) on t1 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1;
+a
+first
+user1@localhost
+user1@localhost
+# Cannot see anything as it might expose some data from columns
+# of t1
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+
+# Now remove each privilege to verify that it was needed for
+# the trigger:
+# D.4) remove table-level SELECT privilege on t2
+
+select user();
+user()
+root@localhost
+revoke select on t2 from user1@localhost;
+grant select (a) on t2 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+# Cannot see anything as it might expose some data from
+# columns of t2
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+	0	1
+	0	1
+
+# D.5) remove table-level SELECT privilege on t3
+
+
+select user();
+user()
+root@localhost
+# Put back privilege removed in D.4
+grant select on t2 to user1@localhost;
+# And remove a next one:
+revoke select on t3 from user1@localhost;
+grant select (a) on t3 to user1@localhost;
+
+select user();
+user()
+user1@localhost
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+insert into t2 values(current_user());
+# Cannot see substatement as it might expose some data from
+# columns of t3
+select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
+QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
+insert into t2 values(current_user())	20	0
+insert into t2 values(current_user())	20	0
+	0	1
+
+# Cleanup
+select user();
+user()
+root@localhost
+drop user user1@localhost;
+
+# ==========================================================
+# Part E.
+# Misc tests.
+# ==========================================================
+
+select user();
+user()
+root@localhost
+drop view v1;
+create sql security definer view v1 as select * from t1 where 'secret';
+create user user1@localhost identified by '';
+grant create, insert, select on somedb.* to user1@localhost;
+grant create routine on somedb.* to user1@localhost;
+
+select user();
+user()
+user1@localhost
+user1 cannot see view's body:
+show create view v1;
+ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
+user1 creates a procedure
+create procedure proc() sql security definer
+begin
+set optimizer_trace="enabled=on";
+set optimizer_trace_offset=0,optimizer_trace_limit=100;
+select * from v1 limit 0;
+create table leak select * from information_schema.optimizer_trace;
+set optimizer_trace="enabled=off";
+end|
+select user();
+user()
+root@localhost
+root runs procedure, without fear of risk as it is SQL SECURITY DEFINER
+call proc();
+a
+
+select user();
+user()
+user1@localhost
+user1 cannot see view's body:
+select * from leak;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+
+# Cleanup
+select user();
+user()
+root@localhost
+drop database somedb;
+drop user user1@localhost;
+set @@global.optimizer_trace_max_mem_size = @old_size;

=== modified file 'mysql-test/r/optimizer_trace_subquery_no_prot.result'
--- a/mysql-test/r/optimizer_trace_subquery_no_prot.result	2011-03-01 10:55:27 +0000
+++ b/mysql-test/r/optimizer_trace_subquery_no_prot.result	2011-05-04 20:53:28 +0000
@@ -12,7 +12,7 @@ NULL	1
 2	2
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT (SELECT a FROM t1 WHERE t1.a=t2.a), a FROM t2	{
   "steps": [
     {
@@ -171,7 +171,7 @@ SELECT (SELECT a FROM t1 WHERE t1.a=t2.a
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 # Subselect execute is traced only the first time it is executed
 SET @@optimizer_trace_features="greedy_search=off,repeated_subselect=off";
@@ -181,7 +181,7 @@ NULL	1
 2	2
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT (SELECT a FROM t1 WHERE t1.a=t2.a), a FROM t2	{
   "steps": [
     {
@@ -326,7 +326,7 @@ SELECT (SELECT a FROM t1 WHERE t1.a=t2.a
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 DROP TABLE t1,t2;
 SET @@optimizer_trace_features="default";
@@ -339,7 +339,7 @@ t1.a= (SELECT a FROM t2 LIMIT 1) ;
 a
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT t1.a
 FROM t1
 WHERE t1.a= (SELECT b FROM t2 LIMIT 1) AND NOT
@@ -461,7 +461,7 @@ t1.a= (SELECT a FROM t2 LIMIT 1)	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 SELECT 1 FROM DUAL
 WHERE NOT EXISTS
@@ -470,7 +470,7 @@ WHERE NOT EXISTS
 1
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT 1 FROM DUAL
 WHERE NOT EXISTS
 (SELECT * FROM t2 WHERE a = 50 AND b = 3)	{
@@ -595,6 +595,6 @@ WHERE NOT EXISTS
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 DROP TABLE t1,t2;

=== modified file 'mysql-test/r/optimizer_trace_subquery_ps_prot.result'
--- a/mysql-test/r/optimizer_trace_subquery_ps_prot.result	2011-03-01 10:55:27 +0000
+++ b/mysql-test/r/optimizer_trace_subquery_ps_prot.result	2011-05-04 20:53:28 +0000
@@ -12,7 +12,7 @@ NULL	1
 2	2
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT (SELECT a FROM t1 WHERE t1.a=t2.a), a FROM t2	{
   "steps": [
     {
@@ -171,7 +171,7 @@ SELECT (SELECT a FROM t1 WHERE t1.a=t2.a
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 # Subselect execute is traced only the first time it is executed
 SET @@optimizer_trace_features="greedy_search=off,repeated_subselect=off";
@@ -181,7 +181,7 @@ NULL	1
 2	2
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT (SELECT a FROM t1 WHERE t1.a=t2.a), a FROM t2	{
   "steps": [
     {
@@ -326,7 +326,7 @@ SELECT (SELECT a FROM t1 WHERE t1.a=t2.a
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 DROP TABLE t1,t2;
 SET @@optimizer_trace_features="default";
@@ -339,7 +339,7 @@ t1.a= (SELECT a FROM t2 LIMIT 1) ;
 a
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT t1.a
 FROM t1
 WHERE t1.a= (SELECT b FROM t2 LIMIT 1) AND NOT
@@ -461,7 +461,7 @@ t1.a= (SELECT a FROM t2 LIMIT 1)	{
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 SELECT 1 FROM DUAL
 WHERE NOT EXISTS
@@ -470,7 +470,7 @@ WHERE NOT EXISTS
 1
 
 SELECT * FROM information_schema.OPTIMIZER_TRACE;
-QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
 SELECT 1 FROM DUAL
 WHERE NOT EXISTS
 (SELECT * FROM t2 WHERE a = 50 AND b = 3)	{
@@ -595,6 +595,6 @@ WHERE NOT EXISTS
       } /* join_execution */
     }
   ] /* steps */
-}	0
+}	0	0
 
 DROP TABLE t1,t2;

=== modified file 'mysql-test/suite/funcs_1/r/is_columns_is.result'
--- a/mysql-test/suite/funcs_1/r/is_columns_is.result	2011-03-21 17:55:41 +0000
+++ b/mysql-test/suite/funcs_1/r/is_columns_is.result	2011-05-04 20:53:28 +0000
@@ -125,6 +125,7 @@ def	information_schema	KEY_COLUMN_USAGE	
 def	information_schema	KEY_COLUMN_USAGE	TABLE_CATALOG	4		NO	varchar	512	1536	NULL	NULL	utf8	utf8_general_ci	varchar(512)			select	
 def	information_schema	KEY_COLUMN_USAGE	TABLE_NAME	6		NO	varchar	64	192	NULL	NULL	utf8	utf8_general_ci	varchar(64)			select	
 def	information_schema	KEY_COLUMN_USAGE	TABLE_SCHEMA	5		NO	varchar	64	192	NULL	NULL	utf8	utf8_general_ci	varchar(64)			select	
+def	information_schema	OPTIMIZER_TRACE	INSUFFICIENT_PRIVILEGES	4	0	NO	tinyint	NULL	NULL	3	0	NULL	NULL	tinyint(1)			select	
 def	information_schema	OPTIMIZER_TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	3	0	NO	int	NULL	NULL	10	0	NULL	NULL	int(20)			select	
 def	information_schema	OPTIMIZER_TRACE	QUERY	1	NULL	NO	longtext	4294967295	4294967295	NULL	NULL	utf8	utf8_general_ci	longtext			select	
 def	information_schema	OPTIMIZER_TRACE	TRACE	2	NULL	NO	longtext	4294967295	4294967295	NULL	NULL	utf8	utf8_general_ci	longtext			select	
@@ -377,6 +378,7 @@ COL_CML	DATA_TYPE	CHARACTER_SET_NAME	COL
 NULL	bigint	NULL	NULL
 NULL	datetime	NULL	NULL
 NULL	int	NULL	NULL
+NULL	tinyint	NULL	NULL
 --> CHAR(0) is allowed (see manual), and here both CHARACHTER_* values
 --> are 0, which is intended behavior, and the result of 0 / 0 IS NULL
 SELECT CHARACTER_OCTET_LENGTH / CHARACTER_MAXIMUM_LENGTH AS COL_CML,
@@ -519,6 +521,7 @@ NULL	information_schema	KEY_COLUMN_USAGE
 1.0000	information_schema	OPTIMIZER_TRACE	QUERY	longtext	4294967295	4294967295	utf8	utf8_general_ci	longtext
 1.0000	information_schema	OPTIMIZER_TRACE	TRACE	longtext	4294967295	4294967295	utf8	utf8_general_ci	longtext
 NULL	information_schema	OPTIMIZER_TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	int	NULL	NULL	NULL	NULL	int(20)
+NULL	information_schema	OPTIMIZER_TRACE	INSUFFICIENT_PRIVILEGES	tinyint	NULL	NULL	NULL	NULL	tinyint(1)
 3.0000	information_schema	PARAMETERS	SPECIFIC_CATALOG	varchar	512	1536	utf8	utf8_general_ci	varchar(512)
 3.0000	information_schema	PARAMETERS	SPECIFIC_SCHEMA	varchar	64	192	utf8	utf8_general_ci	varchar(64)
 3.0000	information_schema	PARAMETERS	SPECIFIC_NAME	varchar	64	192	utf8	utf8_general_ci	varchar(64)

=== removed file 'mysql-test/t/optimizer_trace_bugs.test'
--- a/mysql-test/t/optimizer_trace_bugs.test	2010-09-18 16:25:43 +0000
+++ b/mysql-test/t/optimizer_trace_bugs.test	1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
-# unfixed bugs. Should be deleted before push.
-
---source include/have_optimizer_trace.inc
-
-# no test here so far
-select 1;

=== modified file 'mysql-test/t/optimizer_trace_debug.test'
--- a/mysql-test/t/optimizer_trace_debug.test	2011-03-08 07:18:14 +0000
+++ b/mysql-test/t/optimizer_trace_debug.test	2011-05-04 20:53:28 +0000
@@ -5,15 +5,12 @@
 --source include/have_debug.inc
 
 --echo # We want to make sure that the common case of
---echo # a connection which has not enabled tracing, or is
---echo # running SQL commands which are excluded from
---echo # tracing, or commands which use I_S.OPTIMIZER_TRACE,
---echo # outside of any tricky stored-routine scenarios tested
---echo # in optimizer_trace2.test, are optimized, i.e. no trace is
---echo # created, even a dummy internal one invisible in I_S.
+--echo # a connection which has not enabled tracing,
+--echo # is optimized, i.e. neither Opt_trace_context_impl nor
+--echo # Opt_trace_context_stmt is created.
 
-# make server crash if it creates a trace, even a dummy internal one
-set debug="d,opt_trace_should_not_start";
+# make server crash if it creates Opt_trace_stmt
+set debug="d,no_new_opt_trace_stmt";
 
 # tracable but tracing is off
 select 1;
@@ -22,18 +19,15 @@ select * from information_schema.OPTIMIZ
 # non-tracable
 set @a=25;
 
-set optimizer_trace="enabled=on";
-select * from information_schema.OPTIMIZER_TRACE;
+# Force creation of Opt_trace_context_impl and Opt_trace_stmt
 set debug="default";
+set optimizer_trace="enabled=on";
 select 2;
-set debug="d,opt_trace_should_not_start";
 select * from information_schema.OPTIMIZER_TRACE;
-set @a=25;
 set optimizer_trace="enabled=off";
+set debug="d,no_new_opt_trace_stmt";
 
 # tracable, but tracing is off again (thd->opt_trace->is_started() is false)
 select 3;
 # should see only trace of "select 2"
 select * from information_schema.OPTIMIZER_TRACE;
-
-set optimizer_trace=default;

=== added file 'mysql-test/t/optimizer_trace_security_no_prot.test'
--- a/mysql-test/t/optimizer_trace_security_no_prot.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/optimizer_trace_security_no_prot.test	2011-05-04 20:53:28 +0000
@@ -0,0 +1,8 @@
+if (`SELECT $PS_PROTOCOL + $SP_PROTOCOL + $CURSOR_PROTOCOL
+            + $VIEW_PROTOCOL > 0`)
+{
+   --skip Need normal protocol
+}
+
+# The main testing script
+--source include/optimizer_trace_security.inc

=== added file 'mysql-test/t/optimizer_trace_security_ps_prot.test'
--- a/mysql-test/t/optimizer_trace_security_ps_prot.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/optimizer_trace_security_ps_prot.test	2011-05-04 20:53:28 +0000
@@ -0,0 +1,8 @@
+if (`SELECT $SP_PROTOCOL + $CURSOR_PROTOCOL + $VIEW_PROTOCOL > 0
+        OR $PS_PROTOCOL = 0`)
+{
+   --skip Need ps-protocol
+}
+
+# The main testing script
+--source include/optimizer_trace_security.inc

=== modified file 'sql/item_subselect.cc'
--- a/sql/item_subselect.cc	2011-04-05 07:34:13 +0000
+++ b/sql/item_subselect.cc	2011-05-04 20:53:28 +0000
@@ -307,11 +307,10 @@ bool Item_subselect::exec()
     2) REPEATED_SUBSELECT is disabled
   */
 #ifdef OPTIMIZER_TRACE  
-  Opt_trace_context * const trace= thd->opt_trace;
-  const bool repeated_trace_enabled= trace ? 
-    trace->feature_enabled(Opt_trace_context::REPEATED_SUBSELECT) :
-    false;
-  const bool disable_trace= (traced_before && !repeated_trace_enabled);
+  Opt_trace_context * const trace= &thd->opt_trace;
+  const bool disable_trace=
+    traced_before &&
+    !trace->feature_enabled(Opt_trace_context::REPEATED_SUBSELECT);
   Opt_trace_disable_I_S disable_trace_wrapper(trace, disable_trace);
   traced_before= true;
 
@@ -1144,7 +1143,7 @@ Item_in_subselect::single_value_transfor
 	!(select_lex->next_select()) &&
         select_lex->table_list.elements)
     {
-      OPT_TRACE_TRANSFORM(thd->opt_trace, oto0, oto1,
+      OPT_TRACE_TRANSFORM(&thd->opt_trace, oto0, oto1,
                           select_lex->select_number,
                           "> ALL/ANY (SELECT)", "SELECT(MIN)");
       oto1.add("chosen", true);
@@ -1193,7 +1192,7 @@ Item_in_subselect::single_value_transfor
     }
     else
     {
-      OPT_TRACE_TRANSFORM(thd->opt_trace, oto0, oto1,
+      OPT_TRACE_TRANSFORM(&thd->opt_trace, oto0, oto1,
                           select_lex->select_number,
                           "> ALL/ANY (SELECT)", "MIN (SELECT)");
       oto1.add("chosen", true);
@@ -1300,7 +1299,7 @@ Item_in_subselect::single_value_in_to_ex
   SELECT_LEX *select_lex= join->select_lex;
   DBUG_ENTER("Item_in_subselect::single_value_in_to_exists_transformer");
 
-  OPT_TRACE_TRANSFORM(thd->opt_trace, oto0, oto1, select_lex->select_number,
+  OPT_TRACE_TRANSFORM(&thd->opt_trace, oto0, oto1, select_lex->select_number,
                       "IN (SELECT)", "EXISTS (CORRELATED SELECT)");
   oto1.add("chosen", true);
 
@@ -1580,7 +1579,7 @@ Item_in_subselect::row_value_in_to_exist
                         !select_lex->table_list.elements);
 
   DBUG_ENTER("Item_in_subselect::row_value_in_to_exists_transformer");
-  OPT_TRACE_TRANSFORM(thd->opt_trace, oto0, oto1, select_lex->select_number,
+  OPT_TRACE_TRANSFORM(&thd->opt_trace, oto0, oto1, select_lex->select_number,
                       "IN (SELECT)", "EXISTS (CORRELATED SELECT)");
   oto1.add("chosen", true);
 
@@ -1972,7 +1971,7 @@ bool Item_in_subselect::setup_engine()
   subselect_single_select_engine *old_engine_derived=
     static_cast<subselect_single_select_engine*>(old_engine);
 
-  OPT_TRACE_TRANSFORM(thd->opt_trace, oto0, oto1,
+  OPT_TRACE_TRANSFORM(&thd->opt_trace, oto0, oto1,
                       old_engine_derived->join->select_lex->select_number,
                       "IN (SELECT)", "materialization");
   oto1.add("chosen", true);

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2011-04-12 15:33:01 +0000
+++ b/sql/mysqld.cc	2011-05-04 20:53:28 +0000
@@ -2487,10 +2487,10 @@ the thread stack. Please read http://dev
     fprintf(stderr, "Connection ID (thread ID): %lu\n", (ulong) thd->thread_id);
     fprintf(stderr, "Status: %s\n", kreason);
 #ifdef OPTIMIZER_TRACE
-    if ((thd->opt_trace != NULL) && thd->opt_trace->is_started())
+    if (thd->opt_trace.is_started())
     {
       const size_t max_print_len= 4096; // print those final bytes
-      const char *tail= thd->opt_trace->get_tail(max_print_len);
+      const char *tail= thd->opt_trace.get_tail(max_print_len);
       fprintf(stderr, "Tail of Optimizer trace (%p): ", tail);
       my_safe_print_str(tail, max_print_len);
     }

=== modified file 'sql/opt_range.cc'
--- a/sql/opt_range.cc	2011-04-14 11:49:41 +0000
+++ b/sql/opt_range.cc	2011-05-04 20:53:28 +0000
@@ -2082,7 +2082,7 @@ void TRP_RANGE::trace_basic_info(const P
   trace_object->add_alnum("type", "range_scan").
     add_utf8("index", cur_key.name).add("records", records);
 
-  Opt_trace_array trace_range(param->thd->opt_trace, "ranges");
+  Opt_trace_array trace_range(&param->thd->opt_trace, "ranges");
 
   // TRP_RANGE should not be created if there are no range intervals
   DBUG_ASSERT(key);
@@ -2149,8 +2149,8 @@ void TRP_ROR_INTERSECT::trace_basic_info
     add("covering", is_covering).
     add("clustered_pk_scan", cpk_scan != NULL);
 
-  Opt_trace_context *trace_ctx= param->thd->opt_trace;
-  Opt_trace_array ota(trace_ctx, "intersect_of");
+  Opt_trace_context * const trace= &param->thd->opt_trace;
+  Opt_trace_array ota(trace, "intersect_of");
   for (st_ror_scan_info **cur_scan= first_scan;
        cur_scan != last_scan;
        cur_scan++)
@@ -2158,11 +2158,11 @@ void TRP_ROR_INTERSECT::trace_basic_info
     const KEY &cur_key= param->table->key_info[(*cur_scan)->keynr];
     const KEY_PART_INFO *key_part= cur_key.key_part;
 
-    Opt_trace_object trace_isect_idx(trace_ctx);
+    Opt_trace_object trace_isect_idx(trace);
     trace_isect_idx.add_alnum("type", "range_scan").
       add_utf8("index", cur_key.name).add("records", (*cur_scan)->records);
 
-    Opt_trace_array trace_range(trace_ctx, "ranges");
+    Opt_trace_array trace_range(trace, "ranges");
     for (const SEL_ARG *current= (*cur_scan)->sel_arg;
          current;
          current= current->next)
@@ -2208,13 +2208,14 @@ void TRP_ROR_UNION::trace_basic_info(con
                                      Opt_trace_object *trace_object) const
 {
 #ifdef OPTIMIZER_TRACE
+  Opt_trace_context * const trace= &param->thd->opt_trace;
   trace_object->add_alnum("type", "index_roworder_union");
-  Opt_trace_array ota(param->thd->opt_trace, "union_of");
+  Opt_trace_array ota(trace, "union_of");
   for (TABLE_READ_PLAN **current= first_ror;
        current != last_ror;
        current++)
   {
-    Opt_trace_object trp_info(param->thd->opt_trace);
+    Opt_trace_object trp_info(trace);
     (*current)->trace_basic_info(param, &trp_info);
   }
 #endif
@@ -2244,13 +2245,14 @@ void TRP_INDEX_MERGE::trace_basic_info(c
                                        Opt_trace_object *trace_object) const
 {
 #ifdef OPTIMIZER_TRACE
+  Opt_trace_context * const trace= &param->thd->opt_trace;
   trace_object->add_alnum("type", "index_merge");
-  Opt_trace_array ota(param->thd->opt_trace, "index_merge_of");
+  Opt_trace_array ota(trace, "index_merge_of");
   for (TRP_RANGE **current= range_scans;
        current != range_scans_end;
        current++)
   {
-    Opt_trace_object trp_info(param->thd->opt_trace);
+    Opt_trace_object trp_info(trace);
     (*current)->trace_basic_info(param, &trp_info);
   }
 #endif
@@ -2341,16 +2343,16 @@ void TRP_GROUP_MIN_MAX::trace_basic_info
     add("cost", read_cost);
 
   const KEY_PART_INFO *key_part= index_info->key_part;
+  Opt_trace_context * const trace= &param->thd->opt_trace;
   {
-    Opt_trace_array trace_keyparts(param->thd->opt_trace,
-                                   "key_parts_used_for_access");
+    Opt_trace_array trace_keyparts(trace, "key_parts_used_for_access");
     for (uint partno= 0; partno < used_key_parts; partno++)
     {
       const KEY_PART_INFO *cur_key_part= key_part + partno;
       trace_keyparts.add_utf8(cur_key_part->field->field_name);
     }
   }
-  Opt_trace_array trace_range(param->thd->opt_trace, "ranges");
+  Opt_trace_array trace_range(trace, "ranges");
 
   // can have group quick without ranges
   if (index_tree)
@@ -2500,7 +2502,7 @@ int SQL_SELECT::test_quick_select(THD *t
   else if (read_time <= 2.0 && !force_quick_range)
     DBUG_RETURN(0);				/* No need for quick select */
 
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   Opt_trace_object trace_range(trace, "range_analysis");
   Opt_trace_object(trace, "table_scan").
     add("records", head->file->stats.records).
@@ -4149,7 +4151,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick
   DBUG_ENTER("get_best_disjunct_quick");
   DBUG_PRINT("info", ("Full table scan cost: %g", read_time));
 
-  Opt_trace_context * const trace= param->thd->opt_trace;
+  Opt_trace_context * const trace= &param->thd->opt_trace;
   Opt_trace_object trace_best_disjunct(trace);
   if (!(range_scans= (TRP_RANGE**)alloc_root(param->mem_root,
                                              sizeof(TRP_RANGE*)*
@@ -4947,10 +4949,10 @@ TRP_ROR_INTERSECT *get_best_ror_intersec
 {
   uint idx;
   double min_cost= DBL_MAX;
+  Opt_trace_context * const trace= &param->thd->opt_trace;
   DBUG_ENTER("get_best_ror_intersect");
 
-  Opt_trace_object trace_ror(param->thd->opt_trace, 
-                             "analyzing_roworder_intersect");
+  Opt_trace_object trace_ror(trace, "analyzing_roworder_intersect");
 
   if ((tree->n_ror_scans < 2) || !param->table->file->stats.records ||
       !param->thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT))
@@ -5032,11 +5034,10 @@ TRP_ROR_INTERSECT *get_best_ror_intersec
     Note: trace_isect_idx.end() is called to close this object after
     this while-loop.
   */
-  Opt_trace_array trace_isect_idx(param->thd->opt_trace,
-                                  "intersecting_indices");
+  Opt_trace_array trace_isect_idx(trace, "intersecting_indices");
   while (cur_ror_scan != tree->ror_scans_end && !intersect->is_covering)
   {
-    Opt_trace_object trace_idx(param->thd->opt_trace);
+    Opt_trace_object trace_idx(trace);
     char *idx_name= param->table->key_info[(*cur_ror_scan)->keynr].name;
     trace_idx.add_utf8("index", idx_name);
     /* S= S + first(R);  R= R - first(R); */
@@ -5089,7 +5090,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersec
     covering, it doesn't make sense to add CPK scan.
   */
   { // Scope for trace object
-    Opt_trace_object trace_cpk(param->thd->opt_trace, "clustered_pk");
+    Opt_trace_object trace_cpk(trace, "clustered_pk");
     if (cpk_scan && !intersect->is_covering)
     {
       if (ror_intersect_add(intersect, cpk_scan, TRUE) &&
@@ -5192,7 +5193,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror
   DBUG_ENTER("get_best_covering_ror_intersect");
 
   // None of our tests enter this function
-  Opt_trace_object (param->thd->opt_trace).
+  Opt_trace_object (&param->thd->opt_trace).
     add("get_best_covering_roworder_intersect", true).
     add("untested_code", true).
     add("need_tracing",true);
@@ -5349,6 +5350,7 @@ static TRP_RANGE *get_key_scans_params(P
   DBUG_ENTER("get_key_scans_params");
   LINT_INIT(best_mrr_flags); /* protected by key_to_read */
   LINT_INIT(best_buf_size); /* protected by key_to_read */
+  Opt_trace_context * const trace= &param->thd->opt_trace;
   /*
     Note that there may be trees that have type SEL_TREE::KEY but contain no
     key reads at all, e.g. tree for expression "key1 is not null" where key1
@@ -5356,7 +5358,7 @@ static TRP_RANGE *get_key_scans_params(P
   */
   DBUG_EXECUTE("info", print_sel_tree(param, tree, &tree->keys_map,
                                       "tree scans"););
-  Opt_trace_array ota(param->thd->opt_trace, "range_scan_alternatives");
+  Opt_trace_array ota(trace, "range_scan_alternatives");
 
   tree->ror_scans_map.clear_all();
   tree->n_ror_scans= 0;
@@ -5376,7 +5378,7 @@ static TRP_RANGE *get_key_scans_params(P
       bool read_index_only= index_read_must_be_used ? TRUE :
                             (bool) param->table->covering_keys.is_set(keynr);
 
-      Opt_trace_object trace_idx(param->thd->opt_trace);
+      Opt_trace_object trace_idx(trace);
       trace_idx.add_utf8("index", param->table->key_info[keynr].name);
 
       found_records= check_quick_select(param, idx, read_index_only, *key,
@@ -5386,9 +5388,9 @@ static TRP_RANGE *get_key_scans_params(P
 #ifdef OPTIMIZER_TRACE
       // check_quick_select() says don't use range if it returns HA_POS_ERROR
       if (found_records != HA_POS_ERROR && 
-          param->thd->opt_trace && param->thd->opt_trace->is_started()) 
+          param->thd->opt_trace.is_started())
       { 
-        Opt_trace_array trace_range(param->thd->opt_trace, "ranges");
+        Opt_trace_array trace_range(&param->thd->opt_trace, "ranges");
         
         const KEY &cur_key= param->table->key_info[keynr];
         const KEY_PART_INFO *key_part= cur_key.key_part;
@@ -6605,8 +6607,8 @@ get_mm_leaf(RANGE_OPT_PARAM *param, Item
 end:
   if (impossible_cond_cause)
   {
-    Opt_trace_object wrapper (param->thd->opt_trace);
-    Opt_trace_object (param->thd->opt_trace, "impossible_condition",
+    Opt_trace_object wrapper (&param->thd->opt_trace);
+    Opt_trace_object (&param->thd->opt_trace, "impossible_condition",
                       Opt_trace_context::RANGE_OPTIMIZER).
       add_alnum("cause", impossible_cond_cause);
   }
@@ -10147,10 +10149,11 @@ get_best_group_min_max(PARAM *param, SEL
   ha_rows best_quick_prefix_records= 0;
   uint best_param_idx= 0;
   List_iterator<Item> select_items_it;
+  Opt_trace_context * const trace= &param->thd->opt_trace;
 
   DBUG_ENTER("get_best_group_min_max");
 
-  Opt_trace_object trace_group(thd->opt_trace, "group_index_range",
+  Opt_trace_object trace_group(trace, "group_index_range",
                                Opt_trace_context::RANGE_OPTIMIZER);
   const char* cause= NULL;
 
@@ -10255,12 +10258,11 @@ get_best_group_min_max(PARAM *param, SEL
   SEL_ARG *cur_index_tree= NULL;
   ha_rows cur_quick_prefix_records= 0;
   uint cur_param_idx= MAX_KEY;
-  Opt_trace_array trace_indices(thd->opt_trace,
-                                "potential_group_range_indices");
+  Opt_trace_array trace_indices(trace, "potential_group_range_indices");
   for (uint cur_index= 0 ; cur_index_info != cur_index_info_end ;
        cur_index_info++, cur_index++)
   {
-    Opt_trace_object trace_idx(thd->opt_trace);
+    Opt_trace_object trace_idx(trace);
     trace_idx.add_utf8("index", cur_index_info->name);
     KEY_PART_INFO *cur_part;
     KEY_PART_INFO *end_part; /* Last part for loops. */
@@ -10547,9 +10549,9 @@ get_best_group_min_max(PARAM *param, SEL
 
 #ifdef OPTIMIZER_TRACE
       if (cur_index_tree &&
-          param->thd->opt_trace && param->thd->opt_trace->is_started())
+          param->thd->opt_trace.is_started())
       {
-        Opt_trace_array trace_range(param->thd->opt_trace, "ranges");
+        Opt_trace_array trace_range(&param->thd->opt_trace, "ranges");
 
         const KEY_PART_INFO *key_part= cur_index_info->key_part;
 

=== modified file 'sql/opt_trace.cc'
--- a/sql/opt_trace.cc	2011-04-14 13:02:22 +0000
+++ b/sql/opt_trace.cc	2011-05-04 20:53:28 +0000
@@ -26,6 +26,56 @@
 
 #ifdef OPTIMIZER_TRACE
 
+namespace {
+/**
+  A wrapper of class String, for storing query or trace.
+  Any memory allocation error in this class is reported by my_error(), see
+  OOM_HANDLING in opt_trace.h.
+*/
+class Buffer
+{
+private:
+  size_t allowed_mem_size;   ///< allowed memory size for this String
+  size_t missing_bytes;      ///< how many bytes could not be added
+  String string_buf;
+public:
+  Buffer() : allowed_mem_size(0), missing_bytes(0) {}
+
+  uint32 alloced_length() const { return string_buf.alloced_length(); }
+  uint32 length() const { return string_buf.length(); }
+  void prealloc();    ///< pro-actively extend buffer if soon short of space
+  char *c_ptr_safe() { return string_buf.c_ptr_safe(); }
+  const char *ptr() const { return string_buf.ptr(); }
+
+  const CHARSET_INFO *charset() const { return string_buf.charset(); }
+  void set_charset(const CHARSET_INFO *charset)
+  { string_buf.set_charset(charset); }
+
+  /**
+    Like @c String::append()
+    @param  str     String, in this instance's charset
+    @param  length  length of string
+  */
+  void append(const char *str, size_t length);
+  void append(const char *str) { return append(str, strlen(str)); }
+  /**
+    Like @c append() but escapes certain characters for string values to
+    be JSON-compliant.
+    @param  str     String in UTF8
+    @param  length  length of string
+  */
+  void append_escaped(const char *str, size_t length);
+  void append(char chr);
+
+  size_t get_allowed_mem_size() const { return allowed_mem_size; }
+  size_t get_missing_bytes() const { return missing_bytes; }
+
+  void set_allowed_mem_size(size_t a) { allowed_mem_size= a; }
+};
+
+
+} // namespace
+
 /**
   @class Opt_trace_stmt
 
@@ -38,12 +88,10 @@ class Opt_trace_stmt
 {
 public:
   /**
-     Constructor, starts a trace
+     Constructor, starts a trace for information_schema and dbug.
      @param  ctx_arg          context
-     @param  support_I_S_arg  should trace be in information_schema
   */
-  Opt_trace_stmt(Opt_trace_context *ctx_arg,
-                 enum enum_support_I_S support_I_S_arg);
+  Opt_trace_stmt(Opt_trace_context *ctx_arg);
 
   /**
      Ends a trace; destruction may not be possible immediately as we may have
@@ -151,8 +199,6 @@ public:
   /// @returns 'size' last bytes of the trace buffer
   const char *trace_buffer_tail(size_t size);
 
-  enum enum_support_I_S get_support_I_S() const { return support_I_S; }
-
   /// @returns total memory used by this trace
   size_t alloced_length() const
   { return trace_buffer.alloced_length() + query_buffer.alloced_length(); }
@@ -160,110 +206,44 @@ public:
   void assert_current_struct(const Opt_trace_struct *s) const
   { DBUG_ASSERT(current_struct == s); }
 
-private:
+  void missing_privilege();
 
-  bool ended;           ///< Whether @c end() has been called on this instance
+  bool support_I_S() const { return I_S_disabled == 0; }
 
-  /// Should this trace be in information_schema
-  enum enum_support_I_S support_I_S;
-
-  Opt_trace_context *ctx;                       ///< context
-  Opt_trace_struct *current_struct;             ///< current open structure
-
-  /// Same logic as Opt_trace_context::stack_of_current_stmts.
-  Dynamic_array<Opt_trace_struct *> stack_of_current_structs;
+  /// Temporarily disables I_S output for this statement.
+  void disable_I_S() { ++I_S_disabled; }
 
   /**
-     When we temporarily disable I_S (because of Opt_trace_disable_I_S, or
-     because we are entering a structure belonging to a not-traced optimizer
-     feature), we need to remember the pre-disabling state, to restore it
-     later.
+     Restores I_S support to what it was before the previous call
+     to disable_I_S().
   */
-  Dynamic_array<enum enum_support_I_S> stack_of_values_of_support_I_S;
+  void restore_I_S() { --I_S_disabled; }
 
-  /**
-     Temporarily disables I_S. This is private because only our friend
-     Opt_trace_disable_I_S is trusted enough to use it.
-     @retval false ok
-     @retval true  error (function had no effect, no disabling was done)
-  */
-  bool disable_I_S_for_this_and_children()
-  {
-    if (unlikely(stack_of_values_of_support_I_S.append(support_I_S)))
-    {
-      /*
-        Note that append() above calls my_error() if it fails, so user is
-        informed.
-      */
-      return true;
-    }
-    support_I_S= NO_FOR_THIS_AND_CHILDREN;
-    return false;
-  }
-  /**
-     Restores I_S support to what it was before the previous successful call
-     to disable_I_S_for_this_and_children().
-     @note we said "_successful_": indeed if we failed to append() to the
-     dynamic array before, a pop() now would be wrong: it would pop a wrong
-     cell, or if no cell, would dereference a NULL pointer (@sa pop_dynamic())
-     and crash.
-  */
-  void restore_I_S()
-  {
-    support_I_S= stack_of_values_of_support_I_S.pop();
-  }
-  friend Opt_trace_disable_I_S::Opt_trace_disable_I_S(Opt_trace_context*, bool);
-  friend Opt_trace_disable_I_S::~Opt_trace_disable_I_S();
+private:
+
+  bool ended;           ///< Whether @c end() has been called on this instance
 
   /**
-    A wrapper of class String, for storing query or trace.
-    Any memory allocation error in this class is reported by my_error(), see
-    OOM_HANDLING in opt_trace.h.
+    0 <=> this trace should be in information_schema.
+    In the life of an Opt_trace_stmt, support for I_S may be temporarily
+    disabled.
+    Once disabled, it must stay disabled until re-enabled at the same stack
+    frame. This:
+    Opt_trace_object1 // disables I_S
+       Opt_trace_object2 // re-enables I_S
+    is impossible (the top object wins).
+    So it is sufficient, to keep track of the current state, to have a counter
+    incremented each time we get a request to disable I_S.
   */
-  class Buffer
-  {
-  private:
-    size_t allowed_mem_size;   ///< allowed memory size for this String
-    size_t missing_bytes;      ///< how many bytes could not be added
-    String string_buf;
-  public:
-    Buffer() : allowed_mem_size(0), missing_bytes(0) {}
-
-    uint32 alloced_length() const { return string_buf.alloced_length(); }
-    uint32 length() const { return string_buf.length(); }
-    void prealloc();    ///< pro-actively extend buffer if soon short of space
-    char *c_ptr_safe()
-    {
-      // Alas, String::c_ptr_safe() does no realloc error checking
-      return string_buf.c_ptr_safe();
-    }
-    const char *ptr() const { return string_buf.ptr(); }
+  int I_S_disabled;
 
-    const CHARSET_INFO *charset() const { return string_buf.charset(); }
-    void set_charset(const CHARSET_INFO *charset)
-    { string_buf.set_charset(charset); }
-
-    /**
-       Like @c String::append()
-       @param  str     String, in this instance's charset
-       @param  length  length of string
-    */
-    void append(const char *str, size_t length);
-    void append(const char *str) { return append(str, strlen(str)); }
-    /**
-       Like @c append() but escapes certain characters for string values to
-       be JSON-compliant.
-       @param  str     String in UTF8
-       @param  length  length of string
-    */
-    void append_escaped(const char *str, size_t length);
-    void append(char chr);
+  bool missing_priv; ///< whether user lacks privilege to see this trace
 
-    size_t get_allowed_mem_size() const { return allowed_mem_size; }
-    size_t get_missing_bytes() const { return missing_bytes; }
+  Opt_trace_context *ctx;                       ///< context
+  Opt_trace_struct *current_struct;             ///< current open structure
 
-    void set_allowed_mem_size(size_t a) { allowed_mem_size= a; }
-  }; // end of class Buffer
+  /// Same logic as Opt_trace_context::stack_of_current_stmts.
+  Dynamic_array<Opt_trace_struct *> stack_of_current_structs;
 
   Buffer trace_buffer;                    ///< Where the trace is accumulated
   Buffer query_buffer;                    ///< Where the original query is put
@@ -448,9 +428,8 @@ const char *Opt_trace_struct::check_key(
 
 // Implementation of Opt_trace_stmt class
 
-Opt_trace_stmt::Opt_trace_stmt(Opt_trace_context *ctx_arg,
-                               enum enum_support_I_S support_I_S_arg):
-  ended(false), support_I_S(support_I_S_arg), ctx(ctx_arg),
+Opt_trace_stmt::Opt_trace_stmt(Opt_trace_context *ctx_arg) :
+  ended(false), I_S_disabled(0), missing_priv(false), ctx(ctx_arg),
   current_struct(NULL)
 {
   // Trace is always in UTF8. This is the only charset which JSON accepts.
@@ -462,7 +441,7 @@ Opt_trace_stmt::Opt_trace_stmt(Opt_trace
 void Opt_trace_stmt::end()
 {
   DBUG_ASSERT(stack_of_current_structs.elements() == 0);
-  DBUG_ASSERT(stack_of_values_of_support_I_S.elements() == 0);
+  DBUG_ASSERT(I_S_disabled >= 0);
   ended= true;
   /*
     Because allocation is done in big chunks, buffer->Ptr[str_length]
@@ -485,6 +464,8 @@ void Opt_trace_stmt::end()
                  DBUG_UNLOCK_FILE;
                }
                );
+  if (unlikely(missing_priv))
+    ctx->restore_I_S();
 }
 
 
@@ -500,7 +481,7 @@ void Opt_trace_stmt::set_query(const cha
   // Should be called only once per statement.
   DBUG_ASSERT(query_buffer.ptr() == NULL);
   query_buffer.set_charset(charset);
-  if (support_I_S != YES_FOR_THIS)
+  if (!support_I_S())
   {
     /*
       Query won't be read, don't waste resources storing it. Still we have set
@@ -527,7 +508,7 @@ bool Opt_trace_stmt::open_struct(const c
                                  bool wants_disable_I_S,
                                  char opening_bracket)
 {
-  if (support_I_S == YES_FOR_THIS)
+  if (support_I_S())
   {
     if (wants_disable_I_S)
     {
@@ -548,9 +529,6 @@ bool Opt_trace_stmt::open_struct(const c
         else
           current_struct->add_alnum("...");
       }
-      if (unlikely(stack_of_values_of_support_I_S.append(support_I_S)))
-        goto err;
-      support_I_S= NO_FOR_THIS_AND_CHILDREN;
     }
     else
     {
@@ -567,6 +545,8 @@ bool Opt_trace_stmt::open_struct(const c
       trace_buffer.append(opening_bracket);
     }
   }
+  if (wants_disable_I_S)
+    ctx->disable_I_S_for_this_and_children();
   {
     DBUG_EXECUTE_IF("opt_trace_oom_in_open_struct",
                     DBUG_SET("+d,simulate_out_of_memory"););
@@ -579,12 +559,10 @@ bool Opt_trace_stmt::open_struct(const c
     DBUG_EXECUTE_IF("opt_trace_oom_in_open_struct",
                     DBUG_SET("-d,simulate_out_of_memory"););
     if (unlikely(rc))
-      goto err;
+      return true;
   }
   current_struct= ots;
   return false;
-err:
-  return true;
 }
 
 
@@ -593,7 +571,7 @@ void Opt_trace_stmt::close_struct(const 
                                   char closing_bracket)
 {
   current_struct= stack_of_current_structs.pop();
-  if (support_I_S == YES_FOR_THIS)
+  if (support_I_S())
   {
     next_line();
     trace_buffer.append(closing_bracket);
@@ -605,13 +583,13 @@ void Opt_trace_stmt::close_struct(const 
     }
   }
   if (has_disabled_I_S)
-    support_I_S= stack_of_values_of_support_I_S.pop();
+    ctx->restore_I_S();
 }
 
 
 void Opt_trace_stmt::separator()
 {
-  DBUG_ASSERT(support_I_S == YES_FOR_THIS);
+  DBUG_ASSERT(support_I_S());
   // Put a comma first, if we have already written an object at this level.
   if (current_struct != NULL)
   {
@@ -651,7 +629,7 @@ void Opt_trace_stmt::next_line()
 void Opt_trace_stmt::add(const char *key, const char *val, size_t val_length,
                          bool quotes, bool escape)
 {
-  if (support_I_S != YES_FOR_THIS)
+  if (!support_I_S())
     return;
   separator();
   if (key != NULL)
@@ -679,7 +657,7 @@ void Opt_trace_stmt::add(const char *key
 void Opt_trace_stmt::syntax_error(const char *key)
 {
   DBUG_PRINT("opt", ("syntax error key: %s", key));
-  DBUG_ASSERT(support_I_S == YES_FOR_THIS);
+  DBUG_ASSERT(support_I_S());
 #ifndef DBUG_OFF
   bool no_assert_on_syntax_error= false;
   DBUG_EXECUTE_IF("opt_trace_no_assert_on_syntax_error",
@@ -705,13 +683,23 @@ void Opt_trace_stmt::syntax_error(const 
 
 void Opt_trace_stmt::fill_info(Opt_trace_info *info) const
 {
-  info->trace_ptr=     trace_buffer.ptr();
-  info->trace_length=  trace_buffer.length();
-  info->query_ptr=     query_buffer.ptr();
-  info->query_length=  query_buffer.length();
-  info->query_charset= query_buffer.charset();
-  info->missing_bytes= trace_buffer.get_missing_bytes() +
-    query_buffer.get_missing_bytes();
+  if (unlikely(info->missing_priv= missing_priv))
+  {
+    info->trace_ptr= info->query_ptr= "";
+    info->trace_length= info->query_length= 0;
+    info->query_charset= &my_charset_bin;
+    info->missing_bytes= 0;
+  }
+  else
+  {
+    info->trace_ptr=     trace_buffer.ptr();
+    info->trace_length=  trace_buffer.length();
+    info->query_ptr=     query_buffer.ptr();
+    info->query_length=  query_buffer.length();
+    info->query_charset= query_buffer.charset();
+    info->missing_bytes= trace_buffer.get_missing_bytes() +
+      query_buffer.get_missing_bytes();
+  }
 }
 
 
@@ -725,9 +713,24 @@ const char *Opt_trace_stmt::trace_buffer
 }
 
 
-// Implementation of class Opt_trace_stmt::Buffer
+void Opt_trace_stmt::missing_privilege()
+{
+  if (!missing_priv)
+  {
+    DBUG_PRINT("opt", ("trace denied"));
+    // This mark will make the trace appear empty in OPTIMIZER_TRACE table.
+    missing_priv= true;
+    // And all substatements will not be traced.
+    ctx->disable_I_S_for_this_and_children();
+  }
+}
+
+
+// Implementation of class Buffer
 
-void Opt_trace_stmt::Buffer::append_escaped(const char *str, size_t length)
+namespace {
+
+void Buffer::append_escaped(const char *str, size_t length)
 {
   if (alloced_length() >= allowed_mem_size)
   {
@@ -811,7 +814,7 @@ void Opt_trace_stmt::Buffer::append_esca
 }
 
 
-void Opt_trace_stmt::Buffer::append(const char *str, size_t length)
+void Buffer::append(const char *str, size_t length)
 {
   if (alloced_length() >= allowed_mem_size)
   {
@@ -826,7 +829,7 @@ void Opt_trace_stmt::Buffer::append(cons
 }
 
 
-void Opt_trace_stmt::Buffer::append(char chr)
+void Buffer::append(char chr)
 {
   if (alloced_length() >= allowed_mem_size)
   {
@@ -838,8 +841,7 @@ void Opt_trace_stmt::Buffer::append(char
 }
 
 
-
-void Opt_trace_stmt::Buffer::prealloc()
+void Buffer::prealloc()
 {
   const size_t alloced=   alloced_length();
   const size_t first_increment= 1024;
@@ -882,6 +884,8 @@ void Opt_trace_stmt::Buffer::prealloc()
   }
 }
 
+} // namespace
+
 
 // Implementation of Opt_trace_context class
 
@@ -904,54 +908,78 @@ Opt_trace_context::default_features=
                                    Opt_trace_context::REPEATED_SUBSELECT);
 
 
-Opt_trace_context::Opt_trace_context() :
-  current_stmt_in_gen(NULL), since_offset_0(0)
-{}
-
-
 Opt_trace_context::~Opt_trace_context()
 {
-  /* There may well be some few ended traces left: */
-  purge_stmts(true);
-  /* All should have moved to 'del' list: */
-  DBUG_ASSERT(all_stmts_for_I_S.elements() == 0);
-  /* All of 'del' list should have been deleted: */
-  DBUG_ASSERT(all_stmts_to_del.elements() == 0);
+  if (unlikely(impl != NULL))
+  {
+    /* There may well be some few ended traces left: */
+    purge_stmts(true);
+    /* All should have moved to 'del' list: */
+    DBUG_ASSERT(impl->all_stmts_for_I_S.elements() == 0);
+    /* All of 'del' list should have been deleted: */
+    DBUG_ASSERT(impl->all_stmts_to_del.elements() == 0);
+    delete impl;
+  }
 }
 
 
-bool Opt_trace_context::start(enum enum_support_I_S support_I_S_arg,
+bool Opt_trace_context::start(bool support_I_S_arg,
+                              bool support_dbug_or_support_missing_priv,
                               bool end_marker_arg, bool one_line_arg,
                               long offset_arg, long limit_arg,
                               ulong max_mem_size_arg, ulonglong features_arg)
 {
-  /*
-    Decide whether to-be-created trace should support I_S.
-    Sometimes the parent rules, sometimes not. If the parent
-    trace was disabled due to being "before offset" (case of a positive
-    offset), we don't want the new trace to inherit and be disabled (for
-    example it may be 'after offset').
-  */
-  enum enum_support_I_S new_stmt_support_I_S;
-  bool rc;
   DBUG_ENTER("Opt_trace_context::start");
 
+  if (I_S_disabled != 0)
+  {
+    DBUG_PRINT("opt", ("opt_trace is already disabled"));
+    support_I_S_arg= false;
+  }
+
   /*
-    Tracing may already be started when we come here, for example if we are
-    starting execution of a sub-statement of a stored routine (CALL has
-    tracing enabled too).
+    Decide on optimizations possible to realize the requested support.
+    If I_S or debug output is requested, need to create an Opt_trace_stmt.
+    Same if we should support calls to Opt_trace_context::missing_privilege(),
+    because that function requires an Opt_trace_stmt.
   */
-  if (current_stmt_in_gen != NULL &&
-      current_stmt_in_gen->get_support_I_S() == NO_FOR_THIS_AND_CHILDREN)
+  if (!support_I_S_arg && !support_dbug_or_support_missing_priv)
   {
+    // The statement will not do tracing.
+    if (likely(impl == NULL) || impl->current_stmt_in_gen == NULL)
+    {
+      /*
+        This should be the most commonly taken branch in a release binary,
+        when the connection rarely has optimizer tracing runtime-enabled.
+        It's thus important that it's optimized: we can short-cut the creation
+        and starting of Opt_trace_stmt, unlike in the next "else" branch.
+      */
+      DBUG_RETURN(false);
+    }
     /*
-      Tracing is strictly disabled by the caller. Thus don't listen to any
-      request from the user for enabling tracing or changing settings (offset
-      etc). Doing otherwise would surely bring a problem.
+      If we come here, there is a parent statement which has a trace.
+      Imagine that we don't create a trace for the child statement
+      here. Then trace structures of the child will be accidentally attached
+      to the parent's trace (as it is still 'current_stmt_in_gen', which
+      constructors of Opt_trace_struct will use); thus the child's trace
+      will be visible (as a chunk of the parent's trace). That would be
+      incorrect. To avoid this, we create a trace for the child but with I_S
+      output disabled; this changes 'current_stmt_in_gen', thus this child's
+      trace structures will be attached to the child's trace and thus not be
+      visible.
     */
-    new_stmt_support_I_S= NO_FOR_THIS_AND_CHILDREN;
   }
-  else
+
+  DBUG_EXECUTE_IF("no_new_opt_trace_stmt", DBUG_ASSERT(0););
+
+  if (impl == NULL)
+    impl= new Opt_trace_context_impl(); // OOM-unsafe new.
+
+  /*
+    If tracing is disabled by some caller, then don't change settings (offset
+    etc). Doing otherwise would surely bring a problem.
+  */
+  if (I_S_disabled == 0)
   {
     /*
       Here we allow a stored routine's sub-statement to enable/disable
@@ -959,83 +987,90 @@ bool Opt_trace_context::start(enum enum_
       be some 'SET OPTIMIZER_TRACE="enabled=[on|off]"' to trace only certain
       sub-statements.
     */
-    new_stmt_support_I_S= support_I_S_arg;
-    end_marker= end_marker_arg;
-    one_line= one_line_arg;
-    offset= offset_arg;
-    limit= limit_arg;
-    max_mem_size= max_mem_size_arg;
+    impl->end_marker= end_marker_arg;
+    impl->one_line= one_line_arg;
+    impl->offset= offset_arg;
+    impl->limit= limit_arg;
+    impl->max_mem_size= max_mem_size_arg;
     // MISC always on
-    features= Opt_trace_context::feature_value(features_arg |
-                                               Opt_trace_context::MISC);
+    impl->features= Opt_trace_context::feature_value(features_arg |
+                                                     Opt_trace_context::MISC);
   }
-  if (new_stmt_support_I_S == YES_FOR_THIS && offset >= 0)
+  if (support_I_S_arg && impl->offset >= 0)
   {
     /* If outside the offset/limit window, no need to support I_S */
-    if (since_offset_0 < offset)
+    if (impl->since_offset_0 < impl->offset)
     {
       DBUG_PRINT("opt", ("disabled: since_offset_0(%ld) < offset(%ld)",
-                         since_offset_0, offset));
-      new_stmt_support_I_S= NO_FOR_THIS;
+                         impl->since_offset_0, impl->offset));
+      support_I_S_arg= false;
     }
-    else if (since_offset_0 >= (offset + limit))
+    else if (impl->since_offset_0 >= (impl->offset + impl->limit))
     {
       DBUG_PRINT("opt", ("disabled: since_offset_0(%ld) >="
                          " offset(%ld) + limit(%ld)",
-                         since_offset_0, offset, limit));
-      new_stmt_support_I_S= NO_FOR_THIS;
+                         impl->since_offset_0, impl->offset, impl->limit));
+      support_I_S_arg= false;
     }
-    since_offset_0++;
+    impl->since_offset_0++;
   }
+  {
+    /*
+      OOM-unsafe "new".
+      We don't allocate it in THD's MEM_ROOT as it must survive until a next
+      statement (SELECT) reads the trace.
+    */
+    Opt_trace_stmt *stmt= new Opt_trace_stmt(this);
 
-  // OOM-unsafe "new".
-  Opt_trace_stmt *stmt= new Opt_trace_stmt(this, new_stmt_support_I_S);
-
-  DBUG_PRINT("opt",("new stmt %p support_I_S %d", stmt,
-                    new_stmt_support_I_S));
+    DBUG_PRINT("opt",("new stmt %p support_I_S %d", stmt, support_I_S_arg));
 
-  if (unlikely(stack_of_current_stmts.append(current_stmt_in_gen)))
-  {
-    // append() above called my_error()
-    goto err;
-  }
+    if (unlikely(impl->stack_of_current_stmts
+                 .append(impl->current_stmt_in_gen)))
+      goto err;                            // append() above called my_error()
 
-  if (new_stmt_support_I_S == YES_FOR_THIS)
-    rc= all_stmts_for_I_S.append(stmt);
-  else
-  {
     /*
       If sending only to DBUG, don't show to the user.
       Same if tracing was temporarily disabled at higher layers with
       Opt_trace_disable_I_S.
       So we just link it to the 'del' list for purging when ended.
     */
-    rc= all_stmts_to_del.append(stmt);
-  }
+    Dynamic_array<Opt_trace_stmt *> *list;
+    if (support_I_S_arg)
+      list= &impl->all_stmts_for_I_S;
+    else
+    {
+      stmt->disable_I_S();           // no need to fill a not-shown JSON trace
+      list= &impl->all_stmts_to_del;
+    }
 
-  if (unlikely(rc))
-    goto err;
+    if (unlikely(list->append(stmt)))
+        goto err;
 
-  current_stmt_in_gen= stmt;
+    impl->current_stmt_in_gen= stmt;
 
-  // As we just added one trace, maybe the previous ones are unneeded now
-  purge_stmts(false);
-  // This purge may have freed space, compute max allowed size:
-  stmt->set_allowed_mem_size(allowed_mem_size_for_current_stmt());
-  DBUG_RETURN(false);
+    // As we just added one trace, maybe the previous ones are unneeded now
+    purge_stmts(false);
+    // This purge may have freed space, compute max allowed size:
+    stmt->set_allowed_mem_size(allowed_mem_size_for_current_stmt());
+    DBUG_RETURN(false);
 err:
-  delete stmt;
-  DBUG_RETURN(true);
+    delete stmt;
+    DBUG_ASSERT(0);
+    DBUG_RETURN(true);
+  }
 }
 
 
 void Opt_trace_context::end()
 {
-  if (current_stmt_in_gen != NULL)
+  DBUG_ASSERT(I_S_disabled >= 0);
+  if (likely(impl == NULL))
+    return;
+  if (impl->current_stmt_in_gen != NULL)
   {
-    current_stmt_in_gen->end();
-    Opt_trace_stmt * const parent= stack_of_current_stmts.pop();
-    current_stmt_in_gen= parent;
+    impl->current_stmt_in_gen->end();
+    Opt_trace_stmt * const parent= impl->stack_of_current_stmts.pop();
+    impl->current_stmt_in_gen= parent;
     if (parent != NULL)
     {
       /*
@@ -1044,42 +1079,42 @@ void Opt_trace_context::end()
       */
       parent->set_allowed_mem_size(allowed_mem_size_for_current_stmt());
     }
+    /*
+      Purge again. Indeed when we are here, compared to the previous start()
+      we have one more ended trace, so can potentially free more. Consider
+      offset=-1 and:
+         top_stmt, started
+           sub_stmt, starts: can't free top_stmt as it is not ended yet
+           sub_stmt, ends: won't free sub_stmt (as user will want to see it),
+           can't free top_stmt as not ended yet
+         top_stmt, continued
+         top_stmt, ends: free top_stmt as it's not last and is ended, keep only
+         sub_stmt.
+      Still the purge is done in ::start() too, as an optimization, for this
+      case:
+         sub_stmt, started
+         sub_stmt, ended
+         sub_stmt, starts: can free above sub_stmt, will save memory compared
+         to free-ing it only when the new sub_stmt ends.
+    */
+    purge_stmts(false);
   }
   else
-    DBUG_ASSERT(stack_of_current_stmts.elements() == 0);
-  /*
-    Purge again. Indeed when we are here, compared to the previous start() we
-    have one more ended trace, so can potentially free more. Consider
-    offset=-1 and:
-       top_stmt, started
-         sub_stmt, starts: can't free top_stmt as it is not ended yet
-         sub_stmt, ends: won't free sub_stmt (as user will want to see it),
-         can't free top_stmt as not ended yet
-       top_stmt, continued
-       top_stmt, ends: free top_stmt as it's not last and is ended, keep only
-       sub_stmt.
-    Still the purge is done in ::start() too, as an optimization, for this
-    case:
-       sub_stmt, started
-       sub_stmt, ended
-       sub_stmt, starts: can free above sub_stmt, will save memory compared to
-       free-ing it only when the new sub_stmt ends.
-  */
-  purge_stmts(false);
+    DBUG_ASSERT(impl->stack_of_current_stmts.elements() == 0);
 }
 
 
 bool Opt_trace_context::support_I_S() const
 {
-  return current_stmt_in_gen &&
-    (current_stmt_in_gen->get_support_I_S() == YES_FOR_THIS);
+  return (impl != NULL) && (impl->current_stmt_in_gen != NULL) &&
+    impl->current_stmt_in_gen->support_I_S();
 }
 
 
 void Opt_trace_context::purge_stmts(bool purge_all)
 {
   DBUG_ENTER("Opt_trace_context::purge_stmts");
-  if (!purge_all && offset >= 0)
+  if (!purge_all && impl->offset >= 0)
   {
     /* This case is managed in @c Opt_trace_context::start() */
     DBUG_VOID_RETURN;
@@ -1093,9 +1128,10 @@ void Opt_trace_context::purge_stmts(bool
     incremented to 1, which is past the array's end, so break out of the loop:
     cell 0 (old cell 1) was not deleted, wrong).
   */
-  for (idx= (all_stmts_for_I_S.elements() - 1) ; idx >= 0 ; idx--)
+  for (idx= (impl->all_stmts_for_I_S.elements() - 1) ; idx >= 0 ; idx--)
   {
-    if (!purge_all && ((all_stmts_for_I_S.elements() + offset) <= idx))
+    if (!purge_all &&
+        ((impl->all_stmts_for_I_S.elements() + impl->offset) <= idx))
     {
       /* OFFSET mandates that this trace should be kept; move to previous */
     }
@@ -1107,8 +1143,9 @@ void Opt_trace_context::purge_stmts(bool
       */
       DBUG_EXECUTE_IF("opt_trace_oom_in_purge",
                       DBUG_SET("+d,simulate_out_of_memory"););
-      if (likely(!all_stmts_to_del.append(all_stmts_for_I_S.at(idx))))
-        all_stmts_for_I_S.del(idx);
+      if (likely(!impl->all_stmts_to_del
+                 .append(impl->all_stmts_for_I_S.at(idx))))
+        impl->all_stmts_for_I_S.del(idx);
       else
       {
         /*
@@ -1122,9 +1159,9 @@ void Opt_trace_context::purge_stmts(bool
     }
   }
   /* Examine list of "to be freed" traces and free what can be */
-  for (idx= (all_stmts_to_del.elements() - 1) ; idx >= 0 ; idx--)
+  for (idx= (impl->all_stmts_to_del.elements() - 1) ; idx >= 0 ; idx--)
   {
-    Opt_trace_stmt *stmt= all_stmts_to_del.at(idx);
+    Opt_trace_stmt *stmt= impl->all_stmts_to_del.at(idx);
 #ifndef DBUG_OFF
     bool skip_del= false;
     DBUG_EXECUTE_IF("opt_trace_oom_in_purge", skip_del= true;);
@@ -1159,7 +1196,7 @@ void Opt_trace_context::purge_stmts(bool
     }
     else
     {
-      all_stmts_to_del.del(idx);
+      impl->all_stmts_to_del.del(idx);
       delete stmt;
     }
   }
@@ -1169,46 +1206,82 @@ void Opt_trace_context::purge_stmts(bool
 
 size_t Opt_trace_context::allowed_mem_size_for_current_stmt() const
 {
-  DBUG_ENTER("Opt_trace_context::allowed_mem_size");
   size_t mem_size= 0;
   int idx;
-  for (idx= (all_stmts_for_I_S.elements() - 1) ; idx >= 0 ; idx--)
+  for (idx= (impl->all_stmts_for_I_S.elements() - 1) ; idx >= 0 ; idx--)
   {
-    const Opt_trace_stmt *stmt= all_stmts_for_I_S.at(idx);
+    const Opt_trace_stmt *stmt= impl->all_stmts_for_I_S.at(idx);
     mem_size+= stmt->alloced_length();
   }
   // Even to-be-deleted traces use memory, so consider them in sum
-  for (idx= (all_stmts_to_del.elements() - 1) ; idx >= 0 ; idx--)
+  for (idx= (impl->all_stmts_to_del.elements() - 1) ; idx >= 0 ; idx--)
   {
-    const Opt_trace_stmt *stmt= all_stmts_to_del.at(idx);
+    const Opt_trace_stmt *stmt= impl->all_stmts_to_del.at(idx);
     mem_size+= stmt->alloced_length();
   }
   /* The current statement is in exactly one of the two lists above */
-  mem_size-= current_stmt_in_gen->alloced_length();
-  size_t rc= (mem_size <= max_mem_size) ? (max_mem_size - mem_size) : 0;
+  mem_size-= impl->current_stmt_in_gen->alloced_length();
+  size_t rc= (mem_size <= impl->max_mem_size) ?
+    (impl->max_mem_size - mem_size) : 0;
   DBUG_PRINT("opt", ("rc %llu max_mem_size %llu",
-                     (ulonglong)rc, (ulonglong)max_mem_size));
-  DBUG_RETURN(rc);
+                     (ulonglong)rc, (ulonglong)impl->max_mem_size));
+  return rc;
 }
 
 
 void Opt_trace_context::set_query(const char *query, size_t length,
                                   const CHARSET_INFO *charset)
 {
-  current_stmt_in_gen->set_query(query, length, charset);
+  impl->current_stmt_in_gen->set_query(query, length, charset);
 }
 
 
 const char *Opt_trace_context::get_tail(size_t size)
 {
-  return current_stmt_in_gen->trace_buffer_tail(size);
+  return (impl == NULL) ? "" :
+    impl->current_stmt_in_gen->trace_buffer_tail(size);
 }
 
 
 void Opt_trace_context::reset()
 {
+  if (impl == NULL)
+    return;
   purge_stmts(true);
-  since_offset_0= 0;
+  impl->since_offset_0= 0;
+}
+
+
+void Opt_trace_context::
+Opt_trace_context_impl::disable_I_S_for_this_and_children()
+{
+  if (current_stmt_in_gen != NULL)
+    current_stmt_in_gen->disable_I_S();
+}
+
+
+void Opt_trace_context::Opt_trace_context_impl::restore_I_S()
+{
+  if (current_stmt_in_gen != NULL)
+    current_stmt_in_gen->restore_I_S();
+}
+
+
+void Opt_trace_context::missing_privilege()
+{
+  /*
+    By storing the 'missing_priv' mark in Opt_trace_stmt instead of in
+    Opt_trace_context we get automatic re-enabling of I_S when the stmt ends,
+    Opt_trace_stmt::missing_priv being the "memory" of where I_S has been
+    disabled.
+    Storing in Opt_trace_context would require an external memory (probably a
+    RAII object), which would not be possible in
+    TABLE_LIST::prepare_security(), where I_S must be disabled even after the
+    end of that function - so RAII would not work.
+
+    Which is why this function needs an existing current_stmt_in_gen.
+  */
+  impl->current_stmt_in_gen->missing_privilege();
 }
 
 
@@ -1216,19 +1289,20 @@ const Opt_trace_stmt
 *Opt_trace_context::get_next_stmt_for_I_S(long *got_so_far) const
 {
   const Opt_trace_stmt *p;
-  if (*got_so_far >= limit)
-    p= NULL;
-  else if (*got_so_far >= all_stmts_for_I_S.elements())
+  if ((impl == NULL) ||
+      (*got_so_far >= impl->limit) ||
+      (*got_so_far >= impl->all_stmts_for_I_S.elements()))
     p= NULL;
   else
   {
-    p= all_stmts_for_I_S.at(*got_so_far);
+    p= impl->all_stmts_for_I_S.at(*got_so_far);
     DBUG_ASSERT(p != NULL);
     (*got_so_far)++;
   }
   return p;
 }
 
+
 // Implementation of class Opt_trace_iterator
 
 Opt_trace_iterator::Opt_trace_iterator(Opt_trace_context *ctx_arg) :
@@ -1257,23 +1331,16 @@ Opt_trace_disable_I_S::Opt_trace_disable
 {
   if (disable)
   {
-    if (ctx_arg != NULL)
-    {
-      stmt= ctx_arg->get_current_stmt_in_gen();
-      if (stmt != NULL)
-        if (unlikely(stmt->disable_I_S_for_this_and_children()))
-          disable= false; // failed to disable: be a dummy object
-    }
-    else
-      stmt= NULL;
+    ctx= ctx_arg;
+    ctx->disable_I_S_for_this_and_children();
   }
 }
 
 
 Opt_trace_disable_I_S::~Opt_trace_disable_I_S()
 {
-  if (disable && (stmt != NULL))
-    stmt->restore_I_S();
+  if (disable)
+    ctx->restore_I_S();
 }
 
 #endif // OPTIMIZER_TRACE

=== modified file 'sql/opt_trace.h'
--- a/sql/opt_trace.h	2011-04-14 13:02:22 +0000
+++ b/sql/opt_trace.h	2011-05-04 20:53:28 +0000
@@ -25,6 +25,7 @@
 struct st_schema_table;
 struct TABLE_LIST;
 struct TABLE;
+class sp_instr;
 
 /**
    @file
@@ -335,26 +336,14 @@ struct TABLE;
   As we don't support exceptions, we need new(nothrow) in order to be able to
   handle OOM.
   But "nothrow" is in the standard C++ library, which we don't link with.
-  So we have two calls to "new" (one to create Opt_trace_context, one to
+  So we have two calls to "new" (one to create Opt_trace_context_impl, one to
   create Opt_trace_stmt), which may crash. When we have nothrow we should
-  change them new(nothrow).
+  change them to new(nothrow).
 */
 
 class Opt_trace_struct;
 class Opt_trace_stmt;           // implementation detail local to opt_trace.cc
 
-/**
-   The different ways a trace output can be sent to
-   INFORMATION_SCHEMA.OPTIMIZER_TRACE.
-   Note that a trace may also go to DBUG, independently of the values below.
-*/
-enum enum_support_I_S
-{
-  YES_FOR_THIS= 0,                       ///< sent to I_S
-  NO_FOR_THIS= 1,                        ///< not sent, undefined for children
-  NO_FOR_THIS_AND_CHILDREN= 2            ///< not sent, and children not sent
-};
-
 
 /**
   @class Opt_trace_context
@@ -405,12 +394,18 @@ enum enum_support_I_S
 class Opt_trace_context
 {
 public:
-  Opt_trace_context();
+
+  Opt_trace_context() : impl(NULL), I_S_disabled(0) {}
   ~Opt_trace_context();
 
   /**
      Starts a new trace.
-     @param  support_I_S      Should trace be in information_schema
+     @param  support_I_S      Whether this statement should have its trace in
+                              information_schema
+     @param  support_dbug_or_support_missing_priv 'true' if this statement
+                              should have its trace in the dbug log (--debug),
+                              or if missing_privilege() may be called on this
+                              trace
      @param  end_marker       For a key/(object|array) pair, should the key be
                               repeated in a comment when the object|array
                               closes? like
@@ -437,15 +432,25 @@ public:
                               destructor is permitted on it; any other
                               member function has undefined effects.
   */
-  bool start(enum enum_support_I_S support_I_S,
+  bool start(bool support_I_S,
+             bool support_dbug_or_support_t_missing_priv,
              bool end_marker, bool one_line,
              long offset, long limit, ulong max_mem_size,
              ulonglong features);
-  /// Ends the current (=open, unfinished, being-generated) trace.
+
+  /**
+    Ends the current (=open, unfinished, being-generated) trace.
+
+    If @c missing_privilege() has been called between start() and end(),
+    end() restores I_S support to what it was before the call to
+    @c missing_privilege(). This is the key to ensure that missing_privilege()
+    does not disable I_S support for the rest of the connection's life!
+  */
   void end();
 
   /// Returns whether there is a current trace
-  bool is_started() const { return current_stmt_in_gen != NULL; }
+  bool is_started() const
+  { return unlikely(impl != NULL) && impl->current_stmt_in_gen != NULL; }
 
   /**
      @returns whether the current trace writes to I_S.
@@ -480,9 +485,9 @@ public:
   void reset();
 
   /// @sa parameters of Opt_trace_context::start()
-  bool get_end_marker() const { return end_marker; }
+  bool get_end_marker() const { return impl->end_marker; }
   /// @sa parameters of Opt_trace_context::start()
-  bool get_one_line() const { return one_line; }
+  bool get_one_line() const { return impl->one_line; }
 
   /**
      Names of flags for @@@@optimizer_trace variable of @c sys_vars.cc :
@@ -519,7 +524,7 @@ public:
     GREEDY_SEARCH=      1 << 0,
     RANGE_OPTIMIZER=    1 << 1,
     DYNAMIC_RANGE=      1 << 2,
-    REPEATED_SUBSELECT= 1 << 3, ///@todo join cache, semijoin...
+    REPEATED_SUBSELECT= 1 << 3,
     /*
       If you add here, update feature_value of empty implementation
       and default_features!
@@ -532,6 +537,16 @@ public:
     */
     MISC=               1 << 7
   };
+
+  /**
+     User lacks privileges to see the current trace. Make the trace appear
+     empty in Opt_trace_info, and disable I_S for all its upcoming children.
+
+     Once a call to this function has been made, subsequent calls to it before
+     @c end() have no effects.
+  */
+  void missing_privilege();
+
   static const feature_value default_features;
 
   /**
@@ -539,14 +554,15 @@ public:
      @param  f  feature
   */
   bool feature_enabled (feature_value f) const
-  { return features & f; }
+  { return unlikely(impl != NULL) && (impl->features & f); }
 
   /**
      Opt_trace_struct is passed Opt_trace_context*, and needs to know
      to which statement's trace to attach, so Opt_trace_context must provide
      this information.
   */
-  Opt_trace_stmt *get_current_stmt_in_gen() { return current_stmt_in_gen; }
+  Opt_trace_stmt *get_current_stmt_in_gen()
+  { return impl->current_stmt_in_gen; }
 
   /**
      @returns the next statement to show in I_S.
@@ -556,77 +572,126 @@ public:
    */
   const Opt_trace_stmt *get_next_stmt_for_I_S(long *got_so_far) const;
 
+  /// Temporarily disables I_S for this trace and its children.
+  void disable_I_S_for_this_and_children()
+  {
+    ++I_S_disabled;
+    if (unlikely(impl != NULL))
+      impl->disable_I_S_for_this_and_children();
+  }
+
+  /**
+     Restores I_S support to what it was before the previous call to
+     disable_I_S_for_this_and_children().
+  */
+  void restore_I_S()
+  {
+    --I_S_disabled;
+    DBUG_ASSERT(I_S_disabled >= 0);
+    if (unlikely(impl != NULL))
+      impl->restore_I_S();
+  }
+
 private:
 
   /**
-     Trace which is currently being generated, where structures are being
-     added. "in_gen" stands for "in generation", being-generated.
+     To have the smallest impact on THD's size, most of the implementation is
+     moved to a separate class Opt_trace_context_impl which is instantiated on
+     the heap when really needed. So if a connection never sets
+     @@@@optimizer_trace to "enabled=on" and does not use --debug, this heap
+     allocation never happens.
+     This class is declared here so that frequently called functions like
+     Opt_trace_context::is_started() can be inlined.
+  */
+  class Opt_trace_context_impl
+  {
+  public:
+    Opt_trace_context_impl() : current_stmt_in_gen(NULL),
+      features(feature_value(0)), offset(0), limit(0), since_offset_0(0)
+    {}
 
-     In simple cases it is equal to the last element of array
-     all_stmts_for_I_S. But it can be prior to it, for example when calling a
-     stored routine:
-@verbatim
-     CALL statement starts executing
-       create trace of CALL (call it "trace #1")
-       add structure to trace #1
-       add structure to trace #1
-       First sub-statement executing
-         create trace of sub-statement (call it "trace #2")
-         add structure to trace #2
-         add structure to trace #2
-       First sub-statement ends
-       add structure to trace #1
-@endverbatim
-     In the beginning, the CALL statement's trace is the newest and current;
-     when the first sub-statement is executing, that sub-statement's trace is
-     the newest and current; when the first sub-statement ends, it is still
-     the newest but it's not the current anymore: the current is then again
-     the CALL's one, where structures will be added, until a second
-     sub-statement is executed.
-     Another case is when the current statement sends only to DBUG:
-     all_stmts_for_I_S lists only traces shown in OPTIMIZER_TRACE.
-  */
-  Opt_trace_stmt *current_stmt_in_gen;
-
-  /**
-     To keep track of what is the current statement, as execution goes into a
-     sub-statement, and back to the upper statement, we have a stack of
-     successive values of current_stmt_in_gen:
-     when in a statement we enter a substatement (a new trace), we push the
-     statement's trace on the stack and change current_stmt_in_gen to the
-     substatement's trace; when leaving the substatement we pop from the stack
-     and set current_stmt_in_gen to the popped value.
-  */
-  Dynamic_array<Opt_trace_stmt *> stack_of_current_stmts;
-
-  /**
-     List of remembered traces for putting into the OPTIMIZER_TRACE
-     table. Element 0 is the one created first, will be first row of
-     OPTIMIZER_TRACE table. The array structure fullfills those needs:
-     - to output traces "oldest first" in OPTIMIZER_TRACE
-     - to preserve traces "newest first" when @@@@optimizer_trace_offset<0
-     - to delete a trace in the middle of the list when it is permanently out
+    void disable_I_S_for_this_and_children();
+    void restore_I_S();
+
+    /**
+       Trace which is currently being generated, where structures are being
+       added. "in_gen" stands for "in generation", being-generated.
+
+       In simple cases it is equal to the last element of array
+       all_stmts_for_I_S. But it can be prior to it, for example when calling a
+       stored routine:
+@verbatim
+       CALL statement starts executing
+         create trace of CALL (call it "trace #1")
+         add structure to trace #1
+         add structure to trace #1
+         First sub-statement executing
+           create trace of sub-statement (call it "trace #2")
+           add structure to trace #2
+           add structure to trace #2
+         First sub-statement ends
+         add structure to trace #1
+@endverbatim
+       In the beginning, the CALL statement's trace is the newest and current;
+       when the first sub-statement is executing, that sub-statement's trace
+       is the newest and current; when the first sub-statement ends, it is
+       still the newest but it's not the current anymore: the current is then
+       again the CALL's one, where structures will be added, until a second
+       sub-statement is executed.
+       Another case is when the current statement sends only to DBUG:
+       all_stmts_for_I_S lists only traces shown in OPTIMIZER_TRACE.
+    */
+    Opt_trace_stmt *current_stmt_in_gen;
+
+    /**
+       To keep track of what is the current statement, as execution goes into a
+       sub-statement, and back to the upper statement, we have a stack of
+       successive values of current_stmt_in_gen:
+       when in a statement we enter a substatement (a new trace), we push the
+       statement's trace on the stack and change current_stmt_in_gen to the
+       substatement's trace; when leaving the substatement we pop from the stack
+       and set current_stmt_in_gen to the popped value.
+    */
+    Dynamic_array<Opt_trace_stmt *> stack_of_current_stmts;
+
+    /**
+       List of remembered traces for putting into the OPTIMIZER_TRACE
+       table. Element 0 is the one created first, will be first row of
+       OPTIMIZER_TRACE table. The array structure fullfills those needs:
+       - to output traces "oldest first" in OPTIMIZER_TRACE
+       - to preserve traces "newest first" when @@@@optimizer_trace_offset<0
+       - to delete a trace in the middle of the list when it is permanently out
        of the offset/limit showable window.
-  */
-  Dynamic_array<Opt_trace_stmt *> all_stmts_for_I_S;
-  /**
-     List of traces which are unneeded because of OFFSET/LIMIT, and scheduled
-     for deletion from memory.
-  */
-  Dynamic_array<Opt_trace_stmt *> all_stmts_to_del;
+    */
+    Dynamic_array<Opt_trace_stmt *> all_stmts_for_I_S;
+    /**
+       List of traces which are unneeded because of OFFSET/LIMIT, and scheduled
+       for deletion from memory.
+    */
+    Dynamic_array<Opt_trace_stmt *> all_stmts_to_del;
 
-  bool end_marker;          ///< copy of parameter of Opt_trace_context::start
-  bool one_line;
-  feature_value features;
-  long offset;
-  long limit;
-  size_t max_mem_size;
+    bool end_marker;          ///< copy of parameter of Opt_trace_context::start
+    bool one_line;
+    feature_value features;
+    long offset;
+    long limit;
+    size_t max_mem_size;
+
+    /**
+       Number of statements traced so far since "offset 0", for comparison with
+       OFFSET and LIMIT, when OFFSET >= 0.
+    */
+    long since_offset_0;
+  };
+
+  Opt_trace_context_impl *impl;
 
   /**
-     Number of statements traced so far since "offset 0", for comparison with
-     OFFSET and LIMIT, when OFFSET >= 0.
+    <>0 <=> any to-be-created statement's trace should not be in
+    information_schema. This applies to next statements, their substatements,
+    etc.
   */
-  long since_offset_0;
+  int I_S_disabled;
 
   /**
      Find and delete unneeded traces.
@@ -653,7 +718,6 @@ private:
   Opt_trace_context(const Opt_trace_context&);
   /// Not defined assignment operator, to disallow assignment.
   Opt_trace_context& operator=(const Opt_trace_context&);
-
 };
 
 
@@ -683,6 +747,7 @@ struct Opt_trace_info
     because of @@@@optimizer-trace-max-mem-size).
   */
   size_t missing_bytes;
+  bool missing_priv; ///< whether user lacks privilege to see this trace
 };
 
 
@@ -754,7 +819,7 @@ protected:
     started(false)
   {
     // A first inlined test
-    if (unlikely(ctx_arg != NULL) && ctx_arg->is_started())
+    if (unlikely(ctx_arg->is_started()))
     {
       // Tracing enabled: must fully initialize the structure.
       do_construct(ctx_arg, requires_key_arg, key, feature);
@@ -1184,7 +1249,7 @@ public:
 
 private:
   bool disable;              ///< whether this instance really does disabling.
-  Opt_trace_stmt *stmt;      ///< statement where disabling happens
+  Opt_trace_context *ctx;
   Opt_trace_disable_I_S(const Opt_trace_disable_I_S&); // not defined
   Opt_trace_disable_I_S& operator=(const Opt_trace_disable_I_S&);//not defined
 };
@@ -1198,49 +1263,58 @@ private:
 //@{
 
 /**
-  Start tracing a THD's actions (generally at a statement's start).
-  @param  thd  the THD
-  @param  tbl  list of tables read/written by the statement.
+  Instantiate this class to start tracing a THD's actions (generally at a
+  statement's start), and to set the "original" query (not transformed, as
+  sent by client) for the new trace. Destructor will end the trace.
+
+  If in a routine's instruction, there is no "query". To be helpful to the
+  user, we craft a query using the instruction's print(). We don't leave this
+  to the caller, because it would be inefficient if tracing is off.
+
+  @param  thd          the THD
+  @param  tbl          list of tables read/written by the statement.
   @param  sql_command  SQL command being prepared or executed
-  @returns whether this function decided to trace (and thus the corresponding
-  opt_trace_end() should end the trace).
-  @note if tracing was already started by a top statement above the present
-  sub-statement in the call chain, and this function decides to trace
-  (respectively not trace) the sub-statement, it returns "true"
-  (resp. false). Each sub-statement is responsible for ending the trace which it
-  has started.
-*/
-bool opt_trace_start(THD *thd, const TABLE_LIST *tbl,
-                     enum enum_sql_command sql_command);
+  @param  query        query
+  @param  length       query's length
+  @param  instr        routine's instruction, if applicable; if so, 'query'
+                       and 'query_length' above are ignored
+  @param  charset      charset which was used to encode this query
 
-/**
-  Stop tracing a THD's actions (generally at statement's end).
-  @param  thd  the THD
-  @param  started  whether this frame did tracing
+  @note Each sub-statement is responsible for ending the trace which it
+  has started; this class achieves this by keeping some memory inside.
 */
-void opt_trace_end(THD *thd, bool started);
+class Opt_trace_start
+{
+public:
+  Opt_trace_start(THD *thd_arg, TABLE_LIST *tbl,
+                  enum enum_sql_command sql_command,
+                  const char *query, size_t query_length,
+                  sp_instr *instr,
+                  const CHARSET_INFO *query_charset);
+  ~Opt_trace_start();
+private:
+  Opt_trace_context * const ctx;
+  bool error; ///< whether trace start() had an error
+};
+
 
 class st_select_lex;
 /**
    Prints SELECT query to optimizer trace. It is not the original query (@see
-   opt_trace_set_query()) but a printout of the parse tree (Item-s).
+   Opt_trace_context::set_query()) but a printout of the parse tree (Item-s).
    @param  thd         the THD
    @param  select_lex  query's parse tree
 */
 void opt_trace_print_expanded_query(THD *thd,
                                     st_select_lex *select_lex);
 
-/**
-   Set the "original" query (not transformed, as sent by client) for the
-   current trace.
-   @param   trace    trace context
-   @param   query    query
-   @param   length   query's length
-   @param   charset  charset which was used to encode this query
-*/
-void opt_trace_set_query(Opt_trace_context *trace, const char *query,
-                         size_t query_length,
-                         const CHARSET_INFO *query_charset);
+void opt_trace_disable_if_no_view_access(THD *thd, TABLE_LIST *view,
+                                 TABLE_LIST *underlying_tables);
+
+class sp_head;
+void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp);
+
+void opt_trace_disable_if_no_security_context_access(THD *thd);
 
 /**
    Fills information_schema.OPTIMIZER_TRACE with rows (one per trace)
@@ -1267,7 +1341,7 @@ int make_optimizer_trace_table_for_show(
 class Opt_trace_context
 {
 public:
-  /// We need this enum even if tracing is disabled
+  /// We need those enums even if tracing is disabled
   enum feature_value {
     GREEDY_SEARCH=      1 << 0,
     RANGE_OPTIMIZER=    1 << 1,
@@ -1275,6 +1349,12 @@ public:
     REPEATED_SUBSELECT= 1 << 3,
     MISC=               1 << 7
   };
+  enum {
+    FLAG_DEFAULT=    0,
+    FLAG_ENABLED=    1 << 0,
+    FLAG_END_MARKER= 1 << 1,
+    FLAG_ONE_LINE=   1 << 2
+  };
 };
 
 /** Empty implementation used when optimizer trace is not compiled in */
@@ -1344,10 +1424,20 @@ public:
   Opt_trace_disable_I_S(Opt_trace_context *ctx_arg, bool disable_arg) {}
 };
 
-#define opt_trace_start(thd, tbl, sql_command) (false)
-#define opt_trace_end(thd, started) do {} while (0)
+class Opt_trace_start
+{
+public:
+  Opt_trace_start(THD *thd, const TABLE_LIST *tbl,
+                  enum enum_sql_command sql_command,
+                  const char *query, size_t query_length,
+                  sp_instr *instr,
+                  const CHARSET_INFO *query_charset) {}
+};
+
 #define opt_trace_print_expanded_query(thd, select_lex) do {} while (0)
-#define opt_trace_set_query(trace,q,ql,cs) do {} while (0)
+#define opt_trace_disable_if_no_view_access(thd, view, underlying_tables) do {} while (0)
+#define opt_trace_disable_if_no_stored_proc_func_access(thd, sp) do {} while (0)
+#define opt_trace_disable_if_no_security_context_access(thd) do {} while (0)
 
 #endif /* OPTIMIZER_TRACE */
 

=== modified file 'sql/opt_trace2server.cc'
--- a/sql/opt_trace2server.cc	2011-04-13 15:36:01 +0000
+++ b/sql/opt_trace2server.cc	2011-05-04 20:53:28 +0000
@@ -20,12 +20,13 @@
    are dedicated "to the server" (hence the file's name).
    In order to create a unit test of the optimizer trace without defining
    Item_field (and all its parent classes), st_select_lex..., these helpers
-   are defined in opt_trace.cc.
+   are defined in opt_trace2server.cc.
 */
 
 #include "opt_trace.h"
 #include "sql_show.h"  // schema_table_stored_record()
 #include "sql_parse.h" // sql_command_flags
+#include "sp_head.h"   // for sp_head
 
 #ifdef OPTIMIZER_TRACE
 
@@ -84,130 +85,121 @@ inline bool sql_command_can_be_traced(en
   return (sql_command_flags[sql_command] & CF_OPTIMIZER_TRACE);
 }
 
+
+void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl);
+
 } // namespace
 
-bool opt_trace_start(THD *thd, const TABLE_LIST *tbl,
-                     enum enum_sql_command sql_command)
+
+Opt_trace_start::Opt_trace_start(THD *thd, TABLE_LIST *tbl,
+                                 enum enum_sql_command sql_command,
+                                 const char *query, size_t query_length,
+                                 sp_instr *instr,
+                                 const CHARSET_INFO *query_charset)
+  : ctx(&thd->opt_trace)
 {
   DBUG_ENTER("opt_trace_start");
 
   /*
-    We need an optimizer trace:
-    * if the user asked for it or
-    * if we are using --debug (because the trace serves as a relay for it, for
-      optimizer debug printouts).
-    Additionally, we should *not* be tracing if:
-    * command is not interesting (optimizer-wise)
-    * query involves I_S.OPTIMIZER_TRACE (we do not want to overwrite the
-      trace while reading it with SELECT).
+    By default, we need an optimizer trace:
+    - if the user asked for it or
+    - if we are using --debug (because the trace serves as a relay for it, for
+    optimizer debug printouts).
   */
   const ulonglong var= thd->variables.optimizer_trace;
-  enum enum_support_I_S support_I_S= (var & Opt_trace_context::FLAG_ENABLED) ?
-    YES_FOR_THIS : NO_FOR_THIS;
-  bool need_it_for_dbug= false;
-  bool allocated_here= false;
+  bool support_I_S= false, support_dbug_or_support_missing_priv= false;
 
   /* This will be triggered if --debug or --debug=d:opt_trace is used */
-  DBUG_EXECUTE("opt", need_it_for_dbug= true;);
+  DBUG_EXECUTE("opt", support_dbug_or_support_missing_priv= true;);
+
   // First step, decide on what type of I_S support we want
-  if (!sql_command_can_be_traced(sql_command))
+  if (unlikely(var & Opt_trace_context::FLAG_ENABLED))
   {
-    /*
-      We also constraint its substatements to do no tracing. This is an extra
-      safety, to prevent against tracing happening in unexpected scenarios, in
-      commands which we normally think do no tracing. Assume that in the
-      future, ALTER TABLE would be able to call a stored function, like this:
-        ALTER TABLE t MODIFY COLUMN c DEFAULT stored_func(d)
-      ('d' being another column; to say that by default 'c' should
-      calculated from 'd'). This introduces a new code path, which may lead to
-      some incorrect JSON syntax in the trace.
-
-      With the constraint in place, the SELECT would not be traced.
-      This constraint forces us to enable trace for CALL because otherwise,
-      execution of a stored procedure would not be traced. Same for SQL PREPARE
-      and SQL EXECUTE.
-    */
-    support_I_S= NO_FOR_THIS_AND_CHILDREN;
-  }
-  else if (unlikely(support_I_S == YES_FOR_THIS &&
-                    list_has_optimizer_trace_table(tbl)))
-  {
-    /*
-      Note that list_has_optimizer_trace_table() is an expensive function
-      (scanning the list of all used tables, doing checks on their names) but
-      we call it only if @@optimizer_trace has enabled=on.
-    */
-    support_I_S= NO_FOR_THIS;
-  }
-  /*
-    Second step, decide on optimizations possible to realize this I_S support.
-    DBUG support requires tracing, then we have no choice.
-  */
-  if (support_I_S != YES_FOR_THIS && !need_it_for_dbug)
-  {
-    // The statement will not do tracing.
-    if (thd->opt_trace == NULL || !thd->opt_trace->is_started())
+    if (sql_command_can_be_traced(sql_command) && // (1)
+        !list_has_optimizer_trace_table(tbl) &&   // (2)
+        !thd->system_thread)                      // (3)
     {
       /*
-        This should be the most commonly taken branch in a release binary,
-        when the connection rarely has optimizer tracing runtime-enabled.
-        It's thus important that it's optimized: we can short-cut the creation
-        and starting of Opt_trace_context, unlike in the next "else" branch.
+        (1) This command is interesting Optimizer-wise.
+
+        (2) If a SELECT of I_S.OPTIMIZER_TRACE were traced, it would overwrite
+        the interesting trace of the previous statement. Note that
+        list_has_optimizer_trace_table() is an expensive function (scanning the
+        list of all used tables, doing checks on their names) but we call it
+        only if @@optimizer_trace has enabled=on.
+
+        (3) Usage of the trace in a system thread would be
+        impractical. Additionally:
+        - threads of the Events Scheduler have an unusual security context
+        (thd->main_security_ctx.priv_user==NULL, see comment in
+        Security_context::change_security_context()), so we can do no security
+        checks on them, so cannot safely enable tracing.
+        - statement-based replication of
+        "INSERT INTO real_table SELECT * FROM I_S.OPTIMIZER_TRACE" is
+        anyway impossible as @@optimizer_trace* are not replicated, and trace
+        would be different between master and slave unless data and engines and
+        version of the optimizer are strictly identical.
+        - row-based replication of the INSERT SELECT above is still allowed, it
+        does not require enabling optimizer trace on the slave.
       */
-      DBUG_RETURN(false);
+      support_I_S= true;
     }
     else
     {
       /*
-        If we come here, there is a parent statement which has a trace.
-        Imagine that we don't create a trace for the child statement
-        here. Then trace structures of the child will be accidentally attached
-        to the parent's trace (as it is still 'current_stmt_in_gen', which
-        constructors of Opt_trace_struct will use); thus the child's trace
-        will be visible (as a chunk of the parent's trace). That would be
-        incorrect. To avoid this, we create a trace for the child but with I_S
-        output disabled; this changes 'current_stmt_in_gen', thus this child's
-        trace structures will be attached to the child's trace and thus not be
-        visible.
+        - statement will not be traced in I_S,
+        - if it uses a subquery, this subquery will not be traced,
+        - if it uses a stored routine, this routine's substatements may be
+        traced.
       */
     }
+    /*
+      We will do security checks. This is true even in the exceptions
+      (1)...(3) above. Otherwise, in:
+        SET OPTIMIZER_TRACE="ENABLED=ON";
+        SELECT stored_func() FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
+      (exception 2), we would not check for privilege to do SHOW CREATE on
+      stored_func, then we would enter a substatement, which would be traced,
+      and would expose the function's body.
+      So we will do security checks. So need to inform the trace system that
+      it should be ready for a possible call to missing_privilege() later:
+    */
+    support_dbug_or_support_missing_priv= true;
   }
 
-  DBUG_EXECUTE_IF("opt_trace_should_not_start", DBUG_ASSERT(0););
-  /*
-    We don't allocate it in THD's MEM_ROOT as it must survive until a next
-    statement (SELECT) reads the trace.
-  */
-  if (thd->opt_trace == NULL)
-  {
-    thd->opt_trace= new Opt_trace_context;      // OOM-unsafe "new".
-    allocated_here= true;
-  }
+  error= ctx->start(support_I_S, support_dbug_or_support_missing_priv,
+                    (var & Opt_trace_context::FLAG_END_MARKER),
+                    (var & Opt_trace_context::FLAG_ONE_LINE),
+                    thd->variables.optimizer_trace_offset,
+                    thd->variables.optimizer_trace_limit,
+                    thd->variables.optimizer_trace_max_mem_size,
+                    thd->variables.optimizer_trace_features);
 
-  if (thd->opt_trace->start(support_I_S,
-                            (var & Opt_trace_context::FLAG_END_MARKER),
-                            (var & Opt_trace_context::FLAG_ONE_LINE),
-                            thd->variables.optimizer_trace_offset,
-                            thd->variables.optimizer_trace_limit,
-                            thd->variables.optimizer_trace_max_mem_size,
-                            thd->variables.optimizer_trace_features))
+  if (likely(!error))
   {
-    if (allocated_here)
+    if (unlikely(support_I_S) && ctx->is_started())
     {
-      delete thd->opt_trace;
-      thd->opt_trace= NULL;
+      if (instr != NULL)
+      {
+        String buffer;
+        buffer.set_charset(system_charset_info);
+        instr->print(&buffer);
+        ctx->set_query(buffer.ptr(), buffer.length(), query_charset);
+      }
+      else
+        ctx->set_query(query, query_length, query_charset);
     }
-    DBUG_RETURN(false);
   }
-  DBUG_RETURN(true); // started all ok
+  opt_trace_disable_if_no_tables_access(thd, tbl);
+  DBUG_VOID_RETURN;
 }
 
 
-void opt_trace_end(THD *thd, bool started)
+Opt_trace_start::~Opt_trace_start()
 {
-  DBUG_ENTER("opt_trace_end");
-  if (started)
-    thd->opt_trace->end();
+  DBUG_ENTER("~opt_trace_start");
+  if (likely(!error))
+    ctx->end();
   DBUG_VOID_RETURN;
 }
 
@@ -215,7 +207,7 @@ void opt_trace_end(THD *thd, bool starte
 void opt_trace_print_expanded_query(THD *thd, st_select_lex *select_lex)
 
 {
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   /**
      It's hard to prove that st_select_lex::print() doesn't modify any of its
      Item-s in a dangerous way. Item_int::print(), for example, modifies its
@@ -226,7 +218,7 @@ void opt_trace_print_expanded_query(THD 
      See also the corresponding call to "set_items_ref_array" at end of
      JOIN::exec().
   */
-  if (likely(trace == NULL || !trace->support_I_S()))
+  if (likely(!trace->support_I_S()))
     return;
   char buff[1024];
   String str(buff,(uint32) sizeof(buff), system_charset_info);
@@ -243,50 +235,387 @@ void opt_trace_print_expanded_query(THD 
 }
 
 
-void opt_trace_set_query(Opt_trace_context *trace, const char *query,
-                         size_t query_length,
-                         const CHARSET_INFO *query_charset)
+/**
+   @name Description of trace-induced additional security checks.
+   A trace exposes information. For example if one does SELECT on a view, the
+   trace contains the view's body. So, the user should be allowed to see the
+   trace only if she/he has privilege to see the body, i.e. privilege to do SHOW
+   CREATE VIEW.
+   There are similar issues with stored procedures, functions, triggers.
+
+   We implement this by doing additional checks on SQL objects when tracing is
+   on:
+   @li stored procedures, functions, triggers: checks are done when executing
+   those objects
+   @li base tables and views.
+
+   Base tables or views are listed in some LEX::query_tables.
+   The LEX may be of the executing statement (statement executed by
+   mysql_execute_command(), or by
+   sp_lex_keeper::reset_lex_and_exec_core()), we check this LEX in the
+   constructor of Opt_trace_start.
+   Or it may be a LEX describing a view, we check this LEX when
+   opening the view (mysql_make_view()).
+
+   Those checks are greatly simplified by disabling traces in case of security
+   context changes (@see opt_trace_disable_if_no_security_context_access()).
+
+   Those checks must be done with the security context of the connected
+   user. Checks with the SUID context would be useless: assume the design is
+   that the basic user does not have DML privileges on tables, but only
+   EXECUTE on SUID-highly-privileged routines (which implement _controlled_
+   _approved_ DMLs): then the SUID context would successfully pass all
+   additional privilege checks, routine would generate tracing, and the
+   connected user would view the trace after the routine's execution, seeing
+   secret information.
+*/
+
+//@{
+
+/**
+   If the security context is not that of the connected user, inform the trace
+   system that a privilege is missing. With one exception: see below.
+
+   @param thd
+
+   This serves to eliminate the following issue.
+   Any information readable by a SELECT may theoretically end up in
+   the trace. And a SELECT may read information from other places than tables:
+   - from views (reading their bodies)
+   - from stored routines (reading their bodies)
+   - from files (reading their content), with LOAD_FILE()
+   - from the list of connections (reading their queries...), with
+   I_S.PROCESSLIST.
+   - from etc, maybe.
+   If the connected user has EXECUTE privilege on a routine which does a
+   security context change, the routine can retrieve information internally
+   (if allowed by the SUID context's privileges), and present only a portion
+   of it to the connected user. But with tracing on, all information is
+   possibly in the trace. So the connected user receives more information than
+   the routine's definer intended to provide.  Fixing this issue would require
+   adding, near many privilege checks in the server, a new
+   optimizer-trace-specific check done against the connected user's context,
+   to verify that the connected user has the right to see the retrieved
+   information.
+
+   Instead, our chosen simpler solution is that if we see a security context
+   change where SUID user is not the connected user, we disable tracing. With
+   only one safe exception: if the connected user has all global privileges
+   (because then she/he can find any information anyway). By "all global
+   privileges" we mean everything but WITH GRANT OPTION (that latter one isn't
+   related to information gathering).
+
+   Read access to I_S.OPTIMIZER_TRACE by another user than the connected user
+   is restricted: @see fill_optimizer_trace_info().
+*/
+void opt_trace_disable_if_no_security_context_access(THD *thd)
 {
-  trace->set_query(query, query_length, query_charset);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+  DBUG_ENTER("opt_trace_check_disable_if_no_security_context_access");
+  if (likely(!(thd->variables.optimizer_trace &
+               Opt_trace_context::FLAG_ENABLED)) || // (1)
+      thd->system_thread)                        // (2)
+  {
+    /*
+      (1) We know that the routine's execution starts with "enabled=off".
+      If it stays so until the routine ends, we needn't do security checks on
+      the routine.
+      If it does not stay so, it means the definer sets it to "on" somewhere
+      in the routine's body. Then it is his conscious decision to generate
+      traces, thus it is still correct to skip the security check.
+
+      (2) Threads of the Events Scheduler have an unusual security context
+      (thd->main_security_ctx.priv_user==NULL, see comment in
+      Security_context::change_security_context()).
+    */
+    DBUG_VOID_RETURN;
+  }
+  Opt_trace_context * const trace= &thd->opt_trace;
+  if (!trace->is_started())
+  {
+    /*
+      @@optimizer_trace has "enabled=on" but trace is not started.
+      Either Opt_trace_start ctor was not called for our statement (1), or it
+      was called but at that time, the variable had "enabled=off" (2).
+
+      (1) can happen in execution of COM_FIELD_LIST on a view.
+
+      (2) suggests that the user managed to change the variable during
+      execution of the statement, and this statement is using
+      view/routine (note that we have not been able to provoke this, maybe
+      this is impossible). If it happens it is suspicious.
+
+      We disable I_S output. And we cannot do otherwise: we have no place to
+      store a possible "missing privilege" information (no Opt_trace_stmt, as
+      is_started() is false), so cannot do security checks, so cannot safely
+      do tracing, so have to disable I_S output.
+    */
+    DBUG_ASSERT(thd->get_command() == COM_FIELD_LIST);
+    trace->disable_I_S_for_this_and_children();
+    DBUG_VOID_RETURN;
+  }
+  /*
+    Note that thd->main_security_ctx.master_access is probably invariant
+    accross the life of THD: GRANT/REVOKE don't affect global privileges of an
+    existing connection, per the manual.
+  */
+  if (!(test_all_bits(thd->main_security_ctx.master_access,
+                      (GLOBAL_ACLS & ~GRANT_ACL))) &&
+      (strcmp(thd->main_security_ctx.priv_user,
+              thd->security_ctx->priv_user) ||
+       my_strcasecmp(system_charset_info, thd->main_security_ctx.priv_host,
+                     thd->security_ctx->priv_host)))
+    trace->missing_privilege();
+  DBUG_VOID_RETURN;
+#endif
 }
 
 
-int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *cond)
+/**
+  If tracing is on, checks additional privileges on a stored routine, to make
+  sure that the user has the right to do SHOW CREATE PROCEDURE/FUNCTION. For
+  that, we use the same checks as in those SHOW commands.
+  If a privilege is missing, notifies the trace system.
+
+  This function is not redundant with
+  opt_trace_disable_if_no_security_context_access().
+  Indeed, for a SQL SECURITY INVOKER routine, there is no context change, but
+  we must still verify that the invoker can do SHOW CREATE.
+
+  For triggers, see note in sp_head::execute_trigger().
+
+  @param thd
+  @param sp  routine to check
+ */
+void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp)
 {
-#ifdef OPTIMIZER_TRACE
-  DBUG_ENTER("fill_optimizer_trace_info");
-  if (thd->opt_trace != NULL)
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+  DBUG_ENTER("opt_trace_disable_if_no_stored_proc_func_access");
+  if (likely(!(thd->variables.optimizer_trace &
+               Opt_trace_context::FLAG_ENABLED)) || thd->system_thread)
+    DBUG_VOID_RETURN;
+  Opt_trace_context * const trace= &thd->opt_trace;
+  if (!trace->is_started())
   {
-    TABLE *table= tables->table;
-    Opt_trace_info info;
+    DBUG_ASSERT(thd->get_command() == COM_FIELD_LIST);
+    trace->disable_I_S_for_this_and_children();
+    DBUG_VOID_RETURN;
+  }
+  bool full_access;
+  Security_context * const backup_thd_sctx= thd->security_ctx;
+  DBUG_PRINT("opt", ("routine: '%s'", sp->m_name.str));
+  thd->security_ctx= &thd->main_security_ctx;
+  const bool rc= check_show_routine_access(thd, sp, &full_access) ||
+    !full_access;
+  thd->security_ctx= backup_thd_sctx;
+  if (rc)
+    trace->missing_privilege();
+  DBUG_VOID_RETURN;
+#endif
+}
+
+
+/**
+   If tracing is on, checks additional privileges for a view, to make sure that
+   the user has the right to do SHOW CREATE VIEW. For that:
+   - this function checks SHOW VIEW
+   - SELECT is tested in opt_trace_disable_if_no_tables_access()
+   - SELECT + SHOW VIEW is sufficient for SHOW CREATE VIEW.
+   We also check underlying tables.
+   If a privilege is missing, notifies the trace system.
+   This function should be called when the view's underlying tables have not
+   yet been merged.
+
+   @param thd               THD context
+   @param view              view to check
+   @param underlying_tables underlying tables/views of 'view'
+ */
+void opt_trace_disable_if_no_view_access(THD *thd, TABLE_LIST *view,
+                                         TABLE_LIST *underlying_tables)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+  DBUG_ENTER("opt_trace_disable_if_no_view_access");
+  if (likely(!(thd->variables.optimizer_trace &
+               Opt_trace_context::FLAG_ENABLED)) || thd->system_thread)
+    DBUG_VOID_RETURN;
+  Opt_trace_context * const trace= &thd->opt_trace;
+  if (!trace->is_started())
+  {
+    DBUG_ASSERT(thd->get_command() == COM_FIELD_LIST);
+    trace->disable_I_S_for_this_and_children();
+    DBUG_VOID_RETURN;
+  }
+  DBUG_PRINT("opt", ("view: '%s'", view->table_name));
+  Security_context * const backup_table_sctx= view->security_ctx;
+  Security_context * const backup_thd_sctx= thd->security_ctx;
+  const GRANT_INFO backup_grant_info= view->grant;
+  view->security_ctx= NULL;                   // no SUID context for view
+  thd->security_ctx= &thd->main_security_ctx; // no SUID context for THD
+  const int rc= check_table_access(thd, SHOW_VIEW_ACL, view, false, 1, true);
+  view->security_ctx= backup_table_sctx;
+  thd->security_ctx= backup_thd_sctx;
+  view->grant= backup_grant_info;
+  if (rc)
+  {
+    trace->missing_privilege();
+    DBUG_VOID_RETURN;
+  }
+  /*
+    We needn't check SELECT privilege on this view. Some
+    opt_trace_disable_if_no_tables_access() call has or will check it.
+
+    Now we check underlying tables/views of our view:
+  */
+  opt_trace_disable_if_no_tables_access(thd, underlying_tables);
+  DBUG_VOID_RETURN;
+#endif
+}
+
+
+namespace {
+
+/**
+   If tracing is on, checks additional privileges on a list of tables/views,
+   to make sure that the user has the right to do SHOW CREATE TABLE/VIEW and
+   "SELECT *". For that:
+   - this functions checks table-level SELECT
+   - which is sufficient for SHOW CREATE TABLE and "SELECT *", if a base table
+   - if a view, if the view has not been identified as such then
+   opt_trace_disable_if_no_view_access() will be later called and check SHOW
+   VIEW; other we check SHOW VIEW here; SHOW VIEW + SELECT is sufficient for
+   SHOW CREATE VIEW.
+   If a privilege is missing, notifies the trace system.
+
+   @param thd
+   @param tbl list of tables to check
+*/
+void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+  DBUG_ENTER("opt_trace_disable_if_no_tables_access");
+  if (likely(!(thd->variables.optimizer_trace &
+               Opt_trace_context::FLAG_ENABLED)) || thd->system_thread)
+    DBUG_VOID_RETURN;
+  Opt_trace_context * const trace= &thd->opt_trace;
+  if (!trace->is_started())
+  {
+    DBUG_ASSERT(thd->get_command() == COM_FIELD_LIST);
+    trace->disable_I_S_for_this_and_children();
+    DBUG_VOID_RETURN;
+  }
+  Security_context * const backup_thd_sctx= thd->security_ctx;
+  thd->security_ctx= &thd->main_security_ctx;
+  const TABLE_LIST * const first_not_own_table= thd->lex->first_not_own_table();
+  for (TABLE_LIST *t= tbl;
+       t != NULL && t != first_not_own_table;
+       t= t->next_global)
+  {
+    DBUG_PRINT("opt", ("table: '%s'", t->table_name));
     /*
-      The list must not change during the iterator's life time. This is ok as
-      the life time is only the present block which cannot change the list.
+      Anonymous derived tables (as in
+      "SELECT ... FROM (SELECT ...)") don't have their grant.privilege set.
     */
-    for (Opt_trace_iterator it(thd->opt_trace) ; !it.at_end() ; it.next())
+    if (!t->is_anonymous_derived_table())
     {
-      it.get_value(&info);
-      restore_record(table, s->default_values);
+      const GRANT_INFO backup_grant_info= t->grant;
+      Security_context * const backup_table_sctx= t->security_ctx;
+      t->security_ctx= NULL;
       /*
-        We will put the query, which is in character_set_client, into a column
-        using character_set_client; this is better than UTF8 (see BUG#57306).
-        When literals with introducers are used, see "LiteralsWithIntroducers"
-        in this file.
+        (1) check_table_access() fills t->grant.privilege.
+        (2) Because SELECT privileges can be column-based,
+        check_table_access() will return 'false' as long as there is SELECT
+        privilege on one column. But we want a table-level privilege.
       */
-      table->field[0]->store(info.query_ptr, info.query_length,
-                             info.query_charset);
-      table->field[1]->store(info.trace_ptr, info.trace_length,
-                             system_charset_info);
-      table->field[2]->store(info.missing_bytes, true);
-      if (schema_table_store_record(thd, table))
-        DBUG_RETURN(1);
+
+      bool rc= check_table_access(thd, SELECT_ACL, t, false, 1, true) || // (1)
+        ((t->grant.privilege & SELECT_ACL) == 0); // (2)
+      if (t->view)
+      {
+        /*
+          It's a view which has already been opened: we are executing a
+          prepared statement. The view has been unfolded in the global list of
+          tables. So underlying tables will be automatically checked in the
+          present function, but we need an explicit check of SHOW VIEW:
+        */
+        rc|= check_table_access(thd, SHOW_VIEW_ACL, t, false, 1, true);
+      }
+      t->security_ctx= backup_table_sctx;
+      t->grant= backup_grant_info;
+      if (rc)
+      {
+        trace->missing_privilege();
+        break;
+      }
     }
   }
+  thd->security_ctx= backup_thd_sctx;
+  DBUG_VOID_RETURN;
+#endif
+}
+
+} // namespace
+
+//@}
+
+
+int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *cond)
+{
+#ifdef OPTIMIZER_TRACE
+  TABLE *table= tables->table;
+  Opt_trace_info info;
+
+  /*
+    When executing a routine which is SQL SECURITY DEFINER, opt-trace specific
+    checks are done with the connected user's privileges; this isn't
+    respecting the meaning of SQL SECURITY DEFINER. If a highly privileged
+    user doesn't know that, he may confidently execute a routine, while this
+    routine nastily uses the connected user's privileges to be allowed to do
+    tracing and gain knowledge about secret objects.
+    This possibility is prevented, by making I_S.OPTIMIZER_TRACE look empty
+    when read from a security context which isn't the connected user's
+    context; with an exception if the SUID security context has all
+    global privileges (in which case the nasty definer has anyway all rights
+    to trace everything).
+
+    Objects which are SQL SECURITY INVOKER are not considered here: with or
+    without optimizer trace, a highly privileged user must always inspect the
+    body of such object before invoking it.
+  */
+  if (!test_all_bits(thd->security_ctx->master_access,
+                     (GLOBAL_ACLS & ~GRANT_ACL)) &&
+      (strcmp(thd->main_security_ctx.priv_user,
+              thd->security_ctx->priv_user) ||
+       my_strcasecmp(system_charset_info, thd->main_security_ctx.priv_host,
+                     thd->security_ctx->priv_host)))
+    return 0;
+  /*
+    The list must not change during the iterator's life time. This is ok as
+    the life time is only the present block which cannot change the list.
+  */
+  for (Opt_trace_iterator it(&thd->opt_trace) ; !it.at_end() ; it.next())
+  {
+    it.get_value(&info);
+    restore_record(table, s->default_values);
+    /*
+      We will put the query, which is in character_set_client, into a column
+      using character_set_client; this is better than UTF8 (see BUG#57306).
+      When literals with introducers are used, see "LiteralsWithIntroducers"
+      in this file.
+    */
+    table->field[0]->store(info.query_ptr, info.query_length,
+                           info.query_charset);
+    table->field[1]->store(info.trace_ptr, info.trace_length,
+                           system_charset_info);
+    table->field[2]->store(info.missing_bytes, true);
+    table->field[3]->store(info.missing_priv, true);
+    if (schema_table_store_record(thd, table))
+      return 1;
+  }
 
-  DBUG_RETURN(0);
+  return 0;
 #else
   my_error(ER_FEATURE_DISABLED, MYF(0), "optimizer trace",
-           "-DOPTIMIZER_TRACE=1 or --with-optimizer-trace");
+           "-DOPTIMIZER_TRACE=1");
   return 1;
 #endif
 }
@@ -299,6 +628,8 @@ ST_FIELD_INFO optimizer_trace_info[]=
   {"TRACE", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
   {"MISSING_BYTES_BEYOND_MAX_MEM_SIZE", 20, MYSQL_TYPE_LONG,
    0, false, NULL, SKIP_OPEN_TABLE},
+  {"INSUFFICIENT_PRIVILEGES", 1, MYSQL_TYPE_TINY,
+   0, false, NULL, SKIP_OPEN_TABLE},
   {NULL, 0,  MYSQL_TYPE_STRING, 0, true, NULL, 0}
 };
 

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2011-03-09 20:54:55 +0000
+++ b/sql/sp_head.cc	2011-05-04 20:53:28 +0000
@@ -39,6 +39,7 @@
 #include "sql_parse.h"                          // cleanup_items
 #include "sql_base.h"                           // close_thread_tables
 #include "transaction.h"       // trans_commit_stmt
+#include "opt_trace.h"         // opt_trace_disable_etc
 
 /*
   Sufficient max length of printed destinations and frame offsets (all uints).
@@ -1246,6 +1247,8 @@ sp_head::execute(THD *thd, bool merge_da
   if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet))
     DBUG_RETURN(TRUE);
 
+  opt_trace_disable_if_no_security_context_access(thd);
+
   /* init per-instruction memroot */
   init_sql_alloc(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
 
@@ -1690,6 +1693,13 @@ sp_head::execute_trigger(THD *thd,
     m_security_ctx.restore_security_context(thd, save_ctx);
     DBUG_RETURN(TRUE);
   }
+  /*
+    Optimizer trace note: we needn't explicitely test here that the connected
+    user has TRIGGER privilege: assume he doesn't have it; two possibilities:
+    - connected user == definer: then we threw an error just above;
+    - connected user != definer: then in sp_head::execute(), when checking the
+    security context we will disable tracing.
+  */
 #endif // NO_EMBEDDED_ACCESS_CHECKS
 
   /*
@@ -1927,6 +1937,8 @@ sp_head::execute_function(THD *thd, Item
     thd->variables.option_bits&= ~OPTION_BIN_LOG;
   }
 
+  opt_trace_disable_if_no_stored_proc_func_access(thd, this);
+
   /*
     Switch to call arena/mem_root so objects like sp_cursor or
     Item_cache holders for case expressions can be allocated on it.
@@ -2171,6 +2183,8 @@ sp_head::execute_procedure(THD *thd, Lis
     err_status= set_routine_security_ctx(thd, this, TRUE, &save_security_ctx);
 #endif
 
+  opt_trace_disable_if_no_stored_proc_func_access(thd, this);
+
   if (!err_status)
     err_status= execute(thd, TRUE);
 
@@ -2951,20 +2965,36 @@ sp_lex_keeper::reset_lex_and_exec_core(T
   reinit_stmt_before_use(thd, m_lex);
 
   if (open_tables)
-    res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
-
-  if (!res)
   {
-    res= instr->exec_core(thd, nextp);
-    DBUG_PRINT("info",("exec_core returned: %d", res));
-  }
+    /*
+      IF, CASE, DECLARE, SET, RETURN, have 'open_tables' true; they may
+      have a subquery in parameter and are worth tracing. They don't
+      correspond to a SQL command so we pretend that they are SQLCOM_SELECT.
+    */
+    Opt_trace_start ots(thd, m_lex->query_tables,
+                        SQLCOM_SELECT, NULL, 0, instr,
+                        thd->variables.character_set_client);
+    Opt_trace_object trace_command(&thd->opt_trace);
+    Opt_trace_array trace_command_steps(&thd->opt_trace, "steps");
 
-  /*
-    Call after unit->cleanup() to close open table
-    key read.
-  */
-  if (open_tables)
-  {
+    /*
+      Check whenever we have access to tables for this statement
+      and open and lock them before executing instructions core function.
+    */
+    res= (check_table_access(thd, SELECT_ACL, m_lex->query_tables, FALSE,
+                             UINT_MAX, FALSE) ||
+          open_and_lock_tables(thd, m_lex->query_tables, TRUE, 0));
+
+    if (!res)
+    {
+      res= instr->exec_core(thd, nextp);
+      DBUG_PRINT("info",("exec_core returned: %d", res));
+    }
+
+    /*
+      Call after unit->cleanup() to close open table
+      key read.
+    */
     m_lex->unit.cleanup();
     /* Here we also commit or rollback the current statement. */
     if (! thd->in_sub_stmt)
@@ -2982,6 +3012,11 @@ sp_lex_keeper::reset_lex_and_exec_core(T
     else if (! thd->in_sub_stmt)
       thd->mdl_context.release_statement_locks();
   }
+  else
+  {
+    res= instr->exec_core(thd, nextp);
+    DBUG_PRINT("info",("exec_core returned: %d", res));
+  }
 
   if (m_lex->query_tables_own_last)
   {
@@ -3032,23 +3067,6 @@ sp_lex_keeper::reset_lex_and_exec_core(T
   sp_instr class functions
 */
 
-int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
-{
-  int result;
-
-  /*
-    Check whenever we have access to tables for this statement
-    and open and lock them before executing instructions core function.
-  */
-  if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)
-      || open_and_lock_tables(thd, tables, TRUE, 0))
-    result= -1;
-  else
-    result= 0;
-
-  return result;
-}
-
 uint sp_instr::get_cont_dest()
 {
   return (m_ip+1);

=== modified file 'sql/sp_head.h'
--- a/sql/sp_head.h	2011-03-09 20:54:55 +0000
+++ b/sql/sp_head.h	2011-05-04 20:53:28 +0000
@@ -585,17 +585,6 @@ public:
   virtual int execute(THD *thd, uint *nextp) = 0;
 
   /**
-    Execute <code>open_and_lock_tables()</code> for this statement.
-    Open and lock the tables used by this statement, as a pre-requisite
-    to execute the core logic of this instruction with
-    <code>exec_core()</code>.
-    @param thd the current thread
-    @param tables the list of tables to open and lock
-    @return zero on success, non zero on failure.
-  */
-  int exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables);
-
-  /**
     Get the continuation destination of this instruction.
     @return the continuation destination
   */
@@ -1354,6 +1343,8 @@ sp_prepare_func_item(THD* thd, Item **it
 bool
 sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr);
 
+bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access);
+
 /**
   @} (end of group Stored_Routines)
 */

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2011-03-21 17:55:41 +0000
+++ b/sql/sql_class.cc	2011-05-04 20:53:28 +0000
@@ -56,7 +56,6 @@
 #include "sp_cache.h"
 #include "transaction.h"
 #include "debug_sync.h"
-#include "opt_trace.h"
 #include "sql_parse.h"                          // is_update_query
 #include "sql_callback.h"
 #include "lock.h"
@@ -529,7 +528,6 @@ THD::THD(bool enable_plugins)
    debug_sync_control(0),
 #endif /* defined(ENABLED_DEBUG_SYNC) */
    m_enable_plugins(enable_plugins),
-   opt_trace(NULL),
    main_warning_info(0)
 {
   ulong tmp;
@@ -1126,7 +1124,6 @@ THD::~THD()
   
   mysql_audit_free_thd(this);
 #endif
-  delete opt_trace;
   free_root(&main_mem_root, MYF(0));
   DBUG_VOID_RETURN;
 }

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2011-03-21 17:55:41 +0000
+++ b/sql/sql_class.h	2011-05-04 20:53:28 +0000
@@ -38,7 +38,7 @@
 #include "violite.h"              /* vio_is_connected */
 #include "thr_lock.h"             /* thr_lock_type, THR_LOCK_DATA,
                                      THR_LOCK_INFO */
-
+#include "opt_trace.h"            /* Opt_trace_context */
 
 class Reprepare_observer;
 class Relay_log_info;
@@ -1473,8 +1473,6 @@ private:
 
 extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
 
-class Opt_trace_context;
-
 /**
   @class THD
   For each client connection we create a separate thread with THD serving as
@@ -2822,7 +2820,7 @@ public:
   */
   Internal_error_handler *pop_internal_handler();
 
-  Opt_trace_context *opt_trace; ///< optimizer trace of current statement
+  Opt_trace_context opt_trace; ///< optimizer trace of current statement
   /**
     Raise an exception condition.
     @param code the MYSQL_ERRNO error code of the error

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2011-04-05 07:34:13 +0000
+++ b/sql/sql_delete.cc	2011-05-04 20:53:28 +0000
@@ -193,7 +193,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
     DBUG_RETURN(TRUE);
 
   { // Enter scope for optimizer trace wrapper
-    Opt_trace_object wrapper(thd->opt_trace);
+    Opt_trace_object wrapper(&thd->opt_trace);
     wrapper.add_utf8_table(table);
 
     if ((select && select->check_quick(thd, safe_update, limit)) || !limit)

=== modified file 'sql/sql_help.cc'
--- a/sql/sql_help.cc	2011-03-21 17:55:41 +0000
+++ b/sql/sql_help.cc	2011-05-04 20:53:28 +0000
@@ -580,7 +580,7 @@ SQL_SELECT *prepare_simple_select(THD *t
   SQL_SELECT *res= make_select(table, 0, 0, cond, 0, error);
 
   // Wrapper for correct JSON in optimizer trace
-  Opt_trace_object wrapper(thd->opt_trace);
+  Opt_trace_object wrapper(&thd->opt_trace);
   if (*error || (res && res->check_quick(thd, 0, HA_POS_ERROR)) ||
       (res && res->quick && res->quick->reset()))
   {

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2011-03-21 17:55:41 +0000
+++ b/sql/sql_parse.cc	2011-05-04 20:53:28 +0000
@@ -419,23 +419,14 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_INSTALL_PLUGIN]=    CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]=  CF_CHANGES_DATA;
 
-  // (1): without it, a procedure's substatements would not be traced.
-  sql_command_flags[SQLCOM_CALL]=      CF_REEXECUTION_FRAGILE |
-                                       CF_CAN_GENERATE_ROW_EVENTS |
-                                       CF_OPTIMIZER_TRACE; // (1)
   /*
-    (1): without it, the executed statement would not be traced. Execution of
-    SQLCOM_EXECUTE calls mysql_execute_command() on executed command, which
-    will check whether that executed command can actually be traced.
+    (1): without it, in "CALL some_proc((subq))", subquery would not be
+    traced.
   */
-  sql_command_flags[SQLCOM_EXECUTE]=   CF_CAN_GENERATE_ROW_EVENTS |
+  sql_command_flags[SQLCOM_CALL]=      CF_REEXECUTION_FRAGILE |
+                                       CF_CAN_GENERATE_ROW_EVENTS |
                                        CF_OPTIMIZER_TRACE; // (1)
-  /*
-    (1): without it, the prepared statement would not be traced.
-    check_prepared_statement() will check whether prepared command can
-    actually be traced.
-  */
-  sql_command_flags[SQLCOM_PREPARE]=   CF_OPTIMIZER_TRACE; // (1)
+  sql_command_flags[SQLCOM_EXECUTE]=   CF_CAN_GENERATE_ROW_EVENTS;
 
   /*
     The following admin table operations are allowed
@@ -2019,14 +2010,12 @@ mysql_execute_command(THD *thd)
 
   status_var_increment(thd->status_var.com_stat[lex->sql_command]);
 
-  const bool started_optimizer_trace= opt_trace_start(thd, all_tables,
-                                                      lex->sql_command);
-  if (started_optimizer_trace)
-    opt_trace_set_query(thd->opt_trace, thd->query(), thd->query_length(),
-                        thd->variables.character_set_client);
+  Opt_trace_start ots(thd, all_tables, lex->sql_command,
+                      thd->query(), thd->query_length(), NULL,
+                      thd->variables.character_set_client);
 
-  Opt_trace_object trace_command(thd->opt_trace);
-  Opt_trace_array trace_command_steps(thd->opt_trace, "steps");
+  Opt_trace_object trace_command(&thd->opt_trace);
+  Opt_trace_array trace_command_steps(&thd->opt_trace, "steps");
 
   DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
 
@@ -4485,10 +4474,6 @@ finish:
     thd->mdl_context.release_statement_locks();
   }
 
-  trace_command_steps.end();
-  trace_command.end(); // must be closed before trace is ended below
-  opt_trace_end(thd, started_optimizer_trace);
-
   DBUG_RETURN(res || thd->is_error());
 }
 

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2011-03-21 17:55:41 +0000
+++ b/sql/sql_prepare.cc	2011-05-04 20:53:28 +0000
@@ -1969,15 +1969,12 @@ static bool check_prepared_statement(Pre
     For the optimizer trace, this is the symmetric, for statement preparation,
     of what is done at statement execution (in mysql_execute_command()).
   */
-  const bool started_optimizer_trace= opt_trace_start(thd, tables,
-                                                      sql_command);
-  if (started_optimizer_trace)
-    opt_trace_set_query(thd->opt_trace, thd->query(), thd->query_length(),
-                        thd->variables.character_set_client);
+  Opt_trace_start ots(thd, tables, sql_command,
+                      thd->query(), thd->query_length(), NULL,
+                      thd->variables.character_set_client);
 
-
-  Opt_trace_object trace_command(thd->opt_trace);
-  Opt_trace_array trace_command_steps(thd->opt_trace, "steps");
+  Opt_trace_object trace_command(&thd->opt_trace);
+  Opt_trace_array trace_command_steps(&thd->opt_trace, "steps");
 
   switch (sql_command) {
   case SQLCOM_REPLACE:
@@ -2021,8 +2018,7 @@ static bool check_prepared_statement(Pre
     if (res == 2)
     {
       /* Statement and field info has already been sent */
-      res= FALSE;
-      goto end;
+      DBUG_RETURN(FALSE);
     }
     break;
   case SQLCOM_CREATE_TABLE:
@@ -2033,8 +2029,7 @@ static bool check_prepared_statement(Pre
     if (lex->create_view_mode == VIEW_ALTER)
     {
       my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0));
-      res= true;
-      goto end;
+      goto error;
     }
     res= mysql_test_create_view(stmt);
     break;
@@ -2107,19 +2102,15 @@ static bool check_prepared_statement(Pre
     {
       /* All other statements are not supported yet. */
       my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0));
-      res= true;
-      goto end;
+      goto error;
     }
     break;
   }
   if (res == 0)
-    res= stmt->is_sql_prepare() ?
-      FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush());
-end:
-  trace_command_steps.end();
-  trace_command.end(); // must be closed before trace is ended below
-  opt_trace_end(thd, started_optimizer_trace);
-  DBUG_RETURN(res);
+    DBUG_RETURN(stmt->is_sql_prepare() ?
+                FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush()));
+error:
+  DBUG_RETURN(TRUE);
 }
 
 /**

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2011-04-14 11:49:41 +0000
+++ b/sql/sql_select.cc	2011-05-04 20:53:28 +0000
@@ -562,7 +562,7 @@ JOIN::prepare(Item ***rref_pointer_array
   if (thd->derived_tables_processing)
     select_lex->exclude_from_table_unique_test= TRUE;
 
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   Opt_trace_object trace_wrapper(trace);
   Opt_trace_object trace_prepare(trace, "join_preparation");
   trace_prepare.add_select_number(select_lex->select_number);
@@ -998,7 +998,7 @@ bool resolve_subquery(THD *thd, JOIN *jo
     }
   }
 
-  Opt_trace_context * const trace= join->thd->opt_trace;
+  Opt_trace_context * const trace= &join->thd->opt_trace;
   if (subq_predicate_substype == Item_subselect::IN_SUBS)
   {
     {
@@ -1823,7 +1823,7 @@ JOIN::optimize()
 
   thd_proc_info(thd, "optimizing");
 
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   Opt_trace_object trace_wrapper(trace);
   Opt_trace_object trace_optimize(trace, "join_optimization");
   trace_optimize.add_select_number(select_lex->select_number);
@@ -2825,7 +2825,7 @@ JOIN::save_join_tab()
 void
 JOIN::exec()
 {
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   Opt_trace_object trace_wrapper(trace);
   Opt_trace_object trace_exec(trace, "join_execution");
   trace_exec.add_select_number(select_lex->select_number);
@@ -3468,8 +3468,7 @@ JOIN::exec()
   */
   if (items0 && ((thd->lex->describe & DESCRIBE_EXTENDED)
 #ifdef OPTIMIZER_TRACE
-                 || (thd->opt_trace != NULL &&
-                     thd->opt_trace->support_I_S())
+                 || trace->support_I_S()
 #endif
                  ) &&
       (select_lex->linkage == DERIVED_TABLE_TYPE ||
@@ -4157,7 +4156,7 @@ bool JOIN::flatten_subqueries()
   Item_exists_subselect **subq;
   Item_exists_subselect **subq_end;
   bool outer_join_objection= FALSE;
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   DBUG_ENTER("JOIN::flatten_subqueries");
 
   if (sj_subselects.elements() == 0)
@@ -4478,7 +4477,7 @@ bool pull_out_semijoin_tables(JOIN *join
     DBUG_RETURN(FALSE);
 
   List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests);
-  Opt_trace_context * const trace= join->thd->opt_trace;
+  Opt_trace_context * const trace= &join->thd->opt_trace;
 
   if (join->select_lex->sj_nests.elements == 0)
     DBUG_RETURN(0);
@@ -4725,7 +4724,7 @@ make_join_statistics(JOIN *join, TABLE_L
   table_map outer_join=0;
   SARGABLE_PARAM *sargables= 0;
   JOIN_TAB *stat_vector[MAX_TABLES+1];
-  Opt_trace_context * const trace= join->thd->opt_trace;
+  Opt_trace_context * const trace= &join->thd->opt_trace;
   DBUG_ENTER("make_join_statistics");
 
   table_count=join->tables;
@@ -5275,7 +5274,7 @@ static bool optimize_semijoin_nests(JOIN
   DBUG_ENTER("optimize_semijoin_nests");
   List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests);
   TABLE_LIST *sj_nest;
-  Opt_trace_context * const trace= join->thd->opt_trace;
+  Opt_trace_context * const trace= &join->thd->opt_trace;
 
   while ((sj_nest= sj_list_it++))
   {
@@ -6514,7 +6513,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR
     (void) set_dynamic(keyuse, &key_end, i);
     keyuse->elements=i;
   }
-  print_keyuse_array(thd->opt_trace, keyuse);
+  print_keyuse_array(&thd->opt_trace, keyuse);
   return FALSE;
 }
 
@@ -6703,7 +6702,7 @@ add_group_and_distinct_keys(JOIN *join, 
   if (!possible_keys.is_clear_all() && 
       !(possible_keys == join_tab->const_keys))
   {
-    trace_indices_added_group_distinct(join->thd->opt_trace, join_tab,
+    trace_indices_added_group_distinct(&join->thd->opt_trace, join_tab,
                                        possible_keys, cause);
     join_tab->const_keys.merge(possible_keys);
   }
@@ -6728,7 +6727,7 @@ static void trace_indices_added_group_di
                                                const char* cause)
 {
 #ifdef OPTIMIZER_TRACE
-  if (likely(trace == NULL) || !trace->is_started())
+  if (likely(!trace->is_started()))
     return;
 
   KEY *key_info= join_tab->table->key_info;
@@ -7151,7 +7150,7 @@ best_access_path(JOIN      *join,
   table_map best_ref_depends_map= 0;
   double tmp;
   bool best_uses_jbuf= FALSE;
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   Loose_scan_opt loose_scan_opt;
   DBUG_ENTER("best_access_path");
 
@@ -7530,7 +7529,6 @@ best_access_path(JOIN      *join,
 
       } /* not ft_key */
       trace_access_idx.add("records", records).add("cost", tmp);
-      /** @todo trace quick_matches_more_parts etc? */
       if (tmp < best_time - records/(double) TIME_FOR_COMPARE ||
           (quick_matches_more_parts && 
            quick_records < best_quick_records))
@@ -7805,8 +7803,8 @@ choose_plan(JOIN *join, table_map join_t
             jtab_sort_func, (void*)join->emb_sjm_nest);
   join->cur_sj_inner_tables= 0;
 
-  Opt_trace_object wrapper(join->thd->opt_trace);
-  Opt_trace_array trace_plan(join->thd->opt_trace, "considered_execution_plans",
+  Opt_trace_object wrapper(&join->thd->opt_trace);
+  Opt_trace_array trace_plan(&join->thd->opt_trace, "considered_execution_plans",
                              Opt_trace_context::GREEDY_SEARCH);
   if (straight_join)
     optimize_straight_join(join, join_tables);
@@ -8016,7 +8014,7 @@ optimize_straight_join(JOIN *join, table
   double    read_time=    0.0;
   POSITION  loose_scan_pos;
  
-  Opt_trace_context * const trace= join->thd->opt_trace;
+  Opt_trace_context * const trace= &join->thd->opt_trace;
   for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
   {
     Opt_trace_object trace_table(trace);
@@ -8487,7 +8485,7 @@ best_extension_by_limited_search(JOIN   
   DBUG_ENTER("best_extension_by_limited_search");
 
   THD *thd= join->thd;
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   if (thd->killed)  // Abort
     DBUG_RETURN(TRUE);
 
@@ -8861,7 +8859,7 @@ static bool fix_semijoin_strategies_for_
 {
   table_map remaining_tables= 0;
   table_map handled_tabs= 0;
-  Opt_trace_context * const trace= join->thd->opt_trace;
+  Opt_trace_context * const trace= &join->thd->opt_trace;
   DBUG_ENTER("fix_semijoin_strategies_for_picked_join_order");
 
   if (join->select_lex->sj_nests.is_empty())
@@ -9955,7 +9953,7 @@ static bool make_join_select(JOIN *join,
     */
     table_map used_tables= 0;
     table_map save_used_tables= 0;
-    Opt_trace_context * const trace= thd->opt_trace;
+    Opt_trace_context * const trace= &thd->opt_trace;
     Opt_trace_object trace_wrapper(trace);
     Opt_trace_object trace_conditions(trace,
                                       "attaching_conditions_to_tables");
@@ -10145,10 +10143,9 @@ static bool make_join_select(JOIN *join,
 	       join->best_positions[i].records_read &&
 	       !(join->select_options & OPTION_FOUND_ROWS)))
 	  {
-            Opt_trace_object trace_one_table(thd->opt_trace);
+            Opt_trace_object trace_one_table(trace);
             trace_one_table.add_utf8_table(tab->table);
-            Opt_trace_object trace_table(join->thd->opt_trace, 
-                                         "rechecking_index_usage");
+            Opt_trace_object trace_table(trace, "rechecking_index_usage");
 
 	    /* Join with outer join condition */
 	    Item *orig_cond=sel->cond;
@@ -10273,7 +10270,7 @@ static bool make_join_select(JOIN *join,
     for (uint i= join->const_tables ; i < join->tables ; i++)
     {
       const JOIN_TAB *tab= join->join_tab+i;
-      Opt_trace_object trace_one_table(thd->opt_trace);
+      Opt_trace_object trace_one_table(trace);
       trace_one_table.add_utf8_table(tab->table);
       trace_one_table.add("attached", tab->select_cond);
     }
@@ -11473,7 +11470,7 @@ make_join_readinfo(JOIN *join, ulonglong
   uint last_sjm_table= MAX_TABLES;
   DBUG_ENTER("make_join_readinfo");
 
-  Opt_trace_context * const trace= join->thd->opt_trace;
+  Opt_trace_context * const trace= &join->thd->opt_trace;
   Opt_trace_object wrapper(trace);
   Opt_trace_array trace_refine_plan(trace, "refine_plan");
 
@@ -14147,7 +14144,7 @@ void optimize_wo_join_buffering(JOIN *jo
   double cost, outer_fanout, inner_fanout= 1.0;
   table_map reopt_remaining_tables= last_remaining_tables;
   uint i;
-  Opt_trace_context * const trace= join->thd->opt_trace;
+  Opt_trace_context * const trace= &join->thd->opt_trace;
   DBUG_ENTER("optimize_wo_join_buffering");
 
   Opt_trace_object trace_recompute(trace, "recompute_best_access_paths");
@@ -14272,7 +14269,7 @@ void advance_sj_state(JOIN *join, table_
   TABLE_LIST *emb_sj_nest= new_join_tab->emb_sj_nest;
   POSITION *pos= join->positions + idx;
   THD *thd= join->thd;
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
 
   /* Add this table to the join prefix */
   remaining_tables &= ~new_join_tab->table->map;
@@ -14892,7 +14889,7 @@ optimize_cond(JOIN *join, Item *conds, L
               bool build_equalities, Item::cond_result *cond_value)
 {
   THD *thd= join->thd;
-  Opt_trace_context * const trace= thd->opt_trace;
+  Opt_trace_context * const trace= &thd->opt_trace;
   DBUG_ENTER("optimize_cond");
 
   if (conds)
@@ -18788,12 +18785,10 @@ join_init_quick_read_record(JOIN_TAB *ta
   */
 
 #ifdef OPTIMIZER_TRACE  
-  Opt_trace_context * const trace= tab->join->thd->opt_trace;
-  const bool repeated_trace_enabled= trace ? 
-    trace->feature_enabled(Opt_trace_context::DYNAMIC_RANGE) :
-    false;
-  const bool disable_trace= 
-    (tab->select->traced_before && !repeated_trace_enabled);
+  Opt_trace_context * const trace= &tab->join->thd->opt_trace;
+  const bool disable_trace=
+    tab->select->traced_before &&
+    !trace->feature_enabled(Opt_trace_context::DYNAMIC_RANGE);
   Opt_trace_disable_I_S disable_trace_wrapper(trace, disable_trace);
 
   tab->select->traced_before= true;
@@ -20440,6 +20435,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR
     ref_key_parts= select->quick->used_key_parts;
   }
 
+  Opt_trace_context * const trace= &tab->join->thd->opt_trace;
+
   if (ref_key >= 0)
   {
     /*
@@ -20506,7 +20503,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR
           key_map new_ref_key_map;  // Force the creation of quick select
           new_ref_key_map.set_bit(new_ref_key); // only for new_ref_key.
 
-          Opt_trace_context * const trace= tab->join->thd->opt_trace;
           Opt_trace_object trace_wrapper(trace);
           Opt_trace_object trace_recest(trace, 
                                     "records_estimation_for_index_ordering");
@@ -20561,7 +20557,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR
     {
       if (table->quick_keys.is_set(best_key) && best_key != ref_key)
       {
-        Opt_trace_context * const trace= join->thd->opt_trace;
         Opt_trace_object trace_wrapper(trace);
         Opt_trace_object trace_recest(trace, 
                                      "records_estimation_for_index_ordering");

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2011-03-21 17:55:41 +0000
+++ b/sql/sql_update.cc	2011-05-04 20:53:28 +0000
@@ -405,7 +405,7 @@ int mysql_update(THD *thd,
   select= make_select(table, 0, 0, conds, 0, &error);
 
   { // Enter scope for optimizer trace wrapper
-    Opt_trace_object wrapper(thd->opt_trace);
+    Opt_trace_object wrapper(&thd->opt_trace);
     wrapper.add_utf8_table(table);
 
     if (error || !limit ||

=== modified file 'sql/sql_view.cc'
--- a/sql/sql_view.cc	2011-03-21 17:55:41 +0000
+++ b/sql/sql_view.cc	2011-05-04 20:53:28 +0000
@@ -32,7 +32,7 @@
 #include "sp_head.h"
 #include "sp_cache.h"
 #include "datadict.h"   // dd_frm_type()
-#include "opt_trace.h"
+#include "opt_trace.h"  // opt_trace_disable_etc
 
 #define MD5_BUFF_LENGTH 33
 
@@ -1078,6 +1078,12 @@ bool mysql_make_view(THD *thd, File_pars
       not changed since PREPARE or the previous execution: the only case
       when this information is changed is execution of UPDATE on a view, but
       the original want_access is restored in its end.
+
+      Optimizer trace: because tables have been unfolded already, they are
+      in lex->query_tables of the statement using the view. So privileges on
+      them are checked as done for explicitely listed tables, in constructor
+      of Opt_trace_start. Security context change is checked in
+      prepare_security() below.
     */
     if (!table->prelocking_placeholder && table->prepare_security(thd))
     {
@@ -1133,8 +1139,8 @@ bool mysql_make_view(THD *thd, File_pars
   table->definer.user.str= table->definer.host.str= 0;
   table->definer.user.length= table->definer.host.length= 0;
 
-  Opt_trace_object trace_wrapper(thd->opt_trace);
-  Opt_trace_object trace_view(thd->opt_trace, "view");
+  Opt_trace_object trace_wrapper(&thd->opt_trace);
+  Opt_trace_object trace_view(&thd->opt_trace, "view");
   // When reading I_S.VIEWS, table->alias may be NULL
   trace_view.add_utf8("database", table->db, table->db_length).
     add_utf8("view", table->alias ? table->alias : table->table_name).
@@ -1288,23 +1294,27 @@ bool mysql_make_view(THD *thd, File_pars
       underlying tables.
       Skip this step if we are opening view for prelocking only.
     */
-    if (!table->prelocking_placeholder &&
-        (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
+    if (!table->prelocking_placeholder)
     {
-      if (check_table_access(thd, SELECT_ACL, view_tables, FALSE,
-                             UINT_MAX, TRUE) &&
-          check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, TRUE))
+      // If executing prepared statement: see "Optimizer trace" note above.
+      opt_trace_disable_if_no_view_access(thd, table, view_tables);
+
+      if (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe)
       {
-        my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
-        goto err;
+        if (check_table_access(thd, SELECT_ACL, view_tables, FALSE,
+                               UINT_MAX, TRUE) &&
+            check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, TRUE))
+        {
+          my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
+          goto err;
+        }
+      }
+      else if ((old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
+               !table->belong_to_view)
+      {
+        if (check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, FALSE))
+          goto err;
       }
-    }
-    else if (!table->prelocking_placeholder &&
-             (old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
-             !table->belong_to_view)
-    {
-      if (check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, FALSE))
-        goto err;
     }
 
     if (!(table->view_tables=

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2011-03-21 17:55:41 +0000
+++ b/sql/sys_vars.cc	2011-05-04 20:53:28 +0000
@@ -1530,8 +1530,7 @@ static Sys_var_flagset Sys_optimizer_tra
 static bool optimizer_trace_update(sys_var *self, THD *thd,
                                    enum_var_type type)
 {
-  if (thd->opt_trace != NULL)
-    thd->opt_trace->reset();
+  thd->opt_trace.reset();
   return false;
 }
 

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2011-03-17 09:47:50 +0000
+++ b/sql/table.cc	2011-05-04 20:53:28 +0000
@@ -4232,6 +4232,7 @@ bool TABLE_LIST::prepare_security(THD *t
   if (prepare_view_securety_context(thd))
     DBUG_RETURN(TRUE);
   thd->security_ctx= find_view_security_context(thd);
+  opt_trace_disable_if_no_security_context_access(thd);
   while ((tbl= tb++))
   {
     DBUG_ASSERT(tbl->referencing_view);

=== modified file 'unittest/gunit/opt_trace-t.cc'
--- a/unittest/gunit/opt_trace-t.cc	2011-04-13 15:36:01 +0000
+++ b/unittest/gunit/opt_trace-t.cc	2011-05-04 20:53:28 +0000
@@ -69,6 +69,7 @@ void do_check_json_compliance(const char
   // Send the trace to this new process' stdin:
   FILE *fd= popen(python_cmd, "w");
   ASSERT_TRUE(NULL != fd);
+  ASSERT_NE(0U, length);                        // empty is not compliant
   ASSERT_EQ(1U, fwrite(str, length, 1, fd));
   int rc= pclose(fd);
   rc= WEXITSTATUS(rc);
@@ -109,8 +110,9 @@ TEST_F(TraceContentTest, ConstructAndDes
 /** Test empty trace */
 TEST_F(TraceContentTest, Empty)
 {
-  ASSERT_FALSE(trace.start(YES_FOR_THIS, false, false, -1, 1, ULONG_MAX,
+  ASSERT_FALSE(trace.start(true, false, false, false, -1, 1, ULONG_MAX,
                            all_features));
+  EXPECT_TRUE(trace.is_started());
   EXPECT_TRUE(trace.support_I_S());
   /*
     Add at least an object to it. A really empty trace ("") is not
@@ -135,6 +137,7 @@ TEST_F(TraceContentTest, Empty)
   EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
   check_json_compliance(info.trace_ptr, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   /* Should be no more traces */
   it.next();
@@ -145,7 +148,7 @@ TEST_F(TraceContentTest, Empty)
 /** Test normal usage */
 TEST_F(TraceContentTest, NormalUsage)
 {
-  ASSERT_FALSE(trace.start(YES_FOR_THIS, true, false, -1, 1, ULONG_MAX,
+  ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
                            all_features));
   {
     Opt_trace_object oto(&trace);
@@ -196,6 +199,7 @@ TEST_F(TraceContentTest, NormalUsage)
   EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
   check_json_compliance(info.trace_ptr, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   it.next();
   ASSERT_TRUE(it.at_end());
@@ -208,7 +212,7 @@ TEST_F(TraceContentTest, NormalUsage)
 */
 TEST_F(TraceContentTest, Tail)
 {
-  ASSERT_FALSE(trace.start(YES_FOR_THIS, true, false, -1, 1, ULONG_MAX,
+  ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
                            all_features));
   {
     Opt_trace_object oto(&trace);
@@ -259,6 +263,7 @@ TEST_F(TraceContentTest, Tail)
   EXPECT_EQ((void *)0, info.query_ptr);
   EXPECT_EQ(0U, info.query_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   EXPECT_EQ(0, strcmp(expected + sizeof(expected) - 1 - 40, tail));
   it.next();
@@ -269,7 +274,7 @@ TEST_F(TraceContentTest, Tail)
 /** Test reaction to malformed JSON (object with value without key) */
 TEST_F(TraceContentTest, BuggyObject)
 {
-  ASSERT_FALSE(trace.start(YES_FOR_THIS, true, false, -1, 1, ULONG_MAX,
+  ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
                            all_features));
   {
     Opt_trace_object oto(&trace);
@@ -317,6 +322,7 @@ TEST_F(TraceContentTest, BuggyObject)
   EXPECT_STREQ(expected, info.trace_ptr);
   EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   it.next();
   ASSERT_TRUE(it.at_end());
@@ -326,7 +332,7 @@ TEST_F(TraceContentTest, BuggyObject)
 /** Test reaction to malformed JSON (array with value with key) */
 TEST_F(TraceContentTest, BuggyArray)
 {
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, true, false, -1, 1, ULONG_MAX,
+  ASSERT_EQ(false, trace.start(true, false, true, false, -1, 1, ULONG_MAX,
                                all_features));
   {
     Opt_trace_object oto(&trace);
@@ -363,6 +369,7 @@ TEST_F(TraceContentTest, BuggyArray)
   EXPECT_STREQ(expected, info.trace_ptr);
   EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   it.next();
   ASSERT_TRUE(it.at_end());
@@ -370,9 +377,9 @@ TEST_F(TraceContentTest, BuggyArray)
 
 
 /** Test Opt_trace_disable_I_S */
-TEST_F(TraceContentTest, DisableIS)
+TEST_F(TraceContentTest, DisableISWithObject)
 {
-  ASSERT_FALSE(trace.start(YES_FOR_THIS, true, false, -1, 1, ULONG_MAX,
+  ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
                            all_features));
   {
     Opt_trace_object oto(&trace);
@@ -390,6 +397,13 @@ TEST_F(TraceContentTest, DisableIS)
         /* don't disable... but above layer is stronger */
         Opt_trace_disable_I_S otd2(&trace, false);
         oto2.add("another key inside", 5LL);
+        // disabling should apply to substatements too:
+        ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
+                                 all_features));
+        {
+          Opt_trace_object oto3(&trace);
+        }
+        trace.end();
       }
       ota.add_alnum("one string element");
       ota.add(true);
@@ -429,16 +443,71 @@ TEST_F(TraceContentTest, DisableIS)
   EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
   check_json_compliance(info.trace_ptr, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   it.next();
   ASSERT_TRUE(it.at_end());
 }
 
+
+/** Test Opt_trace_context::disable_I_S_for_this_and_children */
+TEST_F(TraceContentTest, DisableISWithCall)
+{
+  // Test that it disables even before any start()
+  trace.disable_I_S_for_this_and_children();
+  ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
+                           all_features));
+  {
+    Opt_trace_object oto(&trace);
+    {
+      Opt_trace_array ota(&trace, "one array");
+      ota.add(200.4);
+      {
+        Opt_trace_object oto1(&trace);
+        oto1.add_alnum("one key", "one value").
+          add("another key", 100LL);
+        oto1.add("a third key", false);
+        Opt_trace_object oto2(&trace, "a fourth key");
+        oto2.add("key inside", 1LL);
+        // disabling should apply to substatements too:
+        ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
+                                 all_features));
+        {
+          Opt_trace_object oto3(&trace);
+        }
+        trace.end();
+        /* don't disable... but above layer is stronger */
+        Opt_trace_disable_I_S otd2(&trace, false);
+        oto2.add("another key inside", 5LL);
+        // disabling should apply to substatements too:
+        ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
+                                 all_features));
+        {
+          Opt_trace_object oto4(&trace);
+        }
+        trace.end();
+      }
+      ota.add_alnum("one string element");
+      ota.add(true);
+    }
+    oto.add("yet another key", -1000LL);
+    {
+      Opt_trace_array ota(&trace, "another array");
+      ota.add(1LL).add(2LL).add(3LL).add(4LL);
+    }
+  }
+  trace.end();
+  trace.restore_I_S();
+  Opt_trace_iterator it(&trace);
+  ASSERT_TRUE(it.at_end());
+}
+
+
 /** Helper for Trace_settings_test.offset */
 void make_one_trace(Opt_trace_context *trace, const char *name,
                     long offset, long limit)
 {
-  ASSERT_FALSE(trace->start(YES_FOR_THIS, true, false, offset, limit,
+  ASSERT_FALSE(trace->start(true, false, true, false, offset, limit,
                             ULONG_MAX, all_features));
   {
     Opt_trace_object oto(trace);
@@ -539,8 +608,8 @@ TEST_F(TraceContentTest, Offset)
 /** Test truncation by max_mem_size */
 TEST_F(TraceContentTest, MaxMemSize)
 {
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -1, 1,
-                               1000 /* max_mem_size */, all_features));
+  ASSERT_EQ(false, trace.start(true, false, false, false, -1,
+                               1, 1000 /* max_mem_size */, all_features));
   /* make a "long" trace */
   {
     Opt_trace_object oto(&trace);
@@ -567,6 +636,7 @@ TEST_F(TraceContentTest, MaxMemSize)
   */
   EXPECT_EQ(996U, info.trace_length);
   EXPECT_EQ(1027U, info.missing_bytes); // 996+1027=2023
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   EXPECT_EQ(0, strncmp(expected, info.trace_ptr, sizeof(expected) - 1));
   it.next();
@@ -579,8 +649,8 @@ TEST_F(TraceContentTest, MaxMemSize)
 TEST_F(TraceContentTest, MaxMemSize2)
 {
   Opt_trace_context trace;
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -2, 2,
-                               21 /* max_mem_size */, all_features));
+  ASSERT_EQ(false, trace.start(true, false, false, false, -2,
+                               2, 21 /* max_mem_size */, all_features));
   /* make a "long" trace */
   {
     Opt_trace_object oto(&trace);
@@ -588,8 +658,8 @@ TEST_F(TraceContentTest, MaxMemSize2)
   }
   trace.end();
   /* A second similar trace */
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -2, 2, 21,
-                               all_features));
+  ASSERT_EQ(false, trace.start(true, false, false, false, -2,
+                               2, 21, all_features));
   {
     Opt_trace_object oto(&trace);
     oto.add_alnum("some key2", "make it long");
@@ -601,6 +671,7 @@ TEST_F(TraceContentTest, MaxMemSize2)
   it.get_value(&info);
   EXPECT_EQ(17U, info.trace_length);
   EXPECT_EQ(16U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   it.next();
   ASSERT_FALSE(it.at_end());
@@ -614,8 +685,8 @@ TEST_F(TraceContentTest, MaxMemSize2)
     3rd trace; the first one should automatically be purged, thus the 3rd
     should have a bit of room.
   */
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -2, 2, 21,
-                               all_features));
+  ASSERT_EQ(false, trace.start(true, false, false, false, -2,
+                               2, 21, all_features));
   {
     Opt_trace_object oto(&trace);
     oto.add_alnum("some key3", "make it long");
@@ -626,6 +697,7 @@ TEST_F(TraceContentTest, MaxMemSize2)
   it2.get_value(&info);
   EXPECT_EQ(0U, info.trace_length);
   EXPECT_EQ(33U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   it2.next();
   it2.get_value(&info);
@@ -635,6 +707,7 @@ TEST_F(TraceContentTest, MaxMemSize2)
   */
   EXPECT_EQ(14U, info.trace_length);
   EXPECT_EQ(19U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   it2.next();
   ASSERT_TRUE(it2.at_end());
 }
@@ -674,7 +747,7 @@ void open_object(uint count, Opt_trace_c
 /// Test reaction to out-of-memory condition in trace buffer
 TEST_F(TraceContentTest, OOMinBuffer)
 {
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -1, 1, ULONG_MAX,
+  ASSERT_EQ(false, trace.start(true, false, false, false, -1, 1, ULONG_MAX,
                                all_features));
   {
     Opt_trace_object oto(&trace);
@@ -692,6 +765,7 @@ TEST_F(TraceContentTest, OOMinBuffer)
   Opt_trace_info info;
   it.get_value(&info);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   it.next();
   ASSERT_TRUE(it.at_end());
   EXPECT_TRUE(oom);
@@ -701,7 +775,7 @@ TEST_F(TraceContentTest, OOMinBuffer)
 /// Test reaction to out-of-memory condition in book-keeping data structures
 TEST_F(TraceContentTest, OOMinBookKeeping)
 {
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -1, 1, ULONG_MAX,
+  ASSERT_EQ(false, trace.start(true, false, false, false, -1, 1, ULONG_MAX,
                                all_features));
   {
     Opt_trace_object oto(&trace);
@@ -713,6 +787,7 @@ TEST_F(TraceContentTest, OOMinBookKeepin
   Opt_trace_info info;
   it.get_value(&info);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   it.next();
   ASSERT_TRUE(it.at_end());
   EXPECT_TRUE(oom);
@@ -765,7 +840,7 @@ TEST_F(TraceContentTest, OOMinPurge)
 /** Test filtering by feature */
 TEST_F(TraceContentTest, FilteringByFeature)
 {
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -1, 1, ULONG_MAX,
+  ASSERT_EQ(false, trace.start(true, false, false, false, -1, 1, ULONG_MAX,
                                Opt_trace_context::MISC));
   {
     Opt_trace_object oto(&trace);
@@ -818,6 +893,7 @@ TEST_F(TraceContentTest, FilteringByFeat
   EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
   check_json_compliance(info.trace_ptr, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   it.next();
   ASSERT_TRUE(it.at_end());
@@ -827,7 +903,7 @@ TEST_F(TraceContentTest, FilteringByFeat
 /** Test escaping of characters */
 TEST_F(TraceContentTest, Escaping)
 {
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, true, false, -1, 1, ULONG_MAX,
+  ASSERT_EQ(false, trace.start(true, false, true, false, -1, 1, ULONG_MAX,
                                all_features));
   // All ASCII 0-127 chars are valid UTF8 encodings
   char all_chars[130];
@@ -857,6 +933,7 @@ TEST_F(TraceContentTest, Escaping)
   EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
   check_json_compliance(info.trace_ptr, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   EXPECT_EQ(sizeof(all_chars), info.query_length);
   // we get the query unescaped, verbatim, not 0-terminated:
@@ -870,7 +947,7 @@ TEST_F(TraceContentTest, Escaping)
 /** Test how the system handles non-UTF8 characters, a violation of its API */
 TEST_F(TraceContentTest, NonUtf8)
 {
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, true, false, -1, 1, ULONG_MAX,
+  ASSERT_EQ(false, trace.start(true, false, true, false, -1, 1, ULONG_MAX,
                                all_features));
   /*
     A string which starts with invalid utf8 (the four first bytes are éèÄà in
@@ -906,8 +983,8 @@ TEST_F(TraceContentTest, NonUtf8)
     "}";
   EXPECT_STREQ(expected, info.trace_ptr);
   EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
-  check_json_compliance(info.trace_ptr, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   EXPECT_EQ(sizeof(all_chars), info.query_length);
   // we get the query unescaped, verbatim, not 0-terminated:
@@ -924,7 +1001,7 @@ TEST_F(TraceContentTest, NonUtf8)
 */
 TEST_F(TraceContentTest, Indent)
 {
-  ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -1, 1, ULONG_MAX,
+  ASSERT_EQ(false, trace.start(true, false, false, false, -1, 1, ULONG_MAX,
                                all_features));
   {
     Opt_trace_object oto(&trace);
@@ -966,11 +1043,182 @@ TEST_F(TraceContentTest, Indent)
   EXPECT_EQ(20300U, spaces);
   check_json_compliance(info.trace_ptr, info.trace_length);
   EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
+  EXPECT_FALSE(oom);
+  it.next();
+  ASSERT_TRUE(it.at_end());
+}
+
+
+/** Test Opt_trace_context::missing_privilege() */
+TEST_F(TraceContentTest, MissingPrivilege)
+{
+  ASSERT_FALSE(trace.start(true, false, true, false, 0, 100, ULONG_MAX,
+                           all_features));
+  {
+    Opt_trace_object oto(&trace);
+    {
+      Opt_trace_array ota(&trace, "one array");
+      ota.add(200.4);
+      {
+        Opt_trace_object oto1(&trace);
+        oto1.add_alnum("one key", "one value").
+          add("another key", 100LL);
+        oto1.add("a third key", false);
+        Opt_trace_object oto2(&trace, "a fourth key");
+        oto2.add("key inside", 1LL);
+        ASSERT_FALSE(trace.start(true, false, true, false, 0, 100, ULONG_MAX,
+                                 all_features));
+        {
+          Opt_trace_object oto3(&trace);
+          trace.missing_privilege();
+          ASSERT_FALSE(trace.start(true, false, true, false, 0, 100,
+                                   ULONG_MAX, all_features));
+          {
+            Opt_trace_object oto4(&trace);
+            oto4.add_alnum("in4","key4");
+          }
+          trace.end();
+        }
+        trace.end();                        // this should restore I_S support
+        // so this should be visible
+        oto2.add("another key inside", 5LL);
+        // and this new sub statement too:
+        ASSERT_FALSE(trace.start(true, false, true, false, 0, 100, ULONG_MAX,
+                                 all_features));
+        {
+          Opt_trace_object oto5(&trace);
+          oto5.add("in5", true);
+        }
+        trace.end();
+      }
+      ota.add_alnum("one string element");
+      ota.add(true);
+    }
+    oto.add("yet another key", -1000LL);
+    {
+      Opt_trace_array ota(&trace, "another array");
+      ota.add(1LL).add(2LL).add(3LL).add(4LL);
+    }
+  }
+  trace.end();
+  Opt_trace_iterator it(&trace);
+  ASSERT_FALSE(it.at_end());
+  Opt_trace_info info;
+  it.get_value(&info);
+  const char expected[]=
+    "{\n"
+    "  \"one array\": [\n"
+    "    200.4,\n"
+    "    {\n"
+    "      \"one key\": \"one value\",\n"
+    "      \"another key\": 100,\n"
+    "      \"a third key\": false,\n"
+    "      \"a fourth key\": {\n"
+    "        \"key inside\": 1,\n"
+    "        \"another key inside\": 5\n"
+    "      } /* a fourth key */\n"
+    "    },\n"
+    "    \"one string element\",\n"
+    "    true\n"
+    "  ] /* one array */,\n"
+    "  \"yet another key\": -1000,\n"
+    "  \"another array\": [\n"
+    "    1,\n"
+    "    2,\n"
+    "    3,\n"
+    "    4\n"
+    "  ] /* another array */\n"
+    "}";
+  EXPECT_STREQ(expected, info.trace_ptr);
+  EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
+  check_json_compliance(info.trace_ptr, info.trace_length);
+  EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
   EXPECT_FALSE(oom);
   it.next();
+  ASSERT_FALSE(it.at_end());
+  // Now the substatement with a missing privilege
+  it.get_value(&info);
+  const char expected2[]= ""; // because of missing privilege...
+  EXPECT_STREQ(expected2, info.trace_ptr);
+  EXPECT_EQ(sizeof(expected2) - 1, info.trace_length);
+  EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_TRUE(info.missing_priv); // ... tested here.
+  it.next();
+  ASSERT_FALSE(it.at_end());
+  // And now the last substatement, visible
+  it.get_value(&info);
+  const char expected3[]=
+    "{\n"
+    "  \"in5\": true\n"
+    "}";
+  EXPECT_STREQ(expected3, info.trace_ptr);
+  EXPECT_EQ(sizeof(expected3) - 1, info.trace_length);
+  check_json_compliance(info.trace_ptr, info.trace_length);
+  EXPECT_EQ(0U, info.missing_bytes);
+  EXPECT_FALSE(info.missing_priv);
+  it.next();
+  ASSERT_TRUE(it.at_end());
+}
+
+
+/** Test Opt_trace_context::missing_privilege() on absent trace */
+TEST_F(TraceContentTest, MissingPrivilege2)
+{
+  /*
+    Ask for neither I_S not debug output, and no
+    missing_privilege() support
+  */
+  ASSERT_FALSE(trace.start(false, false, true, false, 0, 100, ULONG_MAX,
+                           all_features));
+  EXPECT_FALSE(trace.is_started());
+  trace.end();
+  /*
+    Ask for neither I_S not debug output, but ask that
+    missing_privilege() is supported.
+  */
+  ASSERT_FALSE(trace.start(false, true, true, false, 0, 100, ULONG_MAX,
+                           all_features));
+  EXPECT_TRUE(trace.is_started());
+  trace.missing_privilege();
+  // This above should make the substatement below not be traced:
+  ASSERT_FALSE(trace.start(true, false, true, false, 0, 100, ULONG_MAX,
+                           all_features));
+  {
+    Opt_trace_object oto5(&trace);
+    oto5.add("in5", true);
+  }
+  trace.end();
+  trace.end();
+  Opt_trace_iterator it(&trace);
   ASSERT_TRUE(it.at_end());
 }
 
+
+/**
+   Test an optimization: that no Opt_trace_stmt is created in common case
+   where all statements and substatements ask neither for I_S nor for DBUG,
+   nor for support of missing_privilege() function.
+*/
+TEST_F(TraceContentTest, NoOptTraceStmt)
+{
+  ASSERT_FALSE(trace.start(false, false, false, false, -1, 1, ULONG_MAX,
+                           all_features));
+  EXPECT_FALSE(trace.is_started());
+  // one substatement:
+  ASSERT_FALSE(trace.start(false, false, false, false, -1, 1, ULONG_MAX,
+                           all_features));
+  EXPECT_FALSE(trace.is_started());
+  // another one deeper nested:
+  ASSERT_FALSE(trace.start(false, false, false, false, -1, 1, ULONG_MAX,
+                           all_features));
+  EXPECT_FALSE(trace.is_started());
+  trace.end();
+  trace.end();
+  trace.end();
+}
+
 }  // namespace
 
 #endif // OPTIMIZER_TRACE

Attachment: [text/bzr-bundle] bzr/guilhem.bichot@oracle.com-20110504205328-nh1fn0ujq4y6k9zw.bundle
Thread
bzr commit into mysql-trunk branch (guilhem.bichot:3296) Guilhem Bichot4 May