/*-------------------------------------------------------------------------
*
* printtup.c
- * Routines to print out tuples to the destination (binary or non-binary
- * portals, frontend/interactive backend, etc.).
+ * Routines to print out tuples to the destination (both frontend
+ * clients and interactive backends are supported here).
*
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
*
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.56 2000/12/27 23:59:10 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.69 2003/05/06 00:20:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include "access/printtup.h"
-#include "catalog/pg_type.h"
+#include "libpq/libpq.h"
#include "libpq/pqformat.h"
-#include "utils/syscache.h"
+#include "utils/lsyscache.h"
+
-static void printtup_setup(DestReceiver *self, TupleDesc typeinfo);
+static void printtup_setup(DestReceiver *self, int operation,
+ const char *portalName, TupleDesc typeinfo, List *targetlist);
static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
static void printtup_cleanup(DestReceiver *self);
* ----------------------------------------------------------------
*/
-/* ----------------
- * getTypeOutputInfo -- get info needed for printing values of a type
- * ----------------
- */
-bool
-getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
- bool *typIsVarlena)
-{
- HeapTuple typeTuple;
- Form_pg_type pt;
-
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(type),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "getTypeOutputInfo: Cache lookup of type %u failed", type);
- pt = (Form_pg_type) GETSTRUCT(typeTuple);
-
- *typOutput = pt->typoutput;
- *typElem = pt->typelem;
- *typIsVarlena = (! pt->typbyval) && (pt->typlen == -1);
- ReleaseSysCache(typeTuple);
- return OidIsValid(*typOutput);
-}
-
/* ----------------
* Private state for a printtup destination object
* ----------------
typedef struct
{
DestReceiver pub; /* publicly-known function pointers */
+ 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 */
* ----------------
*/
DestReceiver *
-printtup_create_DR(bool isBinary)
+printtup_create_DR(bool isBinary, bool sendDescrip)
{
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
self->pub.setup = printtup_setup;
self->pub.cleanup = printtup_cleanup;
+ self->sendDescrip = sendDescrip;
+
self->attrinfo = NULL;
self->nattrs = 0;
self->myinfo = NULL;
}
static void
-printtup_setup(DestReceiver *self, TupleDesc typeinfo)
+printtup_setup(DestReceiver *self, int operation,
+ const char *portalName, TupleDesc typeinfo, List *targetlist)
{
+ DR_printtup *myState = (DR_printtup *) self;
+
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ {
+ /*
+ * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
+ *
+ * If portal name not specified, use "blank" portal.
+ */
+ if (portalName == NULL)
+ portalName = "blank";
+
+ pq_puttextmessage('P', portalName);
+ }
+
+ /*
+ * If this is a retrieve, and we are supposed to emit row descriptions,
+ * then we send back the tuple descriptor of the tuples.
+ */
+ if (operation == CMD_SELECT && myState->sendDescrip)
+ SendRowDescriptionMessage(typeinfo, targetlist);
+
/* ----------------
* We could set up the derived attr info at this time, but we postpone it
- * until the first call of printtup, for 3 reasons:
+ * 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).
- * 3. Right now, ExecutorRun passes a NULL for typeinfo anyway :-(
* ----------------
*/
}
+/*
+ * 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 Plan node's
+ * targetlist; it is up to us to ignore resjunk columns in it.
+ */
+void
+SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
+{
+ Form_pg_attribute *attrs = typeinfo->attrs;
+ int natts = typeinfo->natts;
+ int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
+ int i;
+ StringInfoData buf;
+
+ pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
+ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
+
+ for (i = 0; i < natts; ++i)
+ {
+ pq_sendstring(&buf, NameStr(attrs[i]->attname));
+ /* column ID info appears in protocol 3.0 and up */
+ if (proto >= 3)
+ {
+ /* Do we have a non-resjunk tlist item? */
+ while (targetlist &&
+ ((TargetEntry *) lfirst(targetlist))->resdom->resjunk)
+ targetlist = lnext(targetlist);
+ if (targetlist)
+ {
+ Resdom *res = ((TargetEntry *) lfirst(targetlist))->resdom;
+
+ pq_sendint(&buf, res->resorigtbl, 4);
+ pq_sendint(&buf, res->resorigcol, 2);
+ targetlist = lnext(targetlist);
+ }
+ else
+ {
+ /* No info available, so send zeroes */
+ pq_sendint(&buf, 0, 4);
+ pq_sendint(&buf, 0, 2);
+ }
+ }
+ pq_sendint(&buf, (int) attrs[i]->atttypid,
+ sizeof(attrs[i]->atttypid));
+ pq_sendint(&buf, attrs[i]->attlen,
+ sizeof(attrs[i]->attlen));
+ /* typmod appears in protocol 2.0 and up */
+ if (proto >= 2)
+ pq_sendint(&buf, attrs[i]->atttypmod,
+ sizeof(attrs[i]->atttypmod));
+ }
+ pq_endmessage(&buf);
+}
+
static void
printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
{
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts);
- /* ----------------
- * tell the frontend to expect new tuple data (in ASCII style)
- * ----------------
+ /*
+ * tell the frontend to expect new tuple data (in ASCII style)
*/
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'D');
+ pq_beginmessage(&buf, 'D');
- /* ----------------
- * send a bitmap of which attributes are not null
- * ----------------
+ /*
+ * send a bitmap of which attributes are not null
*/
j = 0;
k = 1 << 7;
if (k != (1 << 7)) /* flush last partial byte */
pq_sendint(&buf, j, 1);
- /* ----------------
- * send the attributes of this tuple
- * ----------------
+ /*
+ * send the attributes of this tuple
*/
for (i = 0; i < natts; ++i)
{
if (OidIsValid(thisState->typoutput))
{
/*
- * If we have a toasted datum, forcibly detoast it here to avoid
- * memory leakage inside the type's output routine.
+ * If we have a toasted datum, forcibly detoast it here to
+ * avoid memory leakage inside the type's output routine.
*/
if (thisState->typisvarlena)
attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
attr = origattr;
outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
- attr,
- ObjectIdGetDatum(thisState->typelem),
- Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+ attr,
+ ObjectIdGetDatum(thisState->typelem),
+ Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
* showatts
* ----------------
*/
-void
-showatts(char *name, TupleDesc tupleDesc)
+static void
+showatts(const char *name, TupleDesc tupleDesc)
{
- int i;
int natts = tupleDesc->natts;
Form_pg_attribute *attinfo = tupleDesc->attrs;
+ int i;
puts(name);
for (i = 0; i < natts; ++i)
}
/* ----------------
- * debugtup
+ * debugSetup - prepare to print tuples for an interactive backend
+ * ----------------
+ */
+void
+debugSetup(DestReceiver *self, int operation,
+ const char *portalName, TupleDesc typeinfo, List *targetlist)
+{
+ /*
+ * show the return type of the tuples
+ */
+ if (portalName == NULL)
+ portalName = "blank";
+
+ showatts(portalName, typeinfo);
+}
+
+/* ----------------
+ * debugtup - print one tuple for an interactive backend
* ----------------
*/
void
&typoutput, &typelem, &typisvarlena))
{
/*
- * If we have a toasted datum, forcibly detoast it here to avoid
- * memory leakage inside the type's output routine.
+ * If we have a toasted datum, forcibly detoast it here to
+ * avoid memory leakage inside the type's output routine.
*/
if (typisvarlena)
attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
attr = origattr;
value = DatumGetCString(OidFunctionCall3(typoutput,
- attr,
- ObjectIdGetDatum(typelem),
- Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+ attr,
+ ObjectIdGetDatum(typelem),
+ Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
printatt((unsigned) i + 1, typeinfo->attrs[i], value);
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts);
- /* ----------------
- * tell the frontend to expect new tuple data (in binary style)
- * ----------------
+ /*
+ * tell the frontend to expect new tuple data (in binary style)
*/
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'B');
+ pq_beginmessage(&buf, 'B');
- /* ----------------
- * send a bitmap of which attributes are not null
- * ----------------
+ /*
+ * send a bitmap of which attributes are not null
*/
j = 0;
k = 1 << 7;
if (k != (1 << 7)) /* flush last partial byte */
pq_sendint(&buf, j, 1);
- /* ----------------
- * send the attributes of this tuple
- * ----------------
+ /*
+ * send the attributes of this tuple
*/
#ifdef IPORTAL_DEBUG
fprintf(stderr, "sending tuple with %d atts\n", natts);
}
else
{
- /* fixed size */
+ /* fixed size or cstring */
attr = origattr;
len = typeinfo->attrs[i]->attlen;
+ if (len <= 0)
+ {
+ /* it's a cstring */
+ Assert(len == -2 && !typeinfo->attrs[i]->attbyval);
+ len = strlen(DatumGetCString(attr)) + 1;
+ }
pq_sendint(&buf, len, sizeof(int32));
if (typeinfo->attrs[i]->attbyval)
{