From d4da659b432f47fcbc86ff446f72ce42fc535dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 23 Jan 2025 14:38:08 +0200 Subject: [PATCH] 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 --- mysql-test/suite/innodb/r/foreign_key.result | 24 ++ mysql-test/suite/innodb/t/foreign_key.test | 17 ++ storage/innobase/dict/dict0dict.cc | 163 +++---------- storage/innobase/dict/dict0mem.cc | 8 +- storage/innobase/handler/ha_innodb.cc | 243 ++++++++++--------- storage/innobase/handler/handler0alter.cc | 17 +- storage/innobase/include/dict0dict.h | 16 -- storage/innobase/include/dict0mem.h | 2 +- storage/innobase/include/dict0types.h | 14 +- storage/innobase/include/ha_prototypes.h | 63 ++--- storage/innobase/row/row0mysql.cc | 2 +- 11 files changed, 245 insertions(+), 324 deletions(-) diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result index 80a0afb8d06..0404ab81c61 100644 --- a/mysql-test/suite/innodb/r/foreign_key.result +++ b/mysql-test/suite/innodb/r/foreign_key.result @@ -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; diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test index 506cca3245c..e31320ee810 100644 --- a/mysql-test/suite/innodb/t/foreign_key.test +++ b/mysql-test/suite/innodb/t/foreign_key.test @@ -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; @@ -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; diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index d97e26f7fca..dd0b4c714bf 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1467,6 +1467,26 @@ dict_table_t::rename_tablespace(span 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(strconvert( + cs_from, from, uint(strlen(from)), + cs_to, to, static_cast(len), &errors))); +} + /**********************************************************************//** Renames a table object. @return TRUE if success */ @@ -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 */ @@ -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; @@ -3154,7 +3168,6 @@ dict_scan_id( } } else { while (!my_isspace(cs, *ptr) && *ptr != '(' && *ptr != ')' - && (accept_also_dot || *ptr != '.') && *ptr != ',' && *ptr != '\0') { ptr++; @@ -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(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(mem_heap_alloc(heap, len)); - - innobase_convert_from_table_id(cs, dst, str, len); - } - + ulint dstlen = 3 * len + 1; + char *dst = static_cast(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(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, @@ -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) { diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 9de43abbb17..5aeb5d51728 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -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) { @@ -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; @@ -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; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 31c4f525adf..3229cb67241 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1320,9 +1320,7 @@ static void innodb_drop_database(handlerton*, char *path) namebuf[len] = '/'; namebuf[len + 1] = '\0'; -#ifdef _WIN32 - innobase_casedn_str(namebuf); -#endif /* _WIN32 */ + IF_WIN(my_casedn_str(system_charset_info, namebuf),); THD * const thd= current_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. return true when length of identifier is too long. */ @@ -2474,21 +2457,6 @@ innobase_check_identifier_length( 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. @return 0 if a=b, <0 if a1 if a>b */ @@ -2537,16 +2505,6 @@ innobase_basename( 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. Thread unsafe, can only be called from the thread owning the THD. @param[in] thd MySQL thread handle @@ -3683,13 +3641,13 @@ innobase_format_name( ulint buflen, /*!< in: length of buf, in bytes */ const char* name) /*!< in: table name to format */ { - const char* bufend; + char* bufend; bufend = innobase_convert_name(buf, buflen, name, strlen(name), NULL); 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); 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 separator "P" */ strcpy(par_case_name, norm_name); - innobase_casedn_str(par_case_name); + my_casedn_str(system_charset_info, par_case_name); #else /* On Windows platfrom, check whether there exists table name in @@ -12389,6 +12347,73 @@ public: 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(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 dict_foreign_t items into local_fk_set and then add into system table. @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]; char create_name[MAX_DATABASE_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; fkerr_t index_error = FK_SUCCESS; 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 CHARSET_INFO* cs = thd_charset(m_thd); const char* operation = "Create "; - const char* name = m_table_name; 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) { - dict_table_t* table_to_alter; mem_heap_t* heap = mem_heap_create(10000); - ulint highest_id_so_far; - char* n = dict_get_referenced_table( - name, LEX_STRING_WITH_LEN(m_form->s->db), - LEX_STRING_WITH_LEN(m_form->s->table_name), - &table_to_alter, heap, cs); + LEX_CSTRING t{innodb_convert_name(cs, m_form->s->table_name, + t_name)}; + LEX_CSTRING d{innodb_convert_name(cs, m_form->s->db, db_name)}; + dict_table_t* alter_table; + 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 in the format databasename/tablename_ibfk_[number], where [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. */ /* If we are altering a temporary table, the table name after 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) { - n = table_to_alter->name.m_name; - highest_id_so_far = dict_table_get_highest_foreign_id( - table_to_alter); - } else { - highest_id_so_far = 0; + if (alter_table) { + n = alter_table->name.m_name; + number = 1 + dict_table_get_highest_foreign_id( + alter_table); } char* bufend = innobase_convert_name( create_name, sizeof create_name, n, strlen(n), m_thd); - create_name[bufend - create_name] = '\0'; - number = highest_id_so_far + 1; + *bufend = '\0'; mem_heap_free(heap); operation = "Alter "; - } else if (strstr(name, "#P#") || strstr(name, "#p#")) { + } else if (strstr(m_table_name, "#P#") + || strstr(m_table_name, "#p#")) { /* Partitioned table */ create_name[0] = '\0'; } else { char* bufend = innobase_convert_name(create_name, sizeof create_name, - name, - strlen(name), m_thd); - create_name[bufend - create_name] = '\0'; + LEX_STRING_WITH_LEN(name), + m_thd); + *bufend = '\0'; } Alter_info* alter_info = m_create_info->alter_info; ut_ad(alter_info); List_iterator_fast 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) { ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s foreign key constraint" @@ -12510,27 +12536,27 @@ create_table_info_t::create_foreign_keys() col->field_name.length); success = find_col(table, column_names + i); if (!success) { - key_text k(fk); ib_foreign_warn( m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s foreign key %s constraint" " failed. Column %s was not found.", - operation, create_name, k.str(), + operation, create_name, + key_text(fk).str(), column_names[i]); dict_foreign_free(foreign); return (DB_CANNOT_ADD_CONSTRAINT); } ++i; if (i >= MAX_COLS_PER_FK) { - key_text k(fk); ib_foreign_warn( m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s foreign key %s constraint" " failed. Too many columns: %u (%u " "allowed).", - operation, create_name, k.str(), i, + operation, create_name, + key_text(fk).str(), i, MAX_COLS_PER_FK); dict_foreign_free(foreign); return (DB_CANNOT_ADD_CONSTRAINT); @@ -12542,9 +12568,9 @@ create_table_info_t::create_foreign_keys() &index_error, &err_col, &err_index); if (!index) { - key_text k(fk); foreign_push_index_error(m_trx, operation, create_name, - k.str(), column_names, + key_text(fk).str(), + column_names, index_error, err_col, err_index, table); dict_foreign_free(foreign); @@ -12610,14 +12636,12 @@ create_table_info_t::create_foreign_keys() memcpy(foreign->foreign_col_names, column_names, i * sizeof(void*)); - foreign->referenced_table_name = dict_get_referenced_table( - name, LEX_STRING_WITH_LEN(fk->ref_db), - LEX_STRING_WITH_LEN(fk->ref_table), - &foreign->referenced_table, foreign->heap, cs); - - if (!foreign->referenced_table_name) { - return (DB_OUT_OF_MEMORY); - } + LEX_CSTRING t{innodb_convert_name(cs, fk->ref_table, t_name)}; + LEX_CSTRING d = fk->ref_db.str + ? innodb_convert_name(cs, fk->ref_db, db_name) + : LEX_CSTRING{table->name.m_name, table->name.dblen()}; + foreign->referenced_table_name = dict_table_lookup( + d, t, &foreign->referenced_table, foreign->heap); if (!foreign->referenced_table && m_trx->check_foreigns) { char buf[MAX_TABLE_NAME_LEN + 1] = ""; @@ -12627,15 +12651,15 @@ create_table_info_t::create_foreign_keys() buf, MAX_TABLE_NAME_LEN, foreign->referenced_table_name, strlen(foreign->referenced_table_name), m_thd); - buf[bufend - buf] = '\0'; - key_text k(fk); + *bufend = '\0'; ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s with foreign key %s " "constraint failed. Referenced table " "%s not found in the data dictionary.", - operation, create_name, k.str(), buf); - return (DB_CANNOT_ADD_CONSTRAINT); + operation, create_name, + key_text(fk).str(), buf); + return DB_CANNOT_ADD_CONSTRAINT; } /* 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, ref_column_names + j); if (!success) { - key_text k(fk); ib_foreign_warn( m_trx, DB_CANNOT_ADD_CONSTRAINT, @@ -12667,9 +12690,9 @@ create_table_info_t::create_foreign_keys() "constraint failed. " "Column %s was not found.", operation, create_name, - k.str(), ref_column_names[j]); - - return (DB_CANNOT_ADD_CONSTRAINT); + key_text(fk).str(), + ref_column_names[j]); + return DB_CANNOT_ADD_CONSTRAINT; } } ++j; @@ -12689,16 +12712,15 @@ create_table_info_t::create_foreign_keys() &err_index); if (!index) { - key_text k(fk); 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, err_index, foreign->referenced_table); - - return (DB_CANNOT_ADD_CONSTRAINT); + return DB_CANNOT_ADD_CONSTRAINT; } } else { - ut_a(m_trx->check_foreigns == FALSE); + ut_a(!m_trx->check_foreigns); index = NULL; } @@ -12735,7 +12757,6 @@ create_table_info_t::create_foreign_keys() NULL if the column is not allowed to be NULL! */ - key_text k(fk); ib_foreign_warn( m_trx, DB_CANNOT_ADD_CONSTRAINT, @@ -12746,9 +12767,9 @@ create_table_info_t::create_foreign_keys() "but column '%s' is defined as " "NOT NULL.", 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_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); } #endif @@ -13903,7 +13924,8 @@ static dberr_t innobase_rename_table(trx_t *trx, const char *from, case name, including the partition separator "P" */ strcpy(par_case_name, norm_from); - innobase_casedn_str(par_case_name); + my_casedn_str(system_charset_info, + par_case_name); #else /* On Windows platfrom, check whether there exists table name in @@ -20924,25 +20946,6 @@ const char* SET_TRANSACTION_MSG = const char* INNODB_PARAMETERS_MSG = "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(strconvert( - cs_from, from, uint(strlen(from)), - cs_to, to, static_cast(len), &errors))); -} - /********************************************************************** Converts an identifier from my_charset_filename to UTF-8 charset. @return result string length, as returned by strconvert() */ diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index ff427297375..afcff2f3ea1 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -30,6 +30,7 @@ Smart ALTER TABLE #include #include #include +#include /* Include necessary InnoDB headers */ #include "btr0sea.h" @@ -3231,6 +3232,9 @@ innobase_get_foreign_key_info( ulint num_fk = 0; Alter_info* alter_info = ha_alter_info->alter_info; 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"); @@ -3295,14 +3299,15 @@ innobase_get_foreign_key_info( 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); - referenced_table_name = dict_get_referenced_table( - table->name.m_name, - LEX_STRING_WITH_LEN(fk_key->ref_db), - LEX_STRING_WITH_LEN(fk_key->ref_table), - &referenced_table, - add_fk[num_fk]->heap, cs); + referenced_table_name = dict_table_lookup( + d, t, &referenced_table, add_fk[num_fk]->heap); /* Test the case when referenced_table failed to open, if trx->check_foreigns is not set, we should diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 0197a790faa..519e1c5bd10 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -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. */ void diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 609aaf951ca..3b1ae66d861 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -431,7 +431,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 */ /**********************************************************************//** Sets the referenced_table_name_lookup pointer based on the value of diff --git a/storage/innobase/include/dict0types.h b/storage/innobase/include/dict0types.h index ec50e8cd951..85eed8d416c 100644 --- a/storage/innobase/include/dict0types.h +++ b/storage/innobase/include/dict0types.h @@ -110,7 +110,7 @@ struct table_name_t table_name_t(char* name) : m_name(name) {} /** @return the end of the schema name */ - const char* dbend() const + const char* dbend() const noexcept { const char* sep = strchr(m_name, '/'); ut_ad(sep); @@ -118,11 +118,19 @@ struct table_name_t } /** @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. @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 */ static const char part_suffix[4]; diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index 96ce0cfe783..098986febdf 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -40,6 +40,8 @@ class Field; struct dict_table_t; struct dict_foreign_t; struct table_name_t; +struct mem_block_info_t; +typedef struct mem_block_info_t mem_heap_t; // JAN: TODO missing features: #undef MYSQL_FT_INIT_EXT @@ -156,33 +158,6 @@ const char* innobase_basename( 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 ulint wsrep_innobase_mysql_sort(int mysql_type, uint charset_number, unsigned char* str, ulint str_length, @@ -370,15 +345,6 @@ innobase_next_autoinc( 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. The input to this function is an identifier in charset my_charset_filename. 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 */ uint* errors); /* out: error return */ -/********************************************************************** -Converts an identifier from my_charset_filename to UTF-8 charset. */ -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 */ +/** 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; /** Report that a table cannot be decrypted. @param thd connection context @@ -460,6 +425,16 @@ void destroy_background_thd(MYSQL_THD thd); void 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 /** Append table-level exclusive key. @param thd MySQL thread handle diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index ac5e08d4498..ac11ea56ad0 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -2608,7 +2608,7 @@ row_rename_table_for_mysql( memcpy(par_case_name, old_name, strlen(old_name)); par_case_name[strlen(old_name)] = 0; - innobase_casedn_str(par_case_name); + my_casedn_str(system_charset_info, par_case_name); #else /* On Windows platfrom, check whether there exists table name in