Bug #118476 Unable to create partition with name '1e3fRQM2GhI'
Submitted: 18 Jun 8:45 Modified: 18 Jun 9:01
Reporter: Li Yirong (OCA) Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: DDL Severity:S2 (Serious)
Version:8.0, 8.4 OS:Red Hat
Assigned to: CPU Architecture:x86
Tags: Contribution

[18 Jun 8:45] Li Yirong
Description:
Cannot create partition with name '1e3fRQM2GhI'.

More seriously, adding a partition named '1e3fRQM2GhI' using the Alter statement in MySQL 5.7 will cause the table to be unable to be opened or dropped later.

How to repeat:
this SQL will fail in MySQL5.7 and MySQL8.0
CREATE TABLE `ep_xxx` (   
    `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,   
    `status` varchar(255) NOT NULL,   
    `tenant_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,   
    `created_date` datetime NOT NULL,   
    `project_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,   
    PRIMARY KEY (`id`,`project_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC 
    PARTITION BY LIST  COLUMNS(project_id) (
        PARTITION `1HkZbSJOALI` VALUES IN ('1HkZbSJOALI') ENGINE = InnoDB,  
        PARTITION `1cUPlKUTy1R` VALUES IN ('1cUPlKUTy1R') ENGINE = InnoDB
);

this SQL will fail in MySQL 8.0 and will cause the table to be unable to be opened in MySQL5.7.
CREATE TABLE `ep_xxxx` (   `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,   `status` varchar(255) NOT NULL,   `tenant_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,   `created_date` datetime NOT NULL,   `project_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,   PRIMARY KEY (`id`,`project_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC PARTITION BY LIST  COLUMNS(project_id) (PARTITION `1HkZbSJOALI` VALUES IN ('1HkZbSJOALI') ENGINE = InnoDB,  PARTITION `1cUPlKUTy1R` VALUES IN ('1cUPlKUTy1R') ENGINE = InnoDB);

ALTER TABLE `ep_xxxx` ADD PARTITION (PARTITION `1e3fRQM2GhI` VALUES IN ('1e3fRQM2GhI') ENGINE = InnoDB);

Suggested fix:
1. All partition names should be enclosed in backticks.

2. There exists a bug in function `require_quotes`. This function does not return non-zero even if the parser cannot correctly parse an identifier.
[18 Jun 9:01] MySQL Verification Team
Hello!

Thank you for the report and feedback.

regards,
Umesh
[20 Jun 20:10] MySQL Verification Team
create table t(a int) engine=innodb
partition by list columns(a)(
 partition p1 values in (1)
);

alter table t add partition (partition `1a1` values in (3)); -- works
alter table t add partition (partition `111` values in (4)); -- works
alter table t add partition (partition `1e1` values in (5)); -- syntax error
[26 Jun 13:16] huahua xu
The sql_lex.cc could not parse `1e3fRQM2GhI` as an identifier by analyzing the source code.

```
case MY_LEX_NUMBER_IDENT:  // number or ident which num-start
        if (lip->yyGetLast() == '0') {
          c = lip->yyGet();
          if (c == 'x') {
            while (my_isxdigit(cs, (c = lip->yyGet())))
              ;
            if ((lip->yyLength() >= 3) && !ident_map[c]) {
              /* skip '0x' */
              yylval->lex_str = get_token(lip, 2, lip->yyLength() - 2);
              return (HEX_NUM);
            }
            lip->yyUnget();
            state = MY_LEX_IDENT_START;
            break;
          } else if (c == 'b') {
            while ((c = lip->yyGet()) == '0' || c == '1')
              ;
            if ((lip->yyLength() >= 3) && !ident_map[c]) {
              /* Skip '0b' */
              yylval->lex_str = get_token(lip, 2, lip->yyLength() - 2);
              return (BIN_NUM);
            }
            lip->yyUnget();
            state = MY_LEX_IDENT_START;
            break;
          }
          lip->yyUnget();
        }

        while (my_isdigit(cs, (c = lip->yyGet())))
          ;
        if (!ident_map[c]) {  // Can't be identifier
          state = MY_LEX_INT_OR_REAL;
          break;
        }
        if (c == 'e' || c == 'E') {
          // The following test is written this way to allow numbers of type 1e1
          if (my_isdigit(cs, lip->yyPeek()) || (c = (lip->yyGet())) == '+' ||
              c == '-') {  // Allow 1E+10
            if (my_isdigit(cs,
                           lip->yyPeek()))  // Number must have digit after sign
            {
              lip->yySkip();
              while (my_isdigit(cs, lip->yyGet()))
                ;
              yylval->lex_str = get_token(lip, 0, lip->yyLength());
              return (FLOAT_NUM);
            }
          }
          lip->yyUnget();
        }

```

Suggested fix: the strings starting with floating-point numbers should be required quotes in the method `require_quotes`.
[2 Jul 1:06] Li Yirong
Fix this bug

(*) I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it.

Contribution: 0001-Fix-partition-name-bug.patch (application/octet-stream, text), 19.21 KiB.

[2 Jul 14:47] MySQL Verification Team
Thank you, for your Contribution.

regards,
Umesh