mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 20:36:16 +01:00 
			
		
		
		
	This patch adds support for SYS_REFCURSOR (a weakly typed cursor)
for both sql_mode=ORACLE and sql_mode=DEFAULT.
Works as a regular stored routine variable, parameter and return value:
- can be passed as an IN parameter to stored functions and procedures
- can be passed as an INOUT and OUT parameter to stored procedures
- can be returned from a stored function
Note, strongly typed REF CURSOR will be added separately.
Note, to maintain dependencies easier, some parts of sql_class.h
and item.h were moved to new header files:
- select_results.h:
  class select_result_sink
  class select_result
  class select_result_interceptor
- sp_cursor.h:
  class sp_cursor_statistics
  class sp_cursor
- sp_rcontext_handler.h
  class Sp_rcontext_handler and its descendants
The implementation consists of the following parts:
- A new class sp_cursor_array deriving from Dynamic_array
- A new class Statement_rcontext which contains data shared
  between sub-statements of a compound statement.
  It has a member m_statement_cursors of the sp_cursor_array data type,
  as well as open cursor counter. THD inherits from Statement_rcontext.
- A new data type handler Type_handler_sys_refcursor in plugins/type_cursor/
  It is designed to store uint16 references -
  positions of the cursor in THD::m_statement_cursors.
- Type_handler_sys_refcursor suppresses some derived numeric features.
  When a SYS_REFCURSOR variable is used as an integer an error is raised.
- A new abstract class sp_instr_fetch_cursor. It's needed to share
  the common code between "OPEN cur" (for static cursors) and
  "OPER cur FOR stmt" (for SYS_REFCURSORs).
- New sp_instr classes:
  * sp_instr_copen_by_ref      - OPEN sys_ref_curor FOR stmt;
  * sp_instr_cfetch_by_ref     - FETCH sys_ref_cursor INTO targets;
  * sp_instr_cclose_by_ref     - CLOSE sys_ref_cursor;
  * sp_instr_destruct_variable - to destruct SYS_REFCURSOR variables when
                                 the execution goes out of the BEGIN..END block
                                 where SYS_REFCURSOR variables are declared.
- New methods in LEX:
  * sp_open_cursor_for_stmt   - handles "OPEN sys_ref_cursor FOR stmt".
  * sp_add_instr_fetch_cursor - "FETCH cur INTO targets" for both
                                static cursors and SYS_REFCURSORs.
  * sp_close - handles "CLOSE cur" both for static cursors and SYS_REFCURSORs.
- Changes in cursor functions to handle both static cursors and SYS_REFCURSORs:
  * Item_func_cursor_isopen
  * Item_func_cursor_found
  * Item_func_cursor_notfound
  * Item_func_cursor_rowcount
- A new system variable @@max_open_cursors - to limit the number
  of cursors (static and SYS_REFCURSORs) opened at the same time.
  Its allowed range is [0-65536], with 50 by default.
- A new virtual method Type_handler::can_return_bool() telling
  if calling item->val_bool() is allowed for Items of this data type,
  or if otherwise the "Illegal parameter for operation" error should be raised
  at fix_fields() time.
- New methods in Sp_rcontext_handler:
  * get_cursor()
  * get_cursor_by_ref()
- A new class Sp_rcontext_handler_statement to handle top level statement
  wide cursors which are shared by all substatements.
- A new virtual method expr_event_handler() in classes Item and Field.
  It's needed to close (and make available for a new OPEN)
  unused THD::m_statement_cursors elements which do not have any references
  any more. It can happen in various moments in time, e.g.
  * after evaluation parameters of an SQL routine
  * after assigning a cursor expression into a SYS_REFCURSOR variable
  * when leaving a BEGIN..END block with SYS_REFCURSOR variables
  * after setting OUT/INOUT routine actual parameters from formal
    parameters.
		
	
			
		
			
				
	
	
		
			1694 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1694 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
   Copyright (c) 2002, 2011, Oracle and/or its affiliates.
 | 
						|
   Copyright (c) 2010, 2021, MariaDB
 | 
						|
 | 
						|
   This program is free software; you can redistribute it and/or modify
 | 
						|
   it under the terms of the GNU General Public License as published by
 | 
						|
   the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful,
 | 
						|
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
   GNU General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program; if not, write to the Free Software
 | 
						|
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Derived tables
 | 
						|
  These were introduced by Sinisa <sinisa@mysql.com>
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
#include "mariadb.h"                         /* NO_EMBEDDED_ACCESS_CHECKS */
 | 
						|
#include "sql_priv.h"
 | 
						|
#include "unireg.h"
 | 
						|
#include "sql_select.h"
 | 
						|
#include "derived_handler.h"
 | 
						|
#include "sql_base.h"
 | 
						|
#include "sql_view.h"                         // check_duplicate_names
 | 
						|
#include "sql_acl.h"                          // SELECT_ACL
 | 
						|
#include "sql_class.h"
 | 
						|
#include "sql_derived.h"
 | 
						|
#include "sql_cte.h"
 | 
						|
#include "my_json_writer.h"
 | 
						|
#include "opt_trace.h"
 | 
						|
 | 
						|
typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived);
 | 
						|
 | 
						|
static bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived);
 | 
						|
static bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived);
 | 
						|
static bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived);
 | 
						|
static bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived);
 | 
						|
static bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived);
 | 
						|
static bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived);
 | 
						|
static bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
 | 
						|
static bool mysql_derived_merge_for_insert(THD *thd, LEX *lex,
 | 
						|
                                           TABLE_LIST *derived);
 | 
						|
 | 
						|
