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());
+  }
+
 }