From 31bd903be9a0e217a589ade0d0e8a8ddb7a5220a Mon Sep 17 00:00:00 2001
From: "Steinar H. Gunderson" <steinar.gunderson@oracle.com>
Date: Wed, 18 Dec 2019 16:30:45 +0100
Subject: [PATCH] Bug #30473261: CONVERT THE INDEX SUBQUERY ENGINES INTO USING
 THE ITERATOR EXECUTOR [patch 8/10, deps]

Reduce the number of dependencies on item_subselect.h.
Removes approximately 170/200 dependencies when changing the file.

Change-Id: Id961aaf6a2f58df6304e657ba117304a4ce8543a
---
 sql/comp_creator.h      | 40 ++++++++++++++++++++++++++
 sql/item_subselect.cc   | 44 ++++++++++++++--------------
 sql/item_subselect.h    | 64 ++++++++++++++++-------------------------
 sql/opt_hints.cc        |  9 +++---
 sql/opt_hints.h         |  4 +--
 sql/parse_tree_hints.cc |  6 ++--
 sql/parser_yystype.h    |  1 +
 sql/sql_hints.yy        |  5 ++--
 sql/sql_lex.cc          | 30 +++++++++++++------
 sql/sql_lex.h           | 27 +++++++----------
 sql/sql_optimizer.cc    | 36 +++++++++++------------
 sql/sql_optimizer.h     |  5 ++--
 sql/sql_resolver.cc     |  6 ++--
 13 files changed, 153 insertions(+), 124 deletions(-)
 create mode 100644 sql/comp_creator.h

