diff --git a/core/lib/Drupal/Core/Database/Query/Update.php b/core/lib/Drupal/Core/Database/Query/Update.php index b10514c5b21bff7acd0836f3c1d4ece7d98411ed..6d8b924e19ed00bbf31c2a9b89b796c46df53d35 100644 --- a/core/lib/Drupal/Core/Database/Query/Update.php +++ b/core/lib/Drupal/Core/Database/Query/Update.php @@ -120,27 +120,9 @@ public function expression($field, $expression, ?array $arguments = NULL) { * actually didn't have to be updated because the values didn't change. */ public function execute() { - // Expressions take priority over literal fields, so we process those first - // and remove any literal fields that conflict. - $fields = $this->fields; - $update_values = []; - foreach ($this->expressionFields as $field => $data) { - if (!empty($data['arguments'])) { - $update_values += $data['arguments']; - } - if ($data['expression'] instanceof SelectInterface) { - $data['expression']->compile($this->connection, $this); - $update_values += $data['expression']->arguments(); - } - unset($fields[$field]); - } - // Because we filter $fields the same way here and in __toString(), the - // placeholders will all match up properly. - $max_placeholder = 0; - foreach ($fields as $value) { - $update_values[':db_update_placeholder_' . ($max_placeholder++)] = $value; - } + [$args, $update_values] = $this->getQueryArguments(); + $update_values += $args; if (count($this->condition)) { $this->condition->compile($this->connection, $this); @@ -182,8 +164,10 @@ public function __toString() { } $max_placeholder = 0; + [$args] = $this->getQueryArguments(); + $placeholders = array_keys($args); foreach ($fields as $field => $value) { - $update_fields[] = $this->connection->escapeField($field) . '=:db_update_placeholder_' . ($max_placeholder++); + $update_fields[] = $this->connection->escapeField($field) . '=' . $placeholders[$max_placeholder++]; } $query = $comments . 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields); @@ -197,4 +181,46 @@ public function __toString() { return $query; } + /** + * {@inheritdoc} + */ + public function arguments() { + [$args] = $this->getQueryArguments(); + return $this->condition->arguments() + $args; + } + + /** + * Returns the query arguments with placeholders mapped to their values. + * + * @return array + * An array containing arguments and update values. + * Both arguments and update values are associative array where the keys + * are the placeholder names and the values are the placeholder values. + */ + protected function getQueryArguments(): array { + // Expressions take priority over literal fields, so we process those first + // and remove any literal fields that conflict. + $fields = $this->fields; + $update_values = []; + foreach ($this->expressionFields as $field => $data) { + if (!empty($data['arguments'])) { + $update_values += $data['arguments']; + } + if ($data['expression'] instanceof SelectInterface) { + $data['expression']->compile($this->connection, $this); + $update_values += $data['expression']->arguments(); + } + unset($fields[$field]); + } + + // Because we filter $fields the same way here and in __toString(), the + // placeholders will all match up properly. + $max_placeholder = 0; + $args = []; + foreach ($fields as $value) { + $args[':db_update_placeholder_' . ($max_placeholder++)] = $value; + } + return [$args, $update_values]; + } + } diff --git a/core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php b/core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php index 260237c06b3c4e5087f9db01f600d4022cfba072..52d74dbb02d4c97a04b55b20bea2b1b1407973c1 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php @@ -172,4 +172,30 @@ public function testUpdateValueInSerial(): void { ->execute(); } + /** + * Tests the Update::__toString() method. + */ + public function testToString(): void { + // Prepare query for testing. + $query = $this->connection->update('test') + ->fields(['a' => 27, 'b' => 42]) + ->condition('c', [1, 2], 'IN'); + + // Confirm placeholders are present. + $query_string = (string) $query; + $this->assertStringContainsString(':db_update_placeholder_0', $query_string); + $this->assertStringContainsString(':db_update_placeholder_1', $query_string); + $this->assertStringContainsString(':db_condition_placeholder_0', $query_string); + $this->assertStringContainsString(':db_condition_placeholder_1', $query_string); + + // Test arguments. + $expected = [ + ':db_update_placeholder_0' => 27, + ':db_update_placeholder_1' => 42, + ':db_condition_placeholder_0' => 1, + ':db_condition_placeholder_1' => 2, + ]; + $this->assertEquals($expected, $query->arguments()); + } + }