diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 3989f1a..4f3ba02 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -20,6 +20,9 @@ #ifdef WIN32 #include "win32.h" +#ifdef _MSC_VER +#include +#endif #else #include #endif @@ -51,12 +54,54 @@ static const PGresult OOM_result = { .errMsg = "out of memory\n", }; +/* + * Relaxed 32-bit atomic access for the connectionless PQescapeString / + * PQescapeBytea state below. pqSaveParameterStatus() runs during the + * startup of every PQconnectdb(), so plain accesses there raced across + * concurrent connection establishment (DNTWW-8553). + * + * We cannot use C11 atomics (MSVC does not support them in C mode) nor + * pg_atomic (it is disallowed in frontend code), so use the compiler + * builtins. Relaxed ordering is sufficient: a reader accepts whatever + * the most recently completed connection saw. + */ +#ifdef _MSC_VER + +static inline void +fe_atomic_store_u32(volatile uint32 *p, uint32 v) +{ + (void) _InterlockedExchange((volatile long *) p, (long) v); +} + +static inline uint32 +fe_atomic_load_u32(const volatile uint32 *p) +{ + /* aligned 32-bit loads are single-copy atomic on all MSVC targets */ + return *p; +} +#else + +static inline void +fe_atomic_store_u32(volatile uint32 *p, uint32 v) +{ + __atomic_store_n(p, v, __ATOMIC_RELAXED); +} + +static inline uint32 +fe_atomic_load_u32(const volatile uint32 *p) +{ + return __atomic_load_n(p, __ATOMIC_RELAXED); +} +#endif + /* * static state needed by PQescapeString and PQescapeBytea; initialize to * values that result in backward-compatible behavior + * + * Zero initialization equals the former PG_SQL_ASCII / false values. */ -static int static_client_encoding = PG_SQL_ASCII; -static bool static_std_strings = false; +static volatile uint32 static_client_encoding; +static volatile uint32 static_std_strings; static PGEvent *dupEvents(PGEvent *events, int count, size_t *memSize); @@ -1130,12 +1175,14 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value) /* if we don't recognize the encoding name, fall back to SQL_ASCII */ if (conn->client_encoding < 0) conn->client_encoding = PG_SQL_ASCII; - static_client_encoding = conn->client_encoding; + fe_atomic_store_u32(&static_client_encoding, + (uint32) conn->client_encoding); } else if (strcmp(name, "standard_conforming_strings") == 0) { conn->std_strings = (strcmp(value, "on") == 0); - static_std_strings = conn->std_strings; + fe_atomic_store_u32(&static_std_strings, + (uint32) conn->std_strings); } else if (strcmp(name, "server_version") == 0) { @@ -4064,9 +4111,15 @@ PQescapeStringConn(PGconn *conn, size_t PQescapeString(char *to, const char *from, size_t length) { + int encoding; + bool std_strings; + + encoding = fe_atomic_load_u32(&static_client_encoding); + std_strings = fe_atomic_load_u32(&static_std_strings) != 0; + return PQescapeStringInternal(NULL, to, from, length, NULL, - static_client_encoding, - static_std_strings); + encoding, + std_strings); } @@ -4395,8 +4448,12 @@ PQescapeByteaConn(PGconn *conn, unsigned char * PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length) { + bool std_strings; + + std_strings = fe_atomic_load_u32(&static_std_strings) != 0; + return PQescapeByteaInternal(NULL, from, from_length, to_length, - static_std_strings, + std_strings, false /* can't use hex */ ); }