postgresql/src/backend/access/common/printtup.c
Tom Lane a933ee38bb Change SearchSysCache coding conventions so that a reference count is
maintained for each cache entry.  A cache entry will not be freed until
the matching ReleaseSysCache call has been executed.  This eliminates
worries about cache entries getting dropped while still in use.  See
my posting to pg-hackers of even date for more info.
2000-11-16 22:30:52 +00:00

428 lines
10 KiB
C

/*-------------------------------------------------------------------------
*
* printtup.c
* Routines to print out tuples to the destination (binary or non-binary
* portals, frontend/interactive backend, etc.).
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.54 2000/11/16 22:30:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "access/printtup.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "utils/syscache.h"
static void printtup_setup(DestReceiver *self, TupleDesc typeinfo);
static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
static void printtup_cleanup(DestReceiver *self);
/* ----------------------------------------------------------------
* printtup / debugtup support
* ----------------------------------------------------------------
*/
/* ----------------
* getTypeOutAndElem -- get both typoutput and typelem for a type
*
* We used to fetch these with two separate function calls,
* typtoout() and gettypelem(), which each called SearchSysCache.
* This way takes half the time.
* ----------------
*/
int
getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem)
{
HeapTuple typeTuple;
Form_pg_type pt;
typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(type),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "getTypeOutAndElem: Cache lookup of type %u failed", type);
pt = (Form_pg_type) GETSTRUCT(typeTuple);
*typOutput = pt->typoutput;
*typElem = pt->typelem;
ReleaseSysCache(typeTuple);
return OidIsValid(*typOutput);
}
/* ----------------
* Private state for a printtup destination object
* ----------------
*/
typedef struct
{ /* Per-attribute information */
Oid typoutput; /* Oid for the attribute's type output fn */
Oid typelem; /* typelem value to pass to the output fn */
FmgrInfo finfo; /* Precomputed call info for typoutput */
} PrinttupAttrInfo;
typedef struct
{
DestReceiver pub; /* publicly-known function pointers */
TupleDesc attrinfo; /* The attr info we are set up for */
int nattrs;
PrinttupAttrInfo *myinfo; /* Cached info about each attr */
} DR_printtup;
/* ----------------
* Initialize: create a DestReceiver for printtup
* ----------------
*/
DestReceiver *
printtup_create_DR()
{
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
self->pub.receiveTuple = printtup;
self->pub.setup = printtup_setup;
self->pub.cleanup = printtup_cleanup;
self->attrinfo = NULL;
self->nattrs = 0;
self->myinfo = NULL;
return (DestReceiver *) self;
}
static void
printtup_setup(DestReceiver *self, TupleDesc typeinfo)
{
/* ----------------
* We could set up the derived attr info at this time, but we postpone it
* until the first call of printtup, for 3 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 :-(
* ----------------
*/
}
static void
printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
{
int i;
if (myState->myinfo)
pfree(myState->myinfo); /* get rid of any old data */
myState->myinfo = NULL;
myState->attrinfo = typeinfo;
myState->nattrs = numAttrs;
if (numAttrs <= 0)
return;
myState->myinfo = (PrinttupAttrInfo *)
palloc(numAttrs * sizeof(PrinttupAttrInfo));
for (i = 0; i < numAttrs; i++)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
&thisState->typoutput, &thisState->typelem))
fmgr_info(thisState->typoutput, &thisState->finfo);
}
}
/* ----------------
* printtup
* ----------------
*/
static void
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf;
int i,
j,
k;
char *outputstr;
Datum attr;
bool isnull;
/* Set or update my derived attribute info, if needed */
if (myState->attrinfo != typeinfo ||
myState->nattrs != tuple->t_data->t_natts)
printtup_prepare_info(myState, typeinfo, tuple->t_data->t_natts);
/* ----------------
* tell the frontend to expect new tuple data (in ASCII style)
* ----------------
*/
pq_beginmessage(&buf);
pq_sendbyte(&buf, 'D');
/* ----------------
* send a bitmap of which attributes are not null
* ----------------
*/
j = 0;
k = 1 << 7;
for (i = 0; i < tuple->t_data->t_natts; ++i)
{
if (!heap_attisnull(tuple, i + 1))
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 < tuple->t_data->t_natts; ++i)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
if (isnull)
continue;
if (OidIsValid(thisState->typoutput))
{
outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
attr,
ObjectIdGetDatum(thisState->typelem),
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
pfree(outputstr);
}
else
{
outputstr = "<unprintable>";
pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
}
}
pq_endmessage(&buf);
}
/* ----------------
* printtup_cleanup
* ----------------
*/
static void
printtup_cleanup(DestReceiver *self)
{
DR_printtup *myState = (DR_printtup *) self;
if (myState->myinfo)
pfree(myState->myinfo);
pfree(myState);
}
/* ----------------
* 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');
}
/* ----------------
* showatts
* ----------------
*/
void
showatts(char *name, TupleDesc tupleDesc)
{
int i;
int natts = tupleDesc->natts;
Form_pg_attribute *attinfo = tupleDesc->attrs;
puts(name);
for (i = 0; i < natts; ++i)
printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
printf("\t----\n");
}
/* ----------------
* debugtup
* ----------------
*/
void
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
int i;
Datum attr;
char *value;
bool isnull;
Oid typoutput,
typelem;
for (i = 0; i < tuple->t_data->t_natts; ++i)
{
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
if (isnull)
continue;
if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
&typoutput, &typelem))
{
value = DatumGetCString(OidFunctionCall3(typoutput,
attr,
ObjectIdGetDatum(typelem),
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
printatt((unsigned) i + 1, typeinfo->attrs[i], value);
pfree(value);
}
}
printf("\t----\n");
}
/* ----------------
* printtup_internal
* We use a different data prefix, e.g. 'B' instead of 'D' to
* indicate a tuple in internal (binary) form.
*
* This is same as printtup, except we don't use the typout func,
* and therefore have no need for persistent state.
* ----------------
*/
void
printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
StringInfoData buf;
int i,
j,
k;
Datum attr;
bool isnull;
/* ----------------
* tell the frontend to expect new tuple data (in binary style)
* ----------------
*/
pq_beginmessage(&buf);
pq_sendbyte(&buf, 'B');
/* ----------------
* send a bitmap of which attributes are not null
* ----------------
*/
j = 0;
k = 1 << 7;
for (i = 0; i < tuple->t_data->t_natts; ++i)
{
if (!heap_attisnull(tuple, i + 1))
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
* ----------------
*/
#ifdef IPORTAL_DEBUG
fprintf(stderr, "sending tuple with %d atts\n", tuple->t_data->t_natts);
#endif
for (i = 0; i < tuple->t_data->t_natts; ++i)
{
int32 len = typeinfo->attrs[i]->attlen;
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
if (!isnull)
{
/* # of bytes, and opaque data */
if (len == -1)
{
/* variable length, assume a varlena structure */
len = VARSIZE(attr) - VARHDRSZ;
pq_sendint(&buf, len, VARHDRSZ);
pq_sendbytes(&buf, VARDATA(attr), len);
#ifdef IPORTAL_DEBUG
{
char *d = VARDATA(attr);
fprintf(stderr, "length %d data %x%x%x%x\n",
len, *d, *(d + 1), *(d + 2), *(d + 3));
}
#endif
}
else
{
/* fixed size */
if (typeinfo->attrs[i]->attbyval)
{
int8 i8;
int16 i16;
int32 i32;
pq_sendint(&buf, len, sizeof(int32));
switch (len)
{
case sizeof(int8):
i8 = DatumGetChar(attr);
pq_sendbytes(&buf, (char *) &i8, len);
break;
case sizeof(int16):
i16 = DatumGetInt16(attr);
pq_sendbytes(&buf, (char *) &i16, len);
break;
case sizeof(int32):
i32 = DatumGetInt32(attr);
pq_sendbytes(&buf, (char *) &i32, len);
break;
}
#ifdef IPORTAL_DEBUG
fprintf(stderr, "byval length %d data %d\n", len, attr);
#endif
}
else
{
pq_sendint(&buf, len, sizeof(int32));
pq_sendbytes(&buf, DatumGetPointer(attr), len);
#ifdef IPORTAL_DEBUG
fprintf(stderr, "byref length %d data %x\n", len,
DatumGetPointer(attr));
#endif
}
}
}
}
pq_endmessage(&buf);
}