Skip to content

Commit d1951d8

Browse files
committed
SqlPreprocessor: a multi-insert row with a missing or unexpected column warns
Columns are taken from the first row, so a later row that misses one used to be silently filled with NULL (with an "Undefined array key" warning), and a column the first row does not have was dropped without a trace. Both now warn.
1 parent 7e9d44f commit d1951d8

2 files changed

Lines changed: 43 additions & 3 deletions

File tree

‎src/Database/SqlPreprocessor.php‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace Nette\Database;
99

1010
use Nette;
11-
use function array_key_exists, array_keys, array_map, array_slice, array_values, count, explode, get_debug_type, implode, in_array, is_array, is_bool, is_float, is_int, is_resource, is_scalar, is_string, iterator_to_array, ltrim, number_format, rtrim, str_contains, str_ends_with, stream_get_contents, strtoupper, substr;
11+
use function array_diff_key, array_flip, array_key_exists, array_key_first, array_keys, array_map, array_slice, array_values, count, explode, get_debug_type, implode, in_array, is_array, is_bool, is_float, is_int, is_resource, is_scalar, is_string, iterator_to_array, ltrim, number_format, rtrim, str_contains, str_ends_with, stream_get_contents, strtoupper, substr;
1212

1313

1414
/**
@@ -259,10 +259,19 @@ private function formatMultiInsert(array $groups): string
259259

260260
$cols = array_keys(iterator_to_array($groups[0]));
261261
$vals = [];
262-
foreach ($groups as $group) {
262+
foreach ($groups as $i => $group) {
263+
$group = is_array($group) ? $group : iterator_to_array($group);
263264
$rowVals = [];
264265
foreach ($cols as $k) {
265-
$rowVals[] = $this->formatValue($group[$k]);
266+
if (!array_key_exists($k, $group)) { // the column would be silently filled with NULL
267+
trigger_error("Missing value for column '$k' in multi-insert row #$i.", E_USER_WARNING);
268+
}
269+
270+
$rowVals[] = $this->formatValue($group[$k] ?? null);
271+
}
272+
273+
if ($extra = array_diff_key($group, array_flip($cols))) { // the column is taken from the first row only
274+
trigger_error("Unexpected column '" . array_key_first($extra) . "' in multi-insert row #$i.", E_USER_WARNING);
266275
}
267276

268277
$vals[] = implode(', ', $rowVals);

‎tests/Database/SqlPreprocessor.phpt‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,37 @@ test('Detects incorrect multi-insert usage', function () use ($preprocessor) {
513513
});
514514

515515

516+
test('multi-insert row with a missing or unexpected column warns', function () use ($preprocessor) {
517+
Assert::error(
518+
fn() => $preprocessor->process(['INSERT INTO author', [
519+
['name' => 'Catelyn Stark', 'born' => null],
520+
['name' => 'Sansa Stark'], // the column would be silently filled with NULL
521+
]]),
522+
E_USER_WARNING,
523+
"Missing value for column 'born' in multi-insert row #1.",
524+
);
525+
526+
Assert::error(
527+
fn() => $preprocessor->process(['INSERT INTO author', [
528+
['name' => 'Catelyn Stark'],
529+
['name' => 'Sansa Stark', 'born' => null], // the column would be silently dropped
530+
]]),
531+
E_USER_WARNING,
532+
"Unexpected column 'born' in multi-insert row #1.",
533+
);
534+
535+
[$sql, $params] = $preprocessor->process(['INSERT INTO author', [
536+
['name' => 'Catelyn Stark', 'born' => null],
537+
['born' => null, 'name' => 'Sansa Stark'], // different key order is fine
538+
]]);
539+
Assert::same(reformat([
540+
'sqlite' => 'INSERT INTO author ([name], [born]) SELECT ?, NULL UNION ALL SELECT ?, NULL',
541+
'INSERT INTO author ([name], [born]) VALUES (?, NULL), (?, NULL)',
542+
]), $sql);
543+
Assert::same(['Catelyn Stark', 'Sansa Stark'], $params);
544+
});
545+
546+
516547
test('multi-row INSERT query', function () use ($preprocessor) {
517548
[$sql, $params] = $preprocessor->process(['INSERT INTO author', [
518549
['name' => 'Catelyn Stark', 'born' => new DateTime('2011-11-11')],

0 commit comments

Comments
 (0)