diff --git a/doc/src/sgml/ref/declare.sgml b/doc/src/sgml/ref/declare.sgml index f823cf77bb..269d2c101c 100644 --- a/doc/src/sgml/ref/declare.sgml +++ b/doc/src/sgml/ref/declare.sgml @@ -1,5 +1,5 @@ @@ -172,7 +172,7 @@ DECLARE name [ BINARY ] [ INSENSITI transaction. Thus, DECLARE without WITH HOLD is useless outside a transaction block: the cursor would survive only to the completion of the statement. Therefore - PostgreSQL reports an error if this + PostgreSQL reports an error if such a command is used outside a transaction block. Use , @@ -230,6 +230,11 @@ DECLARE name [ BINARY ] [ INSENSITI will have no effect if the row was changed meanwhile. + + SCROLL may not be specified when the query + includes FOR UPDATE or FOR SHARE. + + The SQL standard only makes provisions for cursors in embedded SQL. The PostgreSQL @@ -265,10 +270,11 @@ DECLARE liahona CURSOR FOR SELECT * FROM films; Compatibility - The SQL standard specifies that by default, cursors are sensitive to - concurrent updates of the underlying data. In + The SQL standard says that it is implementation-dependent whether cursors + are sensitive to concurrent updates of the underlying data by default. In PostgreSQL, cursors are insensitive by default, - and can be made sensitive by specifying FOR UPDATE. + and can be made sensitive by specifying FOR UPDATE. Other + products may work differently. diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 939452650d..e8f21d4f08 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 875e4da291..6d59401d0f 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,12 +975,29 @@ 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 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; } + /* + * 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, diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 3135d85246..567130b18d 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 412fadac54..e1a6198e01 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 b6673073cd..527550eabd 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 bdf5956d69..8275ed78c8 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;