From: Peter Eisentraut Date: Mon, 30 Jan 2012 19:38:52 +0000 (+0200) Subject: PL/Python: Add result metadata functions X-Git-Tag: REL9_2_BETA1~499 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ee7fa66b19f5454fac07caee4b7798810b579a82;p=postgresql PL/Python: Add result metadata functions Add result object functions .colnames, .coltypes, .coltypmods to obtain information about the result column names and types, which was previously not possible in the PL/Python SPI interface. reviewed by Abhijit Menon-Sen --- diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml index 5a3c8caa68..237c881a5c 100644 --- a/doc/src/sgml/plpython.sgml +++ b/doc/src/sgml/plpython.sgml @@ -886,9 +886,12 @@ $$ LANGUAGE plpythonu; list or dictionary object. The result object can be accessed by row number and column name. It has these additional methods: nrows which returns the number of rows - returned by the query, and status which is the - SPI_execute() return value. The result object - can be modified. + returned by the query, status which is the + SPI_execute() return value, + colnames which is the list of column names, + coltypes which is the list of column type OIDs, + and coltypmods which is the list of type-specific type + modifiers for the columns. The result object can be modified. diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out index 3b4d7a3010..9ed081b184 100644 --- a/src/pl/plpython/expected/plpython_spi.out +++ b/src/pl/plpython/expected/plpython_spi.out @@ -117,16 +117,25 @@ SELECT join_sequences(sequences) FROM sequences -- CREATE FUNCTION result_nrows_test() RETURNS int AS $$ -plan = plpy.prepare("SELECT 1 UNION SELECT 2") +plan = plpy.prepare("SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'") plpy.info(plan.status()) # not really documented or useful result = plpy.execute(plan) if result.status() > 0: + plpy.info(result.colnames()) + plpy.info(result.coltypes()) + plpy.info(result.coltypmods()) return result.nrows() else: return None $$ LANGUAGE plpythonu; SELECT result_nrows_test(); INFO: True +CONTEXT: PL/Python function "result_nrows_test" +INFO: ['foo', 'bar'] +CONTEXT: PL/Python function "result_nrows_test" +INFO: [23, 25] +CONTEXT: PL/Python function "result_nrows_test" +INFO: [-1, -1] CONTEXT: PL/Python function "result_nrows_test" result_nrows_test ------------------- diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c index bf46a16595..b25e8083b9 100644 --- a/src/pl/plpython/plpy_resultobject.c +++ b/src/pl/plpython/plpy_resultobject.c @@ -12,6 +12,9 @@ static void PLy_result_dealloc(PyObject *arg); +static PyObject *PLy_result_colnames(PyObject *self, PyObject *unused); +static PyObject *PLy_result_coltypes(PyObject *self, PyObject *unused); +static PyObject *PLy_result_coltypmods(PyObject *self, PyObject *unused); static PyObject *PLy_result_nrows(PyObject *self, PyObject *args); static PyObject *PLy_result_status(PyObject *self, PyObject *args); static Py_ssize_t PLy_result_length(PyObject *arg); @@ -35,6 +38,9 @@ static PySequenceMethods PLy_result_as_sequence = { }; static PyMethodDef PLy_result_methods[] = { + {"colnames", PLy_result_colnames, METH_NOARGS, NULL}, + {"coltypes", PLy_result_coltypes, METH_NOARGS, NULL}, + {"coltypmods", PLy_result_coltypmods, METH_NOARGS, NULL}, {"nrows", PLy_result_nrows, METH_VARARGS, NULL}, {"status", PLy_result_status, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} @@ -96,6 +102,7 @@ PLy_result_new(void) ob->status = Py_None; ob->nrows = PyInt_FromLong(-1); ob->rows = PyList_New(0); + ob->tupdesc = NULL; return (PyObject *) ob; } @@ -108,10 +115,57 @@ PLy_result_dealloc(PyObject *arg) Py_XDECREF(ob->nrows); Py_XDECREF(ob->rows); Py_XDECREF(ob->status); + if (ob->tupdesc) + { + FreeTupleDesc(ob->tupdesc); + ob->tupdesc = NULL; + } arg->ob_type->tp_free(arg); } +static PyObject * +PLy_result_colnames(PyObject *self, PyObject *unused) +{ + PLyResultObject *ob = (PLyResultObject *) self; + PyObject *list; + int i; + + list = PyList_New(ob->tupdesc->natts); + for (i = 0; i < ob->tupdesc->natts; i++) + PyList_SET_ITEM(list, i, PyString_FromString(NameStr(ob->tupdesc->attrs[i]->attname))); + + return list; +} + +static PyObject * +PLy_result_coltypes(PyObject *self, PyObject *unused) +{ + PLyResultObject *ob = (PLyResultObject *) self; + PyObject *list; + int i; + + list = PyList_New(ob->tupdesc->natts); + for (i = 0; i < ob->tupdesc->natts; i++) + PyList_SET_ITEM(list, i, PyInt_FromLong(ob->tupdesc->attrs[i]->atttypid)); + + return list; +} + +static PyObject * +PLy_result_coltypmods(PyObject *self, PyObject *unused) +{ + PLyResultObject *ob = (PLyResultObject *) self; + PyObject *list; + int i; + + list = PyList_New(ob->tupdesc->natts); + for (i = 0; i < ob->tupdesc->natts; i++) + PyList_SET_ITEM(list, i, PyInt_FromLong(ob->tupdesc->attrs[i]->atttypmod)); + + return list; +} + static PyObject * PLy_result_nrows(PyObject *self, PyObject *args) { diff --git a/src/pl/plpython/plpy_resultobject.h b/src/pl/plpython/plpy_resultobject.h index 719828a3ef..1b37d1d0c0 100644 --- a/src/pl/plpython/plpy_resultobject.h +++ b/src/pl/plpython/plpy_resultobject.h @@ -5,6 +5,9 @@ #ifndef PLPY_RESULTOBJECT_H #define PLPY_RESULTOBJECT_H +#include "access/tupdesc.h" + + typedef struct PLyResultObject { PyObject_HEAD @@ -12,6 +15,7 @@ typedef struct PLyResultObject PyObject *nrows; /* number of rows returned by query */ PyObject *rows; /* data rows, or None if no data returned */ PyObject *status; /* query status, SPI_OK_*, or SPI_ERR_* */ + TupleDesc tupdesc; } PLyResultObject; extern void PLy_result_init_type(void); diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c index 3afb1093d5..0d63c4f5ce 100644 --- a/src/pl/plpython/plpy_spi.c +++ b/src/pl/plpython/plpy_spi.c @@ -398,6 +398,8 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) oldcontext = CurrentMemoryContext; PG_TRY(); { + result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc); + if (rows) { Py_DECREF(result->rows); diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql index 874b31e6df..b828744d1f 100644 --- a/src/pl/plpython/sql/plpython_spi.sql +++ b/src/pl/plpython/sql/plpython_spi.sql @@ -95,10 +95,13 @@ SELECT join_sequences(sequences) FROM sequences CREATE FUNCTION result_nrows_test() RETURNS int AS $$ -plan = plpy.prepare("SELECT 1 UNION SELECT 2") +plan = plpy.prepare("SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'") plpy.info(plan.status()) # not really documented or useful result = plpy.execute(plan) if result.status() > 0: + plpy.info(result.colnames()) + plpy.info(result.coltypes()) + plpy.info(result.coltypmods()) return result.nrows() else: return None