aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/common/tupdesc.c67
-rw-r--r--src/backend/catalog/pg_proc.c176
-rw-r--r--src/backend/executor/functions.c5
-rw-r--r--src/backend/executor/nodeFunctionscan.c126
-rw-r--r--src/backend/nodes/copyfuncs.c4
-rw-r--r--src/backend/nodes/equalfuncs.c6
-rw-r--r--src/backend/nodes/outfuncs.c4
-rw-r--r--src/backend/nodes/readfuncs.c6
-rw-r--r--src/backend/parser/gram.y71
-rw-r--r--src/backend/parser/parse_clause.c4
-rw-r--r--src/backend/parser/parse_relation.c336
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_type.h20
-rw-r--r--src/include/nodes/execnodes.h18
-rw-r--r--src/include/nodes/parsenodes.h6
-rw-r--r--src/include/parser/parse_relation.h5
-rw-r--r--src/test/regress/expected/type_sanity.out4
-rw-r--r--src/test/regress/sql/type_sanity.sql4
18 files changed, 595 insertions, 271 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 249be870002..e97f77ce700 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.83 2002/08/02 18:15:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.84 2002/08/04 19:48:09 momjian Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
@@ -24,6 +24,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "nodes/parsenodes.h"
+#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
@@ -600,46 +601,53 @@ RelationNameGetTupleDesc(char *relname)
TupleDesc
TypeGetTupleDesc(Oid typeoid, List *colaliases)
{
- Oid relid = typeidTypeRelid(typeoid);
- TupleDesc tupdesc;
+ char functyptype = typeid_get_typtype(typeoid);
+ TupleDesc tupdesc = NULL;
/*
* Build a suitable tupledesc representing the output rows
*/
- if (OidIsValid(relid))
+ if (functyptype == 'c')
{
/* 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);
+ Oid relid = typeidTypeRelid(typeoid);
- /* check to see if we've given column aliases */
- if(colaliases != NIL)
+ if (OidIsValid(relid))
{
- char *label;
- int varattno;
+ Relation rel;
+ int natts;
- /* 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");
+ rel = relation_open(relid, AccessShareLock);
+ tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+ natts = tupdesc->natts;
+ relation_close(rel, AccessShareLock);
- /* OK, use the aliases instead */
- for (varattno = 0; varattno < natts; varattno++)
+ /* check to see if we've given column aliases */
+ if(colaliases != NIL)
{
- label = strVal(nth(varattno, colaliases));
-
- if (label != NULL)
- namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
- else
- MemSet(NameStr(tupdesc->attrs[varattno]->attname), 0, NAMEDATALEN);
+ 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
+ elog(ERROR, "Invalid return relation specified for function");
}
- else
+ else if (functyptype == 'b')
{
/* Must be a base data type, i.e. scalar */
char *attname;
@@ -664,6 +672,11 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
0,
false);
}
+ else if (functyptype == 'p' && typeoid == RECORDOID)
+ elog(ERROR, "Unable to determine tuple description for function"
+ " returning \"record\"");
+ else
+ elog(ERROR, "Unknown kind of return type specified for function");
return tupdesc;
}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 2624fdaf25d..f2d8e70589c 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.82 2002/08/02 18:15:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.83 2002/08/04 19:48:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,6 +25,7 @@
#include "miscadmin.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
+#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
@@ -33,7 +34,7 @@
#include "utils/syscache.h"
-static void checkretval(Oid rettype, List *queryTreeList);
+static void checkretval(Oid rettype, char fn_typtype, List *queryTreeList);
Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
Datum fmgr_c_validator(PG_FUNCTION_ARGS);
Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
@@ -367,94 +368,113 @@ checkretval(Oid rettype, List *queryTreeList)
*/
tlistlen = ExecCleanTargetListLength(tlist);
- /*
- * For base-type returns, the target list should have exactly one
- * entry, and its type should agree with what the user declared. (As
- * of Postgres 7.2, we accept binary-compatible types too.)
- */
typerelid = typeidTypeRelid(rettype);
- if (typerelid == InvalidOid)
+
+ if (fn_typtype == 'b')
{
- if (tlistlen != 1)
- elog(ERROR, "function declared to return %s returns multiple columns in final SELECT",
- format_type_be(rettype));
+ /*
+ * For base-type returns, the target list should have exactly one
+ * entry, and its type should agree with what the user declared. (As
+ * of Postgres 7.2, we accept binary-compatible types too.)
+ */
- restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
- if (!IsBinaryCompatible(restype, rettype))
- elog(ERROR, "return type mismatch in function: declared to return %s, returns %s",
- format_type_be(rettype), format_type_be(restype));
+ if (typerelid == InvalidOid)
+ {
+ if (tlistlen != 1)
+ elog(ERROR, "function declared to return %s returns multiple columns in final SELECT",
+ format_type_be(rettype));
- return;
- }
+ restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
+ if (!IsBinaryCompatible(restype, rettype))
+ elog(ERROR, "return type mismatch in function: declared to return %s, returns %s",
+ format_type_be(rettype), format_type_be(restype));
- /*
- * If the target list is of length 1, and the type of the varnode in
- * the target list matches the declared return type, this is okay.
- * This can happen, for example, where the body of the function is
- * 'SELECT func2()', where func2 has the same return type as the
- * function that's calling it.
- */
- if (tlistlen == 1)
- {
- restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
- if (IsBinaryCompatible(restype, rettype))
return;
- }
-
- /*
- * By here, the procedure returns a tuple or set of tuples. This part
- * of the typechecking is a hack. We look up the relation that is the
- * declared return type, and scan the non-deleted attributes to ensure
- * that they match the datatypes of the non-resjunk columns.
- */
- reln = heap_open(typerelid, AccessShareLock);
- relnatts = reln->rd_rel->relnatts;
- rellogcols = 0; /* we'll count nondeleted cols as we go */
- colindex = 0;
+ }
- foreach(tlistitem, tlist)
+ /*
+ * If the target list is of length 1, and the type of the varnode in
+ * the target list matches the declared return type, this is okay.
+ * This can happen, for example, where the body of the function is
+ * 'SELECT func2()', where func2 has the same return type as the
+ * function that's calling it.
+ */
+ if (tlistlen == 1)
+ {
+ restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
+ if (IsBinaryCompatible(restype, rettype))
+ return;
+ }
+ }
+ else if (fn_typtype == 'c')
{
- TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
- Form_pg_attribute attr;
- Oid tletype;
- Oid atttype;
+ /*
+ * By here, the procedure returns a tuple or set of tuples. This part
+ * of the typechecking is a hack. We look up the relation that is the
+ * declared return type, and scan the non-deleted attributes to ensure
+ * that they match the datatypes of the non-resjunk columns.
+ */
+ reln = heap_open(typerelid, AccessShareLock);
+ relnatts = reln->rd_rel->relnatts;
+ rellogcols = 0; /* we'll count nondeleted cols as we go */
+ colindex = 0;
+
+ foreach(tlistitem, tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
+ Form_pg_attribute attr;
+ Oid tletype;
+ Oid atttype;
+
+ if (tle->resdom->resjunk)
+ continue;
+
+ do {
+ colindex++;
+ if (colindex > relnatts)
+ elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
+ format_type_be(rettype), rellogcols);
+ attr = reln->rd_att->attrs[colindex - 1];
+ } while (attr->attisdropped);
+ rellogcols++;
- if (tle->resdom->resjunk)
- continue;
+ tletype = exprType(tle->expr);
+ atttype = attr->atttypid;
+ if (!IsBinaryCompatible(tletype, atttype))
+ elog(ERROR, "function declared to return %s returns %s instead of %s at column %d",
+ format_type_be(rettype),
+ format_type_be(tletype),
+ format_type_be(atttype),
+ rellogcols);
+ }
- do {
+ for (;;)
+ {
colindex++;
if (colindex > relnatts)
- elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
- format_type_be(rettype), rellogcols);
- attr = reln->rd_att->attrs[colindex - 1];
- } while (attr->attisdropped);
- rellogcols++;
-
- tletype = exprType(tle->expr);
- atttype = attr->atttypid;
- if (!IsBinaryCompatible(tletype, atttype))
- elog(ERROR, "function declared to return %s returns %s instead of %s at column %d",
- format_type_be(rettype),
- format_type_be(tletype),
- format_type_be(atttype),
- rellogcols);
- }
+ break;
+ if (!reln->rd_att->attrs[colindex - 1]->attisdropped)
+ rellogcols++;
+ }
- for (;;)
- {
- colindex++;
- if (colindex > relnatts)
- break;
- if (!reln->rd_att->attrs[colindex - 1]->attisdropped)
- rellogcols++;
- }
+ if (tlistlen != rellogcols)
+ elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
+ format_type_be(rettype), rellogcols);
- if (tlistlen != rellogcols)
- elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
- format_type_be(rettype), rellogcols);
+ heap_close(reln, AccessShareLock);
- heap_close(reln, AccessShareLock);
+ return;
+ }
+ else if (fn_typtype == 'p' && rettype == RECORDOID)
+ {
+ /*
+ * For RECORD return type, defer this check until we get the
+ * first tuple.
+ */
+ return;
+ }
+ else
+ elog(ERROR, "Unknown kind of return type specified for function");
}
@@ -553,6 +573,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
bool isnull;
Datum tmp;
char *prosrc;
+ char functyptype;
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
if (!HeapTupleIsValid(tuple))
@@ -569,8 +590,11 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+ /* check typtype to see if we have a predetermined return type */
+ functyptype = typeid_get_typtype(proc->prorettype);
+
querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
- checkretval(proc->prorettype, querytree_list);
+ checkretval(proc->prorettype, functyptype, querytree_list);
ReleaseSysCache(tuple);
PG_RETURN_BOOL(true);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index c7b7c398eb2..ab414e19581 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.52 2002/06/20 20:29:28 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.53 2002/08/04 19:48:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -194,7 +194,8 @@ init_sql_fcache(FmgrInfo *finfo)
* get the type length and by-value flag from the type tuple
*/
fcache->typlen = typeStruct->typlen;
- if (typeStruct->typrelid == InvalidOid)
+
+ if (typeStruct->typtype == 'b')
{
/* The return type is not a relation, so just use byval */
fcache->typbyval = typeStruct->typbyval;
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index d87f8e6fe88..66c418c8ba6 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.3 2002/07/20 05:16:58 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.4 2002/08/04 19:48:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -31,6 +31,7 @@
#include "executor/nodeFunctionscan.h"
#include "parser/parsetree.h"
#include "parser/parse_expr.h"
+#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "storage/lmgr.h"
#include "tcop/pquery.h"
@@ -39,14 +40,11 @@
#include "utils/tuplestore.h"
static TupleTableSlot *FunctionNext(FunctionScan *node);
-static TupleTableSlot *function_getonetuple(TupleTableSlot *slot,
- Node *expr,
- ExprContext *econtext,
- TupleDesc tupdesc,
- bool returnsTuple,
+static TupleTableSlot *function_getonetuple(FunctionScanState *scanstate,
bool *isNull,
ExprDoneCond *isDone);
static FunctionMode get_functionmode(Node *expr);
+static bool tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2);
/* ----------------------------------------------------------------
* Scan Support
@@ -62,9 +60,6 @@ static TupleTableSlot *
FunctionNext(FunctionScan *node)
{
TupleTableSlot *slot;
- Node *expr;
- ExprContext *econtext;
- TupleDesc tupdesc;
EState *estate;
ScanDirection direction;
Tuplestorestate *tuplestorestate;
@@ -78,11 +73,8 @@ FunctionNext(FunctionScan *node)
scanstate = (FunctionScanState *) node->scan.scanstate;
estate = node->scan.plan.state;
direction = estate->es_direction;
- econtext = scanstate->csstate.cstate.cs_ExprContext;
tuplestorestate = scanstate->tuplestorestate;
- tupdesc = scanstate->tupdesc;
- expr = scanstate->funcexpr;
/*
* If first time through, read all tuples from function and pass them to
@@ -108,10 +100,7 @@ FunctionNext(FunctionScan *node)
isNull = false;
isDone = ExprSingleResult;
- slot = function_getonetuple(scanstate->csstate.css_ScanTupleSlot,
- expr, econtext, tupdesc,
- scanstate->returnsTuple,
- &isNull, &isDone);
+ slot = function_getonetuple(scanstate, &isNull, &isDone);
if (TupIsNull(slot))
break;
@@ -169,7 +158,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
RangeTblEntry *rte;
Oid funcrettype;
Oid funcrelid;
- TupleDesc tupdesc;
+ char functyptype;
+ TupleDesc tupdesc = NULL;
/*
* FunctionScan should not have any children.
@@ -209,25 +199,36 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
Assert(rte->rtekind == RTE_FUNCTION);
funcrettype = exprType(rte->funcexpr);
- funcrelid = typeidTypeRelid(funcrettype);
+
+ /*
+ * Now determine if the function returns a simple or composite type,
+ * and check/add column aliases.
+ */
+ functyptype = typeid_get_typtype(funcrettype);
/*
* Build a suitable tupledesc representing the output rows
*/
- if (OidIsValid(funcrelid))
+ if (functyptype == 'c')
{
- /*
- * Composite data type, i.e. a table's row type
- * Same as ordinary relation RTE
- */
- Relation rel;
+ funcrelid = typeidTypeRelid(funcrettype);
+ if (OidIsValid(funcrelid))
+ {
+ /*
+ * Composite data type, i.e. a table's row type
+ * Same as ordinary relation RTE
+ */
+ Relation rel;
- rel = relation_open(funcrelid, AccessShareLock);
- tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
- relation_close(rel, AccessShareLock);
- scanstate->returnsTuple = true;
+ rel = relation_open(funcrelid, AccessShareLock);
+ tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+ relation_close(rel, AccessShareLock);
+ scanstate->returnsTuple = true;
+ }
+ else
+ elog(ERROR, "Invalid return relation specified for function");
}
- else
+ else if (functyptype == 'b')
{
/*
* Must be a base data type, i.e. scalar
@@ -244,6 +245,21 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
false);
scanstate->returnsTuple = false;
}
+ else if (functyptype == 'p' && funcrettype == RECORDOID)
+ {
+ /*
+ * Must be a pseudo type, i.e. record
+ */
+ List *coldeflist = rte->coldeflist;
+
+ tupdesc = BuildDescForRelation(coldeflist);
+ scanstate->returnsTuple = true;
+ }
+ else
+ elog(ERROR, "Unknown kind of return type specified for function");
+
+ scanstate->fn_typeid = funcrettype;
+ scanstate->fn_typtype = functyptype;
scanstate->tupdesc = tupdesc;
ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot,
tupdesc, false);
@@ -404,17 +420,20 @@ ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent)
* Run the underlying function to get the next tuple
*/
static TupleTableSlot *
-function_getonetuple(TupleTableSlot *slot,
- Node *expr,
- ExprContext *econtext,
- TupleDesc tupdesc,
- bool returnsTuple,
+function_getonetuple(FunctionScanState *scanstate,
bool *isNull,
ExprDoneCond *isDone)
{
- HeapTuple tuple;
- Datum retDatum;
- char nullflag;
+ HeapTuple tuple;
+ Datum retDatum;
+ char nullflag;
+ TupleDesc tupdesc = scanstate->tupdesc;
+ bool returnsTuple = scanstate->returnsTuple;
+ Node *expr = scanstate->funcexpr;
+ Oid fn_typeid = scanstate->fn_typeid;
+ char fn_typtype = scanstate->fn_typtype;
+ ExprContext *econtext = scanstate->csstate.cstate.cs_ExprContext;
+ TupleTableSlot *slot = scanstate->csstate.css_ScanTupleSlot;
/*
* get the next Datum from the function
@@ -435,6 +454,16 @@ function_getonetuple(TupleTableSlot *slot,
* function returns pointer to tts??
*/
slot = (TupleTableSlot *) retDatum;
+
+ /*
+ * if function return type was RECORD, we need to check to be
+ * sure the structure from the query matches the actual return
+ * structure
+ */
+ if (fn_typtype == 'p' && fn_typeid == RECORDOID)
+ if (tupledesc_mismatch(tupdesc, slot->ttc_tupleDescriptor))
+ elog(ERROR, "Query specified return tuple and actual"
+ " function return tuple do not match");
}
else
{
@@ -467,3 +496,26 @@ get_functionmode(Node *expr)
*/
return PM_REPEATEDCALL;
}
+
+static bool
+tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2)
+{
+ int i;
+
+ if (tupdesc1->natts != tupdesc2->natts)
+ return true;
+
+ for (i = 0; i < tupdesc1->natts; i++)
+ {
+ Form_pg_attribute attr1 = tupdesc1->attrs[i];
+ Form_pg_attribute attr2 = tupdesc2->attrs[i];
+
+ /*
+ * We really only care about number of attributes and data type
+ */
+ if (attr1->atttypid != attr2->atttypid)
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index d7aefc9acfe..954a372181a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.199 2002/08/04 04:31:44 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.200 2002/08/04 19:48:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1482,6 +1482,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
newnode->relid = from->relid;
Node_Copy(from, newnode, subquery);
Node_Copy(from, newnode, funcexpr);
+ Node_Copy(from, newnode, coldeflist);
newnode->jointype = from->jointype;
Node_Copy(from, newnode, joinaliasvars);
Node_Copy(from, newnode, alias);
@@ -1707,6 +1708,7 @@ _copyRangeFunction(RangeFunction *from)
Node_Copy(from, newnode, funccallnode);
Node_Copy(from, newnode, alias);
+ Node_Copy(from, newnode, coldeflist);
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 9b655640b0d..da7567e7c33 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.146 2002/08/04 04:31:44 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.147 2002/08/04 19:48:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1607,6 +1607,8 @@ _equalRangeVar(RangeVar *a, RangeVar *b)
return false;
if (!equal(a->alias, b->alias))
return false;
+ if (!equal(a->coldeflist, b->coldeflist))
+ return false;
return true;
}
@@ -1742,6 +1744,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
return false;
if (!equal(a->funcexpr, b->funcexpr))
return false;
+ if (!equal(a->coldeflist, b->coldeflist))
+ return false;
if (a->jointype != b->jointype)
return false;
if (!equal(a->joinaliasvars, b->joinaliasvars))
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c4dfbec67d0..b992e45a62f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.165 2002/07/18 17:14:19 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.166 2002/08/04 19:48:09 momjian Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1004,6 +1004,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
case RTE_FUNCTION:
appendStringInfo(str, ":funcexpr ");
_outNode(str, node->funcexpr);
+ appendStringInfo(str, ":coldeflist ");
+ _outNode(str, node->coldeflist);
break;
case RTE_JOIN:
appendStringInfo(str, ":jointype %d :joinaliasvars ",
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index ba9eb0449ee..46b2ca2dc85 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.126 2002/07/18 17:14:19 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.127 2002/08/04 19:48:09 momjian Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@@ -1545,6 +1545,10 @@ _readRangeTblEntry(void)
case RTE_FUNCTION:
token = pg_strtok(&length); /* eat :funcexpr */
local_node->funcexpr = nodeRead(true); /* now read it */
+
+ token = pg_strtok(&length); /* eat :coldeflist */
+ local_node->coldeflist = nodeRead(true); /* now read it */
+
break;
case RTE_JOIN:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c794dd7d80e..53759b80296 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.354 2002/08/04 06:51:23 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.355 2002/08/04 19:48:09 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -215,7 +215,8 @@ static void doNegateFloat(Value *v);
target_list, update_target_list, insert_column_list,
insert_target_list, def_list, opt_indirection,
group_clause, TriggerFuncArgs, select_limit,
- opt_select_limit, opclass_item_list, trans_options
+ opt_select_limit, opclass_item_list, trans_options,
+ tableFuncElementList
%type <range> into_clause, OptTempTableName
@@ -256,8 +257,8 @@ static void doNegateFloat(Value *v);
%type <vsetstmt> set_rest
-%type <node> OptTableElement, ConstraintElem
-%type <node> columnDef
+%type <node> OptTableElement, ConstraintElem, tableFuncElement
+%type <node> columnDef, tableFuncColumnDef
%type <defelt> def_elem
%type <node> def_arg, columnElem, where_clause, insert_column_item,
a_expr, b_expr, c_expr, r_expr, AexprConst,
@@ -4448,6 +4449,34 @@ table_ref: relation_expr
{
RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1;
+ n->coldeflist = NIL;
+ $$ = (Node *) n;
+ }
+ | func_table AS '(' tableFuncElementList ')'
+ {
+ RangeFunction *n = makeNode(RangeFunction);
+ n->funccallnode = $1;
+ n->coldeflist = $4;
+ $$ = (Node *) n;
+ }
+ | func_table AS ColId '(' tableFuncElementList ')'
+ {
+ RangeFunction *n = makeNode(RangeFunction);
+ Alias *a = makeNode(Alias);
+ n->funccallnode = $1;
+ a->aliasname = $3;
+ n->alias = a;
+ n->coldeflist = $5;
+ $$ = (Node *) n;
+ }
+ | func_table ColId '(' tableFuncElementList ')'
+ {
+ RangeFunction *n = makeNode(RangeFunction);
+ Alias *a = makeNode(Alias);
+ n->funccallnode = $1;
+ a->aliasname = $2;
+ n->alias = a;
+ n->coldeflist = $4;
$$ = (Node *) n;
}
| func_table alias_clause
@@ -4455,6 +4484,7 @@ table_ref: relation_expr
RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1;
n->alias = $2;
+ n->coldeflist = NIL;
$$ = (Node *) n;
}
| select_with_parens
@@ -4703,6 +4733,39 @@ where_clause:
;
+tableFuncElementList:
+ tableFuncElementList ',' tableFuncElement
+ {
+ if ($3 != NULL)
+ $$ = lappend($1, $3);
+ else
+ $$ = $1;
+ }
+ | tableFuncElement
+ {
+ if ($1 != NULL)
+ $$ = makeList1($1);
+ else
+ $$ = NIL;
+ }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+tableFuncElement:
+ tableFuncColumnDef { $$ = $1; }
+ ;
+
+tableFuncColumnDef: ColId Typename
+ {
+ ColumnDef *n = makeNode(ColumnDef);
+ n->colname = $1;
+ n->typename = $2;
+ n->constraints = NIL;
+
+ $$ = (Node *)n;
+ }
+ ;
+
/*****************************************************************************
*
* Type syntax
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 81328798eb5..ae87b056f3e 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.94 2002/06/20 20:29:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.95 2002/08/04 19:48:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -515,7 +515,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
* OK, build an RTE for the function.
*/
rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
- r->alias, true);
+ r, true);
/*
* We create a RangeTblRef, but we do not add it to the joinlist or
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 99b639d73e3..6a4bda5d62b 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.71 2002/08/02 18:15:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.72 2002/08/04 19:48:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -699,12 +699,14 @@ RangeTblEntry *
addRangeTableEntryForFunction(ParseState *pstate,
char *funcname,
Node *funcexpr,
- Alias *alias,
+ RangeFunction *rangefunc,
bool inFromCl)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
Oid funcrettype = exprType(funcexpr);
- Oid funcrelid;
+ char functyptype;
+ Alias *alias = rangefunc->alias;
+ List *coldeflist = rangefunc->coldeflist;
Alias *eref;
int numaliases;
int varattno;
@@ -713,6 +715,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
rte->relid = InvalidOid;
rte->subquery = NULL;
rte->funcexpr = funcexpr;
+ rte->coldeflist = coldeflist;
rte->alias = alias;
eref = alias ? (Alias *) copyObject(alias) : makeAlias(funcname, NIL);
@@ -724,47 +727,56 @@ addRangeTableEntryForFunction(ParseState *pstate,
* Now determine if the function returns a simple or composite type,
* and check/add column aliases.
*/
- funcrelid = typeidTypeRelid(funcrettype);
+ functyptype = typeid_get_typtype(funcrettype);
- if (OidIsValid(funcrelid))
+ if (functyptype == 'c')
{
/*
- * Composite data type, i.e. a table's row type
- *
- * Get the rel's relcache entry. This access ensures that we have an
- * up-to-date relcache entry for the rel.
+ * Named composite data type, i.e. a table's row type
*/
- Relation rel;
- int maxattrs;
+ Oid funcrelid = typeidTypeRelid(funcrettype);
- rel = heap_open(funcrelid, AccessShareLock);
+ if (OidIsValid(funcrelid))
+ {
+ /*
+ * Get the rel's relcache entry. This access ensures that we have an
+ * up-to-date relcache entry for the rel.
+ */
+ Relation rel;
+ int maxattrs;
- /*
- * Since the rel is open anyway, let's check that the number of column
- * aliases is reasonable.
- */
- maxattrs = RelationGetNumberOfAttributes(rel);
- if (maxattrs < numaliases)
- elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
- RelationGetRelationName(rel), maxattrs, numaliases);
+ rel = heap_open(funcrelid, AccessShareLock);
- /* fill in alias columns using actual column names */
- for (varattno = numaliases; varattno < maxattrs; varattno++)
- {
- char *attrname;
+ /*
+ * Since the rel is open anyway, let's check that the number of column
+ * aliases is reasonable.
+ */
+ maxattrs = RelationGetNumberOfAttributes(rel);
+ if (maxattrs < numaliases)
+ elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
+ RelationGetRelationName(rel), maxattrs, numaliases);
- attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
- eref->colnames = lappend(eref->colnames, makeString(attrname));
- }
+ /* fill in alias columns using actual column names */
+ for (varattno = numaliases; varattno < maxattrs; varattno++)
+ {
+ char *attrname;
- /*
- * Drop the rel refcount, but keep the access lock till end of
- * transaction so that the table can't be deleted or have its schema
- * modified underneath us.
- */
- heap_close(rel, NoLock);
+ attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
+ eref->colnames = lappend(eref->colnames, makeString(attrname));
+ }
+
+ /*
+ * Drop the rel refcount, but keep the access lock till end of
+ * transaction so that the table can't be deleted or have its schema
+ * modified underneath us.
+ */
+ heap_close(rel, NoLock);
+ }
+ else
+ elog(ERROR, "Invalid return relation specified for function %s",
+ funcname);
}
- else
+ else if (functyptype == 'b')
{
/*
* Must be a base data type, i.e. scalar.
@@ -776,6 +788,22 @@ addRangeTableEntryForFunction(ParseState *pstate,
if (numaliases == 0)
eref->colnames = makeList1(makeString(funcname));
}
+ else if (functyptype == 'p' && funcrettype == RECORDOID)
+ {
+ List *col;
+
+ foreach(col, coldeflist)
+ {
+ char *attrname;
+ ColumnDef *n = lfirst(col);
+
+ attrname = pstrdup(n->colname);
+ eref->colnames = lappend(eref->colnames, makeString(attrname));
+ }
+ }
+ else
+ elog(ERROR, "Unknown kind of return type specified for function %s",
+ funcname);
/*----------
* Flags:
@@ -1051,56 +1079,70 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
case RTE_FUNCTION:
{
/* Function RTE */
- Oid funcrettype = exprType(rte->funcexpr);
- Oid funcrelid = typeidTypeRelid(funcrettype);
+ Oid funcrettype = exprType(rte->funcexpr);
+ char functyptype = typeid_get_typtype(funcrettype);
+ List *coldeflist = rte->coldeflist;
- if (OidIsValid(funcrelid))
+ /*
+ * Build a suitable tupledesc representing the output rows
+ */
+ if (functyptype == 'c')
{
- /*
- * Composite data type, i.e. a table's row type
- * Same as ordinary relation RTE
- */
- Relation rel;
- int maxattrs;
- int numaliases;
-
- rel = heap_open(funcrelid, AccessShareLock);
- maxattrs = RelationGetNumberOfAttributes(rel);
- numaliases = length(rte->eref->colnames);
-
- for (varattno = 0; varattno < maxattrs; varattno++)
+ Oid funcrelid = typeidTypeRelid(funcrettype);
+ if (OidIsValid(funcrelid))
{
- Form_pg_attribute attr = rel->rd_att->attrs[varattno];
- if (attr->attisdropped)
- continue;
+ /*
+ * Composite data type, i.e. a table's row type
+ * Same as ordinary relation RTE
+ */
+ Relation rel;
+ int maxattrs;
+ int numaliases;
- if (colnames)
- {
- char *label;
+ rel = heap_open(funcrelid, AccessShareLock);
+ maxattrs = RelationGetNumberOfAttributes(rel);
+ numaliases = length(rte->eref->colnames);
- if (varattno < numaliases)
- label = strVal(nth(varattno, rte->eref->colnames));
- else
- label = NameStr(attr->attname);
- *colnames = lappend(*colnames, makeString(pstrdup(label)));
- }
-
- if (colvars)
+ for (varattno = 0; varattno < maxattrs; varattno++)
{
- Var *varnode;
-
- varnode = makeVar(rtindex, attr->attnum,
- attr->atttypid, attr->atttypmod,
- sublevels_up);
-
- *colvars = lappend(*colvars, varnode);
+ Form_pg_attribute attr = rel->rd_att->attrs[varattno];
+
+ if (attr->attisdropped)
+ continue;
+
+ if (colnames)
+ {
+ char *label;
+
+ if (varattno < numaliases)
+ label = strVal(nth(varattno, rte->eref->colnames));
+ else
+ label = NameStr(attr->attname);
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex,
+ attr->attnum,
+ attr->atttypid,
+ attr->atttypmod,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
}
- }
- heap_close(rel, AccessShareLock);
+ heap_close(rel, AccessShareLock);
+ }
+ else
+ elog(ERROR, "Invalid return relation specified"
+ " for function");
}
- else
+ else if (functyptype == 'b')
{
/*
* Must be a base data type, i.e. scalar
@@ -1120,6 +1162,47 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
*colvars = lappend(*colvars, varnode);
}
}
+ else if (functyptype == 'p' && funcrettype == RECORDOID)
+ {
+ List *col;
+ int attnum = 0;
+
+ foreach(col, coldeflist)
+ {
+ ColumnDef *colDef = lfirst(col);
+
+ attnum++;
+ if (colnames)
+ {
+ char *attrname;
+
+ attrname = pstrdup(colDef->colname);
+ *colnames = lappend(*colnames, makeString(attrname));
+ }
+
+ if (colvars)
+ {
+ Var *varnode;
+ HeapTuple typeTuple;
+ Oid atttypid;
+
+ typeTuple = typenameType(colDef->typename);
+ atttypid = HeapTupleGetOid(typeTuple);
+ ReleaseSysCache(typeTuple);
+
+ varnode = makeVar(rtindex,
+ attnum,
+ atttypid,
+ -1,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
+ }
+ else
+ elog(ERROR, "Unknown kind of return type specified"
+ " for function");
}
break;
case RTE_JOIN:
@@ -1308,40 +1391,52 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
case RTE_FUNCTION:
{
/* Function RTE */
- Oid funcrettype = exprType(rte->funcexpr);
- Oid funcrelid = typeidTypeRelid(funcrettype);
+ Oid funcrettype = exprType(rte->funcexpr);
+ char functyptype = typeid_get_typtype(funcrettype);
+ List *coldeflist = rte->coldeflist;
- if (OidIsValid(funcrelid))
+ /*
+ * Build a suitable tupledesc representing the output rows
+ */
+ if (functyptype == 'c')
{
/*
* Composite data type, i.e. a table's row type
* Same as ordinary relation RTE
*/
- HeapTuple tp;
- Form_pg_attribute att_tup;
+ Oid funcrelid = typeidTypeRelid(funcrettype);
- tp = SearchSysCache(ATTNUM,
- ObjectIdGetDatum(funcrelid),
- Int16GetDatum(attnum),
- 0, 0);
- /* this shouldn't happen... */
- if (!HeapTupleIsValid(tp))
- elog(ERROR, "Relation %s does not have attribute %d",
- get_rel_name(funcrelid), attnum);
- att_tup = (Form_pg_attribute) GETSTRUCT(tp);
- /*
- * If dropped column, pretend it ain't there. See notes
- * in scanRTEForColumn.
- */
- if (att_tup->attisdropped)
- elog(ERROR, "Relation \"%s\" has no column \"%s\"",
- get_rel_name(funcrelid),
- NameStr(att_tup->attname));
- *vartype = att_tup->atttypid;
- *vartypmod = att_tup->atttypmod;
- ReleaseSysCache(tp);
+ if (OidIsValid(funcrelid))
+ {
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+
+ tp = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(funcrelid),
+ Int16GetDatum(attnum),
+ 0, 0);
+ /* this shouldn't happen... */
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "Relation %s does not have attribute %d",
+ get_rel_name(funcrelid), attnum);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ /*
+ * If dropped column, pretend it ain't there. See notes
+ * in scanRTEForColumn.
+ */
+ if (att_tup->attisdropped)
+ elog(ERROR, "Relation \"%s\" has no column \"%s\"",
+ get_rel_name(funcrelid),
+ NameStr(att_tup->attname));
+ *vartype = att_tup->atttypid;
+ *vartypmod = att_tup->atttypmod;
+ ReleaseSysCache(tp);
+ }
+ else
+ elog(ERROR, "Invalid return relation specified"
+ " for function");
}
- else
+ else if (functyptype == 'b')
{
/*
* Must be a base data type, i.e. scalar
@@ -1349,6 +1444,22 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
*vartype = funcrettype;
*vartypmod = -1;
}
+ else if (functyptype == 'p' && funcrettype == RECORDOID)
+ {
+ ColumnDef *colDef = nth(attnum - 1, coldeflist);
+ HeapTuple typeTuple;
+ Oid atttypid;
+
+ typeTuple = typenameType(colDef->typename);
+ atttypid = HeapTupleGetOid(typeTuple);
+ ReleaseSysCache(typeTuple);
+
+ *vartype = atttypid;
+ *vartypmod = -1;
+ }
+ else
+ elog(ERROR, "Unknown kind of return type specified"
+ " for function");
}
break;
case RTE_JOIN:
@@ -1576,3 +1687,28 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
pstate->parentParseState != NULL ? " in subquery" : "",
relation->relname);
}
+
+char
+typeid_get_typtype(Oid typeid)
+{
+ HeapTuple typeTuple;
+ Form_pg_type typeStruct;
+ char result;
+
+ /*
+ * determine if the function returns a simple, named composite,
+ * or anonymous composite type
+ */
+ typeTuple = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(typeid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup for type %u failed", typeid);
+ typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ result = typeStruct->typtype;
+
+ ReleaseSysCache(typeTuple);
+
+ return result;
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index f3e36226b01..12669ed78dd 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: catversion.h,v 1.144 2002/08/02 18:15:09 tgl Exp $
+ * $Id: catversion.h,v 1.145 2002/08/04 19:48:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200208011
+#define CATALOG_VERSION_NO 200208041
#endif
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 1f801611a05..6bc4a2a92ae 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_type.h,v 1.125 2002/07/24 19:11:13 petere Exp $
+ * $Id: pg_type.h,v 1.126 2002/08/04 19:48:10 momjian Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -60,10 +60,10 @@ CATALOG(pg_type) BOOTSTRAP
bool typbyval;
/*
- * typtype is 'b' for a basic type and 'c' for a catalog type (ie a
- * class). If typtype is 'c', typrelid is the OID of the class' entry
- * in pg_class. (Why do we need an entry in pg_type for classes,
- * anyway?)
+ * typtype is 'b' for a basic type, 'c' for a catalog type (ie a
+ * class), or 'p' for a pseudo type. If typtype is 'c', typrelid is the
+ * OID of the class' entry in pg_class. (Why do we need an entry in
+ * pg_type for classes, anyway?)
*/
char typtype;
@@ -501,6 +501,16 @@ DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b t \054 0 2204 array_in
DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b t \054 0 2205 array_in array_out i x f 0 -1 0 _null_ _null_ ));
DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b t \054 0 2206 array_in array_out i x f 0 -1 0 _null_ _null_ ));
+/*
+ * pseudo-types
+ *
+ * types with typtype='p' are special types that represent classes of types
+ * that are not easily defined in advance. Currently there is only one pseudo
+ * type -- record. The record type is used to specify that the value is a
+ * tuple, but of unknown structure until runtime.
+ */
+DATA(insert OID = 2249 ( record PGNSP PGUID 4 t p t \054 0 0 oidin oidout i p f 0 -1 0 _null_ _null_ ));
+#define RECORDOID 2249
/*
* prototypes for functions in pg_type.c
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 2dea171e255..ee25cc62c95 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execnodes.h,v 1.70 2002/06/20 20:29:49 momjian Exp $
+ * $Id: execnodes.h,v 1.71 2002/08/04 19:48:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -509,11 +509,17 @@ typedef struct SubqueryScanState
* Function nodes are used to scan the results of a
* function appearing in FROM (typically a function returning set).
*
- * functionmode function operating mode:
+ * functionmode function operating mode:
* - repeated call
* - materialize
* - return query
+ * tupdesc function's return tuple description
* tuplestorestate private state of tuplestore.c
+ * funcexpr function expression being evaluated
+ * returnsTuple does function return tuples?
+ * fn_typeid OID of function return type
+ * fn_typtype return Datum type, i.e. 'b'ase,
+ * 'c'atalog, or 'p'seudo
* ----------------
*/
typedef enum FunctionMode
@@ -525,12 +531,14 @@ typedef enum FunctionMode
typedef struct FunctionScanState
{
- CommonScanState csstate; /* its first field is NodeTag */
+ CommonScanState csstate; /* its first field is NodeTag */
FunctionMode functionmode;
TupleDesc tupdesc;
void *tuplestorestate;
- Node *funcexpr; /* function expression being evaluated */
- bool returnsTuple; /* does function return tuples? */
+ Node *funcexpr;
+ bool returnsTuple;
+ Oid fn_typeid;
+ char fn_typtype;
} FunctionScanState;
/* ----------------------------------------------------------------
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07e985b377b..765d1ca0514 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.197 2002/08/04 04:31:44 momjian Exp $
+ * $Id: parsenodes.h,v 1.198 2002/08/04 19:48:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -400,6 +400,8 @@ typedef struct RangeFunction
NodeTag type;
Node *funccallnode; /* untransformed function call tree */
Alias *alias; /* table alias & optional column aliases */
+ List *coldeflist; /* list of ColumnDef nodes for runtime
+ * assignment of RECORD TupleDesc */
} RangeFunction;
/*
@@ -527,6 +529,8 @@ typedef struct RangeTblEntry
* Fields valid for a function RTE (else NULL):
*/
Node *funcexpr; /* expression tree for func call */
+ List *coldeflist; /* list of ColumnDef nodes for runtime
+ * assignment of RECORD TupleDesc */
/*
* Fields valid for a join RTE (else NULL/zero):
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index a34e2a5619f..7abfd047865 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_relation.h,v 1.35 2002/08/02 18:15:09 tgl Exp $
+ * $Id: parse_relation.h,v 1.36 2002/08/04 19:48:11 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -44,7 +44,7 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate,
char *funcname,
Node *funcexpr,
- Alias *alias,
+ RangeFunction *rangefunc,
bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
List *colnames,
@@ -61,5 +61,6 @@ extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte);
extern int attnameAttNum(Relation rd, const char *attname, bool sysColOK);
extern Name attnumAttName(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid);
+extern char typeid_get_typtype(Oid typeid);
#endif /* PARSE_RELATION_H */
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 711628c2e40..949393386a4 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -16,7 +16,7 @@
SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE (p1.typlen <= 0 AND p1.typlen != -1) OR
- (p1.typtype != 'b' AND p1.typtype != 'c') OR
+ (p1.typtype != 'b' AND p1.typtype != 'c' AND p1.typtype != 'p') OR
NOT p1.typisdefined OR
(p1.typalign != 'c' AND p1.typalign != 's' AND
p1.typalign != 'i' AND p1.typalign != 'd') OR
@@ -60,7 +60,7 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
-- NOTE: as of 7.3, this check finds SET, smgr, and unknown.
SELECT p1.oid, p1.typname
FROM pg_type as p1
-WHERE p1.typtype != 'c' AND p1.typname NOT LIKE '\\_%' AND NOT EXISTS
+WHERE p1.typtype = 'b' AND p1.typname NOT LIKE '\\_%' AND NOT EXISTS
(SELECT 1 FROM pg_type as p2
WHERE p2.typname = ('_' || p1.typname)::name AND
p2.typelem = p1.oid);
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 87bcad19793..58019a15d25 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -19,7 +19,7 @@
SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE (p1.typlen <= 0 AND p1.typlen != -1) OR
- (p1.typtype != 'b' AND p1.typtype != 'c') OR
+ (p1.typtype != 'b' AND p1.typtype != 'c' AND p1.typtype != 'p') OR
NOT p1.typisdefined OR
(p1.typalign != 'c' AND p1.typalign != 's' AND
p1.typalign != 'i' AND p1.typalign != 'd') OR
@@ -55,7 +55,7 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
SELECT p1.oid, p1.typname
FROM pg_type as p1
-WHERE p1.typtype != 'c' AND p1.typname NOT LIKE '\\_%' AND NOT EXISTS
+WHERE p1.typtype = 'b' AND p1.typname NOT LIKE '\\_%' AND NOT EXISTS
(SELECT 1 FROM pg_type as p2
WHERE p2.typname = ('_' || p1.typname)::name AND
p2.typelem = p1.oid);