Skip to content

Commit ef8646a

Browse files
authored
Merge pull request #6741 from sclubricants/FixAliasBatch
Fix BaseBuilder setAlias() and RawSql use with key value pairs
2 parents 9b21201 + 0084c3c commit ef8646a

File tree

7 files changed

+131
-29
lines changed

7 files changed

+131
-29
lines changed

system/Database/BaseBuilder.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1974,6 +1974,7 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
19741974
public function setAlias(string $alias): BaseBuilder
19751975
{
19761976
if ($alias !== '') {
1977+
$this->db->addTableAlias($alias);
19771978
$this->QBOptions['alias'] = $this->db->protectIdentifiers($alias);
19781979
}
19791980

@@ -2049,6 +2050,10 @@ public function onConstraint($set)
20492050
$value = $this->db->protectIdentifiers($value);
20502051
}
20512052

2053+
if (is_string($key)) {
2054+
$key = $this->db->protectIdentifiers($key);
2055+
}
2056+
20522057
$this->QBOptions['constraints'][$key] = $value;
20532058
}
20542059
}
@@ -2476,9 +2481,20 @@ protected function _updateBatch(string $table, array $keys, array $values): stri
24762481
$sql .= 'WHERE ' . implode(
24772482
' AND ',
24782483
array_map(
2479-
static fn ($key) => ($key instanceof RawSql ?
2480-
$key :
2481-
$table . '.' . $key . ' = ' . $alias . '.' . $key),
2484+
static fn ($key, $value) => (
2485+
($value instanceof RawSql && is_string($key))
2486+
?
2487+
$table . '.' . $key . ' = ' . $value
2488+
:
2489+
(
2490+
$value instanceof RawSql
2491+
?
2492+
$value
2493+
:
2494+
$table . '.' . $value . ' = ' . $alias . '.' . $value
2495+
)
2496+
),
2497+
array_keys($constraints),
24822498
$constraints
24832499
)
24842500
);

system/Database/MySQLi/Builder.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,20 @@ protected function _updateBatch(string $table, array $keys, array $values): stri
9090
$sql .= 'ON ' . implode(
9191
' AND ',
9292
array_map(
93-
static fn ($key) => ($key instanceof RawSql ?
94-
$key :
95-
$table . '.' . $key . ' = ' . $alias . '.' . $key),
93+
static fn ($key, $value) => (
94+
($value instanceof RawSql && is_string($key))
95+
?
96+
$table . '.' . $key . ' = ' . $value
97+
:
98+
(
99+
$value instanceof RawSql
100+
?
101+
$value
102+
:
103+
$table . '.' . $value . ' = ' . $alias . '.' . $value
104+
)
105+
),
106+
array_keys($constraints),
96107
$constraints
97108
)
98109
) . "\n";

system/Database/OCI8/Builder.php

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,20 @@ protected function _updateBatch(string $table, array $keys, array $values): stri
271271
$sql .= 'ON (' . implode(
272272
' AND ',
273273
array_map(
274-
static fn ($key) => ($key instanceof RawSql ?
275-
$key :
276-
$table . '.' . $key . ' = ' . $alias . '.' . $key),
274+
static fn ($key, $value) => (
275+
($value instanceof RawSql && is_string($key))
276+
?
277+
$table . '.' . $key . ' = ' . $value
278+
:
279+
(
280+
$value instanceof RawSql
281+
?
282+
$value
283+
:
284+
$table . '.' . $value . ' = ' . $alias . '.' . $value
285+
)
286+
),
287+
array_keys($constraints),
277288
$constraints
278289
)
279290
) . ")\n";
@@ -354,18 +365,31 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
354365
return ''; // @codeCoverageIgnore
355366
}
356367

368+
$alias = $this->QBOptions['alias'] ?? '"_upsert"';
369+
357370
$updateFields = $this->QBOptions['updateFields'] ?? $this->updateFields($keys, false, $constraints)->QBOptions['updateFields'] ?? [];
358371

359372
$sql = 'MERGE INTO ' . $table . "\nUSING (\n{:_table_:}";
360373

361-
$sql .= ') "_upsert"' . "\nON (";
374+
$sql .= ") {$alias}\nON (";
362375

