Fix plpython to not get totally confused by OUT arguments. (It still doesn't
authorTom Lane
Sat, 3 May 2008 02:47:48 +0000 (02:47 +0000)
committerTom Lane
Sat, 3 May 2008 02:47:48 +0000 (02:47 +0000)
support multiple OUT arguments, though.)

Hannu Krosing

src/pl/plpython/expected/plpython_function.out
src/pl/plpython/expected/plpython_test.out
src/pl/plpython/plpython.c
src/pl/plpython/sql/plpython_function.sql
src/pl/plpython/sql/plpython_test.sql

index e1ffa7302dbd3f5abc7efd66800a8a1d6d2f34b9..4ace0445d9abacc91d4b61b607cc045c696afb26 100644 (file)
@@ -436,3 +436,14 @@ elif typ == 'obj':
    type_record.second = second
    return type_record
 $$ LANGUAGE plpythonu;
+CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
+return first + '_in_to_out';
+$$ LANGUAGE plpythonu;
+-- this doesn't work yet :-(
+CREATE FUNCTION test_in_out_params_multi(first in text,
+                                         second out text, third out text) AS $$
+return first + '_record_in_to_out';
+$$ LANGUAGE plpythonu;
+CREATE FUNCTION test_inout_params(first inout text) AS $$
+return first + '_inout';
+$$ LANGUAGE plpythonu;
index 170abe7ab6e68250df134238e1a61ea85af5752b..ddd565bbff5fda0d56a630d0a26d3a4e47d317b1 100644 (file)
@@ -539,3 +539,18 @@ SELECT * FROM test_type_record_as('obj', null, null, true);
        |       
 (1 row)
 
+SELECT * FROM test_in_out_params('test_in');
+      second       
+-------------------
+ test_in_in_to_out
+(1 row)
+
+-- this doesn't work yet :-(
+SELECT * FROM test_in_out_params_multi('test_in');
+ERROR:  plpython functions cannot return type record
+SELECT * FROM test_inout_params('test_in');
+     first     
+---------------
+ test_in_inout
+(1 row)
+
index 130eca4f71de0e4e1af1244f5c2e4dac256a464a..9fbfe563ee91b4119565abe7a3e7a253a6a422a7 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.108 2008/03/28 00:21:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.109 2008/05/03 02:47:47 tgl Exp $
  *
  *********************************************************************
  */
@@ -1161,9 +1161,6 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
    bool        isnull;
    int         i,
                rv;
-   Datum       argnames;
-   Datum      *elems;
-   int         nelems;
 
    procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 
@@ -1249,58 +1246,83 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
        }
 
        /*
-        * now get information required for input conversion of the
-        * procedure's arguments.
+        * Now get information required for input conversion of the
+        * procedure's arguments.  Note that we ignore output arguments
+        * here --- since we don't support returning record, and that was
+        * already checked above, there's no need to worry about multiple
+        * output arguments.
         */
-       proc->nargs = procStruct->pronargs;
-       if (proc->nargs)
+       if (procStruct->pronargs)
        {
-           argnames = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargnames, &isnull);
-           if (!isnull)
+           Oid     *types;
+           char   **names,
+                   *modes;
+           int      i,
+                    pos,
+                    total;
+
+           /* extract argument type info from the pg_proc tuple */
+           total = get_func_arg_info(procTup, &types, &names, &modes);
+
+           /* count number of in+inout args into proc->nargs */
+           if (modes == NULL)
+               proc->nargs = total;
+           else
            {
-               /* XXX this code is WRONG if there are any output arguments */
-               deconstruct_array(DatumGetArrayTypeP(argnames), TEXTOID, -1, false, 'i',
-                                 &elems, NULL, &nelems);
-               if (nelems != proc->nargs)
-                   elog(ERROR,
-                        "proargnames must have the same number of elements "
-                        "as the function has arguments");
-               proc->argnames = (char **) PLy_malloc(sizeof(char *) * proc->nargs);
-               memset(proc->argnames, 0, sizeof(char *) * proc->nargs);
+               /* proc->nargs was initialized to 0 above */
+               for (i = 0; i < total; i++)
+               {
+                   if (modes[i] != 'o')
+                       (proc->nargs)++;
+               }
            }
