/* Copyright (c) 2011, 2024, Oracle and/or its affiliates.

   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 designed to work 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 either included with
   the program or referenced in the documentation.

   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 */

#ifndef OPT_EXPLAIN_INCLUDED
#define OPT_EXPLAIN_INCLUDED

/**
  @file sql/opt_explain.h
  EXPLAIN @<command@>.

Single table UPDATE/DELETE commands are explained by the
explain_single_table_modification() function.

A query expression (complete SELECT query possibly including
subqueries and unions), INSERT...SELECT and multitable UPDATE/DELETE
commands are explained like this:

(1) explain_query_expression()

Is the entry point. Forwards the job to explain_query_expression().

(2) explain_query_expression()

Is for a Query_expression, prepares, optimizes, explains one JOIN for each
"top-level" Query_blocks of the unit (like: all SELECTs of a UNION; but not
subqueries), and one or more JOIN for the post processing block Query_block of
set operations (e.g. UNION), see query_term.h; each JOIN explain (JOIN::exec())
calls explain_query_specification()

(3) explain_query_specification()

Is for a single Query_block (post processing or not, see query_term.h).
It needs a prepared and optimized JOIN, for which it builds the EXPLAIN rows.
But it also launches the EXPLAIN process for "inner units" (==subqueries of this
Query_block), by calling explain_query_expression() for each of them.
*/

#include "my_base.h"
#include "my_sqlcommand.h"
#include "my_thread_local.h"
#include "sql/opt_explain_format.h"
#include "sql/parse_tree_node_base.h"
#include "sql/query_result.h"  // Query_result_send
#include "sql/sql_cmd.h"       // Sql_cmd
#include "sql/sql_opt_exec_shared.h"
#include "sys/types.h"

class Item;
class Query_block;
class Query_expression;
class Query_term;
class String;
class THD;
struct AccessPath;
struct TABLE;
template <class T>
class mem_root_deque;

extern const char *join_type_str[];

/** Table modification plan for JOIN-less statements (update/delete) */
class Modification_plan {
 public:
  THD *const thd;  ///< Owning thread
  const enum_mod_type
      mod_type;  ///< Modification type - MT_INSERT/MT_UPDATE/etc
  TABLE *table;  ///< Table to modify

  enum join_type type = JT_UNKNOWN;
  AccessPath *range_scan{nullptr};
  Item *condition{nullptr};
  uint key;                   ///< Key to use
  ha_rows limit;              ///< Limit
  bool need_tmp_table;        ///< Whether tmp table needs to be used
  bool need_sort;             ///< Whether to use filesort
  bool used_key_is_modified;  ///< Whether the key used to scan is modified
  const char *message;        ///< Arbitrary message
  bool zero_result;           ///< true <=> plan will not be executed
  ha_rows examined_rows;  ///< # of rows expected to be examined in the table

  Modification_plan(THD *thd_arg, enum_mod_type mt, TABLE *table_arg,
                    enum join_type type_arg, AccessPath *quick_arg,
                    Item *condition_arg, uint key_arg, ha_rows limit_arg,
                    bool need_tmp_table_arg, bool need_sort_arg,
                    bool used_key_is_modified_arg, ha_rows rows);

  Modification_plan(THD *thd_arg, enum_mod_type mt, TABLE *table_arg,
                    const char *message_arg, bool zero_result_arg,
                    ha_rows rows);

  ~Modification_plan();

 private:
  void register_in_thd();
};

/**
  EXPLAIN functionality for Query_result_insert, Query_result_update and
  Query_result_delete.

  This class objects substitute Query_result_insert, Query_result_update and
  Query_result_delete data interceptor objects to implement EXPLAIN for INSERT,
  REPLACE and multi-table UPDATE and DELETE queries.
  Query_result_explain class object initializes tables like Query_result_insert,
  Query_result_update or Query_result_delete data interceptor do, but it
  suppresses table data modification by the underlying interceptor object.
  Thus, we can use Query_result_explain object in the context of EXPLAIN INSERT/
  REPLACE/UPDATE/DELETE query like we use Query_result_send in the context of
  EXPLAIN SELECT command:
  1) in presence of lex->describe flag, pass Query_result_explain object to
     execution function,
  2) it calls prepare(), optimize() and start_execution() functions
     to mark modified tables etc.
*/

class Query_result_explain : public Query_result_send {
 protected:
  /**
    Pointer to underlying Query_result_insert, Query_result_update or
    Query_result_delete object.
  */
  Query_result *interceptor;

 public:
  Query_result_explain(Query_expression *unit_arg,
                       Query_result *interceptor_arg)
      : Query_result_send(), interceptor(interceptor_arg) {
    unit = unit_arg;
  }
  bool use_protocol_adapter() const override { return false; }

 protected:
  bool prepare(THD *thd, const mem_root_deque<Item *> &list,
               Query_expression *u) override {
    return Query_result_send::prepare(thd, list, u) ||
           interceptor->prepare(thd, list, u);
  }

  bool start_execution(THD *thd) override {
    return Query_result_send::start_execution(thd) ||
           interceptor->start_execution(thd);
  }

  void cleanup() override {
    Query_result_send::cleanup();
    interceptor->cleanup();
  }
};

/**
 * Wrapper class for writing EXPLAIN output to a user variable.
 *
 * This class overrides Query_result_send::send_data() to write the output of
 * the EXPLAIN query to the user variable specified by m_variable_name.
 */
class Query_result_explain_into_var final : public Query_result_explain {
 public:
  Query_result_explain_into_var(Query_expression *expr, Query_result *child,
                                std::string_view variable_name)
      : Query_result_explain(expr, child), m_variable_name(variable_name) {}

  bool send_result_set_metadata(THD *, const mem_root_deque<Item *> &,
                                uint) override {
    return false;
  }

  bool send_data(THD *thd, const mem_root_deque<Item *> &items) override;

  bool send_eof(THD *thd) override;

 private:
  std::string_view m_variable_name;
};

bool explain_single_table_modification(THD *explain_thd, const THD *query_thd,
                                       const Modification_plan *plan,
                                       Query_block *select);
bool explain_query(THD *explain_thd, const THD *query_thd,
                   Query_expression *unit);
bool explain_query_specification(THD *explain_thd, const THD *query_thd,
                                 Query_term *query_term,
                                 enum_parsing_context ctx);

class Sql_cmd_explain_other_thread final : public Sql_cmd {
 public:
  explicit Sql_cmd_explain_other_thread(my_thread_id thread_id)
      : m_thread_id(thread_id) {}

  enum_sql_command sql_command_code() const override {
    return SQLCOM_EXPLAIN_OTHER;
  }

  bool execute(THD *thd) override;

 private:
  /// connection_id in EXPLAIN FOR CONNECTION \<connection_id\>
  my_thread_id m_thread_id;
};

// Used to generate the "query" field in JSON explain object.
void print_query_for_explain(const THD *query_thd, Query_expression *unit,
                             String *str);

#endif /* OPT_EXPLAIN_INCLUDED */
