Пакет · cdo

Обработка ошибок

Каждый сбой базы данных всплывает как единственный тип исключения — CDOException, который всегда оборачивает исходный PDOException. Это означает один catch на всю поверхность DML, с полными деталями SQLSTATE на расстоянии одного getPrevious().

Исключение

text
Flytachi\Winter\Cdo\Connection\CDOException
  └── extends \RuntimeException

CDOException — тонкий подкласс RuntimeException. Он несёт человекочитаемое сообщение, описывающее операцию, а его $previous — это нижележащий PDOException, сохраняющий код SQLSTATE, сообщение драйвера и трассировку стека.

Когда бросает каждый метод

Ситуация Метод
Сбой соединения PDO CDO::__construct()
Сбой INSERT insert(), insertGroup()
Сбой UPDATE update()
Сбой DELETE delete()
Сбой запроса апсерта upsert(), upsertGroup()
conflictColumns пуст upsert(), upsertGroup()

in() / notIn() бросают другой тип

Передача пустого массива в Qb::in() / Qb::notIn() бросает InvalidArgumentException (не CDOException) — это программная ошибка на этапе построения, перехватываемая до запуска любого запроса. См. Операторы Qb.

Перехват сбоев

Ловите CDOException, затем доставайте код из предыдущего PDOException:

php
use Flytachi\Winter\Cdo\Connection\CDOException;

try {
  $cdo->insert('users', ['email' => 'alice@example.com', 'name' => 'Alice']);
} catch (CDOException $e) {
  echo $e->getMessage();                 // "Error when creating a record in the database (...)"

  $pdo = $e->getPrevious();              // исходный PDOException
  echo $pdo?->getCode();                 // SQLSTATE, напр. "23505"
  echo $pdo?->getMessage();              // сообщение драйвера
}

Реакция на конкретную ошибку

Ветвитесь по коду SQLSTATE из обёрнутого исключения:

php
try {
  $cdo->insert('users', $data);
} catch (CDOException $e) {
  $pdo = $e->getPrevious();

  // 23xxx = нарушение целостности (unique / FK) и на PG, и на MySQL
  if ($pdo && str_starts_with((string) $pdo->getCode(), '23')) {
      throw new DuplicateException('Already exists');
  }

  throw $e; // пробросить всё нераспознанное
}

Справочник SQLSTATE

Частые коды, видимые через $e->getPrevious()->getCode():

Код Значение База данных
23000 Нарушение ограничения целостности MySQL / MariaDB
23505 Нарушение уникальности PostgreSQL
23503 Нарушение внешнего ключа PostgreSQL
42P01 Неопределённая таблица PostgreSQL
42000 Синтаксическая ошибка MySQL / MariaDB
08006 Сбой соединения PostgreSQL
HY000 Общая ошибка Разные

Шаблон «повтор при сбое»

При временной потере соединения переподключитесь один раз и повторите:

php
try {
  $id = ConnectionPool::db(AppDb::class)->insert('events', $event);
} catch (CDOException $e) {
  ConnectionPool::getConfigDb(AppDb::class)->reconnect();
  $id = ConnectionPool::db(AppDb::class)->insert('events', $event);
}

Связанное