/* Copyright (c) 2017, 2025, 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 TABLE_FUNCTION_INCLUDED
#define TABLE_FUNCTION_INCLUDED

#include <assert.h>
#include <sys/types.h>
#include <array>  // std::array

#include "my_inttypes.h"
#include "my_table_map.h"
#include "sql-common/json_dom.h"   // Json_wrapper
#include "sql-common/json_path.h"  // Json_path
#include "sql/create_field.h"
#include "sql/enum_query_type.h"
#include "sql/mem_root_array.h"
#include "sql/psi_memory_key.h"  // key_memory_JSON
#include "sql/sql_const.h"       // Item_processor, enum_walk
#include "sql/sql_list.h"        // List
#include "sql/table.h"           // TABLE

class Field;
class Item;
class String;
class Table_function_json;
class THD;

/**
  Class representing a table function.
*/

class Table_function {
 protected:
  /// Table function's result table
  TABLE *table;
  /// Whether the table function was already initialized
  bool inited;

 public:
  explicit Table_function() : table(nullptr), inited(false) {}

  virtual ~Table_function() = default;
  /**
    Create, but not instantiate the result table

    @param thd         thread handler
    @param options     options to create table
    @param table_alias table's alias

    @returns
      true  on error
      false on success
  */
  bool create_result_table(THD *thd, ulonglong options,
                           const char *table_alias);
  /**
    Write current record to the result table and handle overflow to disk

    @returns
      true  on error
      false on success
  */
  bool write_row();

  /**
    Returns a field with given index

    @param i field's index

    @returns
      field with given index
  */
  Field *get_field(uint i) {
    assert(i < table->s->fields);
    return table->field[i];
  }
  /**
    Delete all rows in the table
  */
  void empty_table();

  /**
    Set the default row
  */
  void default_row() {}
  /**
    Initialize table function
    @returns
      true  on error
      false on success
  */
  virtual bool init() = 0;
  /**
    Initialize table function after the result table has been created
    @returns
      true  on error
      false on success
  */
  virtual bool init_args();
  /**
    Execute the table function - fill the result table
    @returns
      true  on error
      false on success
  */
  virtual bool fill_result_table() = 0;
  /**
    Returns table function's name
  */
  virtual const char *func_name() const = 0;
  /**
    Return table_map of tables used by the function
  */
  virtual table_map used_tables() const { return 0; }
  /**
    Print table function

    @param thd         thread handler
    @param str         string to print to
    @param query_type  type of the query

    @returns
      true  on error
      false on success
  */
  virtual bool print(const THD *thd, String *str,
                     enum_query_type query_type) const = 0;
  /**
    Clean up table function after one execution
  */
  void cleanup() { do_cleanup(); }

  /**
    Destroy table function object after all executions are complete
  */
  void destroy() { this->~Table_function(); }

  virtual bool walk(Item_processor processor, enum_walk walk, uchar *arg) = 0;

  /**
    Fix after tables have been moved from one query_block level to the parent
    level, e.g by semijoin conversion.

    @param parent_query_block  query_block that tables are moved to.
    @param removed_query_block query_block that tables are moved away from,
                          child of parent_query_block.
  */
  void fix_after_pullout(Query_block *parent_query_block,
                         Query_block *removed_query_block);

 private:
  /**
    Get the list of fields to create the result table
  */
  virtual List<Create_field> *get_field_list() = 0;
  /**
    Initialize table function's arguments

    @returns
      true  on error
      false on success
  */
  virtual bool do_init_args() = 0;
  friend bool Table_ref::setup_table_function(THD *thd);
  virtual void do_cleanup() {}
  virtual void do_fix_after_pullout(Query_block *parent_query_block,
                                    Query_block *removed_query_block) = 0;
};

/****************************************************************************
  JSON_TABLE function
****************************************************************************/

/// Type of columns for JSON_TABLE function
enum class enum_jt_column {
  JTC_ORDINALITY,
  JTC_PATH,
  JTC_EXISTS,
  JTC_NESTED_PATH
};

