From 48875f5ba1a93686208533248fbcba5737c8d288 Mon Sep 17 00:00:00 2001 From: ducng99 Date: Fri, 21 Jun 2024 00:05:21 +1200 Subject: [PATCH 1/6] feat: add `foundRows` config for MySQLi --- app/Config/Database.php | 1 + system/Database/BaseConnection.php | 1 + system/Database/MySQLi/Connection.php | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/app/Config/Database.php b/app/Config/Database.php index 7a1fd21e8d10..1b4c66e52e65 100644 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -43,6 +43,7 @@ class Database extends Config 'failover' => [], 'port' => 3306, 'numberNative' => false, + 'foundRows' => false, 'dateFormat' => [ 'date' => 'Y-m-d', 'datetime' => 'Y-m-d H:i:s', diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index 57c37d8a0a6c..bf18abe12f42 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -35,6 +35,7 @@ * @property-read string $DSN * @property-read array|bool $encrypt * @property-read array $failover + * @property-read bool $foundRows * @property-read string $hostname * @property-read Query $lastQuery * @property-read string $password diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index e8db25c76640..c04d9e364432 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -81,6 +81,16 @@ class Connection extends BaseConnection */ public $numberNative = false; + /** + * Use MYSQLI_CLIENT_FOUND_ROWS + * + * Whether affected_rows should return number of rows found, + * or number of rows changed, after an UPDATE query. + * + * @var bool + */ + public $foundRows = false; + /** * Connect to the database. * @@ -182,6 +192,10 @@ public function connect(bool $persistent = false) $clientFlags += MYSQLI_CLIENT_SSL; } + if ($this->foundRows) { + $clientFlags += MYSQLI_CLIENT_FOUND_ROWS; + } + try { if ($this->mysqli->real_connect( $hostname, From 542df34d0d611ae32aa32b53867e3b3bb8c4e623 Mon Sep 17 00:00:00 2001 From: ducng99 Date: Fri, 21 Jun 2024 01:12:23 +1200 Subject: [PATCH 2/6] test: add test for foundRows db config --- .../Database/Live/MySQLi/FoundRowsTest.php | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 tests/system/Database/Live/MySQLi/FoundRowsTest.php diff --git a/tests/system/Database/Live/MySQLi/FoundRowsTest.php b/tests/system/Database/Live/MySQLi/FoundRowsTest.php new file mode 100644 index 000000000000..011830088d44 --- /dev/null +++ b/tests/system/Database/Live/MySQLi/FoundRowsTest.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\Live\MySQLi; + +use CodeIgniter\Test\CIUnitTestCase; +use CodeIgniter\Test\DatabaseTestTrait; +use Config\Database; +use PHPUnit\Framework\Attributes\Group; +use Tests\Support\Database\Seeds\CITestSeeder; + +/** + * @internal + */ +#[Group('DatabaseLive')] +final class FoundRowsTest extends CIUnitTestCase +{ + use DatabaseTestTrait; + + /** + * @var array + */ + private $tests; + + protected $refresh = true; + protected $seed = CITestSeeder::class; + + protected function setUp(): void + { + parent::setUp(); + + $config = config('Database'); + + $this->tests = $config->tests; + } + + public function testEnableFoundRows(): void + { + $this->tests['foundRows'] = true; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $this->assertTrue($db1->foundRows); + } + + public function testDisableFoundRows(): void + { + $this->tests['foundRows'] = false; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $this->assertFalse($db1->foundRows); + } + + public function testAffectedRowsAfterEnableFoundRowsWithNoChange(): void + { + $this->tests['foundRows'] = true; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $db1->table('db_user') + ->set('country', 'US') + ->where('country', 'US') + ->update(); + + $affectedRows = $db1->affectedRows(); + + $this->assertSame($affectedRows, 2); + } + + public function testAffectedRowsAfterDisableFoundRowsWithNoChange(): void + { + $this->tests['foundRows'] = false; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $db1->table('db_user') + ->set('country', 'US') + ->where('country', 'US') + ->update(); + + $affectedRows = $db1->affectedRows(); + + $this->assertSame($affectedRows, 0); + } + + public function testAffectedRowsAfterEnableFoundRowsWithChange(): void + { + $this->tests['foundRows'] = true; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $db1->table('db_user') + ->set('country', 'NZ') + ->where('country', 'US') + ->update(); + + $affectedRows = $db1->affectedRows(); + + $this->assertSame($affectedRows, 2); + } + + public function testAffectedRowsAfterDisableFoundRowsWithChange(): void + { + $this->tests['foundRows'] = false; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $db1->table('db_user') + ->set('country', 'NZ') + ->where('country', 'US') + ->update(); + + $affectedRows = $db1->affectedRows(); + + $this->assertSame($affectedRows, 2); + } + + public function testAffectedRowsAfterEnableFoundRowsWithPartialChange(): void + { + $this->tests['foundRows'] = true; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $db1->table('db_user') + ->set('name', 'Derek Jones') + ->where('country', 'US') + ->update(); + + $affectedRows = $db1->affectedRows(); + + $this->assertSame($affectedRows, 2); + } + + public function testAffectedRowsAfterDisableFoundRowsWithPartialChange(): void + { + $this->tests['foundRows'] = false; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $db1->table('db_user') + ->set('name', 'Derek Jones') + ->where('country', 'US') + ->update(); + + $affectedRows = $db1->affectedRows(); + + $this->assertSame($affectedRows, 1); + } +} From 698f6432cc4ca851aed01935480e477c6aefcd1a Mon Sep 17 00:00:00 2001 From: ducng99 Date: Fri, 21 Jun 2024 01:12:34 +1200 Subject: [PATCH 3/6] docs: updated changelog and new config field for foundRows --- user_guide_src/source/changelogs/v4.6.0.rst | 2 ++ user_guide_src/source/database/configuration.rst | 1 + 2 files changed, 3 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.6.0.rst b/user_guide_src/source/changelogs/v4.6.0.rst index 5efd08445411..a181a4f95730 100644 --- a/user_guide_src/source/changelogs/v4.6.0.rst +++ b/user_guide_src/source/changelogs/v4.6.0.rst @@ -121,6 +121,8 @@ Forge Others ------ +- Added a new configuration `foundRows` for MySQLi to use `MYSQLI_CLIENT_FOUND_ROWS`. + Model ===== diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst index 86155dea0803..b42317469038 100644 --- a/user_guide_src/source/database/configuration.rst +++ b/user_guide_src/source/database/configuration.rst @@ -179,6 +179,7 @@ Description of Values To enforce Foreign Key constraint, set this config item to true. **busyTimeout** (``SQLite3`` only) milliseconds (int) - Sleeps for a specified amount of time when a table is locked. **numberNative** (``MySQLi`` only) true/false (boolean) - Whether to enable MYSQLI_OPT_INT_AND_FLOAT_NATIVE. +**foundRows** (``MySQLi`` only) true/false (boolean) - Whether to enable MYSQLI_CLIENT_FOUND_ROWS. **dateFormat** The default date/time formats as PHP's `DateTime format`_. * ``date`` - date format * ``datetime`` - date and time format From 56775d06606f8450f06750d993134bde00fb5eef Mon Sep 17 00:00:00 2001 From: ducng99 Date: Fri, 21 Jun 2024 21:13:44 +1200 Subject: [PATCH 4/6] docs: updated to suggested changes Also add an ignore in phpstan to ignore missing foundRows in BaseConnection --- phpstan-baseline.php | 6 ++++++ system/Database/BaseConnection.php | 1 - system/Database/MySQLi/Connection.php | 2 +- user_guide_src/source/changelogs/v4.6.0.rst | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 05825b1d76ed..9e1f1433acfc 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -13459,6 +13459,12 @@ 'count' => 2, 'path' => __DIR__ . '/tests/system/Database/Live/MySQLi/NumberNativeTest.php', ]; +$ignoreErrors[] = [ + // identifier: property.notFound + 'message' => '#^Access to an undefined property CodeIgniter\\\\Database\\\\BaseConnection\\:\\:\\$foundRows\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/tests/system/Database/Live/MySQLi/FoundRowsTest.php', +]; $ignoreErrors[] = [ // identifier: missingType.property 'message' => '#^Property CodeIgniter\\\\Database\\\\Live\\\\MySQLi\\\\NumberNativeTest\\:\\:\\$tests has no type specified\\.$#', diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index bf18abe12f42..57c37d8a0a6c 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -35,7 +35,6 @@ * @property-read string $DSN * @property-read array|bool $encrypt * @property-read array $failover - * @property-read bool $foundRows * @property-read string $hostname * @property-read Query $lastQuery * @property-read string $password diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index c04d9e364432..3868905dca27 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -84,7 +84,7 @@ class Connection extends BaseConnection /** * Use MYSQLI_CLIENT_FOUND_ROWS * - * Whether affected_rows should return number of rows found, + * Whether affectedRows() should return number of rows found, * or number of rows changed, after an UPDATE query. * * @var bool diff --git a/user_guide_src/source/changelogs/v4.6.0.rst b/user_guide_src/source/changelogs/v4.6.0.rst index a181a4f95730..09bc4d413fed 100644 --- a/user_guide_src/source/changelogs/v4.6.0.rst +++ b/user_guide_src/source/changelogs/v4.6.0.rst @@ -121,7 +121,7 @@ Forge Others ------ -- Added a new configuration `foundRows` for MySQLi to use `MYSQLI_CLIENT_FOUND_ROWS`. +- Added a new configuration ``foundRows`` for MySQLi to use ``MYSQLI_CLIENT_FOUND_ROWS``. Model ===== From 16fe250b7b893a7d0e24bca28665bc6ac26cc41c Mon Sep 17 00:00:00 2001 From: ducng99 Date: Fri, 21 Jun 2024 21:28:21 +1200 Subject: [PATCH 5/6] test: moved DBDriver check to setUp() instead of checking in each test --- .../Database/Live/MySQLi/FoundRowsTest.php | 36 +++---------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/tests/system/Database/Live/MySQLi/FoundRowsTest.php b/tests/system/Database/Live/MySQLi/FoundRowsTest.php index 011830088d44..4e5299f8d7aa 100644 --- a/tests/system/Database/Live/MySQLi/FoundRowsTest.php +++ b/tests/system/Database/Live/MySQLi/FoundRowsTest.php @@ -42,6 +42,10 @@ protected function setUp(): void $config = config('Database'); $this->tests = $config->tests; + + if ($this->tests['DBDriver'] !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } } public function testEnableFoundRows(): void @@ -50,10 +54,6 @@ public function testEnableFoundRows(): void $db1 = Database::connect($this->tests); - if ($db1->DBDriver !== 'MySQLi') { - $this->markTestSkipped('Only MySQLi can complete this test.'); - } - $this->assertTrue($db1->foundRows); } @@ -63,10 +63,6 @@ public function testDisableFoundRows(): void $db1 = Database::connect($this->tests); - if ($db1->DBDriver !== 'MySQLi') { - $this->markTestSkipped('Only MySQLi can complete this test.'); - } - $this->assertFalse($db1->foundRows); } @@ -76,10 +72,6 @@ public function testAffectedRowsAfterEnableFoundRowsWithNoChange(): void $db1 = Database::connect($this->tests); - if ($db1->DBDriver !== 'MySQLi') { - $this->markTestSkipped('Only MySQLi can complete this test.'); - } - $db1->table('db_user') ->set('country', 'US') ->where('country', 'US') @@ -96,10 +88,6 @@ public function testAffectedRowsAfterDisableFoundRowsWithNoChange(): void $db1 = Database::connect($this->tests); - if ($db1->DBDriver !== 'MySQLi') { - $this->markTestSkipped('Only MySQLi can complete this test.'); - } - $db1->table('db_user') ->set('country', 'US') ->where('country', 'US') @@ -116,10 +104,6 @@ public function testAffectedRowsAfterEnableFoundRowsWithChange(): void $db1 = Database::connect($this->tests); - if ($db1->DBDriver !== 'MySQLi') { - $this->markTestSkipped('Only MySQLi can complete this test.'); - } - $db1->table('db_user') ->set('country', 'NZ') ->where('country', 'US') @@ -136,10 +120,6 @@ public function testAffectedRowsAfterDisableFoundRowsWithChange(): void $db1 = Database::connect($this->tests); - if ($db1->DBDriver !== 'MySQLi') { - $this->markTestSkipped('Only MySQLi can complete this test.'); - } - $db1->table('db_user') ->set('country', 'NZ') ->where('country', 'US') @@ -156,10 +136,6 @@ public function testAffectedRowsAfterEnableFoundRowsWithPartialChange(): void $db1 = Database::connect($this->tests); - if ($db1->DBDriver !== 'MySQLi') { - $this->markTestSkipped('Only MySQLi can complete this test.'); - } - $db1->table('db_user') ->set('name', 'Derek Jones') ->where('country', 'US') @@ -176,10 +152,6 @@ public function testAffectedRowsAfterDisableFoundRowsWithPartialChange(): void $db1 = Database::connect($this->tests); - if ($db1->DBDriver !== 'MySQLi') { - $this->markTestSkipped('Only MySQLi can complete this test.'); - } - $db1->table('db_user') ->set('name', 'Derek Jones') ->where('country', 'US') From 9efac84b51e9668962b346d2fd5848118e33fe17 Mon Sep 17 00:00:00 2001 From: ducng99 Date: Tue, 25 Jun 2024 09:39:45 +1200 Subject: [PATCH 6/6] test: Skip running tests if DB is not MySQLi --- tests/system/Database/Live/MySQLi/FoundRowsTest.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/system/Database/Live/MySQLi/FoundRowsTest.php b/tests/system/Database/Live/MySQLi/FoundRowsTest.php index 4e5299f8d7aa..d5b88b49af28 100644 --- a/tests/system/Database/Live/MySQLi/FoundRowsTest.php +++ b/tests/system/Database/Live/MySQLi/FoundRowsTest.php @@ -28,7 +28,9 @@ final class FoundRowsTest extends CIUnitTestCase use DatabaseTestTrait; /** - * @var array + * Database config for tests + * + * @var array */ private $tests; @@ -37,8 +39,6 @@ final class FoundRowsTest extends CIUnitTestCase protected function setUp(): void { - parent::setUp(); - $config = config('Database'); $this->tests = $config->tests; @@ -46,6 +46,8 @@ protected function setUp(): void if ($this->tests['DBDriver'] !== 'MySQLi') { $this->markTestSkipped('Only MySQLi can complete this test.'); } + + parent::setUp(); } public function testEnableFoundRows(): void