Description:
Upgrading from MySQL 8.0.40 to 8.4.7 fails with MY-014078 on a valid partitioned table when a stored procedure containing ENGINE=Memory exists on the instance.
During the upgrade, do_server_upgrade_checks runs two phases:
1. check_routines - compiles all stored procedures to validate them
2. check_table_funs - opens all tables with SQL functions in their definitions
In check_routines, if a stored procedure body contains CREATE TEMPORARY TABLE ... ENGINE=Memory, the parser calls resolve_engine() which fails because the MEMORY engine isn't loaded during early bootstrap. This sets error 1286 (ER_UNKNOWN_STORAGE_ENGINE) in the THD diagnostics area. The error is never cleared.
In check_table_funs, for partitioned tables with function-based expressions (e.g., PARTITION BY RANGE (month(col))), the code calls fix_partition_func which calls part_val_int. In MySQL 8.4, part_val_int checks current_thd->is_error() unconditionally after val_int(). It sees the stale error 1286 from the stored procedure check and returns failure, even though the partition expression evaluated correctly.
In MySQL 8.0, part_val_int only checked is_error() inside the null_value branch, so stale THD errors did not affect partition expression evaluation.
The behavior change in part_val_int (sql/sql_partition.cc):
8.0: https://github.com/mysql/mysql-server/blob/mysql-8.0.40/sql/sql_partition.cc
*result = item_expr->val_int();
if (item_expr->null_value) {
if (current_thd->is_error()) return true;
*result = LLONG_MIN;
}
return false;
8.4: https://github.com/mysql/mysql-server/blob/mysql-8.4.0/sql/sql_partition.cc
*result = item_expr->val_int();
if (current_thd->is_error()) return true; // unconditional
if (item_expr->null_value) {
*result = LLONG_MIN;
}
return false;
The error is not logged to the error log. The only visible symptom is:
[MY-013135] Incorrect information in file: './db/table.frm'
[MY-014078] Can not open table; functions in constraints, partitions, or virtual columns may have failed.
[MY-010020] Data Dictionary initialization failed.
The .frm reference is misleading as there are no .frm files on MySQL 8.0 instances.
Confirmed via GDB: calling thd->clear_error() before fix_partition_func runs allows the upgrade to complete successfully.
Related: Bug #118347
How to repeat:
1. Install MySQL 8.0.40 and initialize a data directory.
2. Connect and create the following:
CREATE DATABASE test_repro;
USE test_repro;
DELIMITER //
CREATE PROCEDURE cleanup_proc()
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS temp_ids (
id BIGINT PRIMARY KEY
) ENGINE=Memory;
DROP TEMPORARY TABLE IF EXISTS temp_ids;
END //
DELIMITER ;
CREATE TABLE test_partition (
id BIGINT NOT NULL AUTO_INCREMENT,
dt DATETIME NOT NULL,
PRIMARY KEY (id, dt)
) PARTITION BY RANGE (month(dt)) (
PARTITION p01 VALUES LESS THAN (2),
PARTITION p02 VALUES LESS THAN (3),
PARTITION p03 VALUES LESS THAN (4),
PARTITION p04 VALUES LESS THAN (13)
);
3. Stop the 8.0.40 server.
4. Start the 8.4.7 binary against the same data directory.
5. The upgrade fails with:
2026-04-22T19:16:50.383880Z 1 [ERROR] [MY-013135] [Server] Incorrect information in file: './test_repro/test_partition.frm'
2026-04-22T19:16:50.383906Z 1 [Warning] [MY-014078] [Server] Can not open table `test_repro`.`test_partition`; functions in constraints, partitions, or virtual columns may have failed.
2026-04-22T19:16:50.383919Z 1 [Note] [MY-014079] [Server] TABLE `test_repro`.`test_partition` = { PARTITION = { expr: "month(`dt`)"; };};
2026-04-22T19:16:50.386026Z 0 [ERROR] [MY-010020] [Server] Data Dictionary initialization failed.
6. Drop the stored procedure and retry. The upgrade succeeds.
7. Create a fresh 8.0.40 instance with only the partition table (no stored procedure). Upgrade to 8.4.7. The upgrade succeeds.
Suggested fix:
Either:
1. Clear the THD error state in do_server_upgrade_checks between the check_routines and check_table_funs phases (sql/dd/impl/upgrade/server.cc).
2. Clear the THD error state in check_table_funs before processing each table (sql/dd/impl/upgrade/server.cc, before the open_table call).
3. Revert part_val_int to only check is_error() inside the null_value branch, matching the 8.0 behavior (sql/sql_partition.cc).