fix(pgsql): mark transaction state unknown when backend connection is broken

When a backend connection breaks with an error (e.g. postmaster SIGKILL
mid-query), libpq's PQtransactionStatus() returns PQTRANS_UNKNOWN rather
than PQTRANS_INTRANS, so IsActiveTransaction() reports false for a
connection that was in a transaction moments earlier. The retry path in
PgSQL_Session::handler_minus1_ClientLibraryError() then replays the
failed statement on a fresh backend connection as autocommit, breaking
transactional semantics: the client's next COMMIT lands on a connection
with no open transaction and PostgreSQL emits "WARNING: there is no
transaction in progress".

MySQL does not have this issue because mysql->server_status caches
SERVER_STATUS_IN_TRANS from the last OK packet and survives the
connection breaking.

Fix compute_unknown_transaction_status() to force
unknown_transaction_status=true when the backend is disconnected,
so the retry guard in handler_minus1_ClientLibraryError() refuses to
replay statements that were running inside an explicit transaction.
fix/pgsql-active-tx-on-broken-conn
Rene Cannao 4 weeks ago
parent 872479aeba
commit 68c76eb425

@ -1308,10 +1308,16 @@ void PgSQL_Connection::compute_unknown_transaction_status() {
return;
}
/*if (is_connected() == false) {
// On a broken backend, PQtransactionStatus() returns PQTRANS_UNKNOWN
// even if a transaction was active — libpq has no cached INTRANS bit
// equivalent to MySQL's server_status & SERVER_STATUS_IN_TRANS. Force
// unknown_transaction_status=true so IsActiveTransaction() still
// reports true and the retry path does not replay inside-tx statements
// on a fresh connection (which would run them as autocommit).
if (is_connected() == false) {
unknown_transaction_status = true;
return;
}*/
}
switch (PQtransactionStatus(pgsql_conn)) {
case PQTRANS_INTRANS:

Loading…
Cancel
Save