1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Replace TupleTableSlot convention for whole-row variables and function

results with tuples as ordinary varlena Datums.  This commit does not
in itself do much for us, except eliminate the horrid memory leak
associated with evaluation of whole-row variables.  However, it lays the
groundwork for allowing composite types as table columns, and perhaps
some other useful features as well.  Per my proposal of a few days ago.
This commit is contained in:
Tom Lane
2004-04-01 21:28:47 +00:00
parent 8590a62b75
commit 375369acd1
60 changed files with 1779 additions and 1733 deletions

View File

@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.89 2004/01/16 20:51:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.90 2004/04/01 21:28:43 tgl Exp $
*
* NOTES
* The old interface functions have been converted to macros
@@ -31,6 +31,8 @@
/* ----------------
* ComputeDataSize
*
* Determine size of the data area of a tuple to be constructed
* ----------------
*/
Size
@@ -417,7 +419,7 @@ nocachegetattr(HeapTuple tuple,
* ----------------
*/
Datum
heap_getsysattr(HeapTuple tup, int attnum, bool *isnull)
heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
{
Datum result;
@@ -451,6 +453,31 @@ heap_getsysattr(HeapTuple tup, int attnum, bool *isnull)
case TableOidAttributeNumber:
result = ObjectIdGetDatum(tup->t_tableOid);
break;
/*
* If the attribute number is 0, then we are supposed to return
* the entire tuple as a row-type Datum. (Using zero for this
* purpose is unclean since it risks confusion with "invalid attr"
* result codes, but it's not worth changing now.)
*
* We have to make a copy of the tuple so we can safely insert the
* Datum overhead fields, which are not set in on-disk tuples.
*/
case InvalidAttrNumber:
{
HeapTupleHeader dtup;
dtup = (HeapTupleHeader) palloc(tup->t_len);
memcpy((char *) dtup, (char *) tup->t_data, tup->t_len);
HeapTupleHeaderSetDatumLength(dtup, tup->t_len);
HeapTupleHeaderSetTypeId(dtup, tupleDesc->tdtypeid);
HeapTupleHeaderSetTypMod(dtup, tupleDesc->tdtypmod);
result = PointerGetDatum(dtup);
}
break;
default:
elog(ERROR, "invalid attnum: %d", attnum);
result = 0; /* keep compiler quiet */
@@ -547,19 +574,10 @@ heap_deformtuple(HeapTuple tuple,
/* ----------------
* heap_formtuple
*
* constructs a tuple from the given *value and *null arrays
*
* old comments
* Handles alignment by aligning 2 byte attributes on short boundries
* and 3 or 4 byte attributes on long word boundries on a vax; and
* aligning non-byte attributes on short boundries on a sun. Does
* not properly align fixed length arrays of 1 or 2 byte types (yet).
* constructs a tuple from the given *value and *nulls arrays
*
* Null attributes are indicated by a 'n' in the appropriate byte
* of the *null. Non-null attributes are indicated by a ' ' (space).
*
* Fix me. (Figure that must keep context if debug--allow give oid.)
* Assumes in order.
* of *nulls. Non-null attributes are indicated by a ' ' (space).
* ----------------
*/
HeapTuple
@@ -581,6 +599,9 @@ heap_formtuple(TupleDesc tupleDescriptor,
errmsg("number of columns (%d) exceeds limit (%d)",
numberOfAttributes, MaxTupleAttributeNumber)));
/*
* Determine total space needed
*/
for (i = 0; i < numberOfAttributes; i++)
{
if (nulls[i] != ' ')
@@ -602,15 +623,26 @@ heap_formtuple(TupleDesc tupleDescriptor,
len += ComputeDataSize(tupleDescriptor, value, nulls);
tuple = (HeapTuple) palloc(HEAPTUPLESIZE + len);
/*
* Allocate and zero the space needed. Note that the tuple body and
* HeapTupleData management structure are allocated in one chunk.
*/
tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
tuple->t_datamcxt = CurrentMemoryContext;
td = tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
MemSet((char *) td, 0, len);
tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
/*
* And fill in the information. Note we fill the Datum fields even
* though this tuple may never become a Datum.
*/
tuple->t_len = len;
ItemPointerSetInvalid(&(tuple->t_self));
tuple->t_tableOid = InvalidOid;
HeapTupleHeaderSetDatumLength(td, len);
HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod);
td->t_natts = numberOfAttributes;
td->t_hoff = hoff;
@@ -759,15 +791,15 @@ heap_addheader(int natts, /* max domain index */
hoff = MAXALIGN(hoff);
len = hoff + structlen;
tuple = (HeapTuple) palloc(HEAPTUPLESIZE + len);
tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
tuple->t_datamcxt = CurrentMemoryContext;
tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
tuple->t_len = len;
ItemPointerSetInvalid(&(tuple->t_self));
tuple->t_tableOid = InvalidOid;
tuple->t_datamcxt = CurrentMemoryContext;
tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
MemSet((char *) td, 0, hoff);
/* we don't bother to fill the Datum fields */
td->t_natts = natts;
td->t_hoff = hoff;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.101 2003/11/29 19:51:39 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.102 2004/04/01 21:28:43 tgl Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
@@ -28,12 +28,16 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
/* ----------------------------------------------------------------
* CreateTemplateTupleDesc
*
* This function allocates and zeros a tuple descriptor structure.
*
* Tuple type ID information is initially set for an anonymous record type;
* caller can overwrite this if needed.
* ----------------------------------------------------------------
*/
TupleDesc
@@ -47,24 +51,26 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
AssertArg(natts >= 0);
/*
* allocate enough memory for the tuple descriptor and zero it as
* TupleDescInitEntry assumes that the descriptor is filled with NULL
* pointers.
* Allocate enough memory for the tuple descriptor, and zero the
* attrs[] array since TupleDescInitEntry assumes that the array
* is filled with NULL pointers.
*/
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = natts;
desc->tdhasoid = hasoid;
if (natts > 0)
{
uint32 size = natts * sizeof(Form_pg_attribute);
desc->attrs = (Form_pg_attribute *) palloc0(size);
}
desc->attrs = (Form_pg_attribute *)
palloc0(natts * sizeof(Form_pg_attribute));
else
desc->attrs = NULL;
/*
* Initialize other fields of the tupdesc.
*/
desc->natts = natts;
desc->constr = NULL;
desc->tdtypeid = RECORDOID;
desc->tdtypmod = -1;
desc->tdhasoid = hasoid;
return desc;
}
@@ -74,6 +80,9 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
*
* This function allocates a new TupleDesc pointing to a given
* Form_pg_attribute array
*
* Tuple type ID information is initially set for an anonymous record type;
* caller can overwrite this if needed.
* ----------------------------------------------------------------
*/
TupleDesc
@@ -90,6 +99,8 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
desc->attrs = attrs;
desc->natts = natts;
desc->constr = NULL;
desc->tdtypeid = RECORDOID;
desc->tdtypmod = -1;
desc->tdhasoid = hasoid;
return desc;
@@ -101,22 +112,21 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
* This function creates a new TupleDesc by copying from an existing
* TupleDesc
*
* !!! Constraints are not copied !!!
* !!! Constraints and defaults are not copied !!!
* ----------------------------------------------------------------
*/
TupleDesc
CreateTupleDescCopy(TupleDesc tupdesc)
{
TupleDesc desc;
int i,
size;
int i;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts;
if (desc->natts > 0)
{
size = desc->natts * sizeof(Form_pg_attribute);
desc->attrs = (Form_pg_attribute *) palloc(size);
desc->attrs = (Form_pg_attribute *)
palloc(desc->natts * sizeof(Form_pg_attribute));
for (i = 0; i < desc->natts; i++)
{
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
@@ -127,7 +137,11 @@ CreateTupleDescCopy(TupleDesc tupdesc)
}
else
desc->attrs = NULL;
desc->constr = NULL;
desc->tdtypeid = tupdesc->tdtypeid;
desc->tdtypmod = tupdesc->tdtypmod;
desc->tdhasoid = tupdesc->tdhasoid;
return desc;
@@ -137,7 +151,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
* CreateTupleDescCopyConstr
*
* This function creates a new TupleDesc by copying from an existing
* TupleDesc (with Constraints)
* TupleDesc (including its constraints and defaults)
* ----------------------------------------------------------------
*/
TupleDesc
@@ -145,15 +159,14 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
{
TupleDesc desc;
TupleConstr *constr = tupdesc->constr;
int i,
size;
int i;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts;
if (desc->natts > 0)
{
size = desc->natts * sizeof(Form_pg_attribute);
desc->attrs = (Form_pg_attribute *) palloc(size);
desc->attrs = (Form_pg_attribute *)
palloc(desc->natts * sizeof(Form_pg_attribute));
for (i = 0; i < desc->natts; i++)
{
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
@@ -162,9 +175,10 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
}
else
desc->attrs = NULL;
if (constr)
{
TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
TupleConstr *cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
cpy->has_not_null = constr->has_not_null;
@@ -197,10 +211,16 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
else
desc->constr = NULL;
desc->tdtypeid = tupdesc->tdtypeid;
desc->tdtypmod = tupdesc->tdtypmod;
desc->tdhasoid = tupdesc->tdhasoid;
return desc;
}
/*
* Free a TupleDesc including all substructure
*/
void
FreeTupleDesc(TupleDesc tupdesc)
{
@@ -244,6 +264,10 @@ FreeTupleDesc(TupleDesc tupdesc)
/*
* Compare two TupleDesc structures for logical equality
*
* Note: we deliberately do not check the attrelid and tdtypmod fields.
* This allows typcache.c to use this routine to see if a cached record type
* matches a requested type, and is harmless for relcache.c's uses.
*/
bool
equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
@@ -254,8 +278,11 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
if (tupdesc1->natts != tupdesc2->natts)
return false;
if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
return false;
if (tupdesc1->tdhasoid != tupdesc2->tdhasoid)
return false;
for (i = 0; i < tupdesc1->natts; i++)
{
Form_pg_attribute attr1 = tupdesc1->attrs[i];
@@ -265,6 +292,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
* We do not need to check every single field here: we can
* disregard attrelid, attnum (it was used to place the row in the
* attrs array) and everything derived from the column datatype.
* Also, attcacheoff must NOT be checked since it's possibly not
* set in both copies.
*/
if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
return false;
@@ -272,6 +301,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attstattarget != attr2->attstattarget)
return false;
if (attr1->attndims != attr2->attndims)
return false;
if (attr1->atttypmod != attr2->atttypmod)
return false;
if (attr1->attstorage != attr2->attstorage)
@@ -287,6 +318,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
if (attr1->attinhcount != attr2->attinhcount)
return false;
}
if (tupdesc1->constr != NULL)
{
TupleConstr *constr1 = tupdesc1->constr;
@@ -360,8 +392,7 @@ TupleDescInitEntry(TupleDesc desc,
const char *attributeName,
Oid oidtypeid,
int32 typmod,
int attdim,
bool attisset)
int attdim)
{
HeapTuple tuple;
Form_pg_type typeForm;
@@ -403,7 +434,6 @@ TupleDescInitEntry(TupleDesc desc,
att->attnum = attributeNumber;
att->attndims = attdim;
att->attisset = attisset;
att->attnotnull = false;
att->atthasdef = false;
@@ -416,70 +446,13 @@ TupleDescInitEntry(TupleDesc desc,
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", oidtypeid);
/*
* type info exists so we initialize our attribute information from
* the type tuple we found..
*/
typeForm = (Form_pg_type) GETSTRUCT(tuple);
att->atttypid = HeapTupleGetOid(tuple);
/*
* There are a couple of cases where we must override the information
* stored in pg_type.
*
* First: if this attribute is a set, what is really stored in the
* attribute is the OID of a tuple in the pg_proc catalog. The pg_proc
* tuple contains the query string which defines this set - i.e., the
* query to run to get the set. So the atttypid (just assigned above)
* refers to the type returned by this query, but the actual length of
* this attribute is the length (size) of an OID.
*
* (Why not just make the atttypid point to the OID type, instead of the
* type the query returns? Because the executor uses the atttypid to
* tell the front end what type will be returned, and in the end the
* type returned will be the result of the query, not an OID.)
*
* (Why not wait until the return type of the set is known (i.e., the
* recursive call to the executor to execute the set has returned)
* before telling the front end what the return type will be? Because
* the executor is a delicate thing, and making sure that the correct
* order of front-end commands is maintained is messy, especially
* considering that target lists may change as inherited attributes
* are considered, etc. Ugh.)
*
* Second: if we are dealing with a complex type (a tuple type), then
* pg_type will say that the representation is the same as Oid. But
* if typmod is sizeof(Pointer) then the internal representation is
* actually a pointer to a TupleTableSlot, and we have to substitute
* that information.
*
* A set of complex type is first and foremost a set, so its
* representation is Oid not pointer. So, test that case first.
*/
if (attisset)
{
att->attlen = sizeof(Oid);
att->attbyval = true;
att->attalign = 'i';
att->attstorage = 'p';
}
else if (typeForm->typtype == 'c' && typmod == sizeof(Pointer))
{
att->attlen = sizeof(Pointer);
att->attbyval = true;
att->attalign = 'd'; /* kluge to work with 8-byte pointers */
/* XXX ought to have a separate attalign value for pointers ... */
att->attstorage = 'p';
}
else
{
att->attlen = typeForm->typlen;
att->attbyval = typeForm->typbyval;
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
}
att->atttypid = oidtypeid;
att->attlen = typeForm->typlen;
att->attbyval = typeForm->typbyval;
att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage;
ReleaseSysCache(tuple);
}
@@ -491,7 +464,8 @@ TupleDescInitEntry(TupleDesc desc,
* Given a relation schema (list of ColumnDef nodes), build a TupleDesc.
*
* Note: the default assumption is no OIDs; caller may modify the returned
* TupleDesc if it wants OIDs.
* TupleDesc if it wants OIDs. Also, tdtypeid will need to be filled in
* later on.
*/
TupleDesc
BuildDescForRelation(List *schema)
@@ -501,12 +475,11 @@ BuildDescForRelation(List *schema)
List *p;
TupleDesc desc;
AttrDefault *attrdef = NULL;
TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
char *attname;
int32 atttypmod;
int attdim;
int ndef = 0;
bool attisset;
/*
* allocate a new tuple descriptor
@@ -529,13 +502,18 @@ BuildDescForRelation(List *schema)
attnum++;
attname = entry->colname;
attisset = entry->typename->setof;
atttypmod = entry->typename->typmod;
attdim = length(entry->typename->arrayBounds);
if (entry->typename->setof)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("column \"%s\" cannot be declared SETOF",
attname)));
TupleDescInitEntry(desc, attnum, attname,
typenameTypeId(entry->typename),
atttypmod, attdim, attisset);
atttypmod, attdim);
/* Fill in additional stuff not handled by TupleDescInitEntry */
if (entry->is_not_null)
@@ -586,6 +564,7 @@ BuildDescForRelation(List *schema)
pfree(constr);
desc->constr = NULL;
}
return desc;
}
@@ -603,7 +582,7 @@ RelationNameGetTupleDesc(const char *relname)
TupleDesc tupdesc;
List *relname_list;
/* Open relation and get the tuple description */
/* Open relation and copy the tuple description */
relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
relvar = makeRangeVarFromNameList(relname_list);
rel = relation_openrv(relvar, AccessShareLock);
@@ -620,7 +599,8 @@ RelationNameGetTupleDesc(const char *relname)
*
* If the type is composite, *and* a colaliases List is provided, *and*
* the List is of natts length, use the aliases instead of the relation
* attnames.
* attnames. (NB: this usage is deprecated since it may result in
* creation of unnecessary transient record types.)
*
* If the type is a base type, a single item alias List is required.
*/
@@ -635,22 +615,12 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
*/
if (functyptype == 'c')
{
/* Composite data type, i.e. a table's row type */
Oid relid = typeidTypeRelid(typeoid);
Relation rel;
int natts;
if (!OidIsValid(relid))
elog(ERROR, "invalid typrelid for complex type %u", typeoid);
rel = relation_open(relid, AccessShareLock);
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
natts = tupdesc->natts;
relation_close(rel, AccessShareLock);
/* XXX should we hold the lock to ensure table doesn't change? */
/* Composite data type, e.g. a table's row type */
tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
if (colaliases != NIL)
{
int natts = tupdesc->natts;
int varattno;
/* does the list length match the number of attributes? */
@@ -667,6 +637,10 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
if (label != NULL)
namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
}
/* The tuple type is now an anonymous record type */
tupdesc->tdtypeid = RECORDOID;
tupdesc->tdtypmod = -1;
}
}
else if (functyptype == 'b' || functyptype == 'd')
@@ -695,13 +669,15 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
attname,
typeoid,
-1,
0,
false);
0);
}
else if (functyptype == 'p' && typeoid == RECORDOID)
else if (typeoid == RECORDOID)
{
/* XXX can't support this because typmod wasn't passed in ... */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine row description for function returning record")));
}
else
{
/* crummy error message, but parser should have caught this */

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.163 2004/03/11 01:47:35 ishii Exp $
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.164 2004/04/01 21:28:43 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -1116,6 +1116,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid)
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
HeapTupleHeaderSetXmin(tup->t_data, GetCurrentTransactionId());
HeapTupleHeaderSetCmin(tup->t_data, cid);
HeapTupleHeaderSetCmax(tup->t_data, 0); /* zero out Datum fields */
tup->t_tableOid = relation->rd_id;
/*
@@ -1576,6 +1577,7 @@ l2:
newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
HeapTupleHeaderSetXmin(newtup->t_data, GetCurrentTransactionId());
HeapTupleHeaderSetCmin(newtup->t_data, cid);
HeapTupleHeaderSetCmax(newtup->t_data, 0); /* zero out Datum fields */
/*
* If the toaster needs to be activated, OR if the new tuple will not

View File

@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.177 2004/02/25 19:41:22 momjian Exp $
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.178 2004/04/01 21:28:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -107,34 +107,55 @@ struct typinfo
Oid oid;
Oid elem;
int16 len;
bool byval;
char align;
char storage;
Oid inproc;
Oid outproc;
};
static struct typinfo Procid[] = {
{"bool", BOOLOID, 0, 1, F_BOOLIN, F_BOOLOUT},
{"bytea", BYTEAOID, 0, -1, F_BYTEAIN, F_BYTEAOUT},
{"char", CHAROID, 0, 1, F_CHARIN, F_CHAROUT},
{"name", NAMEOID, 0, NAMEDATALEN, F_NAMEIN, F_NAMEOUT},
{"int2", INT2OID, 0, 2, F_INT2IN, F_INT2OUT},
{"int2vector", INT2VECTOROID, 0, INDEX_MAX_KEYS * 2, F_INT2VECTORIN, F_INT2VECTOROUT},
{"int4", INT4OID, 0, 4, F_INT4IN, F_INT4OUT},
{"regproc", REGPROCOID, 0, 4, F_REGPROCIN, F_REGPROCOUT},
{"regclass", REGCLASSOID, 0, 4, F_REGCLASSIN, F_REGCLASSOUT},
{"regtype", REGTYPEOID, 0, 4, F_REGTYPEIN, F_REGTYPEOUT},
{"text", TEXTOID, 0, -1, F_TEXTIN, F_TEXTOUT},
{"oid", OIDOID, 0, 4, F_OIDIN, F_OIDOUT},
{"tid", TIDOID, 0, 6, F_TIDIN, F_TIDOUT},
{"xid", XIDOID, 0, 4, F_XIDIN, F_XIDOUT},
{"cid", CIDOID, 0, 4, F_CIDIN, F_CIDOUT},
{"oidvector", OIDVECTOROID, 0, INDEX_MAX_KEYS * 4, F_OIDVECTORIN, F_OIDVECTOROUT},
{"smgr", 210, 0, 2, F_SMGRIN, F_SMGROUT},
{"_int4", 1007, INT4OID, -1, F_ARRAY_IN, F_ARRAY_OUT},
{"_text", 1009, TEXTOID, -1, F_ARRAY_IN, F_ARRAY_OUT},
{"_aclitem", 1034, 1033, -1, F_ARRAY_IN, F_ARRAY_OUT}
static const struct typinfo TypInfo[] = {
{"bool", BOOLOID, 0, 1, true, 'c', 'p',
F_BOOLIN, F_BOOLOUT},
{"bytea", BYTEAOID, 0, -1, false, 'i', 'x',
F_BYTEAIN, F_BYTEAOUT},
{"char", CHAROID, 0, 1, true, 'c', 'p',
F_CHARIN, F_CHAROUT},
{"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'i', 'p',
F_NAMEIN, F_NAMEOUT},
{"int2", INT2OID, 0, 2, true, 's', 'p',
F_INT2IN, F_INT2OUT},
{"int4", INT4OID, 0, 4, true, 'i', 'p',
F_INT4IN, F_INT4OUT},
{"regproc", REGPROCOID, 0, 4, true, 'i', 'p',
F_REGPROCIN, F_REGPROCOUT},
{"regclass", REGCLASSOID, 0, 4, true, 'i', 'p',
F_REGCLASSIN, F_REGCLASSOUT},
{"regtype", REGTYPEOID, 0, 4, true, 'i', 'p',
F_REGTYPEIN, F_REGTYPEOUT},
{"text", TEXTOID, 0, -1, false, 'i', 'x',
F_TEXTIN, F_TEXTOUT},
{"oid", OIDOID, 0, 4, true, 'i', 'p',
F_OIDIN, F_OIDOUT},
{"tid", TIDOID, 0, 6, false, 's', 'p',
F_TIDIN, F_TIDOUT},
{"xid", XIDOID, 0, 4, true, 'i', 'p',
F_XIDIN, F_XIDOUT},
{"cid", CIDOID, 0, 4, true, 'i', 'p',
F_CIDIN, F_CIDOUT},
{"int2vector", INT2VECTOROID, INT2OID, INDEX_MAX_KEYS * 2, false, 's', 'p',
F_INT2VECTORIN, F_INT2VECTOROUT},
{"oidvector", OIDVECTOROID, OIDOID, INDEX_MAX_KEYS * 4, false, 'i', 'p',
F_OIDVECTORIN, F_OIDVECTOROUT},
{"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x',
F_ARRAY_IN, F_ARRAY_OUT},
{"_text", 1009, TEXTOID, -1, false, 'i', 'x',
F_ARRAY_IN, F_ARRAY_OUT},
{"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x',
F_ARRAY_IN, F_ARRAY_OUT}
};
static int n_types = sizeof(Procid) / sizeof(struct typinfo);
static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo);
struct typmap
{ /* a hack */
@@ -697,44 +718,13 @@ DefineAttr(char *name, char *type, int attnum)
}
else
{
attrtypes[attnum]->atttypid = Procid[typeoid].oid;
attlen = attrtypes[attnum]->attlen = Procid[typeoid].len;
/*
* Cheat like mad to fill in these items from the length only.
* This only has to work for types that appear in Procid[].
*/
switch (attlen)
{
case 1:
attrtypes[attnum]->attbyval = true;
attrtypes[attnum]->attstorage = 'p';
attrtypes[attnum]->attalign = 'c';
break;
case 2:
attrtypes[attnum]->attbyval = true;
attrtypes[attnum]->attstorage = 'p';
attrtypes[attnum]->attalign = 's';
break;
case 4:
attrtypes[attnum]->attbyval = true;
attrtypes[attnum]->attstorage = 'p';
attrtypes[attnum]->attalign = 'i';
break;
case -1:
attrtypes[attnum]->attbyval = false;
attrtypes[attnum]->attstorage = 'x';
attrtypes[attnum]->attalign = 'i';
break;
default:
/* TID and fixed-length arrays, such as oidvector */
attrtypes[attnum]->attbyval = false;
attrtypes[attnum]->attstorage = 'p';
attrtypes[attnum]->attalign = 'i';
break;
}
attrtypes[attnum]->atttypid = TypInfo[typeoid].oid;
attlen = attrtypes[attnum]->attlen = TypInfo[typeoid].len;
attrtypes[attnum]->attbyval = TypInfo[typeoid].byval;
attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
attrtypes[attnum]->attalign = TypInfo[typeoid].align;
/* if an array type, assume 1-dimensional attribute */
if (Procid[typeoid].elem != InvalidOid && attlen < 0)
if (TypInfo[typeoid].elem != InvalidOid && attlen < 0)
attrtypes[attnum]->attndims = 1;
else
attrtypes[attnum]->attndims = 0;
@@ -844,19 +834,19 @@ InsertOneValue(char *value, int i)
{
for (typeindex = 0; typeindex < n_types; typeindex++)
{
if (Procid[typeindex].oid == attrtypes[i]->atttypid)
if (TypInfo[typeindex].oid == attrtypes[i]->atttypid)
break;
}
if (typeindex >= n_types)
elog(ERROR, "type oid %u not found", attrtypes[i]->atttypid);
elog(DEBUG4, "Typ == NULL, typeindex = %u", typeindex);
values[i] = OidFunctionCall3(Procid[typeindex].inproc,
values[i] = OidFunctionCall3(TypInfo[typeindex].inproc,
CStringGetDatum(value),
ObjectIdGetDatum(Procid[typeindex].elem),
ObjectIdGetDatum(TypInfo[typeindex].elem),
Int32GetDatum(-1));
prt = DatumGetCString(OidFunctionCall3(Procid[typeindex].outproc,
prt = DatumGetCString(OidFunctionCall3(TypInfo[typeindex].outproc,
values[i],
ObjectIdGetDatum(Procid[typeindex].elem),
ObjectIdGetDatum(TypInfo[typeindex].elem),
Int32GetDatum(-1)));
elog(DEBUG4, " -> %s", prt);
pfree(prt);
@@ -930,9 +920,9 @@ cleanup(void)
/* ----------------
* gettype
*
* NB: this is really ugly; it will return an integer index into Procid[],
* NB: this is really ugly; it will return an integer index into TypInfo[],
* and not an OID at all, until the first reference to a type not known in
* Procid[]. At that point it will read and cache pg_type in the Typ array,
* TypInfo[]. At that point it will read and cache pg_type in the Typ array,
* and subsequently return a real OID (and set the global pointer Ap to
* point at the found row in Typ). So caller must check whether Typ is
* still NULL to determine what the return value is!
@@ -962,7 +952,7 @@ gettype(char *type)
{
for (i = 0; i < n_types; i++)
{
if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0)
if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0)
return i;
}
elog(DEBUG4, "external type: %s", type);

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.261 2004/03/23 19:35:16 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.262 2004/04/01 21:28:43 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -97,37 +97,37 @@ static void SetRelationNumChecks(Relation rel, int numchecks);
static FormData_pg_attribute a1 = {
0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
SelfItemPointerAttributeNumber, 0, -1, -1,
false, 'p', false, 'i', true, false, false, true, 0
false, 'p', 's', true, false, false, true, 0
};
static FormData_pg_attribute a2 = {
0, {"oid"}, OIDOID, 0, sizeof(Oid),
ObjectIdAttributeNumber, 0, -1, -1,
true, 'p', false, 'i', true, false, false, true, 0
true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a3 = {
0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
MinTransactionIdAttributeNumber, 0, -1, -1,
true, 'p', false, 'i', true, false, false, true, 0
true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a4 = {
0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
MinCommandIdAttributeNumber, 0, -1, -1,
true, 'p', false, 'i', true, false, false, true, 0
true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a5 = {
0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
MaxTransactionIdAttributeNumber, 0, -1, -1,
true, 'p', false, 'i', true, false, false, true, 0
true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a6 = {
0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
MaxCommandIdAttributeNumber, 0, -1, -1,
true, 'p', false, 'i', true, false, false, true, 0
true, 'p', 'i', true, false, false, true, 0
};
/*
@@ -139,7 +139,7 @@ static FormData_pg_attribute a6 = {
static FormData_pg_attribute a7 = {
0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
TableOidAttributeNumber, 0, -1, -1,
true, 'p', false, 'i', true, false, false, true, 0
true, 'p', 'i', true, false, false, true, 0
};
static Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
@@ -633,6 +633,8 @@ AddNewRelationTuple(Relation pg_class_desc,
new_rel_reltup->reltype = new_type_oid;
new_rel_reltup->relkind = relkind;
new_rel_desc->rd_att->tdtypeid = new_type_oid;
/* ----------------
* now form a tuple to add to pg_class
* XXX Natts_pg_class_fixed is a hack - see pg_class.h
@@ -660,7 +662,7 @@ AddNewRelationTuple(Relation pg_class_desc,
/* --------------------------------
* AddNewRelationType -
*
* define a complex type corresponding to the new relation
* define a composite type corresponding to the new relation
* --------------------------------
*/
static void
@@ -670,27 +672,12 @@ AddNewRelationType(const char *typeName,
char new_rel_kind,
Oid new_type_oid)
{
/*
* We set the I/O procedures of a complex type to record_in and
* record_out, so that a user will get an error message not a weird
* number if he tries to SELECT a complex type.
*
* OLD and probably obsolete comments:
*
* The sizes are set to oid size because it makes implementing sets MUCH
* easier, and no one (we hope) uses these fields to figure out how
* much space to allocate for the type. An oid is the type used for a
* set definition. When a user requests a set, what they actually get
* is the oid of a tuple in the pg_proc catalog, so the size of the
* "set" is the size of an oid. Similarly, byval being true makes sets
* much easier, and it isn't used by anything else.
*/
TypeCreate(typeName, /* type name */
typeNamespace, /* type namespace */
new_type_oid, /* preassigned oid for type */
new_rel_oid, /* relation oid */
new_rel_kind, /* relation kind */
sizeof(Oid), /* internal size */
-1, /* internal size (varlena) */
'c', /* type-type (complex) */
',', /* default array delimiter */
F_RECORD_IN, /* input procedure */
@@ -702,9 +689,9 @@ AddNewRelationType(const char *typeName,
InvalidOid, /* domain base type - irrelevant */
NULL, /* default type value - none */
NULL, /* default type binary representation */
true, /* passed by value */
'i', /* default alignment - same as for OID */
'p', /* Not TOASTable */
false, /* passed by reference */
'd', /* alignment - must be the largest! */
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
false); /* Type NOT NULL */

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.113 2004/03/21 22:29:10 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.114 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,7 +32,6 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/sets.h"
#include "utils/syscache.h"
@@ -137,44 +136,6 @@ ProcedureCreate(const char *procedureName,
/* Process param names, if given */
namesarray = create_parameternames_array(parameterCount, parameterNames);
if (languageObjectId == SQLlanguageId)
{
/*
* If this call is defining a set, check if the set is already
* defined by looking to see whether this call's function text
* matches a function already in pg_proc. If so just return the
* OID of the existing set.
*/
if (strcmp(procedureName, GENERICSETNAME) == 0)
{
#ifdef SETS_FIXED
/*
* The code below doesn't work any more because the PROSRC
* system cache and the pg_proc_prosrc_index have been
* removed. Instead a sequential heap scan or something better
* must get implemented. The reason for removing is that
* nbtree index crashes if sources exceed 2K --- what's likely
* for procedural languages.
*
* 1999/09/30 Jan
*/
text *prosrctext;
prosrctext = DatumGetTextP(DirectFunctionCall1(textin,
CStringGetDatum(prosrc)));
retval = GetSysCacheOid(PROSRC,
PointerGetDatum(prosrctext),
0, 0, 0);
pfree(prosrctext);
if (OidIsValid(retval))
return retval;
#else
elog(ERROR, "lookup for procedure by source needs fix (Jan)");
#endif /* SETS_FIXED */
}
}
/*
* don't allow functions of complex types that have the same name as
* existing attributes of the type

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.119 2004/01/31 05:09:40 neilc Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.120 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -125,7 +125,7 @@ ExplainResultDesc(ExplainStmt *stmt)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
return tupdesc;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.101 2004/03/23 19:35:16 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.102 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1796,7 +1796,6 @@ AlterTableAddColumn(Oid myrelid,
attribute->attnum = i;
attribute->attbyval = tform->typbyval;
attribute->attndims = attndims;
attribute->attisset = (bool) (tform->typtype == 'c');
attribute->attstorage = tform->typstorage;
attribute->attalign = tform->typalign;
attribute->attnotnull = colDef->is_not_null;
@@ -4084,15 +4083,15 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
TupleDescInitEntry(tupdesc, (AttrNumber) 1,
"chunk_id",
OIDOID,
-1, 0, false);
-1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2,
"chunk_seq",
INT4OID,
-1, 0, false);
-1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3,
"chunk_data",
BYTEAOID,
-1, 0, false);
-1, 0);
/*
* Ensure that the toast table doesn't itself get toasted, or we'll be

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.157 2004/03/24 22:40:28 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.158 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,6 +49,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
/* static function decls */
@@ -110,7 +111,7 @@ static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalFieldSelect(GenericExprState *fstate,
static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalRelabelType(GenericExprState *exprstate,
@@ -420,16 +421,25 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
*isDone = ExprSingleResult;
/*
* get the slot we want
* Get the slot and attribute number we want
*
* The asserts check that references to system attributes only appear
* at the level of a relation scan; at higher levels, system attributes
* must be treated as ordinary variables (since we no longer have access
* to the original tuple).
*/
attnum = variable->varattno;
switch (variable->varno)
{
case INNER: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple;
Assert(attnum > 0);
break;
case OUTER: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple;
Assert(attnum > 0);
break;
default: /* get the tuple from the relation being
@@ -444,8 +454,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
heapTuple = slot->val;
tuple_type = slot->ttc_tupleDescriptor;
attnum = variable->varattno;
/*
* Some checks that are only applied for user attribute numbers
* (bogus system attnums will be caught inside heap_getattr).
@@ -481,38 +489,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid);
}
/*
* If the attribute number is invalid, then we are supposed to return
* the entire tuple; we give back a whole slot so that callers know
* what the tuple looks like.
*
* XXX this is a horrid crock: since the pointer to the slot might live
* longer than the current evaluation context, we are forced to copy
* the tuple and slot into a long-lived context --- we use the
* econtext's per-query memory which should be safe enough. This
* represents a serious memory leak if many such tuples are processed
* in one command, however. We ought to redesign the representation
* of whole-tuple datums so that this is not necessary.
*
* We assume it's OK to point to the existing tupleDescriptor, rather
* than copy that too.
*/
if (attnum == InvalidAttrNumber)
{
MemoryContext oldContext;
TupleTableSlot *tempSlot;
HeapTuple tup;
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tempSlot = MakeTupleTableSlot();
tup = heap_copytuple(heapTuple);
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
ExecSetSlotDescriptor(tempSlot, tuple_type, false);
MemoryContextSwitchTo(oldContext);
*isNull = false;
return PointerGetDatum(tempSlot);
}
result = heap_getattr(heapTuple, /* tuple containing attribute */
attnum, /* attribute number of desired
* attribute */
@@ -656,17 +632,23 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
* GetAttributeByName
* GetAttributeByNum
*
* These are functions which return the value of the
* named attribute out of the tuple from the arg slot. User defined
* These functions return the value of the requested attribute
* out of the given tuple Datum.
* C functions which take a tuple as an argument are expected
* to use this. Ex: overpaid(EMP) might call GetAttributeByNum().
* to use these. Ex: overpaid(EMP) might call GetAttributeByNum().
* Note: these are actually rather slow because they do a typcache
* lookup on each call.
*/
Datum
GetAttributeByNum(TupleTableSlot *slot,
GetAttributeByNum(HeapTupleHeader tuple,
AttrNumber attrno,
bool *isNull)
{
Datum retval;
Datum result;
Oid tupType;
int32 tupTypmod;
TupleDesc tupDesc;
HeapTupleData tmptup;
if (!AttributeNumberIsValid(attrno))
elog(ERROR, "invalid attribute number %d", attrno);
@@ -674,29 +656,43 @@ GetAttributeByNum(TupleTableSlot *slot,
if (isNull == NULL)
elog(ERROR, "a NULL isNull pointer was passed");
if (TupIsNull(slot))
if (tuple == NULL)
{
/* Kinda bogus but compatible with old behavior... */
*isNull = true;
return (Datum) 0;
}
retval = heap_getattr(slot->val,
attrno,
slot->ttc_tupleDescriptor,
isNull);
if (*isNull)
return (Datum) 0;
tupType = HeapTupleHeaderGetTypeId(tuple);
tupTypmod = HeapTupleHeaderGetTypMod(tuple);
tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
return retval;
/*
* heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set
* all the fields in the struct just in case user tries to inspect
* system columns.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = tuple;
result = heap_getattr(&tmptup,
attrno,
tupDesc,
isNull);
return result;
}
Datum
GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
{
AttrNumber attrno;
TupleDesc tupdesc;
Datum retval;
int natts;
Datum result;
Oid tupType;
int32 tupTypmod;
TupleDesc tupDesc;
HeapTupleData tmptup;
int i;
if (attname == NULL)
@@ -705,21 +701,23 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
if (isNull == NULL)
elog(ERROR, "a NULL isNull pointer was passed");
if (TupIsNull(slot))
if (tuple == NULL)
{
/* Kinda bogus but compatible with old behavior... */
*isNull = true;
return (Datum) 0;
}
tupdesc = slot->ttc_tupleDescriptor;
natts = slot->val->t_data->t_natts;
tupType = HeapTupleHeaderGetTypeId(tuple);
tupTypmod = HeapTupleHeaderGetTypMod(tuple);
tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
attrno = InvalidAttrNumber;
for (i = 0; i < tupdesc->natts; i++)
for (i = 0; i < tupDesc->natts; i++)
{
if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0)
if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
{
attrno = tupdesc->attrs[i]->attnum;
attrno = tupDesc->attrs[i]->attnum;
break;
}
}
@@ -727,14 +725,21 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
if (attrno == InvalidAttrNumber)
elog(ERROR, "attribute \"%s\" does not exist", attname);
retval = heap_getattr(slot->val,
attrno,
tupdesc,
isNull);
if (*isNull)
return (Datum) 0;
/*
* heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set
* all the fields in the struct just in case user tries to inspect
* system columns.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = tuple;
return retval;
result = heap_getattr(&tmptup,
attrno,
tupDesc,
isNull);
return result;
}
/*
@@ -1133,14 +1138,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
Tuplestorestate *tupstore = NULL;
TupleDesc tupdesc = NULL;
Oid funcrettype;
bool returnsTuple;
FunctionCallInfoData fcinfo;
ReturnSetInfo rsinfo;
HeapTupleData tmptup;
MemoryContext callerContext;
MemoryContext oldcontext;
TupleTableSlot *slot;
bool direct_function_call;
bool first_time = true;
bool returnsTuple = false;
/*
* Normally the passed expression tree will be a FuncExprState, since
@@ -1216,6 +1221,9 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
funcrettype = exprType((Node *) funcexpr->expr);
returnsTuple = (funcrettype == RECORDOID ||
get_typtype(funcrettype) == 'c');
/*
* Prepare a resultinfo node for communication. We always do this
* even if not expecting a set result, so that we can pass
@@ -1281,6 +1289,16 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
if (rsinfo.isDone == ExprEndResult)
break;
/*
* Can't do anything useful with NULL rowtype values. Currently
* we raise an error, but another alternative is to just ignore
* the result and "continue" to get another row.
*/
if (returnsTuple && fcinfo.isnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("function returning row cannot return null value")));
/*
* If first time through, build tupdesc and tuplestore for
* result
@@ -1288,25 +1306,18 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
if (first_time)
{
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
if (funcrettype == RECORDOID ||
get_typtype(funcrettype) == 'c')
if (returnsTuple)
{
/*
* Composite type, so function should have returned a
* TupleTableSlot; use its descriptor
* Use the type info embedded in the rowtype Datum to
* look up the needed tupdesc. Make a copy for the query.
*/
slot = (TupleTableSlot *) DatumGetPointer(result);
if (fcinfo.isnull || !slot)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("function returning row cannot return null value")));
if (!IsA(slot, TupleTableSlot) ||
!slot->ttc_tupleDescriptor)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function returning row did not return a valid tuple slot")));
tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
returnsTuple = true;
HeapTupleHeader td;
td = DatumGetHeapTupleHeader(result);
tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td),
HeapTupleHeaderGetTypMod(td));
tupdesc = CreateTupleDescCopy(tupdesc);
}
else
{
@@ -1319,8 +1330,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
"column",
funcrettype,
-1,
0,
false);
0);
}
tupstore = tuplestore_begin_heap(true, false, work_mem);
MemoryContextSwitchTo(oldcontext);
@@ -1333,15 +1343,17 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
*/
if (returnsTuple)
{
slot = (TupleTableSlot *) DatumGetPointer(result);
if (fcinfo.isnull ||
!slot ||
!IsA(slot, TupleTableSlot) ||
TupIsNull(slot))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("function returning row cannot return null value")));
tuple = slot->val;
HeapTupleHeader td;
td = DatumGetHeapTupleHeader(result);
/*
* tuplestore_puttuple needs a HeapTuple not a bare
* HeapTupleHeader, but it doesn't need all the fields.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
tmptup.t_data = td;
tuple = &tmptup;
}
else
{
@@ -2415,26 +2427,62 @@ ExecEvalCoerceToDomainValue(ExprState *exprstate,
* ----------------------------------------------------------------
*/
static Datum
ExecEvalFieldSelect(GenericExprState *fstate,
ExecEvalFieldSelect(FieldSelectState *fstate,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
{
FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
Datum result;
TupleTableSlot *resSlot;
Datum tupDatum;
HeapTupleHeader tuple;
Oid tupType;
int32 tupTypmod;
TupleDesc tupDesc;
HeapTupleData tmptup;
result = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
/* this test covers the isDone exception too: */
if (*isNull)
return result;
return tupDatum;
resSlot = (TupleTableSlot *) DatumGetPointer(result);
Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
result = heap_getattr(resSlot->val,
tuple = DatumGetHeapTupleHeader(tupDatum);
tupType = HeapTupleHeaderGetTypeId(tuple);
tupTypmod = HeapTupleHeaderGetTypMod(tuple);
/* Lookup tupdesc if first time through or if type changes */
tupDesc = fstate->argdesc;
if (tupDesc == NULL ||
tupType != tupDesc->tdtypeid ||
tupTypmod != tupDesc->tdtypmod)
{
MemoryContext oldcontext;
tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
/* Copy the tupdesc into query storage for safety */
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tupDesc = CreateTupleDescCopy(tupDesc);
if (fstate->argdesc)
FreeTupleDesc(fstate->argdesc);
fstate->argdesc = tupDesc;
MemoryContextSwitchTo(oldcontext);
}
/*
* heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set
* all the fields in the struct just in case user tries to inspect
* system columns.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = tuple;
result = heap_getattr(&tmptup,
fselect->fieldnum,
resSlot->ttc_tupleDescriptor,
tupDesc,
isNull);
return result;
}
@@ -2703,11 +2751,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
case T_FieldSelect:
{
FieldSelect *fselect = (FieldSelect *) node;
GenericExprState *gstate = makeNode(GenericExprState);
FieldSelectState *fstate = makeNode(FieldSelectState);
gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
gstate->arg = ExecInitExpr(fselect->arg, parent);
state = (ExprState *) gstate;
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
fstate->arg = ExecInitExpr(fselect->arg, parent);
fstate->argdesc = NULL;
state = (ExprState *) fstate;
}
break;
case T_RelabelType:
@@ -3088,8 +3137,6 @@ ExecTargetList(List *targetlist,
List *tl;
bool isNull;
bool haveDoneSets;
static struct tupleDesc NullTupleDesc; /* we assume this inits to
* zeroes */
/*
* debugging stuff
@@ -3106,13 +3153,8 @@ ExecTargetList(List *targetlist,
/*
* There used to be some klugy and demonstrably broken code here that
* special-cased the situation where targetlist == NIL. Now we just
* fall through and return an empty-but-valid tuple. We do, however,
* have to cope with the possibility that targettype is NULL ---
* heap_formtuple won't like that, so pass a dummy descriptor with
* natts = 0 to deal with it.
* fall through and return an empty-but-valid tuple.
*/
if (targettype == NULL)
targettype = &NullTupleDesc;
/*
* evaluate all the expressions in the target list
@@ -3285,8 +3327,8 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
/*
* store the tuple in the projection slot and return the slot.
*/
return ExecStoreTuple(newTuple, /* tuple to store */
slot, /* slot to store in */
InvalidBuffer, /* tuple has no buffer */
return ExecStoreTuple(newTuple, /* tuple to store */
slot, /* slot to store in */
InvalidBuffer, /* tuple has no buffer */
true);
}

View File

@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.75 2004/01/07 18:56:26 neilc Exp $
* $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.76 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -109,8 +109,11 @@
#include "funcapi.h"
#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
static TupleDesc ExecTypeFromTLInternal(List *targetList,
bool hasoid, bool skipjunk);
@@ -144,16 +147,11 @@ ExecCreateTupleTable(int initialSize) /* initial number of slots in
/*
* Now allocate our new table along with space for the pointers to the
* tuples.
* tuples. Zero out the slots.
*/
newtable = (TupleTable) palloc(sizeof(TupleTableData));
array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot));
/*
* clean out the slots we just allocated
*/
MemSet(array, 0, initialSize * sizeof(TupleTableSlot));
array = (TupleTableSlot *) palloc0(initialSize * sizeof(TupleTableSlot));
/*
* initialize the new table and return it to the caller.
@@ -514,6 +512,10 @@ TupleTableSlot *
ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{
TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
struct tupleDesc nullTupleDesc;
HeapTuple nullTuple;
Datum values[1];
char nulls[1];
/*
* Since heap_getattr() will treat attributes beyond a tuple's t_natts
@@ -521,16 +523,13 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
* of zero length. However, the slot descriptor must match the real
* tupType.
*/
HeapTuple nullTuple;
Datum values[1];
char nulls[1];
static struct tupleDesc NullTupleDesc; /* we assume this inits to
* zeroes */
nullTupleDesc = *tupType;
nullTupleDesc.natts = 0;
nullTuple = heap_formtuple(&nullTupleDesc, values, nulls);
ExecSetSlotDescriptor(slot, tupType, false);
nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
}
@@ -590,21 +589,45 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
resdom->resname,
resdom->restype,
resdom->restypmod,
0,
false);
0);
}
return typeInfo;
}
/*
* BlessTupleDesc - make a completed tuple descriptor useful for SRFs
*
* Rowtype Datums returned by a function must contain valid type information.
* This happens "for free" if the tupdesc came from a relcache entry, but
* not if we have manufactured a tupdesc for a transient RECORD datatype.
* In that case we have to notify typcache.c of the existence of the type.
*/
TupleDesc
BlessTupleDesc(TupleDesc tupdesc)
{
if (tupdesc->tdtypeid == RECORDOID &&
tupdesc->tdtypmod < 0)
assign_record_type_typmod(tupdesc);
return tupdesc; /* just for notational convenience */
}
/*
* TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
*
* Note: this is obsolete; it is sufficient to call BlessTupleDesc on
* the tupdesc. We keep it around just for backwards compatibility with
* existing user-written SRFs.
*/
TupleTableSlot *
TupleDescGetSlot(TupleDesc tupdesc)
{
TupleTableSlot *slot;
/* The useful work is here */
BlessTupleDesc(tupdesc);
/* Make a standalone slot */
slot = MakeTupleTableSlot();
@@ -634,6 +657,9 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
/* "Bless" the tupledesc so that we can make rowtype datums with it */
attinmeta->tupdesc = BlessTupleDesc(tupdesc);
/*
* Gather info needed later to call the "in" function for each
* attribute
@@ -653,7 +679,6 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
atttypmods[i] = tupdesc->attrs[i]->atttypmod;
}
}
attinmeta->tupdesc = tupdesc;
attinmeta->attinfuncs = attinfuncinfo;
attinmeta->attelems = attelems;
attinmeta->atttypmods = atttypmods;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.78 2004/03/21 22:29:11 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.79 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,8 +24,10 @@
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
/*
@@ -61,10 +63,6 @@ typedef struct
bool returnsTuple; /* true if return type is a tuple */
bool shutdown_reg; /* true if registered shutdown callback */
TupleTableSlot *funcSlot; /* if one result we need to copy it before
* we end execution of the function and
* free stuff */
ParamListInfo paramLI; /* Param list representing current args */
/* head of linked list of execution_state records */
@@ -196,34 +194,9 @@ init_sql_fcache(FmgrInfo *finfo)
* get the type length and by-value flag from the type tuple
*/
fcache->typlen = typeStruct->typlen;
if (typeStruct->typtype != 'c' && rettype != RECORDOID)
{
/* The return type is not a composite type, so just use byval */
fcache->typbyval = typeStruct->typbyval;
fcache->returnsTuple = false;
}
else
{
/*
* This is a hack. We assume here that any function returning a
* tuple returns it by reference. This needs to be fixed, since
* actually the mechanism isn't quite like return-by-reference.
*/
fcache->typbyval = false;
fcache->returnsTuple = true;
}
/*
* If we are returning exactly one result then we have to copy tuples
* and by reference results because we have to end the execution
* before we return the results. When you do this everything
* allocated by the executor (i.e. slots and tuples) is freed.
*/
if (!finfo->fn_retset && !fcache->typbyval)
fcache->funcSlot = MakeTupleTableSlot();
else
fcache->funcSlot = NULL;
fcache->typbyval = typeStruct->typbyval;
fcache->returnsTuple = (typeStruct->typtype == 'c' ||
rettype == RECORDOID);
/*
* Parse and plan the queries. We need the argument type info to pass
@@ -366,39 +339,6 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
fcache->paramLI = paramLI;
}
static TupleTableSlot *
copy_function_result(SQLFunctionCachePtr fcache,
TupleTableSlot *resultSlot)
{
TupleTableSlot *funcSlot;
TupleDesc resultTd;
HeapTuple resultTuple;
HeapTuple newTuple;
Assert(!TupIsNull(resultSlot));
resultTuple = resultSlot->val;
funcSlot = fcache->funcSlot;
if (funcSlot == NULL)
return resultSlot; /* no need to copy result */
/*
* If first time through, we have to initialize the funcSlot's tuple
* descriptor.
*/
if (funcSlot->ttc_tupleDescriptor == NULL)
{
resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
ExecSetSlotDescriptor(funcSlot, resultTd, true);
ExecSetSlotDescriptorIsNew(funcSlot, true);
}
newTuple = heap_copytuple(resultTuple);
return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
}
static Datum
postquel_execute(execution_state *es,
FunctionCallInfo fcinfo,
@@ -429,43 +369,51 @@ postquel_execute(execution_state *es,
if (LAST_POSTQUEL_COMMAND(es))
{
TupleTableSlot *resSlot;
/*
* Copy the result. copy_function_result is smart enough to do
* nothing when no action is called for. This helps reduce the
* logic and code redundancy here.
* Set up to return the function value.
*/
resSlot = copy_function_result(fcache, slot);
HeapTuple tup = slot->val;
TupleDesc tupDesc = slot->ttc_tupleDescriptor;
/*
* If we are supposed to return a tuple, we return the tuple slot
* pointer converted to Datum. If we are supposed to return a
* simple value, then project out the first attribute of the
* result tuple (ie, take the first result column of the final
* SELECT).
*/
if (fcache->returnsTuple)
{
/*
* We are returning the whole tuple, so copy it into current
* execution context and make sure it is a valid Datum.
*
* XXX do we need to remove junk attrs from the result tuple?
* Probably OK to leave them, as long as they are at the end.
*/
value = PointerGetDatum(resSlot);
HeapTupleHeader dtup;
dtup = (HeapTupleHeader) palloc(tup->t_len);
memcpy((char *) dtup, (char *) tup->t_data, tup->t_len);
/*
* For RECORD results, make sure a typmod has been assigned.
*/
if (tupDesc->tdtypeid == RECORDOID &&
tupDesc->tdtypmod < 0)
assign_record_type_typmod(tupDesc);
HeapTupleHeaderSetDatumLength(dtup, tup->t_len);
HeapTupleHeaderSetTypeId(dtup, tupDesc->tdtypeid);
HeapTupleHeaderSetTypMod(dtup, tupDesc->tdtypmod);
value = PointerGetDatum(dtup);
fcinfo->isnull = false;
}
else
{
value = heap_getattr(resSlot->val,
1,
resSlot->ttc_tupleDescriptor,
&(fcinfo->isnull));
/*
* Note: if result type is pass-by-reference then we are
* returning a pointer into the tuple copied by
* copy_function_result. This is OK.
* Returning a scalar, which we have to extract from the
* first column of the SELECT result, and then copy into current
* execution context if needed.
*/
value = heap_getattr(tup, 1, tupDesc, &(fcinfo->isnull));
if (!fcinfo->isnull)
value = datumCopy(value, fcache->typbyval, fcache->typlen);
}
/*

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.23 2003/11/29 19:51:48 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.24 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,6 +32,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
static TupleTableSlot *FunctionNext(FunctionScanState *node);
@@ -194,25 +195,12 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
if (functyptype == 'c')
{
/*
* Composite data type, i.e. a table's row type
*/
Oid funcrelid;
Relation rel;
funcrelid = typeidTypeRelid(funcrettype);
if (!OidIsValid(funcrelid))
elog(ERROR, "invalid typrelid for complex type %u",
funcrettype);
rel = relation_open(funcrelid, AccessShareLock);
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
relation_close(rel, AccessShareLock);
/* Composite data type, e.g. a table's row type */
tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(funcrettype, -1));
}
else if (functyptype == 'b' || functyptype == 'd')
{
/*
* Must be a base data type, i.e. scalar
*/
/* Must be a base data type, i.e. scalar */
char *attname = strVal(lfirst(rte->eref->colnames));
tupdesc = CreateTemplateTupleDesc(1, false);
@@ -221,14 +209,11 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
attname,
funcrettype,
-1,
0,
false);
0);
}
else if (functyptype == 'p' && funcrettype == RECORDOID)
else if (funcrettype == RECORDOID)
{
/*
* Must be a pseudo type, i.e. record
*/
/* Must be a pseudo type, i.e. record */
tupdesc = BuildDescForRelation(rte->coldeflist);
}
else
@@ -237,6 +222,14 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
elog(ERROR, "function in FROM has unsupported return type");
}
/*
* For RECORD results, make sure a typmod has been assigned. (The
* function should do this for itself, but let's cover things in case
* it doesn't.)
*/
if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0)
assign_record_type_typmod(tupdesc);
scanstate->tupdesc = tupdesc;
ExecSetSlotDescriptor(scanstate->ss.ss_ScanTupleSlot,
tupdesc, false);

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.112 2004/03/21 22:29:11 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.113 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,6 +19,7 @@
#include "executor/spi_priv.h"
#include "tcop/tcopprot.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
uint32 SPI_processed = 0;
@@ -380,40 +381,11 @@ SPI_copytuple(HeapTuple tuple)
return ctuple;
}
TupleDesc
SPI_copytupledesc(TupleDesc tupdesc)
HeapTupleHeader
SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt = NULL;
TupleDesc ctupdesc;
if (tupdesc == NULL)
{
SPI_result = SPI_ERROR_ARGUMENT;
return NULL;
}
if (_SPI_curid + 1 == _SPI_connected) /* connected */
{
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
elog(ERROR, "SPI stack corrupted");
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
}
ctupdesc = CreateTupleDescCopy(tupdesc);
if (oldcxt)
MemoryContextSwitchTo(oldcxt);
return ctupdesc;
}
TupleTableSlot *
SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt = NULL;
TupleTableSlot *cslot;
HeapTuple ctuple;
TupleDesc ctupdesc;
HeapTupleHeader dtup;
if (tuple == NULL || tupdesc == NULL)
{
@@ -421,6 +393,11 @@ SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
return NULL;
}
/* For RECORD results, make sure a typmod has been assigned */
if (tupdesc->tdtypeid == RECORDOID &&
tupdesc->tdtypmod < 0)
assign_record_type_typmod(tupdesc);
if (_SPI_curid + 1 == _SPI_connected) /* connected */
{
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
@@ -428,17 +405,17 @@ SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
}
ctuple = heap_copytuple(tuple);
ctupdesc = CreateTupleDescCopy(tupdesc);
dtup = (HeapTupleHeader) palloc(tuple->t_len);
memcpy((char *) dtup, (char *) tuple->t_data, tuple->t_len);
cslot = MakeTupleTableSlot();
ExecSetSlotDescriptor(cslot, ctupdesc, true);
cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true);
HeapTupleHeaderSetDatumLength(dtup, tuple->t_len);
HeapTupleHeaderSetTypeId(dtup, tupdesc->tdtypeid);
HeapTupleHeaderSetTypMod(dtup, tupdesc->tdtypmod);
if (oldcxt)
MemoryContextSwitchTo(oldcxt);
return cslot;
return dtup;
}
HeapTuple

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.165 2004/01/07 18:56:27 neilc Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.166 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -170,9 +170,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* tuple from the relation. We build a special VarNode to
* reflect this -- it has varno set to the correct range table
* entry, but has varattno == 0 to signal that the whole tuple
* is the argument. Also, it has typmod set to
* sizeof(Pointer) to signal that the runtime representation
* will be a pointer not an Oid.
* is the argument.
*/
switch (rte->rtekind)
{
@@ -185,7 +183,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
lfirst(i) = makeVar(vnum,
InvalidAttrNumber,
toid,
sizeof(Pointer),
-1,
sublevels_up);
break;
case RTE_FUNCTION:
@@ -196,7 +194,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
lfirst(i) = makeVar(vnum,
InvalidAttrNumber,
toid,
sizeof(Pointer),
-1,
sublevels_up);
}
else
@@ -214,6 +212,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/*
* RTE is a join or subselect; must fail for lack of a
* named tuple type
*
* XXX FIXME
*/
if (is_column)
unknown_attribute(schemaname, relname,

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.133 2004/01/14 23:01:55 tgl Exp $
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.134 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -523,16 +523,7 @@ build_column_default(Relation rel, int attrno)
* No per-column default, so look for a default for the type
* itself.
*/
if (att_tup->attisset)
{
/*
* Set attributes are represented as OIDs no matter what the
* set element type is, and the element type's default is
* irrelevant too.
*/
}
else
expr = get_typdefault(atttype);
expr = get_typdefault(atttype);
}
if (expr == NULL)

