Skip to content

Commit 60a8f40

Browse files
shlomi-noachpiki
authored andcommitted
RENAME TABLE: Truncate CONSTRAINT name until it fits in length (mysql#80)
* Truncate CONSTRAINT name until it fits in length Signed-off-by: Shlomi Noach <[email protected]> * replace first character with '_' Signed-off-by: Shlomi Noach <[email protected]> * adapt tests Signed-off-by: Shlomi Noach <[email protected]> * adapt check_constraints test Signed-off-by: Shlomi Noach <[email protected]> * use erase() and insert() Signed-off-by: Shlomi Noach <[email protected]> * typo Signed-off-by: Shlomi Noach <[email protected]> * stopping condition Signed-off-by: Shlomi Noach <[email protected]> * stopping condition Signed-off-by: Shlomi Noach <[email protected]> * calculate autogenerated FK name in sql_table Signed-off-by: Shlomi Noach <[email protected]> * new_name.length() Signed-off-by: Shlomi Noach <[email protected]> * consistent evaluation of new fk name Signed-off-by: Shlomi Noach <[email protected]> * consolidate abit of the code Signed-off-by: Shlomi Noach <[email protected]> * ensure UTF table name correctness Signed-off-by: Shlomi Noach <[email protected]> * early return condition Signed-off-by: Shlomi Noach <[email protected]> * Update sql/sql_table.cc Co-authored-by: Patrick Reynolds <[email protected]> * Update sql/sql_table.h Co-authored-by: Patrick Reynolds <[email protected]> --------- Signed-off-by: Shlomi Noach <[email protected]> Co-authored-by: Patrick Reynolds <[email protected]>
1 parent c58ad74 commit 60a8f40

File tree

7 files changed

+99
-38
lines changed

7 files changed

+99
-38
lines changed

mysql-test/r/check_constraints.result

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,20 @@ DROP TABLE t1;
184184
#-----------------------------------------------------------------------
185185
CREATE TABLE t1 (f1 INT CHECK (f1 < 10));
186186
RENAME TABLE t1 TO t123456789012345678901234567890123456789012345678901234567890;
187-
ERROR 42000: Identifier name 't123456789012345678901234567890123456789012345678901234567890_chk_1' is too long
188-
DROP TABLE t1;
187+
DROP TABLE t123456789012345678901234567890123456789012345678901234567890;
188+
#-----------------------------------------------------------------------
189+
# Test case to verify check constraint with too long generated name,
190+
# and with UTF8 characters
191+
#-----------------------------------------------------------------------
192+
CREATE TABLE t1 (f1 INT, CONSTRAINT t1_chk_1_some_dummy_check CHECK (f1 < 10));
193+
RENAME TABLE t1 TO `tאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי`;
194+
SHOW CREATE TABLE `tאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי`;
195+
Table Create Table
196+
tאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי CREATE TABLE `tאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי` (
197+
`f1` int DEFAULT NULL,
198+
CONSTRAINT `_אבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי_chk_1_some_dummy_check` CHECK ((`f1` < 10))
199+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
200+
DROP TABLE `tאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי`;
189201
CREATE TABLE t123456789012345678901234567890123456789012345678901234567890(f1 INT CHECK(f1 < 10));
190202
ERROR 42000: Identifier name 't123456789012345678901234567890123456789012345678901234567890_chk_1' is too long
191203
CREATE TABLE t123456789012345678901234567890123456789012345678901234567890(f1 INT);

mysql-test/r/foreign_key.result

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,18 +391,17 @@ DROP TABLE t2;
391391
# Check long FK generated names due to long table names.
392392
CREATE TABLE t2 (a INT, FOREIGN KEY (a) REFERENCES t1(a));
393393
RENAME TABLE t2 TO t123456789012345678901234567890123456789012345678901234567;
394-
ERROR 42000: Identifier name 't123456789012345678901234567890123456789012345678901234567_ibfk_1' is too long
395-
RENAME TABLE t2 TO t12345678901234567890123456789012345678901234567890123456;
394+
RENAME TABLE t123456789012345678901234567890123456789012345678901234567 TO t12345678901234567890123456789012345678901234567890123456;
396395
SELECT constraint_name FROM information_schema.referential_constraints
397396
WHERE table_name = 't12345678901234567890123456789012345678901234567890123456'
398397
ORDER BY constraint_name;
399398
CONSTRAINT_NAME
400-
t12345678901234567890123456789012345678901234567890123456_ibfk_1
399+
_23456789012345678901234567890123456789012345678901234567_ibfk_1
401400
SELECT constraint_name FROM information_schema.table_constraints
402401
WHERE table_name = 't12345678901234567890123456789012345678901234567890123456'
403402
ORDER BY constraint_name;
404403
CONSTRAINT_NAME
405-
t12345678901234567890123456789012345678901234567890123456_ibfk_1
404+
_23456789012345678901234567890123456789012345678901234567_ibfk_1
406405
DROP TABLE t12345678901234567890123456789012345678901234567890123456;
407406
CREATE TABLE t123456789012345678901234567890123456789012345678901234567(
408407
a INT, FOREIGN KEY (a) REFERENCES t1(a));

mysql-test/t/check_constraints.test

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,16 @@ DROP TABLE t1;
172172
--echo # Test case to verify check constraint with too long generated name.
173173
--echo #-----------------------------------------------------------------------
174174
CREATE TABLE t1 (f1 INT CHECK (f1 < 10));
175-
--error ER_TOO_LONG_IDENT
176175
RENAME TABLE t1 TO t123456789012345678901234567890123456789012345678901234567890;
177-
DROP TABLE t1;
176+
DROP TABLE t123456789012345678901234567890123456789012345678901234567890;
177+
--echo #-----------------------------------------------------------------------
178+
--echo # Test case to verify check constraint with too long generated name,
179+
--echo # and with UTF8 characters
180+
--echo #-----------------------------------------------------------------------
181+
CREATE TABLE t1 (f1 INT, CONSTRAINT t1_chk_1_some_dummy_check CHECK (f1 < 10));
182+
RENAME TABLE t1 TO `tאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי`;
183+
SHOW CREATE TABLE `tאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי`;
184+
DROP TABLE `tאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטיאבגדהוזחטי`;
178185

179186
--error ER_TOO_LONG_IDENT
180187
CREATE TABLE t123456789012345678901234567890123456789012345678901234567890(f1 INT CHECK(f1 < 10));

mysql-test/t/foreign_key.test

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,10 @@ DROP TABLE t2;
345345

346346
--echo # Check long FK generated names due to long table names.
347347
CREATE TABLE t2 (a INT, FOREIGN KEY (a) REFERENCES t1(a));
348-
--error ER_TOO_LONG_IDENT
348+
# Next statement is valid. Constraint name will be truncated.
349349
RENAME TABLE t2 TO t123456789012345678901234567890123456789012345678901234567;
350-
RENAME TABLE t2 TO t12345678901234567890123456789012345678901234567890123456;
350+
# Constraint name not changed further. Still in the truncated form.
351+
RENAME TABLE t123456789012345678901234567890123456789012345678901234567 TO t12345678901234567890123456789012345678901234567890123456;
351352
SELECT constraint_name FROM information_schema.referential_constraints
352353
WHERE table_name = 't12345678901234567890123456789012345678901234567890123456'
353354
ORDER BY constraint_name;

sql/dd/dd_table.cc

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2528,9 +2528,14 @@ bool rename_foreign_keys(THD *thd [[maybe_unused]],
25282528
dd::String_type new_name(table_name);
25292529
// Copy <fk_name_suffix><number> (e.g. "_ibfk_nnnn") from the old name.
25302530
new_name.append(fk->name().substr(old_table_name_norm_len));
2531-
if (check_string_char_length(to_lex_cstring(new_name.c_str()), "",
2532-
NAME_CHAR_LEN, system_charset_info, true)) {
2533-
my_error(ER_TOO_LONG_IDENT, MYF(0), new_name.c_str());
2531+
/*
2532+
PlanetScale patch: See https://bugs.mysql.com/bug.php?id=107772. We want to avoid the situation where a
2533+
RENAME TABLE fails due to CONSTRAINT names becoming too long. This can happen if the target table name
2534+
is long enough. Instead of failing due to the constraint name being too long, we trim the constraint name
2535+
until its length is valid.
2536+
*/
2537+
if (truncate_constraint_name(&new_name)) {
2538+
my_error(ER_TOO_LONG_IDENT, MYF(0), fk->name());
25342539
return true;
25352540
}
25362541

@@ -2839,9 +2844,14 @@ bool rename_check_constraints(const char *old_table_name, dd::Table *new_tab) {
28392844
// Generate new name.
28402845
dd::String_type new_name(new_tab->name());
28412846
new_name.append(cc->name().substr(old_table_name_length));
2842-
if (check_string_char_length(to_lex_cstring(new_name.c_str()), "",
2843-
NAME_CHAR_LEN, system_charset_info, true)) {
2844-
my_error(ER_TOO_LONG_IDENT, MYF(0), new_name.c_str());
2847+
/*
2848+
PlanetScale patch: See https://bugs.mysql.com/bug.php?id=107772. We want to avoid the situation where a
2849+
RENAME TABLE fails due to CONSTRAINT names becoming too long. This can happen if the target table name
2850+
is long enough. Instead of failing due to the constraint name being too long, we trim the constraint name
2851+
until its length is valid.
2852+
*/
2853+
if (truncate_constraint_name(&new_name)) {
2854+
my_error(ER_TOO_LONG_IDENT, MYF(0), cc->name());
28452855
return true;
28462856
}
28472857
// Set new name.

sql/sql_table.cc

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13153,20 +13153,23 @@ static bool collect_fk_names_for_rename_table(
1315313153
if (is_table_renamed &&
1315413154
dd::is_generated_foreign_key_name(old_table_name_norm,
1315513155
old_table_name_norm_len, hton, *fk)) {
13156-
char new_fk_name[NAME_LEN + 1];
13157-
13156+
dd::String_type new_name(new_table_name_lc);
13157+
new_name.append(fk->name().substr(old_table_name_norm_len));
1315813158
/*
13159-
Copy <SE-specific or default FK name suffix><number> part.
13160-
Here we truncate generated name if it is too long. This is sufficient
13161-
for MDL purposes. Error will be reported later in this case.
13159+
PlanetScale patch: See https://bugs.mysql.com/bug.php?id=107772. We want to avoid the situation where a
13160+
RENAME TABLE fails due to CONSTRAINT names becoming too long. This can happen if the target table name
13161+
is long enough. Instead of failing due to the constraint name being too long, we trim the constraint name
13162+
until its length is valid.
1316213163
*/
13163-
strxnmov(new_fk_name, NAME_LEN, new_table_name_lc,
13164-
fk->name().c_str() + old_table_name_norm_len, NullS);
13164+
if (truncate_constraint_name(&new_name)) {
13165+
my_error(ER_TOO_LONG_IDENT, MYF(0), fk->name());
13166+
return true;
13167+
}
1316513168

1316613169
MDL_request *mdl_request2 = new (thd->mem_root) MDL_request;
1316713170
if (mdl_request2 == nullptr) return true;
1316813171

13169-
MDL_REQUEST_INIT(mdl_request2, MDL_key::FOREIGN_KEY, new_db, new_fk_name,
13172+
MDL_REQUEST_INIT(mdl_request2, MDL_key::FOREIGN_KEY, new_db, new_name.c_str(),
1317013173
MDL_EXCLUSIVE, MDL_STATEMENT);
1317113174

1317213175
mdl_requests->push_front(mdl_request2);
@@ -13209,30 +13212,26 @@ static bool check_fk_names_before_rename(THD *thd, Table_ref *table_list,
1320913212
if (alter_ctx.is_table_name_changed() &&
1321013213
dd::is_generated_foreign_key_name(
1321113214
table_list->table_name, table_list->table_name_length, hton, *fk)) {
13212-
// We reserve extra NAME_LEN to ensure that new name fits.
13213-
char new_fk_name[NAME_LEN + NAME_LEN + 1];
13214-
13215+
dd::String_type new_name(alter_ctx.new_name);
13216+
new_name.append(fk->name().substr(table_list->table_name_length));
1321513217
/*
13216-
Construct new name by copying <FK name suffix><number> suffix
13217-
from the old one.
13218+
PlanetScale patch: See https://bugs.mysql.com/bug.php?id=107772. We want to avoid the situation where a
13219+
RENAME TABLE fails due to CONSTRAINT names becoming too long. This can happen if the target table name
13220+
is long enough. Instead of failing due to the constraint name being too long, we trim the constraint name
13221+
until its length is valid.
1321813222
*/
13219-
strxnmov(new_fk_name, sizeof(new_fk_name) - 1, alter_ctx.new_name,
13220-
fk->name().c_str() + table_list->table_name_length, NullS);
13221-
13222-
if (check_string_char_length(to_lex_cstring(new_fk_name), "",
13223-
NAME_CHAR_LEN, system_charset_info,
13224-
true /* no error */)) {
13225-
my_error(ER_TOO_LONG_IDENT, MYF(0), new_fk_name);
13223+
if (truncate_constraint_name(&new_name)) {
13224+
my_error(ER_TOO_LONG_IDENT, MYF(0), fk->name());
1322613225
return true;
1322713226
}
1322813227

1322913228
bool exists;
13230-
if (thd->dd_client()->check_foreign_key_exists(new_schema, new_fk_name,
13229+
if (thd->dd_client()->check_foreign_key_exists(new_schema, new_name.c_str(),
1323113230
&exists))
1323213231
return true;
1323313232

1323413233
if (exists) {
13235-
my_error(ER_FK_DUP_NAME, MYF(0), new_fk_name);
13234+
my_error(ER_FK_DUP_NAME, MYF(0), new_name.c_str());
1323613235
return true;
1323713236
}
1323813237
} else if (alter_ctx.is_database_changed()) {
@@ -19934,3 +19933,27 @@ bool lock_check_constraint_names(THD *thd, Table_ref *tables) {
1993419933

1993519934
return false;
1993619935
}
19936+
19937+
bool truncate_constraint_name(dd::String_type *name) {
19938+
/*
19939+
PlanetScale patch: See https://bugs.mysql.com/bug.php?id=107772. We want to avoid the situation where a
19940+
RENAME TABLE fails due to CONSTRAINT names becoming too long. This can happen if the target table name
19941+
is long enough. Instead of failing due to the constraint name being too long, we trim the constraint name
19942+
until its length is valid.
19943+
*/
19944+
if (name->length() == 1) return false;
19945+
while (name->length() > 1) {
19946+
const LEX_CSTRING lexcs = to_lex_cstring(name->c_str());
19947+
if (!check_string_char_length(lexcs, "", NAME_CHAR_LEN, system_charset_info, true /* no error */)) {
19948+
// name is fine and fits in NAME_CHAR_LEN
19949+
return false;
19950+
}
19951+
19952+
// Find byte width of the first two codepoints (UTF-8 sequences)
19953+
size_t pos2 = system_charset_info->cset->charpos(system_charset_info, lexcs.str, lexcs.str + lexcs.length, 2);
19954+
name->erase(0, pos2);
19955+
name->insert(0, "_");
19956+
}
19957+
// Nothing remains of truncated text. Error.
19958+
return true;
19959+
}

sql/sql_table.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,4 +663,13 @@ bool lock_check_constraint_names_for_rename(THD *thd, const char *db,
663663
bool prepare_check_constraints_for_create(THD *thd, const char *db_name,
664664
const char *table_name,
665665
Alter_info *alter_info);
666+
/**
667+
Method to truncate constraint name as needed to fit within NAME_CHAR_LEN
668+
669+
@param name Name to truncate
670+
671+
@retval false Success.
672+
@retval true Failure.
673+
*/
674+
bool truncate_constraint_name(dd::String_type *name);
666675
#endif /* SQL_TABLE_INCLUDED */

0 commit comments

Comments
 (0)