Skip to content

Commit

Permalink
MDEV-35854: Simplify dict_get_referenced_table()
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dr-m committed Jan 23, 2025
1 parent fa74c1a commit d4da659
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 324 deletions.
24 changes: 24 additions & 0 deletions mysql-test/suite/innodb/r/foreign_key.result
Original file line number Diff line number Diff line change
Expand Up @@ -1118,5 +1118,29 @@ test.binaries check status OK
test.collections check status OK
disconnect con1;
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
SET GLOBAL innodb_stats_persistent = @save_stats_persistent;
17 changes: 17 additions & 0 deletions mysql-test/suite/innodb/t/foreign_key.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

--disable_query_log
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

SET GLOBAL innodb_stats_persistent = 0;
Expand Down Expand Up @@ -1188,6 +1189,22 @@ CHECK TABLE binaries, collections EXTENDED;
# Cleanup
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

SET GLOBAL innodb_stats_persistent = @save_stats_persistent;
Expand Down
163 changes: 33 additions & 130 deletions storage/innobase/dict/dict0dict.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1467,6 +1467,26 @@ dict_table_t::rename_tablespace(span<const char> new_name, bool replace) const
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.
@return TRUE if success */
Expand Down Expand Up @@ -1599,19 +1619,20 @@ dict_table_rename_in_cache(
foreign->referenced_table->referenced_set.erase(foreign);
}

if (strlen(foreign->foreign_table_name)
< strlen(table->name.m_name)) {
const bool do_alloc = strlen(foreign->foreign_table_name)
< strlen(table->name.m_name);

if (do_alloc) {
/* Allocate a longer name buffer;
TODO: store buf len to save memory */

foreign->foreign_table_name = mem_heap_strdup(
foreign->heap, table->name.m_name);
dict_mem_foreign_table_name_lookup_set(foreign, TRUE);
} else {
strcpy(foreign->foreign_table_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, '/')) {
/* This is a >= 4.0.18 format id */

Expand Down Expand Up @@ -3105,20 +3126,13 @@ dict_scan_id(
mem_heap_t* heap, /*!< in: heap where to allocate the id
(NULL=id will not be allocated, but it
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 */
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';
ulint len = 0;
const char* s;
char* str;
char* dst;

*id = NULL;

Expand Down Expand Up @@ -3154,7 +3168,6 @@ dict_scan_id(
}
} else {
while (!my_isspace(cs, *ptr) && *ptr != '(' && *ptr != ')'
&& (accept_also_dot || *ptr != '.')
&& *ptr != ',' && *ptr != '\0') {

ptr++;
Expand Down Expand Up @@ -3188,125 +3201,15 @@ dict_scan_id(
str = mem_heap_strdupl(heap, s, len);
}

if (!table_id) {
convert_id:
/* Convert the identifier from connection character set
to UTF-8. */
len = 3 * len + 1;
*id = dst = static_cast<char*>(mem_heap_alloc(heap, len));

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);
}

ulint dstlen = 3 * len + 1;
char *dst = static_cast<char*>(mem_heap_alloc(heap, dstlen));
*id = dst;
uint errors;
strconvert(cs, str, uint(len), system_charset_info, dst,
uint(dstlen), &errors);
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
(a) '#' to the end of the line,
Expand Down Expand Up @@ -3563,7 +3466,7 @@ dict_foreign_parse_drop_constraints(
}
}

ptr = dict_scan_id(cs, ptr, heap, &id, FALSE, TRUE);
ptr = dict_scan_id(cs, ptr, heap, &id);

if (id == NULL) {

Expand Down
8 changes: 5 additions & 3 deletions storage/innobase/dict/dict0mem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ void
dict_mem_foreign_table_name_lookup_set(
/*===================================*/
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 (do_alloc) {
Expand All @@ -830,7 +830,8 @@ dict_mem_foreign_table_name_lookup_set(
}
strcpy(foreign->foreign_table_name_lookup,
foreign->foreign_table_name);
innobase_casedn_str(foreign->foreign_table_name_lookup);
my_casedn_str(system_charset_info,
foreign->foreign_table_name_lookup);
} else {
foreign->foreign_table_name_lookup
= foreign->foreign_table_name;
Expand Down Expand Up @@ -860,7 +861,8 @@ dict_mem_referenced_table_name_lookup_set(
}
strcpy(foreign->referenced_table_name_lookup,
foreign->referenced_table_name);
innobase_casedn_str(foreign->referenced_table_name_lookup);
my_casedn_str(system_charset_info,
foreign->referenced_table_name_lookup);
} else {
foreign->referenced_table_name_lookup
= foreign->referenced_table_name;
Expand Down
Loading

0 comments on commit d4da659

Please sign in to comment.