summaryrefslogtreecommitdiffstats
path: root/src/backend/access/common/tupdesc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/common/tupdesc.c')
-rw-r--r--src/backend/access/common/tupdesc.c912
1 files changed, 912 insertions, 0 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
new file mode 100644
index 0000000..9f41b1e
--- /dev/null
+++ b/src/backend/access/common/tupdesc.c
@@ -0,0 +1,912 @@
+/*-------------------------------------------------------------------------
+ *
+ * tupdesc.c
+ * POSTGRES tuple descriptor support code
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/common/tupdesc.c
+ *
+ * NOTES
+ * some of the executor utility code such as "ExecTypeFromTL" should be
+ * moved here.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/toast_compression.h"
+#include "access/tupdesc_details.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "common/hashfn.h"
+#include "miscadmin.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/resowner_private.h"
+#include "utils/syscache.h"
+
+
+/*
+ * CreateTemplateTupleDesc
+ * This function allocates an empty tuple descriptor structure.
+ *
+ * Tuple type ID information is initially set for an anonymous record type;
+ * caller can overwrite this if needed.
+ */
+TupleDesc
+CreateTemplateTupleDesc(int natts)
+{
+ TupleDesc desc;
+
+ /*
+ * sanity checks
+ */
+ AssertArg(natts >= 0);
+
+ /*
+ * Allocate enough memory for the tuple descriptor, including the
+ * attribute rows.
+ *
+ * Note: the attribute array stride is sizeof(FormData_pg_attribute),
+ * since we declare the array elements as FormData_pg_attribute for
+ * notational convenience. However, we only guarantee that the first
+ * ATTRIBUTE_FIXED_PART_SIZE bytes of each entry are valid; most code that
+ * copies tupdesc entries around copies just that much. In principle that
+ * could be less due to trailing padding, although with the current
+ * definition of pg_attribute there probably isn't any padding.
+ */
+ desc = (TupleDesc) palloc(offsetof(struct TupleDescData, attrs) +
+ natts * sizeof(FormData_pg_attribute));
+
+ /*
+ * Initialize other fields of the tupdesc.
+ */
+ desc->natts = natts;
+ desc->constr = NULL;
+ desc->tdtypeid = RECORDOID;
+ desc->tdtypmod = -1;
+ desc->tdrefcount = -1; /* assume not reference-counted */
+
+ return desc;
+}
+
+/*
+ * CreateTupleDesc
+ * This function allocates a new TupleDesc by copying 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
+CreateTupleDesc(int natts, Form_pg_attribute *attrs)
+{
+ TupleDesc desc;
+ int i;
+
+ desc = CreateTemplateTupleDesc(natts);
+
+ for (i = 0; i < natts; ++i)
+ memcpy(TupleDescAttr(desc, i), attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
+
+ return desc;
+}
+
+/*
+ * CreateTupleDescCopy
+ * This function creates a new TupleDesc by copying from an existing
+ * TupleDesc.
+ *
+ * !!! Constraints and defaults are not copied !!!
+ */
+TupleDesc
+CreateTupleDescCopy(TupleDesc tupdesc)
+{
+ TupleDesc desc;
+ int i;
+
+ desc = CreateTemplateTupleDesc(tupdesc->natts);
+
+ /* Flat-copy the attribute array */
+ memcpy(TupleDescAttr(desc, 0),
+ TupleDescAttr(tupdesc, 0),
+ desc->natts * sizeof(FormData_pg_attribute));
+
+ /*
+ * Since we're not copying constraints and defaults, clear fields
+ * associated with them.
+ */
+ for (i = 0; i < desc->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(desc, i);
+
+ att->attnotnull = false;
+ att->atthasdef = false;
+ att->atthasmissing = false;
+ att->attidentity = '\0';
+ att->attgenerated = '\0';
+ }
+
+ /* We can copy the tuple type identification, too */
+ desc->tdtypeid = tupdesc->tdtypeid;
+ desc->tdtypmod = tupdesc->tdtypmod;
+
+ return desc;
+}
+
+/*
+ * CreateTupleDescCopyConstr
+ * This function creates a new TupleDesc by copying from an existing
+ * TupleDesc (including its constraints and defaults).
+ */
+TupleDesc
+CreateTupleDescCopyConstr(TupleDesc tupdesc)
+{
+ TupleDesc desc;
+ TupleConstr *constr = tupdesc->constr;
+ int i;
+
+ desc = CreateTemplateTupleDesc(tupdesc->natts);
+
+ /* Flat-copy the attribute array */
+ memcpy(TupleDescAttr(desc, 0),
+ TupleDescAttr(tupdesc, 0),
+ desc->natts * sizeof(FormData_pg_attribute));
+
+ /* Copy the TupleConstr data structure, if any */
+ if (constr)
+ {
+ TupleConstr *cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
+
+ cpy->has_not_null = constr->has_not_null;
+ cpy->has_generated_stored = constr->has_generated_stored;
+
+ if ((cpy->num_defval = constr->num_defval) > 0)
+ {
+ cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault));
+ memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault));
+ for (i = cpy->num_defval - 1; i >= 0; i--)
+ cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
+ }
+
+ if (constr->missing)
+ {
+ cpy->missing = (AttrMissing *) palloc(tupdesc->natts * sizeof(AttrMissing));
+ memcpy(cpy->missing, constr->missing, tupdesc->natts * sizeof(AttrMissing));
+ for (i = tupdesc->natts - 1; i >= 0; i--)
+ {
+ if (constr->missing[i].am_present)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+
+ cpy->missing[i].am_value = datumCopy(constr->missing[i].am_value,
+ attr->attbyval,
+ attr->attlen);
+ }
+ }
+ }
+
+ if ((cpy->num_check = constr->num_check) > 0)
+ {
+ cpy->check = (ConstrCheck *) palloc(cpy->num_check * sizeof(ConstrCheck));
+ memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck));
+ for (i = cpy->num_check - 1; i >= 0; i--)
+ {
+ cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
+ cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
+ cpy->check[i].ccvalid = constr->check[i].ccvalid;
+ cpy->check[i].ccnoinherit = constr->check[i].ccnoinherit;
+ }
+ }
+
+ desc->constr = cpy;
+ }
+
+ /* We can copy the tuple type identification, too */
+ desc->tdtypeid = tupdesc->tdtypeid;
+ desc->tdtypmod = tupdesc->tdtypmod;
+
+ return desc;
+}
+
+/*
+ * TupleDescCopy
+ * Copy a tuple descriptor into caller-supplied memory.
+ * The memory may be shared memory mapped at any address, and must
+ * be sufficient to hold TupleDescSize(src) bytes.
+ *
+ * !!! Constraints and defaults are not copied !!!
+ */
+void
+TupleDescCopy(TupleDesc dst, TupleDesc src)
+{
+ int i;
+
+ /* Flat-copy the header and attribute array */
+ memcpy(dst, src, TupleDescSize(src));
+
+ /*
+ * Since we're not copying constraints and defaults, clear fields
+ * associated with them.
+ */
+ for (i = 0; i < dst->natts; i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(dst, i);
+
+ att->attnotnull = false;
+ att->atthasdef = false;
+ att->atthasmissing = false;
+ att->attidentity = '\0';
+ att->attgenerated = '\0';
+ }
+ dst->constr = NULL;
+
+ /*
+ * Also, assume the destination is not to be ref-counted. (Copying the
+ * source's refcount would be wrong in any case.)
+ */
+ dst->tdrefcount = -1;
+}
+
+/*
+ * TupleDescCopyEntry
+ * This function copies a single attribute structure from one tuple
+ * descriptor to another.
+ *
+ * !!! Constraints and defaults are not copied !!!
+ */
+void
+TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
+ TupleDesc src, AttrNumber srcAttno)
+{
+ Form_pg_attribute dstAtt = TupleDescAttr(dst, dstAttno - 1);
+ Form_pg_attribute srcAtt = TupleDescAttr(src, srcAttno - 1);
+
+ /*
+ * sanity checks
+ */
+ AssertArg(PointerIsValid(src));
+ AssertArg(PointerIsValid(dst));
+ AssertArg(srcAttno >= 1);
+ AssertArg(srcAttno <= src->natts);
+ AssertArg(dstAttno >= 1);
+ AssertArg(dstAttno <= dst->natts);
+
+ memcpy(dstAtt, srcAtt, ATTRIBUTE_FIXED_PART_SIZE);
+
+ /*
+ * Aside from updating the attno, we'd better reset attcacheoff.
+ *
+ * XXX Actually, to be entirely safe we'd need to reset the attcacheoff of
+ * all following columns in dst as well. Current usage scenarios don't
+ * require that though, because all following columns will get initialized
+ * by other uses of this function or TupleDescInitEntry. So we cheat a
+ * bit to avoid a useless O(N^2) penalty.
+ */
+ dstAtt->attnum = dstAttno;
+ dstAtt->attcacheoff = -1;
+
+ /* since we're not copying constraints or defaults, clear these */
+ dstAtt->attnotnull = false;
+ dstAtt->atthasdef = false;
+ dstAtt->atthasmissing = false;
+ dstAtt->attidentity = '\0';
+ dstAtt->attgenerated = '\0';
+}
+
+/*
+ * Free a TupleDesc including all substructure
+ */
+void
+FreeTupleDesc(TupleDesc tupdesc)
+{
+ int i;
+
+ /*
+ * Possibly this should assert tdrefcount == 0, to disallow explicit
+ * freeing of un-refcounted tupdescs?
+ */
+ Assert(tupdesc->tdrefcount <= 0);
+
+ if (tupdesc->constr)
+ {
+ if (tupdesc->constr->num_defval > 0)
+ {
+ AttrDefault *attrdef = tupdesc->constr->defval;
+
+ for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
+ pfree(attrdef[i].adbin);
+ pfree(attrdef);
+ }
+ if (tupdesc->constr->missing)
+ {
+ AttrMissing *attrmiss = tupdesc->constr->missing;
+
+ for (i = tupdesc->natts - 1; i >= 0; i--)
+ {
+ if (attrmiss[i].am_present
+ && !TupleDescAttr(tupdesc, i)->attbyval)
+ pfree(DatumGetPointer(attrmiss[i].am_value));
+ }
+ pfree(attrmiss);
+ }
+ if (tupdesc->constr->num_check > 0)
+ {
+ ConstrCheck *check = tupdesc->constr->check;
+
+ for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
+ {
+ pfree(check[i].ccname);
+ pfree(check[i].ccbin);
+ }
+ pfree(check);
+ }
+ pfree(tupdesc->constr);
+ }
+
+ pfree(tupdesc);
+}
+
+/*
+ * Increment the reference count of a tupdesc, and log the reference in
+ * CurrentResourceOwner.
+ *
+ * Do not apply this to tupdescs that are not being refcounted. (Use the
+ * macro PinTupleDesc for tupdescs of uncertain status.)
+ */
+void
+IncrTupleDescRefCount(TupleDesc tupdesc)
+{
+ Assert(tupdesc->tdrefcount >= 0);
+
+ ResourceOwnerEnlargeTupleDescs(CurrentResourceOwner);
+ tupdesc->tdrefcount++;
+ ResourceOwnerRememberTupleDesc(CurrentResourceOwner, tupdesc);
+}
+
+/*
+ * Decrement the reference count of a tupdesc, remove the corresponding
+ * reference from CurrentResourceOwner, and free the tupdesc if no more
+ * references remain.
+ *
+ * Do not apply this to tupdescs that are not being refcounted. (Use the
+ * macro ReleaseTupleDesc for tupdescs of uncertain status.)
+ */
+void
+DecrTupleDescRefCount(TupleDesc tupdesc)
+{
+ Assert(tupdesc->tdrefcount > 0);
+
+ ResourceOwnerForgetTupleDesc(CurrentResourceOwner, tupdesc);
+ if (--tupdesc->tdrefcount == 0)
+ FreeTupleDesc(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.
+ * We don't compare tdrefcount, either.
+ */
+bool
+equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
+{
+ int i,
+ n;
+
+ if (tupdesc1->natts != tupdesc2->natts)
+ return false;
+ if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
+ return false;
+
+ for (i = 0; i < tupdesc1->natts; i++)
+ {
+ Form_pg_attribute attr1 = TupleDescAttr(tupdesc1, i);
+ Form_pg_attribute attr2 = TupleDescAttr(tupdesc2, i);
+
+ /*
+ * We do not need to check every single field here: we can disregard
+ * attrelid and attnum (which were used to place the row in the attrs
+ * array in the first place). It might look like we could dispense
+ * with checking attlen/attbyval/attalign, since these are derived
+ * from atttypid; but in the case of dropped columns we must check
+ * them (since atttypid will be zero for all dropped columns) and in
+ * general it seems safer to check them always.
+ *
+ * attcacheoff must NOT be checked since it's possibly not set in both
+ * copies. We also intentionally ignore atthasmissing, since that's
+ * not very relevant in tupdescs, which lack the attmissingval field.
+ */
+ if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
+ return false;
+ if (attr1->atttypid != attr2->atttypid)
+ return false;
+ if (attr1->attstattarget != attr2->attstattarget)
+ return false;
+ if (attr1->attlen != attr2->attlen)
+ return false;
+ if (attr1->attndims != attr2->attndims)
+ return false;
+ if (attr1->atttypmod != attr2->atttypmod)
+ return false;
+ if (attr1->attbyval != attr2->attbyval)
+ return false;
+ if (attr1->attalign != attr2->attalign)
+ return false;
+ if (attr1->attstorage != attr2->attstorage)
+ return false;
+ if (attr1->attcompression != attr2->attcompression)
+ return false;
+ if (attr1->attnotnull != attr2->attnotnull)
+ return false;
+ if (attr1->atthasdef != attr2->atthasdef)
+ return false;
+ if (attr1->attidentity != attr2->attidentity)
+ return false;
+ if (attr1->attgenerated != attr2->attgenerated)
+ return false;
+ if (attr1->attisdropped != attr2->attisdropped)
+ return false;
+ if (attr1->attislocal != attr2->attislocal)
+ return false;
+ if (attr1->attinhcount != attr2->attinhcount)
+ return false;
+ if (attr1->attcollation != attr2->attcollation)
+ return false;
+ /* variable-length fields are not even present... */
+ }
+
+ if (tupdesc1->constr != NULL)
+ {
+ TupleConstr *constr1 = tupdesc1->constr;
+ TupleConstr *constr2 = tupdesc2->constr;
+
+ if (constr2 == NULL)
+ return false;
+ if (constr1->has_not_null != constr2->has_not_null)
+ return false;
+ if (constr1->has_generated_stored != constr2->has_generated_stored)
+ return false;
+ n = constr1->num_defval;
+ if (n != (int) constr2->num_defval)
+ return false;
+ /* We assume here that both AttrDefault arrays are in adnum order */
+ for (i = 0; i < n; i++)
+ {
+ AttrDefault *defval1 = constr1->defval + i;
+ AttrDefault *defval2 = constr2->defval + i;
+
+ if (defval1->adnum != defval2->adnum)
+ return false;
+ if (strcmp(defval1->adbin, defval2->adbin) != 0)
+ return false;
+ }
+ if (constr1->missing)
+ {
+ if (!constr2->missing)
+ return false;
+ for (i = 0; i < tupdesc1->natts; i++)
+ {
+ AttrMissing *missval1 = constr1->missing + i;
+ AttrMissing *missval2 = constr2->missing + i;
+
+ if (missval1->am_present != missval2->am_present)
+ return false;
+ if (missval1->am_present)
+ {
+ Form_pg_attribute missatt1 = TupleDescAttr(tupdesc1, i);
+
+ if (!datumIsEqual(missval1->am_value, missval2->am_value,
+ missatt1->attbyval, missatt1->attlen))
+ return false;
+ }
+ }
+ }
+ else if (constr2->missing)
+ return false;
+ n = constr1->num_check;
+ if (n != (int) constr2->num_check)
+ return false;
+
+ /*
+ * Similarly, we rely here on the ConstrCheck entries being sorted by
+ * name. If there are duplicate names, the outcome of the comparison
+ * is uncertain, but that should not happen.
+ */
+ for (i = 0; i < n; i++)
+ {
+ ConstrCheck *check1 = constr1->check + i;
+ ConstrCheck *check2 = constr2->check + i;
+
+ if (!(strcmp(check1->ccname, check2->ccname) == 0 &&
+ strcmp(check1->ccbin, check2->ccbin) == 0 &&
+ check1->ccvalid == check2->ccvalid &&
+ check1->ccnoinherit == check2->ccnoinherit))
+ return false;
+ }
+ }
+ else if (tupdesc2->constr != NULL)
+ return false;
+ return true;
+}
+
+/*
+ * hashTupleDesc
+ * Compute a hash value for a tuple descriptor.
+ *
+ * If two tuple descriptors would be considered equal by equalTupleDescs()
+ * then their hash value will be equal according to this function.
+ *
+ * Note that currently contents of constraint are not hashed - it'd be a bit
+ * painful to do so, and conflicts just due to constraints are unlikely.
+ */
+uint32
+hashTupleDesc(TupleDesc desc)
+{
+ uint32 s;
+ int i;
+
+ s = hash_combine(0, hash_uint32(desc->natts));
+ s = hash_combine(s, hash_uint32(desc->tdtypeid));
+ for (i = 0; i < desc->natts; ++i)
+ s = hash_combine(s, hash_uint32(TupleDescAttr(desc, i)->atttypid));
+
+ return s;
+}
+
+/*
+ * TupleDescInitEntry
+ * This function initializes a single attribute structure in
+ * a previously allocated tuple descriptor.
+ *
+ * If attributeName is NULL, the attname field is set to an empty string
+ * (this is for cases where we don't know or need a name for the field).
+ * Also, some callers use this function to change the datatype-related fields
+ * in an existing tupdesc; they pass attributeName = NameStr(att->attname)
+ * to indicate that the attname field shouldn't be modified.
+ *
+ * Note that attcollation is set to the default for the specified datatype.
+ * If a nondefault collation is needed, insert it afterwards using
+ * TupleDescInitEntryCollation.
+ */
+void
+TupleDescInitEntry(TupleDesc desc,
+ AttrNumber attributeNumber,
+ const char *attributeName,
+ Oid oidtypeid,
+ int32 typmod,
+ int attdim)
+{
+ HeapTuple tuple;
+ Form_pg_type typeForm;
+ Form_pg_attribute att;
+
+ /*
+ * sanity checks
+ */
+ AssertArg(PointerIsValid(desc));
+ AssertArg(attributeNumber >= 1);
+ AssertArg(attributeNumber <= desc->natts);
+
+ /*
+ * initialize the attribute fields
+ */
+ att = TupleDescAttr(desc, attributeNumber - 1);
+
+ att->attrelid = 0; /* dummy value */
+
+ /*
+ * Note: attributeName can be NULL, because the planner doesn't always
+ * fill in valid resname values in targetlists, particularly for resjunk
+ * attributes. Also, do nothing if caller wants to re-use the old attname.
+ */
+ if (attributeName == NULL)
+ MemSet(NameStr(att->attname), 0, NAMEDATALEN);
+ else if (attributeName != NameStr(att->attname))
+ namestrcpy(&(att->attname), attributeName);
+
+ att->attstattarget = -1;
+ att->attcacheoff = -1;
+ att->atttypmod = typmod;
+
+ att->attnum = attributeNumber;
+ att->attndims = attdim;
+
+ att->attnotnull = false;
+ att->atthasdef = false;
+ att->atthasmissing = false;
+ att->attidentity = '\0';
+ att->attgenerated = '\0';
+ att->attisdropped = false;
+ att->attislocal = true;
+ att->attinhcount = 0;
+ /* variable-length fields are not present in tupledescs */
+
+ tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oidtypeid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for type %u", oidtypeid);
+ typeForm = (Form_pg_type) GETSTRUCT(tuple);
+
+ att->atttypid = oidtypeid;
+ att->attlen = typeForm->typlen;
+ att->attbyval = typeForm->typbyval;
+ att->attalign = typeForm->typalign;
+ att->attstorage = typeForm->typstorage;
+ att->attcompression = InvalidCompressionMethod;
+ att->attcollation = typeForm->typcollation;
+
+ ReleaseSysCache(tuple);
+}
+
+/*
+ * TupleDescInitBuiltinEntry
+ * Initialize a tuple descriptor without catalog access. Only
+ * a limited range of builtin types are supported.
+ */
+void
+TupleDescInitBuiltinEntry(TupleDesc desc,
+ AttrNumber attributeNumber,
+ const char *attributeName,
+ Oid oidtypeid,
+ int32 typmod,
+ int attdim)
+{
+ Form_pg_attribute att;
+
+ /* sanity checks */
+ AssertArg(PointerIsValid(desc));
+ AssertArg(attributeNumber >= 1);
+ AssertArg(attributeNumber <= desc->natts);
+
+ /* initialize the attribute fields */
+ att = TupleDescAttr(desc, attributeNumber - 1);
+ att->attrelid = 0; /* dummy value */
+
+ /* unlike TupleDescInitEntry, we require an attribute name */
+ Assert(attributeName != NULL);
+ namestrcpy(&(att->attname), attributeName);
+
+ att->attstattarget = -1;
+ att->attcacheoff = -1;
+ att->atttypmod = typmod;
+
+ att->attnum = attributeNumber;
+ att->attndims = attdim;
+
+ att->attnotnull = false;
+ att->atthasdef = false;
+ att->atthasmissing = false;
+ att->attidentity = '\0';
+ att->attgenerated = '\0';
+ att->attisdropped = false;
+ att->attislocal = true;
+ att->attinhcount = 0;
+ /* variable-length fields are not present in tupledescs */
+
+ att->atttypid = oidtypeid;
+
+ /*
+ * Our goal here is to support just enough types to let basic builtin
+ * commands work without catalog access - e.g. so that we can do certain
+ * things even in processes that are not connected to a database.
+ */
+ switch (oidtypeid)
+ {
+ case TEXTOID:
+ case TEXTARRAYOID:
+ att->attlen = -1;
+ att->attbyval = false;
+ att->attalign = TYPALIGN_INT;
+ att->attstorage = TYPSTORAGE_EXTENDED;
+ att->attcompression = InvalidCompressionMethod;
+ att->attcollation = DEFAULT_COLLATION_OID;
+ break;
+
+ case BOOLOID:
+ att->attlen = 1;
+ att->attbyval = true;
+ att->attalign = TYPALIGN_CHAR;
+ att->attstorage = TYPSTORAGE_PLAIN;
+ att->attcompression = InvalidCompressionMethod;
+ att->attcollation = InvalidOid;
+ break;
+
+ case INT4OID:
+ att->attlen = 4;
+ att->attbyval = true;
+ att->attalign = TYPALIGN_INT;
+ att->attstorage = TYPSTORAGE_PLAIN;
+ att->attcompression = InvalidCompressionMethod;
+ att->attcollation = InvalidOid;
+ break;
+
+ case INT8OID:
+ att->attlen = 8;
+ att->attbyval = FLOAT8PASSBYVAL;
+ att->attalign = TYPALIGN_DOUBLE;
+ att->attstorage = TYPSTORAGE_PLAIN;
+ att->attcompression = InvalidCompressionMethod;
+ att->attcollation = InvalidOid;
+ break;
+
+ default:
+ elog(ERROR, "unsupported type %u", oidtypeid);
+ }
+}
+
+/*
+ * TupleDescInitEntryCollation
+ *
+ * Assign a nondefault collation to a previously initialized tuple descriptor
+ * entry.
+ */
+void
+TupleDescInitEntryCollation(TupleDesc desc,
+ AttrNumber attributeNumber,
+ Oid collationid)
+{
+ /*
+ * sanity checks
+ */
+ AssertArg(PointerIsValid(desc));
+ AssertArg(attributeNumber >= 1);
+ AssertArg(attributeNumber <= desc->natts);
+
+ TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
+}
+
+
+/*
+ * BuildDescForRelation
+ *
+ * Given a relation schema (list of ColumnDef nodes), build a TupleDesc.
+ *
+ * Note: tdtypeid will need to be filled in later on.
+ */
+TupleDesc
+BuildDescForRelation(List *schema)
+{
+ int natts;
+ AttrNumber attnum;
+ ListCell *l;
+ TupleDesc desc;
+ bool has_not_null;
+ char *attname;
+ Oid atttypid;
+ int32 atttypmod;
+ Oid attcollation;
+ int attdim;
+
+ /*
+ * allocate a new tuple descriptor
+ */
+ natts = list_length(schema);
+ desc = CreateTemplateTupleDesc(natts);
+ has_not_null = false;
+
+ attnum = 0;
+
+ foreach(l, schema)
+ {
+ ColumnDef *entry = lfirst(l);
+ AclResult aclresult;
+ Form_pg_attribute att;
+
+ /*
+ * for each entry in the list, get the name and type information from
+ * the list and have TupleDescInitEntry fill in the attribute
+ * information we need.
+ */
+ attnum++;
+
+ attname = entry->colname;
+ typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
+
+ aclresult = pg_type_aclcheck(atttypid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error_type(aclresult, atttypid);
+
+ attcollation = GetColumnDefCollation(NULL, entry, atttypid);
+ attdim = list_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,
+ atttypid, atttypmod, attdim);
+ att = TupleDescAttr(desc, attnum - 1);
+
+ /* Override TupleDescInitEntry's settings as requested */
+ TupleDescInitEntryCollation(desc, attnum, attcollation);
+ if (entry->storage)
+ att->attstorage = entry->storage;
+
+ /* Fill in additional stuff not handled by TupleDescInitEntry */
+ att->attnotnull = entry->is_not_null;
+ has_not_null |= entry->is_not_null;
+ att->attislocal = entry->is_local;
+ att->attinhcount = entry->inhcount;
+ }
+
+ if (has_not_null)
+ {
+ TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
+
+ constr->has_not_null = true;
+ constr->has_generated_stored = false;
+ constr->defval = NULL;
+ constr->missing = NULL;
+ constr->num_defval = 0;
+ constr->check = NULL;
+ constr->num_check = 0;
+ desc->constr = constr;
+ }
+ else
+ {
+ desc->constr = NULL;
+ }
+
+ return desc;
+}
+
+/*
+ * BuildDescFromLists
+ *
+ * Build a TupleDesc given lists of column names (as String nodes),
+ * column type OIDs, typmods, and collation OIDs.
+ *
+ * No constraints are generated.
+ *
+ * This is essentially a cut-down version of BuildDescForRelation for use
+ * with functions returning RECORD.
+ */
+TupleDesc
+BuildDescFromLists(List *names, List *types, List *typmods, List *collations)
+{
+ int natts;
+ AttrNumber attnum;
+ ListCell *l1;
+ ListCell *l2;
+ ListCell *l3;
+ ListCell *l4;
+ TupleDesc desc;
+
+ natts = list_length(names);
+ Assert(natts == list_length(types));
+ Assert(natts == list_length(typmods));
+ Assert(natts == list_length(collations));
+
+ /*
+ * allocate a new tuple descriptor
+ */
+ desc = CreateTemplateTupleDesc(natts);
+
+ attnum = 0;
+ forfour(l1, names, l2, types, l3, typmods, l4, collations)
+ {
+ char *attname = strVal(lfirst(l1));
+ Oid atttypid = lfirst_oid(l2);
+ int32 atttypmod = lfirst_int(l3);
+ Oid attcollation = lfirst_oid(l4);
+
+ attnum++;
+
+ TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0);
+ TupleDescInitEntryCollation(desc, attnum, attcollation);
+ }
+
+ return desc;
+}