/* Copyright (c) 2019, 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 SQL_CONSTRAINT_INCLUDED
#define SQL_CONSTRAINT_INCLUDED

#include "my_inttypes.h"  // ulonglong

class Alter_constraint_enforcement;
class Alter_drop;
class Alter_info;
class KEY;
struct TABLE;
class THD;
namespace dd {
class Table;
}

/**
  Base class to resolve constraints type for the constraints specified in the
  ALTER TABLE ... DROP/ALTER CONSTRAINT operation.

  For a constraint specified in the DROP/ALTER CONSTRAINT operations of a ALTER
  TABLE statement, actual constraint type is unknown. This is the class to
  resolve the actual constraint type (PRIMARY, UNIQUE, FOREIGN KEY or CHECK) of
  a constraint by name. Changes to Alter_info instance are reverted in the
  destructor to make operation safe for re-execution of a stored routines and
  prepared statements.
*/
class Constraint_type_resolver {
 public:
  explicit Constraint_type_resolver(Alter_info *alter_info)
      : m_alter_info(alter_info) {}
  virtual ~Constraint_type_resolver() = default;

  /**
    Method to check if constraint type resolution is needed.

    @retval true  if ALTER TABLE statement has DROP/ALTER CONSTRAINT operations.
    @retval false Otherwise.
  */
  virtual bool is_type_resolution_needed() const = 0;

  /**
    Method to resolve constraints type.

    @param            thd                      Thread handle.
    @param            src_table                TABLE instance of a source table.
    @param            dd_src_table             Data-dictionary table instance of
                                               a source table.

    @retval           false                    Success.
    @retval           true                     Failure.
  */
  virtual bool resolve_constraints_type(THD *thd, const TABLE *src_table,
                                        const dd::Table *dd_src_table) = 0;

 protected:
  /**
    Helper method to check if "name" type is PRIMARY or UNIQUE constraint.

    @param  src_table      TABLE instance of a source table.
    @param  name           Constraint name.

    @retval  non-nullptr   Returns pointer to KEY instance if "name" is PRIMARY
                           or UNIQUE constraint.
    @retval  nullptr       Otherwise.
  */
  KEY *is_primary_or_unique_constraint(const TABLE *src_table,
                                       const char *name);

  /**
    Helper method to check if "name" type is REFERENTIAL constraint.

    @param  dd_src_table   Data-dictionary table instance of a source table.
    @param  name           Constraint name.

    @retval true           If "name" type is REFERENTIAL.
    @retval false          Otherwise.
  */
  bool is_referential_constraint(const dd::Table *dd_src_table,
                                 const char *name);

  /**
    Helper method to check if "name" type is CHECK constraint.

    @param  src_table      TABLE instance of a source table.
    @param  name           Constraint name.

    @retval true           If "name" type is CHECK.
    @retval false          Otherwise.
  */
  bool is_check_constraint(const TABLE *src_table, const char *name);

 protected:
  /// Alter_info instance describing table being altered.
  Alter_info *m_alter_info;
};

/**
  Class to resolve constraints type for the constraints specified in the
  ALTER TABLE ... DROP CONSTRAINT operation.

  For a constraint specified in the DROP CONSTRAINT operations of a ALTER TABLE
  statement, actual constraint type is unknown. This is the class to resolve
  actual constraint type (PRIMARY, UNIQUE, FOREIGN KEY or CHECK) by constraint
  name. Alter_drop elements with actual constraint type are added to the
  Alter_info::Alter_drop list. To make changes safe for re-execution of a stored
  routines and prepared statements, elements added to Alter_drop list are
  removed in the destructor.
*/
class Drop_constraint_type_resolver final : public Constraint_type_resolver {
 public:
  explicit Drop_constraint_type_resolver(Alter_info *alter_info)
      : Constraint_type_resolver(alter_info) {}
  ~Drop_constraint_type_resolver() override;

  /**
    Method to check if constraint type resolution is needed.

    @retval true  if ALTER TABLE statement has DROP CONSTRAINT operations.
    @retval false Otherwise.
  */
  bool is_type_resolution_needed() const override;