dt_processor processors[]=
 | 
						|
{
 | 
						|
  &mysql_derived_init,
 | 
						|
  &mysql_derived_prepare,
 | 
						|
  &mysql_derived_optimize,
 | 
						|
  &mysql_derived_merge,
 | 
						|
  &mysql_derived_merge_for_insert,
 | 
						|
  &mysql_derived_create,
 | 
						|
  &mysql_derived_fill,
 | 
						|
  &mysql_derived_reinit,
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
  Run specified phases on all derived tables/views in given LEX.
 | 
						|
 | 
						|
  @param lex              LEX for this thread
 | 
						|
  @param phases           phases to run derived tables/views through
 | 
						|
 | 
						|
  @return FALSE  OK
 | 
						|
  @return TRUE   Error
 | 
						|
*/
 | 
						|
bool
 | 
						|
mysql_handle_derived(LEX *lex, uint phases)
 | 
						|
{
 | 
						|
  bool res= FALSE;
 | 
						|
  DBUG_ENTER("mysql_handle_derived");
 | 
						|
  DBUG_PRINT("enter", ("phases: 0x%x", phases));
 | 
						|
  if (!lex->derived_tables)
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
 | 
						|
  lex->thd->derived_tables_processing= TRUE;
 | 
						|
 | 
						|
  for (uint phase= 0; phase < DT_PHASES && !res; phase++)
 | 
						|
  {
 | 
						|
    uint phase_flag= DT_INIT << phase;
 | 
						|
    if (phase_flag > phases)
 | 
						|
      break;
 | 
						|
    if (!(phases & phase_flag))
 | 
						|
      continue;
 | 
						|
 | 
						|
    for (SELECT_LEX *sl= lex->all_selects_list;
 | 
						|
	 sl && !res;
 | 
						|
	 sl= sl->next_select_in_list())
 | 
						|
    {
 | 
						|
      TABLE_LIST *cursor= sl->get_table_list();
 | 
						|
      sl->changed_elements|= TOUCHED_SEL_DERIVED;
 | 
						|
      /*
 | 
						|
        DT_MERGE_FOR_INSERT is not needed for views/derived tables inside
 | 
						|
        subqueries. Views and derived tables of subqueries should be
 | 
						|
        processed normally.
 | 
						|
      */
 | 
						|
      if (phases == DT_MERGE_FOR_INSERT &&
 | 
						|
          cursor && (cursor->top_table()->select_lex !=
 | 
						|
                     lex->first_select_lex()))
 | 
						|
        continue;
 | 
						|
      for (;
 | 
						|
	   cursor && !res;
 | 
						|
	   cursor= cursor->next_local)
 | 
						|
      {
 | 
						|
        if (!cursor->is_view_or_derived() && phases == DT_MERGE_FOR_INSERT)
 | 
						|
          continue;
 | 
						|
        uint8 allowed_phases= (cursor->is_merged_derived() ? DT_PHASES_MERGE :
 | 
						|
                               DT_PHASES_MATERIALIZE | DT_MERGE_FOR_INSERT);
 | 
						|
        /*
 | 
						|
          Skip derived tables to which the phase isn't applicable.
 | 
						|
          TODO: mark derived at the parse time, later set it's type
 | 
						|
          (merged or materialized)
 | 
						|
        */
 | 
						|
        if ((phase_flag != DT_PREPARE && !(allowed_phases & phase_flag)) ||
 | 
						|
            (cursor->merged_for_insert && phase_flag != DT_REINIT &&
 | 
						|
             phase_flag != DT_PREPARE))
 | 
						|
          continue;
 | 
						|
	res= (*processors[phase])(lex->thd, lex, cursor);
 | 
						|
      }
 | 
						|
      if (lex->describe)
 | 
						|
      {
 | 
						|
	/*
 | 
						|
	  Force join->join_tmp creation, because we will use this JOIN
 | 
						|
	  twice for EXPLAIN and we have to have unchanged join for EXPLAINing
 | 
						|
	*/
 | 
						|
	sl->uncacheable|= UNCACHEABLE_EXPLAIN;
 | 
						|
	sl->master_unit()->uncacheable|= UNCACHEABLE_EXPLAIN;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  lex->thd->derived_tables_processing= FALSE;
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Run through phases for the given derived table/view.
 | 
						|
 | 
						|
  @param lex             LEX for this thread
 | 
						|
  @param derived         the derived table to handle
 | 
						|
  @param phase_map       phases to process tables/views through
 | 
						|
 | 
						|
  @details
 | 
						|
 | 
						|
  This function process the derived table (view) 'derived' to performs all
 | 
						|
  actions that are to be done on the table at the phases specified by
 | 
						|
  phase_map. The processing is carried out starting from the actions
 | 
						|
  performed at the earlier phases (those having smaller ordinal numbers).
 | 
						|
 | 
						|
  @note
 | 
						|
  This function runs specified phases of the derived tables handling on the
 | 
						|
  given derived table/view. This function is used in the chain of calls:
 | 
						|
    SELECT_LEX::handle_derived ->
 | 
						|
      TABLE_LIST::handle_derived ->
 | 
						|
        mysql_handle_single_derived
 | 
						|
  This chain of calls implements the bottom-up handling of the derived tables:
 | 
						|
  i.e. most inner derived tables/views are handled first. This order is
 | 
						|
  required for the all phases except the merge and the create steps.
 | 
						|
  For the sake of code simplicity this order is kept for all phases.
 | 
						|
 | 
						|
  @return FALSE ok
 | 
						|
  @return TRUE  error
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
 | 
						|
{
 | 
						|
  bool res= FALSE;
 | 
						|
  uint8 allowed_phases= (derived->is_merged_derived() ? DT_PHASES_MERGE :
 | 
						|
                         DT_PHASES_MATERIALIZE);
 | 
						|
  DBUG_ENTER("mysql_handle_single_derived");
 | 
						|
  DBUG_PRINT("enter", ("phases: 0x%x  allowed: 0x%x  alias: '%s'",
 | 
						|
                       phases, allowed_phases,
 | 
						|
                       (derived->alias.str ? derived->alias.str : "<NULL>")));
 | 
						|
  if (!lex->derived_tables)
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
 | 
						|
  if (derived->select_lex)
 | 
						|
    derived->select_lex->changed_elements|= TOUCHED_SEL_DERIVED;
 | 
						|
  else
 | 
						|
    DBUG_ASSERT(derived->prelocking_placeholder);
 | 
						|
  lex->thd->derived_tables_processing= TRUE;
 | 
						|
 | 
						|
  for (uint phase= 0; phase < DT_PHASES; phase++)
 | 
						|
  {
 | 
						|
    uint phase_flag= DT_INIT << phase;
 | 
						|
    if (phase_flag > phases)
 | 
						|
      break;
 | 
						|
    if (!(phases & phase_flag))
 | 
						|
      continue;
 | 
						|
    /* Skip derived tables to which the phase isn't applicable.  */
 | 
						|
    if (phase_flag != DT_PREPARE &&
 | 
						|
        !(allowed_phases & phase_flag))
 | 
						|
      continue;
 | 
						|
 | 
						|
    if ((res= (*processors[phase])(lex->thd, lex, derived)))
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  lex->thd->derived_tables_processing= FALSE;
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Merge a derived table/view into the embedding select
 | 
						|
 | 
						|
  @param thd     thread handle
 | 
						|
  @param lex     LEX of the embedding query.
 | 
						|
  @param derived reference to the derived table.
 | 
						|
 | 
						|
  @details
 | 
						|
  This function merges the given derived table / view into the parent select
 | 
						|
  construction. Any derived table/reference to view occurred in the FROM
 | 
						|
  clause of the embedding select is represented by a TABLE_LIST structure a
 | 
						|
  pointer to which is passed to the function as in the parameter 'derived'.
 | 
						|
  This structure contains  the number/map, alias, a link to SELECT_LEX of the
 | 
						|
  derived table and other info. If the 'derived' table is used in a nested join
 | 
						|
  then additionally the structure contains a reference to the ON expression
 | 
						|
  for this join.
 | 
						|
 | 
						|
  The merge process results in elimination of the derived table (or the
 | 
						|
  reference to a view) such that:
 | 
						|
    - the FROM list of the derived table/view is wrapped into a nested join
 | 
						|
      after which the nest is added to the FROM list of the embedding select
 | 
						|
    - the WHERE condition of the derived table (view) is ANDed with the ON
 | 
						|
      condition attached to the table.
 | 
						|
 | 
						|
  @note
 | 
						|
  Tables are merged into the leaf_tables list, original derived table is removed
 | 
						|
  from this list also. SELECT_LEX::table_list list is left untouched.
 | 
						|
  Where expression is merged with derived table's on_expr and can be found after
 | 
						|
  the merge through the SELECT_LEX::table_list.
 | 
						|
 | 
						|
  Examples of the derived table/view merge:
 | 
						|
 | 
						|
  Schema:
 | 
						|
  Tables: t1(f1), t2(f2), t3(f3)
 | 
						|
  View v1: SELECT f1 FROM t1 WHERE f1 < 1
 | 
						|
 | 
						|
  Example with a view:
 | 
						|
    Before merge:
 | 
						|
 | 
						|
    The query (Q1): SELECT f1,f2 FROM t2 LEFT JOIN v1 ON f1 = f2
 | 
						|
 | 
						|
       (LEX of the main query)
 | 
						|
                 |
 | 
						|
           (select_lex)
 | 
						|
                 |
 | 
						|
         (FROM table list)
 | 
						|
                 |
 | 
						|
            (join list)= t2, v1
 | 
						|
                             / \
 | 
						|
                            /  (on_expr)= (f1 = f2)
 | 
						|
                            |
 | 
						|
                    (LEX of the v1 view)
 | 
						|
                            |
 | 
						|
                       (select_lex)= SELECT f1 FROM t1 WHERE f1 < 1
 | 
						|
 | 
						|
 | 
						|
    After merge:
 | 
						|
 | 
						|
    The rewritten query Q1 (Q1'):
 | 
						|
      SELECT f1,f2 FROM t2 LEFT JOIN (t1) ON ((f1 = f2) and (f1 < 1))
 | 
						|
 | 
						|
        (LEX of the main query)
 | 
						|
                   |
 | 
						|
             (select_lex)
 | 
						|
                   |
 | 
						|
           (FROM table list)
 | 
						|
                   |
 | 
						|
               (join list)= t2, (t1)
 | 
						|
                                    \
 | 
						|
                                   (on_expr)= (f1 = f2) and (f1 < 1)
 | 
						|
 | 
						|
    In this example table numbers are assigned as follows:
 | 
						|
      (outer select): t2 - 1, v1 - 2
 | 
						|
      (inner select): t1 - 1
 | 
						|
    After the merge table numbers will be:
 | 
						|
      (outer select): t2 - 1, t1 - 2
 | 
						|
 | 
						|
  Example with a derived table:
 | 
						|
    The query Q2:
 | 
						|
      SELECT f1,f2
 | 
						|
       FROM (SELECT f1 FROM t1, t3 WHERE f1=f3 and f1 < 1) tt, t2
 | 
						|
       WHERE f1 = f2
 | 
						|
 | 
						|
    Before merge:
 | 
						|
              (LEX of the main query)
 | 
						|
                        |
 | 
						|
                  (select_lex)
 | 
						|
                  /           \
 | 
						|
       (FROM table list)   (WHERE clause)= (f1 = f2)
 | 
						|
                  |
 | 
						|
           (join list)= tt, t2
 | 
						|
                       / \
 | 
						|
                      /  (on_expr)= (empty)
 | 
						|
                     /
 | 
						|
           (select_lex)= SELECT f1 FROM t1, t3 WHERE f1 = f3 and f1 < 1
 | 
						|
 | 
						|
    After merge:
 | 
						|
 | 
						|
    The rewritten query Q2 (Q2'):
 | 
						|
      SELECT f1,f2
 | 
						|
       FROM (t1, t3) JOIN t2 ON (f1 = f3 and f1 < 1)
 | 
						|
       WHERE f1 = f2
 | 
						|
 | 
						|
              (LEX of the main query)
 | 
						|
                        |
 | 
						|
                  (select_lex)
 | 
						|
                  /           \
 | 
						|
       (FROM table list)   (WHERE clause)= (f1 = f2)
 | 
						|
                 |
 | 
						|
          (join list)= t2, (t1, t3)
 | 
						|
                                   \
 | 
						|
                                 (on_expr)= (f1 = f3 and f1 < 1)
 | 
						|
 | 
						|
  In this example table numbers are assigned as follows:
 | 
						|
    (outer select): tt - 1, t2 - 2
 | 
						|
    (inner select): t1 - 1, t3 - 2
 | 
						|
  After the merge table numbers will be:
 | 
						|
    (outer select): t1 - 1, t2 - 2, t3 - 3
 | 
						|
 | 
						|
  @return FALSE if derived table/view were successfully merged.
 | 
						|
  @return TRUE if an error occur.
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  bool res= FALSE;
 | 
						|
  SELECT_LEX *dt_select= derived->get_single_select();
 | 
						|
  table_map map;
 | 
						|
  uint tablenr;
 | 
						|
  SELECT_LEX *parent_lex= derived->select_lex;
 | 
						|
  Query_arena *arena, backup;
 | 
						|
  DBUG_ENTER("mysql_derived_merge");
 | 
						|
  DBUG_PRINT("enter", ("Alias: '%s'  Unit: %p",
 | 
						|
                       (derived->alias.str ? derived->alias.str : "<NULL>"),
 | 
						|
                       derived->get_unit()));
 | 
						|
  const char *cause= NULL;
 | 
						|
 | 
						|
  if (derived->merged)
 | 
						|
  {
 | 
						|
 | 
						|
    DBUG_PRINT("info", ("Irreversibly merged: exit"));
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  }
 | 
						|
 | 
						|
  if (derived->dt_handler)
 | 
						|
  {
 | 
						|
    derived->change_refs_to_fields();
 | 
						|
    derived->set_materialized_derived();
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  }
 | 
						|
 | 
						|
  arena= thd->activate_stmt_arena_if_needed(&backup);  // For easier test
 | 
						|
 | 
						|
  if (!derived->merged_for_insert || 
 | 
						|
      (derived->is_multitable() && 
 | 
						|
       (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
 | 
						|
        thd->lex->sql_command == SQLCOM_DELETE_MULTI)))
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Check whether there is enough free bits in table map to merge subquery.
 | 
						|
      If not - materialize it. This check isn't cached so when there is a big
 | 
						|
      and small subqueries, and the bigger one can't be merged it wouldn't
 | 
						|
      block the smaller one.
 | 
						|
    */
 | 
						|
    if (parent_lex->get_free_table_map(&map, &tablenr) ||
 | 
						|
       dt_select->leaf_tables.elements + tablenr > MAX_TABLES)
 | 
						|
    {
 | 
						|
      /* There is no enough table bits, fall back to materialization. */
 | 
						|
      cause= "Not enough table bits to merge subquery";
 | 
						|
      goto unconditional_materialization;
 | 
						|
    }
 | 
						|
 | 
						|
    if (dt_select->options & OPTION_SCHEMA_TABLE)
 | 
						|
      parent_lex->options |= OPTION_SCHEMA_TABLE;
 | 
						|
 | 
						|
    if (!derived->get_unit()->prepared)
 | 
						|
    {
 | 
						|
      dt_select->leaf_tables.empty();
 | 
						|
      make_leaves_list(thd, dt_select->leaf_tables, derived, TRUE, 0);
 | 
						|
    } 
 | 
						|
 | 
						|
    derived->nested_join= thd->calloc<NESTED_JOIN>(1);
 | 
						|
    if (!derived->nested_join)
 | 
						|
    {
 | 
						|
      res= TRUE;
 | 
						|
      goto exit_merge;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Merge derived table's subquery in the parent select. */
 | 
						|
    if (parent_lex->merge_subquery(thd, derived, dt_select, tablenr, map))
 | 
						|
    {
 | 
						|
      res= TRUE;
 | 
						|
      goto exit_merge;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
      exclude select lex so it doesn't show up in explain.
 | 
						|
      do this only for derived table as for views this is already done.
 | 
						|
 | 
						|
      From sql_view.cc
 | 
						|
        Add subqueries units to SELECT into which we merging current view.
 | 
						|
        unit(->next)* chain starts with subqueries that are used by this
 | 
						|
        view and continues with subqueries that are used by other views.
 | 
						|
        We must not add any subquery twice (otherwise we'll form a loop),
 | 
						|
        to do this we remember in end_unit the first subquery that has
 | 
						|
        been already added.
 | 
						|
    */
 | 
						|
    derived->get_unit()->exclude_level();
 | 
						|
    if (parent_lex->join) 
 | 
						|
      parent_lex->join->table_count+= dt_select->join->table_count - 1;
 | 
						|
  }
 | 
						|
  derived->merged= TRUE;
 | 
						|
  if (derived->get_unit()->prepared)
 | 
						|
  {
 | 
						|
    Item *expr= derived->on_expr;
 | 
						|
    expr= and_conds(thd, expr, dt_select->join ? dt_select->join->conds : 0);
 | 
						|
    if (expr)
 | 
						|
      expr->top_level_item();
 | 
						|
 | 
						|
    if (expr && (derived->prep_on_expr || expr != derived->on_expr))
 | 
						|
    {
 | 
						|
      derived->on_expr= expr;
 | 
						|
      derived->prep_on_expr= expr->copy_andor_structure(thd);
 | 
						|
    }
 | 
						|
    thd->where= THD_WHERE::ON_CLAUSE;
 | 
						|
    if (derived->on_expr &&
 | 
						|
        derived->on_expr->fix_fields_if_needed_for_bool(thd, &derived->on_expr))
 | 
						|
    {
 | 
						|
      res= TRUE; /* purecov: inspected */
 | 
						|
      goto exit_merge;
 | 
						|
    }
 | 
						|
    // Update used tables cache according to new table map
 | 
						|
    if (derived->on_expr)
 | 
						|
    {
 | 
						|
      derived->on_expr->fix_after_pullout(parent_lex, &derived->on_expr,
 | 
						|
                                          TRUE);
 | 
						|
      fix_list_after_tbl_changes(parent_lex, &derived->nested_join->join_list);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
exit_merge:
 | 
						|
  if (arena)
 | 
						|
    thd->restore_active_arena(arena, &backup);
 | 
						|
  DBUG_RETURN(res);
 | 
						|
 | 
						|
unconditional_materialization:
 | 
						|
 | 
						|
  if (unlikely(thd->trace_started()))
 | 
						|
  {
 | 
						|
    OPT_TRACE_VIEWS_TRANSFORM(thd,trace_wrapper, trace_derived,
 | 
						|
                        derived->is_derived() ? "derived" : "view",
 | 
						|
                        derived->alias.str ? derived->alias.str : "<NULL>",
 | 
						|
                        derived->get_unit()->first_select()->select_number,
 | 
						|
                        "materialized");
 | 
						|
    trace_derived.add("cause", cause);
 | 
						|
  }
 | 
						|
 | 
						|
  derived->change_refs_to_fields();
 | 
						|
  derived->set_materialized_derived();
 | 
						|
  if (!derived->table || !derived->table->is_created())
 | 
						|
    res= mysql_derived_create(thd, lex, derived);
 | 
						|
  goto exit_merge;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Merge a view for the embedding INSERT/UPDATE/DELETE
 | 
						|
 | 
						|
  @param thd     thread handle
 | 
						|
  @param lex     LEX of the embedding query.
 | 
						|
  @param derived reference to the derived table.
 | 
						|
 | 
						|
  @details
 | 
						|
  This function substitutes the derived table for the first table from
 | 
						|
  the query of the derived table thus making it a correct target table for the
 | 
						|
  INSERT/UPDATE/DELETE statements. As this operation is correct only for
 | 
						|
  single table views only, for multi table views this function does nothing.
 | 
						|
  The derived parameter isn't checked to be a view as derived tables aren't
 | 
						|
  allowed for INSERT/UPDATE/DELETE statements.
 | 
						|
 | 
						|
  @return FALSE if derived table/view were successfully merged.
 | 
						|
  @return TRUE if an error occur.
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  DBUG_ENTER("mysql_derived_merge_for_insert");
 | 
						|
  DBUG_PRINT("enter", ("Alias: '%s'  Unit: %p",
 | 
						|
                       (derived->alias.str ? derived->alias.str : "<NULL>"),
 | 
						|
                       derived->get_unit()));
 | 
						|
  DBUG_PRINT("info", ("merged_for_insert: %d  is_materialized_derived: %d  "
 | 
						|
                      "is_multitable: %d  single_table_updatable: %d  "
 | 
						|
                      "merge_underlying_list: %d",
 | 
						|
                      derived->merged_for_insert,
 | 
						|
                      derived->is_materialized_derived(),
 | 
						|
                      derived->is_multitable(),
 | 
						|
                      derived->single_table_updatable(),
 | 
						|
                      derived->merge_underlying_list != 0));
 | 
						|
  if (derived->merged_for_insert)
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  if (derived->init_derived(thd, FALSE))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  if (derived->is_materialized_derived())
 | 
						|
    DBUG_RETURN(mysql_derived_prepare(thd, lex, derived));
 | 
						|
  if ((thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
 | 
						|
       thd->lex->sql_command == SQLCOM_DELETE_MULTI))
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  if (!derived->is_multitable())
 | 
						|
  {
 | 
						|
    if (!derived->single_table_updatable())
 | 
						|
      DBUG_RETURN(derived->create_field_translation(thd));
 | 
						|
    if (derived->merge_underlying_list)
 | 
						|
    {
 | 
						|
      derived->table= derived->merge_underlying_list->table;
 | 
						|
      derived->schema_table= derived->merge_underlying_list->schema_table;
 | 
						|
      derived->merged_for_insert= TRUE;
 | 
						|
      DBUG_ASSERT(derived->table);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Initialize a derived table/view
 | 
						|
 | 
						|
  @param thd	     Thread handle
 | 
						|
  @param lex         LEX of the embedding query.
 | 
						|
  @param derived     reference to the derived table.
 | 
						|
 | 
						|
  @detail
 | 
						|
  Fill info about derived table/view without preparing an
 | 
						|
  underlying select. Such as: create a field translation for views, mark it as
 | 
						|
  a multitable if it is and so on.
 | 
						|
 | 
						|
  @return
 | 
						|
    false  OK
 | 
						|
    true   Error
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  SELECT_LEX_UNIT *unit= derived->get_unit();
 | 
						|
  DBUG_ENTER("mysql_derived_init");
 | 
						|
  DBUG_PRINT("enter", ("Alias: '%s'  Unit: %p",
 | 
						|
                       (derived->alias.str ? derived->alias.str : "<NULL>"),
 | 
						|
                       derived->get_unit()));
 | 
						|
 | 
						|
  // Skip already prepared views/DT
 | 
						|
  if (!unit || unit->prepared)
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
 | 
						|
  bool res= derived->init_derived(thd, TRUE);
 | 
						|
 | 
						|
  derived->updatable= derived->updatable && derived->is_view();
 | 
						|
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Prevent name resolution out of context of ON expressions in derived tables
 | 
						|
 | 
						|
  @param
 | 
						|
    join_list  list of tables used in from list of a derived
 | 
						|
 | 
						|
  @details
 | 
						|
    The function sets the Name_resolution_context::outer_context to NULL
 | 
						|
    for all ON expressions contexts in the given join list. It does this
 | 
						|
    recursively for all nested joins the list contains.
 | 
						|
*/
 | 
						|
 | 
						|
static void nullify_outer_context_for_on_clauses(List<TABLE_LIST>& join_list)
 | 
						|
{
 | 
						|
  List_iterator<TABLE_LIST> li(join_list);
 | 
						|
  while (TABLE_LIST *table= li++)
 | 
						|
  {
 | 
						|
    if (table->on_context)
 | 
						|
      table->on_context->outer_context= NULL;
 | 
						|
    if (table->nested_join)
 | 
						|
      nullify_outer_context_for_on_clauses(table->nested_join->join_list);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Create temporary table structure (but do not fill it)
 | 
						|
 | 
						|
  @param thd	     Thread handle
 | 
						|
  @param lex         LEX of the embedding query.
 | 
						|
  @param derived     reference to the derived table.
 | 
						|
 | 
						|
  @detail
 | 
						|
  Prepare underlying select for a derived table/view. To properly resolve
 | 
						|
  names in the embedding query the TABLE structure is created. Actual table
 | 
						|
  is created later by the mysql_derived_create function.
 | 
						|
 | 
						|
  This function is called before any command containing derived table
 | 
						|
  is executed. All types of derived tables are handled by this function:
 | 
						|
  - Anonymous derived tables, or
 | 
						|
  - Named derived tables (aka views).
 | 
						|
 | 
						|
  The table reference, contained in @c derived, is updated with the
 | 
						|
  fields of a new temporary table.
 | 
						|
  Derived tables are stored in @c thd->derived_tables and closed by
 | 
						|
  close_thread_tables().
 | 
						|
 | 
						|
  This function is part of the procedure that starts in
 | 
						|
  open_and_lock_tables(), a procedure that - among other things - introduces
 | 
						|
  new table and table reference objects (to represent derived tables) that
 | 
						|
  don't exist in the privilege database. This means that normal privilege
 | 
						|
  checking cannot handle them. Hence this function does some extra tricks in
 | 
						|
  order to bypass normal privilege checking, by exploiting the fact that the
 | 
						|
  current state of privilege verification is attached as GRANT_INFO structures
 | 
						|
  on the relevant TABLE and TABLE_REF objects.
 | 
						|
 | 
						|
  For table references, the current state of accrued access is stored inside
 | 
						|
  TABLE_LIST::grant. Hence this function must update the state of fulfilled
 | 
						|
  privileges for the new TABLE_LIST, an operation which is normally performed
 | 
						|
  exclusively by the table and database access checking functions,
 | 
						|
  check_access() and check_grant(), respectively. This modification is done
 | 
						|
  for both views and anonymous derived tables: The @c SELECT privilege is set
 | 
						|
  as fulfilled by the user. However, if a view is referenced and the table
 | 
						|
  reference is queried against directly (see TABLE_LIST::referencing_view),
 | 
						|
  the state of privilege checking (GRANT_INFO struct) is copied as-is to the
 | 
						|
  temporary table.
 | 
						|
 | 
						|
  Only the TABLE structure is created here, actual table is created by the
 | 
						|
  mysql_derived_create function.
 | 
						|
 | 
						|
  @note This function sets @c SELECT_ACL for @c TEMPTABLE views as well as
 | 
						|
  anonymous derived tables, but this is ok since later access checking will
 | 
						|
  distinguish between them.
 | 
						|
 | 
						|
  @see mysql_handle_derived(), mysql_derived_fill(), GRANT_INFO
 | 
						|
 | 
						|
  @return
 | 
						|
    false  OK
 | 
						|
    true   Error
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  SELECT_LEX_UNIT *unit= derived->get_unit();
 | 
						|
  SELECT_LEX *first_select;
 | 
						|
  bool res= FALSE, keep_row_order, distinct;
 | 
						|
  DBUG_ENTER("mysql_derived_prepare");
 | 
						|
  DBUG_PRINT("enter", ("unit: %p  table_list: %p  alias: '%s'",
 | 
						|
                       unit, derived, derived->alias.str));
 | 
						|
  if (!unit)
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
 | 
						|
  first_select= unit->first_select();
 | 
						|
  /*
 | 
						|
    If rownum() is used we have to preserve the insert row order
 | 
						|
    to make GROUP BY and ORDER BY with filesort work.
 | 
						|
 | 
						|
    SELECT * from (SELECT a,b from t1 ORDER BY a)) WHERE rownum <= 0;
 | 
						|
 | 
						|
    When rownum is not used the optimizer will skip the ORDER BY clause.
 | 
						|
    With rownum we have to keep the ORDER BY as this is what is expected.
 | 
						|
    We also have to create any sort result temporary table in such a way
 | 
						|
    that the inserted row order is maintained.
 | 
						|
  */
 | 
						|
  keep_row_order= (thd->lex->with_rownum &&
 | 
						|
                   (first_select->group_list.elements ||
 | 
						|
                    first_select->order_list.elements));
 | 
						|
 | 
						|
  if (derived->is_recursive_with_table() &&
 | 
						|
      !derived->is_with_table_recursive_reference() &&
 | 
						|
      !derived->with->rec_result && derived->with->get_sq_rec_ref())
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      This is a non-recursive reference to a recursive CTE whose
 | 
						|
      specification unit has not been prepared at the regular processing of
 | 
						|
      derived table references. This can happen  only in the case when
 | 
						|
      the specification unit has no recursive references at the top level. 
 | 
						|
      Force the preparation of the specification unit. Use a recursive
 | 
						|
      table reference from a subquery for this.
 | 
						|
    */
 | 
						|
    DBUG_ASSERT(derived->with->get_sq_rec_ref());
 | 
						|
    if (unlikely(mysql_derived_prepare(lex->thd, lex,
 | 
						|
                                       derived->with->get_sq_rec_ref())))
 | 
						|
      DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  if (unit->prepared && derived->is_recursive_with_table() &&
 | 
						|
      !derived->table)
 | 
						|
  {
 | 
						|
    /* 
 | 
						|
      Here 'derived' is either a non-recursive table reference to a recursive
 | 
						|
      with table or a recursive table reference to a recursive table whose
 | 
						|
      specification has been already prepared (a secondary recursive table
 | 
						|
      reference.
 | 
						|
    */ 
 | 
						|
    if (!(derived->derived_result= new (thd->mem_root) select_unit(thd)))
 | 
						|
      DBUG_RETURN(TRUE); // out of memory
 | 
						|
    thd->create_tmp_table_for_derived= TRUE;
 | 
						|
    res= derived->derived_result->create_result_table(
 | 
						|
                                  thd, &unit->types, FALSE,
 | 
						|
                                  (first_select->options |
 | 
						|
                                   thd->variables.option_bits |
 | 
						|
                                   TMP_TABLE_ALL_COLUMNS),
 | 
						|
                                  &derived->alias, FALSE, FALSE,
 | 
						|
                                  keep_row_order, 0);
 | 
						|
    thd->create_tmp_table_for_derived= FALSE;
 | 
						|
 | 
						|
    if (likely(!res) && !derived->table)
 | 
						|
    {
 | 
						|
      derived->derived_result->set_unit(unit);
 | 
						|
      derived->table= derived->derived_result->table;
 | 
						|
      if (derived->is_with_table_recursive_reference())
 | 
						|
      {
 | 
						|
        /* Here 'derived" is a secondary recursive table reference */
 | 
						|
         unit->with_element->rec_result->rec_table_refs.push_back(derived);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    DBUG_ASSERT(derived->table || res);
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  // Skip already prepared views/DT
 | 
						|
  if (unit->prepared ||
 | 
						|
      (derived->merged_for_insert && 
 | 
						|
       !(derived->is_multitable() &&
 | 
						|
         (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
 | 
						|
          thd->lex->sql_command == SQLCOM_DELETE_MULTI))))
 | 
						|
  {
 | 
						|
    /*
 | 
						|
       System versioned tables may still require to get versioning conditions
 | 
						|
       when modifying view (see vers_setup_conds()). Only UPDATE and DELETE are
 | 
						|
       affected because they use WHERE condition.
 | 
						|
    */
 | 
						|
    if (!unit->prepared &&
 | 
						|
        derived->table->versioned() &&
 | 
						|
        derived->merge_underlying_list &&
 | 
						|
        /* choose only those merged views that do not select from other views */
 | 
						|
        !derived->merge_underlying_list->merge_underlying_list)
 | 
						|
    {
 | 
						|
      switch (thd->lex->sql_command)
 | 
						|
      {
 | 
						|
      case SQLCOM_DELETE:
 | 
						|
      case SQLCOM_DELETE_MULTI:
 | 
						|
      case SQLCOM_UPDATE:
 | 
						|
      case SQLCOM_UPDATE_MULTI:
 | 
						|
        if ((res= first_select->vers_setup_conds(thd,
 | 
						|
                                                 derived->merge_underlying_list)))
 | 
						|
          goto exit;
 | 
						|
        if (derived->merge_underlying_list->where)
 | 
						|
        {
 | 
						|
          Query_arena_stmt on_stmt_arena(thd);
 | 
						|
          derived->where= and_items(thd, derived->where,
 | 
						|
                                    derived->merge_underlying_list->where);
 | 
						|
        }
 | 
						|
      default:
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  }
 | 
						|
 | 
						|
  /* prevent name resolving out of derived table */
 | 
						|
  for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select())
 | 
						|
  {
 | 
						|
    // Prevent it for the WHERE clause
 | 
						|
    sl->context.outer_context= 0;
 | 
						|
 | 
						|
    // And for ON clauses, if there are any
 | 
						|
    nullify_outer_context_for_on_clauses(*sl->join_list);
 | 
						|
 | 
						|
    if (!derived->is_with_table_recursive_reference() ||
 | 
						|
        (!derived->with->with_anchor && 
 | 
						|
         !derived->with->is_with_prepared_anchor()))
 | 
						|
    {
 | 
						|
      /* 
 | 
						|
        Prepare underlying views/DT first unless 'derived' is a recursive
 | 
						|
        table reference and either the anchors from the specification of
 | 
						|
        'derived' has been already prepared or there no anchor in this
 | 
						|
        specification
 | 
						|
      */  
 | 
						|
      if ((res= sl->handle_derived(lex, DT_PREPARE)))
 | 
						|
        goto exit;
 | 
						|
    }
 | 
						|
    if (derived->outer_join && sl->first_cond_optimization)
 | 
						|
    {
 | 
						|
      /* Mark that table is part of OUTER JOIN and fields may be NULL */
 | 
						|
      for (TABLE_LIST *cursor= (TABLE_LIST*) sl->table_list.first;
 | 
						|
           cursor;
 | 
						|
           cursor= cursor->next_local)
 | 
						|
        cursor->outer_join|= JOIN_TYPE_OUTER;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  // Prevent it for possible ORDER BY clause
 | 
						|
  if (unit->fake_select_lex)
 | 
						|
    unit->fake_select_lex->context.outer_context= 0;
 | 
						|
 | 
						|
  if (unlikely(thd->trace_started()))
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Add to optimizer trace whether a derived table/view
 | 
						|
      is merged into the parent select or not.
 | 
						|
    */
 | 
						|
    OPT_TRACE_VIEWS_TRANSFORM(thd, trace_wrapper, trace_derived,
 | 
						|
                    derived->is_derived() ? "derived" : "view",
 | 
						|
                    derived->alias.str ? derived->alias.str : "<NULL>",
 | 
						|
                    derived->get_unit()->first_select()->select_number,
 | 
						|
                    derived->is_merged_derived() ? "merged" : "materialized");
 | 
						|
  }
 | 
						|
  /*
 | 
						|
    Above cascade call of prepare is important for PS protocol, but after it
 | 
						|
    is called we can check if we really need prepare for this derived
 | 
						|
  */
 | 
						|
  if (derived->merged)
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("Irreversibly merged: exit"));
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  }
 | 
						|
 | 
						|
  derived->fill_me= FALSE;
 | 
						|
 | 
						|
  if ((!derived->is_with_table_recursive_reference() ||
 | 
						|
       !derived->derived_result) &&
 | 
						|
      !(derived->derived_result= new (thd->mem_root) select_unit(thd)))
 | 
						|
    DBUG_RETURN(TRUE); // out of memory
 | 
						|
 | 
						|
  // st_select_lex_unit::prepare correctly work for single select
 | 
						|
  if ((res= unit->prepare(derived, derived->derived_result, 0)))
 | 
						|
    goto exit;
 | 
						|
  if (derived->with &&
 | 
						|
      (res= derived->with->process_columns_of_derived_unit(thd, unit)))
 | 
						|
    goto exit;
 | 
						|
  if ((res= check_duplicate_names(thd, unit->types, 0)))
 | 
						|
    goto exit;
 | 
						|
 | 
						|
  /*
 | 
						|
    Check whether we can merge this derived table into main select.
 | 
						|
    Depending on the result field translation will or will not
 | 
						|
    be created.
 | 
						|
  */
 | 
						|
  if (!derived->is_with_table_recursive_reference() &&
 | 
						|
      derived->init_derived(thd, FALSE))
 | 
						|
    goto exit;
 | 
						|
 | 
						|
  /*
 | 
						|
    Temp table is created so that it honors if UNION without ALL is to be
 | 
						|
    processed
 | 
						|
 | 
						|
    We pass as 'distinct' parameter in any of the above cases
 | 
						|
 | 
						|
    1) It is an UNION and the last part of an union is distinct (as
 | 
						|
       thus the final temporary table should not contain duplicates).
 | 
						|
    2) It is not an UNION and the unit->distinct flag is set. This is the
 | 
						|
       case for WHERE A IN (...).
 | 
						|
 | 
						|
    Note that the underlying query will also control distinct condition.
 | 
						|
  */
 | 
						|
  thd->create_tmp_table_for_derived= TRUE;
 | 
						|
  distinct= (unit->first_select()->next_select() ?
 | 
						|
             unit->union_distinct && !unit->union_distinct->next_select() :
 | 
						|
             unit->distinct);
 | 
						|
 | 
						|
  if (!(derived->table) &&
 | 
						|
      derived->derived_result->create_result_table(thd, &unit->types,
 | 
						|
                                                   distinct,
 | 
						|
                                                   (first_select->options |
 | 
						|
                                                   thd->variables.option_bits |
 | 
						|
                                                   TMP_TABLE_ALL_COLUMNS),
 | 
						|
                                                   &derived->alias,
 | 
						|
                                                   FALSE, FALSE, keep_row_order,
 | 
						|
                                                   0))
 | 
						|
  { 
 | 
						|
    thd->create_tmp_table_for_derived= FALSE;
 | 
						|
    if (thd->is_error())
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        EOM error, or attempted to a create a table with a Field
 | 
						|
        of a not allowed data type, e.g. SYS_REFCURSOR.
 | 
						|
      */
 | 
						|
      res= true;
 | 
						|
    }
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  thd->create_tmp_table_for_derived= FALSE;
 | 
						|
 | 
						|
  if (!derived->table)
 | 
						|
    derived->table= derived->derived_result->table;
 | 
						|
  DBUG_ASSERT(derived->table);
 | 
						|
  if (derived->is_derived() && derived->is_merged_derived())
 | 
						|
    first_select->mark_as_belong_to_derived(derived);
 | 
						|
 | 
						|
  derived->dt_handler= derived->find_derived_handler(thd);
 | 
						|
 | 
						|
exit:
 | 
						|
  /* Hide "Unknown column" or "Unknown function" error */
 | 
						|
  if (derived->view)
 | 
						|
  {
 | 
						|
    if (thd->is_error() &&
 | 
						|
        (thd->get_stmt_da()->sql_errno() == ER_BAD_FIELD_ERROR ||
 | 
						|
        thd->get_stmt_da()->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
 | 
						|
        thd->get_stmt_da()->sql_errno() == ER_SP_DOES_NOT_EXIST))
 | 
						|
    {
 | 
						|
      thd->clear_error();
 | 
						|
      my_error(ER_VIEW_INVALID, MYF(0), derived->db.str,
 | 
						|
               derived->table_name.str);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    if it is preparation PS only or commands that need only VIEW structure
 | 
						|
    then we do not need real data and we can skip execution (and parameters
 | 
						|
    is not defined, too)
 | 
						|
  */
 | 
						|
  if (res)
 | 
						|
  {
 | 
						|
    if (!derived->is_with_table_recursive_reference())
 | 
						|
    {
 | 
						|
      if (derived->table && derived->table->s->tmp_table)
 | 
						|
        free_tmp_table(thd, derived->table);
 | 
						|
      delete derived->derived_result;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    TABLE *table= derived->table;
 | 
						|
    table->derived_select_number= first_select->select_number;
 | 
						|
    table->s->tmp_table= INTERNAL_TMP_TABLE;
 | 
						|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
 | 
						|
    if (derived->is_view())
 | 
						|
      table->grant= derived->grant;
 | 
						|
    else
 | 
						|
    {
 | 
						|
      DBUG_ASSERT(derived->is_derived());
 | 
						|
      DBUG_ASSERT(derived->is_anonymous_derived_table());
 | 
						|
      table->grant.privilege= SELECT_ACL;
 | 
						|
      derived->grant.privilege= SELECT_ACL;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
    /* Add new temporary table to list of open derived tables */
 | 
						|
    if (!derived->is_with_table_recursive_reference())
 | 
						|
    {
 | 
						|
      table->next= thd->derived_tables;
 | 
						|
      thd->derived_tables= table;
 | 
						|
    }
 | 
						|
 | 
						|
    /* If table is used by a left join, mark that any column may be null */
 | 
						|
    if (derived->outer_join)
 | 
						|
      table->maybe_null= 1;
 | 
						|
  }
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Runs optimize phase for a derived table/view.
 | 
						|
 | 
						|
  @param thd     thread handle
 | 
						|
  @param lex     LEX of the embedding query.
 | 
						|
  @param derived reference to the derived table.
 | 
						|
 | 
						|
  @details
 | 
						|
  Runs optimize phase for given 'derived' derived table/view.
 | 
						|
  If optimizer finds out that it's of the type "SELECT a_constant" then this
 | 
						|
  functions also materializes it.
 | 
						|
 | 
						|
  @return FALSE ok.
 | 
						|
  @return TRUE if an error occur.
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  SELECT_LEX_UNIT *unit= derived->get_unit();
 | 
						|
  SELECT_LEX *first_select= unit->first_select();
 | 
						|
  SELECT_LEX *save_current_select= lex->current_select;
 | 
						|
  bool res= FALSE;
 | 
						|
  DBUG_ENTER("mysql_derived_optimize");
 | 
						|
  DBUG_PRINT("enter", ("Alias: '%s'  Unit: %p",
 | 
						|
                       (derived->alias.str ? derived->alias.str : "<NULL>"),
 | 
						|
                       derived->get_unit()));
 | 
						|
  if (derived->merged)
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("Irreversibly merged: exit"));
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  }
 | 
						|
 | 
						|
  if (derived->is_materialized_derived() && derived->dt_handler)
 | 
						|
  {
 | 
						|
    /* Create an object for execution of the query specifying the table */
 | 
						|
    if (!(derived->pushdown_derived=
 | 
						|
            new (thd->mem_root) Pushdown_derived(derived, derived->dt_handler)))
 | 
						|
      DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  lex->current_select= first_select;
 | 
						|
 | 
						|
  if (unit->is_unit_op())
 | 
						|
  {
 | 
						|
    if (unit->optimized)
 | 
						|
      DBUG_RETURN(FALSE);
 | 
						|
    // optimize union without execution
 | 
						|
    res= unit->optimize();
 | 
						|
  }
 | 
						|
  else if (unit->derived)
 | 
						|
  {
 | 
						|
    if (!derived->is_merged_derived())
 | 
						|
    {
 | 
						|
      JOIN *join= first_select->join;
 | 
						|
      unit->set_limit(unit->global_parameters());
 | 
						|
      if (join &&
 | 
						|
          join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE &&
 | 
						|
          join->with_two_phase_optimization)
 | 
						|
      {
 | 
						|
        if (unit->optimized_2)
 | 
						|
          DBUG_RETURN(FALSE);
 | 
						|
        unit->optimized_2= TRUE;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        if (unit->optimized)
 | 
						|
          DBUG_RETURN(FALSE);        
 | 
						|
	unit->optimized= TRUE;
 | 
						|
        if (!join)
 | 
						|
	{
 | 
						|
          /*
 | 
						|
            This happens when derived is used in SELECT for which
 | 
						|
            zer_result_cause != 0.
 | 
						|
            In this case join is already destroyed.
 | 
						|
	  */
 | 
						|
          DBUG_RETURN(FALSE);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if ((res= join->optimize()))
 | 
						|
        goto err;
 | 
						|
      if (join->table_count == join->const_tables)
 | 
						|
        derived->fill_me= TRUE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /*
 | 
						|
    Materialize derived tables/views of the "SELECT a_constant" type.
 | 
						|
    Such tables should be materialized at the optimization phase for
 | 
						|
    correct constant evaluation.
 | 
						|
  */
 | 
						|
  if (!res && derived->fill_me && !derived->merged_for_insert)
 | 
						|
  {
 | 
						|
    if (derived->is_merged_derived())
 | 
						|
    {
 | 
						|
      derived->change_refs_to_fields();
 | 
						|
      derived->set_materialized_derived();
 | 
						|
    }
 | 
						|
    if ((res= mysql_derived_create(thd, lex, derived)))
 | 
						|
      goto err;
 | 
						|
    if ((res= mysql_derived_fill(thd, lex, derived)))
 | 
						|
      goto err;
 | 
						|
  }
 | 
						|
err:
 | 
						|
  lex->current_select= save_current_select;
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Actually create result table for a materialized derived table/view.
 | 
						|
 | 
						|
  @param thd     thread handle
 | 
						|
  @param lex     LEX of the embedding query.
 | 
						|
  @param derived reference to the derived table.
 | 
						|
 | 
						|
  @details
 | 
						|
  This function actually creates the result table for given 'derived'
 | 
						|
  table/view, but it doesn't fill it.
 | 
						|
  'thd' and 'lex' parameters are not used  by this function.
 | 
						|
 | 
						|
  @return FALSE ok.
 | 
						|
  @return TRUE if an error occur.
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  DBUG_ENTER("mysql_derived_create");
 | 
						|
  DBUG_PRINT("enter", ("Alias: '%s'  Unit: %p",
 | 
						|
                       (derived->alias.str ? derived->alias.str : "<NULL>"),
 | 
						|
                       derived->get_unit()));
 | 
						|
  TABLE *table= derived->table;
 | 
						|
  SELECT_LEX_UNIT *unit= derived->get_unit();
 | 
						|
 | 
						|
  if (table->is_created())
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  select_unit *result= derived->derived_result;
 | 
						|
  if (table->s->db_type() == TMP_ENGINE_HTON)
 | 
						|
  {
 | 
						|
    result->tmp_table_param.keyinfo= table->s->key_info;
 | 
						|
    if (create_internal_tmp_table(table, result->tmp_table_param.keyinfo,
 | 
						|
                                  result->tmp_table_param.start_recinfo,
 | 
						|
                                  &result->tmp_table_param.recinfo,
 | 
						|
                                  (unit->first_select()->options |
 | 
						|
                                   thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS)))
 | 
						|
      DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
  if (open_tmp_table(table))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  table->file->extra(HA_EXTRA_WRITE_CACHE);
 | 
						|
  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void TABLE_LIST::register_as_derived_with_rec_ref(With_element *rec_elem)
 | 
						|
{
 | 
						|
  rec_elem->derived_with_rec_ref.insert(this, &this->next_with_rec_ref);
 | 
						|
  is_derived_with_recursive_reference= true;
 | 
						|
  get_unit()->uncacheable|= UNCACHEABLE_DEPENDENT;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool TABLE_LIST::is_nonrecursive_derived_with_rec_ref()
 | 
						|
{
 | 
						|
  return is_derived_with_recursive_reference;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Fill the recursive with table 
 | 
						|
 | 
						|
  @param thd  The thread handle
 | 
						|
 | 
						|
  @details
 | 
						|
    The method is called only for recursive with tables. 
 | 
						|
    The method executes the recursive part of the specification
 | 
						|
    of this with table until no more rows are added to the table
 | 
						|
    or the number of the performed iteration reaches the allowed
 | 
						|
    maximum. 
 | 
						|
 | 
						|
  @retval
 | 
						|
    false   on success
 | 
						|
    true    on failure 
 | 
						|
*/
 | 
						|
 | 
						|
bool TABLE_LIST::fill_recursive(THD *thd)
 | 
						|
{
 | 
						|
  bool rc= false;
 | 
						|
  st_select_lex_unit *unit= get_unit();
 | 
						|
  rc= with->instantiate_tmp_tables();
 | 
						|
  while (!rc && !with->all_are_stabilized())
 | 
						|
  {
 | 
						|
    if (with->level > thd->variables.max_recursive_iterations)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_QUERY_RESULT_INCOMPLETE,
 | 
						|
                          ER_THD(thd, ER_QUERY_RESULT_INCOMPLETE),
 | 
						|
                          "max_recursive_iterations =",
 | 
						|
                          (ulonglong)thd->variables.max_recursive_iterations);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    with->prepare_for_next_iteration();
 | 
						|
    rc= unit->exec_recursive();
 | 
						|
  }
 | 
						|
  if (!rc)
 | 
						|
  {
 | 
						|
    TABLE *src= with->rec_result->table;
 | 
						|
    rc =src->insert_all_rows_into_tmp_table(thd,
 | 
						|
                                            table,
 | 
						|
                                            &with->rec_result->tmp_table_param,
 | 
						|
                                            true);
 | 
						|
  } 
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Execute subquery of a materialized derived table/view and fill the result
 | 
						|
  table.
 | 
						|
 | 
						|
  @param thd      Thread handle
 | 
						|
  @param lex      LEX for this thread
 | 
						|
  @param derived  reference to the derived table.
 | 
						|
 | 
						|
  @details
 | 
						|
  Execute subquery of given 'derived' table/view and fill the result
 | 
						|
  table. After result table is filled, if this is not the EXPLAIN statement
 | 
						|
  and the table is not specified with a recursion the entire unit / node
 | 
						|
  is deleted. unit is deleted if UNION is used  for derived table and node
 | 
						|
  is deleted is it is a simple SELECT.
 | 
						|
  'lex' is unused and 'thd' is passed as an argument to an underlying function.
 | 
						|
 | 
						|
  @note
 | 
						|
  If you use this function, make sure it's not called at prepare.
 | 
						|
  Due to evaluation of LIMIT clause it can not be used at prepared stage.
 | 
						|
 | 
						|
  @return FALSE  OK
 | 
						|
  @return TRUE   Error
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  Field_iterator_table field_iterator;
 | 
						|
  SELECT_LEX_UNIT *unit= derived->get_unit();
 | 
						|
  bool derived_is_recursive= derived->is_recursive_with_table();
 | 
						|
  bool res= FALSE;
 | 
						|
  DBUG_ENTER("mysql_derived_fill");
 | 
						|
  DBUG_PRINT("enter", ("Alias: '%s'  Unit: %p",
 | 
						|
                       (derived->alias.str ? derived->alias.str : "<NULL>"),
 | 
						|
                       derived->get_unit()));
 | 
						|
 | 
						|
  /*
 | 
						|
    Only fill derived tables once, unless the derived table is dependent in
 | 
						|
    which case we will delete all of its rows and refill it below.
 | 
						|
  */
 | 
						|
  if (unit->executed && !(unit->uncacheable & UNCACHEABLE_DEPENDENT) &&
 | 
						|
      !unit->describe && !derived_is_recursive)
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  /*check that table creation passed without problems. */
 | 
						|
  DBUG_ASSERT(derived->table && derived->table->is_created());
 | 
						|
  select_unit *derived_result= derived->derived_result;
 | 
						|
  SELECT_LEX *save_current_select= lex->current_select;
 | 
						|
 | 
						|
  if (derived->pushdown_derived)
 | 
						|
  {
 | 
						|
    int res;
 | 
						|
    if (unit->executed)
 | 
						|
      DBUG_RETURN(FALSE);
 | 
						|
    /* Execute the query that specifies the derived table by a foreign engine */
 | 
						|
    res= derived->pushdown_derived->execute();
 | 
						|
    unit->executed= true;
 | 
						|
    if (res)
 | 
						|
      DBUG_RETURN(res);
 | 
						|
    goto after_exec;
 | 
						|
  }
 | 
						|
 | 
						|
  if (unit->executed && !derived_is_recursive &&
 | 
						|
      (unit->uncacheable & UNCACHEABLE_DEPENDENT))
 | 
						|
  {
 | 
						|
    if ((res= derived->table->file->ha_delete_all_rows()))
 | 
						|
      goto err;
 | 
						|
    JOIN *join= unit->first_select()->join;
 | 
						|
    join->first_record= false;
 | 
						|
    if (join->zero_result_cause)
 | 
						|
      goto err;
 | 
						|
 | 
						|
    for (uint i= join->top_join_tab_count;
 | 
						|
         i < join->top_join_tab_count + join->aggr_tables;
 | 
						|
         i++)
 | 
						|
    { 
 | 
						|
      if ((res= join->join_tab[i].table->file->ha_delete_all_rows()))
 | 
						|
        goto err;
 | 
						|
    }   
 | 
						|
  }
 | 
						|
  
 | 
						|
  if (derived_is_recursive)
 | 
						|
  {
 | 
						|
    if (derived->is_with_table_recursive_reference())
 | 
						|
    {
 | 
						|
      /* Here only one iteration step is performed */
 | 
						|
      res= unit->exec_recursive();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /* In this case all iteration are performed */
 | 
						|
      res= derived->fill_recursive(thd);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (unit->is_unit_op())
 | 
						|
  {
 | 
						|
    // execute union without clean up
 | 
						|
    res= unit->exec();
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(!unit->executed || (unit->uncacheable & UNCACHEABLE_DEPENDENT));
 | 
						|
    SELECT_LEX *first_select= unit->first_select();
 | 
						|
    unit->set_limit(unit->global_parameters());
 | 
						|
    if (unit->lim.is_unlimited())
 | 
						|
      first_select->options&= ~OPTION_FOUND_ROWS;
 | 
						|
 | 
						|
    lex->current_select= first_select;
 | 
						|
    res= mysql_select(thd,
 | 
						|
                      first_select->table_list.first,
 | 
						|
                      first_select->item_list, first_select->where,
 | 
						|
                      (first_select->order_list.elements+
 | 
						|
                       first_select->group_list.elements),
 | 
						|
                      first_select->order_list.first,
 | 
						|
                      first_select->group_list.first,
 | 
						|
                      first_select->having, (ORDER*) NULL,
 | 
						|
                      (first_select->options |thd->variables.option_bits |
 | 
						|
                       SELECT_NO_UNLOCK),
 | 
						|
                      derived_result, unit, first_select);
 | 
						|
  }
 | 
						|
 | 
						|
 after_exec:
 | 
						|
  if (!res && !derived_is_recursive)
 | 
						|
  {
 | 
						|
    if (derived_result->flush())
 | 
						|
      res= TRUE;
 | 
						|
    unit->executed= TRUE;
 | 
						|
 | 
						|
    if (derived->field_translation)
 | 
						|
    {
 | 
						|
      /* reset translation table to materialized table */
 | 
						|
      field_iterator.set_table(derived->table);
 | 
						|
      for (uint i= 0;
 | 
						|
           !field_iterator.end_of_fields();
 | 
						|
           field_iterator.next(), i= i + 1)
 | 
						|
      {
 | 
						|
        Item *item;
 | 
						|
 | 
						|
        if (!(item= field_iterator.create_item(thd)))
 | 
						|
        {
 | 
						|
          res= TRUE;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        thd->change_item_tree(&derived->field_translation[i].item, item);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
err:
 | 
						|
  if (res || (!derived_is_recursive && !lex->describe && !unit->uncacheable))
 | 
						|
    unit->cleanup();
 | 
						|
  lex->current_select= save_current_select;
 | 
						|
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Re-initialize given derived table/view for the next execution.
 | 
						|
 | 
						|
  @param  thd         thread handle
 | 
						|
  @param  lex         LEX for this thread
 | 
						|
  @param  derived     reference to the derived table.
 | 
						|
 | 
						|
  @details
 | 
						|
  Re-initialize given 'derived' table/view for the next execution.
 | 
						|
  All underlying views/derived tables are recursively reinitialized prior
 | 
						|
  to re-initialization of given derived table.
 | 
						|
  'thd' and 'lex' are passed as arguments to called functions.
 | 
						|
 | 
						|
  @return FALSE  OK
 | 
						|
  @return TRUE   Error
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  DBUG_ENTER("mysql_derived_reinit");
 | 
						|
  DBUG_PRINT("enter", ("Alias: '%s'  Unit: %p",
 | 
						|
                       (derived->alias.str ? derived->alias.str : "<NULL>"),
 | 
						|
                       derived->get_unit()));
 | 
						|
  st_select_lex_unit *unit= derived->get_unit();
 | 
						|
 | 
						|
  if (derived->original_names_source)
 | 
						|
    unit->first_select()->set_item_list_names(derived->original_names);
 | 
						|
 | 
						|
  // reset item names to that saved after wildcard expansion in JOIN::prepare
 | 
						|
  for(st_select_lex *sl= unit->first_select(); sl; sl= sl->next_select())
 | 
						|
    sl->restore_item_list_names();
 | 
						|
 | 
						|
  derived->merged_for_insert= FALSE;
 | 
						|
  unit->unclean();
 | 
						|
  unit->types.empty();
 | 
						|
  /* for derived tables & PS (which can't be reset by Item_subselect) */
 | 
						|
  unit->reinit_exec_mechanism();
 | 
						|
  unit->set_thd(thd);
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  @brief
 | 
						|
    Given condition cond and transformer+argument, try transforming as many
 | 
						|
    conjuncts as possible.
 | 
						|
 | 
						|
  @detail
 | 
						|
    The motivation of this function is to convert the condition that's being
 | 
						|
    pushed into a WHERE clause with derived_field_transformer_for_where or
 | 
						|
    with derived_grouping_field_transformer_for_where.
 | 
						|
    The transformer may fail for some sub-condition, in this case we want to
 | 
						|
    convert the most restrictive part of the condition that can be pushed.
 | 
						|
 | 
						|
    This function only does it for top-level AND: conjuncts that could not be
 | 
						|
    converted are dropped.
 | 
						|
 | 
						|
  @return
 | 
						|
    Converted condition, or NULL if nothing could be converted
 | 
						|
*/
 | 
						|
 | 
						|
Item *transform_condition_or_part(THD *thd,
 | 
						|
                                  Item *cond,
 | 
						|
                                  Item_transformer transformer,
 | 
						|
                                  uchar *arg)
 | 
						|
{
 | 
						|
  if (cond->type() != Item::COND_ITEM ||
 | 
						|
      ((Item_cond*) cond)->functype() != Item_func::COND_AND_FUNC)
 | 
						|
  {
 | 
						|
    Item *new_item= cond->transform(thd, transformer, arg);
 | 
						|
      // Indicate that the condition is not pushable
 | 
						|
    if (!new_item)
 | 
						|
      cond->clear_extraction_flag();
 | 
						|
    return new_item;
 | 
						|
  }
 | 
						|
 | 
						|
  List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
 | 
						|
  Item *item;
 | 
						|
  while ((item=li++))
 | 
						|
  {
 | 
						|
    Item *new_item= item->transform(thd, transformer, arg);
 | 
						|
    if (!new_item)
 | 
						|
    {
 | 
						|
      // Indicate that the condition is not pushable
 | 
						|
      item->clear_extraction_flag();
 | 
						|
      li.remove();
 | 
						|
    }
 | 
						|
    else
 | 
						|
      li.replace(new_item);
 | 
						|
  }
 | 
						|
 | 
						|
  switch (((Item_cond*) cond)->argument_list()->elements)
 | 
						|
  {
 | 
						|
  case 0:
 | 
						|
    return NULL;
 | 
						|
  case 1:
 | 
						|
    return ((Item_cond*) cond)->argument_list()->head();
 | 
						|
  default:
 | 
						|
    return cond;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Extract condition that can be pushed into a derived table/view
 | 
						|
   
 | 
						|
  @param thd       the thread handle
 | 
						|
  @param cond      current condition
 | 
						|
  @param derived   the reference to the derived table/view
 | 
						|
 | 
						|
  @details
 | 
						|
    This function builds the most restrictive condition depending only on
 | 
						|
    the derived table/view (directly or indirectly through equality) that
 | 
						|
    can be extracted from the given condition cond and pushes it into the
 | 
						|
    derived table/view.
 | 
						|
 | 
						|
    Example of the transformation:
 | 
						|
 | 
						|
    SELECT *
 | 
						|
    FROM t1,
 | 
						|
    (
 | 
						|
      SELECT x,MAX(y) AS max_y
 | 
						|
      FROM t2
 | 
						|
      GROUP BY x
 | 
						|
    ) AS d_tab
 | 
						|
    WHERE d_tab.x>1 AND d_tab.max_y<30;
 | 
						|
 | 
						|
    =>
 | 
						|
 | 
						|
    SELECT *
 | 
						|
    FROM t1,
 | 
						|
    (
 | 
						|
      SELECT x,z,MAX(y) AS max_y
 | 
						|
      FROM t2
 | 
						|
      WHERE x>1
 | 
						|
      HAVING max_y<30
 | 
						|
      GROUP BY x
 | 
						|
    ) AS d_tab
 | 
						|
    WHERE d_tab.x>1 AND d_tab.max_y<30;
 | 
						|
 | 
						|
    In details:
 | 
						|
    1. Check what pushable formula can be extracted from cond
 | 
						|
    2. Build a clone PC of the formula that can be extracted
 | 
						|
       (the clone is built only if the extracted formula is a AND subformula
 | 
						|
        of cond or conjunction of such subformulas)
 | 
						|
    Do for every select specifying derived table/view:
 | 
						|
    3. If there is no HAVING clause prepare PC to be conjuncted with
 | 
						|
       WHERE clause of the select. Otherwise do 4-7.
 | 
						|
    4. Check what formula PC_where can be extracted from PC to be pushed
 | 
						|
       into the WHERE clause of the select
 | 
						|
    5. Build PC_where and if PC_where is a conjunct(s) of PC remove it from PC
 | 
						|
       getting PC_having
 | 
						|
    6. Prepare PC_where to be conjuncted with the WHERE clause of the select
 | 
						|
    7. Prepare PC_having to be conjuncted with the HAVING clause of the select
 | 
						|
  @note
 | 
						|
    This method is similar to pushdown_cond_for_in_subquery()
 | 
						|
 | 
						|
  @retval TRUE   if an error occurs
 | 
						|
  @retval FALSE  otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
 | 
						|
{
 | 
						|
  DBUG_ENTER("pushdown_cond_for_derived");
 | 
						|
  if (!cond)
 | 
						|
    DBUG_RETURN(false);
 | 
						|
 | 
						|
  st_select_lex_unit *unit= derived->get_unit();
 | 
						|
  st_select_lex *first_sl= unit->first_select();
 | 
						|
  st_select_lex *sl= first_sl;
 | 
						|
 | 
						|
  if (derived->prohibit_cond_pushdown)
 | 
						|
    DBUG_RETURN(false);
 | 
						|
 | 
						|
  /* Do not push conditions into constant derived */
 | 
						|
  if (unit->executed)
 | 
						|
    DBUG_RETURN(false);
 | 
						|
 | 
						|
  /* Do not push conditions into recursive with tables */
 | 
						|
  if (derived->is_recursive_with_table())
 | 
						|
    DBUG_RETURN(false);
 | 
						|
 | 
						|
  /* Do not push conditions into unit with global ORDER BY ... LIMIT */
 | 
						|
  if (unit->fake_select_lex &&
 | 
						|
      unit->fake_select_lex->limit_params.explicit_limit)
 | 
						|
    DBUG_RETURN(false);
 | 
						|
 | 
						|
  /* Check whether any select of 'unit' allows condition pushdown */
 | 
						|
  bool some_select_allows_cond_pushdown= false;
 | 
						|
  for (; sl; sl= sl->next_select())
 | 
						|
  {
 | 
						|
    if (sl->cond_pushdown_is_allowed())
 | 
						|
    {
 | 
						|
      some_select_allows_cond_pushdown= true;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (!some_select_allows_cond_pushdown)
 | 
						|
    DBUG_RETURN(false);
 | 
						|
 | 
						|
  /* 1. Check what pushable formula can be extracted from cond */
 | 
						|
  Item *extracted_cond;
 | 
						|
  cond->check_pushable_cond(&Item::pushable_cond_checker_for_derived,
 | 
						|
                            (uchar *)(&derived->table->map));
 | 
						|
  /* 2. Build a clone PC of the formula that can be extracted */
 | 
						|
  extracted_cond=
 | 
						|
    cond->build_pushable_cond(thd,
 | 
						|
                              &Item::pushable_equality_checker_for_derived,
 | 
						|
                              ((uchar *)&derived->table->map));
 | 
						|
  if (!extracted_cond)
 | 
						|
  {
 | 
						|
    /* Nothing can be pushed into the derived table */
 | 
						|
    DBUG_RETURN(false);
 | 
						|
  }
 | 
						|
 | 
						|
  st_select_lex *save_curr_select= thd->lex->current_select;
 | 
						|
  for (; sl; sl= sl->next_select())
 | 
						|
  {
 | 
						|
    Item *extracted_cond_copy;
 | 
						|
    if (!sl->cond_pushdown_is_allowed())
 | 
						|
      continue;
 | 
						|
    /*
 | 
						|
      For each select of the unit except the last one
 | 
						|
      create a clone of extracted_cond
 | 
						|
    */
 | 
						|
    extracted_cond_copy= !sl->next_select() ?
 | 
						|
                         extracted_cond :
 | 
						|
                         extracted_cond->build_clone(thd);
 | 
						|
    if (!extracted_cond_copy)
 | 
						|
      continue;
 | 
						|
 | 
						|
    /*
 | 
						|
      Rename the columns of all non-first selects of a union to be compatible
 | 
						|
      by names with the columns of the first select. It will allow to use copies
 | 
						|
      of the same expression pushed into having clauses of different selects.
 | 
						|
    */
 | 
						|
    if (sl != first_sl)
 | 
						|
    {
 | 
						|
      DBUG_ASSERT(sl->item_list.elements == first_sl->item_list.elements);
 | 
						|
      sl->save_item_list_names(thd);
 | 
						|
      List_iterator_fast<Item> it(sl->item_list);
 | 
						|
      List_iterator_fast<Item> nm_it(unit->types);
 | 
						|
      while (Item *item= it++)
 | 
						|
        item->share_name_with(nm_it++);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Collect fields that are used in the GROUP BY of sl */
 | 
						|
    if (sl->have_window_funcs())
 | 
						|
    {
 | 
						|
      if (sl->group_list.first || sl->join->implicit_grouping)
 | 
						|
        continue;
 | 
						|
      ORDER *common_partition_fields=
 | 
						|
         sl->find_common_window_func_partition_fields(thd);
 | 
						|
      if (!common_partition_fields)
 | 
						|
        continue;
 | 
						|
      sl->collect_grouping_fields_for_derived(thd, common_partition_fields);
 | 
						|
    }
 | 
						|
    else
 | 
						|
      sl->collect_grouping_fields_for_derived(thd, sl->group_list.first);
 | 
						|
 | 
						|
    Item *remaining_cond= NULL;
 | 
						|
    /* Do 4-6 */
 | 
						|
    sl->pushdown_cond_into_where_clause(thd, extracted_cond_copy,
 | 
						|
                                    &remaining_cond,
 | 
						|
                                    &Item::derived_field_transformer_for_where,
 | 
						|
                                    (uchar *) sl);
 | 
						|
 | 
						|
    if (!remaining_cond)
 | 
						|
      continue;
 | 
						|
    /*
 | 
						|
       7. Prepare PC_having to be conjuncted with the HAVING clause of
 | 
						|
          the select
 | 
						|
    */
 | 
						|
    remaining_cond=
 | 
						|
      remaining_cond->transform(thd,
 | 
						|
                                &Item::derived_field_transformer_for_having,
 | 
						|
                                (uchar *) sl);
 | 
						|
    if (!remaining_cond)
 | 
						|
      continue;
 | 
						|
 | 
						|
    if (remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor,
 | 
						|
                             0, 0))
 | 
						|
      continue;
 | 
						|
 | 
						|
    mark_or_conds_to_avoid_pushdown(remaining_cond);
 | 
						|
 | 
						|
    sl->cond_pushed_into_having= remaining_cond;
 | 
						|
  }
 | 
						|
  thd->lex->current_select= save_curr_select;
 | 
						|
  DBUG_RETURN(false);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Look for provision of the derived_handler interface by a foreign engine
 | 
						|
 | 
						|
  @param thd   The thread handler
 | 
						|
 | 
						|
  @details
 | 
						|
    The function looks through its tables of the query that specifies this
 | 
						|
    derived table searching for a table whose handlerton owns a
 | 
						|
    create_derived call-back function. If the call of this function returns
 | 
						|
    a derived_handler interface object then the server will push the query
 | 
						|
    specifying the derived table into this engine.
 | 
						|
    This is a responsibility of the create_derived call-back function to
 | 
						|
    check whether the engine can execute the query.
 | 
						|
 | 
						|
  @retval the found derived_handler if the search is successful
 | 
						|
          0  otherwise
 | 
						|
*/
 | 
						|
 | 
						|
derived_handler *TABLE_LIST::find_derived_handler(THD *thd)
 | 
						|
{
 | 
						|
  if (!derived || is_recursive_with_table())
 | 
						|
    return 0;
 | 
						|
  for (SELECT_LEX *sl= derived->first_select(); sl; sl= sl->next_select())
 | 
						|
  {
 | 
						|
    if (!(sl->join))
 | 
						|
      continue;
 | 
						|
    for (TABLE_LIST *tbl= sl->join->tables_list; tbl; tbl= tbl->next_local)
 | 
						|
    {
 | 
						|
      if (!tbl->table)
 | 
						|
	continue;
 | 
						|
      handlerton *ht= tbl->table->file->partition_ht();
 | 
						|
      if (!ht->create_derived)
 | 
						|
        continue;
 | 
						|
      derived_handler *dh= ht->create_derived(thd, this);
 | 
						|
      if (dh)
 | 
						|
      {
 | 
						|
        dh->set_derived(this);
 | 
						|
        return dh;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
TABLE_LIST *TABLE_LIST::get_first_table()
 | 
						|
{
 | 
						|
  for (SELECT_LEX *sl= derived->first_select(); sl; sl= sl->next_select())
 | 
						|
  {
 | 
						|
    if (!(sl->join))
 | 
						|
      continue;
 | 
						|
    for (TABLE_LIST *tbl= sl->join->tables_list; tbl; tbl= tbl->next_local)
 | 
						|
    {
 | 
						|
      if (!tbl->table)
 | 
						|
	continue;
 | 
						|
      return tbl;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 |