summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--lib/extras.py9
-rwxr-xr-xtests/extras_dictcursor.py19
3 files changed, 27 insertions, 2 deletions
diff --git a/NEWS b/NEWS
index 60c7d4b..0037c3b 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,7 @@ What's new in psycopg 2.4.1
- Correctly detect an empty query sent to the backend (ticket #46).
- Fixed a SystemError clobbering libpq errors raised without SQLSTATE.
Bug vivisectioned by Eric Snow.
+ - Fixed interaction between NamedTuple and server-side cursors.
- Allow to specify --static-libpq on setup.py command line instead of
just in 'setup.cfg'. Patch provided by Matthew Ryan (ticket #48).
diff --git a/lib/extras.py b/lib/extras.py
index dcbd65e..1a4b730 100644
--- a/lib/extras.py
+++ b/lib/extras.py
@@ -304,7 +304,14 @@ class NamedTupleCursor(_cursor):
return [nt(*t) for t in ts]
def __iter__(self):
- return iter(self.fetchall())
+ # Invoking _cursor.__iter__(self) goes to infinite recursion,
+ # so we do pagination by hand
+ while 1:
+ recs = self.fetchmany(self.itersize)
+ if not recs:
+ return
+ for rec in recs:
+ yield rec
try:
from collections import namedtuple
diff --git a/tests/extras_dictcursor.py b/tests/extras_dictcursor.py
index a92968b..494eca8 100755
--- a/tests/extras_dictcursor.py
+++ b/tests/extras_dictcursor.py
@@ -14,9 +14,11 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
+import time
+from datetime import timedelta
import psycopg2
import psycopg2.extras
-from testutils import unittest, skip_if_no_namedtuple
+from testutils import unittest, skip_before_postgres, skip_if_no_namedtuple
from testconfig import dsn
@@ -293,6 +295,21 @@ class NamedTupleCursorTest(unittest.TestCase):
recs = curs.fetchall()
self.assertEqual(recs[0].i, 42)
+ @skip_if_no_namedtuple
+ @skip_before_postgres(8, 0)
+ def test_not_greedy(self):
+ curs = self.conn.cursor('tmp')
+ curs.itersize = 2
+ curs.execute("""select clock_timestamp() as ts from generate_series(1,3)""")
+ recs = []
+ for t in curs:
+ time.sleep(0.01)
+ recs.append(t)
+
+ # check that the dataset was not fetched in a single gulp
+ self.assert_(recs[1].ts - recs[0].ts < timedelta(seconds=0.005))
+ self.assert_(recs[2].ts - recs[1].ts > timedelta(seconds=0.0099))
+
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)