mysql-server/sql/sql_constraint.cc
2025-03-05 14:31:37 +07:00

261 lines
9.3 KiB
C++

/* 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 */
#include "sql/sql_constraint.h"
#include "my_sys.h" // my_error
#include "mysql/strings/m_ctype.h"
#include "mysqld_error.h" // ER_*
#include "sql/dd/types/table.h" // dd::Table
#include "sql/field.h" // Field
#include "sql/key.h" // KEY
#include "sql/sql_alter.h" // Alter_info
#include "sql/sql_class.h" // THD
#include "sql/table.h" // TABLE
KEY *Constraint_type_resolver::is_primary_or_unique_constraint(
const TABLE *src_table, const char *name) {
if (src_table->key_info == nullptr) return nullptr;
for (KEY *key_info = src_table->key_info;
key_info < (src_table->key_info + src_table->s->keys); key_info++) {
if ((key_info->flags & HA_NOSAME) &&
!my_strcasecmp(system_charset_info, key_info->name, name))
return key_info;
}
return nullptr;
}
bool Constraint_type_resolver::is_referential_constraint(
const dd::Table *dd_src_table, const char *name) {
if (dd_src_table == nullptr) return false;
for (const dd::Foreign_key *fk : dd_src_table->foreign_keys()) {
if (!my_strcasecmp(system_charset_info, fk->name().c_str(), name))
return true;
}
return false;
}
bool Constraint_type_resolver::is_check_constraint(const TABLE *src_table,
const char *name) {
if (src_table->table_check_constraint_list == nullptr) return false;
for (Sql_table_check_constraint &table_cc :
*src_table->table_check_constraint_list) {
if (!my_strcasecmp(system_charset_info, table_cc.name().str, name))
return true;
}
return false;
}
Drop_constraint_type_resolver::~Drop_constraint_type_resolver() {
if (!is_type_resolution_needed()) return;
// Erase Alter_drop elements added while resolving constraint type.
m_alter_info->drop_list.erase(
m_alter_info->drop_list.cbegin() + m_first_fixed_alter_drop_pos,
m_alter_info->drop_list.cend());
// Reset Alter_info::flags.
m_alter_info->flags |= m_flags;
}
bool Drop_constraint_type_resolver::is_type_resolution_needed() const {
return (m_alter_info->flags & Alter_info::DROP_ANY_CONSTRAINT);
}
bool Drop_constraint_type_resolver::resolve_constraint_type(
THD *thd, const TABLE *src_table, const dd::Table *dd_src_table,
const Alter_drop *drop) {
Alter_drop::drop_type type = Alter_drop::ANY_CONSTRAINT;
uint16_t found_count = 0;
// PRIMARY and UNIQUE constraint.
KEY *key_info = is_primary_or_unique_constraint(src_table, drop->name);
if (key_info != nullptr) {
found_count++;
type = Alter_drop::KEY;
m_flags |= ((m_alter_info->flags ^ Alter_info::ALTER_DROP_INDEX) &
Alter_info::ALTER_DROP_INDEX);
}
// REFERENTIAL constraint.
if (is_referential_constraint(dd_src_table, drop->name)) {
found_count++;
type = Alter_drop::FOREIGN_KEY;
m_flags |= ((m_alter_info->flags ^ Alter_info::DROP_FOREIGN_KEY) &
Alter_info::DROP_FOREIGN_KEY);
}
// CHECK constraint.
if ((found_count < 2) && is_check_constraint(src_table, drop->name)) {
found_count++;
type = Alter_drop::CHECK_CONSTRAINT;
m_flags |= ((m_alter_info->flags ^ Alter_info::DROP_CHECK_CONSTRAINT) &
Alter_info::DROP_CHECK_CONSTRAINT);
}
if (found_count == 2) {
my_error(ER_MULTIPLE_CONSTRAINTS_WITH_SAME_NAME, MYF(0), drop->name,
"DROP");
return true;
}
if (type == Alter_drop::ANY_CONSTRAINT) {
my_error(ER_CONSTRAINT_NOT_FOUND, MYF(0), drop->name);
return true;
}
// Add new Alter_drop element with actual type to drop_list.
Alter_drop *new_drop = new (thd->mem_root) Alter_drop(type, drop->name);
if (!new_drop) return true; // OOM error.
m_alter_info->drop_list.push_back(new_drop);
if (key_info == nullptr) return false;
/*
If constraint is a PRIMARY or UNIQUE then check if it is a functional
index. In that case, add hidden generated columns of functional index
to the drop list.
*/
for (uint i = 0; i < key_info->user_defined_key_parts; i++) {
if (key_info->key_part[i].field->is_field_for_functional_index()) {
Alter_drop *column_drop = new (thd->mem_root) Alter_drop(
Alter_drop::COLUMN, key_info->key_part[i].field->field_name);
if (!column_drop) return true; // OOM error.
m_alter_info->drop_list.push_back(column_drop);
m_flags |= ((m_alter_info->flags ^ Alter_info::ALTER_DROP_COLUMN) &
Alter_info::ALTER_DROP_COLUMN);
}
}
return false;
}
bool Drop_constraint_type_resolver::resolve_constraints_type(
THD *thd, const TABLE *src_table, const dd::Table *dd_src_table) {
m_first_fixed_alter_drop_pos = m_alter_info->drop_list.size();
for (const Alter_drop *drop : m_alter_info->drop_list) {
if (drop->type != Alter_drop::ANY_CONSTRAINT) continue;
if (resolve_constraint_type(thd, src_table, dd_src_table, drop))
return true;
}
// Update Alter_info flags.
m_alter_info->flags |= m_flags;
return false;
}
Enforce_constraint_type_resolver::~Enforce_constraint_type_resolver() {
if (!is_type_resolution_needed()) return;
/*
Erase Alter_constraint_enforcement elements added while resolving
constraint type.
*/
m_alter_info->alter_constraint_enforcement_list.erase(
m_alter_info->alter_constraint_enforcement_list.cbegin() +
m_first_fixed_alter_constraint_pos,
m_alter_info->alter_constraint_enforcement_list.cend());
// Reset Alter_info::flags.
m_alter_info->flags |= m_flags;
}
bool Enforce_constraint_type_resolver::is_type_resolution_needed() const {
return (m_alter_info->flags & (Alter_info::ENFORCE_ANY_CONSTRAINT |
Alter_info::SUSPEND_ANY_CONSTRAINT));
}
bool Enforce_constraint_type_resolver::resolve_constraint_type(
THD *thd, const TABLE *src_table, const dd::Table *dd_src_table,
const Alter_constraint_enforcement *alter_constraint) {
Alter_constraint_enforcement::Type type =
Alter_constraint_enforcement::Type::ANY_CONSTRAINT;
// CHECK constraint.
if (is_check_constraint(src_table, alter_constraint->name)) {
type = Alter_constraint_enforcement::Type::CHECK_CONSTRAINT;
const ulonglong check_cons_flag =
alter_constraint->is_enforced ? Alter_info::ENFORCE_CHECK_CONSTRAINT
: Alter_info::SUSPEND_CHECK_CONSTRAINT;
m_flags |= ((m_alter_info->flags ^ check_cons_flag) & check_cons_flag);
}
// PRIMARY, UNIQUE or REFERENTIAL constraint.
if ((is_primary_or_unique_constraint(src_table, alter_constraint->name) !=
nullptr) ||
is_referential_constraint(dd_src_table, alter_constraint->name)) {
if (type != Alter_constraint_enforcement::Type::ANY_CONSTRAINT)
my_error(ER_MULTIPLE_CONSTRAINTS_WITH_SAME_NAME, MYF(0),
alter_constraint->name, "ALTER");
else {
// Enforcement state alter is supported for only CHECK constraints.
my_error(ER_ALTER_CONSTRAINT_ENFORCEMENT_NOT_SUPPORTED, MYF(0),
alter_constraint->name);
}
return true;
}
if (type == Alter_constraint_enforcement::Type::ANY_CONSTRAINT) {
my_error(ER_CONSTRAINT_NOT_FOUND, MYF(0), alter_constraint->name);
return true;
}
/*
Add new Alter_constraint_enforcement element with the actual type to
alter_constraint_enforcement list.
*/
Alter_constraint_enforcement *new_alter_constraint =
new (thd->mem_root) Alter_constraint_enforcement(
type, alter_constraint->name, alter_constraint->is_enforced);
if (!new_alter_constraint) return true; // OOM error.
m_alter_info->alter_constraint_enforcement_list.push_back(
new_alter_constraint);
return false;
}
bool Enforce_constraint_type_resolver::resolve_constraints_type(
THD *thd, const TABLE *src_table, const dd::Table *dd_src_table) {
m_first_fixed_alter_constraint_pos =
m_alter_info->alter_constraint_enforcement_list.size();
for (const Alter_constraint_enforcement *alter_constraint :
m_alter_info->alter_constraint_enforcement_list) {
if (alter_constraint->type !=
Alter_constraint_enforcement::Type::ANY_CONSTRAINT)
continue;
if (resolve_constraint_type(thd, src_table, dd_src_table, alter_constraint))
return true;
}
// Update Alter_info flags.
m_alter_info->flags |= m_flags;
return false;
}