0001-prevent-rewinding-NO-SCROLL-cursor.patch
text/x-diff
Filename: 0001-prevent-rewinding-NO-SCROLL-cursor.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
Series: patch 0001
| File | + | − |
|---|---|---|
| src/backend/tcop/pquery.c | 24 | 3 |
| src/test/regress/expected/portals.out | 18 | 0 |
| src/test/regress/expected/tablesample.out | 1 | 1 |
| src/test/regress/sql/portals.sql | 12 | 0 |
| src/test/regress/sql/tablesample.sql | 1 | 1 |
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index a3c27d9d74..a9e1056057 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -1472,9 +1472,8 @@ PortalRunFetch(Portal portal,
* DoPortalRunFetch
* Guts of PortalRunFetch --- the portal context is already set up
*
- * count <= 0 is interpreted as a no-op: the destination gets started up
- * and shut down, but nothing else happens. Also, count == FETCH_ALL is
- * interpreted as "all rows". (cf FetchStmt.howMany)
+ * Here, count < 0 typically reverses the direction. Also, count == FETCH_ALL
+ * is interpreted as "all rows". (cf FetchStmt.howMany)
*
* Returns number of rows processed (suitable for use in result tag)
*/
@@ -1491,6 +1490,15 @@ DoPortalRunFetch(Portal portal,
portal->strategy == PORTAL_ONE_MOD_WITH ||
portal->strategy == PORTAL_UTIL_SELECT);
+ /*
+ * Note: we disallow backwards fetch (including re-fetch of current row)
+ * for NO SCROLL cursors, but we interpret that very loosely: you can use
+ * any of the FetchDirection options, so long as the end result is to move
+ * forwards by at least one row. Currently it's sufficient to check for
+ * NO SCROLL in DoPortalRewind() and in the forward == false path in
+ * PortalRunSelect(); but someday we might prefer to account for that
+ * restriction explicitly here.
+ */
switch (fdirection)
{
case FETCH_FORWARD:
@@ -1668,6 +1676,19 @@ DoPortalRewind(Portal portal)
{
QueryDesc *queryDesc;
+ /*
+ * No work is needed if we've not advanced the cursor (and we don't want
+ * to throw a NO SCROLL error in this case).
+ */
+ if (portal->atStart && !portal->atEnd)
+ return;
+
+ if (portal->cursorOptions & CURSOR_OPT_NO_SCROLL)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cursor can only scan forward"),
+ errhint("Declare it with SCROLL option to enable backward scan.")));
+
/* Rewind holdStore, if we have one */
if (portal->holdStore)
{
diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out
index 42dc637fd4..d98c58d303 100644
--- a/src/test/regress/expected/portals.out
+++ b/src/test/regress/expected/portals.out
@@ -715,6 +715,24 @@ FETCH BACKWARD 1 FROM foo24; -- should fail
ERROR: cursor can only scan forward
HINT: Declare it with SCROLL option to enable backward scan.
END;
+BEGIN;
+DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2;
+FETCH 1 FROM foo24;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+ 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx
+(1 row)
+
+FETCH ABSOLUTE 2 FROM foo24; -- allowed
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+ 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx
+(1 row)
+
+FETCH ABSOLUTE 1 FROM foo24; -- should fail
+ERROR: cursor can only scan forward
+HINT: Declare it with SCROLL option to enable backward scan.
+END;
--
-- Cursors outside transaction blocks
--
diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out
index 078358d226..60bb4e8e3e 100644
--- a/src/test/regress/expected/tablesample.out
+++ b/src/test/regress/expected/tablesample.out
@@ -88,7 +88,7 @@ View definition:
-- check a sampled query doesn't affect cursor in progress
BEGIN;
-DECLARE tablesample_cur CURSOR FOR
+DECLARE tablesample_cur SCROLL CURSOR FOR
SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
FETCH FIRST FROM tablesample_cur;
id
diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql
index bf1dff884d..2a098a43b9 100644
--- a/src/test/regress/sql/portals.sql
+++ b/src/test/regress/sql/portals.sql
@@ -190,6 +190,18 @@ FETCH BACKWARD 1 FROM foo24; -- should fail
END;
+BEGIN;
+
+DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2;
+
+FETCH 1 FROM foo24;
+
+FETCH ABSOLUTE 2 FROM foo24; -- allowed
+
+FETCH ABSOLUTE 1 FROM foo24; -- should fail
+
+END;
+
--
-- Cursors outside transaction blocks
--
diff --git a/src/test/regress/sql/tablesample.sql b/src/test/regress/sql/tablesample.sql
index c39fe4b750..aa17994277 100644
--- a/src/test/regress/sql/tablesample.sql
+++ b/src/test/regress/sql/tablesample.sql
@@ -24,7 +24,7 @@ CREATE VIEW test_tablesample_v2 AS
-- check a sampled query doesn't affect cursor in progress
BEGIN;
-DECLARE tablesample_cur CURSOR FOR
+DECLARE tablesample_cur SCROLL CURSOR FOR
SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
FETCH FIRST FROM tablesample_cur;