LCOV - code coverage report
Current view: top level - src/backend/access/common - printtup.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 145 148 98.0 %
Date: 2023-06-06 09:15:10 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-2023, 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/libpq.h"
      20             : #include "libpq/pqformat.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      504300 : printtup_create_DR(CommandDest dest)
      72             : {
      73      504300 :     DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup));
      74             : 
      75      504300 :     self->pub.receiveSlot = printtup;    /* might get changed later */
      76      504300 :     self->pub.rStartup = printtup_startup;
      77      504300 :     self->pub.rShutdown = printtup_shutdown;
      78      504300 :     self->pub.rDestroy = printtup_destroy;
      79      504300 :     self->pub.mydest = dest;
      80             : 
      81             :     /*
      82             :      * Send T message automatically if DestRemote, but not if
      83             :      * DestRemoteExecute
      84             :      */
      85      504300 :     self->sendDescrip = (dest == DestRemote);
      86             : 
      87      504300 :     self->attrinfo = NULL;
      88      504300 :     self->nattrs = 0;
      89      504300 :     self->myinfo = NULL;
      90      504300 :     self->buf.data = NULL;
      91      504300 :     self->tmpcontext = NULL;
      92             : 
      93      504300 :     return (DestReceiver *) self;
      94             : }
      95             : 
      96             : /*
      97             :  * Set parameters for a DestRemote (or DestRemoteExecute) receiver
      98             :  */
      99             : void
     100      504300 : SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
     101             : {
     102      504300 :     DR_printtup *myState = (DR_printtup *) self;
     103             : 
     104             :     Assert(myState->pub.mydest == DestRemote ||
     105             :            myState->pub.mydest == DestRemoteExecute);
     106             : 
     107      504300 :     myState->portal = portal;
     108      504300 : }
     109             : 
     110             : static void
     111      233164 : printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
     112             : {
     113      233164 :     DR_printtup *myState = (DR_printtup *) self;
     114      233164 :     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      233164 :     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      233164 :     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      233164 :     if (myState->sendDescrip)
     137      221810 :         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      233164 : }
     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      233298 : SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
     167             :                           List *targetlist, int16 *formats)
     168             : {
     169      233298 :     int         natts = typeinfo->natts;
     170             :     int         i;
     171      233298 :     ListCell   *tlist_item = list_head(targetlist);
     172             : 
     173             :     /* tuple descriptor message type */
     174      233298 :     pq_beginmessage_reuse(buf, 'T');
     175             :     /* # of attrs in tuples */
     176      233298 :     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      233298 :     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      893576 :     for (i = 0; i < natts; ++i)
     196             :     {
     197      660278 :         Form_pg_attribute att = TupleDescAttr(typeinfo, i);
     198      660278 :         Oid         atttypid = att->atttypid;
     199      660278 :         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      660278 :         atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
     209             : 
     210             :         /* Do we have a non-resjunk tlist item? */
     211      660278 :         while (tlist_item &&
     212      647460 :                ((TargetEntry *) lfirst(tlist_item))->resjunk)
     213           0 :             tlist_item = lnext(targetlist, tlist_item);
     214      660278 :         if (tlist_item)
     215             :         {
     216      647460 :             TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
     217             : 
     218      647460 :             resorigtbl = tle->resorigtbl;
     219      647460 :             resorigcol = tle->resorigcol;
     220      647460 :             tlist_item = lnext(targetlist, tlist_item);
     221             :         }
     222             :         else
     223             :         {
     224             :             /* No info available, so send zeroes */
     225       12818 :             resorigtbl = 0;
     226       12818 :             resorigcol = 0;
     227             :         }
     228             : 
     229      660278 :         if (formats)
     230      659922 :             format = formats[i];
     231             :         else
     232         356 :             format = 0;
     233             : 
     234      660278 :         pq_writestring(buf, NameStr(att->attname));
     235      660278 :         pq_writeint32(buf, resorigtbl);
     236      660278 :         pq_writeint16(buf, resorigcol);
     237      660278 :         pq_writeint32(buf, atttypid);
     238      660278 :         pq_writeint16(buf, att->attlen);
     239      660278 :         pq_writeint32(buf, atttypmod);
     240      660278 :         pq_writeint16(buf, format);
     241             :     }
     242             : 
     243      233298 :     pq_endmessage_reuse(buf);
     244      233298 : }
     245             : 
     246             : /*
     247             :  * Get the lookup info that printtup() needs
     248             :  */
     249             : static void
     250      199336 : printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
     251             : {
     252      199336 :     int16      *formats = myState->portal->formats;
     253             :     int         i;
     254             : 
     255             :     /* get rid of any old data */
     256      199336 :     if (myState->myinfo)
     257        1124 :         pfree(myState->myinfo);
     258      199336 :     myState->myinfo = NULL;
     259             : 
     260      199336 :     myState->attrinfo = typeinfo;
     261      199336 :     myState->nattrs = numAttrs;
     262      199336 :     if (numAttrs <= 0)
     263         106 :         return;
     264             : 
     265      199230 :     myState->myinfo = (PrinttupAttrInfo *)
     266      199230 :         palloc0(numAttrs * sizeof(PrinttupAttrInfo));
     267             : 
     268      737112 :     for (i = 0; i < numAttrs; i++)
     269             :     {
     270      537882 :         PrinttupAttrInfo *thisState = myState->myinfo + i;
     271      537882 :         int16       format = (formats ? formats[i] : 0);
     272      537882 :         Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
     273             : 
     274      537882 :         thisState->format = format;
     275      537882 :         if (format == 0)
     276             :         {
     277      537796 :             getTypeOutputInfo(attr->atttypid,
     278             :                               &thisState->typoutput,
     279             :                               &thisState->typisvarlena);
     280      537796 :             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             :  */
     300             : static bool
     301     7204416 : printtup(TupleTableSlot *slot, DestReceiver *self)
     302             : {
     303     7204416 :     TupleDesc   typeinfo = slot->tts_tupleDescriptor;
     304     7204416 :     DR_printtup *myState = (DR_printtup *) self;
     305             :     MemoryContext oldcontext;
     306     7204416 :     StringInfo  buf = &myState->buf;
     307     7204416 :     int         natts = typeinfo->natts;
     308             :     int         i;
     309             : 
     310             :     /* Set or update my derived attribute info, if needed */
     311     7204416 :     if (myState->attrinfo != typeinfo || myState->nattrs != natts)
     312      199336 :         printtup_prepare_info(myState, typeinfo, natts);
     313             : 
     314             :     /* Make sure the tuple is fully deconstructed */
     315     7204416 :     slot_getallattrs(slot);
     316             : 
     317             :     /* Switch into per-row context so we can recover memory below */
     318     7204416 :     oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
     319             : 
     320             :     /*
     321             :      * Prepare a DataRow message (note buffer is in per-row context)
     322             :      */
     323     7204416 :     pq_beginmessage_reuse(buf, 'D');
     324             : 
     325     7204416 :     pq_sendint16(buf, natts);
     326             : 
     327             :     /*
     328             :      * send the attributes of this tuple
     329             :      */
     330    33293078 :     for (i = 0; i < natts; ++i)
     331             :     {
     332    26088662 :         PrinttupAttrInfo *thisState = myState->myinfo + i;
     333    26088662 :         Datum       attr = slot->tts_values[i];
     334             : 
     335    26088662 :         if (slot->tts_isnull[i])
     336             :         {
     337     1011538 :             pq_sendint32(buf, -1);
     338     1011538 :             continue;
     339             :         }
     340             : 
     341             :         /*
     342             :          * Here we catch undefined bytes in datums that are returned to the
     343             :          * client without hitting disk; see comments at the related check in
     344             :          * PageAddItem().  This test is most useful for uncompressed,
     345             :          * non-external datums, but we're quite likely to see such here when
     346             :          * testing new C functions.
     347             :          */
     348    25077124 :         if (thisState->typisvarlena)
     349             :             VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr),
     350             :                                           VARSIZE_ANY(attr));
     351             : 
     352    25077124 :         if (thisState->format == 0)
     353             :         {
     354             :             /* Text output */
     355             :             char       *outputstr;
     356             : 
     357    25062560 :             outputstr = OutputFunctionCall(&thisState->finfo, attr);
     358    25062560 :             pq_sendcountedtext(buf, outputstr, strlen(outputstr), false);
     359             :         }
     360             :         else
     361             :         {
     362             :             /* Binary output */
     363             :             bytea      *outputbytes;
     364             : 
     365       14564 :             outputbytes = SendFunctionCall(&thisState->finfo, attr);
     366       14564 :             pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
     367       14564 :             pq_sendbytes(buf, VARDATA(outputbytes),
     368       14564 :                          VARSIZE(outputbytes) - VARHDRSZ);
     369             :         }
     370             :     }
     371             : 
     372     7204416 :     pq_endmessage_reuse(buf);
     373             : 
     374             :     /* Return to caller's context, and flush row's temporary memory */
     375     7204416 :     MemoryContextSwitchTo(oldcontext);
     376     7204416 :     MemoryContextReset(myState->tmpcontext);
     377             : 
     378     7204416 :     return true;
     379             : }
     380             : 
     381             : /* ----------------
     382             :  *      printtup_shutdown
     383             :  * ----------------
     384             :  */
     385             : static void
     386      228256 : printtup_shutdown(DestReceiver *self)
     387             : {
     388      228256 :     DR_printtup *myState = (DR_printtup *) self;
     389             : 
     390      228256 :     if (myState->myinfo)
     391      197950 :         pfree(myState->myinfo);
     392      228256 :     myState->myinfo = NULL;
     393             : 
     394      228256 :     myState->attrinfo = NULL;
     395             : 
     396      228256 :     if (myState->buf.data)
     397      228256 :         pfree(myState->buf.data);
     398      228256 :     myState->buf.data = NULL;
     399             : 
     400      228256 :     if (myState->tmpcontext)
     401      228256 :         MemoryContextDelete(myState->tmpcontext);
     402      228256 :     myState->tmpcontext = NULL;
     403      228256 : }
     404             : 
     405             : /* ----------------
     406             :  *      printtup_destroy
     407             :  * ----------------
     408             :  */
     409             : static void
     410      481856 : printtup_destroy(DestReceiver *self)
     411             : {
     412      481856 :     pfree(self);
     413      481856 : }
     414             : 
     415             : /* ----------------
     416             :  *      printatt
     417             :  * ----------------
     418             :  */
     419             : static void
     420        2408 : printatt(unsigned attributeId,
     421             :          Form_pg_attribute attributeP,
     422             :          char *value)
     423             : {
     424        2408 :     printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
     425             :            attributeId,
     426             :            NameStr(attributeP->attname),
     427             :            value != NULL ? " = \"" : "",
     428             :            value != NULL ? value : "",
     429             :            value != NULL ? "\"" : "",
     430             :            (unsigned int) (attributeP->atttypid),
     431             :            attributeP->attlen,
     432             :            attributeP->atttypmod,
     433             :            attributeP->attbyval ? 't' : 'f');
     434        2408 : }
     435             : 
     436             : /* ----------------
     437             :  *      debugStartup - prepare to print tuples for an interactive backend
     438             :  * ----------------
     439             :  */
     440             : void
     441        1204 : debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
     442             : {
     443        1204 :     int         natts = typeinfo->natts;
     444             :     int         i;
     445             : 
     446             :     /*
     447             :      * show the return type of the tuples
     448             :      */
     449        2408 :     for (i = 0; i < natts; ++i)
     450        1204 :         printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), NULL);
     451        1204 :     printf("\t----\n");
     452        1204 : }
     453             : 
     454             : /* ----------------
     455             :  *      debugtup - print one tuple for an interactive backend
     456             :  * ----------------
     457             :  */
     458             : bool
     459        1204 : debugtup(TupleTableSlot *slot, DestReceiver *self)
     460             : {
     461        1204 :     TupleDesc   typeinfo = slot->tts_tupleDescriptor;
     462        1204 :     int         natts = typeinfo->natts;
     463             :     int         i;
     464             :     Datum       attr;
     465             :     char       *value;
     466             :     bool        isnull;
     467             :     Oid         typoutput;
     468             :     bool        typisvarlena;
     469             : 
     470        2408 :     for (i = 0; i < natts; ++i)
     471             :     {
     472        1204 :         attr = slot_getattr(slot, i + 1, &isnull);
     473        1204 :         if (isnull)
     474           0 :             continue;
     475        1204 :         getTypeOutputInfo(TupleDescAttr(typeinfo, i)->atttypid,
     476             :                           &typoutput, &typisvarlena);
     477             : 
     478        1204 :         value = OidOutputFunctionCall(typoutput, attr);
     479             : 
     480        1204 :         printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), value);
     481             :     }
     482        1204 :     printf("\t----\n");
     483             : 
     484        1204 :     return true;
     485             : }

Generated by: LCOV version 1.14