MDEV-35854 dict_get_referenced_table() got too many parameters

dict_get_referenced_table(): Make the callers responsible for
converting the database and table names.
This commit is contained in:
Marko Mäkelä 2025-01-15 15:21:30 +02:00
parent d5a417b9d5
commit 301ee7bccb
4 changed files with 180 additions and 133 deletions

View file

@ -3222,66 +3222,17 @@ foreign constraint parser to get the referenced table.
heap memory passed in */ heap memory passed in */
char* char*
dict_get_referenced_table( dict_get_referenced_table(
const char* name, /*!< in: foreign key table name */ LEX_CSTRING database_name, /*!< in: table db name */
const char* database_name, /*!< in: table db name */ LEX_CSTRING table_name, /*!< in: table name */
ulint database_name_len, /*!< in: db name length */ dict_table_t** table, /*!< out: table object or NULL */
const char* table_name, /*!< in: table name */ mem_heap_t* heap) noexcept /*!< in/out: heap memory */
ulint table_name_len, /*!< in: table name length */
dict_table_t** table, /*!< out: table object or NULL */
mem_heap_t* heap, /*!< in/out: heap memory */
CHARSET_INFO* from_cs) /*!< in: table name charset */
{ {
char* ref; const size_t len = database_name.length + table_name.length + 1;
char db_name[MAX_DATABASE_NAME_LEN]; char* ref = static_cast<char*>(mem_heap_alloc(heap, len + 1));
char tbl_name[MAX_TABLE_NAME_LEN]; memcpy(ref, database_name.str, database_name.length);
CHARSET_INFO* to_cs = &my_charset_filename; ref[database_name.length] = '/';
uint errors; memcpy(ref + database_name.length + 1, table_name.str,
ut_ad(database_name || name); table_name.length + 1);
ut_ad(table_name);
if (!strncmp(table_name, srv_mysql50_table_name_prefix,
sizeof(srv_mysql50_table_name_prefix) - 1)) {
/* This is a pre-5.1 table name
containing chars other than [A-Za-z0-9].
Discard the prefix and use raw UTF-8 encoding. */
table_name += sizeof(srv_mysql50_table_name_prefix) - 1;
table_name_len -= sizeof(srv_mysql50_table_name_prefix) - 1;
to_cs = system_charset_info;
}
table_name_len = strconvert(from_cs, table_name, table_name_len, to_cs,
tbl_name, MAX_TABLE_NAME_LEN, &errors);
table_name = tbl_name;
if (database_name) {
to_cs = &my_charset_filename;
if (!strncmp(database_name, srv_mysql50_table_name_prefix,
sizeof(srv_mysql50_table_name_prefix) - 1)) {
database_name
+= sizeof(srv_mysql50_table_name_prefix) - 1;
database_name_len
-= sizeof(srv_mysql50_table_name_prefix) - 1;
to_cs = system_charset_info;
}
database_name_len = strconvert(
from_cs, database_name, database_name_len, to_cs,
db_name, MAX_DATABASE_NAME_LEN, &errors);
database_name = db_name;
} else {
/* Use the database name of the foreign key table */
database_name = name;
database_name_len = dict_get_db_name_len(name);
}
/* Copy database_name, '/', table_name, '\0' */
const size_t len = database_name_len + table_name_len + 1;
ref = static_cast<char*>(mem_heap_alloc(heap, len + 1));
memcpy(ref, database_name, database_name_len);
ref[database_name_len] = '/';
memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
/* Values; 0 = Store and compare as given; case sensitive /* Values; 0 = Store and compare as given; case sensitive
1 = Store and compare in lower; case insensitive 1 = Store and compare in lower; case insensitive
@ -3289,10 +3240,10 @@ dict_get_referenced_table(
if (lower_case_table_names == 2) { if (lower_case_table_names == 2) {
innobase_casedn_str(ref); innobase_casedn_str(ref);
*table = dict_sys.load_table({ref, len}); *table = dict_sys.load_table({ref, len});
memcpy(ref, database_name, database_name_len); memcpy(ref, database_name.str, database_name.length);
ref[database_name_len] = '/'; ref[database_name.length] = '/';
memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); memcpy(ref + database_name.length + 1,
table_name.str, table_name.length + 1);
} else { } else {
#ifndef _WIN32 #ifndef _WIN32
if (lower_case_table_names == 1) { if (lower_case_table_names == 1) {

View file

@ -12404,6 +12404,8 @@ create_table_info_t::create_foreign_keys()
const char* ref_column_names[MAX_COLS_PER_FK]; const char* ref_column_names[MAX_COLS_PER_FK];
char create_name[MAX_DATABASE_NAME_LEN + 1 + char create_name[MAX_DATABASE_NAME_LEN + 1 +
MAX_TABLE_NAME_LEN + 1]; MAX_TABLE_NAME_LEN + 1];
char db_name[MAX_DATABASE_NAME_LEN + 1];
char t_name[MAX_TABLE_NAME_LEN + 1];
dict_index_t* index = NULL; dict_index_t* index = NULL;
fkerr_t index_error = FK_SUCCESS; fkerr_t index_error = FK_SUCCESS;
dict_index_t* err_index = NULL; dict_index_t* err_index = NULL;
@ -12411,59 +12413,84 @@ create_table_info_t::create_foreign_keys()
const bool tmp_table = m_flags2 & DICT_TF2_TEMPORARY; const bool tmp_table = m_flags2 & DICT_TF2_TEMPORARY;
const CHARSET_INFO* cs = thd_charset(m_thd); const CHARSET_INFO* cs = thd_charset(m_thd);
const char* operation = "Create "; const char* operation = "Create ";
const char* name = m_table_name;
enum_sql_command sqlcom = enum_sql_command(thd_sql_command(m_thd)); enum_sql_command sqlcom = enum_sql_command(thd_sql_command(m_thd));
LEX_CSTRING name= {m_table_name, strlen(m_table_name)};
if (sqlcom == SQLCOM_ALTER_TABLE) { if (sqlcom == SQLCOM_ALTER_TABLE) {
dict_table_t* table_to_alter;
mem_heap_t* heap = mem_heap_create(10000); mem_heap_t* heap = mem_heap_create(10000);
ulint highest_id_so_far; LEX_CSTRING table_name = m_form->s->table_name;
char* n = dict_get_referenced_table( CHARSET_INFO* to_cs = &my_charset_filename;
name, LEX_STRING_WITH_LEN(m_form->s->db),
LEX_STRING_WITH_LEN(m_form->s->table_name), if (!strncmp(table_name.str, srv_mysql50_table_name_prefix,
&table_to_alter, heap, cs); sizeof srv_mysql50_table_name_prefix - 1)) {
table_name.str
+= sizeof srv_mysql50_table_name_prefix - 1;
table_name.length
-= sizeof srv_mysql50_table_name_prefix - 1;
to_cs = system_charset_info;
}
uint errors;
LEX_CSTRING t;
t.str = t_name;
t.length = strconvert(cs, LEX_STRING_WITH_LEN(table_name),
to_cs, t_name, MAX_TABLE_NAME_LEN,
&errors);
LEX_CSTRING d = m_form->s->db;
if (!strncmp(d.str, srv_mysql50_table_name_prefix,
sizeof srv_mysql50_table_name_prefix - 1)) {
d.str += sizeof srv_mysql50_table_name_prefix - 1;
d.length -= sizeof srv_mysql50_table_name_prefix - 1;
to_cs = system_charset_info;
} else {
to_cs = &my_charset_filename;
}
d.length = strconvert(cs, LEX_STRING_WITH_LEN(d), to_cs,
db_name, MAX_DATABASE_NAME_LEN,
&errors);
d.str = db_name;
dict_table_t* alter_table;
char* n = dict_get_referenced_table(d, t, &alter_table, heap);
/* Starting from 4.0.18 and 4.1.2, we generate foreign key id's /* Starting from 4.0.18 and 4.1.2, we generate foreign key id's
in the format databasename/tablename_ibfk_[number], where in the format databasename/tablename_ibfk_[number], where
[number] is local to the table; look for the highest [number] [number] is local to the table; look for the highest [number]
for table_to_alter, so that we can assign to new constraints for alter_table, so that we can assign to new constraints
higher numbers. */ higher numbers. */
/* If we are altering a temporary table, the table name after /* If we are altering a temporary table, the table name after
ALTER TABLE does not correspond to the internal table name, and ALTER TABLE does not correspond to the internal table name, and
table_to_alter is NULL. TODO: should we fix this somehow? */ alter_table=nullptr. But, we do not support FOREIGN KEY
constraints for temporary tables. */
if (table_to_alter) { if (alter_table) {
n = table_to_alter->name.m_name; n = alter_table->name.m_name;
highest_id_so_far = dict_table_get_highest_foreign_id( number = 1 + dict_table_get_highest_foreign_id(
table_to_alter); alter_table);
} else {
highest_id_so_far = 0;
} }
char* bufend = innobase_convert_name( *innobase_convert_name(create_name, sizeof create_name,
create_name, sizeof create_name, n, strlen(n), m_thd); n, strlen(n), m_thd) = '\0';
create_name[bufend - create_name] = '\0';
number = highest_id_so_far + 1;
mem_heap_free(heap); mem_heap_free(heap);
operation = "Alter "; operation = "Alter ";
} else if (strstr(name, "#P#") || strstr(name, "#p#")) { } else if (strstr(m_table_name, "#P#")
|| strstr(m_table_name, "#p#")) {
/* Partitioned table */ /* Partitioned table */
create_name[0] = '\0'; create_name[0] = '\0';
} else { } else {
char* bufend = innobase_convert_name(create_name, *innobase_convert_name(create_name, sizeof create_name,
sizeof create_name, LEX_STRING_WITH_LEN(name), m_thd)= '\0';
name,
strlen(name), m_thd);
create_name[bufend - create_name] = '\0';
} }
Alter_info* alter_info = m_create_info->alter_info; Alter_info* alter_info = m_create_info->alter_info;
ut_ad(alter_info); ut_ad(alter_info);
List_iterator_fast<Key> key_it(alter_info->key_list); List_iterator_fast<Key> key_it(alter_info->key_list);
dict_table_t* table = dict_sys.find_table({name,strlen(name)}); dict_table_t* table = dict_sys.find_table({name.str, name.length});
if (!table) { if (!table) {
ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name,
"%s table %s foreign key constraint" "%s table %s foreign key constraint"
@ -12510,27 +12537,27 @@ create_table_info_t::create_foreign_keys()
col->field_name.length); col->field_name.length);
success = find_col(table, column_names + i); success = find_col(table, column_names + i);
if (!success) { if (!success) {
key_text k(fk);
ib_foreign_warn( ib_foreign_warn(
m_trx, DB_CANNOT_ADD_CONSTRAINT, m_trx, DB_CANNOT_ADD_CONSTRAINT,
create_name, create_name,
"%s table %s foreign key %s constraint" "%s table %s foreign key %s constraint"
" failed. Column %s was not found.", " failed. Column %s was not found.",
operation, create_name, k.str(), operation, create_name,
key_text(fk).str(),
column_names[i]); column_names[i]);
dict_foreign_free(foreign); dict_foreign_free(foreign);
return (DB_CANNOT_ADD_CONSTRAINT); return (DB_CANNOT_ADD_CONSTRAINT);
} }
++i; ++i;
if (i >= MAX_COLS_PER_FK) { if (i >= MAX_COLS_PER_FK) {
key_text k(fk);
ib_foreign_warn( ib_foreign_warn(
m_trx, DB_CANNOT_ADD_CONSTRAINT, m_trx, DB_CANNOT_ADD_CONSTRAINT,
create_name, create_name,
"%s table %s foreign key %s constraint" "%s table %s foreign key %s constraint"
" failed. Too many columns: %u (%u " " failed. Too many columns: %u (%u "
"allowed).", "allowed).",
operation, create_name, k.str(), i, operation, create_name,
key_text(fk).str(), i,
MAX_COLS_PER_FK); MAX_COLS_PER_FK);
dict_foreign_free(foreign); dict_foreign_free(foreign);
return (DB_CANNOT_ADD_CONSTRAINT); return (DB_CANNOT_ADD_CONSTRAINT);
@ -12542,9 +12569,9 @@ create_table_info_t::create_foreign_keys()
&index_error, &err_col, &err_index); &index_error, &err_col, &err_index);
if (!index) { if (!index) {
key_text k(fk);
foreign_push_index_error(m_trx, operation, create_name, foreign_push_index_error(m_trx, operation, create_name,
k.str(), column_names, key_text(fk).str(),
column_names,
index_error, err_col, index_error, err_col,
err_index, table); err_index, table);
dict_foreign_free(foreign); dict_foreign_free(foreign);
@ -12610,32 +12637,68 @@ create_table_info_t::create_foreign_keys()
memcpy(foreign->foreign_col_names, column_names, memcpy(foreign->foreign_col_names, column_names,
i * sizeof(void*)); i * sizeof(void*));
foreign->referenced_table_name = dict_get_referenced_table( LEX_CSTRING table_name = fk->ref_table;
name, LEX_STRING_WITH_LEN(fk->ref_db), CHARSET_INFO* to_cs = &my_charset_filename;
LEX_STRING_WITH_LEN(fk->ref_table), uint errors;
&foreign->referenced_table, foreign->heap, cs); LEX_CSTRING t = table_name;
LEX_CSTRING d = fk->ref_db;
if (!foreign->referenced_table_name) { if (!d.str) {
return (DB_OUT_OF_MEMORY); d = {table->name.m_name, table->name.dblen()};
} }
if (!strncmp(table_name.str, srv_mysql50_table_name_prefix,
sizeof srv_mysql50_table_name_prefix - 1)) {
table_name.str
+= sizeof srv_mysql50_table_name_prefix - 1;
table_name.length
-= sizeof srv_mysql50_table_name_prefix - 1;
to_cs = system_charset_info;
}
t.str = t_name;
t.length = strconvert(cs, LEX_STRING_WITH_LEN(table_name),
to_cs, t_name,
MAX_TABLE_NAME_LEN, &errors);
if (!strncmp(d.str, srv_mysql50_table_name_prefix,
sizeof srv_mysql50_table_name_prefix - 1)) {
d.str += sizeof srv_mysql50_table_name_prefix - 1;
d.length -= sizeof srv_mysql50_table_name_prefix - 1;
to_cs = system_charset_info;
} else if (d.str == table->name.m_name) {
goto name_converted;
} else {
to_cs = &my_charset_filename;
}
if (d.str != table->name.m_name) {
d.length = strconvert(cs, LEX_STRING_WITH_LEN(d),
to_cs, db_name,
MAX_DATABASE_NAME_LEN,
&errors);
d.str = db_name;
}
name_converted:
foreign->referenced_table_name = dict_get_referenced_table(
d, t, &foreign->referenced_table, foreign->heap);
if (!foreign->referenced_table && m_trx->check_foreigns) { if (!foreign->referenced_table && m_trx->check_foreigns) {
char buf[MAX_TABLE_NAME_LEN + 1] = ""; char buf[MAX_TABLE_NAME_LEN + 1] = "";
char* bufend;
bufend = innobase_convert_name( *innobase_convert_name(
buf, MAX_TABLE_NAME_LEN, buf, MAX_TABLE_NAME_LEN,
foreign->referenced_table_name, foreign->referenced_table_name,
strlen(foreign->referenced_table_name), m_thd); strlen(foreign->referenced_table_name), m_thd)
buf[bufend - buf] = '\0'; = '\0';
key_text k(fk);
ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT,
create_name, create_name,
"%s table %s with foreign key %s " "%s table %s with foreign key %s "
"constraint failed. Referenced table " "constraint failed. Referenced table "
"%s not found in the data dictionary.", "%s not found in the data dictionary.",
operation, create_name, k.str(), buf); operation, create_name,
return (DB_CANNOT_ADD_CONSTRAINT); key_text(fk).str(), buf);
return DB_CANNOT_ADD_CONSTRAINT;
} }
/* Don't allow foreign keys on partitioned tables yet. */ /* Don't allow foreign keys on partitioned tables yet. */
@ -12658,7 +12721,6 @@ create_table_info_t::create_foreign_keys()
success = find_col(foreign->referenced_table, success = find_col(foreign->referenced_table,
ref_column_names + j); ref_column_names + j);
if (!success) { if (!success) {
key_text k(fk);
ib_foreign_warn( ib_foreign_warn(
m_trx, m_trx,
DB_CANNOT_ADD_CONSTRAINT, DB_CANNOT_ADD_CONSTRAINT,
@ -12667,9 +12729,9 @@ create_table_info_t::create_foreign_keys()
"constraint failed. " "constraint failed. "
"Column %s was not found.", "Column %s was not found.",
operation, create_name, operation, create_name,
k.str(), ref_column_names[j]); key_text(fk).str(),
ref_column_names[j]);
return (DB_CANNOT_ADD_CONSTRAINT); return DB_CANNOT_ADD_CONSTRAINT;
} }
} }
++j; ++j;
@ -12689,16 +12751,15 @@ create_table_info_t::create_foreign_keys()
&err_index); &err_index);
if (!index) { if (!index) {
key_text k(fk);
foreign_push_index_error( foreign_push_index_error(
m_trx, operation, create_name, k.str(), m_trx, operation, create_name,
key_text(fk).str(),
column_names, index_error, err_col, column_names, index_error, err_col,
err_index, foreign->referenced_table); err_index, foreign->referenced_table);
return DB_CANNOT_ADD_CONSTRAINT;
return (DB_CANNOT_ADD_CONSTRAINT);
} }
} else { } else {
ut_a(m_trx->check_foreigns == FALSE); ut_a(!m_trx->check_foreigns);
index = NULL; index = NULL;
} }
@ -12735,7 +12796,6 @@ create_table_info_t::create_foreign_keys()
NULL NULL
if the column is not allowed to be if the column is not allowed to be
NULL! */ NULL! */
key_text k(fk);
ib_foreign_warn( ib_foreign_warn(
m_trx, m_trx,
DB_CANNOT_ADD_CONSTRAINT, DB_CANNOT_ADD_CONSTRAINT,
@ -12746,9 +12806,9 @@ create_table_info_t::create_foreign_keys()
"but column '%s' is defined as " "but column '%s' is defined as "
"NOT NULL.", "NOT NULL.",
operation, create_name, operation, create_name,
k.str(), col_name); key_text(fk).str(), col_name);
return (DB_CANNOT_ADD_CONSTRAINT); return DB_CANNOT_ADD_CONSTRAINT;
} }
} }
} }

