From e8757f539541667dd91eb9cdd7a4ecd747fef096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Fri, 31 Jul 2026 10:57:13 +0200 Subject: [PATCH] MDEV-40417 A column with compression enabled automatically adds DEFAULT '' Compression is stored in unireg_check as Field::TMYSQL_COMPRESSED, but has_default_function() treated any unireg_check other than Field::NONE as a default function. A COMPRESSED NOT NULL column without an explicit DEFAULT therefore never got NO_DEFAULT_VALUE_FLAG, the FRM was written without FIELDFLAG_NO_DEFAULT, and the column silently became optional while SHOW CREATE TABLE displayed a phantom DEFAULT ''. Exclude TMYSQL_COMPRESSED in has_default_function() and use that method in Column_definition::check() as well, so that both places which set NO_DEFAULT_VALUE_FLAG stay in sync. Tables created before the fix keep the wrong pack_flag, so restore the flag while reading the FRM in TABLE_SHARE::init_from_binary_frm_image() and report it in the error log. That is only possible for blobs, whose explicit DEFAULT is always stored as an expression. For VARCHAR and VARBINARY an old FRM with DEFAULT '' is identical to one with the wrong implicit default, so those are left alone and need an explicit ALTER TABLE ... MODIFY. std_data/MDEV-40417.* is a MyISAM table created by 10.11.19 before the fix. Co-Authored-By: Claude Opus 5 (1M context) --- mysql-test/main/column_compression.result | 126 +++++++++++++++++- mysql-test/main/column_compression.test | 85 ++++++++++++ .../main/column_compression_errlog.result | 12 ++ .../main/column_compression_errlog.test | 33 +++++ mysql-test/std_data/MDEV-40417.MYD | Bin 0 -> 24 bytes mysql-test/std_data/MDEV-40417.MYI | Bin 0 -> 1024 bytes mysql-test/std_data/MDEV-40417.frm | Bin 0 -> 840 bytes sql/field.cc | 2 +- sql/field.h | 7 +- sql/table.cc | 31 +++++ 10 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 mysql-test/main/column_compression_errlog.result create mode 100644 mysql-test/main/column_compression_errlog.test create mode 100644 mysql-test/std_data/MDEV-40417.MYD create mode 100644 mysql-test/std_data/MDEV-40417.MYI create mode 100644 mysql-test/std_data/MDEV-40417.frm diff --git a/mysql-test/main/column_compression.result b/mysql-test/main/column_compression.result index 5b35d70a24d92..96f1285b098cc 100644 --- a/mysql-test/main/column_compression.result +++ b/mysql-test/main/column_compression.result @@ -1323,7 +1323,7 @@ LENGTH(a) SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` blob /*M!100301 COMPRESSED*/ NOT NULL DEFAULT '' + `a` blob /*M!100301 COMPRESSED*/ NOT NULL ) ENGINE=CSV DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" DROP TABLE t1; @@ -3051,3 +3051,127 @@ REPLACE INTO t VALUES ('abcdefghijklm'); UPDATE t SET c=MID(c,2,4); DROP TABLE t; # End of 10.6 tests +# +# MDEV-40417 A column with compression enabled automatically adds DEFAULT '' +# +CREATE TABLE t ( +c1 LONGTEXT COMPRESSED NOT NULL, +nc1 LONGTEXT NOT NULL, +c2 LONGTEXT COMPRESSED NOT NULL DEFAULT '', +nc2 LONGTEXT NOT NULL DEFAULT '', +c3 VARCHAR(100) COMPRESSED NOT NULL, +nc3 VARCHAR(100) NOT NULL +) ENGINE=InnoDB; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `c1` longtext /*M!100301 COMPRESSED*/ NOT NULL, + `nc1` longtext NOT NULL, + `c2` longtext /*M!100301 COMPRESSED*/ NOT NULL DEFAULT '', + `nc2` longtext NOT NULL DEFAULT '', + `c3` varchar(100) /*M!100301 COMPRESSED*/ NOT NULL, + `nc3` varchar(100) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +SELECT COLUMN_NAME, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t' ORDER BY ORDINAL_POSITION; +COLUMN_NAME COLUMN_DEFAULT +c1 NULL +nc1 NULL +c2 '' +nc2 '' +c3 NULL +nc3 NULL +# A compressed column must be as mandatory as an uncompressed one +INSERT INTO t (nc1) VALUES ('x'); +ERROR HY000: Field 'c1' doesn't have a default value +INSERT INTO t (c1, nc1, nc3) VALUES ('x', 'x', 'x'); +ERROR HY000: Field 'c3' doesn't have a default value +INSERT INTO t (c1, nc1, c3, nc3) VALUES ('x', 'x', 'x', 'x'); +SELECT * FROM t; +c1 nc1 c2 nc2 c3 nc3 +x x x x +DROP TABLE t; +# ALTER TABLE must not add an implicit default either +CREATE TABLE t (c LONGTEXT COMPRESSED NOT NULL DEFAULT 'x') ENGINE=InnoDB; +ALTER TABLE t MODIFY c LONGTEXT COMPRESSED NOT NULL; +ALTER TABLE t ADD c2 VARCHAR(100) COMPRESSED NOT NULL; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `c` longtext /*M!100301 COMPRESSED*/ NOT NULL, + `c2` varchar(100) /*M!100301 COMPRESSED*/ NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +DROP TABLE t; +# +# MDEV-40417 upgrade: a table created before the fix is repaired when +# its FRM is read. Only blobs can be repaired - for VARCHAR the FRM +# does not tell an explicit DEFAULT '' from the wrong implicit one, +# so those columns are deliberately left alone. +# +# MDEV-40417.frm was created by 10.11.19 as: +# CREATE TABLE mdev40417 ( +# blob_nodef LONGTEXT COMPRESSED NOT NULL, +# blob_def LONGTEXT COMPRESSED NOT NULL DEFAULT 'x', +# vc_nodef VARCHAR(100) COMPRESSED NOT NULL, +# vc_def VARCHAR(100) COMPRESSED NOT NULL DEFAULT '', +# plain_nodef LONGTEXT NOT NULL, +# pad INT) ENGINE=MyISAM CHARSET=latin1; +# INSERT INTO mdev40417 (plain_nodef, pad) VALUES ('p', 1); +# +call mtr.add_suppression("Found wrong implicit DEFAULT"); +SHOW CREATE TABLE mdev40417; +Table Create Table +mdev40417 CREATE TABLE `mdev40417` ( + `blob_nodef` longtext /*M!100301 COMPRESSED*/ NOT NULL, + `blob_def` longtext /*M!100301 COMPRESSED*/ NOT NULL DEFAULT 'x', + `vc_nodef` varchar(100) /*M!100301 COMPRESSED*/ NOT NULL DEFAULT '', + `vc_def` varchar(100) /*M!100301 COMPRESSED*/ NOT NULL DEFAULT '', + `plain_nodef` longtext NOT NULL, + `pad` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +# For the error log reporting see main.column_compression_errlog +SELECT COLUMN_NAME, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA='test' AND TABLE_NAME='mdev40417' ORDER BY ORDINAL_POSITION; +COLUMN_NAME COLUMN_DEFAULT +blob_nodef NULL +blob_def 'x' +vc_nodef '' +vc_def '' +plain_nodef NULL +pad NULL +# The row that the old server allowed to be inserted is still readable +SELECT * FROM mdev40417; +blob_nodef blob_def vc_nodef vc_def plain_nodef pad + x p 1 +# blob_nodef is mandatory again +INSERT INTO mdev40417 (blob_def, vc_nodef, vc_def, plain_nodef) +VALUES ('a', 'b', 'c', 'd'); +ERROR HY000: Field 'blob_nodef' doesn't have a default value +# plain_nodef was never affected by the bug +INSERT INTO mdev40417 (blob_nodef, blob_def, vc_nodef, vc_def) +VALUES ('a', 'b', 'c', 'd'); +ERROR HY000: Field 'plain_nodef' doesn't have a default value +# blob_def keeps its explicit DEFAULT +INSERT INTO mdev40417 (blob_nodef, vc_nodef, vc_def, plain_nodef) +VALUES ('a', 'b', 'c', 'd'); +# vc_nodef is not touched, it still has the old implicit DEFAULT '' +INSERT INTO mdev40417 (blob_nodef, blob_def, plain_nodef) VALUES ('a', 'b', 'd'); +SELECT blob_nodef, blob_def, vc_nodef, vc_def, plain_nodef FROM mdev40417; +blob_nodef blob_def vc_nodef vc_def plain_nodef + x p +a b d +a x b c d +# ALTER TABLE ... FORCE writes the repaired flag back into the FRM +ALTER TABLE mdev40417 FORCE; +SHOW CREATE TABLE mdev40417; +Table Create Table +mdev40417 CREATE TABLE `mdev40417` ( + `blob_nodef` longtext /*M!100301 COMPRESSED*/ NOT NULL, + `blob_def` longtext /*M!100301 COMPRESSED*/ NOT NULL DEFAULT 'x', + `vc_nodef` varchar(100) /*M!100301 COMPRESSED*/ NOT NULL DEFAULT '', + `vc_def` varchar(100) /*M!100301 COMPRESSED*/ NOT NULL DEFAULT '', + `plain_nodef` longtext NOT NULL, + `pad` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +DROP TABLE mdev40417; +# End of 10.11 tests diff --git a/mysql-test/main/column_compression.test b/mysql-test/main/column_compression.test index 2aba8862c6679..ebdfea8bedffa 100644 --- a/mysql-test/main/column_compression.test +++ b/mysql-test/main/column_compression.test @@ -594,3 +594,88 @@ UPDATE t SET c=MID(c,2,4); DROP TABLE t; --echo # End of 10.6 tests + +--echo # +--echo # MDEV-40417 A column with compression enabled automatically adds DEFAULT '' +--echo # + +CREATE TABLE t ( + c1 LONGTEXT COMPRESSED NOT NULL, + nc1 LONGTEXT NOT NULL, + c2 LONGTEXT COMPRESSED NOT NULL DEFAULT '', + nc2 LONGTEXT NOT NULL DEFAULT '', + c3 VARCHAR(100) COMPRESSED NOT NULL, + nc3 VARCHAR(100) NOT NULL +) ENGINE=InnoDB; +SHOW CREATE TABLE t; +SELECT COLUMN_NAME, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t' ORDER BY ORDINAL_POSITION; + +--echo # A compressed column must be as mandatory as an uncompressed one +--error ER_NO_DEFAULT_FOR_FIELD +INSERT INTO t (nc1) VALUES ('x'); +--error ER_NO_DEFAULT_FOR_FIELD +INSERT INTO t (c1, nc1, nc3) VALUES ('x', 'x', 'x'); +INSERT INTO t (c1, nc1, c3, nc3) VALUES ('x', 'x', 'x', 'x'); +SELECT * FROM t; +DROP TABLE t; + +--echo # ALTER TABLE must not add an implicit default either +CREATE TABLE t (c LONGTEXT COMPRESSED NOT NULL DEFAULT 'x') ENGINE=InnoDB; +ALTER TABLE t MODIFY c LONGTEXT COMPRESSED NOT NULL; +ALTER TABLE t ADD c2 VARCHAR(100) COMPRESSED NOT NULL; +SHOW CREATE TABLE t; +DROP TABLE t; + +--echo # +--echo # MDEV-40417 upgrade: a table created before the fix is repaired when +--echo # its FRM is read. Only blobs can be repaired - for VARCHAR the FRM +--echo # does not tell an explicit DEFAULT '' from the wrong implicit one, +--echo # so those columns are deliberately left alone. +--echo # +--echo # MDEV-40417.frm was created by 10.11.19 as: +--echo # CREATE TABLE mdev40417 ( +--echo # blob_nodef LONGTEXT COMPRESSED NOT NULL, +--echo # blob_def LONGTEXT COMPRESSED NOT NULL DEFAULT 'x', +--echo # vc_nodef VARCHAR(100) COMPRESSED NOT NULL, +--echo # vc_def VARCHAR(100) COMPRESSED NOT NULL DEFAULT '', +--echo # plain_nodef LONGTEXT NOT NULL, +--echo # pad INT) ENGINE=MyISAM CHARSET=latin1; +--echo # INSERT INTO mdev40417 (plain_nodef, pad) VALUES ('p', 1); +--echo # + +call mtr.add_suppression("Found wrong implicit DEFAULT"); + +--copy_file std_data/MDEV-40417.frm $MYSQLD_DATADIR/test/mdev40417.frm +--copy_file std_data/MDEV-40417.MYD $MYSQLD_DATADIR/test/mdev40417.MYD +--copy_file std_data/MDEV-40417.MYI $MYSQLD_DATADIR/test/mdev40417.MYI + +SHOW CREATE TABLE mdev40417; +--echo # For the error log reporting see main.column_compression_errlog +SELECT COLUMN_NAME, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA='test' AND TABLE_NAME='mdev40417' ORDER BY ORDINAL_POSITION; +--echo # The row that the old server allowed to be inserted is still readable +SELECT * FROM mdev40417; + +--echo # blob_nodef is mandatory again +--error ER_NO_DEFAULT_FOR_FIELD +INSERT INTO mdev40417 (blob_def, vc_nodef, vc_def, plain_nodef) +VALUES ('a', 'b', 'c', 'd'); +--echo # plain_nodef was never affected by the bug +--error ER_NO_DEFAULT_FOR_FIELD +INSERT INTO mdev40417 (blob_nodef, blob_def, vc_nodef, vc_def) +VALUES ('a', 'b', 'c', 'd'); +--echo # blob_def keeps its explicit DEFAULT +INSERT INTO mdev40417 (blob_nodef, vc_nodef, vc_def, plain_nodef) +VALUES ('a', 'b', 'c', 'd'); +--echo # vc_nodef is not touched, it still has the old implicit DEFAULT '' +INSERT INTO mdev40417 (blob_nodef, blob_def, plain_nodef) VALUES ('a', 'b', 'd'); +--sorted_result +SELECT blob_nodef, blob_def, vc_nodef, vc_def, plain_nodef FROM mdev40417; + +--echo # ALTER TABLE ... FORCE writes the repaired flag back into the FRM +ALTER TABLE mdev40417 FORCE; +SHOW CREATE TABLE mdev40417; +DROP TABLE mdev40417; + +--echo # End of 10.11 tests diff --git a/mysql-test/main/column_compression_errlog.result b/mysql-test/main/column_compression_errlog.result new file mode 100644 index 0000000000000..68a64765d38cd --- /dev/null +++ b/mysql-test/main/column_compression_errlog.result @@ -0,0 +1,12 @@ +call mtr.add_suppression("Found wrong implicit DEFAULT"); +# std_data/MDEV-40417.* was created by 10.11.19 before the fix, see the +# MDEV-40417 section of main.column_compression for its definition. +# Reading the old .frm reports the repaired blob column +SELECT COUNT(*) FROM mdev40417; +COUNT(*) +1 +FOUND 1 /Found wrong implicit DEFAULT '' for compressed field 'blob_nodef'/ in mysqld.1.err +# ... and only that one, vc_nodef cannot be recognized as affected +NOT FOUND /Found wrong implicit DEFAULT '' for compressed field 'vc_nodef'/ in mysqld.1.err +DROP TABLE mdev40417; +# End of 10.11 tests diff --git a/mysql-test/main/column_compression_errlog.test b/mysql-test/main/column_compression_errlog.test new file mode 100644 index 0000000000000..e68d7383863cf --- /dev/null +++ b/mysql-test/main/column_compression_errlog.test @@ -0,0 +1,33 @@ +# +# MDEV-40417 A column with compression enabled automatically adds DEFAULT '' +# +# Error log reporting for a table created before the fix. This lives in a +# file of its own because reading the server error log needs +# include/not_embedded.inc, and sourcing that from main.column_compression +# would skip that whole test for embedded server runs. +# +--source include/not_embedded.inc + +call mtr.add_suppression("Found wrong implicit DEFAULT"); + +let $MYSQLD_DATADIR= `select @@datadir`; + +--echo # std_data/MDEV-40417.* was created by 10.11.19 before the fix, see the +--echo # MDEV-40417 section of main.column_compression for its definition. +--copy_file std_data/MDEV-40417.frm $MYSQLD_DATADIR/test/mdev40417.frm +--copy_file std_data/MDEV-40417.MYD $MYSQLD_DATADIR/test/mdev40417.MYD +--copy_file std_data/MDEV-40417.MYI $MYSQLD_DATADIR/test/mdev40417.MYI + +--echo # Reading the old .frm reports the repaired blob column +SELECT COUNT(*) FROM mdev40417; +--let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let SEARCH_PATTERN= Found wrong implicit DEFAULT '' for compressed field 'blob_nodef' +--source include/search_pattern_in_file.inc + +--echo # ... and only that one, vc_nodef cannot be recognized as affected +--let SEARCH_PATTERN= Found wrong implicit DEFAULT '' for compressed field 'vc_nodef' +--source include/search_pattern_in_file.inc + +DROP TABLE mdev40417; + +--echo # End of 10.11 tests diff --git a/mysql-test/std_data/MDEV-40417.MYD b/mysql-test/std_data/MDEV-40417.MYD new file mode 100644 index 0000000000000000000000000000000000000000..7b1c9ccc23a2723de200d13ec8fe718bbd2ceaee GIT binary patch literal 24 acmZQ(5N2fj$HV{x6$}iFK&k*lFaQ7@@B#w> literal 0 HcmV?d00001 diff --git a/mysql-test/std_data/MDEV-40417.MYI b/mysql-test/std_data/MDEV-40417.MYI new file mode 100644 index 0000000000000000000000000000000000000000..40ed4e29841dc5eafb93c1b56a2c0fa613665e95 GIT binary patch literal 1024 zcmezOkDZZ$kJal>86jF)%`D7{&M>3LpY3FmVYO12;pM0$C3_A6X8o*nr zI7#})#;^tJKV~yRq?j1~!_*+L;6fvT!N%ZQ=^5field ; *ptr ; ptr++, k++) { if ((*ptr)->flags & BLOB_FLAG) + { + Field *field= *ptr; (*save++)= k; + + /* + Tables created before MDEV-40417 was fixed have no + FIELDFLAG_NO_DEFAULT in the FRM for a COMPRESSED NOT NULL column + without an explicit DEFAULT clause, so such a column wrongly looks + like it has DEFAULT ''. Repair the flag here, an explicit DEFAULT + of a blob column is always stored in the FRM as an expression, see + Column_definition::has_default_expression(), hence a blob that has + no default_value provably had no DEFAULT clause. For FRMs written + after the fix the flag is set already and nothing is done. + + VARCHAR and VARBINARY cannot be repaired at all: a constant DEFAULT + of a non-blob column is stored in the default record, exactly like + the wrong implicit default, which makes the two indistinguishable. + */ + if (field->compression_method() && !field->default_value && + !field->vcol_info && + (field->flags & (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG)) == + NOT_NULL_FLAG) + { + field->flags|= NO_DEFAULT_VALUE_FLAG; + sql_print_warning("Found wrong implicit DEFAULT '' for compressed " + "field '%s' of %`s.%`s; Please do " + "\"ALTER TABLE %`s FORCE\" to fix it.", + field->field_name.str, + share->db.str, share->table_name.str, + share->table_name.str); + } + } } }