Commit ee7fa66b authored by Peter Eisentraut's avatar Peter Eisentraut

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
parent c6ea8cce
...@@ -886,9 +886,12 @@ $$ LANGUAGE plpythonu; ...@@ -886,9 +886,12 @@ $$ LANGUAGE plpythonu;
list or dictionary object. The result object can be accessed by list or dictionary object. The result object can be accessed by
row number and column name. It has these additional methods: row number and column name. It has these additional methods:
<function>nrows</function> which returns the number of rows <function>nrows</function> which returns the number of rows
returned by the query, and <function>status</function> which is the returned by the query, <function>status</function> which is the
<function>SPI_execute()</function> return value. The result object <function>SPI_execute()</function> return value,
can be modified. <function>colnames</function> which is the list of column names,
<function>coltypes</function> which is the list of column type OIDs,
and <function>coltypmods</function> which is the list of type-specific type
modifiers for the columns. The result object can be modified.
</para> </para>
<para> <para>
......
...@@ -117,16 +117,25 @@ SELECT join_sequences(sequences) FROM sequences ...@@ -117,16 +117,25 @@ SELECT join_sequences(sequences) FROM sequences
-- --
CREATE FUNCTION result_nrows_test() RETURNS int CREATE FUNCTION result_nrows_test() RETURNS int
AS $$ 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 plpy.info(plan.status()) # not really documented or useful
result = plpy.execute(plan) result = plpy.execute(plan)
if result.status() > 0: if result.status() > 0:
plpy.info(result.colnames())
plpy.info(result.coltypes())
plpy.info(result.coltypmods())
return result.nrows() return result.nrows()
else: else:
return None return None
$$ LANGUAGE plpythonu; $$ LANGUAGE plpythonu;
SELECT result_nrows_test(); SELECT result_nrows_test();
INFO: True 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" CONTEXT: PL/Python function "result_nrows_test"
result_nrows_test result_nrows_test
------------------- -------------------
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
static void PLy_result_dealloc(PyObject *arg); 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_nrows(PyObject *self, PyObject *args);
static PyObject *PLy_result_status(PyObject *self, PyObject *args); static PyObject *PLy_result_status(PyObject *self, PyObject *args);
static Py_ssize_t PLy_result_length(PyObject *arg); static Py_ssize_t PLy_result_length(PyObject *arg);
...@@ -35,6 +38,9 @@ static PySequenceMethods PLy_result_as_sequence = { ...@@ -35,6 +38,9 @@ static PySequenceMethods PLy_result_as_sequence = {
}; };
static PyMethodDef PLy_result_methods[] = { 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}, {"nrows", PLy_result_nrows, METH_VARARGS, NULL},
{"status", PLy_result_status, METH_VARARGS, NULL}, {"status", PLy_result_status, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
...@@ -96,6 +102,7 @@ PLy_result_new(void) ...@@ -96,6 +102,7 @@ PLy_result_new(void)
ob->status = Py_None; ob->status = Py_None;
ob->nrows = PyInt_FromLong(-1); ob->nrows = PyInt_FromLong(-1);
ob->rows = PyList_New(0); ob->rows = PyList_New(0);
ob->tupdesc = NULL;
return (PyObject *) ob; return (PyObject *) ob;
} }
...@@ -108,10 +115,57 @@ PLy_result_dealloc(PyObject *arg) ...@@ -108,10 +115,57 @@ PLy_result_dealloc(PyObject *arg)
Py_XDECREF(ob->nrows); Py_XDECREF(ob->nrows);
Py_XDECREF(ob->rows); Py_XDECREF(ob->rows);
Py_XDECREF(ob->status); Py_XDECREF(ob->status);
if (ob->tupdesc)
{
FreeTupleDesc(ob->tupdesc);
ob->tupdesc = NULL;
}
arg->ob_type->tp_free(arg); 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 * static PyObject *
PLy_result_nrows(PyObject *self, PyObject *args) PLy_result_nrows(PyObject *self, PyObject *args)
{ {
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
#ifndef PLPY_RESULTOBJECT_H #ifndef PLPY_RESULTOBJECT_H
#define PLPY_RESULTOBJECT_H #define PLPY_RESULTOBJECT_H
#include "access/tupdesc.h"
typedef struct PLyResultObject typedef struct PLyResultObject
{ {
PyObject_HEAD PyObject_HEAD
...@@ -12,6 +15,7 @@ typedef struct PLyResultObject ...@@ -12,6 +15,7 @@ typedef struct PLyResultObject
PyObject *nrows; /* number of rows returned by query */ PyObject *nrows; /* number of rows returned by query */
PyObject *rows; /* data rows, or None if no data returned */ PyObject *rows; /* data rows, or None if no data returned */
PyObject *status; /* query status, SPI_OK_*, or SPI_ERR_* */ PyObject *status; /* query status, SPI_OK_*, or SPI_ERR_* */
TupleDesc tupdesc;
} PLyResultObject; } PLyResultObject;
extern void PLy_result_init_type(void); extern void PLy_result_init_type(void);
......
...@@ -398,6 +398,8 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) ...@@ -398,6 +398,8 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
oldcontext = CurrentMemoryContext; oldcontext = CurrentMemoryContext;
PG_TRY(); PG_TRY();
{ {
result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
if (rows) if (rows)
{ {
Py_DECREF(result->rows); Py_DECREF(result->rows);
......
...@@ -95,10 +95,13 @@ SELECT join_sequences(sequences) FROM sequences ...@@ -95,10 +95,13 @@ SELECT join_sequences(sequences) FROM sequences
CREATE FUNCTION result_nrows_test() RETURNS int CREATE FUNCTION result_nrows_test() RETURNS int
AS $$ 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 plpy.info(plan.status()) # not really documented or useful
result = plpy.execute(plan) result = plpy.execute(plan)
if result.status() > 0: if result.status() > 0:
plpy.info(result.colnames())
plpy.info(result.coltypes())
plpy.info(result.coltypmods())
return result.nrows() return result.nrows()
else: else:
return None return None
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment