Commit fb22b320 authored by Tom Lane's avatar Tom Lane

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.
parent 857e210e
...@@ -8,7 +8,7 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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,
......
...@@ -8,7 +8,7 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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
......
...@@ -8,7 +8,7 @@ ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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:
......
...@@ -7,7 +7,7 @@ ...@@ -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) ...@@ -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
* *
......
...@@ -6,7 +6,7 @@ ...@@ -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 ...@@ -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); ...@@ -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);
......
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