From 7a43398c90ac9ed2471879762dc660a9f23d700d Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Fri, 6 Mar 2026 14:18:49 -0800 Subject: [PATCH v7 4/5] libpq: Allow developers to reimplement libpq-oauth For PG19, since we won't have the ability to officially switch out flow plugins, relax the flow-loading code to not require the internal init function. Modules that don't have one will be treated as custom user flows in error messages. This will let bleeding-edge developers more easily test out the API and provide feedback for PG20, by telling the runtime linker to find a different libpq-oauth. It remains undocumented for end users. Discussion: https://postgr.es/m/CAOYmi%2BmrGg%2Bn_X2MOLgeWcj3v_M00gR8uz_D7mM8z%3DdX1JYVbg%40mail.gmail.com --- src/interfaces/libpq/fe-auth-oauth.h | 2 +- src/interfaces/libpq/fe-auth-oauth.c | 71 ++++++++++++++++------------ 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/interfaces/libpq/fe-auth-oauth.h b/src/interfaces/libpq/fe-auth-oauth.h index 2b22d23ffde..872f5df551a 100644 --- a/src/interfaces/libpq/fe-auth-oauth.h +++ b/src/interfaces/libpq/fe-auth-oauth.h @@ -36,7 +36,7 @@ typedef struct bool v1; bool builtin; - void *builtin_flow; + void *flow_module; } fe_oauth_state; extern void pqClearOAuthToken(PGconn *conn); diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c index 40800183eb9..dff00dfc6c9 100644 --- a/src/interfaces/libpq/fe-auth-oauth.c +++ b/src/interfaces/libpq/fe-auth-oauth.c @@ -889,8 +889,8 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re "libpq-oauth" DLSUFFIX; #endif - state->builtin_flow = dlopen(module_name, RTLD_NOW | RTLD_LOCAL); - if (!state->builtin_flow) + state->flow_module = dlopen(module_name, RTLD_NOW | RTLD_LOCAL); + if (!state->flow_module) { /* * For end users, this probably isn't an error condition, it just @@ -905,8 +905,16 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re return 0; } - if ((init = dlsym(state->builtin_flow, "libpq_oauth_init")) == NULL - || (start_flow = dlsym(state->builtin_flow, "pg_start_oauthbearer")) == NULL) + /* + * Our libpq-oauth.so provides a special initialization function for libpq + * integration. If we don't find this, assume that a custom module is in + * use instead. + */ + init = dlsym(state->flow_module, "libpq_oauth_init"); + if (!init) + state->builtin = false; /* adjust our error messages */ + + if ((start_flow = dlsym(state->flow_module, "pg_start_oauthbearer")) == NULL) { /* * This is more of an error condition than the one above, but the @@ -916,8 +924,8 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re if (oauth_unsafe_debugging_enabled()) fprintf(stderr, "failed dlsym for libpq-oauth: %s\n", dlerror()); - dlclose(state->builtin_flow); - state->builtin_flow = NULL; + dlclose(state->flow_module); + state->flow_module = NULL; request->error = libpq_gettext("could not find entry point for libpq-oauth"); return -1; @@ -928,39 +936,42 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re * permanently. */ - /* - * We need to inject necessary function pointers into the module. This - * only needs to be done once -- even if the pointers are constant, - * assigning them while another thread is executing the flows feels like - * tempting fate. - */ - if ((lockerr = pthread_mutex_lock(&init_mutex)) != 0) + if (init) { - /* Should not happen... but don't continue if it does. */ - Assert(false); + /* + * We need to inject necessary function pointers into the module. This + * only needs to be done once -- even if the pointers are constant, + * assigning them while another thread is executing the flows feels + * like tempting fate. + */ + if ((lockerr = pthread_mutex_lock(&init_mutex)) != 0) + { + /* Should not happen... but don't continue if it does. */ + Assert(false); - appendPQExpBuffer(&conn->errorMessage, - "use_builtin_flow: failed to lock mutex (%d)\n", - lockerr); + appendPQExpBuffer(&conn->errorMessage, + "use_builtin_flow: failed to lock mutex (%d)\n", + lockerr); - request->error = ""; /* satisfy report_flow_error() */ - return -1; - } + request->error = ""; /* satisfy report_flow_error() */ + return -1; + } - if (!initialized) - { - init( + if (!initialized) + { + init( #ifdef ENABLE_NLS - libpq_gettext + libpq_gettext #else - NULL + NULL #endif - ); + ); - initialized = true; - } + initialized = true; + } - pthread_mutex_unlock(&init_mutex); + pthread_mutex_unlock(&init_mutex); + } return (start_flow(conn, request) == 0) ? 1 : -1; } -- 2.34.1