mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-02-07 12:50:32 +03:00
Test and document the named cursor stealing technique
This commit is contained in:
parent
dde4c0de3d
commit
37a9eb3615
|
@ -562,7 +562,7 @@ subsequently handled using :sql:`MOVE`, :sql:`FETCH` and :sql:`CLOSE` commands.
|
|||
|
||||
Psycopg wraps the database server side cursor in *named cursors*. A named
|
||||
cursor is created using the `~connection.cursor()` method specifying the
|
||||
`name` parameter. Such cursor will behave mostly like a regular cursor,
|
||||
*name* parameter. Such cursor will behave mostly like a regular cursor,
|
||||
allowing the user to move in the dataset using the `~cursor.scroll()`
|
||||
method and to read the data using `~cursor.fetchone()` and
|
||||
`~cursor.fetchmany()` methods.
|
||||
|
@ -588,6 +588,38 @@ will be eventually be closed. Also note that while :sql:`WITH HOLD` cursors
|
|||
lifetime extends well after `~connection.commit()`, calling
|
||||
`~connection.rollback()` will automatically close the cursor.
|
||||
|
||||
.. note::
|
||||
|
||||
It is also possible to use a named cursor to consume a cursor created
|
||||
in some other way than using the |DECLARE| executed by
|
||||
`~cursor.execute()`. For example, you may have a PL/pgSQL function
|
||||
returning a cursor::
|
||||
|
||||
CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$
|
||||
BEGIN
|
||||
OPEN $1 FOR SELECT col FROM test;
|
||||
RETURN $1;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
You can read the cursor content by calling the function with a regular,
|
||||
non-named, Psycopg cursor:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur1 = conn.cursor()
|
||||
cur1.callproc('reffunc', ['curname'])
|
||||
|
||||
and then use a named cursor in the same transaction to "steal the cursor":
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur2 = conn.cursor('curname')
|
||||
for record in cur2: # or cur2.fetchone, fetchmany...
|
||||
# do something with record
|
||||
pass
|
||||
|
||||
|
||||
.. |DECLARE| replace:: :sql:`DECLARE`
|
||||
.. _DECLARE: http://www.postgresql.org/docs/9.0/static/sql-declare.html
|
||||
|
||||
|
|
|
@ -258,6 +258,20 @@ class CursorTests(unittest.TestCase):
|
|||
self.assertEqual(c.precision, None)
|
||||
self.assertEqual(c.scale, None)
|
||||
|
||||
@skip_before_postgres(8, 0)
|
||||
def test_named_cursor_stealing(self):
|
||||
# you can use a named cursor to iterate on a refcursor created
|
||||
# somewhere else
|
||||
cur1 = self.conn.cursor()
|
||||
cur1.execute("DECLARE test CURSOR WITHOUT HOLD "
|
||||
" FOR SELECT generate_series(1,7)")
|
||||
|
||||
cur2 = self.conn.cursor('test')
|
||||
# can call fetch without execute
|
||||
self.assertEqual((1,), cur2.fetchone())
|
||||
self.assertEqual([(2,), (3,), (4,)], cur2.fetchmany(3))
|
||||
self.assertEqual([(5,), (6,), (7,)], cur2.fetchall())
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
|
Loading…
Reference in New Issue
Block a user