diff --git a/sql/comp_creator.h b/sql/comp_creator.h
new file mode 100644
index 000000000000..fa16f754e96c
--- /dev/null
+++ b/sql/comp_creator.h
@@ -0,0 +1,40 @@
+#ifndef COMP_CREATOR_INCLUDED
+#define COMP_CREATOR_INCLUDED
+
+/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License, version 2.0,
+   as published by the Free Software Foundation.
+
+   This program is also distributed with certain software (including
+   but not limited to OpenSSL) that is licensed under separate terms,
+   as designated in a particular file or component or in included license
+   documentation.  The authors of MySQL hereby grant you an additional
+   permission to link the program and your derivative works with the
+   separately licensed software that they have included with MySQL.
+
+   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, version 2.0, 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 */
+
+// Declaration of chooser_compare_func_creator, in a separate file
+// to make sure that parser_yystype.h does not need to depend on
+// item_subselect.h.
+
+class Comp_creator;
+
+/**
+  Convenience typedef for a function that returns factories for Item comparators
+  (ie., returns Comp_creator).
+
+  @retval nullptr In case of semantic errors.
+*/
+using chooser_compare_func_creator = Comp_creator *(*)(bool invert);
+
+#endif  // COMP_CREATOR_INCLUDED
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 8c7a89af57e3..4bdb7499643f 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -376,7 +376,8 @@ bool Item_in_subselect::mark_as_outer(Item *left_row, size_t col) {
 
 bool Item_in_subselect::finalize_exists_transform(THD *thd,
                                                   SELECT_LEX *select_lex) {
-  DBUG_ASSERT(exec_method == EXEC_EXISTS_OR_MAT || exec_method == EXEC_EXISTS);
+  DBUG_ASSERT(exec_method == SubqueryExecMethod::EXEC_EXISTS_OR_MAT ||
+              exec_method == SubqueryExecMethod::EXEC_EXISTS);
   /*
     Change
       SELECT expr1, expr2
@@ -419,7 +420,7 @@ bool Item_in_subselect::finalize_exists_transform(THD *thd,
     return true; /* purecov: inspected */
 
   select_lex->join->allow_outer_refs = true;  // for JOIN::set_prefix_tables()
-  exec_method = EXEC_EXISTS;
+  exec_method = SubqueryExecMethod::EXEC_EXISTS;
   return false;
 }
 
@@ -463,7 +464,7 @@ Item *Item_in_subselect::remove_in2exists_conds(Item *conds) {
 
 bool Item_in_subselect::finalize_materialization_transform(THD *thd,
                                                            JOIN *join) {
-  DBUG_ASSERT(exec_method == EXEC_EXISTS_OR_MAT);
+  DBUG_ASSERT(exec_method == SubqueryExecMethod::EXEC_EXISTS_OR_MAT);
 
   DBUG_ASSERT(join == subquery->single_select_lex()->join);
   // No UNION in materialized subquery so this holds:
@@ -471,7 +472,7 @@ bool Item_in_subselect::finalize_materialization_transform(THD *thd,
   DBUG_ASSERT(join->unit == unit);
   DBUG_ASSERT(unit->global_parameters()->select_limit == nullptr);
 
-  exec_method = EXEC_MATERIALIZATION;
+  exec_method = SubqueryExecMethod::EXEC_MATERIALIZATION;
 
   /*
     We need to undo several changes which IN->EXISTS had done. But we first
@@ -527,19 +528,19 @@ void Item_in_subselect::cleanup() {
   need_expr_cache = true;
 
   switch (exec_method) {
-    case EXEC_MATERIALIZATION:
+    case SubqueryExecMethod::EXEC_MATERIALIZATION:
       if (in2exists_info->dependent_after) {
         unit->first_select()->uncacheable |= UNCACHEABLE_DEPENDENT;
         unit->uncacheable |= UNCACHEABLE_DEPENDENT;
       }
       // fall through
-    case EXEC_EXISTS:
+    case SubqueryExecMethod::EXEC_EXISTS:
       /*
         Back to EXISTS_OR_MAT, so that next execution of this statement can
         choose between the two.
       */
       unit->global_parameters()->select_limit = nullptr;
-      exec_method = EXEC_EXISTS_OR_MAT;
+      exec_method = SubqueryExecMethod::EXEC_EXISTS_OR_MAT;
       break;
     default:
       break;
@@ -552,7 +553,7 @@ RowIterator *Item_in_subselect::root_iterator() const {
   // Only subselect_hash_sj_engine owns its own iterator;
   // for subselect_indexsubquery_engine, the unit still has it, since it's a
   // normally executed query block. Thus, we should never get called otherwise.
-  DBUG_ASSERT(exec_method == EXEC_MATERIALIZATION &&
+  DBUG_ASSERT(exec_method == SubqueryExecMethod::EXEC_MATERIALIZATION &&
               indexsubquery_engine->engine_type() ==
                   subselect_indexsubquery_engine::HASH_SJ_ENGINE);
   return down_cast<subselect_hash_sj_engine *>(indexsubquery_engine)
@@ -723,8 +724,8 @@ bool Item_in_subselect::walk(Item_processor processor, enum_walk walk,
 
 bool Item_in_subselect::exec(THD *thd) {
   DBUG_TRACE;
-  DBUG_ASSERT(exec_method != EXEC_MATERIALIZATION ||
-              (exec_method == EXEC_MATERIALIZATION &&
+  DBUG_ASSERT(exec_method != SubqueryExecMethod::EXEC_MATERIALIZATION ||
+              (exec_method == SubqueryExecMethod::EXEC_MATERIALIZATION &&
                indexsubquery_engine->engine_type() ==
                    subselect_indexsubquery_engine::HASH_SJ_ENGINE));
   /*
@@ -741,7 +742,8 @@ bool Item_in_subselect::exec(THD *thd) {
       lookup, the cache hit rate, and the savings per cache hit.
   */
   if (need_expr_cache && !left_expr_cache &&
-      exec_method == EXEC_MATERIALIZATION && init_left_expr_cache(thd))
+      exec_method == SubqueryExecMethod::EXEC_MATERIALIZATION &&
+      init_left_expr_cache(thd))
     return true;
 
   if (left_expr_cache != nullptr) {
@@ -1286,11 +1288,7 @@ bool Query_result_exists_subquery::send_data(THD *, List<Item> &) {
 }
 
 Item_exists_subselect::Item_exists_subselect(SELECT_LEX *select)
-    : Item_subselect(),
-      value(false),
-      exec_method(EXEC_UNSPECIFIED),
-      sj_convert_priority(0),
-      embedding_join_nest(nullptr) {
+    : Item_subselect() {
   DBUG_TRACE;
   init(select, new (*THR_MALLOC) Query_result_exists_subquery(this));
   max_columns = UINT_MAX;
@@ -1466,7 +1464,7 @@ bool Item_exists_subselect::resolve_type(THD *thd) {
   set_data_type_longlong();
   max_length = 1;
   max_columns = unit_cols();
-  if (exec_method == EXEC_EXISTS) {
+  if (exec_method == SubqueryExecMethod::EXEC_EXISTS) {
     Prepared_stmt_arena_holder ps_arena_holder(thd);
     /*
       We need only 1 row to determine existence.
@@ -2459,7 +2457,8 @@ Item_subselect::trans_res Item_in_subselect::select_in_like_transformer(
     If we didn't choose an execution method up to this point, we choose
     the IN=>EXISTS transformation, at least temporarily.
   */
-  if (exec_method == EXEC_UNSPECIFIED) exec_method = EXEC_EXISTS_OR_MAT;
+  if (exec_method == SubqueryExecMethod::EXEC_UNSPECIFIED)
+    exec_method = SubqueryExecMethod::EXEC_EXISTS_OR_MAT;
 
   /*
     Both transformers call fix_fields() only for Items created inside them,
@@ -2494,7 +2493,8 @@ void Item_in_subselect::print(const THD *thd, String *str,
   const char *tail = Item_bool_func::bool_transform_names[value_transform];
   if (implicit_is_op) tail = "";
   bool paren = false;
-  if (exec_method == EXEC_EXISTS_OR_MAT || exec_method == EXEC_EXISTS) {
+  if (exec_method == SubqueryExecMethod::EXEC_EXISTS_OR_MAT ||
+      exec_method == SubqueryExecMethod::EXEC_EXISTS) {
     if (value_transform == BOOL_NEGATED) {  // NOT has low associativity, but
                                             // we're inside Item_in_optimizer,
       // so () are needed only if IS TRUE/FALSE is coming.
@@ -2524,7 +2524,8 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) {
   abort_on_null =
       value_transform == BOOL_IS_TRUE || value_transform == BOOL_NOT_TRUE;
 
-  if (exec_method == EXEC_SEMI_JOIN) return !((*ref) = new Item_func_true());
+  if (exec_method == SubqueryExecMethod::EXEC_SEMI_JOIN)
+    return !((*ref) = new Item_func_true());
 
   if ((thd_arg->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) &&
       left_expr && !left_expr->fixed) {
@@ -2686,7 +2687,8 @@ bool Item_subselect::is_evaluated() const { return unit->is_executed(); }
 
 void Item_allany_subselect::print(const THD *thd, String *str,
                                   enum_query_type query_type) const {
-  if (exec_method == EXEC_EXISTS_OR_MAT || exec_method == EXEC_EXISTS)
+  if (exec_method == SubqueryExecMethod::EXEC_EXISTS_OR_MAT ||
+      exec_method == SubqueryExecMethod::EXEC_EXISTS)
     str->append(STRING_WITH_LEN("<exists>"));
   else {
     left_expr->print(thd, str, query_type);
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index e75b08cb5e75..1a318ea8b7a7 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -35,6 +35,7 @@
 #include "my_time.h"
 #include "mysql/udf_registration_types.h"
 #include "mysql_time.h"
+#include "sql/comp_creator.h"
 #include "sql/enum_query_type.h"
 #include "sql/item.h"  // Item_result_field
 #include "sql/parse_tree_node_base.h"
@@ -42,7 +43,6 @@
 #include "sql/sql_const.h"
 #include "template_utils.h"
 
-class Comp_creator;
 class Field;
 class Item_func_not_all;
 class Item_in_optimizer;
@@ -64,14 +64,6 @@ struct TABLE_LIST;
 template <class T>
 class List;
 
-/**
-  Convenience typedef used in this file, and further used by any files
-  including this file.
-
-  @retval NULL In case of semantic errors.
-*/
-typedef Comp_creator *(*chooser_compare_func_creator)(bool invert);
-
 /* base class for subselects */
 
 class Item_subselect : public Item_result_field {
@@ -343,6 +335,22 @@ class Item_maxmin_subselect final : public Item_singlerow_subselect {
 
 /* exists subselect */
 
+enum class SubqueryExecMethod : int {
+  EXEC_UNSPECIFIED,  ///< No execution method specified yet.
+  EXEC_SEMI_JOIN,    ///< Predicate is converted to semi-join nest.
+  /// IN was converted to correlated EXISTS, and this is a final decision.
+  EXEC_EXISTS,
+  /**
+     Decision between EXISTS and MATERIALIZATION is not yet taken.
+     IN was temporarily converted to correlated EXISTS.
+     All descendants of Item_in_subselect must go through this method
+     before they can reach EXISTS.
+  */
+  EXEC_EXISTS_OR_MAT,
+  /// Predicate executed via materialization, and this is a final decision.
+  EXEC_MATERIALIZATION
+};
+
 class Item_exists_subselect : public Item_subselect {
   typedef Item_subselect super;
 
@@ -355,24 +363,9 @@ class Item_exists_subselect : public Item_subselect {
     The method chosen to execute the predicate, currently used for IN, =ANY
     and EXISTS predicates.
   */
-  enum enum_exec_method {
-    EXEC_UNSPECIFIED,  ///< No execution method specified yet.
-    EXEC_SEMI_JOIN,    ///< Predicate is converted to semi-join nest.
-    /// IN was converted to correlated EXISTS, and this is a final decision.
-    EXEC_EXISTS,
-    /**
-       Decision between EXEC_EXISTS and EXEC_MATERIALIZATION is not yet taken.
-       IN was temporarily converted to correlated EXISTS.
-       All descendants of Item_in_subselect must go through this method
-       before they can reach EXEC_EXISTS.
-    */
-    EXEC_EXISTS_OR_MAT,
-    /// Predicate executed via materialization, and this is a final decision.
-    EXEC_MATERIALIZATION
-  };
-  enum_exec_method exec_method;
+  SubqueryExecMethod exec_method{SubqueryExecMethod::EXEC_UNSPECIFIED};
   /// Priority of this predicate in the convert-to-semi-join-nest process.
-  int sj_convert_priority;
+  int sj_convert_priority{0};
   /// Decision on whether predicate is selected for semi-join transformation
   enum enum_sj_selection {
     /// Not selected for semi-join, evaluate as subquery predicate, or
@@ -394,26 +387,16 @@ class Item_exists_subselect : public Item_subselect {
                           predicate is not a candidate for transformation.
     See also THD::emb_on_expr_nest.
   */
-  TABLE_LIST *embedding_join_nest;
+  TABLE_LIST *embedding_join_nest{nullptr};
 
   Item_exists_subselect(SELECT_LEX *select);
 
-  Item_exists_subselect()
-      : Item_subselect(),
-        value(false),
-        exec_method(EXEC_UNSPECIFIED),
-        sj_convert_priority(0),
-        embedding_join_nest(nullptr) {}
+  Item_exists_subselect() : Item_subselect() {}
 
-  explicit Item_exists_subselect(const POS &pos)
-      : super(pos),
-        value(false),
-        exec_method(EXEC_UNSPECIFIED),
-        sj_convert_priority(0),
-        embedding_join_nest(nullptr) {}
+  explicit Item_exists_subselect(const POS &pos) : super(pos) {}
 
   trans_res select_transformer(THD *, SELECT_LEX *) override {
-    exec_method = EXEC_EXISTS;
+    exec_method = SubqueryExecMethod::EXEC_EXISTS;
     return RES_OK;
   }
   subs_type substype() const override { return EXISTS_SUBS; }
@@ -852,4 +835,5 @@ class subselect_hash_sj_engine final : public subselect_indexsubquery_engine {
   RowIterator *root_iterator() const { return m_iterator.get(); }
   void create_iterators(THD *thd) override;
 };
+
 #endif /* ITEM_SUBSELECT_INCLUDED */
diff --git a/sql/opt_hints.cc b/sql/opt_hints.cc
index 204aff077bf1..677198db163c 100644
--- a/sql/opt_hints.cc
+++ b/sql/opt_hints.cc
@@ -34,6 +34,7 @@
 #include "sql/derror.h"  // ER_THD
 #include "sql/error_handler.h"
 #include "sql/item.h"
+#include "sql/item_subselect.h"
 #include "sql/key.h"
 #include "sql/mysqld.h"  // table_alias_charset
 #include "sql/nested_join.h"
@@ -260,13 +261,11 @@ uint Opt_hints_qb::sj_enabled_strategies(uint opt_switches) const {
   return opt_switches;
 }
 
-Item_exists_subselect::enum_exec_method Opt_hints_qb::subquery_strategy()
-    const {
+SubqueryExecMethod Opt_hints_qb::subquery_strategy() const {
   if (subquery_hint)
-    return static_cast<Item_exists_subselect::enum_exec_method>(
-        subquery_hint->get_args());
+    return static_cast<SubqueryExecMethod>(subquery_hint->get_args());
 
-  return Item_exists_subselect::EXEC_UNSPECIFIED;
+  return SubqueryExecMethod::EXEC_UNSPECIFIED;
 }
 
 void Opt_hints_qb::print_irregular_hints(const THD *thd, String *str) {
diff --git a/sql/opt_hints.h b/sql/opt_hints.h
index 7cc9129103e4..d3f6f26b8476 100644
--- a/sql/opt_hints.h
+++ b/sql/opt_hints.h
@@ -37,12 +37,12 @@
 #include "my_dbug.h"
 #include "my_inttypes.h"
 #include "sql/enum_query_type.h"
-#include "sql/item_subselect.h"  // Item_exists_subselect
 #include "sql/mem_root_array.h"  // Mem_root_array
 #include "sql/sql_bitmap.h"      // Bitmap
 #include "sql/sql_show.h"        // append_identifier
 #include "sql_string.h"          // String
 
+enum class SubqueryExecMethod : int;
 class Item;
 class JOIN;
 class Opt_hints_table;
@@ -462,7 +462,7 @@ class Opt_hints_qb : public Opt_hints {
     @retval EXEC_EXISTS In-to-exists execution should be used
     @retval EXEC_UNSPECIFIED No SUBQUERY hint for this query block
   */
-  Item_exists_subselect::enum_exec_method subquery_strategy() const;
+  SubqueryExecMethod subquery_strategy() const;
 
   void print_irregular_hints(const THD *thd, String *str) override;
 
diff --git a/sql/parse_tree_hints.cc b/sql/parse_tree_hints.cc
index b76ba9a34d60..fb58f469f4b7 100644
--- a/sql/parse_tree_hints.cc
+++ b/sql/parse_tree_hints.cc
@@ -284,11 +284,11 @@ void PT_qb_level_hint::append_args(const THD *thd, String *str) const {
       break;
     }
     case SUBQUERY_HINT_ENUM:
-      switch (args) {
-        case Item_exists_subselect::EXEC_MATERIALIZATION:
+      switch (static_cast<SubqueryExecMethod>(args)) {
+        case SubqueryExecMethod::EXEC_MATERIALIZATION:
           str->append(STRING_WITH_LEN(" MATERIALIZATION"));
           break;
-        case Item_exists_subselect::EXEC_EXISTS:
+        case SubqueryExecMethod::EXEC_EXISTS:
           str->append(STRING_WITH_LEN(" INTOEXISTS"));
           break;
         default:  // Exactly one of above strategies should always be specified
diff --git a/sql/parser_yystype.h b/sql/parser_yystype.h
index d32e6adcf776..a716e7c4c3e7 100644
--- a/sql/parser_yystype.h
+++ b/sql/parser_yystype.h
@@ -25,6 +25,7 @@
 
 #include "my_base.h"
 #include "my_time.h"  // interval_type
+#include "sql/comp_creator.h"
 #include "sql/handler.h"
 #include "sql/item_create.h"    // Cast_target
 #include "sql/key_spec.h"       // keytype, fk_option
diff --git a/sql/sql_hints.yy b/sql/sql_hints.yy
index 9152e0e72744..07e6be8ef5bb 100644
--- a/sql/sql_hints.yy
+++ b/sql/sql_hints.yy
@@ -28,6 +28,7 @@
 %{
 #include "my_inttypes.h"
 #include "sql/derror.h"
+#include "sql/item_subselect.h"
 #include "sql/parse_tree_helpers.h"  // check_resource_group_name_len
 #include "sql/parse_tree_hints.h"
 #include "sql/parser_yystype.h"
@@ -403,8 +404,8 @@ semijoin_strategy:
 
 subquery_strategy:
           MATERIALIZATION_HINT { $$=
-                                   Item_exists_subselect::EXEC_MATERIALIZATION; }
-        | INTOEXISTS_HINT      { $$= Item_exists_subselect::EXEC_EXISTS; }
+                                   static_cast<long>(SubqueryExecMethod::EXEC_MATERIALIZATION); }
+        | INTOEXISTS_HINT      { $$= static_cast<long>(SubqueryExecMethod::EXEC_EXISTS); }
         ;
 
 
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index f1ec78457c34..a0652922a939 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -41,6 +41,7 @@
 #include "sql/current_thd.h"
 #include "sql/derror.h"
 #include "sql/item_func.h"
+#include "sql/item_subselect.h"
 #include "sql/mysqld.h"  // table_alias_charset
 #include "sql/nested_join.h"
 #include "sql/parse_location.h"
@@ -4383,8 +4384,7 @@ bool SELECT_LEX::get_optimizable_conditions(THD *thd, Item **new_where,
   return get_optimizable_join_conditions(thd, top_join_list);
 }
 
-Item_exists_subselect::enum_exec_method SELECT_LEX::subquery_strategy(
-    THD *thd) const {
+SubqueryExecMethod SELECT_LEX::subquery_strategy(THD *thd) const {
   if (m_windows.elements > 0)
     /*
       A window function is in the SELECT list.
@@ -4394,21 +4394,20 @@ Item_exists_subselect::enum_exec_method SELECT_LEX::subquery_strategy(
       rows over which the WF is supposed to be calculated.
       So, subquery materialization is imposed. Grep for (and read) WL#10431.
     */
-    return Item_exists_subselect::EXEC_MATERIALIZATION;
+    return SubqueryExecMethod::EXEC_MATERIALIZATION;
 
   if (opt_hints_qb) {
-    Item_exists_subselect::enum_exec_method strategy =
-        opt_hints_qb->subquery_strategy();
-    if (strategy != Item_exists_subselect::EXEC_UNSPECIFIED) return strategy;
+    SubqueryExecMethod strategy = opt_hints_qb->subquery_strategy();
+    if (strategy != SubqueryExecMethod::EXEC_UNSPECIFIED) return strategy;
   }
 
   // No SUBQUERY hint given, base possible strategies on optimizer_switch
   if (thd->optimizer_switch_flag(OPTIMIZER_SWITCH_MATERIALIZATION))
     return thd->optimizer_switch_flag(OPTIMIZER_SWITCH_SUBQ_MAT_COST_BASED)
-               ? Item_exists_subselect::EXEC_EXISTS_OR_MAT
-               : Item_exists_subselect::EXEC_MATERIALIZATION;
+               ? SubqueryExecMethod::EXEC_EXISTS_OR_MAT
+               : SubqueryExecMethod::EXEC_MATERIALIZATION;
 
-  return Item_exists_subselect::EXEC_EXISTS;
+  return SubqueryExecMethod::EXEC_EXISTS;
 }
 
 bool SELECT_LEX::semijoin_enabled(THD *thd) const {
@@ -4557,6 +4556,19 @@ static bool walk_join_condition(mem_root_deque<TABLE_LIST *> *tables,
   return false;
 }
 
+void SELECT_LEX_UNIT::accumulate_used_tables(table_map map) {
+  DBUG_ASSERT(outer_select());
+  if (item)
+    item->accumulate_used_tables(map);
+  else if (m_lateral_deps)
+    m_lateral_deps |= map;
+}
+
+enum_parsing_context SELECT_LEX_UNIT::place() const {
+  DBUG_ASSERT(outer_select());
+  return item ? item->place() : CTX_DERIVED;
+}
+
 bool SELECT_LEX::walk(Item_processor processor, enum_walk walk, uchar *arg) {
   List_iterator<Item> li(item_list);
   Item *item;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 032fbd880ec2..041a6d2b662f 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -64,11 +64,10 @@
 #include "sql/enum_query_type.h"
 #include "sql/field.h"
 #include "sql/handler.h"
-#include "sql/item.h"            // Name_resolution_context
-#include "sql/item_subselect.h"  // chooser_compare_func_creator
-#include "sql/key_spec.h"        // KEY_CREATE_INFO
-#include "sql/lex_symbol.h"      // LEX_SYMBOL
-#include "sql/lexer_yystype.h"   // Lexer_yystype
+#include "sql/item.h"           // Name_resolution_context
+#include "sql/key_spec.h"       // KEY_CREATE_INFO
+#include "sql/lex_symbol.h"     // LEX_SYMBOL
+#include "sql/lexer_yystype.h"  // Lexer_yystype
 #include "sql/mdl.h"
 #include "sql/mem_root_array.h"  // Mem_root_array
 #include "sql/opt_hints.h"
@@ -96,10 +95,13 @@
 #include "violite.h"   // SSL_type
 
 class Item_cond;
+class Item_exists_subselect;
+class Item_subselect;
 class Item_sum;
 class Event_parse_data;
 class Item_func_match;
 class Parse_tree_root;
+class Query_result_interceptor;
 class Window;
 class sp_pcontext;
 enum class enum_jt_column;
@@ -939,23 +941,14 @@ class SELECT_LEX_UNIT {
     Item_subselect, a derived TABLE_LIST), adds to this object a map
     of tables of the upper level which the unit references.
   */
-  void accumulate_used_tables(table_map map) {
-    DBUG_ASSERT(outer_select());
-    if (item)
-      item->accumulate_used_tables(map);
-    else if (m_lateral_deps)
-      m_lateral_deps |= map;
-  }
+  void accumulate_used_tables(table_map map);
 
   /**
     If unit is a subquery, which forms an object of the upper level (an
     Item_subselect, a derived TABLE_LIST), returns the place of this object
     in the upper level query block.
   */
-  enum_parsing_context place() const {
-    DBUG_ASSERT(outer_select());
-    return item ? item->place() : CTX_DERIVED;
-  }
+  enum_parsing_context place() const;
 
   bool walk(Item_processor processor, enum_walk walk, uchar *arg);
 
@@ -1965,7 +1958,7 @@ class SELECT_LEX {
     @retval EXEC_EXISTS           In-to-exists execution should be used
     @retval EXEC_EXISTS_OR_MAT    A cost-based decision should be made
   */
-  Item_exists_subselect::enum_exec_method subquery_strategy(THD *thd) const;
+  SubqueryExecMethod subquery_strategy(THD *thd) const;
 
   /**
     Returns whether semi-join is enabled for this query block
diff --git a/sql/sql_optimizer.cc b/sql/sql_optimizer.cc
index 28019c2c5e5e..3db988360243 100644
--- a/sql/sql_optimizer.cc
+++ b/sql/sql_optimizer.cc
@@ -69,6 +69,7 @@
 #include "sql/item_cmpfunc.h"
 #include "sql/item_func.h"
 #include "sql/item_row.h"
+#include "sql/item_subselect.h"
 #include "sql/item_sum.h"  // Item_sum
 #include "sql/key.h"
 #include "sql/key_spec.h"
@@ -1217,7 +1218,7 @@ int JOIN::replace_index_subquery() {
 
   JOIN_TAB *const first_join_tab = best_ref[0];
 
-  if (in_subs->exec_method == Item_exists_subselect::EXEC_MATERIALIZATION) {
+  if (in_subs->exec_method == SubqueryExecMethod::EXEC_MATERIALIZATION) {
     // We cannot have two engines at the same time
   } else if (first_join_tab->table_ref->is_view_or_derived() &&
              first_join_tab->table_ref->derived_unit()->is_recursive()) {
@@ -10584,16 +10585,16 @@ bool JOIN::decide_subquery_strategy() {
   Item_in_subselect *const in_pred =
       static_cast<Item_in_subselect *>(unit->item);
 
-  Item_exists_subselect::enum_exec_method chosen_method = in_pred->exec_method;
+  SubqueryExecMethod chosen_method = in_pred->exec_method;
   // Materialization does not allow UNION so this can't happen:
-  DBUG_ASSERT(chosen_method != Item_exists_subselect::EXEC_MATERIALIZATION);
+  DBUG_ASSERT(chosen_method != SubqueryExecMethod::EXEC_MATERIALIZATION);
 
-  if ((chosen_method == Item_exists_subselect::EXEC_EXISTS_OR_MAT) &&
+  if ((chosen_method == SubqueryExecMethod::EXEC_EXISTS_OR_MAT) &&
       compare_costs_of_subquery_strategies(&chosen_method))
     return true;
 
   switch (chosen_method) {
-    case Item_exists_subselect::EXEC_EXISTS:
+    case SubqueryExecMethod::EXEC_EXISTS:
       if (select_lex->m_windows.elements > 0)  // grep for WL#10431
       {
         my_error(ER_NOT_SUPPORTED_YET, MYF(0),
@@ -10602,7 +10603,7 @@ bool JOIN::decide_subquery_strategy() {
         return true;
       }
       return in_pred->finalize_exists_transform(thd, select_lex);
-    case Item_exists_subselect::EXEC_MATERIALIZATION:
+    case SubqueryExecMethod::EXEC_MATERIALIZATION:
       return in_pred->finalize_materialization_transform(thd, this);
     default:
       DBUG_ASSERT(false);
@@ -10629,12 +10630,10 @@ bool JOIN::decide_subquery_strategy() {
                        here.
    @returns false if success
 */
-bool JOIN::compare_costs_of_subquery_strategies(
-    Item_exists_subselect::enum_exec_method *method) {
-  *method = Item_exists_subselect::EXEC_EXISTS;
+bool JOIN::compare_costs_of_subquery_strategies(SubqueryExecMethod *method) {
+  *method = SubqueryExecMethod::EXEC_EXISTS;
 
-  Item_exists_subselect::enum_exec_method allowed_strategies =
-      select_lex->subquery_strategy(thd);
+  SubqueryExecMethod allowed_strategies = select_lex->subquery_strategy(thd);
 
   /*
     A non-deterministic subquery should not use materialization, unless forced.
@@ -10642,15 +10641,14 @@ bool JOIN::compare_costs_of_subquery_strategies(
     Here, the same logic is applied also for subqueries that are not converted
     to semi-join.
   */
-  if (allowed_strategies == Item_exists_subselect::EXEC_EXISTS_OR_MAT &&
+  if (allowed_strategies == SubqueryExecMethod::EXEC_EXISTS_OR_MAT &&
       (unit->uncacheable & UNCACHEABLE_RAND))
-    allowed_strategies = Item_exists_subselect::EXEC_EXISTS;
+    allowed_strategies = SubqueryExecMethod::EXEC_EXISTS;
 
-  if (allowed_strategies == Item_exists_subselect::EXEC_EXISTS) return false;
+  if (allowed_strategies == SubqueryExecMethod::EXEC_EXISTS) return false;
 
-  DBUG_ASSERT(allowed_strategies == Item_exists_subselect::EXEC_EXISTS_OR_MAT ||
-              allowed_strategies ==
-                  Item_exists_subselect::EXEC_MATERIALIZATION);
+  DBUG_ASSERT(allowed_strategies == SubqueryExecMethod::EXEC_EXISTS_OR_MAT ||
+              allowed_strategies == SubqueryExecMethod::EXEC_MATERIALIZATION);
 
   const JOIN *parent_join = unit->outer_select()->join;
   if (!parent_join || !parent_join->child_subquery_can_materialize)
@@ -10717,7 +10715,7 @@ bool JOIN::compare_costs_of_subquery_strategies(
   const double cost_mat =
       cost_mat_table + subq_executions * sjm.lookup_cost.total_cost();
   const bool mat_chosen =
-      (allowed_strategies == Item_exists_subselect::EXEC_EXISTS_OR_MAT)
+      (allowed_strategies == SubqueryExecMethod::EXEC_EXISTS_OR_MAT)
           ? (cost_mat < cost_exists)
           : true;
   trace_subq_mat_decision
@@ -10728,7 +10726,7 @@ bool JOIN::compare_costs_of_subquery_strategies(
       .add("cost_of_EXISTS", cost_exists)
       .add("chosen", mat_chosen);
   if (mat_chosen) {
-    *method = Item_exists_subselect::EXEC_MATERIALIZATION;
+    *method = SubqueryExecMethod::EXEC_MATERIALIZATION;
   } else {
     best_read = saved_best_read;
     best_rowcount = saved_best_rowcount;
diff --git a/sql/sql_optimizer.h b/sql/sql_optimizer.h
index 73cbf7869926..971223ab1b3f 100644
--- a/sql/sql_optimizer.h
+++ b/sql/sql_optimizer.h
@@ -48,7 +48,6 @@
 #include "my_table_map.h"
 #include "sql/field.h"
 #include "sql/item.h"
-#include "sql/item_subselect.h"
 #include "sql/mem_root_array.h"
 #include "sql/opt_explain_format.h"  // Explain_sort_clause
 #include "sql/row_iterator.h"
@@ -62,6 +61,7 @@
 #include "sql/temp_table_param.h"
 
 class COND_EQUAL;
+class Item_subselect;
 class Item_sum;
 class Opt_trace_context;
 class THD;
@@ -967,8 +967,7 @@ class JOIN {
   bool add_having_as_tmp_table_cond(uint curr_tmp_table);
   bool make_tmp_tables_info();
   void set_plan_state(enum_plan_state plan_state_arg);
-  bool compare_costs_of_subquery_strategies(
-      Item_exists_subselect::enum_exec_method *method);
+  bool compare_costs_of_subquery_strategies(SubqueryExecMethod *method);
   ORDER *remove_const(ORDER *first_order, Item *cond, bool change_list,
                       bool *simple_order, bool group_by);
 
diff --git a/sql/sql_resolver.cc b/sql/sql_resolver.cc
index 6b4f84b51c51..ed6c0d449576 100644
--- a/sql/sql_resolver.cc
+++ b/sql/sql_resolver.cc
@@ -1201,7 +1201,7 @@ bool SELECT_LEX::resolve_subquery(THD *thd) {
     that are transformed to semijoin, but for other subqueries, this function
     is called for every execution. One solution is perhaps to define
     exec_method in class Item_subselect and exit immediately if unequal to
-    EXEC_UNSPECIFIED.
+    SubqueryExecMethod::EXEC_UNSPECIFIED.
   */
   Item_subselect *subq_predicate = master_unit()->item;
   DBUG_ASSERT(subq_predicate != nullptr);
@@ -1291,7 +1291,7 @@ bool SELECT_LEX::resolve_subquery(THD *thd) {
       outer->sj_candidates &&                                     // 7
       leaf_table_count &&                                         // 8
       predicate->exec_method ==                                   //  9
-          Item_exists_subselect::EXEC_UNSPECIFIED &&              //  9
+          SubqueryExecMethod::EXEC_UNSPECIFIED &&                 //  9
       outer->leaf_table_count &&                                  // 10
       !((active_options() | outer->active_options()) &            // 11
         SELECT_STRAIGHT_JOIN) &&                                  // 11
@@ -2898,7 +2898,7 @@ bool SELECT_LEX::convert_subquery_to_semijoin(
   nested_join->sj_outer_exprs.empty();
   nested_join->sj_inner_exprs.empty();
 
-  subq_pred->exec_method = Item_exists_subselect::EXEC_SEMI_JOIN;
+  subq_pred->exec_method = SubqueryExecMethod::EXEC_SEMI_JOIN;
 
   if (subq_pred->substype() == Item_subselect::IN_SUBS) {
     Item_in_subselect *in_subq_pred = (Item_in_subselect *)subq_pred;