Commit 9218689b authored by Bruce Momjian's avatar Bruce Momjian

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
parent 35d39ba0
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/select.sgml,v 1.54 2002/04/23 02:07:16 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/ref/select.sgml,v 1.55 2002/08/04 19:48:09 momjian Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -40,6 +40,12 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: ...@@ -40,6 +40,12 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
( <replaceable class="PARAMETER">select</replaceable> ) ( <replaceable class="PARAMETER">select</replaceable> )
[ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> ) ] [ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> ) ]
| |
<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
[ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> | <replaceable class="PARAMETER">column_definition_list</replaceable> ) ]
|
<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
AS ( <replaceable class="PARAMETER">column_definition_list</replaceable> )
|
<replaceable class="PARAMETER">from_item</replaceable> [ NATURAL ] <replaceable class="PARAMETER">join_type</replaceable> <replaceable class="PARAMETER">from_item</replaceable> <replaceable class="PARAMETER">from_item</replaceable> [ NATURAL ] <replaceable class="PARAMETER">join_type</replaceable> <replaceable class="PARAMETER">from_item</replaceable>
[ ON <replaceable class="PARAMETER">join_condition</replaceable> | USING ( <replaceable class="PARAMETER">join_column_list</replaceable> ) ] [ ON <replaceable class="PARAMETER">join_condition</replaceable> | USING ( <replaceable class="PARAMETER">join_column_list</replaceable> ) ]
</synopsis> </synopsis>
...@@ -82,7 +88,7 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: ...@@ -82,7 +88,7 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
<term><replaceable class="PARAMETER">from_item</replaceable></term> <term><replaceable class="PARAMETER">from_item</replaceable></term>
<listitem> <listitem>
<para> <para>
A table reference, sub-SELECT, or JOIN clause. See below for details. A table reference, sub-SELECT, table function, or JOIN clause. See below for details.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -156,6 +162,23 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: ...@@ -156,6 +162,23 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">table function</replaceable></term>
<listitem>
<para>
A table function can appear in the FROM clause. This acts as though
its output were created as a temporary table for the duration of
this single SELECT command. An alias may also be used. If an alias is
written, a column alias list can also be written to provide substitute names
for one or more columns of the table function. If the table function has been
defined as returning the RECORD data type, an alias, or the keyword AS, must
also be present, followed by a column definition list in the form
( <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ).
The column definition list must match the actual number and types returned by the function.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="PARAMETER">join_type</replaceable></term> <term><replaceable class="PARAMETER">join_type</replaceable></term>
...@@ -380,6 +403,19 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: ...@@ -380,6 +403,19 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
grouping, aggregation, or sorting in a single query. grouping, aggregation, or sorting in a single query.
</para> </para>
<para>
A FROM item can be a table function (i.e. a function that returns
multiple rows and columns). When a table function is created, it may
be defined to return a named scalar or composite data type (an existing
scalar data type, or a table or view name), or it may be defined to return
a RECORD data type. When a table function is defined to return RECORD, it
must be followed in the FROM clause by an alias, or the keyword AS alone,
and then by a parenthesized list of column names and types. This provides
a query-time composite type definition. The FROM clause composite type
must match the actual composite type returned from the function or an
ERROR will be generated.
</para>
<para> <para>
Finally, a FROM item can be a JOIN clause, which combines two simpler Finally, a FROM item can be a JOIN clause, which combines two simpler
FROM items. (Use parentheses if necessary to determine the order FROM items. (Use parentheses if necessary to determine the order
...@@ -925,6 +961,43 @@ SELECT actors.name ...@@ -925,6 +961,43 @@ SELECT actors.name
Warren Beatty Warren Beatty
Westward Westward
Woody Allen Woody Allen
</programlisting>
</para>
<para>
This example shows how to use a table function, both with and without
a column definition list.
<programlisting>
distributors:
did | name
-----+--------------
108 | Westward
111 | Walt Disney
112 | Warner Bros.
...
CREATE FUNCTION distributors(int)
RETURNS SETOF distributors AS '
SELECT * FROM distributors WHERE did = $1;
' LANGUAGE SQL;
SELECT * FROM distributors(111);
did | name
-----+-------------
111 | Walt Disney
(1 row)
CREATE FUNCTION distributors_2(int)
RETURNS SETOF RECORD AS '
SELECT * FROM distributors WHERE did = $1;
' LANGUAGE SQL;
SELECT * FROM distributors_2(111) AS (f1 int, f2 text);
f1 | f2
-----+-------------
111 | Walt Disney
(1 row)
</programlisting> </programlisting>
</para> </para>
</refsect1> </refsect1>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be * some of the executor utility code such as "ExecTypeFromTL" should be
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -600,46 +601,53 @@ RelationNameGetTupleDesc(char *relname) ...@@ -600,46 +601,53 @@ RelationNameGetTupleDesc(char *relname)
TupleDesc TupleDesc
TypeGetTupleDesc(Oid typeoid, List *colaliases) TypeGetTupleDesc(Oid typeoid, List *colaliases)
{ {
Oid relid = typeidTypeRelid(typeoid); char functyptype = typeid_get_typtype(typeoid);
TupleDesc tupdesc; TupleDesc tupdesc = NULL;
/* /*
* Build a suitable tupledesc representing the output rows * Build a suitable tupledesc representing the output rows
*/ */
if (OidIsValid(relid)) if (functyptype == 'c')
{ {
/* Composite data type, i.e. a table's row type */ /* Composite data type, i.e. a table's row type */
Relation rel; Oid relid = typeidTypeRelid(typeoid);
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 (OidIsValid(relid))
if(colaliases != NIL)
{ {
char *label; Relation rel;
int varattno; int natts;
/* does the List length match the number of attributes */ rel = relation_open(relid, AccessShareLock);
if (length(colaliases) != natts) tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes"); natts = tupdesc->natts;
relation_close(rel, AccessShareLock);
/* OK, use the aliases instead */ /* check to see if we've given column aliases */
for (varattno = 0; varattno < natts; varattno++) if(colaliases != NIL)
{ {
label = strVal(nth(varattno, colaliases)); char *label;
int varattno;
if (label != NULL)
namestrcpy(&(tupdesc->attrs[varattno]->attname), label); /* does the List length match the number of attributes */
else if (length(colaliases) != natts)
MemSet(NameStr(tupdesc->attrs[varattno]->attname), 0, NAMEDATALEN); 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 */ /* Must be a base data type, i.e. scalar */
char *attname; char *attname;
...@@ -664,6 +672,11 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) ...@@ -664,6 +672,11 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
0, 0,
false); 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; return tupdesc;
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -25,6 +25,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -33,7 +34,7 @@ ...@@ -33,7 +34,7 @@
#include "utils/syscache.h" #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_internal_validator(PG_FUNCTION_ARGS);
Datum fmgr_c_validator(PG_FUNCTION_ARGS); Datum fmgr_c_validator(PG_FUNCTION_ARGS);
Datum fmgr_sql_validator(PG_FUNCTION_ARGS); Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
...@@ -367,94 +368,113 @@ checkretval(Oid rettype, List *queryTreeList) ...@@ -367,94 +368,113 @@ checkretval(Oid rettype, List *queryTreeList)
*/ */
tlistlen = ExecCleanTargetListLength(tlist); 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); 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", * For base-type returns, the target list should have exactly one
format_type_be(rettype)); * 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 (typerelid == InvalidOid)
if (!IsBinaryCompatible(restype, rettype)) {
elog(ERROR, "return type mismatch in function: declared to return %s, returns %s", if (tlistlen != 1)
format_type_be(rettype), format_type_be(restype)); 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; 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; * By here, the procedure returns a tuple or set of tuples. This part
Oid tletype; * of the typechecking is a hack. We look up the relation that is the
Oid atttype; * 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) tletype = exprType(tle->expr);
continue; 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++; colindex++;
if (colindex > relnatts) if (colindex > relnatts)
elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", break;
format_type_be(rettype), rellogcols); if (!reln->rd_att->attrs[colindex - 1]->attisdropped)
attr = reln->rd_att->attrs[colindex - 1]; rellogcols++;
} 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);
}
for (;;) if (tlistlen != rellogcols)
{ elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
colindex++; format_type_be(rettype), rellogcols);
if (colindex > relnatts)
break;
if (!reln->rd_att->attrs[colindex - 1]->attisdropped)
rellogcols++;
}
if (tlistlen != rellogcols) heap_close(reln, AccessShareLock);
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); 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) ...@@ -553,6 +573,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
bool isnull; bool isnull;
Datum tmp; Datum tmp;
char *prosrc; char *prosrc;
char functyptype;
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0); tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
if (!HeapTupleIsValid(tuple)) if (!HeapTupleIsValid(tuple))
...@@ -569,8 +590,11 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) ...@@ -569,8 +590,11 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp)); 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); querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
checkretval(proc->prorettype, querytree_list); checkretval(proc->prorettype, functyptype, querytree_list);
ReleaseSysCache(tuple); ReleaseSysCache(tuple);
PG_RETURN_BOOL(true); PG_RETURN_BOOL(true);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -194,7 +194,8 @@ init_sql_fcache(FmgrInfo *finfo)
* get the type length and by-value flag from the type tuple * get the type length and by-value flag from the type tuple
*/ */
fcache->typlen = typeStruct->typlen; fcache->typlen = typeStruct->typlen;
if (typeStruct->typrelid == InvalidOid)
if (typeStruct->typtype == 'b')
{ {
/* The return type is not a relation, so just use byval */ /* The return type is not a relation, so just use byval */
fcache->typbyval = typeStruct->typbyval; fcache->typbyval = typeStruct->typbyval;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -31,6 +31,7 @@
#include "executor/nodeFunctionscan.h" #include "executor/nodeFunctionscan.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "tcop/pquery.h" #include "tcop/pquery.h"
...@@ -39,14 +40,11 @@ ...@@ -39,14 +40,11 @@
#include "utils/tuplestore.h" #include "utils/tuplestore.h"
static TupleTableSlot *FunctionNext(FunctionScan *node); static TupleTableSlot *FunctionNext(FunctionScan *node);
static TupleTableSlot *function_getonetuple(TupleTableSlot *slot, static TupleTableSlot *function_getonetuple(FunctionScanState *scanstate,
Node *expr,
ExprContext *econtext,
TupleDesc tupdesc,
bool returnsTuple,
bool *isNull, bool *isNull,
ExprDoneCond *isDone); ExprDoneCond *isDone);
static FunctionMode get_functionmode(Node *expr); static FunctionMode get_functionmode(Node *expr);
static bool tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* Scan Support * Scan Support
...@@ -62,9 +60,6 @@ static TupleTableSlot * ...@@ -62,9 +60,6 @@ static TupleTableSlot *
FunctionNext(FunctionScan *node) FunctionNext(FunctionScan *node)
{ {
TupleTableSlot *slot; TupleTableSlot *slot;
Node *expr;
ExprContext *econtext;
TupleDesc tupdesc;
EState *estate; EState *estate;
ScanDirection direction; ScanDirection direction;
Tuplestorestate *tuplestorestate; Tuplestorestate *tuplestorestate;
...@@ -78,11 +73,8 @@ FunctionNext(FunctionScan *node) ...@@ -78,11 +73,8 @@ FunctionNext(FunctionScan *node)
scanstate = (FunctionScanState *) node->scan.scanstate; scanstate = (FunctionScanState *) node->scan.scanstate;
estate = node->scan.plan.state; estate = node->scan.plan.state;
direction = estate->es_direction; direction = estate->es_direction;
econtext = scanstate->csstate.cstate.cs_ExprContext;
tuplestorestate = scanstate->tuplestorestate; tuplestorestate = scanstate->tuplestorestate;
tupdesc = scanstate->tupdesc;
expr = scanstate->funcexpr;
/* /*
* If first time through, read all tuples from function and pass them to * If first time through, read all tuples from function and pass them to
...@@ -108,10 +100,7 @@ FunctionNext(FunctionScan *node) ...@@ -108,10 +100,7 @@ FunctionNext(FunctionScan *node)
isNull = false; isNull = false;
isDone = ExprSingleResult; isDone = ExprSingleResult;
slot = function_getonetuple(scanstate->csstate.css_ScanTupleSlot, slot = function_getonetuple(scanstate, &isNull, &isDone);
expr, econtext, tupdesc,
scanstate->returnsTuple,
&isNull, &isDone);
if (TupIsNull(slot)) if (TupIsNull(slot))
break; break;
...@@ -169,7 +158,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent) ...@@ -169,7 +158,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
RangeTblEntry *rte; RangeTblEntry *rte;
Oid funcrettype; Oid funcrettype;
Oid funcrelid; Oid funcrelid;
TupleDesc tupdesc; char functyptype;
TupleDesc tupdesc = NULL;
/* /*
* FunctionScan should not have any children. * FunctionScan should not have any children.
...@@ -209,25 +199,36 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent) ...@@ -209,25 +199,36 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table); rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
Assert(rte->rtekind == RTE_FUNCTION); Assert(rte->rtekind == RTE_FUNCTION);
funcrettype = exprType(rte->funcexpr); 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 * Build a suitable tupledesc representing the output rows
*/ */
if (OidIsValid(funcrelid)) if (functyptype == 'c')
{ {
/* funcrelid = typeidTypeRelid(funcrettype);
* Composite data type, i.e. a table's row type if (OidIsValid(funcrelid))
* Same as ordinary relation RTE {
*/ /*
Relation rel; * Composite data type, i.e. a table's row type
* Same as ordinary relation RTE
*/
Relation rel;
rel = relation_open(funcrelid, AccessShareLock); rel = relation_open(funcrelid, AccessShareLock);
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel)); tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
relation_close(rel, AccessShareLock); relation_close(rel, AccessShareLock);
scanstate->returnsTuple = true; 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 * Must be a base data type, i.e. scalar
...@@ -244,6 +245,21 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent) ...@@ -244,6 +245,21 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
false); false);
scanstate->returnsTuple = 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; scanstate->tupdesc = tupdesc;
ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot, ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot,
tupdesc, false); tupdesc, false);
...@@ -404,17 +420,20 @@ ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent) ...@@ -404,17 +420,20 @@ ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent)
* Run the underlying function to get the next tuple * Run the underlying function to get the next tuple
*/ */
static TupleTableSlot * static TupleTableSlot *
function_getonetuple(TupleTableSlot *slot, function_getonetuple(FunctionScanState *scanstate,
Node *expr,
ExprContext *econtext,
TupleDesc tupdesc,
bool returnsTuple,
bool *isNull, bool *isNull,
ExprDoneCond *isDone) ExprDoneCond *isDone)
{ {
HeapTuple tuple; HeapTuple tuple;
Datum retDatum; Datum retDatum;
char nullflag; 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 * get the next Datum from the function
...@@ -435,6 +454,16 @@ function_getonetuple(TupleTableSlot *slot, ...@@ -435,6 +454,16 @@ function_getonetuple(TupleTableSlot *slot,
* function returns pointer to tts?? * function returns pointer to tts??
*/ */
slot = (TupleTableSlot *) retDatum; 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 else
{ {
...@@ -467,3 +496,26 @@ get_functionmode(Node *expr) ...@@ -467,3 +496,26 @@ get_functionmode(Node *expr)
*/ */
return PM_REPEATEDCALL; 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;
}
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1482,6 +1482,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
newnode->relid = from->relid; newnode->relid = from->relid;
Node_Copy(from, newnode, subquery); Node_Copy(from, newnode, subquery);
Node_Copy(from, newnode, funcexpr); Node_Copy(from, newnode, funcexpr);
Node_Copy(from, newnode, coldeflist);
newnode->jointype = from->jointype; newnode->jointype = from->jointype;
Node_Copy(from, newnode, joinaliasvars); Node_Copy(from, newnode, joinaliasvars);
Node_Copy(from, newnode, alias); Node_Copy(from, newnode, alias);
...@@ -1707,6 +1708,7 @@ _copyRangeFunction(RangeFunction *from) ...@@ -1707,6 +1708,7 @@ _copyRangeFunction(RangeFunction *from)
Node_Copy(from, newnode, funccallnode); Node_Copy(from, newnode, funccallnode);
Node_Copy(from, newnode, alias); Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, coldeflist);
return newnode; return newnode;
} }
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1607,6 +1607,8 @@ _equalRangeVar(RangeVar *a, RangeVar *b)
return false; return false;
if (!equal(a->alias, b->alias)) if (!equal(a->alias, b->alias))
return false; return false;
if (!equal(a->coldeflist, b->coldeflist))
return false;
return true; return true;
} }
...@@ -1742,6 +1744,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) ...@@ -1742,6 +1744,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
return false; return false;
if (!equal(a->funcexpr, b->funcexpr)) if (!equal(a->funcexpr, b->funcexpr))
return false; return false;
if (!equal(a->coldeflist, b->coldeflist))
return false;
if (a->jointype != b->jointype) if (a->jointype != b->jointype)
return false; return false;
if (!equal(a->joinaliasvars, b->joinaliasvars)) if (!equal(a->joinaliasvars, b->joinaliasvars))
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which * Every (plan) node in POSTGRES has an associated "out" routine which
...@@ -1004,6 +1004,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) ...@@ -1004,6 +1004,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
case RTE_FUNCTION: case RTE_FUNCTION:
appendStringInfo(str, ":funcexpr "); appendStringInfo(str, ":funcexpr ");
_outNode(str, node->funcexpr); _outNode(str, node->funcexpr);
appendStringInfo(str, ":coldeflist ");
_outNode(str, node->coldeflist);
break; break;
case RTE_JOIN: case RTE_JOIN:
appendStringInfo(str, ":jointype %d :joinaliasvars ", appendStringInfo(str, ":jointype %d :joinaliasvars ",
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Most of the read functions for plan nodes are tested. (In fact, they * Most of the read functions for plan nodes are tested. (In fact, they
...@@ -1545,6 +1545,10 @@ _readRangeTblEntry(void) ...@@ -1545,6 +1545,10 @@ _readRangeTblEntry(void)
case RTE_FUNCTION: case RTE_FUNCTION:
token = pg_strtok(&length); /* eat :funcexpr */ token = pg_strtok(&length); /* eat :funcexpr */
local_node->funcexpr = nodeRead(true); /* now read it */ local_node->funcexpr = nodeRead(true); /* now read it */
token = pg_strtok(&length); /* eat :coldeflist */
local_node->coldeflist = nodeRead(true); /* now read it */
break; break;
case RTE_JOIN: case RTE_JOIN:
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -215,7 +215,8 @@ static void doNegateFloat(Value *v); ...@@ -215,7 +215,8 @@ static void doNegateFloat(Value *v);
target_list, update_target_list, insert_column_list, target_list, update_target_list, insert_column_list,
insert_target_list, def_list, opt_indirection, insert_target_list, def_list, opt_indirection,
group_clause, TriggerFuncArgs, select_limit, 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 %type <range> into_clause, OptTempTableName
...@@ -256,8 +257,8 @@ static void doNegateFloat(Value *v); ...@@ -256,8 +257,8 @@ static void doNegateFloat(Value *v);
%type <vsetstmt> set_rest %type <vsetstmt> set_rest
%type <node> OptTableElement, ConstraintElem %type <node> OptTableElement, ConstraintElem, tableFuncElement
%type <node> columnDef %type <node> columnDef, tableFuncColumnDef
%type <defelt> def_elem %type <defelt> def_elem
%type <node> def_arg, columnElem, where_clause, insert_column_item, %type <node> def_arg, columnElem, where_clause, insert_column_item,
a_expr, b_expr, c_expr, r_expr, AexprConst, a_expr, b_expr, c_expr, r_expr, AexprConst,
...@@ -4448,6 +4449,34 @@ table_ref: relation_expr ...@@ -4448,6 +4449,34 @@ table_ref: relation_expr
{ {
RangeFunction *n = makeNode(RangeFunction); RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1; 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; $$ = (Node *) n;
} }
| func_table alias_clause | func_table alias_clause
...@@ -4455,6 +4484,7 @@ table_ref: relation_expr ...@@ -4455,6 +4484,7 @@ table_ref: relation_expr
RangeFunction *n = makeNode(RangeFunction); RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1; n->funccallnode = $1;
n->alias = $2; n->alias = $2;
n->coldeflist = NIL;
$$ = (Node *) n; $$ = (Node *) n;
} }
| select_with_parens | select_with_parens
...@@ -4703,6 +4733,39 @@ where_clause: ...@@ -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 * Type syntax
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -515,7 +515,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
* OK, build an RTE for the function. * OK, build an RTE for the function.
*/ */
rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr, rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
r->alias, true); r, true);
/* /*
* We create a RangeTblRef, but we do not add it to the joinlist or * We create a RangeTblRef, but we do not add it to the joinlist or
......
This diff is collapsed.
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200208011 #define CATALOG_VERSION_NO 200208041
#endif #endif
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -60,10 +60,10 @@ CATALOG(pg_type) BOOTSTRAP ...@@ -60,10 +60,10 @@ CATALOG(pg_type) BOOTSTRAP
bool typbyval; bool typbyval;
/* /*
* typtype is 'b' for a basic type and 'c' for a catalog type (ie a * typtype is 'b' for a basic type, 'c' for a catalog type (ie a
* class). If typtype is 'c', typrelid is the OID of the class' entry * class), or 'p' for a pseudo type. If typtype is 'c', typrelid is the
* in pg_class. (Why do we need an entry in pg_type for classes, * OID of the class' entry in pg_class. (Why do we need an entry in
* anyway?) * pg_type for classes, anyway?)
*/ */
char typtype; char typtype;
...@@ -501,6 +501,16 @@ DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b t \054 0 2204 array_in ...@@ -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 = 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_ )); 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 * prototypes for functions in pg_type.c
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -509,11 +509,17 @@ typedef struct SubqueryScanState
* Function nodes are used to scan the results of a * Function nodes are used to scan the results of a
* function appearing in FROM (typically a function returning set). * function appearing in FROM (typically a function returning set).
* *
* functionmode function operating mode: * functionmode function operating mode:
* - repeated call * - repeated call
* - materialize * - materialize
* - return query * - return query
* tupdesc function's return tuple description
* tuplestorestate private state of tuplestore.c * 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 typedef enum FunctionMode
...@@ -525,12 +531,14 @@ typedef enum FunctionMode ...@@ -525,12 +531,14 @@ typedef enum FunctionMode
typedef struct FunctionScanState typedef struct FunctionScanState
{ {
CommonScanState csstate; /* its first field is NodeTag */ CommonScanState csstate; /* its first field is NodeTag */
FunctionMode functionmode; FunctionMode functionmode;
TupleDesc tupdesc; TupleDesc tupdesc;
void *tuplestorestate; void *tuplestorestate;
Node *funcexpr; /* function expression being evaluated */ Node *funcexpr;
bool returnsTuple; /* does function return tuples? */ bool returnsTuple;
Oid fn_typeid;
char fn_typtype;
} FunctionScanState; } FunctionScanState;
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -400,6 +400,8 @@ typedef struct RangeFunction
NodeTag type; NodeTag type;
Node *funccallnode; /* untransformed function call tree */ Node *funccallnode; /* untransformed function call tree */
Alias *alias; /* table alias & optional column aliases */ Alias *alias; /* table alias & optional column aliases */
List *coldeflist; /* list of ColumnDef nodes for runtime
* assignment of RECORD TupleDesc */
} RangeFunction; } RangeFunction;
/* /*
...@@ -527,6 +529,8 @@ typedef struct RangeTblEntry ...@@ -527,6 +529,8 @@ typedef struct RangeTblEntry
* Fields valid for a function RTE (else NULL): * Fields valid for a function RTE (else NULL):
*/ */
Node *funcexpr; /* expression tree for func call */ 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): * Fields valid for a join RTE (else NULL/zero):
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, ...@@ -44,7 +44,7 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate, extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate,
char *funcname, char *funcname,
Node *funcexpr, Node *funcexpr,
Alias *alias, RangeFunction *rangefunc,
bool inFromCl); bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate, extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
List *colnames, List *colnames,
...@@ -61,5 +61,6 @@ extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte); ...@@ -61,5 +61,6 @@ extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte);
extern int attnameAttNum(Relation rd, const char *attname, bool sysColOK); extern int attnameAttNum(Relation rd, const char *attname, bool sysColOK);
extern Name attnumAttName(Relation rd, int attid); extern Name attnumAttName(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid);
extern char typeid_get_typtype(Oid typeid);
#endif /* PARSE_RELATION_H */ #endif /* PARSE_RELATION_H */
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
SELECT p1.oid, p1.typname SELECT p1.oid, p1.typname
FROM pg_type as p1 FROM pg_type as p1
WHERE (p1.typlen <= 0 AND p1.typlen != -1) OR 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 NOT p1.typisdefined OR
(p1.typalign != 'c' AND p1.typalign != 's' AND (p1.typalign != 'c' AND p1.typalign != 's' AND
p1.typalign != 'i' AND p1.typalign != 'd') OR p1.typalign != 'i' AND p1.typalign != 'd') OR
...@@ -60,7 +60,7 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) 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. -- NOTE: as of 7.3, this check finds SET, smgr, and unknown.
SELECT p1.oid, p1.typname SELECT p1.oid, p1.typname
FROM pg_type as p1 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 (SELECT 1 FROM pg_type as p2
WHERE p2.typname = ('_' || p1.typname)::name AND WHERE p2.typname = ('_' || p1.typname)::name AND
p2.typelem = p1.oid); p2.typelem = p1.oid);
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
SELECT p1.oid, p1.typname SELECT p1.oid, p1.typname
FROM pg_type as p1 FROM pg_type as p1
WHERE (p1.typlen <= 0 AND p1.typlen != -1) OR 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 NOT p1.typisdefined OR
(p1.typalign != 'c' AND p1.typalign != 's' AND (p1.typalign != 'c' AND p1.typalign != 's' AND
p1.typalign != 'i' AND p1.typalign != 'd') OR p1.typalign != 'i' AND p1.typalign != 'd') OR
...@@ -55,7 +55,7 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR ...@@ -55,7 +55,7 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
SELECT p1.oid, p1.typname SELECT p1.oid, p1.typname
FROM pg_type as p1 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 (SELECT 1 FROM pg_type as p2
WHERE p2.typname = ('_' || p1.typname)::name AND WHERE p2.typname = ('_' || p1.typname)::name AND
p2.typelem = p1.oid); p2.typelem = p1.oid);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment