I have one patch ready for evaluation.

1: implement fake/pseudo cursors in pg (same as what's in pgdb); I proposed not
to support any "move()" function in pg, since there's no "fetch" (besides
getItem which is newly introduced at your suggestion).  This patch is solid.

I also have additional patches which are "in progress" and less solid.  I'll
prepare after you after your evaluation of (1).

2: implement server/named cursors into pgmodule.c for pg.
This is a bit ugly in several ways..  connQuery needs to call MOVE LAST
immediately in order to support ntuples().  pgdb would (have to) be the same
way if it shared that code, otherwise it could you could consider allowing
.ntuples property to be -1 [0]
[0] https://www.python.org/dev/peps/pep-0249/#rowcount

3: messed with named cursors some more so they're "lazier": otherwise, the
existing logic of queryNext: "ret=query_row(); advance_row(); return ret;"
causes a row to be fetched which may never be used.  I was hoping in the
process of messing around with it to resolve the ugliness of (2) .. but it
wasn't meant to be.

4: started implementing named cursors in pgdb.

I'm sure I've bungled the style all up.  I confess to not knowing SVN but
hopefully this'll do.  None of the interfacea or code in (1) need to be updated
for (2)-(4).  Actually, I added "num_fields" specifically because it was needed
for named cursors anyway.

I wouldn't be surprised if you don't like the implications of server cursors,
and it may be best not to wait on this part until there's something
significantly less ugly than what I've done so far..

Currently named cursors use "WITH HOLD" but I guess that could be configurable,
same as arraysize.

I updated docs and wrote some tests for the first patch.  There's also some
tests in this patch for named cursors (which obviously fail without the
associated patch).  I'm planning to write some more tests, but if you wrote
some during evaluation it'd be good to include those, too.

Justin
Index: docs/contents/pg/query.rst
===================================================================
--- docs/contents/pg/query.rst  (revision 930)
+++ docs/contents/pg/query.rst  (working copy)
@@ -7,7 +7,7 @@
 
 The :class:`Query` object returned by :meth:`Connection.query` and
 :meth:`DB.query` provides the following methods for accessing
-the results of the query:
+the results of the query.
 
 getresult -- get query values as list of tuples
 -----------------------------------------------
@@ -131,3 +131,62 @@
     :raises TypeError: Too many arguments.
 
 This method returns the number of tuples found in a query.
+
+len(Query) -- number of tuples in query object
+----------------------------------------------
+
+.. function:: len(Query())
+
+    :returns: number of tuples in :class:`Query`
+    :rtype: int
+
+The number of rows in the result is also available as len(:meth:`Query`).
+
+.. versionadded:: 5.1
+
+Query -- iterate over query results as tuples\n\n"
+--------------------------------------------------
+.. method:: Query()
+
+    :returns: iterator over tuples of query result
+    :rtype: iterator
+
+The :meth:`DB.query` object can itself be used as an iterator over tuples of
+the resulting rows of the query.
+
+.. versionadded:: 5.1
+
+Query -- access individual row as sequence\n\n"
+-----------------------------------------------
+.. method:: Query()
+
+    :returns: column values of the indexed row
+    :rtype: tuple
+
+Individual rows can be accessed by indexing the .meth:`Query` object as a
+sequence, as in: :meth:`Query` [4]
+
+.. versionadded:: 5.1
+
+dictIter, namedIter -- iterate over query results as dictionaries or named 
tuples\n\n"
+--------------------------------------------------------------------------------------
+.. method:: Query.dictIter()
+
+    :returns: iterator over dictionaries of query result
+    :rtype: iterator
+
+This method returns an iterator over query results as dictionaries, with the
+field names used as its keys.  The dictionaries are the same as in the list
+returned by :meth:`dictresult`.
+
+.. versionadded:: 5.1
+
+.. method:: Query.namedIter()
+
+    :returns: iterator over named tuples of query result
+    :rtype: iterator
+
+This method returns an iterator over query results as named tuples, which are
+the same as in the list returned by :meth:`namedresult`.
+
+.. versionadded:: 5.1
Index: pgmodule.c
===================================================================
--- pgmodule.c  (revision 930)
+++ pgmodule.c  (working copy)
@@ -29,6 +29,8 @@
 
 /* Note: This should be linked against the same C runtime lib as Python */
 
+#undef NDEBUG
+
 #include <Python.h>
 
 #include <libpq-fe.h>
@@ -176,6 +178,10 @@
        connObject *pgcnx;                      /* parent connection object */
        PGresult   *result;                     /* result content */
        int                     encoding;               /* client encoding */
+       int                     current_row;    /* current selected row */
+       int                     max_row;                /* number of rows in 
the result */
+       int                     num_fields;             /* number of fields in 
each row */
+       int                     *col_types;             /* PyGreSQL column 
types */
 }      queryObject;
 #define is_queryObject(v) (PyType(v) == &queryType)
 
@@ -2371,6 +2377,10 @@
        /* stores result and returns object */
        Py_XINCREF(self);
        npgobj->pgcnx = self;
+       npgobj->current_row = 0;
+       npgobj->max_row = PQntuples(result);
+       npgobj->num_fields = PQnfields(result);
+       npgobj->col_types = get_col_types(result, npgobj->num_fields);
        npgobj->result = result;
        npgobj->encoding = encoding;
        return (PyObject *) npgobj;
@@ -4505,6 +4515,8 @@
 queryDealloc(queryObject *self)
 {
        Py_XDECREF(self->pgcnx);
+       if (self->col_types)
+               PyMem_Free(self->col_types);
        if (self->result)
                PQclear(self->result);
 
@@ -4518,7 +4530,7 @@
 static PyObject *
 queryNTuples(queryObject *self, PyObject *noargs)
 {
-       return PyInt_FromLong((long) PQntuples(self->result));
+       return PyInt_FromLong(self->max_row);
 }
 
 /* list fields names from query result */
@@ -4528,17 +4540,15 @@
 static PyObject *
 queryListFields(queryObject *self, PyObject *noargs)
 {
-       int                     i,
-                               n;
+       int                     i;
        char       *name;
        PyObject   *fieldstuple,
                           *str;
 
        /* builds tuple */
-       n = PQnfields(self->result);
-       fieldstuple = PyTuple_New(n);
+       fieldstuple = PyTuple_New(self->num_fields);
 
-       for (i = 0; i < n; ++i)
+       for (i = 0; i < self->num_fields; ++i)
        {
                name = PQfname(self->result, i);
                str = PyStr_FromString(name);
@@ -4567,7 +4577,7 @@
        }
 
        /* checks number validity */
-       if (i >= PQnfields(self->result))
+       if (i >= self->num_fields)
        {
                PyErr_SetString(PyExc_ValueError, "Invalid field number");
                return NULL;
@@ -4606,6 +4616,153 @@
        return PyInt_FromLong(num);
 }
 
+PyObject* queryGetIter(queryObject *self)
+{
+    Py_INCREF(self);
+    self->current_row=0;
+    return (PyObject*)self;
+}
+
+/* Returns value of the given column. Used by query_row and DictNext. */
+static PyObject *
+column_value(queryObject *self, int column)
+{
+       PyObject *val;
+
+       assert(column < self->num_fields);
+       assert(column >= 0);
+
+       /* null case: */
+       if (PQgetisnull(self->result, self->current_row, column))
+       {
+               Py_INCREF(Py_None);
+               return Py_None;
+       }
+
+       /* not null case: */
+
+       /* get the string representation of the value */
+       /* note: this is always null-terminated text format */
+       char   *s = PQgetvalue(self->result, self->current_row, column);
+       /* get the PyGreSQL type of the column */
+       int             type = self->col_types[column];
+       int                     encoding = self->encoding;
+
+       if (type & PYGRES_ARRAY)
+               val = cast_array(s, PQgetlength(self->result, 
self->current_row, column),
+                       encoding, type, NULL, 0);
+       else if (type == PYGRES_BYTEA)
+               val = cast_bytea_text(s);
+       else if (type == PYGRES_OTHER)
+               val = cast_other(s,
+                       PQgetlength(self->result, self->current_row, column), 
encoding,
+                       PQftype(self->result, column), self->pgcnx->cast_hook);
+       else if (type & PYGRES_TEXT)
+               val = cast_sized_text(s, PQgetlength(self->result, 
self->current_row, column),
+                       encoding, type);
+       else
+               val = cast_unsized_simple(s, type);
+
+       return val;
+}
+
+
+/* Return the current_row as a tuple.  Called by queryNext and queryGetItem */
+static PyObject *
+query_row(queryObject *self)
+{
+       PyObject   *res=NULL;
+       int                     j;
+
+       assert(self->current_row >= 0);
+       assert(self->current_row < self->max_row);
+
+       if (!(res = PyTuple_New(self->num_fields)))
+               return NULL;
+
+       for (j = 0; j<self->num_fields; ++j)
+       {
+               PyObject *val;
+               if (NULL == (val = column_value(self, j)))
+               {
+                       Py_DECREF(res);
+                       res = NULL;
+                       goto exit;
+               }
+
+               PyTuple_SET_ITEM(res, j, val);
+       }
+
+exit:
+       return res;
+}
+
+/* Return a tuple of values in the "next" row of the result set */
+static PyObject *
+queryNext(queryObject *self, PyObject *noargs)
+{
+       PyObject   *res=NULL;
+
+       if (self->current_row>=self->max_row) {
+               PyErr_SetNone(PyExc_StopIteration);
+               return NULL;
+       }
+
+       if (self->current_row < 0) {
+               PyErr_SetNone(PyExc_StopIteration); // XXX
+               return NULL;
+       }
+
+       res=query_row(self);
+       if (res) ++self->current_row;
+    return res;
+}
+
+#if 0 // NOT USED
+/* Update reference to current_row */
+static char queryMove__doc__[] =
+"queryMove([rows=1], [relative=True]) -- Move internal cursor-like pointer to 
a different row";
+
+static PyObject *
+UNUSEDqueryMove(queryObject *self, PyObject *args, PyObject *dict)
+{
+       static const char *kwlist[] = {"rows", "relative", NULL,};
+       int rows=1;
+       int relative=1; // True
+
+       if (!PyArg_ParseTupleAndKeywords(args, dict, "|ii", (char **) kwlist,
+               &rows, &relative))
+               return NULL;
+
+       if (relative) {
+               int new=rows + self->current_row;
+
+               if (new < 0) {
+                       /* It's not really so bad, it'll just raise 
StopIteration, but may as well take the opportunity to give a nice error */
+                       PyErr_SetString(PyExc_ValueError, "Integer overflow");
+                       return NULL;
+               }
+
+               if (new >= self->max_row) {
+                       /* It's not really so bad, it'll just raise 
StopIteration, but may as well take the opportunity to give a nice error */
+                       PyErr_SetString(PyExc_ValueError, "Cannot advance 
beyond the last row");
+                       return NULL;
+               }
+
+               self->current_row = new;
+       } else {
+               if (rows<0) {
+                       PyErr_SetString(PyExc_ValueError, "Cannot set negative 
row number");
+                       return NULL;
+               }
+               self->current_row = rows;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+#endif
+
 /* retrieves last result */
 static char queryGetResult__doc__[] =
 "getresult() -- Get the result of a query\n\n"
@@ -4616,80 +4773,64 @@
 queryGetResult(queryObject *self, PyObject *noargs)
 {
        PyObject   *reslist;
-       int                     i, m, n, *col_types;
-       int                     encoding = self->encoding;
+       int                     i;
 
        /* stores result in tuple */
-       m = PQntuples(self->result);
-       n = PQnfields(self->result);
-       if (!(reslist = PyList_New(m))) return NULL;
+       if (!(reslist = PyList_New(self->max_row))) return NULL;
 
-       if (!(col_types = get_col_types(self->result, n))) return NULL;
-
-       for (i = 0; i < m; ++i)
+       self->current_row=0;
+       for (i = 0; i < self->max_row; ++i)
        {
                PyObject   *rowtuple;
-               int                     j;
 
-               if (!(rowtuple = PyTuple_New(n)))
+               if (NULL==(rowtuple=queryNext(self, noargs)))
                {
                        Py_DECREF(reslist);
-                       reslist = NULL;
-                       goto exit;
+                       return NULL;
                }
 
-               for (j = 0; j < n; ++j)
-               {
-                       PyObject * val;
+               PyList_SET_ITEM(reslist, i, rowtuple);
+       }
 
-                       if (PQgetisnull(self->result, i, j))
-                       {
-                               Py_INCREF(Py_None);
-                               val = Py_None;
-                       }
-                       else /* not null */
-                       {
-                               /* get the string representation of the value */
-                               /* note: this is always null-terminated text 
format */
-                               char   *s = PQgetvalue(self->result, i, j);
-                               /* get the PyGreSQL type of the column */
-                               int             type = col_types[j];
+       /* returns list */
+       return reslist;
+}
 
-                               if (type & PYGRES_ARRAY)
-                                       val = cast_array(s, 
PQgetlength(self->result, i, j),
-                                               encoding, type, NULL, 0);
-                               else if (type == PYGRES_BYTEA)
-                                       val = cast_bytea_text(s);
-                               else if (type == PYGRES_OTHER)
-                                       val = cast_other(s,
-                                               PQgetlength(self->result, i, 
j), encoding,
-                                               PQftype(self->result, j), 
self->pgcnx->cast_hook);
-                               else if (type & PYGRES_TEXT)
-                                       val = cast_sized_text(s, 
PQgetlength(self->result, i, j),
-                                               encoding, type);
-                               else
-                                       val = cast_unsized_simple(s, type);
-                       }
+/* Return the next row as a dictionary */
+static PyObject *
+queryDictNext(queryObject *self, PyObject *noargs)
+{
+       PyObject        *dict;
+       int             j;
 
-                       if (!val)
-                       {
-                               Py_DECREF(reslist);
-                               Py_DECREF(rowtuple);
-                               reslist = NULL;
-                               goto exit;
-                       }
+       if (self->current_row>=self->max_row) {
+               PyErr_SetNone(PyExc_StopIteration);
+               return NULL;
+       }
 
-                       PyTuple_SET_ITEM(rowtuple, j, val);
+       if (self->current_row < 0) {
+               PyErr_SetNone(PyExc_StopIteration); // XXX
+               return NULL;
+       }
+
+       if (!(dict = PyDict_New()))
+               return NULL;
+
+       for (j = 0; j<self->num_fields; ++j)
+       {
+               PyObject *val;
+               if (NULL == (val = column_value(self, j)))
+               {
+                       Py_DECREF(dict);
+                       return NULL;
                }
 
-               PyList_SET_ITEM(reslist, i, rowtuple);
+               PyDict_SetItemString(dict, PQfname(self->result, j), val);
+               Py_DECREF(val);
        }
 
-exit:
-       PyMem_Free(col_types);
-
-       /* returns list */
-       return reslist;
+       ++self->current_row;
+       return dict;
 }
 
 /* retrieves last result as a list of dictionaries*/
@@ -4696,92 +4837,45 @@
 static char queryDictResult__doc__[] =
 "dictresult() -- Get the result of a query\n\n"
 "The result is returned as a list of rows, each one a dictionary with\n"
-"the field names used as the labels.\n";
+"the field names used as its keys.\n";
 
 static PyObject *
 queryDictResult(queryObject *self, PyObject *noargs)
 {
        PyObject   *reslist;
-       int                     i,
-                               m,
-                               n,
-                          *col_types;
-       int                     encoding = self->encoding;
+       int                     i;
 
        /* stores result in list */
-       m = PQntuples(self->result);
-       n = PQnfields(self->result);
-       if (!(reslist = PyList_New(m))) return NULL;
+       if (!(reslist = PyList_New(self->max_row))) return NULL;
 
-       if (!(col_types = get_col_types(self->result, n))) return NULL;
-
-       for (i = 0; i < m; ++i)
+       self->current_row=0;
+       for (i = 0; i < self->max_row; ++i)
        {
                PyObject   *dict;
-               int                     j;
-
-               if (!(dict = PyDict_New()))
-               {
+               if (NULL==(dict=queryDictNext(self, noargs))) {
                        Py_DECREF(reslist);
-                       reslist = NULL;
-                       goto exit;
+                       return NULL;
                }
-
-               for (j = 0; j < n; ++j)
-               {
-                       PyObject * val;
-
-                       if (PQgetisnull(self->result, i, j))
-                       {
-                               Py_INCREF(Py_None);
-                               val = Py_None;
-                       }
-                       else /* not null */
-                       {
-                               /* get the string representation of the value */
-                               /* note: this is always null-terminated text 
format */
-                               char   *s = PQgetvalue(self->result, i, j);
-                               /* get the PyGreSQL type of the column */
-                               int             type = col_types[j];
-
-                               if (type & PYGRES_ARRAY)
-                                       val = cast_array(s, 
PQgetlength(self->result, i, j),
-                                               encoding, type, NULL, 0);
-                               else if (type == PYGRES_BYTEA)
-                                       val = cast_bytea_text(s);
-                               else if (type == PYGRES_OTHER)
-                                       val = cast_other(s,
-                                               PQgetlength(self->result, i, 
j), encoding,
-                                               PQftype(self->result, j), 
self->pgcnx->cast_hook);
-                               else if (type & PYGRES_TEXT)
-                                       val = cast_sized_text(s, 
PQgetlength(self->result, i, j),
-                                               encoding, type);
-                               else
-                                       val = cast_unsized_simple(s, type);
-                       }
-
-                       if (!val)
-                       {
-                               Py_DECREF(dict);
-                               Py_DECREF(reslist);
-                               reslist = NULL;
-                               goto exit;
-                       }
-
-                       PyDict_SetItemString(dict, PQfname(self->result, j), 
val);
-                       Py_DECREF(val);
-               }
-
                PyList_SET_ITEM(reslist, i, dict);
        }
 
-exit:
-       PyMem_Free(col_types);
-
        /* returns list */
        return reslist;
 }
 
+/* retrieves next row as a dictionary */
+static char queryDictIter__doc__ [] =
+"dictIter() -- Return an iterator over query results\n\n"
+"The next row is returned as a dictionary with\n"
+"the field names used as its keys.\n";
+
+static PyObject *
+queryDictIter(queryObject *self, PyObject *noargs)
+{
+       self->current_row=0;
+       return PyObject_GetIter(queryDictResult(self, noargs));
+}
+
 /* retrieves last result as named tuples */
 static char queryNamedResult__doc__[] =
 "namedresult() -- Get the result of a query\n\n"
@@ -4791,21 +4885,23 @@
 static PyObject *
 queryNamedResult(queryObject *self, PyObject *noargs)
 {
-       PyObject   *ret;
-
        if (namedresult)
-       {
-               ret = PyObject_CallFunction(namedresult, "(O)", self);
-
-               if (ret == NULL)
-                       return NULL;
-               }
+               return PyObject_CallFunction(namedresult, "(O)", self);
        else
-       {
-               ret = queryGetResult(self, NULL);
-       }
+               return queryGetResult(self, NULL);
+}
 
-       return ret;
+
+/* retrieves next row as a named tuple (if supported) */
+static char queryNamedIter__doc__ [] =
+"namedIter() -- Return an iterator over query results as named tuples\n\n"
+"The next row is returned as a named tuple";
+
+static PyObject *
+queryNamedIter(queryObject *self, PyObject *noargs)
+{
+       self->current_row=0;
+       return PyObject_GetIter(queryDictResult(self, noargs));
 }
 
 /* gets notice object attributes */
@@ -4891,6 +4987,43 @@
        {NULL, NULL}
 };
 
+/* Return length of a query object */
+static Py_ssize_t
+queryLen(PyObject *self)
+{
+       PyObject *tmp;
+       tmp = PyLong_FromLong(((queryObject*)self)->max_row);
+       long len = PyLong_AsSsize_t(tmp);
+       Py_DECREF(tmp);
+       return len;
+}
+
+/* Return given item from a query object */
+static PyObject *
+queryGetItem(PyObject *self, Py_ssize_t i)
+{
+       queryObject     *q = (queryObject *)self;
+       PyObject        *tmp;
+       long            row;
+
+       tmp = PyLong_FromSize_t(i);
+       row = PyLong_AsLong(tmp);
+       Py_DECREF(tmp);
+
+       if (row<0 || row>=q->max_row) {
+               PyErr_SetNone(PyExc_IndexError);
+               return NULL;
+       }
+
+       q->current_row = row;
+       return query_row(q);
+}
+
+PySequenceMethods queryTypeSequenceMethods={
+       .sq_length = queryLen,
+       .sq_item = queryGetItem, // Is there a convenient typedef to cast this 
to?
+};
+
 /* notice type definition */
 static PyTypeObject noticeType = {
        PyVarObject_HEAD_INIT(NULL, 0)
@@ -4926,12 +5059,22 @@
 
 /* query object methods */
 static struct PyMethodDef queryMethods[] = {
+#if 0 // NOT USED
+       {"__len__", (PyCFunction) queryNTuples, METH_NOARGS, // XXX: doesn't 
work: TypeError: object of type 'pg.Query' has no len()
+                       queryNTuples__doc__},
+       {"move", (PyCFunction) queryMove, METH_VARARGS|METH_KEYWORDS,
+                       queryMove__doc__},
+#endif
        {"getresult", (PyCFunction) queryGetResult, METH_NOARGS,
                        queryGetResult__doc__},
        {"dictresult", (PyCFunction) queryDictResult, METH_NOARGS,
                        queryDictResult__doc__},
+       {"dictIter", (PyCFunction) queryDictIter, METH_NOARGS,
+                       queryDictIter__doc__},
        {"namedresult", (PyCFunction) queryNamedResult, METH_NOARGS,
                        queryNamedResult__doc__},
+       {"namedIter", (PyCFunction) queryNamedIter, METH_NOARGS,
+                       queryNamedIter__doc__},
        {"fieldname", (PyCFunction) queryFieldName, METH_VARARGS,
                         queryFieldName__doc__},
        {"fieldnum", (PyCFunction) queryFieldNumber, METH_VARARGS,
@@ -4957,7 +5100,7 @@
        0,                                                              /* 
tp_compare */
        0,                                                              /* 
tp_repr */
        0,                                                              /* 
tp_as_number */
-       0,                                                              /* 
tp_as_sequence */
+       &queryTypeSequenceMethods,              /* tp_as_sequence */
        0,                                                              /* 
tp_as_mapping */
        0,                                                              /* 
tp_hash */
        0,                                                              /* 
tp_call */
@@ -4965,17 +5108,18 @@
        PyObject_GenericGetAttr,                /* tp_getattro */
        0,                                                              /* 
tp_setattro */
        0,                                                              /* 
tp_as_buffer */
-       Py_TPFLAGS_DEFAULT,                             /* tp_flags */
+       Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_ITER,                        /* 
tp_flags */
        0,                                                              /* 
tp_doc */
        0,                                                              /* 
tp_traverse */
        0,                                                              /* 
tp_clear */
        0,                                                              /* 
tp_richcompare */
        0,                                                              /* 
tp_weaklistoffset */
-       0,                                                              /* 
tp_iter */
-       0,                                                              /* 
tp_iternext */
-       queryMethods,                                   /* tp_methods */
+       (getiterfunc)queryGetIter,                                      /* 
tp_iter */
+       (iternextfunc)queryNext,                                        /* 
tp_iternext */
+       queryMethods,                                                   /* 
tp_methods */
 };
 
+
 /* --------------------------------------------------------------------- */
 
 /* MODULE FUNCTIONS */
Index: tests/test_classic_connection.py
===================================================================
--- tests/test_classic_connection.py    (revision 930)
+++ tests/test_classic_connection.py    (working copy)
@@ -292,6 +292,161 @@
         self.assertEqual(r, s)
 
 
+class TestDictIteratorQueries(unittest.TestCase):
+    """Test cursor-like iterator around libpq results."""
+
+    def setUp(self):
+        self.c = connect()
+
+    def tearDown(self):
+        self.doCleanups()
+        self.c.close()
+
+    def testIterate(self):
+        r = self.c.query("SELECT i, i*i FROM generate_series(9, 22) 
i").dictIter()
+        for d in r:
+            k=int(d.keys()[0])
+            v=d.values()[0]
+            self.assertEqual(k*k, v)
+        for v in enumerate(r): # XXX: shouldn't happen
+            pass
+        self.assertEqual(20, i)
+# TODO: more tests
+
+class TestIteratorQueries(unittest.TestCase):
+    """Test cursor-like iterator around libpq results."""
+
+    def setUp(self):
+        self.c = connect()
+
+    def tearDown(self):
+        self.doCleanups()
+        self.c.close()
+
+    def testIterate(self):
+        r = self.c.query("SELECT generate_series(9,22) i")
+        for i, v in enumerate(r):
+            self.assertEqual(v[0], 9+i)
+        self.assertEqual(13, i)
+        for i, v in enumerate(r):
+            pass
+        # Check that we can iterate multiple times:
+        self.assertEqual(13, i) # enumerate starts at zero...
+        self.assertEqual(14, len(r))
+        self.assertEqual(14, len(r.getresult())) # XXX
+        self.assertEqual(14, len(r.dictresult()))
+        self.assertEqual(14, len(r.namedresult()))
+
+        #r.move(0, relative=False)
+        for i, v in enumerate(r):
+            self.assertEqual(v[0], 9+i)
+
+        # Test that iteration starts from scratch:
+        #r.move(-1) # Has no effect
+        for i, v in enumerate(r):
+            self.assertEqual(v[0], 9+i)
+
+    def testIteratePeek(self):
+        r = self.c.query("SELECT generate_series(9,22) i")
+        for i, v in enumerate(r):
+            self.assertEqual(v[0], 9+i)
+            break
+
+           # Peeked at the data, now test iteration from scratch...
+        for i, v in enumerate(r):
+            self.assertEqual(v[0], 9+i)
+
+    #def testIterateOverflow(self):
+        #r = self.c.query("SELECT * FROM generate_series(2,22) i")
+        #self.assertRaises(ValueError, r.move, 99)
+
+    #def testIterateOverflow2(self):
+        #r = self.c.query("SELECT * FROM generate_series(9,99) i")
+        #for i, v in enumerate(r):
+            #self.assertEqual(v[0], 9+i)
+        #self.assertRaises(ValueError, r.move, 0)
+
+    #def testIterateIntOverflow(self):
+        #r = self.c.query("SELECT * FROM generate_series(9,99) i")
+        #for i, v in enumerate(r):
+            #self.assertEqual(v[0], 9+i)
+       #self.assertRaisesRegexp(ValueError, 'Integer overflow', r.move, 
(1<<31)-1) # this depends on size of machine's "ints" ?
+
+    #def testIterateUnderflow(self):
+        #r = self.c.query("SELECT * FROM generate_series(9,99) i")
+        #self.assertRaises(ValueError, r.move, -1)
+
+class TestSequence(unittest.TestCase):
+    """Test sequence protocol: Query[a] ."""
+
+    def setUp(self):
+        self.c = connect()
+
+    def tearDown(self):
+        self.doCleanups()
+        self.c.close()
+
+    def testLen(self):
+        n = self.c.query("SELECT generate_series(9,22)")
+        # Test len without explicit fetch
+        self.assertEqual(14, len(n))
+        self.assertEqual(14, n.ntuples())
+
+    def testLen(self):
+        n = self.c.query("SELECT generate_series(9,22)", name='x')
+        # Test len without explicit fetch, with cursor
+        self.assertEqual(14, len(n))
+        self.assertEqual(14, n.ntuples())
+
+    def testBackward(self):
+        a = self.c.query("SELECT generate_series(9,22)")
+        for i in range(len(a)):
+                       self.assertEqual(22-i, a[len(a)-i-1][0])
+
+    def testArraysizeDivisor(self):
+        a = self.c.query("SELECT generate_series(9,22)", name='x', arraysize=7)
+        for i in range(len(a)):
+                       self.assertEqual(22-i, a[len(a)-i-1][0])
+
+    def testArraysizeNonDivisor(self):
+        a = self.c.query("SELECT generate_series(9,22)", name='x', arraysize=3)
+        for i in range(len(a)):
+                       self.assertEqual(22-i, a[len(a)-i-1][0])
+
+class TestCursorQueries(unittest.TestCase):
+    """Test named cursors around libpq results."""
+
+    def setUp(self):
+        self.c = connect()
+
+    def tearDown(self):
+        self.doCleanups()
+        self.c.close()
+
+    def testIterate(self):
+        for asize in [1, 1, 2, 3, 5, 8, 13, 20, 33,]:
+                       r = self.c.query('begin')
+                       r = self.c.query("SELECT generate_series(9,22)", 
name='x', arraysize=asize)
+                       #XXX: print 'one',r #XXX: prints just the first row??
+                       for i, v in enumerate(r):
+                               self.assertEqual(v[0], 9+i)
+                       self.assertEqual(13, i)
+                       for i, v in enumerate(r):
+                               pass
+                       # Check that we can iterate multiple times:
+                       self.assertEqual(13, i) # enumerate starts at zero...
+                       self.assertEqual(14, len(r.getresult()))
+                       self.assertEqual(14, len(r.dictresult()))
+                       self.assertEqual(14, len(r.namedresult()))
+
+                       for i, v in enumerate(r):
+                               self.assertEqual(v[0], 9+i)
+
+                       # Test that iteration starts from scratch:
+                       for i, v in enumerate(r):
+                               self.assertEqual(v[0], 9+i)
+                       r = self.c.query('rollback')
+
 class TestSimpleQueries(unittest.TestCase):
     """Test simple queries via a basic pg connection."""
 
@@ -1539,10 +1694,11 @@
             self.skipTest("database does not support English money")
         pg.set_decimal_point(None)
         try:
-            r = r.getresult()[0][0]
+            rr = r.getresult()[0][0]
         finally:
             pg.set_decimal_point(point)
-        self.assertIsInstance(r, str)
+        print 'r',rr,type(rr)
+        self.assertIsInstance(rr, str)
         self.assertIn(r, en_money)
         r = query(select_money)
         pg.set_decimal_point('')
_______________________________________________
PyGreSQL mailing list
[email protected]
https://mail.vex.net/mailman/listinfo.cgi/pygresql

Reply via email to