Skip to content

Commit 1c9e9d2

Browse files
committed
fix(MySQL): add an auto-increment column together with its index
MySQL rejects an AUTO_INCREMENT column without a key (error 1075), so adding one to an existing table and creating its index afterwards always failed. The column and the indexes covering it now go into a single ALTER TABLE; a column with no index fails with the same SchemaException as on table creation, before the column is added. Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent c8cff96 commit 1c9e9d2

1 file changed

Lines changed: 39 additions & 4 deletions

File tree

‎src/Driver/MySQL/MySQLHandler.php‎

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Cycle\Database\Schema\AbstractForeignKey;
2020
use Cycle\Database\Schema\AbstractIndex;
2121
use Cycle\Database\Schema\AbstractTable;
22+
use Cycle\Database\Schema\ComparatorInterface;
2223

2324
class MySQLHandler extends Handler
2425
{
@@ -158,19 +159,53 @@ protected function createStatement(AbstractTable $table): string
158159
return $statement . " ENGINE {$table->getEngine()}";
159160
}
160161

162+
#[\Override]
163+
protected function createColumns(AbstractTable $table, ComparatorInterface $comparator): void
164+
{
165+
foreach ($comparator->addedColumns() as $column) {
166+
$this->assertValid($column);
167+
168+
// MySQL rejects an AUTO_INCREMENT column without a key, so its index is added in the same statement.
169+
$statement = "ALTER TABLE {$this->identify($table)} ADD COLUMN {$column->sqlStatement($this->getDriver())}";
170+
foreach ($this->autoIncrementIndexes($table, [$column], $comparator->addedIndexes()) as $index) {
171+
$statement .= ", ADD {$index->sqlStatement($this->getDriver(), false)}";
172+
}
173+
174+
$this->run($statement);
175+
}
176+
}
177+
178+
#[\Override]
179+
protected function createIndexes(AbstractTable $table, ComparatorInterface $comparator): void
180+
{
181+
$created = $this->autoIncrementIndexes($table, $comparator->addedColumns(), $comparator->addedIndexes());
182+
foreach ($comparator->addedIndexes() as $index) {
183+
if (!\in_array($index, $created, true)) {
184+
$this->createIndex($table, $index);
185+
}
186+
}
187+
}
188+
161189
/**
162190
* Indexes covering AUTO_INCREMENT columns that do not lead the primary key.
163191
*
192+
* @param AbstractColumn[]|null $columns Columns to check, all table columns by default.
193+
* @param AbstractIndex[]|null $indexes Indexes to search, all table indexes by default.
194+
*
164195
* @return list<AbstractIndex>
165196
*
166197
* @throws SchemaException When such a column is neither in the primary key nor in an index.
167198
*/
168-
protected function autoIncrementIndexes(AbstractTable $table): array
169-
{
199+
protected function autoIncrementIndexes(
200+
AbstractTable $table,
201+
?array $columns = null,
202+
?array $indexes = null,
203+
): array {
170204
$primaryKeys = $table->getPrimaryKeys();
205+
$indexes ??= $table->getIndexes();
171206

172207
$result = [];
173-
foreach ($table->getColumns() as $column) {
208+
foreach ($columns ?? $table->getColumns() as $column) {
174209
if (
175210
($column->getAttributes()['autoIncrement'] ?? false) !== true
176211
|| ($primaryKeys[0] ?? null) === $column->getName()
@@ -179,7 +214,7 @@ protected function autoIncrementIndexes(AbstractTable $table): array
179214
}
180215

181216
$found = \in_array($column->getName(), $primaryKeys, true);
182-
foreach ($table->getIndexes() as $index) {
217+
foreach ($indexes as $index) {
183218
if (\in_array($column->getName(), $index->getColumns(), true)) {
184219
$found = true;
185220
\in_array($index, $result, true) or $result[] = $index;

0 commit comments

Comments
 (0)