PostgreSQL-compatible databases
Which PostgreSQL-compatible systems, such as CockroachDB and YugabyteDB, QueryProxy is tested against as targets, and where each differs from PostgreSQL.
QueryProxy talks to PostgreSQL through its pgsql driver. A system that speaks
the PostgreSQL protocol can be added as a connection with the driver
PostgreSQL, but each one differs from PostgreSQL in places the application
relies on: how a running statement is stopped, how read-only mode works, how
locks time out. This page lists what has been tested and what is known to differ.
“Tested in CI” means the integration tests run against a real server of that system on every pull request. “Not tested” means nothing has been run against it: it may work, and nothing here promises it.
Status
| System | Version tested | Status |
|---|---|---|
| CockroachDB | v25.4.19 | Tested in CI, with known differences |
| YugabyteDB | 2025.2.8.0-b60 (PostgreSQL 15.12-YB) | Tested in CI, with known differences |
| Aurora PostgreSQL | none | Not tested |
| Neon | none | Not tested |
| Supabase | none | Not tested |
These are target databases only. The application’s own database stays SQLite, MySQL, MariaDB, PostgreSQL or SQL Server; CockroachDB and YugabyteDB are not supported as the application database.
The tests cover read-only enforcement, statement and lock timeouts, stopping a running statement, streaming of large results and required TLS. How each system does:
| Behaviour | CockroachDB | YugabyteDB |
|---|---|---|
| The database refuses writes and DDL in read requests | Yes (read-only mode) | Yes |
| The SQL guard knows the system’s own statements | No, PostgreSQL dialect only (SQL guard) | PostgreSQL dialect; YugabyteDB-only statements not reviewed |
| Statement timeout cuts a long read or write | Yes | Yes |
| Lock timeout cuts a wait for a row lock | Yes | No (lock waits) |
| Stop ends a running write | Yes | Yes |
| Stop ends a running read | No, the statement timeout ends it | Yes |
| Large results are streamed (server-side cursor) | Yes | Yes |
| Required TLS refuses a server without TLS; CA verification | Yes | Not tested |
| Connection test reports the session as encrypted | Reports “unknown” | Not tested |
CockroachDB
Add the connection with the driver PostgreSQL, port 26257 (the SQL port),
and the database name. Use a secure cluster, with TLS and password logins. An
insecure node (--insecure) accepts no passwords and offers no TLS, so it cannot
meet the TLS requirement of new connections and cannot hold a read-only login.
Known differences:
- Stopping a statement. CockroachDB has no
pg_cancel_backend. QueryProxy records the session id (SHOW session_id) next to the process number and stops a statement withCANCEL QUERIES SELECT query_id FROM [SHOW CLUSTER STATEMENTS] WHERE session_id = .... The session stays, as with PostgreSQL. The login must be allowed to see its own statements (every login is), and the nodes must reach each other at their advertised address: a node advertising0.0.0.0makesSHOW CLUSTER STATEMENTScome back empty and the stop is not delivered. - Stopping a read. A read request runs through a server-side cursor
(
DECLARE ... CURSOR). CockroachDB evaluates a cursor’s query when it is declared and ignoresCANCEL QUERIESandCANCEL SESSIONuntil that is done. Stop marks the request as cancelled at once, but the statement keeps running until it finishes or the database-side statement timeout cuts it. QueryProxy always sets one: the execution timeout minus 5 seconds. A stopped write is interrupted at once. - Read-only mode. CockroachDB commits the open transaction before a DDL
statement (the
autocommit_before_ddlsetting), the way MySQL does, so aREAD ONLYtransaction alone would let DDL through. On CockroachDB QueryProxy therefore also setsdefault_transaction_read_onlyfor the whole session, and the server then refuses DDL and data changes in a read request. Unlike PostgreSQL, CockroachDB lets a statement switch its own transaction back to read-write (SELECT set_config('transaction_read_only', 'off', false)orSET transaction_read_only = off). The SQL guard refuses those statements in read requests, but the database would not. For a guarantee on the database side, configure a read-only login on the connection. - SQL guard. The guard’s deny lists are written for the PostgreSQL dialect and
do not know statements that exist only in CockroachDB. These pass the guard as
ordinary writes, or, for
crdb_internal.*functions such ascrdb_internal.force_panic, as reads:CANCEL QUERYandCANCEL SESSION(and the plural forms),SET CLUSTER SETTING,EXPORT,IMPORT,BACKUPandRESTORE. PostgreSQL’s counterparts, such aspg_cancel_backendandALTER SYSTEM, are refused. On CockroachDB the protection against these is the login, not the guard: connect with the least-privileged login that does the job, not a member of theadminrole and without theCANCELQUERYandMODIFYCLUSTERSETTINGrole options or theEXTERNALIOIMPLICITACCESSprivilege, and add a separate read-only login for read requests. Widening the guard to the CockroachDB dialect is not part of 0.4.1. - TLS state.
pg_stat_sslis empty on CockroachDB, so the connection test reports the session’s encryption as unknown instead of yes or no. The TLS requirement itself works: arequireconnection to a node without TLS fails, and certificate verification (verify-fullwith a CA) works. - Isolation. CockroachDB runs transactions at
SERIALIZABLE. A statement can fail with a retryable error (SQLSTATE40001). QueryProxy reports it as the error of the request and does not retry.
YugabyteDB
Add the connection with the driver PostgreSQL, port 5433 (YSQL), and the
database name. Session ids, pg_cancel_backend, pg_stat_activity,
pg_stat_ssl, read-only transactions and server-side cursors behave as in
PostgreSQL, so stopping a statement, read-only mode, the statement timeout and
streaming need no special handling.
Known differences:
- Lock waits.
lock_timeoutis not applied while a statement waits for a row lock held by another transaction (observed on 2025.2.8.0 with wait queues on, the default). The wait ends at the statement timeout instead. A singleUPDATErun outside a transaction that the statement timeout cuts during that wait can still be applied once the other transaction lets go, although the request is reported as failed. Inside a transaction it is rolled back. Do not rely on the lock timeout, or on a failed write meaning “nothing changed”, for rows that other sessions hold locks on. - TLS. Not tested: the test server runs without client-to-server encryption.
Not tested
Aurora PostgreSQL, Neon and Supabase speak the PostgreSQL protocol and are expected to behave like PostgreSQL for the features above, but nothing has been run against them. Connection poolers in transaction mode, such as Neon’s pooled endpoint and Supabase’s pooler, are a known risk for session-level settings like the statement timeout outside transactions, and for the process number used to stop a statement.
Next steps
- SQL guards for what the guard refuses and why.
- Configuration for timeouts, TLS to target databases and read credentials.
- Threat model for what the database-side settings are meant to hold.