summaryrefslogtreecommitdiffstats
path: root/src/backend/access/common/printtup.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/access/common/printtup.c
parentInitial commit. (diff)
downloadpostgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz
postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/access/common/printtup.c')
-rw-r--r--src/backend/access/common/printtup.c485
1 files changed, 485 insertions, 0 deletions
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
new file mode 100644
index 0000000..54b539f
--- /dev/null
+++ b/src/backend/access/common/printtup.c
@@ -0,0 +1,485 @@
+/*-------------------------------------------------------------------------
+ *
+ * printtup.c
+ * Routines to print out tuples to the destination (both frontend
+ * clients and standalone backends are supported here).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/common/printtup.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/printtup.h"
+#include "libpq/libpq.h"
+#include "libpq/pqformat.h"
+#include "tcop/pquery.h"
+#include "utils/lsyscache.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+
+
+static void printtup_startup(DestReceiver *self, int operation,
+ TupleDesc typeinfo);
+static bool printtup(TupleTableSlot *slot, DestReceiver *self);
+static void printtup_shutdown(DestReceiver *self);
+static void printtup_destroy(DestReceiver *self);
+
+/* ----------------------------------------------------------------
+ * printtup / debugtup support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * Private state for a printtup destination object
+ *
+ * NOTE: finfo is the lookup info for either typoutput or typsend, whichever
+ * we are using for this column.
+ * ----------------
+ */
+typedef struct
+{ /* Per-attribute information */
+ Oid typoutput; /* Oid for the type's text output fn */
+ Oid typsend; /* Oid for the type's binary output fn */
+ bool typisvarlena; /* is it varlena (ie possibly toastable)? */
+ int16 format; /* format code for this column */
+ FmgrInfo finfo; /* Precomputed call info for output fn */
+} PrinttupAttrInfo;
+
+typedef struct
+{
+ DestReceiver pub; /* publicly-known function pointers */
+ Portal portal; /* the Portal we are printing from */
+ bool sendDescrip; /* send RowDescription at startup? */
+ TupleDesc attrinfo; /* The attr info we are set up for */
+ int nattrs;
+ PrinttupAttrInfo *myinfo; /* Cached info about each attr */
+ StringInfoData buf; /* output buffer (*not* in tmpcontext) */
+ MemoryContext tmpcontext; /* Memory context for per-row workspace */
+} DR_printtup;
+
+/* ----------------
+ * Initialize: create a DestReceiver for printtup
+ * ----------------
+ */
+DestReceiver *
+printtup_create_DR(CommandDest dest)
+{
+ DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup));
+
+ self->pub.receiveSlot = printtup; /* might get changed later */
+ self->pub.rStartup = printtup_startup;
+ self->pub.rShutdown = printtup_shutdown;
+ self->pub.rDestroy = printtup_destroy;
+ self->pub.mydest = dest;
+
+ /*
+ * Send T message automatically if DestRemote, but not if
+ * DestRemoteExecute
+ */
+ self->sendDescrip = (dest == DestRemote);
+
+ self->attrinfo = NULL;
+ self->nattrs = 0;
+ self->myinfo = NULL;
+ self->buf.data = NULL;
+ self->tmpcontext = NULL;
+
+ return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a DestRemote (or DestRemoteExecute) receiver
+ */
+void
+SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
+{
+ DR_printtup *myState = (DR_printtup *) self;
+
+ Assert(myState->pub.mydest == DestRemote ||
+ myState->pub.mydest == DestRemoteExecute);
+
+ myState->portal = portal;
+}
+
+static void
+printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ DR_printtup *myState = (DR_printtup *) self;
+ Portal portal = myState->portal;
+
+ /*
+ * Create I/O buffer to be used for all messages. This cannot be inside
+ * tmpcontext, since we want to re-use it across rows.
+ */
+ initStringInfo(&myState->buf);
+
+ /*
+ * Create a temporary memory context that we can reset once per row to
+ * recover palloc'd memory. This avoids any problems with leaks inside
+ * datatype output routines, and should be faster than retail pfree's
+ * anyway.
+ */
+ myState->tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
+ "printtup",
+ ALLOCSET_DEFAULT_SIZES);
+
+ /*
+ * If we are supposed to emit row descriptions, then send the tuple
+ * descriptor of the tuples.
+ */
+ if (myState->sendDescrip)
+ SendRowDescriptionMessage(&myState->buf,
+ typeinfo,
+ FetchPortalTargetList(portal),
+ portal->formats);
+
+ /* ----------------
+ * We could set up the derived attr info at this time, but we postpone it
+ * until the first call of printtup, for 2 reasons:
+ * 1. We don't waste time (compared to the old way) if there are no
+ * tuples at all to output.
+ * 2. Checking in printtup allows us to handle the case that the tuples
+ * change type midway through (although this probably can't happen in
+ * the current executor).
+ * ----------------
+ */
+}
+
+/*
+ * SendRowDescriptionMessage --- send a RowDescription message to the frontend
+ *
+ * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
+ * or some similar function; it does not contain a full set of fields.
+ * The targetlist will be NIL when executing a utility function that does
+ * not have a plan. If the targetlist isn't NIL then it is a Query node's
+ * targetlist; it is up to us to ignore resjunk columns in it. The formats[]
+ * array pointer might be NULL (if we are doing Describe on a prepared stmt);
+ * send zeroes for the format codes in that case.
+ */
+void
+SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
+ List *targetlist, int16 *formats)
+{
+ int natts = typeinfo->natts;
+ int i;
+ ListCell *tlist_item = list_head(targetlist);
+
+ /* tuple descriptor message type */
+ pq_beginmessage_reuse(buf, 'T');
+ /* # of attrs in tuples */
+ pq_sendint16(buf, natts);
+
+ /*
+ * Preallocate memory for the entire message to be sent. That allows to
+ * use the significantly faster inline pqformat.h functions and to avoid
+ * reallocations.
+ *
+ * Have to overestimate the size of the column-names, to account for
+ * character set overhead.
+ */
+ enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
+ + sizeof(Oid) /* resorigtbl */
+ + sizeof(AttrNumber) /* resorigcol */
+ + sizeof(Oid) /* atttypid */
+ + sizeof(int16) /* attlen */
+ + sizeof(int32) /* attypmod */
+ + sizeof(int16) /* format */
+ ) * natts);
+
+ for (i = 0; i < natts; ++i)
+ {
+ Form_pg_attribute att = TupleDescAttr(typeinfo, i);
+ Oid atttypid = att->atttypid;
+ int32 atttypmod = att->atttypmod;
+ Oid resorigtbl;
+ AttrNumber resorigcol;
+ int16 format;
+
+ /*
+ * If column is a domain, send the base type and typmod instead.
+ * Lookup before sending any ints, for efficiency.
+ */
+ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
+
+ /* Do we have a non-resjunk tlist item? */
+ while (tlist_item &&
+ ((TargetEntry *) lfirst(tlist_item))->resjunk)
+ tlist_item = lnext(targetlist, tlist_item);
+ if (tlist_item)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
+
+ resorigtbl = tle->resorigtbl;
+ resorigcol = tle->resorigcol;
+ tlist_item = lnext(targetlist, tlist_item);
+ }
+ else
+ {
+ /* No info available, so send zeroes */
+ resorigtbl = 0;
+ resorigcol = 0;
+ }
+
+ if (formats)
+ format = formats[i];
+ else
+ format = 0;
+
+ pq_writestring(buf, NameStr(att->attname));
+ pq_writeint32(buf, resorigtbl);
+ pq_writeint16(buf, resorigcol);
+ pq_writeint32(buf, atttypid);
+ pq_writeint16(buf, att->attlen);
+ pq_writeint32(buf, atttypmod);
+ pq_writeint16(buf, format);
+ }
+
+ pq_endmessage_reuse(buf);
+}
+
+/*
+ * Get the lookup info that printtup() needs
+ */
+static void
+printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
+{
+ int16 *formats = myState->portal->formats;
+ int i;
+
+ /* get rid of any old data */
+ if (myState->myinfo)
+ pfree(myState->myinfo);
+ myState->myinfo = NULL;
+
+ myState->attrinfo = typeinfo;
+ myState->nattrs = numAttrs;
+ if (numAttrs <= 0)
+ return;
+
+ myState->myinfo = (PrinttupAttrInfo *)
+ palloc0(numAttrs * sizeof(PrinttupAttrInfo));
+
+ for (i = 0; i < numAttrs; i++)
+ {
+ PrinttupAttrInfo *thisState = myState->myinfo + i;
+ int16 format = (formats ? formats[i] : 0);
+ Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
+
+ thisState->format = format;
+ if (format == 0)
+ {
+ getTypeOutputInfo(attr->atttypid,
+ &thisState->typoutput,
+ &thisState->typisvarlena);
+ fmgr_info(thisState->typoutput, &thisState->finfo);
+ }
+ else if (format == 1)
+ {
+ getTypeBinaryOutputInfo(attr->atttypid,
+ &thisState->typsend,
+ &thisState->typisvarlena);
+ fmgr_info(thisState->typsend, &thisState->finfo);
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unsupported format code: %d", format)));
+ }
+}
+
+/* ----------------
+ * printtup --- send a tuple to the client
+ * ----------------
+ */
+static bool
+printtup(TupleTableSlot *slot, DestReceiver *self)
+{
+ TupleDesc typeinfo = slot->tts_tupleDescriptor;
+ DR_printtup *myState = (DR_printtup *) self;
+ MemoryContext oldcontext;
+ StringInfo buf = &myState->buf;
+ int natts = typeinfo->natts;
+ int i;
+
+ /* Set or update my derived attribute info, if needed */
+ if (myState->attrinfo != typeinfo || myState->nattrs != natts)
+ printtup_prepare_info(myState, typeinfo, natts);
+
+ /* Make sure the tuple is fully deconstructed */
+ slot_getallattrs(slot);
+
+ /* Switch into per-row context so we can recover memory below */
+ oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
+
+ /*
+ * Prepare a DataRow message (note buffer is in per-row context)
+ */
+ pq_beginmessage_reuse(buf, 'D');
+
+ pq_sendint16(buf, natts);
+
+ /*
+ * send the attributes of this tuple
+ */
+ for (i = 0; i < natts; ++i)
+ {
+ PrinttupAttrInfo *thisState = myState->myinfo + i;
+ Datum attr = slot->tts_values[i];
+
+ if (slot->tts_isnull[i])
+ {
+ pq_sendint32(buf, -1);
+ continue;
+ }
+
+ /*
+ * Here we catch undefined bytes in datums that are returned to the
+ * client without hitting disk; see comments at the related check in
+ * PageAddItem(). This test is most useful for uncompressed,
+ * non-external datums, but we're quite likely to see such here when
+ * testing new C functions.
+ */
+ if (thisState->typisvarlena)
+ VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr),
+ VARSIZE_ANY(attr));
+
+ if (thisState->format == 0)
+ {
+ /* Text output */
+ char *outputstr;
+
+ outputstr = OutputFunctionCall(&thisState->finfo, attr);
+ pq_sendcountedtext(buf, outputstr, strlen(outputstr), false);
+ }
+ else
+ {
+ /* Binary output */
+ bytea *outputbytes;
+
+ outputbytes = SendFunctionCall(&thisState->finfo, attr);
+ pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(outputbytes),
+ VARSIZE(outputbytes) - VARHDRSZ);
+ }
+ }
+
+ pq_endmessage_reuse(buf);
+
+ /* Return to caller's context, and flush row's temporary memory */
+ MemoryContextSwitchTo(oldcontext);
+ MemoryContextReset(myState->tmpcontext);
+
+ return true;
+}
+
+/* ----------------
+ * printtup_shutdown
+ * ----------------
+ */
+static void
+printtup_shutdown(DestReceiver *self)
+{
+ DR_printtup *myState = (DR_printtup *) self;
+
+ if (myState->myinfo)
+ pfree(myState->myinfo);
+ myState->myinfo = NULL;
+
+ myState->attrinfo = NULL;
+
+ if (myState->buf.data)
+ pfree(myState->buf.data);
+ myState->buf.data = NULL;
+
+ if (myState->tmpcontext)
+ MemoryContextDelete(myState->tmpcontext);
+ myState->tmpcontext = NULL;
+}
+
+/* ----------------
+ * printtup_destroy
+ * ----------------
+ */
+static void
+printtup_destroy(DestReceiver *self)
+{
+ pfree(self);
+}
+
+/* ----------------
+ * printatt
+ * ----------------
+ */
+static void
+printatt(unsigned attributeId,
+ Form_pg_attribute attributeP,
+ char *value)
+{
+ printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
+ attributeId,
+ NameStr(attributeP->attname),
+ value != NULL ? " = \"" : "",
+ value != NULL ? value : "",
+ value != NULL ? "\"" : "",
+ (unsigned int) (attributeP->atttypid),
+ attributeP->attlen,
+ attributeP->atttypmod,
+ attributeP->attbyval ? 't' : 'f');
+}
+
+/* ----------------
+ * debugStartup - prepare to print tuples for an interactive backend
+ * ----------------
+ */
+void
+debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+ int natts = typeinfo->natts;
+ int i;
+
+ /*
+ * show the return type of the tuples
+ */
+ for (i = 0; i < natts; ++i)
+ printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), NULL);
+ printf("\t----\n");
+}
+
+/* ----------------
+ * debugtup - print one tuple for an interactive backend
+ * ----------------
+ */
+bool
+debugtup(TupleTableSlot *slot, DestReceiver *self)
+{
+ TupleDesc typeinfo = slot->tts_tupleDescriptor;
+ int natts = typeinfo->natts;
+ int i;
+ Datum attr;
+ char *value;
+ bool isnull;
+ Oid typoutput;
+ bool typisvarlena;
+
+ for (i = 0; i < natts; ++i)
+ {
+ attr = slot_getattr(slot, i + 1, &isnull);
+ if (isnull)
+ continue;
+ getTypeOutputInfo(TupleDescAttr(typeinfo, i)->atttypid,
+ &typoutput, &typisvarlena);
+
+ value = OidOutputFunctionCall(typoutput, attr);
+
+ printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), value);
+ }
+ printf("\t----\n");
+
+ return true;
+}