Package · cdo

CDO API

CDO extends PDO, adding the DML methods below. Everything else — query, prepare, fetch*, transactions, attributes — is inherited from PDO unchanged. Every method here throws CDOException on failure, wrapping the original PDOException.

Identifiers are quoted; values are bound

Values are bound as prepared-statement parameters. Table and column names — which cannot be bound — are quoted with the driver’s quoting characters ("…" for PostgreSQL/SQLite/Oracle, `…` for MySQL/MariaDB), so these DML methods are safe even with dynamically built names. Because quoted identifiers are case-sensitive in PostgreSQL, pass names exactly as they exist in the schema (users, not Users). Note this quoting applies to the CDO methods only — column names passed to Qb are not quoted.

Construction

You rarely construct CDO directly — ConnectionPool does it for you. The constructor is:

php
public function __construct(
  DbConfigInterface $config,
  int $timeout = 5,
  bool $debug = false
)
Parameter Type Default Description
config DbConfigInterface Connection config (credentials, driver, logger)
timeout int 5 Connection timeout in seconds (PDO::ATTR_TIMEOUT)
debug bool false When true, sets PDO::ERRMODE_EXCEPTION

getDriverName

php
public function getDriverName(): string

Returns the normalised driver: 'pgsql', 'mysql', 'mariadb', 'sqlite', or 'oci'. Unlike PDO::ATTR_DRIVER_NAME, this distinguishes MariaDB from MySQL — see Driver detection.

insert

php
final public function insert(string $table, object|array $entity): mixed

Inserts one row and returns the generated primary key.

Parameter Type Description
table string Table name
entity `object\ array`
  • Returns the primary key — the value of the entity’s first key, read via RETURNING (PostgreSQL/MariaDB) or lastInsertId() (MySQL/SQLite/Oracle). Returns null when there is no generated id to report.
  • null values in entity are excluded from the INSERT.
  • Throws CDOException on query failure.

insertBatch

php
final public function insertBatch(
  string $table,
  iterable $entities,
  int $chunkSize = 1000
): int

Batch-inserts many rows as chunked multi-row INSERTs.

Parameter Type Default Description
table string Table name
entities iterable Array or generator of arrays/objects
chunkSize int 1000 Rows per INSERT statement
  • Returns the total number of inserted rows — a plain INSERT reports this count identically on PostgreSQL/MySQL/MariaDB/SQLite. An empty entities array is a no-op that returns 0.
  • Rows are grouped by their column signature (the set of non-null columns) before chunking, so rows sharing the same shape batch together and mixed shapes no longer break the multi-row VALUES list.
  • Per-row null values are excluded. Throws CDOException on failure, or if a row has no non-null columns.

upsert

php
final public function upsert(
  string $table,
  object|array $entity,
  array $conflictColumns,
  ?array $updateColumns = null
): mixed

Inserts a row, or updates/ignores it on unique conflict.

Parameter Type Default Description
table string Table name
entity `object\ array`
conflictColumns array Column(s) defining uniqueness
updateColumns `array\ null` null
  • Returns the primary key on PostgreSQL (via RETURNING); on MySQL/MariaDB/SQLite returns lastInsertId() (null if no new row was inserted).
  • Conflict syntax adapts to the driver: PostgreSQL and SQLite use ON CONFLICT, while MySQL/MariaDB use INSERT IGNORE / ON DUPLICATE KEY UPDATE.
  • Throws CDOException if conflictColumns is empty, or on query failure.
  • Expression tokens :new / :current — see Upsert placeholders.

upsertBatch

php
final public function upsertBatch(
  string $table,
  iterable $entities,
  array $conflictColumns,
  ?array $updateColumns = null,
  int $chunkSize = 500
): void

Batch upsert with chunking.

Parameter Type Default Description
table string Table name
entities iterable Array or generator of arrays/objects
conflictColumns array Column(s) defining uniqueness
updateColumns `array\ null` null
chunkSize int 500 Rows per statement
  • Returns nothing — void by design: an upsert’s affected-row count is not comparable across drivers, so there is no stable value to return. Empty entities ⇒ no-op; empty conflictColumns ⇒ throws.

update

php
final public function update(string $table, object|array $entity, Qb $qb): int

Updates rows matching a Qb condition.

Parameter Type Description
table string Table name
entity `object\ array`
qb Qb Condition for the WHERE clause
  • Returns the number of affected rows (rowCount()).
  • Throws CDOException on failure.

delete

php
final public function delete(string $table, Qb $qb): int

Deletes rows matching a Qb condition.

Parameter Type Description
table string Table name
qb Qb Condition for the WHERE clause
  • Returns the number of deleted rows. Throws CDOException on failure.

transaction

php
public function transaction(Closure $callback): void

Runs $callback inside a transaction: begins, invokes the callback, and commits on success. If the callback throws, the transaction is rolled back and the exception re-thrown.

  • The rollback is guarded by inTransaction() and its own failure is logged (not thrown), so the original callback exception is always the one that propagates — a failing rollback never masks it.
  • Throws whatever the callback throws (after the rollback attempt).

Cross-database behaviour

Works on all drivers, but with caveats: on MySQL only InnoDB tables roll back (MyISAM does not). DDL inside a transaction is transactional on PostgreSQL/SQLite, but on MySQL/MariaDB/Oracle a DDL statement causes an implicit commit and cannot be rolled back. Nesting is not supported — PDO does not support nested transactions.

applyDatabaseTimezone

php
public function applyDatabaseTimezone(mixed $driver, string $tz): void

Sets the session timezone to match PHP’s. Called automatically at connect time; exposed as public so you can re-apply it after a driver-level reset. Behaviour per driver is documented in Driver detection.

Writes only; reads stay PDO

There is intentionally no select / find here. Read with the inherited PDO methods and a Qb fragment for the WHERE.