From d33b1aff668f832cd09a1134966e19d00675a49d Mon Sep 17 00:00:00 2001 From: Rick Lam Date: Thu, 30 Apr 2026 16:14:55 +0100 Subject: [PATCH] Skip ROW_FORMAT=FIXED for InnoDB to avoid MySQL 8 error 1031 ROW_FORMAT=FIXED is only valid for MyISAM. MySQL 5.6 and earlier silently downgraded it to COMPACT for InnoDB; MySQL 8 rejects it with `ERROR 1031: Table storage engine ... doesn't have this option`, which makes morphism diff/apply fail on tables that were originally defined with ROW_FORMAT=FIXED on an InnoDB engine. Drop ROW_FORMAT silently for InnoDB at both emission sites (the .sql parser in TableOptions::toString() and the live-DB extractor in Extractor::getTableOptionDefs()) so the resulting CREATE/ALTER statements run cleanly on MySQL 8. ROW_FORMAT=FIXED is preserved on MyISAM, where it remains valid. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Extractor.php | 8 +++++++- src/Parse/TableOptions.php | 8 ++++++++ tests/Parse/TableOptionsTest.php | 5 ++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Extractor.php b/src/Extractor.php index b4f3fdb..ec91be2 100644 --- a/src/Extractor.php +++ b/src/Extractor.php @@ -443,7 +443,13 @@ private function getTableOptionDefs($table) $defTableOptions = []; $defTableOptions[] = "ENGINE=$table->ENGINE"; if (strcasecmp($table->ROW_FORMAT, 'COMPACT') != 0) { - $defTableOptions[] = "ROW_FORMAT=$table->ROW_FORMAT"; + // ROW_FORMAT=FIXED is only valid for MyISAM; MySQL 8 errors with + // 1031 if applied to InnoDB. Drop it silently for InnoDB. + $skipRowFormat = strcasecmp((string) $table->ROW_FORMAT, 'FIXED') === 0 + && strcasecmp((string) $table->ENGINE, 'InnoDB') === 0; + if (!$skipRowFormat) { + $defTableOptions[] = "ROW_FORMAT=$table->ROW_FORMAT"; + } } if ($table->AUTO_INCREMENT) { $defTableOptions[] = "AUTO_INCREMENT=$table->AUTO_INCREMENT"; diff --git a/src/Parse/TableOptions.php b/src/Parse/TableOptions.php index 7db93ec..c2e3831 100644 --- a/src/Parse/TableOptions.php +++ b/src/Parse/TableOptions.php @@ -300,6 +300,14 @@ public function toString() ] as $option) { if ($this->options[$option] !== $this->defaultOptions[$option]) { $value = $this->options[$option]; + // ROW_FORMAT=FIXED is only valid for MyISAM; MySQL 8 errors with + // 1031 if applied to InnoDB. Drop it silently for InnoDB. + if ($option === 'ROW_FORMAT' + && strcasecmp((string) $value, 'FIXED') === 0 + && strcasecmp((string) $this->engine, 'InnoDB') === 0 + ) { + continue; + } if (in_array($option, ['COMMENT', 'CONNECTION'])) { $value = Token::escapeString($value); } diff --git a/tests/Parse/TableOptionsTest.php b/tests/Parse/TableOptionsTest.php index 1c9f368..e1c1b09 100644 --- a/tests/Parse/TableOptionsTest.php +++ b/tests/Parse/TableOptionsTest.php @@ -89,7 +89,10 @@ public function parseProvider() ["row_format=default", "ENGINE=InnoDB"], ["row_format=dynamic", "ENGINE=InnoDB ROW_FORMAT=DYNAMIC"], - ["row_format=fixed", "ENGINE=InnoDB ROW_FORMAT=FIXED"], + // ROW_FORMAT=FIXED is invalid for InnoDB on MySQL 8 (error 1031); + // morphism silently drops it so the table can be created. + ["row_format=fixed", "ENGINE=InnoDB"], + ["engine=MyISAM row_format=fixed", "ENGINE=MyISAM ROW_FORMAT=FIXED"], ["row_format=compressed", "ENGINE=InnoDB ROW_FORMAT=COMPRESSED"], ["row_format=redundant", "ENGINE=InnoDB ROW_FORMAT=REDUNDANT"], ["row_format=compact", "ENGINE=InnoDB ROW_FORMAT=COMPACT"],