MySQL 8.4.2
Source Code Documentation
|
#include <parse_tree_nodes.h>
Public Member Functions | |
PT_set_operation (const POS &pos, 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_list * | get_row_value_list () const override |
Public Member Functions inherited from PT_query_expression_body | |
PT_query_expression_body (const POS &pos) | |
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) final |
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) |
void | add_json_info (Json_object *obj) override |
Add all the node-specific json fields. More... | |
Protected Member Functions inherited from Parse_tree_node_tmpl< Context > | |
Parse_tree_node_tmpl ()=delete | |
Parse_tree_node_tmpl (const POS &pos) | |
Parse_tree_node_tmpl (const POS &start_pos, const POS &end_pos) | |
bool | begin_parse_tree (Show_parse_tree *tree) |
bool | end_parse_tree (Show_parse_tree *tree) |
virtual bool | do_contextualize (Context *pc) |
Do all context-sensitive things and mark the node as contextualized. More... | |
Protected Attributes | |
PT_query_expression_body * | m_lhs |
bool | m_is_distinct |
PT_query_expression_body * | m_rhs |
PT_into_destination * | m_into {nullptr} |
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 |
Public Attributes inherited from Parse_tree_node_tmpl< Context > | |
POS | m_pos |
|
private |
|
inline |
|
inlineoverrideprotectedvirtual |
Add all the node-specific json fields.
Any class that needs to add such info should override this function rather than doing it in do_contextualize(). E.g. the parse tree node for AVG() may have "distinct" field to indicate if AVG(DISTINCT ...) is used or not.
json_obj | Json object for this parse tree node. |
Reimplemented from Parse_tree_node_tmpl< Context >.
|
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.
order | True if the outer query block has the ORDER BY clause. |
limit | True if the outer query block has the LIMIT clause. |
Implements PT_query_expression_body.
|
protected |
|
inlineoverridevirtual |
Implements PT_query_expression_body.
|
inlineoverridevirtual |
Implements PT_query_expression_body.
|
inlineoverridevirtual |
Implements PT_query_expression_body.
|
inlineoverridevirtual |
Implements PT_query_expression_body.
|
inlineoverridevirtual |
Implements PT_query_expression_body.
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.
pc | the parse context |
setop | the set operation query term to be filled in with children |
ql | parsing query level |
computes number for members if we collapse
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |