Bug #121030 Upgrade fails with MY-013135 on tables with CHECK constraints/generated columns/partitions
Submitted: 29 Jul 21:55 Modified: 31 Jul 7:54
Reporter: Sabalesh Mahajan Email Updates:
Status: Patch pending Impact on me:
None 
Category:MySQL Server: Data Dictionary Severity:S2 (Serious)
Version:8.0.42+ and 8.4.5+ OS:Any
Assigned to: CPU Architecture:Any

[29 Jul 21:55] Sabalesh Mahajan
Description:
In-place upgrade to MySQL 8.0.42+ or 8.4.5+ fails with MY-013135 ("Incorrect information in file") when both of these conditions exist on the instance:

1. A stored procedure whose body contains `CREATE TEMPORARY TABLE ... ENGINE=Memory`
2. A table with CHECK constraints, generated columns, or partition expressions that use SQL functions (e.g., `regexp_like()`)

Either condition alone does not cause the failure. Both must be present.

The root cause is in `sql/dd/impl/upgrade/server.cc`. During `do_server_upgrade_checks()`, the `check_routines` phase compiles stored procedures. When it encounters `ENGINE=Memory`, `resolve_engine()` fails because the MEMORY storage engine is not loaded during bootstrap. This sets error 1286 (`ER_UNKNOWN_STORAGE_ENGINE`) in the THD diagnostics area. The function `invalid_routine()` returns early without clearing the error:

```c
// sql/dd/impl/upgrade/server.cc — invalid_routine()
if (error) return (thd->get_stmt_da()->mysql_errno() == ER_PARSE_ERROR);
thd->clear_error();  // ONLY reached when error == false
```

The stale THD error then propagates to `check_table_funs()` (added in MySQL 8.4.5, backported to 8.0.42+), which calls `open_table()` → `open_table_from_share()` → `unpack_value_generator()` → `fix_fields()`. Expression evaluation sees the stale `is_error()` and fails, producing MY-013135.

The `invalid_routine()` function is byte-for-byte identical in 8.0.40, 8.0.44, 8.4.4, and 8.4.5. The bug was latent until `check_table_funs` was added to the `process_schema` pipeline.

Related: Bug #120326 (identical mechanism with partitioned tables instead of CHECK constraints)

How to repeat:
### 1. Deploy MySQL 8.0.40 (or any version below 8.0.42)

```bash
mysqld --initialize-insecure --datadir=/tmp/repro_data --basedir=/path/to/mysql-8.0.40
mysqld --defaults-file=/dev/null --datadir=/tmp/repro_data --port=3307 \
  --socket=/tmp/repro.sock --log-error=/tmp/repro.err --log_error_verbosity=3 &
```

### 2. Create stored procedure with ENGINE=Memory (condition 1)

```sql
CREATE DATABASE testrepro;
USE testrepro;

DELIMITER //
CREATE PROCEDURE my_proc()
BEGIN
  CREATE TEMPORARY TABLE IF NOT EXISTS temp_memory (
    id BIGINT PRIMARY KEY
  ) ENGINE=Memory;
  DROP TEMPORARY TABLE IF EXISTS temp_memory;
END //
DELIMITER ;
```

### 3. Create table with CHECK constraints using regexp_like() (condition 2)

```sql
CREATE TABLE job_config (
  id smallint unsigned NOT NULL AUTO_INCREMENT,
  source_name varchar(128) NOT NULL,
  target_name varchar(128) NOT NULL,
  fallback_name varchar(128) DEFAULT NULL,
  job_type enum('standard','tiered','custom') NOT NULL DEFAULT 'standard',
  filter_col varchar(128) NOT NULL DEFAULT 'created_at',
  retain_months smallint unsigned NOT NULL DEFAULT '36',
  expire_months smallint unsigned NOT NULL DEFAULT '84',
  stage_months smallint unsigned DEFAULT NULL,
  active tinyint(1) NOT NULL DEFAULT '1',
  priority smallint unsigned NOT NULL DEFAULT '100',
  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY uq_source (source_name),
  KEY idx_type_active (job_type, active, priority),
  CONSTRAINT chk_retain_min CHECK ((retain_months >= 6)),
  CONSTRAINT chk_target_name CHECK (regexp_like(target_name,_utf8mb4'^[a-zA-Z_][a-zA-Z0-9_]*$')),
  CONSTRAINT chk_fallback_name CHECK (((fallback_name is null) or regexp_like(fallback_name,_utf8mb4'^[a-zA-Z_][a-zA-Z0-9_]*$'))),
  CONSTRAINT chk_source_name CHECK (regexp_like(source_name,_utf8mb4'^[a-zA-Z_][a-zA-Z0-9_]*$')),
  CONSTRAINT chk_expire_gt_retain CHECK ((expire_months > retain_months)),
  CONSTRAINT chk_expire_min CHECK ((expire_months >= 6)),
  CONSTRAINT chk_filter_col CHECK (regexp_like(filter_col,_utf8mb4'^[a-zA-Z_][a-zA-Z0-9_]*$')),
  CONSTRAINT chk_tiered CHECK (((job_type <> _utf8mb4'tiered') or ((fallback_name is not null) and (stage_months is not null))))
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO job_config (source_name, target_name) VALUES ('events', 'events_history');
```

### 4. Stop 8.0.40 and start 8.4.5 (or 8.0.44)

