MySQL 8.0.39
Source Code Documentation
PT_set_operation Class Reference

#include <parse_tree_nodes.h>

Inheritance diagram for PT_set_operation:
[legend]

Public Member Functions

 PT_set_operation (PT_query_expression_body *lhs, bool is_distinct, PT_query_expression_body *rhs, bool is_rhs_in_parentheses=false)
 
void merge_descendants (Parse_context *pc, Query_term_set_op *setop, QueryLevel &ql)
 Possibly merge lower syntactic levels of set operations (UNION, INTERSECT and EXCEPT) into setop, and set new last DISTINCT index for setop. More...
 
bool is_set_operation () const override
 
bool has_into_clause () const override
 
bool has_trailing_into_clause () const override
 
bool can_absorb_order_and_limit (bool, bool) const override
 True if this query expression can absorb an extraneous order by/limit clause. More...
 
bool is_table_value_constructor () const override
 
PT_insert_values_listget_row_value_list () const override
 
- Public Member Functions inherited from Parse_tree_node_tmpl< Context >
virtual ~Parse_tree_node_tmpl ()=default
 
bool is_contextualized () const
 
virtual bool contextualize (Context *pc)
 Do all context-sensitive things and mark the node as contextualized. More...
 
void error (Context *pc, const POS &pos) const
 syntax_error() function replacement for deferred reporting of syntax errors More...
 
void error (Context *pc, const POS &pos, const char *msg) const
 syntax_error() function replacement for deferred reporting of syntax errors More...
 
void errorf (Context *pc, const POS &pos, const char *format,...) const
 syntax_error() function replacement for deferred reporting of syntax errors More...
 

Protected Member Functions

bool contextualize_setop (Parse_context *pc, Query_term_type setop_type, Surrounding_context context)
 
- Protected Member Functions inherited from Parse_tree_node_tmpl< Context >
 Parse_tree_node_tmpl ()
 

Protected Attributes

PT_query_expression_bodym_lhs
 
bool m_is_distinct
 
PT_query_expression_bodym_rhs
 
PT_into_destinationm_into
 
const bool m_is_rhs_in_parentheses
 

Private Types

using super = PT_query_expression_body
 

Additional Inherited Members

- Public Types inherited from Parse_tree_node_tmpl< Context >
typedef Context context_t
 
- Static Public Member Functions inherited from Parse_tree_node_tmpl< Context >
static void * operator new (size_t size, MEM_ROOT *mem_root, const std::nothrow_t &arg=std::nothrow) noexcept
 
static void operator delete (void *ptr, size_t size)
 
static void operator delete (void *, MEM_ROOT *, const std::nothrow_t &) noexcept
 

Member Typedef Documentation

◆ super

Constructor & Destructor Documentation

◆ PT_set_operation()

PT_set_operation::PT_set_operation ( PT_query_expression_body lhs,
bool  is_distinct,
PT_query_expression_body rhs,
bool  is_rhs_in_parentheses = false 
)
inline

Member Function Documentation

◆ can_absorb_order_and_limit()

bool PT_set_operation::can_absorb_order_and_limit ( bool  order,
bool  limit 
) const
inlineoverridevirtual

True if this query expression can absorb an extraneous order by/limit clause.

The ORDER BY/LIMIT syntax is mostly consistestent, i.e. a trailing clause may not refer to the tables in the <query primary>, with one glaring exception:

(...( SELECT ... )...) ORDER BY ...

If the nested query expression doesn't contain ORDER BY, the statement is interpreted as if the ORDER BY was absorbed by the innermost query expression, i.e.:

(...( SELECT ... ORDER BY ... )...)

There is no rewriting of the parse tree nor AST happening here, the transformation is done by the contextualizer (see PT_query_expression::contextualize_order_and_limit), which interprets the parse tree, and builds the AST according to this interpretation. This interpretation is governed by the following rule: An ORDER BY can be absorbed if none the nested query expressions contains an ORDER BY or LIMIT. The rule is complex, so here are some examples for illustration:

In these cases the ORDER BY is absorbed:

( SELECT * FROM t1 ) ORDER BY t1.a;
(( SELECT * FROM t1 )) ORDER BY t1.a;

In these cases the ORDER BY is not absorbed:

( SELECT * FROM t1 ORDER BY 1 ) ORDER BY t1.a;
(( SELECT * FROM t1 ) ORDER BY 1 ) ORDER BY t1.a;
( SELECT * FROM t1 LIMIT 1 ) ORDER BY t1.a;
(( SELECT * FROM t1 ) LIMIT 1 ) ORDER BY t1.a;

The same happens with LIMIT, obviously, but the optimizer is freeer to choose when to apply the limit, and there are name no resolution issues involved.

Parameters
orderTrue if the outer query block has the ORDER BY clause.
limitTrue if the outer query block has the LIMIT clause.

Implements PT_query_expression_body.

◆ contextualize_setop()

bool PT_set_operation::contextualize_setop ( Parse_context pc,
Query_term_type  setop_type,
Surrounding_context  context 
)
protected

