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

Generated by: LCOV version 1.14