363376
$sql .= implode(
364377
' AND ',
365378
array_map(
366-
static fn ($key) => ($key instanceof RawSql ?
367-
$key :
368-
$table . '.' . $key . ' = "_upsert".' . $key),
379+
static fn ($key, $value) => (
380+
($value instanceof RawSql && is_string($key))
381+
?
382+
$table . '.' . $key . ' = ' . $value
383+
:
384+
(
385+
$value instanceof RawSql
386+
?
387+
$value
388+
:
389+
$table . '.' . $value . ' = ' . $alias . '.' . $value
390+
)
391+
),
392+
array_keys($constraints),
369393
$constraints
370394
)
371395
) . ")\n";
@@ -376,8 +400,8 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
376400
",\n",
377401
array_map(
378402
static fn ($key, $value) => $key . ($value instanceof RawSql ?
379-
' = ' . $value :
380-
' = "_upsert".' . $value),
403+
" = {$value}" :
404+
" = {$alias}.{$value}"),
381405
array_keys($updateFields),
382406
$updateFields
383407
)
@@ -386,7 +410,7 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
386410
$sql .= "\nWHEN NOT MATCHED THEN INSERT (" . implode(', ', $keys) . ")\nVALUES ";
387411

388412
$sql .= (' ('
389-
. implode(', ', array_map(static fn ($columnName) => '"_upsert".' . $columnName, $keys))
413+
. implode(', ', array_map(static fn ($columnName) => "{$alias}.{$columnName}", $keys))
390414
. ')');
391415

392416
$this->QBOptions['sql'] = $sql;

system/Database/Postgre/Builder.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use CodeIgniter\Database\BaseBuilder;
1515
use CodeIgniter\Database\Exceptions\DatabaseException;
1616
use CodeIgniter\Database\RawSql;
17+
use InvalidArgumentException;
1718

