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
|
|
|
*
|
2004-12-31 23:04:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2005, 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
|
2004-12-31 23:04:05 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.108 2004/12/31 21:59:07 pgsql 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"
|
2002-08-05 04:30:50 +02:00
|
|
|
#include "utils/lsyscache.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "utils/syscache.h"
|
2004-04-01 23:28:47 +02:00
|
|
|
#include "utils/typcache.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.
|
2004-04-01 23:28:47 +02:00
|
|
|
*
|
|
|
|
* Tuple type ID information is initially set for an anonymous record type;
|
|
|
|
* caller can overwrite this if needed.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
TupleDesc
|
2002-09-02 03:05:06 +02:00
|
|
|
CreateTemplateTupleDesc(int natts, bool hasoid)
|
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
|
|
|
*/
|
2002-09-28 22:00:19 +02:00
|
|
|
AssertArg(natts >= 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2004-04-01 23:28:47 +02:00
|
|
|
* Allocate enough memory for the tuple descriptor, and zero the
|
2004-08-29 07:07:03 +02:00
|
|
|
* attrs[] array since TupleDescInitEntry assumes that the array is
|
|
|
|
* filled with NULL pointers.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
|
|
|
|
2002-09-28 22:00:19 +02:00
|
|
|
if (natts > 0)
|
2004-04-01 23:28:47 +02:00
|
|
|
desc->attrs = (Form_pg_attribute *)
|
|
|
|
palloc0(natts * sizeof(Form_pg_attribute));
|
2002-09-28 22:00:19 +02:00
|
|
|
else
|
|
|
|
desc->attrs = NULL;
|
2004-04-01 23:28:47 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize other fields of the tupdesc.
|
|
|
|
*/
|
|
|
|
desc->natts = natts;
|
2002-09-28 22:00:19 +02:00
|
|
|
desc->constr = NULL;
|
2004-04-01 23:28:47 +02:00
|
|
|
desc->tdtypeid = RECORDOID;
|
|
|
|
desc->tdtypmod = -1;
|
|
|
|
desc->tdhasoid = hasoid;
|
2002-09-28 22:00:19 +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
|
|
|
*
|
2002-09-02 03:05:06 +02:00
|
|
|
* This function allocates a new TupleDesc pointing to a given
|
|
|
|
* Form_pg_attribute array
|
2004-04-01 23:28:47 +02:00
|
|
|
*
|
|
|
|
* Tuple type ID information is initially set for an anonymous record type;
|
|
|
|
* caller can overwrite this if needed.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
TupleDesc
|
2002-09-02 03:05:06 +02:00
|
|
|
CreateTupleDesc(int natts, bool hasoid, 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
|
|
|
*/
|
2002-09-28 22:00:19 +02:00
|
|
|
AssertArg(natts >= 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
|
|
|
desc->attrs = attrs;
|
|
|
|
desc->natts = natts;
|
|
|
|
desc->constr = NULL;
|
2004-04-01 23:28:47 +02:00
|
|
|
desc->tdtypeid = RECORDOID;
|
|
|
|
desc->tdtypmod = -1;
|
2002-09-02 03:05:06 +02:00
|
|
|
desc->tdhasoid = hasoid;
|
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
|
|
|
|
*
|
2004-04-01 23:28:47 +02:00
|
|
|
* !!! Constraints and defaults 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;
|
2004-04-01 23:28:47 +02:00
|
|
|
int i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
|
|
|
desc->natts = tupdesc->natts;
|
2002-09-28 22:00:19 +02:00
|
|
|
if (desc->natts > 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-04-01 23:28:47 +02:00
|
|
|
desc->attrs = (Form_pg_attribute *)
|
|
|
|
palloc(desc->natts * sizeof(Form_pg_attribute));
|
2002-09-28 22:00:19 +02:00
|
|
|
for (i = 0; i < desc->natts; i++)
|
|
|
|
{
|
|
|
|
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
desc->attrs[i]->attnotnull = false;
|
|
|
|
desc->attrs[i]->atthasdef = false;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2002-09-28 22:00:19 +02:00
|
|
|
else
|
|
|
|
desc->attrs = NULL;
|
2004-04-01 23:28:47 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
desc->constr = NULL;
|
2004-04-01 23:28:47 +02:00
|
|
|
|
|
|
|
desc->tdtypeid = tupdesc->tdtypeid;
|
|
|
|
desc->tdtypmod = tupdesc->tdtypmod;
|
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
|
2004-04-01 23:28:47 +02:00
|
|
|
* TupleDesc (including its constraints and defaults)
|
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;
|
2004-04-01 23:28:47 +02:00
|
|
|
int i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
|
|
|
desc->natts = tupdesc->natts;
|
2002-09-28 22:00:19 +02:00
|
|
|
if (desc->natts > 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-04-01 23:28:47 +02:00
|
|
|
desc->attrs = (Form_pg_attribute *)
|
|
|
|
palloc(desc->natts * sizeof(Form_pg_attribute));
|
2002-09-28 22:00:19 +02:00
|
|
|
for (i = 0; i < desc->natts; i++)
|
|
|
|
{
|
|
|
|
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2002-09-28 22:00:19 +02:00
|
|
|
else
|
|
|
|
desc->attrs = NULL;
|
2004-04-01 23:28:47 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (constr)
|
|
|
|
{
|
2004-04-01 23:28:47 +02:00
|
|
|
TupleConstr *cpy = (TupleConstr *) palloc0(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;
|
|
|
|
|
2004-04-01 23:28:47 +02:00
|
|
|
desc->tdtypeid = tupdesc->tdtypeid;
|
|
|
|
desc->tdtypmod = tupdesc->tdtypmod;
|
2002-07-20 07:16:59 +02:00
|
|
|
desc->tdhasoid = tupdesc->tdhasoid;
|
2004-04-01 23:28:47 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
return desc;
|
1997-08-22 04:55:39 +02:00
|
|
|
}
|
|
|
|
|
2004-04-01 23:28:47 +02:00
|
|
|
/*
|
|
|
|
* Free a TupleDesc including all substructure
|
|
|
|
*/
|
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]);
|
2002-09-28 22:00:19 +02:00
|
|
|
if (tupdesc->attrs)
|
|
|
|
pfree(tupdesc->attrs);
|
1997-09-07 07:04:48 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2002-08-30 21:23:20 +02:00
|
|
|
/*
|
|
|
|
* Compare two TupleDesc structures for logical equality
|
2004-04-01 23:28:47 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2002-08-30 21:23:20 +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;
|
2004-04-01 23:28:47 +02:00
|
|
|
if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
|
|
|
|
return false;
|
2002-07-20 07:16:59 +02:00
|
|
|
if (tupdesc1->tdhasoid != tupdesc2->tdhasoid)
|
|
|
|
return false;
|
2004-04-01 23:28:47 +02:00
|
|
|
|
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.
|
2004-04-01 23:28:47 +02:00
|
|
|
* Also, attcacheoff must NOT be checked since it's possibly not
|
|
|
|
* set in both copies.
|
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;
|
2004-04-01 23:28:47 +02:00
|
|
|
if (attr1->attndims != attr2->attndims)
|
|
|
|
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;
|
2002-08-30 21:23:20 +02:00
|
|
|
if (attr1->atthasdef != attr2->atthasdef)
|
|
|
|
return false;
|
2002-08-02 20:15:10 +02:00
|
|
|
if (attr1->attisdropped != attr2->attisdropped)
|
|
|
|
return false;
|
2002-09-22 21:42:52 +02:00
|
|
|
if (attr1->attislocal != attr2->attislocal)
|
|
|
|
return false;
|
|
|
|
if (attr1->attinhcount != attr2->attinhcount)
|
2002-08-30 21:23:20 +02:00
|
|
|
return false;
|
2000-01-31 05:35:57 +01:00
|
|
|
}
|
2004-04-01 23:28:47 +02:00
|
|
|
|
2000-01-31 05:35:57 +01:00
|
|
|
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,
|
2003-08-12 01:04:50 +02:00
|
|
|
const char *attributeName,
|
2000-05-22 04:34:23 +02:00
|
|
|
Oid oidtypeid,
|
1998-07-12 23:29:40 +02:00
|
|
|
int32 typmod,
|
2004-04-01 23:28:47 +02:00
|
|
|
int attdim)
|
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);
|
2002-09-28 22:00:19 +02:00
|
|
|
AssertArg(attributeNumber <= desc->natts);
|
1997-09-07 07:04:48 +02:00
|
|
|
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 */
|
|
|
|
|
2003-08-12 01:04:50 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
2003-08-12 01:04:50 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
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;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
att->attnotnull = false;
|
|
|
|
att->atthasdef = false;
|
2002-08-02 20:15:10 +02:00
|
|
|
att->attisdropped = false;
|
2002-09-22 21:42:52 +02:00
|
|
|
att->attislocal = true;
|
|
|
|
att->attinhcount = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
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))
|
2003-07-21 22:29:40 +02:00
|
|
|
elog(ERROR, "cache lookup failed for type %u", oidtypeid);
|
1998-09-01 05:29:17 +02:00
|
|
|
typeForm = (Form_pg_type) GETSTRUCT(tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-04-01 23:28:47 +02:00
|
|
|
att->atttypid = oidtypeid;
|
|
|
|
att->attlen = typeForm->typlen;
|
|
|
|
att->attbyval = typeForm->typbyval;
|
|
|
|
att->attalign = typeForm->typalign;
|
|
|
|
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.
|
2002-09-02 03:05:06 +02:00
|
|
|
*
|
|
|
|
* 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
|
2004-04-01 23:28:47 +02:00
|
|
|
* later on.
|
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;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc desc;
|
|
|
|
AttrDefault *attrdef = NULL;
|
2004-04-01 23:28:47 +02:00
|
|
|
TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
|
1997-09-08 04:41:22 +02:00
|
|
|
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;
|
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
|
|
|
*/
|
2004-05-31 01:40:41 +02:00
|
|
|
natts = list_length(schema);
|
2002-09-02 03:05:06 +02:00
|
|
|
desc = CreateTemplateTupleDesc(natts, false);
|
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;
|
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, schema)
|
1997-08-22 04:55:39 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
ColumnDef *entry = lfirst(l);
|
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;
|
1998-02-10 05:02:59 +01:00
|
|
|
atttypmod = entry->typename->typmod;
|
2004-05-31 01:40:41 +02:00
|
|
|
attdim = list_length(entry->typename->arrayBounds);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-04-01 23:28:47 +02:00
|
|
|
if (entry->typename->setof)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
|
|
|
errmsg("column \"%s\" cannot be declared SETOF",
|
|
|
|
attname)));
|
|
|
|
|
2002-03-29 20:06:29 +01:00
|
|
|
TupleDescInitEntry(desc, attnum, attname,
|
|
|
|
typenameTypeId(entry->typename),
|
2004-04-01 23:28:47 +02:00
|
|
|
atttypmod, attdim);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-30 21:23:20 +02:00
|
|
|
/* Fill in additional stuff not handled by TupleDescInitEntry */
|
1997-09-07 07:04:48 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-09-22 21:42:52 +02:00
|
|
|
desc->attrs[attnum - 1]->attislocal = entry->is_local;
|
|
|
|
desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
|
1997-08-22 04:55:39 +02:00
|
|
|
}
|
2002-08-30 21:23:20 +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;
|
|
|
|
}
|
2004-04-01 23:28:47 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
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
|
2002-08-29 02:17:06 +02:00
|
|
|
RelationNameGetTupleDesc(const char *relname)
|
2002-06-20 19:19:08 +02:00
|
|
|
{
|
|
|
|
RangeVar *relvar;
|
|
|
|
Relation rel;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
List *relname_list;
|
|
|
|
|
2004-04-01 23:28:47 +02:00
|
|
|
/* Open relation and copy the tuple description */
|
2002-06-20 19:19:08 +02:00
|
|
|
relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
|
|
|
|
relvar = makeRangeVarFromNameList(relname_list);
|
2002-08-29 02:17:06 +02:00
|
|
|
rel = relation_openrv(relvar, AccessShareLock);
|
2002-06-20 19:19:08 +02:00
|
|
|
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
|
2004-04-01 23:28:47 +02:00
|
|
|
* attnames. (NB: this usage is deprecated since it may result in
|
|
|
|
* creation of unnecessary transient record types.)
|
2002-06-20 19:19:08 +02:00
|
|
|
*
|
|
|
|
* If the type is a base type, a single item alias List is required.
|
|
|
|
*/
|
|
|
|
TupleDesc
|
|
|
|
TypeGetTupleDesc(Oid typeoid, List *colaliases)
|
|
|
|
{
|
2004-10-20 18:04:50 +02:00
|
|
|
TypeFuncClass functypclass = get_type_func_class(typeoid);
|
Attached are two patches to implement and document anonymous composite
types for Table Functions, as previously proposed on HACKERS. Here is a
brief explanation:
1. Creates a new pg_type typtype: 'p' for pseudo type (currently either
'b' for base or 'c' for catalog, i.e. a class).
2. Creates new builtin type of typtype='p' named RECORD. This is the
first of potentially several pseudo types.
3. Modify FROM clause grammer to accept:
SELECT * FROM my_func() AS m(colname1 type1, colname2 type1, ...)
where m is the table alias, colname1, etc are the column names, and
type1, etc are the column types.
4. When typtype == 'p' and the function return type is RECORD, a list
of column defs is required, and when typtype != 'p', it is
disallowed.
5. A check was added to ensure that the tupdesc provide via the parser
and the actual return tupdesc match in number and type of
attributes.
When creating a function you can do:
CREATE FUNCTION foo(text) RETURNS setof RECORD ...
When using it you can do:
SELECT * from foo(sqlstmt) AS (f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) AS f(f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) f(f1 int, f2 text, f3 timestamp)
Included in the patches are adjustments to the regression test sql and
expected files, and documentation.
p.s.
This potentially solves (or at least improves) the issue of builtin
Table Functions. They can be bootstrapped as returning RECORD, and
we can wrap system views around them with properly specified column
defs. For example:
CREATE VIEW pg_settings AS
SELECT s.name, s.setting
FROM show_all_settings()AS s(name text, setting text);
Then we can also add the UPDATE RULE that I previously posted to
pg_settings, and have pg_settings act like a virtual table, allowing
settings to be queried and set.
Joe Conway
2002-08-04 21:48:11 +02:00
|
|
|
TupleDesc tupdesc = NULL;
|
2002-06-20 19:19:08 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Build a suitable tupledesc representing the output rows
|
|
|
|
*/
|
2004-10-20 18:04:50 +02:00
|
|
|
if (functypclass == TYPEFUNC_COMPOSITE)
|
2002-06-20 19:19:08 +02:00
|
|
|
{
|
2004-04-01 23:28:47 +02:00
|
|
|
/* Composite data type, e.g. a table's row type */
|
|
|
|
tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
|
2002-06-20 19:19:08 +02:00
|
|
|
|
2002-08-29 02:17:06 +02:00
|
|
|
if (colaliases != NIL)
|
|
|
|
{
|
2004-04-01 23:28:47 +02:00
|
|
|
int natts = tupdesc->natts;
|
2002-08-29 02:17:06 +02:00
|
|
|
int varattno;
|
Attached are two patches to implement and document anonymous composite
types for Table Functions, as previously proposed on HACKERS. Here is a
brief explanation:
1. Creates a new pg_type typtype: 'p' for pseudo type (currently either
'b' for base or 'c' for catalog, i.e. a class).
2. Creates new builtin type of typtype='p' named RECORD. This is the
first of potentially several pseudo types.
3. Modify FROM clause grammer to accept:
SELECT * FROM my_func() AS m(colname1 type1, colname2 type1, ...)
where m is the table alias, colname1, etc are the column names, and
type1, etc are the column types.
4. When typtype == 'p' and the function return type is RECORD, a list
of column defs is required, and when typtype != 'p', it is
disallowed.
5. A check was added to ensure that the tupdesc provide via the parser
and the actual return tupdesc match in number and type of
attributes.
When creating a function you can do:
CREATE FUNCTION foo(text) RETURNS setof RECORD ...
When using it you can do:
SELECT * from foo(sqlstmt) AS (f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) AS f(f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) f(f1 int, f2 text, f3 timestamp)
Included in the patches are adjustments to the regression test sql and
expected files, and documentation.
p.s.
This potentially solves (or at least improves) the issue of builtin
Table Functions. They can be bootstrapped as returning RECORD, and
we can wrap system views around them with properly specified column
defs. For example:
CREATE VIEW pg_settings AS
SELECT s.name, s.setting
FROM show_all_settings()AS s(name text, setting text);
Then we can also add the UPDATE RULE that I previously posted to
pg_settings, and have pg_settings act like a virtual table, allowing
settings to be queried and set.
Joe Conway
2002-08-04 21:48:11 +02:00
|
|
|
|
2002-08-29 02:17:06 +02:00
|
|
|
/* does the list length match the number of attributes? */
|
2004-05-31 01:40:41 +02:00
|
|
|
if (list_length(colaliases) != natts)
|
2003-07-21 22:29:40 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("number of aliases does not match number of columns")));
|
Attached are two patches to implement and document anonymous composite
types for Table Functions, as previously proposed on HACKERS. Here is a
brief explanation:
1. Creates a new pg_type typtype: 'p' for pseudo type (currently either
'b' for base or 'c' for catalog, i.e. a class).
2. Creates new builtin type of typtype='p' named RECORD. This is the
first of potentially several pseudo types.
3. Modify FROM clause grammer to accept:
SELECT * FROM my_func() AS m(colname1 type1, colname2 type1, ...)
where m is the table alias, colname1, etc are the column names, and
type1, etc are the column types.
4. When typtype == 'p' and the function return type is RECORD, a list
of column defs is required, and when typtype != 'p', it is
disallowed.
5. A check was added to ensure that the tupdesc provide via the parser
and the actual return tupdesc match in number and type of
attributes.
When creating a function you can do:
CREATE FUNCTION foo(text) RETURNS setof RECORD ...
When using it you can do:
SELECT * from foo(sqlstmt) AS (f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) AS f(f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) f(f1 int, f2 text, f3 timestamp)
Included in the patches are adjustments to the regression test sql and
expected files, and documentation.
p.s.
This potentially solves (or at least improves) the issue of builtin
Table Functions. They can be bootstrapped as returning RECORD, and
we can wrap system views around them with properly specified column
defs. For example:
CREATE VIEW pg_settings AS
SELECT s.name, s.setting
FROM show_all_settings()AS s(name text, setting text);
Then we can also add the UPDATE RULE that I previously posted to
pg_settings, and have pg_settings act like a virtual table, allowing
settings to be queried and set.
Joe Conway
2002-08-04 21:48:11 +02:00
|
|
|
|
2002-08-29 02:17:06 +02:00
|
|
|
/* OK, use the aliases instead */
|
|
|
|
for (varattno = 0; varattno < natts; varattno++)
|
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
char *label = strVal(list_nth(colaliases, varattno));
|
Attached are two patches to implement and document anonymous composite
types for Table Functions, as previously proposed on HACKERS. Here is a
brief explanation:
1. Creates a new pg_type typtype: 'p' for pseudo type (currently either
'b' for base or 'c' for catalog, i.e. a class).
2. Creates new builtin type of typtype='p' named RECORD. This is the
first of potentially several pseudo types.
3. Modify FROM clause grammer to accept:
SELECT * FROM my_func() AS m(colname1 type1, colname2 type1, ...)
where m is the table alias, colname1, etc are the column names, and
type1, etc are the column types.
4. When typtype == 'p' and the function return type is RECORD, a list
of column defs is required, and when typtype != 'p', it is
disallowed.
5. A check was added to ensure that the tupdesc provide via the parser
and the actual return tupdesc match in number and type of
attributes.
When creating a function you can do:
CREATE FUNCTION foo(text) RETURNS setof RECORD ...
When using it you can do:
SELECT * from foo(sqlstmt) AS (f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) AS f(f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) f(f1 int, f2 text, f3 timestamp)
Included in the patches are adjustments to the regression test sql and
expected files, and documentation.
p.s.
This potentially solves (or at least improves) the issue of builtin
Table Functions. They can be bootstrapped as returning RECORD, and
we can wrap system views around them with properly specified column
defs. For example:
CREATE VIEW pg_settings AS
SELECT s.name, s.setting
FROM show_all_settings()AS s(name text, setting text);
Then we can also add the UPDATE RULE that I previously posted to
pg_settings, and have pg_settings act like a virtual table, allowing
settings to be queried and set.
Joe Conway
2002-08-04 21:48:11 +02:00
|
|
|
|
2002-08-29 02:17:06 +02:00
|
|
|
if (label != NULL)
|
|
|
|
namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
|
2002-06-20 19:19:08 +02:00
|
|
|
}
|
2004-04-01 23:28:47 +02:00
|
|
|
|
|
|
|
/* The tuple type is now an anonymous record type */
|
|
|
|
tupdesc->tdtypeid = RECORDOID;
|
|
|
|
tupdesc->tdtypmod = -1;
|
2002-06-20 19:19:08 +02:00
|
|
|
}
|
|
|
|
}
|
2004-10-20 18:04:50 +02:00
|
|
|
else if (functypclass == TYPEFUNC_SCALAR)
|
2002-06-20 19:19:08 +02:00
|
|
|
{
|
2004-10-20 18:04:50 +02:00
|
|
|
/* Base data type, i.e. scalar */
|
2002-06-20 19:19:08 +02:00
|
|
|
char *attname;
|
|
|
|
|
2002-08-29 02:17:06 +02:00
|
|
|
/* the alias list is required for base types */
|
2002-06-20 19:19:08 +02:00
|
|
|
if (colaliases == NIL)
|
2003-07-21 22:29:40 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
|
|
errmsg("no column alias was provided")));
|
2002-06-20 19:19:08 +02:00
|
|
|
|
2002-08-29 02:17:06 +02:00
|
|
|
/* the alias list length must be 1 */
|
2004-05-31 01:40:41 +02:00
|
|
|
if (list_length(colaliases) != 1)
|
2003-07-21 22:29:40 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("number of aliases does not match number of columns")));
|
2002-06-20 19:19:08 +02:00
|
|
|
|
|
|
|
/* OK, get the column alias */
|
2004-05-26 06:41:50 +02:00
|
|
|
attname = strVal(linitial(colaliases));
|
2002-06-20 19:19:08 +02:00
|
|
|
|
2002-09-02 03:05:06 +02:00
|
|
|
tupdesc = CreateTemplateTupleDesc(1, false);
|
2002-06-20 19:19:08 +02:00
|
|
|
TupleDescInitEntry(tupdesc,
|
|
|
|
(AttrNumber) 1,
|
|
|
|
attname,
|
|
|
|
typeoid,
|
|
|
|
-1,
|
2004-04-01 23:28:47 +02:00
|
|
|
0);
|
2002-06-20 19:19:08 +02:00
|
|
|
}
|
2004-10-20 18:04:50 +02:00
|
|
|
else if (functypclass == TYPEFUNC_RECORD)
|
2004-04-01 23:28:47 +02:00
|
|
|
{
|
|
|
|
/* XXX can't support this because typmod wasn't passed in ... */
|
2003-07-21 22:29:40 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("could not determine row description for function returning record")));
|
2004-04-01 23:28:47 +02:00
|
|
|
}
|
Attached are two patches to implement and document anonymous composite
types for Table Functions, as previously proposed on HACKERS. Here is a
brief explanation:
1. Creates a new pg_type typtype: 'p' for pseudo type (currently either
'b' for base or 'c' for catalog, i.e. a class).
2. Creates new builtin type of typtype='p' named RECORD. This is the
first of potentially several pseudo types.
3. Modify FROM clause grammer to accept:
SELECT * FROM my_func() AS m(colname1 type1, colname2 type1, ...)
where m is the table alias, colname1, etc are the column names, and
type1, etc are the column types.
4. When typtype == 'p' and the function return type is RECORD, a list
of column defs is required, and when typtype != 'p', it is
disallowed.
5. A check was added to ensure that the tupdesc provide via the parser
and the actual return tupdesc match in number and type of
attributes.
When creating a function you can do:
CREATE FUNCTION foo(text) RETURNS setof RECORD ...
When using it you can do:
SELECT * from foo(sqlstmt) AS (f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) AS f(f1 int, f2 text, f3 timestamp)
or
SELECT * from foo(sqlstmt) f(f1 int, f2 text, f3 timestamp)
Included in the patches are adjustments to the regression test sql and
expected files, and documentation.
p.s.
This potentially solves (or at least improves) the issue of builtin
Table Functions. They can be bootstrapped as returning RECORD, and
we can wrap system views around them with properly specified column
defs. For example:
CREATE VIEW pg_settings AS
SELECT s.name, s.setting
FROM show_all_settings()AS s(name text, setting text);
Then we can also add the UPDATE RULE that I previously posted to
pg_settings, and have pg_settings act like a virtual table, allowing
settings to be queried and set.
Joe Conway
2002-08-04 21:48:11 +02:00
|
|
|
else
|
2003-06-15 19:59:10 +02:00
|
|
|
{
|
|
|
|
/* crummy error message, but parser should have caught this */
|
|
|
|
elog(ERROR, "function in FROM has unsupported return type");
|
|
|
|
}
|
2002-06-20 19:19:08 +02:00
|
|
|
|
|
|
|
return tupdesc;
|
|
|
|
}
|