Skip to content

Commit 8a58d13

Browse files
authored
document CURSOR WITH HOLD (#19590)
* document CURSOR WITH HOLD
1 parent 59ffbc0 commit 8a58d13

File tree

2 files changed

+89
-20
lines changed

2 files changed

+89
-20
lines changed

src/current/_includes/v25.2/known-limitations/sql-cursors.md

-16
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,6 @@ CockroachDB implements SQL {% if page.name == "known-limitations.md" %} [cursor]
33
- `DECLARE` only supports forward cursors. Reverse cursors created with `DECLARE SCROLL` are not supported. [#77102](https://github.com/cockroachdb/cockroach/issues/77102)
44
- `FETCH` supports forward, relative, and absolute variants, but only for forward cursors. [#77102](https://github.com/cockroachdb/cockroach/issues/77102)
55
- `BINARY CURSOR`, which returns data in the Postgres binary format, is not supported. [#77099](https://github.com/cockroachdb/cockroach/issues/77099)
6-
- `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)
7-
- This syntax is accepted (but does not have any effect):
8-
{% include_cached copy-clipboard.html %}
9-
~~~ sql
10-
BEGIN;
11-
DECLARE test_cur CURSOR WITH HOLD FOR SELECT * FROM foo ORDER BY bar;
12-
CLOSE test_cur;
13-
COMMIT;
14-
~~~
15-
- This syntax is not accepted, and will result in an error:
16-
{% include_cached copy-clipboard.html %}
17-
~~~ sql
18-
BEGIN;
19-
DECLARE test_cur CURSOR WITH HOLD FOR SELECT * FROM foo ORDER BY bar;
20-
COMMIT; -- This will fail with an error because CLOSE test_cur was not called inside the transaction.
21-
~~~
226
- Scrollable cursor (also known as reverse `FETCH`) is not supported. [#77102](https://github.com/cockroachdb/cockroach/issues/77102)
237
- [`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)
248
- 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)

src/current/v25.2/cursors.md

+89-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Cursors differ from [keyset pagination]({% link {{ page.version.version }}/pagin
2121
Cursors are declared and used with the following keywords:
2222

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

@@ -75,6 +76,89 @@ CLOSE rides_cursor;
7576
COMMIT;
7677
~~~
7778

79+
### Use a holdable cursor
80+
81+
By default, a cursor closes when the transaction ends. The `WITH HOLD` clause defines a *holdable cursor*, which behaves as follows:
82+
83+
- A holdable cursor writes its results into a buffer, and stays open after a transaction commits.
84+
- If a transaction aborts, all cursors opened within that transaction are also rolled back. However, holdable cursors from previously committed transactions remain open.
85+
- A holdable cursor can be opened in both explicit and implicit transactions.
86+
- 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.
87+
88+
Use `WITH HOLD` to access data across multiple transactions without redefining the cursor.
89+
90+
{{site.data.alerts.callout_info}}
91+
The `WITHOUT HOLD` clause specifies the default non-holdable cursor behavior.
92+
{{site.data.alerts.end}}
93+
94+
The following example uses a holdable cursor to return vehicles that are available for rides.
95+
96+
<div class="filters filters-big clearfix">
97+
<button class="filter-button" data-scope="explicit">Explicit</button>
98+
<button class="filter-button" data-scope="implicit">Implicit</button>
99+
</div>
100+
101+
<section class="filter-content" markdown="1" data-scope="explicit">
102+
Start a transaction:
103+
104+
{% include_cached copy-clipboard.html %}
105+
~~~ sql
106+
BEGIN;
107+
~~~
108+
</section>
109+
110+
Declare a cursor using `WITH HOLD` to keep it open after the transaction commits:
111+
112+
{% include_cached copy-clipboard.html %}
113+
~~~ sql
114+
DECLARE available_vehicles_cursor CURSOR WITH HOLD FOR
115+
SELECT id, type, city, status FROM vehicles WHERE status = 'available';
116+
~~~
117+
118+
Fetch the first two rows from the cursor:
119+
120+
{% include_cached copy-clipboard.html %}
121+
~~~ sql
122+
FETCH 2 FROM available_vehicles_cursor;
123+
~~~
124+
125+
~~~
126+
id | type | city | status
127+
---------------------------------------+---------+-----------+------------
128+
bbbbbbbb-bbbb-4800-8000-00000000000b | scooter | amsterdam | available
129+
22222222-2222-4200-8000-000000000002 | scooter | boston | available
130+
~~~
131+
132+
<section class="filter-content" markdown="1" data-scope="explicit">
133+
Commit the transaction:
134+
135+
{% include_cached copy-clipboard.html %}
136+
~~~ sql
137+
COMMIT;
138+
~~~
139+
</section>
140+
141+
Continue fetching rows from the cursor:
142+
143+
{% include_cached copy-clipboard.html %}
144+
~~~ sql
145+
FETCH 2 FROM available_vehicles_cursor;
146+
~~~
147+
148+
~~~
149+
id | type | city | status
150+
---------------------------------------+------------+---------+------------
151+
33333333-3333-4400-8000-000000000003 | bike | boston | available
152+
55555555-5555-4400-8000-000000000005 | skateboard | seattle | available
153+
~~~
154+
155+
Close the cursor:
156+
157+
{% include_cached copy-clipboard.html %}
158+
~~~ sql
159+
CLOSE available_vehicles_cursor;
160+
~~~
161+
78162
### View all open cursors
79163

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

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

92177
## Known limitations

0 commit comments

Comments
 (0)