/// Types of ON EMPTY/ON ERROR clauses for JSON_TABLE and JSON_VALUE.
/// @note uint16 enum base limitation is necessary for YYSTYPE.
enum class Json_on_response_type : uint16 {
  ERROR,
  NULL_VALUE,
  DEFAULT,
  IMPLICIT
};

/**
  JT_data_source is used as a data source. It's assigned to each NESTED PATH
  node.
*/

class JT_data_source {
 public:
  /// Vector of found values
  Json_wrapper_vector v;
  /// Iterator for vector above
  Json_wrapper_vector::iterator it;
  /// JSON data to seek columns' paths in
  Json_wrapper jdata;
  /// Current m_rowid, used for ORDINALITY columns
  uint m_rowid;
  /**
    true <=> NESTED PATH associated with this element is producing records.
    Used to turn off (set to null) sibling NESTED PATHs, when one of them is
    used to fill result table.
  */
  bool producing_records;

  JT_data_source() : v(key_memory_JSON), producing_records(false) {}

  void cleanup();
};

/**
  Reason for skipping a NESTED PATH
*/
enum jt_skip_reason {
  JTS_NONE = 0,  // NESTED PATH isn't skipped
  JTS_EOD,       // No more data
  JTS_SIBLING    // Skipped due to other sibling NESTED PATH is running
};

/// Column description for JSON_TABLE function
class Json_table_column : public Create_field {
 public:
  /// Column type
  enum_jt_column m_jtc_type;
  /// Type of ON ERROR clause
  Json_on_response_type m_on_error{Json_on_response_type::IMPLICIT};
  /// Type of ON EMPTY clause
  Json_on_response_type m_on_empty{Json_on_response_type::IMPLICIT};
  /// Default value string for ON EMPTY clause
  Item *m_default_empty_string{nullptr};
  /// Parsed JSON for default value of ON MISSING clause
  Json_wrapper m_default_empty_json;
  /// Default value string for ON ERROR clause
  Item *m_default_error_string{nullptr};
  /// Parsed JSON string for ON ERROR clause
  Json_wrapper m_default_error_json;
  /// List of nested columns, valid only for NESTED PATH
  List<Json_table_column> *m_nested_columns{nullptr};
  /// Nested path
  Item *m_path_string{nullptr};
  /// parsed nested path
  Json_path m_path_json{key_memory_JSON};
  /// An element in table function's data source array
  JT_data_source *m_jds_elt{nullptr};
  /**
    Element in table function's data source array to feed data to child
    nodes. Valid only for NESTED PATH.
  */
  JT_data_source *m_child_jds_elt{nullptr};
  ///  Next sibling NESTED PATH
  Json_table_column *m_next_nested{nullptr};
  ///  Previous sibling NESTED PATH
  Json_table_column *m_prev_nested{nullptr};
  /// Index of field in the result table
  int m_field_idx{-1};

 public:
  explicit Json_table_column(enum_jt_column type) : m_jtc_type(type) {}
  Json_table_column(enum_jt_column col_type, Item *path,
                    Json_on_response_type on_err, Item *error_default,
                    Json_on_response_type on_miss, Item *missing_default)
      : m_jtc_type(col_type),
        m_on_error(on_err),
        m_on_empty(on_miss),
        m_default_empty_string(missing_default),
        m_default_error_string(error_default),
        m_path_string(path) {}
  Json_table_column(Item *path, List<Json_table_column> *cols)
      : m_jtc_type(enum_jt_column::JTC_NESTED_PATH),
        m_nested_columns(cols),
        m_path_string(path) {}
  ~Json_table_column();
  void cleanup() {}

  /**
    Fill a json table column

    @details Fills a column with data, according to specification in
    JSON_TABLE. This function handles all kinds of columns:
    Ordinality)  just saves the counter into the column's field
    Path)        extracts value, saves it to the column's field and handles
                 ON ERROR/ON EMPTY clauses
    Exists)      checks the path existence and saves either 1 or 0 into result
                 field
    Nested path) matches the path expression against data source. If there're
                 matches, this function sets NESTED PATH's iterator over those
                 matches and resets ordinality counter.

    @param[in]   table_function the JSON table function
    @param[out]  skip  true <=> it's a NESTED PATH node and its path
                       expression didn't return any matches or a
                       previous sibling NESTED PATH clause still producing
                       records, thus all columns of this NESTED PATH node
                       should be skipped

    @returns
      false column is filled
      true  an error occurred, execution should be stopped
   */
  bool fill_column(Table_function_json *table_function, jt_skip_reason *skip);
};

