Project: YugabyteDB (App Definition and Development / Database)
File: dml_declare.md

title: DECLARE statement [YSQL] headerTitle: DECLARE linkTitle: DECLARE description: Use the DECLARE statement to create a cursor. (There's no notion of "opening" a cursor in top-level SQL.) menu: v2.18: identifier: dml_declare parent: statements type: docs {{< warning title="YSQL currently supports only fetching rows from a cursor consecutively in the forward direction." >}} See the subsection in the generic section . {{< /warning >}} Use the `DECLARE` statement to create a . The `DECLARE` statement is used jointly with the , , and statements. The term cursor is both a SQL keyword that's used in the `DECLARE` statement (but nowhere else) and a term of art that denotes the implementation phenomenon that handles everything to do with executing a `SELECT`, `VALUES`, `INSERT`, `UPDATE`, or `DELETE` statementfor example, representing its execution plan and, for a `SELECT` or `VALUES` statement, or a DML statement that uses the `RETURNING` keyword, representing the current, next-to-be-fetched row. Some tools (most notably psql and therefore ysqlsh (and, for that matter, the ) allow you to issue `SELECT` and DML statements without yourself doing the cursor management. But, if the use case calls for it, you can do the cursor management explicitly using the SQL API that the `DECLARE`, , , and statements jointly expose. There's also a PL/pgSQL API for explicit cursor management. See the generic section . {{%ebnf%}} declare {{%/ebnf%}} `DECLARE` creates a cursor. (There's no notion of so-called "opening" a cursor in top-level SQL.) A cursor's duration is limited to the duration of the session that declares it. (The statement drops a cursor so that you can shorten its lifetime if you want totypically in order to save resources.) Notice the critical maximum lifetime difference between a holdable, and a non-holdable, cursor. The pgcursors catalog view lists all the currently existing cursors_ in the current session. A cursor is identified only by an unqualified name and is visible only in the session that declares it. This determines the uniqueness scope for its name. (The name of a cursor is like that of a prepared statement in this respect.) Specifies that the cursor returns data in binary rather than in text format. Usually, a cursor is specified to return data in text format, the same as a `SELECT` statement would produce. This binary format reduces conversion effort for both the server and client at the cost of more programmer effort to deal with platform-dependent binary data formats. For example, if a query returns a value of one from an integer column, you would get the string value 1 with the default choice. But with a binary cursor you would get a 4-byte field containing the internal representation of the value (in big-endian byte order). Use binary cursors carefully. Many applications, including ysqlsh (and psql) are not prepared to handle binary cursors and expect data to come back in the text format. {{< note title="BINARY cursors and the 'extended' query protocol" >}} When the client application uses the extended" query protocol to issue a `FETCH` statement, the bind protocol message specifies whether data is to be retrieved in text or binary format. This choice overrides the way that the cursor is

It therefore isn't useful to declare a cursor explicitly as `BINARY` because any cursor can be treated as either text or binary when you use the extended query protocol. {{< /note >}} This indicates that data retrieved from the cursor should be unaffected by updates to the table(s) underlying the cursor that occur after the cursor has been declared. In PostgreSQL, and therefore in YSQL, this is the only behavior. This key word therefore has no effect and is accepted only for compatibility with the SQL standard. `SCROLL` specifies that you can use the flavors of `FETCH` and `MOVE` to access the current row and rows in the cursor's result set that lie before the current row (i.e. including and before the row that `FETCH RELATIVE 0` accesses). In simple cases, the execution plan is intrinsically reversible: it allows backwards fetching just as easily as it allows forwards fetching. But not all execution plans are reversible; and when a plan is not reversible, specifying `SCROLL` implies creating a cache of the rows that the cursor's subquery defines at the moment that the first `MOVE` or `FETCH` statement is executed (or on demand as new rows are accessed). This implies both a performance cost and a resource consumption cost. `NO SCROLL` specifies that the cursor cannot be used to retrieve the current row or rows that lie before it. When you specify neither `SCROLL` nor `NO SCROLL`, then allow scrolling is allowed in only some casesand this is therefore different from specifying `SCROLL` explicitly. {{< tip title="Always specify either SCROLL or NO SCROLL explicitly" >}} See the in the subsection on the dedicated page. Choose the mode that you want explicitly to honor the requirements that you must meet. Notice that while remains open, your only viable choice is `NO SCROLL`. {{< /tip >}} `WITHOUT HOLD` specifies that the cursor cannot be used after the transaction that created it ends (even if it ends with a successful commit). `WITH HOLD` specifies that the cursor can continue to be used after the transaction that created it successfully commits. (Of course, it vanishes if the transaction that created it rolls back.) Specifying neither `WITHOUT HOLD` nor `WITH HOLD` is the same as specifying `WITHOUT HOLD`. ```plpgsql drop table if exists t cascade; create table t(k, v) as select g.val, g.val*100 from generate_series(1, 22) as g(val); start transaction; declare cur scroll cursor without hold for select k, v from t where (k <> all (array[1, 3, 5, 7, 11, 13, 17, 19])) order by k; select statement, is_holdable::text, is_scrollable::text from pg_cursors where name = 'cur' and not is_binary and creationtime < (transactiontimestamp() + make_interval(secs=>0.05)); fetch all from cur; close cur; rollback; ``` This is the result from "select... from pgcursors..."_: ```output statement | isholdable | isscrollable --+-+ declare cur scroll cursor without hold for +| false | true select k, v +| | from t +| | where (k <> all (array[1, 3, 5, 7, 11, 13, 17, 19]))+| | order by k; | | ``` And this is the result from `FETCH ALL`: ```output k | v -+ 2 | 200 4 | 400 6 | 600 8 | 800 9 | 900 10 | 1000 12 | 1200 14 | 1400 15 | 1500 16 | 1600 18 | 1800 20 | 2000 21 | 2100 22 | 2200 ```