-       }
-       for (i = 0; i < proc->nargs; i++)
-       {
-           HeapTuple   argTypeTup;
-           Form_pg_type argTypeStruct;
 
-           argTypeTup = SearchSysCache(TYPEOID,
-                        ObjectIdGetDatum(procStruct->proargtypes.values[i]),
-                                       0, 0, 0);
-           if (!HeapTupleIsValid(argTypeTup))
-               elog(ERROR, "cache lookup failed for type %u",
-                    procStruct->proargtypes.values[i]);
-           argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
+           proc->argnames = (char **) PLy_malloc(sizeof(char *) * proc->nargs);
+           for (i = pos = 0; i < total; i++)
+           {
+               HeapTuple   argTypeTup;
+               Form_pg_type argTypeStruct;
 
-           /* Disallow pseudotype argument */
-           if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
-               ereport(ERROR,
-                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                        errmsg("plpython functions cannot take type %s",
-                       format_type_be(procStruct->proargtypes.values[i]))));
-
-           if (argTypeStruct->typtype != TYPTYPE_COMPOSITE)
-               PLy_input_datum_func(&(proc->args[i]),
-                                    procStruct->proargtypes.values[i],
-                                    argTypeTup);
-           else
-               proc->args[i].is_rowtype = 2;   /* still need to set I/O funcs */
+               if (modes && modes[i] == 'o')   /* skip OUT arguments */
+                   continue;
+
+               Assert(types[i] == procStruct->proargtypes.values[pos]);
+
+               argTypeTup = SearchSysCache(TYPEOID,
+                                           ObjectIdGetDatum(types[i]),
+                                           0, 0, 0);
+               if (!HeapTupleIsValid(argTypeTup))
+                   elog(ERROR, "cache lookup failed for type %u", types[i]);
+               argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
+
+               /* check argument type is OK, set up I/O function info */
+               switch (argTypeStruct->typtype)
+               {
+                   case TYPTYPE_PSEUDO:
+                       /* Disallow pseudotype argument */
+                       ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("plpython functions cannot take type %s",
+                                format_type_be(types[i]))));
+                       break;
+                   case TYPTYPE_COMPOSITE:
+                       /* we'll set IO funcs at first call */
+                       proc->args[pos].is_rowtype = 2;
+                       break;
+                   default:
+                       PLy_input_datum_func(&(proc->args[pos]),
+                                            types[i],
+                                            argTypeTup);
+                       break;
+               }
 
-           ReleaseSysCache(argTypeTup);
+               /* get argument name */
+               proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;
 
-           /* Fetch argument name */
-           if (proc->argnames)
-               proc->argnames[i] = PLy_strdup(TextDatumGetCString(elems[i]));
+               ReleaseSysCache(argTypeTup);
+
+               pos++;
+           }
        }
 
        /*
index 224d5196a3c236782f695d6bc061c503516da511..cf01e8e0cdc81b2f991614f2dbb50b3c1127a572 100644 (file)
@@ -480,3 +480,16 @@ elif typ == 'obj':
    return type_record
 $$ LANGUAGE plpythonu;
 
+CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
+return first + '_in_to_out';
+$$ LANGUAGE plpythonu;
+
+-- this doesn't work yet :-(
+CREATE FUNCTION test_in_out_params_multi(first in text,
+                                         second out text, third out text) AS $$
+return first + '_record_in_to_out';
+$$ LANGUAGE plpythonu;
+
+CREATE FUNCTION test_inout_params(first inout text) AS $$
+return first + '_inout';
+$$ LANGUAGE plpythonu;
index dafcae089e92c5b36eb5aaba15e9dd25b45fc422..f7321ab9e006cb7ea31395bd636d175fe3e8d987 100644 (file)
@@ -143,3 +143,8 @@ SELECT * FROM test_type_record_as('obj', 'one', null, false);
 SELECT * FROM test_type_record_as('obj', null, 2, false);
 SELECT * FROM test_type_record_as('obj', 'three', 3, false);
 SELECT * FROM test_type_record_as('obj', null, null, true);
+
+SELECT * FROM test_in_out_params('test_in');
+-- this doesn't work yet :-(
+SELECT * FROM test_in_out_params_multi('test_in');
+SELECT * FROM test_inout_params('test_in');