@@ -110,6 +110,16 @@ class BaseBuilder
110110 */
111111 protected $ QBOffset = false ;
112112
113+ /**
114+ * QB FOR UPDATE flag
115+ */
116+ protected bool $ QBLockForUpdate = false ;
117+
118+ /**
119+ * QB SELECT aggregate helper flag
120+ */
121+ protected bool $ QBSelectUsesAggregate = false ;
122+
113123 /**
114124 * QB ORDER BY data
115125 *
@@ -542,8 +552,9 @@ protected function maxMinAvgSum(string $select = '', string $alias = '', string
542552
543553 $ sql = $ type . '( ' . $ this ->db ->protectIdentifiers (trim ($ select )) . ') AS ' . $ this ->db ->escapeIdentifiers (trim ($ alias ));
544554
545- $ this ->QBSelect [] = $ sql ;
546- $ this ->QBNoEscape [] = null ;
555+ $ this ->QBSelect [] = $ sql ;
556+ $ this ->QBNoEscape [] = null ;
557+ $ this ->QBSelectUsesAggregate = true ;
547558
548559 return $ this ;
549560 }
@@ -1620,6 +1631,16 @@ public function limit(?int $value = null, ?int $offset = 0)
16201631 return $ this ;
16211632 }
16221633
1634+ /**
1635+ * Locks the selected rows for update.
1636+ */
1637+ public function lockForUpdate (): static
1638+ {
1639+ $ this ->QBLockForUpdate = true ;
1640+
1641+ return $ this ;
1642+ }
1643+
16231644 /**
16241645 * Sets the OFFSET value
16251646 *
@@ -1801,20 +1822,26 @@ public function countAllResults(bool $reset = true)
18011822 }
18021823
18031824 // We cannot use a LIMIT when getting the single row COUNT(*) result
1804- $ limit = $ this ->QBLimit ;
1825+ $ limit = $ this ->QBLimit ;
1826+ $ lockForUpdate = $ this ->QBLockForUpdate ;
18051827
1806- $ this ->QBLimit = false ;
1828+ $ this ->QBLimit = false ;
1829+ $ this ->QBLockForUpdate = false ;
18071830
1808- if ($ this ->QBDistinct === true || ! empty ($ this ->QBGroupBy )) {
1809- // We need to backup the original SELECT in case DBPrefix is used
1810- $ select = $ this ->QBSelect ;
1811- $ sql = $ this ->countString . $ this ->db ->protectIdentifiers ('numrows ' ) . "\nFROM ( \n" . $ this ->compileSelect () . "\n) CI_count_all_results " ;
1831+ try {
1832+ if ($ this ->QBDistinct === true || ! empty ($ this ->QBGroupBy )) {
1833+ // We need to backup the original SELECT in case DBPrefix is used
1834+ $ select = $ this ->QBSelect ;
1835+ $ sql = $ this ->countString . $ this ->db ->protectIdentifiers ('numrows ' ) . "\nFROM ( \n" . $ this ->compileSelect () . "\n) CI_count_all_results " ;
18121836
1813- // Restore SELECT part
1814- $ this ->QBSelect = $ select ;
1815- unset($ select );
1816- } else {
1817- $ sql = $ this ->compileSelect ($ this ->countString . $ this ->db ->protectIdentifiers ('numrows ' ));
1837+ // Restore SELECT part
1838+ $ this ->QBSelect = $ select ;
1839+ unset($ select );
1840+ } else {
1841+ $ sql = $ this ->compileSelect ($ this ->countString . $ this ->db ->protectIdentifiers ('numrows ' ));
1842+ }
1843+ } finally {
1844+ $ this ->QBLockForUpdate = $ lockForUpdate ;
18181845 }
18191846
18201847 if ($ this ->testMode ) {
@@ -3223,9 +3250,23 @@ protected function compileSelect($selectOverride = false): string
32233250 $ sql = $ this ->_limit ($ sql . "\n" );
32243251 }
32253252
3253+ $ sql .= $ this ->compileLockForUpdate ();
3254+
32263255 return $ this ->unionInjection ($ sql );
32273256 }
32283257
3258+ /**
3259+ * Compile the SELECT lock clause.
3260+ */
3261+ protected function compileLockForUpdate (): string
3262+ {
3263+ if ($ this ->QBLockForUpdate && $ this ->QBUnion !== []) {
3264+ throw new DatabaseException ('Query Builder does not support lockForUpdate() with union() or unionAll(). ' );
3265+ }
3266+
3267+ return $ this ->QBLockForUpdate ? "\nFOR UPDATE " : '' ;
3268+ }
3269+
32293270 /**
32303271 * Checks if the ignore option is supported by
32313272 * the Database Driver for the specific statement.
@@ -3533,17 +3574,19 @@ protected function resetRun(array $qbResetItems)
35333574 protected function resetSelect ()
35343575 {
35353576 $ this ->resetRun ([
3536- 'QBSelect ' => [],
3537- 'QBJoin ' => [],
3538- 'QBWhere ' => [],
3539- 'QBGroupBy ' => [],
3540- 'QBHaving ' => [],
3541- 'QBOrderBy ' => [],
3542- 'QBNoEscape ' => [],
3543- 'QBDistinct ' => false ,
3544- 'QBLimit ' => false ,
3545- 'QBOffset ' => false ,
3546- 'QBUnion ' => [],
3577+ 'QBSelect ' => [],
3578+ 'QBJoin ' => [],
3579+ 'QBWhere ' => [],
3580+ 'QBGroupBy ' => [],
3581+ 'QBHaving ' => [],
3582+ 'QBOrderBy ' => [],
3583+ 'QBNoEscape ' => [],
3584+ 'QBDistinct ' => false ,
3585+ 'QBLimit ' => false ,
3586+ 'QBOffset ' => false ,
3587+ 'QBLockForUpdate ' => false ,
3588+ 'QBSelectUsesAggregate ' => false ,
3589+ 'QBUnion ' => [],
35473590 ]);
35483591
35493592 if ($ this ->db instanceof BaseConnection) {
0 commit comments