The attached patch enables PL/pgSQL functions (but not triggers) to accept and return polymorphic types. It is careful to return false from func_up_to_date() if any of the polymorphic types change from call-to-call. It also falls back to the pg_proc declared types if the caller didn't setup the FuncExpr node.

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])

Reply via email to