to a PostgreSQL database session. They are normally created by the
connection's `~Connection.cursor()` method.
+Using the *name* parameter on `!cursor()` will create a `ServerCursor` or
+`AsyncServerCursor`, which can be used to retrieve partial results from a
+database.
+
A `Connection` can create several cursors, but only one at time can perform
operations, so they are not the best way to achieve parallelism (you may want
to operate with several connections instead). All the cursors on the same
.. autoclass:: Cursor()
- This class implements `DBAPI-compliant interface`__. It is what the
+ This class implements a `DBAPI-compliant interface`__. It is what the
classic `Connection.cursor()` method returns. `AsyncConnection.cursor()`
will create instead `AsyncCursor` objects, which have the same set of
method but expose an `asyncio` interface and require ``async`` and
``await`` keywords to operate.
- .. __: https://www.python.org/dev/peps/pep-0249/#cursor-objects
+ .. __: dbapi-cursor_
Cursors behave as context managers: on block exit they are closed and
further operation will not be possible. Closing a cursor will not
.. rubric:: Methods to send commands
- .. automethod:: execute(query, params=None, prepare=None) -> Cursor
+ .. automethod:: execute(query, params=None, *, prepare=None) -> Cursor
:param query: The query to execute.
:type query: `!str`, `!bytes`, or `sql.Composable`
The parameters are adapted to PostgreSQL format.
+The `!ServerCursor` class
+--------------------------
+
+.. autoclass:: ServerCursor()
+
+ This class also implements a `DBAPI-compliant interface`__. It is created
+ by `Connection.cursor()` specifying the *name* parameter. Using this
+ object results in the creation of an equivalent PostgreSQL cursor in the
+ server. DBAPI-extension methods (such as `~Cursor.copy()` or
+ `~Cursor.stream()`) are not implemented on this object: use a normal
+ `Cursor` instead.
+
+ .. __: dbapi-cursor_
+
+ Most attribute and methods behave exactly like in `Cursor`, here are
+ documented the differences:
+
+ .. autoattribute:: name
+ :annotation: str
+
+ .. automethod:: close
+
+ .. warning:: Closing a server-side cursor is more important than
+ closing a client-side one because it also releases the resources
+ on the server, which otherwise might remain allocated until the
+ end of the session (memory, locks). Using the `with conn.cursor():
+ ...` pattern is especially useful so that the cursor is closed at
+ the end of the block.
+
+ .. automethod:: execute(query, params=None, *, scrollable=None, hold=False) -> ServerCursor
+
+ :param query: The query to execute.
+ :type query: `!str`, `!bytes`, or `sql.Composable`
+ :param params: The parameters to pass to the query, if any.
+ :type params: Sequence or Mapping
+ :param scrollable: if `!True` make the cursor scrollable, if `!False`
+ not. if `!None` leave the choice to the server.
+ :type scrollable: `!Optional[bool]`
+ :param hold: if `!True` allow the cursor to be used after the
+ transaction cretaing it has committed.
+ :type hold: `!bool`
+
+ Create a server cursor with given `name` and the *query* in argument.
+ If using :sql:`DECLARE` is not appropriate you can avoid to use
+ `!execute()`, crete the cursor in other ways, and use directly the
+ `!fetch*()` methods instead. See :ref:`cursor-steal` for an example.
+
+ Using `!execute()` more than once will close the previous cursor and
+ open a new one with the same name.
+
+ .. seealso:: The PostgreSQL DECLARE_ statement documetation describe
+ in details all the parameters.
+
+ .. _DECLARE: https://www.postgresql.org/docs/current/sql-declare.html
+
+ .. automethod:: executemany(query: Query, params_seq: Sequence[Args])
+
+ .. automethod:: fetchone
+ .. automethod:: fetchmany
+ .. automethod:: fetchall
+
+ These methods use the FETCH_ SQL statement to retrieve some of the
+ records from the cursor's current position.
+
+ .. _FETCH: https://www.postgresql.org/docs/current/sql-fetch.html
+
+ .. note:: You can also iterate on the cursor to read its result one at
+ time with `for record in cur: ...`. In this case, the records are
+ not fetched one at time from the server but they are retrieved in
+ batches of `itersize` to reduce the number of server roundtrips.
+
+ .. autoattribute:: itersize
+ :annotation: int
+
+ Number of records to fetch at time when iterating on the cursor. The
+ default is 100.
+
+ .. automethod:: scroll
+
+ These method uses the MOVE_ SQL statement to move the current position
+ in the server-side cursor, which will affect following `!fetch*()`
+ operations. If you need to scroll backwards you should probably
+ use `scrollable=True` in `execute()`.
+
+ Note that PostgreSQL doesn't provide a reliable way to report when a
+ cursor moves out of bound, so the method might not raise `!IndexError`
+ when it happens, but it might rather stop at the cursor boundary.
+
+ .. _MOVE: https://www.postgresql.org/docs/current/sql-fetch.html
+
+
The `!AsyncCursor` class
------------------------
.. note:: You can use ``async with conn.cursor(): ...`` to close the
cursor automatically when the block is exited.
- .. automethod:: execute(query, params=None, prepare=None) -> AsyncCursor
+ .. automethod:: execute(query, params=None, *, prepare=None) -> AsyncCursor
.. automethod:: executemany(query: Query, params_seq: Sequence[Args])
.. automethod:: copy(statement: Query) -> AsyncCopy
on the async cursor results.
-Cursor support objects
-----------------------
+The `!AsyncServerCursor` class
+------------------------------
+
+.. autoclass:: AsyncServerCursor()
+
+ This class implements a DBAPI-inspired interface as the `AsyncCursor`
+ does, but wraps a server-side cursor like the `ServerCursor` class. It is
+ created by `AsyncConnection.cursor()` specifying the *name* parameter.
+
+ The following are the methods exposing a different (async) interface from
+ the `ServerCursor` counterpart, but sharing the same semantics.
+
+ .. automethod:: close
+
+ .. note:: You can close the cursor automatically using :samp:`async
+ with conn.cursor({name}): ...`
+
+ .. automethod:: execute(query, params=None, *, scrollable=None, hold=False) -> AsyncServerCursor
+ .. automethod:: executemany(query: Query, params_seq: Sequence[Args])
+ .. automethod:: fetchone
+ .. automethod:: fetchmany
+ .. automethod:: fetchall
+
+ .. note:: You can also iterate on the cursor using `async for record
+ in cur: ...`.
+
+ .. automethod:: scroll
+
+
+The description `Column` object
+-------------------------------
.. autoclass:: Column()
.. autoproperty:: scale
+COPY-related objects
+--------------------
+
.. autoclass:: Copy()
The object is normally returned by ``with`` `Cursor.copy()`.
Use it as `async for record in copy.rows():` ...
.. automethod:: read_row
+
+
+.. _dbapi-cursor: https://www.python.org/dev/peps/pep-0249/#cursor-objects
--- /dev/null
+.. currentmodule:: psycopg3
+
+.. index::
+ single: Cursor
+
+.. _cursor-types:
+
+Cursor types
+============
+
+`!psycopg3` can manage kinds of "cursors" which differ in where the state of a
+query being processed is stored: :ref:`client-side-cursors` and
+:ref:`server-side-cursors`.
+
+.. index::
+ double: Cursor; Client-side
+
+.. _client-side-cursors:
+
+Client-side cursors
+-------------------
+
+Client-side cursors are what `!psycopg3` uses in its normal querying process.
+They are implemented by the `Cursor` and `AsyncCursor` classes. In such
+querying pattern, after a cursor sends a query to the server (usually calling
+`~Cursor.execute()`), the server replies transferring to the client the whole
+set of results requested, which is stored in the state of the same cursor and
+from where it can be read from Python code (using methods such as
+`~Cursor.fetchone()` and siblings).
+
+This querying process is very scalable because, after a query result has been
+transmitted to the client, the server doesn't keep any state. Because the
+results are already in the client memory, iterating its rows is very quick.
+
+The downside of this querying method is that the entire result has to be
+transmitted completely to the client (with a time proportional to its size)
+and the client needs enough memory to hold it, so it is only suitable for
+reasonably small result sets.
+
+
+.. index::
+ double: Cursor; Server-side
+ single: Portal
+ double: Cursor; Named
+
+.. _server-side-cursors:
+
+Server-side cursors
+-------------------
+
+PostgreSQL has also its own concept of *cursor* (sometimes also called
+*portal*). When a database cursor is created, the query is not necessarily
+completely processed: the server might be able to produce results only as they
+are needed. Only the results requested are transmitted to the client: if the
+query result is very large but the client only needs the first few records it
+is possible to transmit only them.
+
+The downside is that the server needs to keep track of the partially
+processed results, so it uses more memory and resources on the server.
+
+`!psycopg3` allows the use of server-side cursors using the classes
+`ServerCursor` and `AsyncServerCursor`. They are usually created by passing
+the *name* parameter to the `~Connection.cursor()` method (in `!psycopg2` they
+are also called *named cursors*). The use of these classes is similar to their
+client-side counterparts: their interface is the same, but behind the scene
+they send commands to control the state of the cursor in the server (for
+instance when fetching new records or when moving using `~Cursor.scroll()`).
+
+Using a server-side cursor it is possible to process datasets larger than what
+would fit in the client memory. However for small queries they are less
+efficient because it takes more commands to receive their result, so you
+should use them only if you need to process huge results or if only a partial
+result is needed.
+
+.. seealso::
+
+ Server-side cursors are created and managed by `ServerCursor` using SQL
+ commands such as DECLARE_, FETCH_, MOVE_. The PostgreSQL documentation
+ gives a good idea of what is possible to do with them.
+
+ .. _DECLARE: https://www.postgresql.org/docs/current/sql-declare.html
+ .. _FETCH: https://www.postgresql.org/docs/current/sql-fetch.html
+ .. _MOVE: https://www.postgresql.org/docs/current/sql-move.html
+
+
+.. _cursor-steal:
+
+"Stealing" an existing cursor
+-----------------------------
+
+A `!psycopg3` `ServerCursor` can be also used to consume a cursor that the
+:sql:`DECLARE` generated by its `~ServerCursor.execute()` method can produce.
+
+For instance if you have a PL/pgSQL function creating a cursor:
+
+.. code:: postgres
+
+ CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$
+ BEGIN
+ OPEN $1 FOR SELECT col FROM test;
+ RETURN $1;
+ END;
+ $$ LANGUAGE plpgsql;
+
+you can run a one-off command (e.g. using `Connection.execute()`) to call it
+and create the server cursor:
+
+.. code:: python
+
+ conn.execute("select reffunc('curname')")
+
+then create a server-side cursor with the same name and call directly the
+fetch methods, omitting to call `~ServerCursor.execute()` before:
+
+.. code:: python
+
+ cur = conn.cursor('curname')
+ # no cur.execute()
+ for record in cur: # or cur.fetchone(), cur.fetchmany()...
+ # do something with record