Here is an example that I can add to the plpgsql regression test if desired:
CREATE OR REPLACE FUNCTION tst(anyelement) returns anyarray as ' begin if $1 is of (int2, int4, int8, float4, float8, numeric) then return array[$1 * 2]; elsif $1 is of (text) then return array[$1 || $1]; else return array[$1]; end if; end; ' language 'plpgsql';
create table plpgsql(f1 int, f2 float8, f3 text, f4 oid);
insert into plpgsql values(1, 1.1, 'a', 1);
insert into plpgsql values(2, 2.2, 'b', 2);
regression=# SELECT tst(f1), tst(f2), tst(f3), tst(f4) from plpgsql;
tst | tst | tst | tst
-----+-------+------+-----
{2} | {2.2} | {aa} | {1}
{4} | {4.4} | {bb} | {2}
(2 rows)If there are no objections, please apply.
Thanks,
Joe
Index: src/pl/plpgsql/src/pl_comp.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v
retrieving revision 1.58
diff -c -r1.58 pl_comp.c
*** src/pl/plpgsql/src/pl_comp.c 5 May 2003 16:46:27 -0000 1.58
--- src/pl/plpgsql/src/pl_comp.c 30 Jun 2003 05:36:10 -0000
***************
*** 108,114 ****
* ----------
*/
PLpgSQL_function *
! plpgsql_compile(Oid fn_oid, int functype)
{
int parse_rc;
HeapTuple procTup;
--- 108,116 ----
* ----------
*/
PLpgSQL_function *
! plpgsql_compile(Oid fn_oid,
! int functype,
! FunctionCallInfo fcinfo)
{
int parse_rc;
HeapTuple procTup;
***************
*** 123,128 ****
--- 125,131 ----
int i;
int arg_varnos[FUNC_MAX_ARGS];
ErrorContextCallback plerrcontext;
+ Oid rettypeid;
/*
* Lookup the pg_proc tuple by Oid
***************
*** 185,223 ****
case T_FUNCTION:
/*
* Normal function has a defined returntype
*/
! function->fn_rettype = procStruct->prorettype;
function->fn_retset = procStruct->proretset;
/*
* Lookup the functions return type
*/
typeTup = SearchSysCache(TYPEOID,
!
ObjectIdGetDatum(procStruct->prorettype),
0, 0, 0);
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup for return type %u failed",
! procStruct->prorettype);
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype result, except VOID or RECORD */
if (typeStruct->typtype == 'p')
{
! if (procStruct->prorettype == VOIDOID ||
! procStruct->prorettype == RECORDOID)
/* okay */ ;
! else if (procStruct->prorettype == TRIGGEROID)
elog(ERROR, "plpgsql functions cannot return
type %s"
"\n\texcept when used as triggers",
!
format_type_be(procStruct->prorettype));
else
elog(ERROR, "plpgsql functions cannot return
type %s",
!
format_type_be(procStruct->prorettype));
}
if (typeStruct->typrelid != InvalidOid ||
! procStruct->prorettype == RECORDOID)
function->fn_retistuple = true;
else
{
--- 188,241 ----
case T_FUNCTION:
/*
+ * Check for a polymorphic returntype. If found, use the actual
+ * returntype from the caller's FuncExpr node, if we
+ * have one.
+ */
+ if (procStruct->prorettype == ANYARRAYOID ||
+ procStruct->prorettype == ANYELEMENTOID)
+ {
+ rettypeid = get_fn_expr_rettype(fcinfo);
+ if (!OidIsValid(rettypeid))
+ rettypeid = procStruct->prorettype;
+ }
+ else
+ rettypeid = procStruct->prorettype;
+
+ /*
* Normal function has a defined returntype
*/
! function->fn_rettype = rettypeid;
function->fn_retset = procStruct->proretset;
/*
* Lookup the functions return type
*/
typeTup = SearchSysCache(TYPEOID,
!
ObjectIdGetDatum(rettypeid),
0, 0, 0);
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup for return type %u failed",
! rettypeid);
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype result, except VOID or RECORD */
if (typeStruct->typtype == 'p')
{
! if (rettypeid == VOIDOID ||
! rettypeid == RECORDOID)
/* okay */ ;
! else if (rettypeid == TRIGGEROID)
elog(ERROR, "plpgsql functions cannot return
type %s"
"\n\texcept when used as triggers",
! format_type_be(rettypeid));
else
elog(ERROR, "plpgsql functions cannot return
type %s",
! format_type_be(rettypeid));
}
if (typeStruct->typrelid != InvalidOid ||
! rettypeid == RECORDOID)
function->fn_retistuple = true;
else
{
***************
*** 234,257 ****
for (i = 0; i < procStruct->pronargs; i++)
{
char buf[32];
snprintf(buf, sizeof(buf), "$%d", i + 1);
/* name for variable */
/*
* Get the parameters type
*/
typeTup = SearchSysCache(TYPEOID,
!
ObjectIdGetDatum(procStruct->proargtypes[i]),
0, 0,
0);
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup for argument type %u
failed",
! procStruct->proargtypes[i]);
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype argument */
if (typeStruct->typtype == 'p')
elog(ERROR, "plpgsql functions cannot take
type %s",
!
format_type_be(procStruct->proargtypes[i]));
if (typeStruct->typrelid != InvalidOid)
{
--- 252,291 ----
for (i = 0; i < procStruct->pronargs; i++)
{
char buf[32];
+ Oid argtypeid = InvalidOid;
snprintf(buf, sizeof(buf), "$%d", i + 1);
/* name for variable */
/*
+ * Check for polymorphic arguments. If found, use the
actual
+ * parameter type from the caller's FuncExpr node, if
we
+ * have one.
+ */
+ if (procStruct->proargtypes[i] == ANYARRAYOID ||
+ procStruct->proargtypes[i] == ANYELEMENTOID)
+ {
+ argtypeid = get_fn_expr_argtype(fcinfo, i);
+ if (!OidIsValid(argtypeid))
+ argtypeid = procStruct->proargtypes[i];
+ }
+ else
+ argtypeid = procStruct->proargtypes[i];
+
+ /*
* Get the parameters type
*/
typeTup = SearchSysCache(TYPEOID,
! ObjectIdGetDatum(argtypeid),
0, 0,
0);
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup for argument type %u
failed",
! argtypeid);
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype argument */
if (typeStruct->typtype == 'p')
elog(ERROR, "plpgsql functions cannot take
type %s",
! format_type_be(argtypeid));
if (typeStruct->typrelid != InvalidOid)
{
Index: src/pl/plpgsql/src/pl_handler.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_handler.c,v
retrieving revision 1.12
diff -c -r1.12 pl_handler.c
*** src/pl/plpgsql/src/pl_handler.c 30 Aug 2002 00:28:41 -0000 1.12
--- src/pl/plpgsql/src/pl_handler.c 30 Jun 2003 05:17:03 -0000
***************
*** 51,57 ****
static PLpgSQL_function *compiled_functions = NULL;
! static bool func_up_to_date(PLpgSQL_function * func);
/* ----------
--- 51,57 ----
static PLpgSQL_function *compiled_functions = NULL;
! static bool func_up_to_date(PLpgSQL_function * func, FunctionCallInfo fcinfo);
/* ----------
***************
*** 90,96 ****
/*
* But is the function still up to date?
*/
! if (!func_up_to_date(func))
func = NULL;
}
--- 90,96 ----
/*
* But is the function still up to date?
*/
! if (!func_up_to_date(func, fcinfo))
func = NULL;
}
***************
*** 101,107 ****
*/
for (func = compiled_functions; func != NULL; func = func->next)
{
! if (funcOid == func->fn_oid && func_up_to_date(func))
break;
}
--- 101,107 ----
*/
for (func = compiled_functions; func != NULL; func = func->next)
{
! if (funcOid == func->fn_oid && func_up_to_date(func, fcinfo))
break;
}
***************
*** 111,117 ****
if (func == NULL)
{
func = plpgsql_compile(funcOid,
! isTrigger ?
T_TRIGGER : T_FUNCTION);
func->next = compiled_functions;
compiled_functions = func;
}
--- 111,118 ----
if (func == NULL)
{
func = plpgsql_compile(funcOid,
! isTrigger ?
T_TRIGGER : T_FUNCTION,
! fcinfo);
func->next = compiled_functions;
compiled_functions = func;
}
***************
*** 146,154 ****
* Check to see if a compiled function is still up-to-date. This
* is needed because CREATE OR REPLACE FUNCTION can modify the
* function's pg_proc entry without changing its OID.
*/
static bool
! func_up_to_date(PLpgSQL_function * func)
{
HeapTuple procTup;
bool result;
--- 147,158 ----
* Check to see if a compiled function is still up-to-date. This
* is needed because CREATE OR REPLACE FUNCTION can modify the
* function's pg_proc entry without changing its OID.
+ * A compiled function can also be out-of-date if it accepts or
+ * returns polymorphic types, because these may change beneath
+ * us.
*/
static bool
! func_up_to_date(PLpgSQL_function * func, FunctionCallInfo fcinfo)
{
HeapTuple procTup;
bool result;
***************
*** 162,167 ****
--- 166,203 ----
result = (func->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
func->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data));
+
+ /* no sense in checking if result is already false */
+ if (result)
+ {
+ Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
+
+ /* check for change in return type if applicable */
+ if (procStruct->prorettype == ANYARRAYOID ||
+ procStruct->prorettype == ANYELEMENTOID)
+ result = (func->fn_rettype == get_fn_expr_rettype(fcinfo));
+
+ /* check for change in any arguments' type */
+ if (result)
+ {
+ int i;
+
+ for (i = 0; i < procStruct->pronargs; i++)
+ {
+ if (procStruct->proargtypes[i] == ANYARRAYOID ||
+ procStruct->proargtypes[i] == ANYELEMENTOID)
+ {
+ int j =
func->fn_argvarnos[i];
+ PLpgSQL_var *var = (PLpgSQL_var *)
func->datums[j];
+ Oid vartypeid =
var->datatype->typoid;
+
+ result = (vartypeid ==
get_fn_expr_argtype(fcinfo, i));
+ if (!result)
+ break;
+ }
+ }
+ }
+ }
ReleaseSysCache(procTup);
Index: src/pl/plpgsql/src/plpgsql.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/plpgsql.h,v
retrieving revision 1.36
diff -c -r1.36 plpgsql.h
*** src/pl/plpgsql/src/plpgsql.h 5 May 2003 16:46:28 -0000 1.36
--- src/pl/plpgsql/src/plpgsql.h 30 Jun 2003 04:10:57 -0000
***************
*** 588,594 ****
* Functions in pl_comp.c
* ----------
*/
! extern PLpgSQL_function *plpgsql_compile(Oid fn_oid, int functype);
extern int plpgsql_parse_word(char *word);
extern int plpgsql_parse_dblword(char *word);
extern int plpgsql_parse_tripword(char *word);
--- 588,596 ----
* Functions in pl_comp.c
* ----------
*/
! extern PLpgSQL_function *plpgsql_compile(Oid fn_oid,
! int
functype,
!
FunctionCallInfo fcinfo);
extern int plpgsql_parse_word(char *word);
extern int plpgsql_parse_dblword(char *word);
extern int plpgsql_parse_tripword(char *word);
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
(send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])
