aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2007-10-24 23:27:08 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2007-10-24 23:27:08 +0000
commit048efc25e46d95f6a6dad20d65f6d9dd10c640d4 (patch)
tree62aa8e84ce59710b3317eb97926cfa86452d8bd0 /src
parent8a35b07e1849c1af7acbdc8eea0bc357b5ad51e3 (diff)
downloadpostgresql-048efc25e46d95f6a6dad20d65f6d9dd10c640d4.tar.gz
postgresql-048efc25e46d95f6a6dad20d65f6d9dd10c640d4.zip
Disallow scrolling of FOR UPDATE/FOR SHARE cursors, so as to avoid problems
in corner cases such as re-fetching a just-deleted row. We may be able to relax this someday, but let's find out how many people really care before we invest a lot of work in it. Per report from Heikki and subsequent discussion. While in the neighborhood, make the combination of INSENSITIVE and FOR UPDATE throw an error, since they are semantically incompatible. (Up to now we've accepted but just ignored the INSENSITIVE option of DECLARE CURSOR.)
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/portalcmds.c7
-rw-r--r--src/backend/executor/spi.c19
-rw-r--r--src/backend/parser/analyze.c16
-rw-r--r--src/include/nodes/parsenodes.h4
-rw-r--r--src/test/regress/expected/portals.out31
-rw-r--r--src/test/regress/sql/portals.sql4
6 files changed, 51 insertions, 30 deletions
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 939452650d0..e8f21d4f083 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.65 2007/04/27 22:05:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.66 2007/10/24 23:27:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -102,12 +102,13 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
*
* If the user didn't specify a SCROLL type, allow or disallow scrolling
* based on whether it would require any additional runtime overhead to do
- * so.
+ * so. Also, we disallow scrolling for FOR UPDATE cursors.
*/
portal->cursorOptions = cstmt->options;
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
{
- if (ExecSupportsBackwardScan(stmt->planTree))
+ if (stmt->rowMarks == NIL &&
+ ExecSupportsBackwardScan(stmt->planTree))
portal->cursorOptions |= CURSOR_OPT_SCROLL;
else
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 875e4da2914..6d59401d0fb 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.181 2007/09/20 17:56:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.182 2007/10/24 23:27:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -975,6 +975,7 @@ SPI_cursor_open(const char *name, SPIPlanPtr plan,
{
if (list_length(stmt_list) == 1 &&
IsA((Node *) linitial(stmt_list), PlannedStmt) &&
+ ((PlannedStmt *) linitial(stmt_list))->rowMarks == NIL &&
ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree))
portal->cursorOptions |= CURSOR_OPT_SCROLL;
else
@@ -982,6 +983,22 @@ SPI_cursor_open(const char *name, SPIPlanPtr plan,
}
/*
+ * Disallow SCROLL with SELECT FOR UPDATE. This is not redundant with
+ * the check in transformDeclareCursorStmt because the cursor options
+ * might not have come through there.
+ */
+ if (portal->cursorOptions & CURSOR_OPT_SCROLL)
+ {
+ if (list_length(stmt_list) == 1 &&
+ IsA((Node *) linitial(stmt_list), PlannedStmt) &&
+ ((PlannedStmt *) linitial(stmt_list))->rowMarks != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("DECLARE CURSOR SCROLL ... FOR UPDATE/SHARE is not supported"),
+ errdetail("Scrollable cursors must be READ ONLY.")));
+ }
+
+ /*
* If told to be read-only, we'd better check for read-only queries.
* This can't be done earlier because we need to look at the finished,
* planned queries. (In particular, we don't want to do it between
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 3135d852467..567130b18db 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -17,7 +17,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.367 2007/06/23 22:12:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.368 2007/10/24 23:27:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1623,6 +1623,20 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
errmsg("DECLARE CURSOR WITH HOLD ... FOR UPDATE/SHARE is not supported"),
errdetail("Holdable cursors must be READ ONLY.")));
+ /* FOR UPDATE and SCROLL are not compatible */
+ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("DECLARE CURSOR SCROLL ... FOR UPDATE/SHARE is not supported"),
+ errdetail("Scrollable cursors must be READ ONLY.")));
+
+ /* FOR UPDATE and INSENSITIVE are not compatible */
+ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("DECLARE CURSOR INSENSITIVE ... FOR UPDATE/SHARE is not supported"),
+ errdetail("Insensitive cursors must be READ ONLY.")));
+
/* We won't need the raw querytree any more */
stmt->query = NULL;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 412fadac54e..e1a6198e012 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.353 2007/09/03 18:46:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.354 2007/10/24 23:27:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1479,7 +1479,7 @@ typedef struct CommentStmt
#define CURSOR_OPT_BINARY 0x0001 /* BINARY */
#define CURSOR_OPT_SCROLL 0x0002 /* SCROLL explicitly given */
#define CURSOR_OPT_NO_SCROLL 0x0004 /* NO SCROLL explicitly given */
-#define CURSOR_OPT_INSENSITIVE 0x0008 /* INSENSITIVE (unimplemented) */
+#define CURSOR_OPT_INSENSITIVE 0x0008 /* INSENSITIVE */
#define CURSOR_OPT_HOLD 0x0010 /* WITH HOLD */
#define CURSOR_OPT_FAST_PLAN 0x0020 /* prefer fast-start plan */
diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out
index b6673073cdf..527550eabde 100644
--- a/src/test/regress/expected/portals.out
+++ b/src/test/regress/expected/portals.out
@@ -1073,40 +1073,31 @@ SELECT * FROM uctest;
23 | three
(2 rows)
--- sensitive cursor should show effects of updates or deletes
--- XXX current behavior is WRONG
-FETCH RELATIVE 0 FROM c1;
+DELETE FROM uctest WHERE CURRENT OF c1;
+SELECT * FROM uctest;
f1 | f2
----+-----
8 | one
(1 row)
-DELETE FROM uctest WHERE CURRENT OF c1;
-SELECT * FROM uctest;
- f1 | f2
-----+-------
- 23 | three
-(1 row)
-
DELETE FROM uctest WHERE CURRENT OF c1; -- no-op
SELECT * FROM uctest;
- f1 | f2
-----+-------
- 23 | three
+ f1 | f2
+----+-----
+ 8 | one
(1 row)
UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op
SELECT * FROM uctest;
- f1 | f2
-----+-------
- 23 | three
+ f1 | f2
+----+-----
+ 8 | one
(1 row)
+--- sensitive cursors can't currently scroll back, so this is an error:
FETCH RELATIVE 0 FROM c1;
- f1 | f2
-----+----
-(0 rows)
-
+ERROR: cursor can only scan forward
+HINT: Declare it with SCROLL option to enable backward scan.
ROLLBACK;
SELECT * FROM uctest;
f1 | f2
diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql
index bdf5956d69c..8275ed78c84 100644
--- a/src/test/regress/sql/portals.sql
+++ b/src/test/regress/sql/portals.sql
@@ -376,15 +376,13 @@ UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
SELECT * FROM uctest;
UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
SELECT * FROM uctest;
--- sensitive cursor should show effects of updates or deletes
--- XXX current behavior is WRONG
-FETCH RELATIVE 0 FROM c1;
DELETE FROM uctest WHERE CURRENT OF c1;
SELECT * FROM uctest;
DELETE FROM uctest WHERE CURRENT OF c1; -- no-op
SELECT * FROM uctest;
UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op
SELECT * FROM uctest;
+--- sensitive cursors can't currently scroll back, so this is an error:
FETCH RELATIVE 0 FROM c1;
ROLLBACK;
SELECT * FROM uctest;