Skip to content
Open
73 changes: 69 additions & 4 deletions system/Database/BaseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use CodeIgniter\Exceptions\InvalidArgumentException;
use CodeIgniter\Traits\ConditionalTrait;
use Config\Feature;
use TypeError;

/**
* Class BaseBuilder
Expand Down Expand Up @@ -2929,9 +2930,41 @@ protected function _deleteBatch(string $table, array $keys, array $values): stri
*/
public function increment(string $column, int $value = 1)
{
$column = $this->db->protectIdentifiers($column);
return $this->incrementMany([$column], $value);
}

/**
* Increments multiple numeric columns by the specified value(s).
*
* @param array<string, int>|list<string> $columns A list of columns or array of column => value pairs to increment.
* @param int $value The value to increment by if $columns is a list of column names.
*/
public function incrementMany(array $columns, int $value = 1): bool
{
if ($columns === []) {
throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.');
}

if (array_is_list($columns)) {
$columns = array_fill_keys($columns, $value);
}

$fields = [];

foreach ($columns as $col => $val) {
if (! is_int($val)) {
throw new TypeError(sprintf(
'Argument #1 ($columns) must contain only int values, %s given for "%s".',
get_debug_type($val),
$col,
));
}

$col = $this->db->protectIdentifiers($col);
$fields[$col] = "{$col} + {$val}";
}
Comment thread
patel-vansh marked this conversation as resolved.

$sql = $this->_update($this->QBFrom[0], [$column => "{$column} + {$value}"]);
$sql = $this->_update($this->QBFrom[0], $fields);
Comment thread
patel-vansh marked this conversation as resolved.

if (! $this->testMode) {
$this->resetWrite();
Expand All @@ -2949,9 +2982,41 @@ public function increment(string $column, int $value = 1)
*/
public function decrement(string $column, int $value = 1)
{
$column = $this->db->protectIdentifiers($column);
return $this->decrementMany([$column], $value);
}

/**
* Decrements multiple numeric columns by the specified value(s).
*
* @param array<string, int>|list<string> $columns A list of columns or array of column => value pairs to decrement.
* @param int $value The value to decrement by if $columns is a list of column names.
*/
public function decrementMany(array $columns, int $value = 1): bool
{
if ($columns === []) {
throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.');
}

if (array_is_list($columns)) {
$columns = array_fill_keys($columns, $value);
}

$fields = [];

foreach ($columns as $col => $val) {
if (! is_int($val)) {
throw new TypeError(sprintf(
'Argument #1 ($columns) must contain only int values, %s given for "%s".',
get_debug_type($val),
$col,
));
}

$col = $this->db->protectIdentifiers($col);
$fields[$col] = "{$col} - {$val}";
}

$sql = $this->_update($this->QBFrom[0], [$column => "{$column}-{$value}"]);
$sql = $this->_update($this->QBFrom[0], $fields);

if (! $this->testMode) {
$this->resetWrite();
Expand Down
69 changes: 55 additions & 14 deletions system/Database/Postgre/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\RawSql;
use CodeIgniter\Exceptions\InvalidArgumentException;
use TypeError;

/**
* Builder for Postgre
Expand Down Expand Up @@ -87,17 +88,37 @@ public function orderBy(string $orderBy, string $direction = '', ?bool $escape =
}

/**
* Increments a numeric column by the specified value.
* Increments multiple numeric columns by the specified value(s).
*
* @return mixed
*
* @throws DatabaseException
* @param array<string, int>|list<string> $columns A list of columns or array of column => value pairs to increment.
* @param int $value The value to increment by if $columns is a list of column names.
*/
public function increment(string $column, int $value = 1)
public function incrementMany(array $columns, int $value = 1): bool
{
$column = $this->db->protectIdentifiers($column);
if ($columns === []) {
throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.');
}

if (array_is_list($columns)) {
$columns = array_fill_keys($columns, $value);
}

$fields = [];

foreach ($columns as $col => $val) {
if (! is_int($val)) {
Copy link
Copy Markdown
Contributor

@neznaika0 neznaika0 May 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot to test negative values. The is_int() check will work correctly and the data will be changed incorrectly: field + -1. It is necessary to prohibit these values.

The comment applies to all new methods

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, negative values were allowed in earlier behaviour. increment() method allowed negative values which would actually subtract from the field instead of adding. So, I tried to retain that behaviour. If everyone agrees to change that, I would happily change this to disallow negative values in both incrementMany() and decrementMany() method

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no problem with allowing negative values. This may be an actively used feature.

throw new TypeError(sprintf(
'Argument #1 ($columns) must contain only int values, %s given for "%s".',
get_debug_type($val),
$col,
));
}

$col = $this->db->protectIdentifiers($col);
$fields[$col] = "to_number({$col}, '9999999') + {$val}";
}

$sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') + {$value}"]);
$sql = $this->_update($this->QBFrom[0], $fields);

if (! $this->testMode) {
$this->resetWrite();
Expand All @@ -109,17 +130,37 @@ public function increment(string $column, int $value = 1)
}

/**
* Decrements a numeric column by the specified value.
* Decrements multiple numeric columns by the specified value(s).
*
* @return mixed
*
* @throws DatabaseException
* @param array<string, int>|list<string> $columns A list of columns or array of column => value pairs to decrement.
* @param int $value The value to decrement by if $columns is a list of column names.
*/
public function decrement(string $column, int $value = 1)
public function decrementMany(array $columns, int $value = 1): bool
{
$column = $this->db->protectIdentifiers($column);
if ($columns === []) {
throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.');
}

if (array_is_list($columns)) {
$columns = array_fill_keys($columns, $value);
}

$fields = [];

foreach ($columns as $col => $val) {
if (! is_int($val)) {
throw new TypeError(sprintf(
'Argument #1 ($columns) must contain only int values, %s given for "%s".',
get_debug_type($val),
$col,
));
}

$col = $this->db->protectIdentifiers($col);
$fields[$col] = "to_number({$col}, '9999999') - {$val}";
}

$sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') - {$value}"]);
$sql = $this->_update($this->QBFrom[0], $fields);

if (! $this->testMode) {
$this->resetWrite();
Expand Down
78 changes: 60 additions & 18 deletions system/Database/SQLSRV/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
use CodeIgniter\Database\Exceptions\DataException;
use CodeIgniter\Database\RawSql;
use CodeIgniter\Database\ResultInterface;
use CodeIgniter\Exceptions\InvalidArgumentException;
use Config\Feature;
use TypeError;

/**
* Builder for SQLSRV
Expand Down Expand Up @@ -232,21 +234,41 @@ protected function _update(string $table, array $values): string
}

/**
* Increments a numeric column by the specified value.
* Increments multiple numeric columns by the specified value(s).
*
* @return bool
* @param array<string, int>|list<string> $columns A list of columns or array of column => value pairs to increment.
* @param int $value The value to increment by if $columns is a list of column names.
*/
public function increment(string $column, int $value = 1)
public function incrementMany(array $columns, int $value = 1): bool
{
$column = $this->db->protectIdentifiers($column);
if ($columns === []) {
throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.');
}

if ($this->castTextToInt) {
$values = [$column => "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) + {$value})"];
} else {
$values = [$column => "{$column} + {$value}"];
if (array_is_list($columns)) {
$columns = array_fill_keys($columns, $value);
}

$fields = [];

foreach ($columns as $col => $val) {
if (! is_int($val)) {
throw new TypeError(sprintf(
'Argument #1 ($columns) must contain only int values, %s given for "%s".',
get_debug_type($val),
$col,
));
}

$col = $this->db->protectIdentifiers($col);
if ($this->castTextToInt) {
$fields[$col] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$col})) + {$val})";
} else {
$fields[$col] = "{$col} + {$val}";
}
}

$sql = $this->_update($this->QBFrom[0], $values);
$sql = $this->_update($this->QBFrom[0], $fields);

if (! $this->testMode) {
$this->resetWrite();
Expand All @@ -258,21 +280,41 @@ public function increment(string $column, int $value = 1)
}

/**
* Decrements a numeric column by the specified value.
* Decrements multiple numeric columns by the specified value(s).
*
* @return bool
* @param array<string, int>|list<string> $columns A list of columns or array of column => value pairs to decrement.
* @param int $value The value to decrement by if $columns is a list of column names.
*/
public function decrement(string $column, int $value = 1)
public function decrementMany(array $columns, int $value = 1): bool
{
$column = $this->db->protectIdentifiers($column);
if ($columns === []) {
throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.');
}

if ($this->castTextToInt) {
$values = [$column => "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) - {$value})"];
} else {
$values = [$column => "{$column} + {$value}"];
Comment thread
patel-vansh marked this conversation as resolved.
if (array_is_list($columns)) {
$columns = array_fill_keys($columns, $value);
}

$fields = [];

foreach ($columns as $col => $val) {
if (! is_int($val)) {
throw new TypeError(sprintf(
'Argument #1 ($columns) must contain only int values, %s given for "%s".',
get_debug_type($val),
$col,
));
}

$col = $this->db->protectIdentifiers($col);
if ($this->castTextToInt) {
$fields[$col] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$col})) - {$val})";
} else {
$fields[$col] = "{$col} - {$val}";
}
}

$sql = $this->_update($this->QBFrom[0], $values);
$sql = $this->_update($this->QBFrom[0], $fields);

if (! $this->testMode) {
$this->resetWrite();
Expand Down
Loading
Loading