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

Generated by: LCOV version 1.14