1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

PL/Python array support

Support arrays as parameters and return values of PL/Python functions.
This commit is contained in:
Peter Eisentraut
2009-12-10 20:43:40 +00:00
parent a37b001b80
commit db7386187f
4 changed files with 343 additions and 4 deletions

View File

@ -1,7 +1,7 @@
/**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL
*
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.132 2009/11/03 11:05:02 petere Exp $
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.133 2009/12/10 20:43:40 petere Exp $
*
*********************************************************************
*/
@ -89,6 +89,9 @@ typedef struct PLyDatumToOb
Oid typoid; /* The OID of the type */
Oid typioparam;
bool typbyval;
int16 typlen;
char typalign;
struct PLyDatumToOb *elm;
} PLyDatumToOb;
typedef struct PLyTupleToOb
@ -120,6 +123,9 @@ typedef struct PLyObToDatum
Oid typoid; /* The OID of the type */
Oid typioparam;
bool typbyval;
int16 typlen;
char typalign;
struct PLyObToDatum *elm;
} PLyObToDatum;
typedef struct PLyObToTuple
@ -284,6 +290,7 @@ static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
static PyObject *PLyString_FromBytea(PLyDatumToOb *arg, Datum d);
static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d);
static PyObject *PLyList_FromArray(PLyDatumToOb *arg, Datum d);
static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);
@ -293,6 +300,8 @@ static Datum PLyObject_ToBytea(PLyTypeInfo *, PLyObToDatum *,
PyObject *);
static Datum PLyObject_ToDatum(PLyTypeInfo *, PLyObToDatum *,
PyObject *);
static Datum PLySequence_ToArray(PLyTypeInfo *, PLyObToDatum *,
PyObject *);
static HeapTuple PLyMapping_ToTuple(PLyTypeInfo *, PyObject *);
static HeapTuple PLySequence_ToTuple(PLyTypeInfo *, PyObject *);
@ -1653,18 +1662,21 @@ static void
PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
Oid element_type;
perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
arg->typoid = HeapTupleGetOid(typeTup);
arg->typioparam = getTypeIOParam(typeTup);
arg->typbyval = typeStruct->typbyval;
element_type = get_element_type(arg->typoid);
/*
* Select a conversion function to convert Python objects to
* PostgreSQL datums. Most data types can go through the generic
* function.
*/
switch (getBaseType(arg->typoid))
switch (getBaseType(element_type ? element_type : arg->typoid))
{
case BOOLOID:
arg->func = PLyObject_ToBool;
@ -1676,6 +1688,29 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
arg->func = PLyObject_ToDatum;
break;
}
if (element_type)
{
char dummy_delim;
Oid funcid;
if (type_is_rowtype(element_type))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PL/Python functions cannot return type %s",
format_type_be(arg->typoid)),
errdetail("PL/Python does not support conversion to arrays of row types.")));
arg->elm = PLy_malloc0(sizeof(*arg->elm));
arg->elm->func = arg->func;
arg->func = PLySequence_ToArray;
arg->elm->typoid = element_type;
get_type_io_data(element_type, IOFunc_input,
&arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim,
&arg->elm->typioparam, &funcid);
perm_fmgr_info(funcid, &arg->elm->typfunc);
}
}
static void
@ -1691,15 +1726,17 @@ static void
PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
Oid element_type = get_element_type(typeOid);
/* Get the type's conversion information */
perm_fmgr_info(typeStruct->typoutput, &arg->typfunc);
arg->typoid = HeapTupleGetOid(typeTup);
arg->typioparam = getTypeIOParam(typeTup);
arg->typbyval = typeStruct->typbyval;
arg->typlen = typeStruct->typlen;
/* Determine which kind of Python object we will convert to */
switch (getBaseType(typeOid))
switch (getBaseType(element_type ? element_type : typeOid))
{
case BOOLOID:
arg->func = PLyBool_FromBool;
@ -1729,6 +1766,14 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
arg->func = PLyString_FromDatum;
break;
}
if (element_type)
{
arg->elm = PLy_malloc0(sizeof(*arg->elm));
arg->elm->func = arg->func;
arg->func = PLyList_FromArray;
get_typlenbyvalalign(element_type, &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign);
}
}
static void
@ -1832,6 +1877,45 @@ PLyString_FromDatum(PLyDatumToOb *arg, Datum d)
return r;
}
static PyObject *
PLyList_FromArray(PLyDatumToOb *arg, Datum d)
{
ArrayType *array = DatumGetArrayTypeP(d);
PyObject *list;
int length;
int lbound;
int i;
if (ARR_NDIM(array) == 0)
return PyList_New(0);
if (ARR_NDIM(array) != 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert multidimensional array to Python list"),
errdetail("PL/Python only supports one-dimensional arrays.")));
length = ARR_DIMS(array)[0];
lbound = ARR_LBOUND(array)[0];
list = PyList_New(length);
for (i = 0; i < length; i++)
{
Datum elem;
bool isnull;
int offset;
offset = lbound + i;
elem = array_ref(array, 1, &offset, arg->typlen, arg->elm->typlen, arg->elm->typbyval, arg->elm->typalign, &isnull);
if (isnull)
PyList_SET_ITEM(list, i, Py_None);
else
PyList_SET_ITEM(list, i, arg->elm->func(arg, elem));
}
return list;
}
static PyObject *
PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
{
@ -1994,6 +2078,49 @@ PLyObject_ToDatum(PLyTypeInfo *info,
return rv;
}
static Datum
PLySequence_ToArray(PLyTypeInfo *info,
PLyObToDatum *arg,
PyObject *plrv)
{
ArrayType *array;
int i;
Datum *elems;
bool *nulls;
int len;
int lbs;
Assert(plrv != Py_None);
if (!PySequence_Check(plrv))
PLy_elog(ERROR, "return value of function with array return type is not a Python sequence");
len = PySequence_Length(plrv);
elems = palloc(sizeof(*elems) * len);
nulls = palloc(sizeof(*nulls) * len);
for (i = 0; i < len; i++)
{
PyObject *obj = PySequence_GetItem(plrv, i);
if (obj == Py_None)
nulls[i] = true;
else
{
nulls[i] = false;
/* We don't support arrays of row types yet, so the first
* argument can be NULL. */
elems[i] = arg->elm->func(NULL, arg->elm, obj);
}
Py_XDECREF(obj);
}
lbs = 1;
array = construct_md_array(elems, nulls, 1, &len, &lbs,
get_element_type(arg->typoid), arg->elm->typlen, arg->elm->typbyval, arg->elm->typalign);
return PointerGetDatum(array);
}
static HeapTuple
PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
{