  /**
    Method to resolve constraints type.

    @param            thd                      Thread handle.
    @param            src_table                TABLE instance of a source table.
    @param            dd_src_table             Data-dictionary table instance of
                                               a source table.

    @retval           false                    Success.
    @retval           true                     Failure.
  */
  bool resolve_constraints_type(THD *thd, const TABLE *src_table,
                                const dd::Table *dd_src_table) override;

 private:
  /**
    Method to resolve constraint type.

    @param            thd                      Thread handle.
    @param            src_table                TABLE instance of a source table.
    @param            dd_src_table             Data-dictionary table instance of
                                               a source table.
    @param            drop                     Alter_drop instance for which
                                               type should be resolved.

    @retval           false                    Success.
    @retval           true                     Failure.
  */
  bool resolve_constraint_type(THD *thd, const TABLE *src_table,
                               const dd::Table *dd_src_table,
                               const Alter_drop *drop);

 private:
  /// Flags set in Alter_info::flags while fixing type for constraint.
  ulonglong m_flags{0};

  /*
    Alter_drop element with actual type is added to the Alter_info::drop_list
    for each DROP CONSTRAINT operation. Member holds the position of the first
    Alter_drop element with actual type in the Alter_info::drop_list list.
  */
  uint m_first_fixed_alter_drop_pos{0};
};

/**
  Class to resolve constraints type for the constraints specified in the
  ALTER TABLE ... ALTER CONSTRAINT operation.

  For a constraint specified in the ALTER CONSTRAINT operations of a ALTER TABLE
  statement, actual constraint type is unknown. This is the class to resolve
  actual constraint type (PRIMARY, UNIQUE, FOREIGN KEY or CHECK) by constraint
  name. Alter_constraint_enforcement elements with actual constraint type are
  added to the alter_constraint_enforcement_list. To make changes safe for
  re-execution of stored routines and prepared statements, elements added to
  list are removed in the destructor.
*/
class Enforce_constraint_type_resolver final : public Constraint_type_resolver {
 public:
  explicit Enforce_constraint_type_resolver(Alter_info *alter_info)
      : Constraint_type_resolver(alter_info) {}
  ~Enforce_constraint_type_resolver() override;

  /**
    Method to check if constraint type resolution is needed.

    @retval true  if ALTER TABLE statement has ALTER CONSTRAINT operations.
    @retval false Otherwise.
  */
  bool is_type_resolution_needed() const override;

  /**
    Method to resolve constraints type.

    @param            thd                      Thread handle.
    @param            src_table                TABLE instance of a source table.
    @param            dd_src_table             Data-dictionary table instance of
                                               a source table.

    @retval           false                    Success.
    @retval           true                     Failure.
  */
  bool resolve_constraints_type(THD *thd, const TABLE *src_table,
                                const dd::Table *dd_src_table) override;

 private:
  /**
    Method to resolve constraint type.

    @param            thd                      Thread handle.
    @param            src_table                TABLE instance of a source table.
    @param            dd_src_table             Data-dictionary table instance of
                                               a source table.
    @param            alter_constraint         Alter_constraint_enforcement
                                               instance for which type should be
                                               resolved.

    @retval           false                    Success.
    @retval           true                     Failure.
  */
  bool resolve_constraint_type(
      THD *thd, const TABLE *src_table, const dd::Table *dd_src_table,
      const Alter_constraint_enforcement *alter_constraint);

 private:
  /// Flags set in Alter_info::flags while fixing type for check constraint.
  ulonglong m_flags{0};

  /*
    Alter_constraint_enforcement element with actual type is added to the
    Alter_info::alter_constraint_enforcement_list for each ALTER CONSTRAINT
    operation. Member holds position of the first Alter_constraint_enforcement
    element with actual type in the alter_constraint_enforcement_list.
  */
  uint m_first_fixed_alter_constraint_pos{0};
};
#endif  // SQL_CONSTRAINT_INCLUDED
