Fix plpython to not get totally confused by OUT arguments. (It still doesn't

support multiple OUT arguments, though.)

Hannu Krosing
This commit is contained in:
Tom Lane 2008-05-03 02:47:48 +00:00
parent d61eecb5a1
commit bdc7dd6799
5 changed files with 115 additions and 49 deletions

View File

@ -436,3 +436,14 @@ elif typ == 'obj':
type_record.second = second type_record.second = second
return type_record return type_record
$$ LANGUAGE plpythonu; $$ 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;

View File

@ -539,3 +539,18 @@ SELECT * FROM test_type_record_as('obj', null, null, true);
| |
(1 row) (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)

View File

@ -1,7 +1,7 @@
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * 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; bool isnull;
int i, int i,
rv; rv;
Datum argnames;
Datum *elems;
int nelems;
procStruct = (Form_pg_proc) GETSTRUCT(procTup); 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 * Now get information required for input conversion of the
* procedure's arguments. * 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 (procStruct->pronargs)
if (proc->nargs)
{ {
argnames = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargnames, &isnull); Oid *types;
if (!isnull) 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 */ /* proc->nargs was initialized to 0 above */
deconstruct_array(DatumGetArrayTypeP(argnames), TEXTOID, -1, false, 'i', for (i = 0; i < total; i++)
&elems, NULL, &nelems); {
if (nelems != proc->nargs) if (modes[i] != 'o')
elog(ERROR, (proc->nargs)++;
"proargnames must have the same number of elements " }
"as the function has arguments"); }
proc->argnames = (char **) PLy_malloc(sizeof(char *) * proc->nargs); proc->argnames = (char **) PLy_malloc(sizeof(char *) * proc->nargs);
memset(proc->argnames, 0, sizeof(char *) * proc->nargs); for (i = pos = 0; i < total; i++)
}
}
for (i = 0; i < proc->nargs; i++)
{ {
HeapTuple argTypeTup; HeapTuple argTypeTup;
Form_pg_type argTypeStruct; Form_pg_type argTypeStruct;
if (modes && modes[i] == 'o') /* skip OUT arguments */
continue;
Assert(types[i] == procStruct->proargtypes.values[pos]);
argTypeTup = SearchSysCache(TYPEOID, argTypeTup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(procStruct->proargtypes.values[i]), ObjectIdGetDatum(types[i]),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(argTypeTup)) if (!HeapTupleIsValid(argTypeTup))
elog(ERROR, "cache lookup failed for type %u", elog(ERROR, "cache lookup failed for type %u", types[i]);
procStruct->proargtypes.values[i]);
argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup); 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 */ /* Disallow pseudotype argument */
if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("plpython functions cannot take type %s", errmsg("plpython functions cannot take type %s",
format_type_be(procStruct->proargtypes.values[i])))); format_type_be(types[i]))));
break;
if (argTypeStruct->typtype != TYPTYPE_COMPOSITE) case TYPTYPE_COMPOSITE:
PLy_input_datum_func(&(proc->args[i]), /* we'll set IO funcs at first call */
procStruct->proargtypes.values[i], proc->args[pos].is_rowtype = 2;
break;
default:
PLy_input_datum_func(&(proc->args[pos]),
types[i],
argTypeTup); argTypeTup);
else break;
proc->args[i].is_rowtype = 2; /* still need to set I/O funcs */ }
/* get argument name */
proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;
ReleaseSysCache(argTypeTup); ReleaseSysCache(argTypeTup);
/* Fetch argument name */ pos++;
if (proc->argnames) }
proc->argnames[i] = PLy_strdup(TextDatumGetCString(elems[i]));
} }
/* /*

View File

@ -480,3 +480,16 @@ elif typ == 'obj':
return type_record return type_record
$$ LANGUAGE plpythonu; $$ 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;

View 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', null, 2, false);
SELECT * FROM test_type_record_as('obj', 'three', 3, false); SELECT * FROM test_type_record_as('obj', 'three', 3, false);
SELECT * FROM test_type_record_as('obj', null, null, true); 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');