postgresql-interfaces/psqlodbc GitHub issues and pull requests (mirror)
help / color / mirror / Atom feed[postgresql-interfaces/psqlodbc] PR #188: Clear PGresults from per-query rollback
2+ messages / 2 participants
[nested] [flat]
* [postgresql-interfaces/psqlodbc] PR #188: Clear PGresults from per-query rollback
@ 2026-05-15 06:15 "jarvis24young (@jarvis24young)" <[email protected]>
0 siblings, 0 replies; 2+ messages in thread
From: jarvis24young (@jarvis24young) @ 2026-05-15 06:15 UTC (permalink / raw)
To: postgresql-interfaces/psqlodbc <[email protected]>
## Problem
CC_internal_rollback() drains all PGresults returned by PQgetResult() in the PER_QUERY_ROLLBACK path, but did not clear each non-NULL result inside the loop. The command sent in this path contains two statements, ROLLBACK TO _per_query_svp_ and RELEASE _per_query_svp_, so libpq can return more than one result. Overwriting pgres on the next iteration loses the previous result and leaks it.
## Fix
Clear each PGresult immediately after handling it, matching the pattern already used by other PQgetResult() loops in connection.c.
## Verification
Built the driver in WSL with ASan/UBSan and exercised the real unixODBC + psqlODBC + PostgreSQL path. The black-box reproducer used Protocol=7.4-2 and UseServerSidePrepare=1, prepared and executed a statement, ran DEALLOCATE ALL through ODBC, then freed the prepared ODBC statement. That makes the driver's internal DEALLOCATE fail, logs SAVEPOINT _per_query_svp_ followed by ROLLBACK TO _per_query_svp_; RELEASE _per_query_svp_, and reaches CC_internal_rollback(PER_QUERY_ROLLBACK).
Before this patch, LSan reported: SUMMARY: AddressSanitizer: 432 byte(s) leaked in 2 allocation(s), with the stack PQgetResult -> CC_internal_rollback -> CC_send_query_append -> SC_set_prepared.
After this patch, the same reproducer exits with no LSan leak report. I also reran test/exe/error-rollback-test under the same ASan build; it passed.
^ permalink raw reply [nested|flat] 2+ messages in thread
* Re: [postgresql-interfaces/psqlodbc] PR #188: Clear PGresults from per-query rollback
@ 2026-05-18 19:12 "davecramer (@davecramer)" <[email protected]>
0 siblings, 0 replies; 2+ messages in thread
From: davecramer (@davecramer) @ 2026-05-18 19:12 UTC (permalink / raw)
To: postgresql-interfaces/psqlodbc <[email protected]>
The Bug
CC_internal_rollback() loops over PQgetResult() to drain all results from a multi-statement command (ROLLBACK TO
per_query_svp; RELEASE per_query_svp). Each iteration overwrites pgres with the next result, but never calls
PQclear() on the previous one — leaking it.
The Fix
default:
handle_pgres_error(self, pgres, __FUNCTION__, NULL, !ret);
}
+ PQclear(pgres);
+ pgres = NULL;
}
PQclear(pgres) inside the loop, immediately after the switch handles the result. pgres = NULL is defensive (prevents
double-free if the loop logic changes).
Assessment
Correct. This is the standard libpq pattern — every non-NULL PQgetResult() must be paired with a PQclear(). The fix
is placed after the switch statement so all cases (success, error) get cleared. PQclear(NULL) is a no-op per libpq
docs, so the NULL assignment is safe but not strictly necessary.
Verified by LSan. The PR description shows the leak was 432 bytes across 2 allocations (matching the two results
from the two-statement command), and the leak disappears after the fix.
No test added — the existing error-rollback-test exercises this path and passes. A dedicated leak test would require
LSan integration in CI, which is out of scope.
^ permalink raw reply [nested|flat] 2+ messages in thread
end of thread, other threads:[~2026-05-18 19:12 UTC | newest]
Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-05-15 06:15 [postgresql-interfaces/psqlodbc] PR #188: Clear PGresults from per-query rollback "jarvis24young (@jarvis24young)" <[email protected]>
2026-05-18 19:12 Re: [postgresql-interfaces/psqlodbc] PR #188: Clear PGresults from per-query rollback "davecramer (@davecramer)" <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox