WITH HOLD documentation a argument parsing changes

Now any true value will do for the withhold parameter.
This commit is contained in:
Federico Di Gregorio 2011-08-10 19:21:12 +02:00
parent a59d88c703
commit 880aa07a58
5 changed files with 34 additions and 12 deletions

View File

@ -21,13 +21,16 @@ The ``connection`` class
Connections are thread safe and can be shared among many thread. See Connections are thread safe and can be shared among many thread. See
:ref:`thread-safety` for details. :ref:`thread-safety` for details.
.. method:: cursor([name] [, cursor_factory]) .. method:: cursor([name] [, cursor_factory] [, withhold])
Return a new `cursor` object using the connection. Return a new `cursor` object using the connection.
If *name* is specified, the returned cursor will be a :ref:`server If *name* is specified, the returned cursor will be a :ref:`server
side cursor <server-side-cursors>` (also known as *named cursor*). side cursor <server-side-cursors>` (also known as *named cursor*).
Otherwise it will be a regular *client side* cursor. Otherwise it will be a regular *client side* cursor. By default a
:sql:`WITHOUT HOLD` cursor is created; to create a :sql:`WITH HOLD`
cursor, pass a `!True` value as the *withhold* parameter. See
:ref:`server-side-cursors`.
The name can be a string not valid as a PostgreSQL identifier: for The name can be a string not valid as a PostgreSQL identifier: for
example it may start with a digit and contain non-alphanumeric example it may start with a digit and contain non-alphanumeric

View File

@ -114,6 +114,19 @@ The ``cursor`` class
The `name` attribute is a Psycopg extension to the |DBAPI|. The `name` attribute is a Psycopg extension to the |DBAPI|.
.. attribute:: withhold
Read/write attribute: specifies if a named cursor lifetime should
extend outside of the current transaction, i.e., it is possible to
fetch from the cursor even after a `commection.commit()` (but not after
a `connection.rollback()`). See :ref:`server-side-cursors`
.. versionadded:: 2.4.3
.. extension::
The `name` attribute is a Psycopg extension to the |DBAPI|.
.. |execute*| replace:: `execute*()` .. |execute*| replace:: `execute*()`

View File

@ -575,6 +575,19 @@ during the iteration: the default value of 2000 allows to fetch about 100KB
per roundtrip assuming records of 10-20 columns of mixed number and strings; per roundtrip assuming records of 10-20 columns of mixed number and strings;
you may decrease this value if you are dealing with huge records. you may decrease this value if you are dealing with huge records.
Named cursors are usually created :sql:`WITHOUT HOLD`, meaning they live only
as long as the current transaction. Trying to fetch from a named cursor after
a `~connection.commit()` or to create a named cursor when the `connection`
transaction isolation level is set to `AUTOCOMMIT` will result in an exception.
It is possible to create a :sql:`WITH HOLD` cursor by specifying a `!True`
value for the `withhold` parameter to `~connection.cursor()` or by setting the
`~cursor.withhold` attribute to `!True` before calling `~cursor.execute()` on
the cursor. It is extremely important to always `~cursor.close()` such cursors,
otherwise they will continue to hold server-side resources until the connection
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.
.. |DECLARE| replace:: :sql:`DECLARE` .. |DECLARE| replace:: :sql:`DECLARE`
.. _DECLARE: http://www.postgresql.org/docs/9.0/static/sql-declare.html .. _DECLARE: http://www.postgresql.org/docs/9.0/static/sql-declare.html

View File

@ -42,7 +42,7 @@
/* cursor method - allocate a new cursor */ /* cursor method - allocate a new cursor */
#define psyco_conn_cursor_doc \ #define psyco_conn_cursor_doc \
"cursor(cursor_factory=extensions.cursor) -- new cursor\n\n" \ "cursor(name=None, cursor_factory=extensions.cursor, withhold=None) -- new cursor\n\n" \
"Return a new cursor.\n\nThe ``cursor_factory`` argument can be used to\n" \ "Return a new cursor.\n\nThe ``cursor_factory`` argument can be used to\n" \
"create non-standard cursors by passing a class different from the\n" \ "create non-standard cursors by passing a class different from the\n" \
"default. Note that the new class *should* be a sub-class of\n" \ "default. Note that the new class *should* be a sub-class of\n" \
@ -63,16 +63,11 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds)
} }
if (withhold != NULL) { if (withhold != NULL) {
if (withhold == Py_True && name == NULL) { if (PyObject_IsTrue(withhold) && name == NULL) {
PyErr_SetString(ProgrammingError, PyErr_SetString(ProgrammingError,
"'withhold=True can be specified only for named cursors"); "'withhold=True can be specified only for named cursors");
return NULL; return NULL;
} }
if (withhold != NULL && withhold != Py_True && withhold != Py_False) {
PyErr_SetString(ProgrammingError,
"'withhold should be True or False");
return NULL;
}
} }
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
@ -109,7 +104,7 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds)
return NULL; return NULL;
} }
if (withhold == Py_True) if (withhold != NULL && PyObject_IsTrue(withhold))
((cursorObject*)obj)->withhold = 1; ((cursorObject*)obj)->withhold = 1;
Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = " Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = "

View File

@ -161,8 +161,6 @@ class CursorTests(unittest.TestCase):
def test_withhold(self): def test_withhold(self):
self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor, self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor,
withhold=True) withhold=True)
self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor,
"NAME", withhold="")
curs = self.conn.cursor() curs = self.conn.cursor()
curs.execute("drop table if exists withhold") curs.execute("drop table if exists withhold")