```bash
mysqladmin -S /tmp/repro.sock shutdown

/path/to/mysql-8.4.5/bin/mysqld --defaults-file=/dev/null \
  --datadir=/tmp/repro_data --port=3307 --socket=/tmp/repro.sock \
  --log-error=/tmp/repro_upgrade.err --log_error_verbosity=3 --upgrade=AUTO
```

### 5. Observe failure

```
[ERROR] [MY-013135] [Server] Incorrect information in file: './testrepro/job_config.frm'
[Warning] [MY-014078] [Server] Can not open table `testrepro`.`job_config`; functions in constraints, partitions, or virtual columns may have failed.
[ERROR] [MY-010020] [Server] Data Dictionary initialization failed.
[ERROR] [MY-010119] [Server] Aborting
```

### 6. Verify: drop the stored procedure, retry — SUCCEEDS

On a fresh datadir with the same table but no stored procedure referencing ENGINE=Memory, upgrade to 8.4.5 completes without error.

### 7. Verify: drop the CHECK constraints, retry — SUCCEEDS

On a fresh datadir with the stored procedure but only simple CHECK constraints (no `regexp_like()` or other function calls), upgrade to 8.4.5 completes without error.

---

## Test Matrix (confirmed)

| Source | Target | check_table_funs? | Result |
|--------|--------|-------------------|--------|
| 8.0.40 | 8.0.44 | YES | FAILED |
| 8.0.40 | 8.4.4 | NO | SUCCEEDED |
| 8.0.40 | 8.4.5 | YES | FAILED |
| 8.0.40 | 8.4.6 | YES | FAILED |
| 8.0.40 | 8.4.7 | YES | FAILED |
| 8.0.44 (fresh) | 8.4.4 | NO | SUCCEEDED |
| 8.0.44 (fresh) | 8.4.5 | YES | FAILED |

---

Suggested fix:
Clear THD before calling `check_table_funs`:

```c
// sql/dd/impl/upgrade/server.cc — do_server_upgrade_checks()
auto process_schema = [&](std::unique_ptr<Schema> &schema) {
    return check_tables(...) || check_events(...) || check_routines(...) || check_views(...) ||
           (thd->clear_error(), false) ||
           check_table_funs(...);
};
```
[9 Sep 9:43] Dyre Tjeldvoll
Posted by developer:
 
Thank you for the bug report. The code issue mentioned is real and still present AFAICT. However, after the fix for Bug#38031020 (8.0.47) the repro no longer triggers the issue, because the order in which dd-elements are checked has changed. Now all routines are checked after all tables have been checked, so the uncleared error does not appear to cause the same symptom.
[9 Sep 11:30] Jean-François Gagné
> after the fix for Bug#38031020 (8.0.47)

I only find 8.0.46 on the download page, so I guess you meant 8.0.46, or is there a planned 8.0.47.

I found a reference to Bug#38031020 in below commits which is included in 8.4.11, 9.7.2 and 26.7.0.

8.4.11, 9.7.2 and 26.7.0: https://github.com/mysql/mysql-server/commit/ae636065031203585b08f9e3434f0ccc59228a93

https://github.com/mysql/mysql-server/commit/6be5e1d36b6a56fdaa917889d106be26dbe1e1ea

I also found a reference to Bug#38031020 in the 8.4.11 release notes, but not in the 8.0.46 (links below).

https://dev.mysql.com/doc/relnotes/mysql/8.4/en/news-8-4-11.html

https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-46.html

I am surprised that the status of this report is still Verified, shouldn't it be Closed / Fixed in 8.4.11, 9.7.2 and 26.7.0 ?
[9 Sep 13:10] Dyre Tjeldvoll
Posted by developer:
 
Ensuring that thd->is_error() is false before proceeding to the next object in Dictionary_client::foreach() reveals that the problem described above does indeed happen in existing upgrade tests, but without an observable symptom. In this case invalid_routine fails to clear

sqlstate:[42S02], mysql_errno:[1109], message_text:[Unknown table 'INNODB_TRX' in information_schema]

for an internal routine.
[9 Sep 13:21] Dyre Tjeldvoll
Posted by developer:
 
The number 8.0.47 comes from the content of the MYSQL_VERSION file in commit of the bug. In this case:

git show e33c895839f223f02eee7c9b5d75420dbb2d5c16:MYSQL_VERSION
MYSQL_VERSION_MAJOR=8
MYSQL_VERSION_MINOR=0
MYSQL_VERSION_PATCH=47
MYSQL_VERSION_EXTRA=
MYSQL_VERSION_STABILITY="LTS"

Normally, that would correspond to the version in which the issue was fixed. But, yes, there has not been another release on 8.0 which includes this. Not sure if one is planned.
(The content of MYSQL_VERSION in a commit is not correct in the latest releases where the version numbering was changed after commit was pushed).

Regarding state; as mentioned earlier there is still an issue to be fixed here even if it cannot be demonstrated with the same repro.
[9 Sep 14:02] Jean-François Gagné
> Regarding state [...] there is still an issue to be fixed here even if it cannot be demonstrated with the same repro.

Noted and thanks for the clarification Dyre.  Two questions:

1) Should 9.7 and 26.07 be added as affected ?

2) Is Severity S2 (Serious) still relevant after the fix for Bug#38031020, or should this be downgraded to S3 (Non-critical) or S5 (Performance). or maybe upgraded to S1 (Critical) because an upgrade failing is more serious than S2 ?
[11 Sep 12:45] Dyre Tjeldvoll
Posted by developer:
 
I left the severity as is. (They are rarely changed for bugs already being worked on).
[11 Sep 14:48] MySQL Admin
Posted by developer: Bug status updated to 'Patch pending'