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:
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
38
src/backend/utils/cache/lsyscache.c
vendored
38
src/backend/utils/cache/lsyscache.c
vendored
@ -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
|
||||||
*
|
*
|
||||||
|
@ -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);
|
||||||
|
Reference in New Issue
Block a user