diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php
index b7fd9ed419c478b379ae8bcdeea307d69cc88cfa..c76a23cafd768561ed3f600283184af48b84b00f 100644
--- a/core/lib/Drupal/Core/Database/Database.php
+++ b/core/lib/Drupal/Core/Database/Database.php
@@ -3,8 +3,7 @@
 namespace Drupal\Core\Database;
 
 use Composer\Autoload\ClassLoader;
-use Drupal\Core\Database\Event\StatementExecutionEndEvent;
-use Drupal\Core\Database\Event\StatementExecutionStartEvent;
+use Drupal\Core\Database\Event\StatementEvent;
 use Drupal\Core\Extension\DatabaseDriverList;
 use Drupal\Core\Cache\NullBackend;
 
@@ -125,10 +124,7 @@ final public static function startLog($logging_key, $key = 'default') {
       // logging object associated with it.
       if (!empty(self::$connections[$key])) {
         foreach (self::$connections[$key] as $connection) {
-          $connection->enableEvents([
-            StatementExecutionStartEvent::class,
-            StatementExecutionEndEvent::class,
-          ]);
+          $connection->enableEvents(StatementEvent::all());
           $connection->setLogger(self::$logs[$key]);
         }
       }
@@ -469,10 +465,7 @@ final protected static function openConnection($key, $target) {
     // If we have any active logging objects for this connection key, we need
     // to associate them with the connection we just opened.
     if (!empty(self::$logs[$key])) {
-      $new_connection->enableEvents([
-        StatementExecutionStartEvent::class,
-        StatementExecutionEndEvent::class,
-      ]);
+      $new_connection->enableEvents(StatementEvent::all());
       $new_connection->setLogger(self::$logs[$key]);
     }
 
diff --git a/core/lib/Drupal/Core/Database/Event/StatementEvent.php b/core/lib/Drupal/Core/Database/Event/StatementEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..452c2003538d14a9b99e82bc9b61cd5c9736f907
--- /dev/null
+++ b/core/lib/Drupal/Core/Database/Event/StatementEvent.php
@@ -0,0 +1,26 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Core\Database\Event;
+
+/**
+ * Enumeration of the statement related database events.
+ */
+enum StatementEvent: string {
+
+  case ExecutionStart = StatementExecutionStartEvent::class;
+  case ExecutionEnd = StatementExecutionEndEvent::class;
+  case ExecutionFailure = StatementExecutionFailureEvent::class;
+
+  /**
+   * Returns an array with all statement related events.
+   *
+   * @return list<class-string<\Drupal\Core\Database\Event\DatabaseEvent>>
+   *   An array with all statement related events.
+   */
+  public static function all(): array {
+    return array_map(fn(self $case) => $case->value, self::cases());
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Database/Event/StatementExecutionFailureEvent.php b/core/lib/Drupal/Core/Database/Event/StatementExecutionFailureEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..682e59f23898319ba266c1f3a42b85e4d4cf0a26
--- /dev/null
+++ b/core/lib/Drupal/Core/Database/Event/StatementExecutionFailureEvent.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\Core\Database\Event;
+
+/**
+ * Represents the failure of a statement execution as an event.
+ */
+class StatementExecutionFailureEvent extends StatementExecutionEndEvent {
+
+  /**
+   * Constructor.
+   *
+   * See 'Customizing database settings' in settings.php for an explanation of
+   * the $key and $target connection values.
+   *
+   * @param int $statementObjectId
+   *   The id of the StatementInterface object as returned by spl_object_id().
+   * @param string $key
+   *   The database connection key.
+   * @param string $target
+   *   The database connection target.
+   * @param string $queryString
+   *   The SQL statement string being executed, with placeholders.
+   * @param array $args
+   *   The placeholders' replacement values.
+   * @param array $caller
+   *   A normalized debug backtrace entry representing the last non-db method
+   *   called.
+   * @param float $startTime
+   *   The time of the statement execution start.
+   * @param string $exceptionClass
+   *   The class of the exception that was thrown.
+   * @param int|string $exceptionCode
+   *   The code of the exception that was thrown.
+   * @param string $exceptionMessage
+   *   The message of the exception that was thrown.
+   */
+  public function __construct(
+    int $statementObjectId,
+    string $key,
+    string $target,
+    string $queryString,
+    array $args,
+    array $caller,
+    float $startTime,
+    public readonly string $exceptionClass,
+    public readonly int|string $exceptionCode,
+    public readonly string $exceptionMessage,
+  ) {
+    parent::__construct($statementObjectId, $key, $target, $queryString, $args, $caller, $startTime);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Database/StatementPrefetchIterator.php b/core/lib/Drupal/Core/Database/StatementPrefetchIterator.php
index 20a3378302f938ebcde46591159bf26bde380af8..96e3acfa3d7068929f79eab7a5713e17efe6c8c8 100644
--- a/core/lib/Drupal/Core/Database/StatementPrefetchIterator.php
+++ b/core/lib/Drupal/Core/Database/StatementPrefetchIterator.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Database;
 
 use Drupal\Core\Database\Event\StatementExecutionEndEvent;
+use Drupal\Core\Database\Event\StatementExecutionFailureEvent;
 use Drupal\Core\Database\Event\StatementExecutionStartEvent;
 
 /**
@@ -111,15 +112,27 @@ public function execute($args = [], $options = []) {
       $this->connection->dispatchEvent($startEvent);
     }
 
-    // Prepare the query.
-    $statement = $this->getStatement($this->queryString, $args);
-    if (!$statement) {
-      $this->throwPDOException();
+    // Prepare and execute the statement.
+    try {
+      $statement = $this->getStatement($this->queryString, $args);
+      $return = $statement->execute($args);
     }
-
-    $return = $statement->execute($args);
-    if (!$return) {
-      $this->throwPDOException();
+    catch (\Exception $e) {
+      if (isset($startEvent) && $this->connection->isEventEnabled(StatementExecutionFailureEvent::class)) {
+        $this->connection->dispatchEvent(new StatementExecutionFailureEvent(
+          $startEvent->statementObjectId,
+          $startEvent->key,
+          $startEvent->target,
+          $startEvent->queryString,
+          $startEvent->args,
+          $startEvent->caller,
+          $startEvent->time,
+          get_class($e),
+          $e->getCode(),
+          $e->getMessage(),
+        ));
+      }
+      throw $e;
     }
 
     // Fetch all the data from the reply, in order to release any lock as soon
@@ -150,8 +163,14 @@ public function execute($args = [], $options = []) {
 
   /**
    * Throw a PDO Exception based on the last PDO error.
+   *
+   * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is
+   *   no replacement.
+   *
+   * @see https://www.drupal.org/node/3410663
    */
   protected function throwPDOException(): void {
+    @trigger_error(__METHOD__ . '() is deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no replacement. See https://www.drupal.org/node/3410663', E_USER_DEPRECATED);
     $error_info = $this->connection->errorInfo();
     // We rebuild a message formatted in the same way as PDO.
     $exception = new \PDOException("SQLSTATE[" . $error_info[0] . "]: General error " . $error_info[1] . ": " . $error_info[2]);
diff --git a/core/lib/Drupal/Core/Database/StatementWrapperIterator.php b/core/lib/Drupal/Core/Database/StatementWrapperIterator.php
index b0474045a3752f096702499f9f7a29a3e1e59379..c6dd50383b184302abb9e26e633600209e62773e 100644
--- a/core/lib/Drupal/Core/Database/StatementWrapperIterator.php
+++ b/core/lib/Drupal/Core/Database/StatementWrapperIterator.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Database;
 
 use Drupal\Core\Database\Event\StatementExecutionEndEvent;
+use Drupal\Core\Database\Event\StatementExecutionFailureEvent;
 use Drupal\Core\Database\Event\StatementExecutionStartEvent;
 
 // cSpell:ignore maxlen driverdata INOUT
@@ -108,8 +109,27 @@ public function execute($args = [], $options = []) {
       $this->connection->dispatchEvent($startEvent);
     }
 
-    $return = $this->clientStatement->execute($args);
-    $this->markResultsetIterable($return);
+    try {
+      $return = $this->clientStatement->execute($args);
+      $this->markResultsetIterable($return);
+    }
+    catch (\Exception $e) {
+      if (isset($startEvent) && $this->connection->isEventEnabled(StatementExecutionFailureEvent::class)) {
+        $this->connection->dispatchEvent(new StatementExecutionFailureEvent(
+          $startEvent->statementObjectId,
+          $startEvent->key,
+          $startEvent->target,
+          $startEvent->queryString,
+          $startEvent->args,
+          $startEvent->caller,
+          $startEvent->time,
+          get_class($e),
+          $e->getCode(),
+          $e->getMessage(),
+        ));
+      }
+      throw $e;
+    }
 
     if (isset($startEvent) && $this->connection->isEventEnabled(StatementExecutionEndEvent::class)) {
       $this->connection->dispatchEvent(new StatementExecutionEndEvent(
diff --git a/core/modules/system/tests/modules/database_test/src/EventSubscriber/DatabaseEventSubscriber.php b/core/modules/system/tests/modules/database_test/src/EventSubscriber/DatabaseEventSubscriber.php
index e35d333fd234216529d3a1287eab916a542851c0..767edc64a9ed40bf00ec04fd730868db8ae1a7df 100644
--- a/core/modules/system/tests/modules/database_test/src/EventSubscriber/DatabaseEventSubscriber.php
+++ b/core/modules/system/tests/modules/database_test/src/EventSubscriber/DatabaseEventSubscriber.php
@@ -3,6 +3,7 @@
 namespace Drupal\database_test\EventSubscriber;
 
 use Drupal\Core\Database\Event\StatementExecutionEndEvent;
+use Drupal\Core\Database\Event\StatementExecutionFailureEvent;
 use Drupal\Core\Database\Event\StatementExecutionStartEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
@@ -21,6 +22,11 @@ class DatabaseEventSubscriber implements EventSubscriberInterface {
    */
   public int $countStatementEnds = 0;
 
+  /**
+   * A counter of failed statement executions.
+   */
+  public int $countStatementFailures = 0;
+
   /**
    * A map of statements being executed.
    */
@@ -33,6 +39,7 @@ public static function getSubscribedEvents(): array {
     return [
       StatementExecutionStartEvent::class => 'onStatementExecutionStart',
       StatementExecutionEndEvent::class => 'onStatementExecutionEnd',
+      StatementExecutionFailureEvent::class => 'onStatementExecutionFailure',
     ];
   }
 
@@ -58,4 +65,15 @@ public function onStatementExecutionEnd(StatementExecutionEndEvent $event): void
     $this->countStatementEnds++;
   }
 
+  /**
+   * Subscribes to a statement execution failure event.
+   *
+   * @param \Drupal\Core\Database\Event\StatementExecutionFailureEvent $event
+   *   The database event.
+   */
+  public function onStatementExecutionFailure(StatementExecutionFailureEvent $event): void {
+    unset($this->statementIdsInExecution[$event->statementObjectId]);
+    $this->countStatementFailures++;
+  }
+
 }
diff --git a/core/tests/Drupal/KernelTests/Core/Database/DatabaseEventTest.php b/core/tests/Drupal/KernelTests/Core/Database/DatabaseEventTest.php
index 895b61b880746ad4e9483a45713869f20ab623f8..ecd2b580f38d58965c7e4c3a6fd866fa090ae308 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/DatabaseEventTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/DatabaseEventTest.php
@@ -2,7 +2,9 @@
 
 namespace Drupal\KernelTests\Core\Database;
 
+use Drupal\Core\Database\Event\StatementEvent;
 use Drupal\Core\Database\Event\StatementExecutionEndEvent;
+use Drupal\Core\Database\Event\StatementExecutionFailureEvent;
 use Drupal\Core\Database\Event\StatementExecutionStartEvent;
 use Drupal\database_test\EventSubscriber\DatabaseEventSubscriber;
 
@@ -56,23 +58,40 @@ public function testStatementExecutionEvents(): void {
     $this->assertTrue($this->connection->isEventEnabled(StatementExecutionStartEvent::class));
     $this->assertTrue($this->connection->isEventEnabled(StatementExecutionEndEvent::class));
 
-    // Disable both events, no more events captured.
-    $this->connection->disableEvents([
-      StatementExecutionStartEvent::class,
-      StatementExecutionEndEvent::class,
-    ]);
+    // Enable the statement execution failure event and execute a failing
+    // query.
+    $this->connection->enableEvents([StatementExecutionFailureEvent::class]);
+    try {
+      $this->connection->query('bananas on the palm tree');
+      $this->fail('An exception was expected, but was not thrown');
+    }
+    catch (\Exception $e) {
+      // Expected, keep going.
+    }
+    $this->assertSame(3, $subscriber->countStatementStarts);
+    $this->assertSame(1, $subscriber->countStatementEnds);
+    $this->assertSame(1, $subscriber->countStatementFailures);
+    $this->assertEmpty($subscriber->statementIdsInExecution);
+    $this->assertTrue($this->connection->isEventEnabled(StatementExecutionStartEvent::class));
+    $this->assertTrue($this->connection->isEventEnabled(StatementExecutionEndEvent::class));
+    $this->assertTrue($this->connection->isEventEnabled(StatementExecutionFailureEvent::class));
+
+    // Disable all events, no more events captured.
+    $this->connection->disableEvents(StatementEvent::all());
     $this->connection->query('SELECT * FROM {test}');
-    $this->assertSame(2, $subscriber->countStatementStarts);
+    $this->assertSame(3, $subscriber->countStatementStarts);
     $this->assertSame(1, $subscriber->countStatementEnds);
+    $this->assertSame(1, $subscriber->countStatementFailures);
     $this->assertEmpty($subscriber->statementIdsInExecution);
     $this->assertFalse($this->connection->isEventEnabled(StatementExecutionStartEvent::class));
     $this->assertFalse($this->connection->isEventEnabled(StatementExecutionEndEvent::class));
+    $this->assertFalse($this->connection->isEventEnabled(StatementExecutionFailureEvent::class));
 
     // Enable the statement execution end only, no events captured since the
     // start event is required before the end one can be fired.
     $this->connection->enableEvents([StatementExecutionEndEvent::class]);
     $this->connection->query('SELECT * FROM {test}');
-    $this->assertSame(2, $subscriber->countStatementStarts);
+    $this->assertSame(3, $subscriber->countStatementStarts);
     $this->assertSame(1, $subscriber->countStatementEnds);
     $this->assertEmpty($subscriber->statementIdsInExecution);
     $this->assertFalse($this->connection->isEventEnabled(StatementExecutionStartEvent::class));
diff --git a/core/tests/Drupal/Tests/Core/Database/DatabaseEventsTest.php b/core/tests/Drupal/Tests/Core/Database/DatabaseEventsTest.php
index 433a72c49cabe80b93919c24c8712950e7c2dcda..f8fd9858d7ce70c0a6df932eb5b46bddef533802 100644
--- a/core/tests/Drupal/Tests/Core/Database/DatabaseEventsTest.php
+++ b/core/tests/Drupal/Tests/Core/Database/DatabaseEventsTest.php
@@ -6,7 +6,9 @@
 
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Database\Event\DatabaseEvent;
+use Drupal\Core\Database\Event\StatementEvent;
 use Drupal\Core\Database\Event\StatementExecutionEndEvent;
+use Drupal\Core\Database\Event\StatementExecutionFailureEvent;
 use Drupal\Core\Database\Event\StatementExecutionStartEvent;
 use Drupal\Core\Database\Exception\EventException;
 use Drupal\Tests\Core\Database\Stub\StubConnection;
@@ -40,17 +42,20 @@ protected function setUp(): void {
    * @covers ::disableEvents
    */
   public function testEventEnablingAndDisabling(): void {
-    $this->connection->enableEvents([
-      StatementExecutionStartEvent::class,
-      StatementExecutionEndEvent::class,
-    ]);
+    $this->connection->enableEvents(StatementEvent::all());
     $this->assertTrue($this->connection->isEventEnabled(StatementExecutionStartEvent::class));
     $this->assertTrue($this->connection->isEventEnabled(StatementExecutionEndEvent::class));
+    $this->assertTrue($this->connection->isEventEnabled(StatementExecutionFailureEvent::class));
     $this->connection->disableEvents([
       StatementExecutionEndEvent::class,
     ]);
     $this->assertTrue($this->connection->isEventEnabled(StatementExecutionStartEvent::class));
     $this->assertFalse($this->connection->isEventEnabled(StatementExecutionEndEvent::class));
+    $this->assertTrue($this->connection->isEventEnabled(StatementExecutionFailureEvent::class));
+    $this->connection->disableEvents(StatementEvent::all());
+    $this->assertFalse($this->connection->isEventEnabled(StatementExecutionStartEvent::class));
+    $this->assertFalse($this->connection->isEventEnabled(StatementExecutionEndEvent::class));
+    $this->assertFalse($this->connection->isEventEnabled(StatementExecutionFailureEvent::class));
   }
 
   /**