Skip to content

Commit

Permalink
fix: updateBatch() Postgre type error
Browse files Browse the repository at this point in the history
  • Loading branch information
kenjis committed Jan 17, 2024
1 parent f0fd848 commit 425bb79
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 21 deletions.
42 changes: 33 additions & 9 deletions system/Database/BaseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class BaseBuilder
/**
* QB data sets
*
* @var array<string, string>|list<list<int|string>>
* @var array<string, string>|list<list<int|SqlValue|string>>
*/
protected $QBSet = [];

Expand Down Expand Up @@ -1852,8 +1852,16 @@ public function setData($set, ?bool $escape = null, string $alias = '')

$clean = [];

foreach ($row as $rowValue) {
$clean[] = $escape ? $this->db->escape($rowValue) : $rowValue;
foreach ($row as $key => $rowValue) {
[$keyName, $keyType] = $this->parseKey($key);

$escapedValue = $escape ? $this->db->escape($rowValue) : $rowValue;

if ($keyType !== null) {
$clean[] = new SqlValue($escapedValue, $keyType);
} else {
$clean[] = $escapedValue;
}
}

$row = $clean;
Expand All @@ -1862,16 +1870,32 @@ public function setData($set, ?bool $escape = null, string $alias = '')
}

foreach ($keys as $k) {
$k = $this->db->protectIdentifiers($k, false);
[$keyName] = $this->parseKey($k);

if (! in_array($k, $this->QBKeys, true)) {
$this->QBKeys[] = $k;
$keyName = $this->db->protectIdentifiers($keyName, false);

if (! in_array($keyName, $this->QBKeys, true)) {
$this->QBKeys[] = $keyName;
}
}

return $this;
}

/**
* Parses column name (with type) and returns name and type
* Key examples:
* - 'updated_at::TIMESTAMP'
*/
private function parseKey(string $key): array
{
$keyInfo = explode('::', $key, 2);
$keyName = $keyInfo[0];
$keyType = $keyInfo[1] ?? null;

return [$keyName, $keyType];
}

/**
* Compiles an upsert query and returns the sql
*
Expand Down Expand Up @@ -2560,9 +2584,9 @@ public function updateBatch($set = null, $constraints = null, int $batchSize = 1
*
* @used-by batchExecute
*
* @param string $table Protected table name
* @param list<string> $keys QBKeys
* @param list<list<int|string>> $values QBSet
* @param string $table Protected table name
* @param list<string> $keys QBKeys
* @param list<list<int|SqlValue|string>> $values QBSet
*/
protected function _updateBatch(string $table, array $keys, array $values): string
{
Expand Down
15 changes: 11 additions & 4 deletions system/Database/Postgre/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\RawSql;
use CodeIgniter\Database\SqlValue;
use InvalidArgumentException;

/**
Expand Down Expand Up @@ -317,9 +318,9 @@ public function join(string $table, $cond, string $type = '', ?bool $escape = nu
*
* @used-by batchExecute
*
* @param string $table Protected table name
* @param list<string> $keys QBKeys
* @param list<list<int|string>> $values QBSet
* @param string $table Protected table name
* @param list<string> $keys QBKeys
* @param list<list<int|SqlValue|string>> $values QBSet
*/
protected function _updateBatch(string $table, array $keys, array $values): string
{
Expand Down Expand Up @@ -393,7 +394,13 @@ protected function _updateBatch(string $table, array $keys, array $values): stri
" UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static function ($key, $index) {
if ($index instanceof SqlValue) {
return $index->getValue() . '::' . $index->getType() . ' ' . $key;
}

return $index . ' ' . $key;
},
$keys,
$value
)),
Expand Down
53 changes: 53 additions & 0 deletions system/Database/SqlValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\Database;

/**
* @interal
*/
class SqlValue
{
/**
* @var string Escaped column value.
*/
private string $value;

/**
* @var string|null Column type.
*/
private ?string $type;

/**
* @param string $value Escaped column value.
* @param string|null $type Column type.
*/
public function __construct(string $value, ?string $type = null)
{
$this->value = $value;
$this->type = $type;
}

public function getValue(): string
{
return $this->value;
}

public function getType(): string
{
return $this->type;
}

public function __toString(): string
{
return $this->value;
}
}
36 changes: 28 additions & 8 deletions tests/system/Database/Live/UpdateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,24 +115,44 @@ public function testUpdateBatch(): void
{
$data = [
[
'name' => 'Derek Jones',
'country' => 'Greece',
'name' => 'Derek Jones',
'country' => 'Greece',
'updated_at' => '2023-12-02 18:47:52',
],
[
'name' => 'Ahmadinejad',
'country' => 'Greece',
'name' => 'Ahmadinejad',
'country' => 'Greece',
'updated_at' => '2023-12-02 18:47:52',
],
];

if ($this->db->DBDriver === 'Postgre') {
// PostgreSQL needs column type.
$data = [
[
'name' => 'Derek Jones',
'country' => 'Greece',
'updated_at::TIMESTAMP' => '2023-12-02 18:47:52',
],
[
'name' => 'Ahmadinejad',
'country' => 'Greece',
'updated_at::TIMESTAMP' => '2023-12-02 18:47:52',
],
];
}

$this->db->table('user')->updateBatch($data, 'name');

$this->seeInDatabase('user', [
'name' => 'Derek Jones',
'country' => 'Greece',
'name' => 'Derek Jones',
'country' => 'Greece',
'updated_at' => '2023-12-02 18:47:52',
]);
$this->seeInDatabase('user', [
'name' => 'Ahmadinejad',
'country' => 'Greece',
'name' => 'Ahmadinejad',
'country' => 'Greece',
'updated_at' => '2023-12-02 18:47:52',
]);
}

Expand Down

0 comments on commit 425bb79

Please sign in to comment.