diff options
Diffstat (limited to 'src/backend')
24 files changed, 900 insertions, 908 deletions
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index f1b20ff2732..4355c0df4c7 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -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; diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 614cb00f5c2..1a016d86f80 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -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 */ diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 902f0621377..695ef36b509 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -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 diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index f18fece3982..c7fcb400873 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -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); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index ed68f9124d5..087e2d7f23c 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -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 */ diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 6fe64eadd0d..0640aacbe5a 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -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 diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 3c134a5c166..e2f3d4aa813 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -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; } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1a6bd2fdfc0..a6e3a93d349 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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 diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 4c6f95a9a6f..b27e86122bc 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -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, + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); + tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + + /* + * 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, - slot->ttc_tupleDescriptor, + tupDesc, isNull); - if (*isNull) - return (Datum) 0; - - return retval; + 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, + /* + * 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, + tupDesc, isNull); - if (*isNull) - return (Datum) 0; - - return retval; + 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 @@ -1282,31 +1290,34 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, 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 */ 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; + + 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); + } - resSlot = (TupleTableSlot *) DatumGetPointer(result); - Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot)); - result = heap_getattr(resSlot->val, + /* + * 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); } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 0bb59d26b11..faf910b736f 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -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,15 +523,12 @@ 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; - ExecSetSlotDescriptor(slot, tupType, false); + nullTuple = heap_formtuple(&nullTupleDesc, values, nulls); - nullTuple = heap_formtuple(&NullTupleDesc, values, nulls); + ExecSetSlotDescriptor(slot, tupType, false); 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; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 8dec6131fb4..aa7652e07ef 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -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); } /* diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index f3fa17c8881..7847b24ffe2 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -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); diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 1fb60fff02d..bcf7f8d52b6 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -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 diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 24b2fefe16a..1677493abbc 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -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, diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index e66eb905f56..d83e8ac4580 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -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) diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 5223e33816e..427d6334cd4 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -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 \ diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index b009e167a3f..bc4901aa546 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -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); } diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index 71b99553bc1..0526f52e677 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -28,59 +28,6 @@ /* - * 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. * * We might as well allow this to support constructs like "foo_in('blah')". diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c new file mode 100644 index 00000000000..b487dfc9047 --- /dev/null +++ b/src/backend/utils/adt/rowtypes.c @@ -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 */ +} diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c deleted file mode 100644 index 33f1ed1a4a8..00000000000 --- a/src/backend/utils/adt/sets.c +++ /dev/null @@ -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); -} diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 5c302e18d39..85ad6ffe788 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -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; diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index d6e560c34c5..7a8e67c83c8 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -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; +} diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index e6ef3fcec3c..dac4a8916e1 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -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); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index bc2499ed3af..26f4210ad0e 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -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); } |