LCOV - code coverage report
Current view: top level - src/backend/access/common - printtup.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 145 148 98.0 %
Date: 2025-10-10 18:17:38 Functions: 11 11 100.0 %
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-2025, 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      627138 : printtup_create_DR(CommandDest dest)
      73             : {
      74      627138 :     DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup));
      75             : 
      76      627138 :     self->pub.receiveSlot = printtup;    /* might get changed later */
      77      627138 :     self->pub.rStartup = printtup_startup;
      78      627138 :     self->pub.rShutdown = printtup_shutdown;
      79      627138 :     self->pub.rDestroy = printtup_destroy;
      80      627138 :     self->pub.mydest = dest;
      81             : 
      82             :     /*
      83             :      * Send T message automatically if DestRemote, but not if
      84             :      * DestRemoteExecute
      85             :      */
      86      627138 :     self->sendDescrip = (dest == DestRemote);
      87             : 
      88      627138 :     self->attrinfo = NULL;
      89      627138 :     self->nattrs = 0;
      90      627138 :     self->myinfo = NULL;
      91      627138 :     self->buf.data = NULL;
      92      627138 :     self->tmpcontext = NULL;
      93             : 
      94      627138 :     return (DestReceiver *) self;
      95             : }
      96             : 
      97             : /*
      98             :  * Set parameters for a DestRemote (or DestRemoteExecute) receiver
      99             :  */
     100             : void
     101      627138 : SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
     102             : {
     103      627138 :     DR_printtup *myState = (DR_printtup *) self;
     104             : 
     105             :     Assert(myState->pub.mydest == DestRemote ||
     106             :            myState->pub.mydest == DestRemoteExecute);
     107             : 
     108      627138 :     myState->portal = portal;
     109      627138 : }
     110             : 
     111             : static void
     112      306932 : printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
     113             : {
     114      306932 :     DR_printtup *myState = (DR_printtup *) self;
     115      306932 :     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      306932 :     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      306932 :     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      306932 :     if (myState->sendDescrip)
     138      297804 :         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      306932 : }
     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      307066 : SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
     168             :                           List *targetlist, int16 *formats)
     169             : {
     170      307066 :     int         natts = typeinfo->natts;
     171             :     int         i;
     172      307066 :     ListCell   *tlist_item = list_head(targetlist);
     173             : 
     174             :     /* tuple descriptor message type */
     175      307066 :     pq_beginmessage_reuse(buf, PqMsg_RowDescription);
     176             :     /* # of attrs in tuples */
     177      307066 :     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      307066 :     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     1292570 :     for (i = 0; i < natts; ++i)
     197             :     {
     198      985504 :         Form_pg_attribute att = TupleDescAttr(typeinfo, i);
     199      985504 :         Oid         atttypid = att->atttypid;
     200      985504 :         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      985504 :         atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
     210             : 
     211             :         /* Do we have a non-resjunk tlist item? */
     212      985504 :         while (tlist_item &&
     213      968378 :                ((TargetEntry *) lfirst(tlist_item))->resjunk)
     214           0 :             tlist_item = lnext(targetlist, tlist_item);
     215      985504 :         if (tlist_item)
     216             :         {
     217      968378 :             TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
     218             : 
     219      968378 :             resorigtbl = tle->resorigtbl;
     220      968378 :             resorigcol = tle->resorigcol;
     221      968378 :             tlist_item = lnext(targetlist, tlist_item);
     222             :         }
     223             :         else
     224             :         {
     225             :             /* No info available, so send zeroes */
     226       17126 :             resorigtbl = 0;
     227       17126 :             resorigcol = 0;
     228             :         }
     229             : 
     230      985504 :         if (formats)
     231      985076 :             format = formats[i];
     232             :         else
     233         428 :             format = 0;
     234             : 
     235      985504 :         pq_writestring(buf, NameStr(att->attname));
     236      985504 :         pq_writeint32(buf, resorigtbl);
     237      985504 :         pq_writeint16(buf, resorigcol);
     238      985504 :         pq_writeint32(buf, atttypid);
     239      985504 :         pq_writeint16(buf, att->attlen);
     240      985504 :         pq_writeint32(buf, atttypmod);
     241      985504 :         pq_writeint16(buf, format);
     242             :     }
     243             : 
     244      307066 :     pq_endmessage_reuse(buf);
     245      307066 : }
     246             : 
     247             : /*
     248             :  * Get the lookup info that printtup() needs
     249             :  */
     250             : static void
     251      253774 : printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
     252             : {
     253      253774 :     int16      *formats = myState->portal->formats;
     254             :     int         i;
     255             : 
     256             :     /* get rid of any old data */
     257      253774 :     if (myState->myinfo)
     258        1624 :         pfree(myState->myinfo);
     259      253774 :     myState->myinfo = NULL;
     260             : 
     261      253774 :     myState->attrinfo = typeinfo;
     262      253774 :     myState->nattrs = numAttrs;
     263      253774 :     if (numAttrs <= 0)
     264         322 :         return;
     265             : 
     266      253452 :     myState->myinfo = (PrinttupAttrInfo *)
     267      253452 :         palloc0(numAttrs * sizeof(PrinttupAttrInfo));
     268             : 
     269     1024800 :     for (i = 0; i < numAttrs; i++)
     270             :     {
     271      771348 :         PrinttupAttrInfo *thisState = myState->myinfo + i;
     272      771348 :         int16       format = (formats ? formats[i] : 0);
     273      771348 :         Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
     274             : 
     275      771348 :         thisState->format = format;
     276      771348 :         if (format == 0)
     277             :         {
     278      771262 :             getTypeOutputInfo(attr->atttypid,
     279             :                               &thisState->typoutput,
     280             :                               &thisState->typisvarlena);
     281      771262 :             fmgr_info(thisState->typoutput, &thisState->finfo);
     282             :         }
     283          86 :         else if (format == 1)
     284             :         {
     285          86 :             getTypeBinaryOutputInfo(attr->atttypid,
     286             :                                     &thisState->typsend,
     287             :                                     &thisState->typisvarlena);
     288          86 :             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     6907596 : printtup(TupleTableSlot *slot, DestReceiver *self)
     306             : {
     307     6907596 :     TupleDesc   typeinfo = slot->tts_tupleDescriptor;
     308     6907596 :     DR_printtup *myState = (DR_printtup *) self;
     309             :     MemoryContext oldcontext;
     310     6907596 :     StringInfo  buf = &myState->buf;
     311     6907596 :     int         natts = typeinfo->natts;
     312             :     int         i;
     313             : 
     314             :     /* Set or update my derived attribute info, if needed */
     315     6907596 :     if (myState->attrinfo != typeinfo || myState->nattrs != natts)
     316      253774 :         printtup_prepare_info(myState, typeinfo, natts);
     317             : 
     318             :     /* Make sure the tuple is fully deconstructed */
     319     6907596 :     slot_getallattrs(slot);
     320             : 
     321             :     /* Switch into per-row context so we can recover memory below */
     322     6907596 :     oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
     323             : 
     324             :     /*
     325             :      * Prepare a DataRow message (note buffer is in per-query context)
     326             :      */
     327     6907596 :     pq_beginmessage_reuse(buf, PqMsg_DataRow);
     328             : 
     329     6907596 :     pq_sendint16(buf, natts);
     330             : 
     331             :     /*
     332             :      * send the attributes of this tuple
     333             :      */
     334    41369090 :     for (i = 0; i < natts; ++i)
     335             :     {
     336    34461494 :         PrinttupAttrInfo *thisState = myState->myinfo + i;
     337    34461494 :         Datum       attr = slot->tts_values[i];
     338             : 
     339    34461494 :         if (slot->tts_isnull[i])
     340             :         {
     341     1998942 :             pq_sendint32(buf, -1);
     342     1998942 :             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    32462552 :         if (thisState->typisvarlena)
     353             :             VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr),
     354             :                                           VARSIZE_ANY(DatumGetPointer(attr)));
     355             : 
     356    32462552 :         if (thisState->format == 0)
     357             :         {
     358             :             /* Text output */
     359             :             char       *outputstr;
     360             : 
     361    32447832 :             outputstr = OutputFunctionCall(&thisState->finfo, attr);
     362    32447832 :             pq_sendcountedtext(buf, outputstr, strlen(outputstr));
     363             :         }
     364             :         else
     365             :         {
     366             :             /* Binary output */
     367             :             bytea      *outputbytes;
     368             : 
     369       14720 :             outputbytes = SendFunctionCall(&thisState->finfo, attr);
     370       14720 :             pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
     371       14720 :             pq_sendbytes(buf, VARDATA(outputbytes),
     372       14720 :                          VARSIZE(outputbytes) - VARHDRSZ);
     373             :         }
     374             :     }
     375             : 
     376     6907596 :     pq_endmessage_reuse(buf);
     377             : 
     378             :     /* Return to caller's context, and flush row's temporary memory */
     379     6907596 :     MemoryContextSwitchTo(oldcontext);
     380     6907596 :     MemoryContextReset(myState->tmpcontext);
     381             : 
     382     6907596 :     return true;
     383             : }
     384             : 
     385             : /* ----------------
     386             :  *      printtup_shutdown
     387             :  * ----------------
     388             :  */
     389             : static void
     390      299574 : printtup_shutdown(DestReceiver *self)
     391             : {
     392      299574 :     DR_printtup *myState = (DR_printtup *) self;
     393             : 
     394      299574 :     if (myState->myinfo)
     395      251632 :         pfree(myState->myinfo);
     396      299574 :     myState->myinfo = NULL;
     397             : 
     398      299574 :     myState->attrinfo = NULL;
     399             : 
     400      299574 :     if (myState->buf.data)
     401      299574 :         pfree(myState->buf.data);
     402      299574 :     myState->buf.data = NULL;
     403             : 
     404      299574 :     if (myState->tmpcontext)
     405      299574 :         MemoryContextDelete(myState->tmpcontext);
     406      299574 :     myState->tmpcontext = NULL;
     407      299574 : }
     408             : 
     409             : /* ----------------
     410             :  *      printtup_destroy
     411             :  * ----------------
     412             :  */
     413             : static void
     414      598758 : printtup_destroy(DestReceiver *self)
     415             : {
     416      598758 :     pfree(self);
     417      598758 : }
     418             : 
     419             : /* ----------------
     420             :  *      printatt
     421             :  * ----------------
     422             :  */
     423             : static void
     424         428 : printatt(unsigned attributeId,
     425             :          Form_pg_attribute attributeP,
     426             :          char *value)
     427             : {
     428         428 :     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             :            (unsigned int) (attributeP->atttypid),
     435             :            attributeP->attlen,
     436             :            attributeP->atttypmod,
     437             :            attributeP->attbyval ? 't' : 'f');
     438         428 : }
     439             : 
     440             : /* ----------------
     441             :  *      debugStartup - prepare to print tuples for an interactive backend
     442             :  * ----------------
     443             :  */
     444             : void
     445         212 : debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
     446             : {
     447         212 :     int         natts = typeinfo->natts;
     448             :     int         i;
     449             : 
     450             :     /*
     451             :      * show the return type of the tuples
     452             :      */
     453         424 :     for (i = 0; i < natts; ++i)
     454         212 :         printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), NULL);
     455         212 :     printf("\t----\n");
     456         212 : }
     457             : 
     458             : /* ----------------
     459             :  *      debugtup - print one tuple for an interactive backend
     460             :  * ----------------
     461             :  */
     462             : bool
     463         216 : debugtup(TupleTableSlot *slot, DestReceiver *self)
     464             : {
     465         216 :     TupleDesc   typeinfo = slot->tts_tupleDescriptor;
     466         216 :     int         natts = typeinfo->natts;
     467             :     int         i;
     468             :     Datum       attr;
     469             :     char       *value;
     470             :     bool        isnull;
     471             :     Oid         typoutput;
     472             :     bool        typisvarlena;
     473             : 
     474         432 :     for (i = 0; i < natts; ++i)
     475             :     {
     476         216 :         attr = slot_getattr(slot, i + 1, &isnull);
     477         216 :         if (isnull)
     478           0 :             continue;
     479         216 :         getTypeOutputInfo(TupleDescAttr(typeinfo, i)->atttypid,
     480             :                           &typoutput, &typisvarlena);
     481             : 
     482         216 :         value = OidOutputFunctionCall(typoutput, attr);
     483             : 
     484         216 :         printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), value);
     485             :     }
     486         216 :     printf("\t----\n");
     487             : 
     488         216 :     return true;
     489             : }

Generated by: LCOV version 1.16