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 15:15:32 Functions: 100.0 % 5 5 5
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           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 *
      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 */
     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                 :     }
     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                 :     {
     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                 :      */
     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                 :     }
     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                 :     }
     239 GNC         631 :     PG_END_TRY();
     240                 : }
     241                 : 
     242                 : #endif                          /* LIBPQ_BE_FE_HELPERS_H */
        

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