◆ get_row_value_list()

PT_insert_values_list * PT_set_operation::get_row_value_list ( ) const
inlineoverridevirtual

◆ has_into_clause()

bool PT_set_operation::has_into_clause ( ) const
inlineoverridevirtual

◆ has_trailing_into_clause()

bool PT_set_operation::has_trailing_into_clause ( ) const
inlineoverridevirtual

◆ is_set_operation()

bool PT_set_operation::is_set_operation ( ) const
inlineoverridevirtual

◆ is_table_value_constructor()

bool PT_set_operation::is_table_value_constructor ( ) const
inlineoverridevirtual

◆ merge_descendants()

void PT_set_operation::merge_descendants ( Parse_context pc,
Query_term_set_op setop,
QueryLevel ql 
)

Possibly merge lower syntactic levels of set operations (UNION, INTERSECT and EXCEPT) into setop, and set new last DISTINCT index for setop.

We only ever merge set operations of the same kind, but even that, not always: we prefer streaming of UNIONs when possible, and merging UNIONs with a mix of DISTINCT and ALL at the top level may lead to inefficient evaluation. Streaming only makes sense when the result set is sent from the server, i.e. not further materialized, e.g. for ORDER BY or windowing.

For example, the query:

     EXPLAIN FORMAT=tree
     SELECT * FROM t1 UNION DISTINCT
     SELECT * FROM t2 UNION ALL
     SELECT * FROM t3;

will yield the plan:

-> Append
   -> Stream results
       -> Table scan on \<union temporary\>
           -> Union materialize with deduplication
               -> Table scan on t1
               -> Table scan on t2
   -> Stream results
       -> Table scan on t3

but EXPLAIN FORMAT=tree SELECT * FROM t1 UNION DISTINCT SELECT * FROM t2 UNION DISTINCT SELECT * FROM t3;

will yield the following plan, i.e. we merge the two syntactic levels. The former case could also be merged, but the we'd need to write the rows of t3 to the temporary table, which gives a performance penalty, so we don't merge.

-> Table scan on \<union temporary\>
    -> Union materialize with deduplication
        -> Table scan on t1
        -> Table scan on t2
        -> Table scan on t3

If have an outer ORDER BY, we will see how this would look (with merge):

   EXPLAIN FORMAT=tree
   SELECT * FROM t1 UNION DISTINCT
   SELECT * FROM t2 UNION ALL
   SELECT * FROM t3
   ORDER BY a;

will yield

-> Sort: a
    -> Table scan on \<union temporary\>
        -> Union materialize with deduplication
            -> Table scan on t1
            -> Table scan on t2
            -> Disable deduplication
                -> Table scan on t3

since in this case, merging is advantageous, in that we need only one temporary file. Another interesting case is when the upper level is DISTINCT:

   EXPLAIN FORMAT=tree
   SELECT * FROM t1 UNION DISTINCT
   ( SELECT * FROM t2 UNION ALL
     SELECT * FROM t3 );

will merge and remove the lower ALL level:

-> Table scan on <union temporary>
    -> Union materialize with deduplication
        -> Table scan on t1
        -> Table scan on t2
        -> Table scan on t3

For INTERSECT, the presence of one DISTINCT operator effectively makes any INTERSECT ALL equivalent to DISTINCT, so we only retain ALL if all operators are ALL, i.e. for a N-ary INTERSECT with at least one DISTINCT, has_mixed_distinct_operators always returns false. We aggressively merge up all sub-nests for INTERSECT.

INTERSECT ALL is only binary due to current implementation method, no merge up.

EXCEPT [ALL] is not right associative, so be careful when merging: we only merge up a left-most nested EXCEPT into an outer level EXCEPT, since we evaluate from left to right. Note that for an N-ary EXCEPT operation, a mix of ALL and DISTINCT is meaningful and supported. After the first operator with DISTINCT, further ALL operators are moot since no duplicates are left. Example explain:

SELECT * FROM r EXCEPT ALL SELECT * FROM s EXCEPT SELECT * FROM t

-> Table scan on <except temporary> (cost=..) -> Except materialize with deduplication (cost=..) -> Table scan on r (cost=..) -> Disable deduplication -> Table scan on s (cost=..) -> Table scan on t (cost=..)

We can see that the first two operand tables are compared with ALL ("disable de-duplication"), whereas the final one, t, is DISTINCT.

Parameters
pcthe parse context
setopthe set operation query term to be filled in with children
qlparsing query level

computes number for members if we collapse

Member Data Documentation

◆ m_into

PT_into_destination* PT_set_operation::m_into
protected

◆ m_is_distinct

bool PT_set_operation::m_is_distinct
protected

◆ m_is_rhs_in_parentheses

const bool PT_set_operation::m_is_rhs_in_parentheses
protected

◆ m_lhs

PT_query_expression_body* PT_set_operation::m_lhs
protected

◆ m_rhs

PT_query_expression_body* PT_set_operation::m_rhs
protected

The documentation for this class was generated from the following files: