diff --git a/src/Db/Adapter/MysqlAdapter.php b/src/Db/Adapter/MysqlAdapter.php index c01bce6f..35c9d8f3 100644 --- a/src/Db/Adapter/MysqlAdapter.php +++ b/src/Db/Adapter/MysqlAdapter.php @@ -203,7 +203,10 @@ public function quoteTableName(string $tableName): string */ public function hasTable(string $tableName): bool { - if ($this->hasCreatedTable($tableName)) { + // Only use the cache in dry-run mode where tables aren't actually created. + // In normal mode, always check the database to handle cases where tables + // are dropped via execute() which doesn't update the cache. + if ($this->isDryRunEnabled() && $this->hasCreatedTable($tableName)) { return true; } diff --git a/src/Db/Adapter/PostgresAdapter.php b/src/Db/Adapter/PostgresAdapter.php index 6ec16ce0..e229a180 100644 --- a/src/Db/Adapter/PostgresAdapter.php +++ b/src/Db/Adapter/PostgresAdapter.php @@ -100,9 +100,13 @@ public function quoteTableName(string $tableName): string */ public function hasTable(string $tableName): bool { - if ($this->hasCreatedTable($tableName)) { + // Only use the cache in dry-run mode where tables aren't actually created. + // In normal mode, always check the database to handle cases where tables + // are dropped via execute() which doesn't update the cache. + if ($this->isDryRunEnabled() && $this->hasCreatedTable($tableName)) { return true; } + $parts = $this->getSchemaName($tableName); $tableName = $parts['table']; diff --git a/src/Db/Adapter/SqliteAdapter.php b/src/Db/Adapter/SqliteAdapter.php index 4c163755..7f5bd972 100644 --- a/src/Db/Adapter/SqliteAdapter.php +++ b/src/Db/Adapter/SqliteAdapter.php @@ -229,7 +229,14 @@ protected function resolveTable(string $tableName): array */ public function hasTable(string $tableName): bool { - return $this->hasCreatedTable($tableName) || $this->resolveTable($tableName)['exists']; + // Only use the cache in dry-run mode where tables aren't actually created. + // In normal mode, always check the database to handle cases where tables + // are dropped via execute() which doesn't update the cache. + if ($this->isDryRunEnabled() && $this->hasCreatedTable($tableName)) { + return true; + } + + return $this->resolveTable($tableName)['exists']; } /** diff --git a/src/Db/Adapter/SqlserverAdapter.php b/src/Db/Adapter/SqlserverAdapter.php index ec3c0fc0..e970da2d 100644 --- a/src/Db/Adapter/SqlserverAdapter.php +++ b/src/Db/Adapter/SqlserverAdapter.php @@ -67,13 +67,17 @@ public function quoteTableName(string $tableName): string */ public function hasTable(string $tableName): bool { - if ($this->hasCreatedTable($tableName)) { + // Only use the cache in dry-run mode where tables aren't actually created. + // In normal mode, always check the database to handle cases where tables + // are dropped via execute() which doesn't update the cache. + if ($this->isDryRunEnabled() && $this->hasCreatedTable($tableName)) { return true; } + $parts = $this->getSchemaName($tableName); $dialect = $this->getSchemaDialect(); - return $dialect->hasTable($tableName, $parts['schema']); + return $dialect->hasTable($parts['table'], $parts['schema']); } /** diff --git a/tests/TestCase/Db/Adapter/SqliteAdapterTest.php b/tests/TestCase/Db/Adapter/SqliteAdapterTest.php index 5c76b523..ee9ef10e 100644 --- a/tests/TestCase/Db/Adapter/SqliteAdapterTest.php +++ b/tests/TestCase/Db/Adapter/SqliteAdapterTest.php @@ -2402,6 +2402,27 @@ public static function provideTableNamesForPresenceCheck() ]; } + /** + * Test that hasTable() returns false after a table is dropped via execute(). + * + * This verifies that hasTable() always checks the database rather than + * relying on an internal cache that could become stale when raw SQL is used. + */ + public function testHasTableAfterExecuteDrop(): void + { + // Create table via API + $table = new Table('cache_test', [], $this->adapter); + $table->addColumn('name', 'string') + ->save(); + + $this->assertTrue($this->adapter->hasTable('cache_test')); + + // Drop via execute() - hasTable() must still return false + $this->adapter->execute('DROP TABLE "cache_test"'); + + $this->assertFalse($this->adapter->hasTable('cache_test')); + } + #[DataProvider('provideIndexColumnsToCheck')] public function testHasIndex($tableDef, $cols, $exp) {