From 225d63146361eab3350a81eaeedc3ba63fd7bceb Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 10 Nov 2025 17:27:54 +0100 Subject: [PATCH 1/3] Fix SQLiteAdapter when changing primary key --- src/Db/Adapter/SqliteAdapter.php | 11 ++++- .../TestCase/Db/Adapter/SqliteAdapterTest.php | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/Db/Adapter/SqliteAdapter.php b/src/Db/Adapter/SqliteAdapter.php index 91847376..202dab39 100644 --- a/src/Db/Adapter/SqliteAdapter.php +++ b/src/Db/Adapter/SqliteAdapter.php @@ -1343,14 +1343,21 @@ protected function getAddPrimaryKeyInstructions(TableMetadata $table, string $co $instructions->addPostStep(function ($state) use ($column) { $quotedColumn = preg_quote($column); $columnPattern = "`{$quotedColumn}`|\"{$quotedColumn}\"|\[{$quotedColumn}\]"; - $matchPattern = "/($columnPattern)\s+(\w+(\(\d+\))?)(\s+(NOT )?NULL)?/"; + // Extended pattern to capture AUTOINCREMENT if present + $matchPattern = "/($columnPattern)\s+(\w+(\(\d+\))?)(\s+(NOT )?NULL)?(\s+(?:PRIMARY KEY\s+)?AUTOINCREMENT)?/i"; $sql = $state['createSQL']; if (preg_match($matchPattern, $state['createSQL'], $matches)) { if (isset($matches[2])) { - if ($matches[2] === 'INTEGER') { + $hasAutoIncrement = isset($matches[6]) && stripos($matches[6], 'AUTOINCREMENT') !== false; + + if ($matches[2] === 'INTEGER' && $hasAutoIncrement) { + // Only add AUTOINCREMENT if the column already had it $replace = '$1 INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT'; + } elseif ($matches[2] === 'INTEGER') { + // INTEGER column without AUTOINCREMENT should stay that way + $replace = '$1 INTEGER NOT NULL PRIMARY KEY'; } else { $replace = '$1 $2 NOT NULL PRIMARY KEY'; } diff --git a/tests/TestCase/Db/Adapter/SqliteAdapterTest.php b/tests/TestCase/Db/Adapter/SqliteAdapterTest.php index 4c92a8b4..ef42a23c 100644 --- a/tests/TestCase/Db/Adapter/SqliteAdapterTest.php +++ b/tests/TestCase/Db/Adapter/SqliteAdapterTest.php @@ -428,6 +428,51 @@ public function testChangePrimaryKeyNonInteger() $this->assertTrue($this->adapter->hasPrimaryKey('table1', ['column2'])); } + public function testChangePrimaryKeyWithoutAutoIncrement() + { + // Create table with id_1 as PK without AUTOINCREMENT keyword + $this->adapter->execute('CREATE TABLE table1 (id_1 INTEGER NOT NULL PRIMARY KEY, id_2 INTEGER NOT NULL)'); + + // Verify initial SQL does not have AUTOINCREMENT + $result = $this->adapter->fetchRow("SELECT sql FROM sqlite_master WHERE type='table' AND name='table1'"); + $this->assertStringNotContainsString('AUTOINCREMENT', $result['sql']); + + // Change primary key to id_2 + $table = new Table('table1', [], $this->adapter); + $table->changePrimaryKey('id_2')->save(); + + // Verify primary key changed + $this->assertFalse($this->adapter->hasPrimaryKey('table1', ['id_1'])); + $this->assertTrue($this->adapter->hasPrimaryKey('table1', ['id_2'])); + + // Verify the SQL does NOT have AUTOINCREMENT added to id_2 + $result = $this->adapter->fetchRow("SELECT sql FROM sqlite_master WHERE type='table' AND name='table1'"); + $this->assertStringNotContainsString('AUTOINCREMENT', $result['sql'], 'AUTOINCREMENT should not be added when changing PK to a column that did not have it'); + } + + public function testChangePrimaryKeyFromAutoIncrementColumn() + { + // Create table with id_1 as PK with AUTOINCREMENT + $this->adapter->execute('CREATE TABLE table1 (id_1 INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, id_2 INTEGER NOT NULL)'); + + // Verify initial SQL has AUTOINCREMENT + $result = $this->adapter->fetchRow("SELECT sql FROM sqlite_master WHERE type='table' AND name='table1'"); + $this->assertStringContainsString('AUTOINCREMENT', $result['sql']); + + // Change primary key to id_2 (should NOT get AUTOINCREMENT since id_2 doesn't have it) + $table = new Table('table1', [], $this->adapter); + $table->changePrimaryKey('id_2')->save(); + + // Verify primary key changed + $this->assertFalse($this->adapter->hasPrimaryKey('table1', ['id_1'])); + $this->assertTrue($this->adapter->hasPrimaryKey('table1', ['id_2'])); + + // Verify the SQL does NOT have AUTOINCREMENT on id_2 + // (id_1 lost its AUTOINCREMENT when PK was dropped, and id_2 never had it) + $result = $this->adapter->fetchRow("SELECT sql FROM sqlite_master WHERE type='table' AND name='table1'"); + $this->assertStringNotContainsString('AUTOINCREMENT', $result['sql'], 'AUTOINCREMENT should not be added when changing PK to a column that never had it'); + } + public function testDropPrimaryKey() { $table = new Table('table1', ['id' => false, 'primary_key' => 'column1'], $this->adapter); From 34cd4c4accc94638b1ffb44ceae463e804480eb9 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Tue, 11 Nov 2025 17:51:56 +0100 Subject: [PATCH 2/3] Update src/Db/Adapter/SqliteAdapter.php Co-authored-by: Mark Story --- src/Db/Adapter/SqliteAdapter.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Db/Adapter/SqliteAdapter.php b/src/Db/Adapter/SqliteAdapter.php index 202dab39..5d9cd773 100644 --- a/src/Db/Adapter/SqliteAdapter.php +++ b/src/Db/Adapter/SqliteAdapter.php @@ -1343,7 +1343,6 @@ protected function getAddPrimaryKeyInstructions(TableMetadata $table, string $co $instructions->addPostStep(function ($state) use ($column) { $quotedColumn = preg_quote($column); $columnPattern = "`{$quotedColumn}`|\"{$quotedColumn}\"|\[{$quotedColumn}\]"; - // Extended pattern to capture AUTOINCREMENT if present $matchPattern = "/($columnPattern)\s+(\w+(\(\d+\))?)(\s+(NOT )?NULL)?(\s+(?:PRIMARY KEY\s+)?AUTOINCREMENT)?/i"; $sql = $state['createSQL']; From 4337512e3cc4605f07a1283c1b71fe6d0245b19b Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 12 Nov 2025 23:09:27 -0500 Subject: [PATCH 3/3] Simplify --- src/Db/Adapter/SqliteAdapter.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Db/Adapter/SqliteAdapter.php b/src/Db/Adapter/SqliteAdapter.php index 5d9cd773..93729cf1 100644 --- a/src/Db/Adapter/SqliteAdapter.php +++ b/src/Db/Adapter/SqliteAdapter.php @@ -1354,9 +1354,6 @@ protected function getAddPrimaryKeyInstructions(TableMetadata $table, string $co if ($matches[2] === 'INTEGER' && $hasAutoIncrement) { // Only add AUTOINCREMENT if the column already had it $replace = '$1 INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT'; - } elseif ($matches[2] === 'INTEGER') { - // INTEGER column without AUTOINCREMENT should stay that way - $replace = '$1 INTEGER NOT NULL PRIMARY KEY'; } else { $replace = '$1 $2 NOT NULL PRIMARY KEY'; }