Repository: codeigniter4/CodeIgniter4 (PHP)

Issue:
Bug(SQLite3): Forge::dropColumn() seems to always return false
### PHP Version

8.3

### CodeIgniter4 Version

4.5.1

### CodeIgniter4 Installation Method

Composer (as dependency to an existing project)

### Which operating systems have you tested for this bug?

Linux

### Which server did you use?

cli

### Database

SQLite3

### What happened?

I was checking whether dropColumn of forge returned false when migrating down. Which always seemed to be the case.
When checking the implementation I noticed that Forge expects from the driver that `_alterTable` returns a SQL statement to be executed, but in this case SQLite3 will return an empty string, because it runs multiple statements in the background, creating a new temporary table without the specified columns, moving data over and renaming afterwards.


### Steps to Reproduce



```php
<?php

declare(strict_types=1);

namespace App\Database\Migrations;

use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\Migration;

class AddAColumnToExistingTable extends Migration
{
    protected string $table = 'existing_table';

    public function up(): void
    {
        $this->forge->addColumn($this->table, [
            'column'   => ['type' => 'varchar', 'null' => false],
        ]) or throw new DatabaseException("Could not add columns to table {$this->table}: " . var_export($this->db->error(), true));
    }

    public function down(): void
    {
        $this->forge->dropColumn($this->table, 'column')
            or throw new DatabaseException("Could drop column from table {$this->table}: " . var_export($this->db->error(), true));
    }
}
```



### Expected Output

None, but the DatabaseException is thrown in the down() method, telling me that no error occured.
But dropColumn() returned false, because the statement is empty.

### Anything else?

_No response_


Discussion:
I confirmed the bug.

```diff
--- a/tests/system/Database/Live/ForgeTest.php
+++ b/tests/system/Database/Live/ForgeTest.php
@@ -1317,7 +1317,9 @@ final class ForgeTest extends CIUnitTestCase
 
         $this->assertTrue($this->db->fieldExists('name', 'forge_test_two'));
 
-        $this->forge->dropColumn('forge_test_two', 'name');
+        $return = $this->forge->dropColumn('forge_test_two', 'name');
+
+        $this->assertTrue($return);
 
         $this->db->resetDataCache();
 
```

```
1) CodeIgniter\Database\Live\ForgeTest::testDropColumn
Failed asserting that false is true.
```




Patch:
diff --git a/system/Database/Forge.php b/system/Database/Forge.php
index b41b8f34f8c8..974c45848d1c 100644
--- a/system/Database/Forge.php
+++ b/system/Database/Forge.php
@@ -778,7 +778,7 @@ public function addColumn(string $table, $fields): bool
     }
 
     /**
-     * @param array|string $columnNames column names to DROP
+     * @param list<string>|string $columnNames column names to DROP
      *
      * @return bool
      *
diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php
index 48e9abcc425c..03120dd7a0da 100644
--- a/system/Database/SQLite3/Forge.php
+++ b/system/Database/SQLite3/Forge.php
@@ -110,6 +110,31 @@ public function dropDatabase(string $dbName): bool
         return true;
     }
 
+    /**
+     * @param list<string>|string $columnNames
+     *
+     * @throws DatabaseException
+     */
+    public function dropColumn(string $table, $columnNames): bool
+    {
+        $columns = is_array($columnNames) ? $columnNames : array_map(trim(...), explode(',', $columnNames));
+        $result  = (new Table($this->db, $this))
+            ->fromTable($this->db->DBPrefix . $table)
+            ->dropColumn($columns)
+            ->run();
+
+        if (! $result && $this->db->DBDebug) {
+            throw new DatabaseException(sprintf(
+                'Failed to drop column%s "%s" on "%s" table.',
+                count($columns) > 1 ? 's' : '',
+                implode('", "', $columns),
+                $table,
+            ));
+        }
+
+        return $result;
+    }
+
     /**
      * @param array|string $processedFields Processed column definitions
      *                                      or column names to DROP
@@ -121,17 +146,6 @@ public function dropDatabase(string $dbName): bool
     protected function _alterTable(string $alterType, string $table, $processedFields)
     {
         switch ($alterType) {
-            case 'DROP':
-                $columnNamesToDrop = $processedFields;
-
-                $sqlTable = new Table($this->db, $this);
-
-                $sqlTable->fromTable($table)
-                    ->dropColumn($columnNamesToDrop)
-                    ->run();
-
-                return ''; // Why empty string?
-
             case 'CHANGE':
                 $fieldsToModify = [];
 
diff --git a/user_guide_src/source/changelogs/v4.5.7.rst b/user_guide_src/source/changelogs/v4.5.7.rst
index b196c5f5fffe..fc36acf3b314 100644
--- a/user_guide_src/source/changelogs/v4.5.7.rst
+++ b/user_guide_src/source/changelogs/v4.5.7.rst
@@ -31,6 +31,7 @@ Bugs Fixed
 **********
 
 - **Common:** Fixed a bug where the ``helper()`` method may throw `FileNotFoundException` on valid namespaced helper.
+- **Forge:** Fixed an issue where `SQLite3`'s Forge always returns `false` when calling ``dropColumn()``.
 
 See the repo's
 `CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_
diff --git a/utils/phpstan-baseline/missingType.iterableValue.neon b/utils/phpstan-baseline/missingType.iterableValue.neon
index 812228800d8f..6abf970718c7 100644
--- a/utils/phpstan-baseline/missingType.iterableValue.neon
+++ b/utils/phpstan-baseline/missingType.iterableValue.neon
@@ -1,4 +1,4 @@
-# total 1686 errors
+# total 1685 errors
 
 parameters:
     ignoreErrors:
@@ -1697,11 +1697,6 @@ parameters:
             count: 1
             path: ../../system/Database/Forge.php
 
-        -
-            message: '#^Method CodeIgniter\\Database\\Forge\:\:dropColumn\(\) has parameter \$columnNames with no value type specified in iterable type array\.$#'
-            count: 1
-            path: ../../system/Database/Forge.php
-
         -
             message: '#^Method CodeIgniter\\Database\\Forge\:\:modifyColumn\(\) has parameter \$fields with no value type specified in iterable type array\.$#'
             count: 1


Test patch:
diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php
index 8f76efd12b42..0ab672e72067 100644
--- a/tests/system/Database/Live/ForgeTest.php
+++ b/tests/system/Database/Live/ForgeTest.php
@@ -1319,8 +1319,7 @@ public function testDropColumn(): void
         $this->forge->createTable('forge_test_two');
 
         $this->assertTrue($this->db->fieldExists('name', 'forge_test_two'));
-
-        $this->forge->dropColumn('forge_test_two', 'name');
+        $this->assertTrue($this->forge->dropColumn('forge_test_two', 'name'));
 
         $this->db->resetDataCache();