MDEV-35854: Simplify dict_get_referenced_table()

innodb_convert_name(): Convert a schema or table name to
my_charset_filename compatible format.

dict_table_lookup(): Replaces dict_get_referenced_table().
Make the callers responsible for invoking innodb_convert_name().

innobase_casedn_str(): Remove. Let us invoke my_casedn_str() directly.

dict_table_rename_in_cache(): Do not duplicate a call to
dict_mem_foreign_table_name_lookup_set().

innobase_convert_to_filename_charset(): Defined static in the only
compilation unit that needs it.

dict_scan_id(): Remove the constant parameters
table_id=FALSE, accept_also_dot=TRUE. Invoke strconvert() directly.

innobase_convert_from_id(): Remove; only called from dict_scan_id().

innobase_convert_from_table_id(): Remove (dead code).

table_name_t::dblen(), table_name_t::basename(): In non-debug builds,
tolerate names that may miss a '/' separator.

Reviewed by: Debarun Banerjee
This commit is contained in:
Marko Mäkelä 2025-01-23 14:38:08 +02:00
parent fa74c1a40f
commit d4da659b43
11 changed files with 245 additions and 324 deletions

View File

@ -1118,5 +1118,29 @@ test.binaries check status OK
test.collections check status OK test.collections check status OK
disconnect con1; disconnect con1;
DROP TABLE binaries, collections; DROP TABLE binaries, collections;
CREATE SCHEMA `#mysql50##mysql50#d-b`;
CREATE TABLE `#mysql50##mysql50#d-b`.t1 (a INT PRIMARY KEY, b INT UNIQUE) engine=InnoDB;
USE `#mysql50##mysql50#d-b`;
CREATE TABLE t2 (a INT PRIMARY KEY, b INT UNIQUE REFERENCES t1(b)) ENGINE=InnoDB;
SET STATEMENT foreign_key_checks=0 FOR
ALTER TABLE t2 ADD FOREIGN KEY (a) REFERENCES t1(a);
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `b` (`b`),
CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`b`) REFERENCES `t1` (`b`),
CONSTRAINT `t2_ibfk_2` FOREIGN KEY (`a`) REFERENCES `t1` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
INSERT INTO t1 SET a=1;
INSERT INTO t2 SET a=1;
DELETE FROM t1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`#mysql50#d-b`.`t2`, CONSTRAINT `t2_ibfk_2` FOREIGN KEY (`a`) REFERENCES `t1` (`a`))
DELETE FROM t2;
DELETE FROM t1;
DROP DATABASE `#mysql50##mysql50#d-b`;
USE test;
# End of 10.6 tests # End of 10.6 tests
SET GLOBAL innodb_stats_persistent = @save_stats_persistent; SET GLOBAL innodb_stats_persistent = @save_stats_persistent;

View File

@ -4,6 +4,7 @@
--disable_query_log --disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to "); call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
call mtr.add_suppression("Invalid \\(old\\?\\) table or database name '#mysql50#d-b'");
--enable_query_log --enable_query_log
SET GLOBAL innodb_stats_persistent = 0; SET GLOBAL innodb_stats_persistent = 0;
@ -1188,6 +1189,22 @@ CHECK TABLE binaries, collections EXTENDED;
# Cleanup # Cleanup
DROP TABLE binaries, collections; DROP TABLE binaries, collections;
CREATE SCHEMA `#mysql50##mysql50#d-b`;
CREATE TABLE `#mysql50##mysql50#d-b`.t1 (a INT PRIMARY KEY, b INT UNIQUE) engine=InnoDB;
USE `#mysql50##mysql50#d-b`;
CREATE TABLE t2 (a INT PRIMARY KEY, b INT UNIQUE REFERENCES t1(b)) ENGINE=InnoDB;
SET STATEMENT foreign_key_checks=0 FOR
ALTER TABLE t2 ADD FOREIGN KEY (a) REFERENCES t1(a);
SHOW CREATE TABLE t2;
INSERT INTO t1 SET a=1;
INSERT INTO t2 SET a=1;
--error ER_ROW_IS_REFERENCED_2
DELETE FROM t1;
DELETE FROM t2;
DELETE FROM t1;
DROP DATABASE `#mysql50##mysql50#d-b`;
USE test;
--echo # End of 10.6 tests --echo # End of 10.6 tests
SET GLOBAL innodb_stats_persistent = @save_stats_persistent; SET GLOBAL innodb_stats_persistent = @save_stats_persistent;

