LCOV - differential code coverage report
Current view: top level - src/include/libpq - libpq-be-fe-helpers.h (source / functions) Coverage Total Hit UNC GNC
Current: Differential Code Coverage HEAD vs 15 Lines: 84.1 % 44 37 7 37
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 5 5 5
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (60,120] days: 84.1 % 44 37 7 37
Legend: Lines: hit not hit Function coverage date bins:
(60,120] days: 100.0 % 5 5 5

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*-------------------------------------------------------------------------
                                  2                 :  *
                                  3                 :  * libpq-be-fe-helpers.h
                                  4                 :  *    Helper functions for using libpq in extensions
                                  5                 :  *
                                  6                 :  * Code built directly into the backend is not allowed to link to libpq
                                  7                 :  * directly. Extension code is allowed to use libpq however. However, libpq
                                  8                 :  * used in extensions has to be careful to block inside libpq, otherwise
                                  9                 :  * interrupts will not be processed, leading to issues like unresolvable
                                 10                 :  * deadlocks. Backend code also needs to take care to acquire/release an
                                 11                 :  * external fd for the connection, otherwise fd.c's accounting of fd's is
                                 12                 :  * broken.
                                 13                 :  *
                                 14                 :  * This file provides helper functions to make it easier to comply with these
                                 15                 :  * rules. It is a header only library as it needs to be linked into each
                                 16                 :  * extension using libpq, and it seems too small to be worth adding a
                                 17                 :  * dedicated static library for.
                                 18                 :  *
                                 19                 :  * TODO: For historical reasons the connections established here are not put
                                 20                 :  * into non-blocking mode. That can lead to blocking even when only the async
                                 21                 :  * libpq functions are used. This should be fixed.
                                 22                 :  *
                                 23                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
                                 24                 :  * Portions Copyright (c) 1994, Regents of the University of California
                                 25                 :  *
                                 26                 :  * src/include/libpq/libpq-be-fe-helpers.h
                                 27                 :  *
                                 28                 :  *-------------------------------------------------------------------------
                                 29                 :  */
                                 30                 : #ifndef LIBPQ_BE_FE_HELPERS_H
                                 31                 : #define LIBPQ_BE_FE_HELPERS_H
                                 32                 : 
                                 33                 : /*
                                 34                 :  * Despite the name, BUILDING_DLL is set only when building code directly part
                                 35                 :  * of the backend. Which also is where libpq isn't allowed to be
                                 36                 :  * used. Obviously this doesn't protect against libpq-fe.h getting included
                                 37                 :  * otherwise, but perhaps still protects against a few mistakes...
                                 38                 :  */
                                 39                 : #ifdef BUILDING_DLL
                                 40                 : #error "libpq may not be used code directly built into the backend"
                                 41                 : #endif
                                 42                 : 
                                 43                 : #include "libpq-fe.h"
                                 44                 : #include "miscadmin.h"
                                 45                 : #include "storage/fd.h"
                                 46                 : #include "storage/latch.h"
                                 47                 : #include "utils/wait_event.h"
                                 48                 : 
                                 49                 : 
                                 50                 : static inline void libpqsrv_connect_prepare(void);
                                 51                 : static inline void libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info);
                                 52                 : 
                                 53                 : 
                                 54                 : /*
                                 55                 :  * PQconnectdb() wrapper that reserves a file descriptor and processes
                                 56                 :  * interrupts during connection establishment.
                                 57                 :  *
                                 58                 :  * Throws an error if AcquireExternalFD() fails, but does not throw if
                                 59                 :  * connection establishment itself fails. Callers need to use PQstatus() to
                                 60                 :  * check if connection establishment succeeded.
                                 61                 :  */
                                 62                 : static inline PGconn *
   76 andres                     63 GNC          21 : libpqsrv_connect(const char *conninfo, uint32 wait_event_info)
                                 64                 : {
                                 65              21 :     PGconn     *conn = NULL;
                                 66                 : 
                                 67              21 :     libpqsrv_connect_prepare();
                                 68                 : 
                                 69              21 :     conn = PQconnectStart(conninfo);
                                 70                 : 
                                 71              21 :     libpqsrv_connect_internal(conn, wait_event_info);
                                 72                 : 
                                 73              21 :     return conn;
                                 74                 : }
                                 75                 : 
                                 76                 : /*
                                 77                 :  * Like libpqsrv_connect(), except that this is a wrapper for
                                 78                 :  * PQconnectdbParams().
                                 79                 :   */
                                 80                 : static inline PGconn *
                                 81             668 : libpqsrv_connect_params(const char *const *keywords,
                                 82                 :                         const char *const *values,
                                 83                 :                         int expand_dbname,
                                 84                 :                         uint32 wait_event_info)
                                 85                 : {
                                 86             668 :     PGconn     *conn = NULL;
                                 87                 : 
                                 88             668 :     libpqsrv_connect_prepare();
                                 89                 : 
                                 90             668 :     conn = PQconnectStartParams(keywords, values, expand_dbname);
                                 91                 : 
                                 92             668 :     libpqsrv_connect_internal(conn, wait_event_info);
                                 93                 : 
                                 94             667 :     return conn;
                                 95                 : }
                                 96                 : 
                                 97                 : /*
                                 98                 :  * PQfinish() wrapper that additionally releases the reserved file descriptor.
                                 99                 :  *
                                100                 :  * It is allowed to call this with a NULL pgconn iff NULL was returned by
                                101                 :  * libpqsrv_connect*.
                                102                 :  */
                                103                 : static inline void
                                104             686 : libpqsrv_disconnect(PGconn *conn)
                                105                 : {
                                106                 :     /*
                                107                 :      * If no connection was established, we haven't reserved an FD for it (or
                                108                 :      * already released it). This rule makes it easier to write PG_CATCH()
                                109                 :      * handlers for this facility's users.
                                110                 :      *
                                111                 :      * See also libpqsrv_connect_internal().
                                112                 :      */
                                113             686 :     if (conn == NULL)
                                114               2 :         return;
                                115                 : 
                                116             684 :     ReleaseExternalFD();
                                117             684 :     PQfinish(conn);
                                118                 : }
                                119                 : 
                                120                 : 
                                121                 : /* internal helper functions follow */
                                122                 : 
                                123                 : 
                                124                 : /*
                                125                 :  * Helper function for all connection establishment functions.
                                126                 :  */
                                127                 : static inline void
                                128             689 : libpqsrv_connect_prepare(void)
                                129                 : {
                                130                 :     /*
                                131                 :      * We must obey fd.c's limit on non-virtual file descriptors.  Assume that
                                132                 :      * a PGconn represents one long-lived FD.  (Doing this here also ensures
                                133                 :      * that VFDs are closed if needed to make room.)
                                134                 :      */
                                135             689 :     if (!AcquireExternalFD())
                                136                 :     {
                                137                 : #ifndef WIN32                   /* can't write #if within ereport() macro */
   76 andres                    138 UNC           0 :         ereport(ERROR,
                                139                 :                 (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
                                140                 :                  errmsg("could not establish connection"),
                                141                 :                  errdetail("There are too many open files on the local server."),
                                142                 :                  errhint("Raise the server's max_files_per_process and/or \"ulimit -n\" limits.")));
                                143                 : #else
                                144                 :         ereport(ERROR,
                                145                 :                 (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
                                146                 :                  errmsg("could not establish connection"),
                                147                 :                  errdetail("There are too many open files on the local server."),
                                148                 :                  errhint("Raise the server's max_files_per_process setting.")));
                                149                 : #endif
                                150                 :     }
   76 andres                    151 GNC         689 : }
                                152                 : 
                                153                 : /*
                                154                 :  * Helper function for all connection establishment functions.
                                155                 :  */
                                156                 : static inline void
                                157             689 : libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
                                158                 : {
                                159                 :     /*
                                160                 :      * With conn == NULL libpqsrv_disconnect() wouldn't release the FD. So do
                                161                 :      * that here.
                                162                 :      */
                                163             689 :     if (conn == NULL)
                                164                 :     {
   76 andres                    165 UNC           0 :         ReleaseExternalFD();
                                166               0 :         return;
                                167                 :     }
                                168                 : 
                                169                 :     /*
                                170                 :      * Can't wait without a socket. Note that we don't want to close the libpq
                                171                 :      * connection yet, so callers can emit a useful error.
                                172                 :      */
   76 andres                    173 GNC         689 :     if (PQstatus(conn) == CONNECTION_BAD)
                                174              57 :         return;
                                175                 : 
                                176                 :     /*
                                177                 :      * WaitLatchOrSocket() can conceivably fail, handle that case here instead
                                178                 :      * of requiring all callers to do so.
                                179                 :      */
                                180             632 :     PG_TRY();
                                181                 :     {
                                182                 :         PostgresPollingStatusType status;
                                183                 : 
                                184                 :         /*
                                185                 :          * Poll connection until we have OK or FAILED status.
                                186                 :          *
                                187                 :          * Per spec for PQconnectPoll, first wait till socket is write-ready.
                                188                 :          */
                                189             632 :         status = PGRES_POLLING_WRITING;
                                190            2975 :         while (status != PGRES_POLLING_OK && status != PGRES_POLLING_FAILED)
                                191                 :         {
                                192                 :             int         io_flag;
                                193                 :             int         rc;
                                194                 : 
                                195            1712 :             if (status == PGRES_POLLING_READING)
                                196             634 :                 io_flag = WL_SOCKET_READABLE;
                                197                 : #ifdef WIN32
                                198                 : 
                                199                 :             /*
                                200                 :              * Windows needs a different test while waiting for
                                201                 :              * connection-made
                                202                 :              */
                                203                 :             else if (PQstatus(conn) == CONNECTION_STARTED)
                                204                 :                 io_flag = WL_SOCKET_CONNECTED;
                                205                 : #endif
                                206                 :             else
                                207            1078 :                 io_flag = WL_SOCKET_WRITEABLE;
                                208                 : 
                                209            1712 :             rc = WaitLatchOrSocket(MyLatch,
                                210                 :                                    WL_EXIT_ON_PM_DEATH | WL_LATCH_SET | io_flag,
                                211                 :                                    PQsocket(conn),
                                212                 :                                    0,
                                213                 :                                    wait_event_info);
                                214                 : 
                                215                 :             /* Interrupted? */
                                216            1712 :             if (rc & WL_LATCH_SET)
                                217                 :             {
                                218             449 :                 ResetLatch(MyLatch);
                                219             449 :                 CHECK_FOR_INTERRUPTS();
                                220                 :             }
                                221                 : 
                                222                 :             /* If socket is ready, advance the libpq state machine */
                                223            1711 :             if (rc & io_flag)
                                224            1263 :                 status = PQconnectPoll(conn);
                                225                 :         }
                                226                 :     }
   76 andres                    227 UNC           0 :     PG_CATCH();
                                228                 :     {
                                229                 :         /*
                                230                 :          * If an error is thrown here, the callers won't call
                                231                 :          * libpqsrv_disconnect() with a conn, so release resources
                                232                 :          * immediately.
                                233                 :          */
                                234               0 :         ReleaseExternalFD();
                                235               0 :         PQfinish(conn);
                                236                 : 
                                237               0 :         PG_RE_THROW();
                                238                 :     }
   76 andres                    239 GNC         631 :     PG_END_TRY();
                                240                 : }
                                241                 : 
                                242                 : #endif                          /* LIBPQ_BE_FE_HELPERS_H */
        

Generated by: LCOV version v1.16-55-g56c0a2a