mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1752 lines
		
	
	
	
		
			56 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1752 lines
		
	
	
	
		
			56 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
   Copyright (c) 2016, 2017 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-1301  USA */
 | 
						|
 | 
						|
#include "mariadb.h"
 | 
						|
#include "sql_class.h"
 | 
						|
#include "sql_lex.h"
 | 
						|
#include "sql_cte.h"
 | 
						|
#include "sql_view.h"    // for make_valid_column_names
 | 
						|
#include "sql_parse.h"
 | 
						|
#include "sql_select.h"
 | 
						|
#include "sql_show.h"                // append_definer, append_identifier
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Add a new element to this with clause 
 | 
						|
 | 
						|
  @param elem  The with element to add to this with clause
 | 
						|
 | 
						|
  @details
 | 
						|
    The method adds the with element 'elem' to the elements
 | 
						|
    in this with clause. The method reports an error if
 | 
						|
    the number of the added element exceeds the value
 | 
						|
    of the constant max_number_of_elements_in_with_clause.
 | 
						|
 | 
						|
  @retval
 | 
						|
    true    if an error is reported 
 | 
						|
    false   otherwise
 | 
						|
*/
 | 
						|
  
 | 
						|
bool With_clause::add_with_element(With_element *elem)
 | 
						|
{ 
 | 
						|
  if (with_list.elements == max_number_of_elements_in_with_clause)
 | 
						|
  {
 | 
						|
    my_error(ER_TOO_MANY_DEFINITIONS_IN_WITH_CLAUSE, MYF(0));
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  elem->owner= this;
 | 
						|
  elem->number= with_list.elements;
 | 
						|
  elem->spec->with_element= elem;
 | 
						|
  with_list.link_in_list(elem, &elem->next);
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void  st_select_lex_unit::set_with_clause(With_clause *with_cl)
 | 
						|
{ 
 | 
						|
  with_clause= with_cl;
 | 
						|
  if (with_clause)
 | 
						|
    with_clause->set_owner(this);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Check dependencies between tables defined in a list of with clauses
 | 
						|
 
 | 
						|
  @param 
 | 
						|
    with_clauses_list  Pointer to the first clause in the list
 | 
						|
 | 
						|
  @details
 | 
						|
    For each with clause from the given list the procedure finds all
 | 
						|
    dependencies between tables defined in the clause by calling the
 | 
						|
    method With_clause::checked_dependencies.
 | 
						|
    Additionally, based on the info collected by this method the procedure
 | 
						|
    finds anchors for each recursive definition and moves them at the head
 | 
						|
    of the definition.
 | 
						|
 | 
						|
  @retval
 | 
						|
    false   on success
 | 
						|
    true    on failure
 | 
						|
*/
 | 
						|
 | 
						|
bool LEX::check_dependencies_in_with_clauses()
 | 
						|
{
 | 
						|
  for (With_clause *with_clause= with_clauses_list;
 | 
						|
       with_clause;
 | 
						|
       with_clause= with_clause->next_with_clause)
 | 
						|
  {
 | 
						|
    if (with_clause->check_dependencies())
 | 
						|
      return true;
 | 
						|
    if (with_clause->check_anchors())
 | 
						|
      return true;
 | 
						|
    with_clause->move_anchors_ahead();
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Resolve table references to CTE from a sub-chain of table references
 | 
						|
 | 
						|
  @param tables      Points to the beginning of the sub-chain
 | 
						|
  @param tables_last Points to the address with the sub-chain barrier
 | 
						|
  @param excl_spec   Ignore the definition with this spec
 | 
						|
 | 
						|
  @details
 | 
						|
    The method resolves tables references to CTE from the chain of
 | 
						|
    table references specified by the parameters 'tables' and 'tables_last'.
 | 
						|
    It resolves the references against the CTE definition occurred in a query
 | 
						|
    or the specification of a CTE whose parsing tree is represented by
 | 
						|
    this LEX structure. The method is always called right after the process
 | 
						|
    of parsing the query or of the specification of a CTE has been finished,
 | 
						|
    thus the chain of table references used in the parsed fragment has been
 | 
						|
    already built. It is assumed that parameters of the method specify a
 | 
						|
    a sub-chain of this chain.
 | 
						|
    If a table reference can be potentially a table reference to a CTE and it
 | 
						|
    has not been resolved yet then the method tries to find the definition
 | 
						|
    of the CTE against which the reference can be resolved. If it succeeds
 | 
						|
    it sets the field TABLE_LIST::with to point to the found definition.
 | 
						|
    It also sets the field TABLE_LIST::derived to point to the specification
 | 
						|
    of the found CTE and sets TABLE::db.str to empty_c_string. This will
 | 
						|
    allow to handle this table reference like a reference to a derived handle.
 | 
						|
    If another table reference has been already resolved against this CTE
 | 
						|
    and this CTE is not recursive then a clone of the CTE specification is
 | 
						|
    constructed using the function With_element::clone_parsed_spec() and
 | 
						|
    TABLE_LIST::derived is set to point to this clone rather than to the
 | 
						|
    original specification.
 | 
						|
    If the method does not find a matched CTE definition in the parsed fragment
 | 
						|
    then in the case when the flag this->only_cte_resolution is set to true
 | 
						|
    it just moves to the resolution of the next table reference from the
 | 
						|
    specified sub-chain while in the case when this->only_cte_resolution is set
 | 
						|
    to false the method additionally sets an mdl request for this table
 | 
						|
    reference.
 | 
						|
 | 
						|
  @notes
 | 
						|
    The flag this->only_cte_resolution is set to true in the cases when
 | 
						|
    the failure to resolve a table reference as a CTE reference within
 | 
						|
    the fragment associated with this LEX structure does not imply that
 | 
						|
    this table reference cannot be resolved as such at all.
 | 
						|
 | 
						|
  @retval false  On success: no errors reported, no memory allocations failed
 | 
						|
  @retval true   Otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
 | 
						|
                                    TABLE_LIST **tables_last,
 | 
						|
                                    st_select_lex_unit *excl_spec)
 | 
						|
{
 | 
						|
  With_element *with_elem= 0;
 | 
						|
 | 
						|
  for (TABLE_LIST *tbl= tables; tbl != *tables_last; tbl= tbl->next_global)
 | 
						|
  {
 | 
						|
    if (tbl->derived)
 | 
						|
      continue;
 | 
						|
    if (!tbl->db.str && !tbl->with)
 | 
						|
      tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl,
 | 
						|
                                                                 excl_spec);
 | 
						|
    if (!tbl->with)    // no CTE matches table reference tbl
 | 
						|
    {
 | 
						|
      if (only_cte_resolution)
 | 
						|
        continue;
 | 
						|
      if (!tbl->db.str)   // no database specified in table reference tbl
 | 
						|
      {
 | 
						|
        if (!thd->db.str) // no default database is set
 | 
						|
        {
 | 
						|
          my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
        if (copy_db_to(&tbl->db))
 | 
						|
          return true;
 | 
						|
        if (!(tbl->table_options & TL_OPTION_ALIAS))
 | 
						|
          MDL_REQUEST_INIT(&tbl->mdl_request, MDL_key::TABLE,
 | 
						|
                           tbl->db.str, tbl->table_name.str,
 | 
						|
                           tbl->mdl_type, MDL_TRANSACTION);
 | 
						|
        tbl->mdl_request.set_type((tbl->lock_type >= TL_WRITE_ALLOW_WRITE) ?
 | 
						|
                                   MDL_SHARED_WRITE : MDL_SHARED_READ);
 | 
						|
      }
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    with_elem= tbl->with;
 | 
						|
    if (tbl->is_recursive_with_table() &&
 | 
						|
        !tbl->is_with_table_recursive_reference())
 | 
						|
    {
 | 
						|
      tbl->with->rec_outer_references++;
 | 
						|
      while ((with_elem= with_elem->get_next_mutually_recursive()) !=
 | 
						|
             tbl->with)
 | 
						|
	with_elem->rec_outer_references++;
 | 
						|
    }
 | 
						|
    if (!with_elem->is_used_in_query || with_elem->is_recursive)
 | 
						|
    {
 | 
						|
      tbl->derived= with_elem->spec;
 | 
						|
      if (tbl->derived != tbl->select_lex->master_unit() &&
 | 
						|
          !with_elem->is_recursive &&
 | 
						|
          !tbl->is_with_table_recursive_reference())
 | 
						|
      {
 | 
						|
        tbl->derived->move_as_slave(tbl->select_lex);
 | 
						|
      }
 | 
						|
      with_elem->is_used_in_query= true;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (!(tbl->derived= tbl->with->clone_parsed_spec(thd->lex, tbl)))
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    tbl->db.str= empty_c_string;
 | 
						|
    tbl->db.length= 0;
 | 
						|
    tbl->schema_table= 0;
 | 
						|
    if (tbl->derived)
 | 
						|
    {
 | 
						|
      tbl->derived->first_select()->set_linkage(DERIVED_TABLE_TYPE);
 | 
						|
      tbl->select_lex->add_statistics(tbl->derived);
 | 
						|
    }
 | 
						|
    if (tbl->with->is_recursive && tbl->is_with_table_recursive_reference())
 | 
						|
      continue;
 | 
						|
    with_elem->inc_references();
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Find out dependencies between CTEs, resolve references to them
 | 
						|
 | 
						|
  @details
 | 
						|
    The function can be called in two modes. With this->with_cte_resolution
 | 
						|
    set to false the function only finds out all dependencies between CTEs
 | 
						|
    used in a query expression with a WITH clause whose parsing has been
 | 
						|
    just finished. Based on these dependencies recursive CTEs are detected.
 | 
						|
    If this->with_cte_resolution is set to true the function additionally
 | 
						|
    resolves all references to CTE occurred in this query expression.
 | 
						|
 | 
						|
  @retval
 | 
						|
    true   on failure
 | 
						|
    false  on success
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
LEX::check_cte_dependencies_and_resolve_references()
 | 
						|
{
 | 
						|
  if (check_dependencies_in_with_clauses())
 | 
						|
    return true;
 | 
						|
  if (!with_cte_resolution)
 | 
						|
    return false;
 | 
						|
  if (resolve_references_to_cte(query_tables, query_tables_last, NULL))
 | 
						|
    return true;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Check dependencies between tables defined in this with clause
 | 
						|
 | 
						|
 @details
 | 
						|
    The method performs the following for this with clause:
 | 
						|
    - checks that there are no definitions of the tables with the same name
 | 
						|
    - for each table T defined in this with clause looks for the tables
 | 
						|
      from the same with clause that are used in the query that specifies T
 | 
						|
      and set the dependencies of T on these tables in a bitmap. 
 | 
						|
    - builds the transitive closure of the above direct dependencies
 | 
						|
      to find out all recursive definitions.
 | 
						|
 | 
						|
  @retval
 | 
						|
    true    if an error is reported 
 | 
						|
    false   otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool With_clause::check_dependencies()
 | 
						|
{
 | 
						|
  if (dependencies_are_checked)
 | 
						|
    return false;
 | 
						|
  /* 
 | 
						|
    Look for for definitions with the same query name.
 | 
						|
    When found report an error and return true immediately.
 | 
						|
    For each table T defined in this with clause look for all other tables
 | 
						|
    from the same with clause that are used in the specification of T.
 | 
						|
    For each such table set the dependency bit in the dependency map of
 | 
						|
    the with element for T.
 | 
						|
  */
 | 
						|
  for (With_element *with_elem= with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    for (With_element *elem= with_list.first;
 | 
						|
         elem != with_elem;
 | 
						|
         elem= elem->next)
 | 
						|
    {
 | 
						|
      if (lex_string_cmp(system_charset_info, with_elem->get_name(),
 | 
						|
                         elem->get_name()) == 0)
 | 
						|
      {
 | 
						|
        my_error(ER_DUP_QUERY_NAME, MYF(0),
 | 
						|
                 with_elem->get_name_str());
 | 
						|
	return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (with_elem->check_dependencies_in_spec())
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  /* Build the transitive closure of the direct dependencies found above */
 | 
						|
  for (With_element *with_elem= with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
    with_elem->derived_dep_map= with_elem->base_dep_map;
 | 
						|
  for (With_element *with_elem= with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    table_map with_elem_map=  with_elem->get_elem_map();
 | 
						|
    for (With_element *elem= with_list.first; elem; elem= elem->next)
 | 
						|
    {
 | 
						|
      if (elem->derived_dep_map & with_elem_map)
 | 
						|
        elem->derived_dep_map |= with_elem->derived_dep_map;
 | 
						|
    }   
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Mark those elements where tables are defined with direct or indirect
 | 
						|
    recursion.
 | 
						|
  */ 
 | 
						|
  for (With_element *with_elem= with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    if (with_elem->derived_dep_map & with_elem->get_elem_map())
 | 
						|
      with_elem->is_recursive= true;
 | 
						|
  }   
 | 
						|
	
 | 
						|
  dependencies_are_checked= true;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  This structure describes an element of the stack of embedded units.
 | 
						|
  The stack is used when looking for a definition of a table in
 | 
						|
  with clauses. The definition can be found only in the scopes
 | 
						|
  of the with clauses attached to the units from the stack.
 | 
						|
  The with clauses are looked through from starting from the top
 | 
						|
  element of the stack. 
 | 
						|
*/
 | 
						|
 | 
						|
struct st_unit_ctxt_elem
 | 
						|
{
 | 
						|
  st_unit_ctxt_elem *prev;   // the previous element of the stack
 | 
						|
  st_select_lex_unit *unit;   
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Find the dependencies of this element on its siblings in its specification
 | 
						|
 | 
						|
  @details
 | 
						|
    For each table reference ref(T) from the FROM list of every select sl 
 | 
						|
    immediately contained in the specification query of this element this
 | 
						|
    method searches for the definition of T in the the with clause which
 | 
						|
    this element belongs to. If such definition is found then the dependency
 | 
						|
    on it is set in sl->with_dep and in this->base_dep_map.  
 | 
						|
*/
 | 
						|
 | 
						|
bool With_element::check_dependencies_in_spec()
 | 
						|
{ 
 | 
						|
  for (st_select_lex *sl=  spec->first_select(); sl; sl= sl->next_select())
 | 
						|
  {
 | 
						|
    if (owner->with_recursive)
 | 
						|
    {
 | 
						|
      st_unit_ctxt_elem ctxt0= {NULL, owner->owner};
 | 
						|
      st_unit_ctxt_elem ctxt1= {&ctxt0, spec};
 | 
						|
      check_dependencies_in_select(sl, &ctxt1, false, &sl->with_dep);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      st_unit_ctxt_elem ctxt= {NULL, spec};
 | 
						|
      check_dependencies_in_select(sl, &ctxt, false, &sl->with_dep);
 | 
						|
    }
 | 
						|
    base_dep_map|= sl->with_dep;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Search for the definition of a table among the elements of this with clause
 | 
						|
 
 | 
						|
  @param table    The reference to the table that is looked for
 | 
						|
  @param barrier  The barrier with element for the search
 | 
						|
  @param excl_spec Ignore the definition with this spec
 | 
						|
 | 
						|
  @details
 | 
						|
    The function looks through the elements of this with clause trying to find
 | 
						|
    the definition of the given table. When it encounters the element with
 | 
						|
    the same query name as the table's name it returns this element. If no
 | 
						|
    such definitions are found the function returns NULL.
 | 
						|
 | 
						|
  @retval
 | 
						|
    found with element if the search succeeded
 | 
						|
    NULL - otherwise
 | 
						|
*/    
 | 
						|
 | 
						|
With_element *With_clause::find_table_def(TABLE_LIST *table,
 | 
						|
                                          With_element *barrier,
 | 
						|
                                          st_select_lex_unit *excl_spec)
 | 
						|
{
 | 
						|
  for (With_element *with_elem= with_list.first; 
 | 
						|
       with_elem != barrier;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    if (excl_spec && with_elem->spec == excl_spec)
 | 
						|
      continue;
 | 
						|
    if (my_strcasecmp(system_charset_info, with_elem->get_name_str(),
 | 
						|
                      table->table_name.str) == 0 &&
 | 
						|
        !table->is_fqtn)
 | 
						|
    {
 | 
						|
      table->set_derived();
 | 
						|
      with_elem->referenced= true;
 | 
						|
      return with_elem;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Search for the definition of a table in with clauses
 | 
						|
 
 | 
						|
  @param tbl     The reference to the table that is looked for
 | 
						|
  @param ctxt    The context describing in what with clauses of the upper
 | 
						|
                 levels the table has to be searched for.
 | 
						|
 | 
						|
  @details
 | 
						|
    The function looks for the definition of the table tbl in the definitions
 | 
						|
    of the with clauses from the upper levels specified by the parameter ctxt.
 | 
						|
    When it encounters the element with the same query name as the table's name
 | 
						|
    it returns this element. If no such definitions are found the function
 | 
						|
    returns NULL.
 | 
						|
 | 
						|
  @retval
 | 
						|
    found with element if the search succeeded
 | 
						|
    NULL - otherwise
 | 
						|
*/    
 | 
						|
 | 
						|
With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
 | 
						|
                                             st_unit_ctxt_elem *ctxt)
 | 
						|
{
 | 
						|
  With_element *found= 0;
 | 
						|
  st_select_lex_unit *top_unit= 0;
 | 
						|
  for (st_unit_ctxt_elem *unit_ctxt_elem= ctxt;
 | 
						|
       unit_ctxt_elem;
 | 
						|
       unit_ctxt_elem= unit_ctxt_elem->prev)
 | 
						|
  {
 | 
						|
    st_select_lex_unit *unit= unit_ctxt_elem->unit;
 | 
						|
    With_clause *with_clause= unit->with_clause;
 | 
						|
    if (with_clause)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        If the reference to tbl that has to be resolved belongs to
 | 
						|
        the FROM clause of a descendant of top_unit->with_element
 | 
						|
        and this with element belongs to with_clause then this
 | 
						|
        element must be used as the barrier for the search in the
 | 
						|
        the list of CTEs from with_clause unless the clause contains
 | 
						|
        RECURSIVE.
 | 
						|
      */
 | 
						|
      With_element *barrier= 0;
 | 
						|
      if (top_unit && !with_clause->with_recursive &&
 | 
						|
          top_unit->with_element &&
 | 
						|
          top_unit->with_element->get_owner() == with_clause)
 | 
						|
        barrier= top_unit->with_element;
 | 
						|
      found= with_clause->find_table_def(tbl, barrier, NULL);
 | 
						|
      if (found)
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    top_unit= unit;
 | 
						|
  }
 | 
						|
  return found;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Find the dependencies of this element on its siblings in a select
 | 
						|
 
 | 
						|
  @param  sl       The select where to look for the dependencies
 | 
						|
  @param  ctxt     The structure specifying the scope of the definitions
 | 
						|
                   of the with elements of the upper levels
 | 
						|
  @param  in_sbq   if true mark dependencies found in subqueries in 
 | 
						|
                   this->sq_dep_map
 | 
						|
  @param  dep_map  IN/OUT The bit where to mark the found dependencies
 | 
						|
 | 
						|
  @details
 | 
						|
    For each table reference ref(T) from the FROM list of the select sl
 | 
						|
    the method searches in with clauses for the definition of the table T.
 | 
						|
    If the found definition belongs to the same with clause as this with
 | 
						|
    element then the method set dependency on T in the in/out parameter
 | 
						|
    dep_map, add if required - in this->sq_dep_map.
 | 
						|
    The parameter ctxt describes the proper context for the search 
 | 
						|
    of the definition of T.      
 | 
						|
*/
 | 
						|
 | 
						|
void With_element::check_dependencies_in_select(st_select_lex *sl,
 | 
						|
                                                st_unit_ctxt_elem *ctxt,
 | 
						|
                                                bool in_subq,
 | 
						|
                                                table_map *dep_map)
 | 
						|
{
 | 
						|
  bool is_spec_select= sl->get_with_element() == this;
 | 
						|
 | 
						|
  for (TABLE_LIST *tbl= sl->table_list.first; tbl; tbl= tbl->next_local)
 | 
						|
  {
 | 
						|
    if (tbl->with || tbl->derived || tbl->nested_join)
 | 
						|
      continue;
 | 
						|
    tbl->with_internal_reference_map= 0;
 | 
						|
    /*
 | 
						|
      Look first for the definition of tbl in the with clause to which
 | 
						|
      this with element belongs. If such definition is not found there
 | 
						|
      look in the with clauses of the upper levels via the context
 | 
						|
      chain of embedding with elements.
 | 
						|
      If the definition of tbl is found somewhere in with clauses
 | 
						|
      then tbl->with is set to point to this definition.
 | 
						|
    */
 | 
						|
    if (is_spec_select)
 | 
						|
    {
 | 
						|
      With_clause *with_clause= sl->master_unit()->with_clause;
 | 
						|
      if (with_clause)
 | 
						|
        tbl->with= with_clause->find_table_def(tbl, NULL, NULL);
 | 
						|
      if (!tbl->with)
 | 
						|
        tbl->with= owner->find_table_def(tbl,
 | 
						|
                                         owner->with_recursive ? NULL : this,
 | 
						|
                                         NULL);
 | 
						|
    }
 | 
						|
    if (!tbl->with)
 | 
						|
      tbl->with= find_table_def_in_with_clauses(tbl, ctxt);
 | 
						|
 | 
						|
    if (tbl->with && tbl->with->owner== this->owner)
 | 
						|
    {   
 | 
						|
      /* 
 | 
						|
        The found definition T of tbl belongs to the same
 | 
						|
        with clause as this with element. In this case:
 | 
						|
        - set the dependence on T in the bitmap dep_map
 | 
						|
        - set tbl->with_internal_reference_map with
 | 
						|
          the bitmap for this definition
 | 
						|
        - set the dependence on T in the bitmap this->sq_dep_map
 | 
						|
          if needed 
 | 
						|
      */     
 | 
						|
      *dep_map|= tbl->with->get_elem_map();
 | 
						|
      tbl->with_internal_reference_map= get_elem_map();
 | 
						|
      if (in_subq)
 | 
						|
        sq_dep_map|= tbl->with->get_elem_map();
 | 
						|
      else
 | 
						|
        top_level_dep_map|= tbl->with->get_elem_map();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /* Now look for the dependencies in the subqueries of sl */
 | 
						|
  st_select_lex_unit *inner_unit= sl->first_inner_unit();
 | 
						|
  for (; inner_unit; inner_unit= inner_unit->next_unit())
 | 
						|
  {
 | 
						|
    check_dependencies_in_unit(inner_unit, ctxt, in_subq, dep_map);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Find a recursive reference to this with element in subqueries of a select
 | 
						|
 
 | 
						|
  @param  sel      The select in whose subqueries the reference
 | 
						|
                   to be looked for
 | 
						|
 | 
						|
  @details
 | 
						|
    The function looks for a recursive reference to this with element in
 | 
						|
    subqueries of select sl. When the first such reference is found
 | 
						|
    it is returned as the result.
 | 
						|
    The function assumes that the identification of all CTE references
 | 
						|
    has been performed earlier.
 | 
						|
 | 
						|
  @retval
 | 
						|
    Pointer to the found recursive reference if the search succeeded
 | 
						|
    NULL - otherwise 
 | 
						|
*/
 | 
						|
 | 
						|
TABLE_LIST *With_element::find_first_sq_rec_ref_in_select(st_select_lex *sel)
 | 
						|
{
 | 
						|
  TABLE_LIST *rec_ref= NULL;
 | 
						|
  st_select_lex_unit *inner_unit= sel->first_inner_unit();
 | 
						|
  for (; inner_unit; inner_unit= inner_unit->next_unit())
 | 
						|
  {
 | 
						|
    st_select_lex *sl= inner_unit->first_select();
 | 
						|
    for (; sl; sl= sl->next_select())
 | 
						|
    {
 | 
						|
      for (TABLE_LIST *tbl= sl->table_list.first; tbl; tbl= tbl->next_local)
 | 
						|
      {
 | 
						|
        if (tbl->derived || tbl->nested_join)
 | 
						|
          continue;
 | 
						|
        if (tbl->with && tbl->with->owner== this->owner &&
 | 
						|
            (tbl->with_internal_reference_map & mutually_recursive))
 | 
						|
	{
 | 
						|
	  rec_ref= tbl;
 | 
						|
          return rec_ref;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if ((rec_ref= find_first_sq_rec_ref_in_select(sl)))
 | 
						|
        return rec_ref;
 | 
						|
    } 
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Find the dependencies of this element on its siblings in a unit
 | 
						|
 
 | 
						|
  @param  unit     The unit where to look for the dependencies
 | 
						|
  @param  ctxt     The structure specifying the scope of the definitions
 | 
						|
                   of the with elements of the upper levels
 | 
						|
  @param  in_sbq   if true mark dependencies found in subqueries in 
 | 
						|
                   this->sq_dep_map
 | 
						|
  @param  dep_map  IN/OUT The bit where to mark the found dependencies
 | 
						|
 | 
						|
  @details
 | 
						|
    This method searches in the unit 'unit' for the the references in FROM
 | 
						|
    lists of all selects contained in this unit and in the with clause
 | 
						|
    attached to this unit that refer to definitions of tables from the
 | 
						|
    same with clause as this element.
 | 
						|
    If such definitions are found then the dependencies on them are
 | 
						|
    set in the in/out parameter dep_map and optionally in this->sq_dep_map.  
 | 
						|
    The parameter ctxt describes the proper context for the search.      
 | 
						|
*/
 | 
						|
 | 
						|
void With_element::check_dependencies_in_unit(st_select_lex_unit *unit,
 | 
						|
                                              st_unit_ctxt_elem *ctxt,
 | 
						|
                                              bool in_subq,
 | 
						|
                                              table_map *dep_map)
 | 
						|
{
 | 
						|
  st_unit_ctxt_elem unit_ctxt_elem= {ctxt, unit};
 | 
						|
  if (unit->with_clause)
 | 
						|
  {
 | 
						|
    (void) unit->with_clause->check_dependencies();
 | 
						|
    check_dependencies_in_with_clause(unit->with_clause, &unit_ctxt_elem,
 | 
						|
                                      in_subq, dep_map);
 | 
						|
  }
 | 
						|
  in_subq |= unit->item != NULL;
 | 
						|
  st_select_lex *sl= unit->first_select();
 | 
						|
  for (; sl; sl= sl->next_select())
 | 
						|
  {
 | 
						|
    check_dependencies_in_select(sl, &unit_ctxt_elem, in_subq, dep_map);
 | 
						|
  }
 | 
						|
  if ((sl= unit->fake_select_lex))
 | 
						|
    check_dependencies_in_select(sl, &unit_ctxt_elem, in_subq, dep_map);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Find the dependencies of this element on its siblings in a with clause
 | 
						|
 
 | 
						|
  @param  witt_clause  The with clause where to look for the dependencies
 | 
						|
  @param  ctxt         The structure specifying the scope of the definitions
 | 
						|
                       of the with elements of the upper levels
 | 
						|
  @param  in_sbq       if true mark dependencies found in subqueries in 
 | 
						|
                       this->sq_dep_map
 | 
						|
  @param  dep_map      IN/OUT The bit where to mark the found dependencies
 | 
						|
 | 
						|
  @details
 | 
						|
    This method searches in the with_clause for the the references in FROM
 | 
						|
    lists of all selects contained in the specifications of the with elements
 | 
						|
    from this with_clause that refer to definitions of tables from the
 | 
						|
    same with clause as this element.
 | 
						|
    If such definitions are found then the dependencies on them are
 | 
						|
    set in the in/out parameter dep_map and optionally in this->sq_dep_map.  
 | 
						|
    The parameter ctxt describes the proper context for the search.      
 | 
						|
*/
 | 
						|
 | 
						|
void 
 | 
						|
With_element::check_dependencies_in_with_clause(With_clause *with_clause,
 | 
						|
                                                st_unit_ctxt_elem *ctxt,
 | 
						|
                                                bool in_subq,
 | 
						|
                                                table_map *dep_map)
 | 
						|
{
 | 
						|
  for (With_element *with_elem= with_clause->with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    check_dependencies_in_unit(with_elem->spec, ctxt, in_subq, dep_map);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Find mutually recursive with elements and check that they have ancors
 | 
						|
 
 | 
						|
  @details
 | 
						|
    This method performs the following:
 | 
						|
    - for each recursive with element finds all mutually recursive with it
 | 
						|
    - links each group of mutually recursive with elements into a ring chain
 | 
						|
    - checks that every group of mutually recursive with elements contains
 | 
						|
      at least one anchor
 | 
						|
    - checks that after removing any with element with anchor the remaining
 | 
						|
      with elements mutually recursive with the removed one are not recursive
 | 
						|
      anymore
 | 
						|
 | 
						|
  @retval
 | 
						|
    true    if an error is reported 
 | 
						|
    false   otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool With_clause::check_anchors()
 | 
						|
{
 | 
						|
  for (With_element *with_elem= with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    if (!with_elem->is_recursive)
 | 
						|
      continue;
 | 
						|
 | 
						|
  /* 
 | 
						|
    It with_elem is recursive with element find all elements mutually recursive
 | 
						|
    with it (any recursive element is mutually recursive with itself). Mark all
 | 
						|
    these elements in the bitmap->mutually_recursive. Also link all these 
 | 
						|
    elements into a ring chain.
 | 
						|
  */
 | 
						|
    if (!with_elem->next_mutually_recursive)
 | 
						|
    {
 | 
						|
      With_element *last_mutually_recursive= with_elem;
 | 
						|
      table_map with_elem_dep= with_elem->derived_dep_map;
 | 
						|
      table_map with_elem_map= with_elem->get_elem_map();
 | 
						|
      for (With_element *elem= with_elem; elem; elem= elem->next)
 | 
						|
      {
 | 
						|
        if (!elem->is_recursive)
 | 
						|
          continue;
 | 
						|
 | 
						|
        if (elem == with_elem ||
 | 
						|
	    ((elem->derived_dep_map & with_elem_map) &&
 | 
						|
	     (with_elem_dep & elem->get_elem_map())))
 | 
						|
	{        
 | 
						|
          elem->next_mutually_recursive= with_elem;
 | 
						|
          last_mutually_recursive->next_mutually_recursive= elem;
 | 
						|
          last_mutually_recursive= elem;
 | 
						|
	  with_elem->mutually_recursive|= elem->get_elem_map();
 | 
						|
	}
 | 
						|
      }
 | 
						|
      for (With_element *elem= with_elem->next_mutually_recursive;
 | 
						|
           elem != with_elem; 
 | 
						|
           elem=  elem->next_mutually_recursive)
 | 
						|
	elem->mutually_recursive= with_elem->mutually_recursive;
 | 
						|
    }
 | 
						|
 
 | 
						|
    /*
 | 
						|
      For each select from the specification of 'with_elem' check whether
 | 
						|
      it is an anchor i.e. does not depend on any with elements mutually
 | 
						|
      recursive with 'with_elem".
 | 
						|
    */
 | 
						|
    for (st_select_lex *sl= with_elem->spec->first_select();
 | 
						|
         sl;
 | 
						|
         sl= sl->next_select())
 | 
						|
    {
 | 
						|
      if (with_elem->is_anchor(sl))
 | 
						|
      {
 | 
						|
        with_elem->with_anchor= true;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* 
 | 
						|
    Check that for any group of mutually recursive with elements
 | 
						|
    - there is at least one anchor
 | 
						|
    - after removing any with element with anchor the remaining with elements
 | 
						|
      mutually recursive with the removed one are not recursive anymore
 | 
						|
  */ 
 | 
						|
  for (With_element *with_elem= with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    if (!with_elem->is_recursive)
 | 
						|
      continue;
 | 
						|
    
 | 
						|
    if (!with_elem->with_anchor)
 | 
						|
    {
 | 
						|
      /* 
 | 
						|
        Check that the other with elements mutually recursive with 'with_elem' 
 | 
						|
        contain at least one anchor.
 | 
						|
      */
 | 
						|
      With_element *elem= with_elem;
 | 
						|
      while ((elem= elem->get_next_mutually_recursive()) != with_elem)
 | 
						|
      {
 | 
						|
        if (elem->with_anchor)
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      if (elem == with_elem)
 | 
						|
      {
 | 
						|
        my_error(ER_RECURSIVE_WITHOUT_ANCHORS, MYF(0),
 | 
						|
        with_elem->get_name_str());
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /* 'with_elem' is a with element with an anchor */
 | 
						|
      With_element *elem= with_elem;
 | 
						|
      /* 
 | 
						|
        For the other with elements mutually recursive with 'with_elem'
 | 
						|
        set dependency bits between those elements in the field work_dep_map
 | 
						|
        and build transitive closure of these dependencies
 | 
						|
      */         
 | 
						|
      while ((elem= elem->get_next_mutually_recursive()) != with_elem)
 | 
						|
	elem->work_dep_map= elem->base_dep_map & elem->mutually_recursive;
 | 
						|
      elem= with_elem;
 | 
						|
      while ((elem= elem->get_next_mutually_recursive()) != with_elem)
 | 
						|
      {
 | 
						|
        table_map elem_map= elem->get_elem_map();
 | 
						|
        With_element *el= with_elem;
 | 
						|
        while ((el= el->get_next_mutually_recursive()) != with_elem)
 | 
						|
	{
 | 
						|
          if (el->work_dep_map & elem_map)
 | 
						|
	    el->work_dep_map|= elem->work_dep_map;          
 | 
						|
        }
 | 
						|
      }
 | 
						|
      /* If the transitive closure displays any cycle report an arror */
 | 
						|
      elem= with_elem;
 | 
						|
      while ((elem= elem->get_next_mutually_recursive()) != with_elem)
 | 
						|
      {
 | 
						|
        if (elem->work_dep_map & elem->get_elem_map())
 | 
						|
	{
 | 
						|
          my_error(ER_UNACCEPTABLE_MUTUAL_RECURSION, MYF(0),
 | 
						|
          with_elem->get_name_str());
 | 
						|
          return true;
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Move anchors at the beginning of the specifications for with elements
 | 
						|
 
 | 
						|
  @details
 | 
						|
    This method moves anchors at the beginning of the specifications for
 | 
						|
    all recursive with elements.
 | 
						|
*/
 | 
						|
 | 
						|
void With_clause::move_anchors_ahead()
 | 
						|
{
 | 
						|
  for (With_element *with_elem= with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    if (with_elem->is_recursive)
 | 
						|
     with_elem->move_anchors_ahead();
 | 
						|
  }
 | 
						|
}
 | 
						|
	
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Move anchors at the beginning of the specification of this with element
 | 
						|
 
 | 
						|
  @details
 | 
						|
    If the specification of this with element contains anchors the method
 | 
						|
    moves them at the very beginning of the specification.
 | 
						|
    Additionally for the other selects of the specification if none of them
 | 
						|
    contains a recursive reference to this with element or a mutually recursive
 | 
						|
    one the method looks for the first such reference in the first recursive 
 | 
						|
    select and set a pointer to it in this->sq_rec_ref.   
 | 
						|
*/
 | 
						|
 | 
						|
void With_element::move_anchors_ahead()
 | 
						|
{
 | 
						|
  st_select_lex *next_sl;
 | 
						|
  st_select_lex *new_pos= spec->first_select();
 | 
						|
  new_pos->set_linkage(UNION_TYPE);
 | 
						|
  for (st_select_lex *sl= new_pos; sl; sl= next_sl)
 | 
						|
  {
 | 
						|
    next_sl= sl->next_select(); 
 | 
						|
    if (is_anchor(sl))
 | 
						|
    {
 | 
						|
      sl->move_node(new_pos);
 | 
						|
      if (new_pos == spec->first_select())
 | 
						|
      {
 | 
						|
        enum sub_select_type type= new_pos->get_linkage();
 | 
						|
        new_pos->set_linkage(sl->get_linkage());
 | 
						|
        sl->set_linkage(type);
 | 
						|
        new_pos->with_all_modifier= sl->with_all_modifier;
 | 
						|
        sl->with_all_modifier= false;
 | 
						|
      }
 | 
						|
      new_pos= sl->next_select();
 | 
						|
    }
 | 
						|
    else if (!sq_rec_ref && no_rec_ref_on_top_level())
 | 
						|
    {
 | 
						|
      sq_rec_ref= find_first_sq_rec_ref_in_select(sl);
 | 
						|
      DBUG_ASSERT(sq_rec_ref != NULL);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  first_recursive= new_pos;
 | 
						|
  spec->first_select()->set_linkage(DERIVED_TABLE_TYPE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Perform context analysis for all unreferenced tables defined in with clause
 | 
						|
 | 
						|
  @param thd   The context of the statement containing this with clause
 | 
						|
 | 
						|
  @details
 | 
						|
    For each unreferenced table T defined in this with clause the method
 | 
						|
    calls the method With_element::prepare_unreferenced that performs
 | 
						|
    context analysis of the element with the definition of T.
 | 
						|
 | 
						|
  @retval
 | 
						|
    false   If context analysis does not report any error
 | 
						|
    true    Otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool With_clause::prepare_unreferenced_elements(THD *thd)
 | 
						|
{
 | 
						|
  for (With_element *with_elem= with_list.first; 
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    if ((with_elem->is_hanging_recursive() || !with_elem->is_referenced()) &&
 | 
						|
        with_elem->prepare_unreferenced(thd))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Save the specification of the given with table as a string
 | 
						|
 | 
						|
  @param thd         The context of the statement containing this with element
 | 
						|
  @param spec_start  The beginning of the specification in the input string
 | 
						|
  @param spec_end    The end of the specification in the input string
 | 
						|
  @param spec_offset The offset of the specification in the input string
 | 
						|
 | 
						|
  @details
 | 
						|
    The method creates for a string copy of the specification used in this
 | 
						|
    element. The method is called when the element is parsed. The copy may be
 | 
						|
    used to create clones of the specification whenever they are needed.
 | 
						|
 | 
						|
  @retval
 | 
						|
    false   on success
 | 
						|
    true    on failure
 | 
						|
*/
 | 
						|
  
 | 
						|
bool With_element::set_unparsed_spec(THD *thd,
 | 
						|
                                     const char *spec_start,
 | 
						|
                                     const char *spec_end,
 | 
						|
                                     my_ptrdiff_t spec_offset)
 | 
						|
{
 | 
						|
  stmt_prepare_mode= thd->m_parser_state->m_lip.stmt_prepare_mode;
 | 
						|
  unparsed_spec.length= spec_end - spec_start;
 | 
						|
 | 
						|
  if (stmt_prepare_mode || !thd->lex->sphead)
 | 
						|
    unparsed_spec.str= spec_start;
 | 
						|
  else
 | 
						|
    unparsed_spec.str= thd->strmake(spec_start, unparsed_spec.length);
 | 
						|
  unparsed_spec_offset= spec_offset;
 | 
						|
 | 
						|
  if (!unparsed_spec.str)
 | 
						|
  {
 | 
						|
    my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 
 | 
						|
             static_cast<int>(unparsed_spec.length));
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
   Create a clone of the specification for the given with table
 | 
						|
    
 | 
						|
  @param old_lex    The LEX structure created for the query or CTE specification
 | 
						|
                    where this With_element is defined
 | 
						|
  @param with_table The reference to the table defined in this element for which
 | 
						|
                     the clone is created.
 | 
						|
 | 
						|
  @details
 | 
						|
    The method creates a clone of the specification used in this element.
 | 
						|
    The clone is created for the given reference to the table defined by
 | 
						|
    this element.
 | 
						|
    The clone is created when the string with the specification saved in
 | 
						|
    unparsed_spec is fed into the parser as an input string. The parsing
 | 
						|
    this string a unit object representing the specification is built.
 | 
						|
    A chain of all table references occurred in the specification is also
 | 
						|
    formed.
 | 
						|
    The method includes the new unit and its sub-unit into hierarchy of
 | 
						|
    the units of the main query. I also insert the constructed chain of the 
 | 
						|
    table references into the chain of all table references of the main query.
 | 
						|
    The method resolves all references to CTE in the clone.
 | 
						|
 | 
						|
  @note
 | 
						|
    Clones is created only for not first references to tables defined in
 | 
						|
    the with clause. They are necessary for merged specifications because
 | 
						|
    the optimizer handles any such specification as independent on the others.
 | 
						|
    When a table defined in the with clause is materialized in a temporary table
 | 
						|
    one could do without specification clones. However in this case they
 | 
						|
    are created as well, because currently different table references to a
 | 
						|
    the same temporary table cannot share the same definition structure.
 | 
						|
 | 
						|
  @retval
 | 
						|
     pointer to the built clone if succeeds
 | 
						|
     NULL - otherwise
 | 
						|
*/
 | 
						|
 | 
						|
st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex,
 | 
						|
                                                    TABLE_LIST *with_table)
 | 
						|
{
 | 
						|
  THD *thd= old_lex->thd;
 | 
						|
  LEX *lex;
 | 
						|
  st_select_lex_unit *res= NULL;
 | 
						|
 | 
						|
  if (!(lex= (LEX*) new(thd->mem_root) st_lex_local))
 | 
						|
    return res;
 | 
						|
  thd->lex= lex;
 | 
						|
 | 
						|
  bool parse_status= false;
 | 
						|
  st_select_lex *with_select;
 | 
						|
  st_select_lex *last_clone_select;
 | 
						|
 | 
						|
  char save_end= unparsed_spec.str[unparsed_spec.length];
 | 
						|
  ((char*) &unparsed_spec.str[unparsed_spec.length])[0]= '\0';
 | 
						|
 | 
						|
  lex_start(thd);
 | 
						|
  lex->clone_spec_offset= unparsed_spec_offset;
 | 
						|
  lex->with_cte_resolution= true;
 | 
						|
  /*
 | 
						|
    There's no need to add SPs/SFs referenced in the clone to the global
 | 
						|
    list of the SPs/SFs used in the query as they were added when the first
 | 
						|
    reference to the cloned CTE was parsed. Yet the recursive call of the
 | 
						|
    parser must to know that they were already included into the list.
 | 
						|
  */
 | 
						|
  lex->sroutines= old_lex->sroutines;
 | 
						|
  lex->sroutines_list_own_last= old_lex->sroutines_list_own_last;
 | 
						|
  lex->sroutines_list_own_elements= old_lex->sroutines_list_own_elements;
 | 
						|
 | 
						|
  /*
 | 
						|
    The specification of a CTE is to be parsed as a regular query.
 | 
						|
    At the very end of the parsing query the function
 | 
						|
    check_cte_dependencies_and_resolve_references() will be called.
 | 
						|
    It will check the dependencies between CTEs that are defined
 | 
						|
    within the query and will resolve CTE references in this query.
 | 
						|
    If a table reference is not resolved as a CTE reference within
 | 
						|
    this query it still can be resolved as a reference to a CTE defined
 | 
						|
    in the same clause as the CTE whose specification is to be parsed
 | 
						|
    or defined in an embedding CTE definition.
 | 
						|
 | 
						|
    Example:
 | 
						|
      with
 | 
						|
      cte1 as ( ... ),
 | 
						|
      cte2 as ([WITH ...] select ... from cte1 ...)
 | 
						|
      select ... from cte2 as r, ..., cte2 as s ...
 | 
						|
 | 
						|
    Here the specification of cte2 has be cloned for table reference
 | 
						|
    with alias s1. The specification contains a reference to cte1
 | 
						|
    that is defined outside this specification. If the reference to
 | 
						|
    cte1 cannot be resolved within the specification of cte2 it's
 | 
						|
    not necessarily has to be a reference to a non-CTE table. That's
 | 
						|
    why the flag lex->only_cte_resolution has to be set to true
 | 
						|
    before parsing of the specification of cte2 invoked by this
 | 
						|
    function starts. Otherwise an mdl_lock would be requested for s
 | 
						|
    and this would not be correct.
 | 
						|
  */
 | 
						|
 | 
						|
  lex->only_cte_resolution= true;
 | 
						|
 | 
						|
  lex->stmt_lex= old_lex->stmt_lex ? old_lex->stmt_lex : old_lex;
 | 
						|
 | 
						|
  parse_status= thd->sql_parser(old_lex, lex,
 | 
						|
                                (char*) unparsed_spec.str,
 | 
						|
                                (unsigned int)unparsed_spec.length,
 | 
						|
                                stmt_prepare_mode);
 | 
						|
 | 
						|
  ((char*) &unparsed_spec.str[unparsed_spec.length])[0]= save_end;
 | 
						|
  with_select= lex->unit.first_select();
 | 
						|
 | 
						|
  if (parse_status)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  /*
 | 
						|
    The unit of the specification that just has been parsed is included
 | 
						|
    as a slave of the select that contained in its from list the table
 | 
						|
    reference for which the unit has been created.
 | 
						|
  */
 | 
						|
  lex->unit.include_down(with_table->select_lex);
 | 
						|
  lex->unit.set_slave(with_select);
 | 
						|
  lex->unit.cloned_from= spec;
 | 
						|
 | 
						|
  /*
 | 
						|
    Now all references to the CTE defined outside of the cloned specification
 | 
						|
    has to be resolved. Additionally if old_lex->only_cte_resolution == false
 | 
						|
    for the table references that has not been resolved requests for mdl_locks
 | 
						|
    has to be set.
 | 
						|
  */
 | 
						|
  lex->only_cte_resolution= old_lex->only_cte_resolution;
 | 
						|
  if (lex->resolve_references_to_cte(lex->query_tables,
 | 
						|
                                     lex->query_tables_last,
 | 
						|
                                     spec))
 | 
						|
  {
 | 
						|
    res= NULL;
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    The global chain of TABLE_LIST objects created for the specification that
 | 
						|
    just has been parsed is added to such chain that contains the reference
 | 
						|
    to the CTE whose specification is parsed right after the TABLE_LIST object
 | 
						|
    created for the reference.
 | 
						|
  */
 | 
						|
  if (lex->query_tables)
 | 
						|
  {
 | 
						|
    head->tables_pos.set_start_pos(&with_table->next_global);
 | 
						|
    head->tables_pos.set_end_pos(lex->query_tables_last);
 | 
						|
    TABLE_LIST *next_tbl= with_table->next_global;
 | 
						|
    if (next_tbl)
 | 
						|
    {
 | 
						|
      *(lex->query_tables->prev_global= next_tbl->prev_global)=
 | 
						|
        lex->query_tables;
 | 
						|
      *(next_tbl->prev_global= lex->query_tables_last)= next_tbl;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      *(lex->query_tables->prev_global= old_lex->query_tables_last)=
 | 
						|
        lex->query_tables;
 | 
						|
      old_lex->query_tables_last= lex->query_tables_last;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  old_lex->sroutines_list_own_last= lex->sroutines_list_own_last;
 | 
						|
  old_lex->sroutines_list_own_elements= lex->sroutines_list_own_elements;
 | 
						|
  res= &lex->unit;
 | 
						|
  res->with_element= this;
 | 
						|
  
 | 
						|
  last_clone_select= lex->all_selects_list;
 | 
						|
  while (last_clone_select->next_select_in_list())
 | 
						|
    last_clone_select= last_clone_select->next_select_in_list();
 | 
						|
  old_lex->all_selects_list=
 | 
						|
    (st_select_lex*) (lex->all_selects_list->
 | 
						|
                     insert_chain_before(
 | 
						|
                       (st_select_lex_node **) &(old_lex->all_selects_list),
 | 
						|
                       last_clone_select));
 | 
						|
 | 
						|
 lex->sphead= NULL;    // in order not to delete lex->sphead
 | 
						|
  lex_end(lex);
 | 
						|
err:
 | 
						|
  thd->lex= old_lex;
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Rename columns of the unit derived from the spec of this with element
 | 
						|
  @param thd        The context of the statement containing the with element
 | 
						|
  @param unit       The specification of the with element or its clone
 | 
						|
 | 
						|
  @details
 | 
						|
    The method assumes that the parameter unit is either specification itself
 | 
						|
    of this with element or a clone of this specification. The looks through
 | 
						|
    the column list in this with element. It reports an error if the cardinality
 | 
						|
    of this list differs from the cardinality of select lists in 'unit'.
 | 
						|
    Otherwise it renames the columns  of the first select list and sets the flag
 | 
						|
    unit->column_list_is_processed to true preventing renaming columns for the
 | 
						|
    second time.
 | 
						|
 | 
						|
  @retval
 | 
						|
    true   if an error was reported 
 | 
						|
    false  otherwise
 | 
						|
*/    
 | 
						|
 | 
						|
bool
 | 
						|
With_element::process_columns_of_derived_unit(THD *thd,
 | 
						|
                                              st_select_lex_unit *unit)
 | 
						|
{
 | 
						|
  if (unit->columns_are_renamed)
 | 
						|
    return false;
 | 
						|
 | 
						|
  st_select_lex *select= unit->first_select();
 | 
						|
 | 
						|
  if (column_list.elements)  //  The column list is optional
 | 
						|
  {
 | 
						|
    List_iterator_fast<Item> it(select->item_list);
 | 
						|
    List_iterator_fast<Lex_ident_sys> nm(column_list);
 | 
						|
    Item *item;
 | 
						|
    LEX_CSTRING *name;
 | 
						|
 | 
						|
    if (column_list.elements != select->item_list.elements)
 | 
						|
    {
 | 
						|
      my_error(ER_WITH_COL_WRONG_LIST, MYF(0));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
 | 
						|
    Query_arena *arena, backup;
 | 
						|
    arena= thd->activate_stmt_arena_if_needed(&backup);
 | 
						|
 | 
						|
    /* Rename the columns of the first select in the unit */
 | 
						|
    while ((item= it++, name= nm++))
 | 
						|
    {
 | 
						|
      lex_string_set(&item->name, name->str);
 | 
						|
      item->base_flags|= item_base_t::IS_EXPLICIT_NAME;
 | 
						|
    }
 | 
						|
 | 
						|
    if (arena)
 | 
						|
      thd->restore_active_arena(arena, &backup);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    make_valid_column_names(thd, select->item_list);
 | 
						|
 | 
						|
  if (cycle_list)
 | 
						|
  {
 | 
						|
    List_iterator_fast<Item> it(select->item_list);
 | 
						|
    List_iterator_fast<Lex_ident_sys> nm(*cycle_list);
 | 
						|
    List_iterator_fast<Lex_ident_sys> nm_check(*cycle_list);
 | 
						|
    DBUG_ASSERT(cycle_list->elements != 0);
 | 
						|
    while (LEX_CSTRING *name= nm++)
 | 
						|
    {
 | 
						|
      Item *item;
 | 
						|
      /*
 | 
						|
        Check for uniqueness of each element in the cycle list:
 | 
						|
        It's sufficient to check that there is no duplicate of 'name'
 | 
						|
        among the elements that precede it.
 | 
						|
      */
 | 
						|
      LEX_CSTRING *check;
 | 
						|
      nm_check.rewind();
 | 
						|
      while ((check= nm_check++) && check != name)
 | 
						|
      {
 | 
						|
        if (check->length == name->length &&
 | 
						|
            strncmp(check->str, name->str, name->length) == 0)
 | 
						|
        {
 | 
						|
          my_error(ER_DUP_FIELDNAME, MYF(0), check->str);
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      /* Check that 'name' is the name of a column of the processed CTE */
 | 
						|
      while ((item= it++) &&
 | 
						|
             (item->name.length != name->length ||
 | 
						|
              strncmp(item->name.str, name->str, name->length) != 0));
 | 
						|
      if (item == NULL)
 | 
						|
      {
 | 
						|
        my_error(ER_BAD_FIELD_ERROR, MYF(0), name->str, "CYCLE clause");
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      item->base_flags|= item_base_t::IS_IN_WITH_CYCLE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  unit->columns_are_renamed= true;
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Perform context analysis the definition of an unreferenced table
 | 
						|
     
 | 
						|
  @param thd        The context of the statement containing this with element
 | 
						|
 | 
						|
  @details
 | 
						|
    The method assumes that this with element contains the definition
 | 
						|
    of a table that is not used anywhere. In this case one has to check
 | 
						|
    that context conditions are met. 
 | 
						|
 | 
						|
  @retval
 | 
						|
    true   if an error was reported 
 | 
						|
    false  otherwise
 | 
						|
*/    
 | 
						|
 | 
						|
bool With_element::prepare_unreferenced(THD *thd)
 | 
						|
{
 | 
						|
  bool rc= false;
 | 
						|
  st_select_lex *first_sl= spec->first_select();
 | 
						|
 | 
						|
  /* Prevent name resolution for field references out of with elements */
 | 
						|
  for (st_select_lex *sl= first_sl;
 | 
						|
       sl;
 | 
						|
       sl= sl->next_select())
 | 
						|
    sl->context.outer_context= 0;
 | 
						|
 | 
						|
  uint8 save_context_analysys_only= thd->lex->context_analysis_only;
 | 
						|
  thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
 | 
						|
  if (!spec->prepared &&
 | 
						|
      (spec->prepare(spec->derived, 0, 0) ||
 | 
						|
       process_columns_of_derived_unit(thd, spec) ||
 | 
						|
       check_duplicate_names(thd, first_sl->item_list, 1)))
 | 
						|
    rc= true;
 | 
						|
  thd->lex->context_analysis_only= save_context_analysys_only;
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool With_element::is_anchor(st_select_lex *sel)
 | 
						|
{
 | 
						|
  return !(mutually_recursive & sel->with_dep);
 | 
						|
}   
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   @brief
 | 
						|
     Search for the definition of the given table referred in this select node
 | 
						|
 | 
						|
  @param table  reference to the table whose definition is searched for
 | 
						|
  @param excl_spec  ignore the definition with this spec
 | 
						|
     
 | 
						|
  @details  
 | 
						|
    The method looks for the definition of the table whose reference is occurred
 | 
						|
    in the FROM list of this select node. First it searches for it in the
 | 
						|
    with clause attached to the unit this select node belongs to. If such a
 | 
						|
    definition is not found then the embedding units are looked through.
 | 
						|
 | 
						|
  @retval
 | 
						|
    pointer to the found definition if the search has been successful
 | 
						|
    NULL -  otherwise
 | 
						|
*/    
 | 
						|
 | 
						|
With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table,
 | 
						|
                                                 st_select_lex_unit *excl_spec)
 | 
						|
{
 | 
						|
  With_element *found= NULL;
 | 
						|
  With_clause *containing_with_clause= NULL;
 | 
						|
  st_select_lex_unit *master_unit;
 | 
						|
  st_select_lex *outer_sl;
 | 
						|
  for (st_select_lex *sl= this; sl; sl= outer_sl)
 | 
						|
  {
 | 
						|
    /* 
 | 
						|
      If sl->master_unit() is the spec of a with element then the search for 
 | 
						|
      a definition was already done by With_element::check_dependencies_in_spec
 | 
						|
      and it was unsuccesful. Yet for units cloned from the spec it has not 
 | 
						|
      been done yet.
 | 
						|
    */
 | 
						|
    With_clause *attached_with_clause= sl->get_with_clause();
 | 
						|
    if (attached_with_clause &&
 | 
						|
        attached_with_clause != containing_with_clause &&
 | 
						|
        (found= attached_with_clause->find_table_def(table, NULL, excl_spec)))
 | 
						|
      break;
 | 
						|
    master_unit= sl->master_unit();
 | 
						|
    outer_sl= master_unit->outer_select();
 | 
						|
    With_element *with_elem= sl->get_with_element();
 | 
						|
    if (with_elem)
 | 
						|
    {
 | 
						|
      containing_with_clause= with_elem->get_owner();
 | 
						|
      With_element *barrier= containing_with_clause->with_recursive ?
 | 
						|
                               NULL : with_elem;
 | 
						|
      if ((found= containing_with_clause->find_table_def(table, barrier,
 | 
						|
                                                         excl_spec)))
 | 
						|
        break;
 | 
						|
      if (outer_sl && !outer_sl->get_with_element())
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    /* Do not look for the table's definition beyond the scope of the view */
 | 
						|
    if (master_unit->is_view)
 | 
						|
      break; 
 | 
						|
  }
 | 
						|
  return found;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool TABLE_LIST::is_recursive_with_table()
 | 
						|
{
 | 
						|
  return with && with->is_recursive;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  A reference to a with table T is recursive if it occurs somewhere
 | 
						|
  in the query specifying T or in the query specifying one of the tables
 | 
						|
  mutually recursive with T.
 | 
						|
*/
 | 
						|
 | 
						|
bool TABLE_LIST::is_with_table_recursive_reference()
 | 
						|
{
 | 
						|
  return (with_internal_reference_map &&
 | 
						|
          (with->get_mutually_recursive() & with_internal_reference_map));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* 
 | 
						|
  Specifications of with tables with recursive table references
 | 
						|
  in non-mergeable derived tables are not allowed in this
 | 
						|
  implementation. 
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  We say that the specification of a with table T is restricted  
 | 
						|
  if all below is true.
 | 
						|
    1. Any immediate select of the specification contains at most one 
 | 
						|
     recursive table reference taking into account table references
 | 
						|
     from mergeable derived tables.
 | 
						|
    2. Any recursive table reference is not an inner operand of an
 | 
						|
     outer join operation used in an immediate select of the
 | 
						|
     specification.
 | 
						|
    3. Any immediate select from the specification of T does not
 | 
						|
     contain aggregate functions.
 | 
						|
    4. The specification of T does not contain recursive table references.
 | 
						|
 | 
						|
  If the specification of T is not restricted we call the corresponding
 | 
						|
  with element unrestricted.
 | 
						|
 | 
						|
  The SQL standards allows only with elements with restricted specification.
 | 
						|
  By default we comply with the standards here.  
 | 
						|
 
 | 
						|
  Yet we allow unrestricted specification if the status variable
 | 
						|
  'standards_compliant_cte' set to 'off'(0).
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Check if this select makes the including specification unrestricted
 | 
						|
 
 | 
						|
  @param
 | 
						|
    only_standards_compliant  true if  the system variable
 | 
						|
                              'standards_compliant_cte' is set to 'on'
 | 
						|
  @details
 | 
						|
    This method checks whether the conditions 1-4 (see the comment above)
 | 
						|
    are satisfied for this select. If not then mark this element as
 | 
						|
    unrestricted and report an error if 'only_standards_compliant' is true.
 | 
						|
 | 
						|
  @retval
 | 
						|
    true    if an error is reported 
 | 
						|
    false   otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool st_select_lex::check_unrestricted_recursive(bool only_standard_compliant)
 | 
						|
{
 | 
						|
  With_element *with_elem= get_with_element();
 | 
						|
  if (!with_elem ||!with_elem->is_recursive)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      If this select is not from the specifiocation of a with elememt or
 | 
						|
      if this not a recursive with element then there is nothing to check.
 | 
						|
    */
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Check conditions 1-2 for restricted specification*/
 | 
						|
  table_map unrestricted= 0;
 | 
						|
  table_map encountered= 0;
 | 
						|
  if (with_elem->check_unrestricted_recursive(this, 
 | 
						|
					      unrestricted, 
 | 
						|
					      encountered))
 | 
						|
    return true;
 | 
						|
  with_elem->get_owner()->add_unrestricted(unrestricted);
 | 
						|
 | 
						|
 | 
						|
  /* Check conditions 3-4 for restricted specification*/
 | 
						|
  if ((with_sum_func && !with_elem->is_anchor(this)) ||
 | 
						|
      (with_elem->contains_sq_with_recursive_reference()))
 | 
						|
    with_elem->get_owner()->add_unrestricted(
 | 
						|
                              with_elem->get_mutually_recursive());
 | 
						|
 | 
						|
  /* Report an error on unrestricted specification if this is required */
 | 
						|
  if (only_standard_compliant && with_elem->is_unrestricted())
 | 
						|
  {
 | 
						|
    my_error(ER_NOT_STANDARD_COMPLIANT_RECURSIVE,
 | 
						|
             MYF(0), with_elem->get_name_str());
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Check if a select from the spec of this with element is partially restricted
 | 
						|
 
 | 
						|
  @param
 | 
						|
    sel             select from the specification of this element where to check
 | 
						|
                    whether conditions 1-2 are satisfied
 | 
						|
    unrestricted    IN/OUT bitmap where to mark unrestricted specs
 | 
						|
    encountered     IN/OUT bitmap where to mark encountered recursive references
 | 
						|
  @details
 | 
						|
    This method checks whether the conditions 1-2 (see the comment above)
 | 
						|
    are satisfied for the select sel. 
 | 
						|
    This method is called recursively for derived tables.
 | 
						|
 | 
						|
  @retval
 | 
						|
    true    if an error is reported 
 | 
						|
    false   otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool With_element::check_unrestricted_recursive(st_select_lex *sel, 
 | 
						|
					        table_map &unrestricted, 
 | 
						|
						table_map &encountered)
 | 
						|
{
 | 
						|
  /* Check conditions 1 for restricted specification*/
 | 
						|
  List_iterator<TABLE_LIST> ti(sel->leaf_tables);
 | 
						|
  TABLE_LIST *tbl;
 | 
						|
  while ((tbl= ti++))
 | 
						|
  {
 | 
						|
    st_select_lex_unit *unit= tbl->get_unit();
 | 
						|
    if (unit)
 | 
						|
    { 
 | 
						|
      if(!tbl->is_with_table())
 | 
						|
      {
 | 
						|
        if (check_unrestricted_recursive(unit->first_select(), 
 | 
						|
					 unrestricted, 
 | 
						|
				         encountered))
 | 
						|
          return true;
 | 
						|
      }
 | 
						|
      if (!(tbl->is_recursive_with_table() && unit->with_element->owner == owner))
 | 
						|
        continue;
 | 
						|
      With_element *with_elem= unit->with_element;
 | 
						|
      if (encountered & with_elem->get_elem_map())
 | 
						|
        unrestricted|= with_elem->mutually_recursive;
 | 
						|
      else if (with_elem ==this)
 | 
						|
        encountered|= with_elem->get_elem_map();
 | 
						|
    }
 | 
						|
  } 
 | 
						|
  for (With_element *with_elem= owner->with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    if (!with_elem->is_recursive && (unrestricted & with_elem->get_elem_map()))
 | 
						|
      continue;
 | 
						|
    if (encountered & with_elem->get_elem_map())
 | 
						|
    {
 | 
						|
      uint cnt= 0;
 | 
						|
      table_map encountered_mr= encountered & with_elem->mutually_recursive;
 | 
						|
      for (table_map map= encountered_mr >> with_elem->number;
 | 
						|
           map != 0;
 | 
						|
           map>>= 1) 
 | 
						|
      {
 | 
						|
        if (map & 1)
 | 
						|
        { 
 | 
						|
          if (cnt)
 | 
						|
          { 
 | 
						|
            unrestricted|= with_elem->mutually_recursive;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          else
 | 
						|
            cnt++;
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  
 | 
						|
  /* Check conditions 2 for restricted specification*/
 | 
						|
  ti.rewind();
 | 
						|
  while ((tbl= ti++))
 | 
						|
  {
 | 
						|
    if (!tbl->is_with_table_recursive_reference())
 | 
						|
      continue;
 | 
						|
    for (TABLE_LIST *tab= tbl; tab; tab= tab->embedding)
 | 
						|
    {
 | 
						|
      if (tab->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
 | 
						|
      {
 | 
						|
        unrestricted|= mutually_recursive;
 | 
						|
	break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
  Check subqueries with recursive table references from FROM list of this select
 | 
						|
 
 | 
						|
 @details
 | 
						|
    For each recursive table reference from the FROM list of this select 
 | 
						|
    this method checks:
 | 
						|
    - whether this reference is within a materialized derived table and
 | 
						|
      if so it report an error
 | 
						|
    - whether this reference is within a subquery and if so it set a flag
 | 
						|
      in this subquery that disallows some optimization strategies for
 | 
						|
      this subquery.
 | 
						|
 
 | 
						|
  @retval
 | 
						|
    true    if an error is reported 
 | 
						|
    false   otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool st_select_lex::check_subqueries_with_recursive_references()
 | 
						|
{
 | 
						|
  List_iterator<TABLE_LIST> ti(leaf_tables);
 | 
						|
  TABLE_LIST *tbl;
 | 
						|
  while ((tbl= ti++))
 | 
						|
  {
 | 
						|
    if (!(tbl->is_with_table_recursive_reference()))
 | 
						|
      continue;
 | 
						|
    With_element *rec_elem= tbl->with;
 | 
						|
    st_select_lex_unit *sl_master;
 | 
						|
    for (st_select_lex *sl= this; sl; sl= sl_master->outer_select())
 | 
						|
    {
 | 
						|
      sl_master= sl->master_unit();
 | 
						|
      if (sl_master->with_element &&
 | 
						|
          sl_master->with_element->get_owner() == rec_elem->get_owner())
 | 
						|
        break;
 | 
						|
      sl->uncacheable|= UNCACHEABLE_DEPENDENT;
 | 
						|
      sl_master->uncacheable|= UNCACHEABLE_DEPENDENT;
 | 
						|
      if (sl_master->derived)
 | 
						|
        sl_master->derived->register_as_derived_with_rec_ref(rec_elem);
 | 
						|
      if (sl_master->item)
 | 
						|
      {
 | 
						|
        Item_subselect *subq= (Item_subselect *) (sl_master->item);
 | 
						|
        subq->register_as_with_rec_ref(rec_elem);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief
 | 
						|
    Print this with clause
 | 
						|
 | 
						|
  @param thd         Thread handle
 | 
						|
  @param str         Where to print to
 | 
						|
  @param query_type  The mode of printing
 | 
						|
 | 
						|
  @details
 | 
						|
    The method prints a string representation of this clause in the
 | 
						|
    string str. The parameter query_type specifies the mode of printing.
 | 
						|
*/
 | 
						|
 | 
						|
void With_clause::print(THD *thd, String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  /*
 | 
						|
    Any with clause contains just definitions of CTE tables.
 | 
						|
    No data expansion is applied to these definitions.
 | 
						|
  */
 | 
						|
  query_type= (enum_query_type) (query_type | QT_NO_DATA_EXPANSION);
 | 
						|
 | 
						|
  str->append(STRING_WITH_LEN("with "));
 | 
						|
  if (with_recursive)
 | 
						|
    str->append(STRING_WITH_LEN("recursive "));
 | 
						|
  for (With_element *with_elem= with_list.first;
 | 
						|
       with_elem;
 | 
						|
       with_elem= with_elem->next)
 | 
						|
  {
 | 
						|
    if (with_elem != with_list.first)
 | 
						|
      str->append(STRING_WITH_LEN(", "));
 | 
						|
    with_elem->print(thd, str, query_type);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void list_strlex_print(THD *thd, String *str, List<Lex_ident_sys> *list)
 | 
						|
{
 | 
						|
  List_iterator_fast<Lex_ident_sys> li(*list);
 | 
						|
  bool first= TRUE;
 | 
						|
  while(Lex_ident_sys *col_name= li++)
 | 
						|
  {
 | 
						|
    if (first)
 | 
						|
      first= FALSE;
 | 
						|
    else
 | 
						|
      str->append(',');
 | 
						|
    append_identifier(thd, str, col_name);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   @brief
 | 
						|
     Print this with element
 | 
						|
 | 
						|
  @param thd         Thread handle
 | 
						|
  @param str         Where to print to
 | 
						|
  @param query_type  The mode of printing
 | 
						|
 | 
						|
  @details
 | 
						|
    The method prints a string representation of this with element in the
 | 
						|
    string str. The parameter query_type specifies the mode of printing.
 | 
						|
*/
 | 
						|
 | 
						|
void With_element::print(THD *thd, String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(get_name());
 | 
						|
  if (column_list.elements)
 | 
						|
  {
 | 
						|
    List_iterator_fast<Lex_ident_sys> li(column_list);
 | 
						|
    str->append('(');
 | 
						|
    list_strlex_print(thd, str, &column_list);
 | 
						|
    str->append(')');
 | 
						|
  }
 | 
						|
  str->append(STRING_WITH_LEN(" as ("));
 | 
						|
  spec->print(str, query_type);
 | 
						|
  str->append(')');
 | 
						|
 | 
						|
  if (cycle_list)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(cycle_list->elements != 0);
 | 
						|
    str->append(STRING_WITH_LEN(" CYCLE "));
 | 
						|
    list_strlex_print(thd, str, cycle_list);
 | 
						|
    str->append(STRING_WITH_LEN(" RESTRICT "));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool With_element::instantiate_tmp_tables()
 | 
						|
{
 | 
						|
  List_iterator_fast<TABLE_LIST> li(rec_result->rec_table_refs);
 | 
						|
  TABLE_LIST *rec_tbl;
 | 
						|
  while ((rec_tbl= li++))
 | 
						|
  {
 | 
						|
    TABLE *rec_table= rec_tbl->table;
 | 
						|
    if (!rec_table->is_created() &&
 | 
						|
        instantiate_tmp_table(rec_table,
 | 
						|
                              rec_table->s->key_info,
 | 
						|
                              rec_result->tmp_table_param.start_recinfo,
 | 
						|
                              &rec_result->tmp_table_param.recinfo,
 | 
						|
                              0))
 | 
						|
      return true;
 | 
						|
 | 
						|
    rec_table->file->extra(HA_EXTRA_WRITE_CACHE);
 | 
						|
    rec_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
void With_element::set_cycle_list(List<Lex_ident_sys> *cycle_list_arg)
 | 
						|
{
 | 
						|
  cycle_list= cycle_list_arg;
 | 
						|
 | 
						|
  /*
 | 
						|
    If a CTE table with columns c1,...,cn is defined with a cycle
 | 
						|
    clause CYCLE(ci1,...,cik) then no two rows r1 and r2 from the
 | 
						|
    table shall have r1.ci1=r2.ci1 && ... && r1.cik=r2.cik.
 | 
						|
 | 
						|
    If a cycle clause is used in the specification of a CTE then
 | 
						|
    each UNION ALL at the top level of the specification is interpreted
 | 
						|
    as a UNION DISTINCT over the cycle columns.
 | 
						|
  */
 | 
						|
  for (st_select_lex *sl=  spec->first_select(); sl; sl= sl->next_select())
 | 
						|
  {
 | 
						|
    spec->union_distinct= sl;
 | 
						|
    if (sl != spec->first_select())
 | 
						|
    {
 | 
						|
      sl->distinct= TRUE;
 | 
						|
      sl->with_all_modifier= FALSE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |