Skip to content

Commit 735be40

Browse files
committed
test(database): reject MySQLi lockForUpdate with fromSubquery
Signed-off-by: memleakd <121398829+memleakd@users.noreply.github.com>
1 parent 026bcb7 commit 735be40

3 files changed

Lines changed: 38 additions & 0 deletions

File tree

system/Database/MySQLi/Builder.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ protected function _fromTables(): string
5858
return implode(', ', $this->QBFrom);
5959
}
6060

61+
/**
62+
* Compile the SELECT lock clause.
63+
*/
64+
protected function compileLockForUpdate(): string
65+
{
66+
if (! $this->QBLockForUpdate) {
67+
return '';
68+
}
69+
70+
foreach ($this->QBFrom as $value) {
71+
if (str_starts_with($value, '(SELECT')) {
72+
throw new DatabaseException('MySQLi does not support lockForUpdate() with fromSubquery().');
73+
}
74+
}
75+
76+
return parent::compileLockForUpdate();
77+
}
78+
6179
/**
6280
* Generates a platform-specific batch update string from the supplied data
6381
*/

tests/system/Database/Builder/SelectTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use CodeIgniter\Database\BaseBuilder;
1717
use CodeIgniter\Database\Exceptions\DatabaseException;
1818
use CodeIgniter\Database\Exceptions\DataException;
19+
use CodeIgniter\Database\MySQLi\Builder as MySQLiBuilder;
1920
use CodeIgniter\Database\OCI8\Builder as OCI8Builder;
2021
use CodeIgniter\Database\Postgre\Builder as PostgreBuilder;
2122
use CodeIgniter\Database\RawSql;
@@ -440,6 +441,21 @@ public function testLockForUpdateThrowsExceptionWithSQLSRVUnion(): void
440441
$builder->union(new SQLSRVBuilder('jobs', $this->db))->lockForUpdate()->getCompiledSelect();
441442
}
442443

444+
public function testLockForUpdateThrowsExceptionOnMySQLiSubquery(): void
445+
{
446+
$this->db = new MockConnection(['DBDriver' => 'MySQLi']);
447+
448+
$subquery = new MySQLiBuilder('users', $this->db);
449+
$builder = new MySQLiBuilder('jobs', $this->db);
450+
451+
$builder->fromSubquery($subquery, 'users_1');
452+
453+
$this->expectException(DatabaseException::class);
454+
$this->expectExceptionMessage('MySQLi does not support lockForUpdate() with fromSubquery().');
455+
456+
$builder->lockForUpdate()->getCompiledSelect();
457+
}
458+
443459
public function testLockForUpdateWithOCI8(): void
444460
{
445461
$builder = new OCI8Builder('users', $this->db);

user_guide_src/source/database/query_builder.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,10 @@ Some databases restrict which query shapes can be used with row locking. When
783783
CodeIgniter can detect an unsupported combination, it throws a
784784
``DatabaseException``. See the following warnings for driver-specific behavior.
785785

786+
.. warning:: MySQLi does not support ``lockForUpdate()`` with ``fromSubquery()``
787+
because an outer locking read on a derived table does not lock the underlying
788+
rows as users may expect.
789+
786790
.. warning:: Postgre does not support ``lockForUpdate()`` with ``distinct()``,
787791
``groupBy()``, ``having()``, or aggregate helper selections such as
788792
``selectCount()``.

0 commit comments

Comments
 (0)