Skip to content

Commit 8a8a23a

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 7023d18 commit 8a8a23a

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

‎src/Database/SqlPreprocessor.php‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,19 @@ private function formatMultiInsert(array $groups): string
260260

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

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

‎tests/Database/SqlPreprocessor.phpt‎

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

530530

531+
test('multi-insert row with a missing or unexpected column warns', function () use ($preprocessor) {
532+
Assert::error(
533+
fn() => $preprocessor->process(['INSERT INTO author', [
534+
['name' => 'Catelyn Stark', 'born' => null],
535+
['name' => 'Sansa Stark'], // the column would be silently filled with NULL
536+
]]),
537+
E_USER_WARNING,
538+
"Missing value for column 'born' in multi-insert row #1.",
539+
);
540+
541+
Assert::error(
542+
fn() => $preprocessor->process(['INSERT INTO author', [
543+
['name' => 'Catelyn Stark'],
544+
['name' => 'Sansa Stark', 'born' => null], // the column would be silently dropped
545+
]]),
546+
E_USER_WARNING,
547+
"Unexpected column 'born' in multi-insert row #1.",
548+
);
549+
550+
[$sql, $params] = $preprocessor->process(['INSERT INTO author', [
551+
['name' => 'Catelyn Stark', 'born' => null],
552+
['born' => null, 'name' => 'Sansa Stark'], // different key order is fine
553+
]]);
554+
Assert::same(reformat([
555+
'sqlite' => 'INSERT INTO author ([name], [born]) SELECT ?, NULL UNION ALL SELECT ?, NULL',
556+
'INSERT INTO author ([name], [born]) VALUES (?, NULL), (?, NULL)',
557+
]), $sql);
558+
Assert::same(['Catelyn Stark', 'Sansa Stark'], $params);
559+
});
560+
561+
531562
test('multi-row INSERT query', function () use ($preprocessor) {
532563
[$sql, $params] = $preprocessor->process(['INSERT INTO author', [
533564
['name' => 'Catelyn Stark', 'born' => new DateTime('2011-11-11')],

0 commit comments

Comments
 (0)