Package · cdo

Driver detection & dialects

CDO adapts its SQL to the database it’s actually talking to. This page explains how it figures out which database that is, and every place the generated SQL branches on it — written from the connection code, not from memory.

The problem PDO leaves open

PDO::ATTR_DRIVER_NAME returns 'mysql' for both MySQL and MariaDB. But the two diverge on a feature CDO relies on: MariaDB ≥ 10.5 supports INSERT ... RETURNING; MySQL never has. Treating them the same would break the returned-id path on one of them.

How the driver is normalised

At connect time (applyDatabase()), CDO reads PDO::ATTR_DRIVER_NAME and, for mysql, inspects PDO::ATTR_SERVER_VERSION. If the version string contains "MariaDB" (e.g. "5.5.5-10.11.6-MariaDB" or "11.4.2-MariaDB"), the driver is recorded as mariadb. The normalised value — one of pgsql, mysql, mariadb, oci — is cached and returned by getDriverName().

text
ATTR_DRIVER_NAME = 'pgsql'                        → 'pgsql'
ATTR_DRIVER_NAME = 'mysql', version has 'MariaDB' → 'mariadb'
ATTR_DRIVER_NAME = 'mysql', version lacks it      → 'mysql'
ATTR_DRIVER_NAME = 'oci'                          → 'oci'

PDO attributes set per driver

applyDatabase() also tunes PDO based on the raw driver:

Driver ATTR_EMULATE_PREPARES Rationale
pgsql false Native server-side prepares
mysql true Emulated prepares
oci true Emulated prepares

The default fetch mode is set to PDO::FETCH_ASSOC for every driver, so inherited fetch() / fetchAll() return associative arrays unless you override it.

Where the SQL branches: returned id

insert() uses RETURNING only where it’s supported, and lastInsertId() elsewhere:

Driver insert() strategy
pgsql INSERT … RETURNING pkfetchColumn()
mariadb INSERT … RETURNING pkfetchColumn()
mysql plain INSERTlastInsertId()
oci plain INSERTlastInsertId()

A falsy result is normalised to null — meaning “no generated id to report” (e.g. an INSERT into a table without an auto-increment/serial column), not an error. Real SQL errors come through PDOException and become CDOException.

Upsert RETURNING is narrower

upsert() uses RETURNING on PostgreSQL only, even though MariaDB supports RETURNING for plain inserts — MariaDB disallows it together with ON DUPLICATE KEY UPDATE / INSERT IGNORE, so CDO falls back to lastInsertId() there. See Upsert placeholders.

Where the SQL branches: upsert dialect

Driver Ignore conflicts Update on conflict
pgsql ON CONFLICT (cols) DO NOTHING ON CONFLICT (cols) DO UPDATE SET …
everything else INSERT IGNORE ON DUPLICATE KEY UPDATE …

The :new / :current token substitution also depends on the driver — full matrix in Upsert placeholders.

Where the SQL branches: timezone

On connect, CDO sets the database session timezone to PHP’s (date_default_timezone_get()), per driver:

Driver Statement
pgsql SET TIMEZONE TO '<tz>'
mysql (and MariaDB) SET time_zone = '<offset>'
oci ALTER SESSION SET TIME_ZONE = '<tz>'
other not implemented — logs a warning

MySQL path needs an offset helper

For MySQL/MariaDB, CDO converts the PHP timezone name to a numeric offset via a timezoneToOffset() function it expects to exist in the global scope. It is not shipped by this package — it’s provided by the host Winter framework. Outside that framework, define an equivalent global (or the MySQL SET time_zone step is skipped when the helper returns null). PostgreSQL and Oracle take the timezone name directly and have no such dependency.

Driver capability summary

Operation PostgreSQL MySQL MariaDB Oracle
insert returns id ✅ RETURNING ✅ lastInsertId ✅ RETURNING ⚠️ lastInsertId
insertGroup
upsert / upsertGroup
update / delete