1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-18 17:42:25 +03:00

Attached are two patches to implement and document anonymous composite

types for Table Functions, as previously proposed on HACKERS. Here is a
brief explanation:

1. Creates a new pg_type typtype: 'p' for pseudo type (currently either
     'b' for base or 'c' for catalog, i.e. a class).

2. Creates new builtin type of typtype='p' named RECORD. This is the
     first of potentially several pseudo types.

3. Modify FROM clause grammer to accept:
     SELECT * FROM my_func() AS m(colname1 type1, colname2 type1, ...)
     where m is the table alias, colname1, etc are the column names, and
     type1, etc are the column types.

4. When typtype == 'p' and the function return type is RECORD, a list
     of column defs is required, and when typtype != 'p', it is
disallowed.

5. A check was added to ensure that the tupdesc provide via the parser
     and the actual return tupdesc match in number and type of
attributes.

When creating a function you can do:
     CREATE FUNCTION foo(text) RETURNS setof RECORD ...

When using it you can do:
     SELECT * from foo(sqlstmt) AS (f1 int, f2 text, f3 timestamp)
       or
     SELECT * from foo(sqlstmt) AS f(f1 int, f2 text, f3 timestamp)
       or
     SELECT * from foo(sqlstmt) f(f1 int, f2 text, f3 timestamp)

Included in the patches are adjustments to the regression test sql and
expected files, and documentation.

p.s.
     This potentially solves (or at least improves) the issue of builtin
     Table Functions. They can be bootstrapped as returning RECORD, and
     we can wrap system views around them with properly specified column
     defs. For example:

     CREATE VIEW pg_settings AS
       SELECT s.name, s.setting
       FROM show_all_settings()AS s(name text, setting text);

     Then we can also add the UPDATE RULE that I previously posted to
     pg_settings, and have pg_settings act like a virtual table, allowing
     settings to be queried and set.


Joe Conway
This commit is contained in:
Bruce Momjian
2002-08-04 19:48:11 +00:00
parent 35d39ba081
commit 9218689b69
19 changed files with 677 additions and 280 deletions

View File

@ -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;
Oid relid = typeidTypeRelid(typeoid);
rel = relation_open(relid, AccessShareLock);
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
natts = tupdesc->natts;
relation_close(rel, AccessShareLock);
/* check to see if we've given column aliases */
if(colaliases != NIL)
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));
char *label;
int varattno;
if (label != NULL)
namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
else
MemSet(NameStr(tupdesc->attrs[varattno]->attname), 0, NAMEDATALEN);
/* 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;
}