1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-17 17:02:08 +03:00

Allow functions returning void or cstring to appear in FROM clause,

to make life cushy for the JDBC driver.  Centralize the decision-making
that affects this by inventing a get_type_func_class() function, rather
than adding special cases in half a dozen places.
This commit is contained in:
Tom Lane
2004-10-20 16:04:50 +00:00
parent 857e210ea9
commit fb22b32095
5 changed files with 94 additions and 55 deletions

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.106 2004/08/29 05:06:39 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.107 2004/10/20 16:04:47 tgl 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
@ -607,13 +607,13 @@ RelationNameGetTupleDesc(const char *relname)
TupleDesc TupleDesc
TypeGetTupleDesc(Oid typeoid, List *colaliases) TypeGetTupleDesc(Oid typeoid, List *colaliases)
{ {
char functyptype = get_typtype(typeoid); TypeFuncClass functypclass = get_type_func_class(typeoid);
TupleDesc tupdesc = NULL; TupleDesc tupdesc = NULL;
/* /*
* Build a suitable tupledesc representing the output rows * Build a suitable tupledesc representing the output rows
*/ */
if (functyptype == 'c') if (functypclass == TYPEFUNC_COMPOSITE)
{ {
/* Composite data type, e.g. a table's row type */ /* Composite data type, e.g. a table's row type */
tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1)); tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
@ -643,9 +643,9 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
tupdesc->tdtypmod = -1; tupdesc->tdtypmod = -1;
} }
} }
else if (functyptype == 'b' || functyptype == 'd') else if (functypclass == TYPEFUNC_SCALAR)
{ {
/* Must be a base data type, i.e. scalar */ /* Base data type, i.e. scalar */
char *attname; char *attname;
/* the alias list is required for base types */ /* the alias list is required for base types */
@ -671,7 +671,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
-1, -1,
0); 0);
} }
else if (typeoid == RECORDOID) else if (functypclass == TYPEFUNC_RECORD)
{ {
/* XXX can't support this because typmod wasn't passed in ... */ /* XXX can't support this because typmod wasn't passed in ... */
ereport(ERROR, ereport(ERROR,

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.27 2004/09/22 17:41:51 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.28 2004/10/20 16:04:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -132,7 +132,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
FunctionScanState *scanstate; FunctionScanState *scanstate;
RangeTblEntry *rte; RangeTblEntry *rte;
Oid funcrettype; Oid funcrettype;
char functyptype; TypeFuncClass functypclass;
TupleDesc tupdesc = NULL; TupleDesc tupdesc = NULL;
/* /*
@ -184,16 +184,16 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
* Now determine if the function returns a simple or composite type, * Now determine if the function returns a simple or composite type,
* and build an appropriate tupdesc. * and build an appropriate tupdesc.
*/ */
functyptype = get_typtype(funcrettype); functypclass = get_type_func_class(funcrettype);
if (functyptype == 'c') if (functypclass == TYPEFUNC_COMPOSITE)
{ {
/* Composite data type, e.g. a table's row type */ /* Composite data type, e.g. a table's row type */
tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(funcrettype, -1)); tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(funcrettype, -1));
} }
else if (functyptype == 'b' || functyptype == 'd') else if (functypclass == TYPEFUNC_SCALAR)
{ {
/* Must be a base data type, i.e. scalar */ /* Base data type, i.e. scalar */
char *attname = strVal(linitial(rte->eref->colnames)); char *attname = strVal(linitial(rte->eref->colnames));
tupdesc = CreateTemplateTupleDesc(1, false); tupdesc = CreateTemplateTupleDesc(1, false);
@ -204,9 +204,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
-1, -1,
0); 0);
} }
else if (funcrettype == RECORDOID) else if (functypclass == TYPEFUNC_RECORD)
{ {
/* Must be a pseudo type, i.e. record */
tupdesc = BuildDescForRelation(rte->coldeflist); tupdesc = BuildDescForRelation(rte->coldeflist);
} }
else else

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.100 2004/08/29 05:06:44 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.101 2004/10/20 16:04:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -966,7 +966,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
{ {
RangeTblEntry *rte = makeNode(RangeTblEntry); RangeTblEntry *rte = makeNode(RangeTblEntry);
Oid funcrettype = exprType(funcexpr); Oid funcrettype = exprType(funcexpr);
char functyptype; TypeFuncClass functypclass;
Alias *alias = rangefunc->alias; Alias *alias = rangefunc->alias;
List *coldeflist = rangefunc->coldeflist; List *coldeflist = rangefunc->coldeflist;
Alias *eref; Alias *eref;
@ -1008,18 +1008,15 @@ addRangeTableEntryForFunction(ParseState *pstate,
errmsg("a column definition list is required for functions returning \"record\""))); errmsg("a column definition list is required for functions returning \"record\"")));
} }
functyptype = get_typtype(funcrettype); functypclass = get_type_func_class(funcrettype);
if (functyptype == 'c') if (functypclass == TYPEFUNC_COMPOSITE)
{ {
/* /* Composite data type, e.g. a table's row type */
* Named composite data type, i.e. a table's row type
*/
Oid funcrelid = typeidTypeRelid(funcrettype); Oid funcrelid = typeidTypeRelid(funcrettype);
Relation rel; Relation rel;
if (!OidIsValid(funcrelid)) /* shouldn't happen if typtype is if (!OidIsValid(funcrelid)) /* shouldn't happen */
* 'c' */
elog(ERROR, "invalid typrelid for complex type %u", funcrettype); elog(ERROR, "invalid typrelid for complex type %u", funcrettype);
/* /*
@ -1038,12 +1035,10 @@ addRangeTableEntryForFunction(ParseState *pstate,
*/ */
relation_close(rel, NoLock); relation_close(rel, NoLock);
} }
else if (functyptype == 'b' || functyptype == 'd') else if (functypclass == TYPEFUNC_SCALAR)
{ {
/* /* Base data type, i.e. scalar */
* Must be a base data type, i.e. scalar. Just add one alias /* Just add one alias column named for the function. */
* column named for the function.
*/
if (alias && alias->colnames != NIL) if (alias && alias->colnames != NIL)
{ {
if (list_length(alias->colnames) != 1) if (list_length(alias->colnames) != 1)
@ -1056,7 +1051,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
else else
eref->colnames = list_make1(makeString(eref->aliasname)); eref->colnames = list_make1(makeString(eref->aliasname));
} }
else if (functyptype == 'p' && funcrettype == RECORDOID) else if (functypclass == TYPEFUNC_RECORD)
{ {
ListCell *col; ListCell *col;
@ -1073,8 +1068,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function \"%s\" in FROM has unsupported return type", errmsg("function \"%s\" in FROM has unsupported return type %s",
funcname))); funcname, format_type_be(funcrettype))));
/*---------- /*----------
* Flags: * Flags:
@ -1314,9 +1309,9 @@ expandRTE(List *rtable, int rtindex, int sublevels_up,
{ {
/* Function RTE */ /* Function RTE */
Oid funcrettype = exprType(rte->funcexpr); Oid funcrettype = exprType(rte->funcexpr);
char functyptype = get_typtype(funcrettype); TypeFuncClass functypclass = get_type_func_class(funcrettype);
if (functyptype == 'c') if (functypclass == TYPEFUNC_COMPOSITE)
{ {
/* /*
* Composite data type, i.e. a table's row type * Composite data type, i.e. a table's row type
@ -1332,11 +1327,9 @@ expandRTE(List *rtable, int rtindex, int sublevels_up,
expandRelation(funcrelid, rte->eref, rtindex, sublevels_up, expandRelation(funcrelid, rte->eref, rtindex, sublevels_up,
include_dropped, colnames, colvars); include_dropped, colnames, colvars);
} }
else if (functyptype == 'b' || functyptype == 'd') else if (functypclass == TYPEFUNC_SCALAR)
{ {
/* /* Base data type, i.e. scalar */
* Must be a base data type, i.e. scalar
*/
if (colnames) if (colnames)
*colnames = lappend(*colnames, *colnames = lappend(*colnames,
linitial(rte->eref->colnames)); linitial(rte->eref->colnames));
@ -1352,7 +1345,7 @@ expandRTE(List *rtable, int rtindex, int sublevels_up,
*colvars = lappend(*colvars, varnode); *colvars = lappend(*colvars, varnode);
} }
} }
else if (functyptype == 'p' && funcrettype == RECORDOID) else if (functypclass == TYPEFUNC_RECORD)
{ {
List *coldeflist = rte->coldeflist; List *coldeflist = rte->coldeflist;
ListCell *col; ListCell *col;
@ -1389,9 +1382,10 @@ expandRTE(List *rtable, int rtindex, int sublevels_up,
} }
} }
else else
ereport(ERROR, {
(errcode(ERRCODE_DATATYPE_MISMATCH), /* addRangeTableEntryForFunction should've caught this */
errmsg("function in FROM has unsupported return type"))); elog(ERROR, "function in FROM has unsupported return type");
}
} }
break; break;
case RTE_JOIN: case RTE_JOIN:
@ -1669,14 +1663,15 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
{ {
/* Function RTE */ /* Function RTE */
Oid funcrettype = exprType(rte->funcexpr); Oid funcrettype = exprType(rte->funcexpr);
char functyptype = get_typtype(funcrettype); TypeFuncClass functypclass = get_type_func_class(funcrettype);
List *coldeflist = rte->coldeflist; List *coldeflist = rte->coldeflist;
if (functyptype == 'c') if (functypclass == TYPEFUNC_COMPOSITE)
{ {
/* /*
* Composite data type, i.e. a table's row type Same * Composite data type, i.e. a table's row type
* as ordinary relation RTE *
* Same as ordinary relation RTE
*/ */
Oid funcrelid = typeidTypeRelid(funcrettype); Oid funcrelid = typeidTypeRelid(funcrettype);
HeapTuple tp; HeapTuple tp;
@ -1709,15 +1704,13 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
*vartypmod = att_tup->atttypmod; *vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp); ReleaseSysCache(tp);
} }
else if (functyptype == 'b' || functyptype == 'd') else if (functypclass == TYPEFUNC_SCALAR)
{ {
/* /* Base data type, i.e. scalar */
* Must be a base data type, i.e. scalar
*/
*vartype = funcrettype; *vartype = funcrettype;
*vartypmod = -1; *vartypmod = -1;
} }
else if (functyptype == 'p' && funcrettype == RECORDOID) else if (functypclass == TYPEFUNC_RECORD)
{ {
ColumnDef *colDef = list_nth(coldeflist, attnum - 1); ColumnDef *colDef = list_nth(coldeflist, attnum - 1);
@ -1725,9 +1718,10 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
*vartypmod = -1; *vartypmod = -1;
} }
else else
ereport(ERROR, {
(errcode(ERRCODE_DATATYPE_MISMATCH), /* addRangeTableEntryForFunction should've caught this */
errmsg("function in FROM has unsupported return type"))); elog(ERROR, "function in FROM has unsupported return type");
}
} }
break; break;
case RTE_JOIN: case RTE_JOIN:

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.116 2004/08/29 05:06:50 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.117 2004/10/20 16:04:49 tgl Exp $
* *
* NOTES * NOTES
* Eventually, the index information should go through here, too. * Eventually, the index information should go through here, too.
@ -1547,6 +1547,42 @@ get_typtype(Oid typid)
return '\0'; return '\0';
} }
/*
* get_type_func_class
*
* Given the type OID, obtain its TYPEFUNC classification.
*
* This is intended to centralize a bunch of formerly ad-hoc code for
* classifying types. The categories used here are useful for deciding
* how to handle functions returning the datatype.
*/
TypeFuncClass
get_type_func_class(Oid typid)
{
switch (get_typtype(typid))
{
case 'c':
return TYPEFUNC_COMPOSITE;
case 'b':
case 'd':
return TYPEFUNC_SCALAR;
case 'p':
if (typid == RECORDOID)
return TYPEFUNC_RECORD;
/*
* We treat VOID and CSTRING as legitimate scalar datatypes,
* mostly for the convenience of the JDBC driver (which wants
* to be able to do "SELECT * FROM foo()" for all legitimately
* user-callable functions).
*/
if (typid == VOIDOID || typid == CSTRINGOID)
return TYPEFUNC_SCALAR;
return TYPEFUNC_OTHER;
}
/* shouldn't get here, probably */
return TYPEFUNC_OTHER;
}
/* /*
* get_typ_typrelid * get_typ_typrelid
* *

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.90 2004/08/29 05:06:59 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.91 2004/10/20 16:04:50 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -24,6 +24,15 @@ typedef enum IOFuncSelector
IOFunc_send IOFunc_send
} IOFuncSelector; } IOFuncSelector;
/* Type categories for get_type_func_class */
typedef enum TypeFuncClass
{
TYPEFUNC_SCALAR,
TYPEFUNC_COMPOSITE,
TYPEFUNC_RECORD,
TYPEFUNC_OTHER
} TypeFuncClass;
extern bool op_in_opclass(Oid opno, Oid opclass); extern bool op_in_opclass(Oid opno, Oid opclass);
extern void get_op_opclass_properties(Oid opno, Oid opclass, extern void get_op_opclass_properties(Oid opno, Oid opclass,
int *strategy, Oid *subtype, int *strategy, Oid *subtype,
@ -85,6 +94,7 @@ extern char get_typstorage(Oid typid);
extern int32 get_typtypmod(Oid typid); extern int32 get_typtypmod(Oid typid);
extern Node *get_typdefault(Oid typid); extern Node *get_typdefault(Oid typid);
extern char get_typtype(Oid typid); extern char get_typtype(Oid typid);
extern TypeFuncClass get_type_func_class(Oid typid);
extern Oid get_typ_typrelid(Oid typid); extern Oid get_typ_typrelid(Oid typid);
extern Oid get_element_type(Oid typid); extern Oid get_element_type(Oid typid);
extern Oid get_array_type(Oid typid); extern Oid get_array_type(Oid typid);