View File

@@ -1,7 +1,7 @@
#
# Makefile for utils/adt
#
# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.56 2003/11/29 19:51:57 pgsql Exp $
# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.57 2004/04/01 21:28:45 tgl Exp $
#
subdir = src/backend/utils/adt
@@ -19,8 +19,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o float.o format_type.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o \
regexp.o regproc.o ruleutils.o selfuncs.o sets.o \
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
regexp.o regproc.o ruleutils.o selfuncs.o \
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
network.o mac.o inet_net_ntop.o inet_net_pton.o \
ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \

View File

@@ -6,7 +6,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.12 2003/11/29 19:51:58 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,22 +52,22 @@ pg_lock_status(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* build tupdesc for result tuples */
/* this had better match pg_locks view in initdb.sh */
/* this had better match pg_locks view in system_views.sql */
tupdesc = CreateTemplateTupleDesc(6, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
OIDOID, -1, 0, false);
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
OIDOID, -1, 0, false);
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "transaction",
XIDOID, -1, 0, false);
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pid",
INT4OID, -1, 0, false);
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "mode",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "granted",
BOOLOID, -1, 0, false);
BOOLOID, -1, 0);
funcctx->slot = TupleDescGetSlot(tupdesc);
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
/*
* Collect all the locking information that we will format and
@@ -173,9 +173,8 @@ pg_lock_status(PG_FUNCTION_ARGS)
CStringGetDatum(GetLockmodeName(mode)));
values[5] = BoolGetDatum(granted);
tuple = heap_formtuple(funcctx->slot->ttc_tupleDescriptor,
values, nulls);
result = TupleGetDatum(funcctx->slot, tuple);
tuple = heap_formtuple(funcctx->tuple_desc, values, nulls);
result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}

View File

@@ -16,7 +16,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.12 2003/11/29 19:51:59 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,59 +27,6 @@
#include "utils/builtins.h"
/*
* record_in - input routine for pseudo-type RECORD.
*/
Datum
record_in(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type record")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* record_out - output routine for pseudo-type RECORD.
*/
Datum
record_out(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot display a value of type record")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* record_recv - binary input routine for pseudo-type RECORD.
*/
Datum
record_recv(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type record")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* record_send - binary output routine for pseudo-type RECORD.
*/
Datum
record_send(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot display a value of type record")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* cstring_in - input routine for pseudo-type CSTRING.
*

View File

@@ -0,0 +1,75 @@
/*-------------------------------------------------------------------------
*
* rowtypes.c
* I/O functions for generic composite types.
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.1 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
/*
* record_in - input routine for any composite type.
*/
Datum
record_in(PG_FUNCTION_ARGS)
{
/* Need to decide on external format before we can write this */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("input of composite types not implemented yet")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* record_out - output routine for any composite type.
*/
Datum
record_out(PG_FUNCTION_ARGS)
{
/* Need to decide on external format before we can write this */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("output of composite types not implemented yet")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* record_recv - binary input routine for any composite type.
*/
Datum
record_recv(PG_FUNCTION_ARGS)
{
/* Need to decide on external format before we can write this */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("input of composite types not implemented yet")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* record_send - binary output routine for any composite type.
*/
Datum
record_send(PG_FUNCTION_ARGS)
{
/* Need to decide on external format before we can write this */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("output of composite types not implemented yet")));
PG_RETURN_VOID(); /* keep compiler quiet */
}

View File

@@ -1,213 +0,0 @@
/*-------------------------------------------------------------------------
*
* sets.c
* Functions for sets, which are defined by queries.
* Example: a set is defined as being the result of the query
* retrieve (X.all)
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/sets.c,v 1.62 2004/01/06 23:55:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "utils/fmgroids.h"
#include "utils/sets.h"
#include "utils/syscache.h"
/*
* SetDefine - converts query string defining set to an oid
*
* We create an SQL function having the given querystring as its body.
* The name of the function is then changed to use the OID of its tuple
* in pg_proc.
*/
Oid
SetDefine(char *querystr, Oid elemType)
{
Oid setoid;
char *procname = GENERICSETNAME;
char *fileName = "-";
char realprocname[NAMEDATALEN];
HeapTuple tup,
newtup = NULL;
Form_pg_proc proc;
Relation procrel;
int i;
Datum replValue[Natts_pg_proc];
char replNull[Natts_pg_proc];
char repl[Natts_pg_proc];
setoid = ProcedureCreate(procname, /* changed below, after oid known */
PG_CATALOG_NAMESPACE, /* XXX wrong */
false, /* don't replace */
true, /* returnsSet */
elemType, /* returnType */
SQLlanguageId, /* language */
F_FMGR_SQL_VALIDATOR,
querystr, /* prosrc */
fileName, /* probin */
false, /* not aggregate */
false, /* security invoker */
false, /* isStrict (irrelevant, no args) */
PROVOLATILE_VOLATILE, /* assume unsafe */
0, /* parameterCount */
NULL, /* parameterTypes */
NULL); /* parameterNames */
/*
* Since we're still inside this command of the transaction, we can't
* see the results of the procedure definition unless we pretend we've
* started the next command. (Postgres's solution to the Halloween
* problem is to not allow you to see the results of your command
* until you start the next command.)
*/
CommandCounterIncrement();
procrel = heap_openr(ProcedureRelationName, RowExclusiveLock);
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(setoid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for function %u", setoid);
/*
* We can tell whether the set was already defined by checking the
* name. If it's GENERICSETNAME, the set is new. If it's "set<some
* oid>" it's already defined.
*/
proc = (Form_pg_proc) GETSTRUCT(tup);
if (strcmp(procname, NameStr(proc->proname)) == 0)
{
/* make the real proc name */
snprintf(realprocname, sizeof(realprocname), "set%u", setoid);
/* set up the attributes to be modified or kept the same */
repl[0] = 'r';
for (i = 1; i < Natts_pg_proc; i++)
repl[i] = ' ';
replValue[0] = (Datum) realprocname;
for (i = 1; i < Natts_pg_proc; i++)
replValue[i] = (Datum) 0;
for (i = 0; i < Natts_pg_proc; i++)
replNull[i] = ' ';
/* change the pg_proc tuple */
newtup = heap_modifytuple(tup,
procrel,
replValue,
replNull,
repl);
simple_heap_update(procrel, &newtup->t_self, newtup);
setoid = HeapTupleGetOid(newtup);
CatalogUpdateIndexes(procrel, newtup);
heap_freetuple(newtup);
}
ReleaseSysCache(tup);
heap_close(procrel, RowExclusiveLock);
return setoid;
}
/*
* This function executes set evaluation. The parser sets up a set reference
* as a call to this function with the OID of the set to evaluate as argument.
*
* We build a new fcache for execution of the set's function and run the
* function until it says "no mas". The fn_extra field of the call's
* FmgrInfo record is a handy place to hold onto the fcache. (Since this
* is a built-in function, there is no competing use of fn_extra.)
*/
Datum
seteval(PG_FUNCTION_ARGS)
{
Oid funcoid = PG_GETARG_OID(0);
FuncExprState *fcache;
Datum result;
bool isNull;
ExprDoneCond isDone;
/*
* If this is the first call, we need to set up the fcache for the
* target set's function.
*/
fcache = (FuncExprState *) fcinfo->flinfo->fn_extra;
if (fcache == NULL)
{
MemoryContext oldcontext;
FuncExpr *func;
oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
func = makeNode(FuncExpr);
func->funcid = funcoid;
func->funcresulttype = InvalidOid; /* nothing will look at
* this */
func->funcretset = true;
func->funcformat = COERCE_EXPLICIT_CALL;
func->args = NIL; /* there are no arguments */
fcache = (FuncExprState *) ExecInitExpr((Expr *) func, NULL);
MemoryContextSwitchTo(oldcontext);
init_fcache(funcoid, fcache, fcinfo->flinfo->fn_mcxt);
fcinfo->flinfo->fn_extra = (void *) fcache;
}
/*
* Evaluate the function. NOTE: we need no econtext because there are
* no arguments to evaluate.
*/
/* ExecMakeFunctionResult assumes these are initialized at call: */
isNull = false;
isDone = ExprSingleResult;
result = ExecMakeFunctionResult(fcache,
NULL, /* no econtext, see above */
&isNull,
&isDone);
/*
* Return isNull/isDone status.
*/
fcinfo->isnull = isNull;
if (isDone != ExprSingleResult)
{
ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
if (rsi && IsA(rsi, ReturnSetInfo))
rsi->isDone = isDone;
else
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that "
"cannot accept a set")));
}
PG_RETURN_DATUM(result);
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.200 2004/03/16 05:05:58 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.201 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -63,6 +63,7 @@
#include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
/*
@@ -432,7 +433,10 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
AttrDefault *attrdef = NULL;
int ndef = 0;
relation->rd_att->tdhasoid = RelationGetForm(relation)->relhasoids;
/* copy some fields from pg_class row to rd_att */
relation->rd_att->tdtypeid = relation->rd_rel->reltype;
relation->rd_att->tdtypmod = -1; /* unnecessary, but... */
relation->rd_att->tdhasoid = relation->rd_rel->relhasoids;
constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
sizeof(TupleConstr));
@@ -1312,9 +1316,12 @@ formrdesc(const char *relationName,
* Unlike the case with the relation tuple, this data had better be right
* because it will never be replaced. The input values must be
* correctly defined by macros in src/include/catalog/ headers.
*
* Note however that rd_att's tdtypeid, tdtypmod, tdhasoid fields are
* not right at this point. They will be fixed later when the real
* pg_class row is loaded.
*/
relation->rd_att = CreateTemplateTupleDesc(natts,
relation->rd_rel->relhasoids);
relation->rd_att = CreateTemplateTupleDesc(natts, false);
/*
* initialize tuple desc info
@@ -1595,6 +1602,7 @@ RelationReloadClassinfo(Relation relation)
static void
RelationClearRelation(Relation relation, bool rebuild)
{
Oid old_reltype = relation->rd_rel->reltype;
MemoryContext oldcxt;
/*
@@ -1679,6 +1687,7 @@ RelationClearRelation(Relation relation, bool rebuild)
if (!rebuild)
{
/* ok to zap remaining substructure */
flush_rowtype_cache(old_reltype);
FreeTupleDesc(relation->rd_att);
if (relation->rd_rulescxt)
MemoryContextDelete(relation->rd_rulescxt);
@@ -1704,6 +1713,7 @@ RelationClearRelation(Relation relation, bool rebuild)
if (RelationBuildDesc(buildinfo, relation) != relation)
{
/* Should only get here if relation was deleted */
flush_rowtype_cache(old_reltype);
FreeTupleDesc(old_att);
if (old_rulescxt)
MemoryContextDelete(old_rulescxt);
@@ -1715,11 +1725,15 @@ RelationClearRelation(Relation relation, bool rebuild)
relation->rd_isnew = old_isnew;
if (equalTupleDescs(old_att, relation->rd_att))
{
/* needn't flush typcache here */
FreeTupleDesc(relation->rd_att);
relation->rd_att = old_att;
}
else
{
flush_rowtype_cache(old_reltype);
FreeTupleDesc(old_att);
}
if (equalRuleLocks(old_rules, relation->rd_rules))
{
if (relation->rd_rulescxt)
@@ -2329,6 +2343,12 @@ RelationCacheInitializePhase2(void)
*/
Assert(relation->rd_rel != NULL);
memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
/*
* Also update the derived fields in rd_att.
*/
relation->rd_att->tdtypeid = relp->reltype;
relation->rd_att->tdtypmod = -1; /* unnecessary, but... */
relation->rd_att->tdhasoid = relp->relhasoids;
ReleaseSysCache(htup);
@@ -2918,6 +2938,8 @@ load_relcache_init_file(void)
/* initialize attribute tuple forms */
rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
relform->relhasoids);
rel->rd_att->tdtypeid = relform->reltype;
rel->rd_att->tdtypmod = -1; /* unnecessary, but... */
/* next read all the attribute tuple form data entries */
has_not_null = false;

View File

@@ -28,12 +28,15 @@
* doesn't cope with opclasses changing under it, either, so this seems
* a low-priority problem.
*
* We do support clearing the tuple descriptor part of a rowtype's cache
* entry, since that may need to change as a consequence of ALTER TABLE.
*
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.4 2003/11/29 19:52:00 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.5 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,11 +56,42 @@
#include "utils/fmgroids.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
/* The main type cache hashtable searched by lookup_type_cache */
static HTAB *TypeCacheHash = NULL;
/*
* We use a separate table for storing the definitions of non-anonymous
* record types. Once defined, a record type will be remembered for the
* life of the backend. Subsequent uses of the "same" record type (where
* sameness means equalTupleDescs) will refer to the existing table entry.
*
* Stored record types are remembered in a linear array of TupleDescs,
* which can be indexed quickly with the assigned typmod. There is also
* a hash table to speed searches for matching TupleDescs. The hash key
* uses just the first N columns' type OIDs, and so we may have multiple
* entries with the same hash key.
*/
#define REC_HASH_KEYS 16 /* use this many columns in hash key */
typedef struct RecordCacheEntry
{
/* the hash lookup key MUST BE FIRST */
Oid hashkey[REC_HASH_KEYS]; /* column type IDs, zero-filled */
/* list of TupleDescs for record types with this hashkey */
List *tupdescs;
} RecordCacheEntry;
static HTAB *RecordCacheHash = NULL;
static TupleDesc *RecordCacheArray = NULL;
static int32 RecordCacheArrayLen = 0; /* allocated length of array */
static int32 NextRecordTypmod = 0; /* number of entries used */
static Oid lookup_default_opclass(Oid type_id, Oid am_id);
@@ -102,16 +136,26 @@ lookup_type_cache(Oid type_id, int flags)
if (typentry == NULL)
{
/*
* If we didn't find one, we want to make one. But first get the
* required info from the pg_type row, just to make sure we don't
* make a cache entry for an invalid type OID.
* If we didn't find one, we want to make one. But first look up
* the pg_type row, just to make sure we don't make a cache entry
* for an invalid type OID.
*/
int16 typlen;
bool typbyval;
char typalign;
HeapTuple tp;
Form_pg_type typtup;
get_typlenbyvalalign(type_id, &typlen, &typbyval, &typalign);
tp = SearchSysCache(TYPEOID,
ObjectIdGetDatum(type_id),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for type %u", type_id);
typtup = (Form_pg_type) GETSTRUCT(tp);
if (!typtup->typisdefined)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" is only a shell",
NameStr(typtup->typname))));
/* Now make the typcache entry */
typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
(void *) &type_id,
HASH_ENTER, &found);
@@ -123,13 +167,20 @@ lookup_type_cache(Oid type_id, int flags)
MemSet(typentry, 0, sizeof(TypeCacheEntry));
typentry->type_id = type_id;
typentry->typlen = typlen;
typentry->typbyval = typbyval;
typentry->typalign = typalign;
typentry->typlen = typtup->typlen;
typentry->typbyval = typtup->typbyval;
typentry->typalign = typtup->typalign;
typentry->typtype = typtup->typtype;
typentry->typrelid = typtup->typrelid;
ReleaseSysCache(tp);
}
/* If we haven't already found the opclass, try to do so */
if (flags != 0 && typentry->btree_opc == InvalidOid)
if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
TYPECACHE_CMP_PROC |
TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO)) &&
typentry->btree_opc == InvalidOid)
{
typentry->btree_opc = lookup_default_opclass(type_id,
BTREE_AM_OID);
@@ -215,6 +266,30 @@ lookup_type_cache(Oid type_id, int flags)
CacheMemoryContext);
}
/*
* If it's a composite type (row type), get tupdesc if requested
*/
if ((flags & TYPECACHE_TUPDESC) &&
typentry->tupDesc == NULL &&
typentry->typtype == 'c')
{
Relation rel;
if (!OidIsValid(typentry->typrelid)) /* should not happen */
elog(ERROR, "invalid typrelid for composite type %u",
typentry->type_id);
rel = relation_open(typentry->typrelid, AccessShareLock);
Assert(rel->rd_rel->reltype == typentry->type_id);
/*
* Notice that we simply store a link to the relcache's tupdesc.
* Since we are relying on relcache to detect cache flush events,
* there's not a lot of point to maintaining an independent copy.
*/
typentry->tupDesc = RelationGetDescr(rel);
relation_close(rel, AccessShareLock);
}
return typentry;
}
@@ -296,3 +371,172 @@ lookup_default_opclass(Oid type_id, Oid am_id)
return InvalidOid;
}
/*
* lookup_rowtype_tupdesc
*
* Given a typeid/typmod that should describe a known composite type,
* return the tuple descriptor for the type. Will ereport on failure.
*
* Note: returned TupleDesc points to cached copy; caller must copy it
* if intending to scribble on it or keep a reference for a long time.
*/
TupleDesc
lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
{
if (type_id != RECORDOID)
{
/*
* It's a named composite type, so use the regular typcache.
*/
TypeCacheEntry *typentry;
typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
/* this should not happen unless caller messed up: */
if (typentry->tupDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("type %u is not composite",
type_id)));
return typentry->tupDesc;
}
else
{
/*
* It's a transient record type, so look in our record-type table.
*/
if (typmod < 0 || typmod >= NextRecordTypmod)
{
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("record type has not been registered")));
}
return RecordCacheArray[typmod];
}
}
/*
* assign_record_type_typmod
*
* Given a tuple descriptor for a RECORD type, find or create a cache entry
* for the type, and set the tupdesc's tdtypmod field to a value that will
* identify this cache entry to lookup_rowtype_tupdesc.
*/
void
assign_record_type_typmod(TupleDesc tupDesc)
{
RecordCacheEntry *recentry;
TupleDesc entDesc;
Oid hashkey[REC_HASH_KEYS];
bool found;
int i;
List *l;
int32 newtypmod;
MemoryContext oldcxt;
Assert(tupDesc->tdtypeid == RECORDOID);
if (RecordCacheHash == NULL)
{
/* First time through: initialize the hash table */
HASHCTL ctl;
if (!CacheMemoryContext)
CreateCacheMemoryContext();
MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
ctl.entrysize = sizeof(RecordCacheEntry);
ctl.hash = tag_hash;
RecordCacheHash = hash_create("Record information cache", 64,
&ctl, HASH_ELEM | HASH_FUNCTION);
}
/* Find or create a hashtable entry for this hash class */
MemSet(hashkey, 0, sizeof(hashkey));
for (i = 0; i < tupDesc->natts; i++)
{
if (i >= REC_HASH_KEYS)
break;
hashkey[i] = tupDesc->attrs[i]->atttypid;
}
recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
(void *) hashkey,
HASH_ENTER, &found);
if (recentry == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
if (!found)
{
/* New entry ... hash_search initialized only the hash key */
recentry->tupdescs = NIL;
}
/* Look for existing record cache entry */
foreach(l, recentry->tupdescs)
{
entDesc = (TupleDesc) lfirst(l);
if (equalTupleDescs(tupDesc, entDesc))
{
tupDesc->tdtypmod = entDesc->tdtypmod;
return;
}
}
/* Not present, so need to manufacture an entry */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
if (RecordCacheArray == NULL)
{
RecordCacheArray = (TupleDesc *) palloc(64 * sizeof(TupleDesc));
RecordCacheArrayLen = 64;
}
else if (NextRecordTypmod >= RecordCacheArrayLen)
{
int32 newlen = RecordCacheArrayLen * 2;
RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
newlen * sizeof(TupleDesc));
RecordCacheArrayLen = newlen;
}
/* if fail in subrs, no damage except possibly some wasted memory... */
entDesc = CreateTupleDescCopy(tupDesc);
recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
/* now it's safe to advance NextRecordTypmod */
newtypmod = NextRecordTypmod++;
entDesc->tdtypmod = newtypmod;
RecordCacheArray[newtypmod] = entDesc;
/* report to caller as well */
tupDesc->tdtypmod = newtypmod;
MemoryContextSwitchTo(oldcxt);
}
/*
* flush_rowtype_cache
*
* If a typcache entry exists for a rowtype, delete the entry's cached
* tuple descriptor link. This is called from relcache.c when a cached
* relation tupdesc is about to be dropped.
*/
void
flush_rowtype_cache(Oid type_id)
{
TypeCacheEntry *typentry;
if (TypeCacheHash == NULL)
return; /* no table, so certainly no entry */
typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
(void *) &type_id,
HASH_FIND, NULL);
if (typentry == NULL)
return; /* no matching entry */
typentry->tupDesc = NULL;
}

