Skip to content

document CURSOR WITH HOLD #19590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions src/current/_includes/v25.2/known-limitations/sql-cursors.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,6 @@ CockroachDB implements SQL {% if page.name == "known-limitations.md" %} [cursor]
- `DECLARE` only supports forward cursors. Reverse cursors created with `DECLARE SCROLL` are not supported. [#77102](https://github.com/cockroachdb/cockroach/issues/77102)
- `FETCH` supports forward, relative, and absolute variants, but only for forward cursors. [#77102](https://github.com/cockroachdb/cockroach/issues/77102)
- `BINARY CURSOR`, which returns data in the Postgres binary format, is not supported. [#77099](https://github.com/cockroachdb/cockroach/issues/77099)
- `WITH HOLD`, which allows keeping a cursor open for longer than a transaction by writing its results into a buffer, is accepted as valid syntax within a single transaction but is not supported. It acts as a no-op and does not actually perform the function of `WITH HOLD`, which is to make the cursor live outside its parent transaction. Instead, if you are using `WITH HOLD`, you will be forced to close that cursor within the transaction it was created in. [#77101](https://github.com/cockroachdb/cockroach/issues/77101)
- This syntax is accepted (but does not have any effect):
{% include_cached copy-clipboard.html %}
~~~ sql
BEGIN;
DECLARE test_cur CURSOR WITH HOLD FOR SELECT * FROM foo ORDER BY bar;
CLOSE test_cur;
COMMIT;
~~~
- This syntax is not accepted, and will result in an error:
{% include_cached copy-clipboard.html %}
~~~ sql
BEGIN;
DECLARE test_cur CURSOR WITH HOLD FOR SELECT * FROM foo ORDER BY bar;
COMMIT; -- This will fail with an error because CLOSE test_cur was not called inside the transaction.
~~~
- Scrollable cursor (also known as reverse `FETCH`) is not supported. [#77102](https://github.com/cockroachdb/cockroach/issues/77102)
- [`SELECT ... FOR UPDATE`]({% link {{ page.version.version }}/select-for-update.md %}) with a cursor is not supported. [#77103](https://github.com/cockroachdb/cockroach/issues/77103)
- Respect for [`SAVEPOINT`s]({% link {{ page.version.version }}/savepoint.md %}) is not supported. Cursor definitions do not disappear properly if rolled back to a `SAVEPOINT` from before they were created. [#77104](https://github.com/cockroachdb/cockroach/issues/77104)
93 changes: 89 additions & 4 deletions src/current/v25.2/cursors.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Cursors differ from [keyset pagination]({% link {{ page.version.version }}/pagin
Cursors are declared and used with the following keywords:

- [`DECLARE`]({% link {{ page.version.version }}/sql-grammar.md %}#declare_cursor_stmt)
- [`WITH HOLD`](#use-a-holdable-cursor)
- [`FETCH`]({% link {{ page.version.version }}/sql-grammar.md %}#fetch_cursor_stmt)
- [`CLOSE`]({% link {{ page.version.version }}/sql-grammar.md %}#close_cursor_stmt)

Expand Down Expand Up @@ -75,6 +76,89 @@ CLOSE rides_cursor;
COMMIT;
~~~

### Use a holdable cursor

By default, a cursor closes when the transaction ends. The `WITH HOLD` clause defines a *holdable cursor*, which behaves as follows:

- A holdable cursor writes its results into a buffer, and stays open after a transaction commits.
- If a transaction aborts, all cursors opened within that transaction are also rolled back. However, holdable cursors from previously committed transactions remain open.
- A holdable cursor can be opened in both explicit and implicit transactions.
- If a holdable cursor results in an error as it is being persisted, it will cause the current transaction (implicit or explicit) to be rolled back.

Use `WITH HOLD` to access data across multiple transactions without redefining the cursor.

{{site.data.alerts.callout_info}}
The `WITHOUT HOLD` clause specifies the default non-holdable cursor behavior.
{{site.data.alerts.end}}

The following example uses a holdable cursor to return vehicles that are available for rides.

<div class="filters filters-big clearfix">
<button class="filter-button" data-scope="explicit">Explicit</button>
<button class="filter-button" data-scope="implicit">Implicit</button>
</div>

<section class="filter-content" markdown="1" data-scope="explicit">
Start a transaction:

{% include_cached copy-clipboard.html %}
~~~ sql
BEGIN;
~~~
</section>

Declare a cursor using `WITH HOLD` to keep it open after the transaction commits:

{% include_cached copy-clipboard.html %}
~~~ sql
DECLARE available_vehicles_cursor CURSOR WITH HOLD FOR
SELECT id, type, city, status FROM vehicles WHERE status = 'available';
~~~

Fetch the first two rows from the cursor:

{% include_cached copy-clipboard.html %}
~~~ sql
FETCH 2 FROM available_vehicles_cursor;
~~~

~~~
id | type | city | status
---------------------------------------+---------+-----------+------------
bbbbbbbb-bbbb-4800-8000-00000000000b | scooter | amsterdam | available
22222222-2222-4200-8000-000000000002 | scooter | boston | available
~~~

<section class="filter-content" markdown="1" data-scope="explicit">
Commit the transaction:

{% include_cached copy-clipboard.html %}
~~~ sql
COMMIT;
~~~
</section>

Continue fetching rows from the cursor:

{% include_cached copy-clipboard.html %}
~~~ sql
FETCH 2 FROM available_vehicles_cursor;
~~~

~~~
id | type | city | status
---------------------------------------+------------+---------+------------
33333333-3333-4400-8000-000000000003 | bike | boston | available
55555555-5555-4400-8000-000000000005 | skateboard | seattle | available
~~~

Close the cursor:

{% include_cached copy-clipboard.html %}
~~~ sql
CLOSE available_vehicles_cursor;
~~~

### View all open cursors

{% include_cached copy-clipboard.html %}
Expand All @@ -83,10 +167,11 @@ SELECT * FROM pg_cursors;
~~~

~~~
name | statement | is_holdable | is_binary | is_scrollable | creation_time
---------------+---------------------+-------------+-----------+---------------+--------------------------------
rides_cursor | SELECT * FROM rides | f | f | f | 2023-03-30 15:24:37.568054+00
(1 row)
name | statement | is_holdable | is_binary | is_scrollable | creation_time
----------------------------+------------------------------------------------------------------------+-------------+-----------+---------------+--------------------------------
rides_cursor | SELECT * FROM movr.rides | f | f | f | 2025-05-07 21:12:53.32978+00
available_vehicles_cursor | SELECT id, type, city, status FROM vehicles WHERE status = 'available' | t | f | f | 2025-05-07 21:12:59.605647+00
(2 rows)
~~~

## Known limitations
Expand Down
Loading