LCOV - code coverage report
Current view: top level - src/include/libpq - libpq-be-fe.h (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 82.4 % 74 61
Test Date: 2026-03-03 10:15:07 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * libpq-be-fe.h
       4              :  *    Wrapper 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. One of the
       8              :  * main risks in doing so is leaking the malloc-allocated structures
       9              :  * returned by libpq, causing a process-lifespan memory leak.
      10              :  *
      11              :  * This file provides wrapper objects to help in building memory-safe code.
      12              :  * A PGresult object wrapped this way acts much as if it were palloc'd:
      13              :  * it will go away when the specified context is reset or deleted.
      14              :  * We might later extend the concept to other objects such as PGconns.
      15              :  *
      16              :  * See also the libpq-be-fe-helpers.h file, which provides additional
      17              :  * facilities built on top of this one.
      18              :  *
      19              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      20              :  * Portions Copyright (c) 1994, Regents of the University of California
      21              :  *
      22              :  * src/include/libpq/libpq-be-fe.h
      23              :  *
      24              :  *-------------------------------------------------------------------------
      25              :  */
      26              : #ifndef LIBPQ_BE_FE_H
      27              : #define LIBPQ_BE_FE_H
      28              : 
      29              : /*
      30              :  * Despite the name, BUILDING_DLL is set only when building code directly part
      31              :  * of the backend. Which also is where libpq isn't allowed to be
      32              :  * used. Obviously this doesn't protect against libpq-fe.h getting included
      33              :  * otherwise, but perhaps still protects against a few mistakes...
      34              :  */
      35              : #ifdef BUILDING_DLL
      36              : #error "libpq may not be used in code directly built into the backend"
      37              : #endif
      38              : 
      39              : #include "libpq-fe.h"
      40              : 
      41              : /*
      42              :  * Memory-context-safe wrapper object for a PGresult.
      43              :  */
      44              : typedef struct libpqsrv_PGresult
      45              : {
      46              :     PGresult   *res;            /* the wrapped PGresult */
      47              :     MemoryContext ctx;          /* the MemoryContext it's attached to */
      48              :     MemoryContextCallback cb;   /* the callback that implements freeing */
      49              : } libpqsrv_PGresult;
      50              : 
      51              : 
      52              : /*
      53              :  * Wrap the given PGresult in a libpqsrv_PGresult object, so that it will
      54              :  * go away automatically if the current memory context is reset or deleted.
      55              :  *
      56              :  * To avoid potential memory leaks, backend code must always apply this
      57              :  * immediately to the output of any PGresult-yielding libpq function.
      58              :  */
      59              : static inline libpqsrv_PGresult *
      60        25458 : libpqsrv_PQwrap(PGresult *res)
      61              : {
      62              :     libpqsrv_PGresult *bres;
      63        25458 :     MemoryContext ctx = CurrentMemoryContext;
      64              : 
      65              :     /* We pass through a NULL result as-is, since there's nothing to free */
      66        25458 :     if (res == NULL)
      67        12113 :         return NULL;
      68              :     /* Attempt to allocate the wrapper ... this had better not throw error */
      69              :     bres = (libpqsrv_PGresult *)
      70        13345 :         MemoryContextAllocExtended(ctx,
      71              :                                    sizeof(libpqsrv_PGresult),
      72              :                                    MCXT_ALLOC_NO_OOM);
      73              :     /* If we failed to allocate a wrapper, free the PGresult before failing */
      74        13345 :     if (bres == NULL)
      75              :     {
      76            0 :         PQclear(res);
      77            0 :         ereport(ERROR,
      78              :                 (errcode(ERRCODE_OUT_OF_MEMORY),
      79              :                  errmsg("out of memory")));
      80              :     }
      81              :     /* Okay, set up the wrapper */
      82        13345 :     bres->res = res;
      83        13345 :     bres->ctx = ctx;
      84        13345 :     bres->cb.func = (MemoryContextCallbackFunction) PQclear;
      85        13345 :     bres->cb.arg = res;
      86        13345 :     MemoryContextRegisterResetCallback(ctx, &bres->cb);
      87        13345 :     return bres;
      88              : }
      89              : 
      90              : /*
      91              :  * Free a wrapped PGresult, after detaching it from the memory context.
      92              :  * Like PQclear(), allow the argument to be NULL.
      93              :  */
      94              : static inline void
      95        25850 : libpqsrv_PQclear(libpqsrv_PGresult *bres)
      96              : {
      97        25850 :     if (bres)
      98              :     {
      99        13311 :         MemoryContextUnregisterResetCallback(bres->ctx, &bres->cb);
     100        13311 :         PQclear(bres->res);
     101        13311 :         pfree(bres);
     102              :     }
     103        25850 : }
     104              : 
     105              : /*
     106              :  * Move a wrapped PGresult to have a different parent context.
     107              :  */
     108              : static inline libpqsrv_PGresult *
     109           67 : libpqsrv_PGresultSetParent(libpqsrv_PGresult *bres, MemoryContext ctx)
     110              : {
     111              :     libpqsrv_PGresult *newres;
     112              : 
     113              :     /* We pass through a NULL result as-is */
     114           67 :     if (bres == NULL)
     115            0 :         return NULL;
     116              :     /* Make a new wrapper in the target context, raising error on OOM */
     117              :     newres = (libpqsrv_PGresult *)
     118           67 :         MemoryContextAlloc(ctx, sizeof(libpqsrv_PGresult));
     119              :     /* Okay, set up the new wrapper */
     120           67 :     newres->res = bres->res;
     121           67 :     newres->ctx = ctx;
     122           67 :     newres->cb.func = (MemoryContextCallbackFunction) PQclear;
     123           67 :     newres->cb.arg = bres->res;
     124           67 :     MemoryContextRegisterResetCallback(ctx, &newres->cb);
     125              :     /* Disarm and delete the old wrapper */
     126           67 :     MemoryContextUnregisterResetCallback(bres->ctx, &bres->cb);
     127           67 :     pfree(bres);
     128           67 :     return newres;
     129              : }
     130              : 
     131              : /*
     132              :  * Convenience wrapper for PQgetResult.
     133              :  *
     134              :  * We could supply wrappers for other PGresult-returning functions too,
     135              :  * but at present there's no need.
     136              :  */
     137              : static inline libpqsrv_PGresult *
     138        25458 : libpqsrv_PQgetResult(PGconn *conn)
     139              : {
     140        25458 :     return libpqsrv_PQwrap(PQgetResult(conn));
     141              : }
     142              : 
     143              : /*
     144              :  * Accessor functions for libpqsrv_PGresult.  While it's not necessary to use
     145              :  * these, they emulate the behavior of the underlying libpq functions when
     146              :  * passed a NULL pointer.  This is particularly important for PQresultStatus,
     147              :  * which is often the first check on a result.
     148              :  */
     149              : 
     150              : static inline ExecStatusType
     151        51164 : libpqsrv_PQresultStatus(const libpqsrv_PGresult *res)
     152              : {
     153        51164 :     if (!res)
     154            0 :         return PGRES_FATAL_ERROR;
     155        51164 :     return PQresultStatus(res->res);
     156              : }
     157              : 
     158              : static inline const char *
     159              : libpqsrv_PQresultErrorMessage(const libpqsrv_PGresult *res)
     160              : {
     161              :     if (!res)
     162              :         return "";
     163              :     return PQresultErrorMessage(res->res);
     164              : }
     165              : 
     166              : static inline char *
     167          143 : libpqsrv_PQresultErrorField(const libpqsrv_PGresult *res, int fieldcode)
     168              : {
     169          143 :     if (!res)
     170            0 :         return NULL;
     171          143 :     return PQresultErrorField(res->res, fieldcode);
     172              : }
     173              : 
     174              : static inline char *
     175           24 : libpqsrv_PQcmdStatus(const libpqsrv_PGresult *res)
     176              : {
     177           24 :     if (!res)
     178            0 :         return NULL;
     179           24 :     return PQcmdStatus(res->res);
     180              : }
     181              : 
     182              : static inline int
     183         6276 : libpqsrv_PQntuples(const libpqsrv_PGresult *res)
     184              : {
     185         6276 :     if (!res)
     186            0 :         return 0;
     187         6276 :     return PQntuples(res->res);
     188              : }
     189              : 
     190              : static inline int
     191        95288 : libpqsrv_PQnfields(const libpqsrv_PGresult *res)
     192              : {
     193        95288 :     if (!res)
     194            0 :         return 0;
     195        95288 :     return PQnfields(res->res);
     196              : }
     197              : 
     198              : static inline char *
     199       256501 : libpqsrv_PQgetvalue(const libpqsrv_PGresult *res, int tup_num, int field_num)
     200              : {
     201       256501 :     if (!res)
     202            0 :         return NULL;
     203       256501 :     return PQgetvalue(res->res, tup_num, field_num);
     204              : }
     205              : 
     206              : static inline int
     207           11 : libpqsrv_PQgetlength(const libpqsrv_PGresult *res, int tup_num, int field_num)
     208              : {
     209           11 :     if (!res)
     210            0 :         return 0;
     211           11 :     return PQgetlength(res->res, tup_num, field_num);
     212              : }
     213              : 
     214              : static inline int
     215       265647 : libpqsrv_PQgetisnull(const libpqsrv_PGresult *res, int tup_num, int field_num)
     216              : {
     217       265647 :     if (!res)
     218            0 :         return 1;               /* pretend it is null */
     219       265647 :     return PQgetisnull(res->res, tup_num, field_num);
     220              : }
     221              : 
     222              : static inline char *
     223         3211 : libpqsrv_PQfname(const libpqsrv_PGresult *res, int field_num)
     224              : {
     225         3211 :     if (!res)
     226            0 :         return NULL;
     227         3211 :     return PQfname(res->res, field_num);
     228              : }
     229              : 
     230              : static inline const char *
     231          992 : libpqsrv_PQcmdTuples(const libpqsrv_PGresult *res)
     232              : {
     233          992 :     if (!res)
     234            0 :         return "";
     235          992 :     return PQcmdTuples(res->res);
     236              : }
     237              : 
     238              : /*
     239              :  * Redefine these libpq entry point names concerned with PGresults so that
     240              :  * they will operate on libpqsrv_PGresults instead.  This avoids needing to
     241              :  * convert a lot of pre-existing code, and reduces the notational differences
     242              :  * between frontend and backend libpq-using code.
     243              :  */
     244              : #define PGresult libpqsrv_PGresult
     245              : #define PQclear libpqsrv_PQclear
     246              : #define PQgetResult libpqsrv_PQgetResult
     247              : #define PQresultStatus libpqsrv_PQresultStatus
     248              : #define PQresultErrorMessage libpqsrv_PQresultErrorMessage
     249              : #define PQresultErrorField libpqsrv_PQresultErrorField
     250              : #define PQcmdStatus libpqsrv_PQcmdStatus
     251              : #define PQntuples libpqsrv_PQntuples
     252              : #define PQnfields libpqsrv_PQnfields
     253              : #define PQgetvalue libpqsrv_PQgetvalue
     254              : #define PQgetlength libpqsrv_PQgetlength
     255              : #define PQgetisnull libpqsrv_PQgetisnull
     256              : #define PQfname libpqsrv_PQfname
     257              : #define PQcmdTuples libpqsrv_PQcmdTuples
     258              : 
     259              : #endif                          /* LIBPQ_BE_FE_H */
        

Generated by: LCOV version 2.0-1