View File

@@ -7,7 +7,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.13 2003/12/19 00:02:11 joe Exp $
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.14 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,9 +49,8 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
* Allocate suitably long-lived space and zero it
*/
retval = (FuncCallContext *)
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(FuncCallContext));
MemSet(retval, 0, sizeof(FuncCallContext));
MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
sizeof(FuncCallContext));
/*
* initialize the elements
@@ -61,6 +60,7 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
retval->slot = NULL;
retval->user_fctx = NULL;
retval->attinmeta = NULL;
retval->tuple_desc = NULL;
retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt;
/*
@@ -104,8 +104,11 @@ per_MultiFuncCall(PG_FUNCTION_ARGS)
* FuncCallContext is pointing to it), but in most usage patterns the
* tuples stored in it will be in the function's per-tuple context. So
* at the beginning of each call, the Slot will hold a dangling
* pointer to an already-recycled tuple. We clear it out here. (See
* also the definition of TupleGetDatum() in funcapi.h!)
* pointer to an already-recycled tuple. We clear it out here.
*
* Note: use of retval->slot is obsolete as of 7.5, and we expect that
* it will always be NULL. This is just here for backwards compatibility
* in case someone creates a slot anyway.
*/
if (retval->slot != NULL)
ExecClearTuple(retval->slot);