#define MAX_NESTED_PATH 16

class Table_function_json final : public Table_function {
  /// Array of JSON Data Source for each NESTED PATH clause
  std::array<JT_data_source, MAX_NESTED_PATH> m_jds;
  /// List of fields for tmp table creation
  List<Json_table_column> m_vt_list;
  /// Tree of COLUMN clauses
  List<Json_table_column> *m_columns;
  /// Array of all columns - the flattened tree above
  Mem_root_array<Json_table_column *> m_all_columns;
  /// JSON_TABLE's alias, for error reporting
  const char *m_table_alias;

  /** Whether source data has been parsed. */
  bool is_source_parsed;
  /// JSON_TABLE's data source expression
  Item *source;

 public:
  Table_function_json(const char *alias, Item *a,
                      List<Json_table_column> *cols);

  ~Table_function_json() override {
    for (uint i = 0; i < m_all_columns.size(); i++)
      m_all_columns[i]->~Json_table_column();
  }

  /**
    Returns function's name
  */
  const char *func_name() const override { return "json_table"; }

  List<Json_table_column> get_columns() { return *m_columns; }

  Item *get_source() { return source; }

  /**
    Initialize the table function before creation of result table

    @returns
      true  on error
      false on success
  */
  bool init() override;

  /**
    Execute table function

    @returns
      true  on error
      false on success
  */
  bool fill_result_table() override;

  /**
    Return table_map of tables used by function's data source
  */
  table_map used_tables() const override;

  /**
    JSON_TABLE printout

    @param thd        thread handler
    @param str        string to print to
    @param query_type type of query

    @returns
      true  on error
      false on success
  */
  bool print(const THD *thd, String *str,
             enum_query_type query_type) const override;

  bool walk(Item_processor processor, enum_walk walk, uchar *arg) override;

 private:
  /**
    Fill the result table

    @returns
      true  on error
      false on success
  */
  bool fill_json_table();

  /**
    Initialize columns and lists for json table

    @details This function does several things:
    1) sets up list of fields (vt_list) for result table creation
    2) fills array of all columns (m_all_columns) for execution
    3) for each column that has default ON EMPTY or ON ERROR clauses, checks
      the value to be proper json and initializes column appropriately
    4) for each column that involves path, the path is checked to be correct.
    The function goes recursively, starting from the top NESTED PATH clause
    and going in the depth-first way, traverses the tree of columns.

    @param nest_idx  index of parent's element in the nesting data array
    @param parent    Parent of the NESTED PATH clause being initialized

    @returns
      false  ok
      true   an error occurred
   */
  bool init_json_table_col_lists(uint *nest_idx, Json_table_column *parent);
  /**
    A helper function which sets all columns under given NESTED PATH column
    to nullptr. Used to evaluate sibling NESTED PATHS.

    @param       root  root NESTED PATH column
    @param [out] last  last column which belongs to the given NESTED PATH
   */
  void set_subtree_to_null(Json_table_column *root, Json_table_column **last);

  /**
    Return list of fields to create result table from
  */
  List<Create_field> *get_field_list() override;
  bool do_init_args() override;
  void do_cleanup() override;
  void do_fix_after_pullout(Query_block *parent_query_block,
                            Query_block *removed_query_block) override;
};

/**
  Print ON EMPTY or ON ERROR clauses.

  @param thd             thread handler
  @param str             the string to print to
  @param query_type      formatting options
  @param on_empty        true for ON EMPTY, false for ON ERROR
  @param response_type   the type of the ON ERROR/ON EMPTY response
  @param default_string  the default string in case of DEFAULT type
*/
void print_on_empty_or_error(const THD *thd, String *str,
                             enum_query_type query_type, bool on_empty,
                             Json_on_response_type response_type,
                             const Item *default_string);
#endif /* TABLE_FUNCTION_INCLUDED */
