postgresql/src/backend/access/common/tupdesc.c

546 lines
13 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* tupdesc.c
* POSTGRES tuple descriptor support code
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.110 2005/03/31 22:46:04 tgl Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
* moved here.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "parser/parse_type.h"
#include "utils/builtins.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, bool hasoid)
{
TupleDesc desc;
char *stg;
int attroffset;
/*
* sanity checks
*/
AssertArg(natts >= 0);
/*
* Allocate enough memory for the tuple descriptor, including the
* attribute rows, and set up the attribute row pointers.
*
* Note: we assume that sizeof(struct tupleDesc) is a multiple of
* the struct pointer alignment requirement, and hence we don't need
* to insert alignment padding between the struct and the array of
* attribute row pointers.
*/
attroffset = sizeof(struct tupleDesc) + natts * sizeof(Form_pg_attribute);
attroffset = MAXALIGN(attroffset);
stg = palloc(attroffset + natts * MAXALIGN(ATTRIBUTE_TUPLE_SIZE));
desc = (TupleDesc) stg;
if (natts > 0)
{
Form_pg_attribute *attrs;
int i;
attrs = (Form_pg_attribute *) (stg + sizeof(struct tupleDesc));
desc->attrs = attrs;
stg += attroffset;
for (i = 0; i < natts; i++)
{
attrs[i] = (Form_pg_attribute) stg;
stg += MAXALIGN(ATTRIBUTE_TUPLE_SIZE);
}
}
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;
1998-09-01 05:29:17 +02:00
return desc;
}
/*
* CreateTupleDesc
* This function allocates a new TupleDesc pointing to a given
* Form_pg_attribute array.
*
* Note: if the TupleDesc is ever freed, the Form_pg_attribute array
* will not be freed thereby.
*
* Tuple type ID information is initially set for an anonymous record type;
* caller can overwrite this if needed.
*/
TupleDesc
CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
{
TupleDesc desc;
/*
* sanity checks
*/
AssertArg(natts >= 0);
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = attrs;
desc->natts = natts;
desc->constr = NULL;
desc->tdtypeid = RECORDOID;
desc->tdtypmod = -1;
desc->tdhasoid = hasoid;
1998-09-01 05:29:17 +02:00
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, tupdesc->tdhasoid);
for (i = 0; i < desc->natts; i++)
{
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
desc->attrs[i]->attnotnull = false;
desc->attrs[i]->atthasdef = false;
}
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, tupdesc->tdhasoid);
for (i = 0; i < desc->natts; i++)
{
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
}
if (constr)
{
TupleConstr *cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
cpy->has_not_null = constr->has_not_null;
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--)
{
if (constr->defval[i].adbin)
cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
}
}
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--)
{
if (constr->check[i].ccname)
cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
if (constr->check[i].ccbin)
cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
}
}
desc->constr = cpy;
}
desc->tdtypeid = tupdesc->tdtypeid;
desc->tdtypmod = tupdesc->tdtypmod;
return desc;
}
/*
* Free a TupleDesc including all substructure
*/
void
FreeTupleDesc(TupleDesc tupdesc)
{
int i;
if (tupdesc->constr)
{
if (tupdesc->constr->num_defval > 0)
{
AttrDefault *attrdef = tupdesc->constr->defval;
for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
{
if (attrdef[i].adbin)
pfree(attrdef[i].adbin);
}
pfree(attrdef);
}
if (tupdesc->constr->num_check > 0)
{
ConstrCheck *check = tupdesc->constr->check;
for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
{
if (check[i].ccname)
pfree(check[i].ccname);
if (check[i].ccbin)
pfree(check[i].ccbin);
}
pfree(check);
}
pfree(tupdesc->constr);
}
pfree(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)
{
int i,
j,
n;
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];
Form_pg_attribute attr2 = tupdesc2->attrs[i];
/*
* 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;
if (attr1->atttypid != attr2->atttypid)
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)
return false;
if (attr1->attnotnull != attr2->attnotnull)
return false;
if (attr1->atthasdef != attr2->atthasdef)
return false;
if (attr1->attisdropped != attr2->attisdropped)
return false;
if (attr1->attislocal != attr2->attislocal)
return false;
if (attr1->attinhcount != attr2->attinhcount)
return false;
}
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;
n = constr1->num_defval;
if (n != (int) constr2->num_defval)
return false;
for (i = 0; i < n; i++)
{
AttrDefault *defval1 = constr1->defval + i;
AttrDefault *defval2 = constr2->defval;
/*
* We can't assume that the items are always read from the
2001-03-22 05:01:46 +01:00
* system catalogs in the same order; so use the adnum field
* to identify the matching item to compare.
*/
for (j = 0; j < n; defval2++, j++)
{
if (defval1->adnum == defval2->adnum)
break;
}
if (j >= n)
return false;
if (strcmp(defval1->adbin, defval2->adbin) != 0)
return false;
}
n = constr1->num_check;
if (n != (int) constr2->num_check)
return false;
for (i = 0; i < n; i++)
{
ConstrCheck *check1 = constr1->check + i;
ConstrCheck *check2 = constr2->check;
/*
2001-03-22 05:01:46 +01:00
* Similarly, don't assume that the checks are always read in
* the same order; match them up by name and contents. (The
* name *should* be unique, but...)
*/
for (j = 0; j < n; check2++, j++)
{
if (strcmp(check1->ccname, check2->ccname) == 0 &&
strcmp(check1->ccbin, check2->ccbin) == 0)
break;
}
if (j >= n)
return false;
}
}
else if (tupdesc2->constr != NULL)
return false;
return true;
}
/*
* TupleDescInitEntry
* This function initializes a single attribute structure in
* a previously allocated tuple descriptor.
*/
void
TupleDescInitEntry(TupleDesc desc,
AttrNumber attributeNumber,
const char *attributeName,
Oid oidtypeid,
int32 typmod,
int attdim)
{
HeapTuple tuple;
1998-09-01 05:29:17 +02:00
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 = desc->attrs[attributeNumber - 1];
att->attrelid = 0; /* dummy value */
/*
* Note: attributeName can be NULL, because the planner doesn't always
2004-08-29 07:07:03 +02:00
* fill in valid resname values in targetlists, particularly for
* resjunk attributes.
*/
if (attributeName != NULL)
namestrcpy(&(att->attname), attributeName);
else
MemSet(NameStr(att->attname), 0, NAMEDATALEN);
att->attstattarget = -1;
att->attcacheoff = -1;
att->atttypmod = typmod;
att->attnum = attributeNumber;
att->attndims = attdim;
att->attnotnull = false;
att->atthasdef = false;
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
tuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(oidtypeid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", oidtypeid);
1998-09-01 05:29:17 +02:00
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;
ReleaseSysCache(tuple);
}
/*
* BuildDescForRelation
*
* Given a relation schema (list of ColumnDef nodes), build a TupleDesc.
*
* Note: the default assumption is no OIDs; caller may modify the returned
2004-08-29 07:07:03 +02:00
* TupleDesc if it wants OIDs. Also, tdtypeid will need to be filled in
* later on.
*/
TupleDesc
BuildDescForRelation(List *schema)
{
int natts;
AttrNumber attnum;
ListCell *l;
TupleDesc desc;
AttrDefault *attrdef = NULL;
TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
char *attname;
int32 atttypmod;
int attdim;
int ndef = 0;
/*
* allocate a new tuple descriptor
*/
natts = list_length(schema);
desc = CreateTemplateTupleDesc(natts, false);
constr->has_not_null = false;
attnum = 0;
foreach(l, schema)
{
ColumnDef *entry = lfirst(l);
/*
* 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;
atttypmod = entry->typename->typmod;
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,
typenameTypeId(entry->typename),
atttypmod, attdim);
/* Fill in additional stuff not handled by TupleDescInitEntry */
if (entry->is_not_null)
constr->has_not_null = true;
desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
/*
* Note we copy only pre-cooked default expressions. Digestion of
* raw ones is someone else's problem.
*/
if (entry->cooked_default != NULL)
{
if (attrdef == NULL)
attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault));
attrdef[ndef].adnum = attnum;
attrdef[ndef].adbin = pstrdup(entry->cooked_default);
ndef++;
desc->attrs[attnum - 1]->atthasdef = true;
}
desc->attrs[attnum - 1]->attislocal = entry->is_local;
desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
}
if (constr->has_not_null || ndef > 0)
{
desc->constr = constr;
if (ndef > 0) /* DEFAULTs */
{
if (ndef < natts)
constr->defval = (AttrDefault *)
repalloc(attrdef, ndef * sizeof(AttrDefault));
else
constr->defval = attrdef;
constr->num_defval = ndef;
}
else
{
constr->defval = NULL;
constr->num_defval = 0;
}
constr->check = NULL;
constr->num_check = 0;
}
else
{
pfree(constr);
desc->constr = NULL;
}
return desc;
}