View File

@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.194 2004/04/01 14:25:47 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.195 2004/04/01 21:28:45 tgl Exp $
*
*--------------------------------------------------------------------
*/
@@ -3453,9 +3453,9 @@ GetPGVariableResultDesc(const char *name)
/* need a tuple descriptor representing two TEXT columns */
tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
}
else
{
@@ -3467,7 +3467,7 @@ GetPGVariableResultDesc(const char *name)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
}
return tupdesc;
}
@@ -3507,7 +3507,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
/* prepare for projection of tuples */
tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -3532,9 +3532,9 @@ ShowAllGUCConfig(DestReceiver *dest)
/* need a tuple descriptor representing two TEXT columns */
tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
/* prepare for projection of tuples */
tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -3740,7 +3740,6 @@ show_all_settings(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
int call_cntr;
int max_calls;
TupleTableSlot *slot;
AttInMetadata *attinmeta;
MemoryContext oldcontext;
@@ -3762,31 +3761,25 @@ show_all_settings(PG_FUNCTION_ARGS)
*/
tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "category",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "short_desc",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "extra_desc",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "context",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "vartype",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "source",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "min_val",
TEXTOID, -1, 0, false);
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "max_val",
TEXTOID, -1, 0, false);
/* allocate a slot for a tuple with this tupdesc */
slot = TupleDescGetSlot(tupdesc);
/* assign slot to function context */
funcctx->slot = slot;
TEXTOID, -1, 0);
/*
* Generate attribute metadata needed later to produce tuples from
@@ -3806,7 +3799,6 @@ show_all_settings(PG_FUNCTION_ARGS)
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
slot = funcctx->slot;
attinmeta = funcctx->attinmeta;
if (call_cntr < max_calls) /* do when there is more left to send */
@@ -3837,7 +3829,7 @@ show_all_settings(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
result = TupleGetDatum(slot, tuple);
result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}