From b478ba3363eb297bc7a2eaadbb71ff64362e3079 Mon Sep 17 00:00:00 2001 From: Jeremy Tan Date: Wed, 21 Oct 2026 08:49:31 +0000 Subject: [PATCH 1/3] fix(c/driver/postgresql): make connection init/release thread-safe --- c/driver/postgresql/README.md | 9 +++++++++ c/driver/postgresql/database.cc | 9 +++++---- c/driver/postgresql/database.h | 3 ++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/c/driver/postgresql/README.md b/c/driver/postgresql/README.md index b83ce2de70..ac11a36bcb 100644 --- a/c/driver/postgresql/README.md +++ b/c/driver/postgresql/README.md @@ -36,6 +36,15 @@ approach. **NOTE:** this project is not affiliated with PostgreSQL in any way. +## Thread safety + +The PostgreSQL driver supports concurrent `AdbcConnectionInit` and +`AdbcConnectionRelease` calls on distinct connections that share the same initialized +`AdbcDatabase`, including overlapping initialization and release. This allows connections to +be created and released in parallel. It does not make concurrent calls on the same connection, +or concurrent changes to or release of the database, safe; serialise those operations as +described by the general ADBC thread-safety guidance. + [libpq]: https://www.postgresql.org/docs/current/libpq.html [pgeon]: https://github.com/0x0L/pgeon diff --git a/c/driver/postgresql/database.cc b/c/driver/postgresql/database.cc index a164473550..c8391054e7 100644 --- a/c/driver/postgresql/database.cc +++ b/c/driver/postgresql/database.cc @@ -120,9 +120,10 @@ AdbcStatusCode PostgresDatabase::Init(struct AdbcError* error) { } AdbcStatusCode PostgresDatabase::Release(struct AdbcError* error) { - if (open_connections_ != 0) { + const int32_t open_connections = open_connections_.load(std::memory_order_relaxed); + if (open_connections != 0) { InternalAdbcSetError(error, "%s%" PRId32 "%s", "[libpq] Database released with ", - open_connections_, " open connections"); + open_connections, " open connections"); return ADBC_STATUS_INVALID_STATE; } return ADBC_STATUS_OK; @@ -194,14 +195,14 @@ AdbcStatusCode PostgresDatabase::Connect(PGconn** conn, struct AdbcError* error) *conn = nullptr; return ADBC_STATUS_IO; } - open_connections_++; + open_connections_.fetch_add(1, std::memory_order_relaxed); return ADBC_STATUS_OK; } AdbcStatusCode PostgresDatabase::Disconnect(PGconn** conn, struct AdbcError* error) { PQfinish(*conn); *conn = nullptr; - if (--open_connections_ < 0) { + if (open_connections_.fetch_sub(1, std::memory_order_relaxed) <= 0) { InternalAdbcSetError(error, "%s", "[libpq] Open connection count underflowed"); return ADBC_STATUS_INTERNAL; } diff --git a/c/driver/postgresql/database.h b/c/driver/postgresql/database.h index a608ef2d3d..5842a08f9c 100644 --- a/c/driver/postgresql/database.h +++ b/c/driver/postgresql/database.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include #include @@ -69,7 +70,7 @@ class PostgresDatabase { bool use_copy() const { return use_copy_; } private: - int32_t open_connections_; + std::atomic open_connections_; std::string uri_; std::shared_ptr type_resolver_; std::array postgres_server_version_{}; From 9a54302d7f72266920de1f702fc213472b27d704 Mon Sep 17 00:00:00 2001 From: Jeremy Tan Date: Fri, 23 Oct 2026 07:55:46 +0000 Subject: [PATCH 2/3] use seq_cst --- c/driver/postgresql/database.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/c/driver/postgresql/database.cc b/c/driver/postgresql/database.cc index c8391054e7..b416ee7c94 100644 --- a/c/driver/postgresql/database.cc +++ b/c/driver/postgresql/database.cc @@ -120,7 +120,7 @@ AdbcStatusCode PostgresDatabase::Init(struct AdbcError* error) { } AdbcStatusCode PostgresDatabase::Release(struct AdbcError* error) { - const int32_t open_connections = open_connections_.load(std::memory_order_relaxed); + const int32_t open_connections = open_connections_; if (open_connections != 0) { InternalAdbcSetError(error, "%s%" PRId32 "%s", "[libpq] Database released with ", open_connections, " open connections"); @@ -195,14 +195,14 @@ AdbcStatusCode PostgresDatabase::Connect(PGconn** conn, struct AdbcError* error) *conn = nullptr; return ADBC_STATUS_IO; } - open_connections_.fetch_add(1, std::memory_order_relaxed); + open_connections_++; return ADBC_STATUS_OK; } AdbcStatusCode PostgresDatabase::Disconnect(PGconn** conn, struct AdbcError* error) { PQfinish(*conn); *conn = nullptr; - if (open_connections_.fetch_sub(1, std::memory_order_relaxed) <= 0) { + if (--open_connections_ < 0) { InternalAdbcSetError(error, "%s", "[libpq] Open connection count underflowed"); return ADBC_STATUS_INTERNAL; } From 501b26eee21f0a807589779b8b5357e5e2083287 Mon Sep 17 00:00:00 2001 From: Jeremy Tan Date: Fri, 30 Oct 2026 12:34:53 +1000 Subject: [PATCH 3/3] delete --- c/driver/postgresql/README.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/c/driver/postgresql/README.md b/c/driver/postgresql/README.md index ac11a36bcb..b83ce2de70 100644 --- a/c/driver/postgresql/README.md +++ b/c/driver/postgresql/README.md @@ -36,15 +36,6 @@ approach. **NOTE:** this project is not affiliated with PostgreSQL in any way. -## Thread safety - -The PostgreSQL driver supports concurrent `AdbcConnectionInit` and -`AdbcConnectionRelease` calls on distinct connections that share the same initialized -`AdbcDatabase`, including overlapping initialization and release. This allows connections to -be created and released in parallel. It does not make concurrent calls on the same connection, -or concurrent changes to or release of the database, safe; serialise those operations as -described by the general ADBC thread-safety guidance. - [libpq]: https://www.postgresql.org/docs/current/libpq.html [pgeon]: https://github.com/0x0L/pgeon