Database portability
The generated client is shared across Rent's supported SQL databases. The selected dialect changes SQL rendering, bind limits, identity handling, catalog inspection, migration operations, transaction options, and capability checks. Applications do not implement a separate repository for each vendor.
| Capability | PostgreSQL | MySQL | MariaDB | SQLite |
|---|---|---|---|---|
| Typed CRUD and relationships | Yes | Yes | Yes | Yes |
| Transactions and savepoints | Yes | Yes | Yes | Yes |
| Optimistic locking | Yes | Yes | Yes | Yes |
| Row locking | Full server forms | Server forms | Server forms | Not supported; a lock clause fails validation |
| Insert returning | Complete rows | Generated ID plus reload when needed | Generated ID plus reload when needed | Complete rows |
| Multiple schemas | Yes | Database catalogs | Database catalogs | Not supported; @@schema is rejected |
| Structural alter migrations | Native operations | Native operations | Native operations | Atomic table rebuild when required |
| PostgreSQL extensions and RLS | Yes | No | No | No |
Portable by default
Fields, relationships, predicates, typed projections, keyset cursors, eager loads, hooks, policies, encryption, external blobs, bulk writes, and transaction closures have one application-facing API. Rent's backend capability profile rejects an unsupported operation instead of silently changing its meaning.
The driver uses one closed database-pool abstraction internally because the supported backends are known at compile time. Backend-specific SQL binding and inspection stay behind that boundary; query and migration layers operate on shared AST and catalog types. This avoids duplicating high-level behavior while still allowing static dispatch in the hot path.
Database-specific behavior
Statement parameter budgets
Rent allows up to 65,535 bound parameters per statement on PostgreSQL, MySQL, and MariaDB, and 32,766 on bundled SQLite. An IN filter uses one parameter per value; compound keys use one per component. Filters, projections, and pagination values share that budget.
Generated bulk inserts and eager-loading key lists are batched automatically. Arbitrary queries are not split:
doing so could change sorting, aggregate values, or a global limit. An oversized statement returns
DriverError::ParameterLimit with the dialect, actual count, and maximum before execution. It does not include
SQL or parameter values. A rejected statement leaves an open transaction usable.
For a large application-supplied membership filter, reduce the input set or express membership as a database subquery. Split independent writes inside a transaction when they must succeed or fail together. Custom database builds or connection-level restrictions can impose smaller limits, which are reported by the database itself.
The lower-level mutation planner also bounds insert and foreign-key attachment statements. Execute every
statement in a transactional plan on the same transaction. Oversized PostgreSQL DO UPDATE plans and
unconditional bidirectional updates require explicit smaller batches: automatic splitting could change how
duplicate targets are handled. A planning error occurs before any statement is executed.
Native capabilities
Use explicit dialect features when the database offers something valuable that is not portable. PostgreSQL extensions, row-level security, domains, operator classes, and preload requirements are first-class catalog or extension concepts. MySQL and MariaDB retain their server identity and catalog differences. SQLite migration plans model table rebuilds so rows, indexes, and foreign keys can be preserved atomically.
Raw SQL remains available for a database feature Rent has not modeled. Keep that code at a narrow boundary and add a live test for every database it claims to support.
Compile only the required driver using the database setup guide. Use
rent migrate preflight to discover unsupported or risky migration work
before deployment.