2.9 KiB
0002 - Multi-database support strategy
- Status: Proposed
- Date: 2026-07-12
- Deciders: Cacti maintainers
Context
Cacti supports MariaDB/MySQL only. Requests for PostgreSQL recur (forum thread t=62859) and upstream has declined them, citing maintenance cost.
The connection layer is already driver-neutral. db_connect_real() in
lib/database.php accepts $db_type (default mysql) and feeds it to the
PDO DSN, and the db_fetch_*/db_execute_* wrappers sit on portable PDO
calls. The cost of a second backend is not the driver. It is the SQL dialect:
upsert semantics (ON DUPLICATE KEY UPDATE), DDL (backticks, unsigned,
AUTO_INCREMENT, ENGINE=), retry logic keyed on numeric MySQL error codes,
and MySQL-only introspection (SELECT DATABASE(), SHOW GLOBAL VARIABLES,
information_schema.processlist). MySQL-isms appear in ~155 PHP files and
throughout the ~3,557-line cacti.sql.
Decision
Introduce a dialect-abstraction layer behind the existing db_* API.
Call-site signatures do not change. The layer owns identifier quoting,
upsert rendering, LIMIT/OFFSET, type mapping, auto-increment vs identity,
introspection queries, and error-code classification. Driver is chosen from
$db_type at connect time.
MySQL/MariaDB stays tier-1: the default backend and the gate for merges. PostgreSQL is added as opt-in tier-2. New code uses standard SQL; unavoidable MySQL-isms route through helpers. Existing raw SQL migrates incrementally, hottest paths first. Plugin-authored MySQL SQL is out of scope.
Considered options
- Dialect-abstraction layer (chosen). Small surface, keeps the
db_*API, migrates incrementally, no rewrite. Cost: touches many call sites over time and adds a second CI backend. - Full ORM (Doctrine/Eloquent). Portable, but a rewrite of the entire data layer, a heavy new dependency, and a performance risk for the poller. Rejected.
- Status quo, MySQL only. Zero cost, no portability. The current state.
- Query-builder layer. Lighter than an ORM but still rewrites call sites into a fluent API for little gain over targeted helpers. Rejected.
Consequences
Positive: PostgreSQL becomes reachable without a rewrite; new code trends toward portable SQL; error handling stops depending on raw MySQL codes.
Negative: a second CI backend to maintain; incremental migration leaves a period where some paths are MySQL-only; contributors learn the helper API; PostgreSQL ships with known gaps until coverage completes.
Testing strategy
Test-first. Dialect conformance tests assert that one logical operation
renders the correct SQL per dialect (upsert, quoting, LIMIT/OFFSET, type
mapping), written before implementation. A schema round-trip test generates
DDL from the table-definition arrays, applies it to both backends, and reads
the columns back. CI runs the existing Pest suite against MySQL and
PostgreSQL; MySQL remains the gating backend.