1819
/**
1920
* Builder for Postgre
@@ -365,6 +366,12 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
365366
}
366367
}
367368

369+
$alias = $this->QBOptions['alias'] ?? '"excluded"';
370+
371+
if (strtolower($alias) !== '"excluded"') {
372+
throw new InvalidArgumentException('Postgres alias is always named "excluded". A custom alias cannot be used.');
373+
}
374+
368375
$updateFields = $this->QBOptions['updateFields'] ?? $this->updateFields($keys, false, $constraints)->QBOptions['updateFields'] ?? [];
369376

370377
$sql = 'INSERT INTO ' . $table . ' (';
@@ -383,8 +390,8 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
383390
",\n",
384391
array_map(
385392
static fn ($key, $value) => $key . ($value instanceof RawSql ?
386-
' = ' . $value :
387-
' = "excluded".' . $value),
393+
" = {$value}" :
394+
" = {$alias}.{$value}"),
388395
array_keys($updateFields),
389396
$updateFields
390397
)

system/Database/SQLSRV/Builder.php

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -685,13 +685,15 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
685685
return ''; // @codeCoverageIgnore
686686
}
687687

688+
$alias = $this->QBOptions['alias'] ?? '"_upsert"';
689+
688690
$updateFields = $this->QBOptions['updateFields'] ?? $this->updateFields($keys, false, $constraints)->QBOptions['updateFields'] ?? [];
689691

690692
$sql = 'MERGE INTO ' . $fullTableName . "\nUSING (\n";
691693

692694
$sql .= '{:_table_:}';
693695

694-
$sql .= ') "_upsert" (';
696+
$sql .= ") {$alias} (";
695697

696698
$sql .= implode(', ', $keys);
697699

@@ -702,9 +704,20 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
702704
$sql .= implode(
703705
' AND ',
704706
array_map(
705-
static fn ($key) => ($key instanceof RawSql ?
706-
$key :
707-
$fullTableName . '.' . $key . ' = "_upsert".' . $key),
707+
static fn ($key, $value) => (
708+
($value instanceof RawSql && is_string($key))
709+
?
710+
$fullTableName . '.' . $key . ' = ' . $value
711+
:
712+
(
713+
$value instanceof RawSql
714+
?
715+
$value
716+
:
717+
$fullTableName . '.' . $value . ' = ' . $alias . '.' . $value
718+
)
719+
),
720+
array_keys($constraints),
708721
$constraints
709722
)
710723
) . ")\n";
@@ -716,7 +729,7 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
716729
array_map(
717730
static fn ($key, $value) => $key . ($value instanceof RawSql ?
718731
' = ' . $value :
719-
' = "_upsert".' . $value),
732+
" = {$alias}.{$value}"),
720733
array_keys($updateFields),
721734
$updateFields
722735
)
@@ -729,10 +742,10 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
729742
', ',
730743
array_map(
731744
static fn ($columnName) => $columnName === $tableIdentity
732-
? 'CASE WHEN "_upsert".' . $columnName . ' IS NULL THEN (SELECT '
745+
? "CASE WHEN {$alias}.{$columnName} IS NULL THEN (SELECT "
733746
. 'isnull(IDENT_CURRENT(\'' . $fullTableName . '\')+IDENT_INCR(\''
734-
. $fullTableName . '\'),1)) ELSE "_upsert".' . $columnName . ' END'
735-
: '"_upsert".' . $columnName,
747+
. $fullTableName . "'),1)) ELSE {$alias}.{$columnName} END"
748+
: "{$alias}.{$columnName}",
736749
$keys
737750
)
738751
) . ');'

system/Database/SQLite3/Builder.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use CodeIgniter\Database\BaseBuilder;
1515
use CodeIgniter\Database\Exceptions\DatabaseException;
1616
use CodeIgniter\Database\RawSql;
17+
use InvalidArgumentException;
1718

1819
/**
1920
* Builder for SQLite3
@@ -164,6 +165,12 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
164165
return ''; // @codeCoverageIgnore
165166
}
166167

168+
$alias = $this->QBOptions['alias'] ?? '`excluded`';
169+
170+
if (strtolower($alias) !== '`excluded`') {
171+
throw new InvalidArgumentException('SQLite alias is always named "excluded". A custom alias cannot be used.');
172+
}
173+
167174
$updateFields = $this->QBOptions['updateFields'] ??
168175
$this->updateFields($keys, false, $constraints)->QBOptions['updateFields'] ??
169176
[];
@@ -184,8 +191,8 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri
184191
",\n",
185192
array_map(
186193
static fn ($key, $value) => $key . ($value instanceof RawSql ?
187-
' = ' . $value :
188-
' = `excluded`.' . $value),
194+
" = {$value}" :
195+
" = {$alias}.{$value}"),
189196
array_keys($updateFields),
190197
$updateFields
191198
)

tests/system/Database/Live/UpdateTest.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,14 +475,38 @@ public function testRawSqlConstraint()
475475

476476
$builder = $this->db->table('user');
477477

478-
$builder->setData($data, true, 'db_myalias')
478+
$builder->setData($data, true, 'myalias')
479479
->updateFields('name, country')
480480
->onConstraint(new RawSql($this->db->protectIdentifiers('user.email') . ' = ' . $this->db->protectIdentifiers('myalias.email')))
481481
->updateBatch();
482482

483483
$this->seeInDatabase('user', ['email' => '[email protected]', 'country' => 'Germany']);
484484
}
485485

486+
public function testRawSqlConstraintWithKey()
487+
{
488+
if ($this->db->DBDriver === 'SQLite3' && ! (version_compare($this->db->getVersion(), '3.33.0') >= 0)) {
489+
$this->markTestSkipped('Only SQLite 3.33 and newer can complete this test.');
490+
}
491+
492+
$data = [
493+
[
494+
'name' => 'Derek Jones',
495+
'email' => '[email protected]',
496+
'country' => 'Germany',
497+
],
498+
];
499+
500+
$builder = $this->db->table('user');
501+
502+
$builder->setData($data, true, 'myalias')
503+
->updateFields('name, country')
504+
->onConstraint(['email' => new RawSql($this->db->protectIdentifiers('myalias.email'))])
505+
->updateBatch();
506+
507+
$this->seeInDatabase('user', ['email' => '[email protected]', 'country' => 'Germany']);
508+
}
509+
486510
public function testNoConstraintFound()
487511
{
488512
$jobData = [

0 commit comments

Comments
 (0)