1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* tupdesc.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* POSTGRES tuple descriptor support code
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2002-06-20 22:29:54 +02:00
|
|
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-07-31 19:19:54 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.82 2002/07/31 17:19:51 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* NOTES
|
1997-09-07 07:04:48 +02:00
|
|
|
* some of the executor utility code such as "ExecTypeFromTL" should be
|
|
|
|
* moved here.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1996-11-01 10:41:41 +01:00
|
|
|
|
2002-06-20 19:19:08 +02:00
|
|
|
#include "funcapi.h"
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/namespace.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/pg_type.h"
|
2001-06-25 23:11:45 +02:00
|
|
|
#include "nodes/parsenodes.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "parser/parse_type.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/syscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1996-10-21 00:04:49 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* CreateTemplateTupleDesc
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This function allocates and zeros a tuple descriptor structure.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
TupleDesc
|
2002-07-20 07:16:59 +02:00
|
|
|
CreateTemplateTupleDesc(int natts, hasoid_t withoid)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
uint32 size;
|
|
|
|
TupleDesc desc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* sanity checks
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
AssertArg(natts >= 1);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* allocate enough memory for the tuple descriptor and zero it as
|
|
|
|
* TupleDescInitEntry assumes that the descriptor is filled with NULL
|
|
|
|
* pointers.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1998-09-01 05:29:17 +02:00
|
|
|
size = natts * sizeof(Form_pg_attribute);
|
1997-09-07 07:04:48 +02:00
|
|
|
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
1998-09-01 05:29:17 +02:00
|
|
|
desc->attrs = (Form_pg_attribute *) palloc(size);
|
1997-09-07 07:04:48 +02:00
|
|
|
desc->constr = NULL;
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(desc->attrs, 0, size);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
desc->natts = natts;
|
2002-07-20 07:16:59 +02:00
|
|
|
desc->tdhasoid = withoid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return desc;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* CreateTupleDesc
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1998-09-01 05:29:17 +02:00
|
|
|
* This function allocates a new TupleDesc from Form_pg_attribute array
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
TupleDesc
|
1999-05-26 00:43:53 +02:00
|
|
|
CreateTupleDesc(int natts, Form_pg_attribute *attrs)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc desc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* sanity checks
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
AssertArg(natts >= 1);
|
|
|
|
|
|
|
|
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
|
|
|
desc->attrs = attrs;
|
|
|
|
desc->natts = natts;
|
|
|
|
desc->constr = NULL;
|
2002-07-20 07:16:59 +02:00
|
|
|
desc->tdhasoid = UNDEFOID;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return desc;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* CreateTupleDescCopy
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This function creates a new TupleDesc by copying from an existing
|
|
|
|
* TupleDesc
|
|
|
|
*
|
|
|
|
* !!! Constraints are not copied !!!
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
TupleDesc
|
|
|
|
CreateTupleDescCopy(TupleDesc tupdesc)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc desc;
|
|
|
|
int i,
|
|
|
|
size;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
|
|
|
desc->natts = tupdesc->natts;
|
1998-09-01 05:29:17 +02:00
|
|
|
size = desc->natts * sizeof(Form_pg_attribute);
|
|
|
|
desc->attrs = (Form_pg_attribute *) palloc(size);
|
1997-09-07 07:04:48 +02:00
|
|
|
for (i = 0; i < desc->natts; i++)
|
|
|
|
{
|
1999-02-03 22:18:02 +01:00
|
|
|
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
1997-09-07 07:04:48 +02:00
|
|
|
memmove(desc->attrs[i],
|
|
|
|
tupdesc->attrs[i],
|
|
|
|
ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
desc->attrs[i]->attnotnull = false;
|
|
|
|
desc->attrs[i]->atthasdef = false;
|
|
|
|
}
|
|
|
|
desc->constr = NULL;
|
2002-07-20 07:16:59 +02:00
|
|
|
desc->tdhasoid = tupdesc->tdhasoid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
return desc;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-08-22 04:55:39 +02:00
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* CreateTupleDescCopyConstr
|
|
|
|
*
|
|
|
|
* This function creates a new TupleDesc by copying from an existing
|
|
|
|
* TupleDesc (with Constraints)
|
1997-08-22 04:55:39 +02:00
|
|
|
*
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
TupleDesc
|
|
|
|
CreateTupleDescCopyConstr(TupleDesc tupdesc)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc desc;
|
|
|
|
TupleConstr *constr = tupdesc->constr;
|
|
|
|
int i,
|
|
|
|
size;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
|
|
|
desc->natts = tupdesc->natts;
|
1998-09-01 05:29:17 +02:00
|
|
|
size = desc->natts * sizeof(Form_pg_attribute);
|
|
|
|
desc->attrs = (Form_pg_attribute *) palloc(size);
|
1997-09-07 07:04:48 +02:00
|
|
|
for (i = 0; i < desc->natts; i++)
|
|
|
|
{
|
1999-02-03 22:18:02 +01:00
|
|
|
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
1997-09-07 07:04:48 +02:00
|
|
|
memmove(desc->attrs[i],
|
|
|
|
tupdesc->attrs[i],
|
|
|
|
ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
}
|
|
|
|
if (constr)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
desc->constr = NULL;
|
|
|
|
|
2002-07-20 07:16:59 +02:00
|
|
|
desc->tdhasoid = tupdesc->tdhasoid;
|
1997-09-07 07:04:48 +02:00
|
|
|
return desc;
|
1997-08-22 04:55:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1997-09-07 07:04:48 +02:00
|
|
|
FreeTupleDesc(TupleDesc tupdesc)
|
1997-08-22 04:55:39 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
for (i = 0; i < tupdesc->natts; i++)
|
|
|
|
pfree(tupdesc->attrs[i]);
|
|
|
|
pfree(tupdesc->attrs);
|
|
|
|
if (tupdesc->constr)
|
|
|
|
{
|
|
|
|
if (tupdesc->constr->num_defval > 0)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
AttrDefault *attrdef = tupdesc->constr->defval;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
ConstrCheck *check = tupdesc->constr->check;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
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);
|
1997-08-22 04:55:39 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2000-01-31 05:35:57 +01:00
|
|
|
bool
|
|
|
|
equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
|
|
|
|
{
|
2000-11-08 23:10:03 +01:00
|
|
|
int i,
|
|
|
|
j,
|
|
|
|
n;
|
2000-01-31 05:35:57 +01:00
|
|
|
|
|
|
|
if (tupdesc1->natts != tupdesc2->natts)
|
|
|
|
return false;
|
2002-07-20 07:16:59 +02:00
|
|
|
if (tupdesc1->tdhasoid != tupdesc2->tdhasoid)
|
|
|
|
return false;
|
2000-01-31 05:35:57 +01:00
|
|
|
for (i = 0; i < tupdesc1->natts; i++)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Form_pg_attribute attr1 = tupdesc1->attrs[i];
|
|
|
|
Form_pg_attribute attr2 = tupdesc2->attrs[i];
|
2000-01-31 05:35:57 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* 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.
|
2000-01-31 05:35:57 +01:00
|
|
|
*/
|
|
|
|
if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
|
|
|
|
return false;
|
|
|
|
if (attr1->atttypid != attr2->atttypid)
|
|
|
|
return false;
|
2001-05-07 02:43:27 +02:00
|
|
|
if (attr1->attstattarget != attr2->attstattarget)
|
|
|
|
return false;
|
2000-01-31 05:35:57 +01:00
|
|
|
if (attr1->atttypmod != attr2->atttypmod)
|
|
|
|
return false;
|
|
|
|
if (attr1->attstorage != attr2->attstorage)
|
|
|
|
return false;
|
|
|
|
if (attr1->attnotnull != attr2->attnotnull)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (tupdesc1->constr != NULL)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
TupleConstr *constr1 = tupdesc1->constr;
|
|
|
|
TupleConstr *constr2 = tupdesc2->constr;
|
2000-01-31 05:35:57 +01:00
|
|
|
|
|
|
|
if (constr2 == NULL)
|
|
|
|
return false;
|
2000-11-08 23:10:03 +01:00
|
|
|
if (constr1->has_not_null != constr2->has_not_null)
|
|
|
|
return false;
|
|
|
|
n = constr1->num_defval;
|
|
|
|
if (n != (int) constr2->num_defval)
|
2000-01-31 05:35:57 +01:00
|
|
|
return false;
|
2000-11-08 23:10:03 +01:00
|
|
|
for (i = 0; i < n; i++)
|
2000-01-31 05:35:57 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
AttrDefault *defval1 = constr1->defval + i;
|
2000-11-08 23:10:03 +01:00
|
|
|
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.
|
2000-11-08 23:10:03 +01:00
|
|
|
*/
|
|
|
|
for (j = 0; j < n; defval2++, j++)
|
|
|
|
{
|
|
|
|
if (defval1->adnum == defval2->adnum)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (j >= n)
|
2000-01-31 05:35:57 +01:00
|
|
|
return false;
|
|
|
|
if (strcmp(defval1->adbin, defval2->adbin) != 0)
|
|
|
|
return false;
|
|
|
|
}
|
2000-11-08 23:10:03 +01:00
|
|
|
n = constr1->num_check;
|
|
|
|
if (n != (int) constr2->num_check)
|
2000-01-31 05:35:57 +01:00
|
|
|
return false;
|
2000-11-08 23:10:03 +01:00
|
|
|
for (i = 0; i < n; i++)
|
2000-01-31 05:35:57 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
ConstrCheck *check1 = constr1->check + i;
|
2000-11-08 23:10:03 +01:00
|
|
|
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...)
|
2000-11-08 23:10:03 +01:00
|
|
|
*/
|
|
|
|
for (j = 0; j < n; check2++, j++)
|
|
|
|
{
|
|
|
|
if (strcmp(check1->ccname, check2->ccname) == 0 &&
|
|
|
|
strcmp(check1->ccbin, check2->ccbin) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (j >= n)
|
2000-01-31 05:35:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tupdesc2->constr != NULL)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* TupleDescInitEntry
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This function initializes a single attribute structure in
|
|
|
|
* a preallocated tuple descriptor.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
2002-03-29 20:06:29 +01:00
|
|
|
void
|
1996-07-09 08:22:35 +02:00
|
|
|
TupleDescInitEntry(TupleDesc desc,
|
1997-09-07 07:04:48 +02:00
|
|
|
AttrNumber attributeNumber,
|
|
|
|
char *attributeName,
|
2000-05-22 04:34:23 +02:00
|
|
|
Oid oidtypeid,
|
1998-07-12 23:29:40 +02:00
|
|
|
int32 typmod,
|
1997-09-07 07:04:48 +02:00
|
|
|
int attdim,
|
|
|
|
bool attisset)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tuple;
|
1998-09-01 05:29:17 +02:00
|
|
|
Form_pg_type typeForm;
|
|
|
|
Form_pg_attribute att;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* sanity checks
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
AssertArg(PointerIsValid(desc));
|
|
|
|
AssertArg(attributeNumber >= 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* attributeName's are sometimes NULL, from resdom's. I don't know
|
|
|
|
* why that is, though -- Jolly
|
|
|
|
*/
|
|
|
|
/* AssertArg(NameIsValid(attributeName));*/
|
|
|
|
|
|
|
|
AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* allocate storage for this attribute
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
att = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
1997-09-07 07:04:48 +02:00
|
|
|
desc->attrs[attributeNumber - 1] = att;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* initialize the attribute fields
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
att->attrelid = 0; /* dummy value */
|
|
|
|
|
|
|
|
if (attributeName != NULL)
|
|
|
|
namestrcpy(&(att->attname), attributeName);
|
|
|
|
else
|
1999-11-08 00:08:36 +01:00
|
|
|
MemSet(NameStr(att->attname), 0, NAMEDATALEN);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-07-31 19:19:54 +02:00
|
|
|
att->attstattarget = -1;
|
1997-09-07 07:04:48 +02:00
|
|
|
att->attcacheoff = -1;
|
1998-02-10 05:02:59 +01:00
|
|
|
att->atttypmod = typmod;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
att->attnum = attributeNumber;
|
2001-05-07 02:43:27 +02:00
|
|
|
att->attndims = attdim;
|
1997-09-07 07:04:48 +02:00
|
|
|
att->attisset = attisset;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
att->attnotnull = false;
|
|
|
|
att->atthasdef = false;
|
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(oidtypeid),
|
|
|
|
0, 0, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2002-03-29 20:06:29 +01:00
|
|
|
elog(ERROR, "Unable to look up type id %u", oidtypeid);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* type info exists so we initialize our attribute information from
|
|
|
|
* the type tuple we found..
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1998-09-01 05:29:17 +02:00
|
|
|
typeForm = (Form_pg_type) GETSTRUCT(tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-07-20 07:16:59 +02:00
|
|
|
att->atttypid = HeapTupleGetOid(tuple);
|
2000-12-28 00:59:14 +01:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-12-28 00:59:14 +01:00
|
|
|
* 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
|
2001-03-22 07:16:21 +01:00
|
|
|
* 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.
|
2000-12-28 00:59:14 +01:00
|
|
|
*
|
2001-03-22 07:16:21 +01:00
|
|
|
* (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
|
2002-02-27 20:36:13 +01:00
|
|
|
* tell the front end what type will be returned,
|
2001-03-22 07:16:21 +01:00
|
|
|
* and in the end the type returned will be the result of the query,
|
|
|
|
* not an OID.)
|
2000-12-28 00:59:14 +01:00
|
|
|
*
|
|
|
|
* (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.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
if (attisset)
|
|
|
|
{
|
2000-11-16 23:30:52 +01:00
|
|
|
att->attlen = sizeof(Oid);
|
|
|
|
att->attbyval = true;
|
2000-12-28 00:59:14 +01:00
|
|
|
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 ... */
|
2000-11-16 23:30:52 +01:00
|
|
|
att->attstorage = 'p';
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
att->attlen = typeForm->typlen;
|
|
|
|
att->attbyval = typeForm->typbyval;
|
2000-12-28 00:59:14 +01:00
|
|
|
att->attalign = typeForm->typalign;
|
2000-07-04 01:10:14 +02:00
|
|
|
att->attstorage = typeForm->typstorage;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-03-29 20:06:29 +01:00
|
|
|
/*
|
|
|
|
* BuildDescForRelation
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2002-03-29 20:06:29 +01:00
|
|
|
* Given a relation schema (list of ColumnDef nodes), build a TupleDesc.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
TupleDesc
|
2002-03-29 20:06:29 +01:00
|
|
|
BuildDescForRelation(List *schema)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int natts;
|
|
|
|
AttrNumber attnum;
|
|
|
|
List *p;
|
|
|
|
TupleDesc desc;
|
|
|
|
AttrDefault *attrdef = NULL;
|
|
|
|
TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
|
|
|
|
char *attname;
|
1998-07-12 23:29:40 +02:00
|
|
|
int32 atttypmod;
|
1997-09-08 04:41:22 +02:00
|
|
|
int attdim;
|
|
|
|
int ndef = 0;
|
|
|
|
bool attisset;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* allocate a new tuple descriptor
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
natts = length(schema);
|
2002-07-20 07:16:59 +02:00
|
|
|
desc = CreateTemplateTupleDesc(natts, UNDEFOID);
|
1997-09-07 07:04:48 +02:00
|
|
|
constr->has_not_null = false;
|
1997-08-19 06:46:15 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
attnum = 0;
|
|
|
|
|
|
|
|
foreach(p, schema)
|
1997-08-22 04:55:39 +02:00
|
|
|
{
|
1999-10-04 01:55:40 +02:00
|
|
|
ColumnDef *entry = lfirst(p);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
attnum++;
|
|
|
|
|
|
|
|
attname = entry->colname;
|
|
|
|
attisset = entry->typename->setof;
|
1998-02-10 05:02:59 +01:00
|
|
|
atttypmod = entry->typename->typmod;
|
2002-03-29 20:06:29 +01:00
|
|
|
attdim = length(entry->typename->arrayBounds);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-29 20:06:29 +01:00
|
|
|
TupleDescInitEntry(desc, attnum, attname,
|
|
|
|
typenameTypeId(entry->typename),
|
|
|
|
atttypmod, attdim, attisset);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* This is for constraints */
|
|
|
|
if (entry->is_not_null)
|
|
|
|
constr->has_not_null = true;
|
|
|
|
desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Note we copy only pre-cooked default expressions. Digestion of
|
|
|
|
* raw ones is someone else's problem.
|
1999-10-04 01:55:40 +02:00
|
|
|
*/
|
|
|
|
if (entry->cooked_default != NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
if (attrdef == NULL)
|
|
|
|
attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault));
|
|
|
|
attrdef[ndef].adnum = attnum;
|
1999-10-04 01:55:40 +02:00
|
|
|
attrdef[ndef].adbin = pstrdup(entry->cooked_default);
|
1997-09-07 07:04:48 +02:00
|
|
|
ndef++;
|
|
|
|
desc->attrs[attnum - 1]->atthasdef = true;
|
|
|
|
}
|
|
|
|
|
1997-08-22 04:55:39 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
if (constr->has_not_null || ndef > 0)
|
|
|
|
{
|
|
|
|
desc->constr = constr;
|
1997-08-19 06:46:15 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (ndef > 0) /* DEFAULTs */
|
|
|
|
{
|
|
|
|
if (ndef < natts)
|
|
|
|
constr->defval = (AttrDefault *)
|
|
|
|
repalloc(attrdef, ndef * sizeof(AttrDefault));
|
|
|
|
else
|
|
|
|
constr->defval = attrdef;
|
|
|
|
constr->num_defval = ndef;
|
|
|
|
}
|
|
|
|
else
|
1999-10-04 01:55:40 +02:00
|
|
|
{
|
|
|
|
constr->defval = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
constr->num_defval = 0;
|
1999-10-04 01:55:40 +02:00
|
|
|
}
|
|
|
|
constr->check = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
constr->num_check = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pfree(constr);
|
|
|
|
desc->constr = NULL;
|
|
|
|
}
|
|
|
|
return desc;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2002-06-20 19:19:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RelationNameGetTupleDesc
|
|
|
|
*
|
|
|
|
* Given a (possibly qualified) relation name, build a TupleDesc.
|
|
|
|
*/
|
|
|
|
TupleDesc
|
|
|
|
RelationNameGetTupleDesc(char *relname)
|
|
|
|
{
|
|
|
|
RangeVar *relvar;
|
|
|
|
Relation rel;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
List *relname_list;
|
|
|
|
|
|
|
|
/* Open relation and get the tuple description */
|
|
|
|
relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
|
|
|
|
relvar = makeRangeVarFromNameList(relname_list);
|
|
|
|
rel = heap_openrv(relvar, AccessShareLock);
|
|
|
|
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
|
|
|
|
relation_close(rel, AccessShareLock);
|
|
|
|
|
|
|
|
return tupdesc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TypeGetTupleDesc
|
|
|
|
*
|
|
|
|
* Given a type Oid, build a TupleDesc.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* If the type is a base type, a single item alias List is required.
|
|
|
|
*/
|
|
|
|
TupleDesc
|
|
|
|
TypeGetTupleDesc(Oid typeoid, List *colaliases)
|
|
|
|
{
|
|
|
|
Oid relid = typeidTypeRelid(typeoid);
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build a suitable tupledesc representing the output rows
|
|
|
|
*/
|
|
|
|
if (OidIsValid(relid))
|
|
|
|
{
|
|
|
|
/* Composite data type, i.e. a table's row type */
|
|
|
|
Relation rel;
|
|
|
|
int natts;
|
|
|
|
|
|
|
|
rel = relation_open(relid, AccessShareLock);
|
|
|
|
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
|
|
|
|
natts = tupdesc->natts;
|
|
|
|
relation_close(rel, AccessShareLock);
|
|
|
|
|
|
|
|
/* check to see if we've given column aliases */
|
|
|
|
if(colaliases != NIL)
|
|
|
|
{
|
|
|
|
char *label;
|
|
|
|
int varattno;
|
|
|
|
|
|
|
|
/* does the List length match the number of attributes */
|
|
|
|
if (length(colaliases) != natts)
|
|
|
|
elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
|
|
|
|
|
|
|
|
/* OK, use the aliases instead */
|
|
|
|
for (varattno = 0; varattno < natts; varattno++)
|
|
|
|
{
|
|
|
|
label = strVal(nth(varattno, colaliases));
|
|
|
|
|
|
|
|
if (label != NULL)
|
|
|
|
namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
|
|
|
|
else
|
|
|
|
MemSet(NameStr(tupdesc->attrs[varattno]->attname), 0, NAMEDATALEN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Must be a base data type, i.e. scalar */
|
|
|
|
char *attname;
|
|
|
|
|
|
|
|
/* the alias List is required for base types */
|
|
|
|
if (colaliases == NIL)
|
|
|
|
elog(ERROR, "TypeGetTupleDesc: no column alias was provided");
|
|
|
|
|
|
|
|
/* the alias List length must be 1 */
|
|
|
|
if (length(colaliases) != 1)
|
|
|
|
elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
|
|
|
|
|
|
|
|
/* OK, get the column alias */
|
|
|
|
attname = strVal(lfirst(colaliases));
|
|
|
|
|
2002-07-20 07:16:59 +02:00
|
|
|
tupdesc = CreateTemplateTupleDesc(1, WITHOUTOID);
|
2002-06-20 19:19:08 +02:00
|
|
|
TupleDescInitEntry(tupdesc,
|
|
|
|
(AttrNumber) 1,
|
|
|
|
attname,
|
|
|
|
typeoid,
|
|
|
|
-1,
|
|
|
|
0,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return tupdesc;
|
|
|
|
}
|