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 PyGreSQL@Vex.Net https://mail.vex.net/mailman/listinfo/pygresql