diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index ca64800463a2..51ed022d131d 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -2198,7 +2198,7 @@ protected function formatValues(array $values): array * * @param array|object|null $set a dataset * - * @return false|int|list Number of rows inserted or FALSE on failure, SQL array when testMode + * @return false|int|list Number of rows inserted or FALSE on no data to perform an insert operation, SQL array when testMode */ public function insertBatch($set = null, ?bool $escape = null, int $batchSize = 100) { diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index ad278145afef..64b85eafb064 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -227,6 +227,10 @@ protected function getDriverFunctionPrefix(): string */ public function affectedRows(): int { + if ($this->resultID === false) { + return 0; + } + return pg_affected_rows($this->resultID); } diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index 6903f42eebac..26b4983dba0a 100644 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -444,6 +444,10 @@ public function error(): array */ public function affectedRows(): int { + if ($this->resultID === false) { + return 0; + } + return sqlsrv_rows_affected($this->resultID); } diff --git a/tests/system/Database/Live/InsertTest.php b/tests/system/Database/Live/InsertTest.php index ca2117a06b35..79281a384e9a 100644 --- a/tests/system/Database/Live/InsertTest.php +++ b/tests/system/Database/Live/InsertTest.php @@ -13,6 +13,7 @@ namespace CodeIgniter\Database\Live; +use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Forge; use CodeIgniter\Database\RawSql; use CodeIgniter\Test\CIUnitTestCase; @@ -79,13 +80,41 @@ public function testInsertBatch(): void ], ]; - $this->db->table($table)->insertBatch($data); + $count = $this->db->table($table)->insertBatch($data); + + $this->assertSame(2, $count); $expected = $data; $this->seeInDatabase($table, $expected[0]); $this->seeInDatabase($table, $expected[1]); } + public function testInsertBatchFailed(): void + { + $this->expectException(DatabaseException::class); + + $data = [ + [ + 'name' => 'Grocery Sales', + ], + [ + 'name' => null, + ], + ]; + + $db = $this->db; + + if ($this->db->DBDriver === 'MySQLi') { + // strict mode is required for MySQLi to throw an exception here + $config = config('Database'); + $config->tests['strictOn'] = true; + + $db = Database::connect($config->tests); + } + + $db->table('job')->insertBatch($data); + } + public function testReplaceWithNoMatchingData(): void { $data = [ diff --git a/tests/system/Database/Live/TransactionTest.php b/tests/system/Database/Live/TransactionTest.php index 3414d2dfffdf..74df4e3fd974 100644 --- a/tests/system/Database/Live/TransactionTest.php +++ b/tests/system/Database/Live/TransactionTest.php @@ -239,4 +239,40 @@ public function testTransStrictFalseAndDBDebugFalse(): void $this->enableDBDebug(); } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/9362 + */ + public function testTransInsertBatchFailed(): void + { + $data = [ + [ + 'name' => 'Grocery Sales', + ], + [ + 'name' => null, + ], + ]; + + $db = $this->db; + + if ($this->db->DBDriver === 'MySQLi') { + // strict mode is required for MySQLi to throw an exception here + $config = config('Database'); + $config->tests['strictOn'] = true; + + $db = Database::connect($config->tests); + } + + $db->transStrict(false)->transBegin(); + $db->table('job')->insertBatch($data); + + $this->assertFalse($db->transStatus()); + + $db->transComplete(); + + $db->transStrict(); + + $this->dontSeeInDatabase('job', ['name' => 'Grocery Sales']); + } } diff --git a/user_guide_src/source/changelogs/v4.5.8.rst b/user_guide_src/source/changelogs/v4.5.8.rst index cad7fec30248..ae29b59fb165 100644 --- a/user_guide_src/source/changelogs/v4.5.8.rst +++ b/user_guide_src/source/changelogs/v4.5.8.rst @@ -30,6 +30,8 @@ Deprecations Bugs Fixed ********** +- **Database:** Fixed a bug where ``Builder::affectedRows()`` threw an error when the previous query call failed in ``Postgre`` and ``SQLSRV`` drivers. + See the repo's `CHANGELOG.md `_ for a complete list of bugs fixed. diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index 454fc56c6bbf..6e4b1359c898 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -1870,7 +1870,7 @@ Class Reference :param array $set: Data to insert :param bool $escape: Whether to escape values :param int $batch_size: Count of rows to insert at once - :returns: Number of rows inserted or ``false`` on failure + :returns: Number of rows inserted or ``false`` on no data to perform an insert operation :rtype: int|false Compiles and executes batch ``INSERT`` statements.