On Fri, Jun 07, 2019 at 09:06:26PM +0200, Christoph Zwerschke wrote:
> >pgdb has
> >|Cursor.coltypes
> >| The list of columns types of the current result set
> >|The values in this list are the same values as the type_code elements in
> >the Cursor.description attribute. Always use the latter if you want to
> >remain standard compliant.
> >
> >But I was surprised to find that pg doesn't expose anything similar. I
> >wondered whether we shouldn't add interfaces for PQftype and maybe PQfmod,
> >pehaps by exposing query_obj->col_types.
>
> Yes, that's really lacking. The interface has grown over time and I think
> originally, the focus was on the entity methods like get() and insert() and
> update().
> You would get the types via attnames().
I'm not sure if I understood the last sentence. Do you mean the types of
column in a table ? That's not the same as types of result columns, though.
It could be computed from columns of different types in multiple tables like
SELECT 1.0*a.incol1/b.intcol2, or not tied to table at all.
In the immediate case, I discovered/realized that our server application is
sending a list of types to the client, along with the result (which uses
iteration as of last month). In order to find the type of a column, it's
iterating over rows until it finds a non-null value, and testing if its type()
in [int,float,long]. However our reports might have many all-null columns (for
features the customer doesn't have enabled). So it's iterating over 1e6 rows a
few hundred times. An 7min query was being followed by 15min of looping over
iterable. I mitigated it for now by making it loop over rows first rather than
column, so we're calling into pygres to build each row once rather than once
per all-null row. It'd be much nicer if we could call ftype() and avoid
looping over rows for this at all :)
I threw this together, I guess we should do a layer of processing, like what's
done by pgdb.cursor._make_description.
Justin
Index: pgquery.c
===================================================================
--- pgquery.c (revision 1017)
+++ pgquery.c (working copy)
@@ -239,25 +273,59 @@
return PyInt_FromLong(num);
}
+/* Get field number from name in last result. */
+static char query_fieldinfo__doc__[] =
+"fieldinfo(name) -- return information about query result";
+
+/* Return information on field types */
+static PyObject *query_fieldinfo(PyObject *self, void *closure)
+{
+ queryObject *q=(queryObject *)self;
+ PyObject *result;
+ int i;
+
+ if (!(result = PyTuple_New(q->num_fields)))
+ return NULL;
+
+ for (i = 0; i < q->num_fields; ++i) {
+ PyObject *tup = PyTuple_New(4);
+ if (!tup)
+ return NULL;
+
+ PyTuple_SET_ITEM(result, i, tup);
+ // PyTuple_SET_ITEM(tup, 0, PyInt_FromLong(i));
+ PyTuple_SET_ITEM(tup, 0,
+ PyStr_FromString(PQfname(q->result, i)));
+ PyTuple_SET_ITEM(tup, 1,
+ PyInt_FromLong(PQftype(q->result, i)));
+ PyTuple_SET_ITEM(tup, 2,
+ PyInt_FromLong(PQfsize(q->result, i)));
+ PyTuple_SET_ITEM(tup, 3,
+ PyInt_FromLong(PQfmod(q->result, i)));
+ }
+
+ /* returns result */
+ return result;
+}
+
/* Retrieve one row from the result as a tuple. */
static char query_one__doc__[] =
"one() -- Get one row from the result of a query\n\n"
"Only one row from the result is returned as a tuple of fields.\n"
"This method can be called multiple times to return more rows.\n"
-"It returns None if the result does not contain one more row.\n";
+"It returns None if the result does not contain any more rows.\n";
static PyObject *
query_one(queryObject *self, PyObject *noargs)
{
@@ -265,14 +333,13 @@
"single() -- Get the result of a query as single row\n\n"
"The single row from the query result is returned as a tuple of fields.\n"
"This method returns the same single row when called multiple times.\n"
-"It raises an InvalidResultError if the result doesn't have exactly one row,\n"
-"which will be of type NoResultError or MultipleResultsError specifically.\n";
+"It raises an Exception if the result doesn't have exactly one row.\n"
+"The exception will be a subclass of InvalidResultError; specifically, either\n"
+"NoResultError or MultipleResultsError.\n";
static PyObject *
query_single(queryObject *self, PyObject *noargs)
{
/* Query sequence protocol methods */
@@ -707,6 +772,8 @@
METH_VARARGS, query_fieldnum__doc__},
{"listfields", (PyCFunction) query_listfields,
METH_NOARGS, query_listfields__doc__},
+ {"fieldinfo", (PyCFunction) query_fieldinfo,
+ METH_NOARGS, query_fieldinfo__doc__},
{"ntuples", (PyCFunction) query_ntuples,
METH_NOARGS, query_ntuples__doc__},
{NULL, NULL}
@@ -714,6 +781,11 @@
static char query__doc__[] = "PyGreSQL query object";
+static struct PyGetSetDef getset[]={
+ {"description", query_fieldinfo, NULL, query_fieldinfo__doc__, NULL, },
+ {NULL,}
+};
+
/* Query type definition */
static PyTypeObject queryType = {
PyVarObject_HEAD_INIT(NULL, 0)
@@ -746,4 +818,6 @@
(getiterfunc) query_iter, /* tp_iter */
(iternextfunc) query_next, /* tp_iternext */
query_methods, /* tp_methods */
+ 0, /* tp_members */
+ getset, /* tp_getset */
};
_______________________________________________
PyGreSQL mailing list
[email protected]
https://mail.vex.net/mailman/listinfo/pygresql