postgresql/src/backend/access/common/printtup.c
PostgreSQL Daemon 2ff501590b Tag appropriate files for rc3
Also performed an initial run through of upgrading our Copyright date to
extend to 2005 ... first run here was very simple ... change everything
where: grep 1996-2004 && the word 'Copyright' ... scanned through the
generated list with 'less' first, and after, to make sure that I only
picked up the right entries ...
2004-12-31 22:04:05 +00:00

689 lines
18 KiB
C

/*-------------------------------------------------------------------------
*
* printtup.c
* Routines to print out tuples to the destination (both frontend
* clients and standalone backends are supported here).
*
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.86 2004/12/31 21:59:07 pgsql Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "access/printtup.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/lsyscache.h"
#include "utils/portal.h"
static void printtup_startup(DestReceiver *self, int operation,
TupleDesc typeinfo);
static void printtup(HeapTuple tuple, TupleDesc typeinfo,
DestReceiver *self);
static void printtup_20(HeapTuple tuple, TupleDesc typeinfo,
DestReceiver *self);
static void printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo,
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 */
Oid typioparam; /* param to pass to the 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 */
Datum *values; /* preallocated space for deformtuple */
char *nulls;
} DR_printtup;
/* ----------------
* Initialize: create a DestReceiver for printtup
* ----------------
*/
DestReceiver *
printtup_create_DR(CommandDest dest, Portal portal)
{
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
self->pub.receiveTuple = printtup;
else
{
/*
* In protocol 2.0 the Bind message does not exist, so there is no
* way for the columns to have different print formats; it's
* sufficient to look at the first one.
*/
if (portal->formats && portal->formats[0] != 0)
self->pub.receiveTuple = printtup_internal_20;
else
self->pub.receiveTuple = printtup_20;
}
self->pub.rStartup = printtup_startup;
self->pub.rShutdown = printtup_shutdown;
self->pub.rDestroy = printtup_destroy;
self->pub.mydest = dest;
self->portal = portal;
/* Send T message automatically if Remote, but not if RemoteExecute */
self->sendDescrip = (dest == Remote);
self->attrinfo = NULL;
self->nattrs = 0;
self->myinfo = NULL;
self->values = NULL;
self->nulls = NULL;
return (DestReceiver *) self;
}
static void
printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
{
DR_printtup *myState = (DR_printtup *) self;
Portal portal = myState->portal;
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.
*/
const char *portalName = portal->name;
if (portalName == NULL || portalName[0] == '\0')
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)
{
List *targetlist;
if (portal->strategy == PORTAL_ONE_SELECT)
targetlist = ((Query *) linitial(portal->parseTrees))->targetList;
else
targetlist = NIL;
SendRowDescriptionMessage(typeinfo, targetlist, 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(TupleDesc typeinfo, List *targetlist, int16 *formats)
{
Form_pg_attribute *attrs = typeinfo->attrs;
int natts = typeinfo->natts;
int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
int i;
StringInfoData buf;
ListCell *tlist_item = list_head(targetlist);
pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
for (i = 0; i < natts; ++i)
{
Oid atttypid = attrs[i]->atttypid;
int32 atttypmod = attrs[i]->atttypmod;
Oid basetype;
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 (tlist_item &&
((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
tlist_item = lnext(tlist_item);
if (tlist_item)
{
Resdom *res = ((TargetEntry *) lfirst(tlist_item))->resdom;
pq_sendint(&buf, res->resorigtbl, 4);
pq_sendint(&buf, res->resorigcol, 2);
tlist_item = lnext(tlist_item);
}
else
{
/* No info available, so send zeroes */
pq_sendint(&buf, 0, 4);
pq_sendint(&buf, 0, 2);
}
}
/* If column is a domain, send the base type and typmod instead */
basetype = getBaseType(atttypid);
if (basetype != atttypid)
{
atttypmod = get_typtypmod(atttypid);
atttypid = basetype;
}
pq_sendint(&buf, (int) atttypid, sizeof(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, atttypmod, sizeof(atttypmod));
/* format info appears in protocol 3.0 and up */
if (proto >= 3)
{
if (formats)
pq_sendint(&buf, formats[i], 2);
else
pq_sendint(&buf, 0, 2);
}
}
pq_endmessage(&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;
if (myState->values)
pfree(myState->values);
myState->values = NULL;
if (myState->nulls)
pfree(myState->nulls);
myState->nulls = NULL;
myState->attrinfo = typeinfo;
myState->nattrs = numAttrs;
if (numAttrs <= 0)
return;
myState->myinfo = (PrinttupAttrInfo *)
palloc0(numAttrs * sizeof(PrinttupAttrInfo));
myState->values = (Datum *) palloc(numAttrs * sizeof(Datum));
myState->nulls = (char *) palloc(numAttrs * sizeof(char));
for (i = 0; i < numAttrs; i++)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
int16 format = (formats ? formats[i] : 0);
thisState->format = format;
if (format == 0)
{
getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
&thisState->typoutput,
&thisState->typioparam,
&thisState->typisvarlena);
fmgr_info(thisState->typoutput, &thisState->finfo);
}
else if (format == 1)
{
getTypeBinaryOutputInfo(typeinfo->attrs[i]->atttypid,
&thisState->typsend,
&thisState->typioparam,
&thisState->typisvarlena);
fmgr_info(thisState->typsend, &thisState->finfo);
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unsupported format code: %d", format)));
}
}
/* ----------------
* printtup --- print a tuple in protocol 3.0
* ----------------
*/
static void
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
DR_printtup *myState = (DR_printtup *) self;
StringInfoData 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);
/*
* deconstruct the tuple (faster than a heap_getattr loop)
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/*
* Prepare a DataRow message
*/
pq_beginmessage(&buf, 'D');
pq_sendint(&buf, natts, 2);
/*
* send the attributes of this tuple
*/
for (i = 0; i < natts; ++i)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr = myState->values[i],
attr;
if (myState->nulls[i] == 'n')
{
pq_sendint(&buf, -1, 4);
continue;
}
/*
* 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));
else
attr = origattr;
if (thisState->format == 0)
{
/* Text output */
char *outputstr;
outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
attr,
ObjectIdGetDatum(thisState->typioparam),
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
pfree(outputstr);
}
else
{
/* Binary output */
bytea *outputbytes;
outputbytes = DatumGetByteaP(FunctionCall2(&thisState->finfo,
attr,
ObjectIdGetDatum(thisState->typioparam)));
/* We assume the result will not have been toasted */
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
pq_sendbytes(&buf, VARDATA(outputbytes),
VARSIZE(outputbytes) - VARHDRSZ);
pfree(outputbytes);
}
/* Clean up detoasted copy, if any */
if (attr != origattr)
pfree(DatumGetPointer(attr));
}
pq_endmessage(&buf);
}
/* ----------------
* printtup_20 --- print a tuple in protocol 2.0
* ----------------
*/
static void
printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf;
int natts = typeinfo->natts;
int i,
j,
k;
/* Set or update my derived attribute info, if needed */
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts);
/*
* deconstruct the tuple (faster than a heap_getattr loop)
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/*
* tell the frontend to expect new tuple data (in ASCII style)
*/
pq_beginmessage(&buf, 'D');
/*
* send a bitmap of which attributes are not null
*/
j = 0;
k = 1 << 7;
for (i = 0; i < natts; ++i)
{
if (myState->nulls[i] != 'n')
j |= k; /* set bit if not null */
k >>= 1;
if (k == 0) /* end of byte? */
{
pq_sendint(&buf, j, 1);
j = 0;
k = 1 << 7;
}
}
if (k != (1 << 7)) /* flush last partial byte */
pq_sendint(&buf, j, 1);
/*
* send the attributes of this tuple
*/
for (i = 0; i < natts; ++i)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr = myState->values[i],
attr;
char *outputstr;
if (myState->nulls[i] == 'n')
continue;
Assert(thisState->format == 0);
/*
* 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));
else
attr = origattr;
outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
attr,
ObjectIdGetDatum(thisState->typioparam),
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
pfree(outputstr);
/* Clean up detoasted copy, if any */
if (attr != origattr)
pfree(DatumGetPointer(attr));
}
pq_endmessage(&buf);
}
/* ----------------
* printtup_shutdown
* ----------------
*/
static void
printtup_shutdown(DestReceiver *self)
{
DR_printtup *myState = (DR_printtup *) self;
if (myState->myinfo)
pfree(myState->myinfo);
myState->myinfo = NULL;
if (myState->values)
pfree(myState->values);
myState->values = NULL;
if (myState->nulls)
pfree(myState->nulls);
myState->nulls = NULL;
myState->attrinfo = 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;
Form_pg_attribute *attinfo = typeinfo->attrs;
int i;
/*
* show the return type of the tuples
*/
for (i = 0; i < natts; ++i)
printatt((unsigned) i + 1, attinfo[i], NULL);
printf("\t----\n");
}
/* ----------------
* debugtup - print one tuple for an interactive backend
* ----------------
*/
void
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
int natts = typeinfo->natts;
int i;
Datum origattr,
attr;
char *value;
bool isnull;
Oid typoutput,
typioparam;
bool typisvarlena;
for (i = 0; i < natts; ++i)
{
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
if (isnull)
continue;
getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
&typoutput, &typioparam, &typisvarlena);
/*
* 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));
else
attr = origattr;
value = DatumGetCString(OidFunctionCall3(typoutput,
attr,
ObjectIdGetDatum(typioparam),
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
printatt((unsigned) i + 1, typeinfo->attrs[i], value);
pfree(value);
/* Clean up detoasted copy, if any */
if (attr != origattr)
pfree(DatumGetPointer(attr));
}
printf("\t----\n");
}
/* ----------------
* printtup_internal_20 --- print a binary tuple in protocol 2.0
*
* We use a different message type, i.e. 'B' instead of 'D' to
* indicate a tuple in internal (binary) form.
*
* This is largely same as printtup_20, except we use binary formatting.
* ----------------
*/
static void
printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf;
int natts = typeinfo->natts;
int i,
j,
k;
/* Set or update my derived attribute info, if needed */
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts);
/*
* deconstruct the tuple (faster than a heap_getattr loop)
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/*
* tell the frontend to expect new tuple data (in binary style)
*/
pq_beginmessage(&buf, 'B');
/*
* send a bitmap of which attributes are not null
*/
j = 0;
k = 1 << 7;
for (i = 0; i < natts; ++i)
{
if (myState->nulls[i] != 'n')
j |= k; /* set bit if not null */
k >>= 1;
if (k == 0) /* end of byte? */
{
pq_sendint(&buf, j, 1);
j = 0;
k = 1 << 7;
}
}
if (k != (1 << 7)) /* flush last partial byte */
pq_sendint(&buf, j, 1);
/*
* send the attributes of this tuple
*/
for (i = 0; i < natts; ++i)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr = myState->values[i],
attr;
bytea *outputbytes;
if (myState->nulls[i] == 'n')
continue;
Assert(thisState->format == 1);
/*
* 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));
else
attr = origattr;
outputbytes = DatumGetByteaP(FunctionCall2(&thisState->finfo,
attr,
ObjectIdGetDatum(thisState->typioparam)));
/* We assume the result will not have been toasted */
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
pq_sendbytes(&buf, VARDATA(outputbytes),
VARSIZE(outputbytes) - VARHDRSZ);
pfree(outputbytes);
/* Clean up detoasted copy, if any */
if (attr != origattr)
pfree(DatumGetPointer(attr));
}
pq_endmessage(&buf);
}