View file

@ -30,6 +30,7 @@ Smart ALTER TABLE
#include <sql_class.h> #include <sql_class.h>
#include <sql_table.h> #include <sql_table.h>
#include <mysql/plugin.h> #include <mysql/plugin.h>
#include <strfunc.h>
/* Include necessary InnoDB headers */ /* Include necessary InnoDB headers */
#include "btr0sea.h" #include "btr0sea.h"
@ -3231,6 +3232,8 @@ innobase_get_foreign_key_info(
ulint num_fk = 0; ulint num_fk = 0;
Alter_info* alter_info = ha_alter_info->alter_info; Alter_info* alter_info = ha_alter_info->alter_info;
const CHARSET_INFO* cs = thd_charset(trx->mysql_thd); const CHARSET_INFO* cs = thd_charset(trx->mysql_thd);
char db_name[MAX_DATABASE_NAME_LEN + 1];
char t_name[MAX_TABLE_NAME_LEN + 1];
DBUG_ENTER("innobase_get_foreign_key_info"); DBUG_ENTER("innobase_get_foreign_key_info");
@ -3295,14 +3298,51 @@ innobase_get_foreign_key_info(
add_fk[num_fk] = dict_mem_foreign_create(); add_fk[num_fk] = dict_mem_foreign_create();
LEX_CSTRING table_name = fk_key->ref_table;
CHARSET_INFO* to_cs = &my_charset_filename;
if (!strncmp(table_name.str, srv_mysql50_table_name_prefix,
sizeof srv_mysql50_table_name_prefix - 1)) {
table_name.str
+= sizeof srv_mysql50_table_name_prefix - 1;
table_name.length
-= sizeof srv_mysql50_table_name_prefix - 1;
to_cs = system_charset_info;
}
uint errors;
LEX_CSTRING t;
t.str = t_name;
t.length = strconvert(cs, LEX_STRING_WITH_LEN(table_name),
to_cs, t_name, MAX_TABLE_NAME_LEN,
&errors);
LEX_CSTRING d = fk_key->ref_db;
if (!d.str) {
d.str = table->name.m_name;
d.length = table->name.dblen();
}
if (!strncmp(d.str, srv_mysql50_table_name_prefix,
sizeof srv_mysql50_table_name_prefix - 1)) {
d.str += sizeof srv_mysql50_table_name_prefix - 1;
d.length -= sizeof srv_mysql50_table_name_prefix - 1;
to_cs = system_charset_info;
} else if (d.str == table->name.m_name) {
goto name_converted;
} else {
to_cs = &my_charset_filename;
}
d.length = strconvert(cs, LEX_STRING_WITH_LEN(d), to_cs,
db_name, MAX_DATABASE_NAME_LEN,
&errors);
d.str = db_name;
name_converted:
dict_sys.lock(SRW_LOCK_CALL); dict_sys.lock(SRW_LOCK_CALL);
referenced_table_name = dict_get_referenced_table( referenced_table_name = dict_get_referenced_table(
table->name.m_name, d, t, &referenced_table, add_fk[num_fk]->heap);
LEX_STRING_WITH_LEN(fk_key->ref_db),
LEX_STRING_WITH_LEN(fk_key->ref_table),
&referenced_table,
add_fk[num_fk]->heap, cs);
/* Test the case when referenced_table failed to /* Test the case when referenced_table failed to
open, if trx->check_foreigns is not set, we should open, if trx->check_foreigns is not set, we should

View file

@ -62,15 +62,11 @@ foreign constraint parser to get the referenced table.
heap memory passed in */ heap memory passed in */
char* char*
dict_get_referenced_table( dict_get_referenced_table(
/*======================*/ LEX_CSTRING database_name, /*!< in: table db name */
const char* name, /*!< in: foreign key table name */ LEX_CSTRING table_name, /*!< in: table name */
const char* database_name, /*!< in: table db name */
ulint database_name_len,/*!< in: db name length */
const char* table_name, /*!< in: table name */
ulint table_name_len, /*!< in: table name length */
dict_table_t** table, /*!< out: table object or NULL */ dict_table_t** table, /*!< out: table object or NULL */
mem_heap_t* heap, /*!< in: heap memory */ mem_heap_t* heap) /*!< in/out: heap memory */
CHARSET_INFO* from_cs); /*!< in: table name charset */ noexcept MY_ATTRIBUTE((nonnull));
/*********************************************************************//** /*********************************************************************//**
Frees a foreign key struct. */ Frees a foreign key struct. */
void void