Bug #121692 Functional index hidden column keeps stale type after MODIFY on dependent column

Submitted: 7 Nov 3:28
Reporter: fei yang Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:8.0, 9.7.0 OS:Any
Assigned to: CPU Architecture:Any

[7 Nov 3:28] fei yang
Description:
A functional index KEY kf ((expr)) is backed by a hidden generated column whose type/length is derived from expr. If you MODIFY a column that expr depends on (not the hidden column itself), the hidden column keeps its old type/length instead of being re-derived. The index is rebuilt on the stale hidden column, causing:

Wrong results (non-strict mode): values silently truncate; index scans miss rows, table scans find them. CHECK TABLE reports OK.
Spurious errors (strict mode): ER_DATA_TOO_LONG on valid data.
UNIQUE variant: false ER_DUP_ENTRY (1062) — distinct values truncate to the same key.
ALTER TABLE ... FORCE does not repair it (same code path). Only DROP INDEX + ADD INDEX fixes it.
The bug originates in the SQL-layer table preparation (prepare_fields_and_keys), before the engine's algorithm choice — so it is unaffected by ALGORITHM or online-DDL toggles.

How to repeat:
SET sql_mode='';

CREATE TABLE t (id INT PRIMARY KEY, a VARCHAR(50),
  v VARCHAR(10) AS (LEFT(a,10)) VIRTUAL,
  KEY kf ((CONCAT(v,'!')))) ENGINE=InnoDB;

INSERT INTO t(id,a) VALUES (1,'abcdefghij'),(2,'xyz');

ALTER TABLE t MODIFY v VARCHAR(20) AS (LEFT(a,20)) VIRTUAL;

UPDATE t SET a='0123456789ABCDEFGHIJ' WHERE id=2;
-- Warning 3751: Data truncated for functional index 'kf'

SELECT id FROM t FORCE INDEX(kf)  WHERE CONCAT(v,'!')='0123456789ABCDEFGHIJ!';  -- empty (wrong)
SELECT id FROM t IGNORE INDEX(kf) WHERE CONCAT(v,'!')='0123456789ABCDEFGHIJ!';  -- 2     (correct)

CHECK TABLE t;  -- OK

ALTER TABLE t FORCE;  -- does NOT fix it
SELECT id FROM t FORCE INDEX(kf) WHERE CONCAT(v,'!')='0123456789ABCDEFGHIJ!';  -- still empty

DROP INDEX kf ON t;
ALTER TABLE t ADD KEY kf ((CONCAT(v,'!')));
SELECT id FROM t FORCE INDEX(kf) WHERE CONCAT(v,'!')='0123456789ABCDEFGHIJ!';  -- 2 (fixed)

Suggested fix:
In sql/sql_table.cc, prepare_fields_and_keys() copies each existing column not in the user's MODIFY list verbatim: def = new Create_field(field, field). The hidden column backing a functional index is never in the user's alter list (the user modifies v, not !hidden!kf!0!0), so it is copied with its old type.

When the existing functional index key part is rebuilt, it carries both the hidden column name and the expression. Then mysql_prepare_create_table() skips re-derivation:

if (!key_part_spec->has_expression() ||
    (key_part_spec->get_field_name() != nullptr &&
     column_exists_in_create_list(key_part_spec->get_field_name(),
                                  alter_info->create_list))) {
  continue;   // hidden column already present (stale type) -> skip
}
So add_functional_index_to_create_list() — which would call generate_create_field() to re-derive the correct type — is skipped, and the stale hidden column is used.

By contrast, KEY kcb (cv) (normal index on the virtual column) + MODIFY cv works: cv is the index column and is in the alter list, so it takes the modified-definition path and the index rebuilds on the new type.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.048 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.