LCOV - code coverage report
Current view: top level - src/backend/access/common - printtup.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 98.0 % 148 145
Test Date: 2026-02-28 02:14:46 Functions: 100.0 % 11 11
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * printtup.c
       4              :  *    Routines to print out tuples to the destination (both frontend
       5              :  *    clients and standalone backends are supported here).
       6              :  *
       7              :  *
       8              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       9              :  * Portions Copyright (c) 1994, Regents of the University of California
      10              :  *
      11              :  * IDENTIFICATION
      12              :  *    src/backend/access/common/printtup.c
      13              :  *
      14              :  *-------------------------------------------------------------------------
      15              :  */
      16              : #include "postgres.h"
      17              : 
      18              : #include "access/printtup.h"
      19              : #include "libpq/pqformat.h"
      20              : #include "libpq/protocol.h"
      21              : #include "tcop/pquery.h"
      22              : #include "utils/lsyscache.h"
      23              : #include "utils/memdebug.h"
      24              : #include "utils/memutils.h"
      25              : #include "varatt.h"
      26              : 
      27              : 
      28              : static void printtup_startup(DestReceiver *self, int operation,
      29              :                              TupleDesc typeinfo);
      30              : static bool printtup(TupleTableSlot *slot, DestReceiver *self);
      31              : static void printtup_shutdown(DestReceiver *self);
      32              : static void printtup_destroy(DestReceiver *self);
      33              : 
      34              : /* ----------------------------------------------------------------
      35              :  *      printtup / debugtup support
      36              :  * ----------------------------------------------------------------
      37              :  */
      38              : 
      39              : /* ----------------
      40              :  *      Private state for a printtup destination object
      41              :  *
      42              :  * NOTE: finfo is the lookup info for either typoutput or typsend, whichever
      43              :  * we are using for this column.
      44              :  * ----------------
      45              :  */
      46              : typedef struct
      47              : {                               /* Per-attribute information */
      48              :     Oid         typoutput;      /* Oid for the type's text output fn */
      49              :     Oid         typsend;        /* Oid for the type's binary output fn */
      50              :     bool        typisvarlena;   /* is it varlena (ie possibly toastable)? */
      51              :     int16       format;         /* format code for this column */
      52              :     FmgrInfo    finfo;          /* Precomputed call info for output fn */
      53              : } PrinttupAttrInfo;
      54              : 
      55              : typedef struct
      56              : {
      57              :     DestReceiver pub;           /* publicly-known function pointers */
      58              :     Portal      portal;         /* the Portal we are printing from */
      59              :     bool        sendDescrip;    /* send RowDescription at startup? */
      60              :     TupleDesc   attrinfo;       /* The attr info we are set up for */
      61              :     int         nattrs;
      62              :     PrinttupAttrInfo *myinfo;   /* Cached info about each attr */
      63              :     StringInfoData buf;         /* output buffer (*not* in tmpcontext) */
      64              :     MemoryContext tmpcontext;   /* Memory context for per-row workspace */
      65              : } DR_printtup;
      66              : 
      67              : /* ----------------
      68              :  *      Initialize: create a DestReceiver for printtup
      69              :  * ----------------
      70              :  */
      71              : DestReceiver *
      72       336846 : printtup_create_DR(CommandDest dest)
      73              : {
      74       336846 :     DR_printtup *self = palloc0_object(DR_printtup);
      75              : 
      76       336846 :     self->pub.receiveSlot = printtup;    /* might get changed later */
      77       336846 :     self->pub.rStartup = printtup_startup;
      78       336846 :     self->pub.rShutdown = printtup_shutdown;
      79       336846 :     self->pub.rDestroy = printtup_destroy;
      80       336846 :     self->pub.mydest = dest;
      81              : 
      82              :     /*
      83              :      * Send T message automatically if DestRemote, but not if
      84              :      * DestRemoteExecute
      85              :      */
      86       336846 :     self->sendDescrip = (dest == DestRemote);
      87              : 
      88       336846 :     self->attrinfo = NULL;
      89       336846 :     self->nattrs = 0;
      90       336846 :     self->myinfo = NULL;
      91       336846 :     self->buf.data = NULL;
      92       336846 :     self->tmpcontext = NULL;
      93              : 
      94       336846 :     return (DestReceiver *) self;
      95              : }
      96              : 
      97              : /*
      98              :  * Set parameters for a DestRemote (or DestRemoteExecute) receiver
      99              :  */
     100              : void
     101       336846 : SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
     102              : {
     103       336846 :     DR_printtup *myState = (DR_printtup *) self;
     104              : 
     105              :     Assert(myState->pub.mydest == DestRemote ||
     106              :            myState->pub.mydest == DestRemoteExecute);
     107              : 
     108       336846 :     myState->portal = portal;
     109       336846 : }
     110              : 
     111              : static void
     112       164062 : printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
     113              : {
     114       164062 :     DR_printtup *myState = (DR_printtup *) self;
     115       164062 :     Portal      portal = myState->portal;
     116              : 
     117              :     /*
     118              :      * Create I/O buffer to be used for all messages.  This cannot be inside
     119              :      * tmpcontext, since we want to re-use it across rows.
     120              :      */
     121       164062 :     initStringInfo(&myState->buf);
     122              : 
     123              :     /*
     124              :      * Create a temporary memory context that we can reset once per row to
     125              :      * recover palloc'd memory.  This avoids any problems with leaks inside
     126              :      * datatype output routines, and should be faster than retail pfree's
     127              :      * anyway.
     128              :      */
     129       164062 :     myState->tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
     130              :                                                 "printtup",
     131              :                                                 ALLOCSET_DEFAULT_SIZES);
     132              : 
     133              :     /*
     134              :      * If we are supposed to emit row descriptions, then send the tuple
     135              :      * descriptor of the tuples.
     136              :      */
     137       164062 :     if (myState->sendDescrip)
     138       159084 :         SendRowDescriptionMessage(&myState->buf,
     139              :                                   typeinfo,
     140              :                                   FetchPortalTargetList(portal),
     141              :                                   portal->formats);
     142              : 
     143              :     /* ----------------
     144              :      * We could set up the derived attr info at this time, but we postpone it
     145              :      * until the first call of printtup, for 2 reasons:
     146              :      * 1. We don't waste time (compared to the old way) if there are no
     147              :      *    tuples at all to output.
     148              :      * 2. Checking in printtup allows us to handle the case that the tuples
     149              :      *    change type midway through (although this probably can't happen in
     150              :      *    the current executor).
     151              :      * ----------------
     152              :      */
     153       164062 : }
     154              : 
     155              : /*
     156              :  * SendRowDescriptionMessage --- send a RowDescription message to the frontend
     157              :  *
     158              :  * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
     159              :  * or some similar function; it does not contain a full set of fields.
     160              :  * The targetlist will be NIL when executing a utility function that does
     161              :  * not have a plan.  If the targetlist isn't NIL then it is a Query node's
     162              :  * targetlist; it is up to us to ignore resjunk columns in it.  The formats[]
     163              :  * array pointer might be NULL (if we are doing Describe on a prepared stmt);
     164              :  * send zeroes for the format codes in that case.
     165              :  */
     166              : void
     167       164129 : SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
     168              :                           List *targetlist, int16 *formats)
     169              : {
     170       164129 :     int         natts = typeinfo->natts;
     171              :     int         i;
     172       164129 :     ListCell   *tlist_item = list_head(targetlist);
     173              : 
     174              :     /* tuple descriptor message type */
     175       164129 :     pq_beginmessage_reuse(buf, PqMsg_RowDescription);
     176              :     /* # of attrs in tuples */
     177       164129 :     pq_sendint16(buf, natts);
     178              : 
     179              :     /*
     180              :      * Preallocate memory for the entire message to be sent. That allows to
     181              :      * use the significantly faster inline pqformat.h functions and to avoid
     182              :      * reallocations.
     183              :      *
     184              :      * Have to overestimate the size of the column-names, to account for
     185              :      * character set overhead.
     186              :      */
     187       164129 :     enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
     188              :                             + sizeof(Oid)   /* resorigtbl */
     189              :                             + sizeof(AttrNumber)    /* resorigcol */
     190              :                             + sizeof(Oid)   /* atttypid */
     191              :                             + sizeof(int16) /* attlen */
     192              :                             + sizeof(int32) /* attypmod */
     193              :                             + sizeof(int16) /* format */
     194              :                             ) * natts);
     195              : 
     196       697080 :     for (i = 0; i < natts; ++i)
     197              :     {
     198       532951 :         Form_pg_attribute att = TupleDescAttr(typeinfo, i);
     199       532951 :         Oid         atttypid = att->atttypid;
     200       532951 :         int32       atttypmod = att->atttypmod;
     201              :         Oid         resorigtbl;
     202              :         AttrNumber  resorigcol;
     203              :         int16       format;
     204              : 
     205              :         /*
     206              :          * If column is a domain, send the base type and typmod instead.
     207              :          * Lookup before sending any ints, for efficiency.
     208              :          */
     209       532951 :         atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
     210              : 
     211              :         /* Do we have a non-resjunk tlist item? */
     212       532951 :         while (tlist_item &&
     213       524020 :                ((TargetEntry *) lfirst(tlist_item))->resjunk)
     214            0 :             tlist_item = lnext(targetlist, tlist_item);
     215       532951 :         if (tlist_item)
     216              :         {
     217       524020 :             TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
     218              : 
     219       524020 :             resorigtbl = tle->resorigtbl;
     220       524020 :             resorigcol = tle->resorigcol;
     221       524020 :             tlist_item = lnext(targetlist, tlist_item);
     222              :         }
     223              :         else
     224              :         {
     225              :             /* No info available, so send zeroes */
     226         8931 :             resorigtbl = 0;
     227         8931 :             resorigcol = 0;
     228              :         }
     229              : 
     230       532951 :         if (formats)
     231       532737 :             format = formats[i];
     232              :         else
     233          214 :             format = 0;
     234              : 
     235       532951 :         pq_writestring(buf, NameStr(att->attname));
     236       532951 :         pq_writeint32(buf, resorigtbl);
     237       532951 :         pq_writeint16(buf, resorigcol);
     238       532951 :         pq_writeint32(buf, atttypid);
     239       532951 :         pq_writeint16(buf, att->attlen);
     240       532951 :         pq_writeint32(buf, atttypmod);
     241       532951 :         pq_writeint16(buf, format);
     242              :     }
     243              : 
     244       164129 :     pq_endmessage_reuse(buf);
     245       164129 : }
     246              : 
     247              : /*
     248              :  * Get the lookup info that printtup() needs
     249              :  */
     250              : static void
     251       134945 : printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
     252              : {
     253       134945 :     int16      *formats = myState->portal->formats;
     254              :     int         i;
     255              : 
     256              :     /* get rid of any old data */
     257       134945 :     if (myState->myinfo)
     258          838 :         pfree(myState->myinfo);
     259       134945 :     myState->myinfo = NULL;
     260              : 
     261       134945 :     myState->attrinfo = typeinfo;
     262       134945 :     myState->nattrs = numAttrs;
     263       134945 :     if (numAttrs <= 0)
     264          169 :         return;
     265              : 
     266       134776 :     myState->myinfo = (PrinttupAttrInfo *)
     267       134776 :         palloc0(numAttrs * sizeof(PrinttupAttrInfo));
     268              : 
     269       545060 :     for (i = 0; i < numAttrs; i++)
     270              :     {
     271       410284 :         PrinttupAttrInfo *thisState = myState->myinfo + i;
     272       410284 :         int16       format = (formats ? formats[i] : 0);
     273       410284 :         Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
     274              : 
     275       410284 :         thisState->format = format;
     276       410284 :         if (format == 0)
     277              :         {
     278       410241 :             getTypeOutputInfo(attr->atttypid,
     279              :                               &thisState->typoutput,
     280              :                               &thisState->typisvarlena);
     281       410241 :             fmgr_info(thisState->typoutput, &thisState->finfo);
     282              :         }
     283           43 :         else if (format == 1)
     284              :         {
     285           43 :             getTypeBinaryOutputInfo(attr->atttypid,
     286              :                                     &thisState->typsend,
     287              :                                     &thisState->typisvarlena);
     288           43 :             fmgr_info(thisState->typsend, &thisState->finfo);
     289              :         }
     290              :         else
     291            0 :             ereport(ERROR,
     292              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     293              :                      errmsg("unsupported format code: %d", format)));
     294              :     }
     295              : }
     296              : 
     297              : /* ----------------
     298              :  *      printtup --- send a tuple to the client
     299              :  *
     300              :  * Note: if you change this function, see also serializeAnalyzeReceive
     301              :  * in explain.c, which is meant to replicate the computations done here.
     302              :  * ----------------
     303              :  */
     304              : static bool
     305      4208434 : printtup(TupleTableSlot *slot, DestReceiver *self)
     306              : {
     307      4208434 :     TupleDesc   typeinfo = slot->tts_tupleDescriptor;
     308      4208434 :     DR_printtup *myState = (DR_printtup *) self;
     309              :     MemoryContext oldcontext;
     310      4208434 :     StringInfo  buf = &myState->buf;
     311      4208434 :     int         natts = typeinfo->natts;
     312              :     int         i;
     313              : 
     314              :     /* Set or update my derived attribute info, if needed */
     315      4208434 :     if (myState->attrinfo != typeinfo || myState->nattrs != natts)
     316       134945 :         printtup_prepare_info(myState, typeinfo, natts);
     317              : 
     318              :     /* Make sure the tuple is fully deconstructed */
     319      4208434 :     slot_getallattrs(slot);
     320              : 
     321              :     /* Switch into per-row context so we can recover memory below */
     322      4208434 :     oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
     323              : 
     324              :     /*
     325              :      * Prepare a DataRow message (note buffer is in per-query context)
     326              :      */
     327      4208434 :     pq_beginmessage_reuse(buf, PqMsg_DataRow);
     328              : 
     329      4208434 :     pq_sendint16(buf, natts);
     330              : 
     331              :     /*
     332              :      * send the attributes of this tuple
     333              :      */
     334     26013226 :     for (i = 0; i < natts; ++i)
     335              :     {
     336     21804798 :         PrinttupAttrInfo *thisState = myState->myinfo + i;
     337     21804798 :         Datum       attr = slot->tts_values[i];
     338              : 
     339     21804798 :         if (slot->tts_isnull[i])
     340              :         {
     341      1180285 :             pq_sendint32(buf, -1);
     342      1180285 :             continue;
     343              :         }
     344              : 
     345              :         /*
     346              :          * Here we catch undefined bytes in datums that are returned to the
     347              :          * client without hitting disk; see comments at the related check in
     348              :          * PageAddItem().  This test is most useful for uncompressed,
     349              :          * non-external datums, but we're quite likely to see such here when
     350              :          * testing new C functions.
     351              :          */
     352     20624513 :         if (thisState->typisvarlena)
     353              :             VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr),
     354              :                                           VARSIZE_ANY(DatumGetPointer(attr)));
     355              : 
     356     20624513 :         if (thisState->format == 0)
     357              :         {
     358              :             /* Text output */
     359              :             char       *outputstr;
     360              : 
     361     20617441 :             outputstr = OutputFunctionCall(&thisState->finfo, attr);
     362     20617435 :             pq_sendcountedtext(buf, outputstr, strlen(outputstr));
     363              :         }
     364              :         else
     365              :         {
     366              :             /* Binary output */
     367              :             bytea      *outputbytes;
     368              : 
     369         7072 :             outputbytes = SendFunctionCall(&thisState->finfo, attr);
     370         7072 :             pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
     371         7072 :             pq_sendbytes(buf, VARDATA(outputbytes),
     372         7072 :                          VARSIZE(outputbytes) - VARHDRSZ);
     373              :         }
     374              :     }
     375              : 
     376      4208428 :     pq_endmessage_reuse(buf);
     377              : 
     378              :     /* Return to caller's context, and flush row's temporary memory */
     379      4208428 :     MemoryContextSwitchTo(oldcontext);
     380      4208428 :     MemoryContextReset(myState->tmpcontext);
     381              : 
     382      4208428 :     return true;
     383              : }
     384              : 
     385              : /* ----------------
     386              :  *      printtup_shutdown
     387              :  * ----------------
     388              :  */
     389              : static void
     390       160269 : printtup_shutdown(DestReceiver *self)
     391              : {
     392       160269 :     DR_printtup *myState = (DR_printtup *) self;
     393              : 
     394       160269 :     if (myState->myinfo)
     395       133834 :         pfree(myState->myinfo);
     396       160269 :     myState->myinfo = NULL;
     397              : 
     398       160269 :     myState->attrinfo = NULL;
     399              : 
     400       160269 :     if (myState->buf.data)
     401       160269 :         pfree(myState->buf.data);
     402       160269 :     myState->buf.data = NULL;
     403              : 
     404       160269 :     if (myState->tmpcontext)
     405       160269 :         MemoryContextDelete(myState->tmpcontext);
     406       160269 :     myState->tmpcontext = NULL;
     407       160269 : }
     408              : 
     409              : /* ----------------
     410              :  *      printtup_destroy
     411              :  * ----------------
     412              :  */
     413              : static void
     414       322190 : printtup_destroy(DestReceiver *self)
     415              : {
     416       322190 :     pfree(self);
     417       322190 : }
     418              : 
     419              : /* ----------------
     420              :  *      printatt
     421              :  * ----------------
     422              :  */
     423              : static void
     424          218 : printatt(unsigned attributeId,
     425              :          Form_pg_attribute attributeP,
     426              :          char *value)
     427              : {
     428          218 :     printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
     429              :            attributeId,
     430              :            NameStr(attributeP->attname),
     431              :            value != NULL ? " = \"" : "",
     432              :            value != NULL ? value : "",
     433              :            value != NULL ? "\"" : "",
     434              :            attributeP->atttypid,
     435              :            attributeP->attlen,
     436              :            attributeP->atttypmod,
     437              :            attributeP->attbyval ? 't' : 'f');
     438          218 : }
     439              : 
     440              : /* ----------------
     441              :  *      debugStartup - prepare to print tuples for an interactive backend
     442              :  * ----------------
     443              :  */
     444              : void
     445          108 : debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
     446              : {
     447          108 :     int         natts = typeinfo->natts;
     448              :     int         i;
     449              : 
     450              :     /*
     451              :      * show the return type of the tuples
     452              :      */
     453          216 :     for (i = 0; i < natts; ++i)
     454          108 :         printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), NULL);
     455          108 :     printf("\t----\n");
     456          108 : }
     457              : 
     458              : /* ----------------
     459              :  *      debugtup - print one tuple for an interactive backend
     460              :  * ----------------
     461              :  */
     462              : bool
     463          110 : debugtup(TupleTableSlot *slot, DestReceiver *self)
     464              : {
     465          110 :     TupleDesc   typeinfo = slot->tts_tupleDescriptor;
     466          110 :     int         natts = typeinfo->natts;
     467              :     int         i;
     468              :     Datum       attr;
     469              :     char       *value;
     470              :     bool        isnull;
     471              :     Oid         typoutput;
     472              :     bool        typisvarlena;
     473              : 
     474          220 :     for (i = 0; i < natts; ++i)
     475              :     {
     476          110 :         attr = slot_getattr(slot, i + 1, &isnull);
     477          110 :         if (isnull)
     478            0 :             continue;
     479          110 :         getTypeOutputInfo(TupleDescAttr(typeinfo, i)->atttypid,
     480              :                           &typoutput, &typisvarlena);
     481              : 
     482          110 :         value = OidOutputFunctionCall(typoutput, attr);
     483              : 
     484          110 :         printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), value);
     485              :     }
     486          110 :     printf("\t----\n");
     487              : 
     488          110 :     return true;
     489              : }
        

Generated by: LCOV version 2.0-1