View File

@ -1467,6 +1467,26 @@ dict_table_t::rename_tablespace(span<const char> new_name, bool replace) const
return err; return err;
} }
/**********************************************************************
Converts an identifier from my_charset_filename to UTF-8 charset.
@return result string length, as returned by strconvert() */
static
uint
innobase_convert_to_filename_charset(
/*=================================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len) /* in: length of 'to', in bytes */
{
uint errors;
CHARSET_INFO* cs_to = &my_charset_filename;
CHARSET_INFO* cs_from = system_charset_info;
return(static_cast<uint>(strconvert(
cs_from, from, uint(strlen(from)),
cs_to, to, static_cast<uint>(len), &errors)));
}
/**********************************************************************//** /**********************************************************************//**
Renames a table object. Renames a table object.
@return TRUE if success */ @return TRUE if success */
@ -1599,19 +1619,20 @@ dict_table_rename_in_cache(
foreign->referenced_table->referenced_set.erase(foreign); foreign->referenced_table->referenced_set.erase(foreign);
} }
if (strlen(foreign->foreign_table_name) const bool do_alloc = strlen(foreign->foreign_table_name)
< strlen(table->name.m_name)) { < strlen(table->name.m_name);
if (do_alloc) {
/* Allocate a longer name buffer; /* Allocate a longer name buffer;
TODO: store buf len to save memory */ TODO: store buf len to save memory */
foreign->foreign_table_name = mem_heap_strdup( foreign->foreign_table_name = mem_heap_strdup(
foreign->heap, table->name.m_name); foreign->heap, table->name.m_name);
dict_mem_foreign_table_name_lookup_set(foreign, TRUE);
} else { } else {
strcpy(foreign->foreign_table_name, strcpy(foreign->foreign_table_name,
table->name.m_name); table->name.m_name);
dict_mem_foreign_table_name_lookup_set(foreign, FALSE);
} }
dict_mem_foreign_table_name_lookup_set(foreign, do_alloc);
if (strchr(foreign->id, '/')) { if (strchr(foreign->id, '/')) {
/* This is a >= 4.0.18 format id */ /* This is a >= 4.0.18 format id */
@ -3105,20 +3126,13 @@ dict_scan_id(
mem_heap_t* heap, /*!< in: heap where to allocate the id mem_heap_t* heap, /*!< in: heap where to allocate the id
(NULL=id will not be allocated, but it (NULL=id will not be allocated, but it
will point to string near ptr) */ will point to string near ptr) */
const char** id, /*!< out,own: the id; NULL if no id was const char** id) /*!< out,own: the id; NULL if no id was
scannable */ scannable */
ibool table_id,/*!< in: TRUE=convert the allocated id
as a table name; FALSE=convert to UTF-8 */
ibool accept_also_dot)
/*!< in: TRUE if also a dot can appear in a
non-quoted id; in a quoted id it can appear
always */
{ {
char quote = '\0'; char quote = '\0';
ulint len = 0; ulint len = 0;
const char* s; const char* s;
char* str; char* str;
char* dst;
*id = NULL; *id = NULL;
@ -3154,7 +3168,6 @@ dict_scan_id(
} }
} else { } else {
while (!my_isspace(cs, *ptr) && *ptr != '(' && *ptr != ')' while (!my_isspace(cs, *ptr) && *ptr != '(' && *ptr != ')'
&& (accept_also_dot || *ptr != '.')
&& *ptr != ',' && *ptr != '\0') { && *ptr != ',' && *ptr != '\0') {
ptr++; ptr++;
@ -3188,125 +3201,15 @@ dict_scan_id(
str = mem_heap_strdupl(heap, s, len); str = mem_heap_strdupl(heap, s, len);
} }
if (!table_id) { ulint dstlen = 3 * len + 1;
convert_id: char *dst = static_cast<char*>(mem_heap_alloc(heap, dstlen));
/* Convert the identifier from connection character set *id = dst;
to UTF-8. */ uint errors;
len = 3 * len + 1; strconvert(cs, str, uint(len), system_charset_info, dst,
*id = dst = static_cast<char*>(mem_heap_alloc(heap, len)); uint(dstlen), &errors);
innobase_convert_from_id(cs, dst, str, len);
} else if (!strncmp(str, 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. */
str += sizeof(srv_mysql50_table_name_prefix) - 1;
len -= sizeof(srv_mysql50_table_name_prefix) - 1;
goto convert_id;
} else {
/* Encode using filename-safe characters. */
len = 5 * len + 1;
*id = dst = static_cast<char*>(mem_heap_alloc(heap, len));
innobase_convert_from_table_id(cs, dst, str, len);
}
return(ptr); return(ptr);
} }
/*********************************************************************//**
Open a table from its database and table name, this is currently used by
foreign constraint parser to get the referenced table.
@return complete table name with database and table name, allocated from
heap memory passed in */
char*
dict_get_referenced_table(
const char* name, /*!< in: foreign key 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 */
mem_heap_t* heap, /*!< in/out: heap memory */
CHARSET_INFO* from_cs) /*!< in: table name charset */
{
char* ref;
char db_name[MAX_DATABASE_NAME_LEN];
char tbl_name[MAX_TABLE_NAME_LEN];
CHARSET_INFO* to_cs = &my_charset_filename;
uint errors;
ut_ad(database_name || name);
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
1 = Store and compare in lower; case insensitive
2 = Store as given, compare in lower; case semi-sensitive */
if (lower_case_table_names == 2) {
innobase_casedn_str(ref);
*table = dict_sys.load_table({ref, len});
memcpy(ref, database_name, database_name_len);
ref[database_name_len] = '/';
memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
} else {
#ifndef _WIN32
if (lower_case_table_names == 1) {
innobase_casedn_str(ref);
}
#else
innobase_casedn_str(ref);
#endif /* !_WIN32 */
*table = dict_sys.load_table({ref, len});
}
return(ref);
}
/*********************************************************************//** /*********************************************************************//**
Removes MySQL comments from an SQL string. A comment is either Removes MySQL comments from an SQL string. A comment is either
(a) '#' to the end of the line, (a) '#' to the end of the line,
@ -3563,7 +3466,7 @@ loop:
} }
} }
ptr = dict_scan_id(cs, ptr, heap, &id, FALSE, TRUE); ptr = dict_scan_id(cs, ptr, heap, &id);
if (id == NULL) { if (id == NULL) {

View File

@ -816,7 +816,7 @@ void
dict_mem_foreign_table_name_lookup_set( dict_mem_foreign_table_name_lookup_set(
/*===================================*/ /*===================================*/
dict_foreign_t* foreign, /*!< in/out: foreign struct */ dict_foreign_t* foreign, /*!< in/out: foreign struct */
ibool do_alloc) /*!< in: is an alloc needed */ bool do_alloc) /*!< in: is an alloc needed */
{ {
if (lower_case_table_names == 2) { if (lower_case_table_names == 2) {
if (do_alloc) { if (do_alloc) {
@ -830,7 +830,8 @@ dict_mem_foreign_table_name_lookup_set(
} }
strcpy(foreign->foreign_table_name_lookup, strcpy(foreign->foreign_table_name_lookup,
foreign->foreign_table_name); foreign->foreign_table_name);
innobase_casedn_str(foreign->foreign_table_name_lookup); my_casedn_str(system_charset_info,
foreign->foreign_table_name_lookup);
} else { } else {
foreign->foreign_table_name_lookup foreign->foreign_table_name_lookup
= foreign->foreign_table_name; = foreign->foreign_table_name;
@ -860,7 +861,8 @@ dict_mem_referenced_table_name_lookup_set(
} }
strcpy(foreign->referenced_table_name_lookup, strcpy(foreign->referenced_table_name_lookup,
foreign->referenced_table_name); foreign->referenced_table_name);
innobase_casedn_str(foreign->referenced_table_name_lookup); my_casedn_str(system_charset_info,
foreign->referenced_table_name_lookup);
} else { } else {
foreign->referenced_table_name_lookup foreign->referenced_table_name_lookup
= foreign->referenced_table_name; = foreign->referenced_table_name;

View File

@ -1320,9 +1320,7 @@ static void innodb_drop_database(handlerton*, char *path)
namebuf[len] = '/'; namebuf[len] = '/';
namebuf[len + 1] = '\0'; namebuf[len + 1] = '\0';
#ifdef _WIN32 IF_WIN(my_casedn_str(system_charset_info, namebuf),);
innobase_casedn_str(namebuf);
#endif /* _WIN32 */
THD * const thd= current_thd; THD * const thd= current_thd;
trx_t *trx= innobase_trx_allocate(thd); trx_t *trx= innobase_trx_allocate(thd);
@ -2435,21 +2433,6 @@ dtype_get_mblen(
} }
} }
/******************************************************************//**
Converts an identifier to a table name. */
void
innobase_convert_from_table_id(
/*===========================*/
CHARSET_INFO* cs, /*!< in: the 'from' character set */
char* to, /*!< out: converted identifier */
const char* from, /*!< in: identifier to convert */
ulint len) /*!< in: length of 'to', in bytes */
{
uint errors;
strconvert(cs, from, FN_REFLEN, &my_charset_filename, to, (uint) len, &errors);
}
/********************************************************************** /**********************************************************************
Check if the length of the identifier exceeds the maximum allowed. Check if the length of the identifier exceeds the maximum allowed.
return true when length of identifier is too long. */ return true when length of identifier is too long. */
@ -2474,21 +2457,6 @@ innobase_check_identifier_length(
DBUG_RETURN(false); DBUG_RETURN(false);
} }
/******************************************************************//**
Converts an identifier to UTF-8. */
void
innobase_convert_from_id(
/*=====================*/
CHARSET_INFO* cs, /*!< in: the 'from' character set */
char* to, /*!< out: converted identifier */
const char* from, /*!< in: identifier to convert */
ulint len) /*!< in: length of 'to', in bytes */
{
uint errors;
strconvert(cs, from, FN_REFLEN, system_charset_info, to, (uint) len, &errors);
}
/******************************************************************//** /******************************************************************//**
Compares NUL-terminated UTF-8 strings case insensitively. Compares NUL-terminated UTF-8 strings case insensitively.
@return 0 if a=b, <0 if a<b, >1 if a>b */ @return 0 if a=b, <0 if a<b, >1 if a>b */
@ -2537,16 +2505,6 @@ innobase_basename(
return((name) ? name : "null"); return((name) ? name : "null");
} }
/******************************************************************//**
Makes all characters in a NUL-terminated UTF-8 string lower case. */
void
innobase_casedn_str(
/*================*/
char* a) /*!< in/out: string to put in lower case */
{
my_casedn_str(system_charset_info, a);
}
/** Determines the current SQL statement. /** Determines the current SQL statement.
Thread unsafe, can only be called from the thread owning the THD. Thread unsafe, can only be called from the thread owning the THD.
@param[in] thd MySQL thread handle @param[in] thd MySQL thread handle
@ -3683,13 +3641,13 @@ innobase_format_name(
ulint buflen, /*!< in: length of buf, in bytes */ ulint buflen, /*!< in: length of buf, in bytes */
const char* name) /*!< in: table name to format */ const char* name) /*!< in: table name to format */
{ {
const char* bufend; char* bufend;
bufend = innobase_convert_name(buf, buflen, name, strlen(name), NULL); bufend = innobase_convert_name(buf, buflen, name, strlen(name), NULL);
ut_ad((ulint) (bufend - buf) < buflen); ut_ad((ulint) (bufend - buf) < buflen);
buf[bufend - buf] = '\0'; *bufend = '\0';
} }
/**********************************************************************//** /**********************************************************************//**
@ -5386,7 +5344,7 @@ normalize_table_name_c_low(
memcpy(norm_name + db_len + 1, name_ptr, name_len + 1); memcpy(norm_name + db_len + 1, name_ptr, name_len + 1);
if (set_lower_case) { if (set_lower_case) {
innobase_casedn_str(norm_name); my_casedn_str(system_charset_info, norm_name);
} }
} }
@ -6261,7 +6219,7 @@ ha_innobase::open_dict_table(
case name, including the partition case name, including the partition
separator "P" */ separator "P" */
strcpy(par_case_name, norm_name); strcpy(par_case_name, norm_name);
innobase_casedn_str(par_case_name); my_casedn_str(system_charset_info, par_case_name);
#else #else
/* On Windows platfrom, check /* On Windows platfrom, check
whether there exists table name in whether there exists table name in
@ -12389,6 +12347,73 @@ public:
const char* str() { return buf; } const char* str() { return buf; }
}; };
/** Construct an InnoDB table name from a schema and table name.
@param table_name buffer InnoDB table name being constructed
@param db schema name
@param name table name
@return table_name filled in */
static char *copy_name(char *table_name, LEX_CSTRING db, LEX_CSTRING name)
noexcept
{
memcpy(table_name, db.str, db.length);
table_name[db.length] = '/';
memcpy(table_name + db.length + 1, name.str, name.length + 1);
return table_name;
}
char *dict_table_lookup(LEX_CSTRING db, LEX_CSTRING name,
dict_table_t **table, mem_heap_t *heap) noexcept
{
const size_t len= db.length + name.length + 1;
char *ref= static_cast<char*>(mem_heap_alloc(heap, len + 1));
copy_name(ref, db, name);
switch (lower_case_table_names) {
case 2: /* store as given, compare in lower case */
my_casedn_str(system_charset_info, ref);
*table= dict_sys.load_table({ref, len});
return copy_name(ref, db, name);
case 0: /* store and compare as given; case sensitive */
#ifndef _WIN32 /* On Windows, InnoDB treats 0 as lower_case_table_names=1 */
break;
#endif
case 1: /* store and compare in lower case */
my_casedn_str(system_charset_info, ref);
}
*table = dict_sys.load_table({ref, len});
return ref;
}
/** Convert a schema or table name to InnoDB (and file system) format.
@param cs source character set
@param name name encoded in cs
@param buf output buffer (MAX_TABLE_NAME_LEN + 1 bytes)
@return the converted string (within buf) */
LEX_CSTRING innodb_convert_name(CHARSET_INFO *cs, LEX_CSTRING name, char *buf)
noexcept
{
CHARSET_INFO *to_cs= &my_charset_filename;
if (!strncmp(name.str, srv_mysql50_table_name_prefix,
sizeof srv_mysql50_table_name_prefix - 1))
{
/* Before MySQL 5.1 introduced my_charset_filename, schema and
table names were stored in the file system as specified by the
user, hopefully in ASCII encoding, but it could also be in ISO
8859-1 or UTF-8. Such schema or table names are distinguished by
the #mysql50# prefix.
Let us discard that prefix and convert the name to UTF-8
(system_charset_info). */
name.str+= sizeof srv_mysql50_table_name_prefix - 1;
name.length-= sizeof srv_mysql50_table_name_prefix - 1;
to_cs= system_charset_info;
}
uint errors;
return LEX_CSTRING{buf, strconvert(cs, name.str, name.length, to_cs,
buf, MAX_TABLE_NAME_LEN, &errors)};
}
/** Create InnoDB foreign keys from MySQL alter_info. Collect all /** Create InnoDB foreign keys from MySQL alter_info. Collect all
dict_foreign_t items into local_fk_set and then add into system table. dict_foreign_t items into local_fk_set and then add into system table.
@return DB_SUCCESS or specific error code */ @return DB_SUCCESS or specific error code */
@ -12404,6 +12429,9 @@ 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];
static_assert(MAX_TABLE_NAME_LEN == MAX_DATABASE_NAME_LEN, "");
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 +12439,57 @@ 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 t{innodb_convert_name(cs, m_form->s->table_name,
char* n = dict_get_referenced_table( t_name)};
name, LEX_STRING_WITH_LEN(m_form->s->db), LEX_CSTRING d{innodb_convert_name(cs, m_form->s->db, db_name)};
LEX_STRING_WITH_LEN(m_form->s->table_name), dict_table_t* alter_table;
&table_to_alter, heap, cs); char* n = dict_table_lookup(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( char* bufend = innobase_convert_name(
create_name, sizeof create_name, n, strlen(n), m_thd); create_name, sizeof create_name, n, strlen(n), m_thd);
create_name[bufend - create_name] = '\0'; *bufend = '\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, char* bufend = innobase_convert_name(create_name,
sizeof create_name, sizeof create_name,
name, LEX_STRING_WITH_LEN(name),
strlen(name), m_thd); m_thd);
create_name[bufend - create_name] = '\0'; *bufend = '\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 +12536,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 +12568,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,14 +12636,12 @@ 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 t{innodb_convert_name(cs, fk->ref_table, t_name)};
name, LEX_STRING_WITH_LEN(fk->ref_db), LEX_CSTRING d = fk->ref_db.str
LEX_STRING_WITH_LEN(fk->ref_table), ? innodb_convert_name(cs, fk->ref_db, db_name)
&foreign->referenced_table, foreign->heap, cs); : LEX_CSTRING{table->name.m_name, table->name.dblen()};
foreign->referenced_table_name = dict_table_lookup(
if (!foreign->referenced_table_name) { d, t, &foreign->referenced_table, foreign->heap);
return (DB_OUT_OF_MEMORY);
}
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] = "";
@ -12627,15 +12651,15 @@ create_table_info_t::create_foreign_keys()
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'; *bufend = '\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 +12682,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 +12690,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 +12712,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 +12757,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 +12767,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;
} }
} }
} }
@ -13596,7 +13617,7 @@ int ha_innobase::delete_table(const char *name)
if (!table && lower_case_table_names == 1 && is_partition(norm_name)) if (!table && lower_case_table_names == 1 && is_partition(norm_name))
{ {
IF_WIN(normalize_table_name_c_low(norm_name, name, false), IF_WIN(normalize_table_name_c_low(norm_name, name, false),
innobase_casedn_str(norm_name)); my_casedn_str(system_charset_info, norm_name));
table= dict_sys.load_table(n, DICT_ERR_IGNORE_DROP); table= dict_sys.load_table(n, DICT_ERR_IGNORE_DROP);
} }
#endif #endif
@ -13903,7 +13924,8 @@ static dberr_t innobase_rename_table(trx_t *trx, const char *from,
case name, including the partition case name, including the partition
separator "P" */ separator "P" */
strcpy(par_case_name, norm_from); strcpy(par_case_name, norm_from);
innobase_casedn_str(par_case_name); my_casedn_str(system_charset_info,
par_case_name);
#else #else
/* On Windows platfrom, check /* On Windows platfrom, check
whether there exists table name in whether there exists table name in
@ -20924,25 +20946,6 @@ const char* SET_TRANSACTION_MSG =
const char* INNODB_PARAMETERS_MSG = const char* INNODB_PARAMETERS_MSG =
"Please refer to https://mariadb.com/kb/en/library/innodb-system-variables/"; "Please refer to https://mariadb.com/kb/en/library/innodb-system-variables/";
/**********************************************************************
Converts an identifier from my_charset_filename to UTF-8 charset.
@return result string length, as returned by strconvert() */
uint
innobase_convert_to_filename_charset(
/*=================================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len) /* in: length of 'to', in bytes */
{
uint errors;
CHARSET_INFO* cs_to = &my_charset_filename;
CHARSET_INFO* cs_from = system_charset_info;
return(static_cast<uint>(strconvert(
cs_from, from, uint(strlen(from)),
cs_to, to, static_cast<uint>(len), &errors)));
}
/********************************************************************** /**********************************************************************
Converts an identifier from my_charset_filename to UTF-8 charset. Converts an identifier from my_charset_filename to UTF-8 charset.
@return result string length, as returned by strconvert() */ @return result string length, as returned by strconvert() */

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,9 @@ 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];
static_assert(MAX_TABLE_NAME_LEN == MAX_DATABASE_NAME_LEN, "");
DBUG_ENTER("innobase_get_foreign_key_info"); DBUG_ENTER("innobase_get_foreign_key_info");
@ -3295,14 +3299,15 @@ innobase_get_foreign_key_info(
add_fk[num_fk] = dict_mem_foreign_create(); add_fk[num_fk] = dict_mem_foreign_create();
LEX_CSTRING t = innodb_convert_name(cs, fk_key->ref_table,
t_name);
LEX_CSTRING d = fk_key->ref_db.str
? innodb_convert_name(cs, fk_key->ref_db, db_name)
: LEX_CSTRING{table->name.m_name, table->name.dblen()};
dict_sys.lock(SRW_LOCK_CALL); dict_sys.lock(SRW_LOCK_CALL);
referenced_table_name = dict_get_referenced_table( referenced_table_name = dict_table_lookup(
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

@ -55,22 +55,6 @@ inline size_t dict_get_db_name_len(const char *name)
} }
/*********************************************************************//**
Open a table from its database and table name, this is currently used by
foreign constraint parser to get the referenced table.
@return complete table name with database and table name, allocated from
heap memory passed in */
char*
dict_get_referenced_table(
/*======================*/
const char* name, /*!< in: foreign key 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 */
mem_heap_t* heap, /*!< in: heap memory */
CHARSET_INFO* from_cs); /*!< in: table name charset */
/*********************************************************************//** /*********************************************************************//**
Frees a foreign key struct. */ Frees a foreign key struct. */
void void

View File

@ -431,7 +431,7 @@ void
dict_mem_foreign_table_name_lookup_set( dict_mem_foreign_table_name_lookup_set(
/*===================================*/ /*===================================*/
dict_foreign_t* foreign, /*!< in/out: foreign struct */ dict_foreign_t* foreign, /*!< in/out: foreign struct */
ibool do_alloc); /*!< in: is an alloc needed */ bool do_alloc); /*!< in: is an alloc needed */
/**********************************************************************//** /**********************************************************************//**
Sets the referenced_table_name_lookup pointer based on the value of Sets the referenced_table_name_lookup pointer based on the value of

View File

@ -110,7 +110,7 @@ struct table_name_t
table_name_t(char* name) : m_name(name) {} table_name_t(char* name) : m_name(name) {}
/** @return the end of the schema name */ /** @return the end of the schema name */
const char* dbend() const const char* dbend() const noexcept
{ {
const char* sep = strchr(m_name, '/'); const char* sep = strchr(m_name, '/');
ut_ad(sep); ut_ad(sep);
@ -118,11 +118,19 @@ struct table_name_t
} }
/** @return the length of the schema name, in bytes */ /** @return the length of the schema name, in bytes */
size_t dblen() const { return size_t(dbend() - m_name); } size_t dblen() const noexcept
{
const char *end= dbend();
return UNIV_LIKELY(end != nullptr) ? size_t(end - m_name) : 0;
}
/** Determine the filename-safe encoded table name. /** Determine the filename-safe encoded table name.
@return the filename-safe encoded table name */ @return the filename-safe encoded table name */
const char* basename() const { return dbend() + 1; } const char* basename() const noexcept
{
const char *end= dbend();
return UNIV_LIKELY(end != nullptr) ? end + 1 : nullptr;
}
/** The start of the table basename suffix for partitioned tables */ /** The start of the table basename suffix for partitioned tables */
static const char part_suffix[4]; static const char part_suffix[4];

View File

@ -40,6 +40,8 @@ class Field;
struct dict_table_t; struct dict_table_t;
struct dict_foreign_t; struct dict_foreign_t;
struct table_name_t; struct table_name_t;
struct mem_block_info_t;
typedef struct mem_block_info_t mem_heap_t;
// JAN: TODO missing features: // JAN: TODO missing features:
#undef MYSQL_FT_INIT_EXT #undef MYSQL_FT_INIT_EXT
@ -156,33 +158,6 @@ const char*
innobase_basename( innobase_basename(
const char* path_name); const char* path_name);
/******************************************************************//**
Converts an identifier to a table name. */
void
innobase_convert_from_table_id(
/*===========================*/
CHARSET_INFO* cs, /*!< in: the 'from' character set */
char* to, /*!< out: converted identifier */
const char* from, /*!< in: identifier to convert */
ulint len); /*!< in: length of 'to', in bytes; should
be at least 5 * strlen(to) + 1 */
/******************************************************************//**
Converts an identifier to UTF-8. */
void
innobase_convert_from_id(
/*=====================*/
CHARSET_INFO* cs, /*!< in: the 'from' character set */
char* to, /*!< out: converted identifier */
const char* from, /*!< in: identifier to convert */
ulint len); /*!< in: length of 'to', in bytes;
should be at least 3 * strlen(to) + 1 */
/******************************************************************//**
Makes all characters in a NUL-terminated UTF-8 string lower case. */
void
innobase_casedn_str(
/*================*/
char* a); /*!< in/out: string to put in lower case */
#ifdef WITH_WSREP #ifdef WITH_WSREP
ulint wsrep_innobase_mysql_sort(int mysql_type, uint charset_number, ulint wsrep_innobase_mysql_sort(int mysql_type, uint charset_number,
unsigned char* str, ulint str_length, unsigned char* str, ulint str_length,
@ -370,15 +345,6 @@ innobase_next_autoinc(
MY_ATTRIBUTE((pure, warn_unused_result)); MY_ATTRIBUTE((pure, warn_unused_result));
/********************************************************************** /**********************************************************************
Converts an identifier from my_charset_filename to UTF-8 charset. */
uint
innobase_convert_to_system_charset(
/*===============================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len, /* in: length of 'to', in bytes */
uint* errors); /* out: error return */
/**********************************************************************
Check if the length of the identifier exceeds the maximum allowed. Check if the length of the identifier exceeds the maximum allowed.
The input to this function is an identifier in charset my_charset_filename. The input to this function is an identifier in charset my_charset_filename.
return true when length of identifier is too long. */ return true when length of identifier is too long. */
@ -398,14 +364,13 @@ innobase_convert_to_system_charset(
ulint len, /* in: length of 'to', in bytes */ ulint len, /* in: length of 'to', in bytes */
uint* errors); /* out: error return */ uint* errors); /* out: error return */
/********************************************************************** /** Convert a schema or table name to InnoDB (and file system) format.
Converts an identifier from my_charset_filename to UTF-8 charset. */ @param cs source character set
uint @param name name encoded in cs
innobase_convert_to_filename_charset( @param buf output buffer (MAX_TABLE_NAME_LEN + 1 bytes)
/*=================================*/ @return the converted string (within buf) */
char* to, /* out: converted identifier */ LEX_CSTRING innodb_convert_name(CHARSET_INFO *cs, LEX_CSTRING name, char *buf)
const char* from, /* in: identifier to convert */ noexcept;
ulint len); /* in: length of 'to', in bytes */
/** Report that a table cannot be decrypted. /** Report that a table cannot be decrypted.
@param thd connection context @param thd connection context
@ -460,6 +425,16 @@ void destroy_background_thd(MYSQL_THD thd);
void void
innobase_reset_background_thd(MYSQL_THD); innobase_reset_background_thd(MYSQL_THD);
/** Open a table based on a database and table name.
@param db schema name
@param name table name within the schema
@param table table
@param heap memory heap for allocating a converted name
@return InnoDB format table name with database and table name,
allocated from heap */
char *dict_table_lookup(LEX_CSTRING db, LEX_CSTRING name,
dict_table_t **table, mem_heap_t *heap) noexcept;
#ifdef WITH_WSREP #ifdef WITH_WSREP
/** Append table-level exclusive key. /** Append table-level exclusive key.
@param thd MySQL thread handle @param thd MySQL thread handle

View File

@ -2608,7 +2608,7 @@ row_rename_table_for_mysql(
memcpy(par_case_name, old_name, memcpy(par_case_name, old_name,
strlen(old_name)); strlen(old_name));
par_case_name[strlen(old_name)] = 0; par_case_name[strlen(old_name)] = 0;
innobase_casedn_str(par_case_name); my_casedn_str(system_charset_info, par_case_name);
#else #else
/* On Windows platfrom, check /* On Windows platfrom, check
whether there exists table name in whether there exists table name in