I wrote:
> [ pokes at it some more ... ] Hm, we let you do this:
> ...
> which definitely flies in the face of the fact that we disallow
> combining SCROLL and FOR UPDATE:
> regression=*# declare c scroll cursor for select * from int8_tbl for update;
> ERROR: DECLARE SCROLL CURSOR ... FOR UPDATE is not supported
> DETAIL: Scrollable cursors must be READ ONLY.
>
> I don't recall the exact reason for that prohibition, but I wonder
> if there aren't user-visible anomalies reachable from the fact that
> you can bypass it.
I dug in the archives. The above-quoted error message was added by
me in 048efc25e, responding to Heikki's complaint here:
https://www.postgresql.org/message-id/471F69FE.5000500%40enterprisedb.com
What I now see is that I put that check at the wrong level. It
successfully blocks off the case Heikki complained of:
DROP TABLE IF EXISTS foo;
CREATE TABLE foo (id integer);
INSERT INTO foo SELECT a from generate_series(1,10) a;
BEGIN;
DECLARE c CURSOR FOR SELECT id FROM foo FOR UPDATE;
FETCH 2 FROM c;
UPDATE foo set ID=20 WHERE CURRENT OF c;
FETCH RELATIVE 0 FROM c;
COMMIT;
The FETCH RELATIVE 0 fails with
ERROR: cursor can only scan forward
HINT: Declare it with SCROLL option to enable backward scan.
However, if you replace that with the should-be-equivalent
FETCH ABSOLUTE 2 FROM c;
then what you get is not an error but
id
----
3
(1 row)
which is for certain anomalous, because that is not the row you
saw as being row 2 in the initial FETCH.
The reason for this behavior is that the only-scan-forward check
is in the relatively low-level function PortalRunSelect, which
is passed a "forward" flag and a count. The place that interprets
FETCH_ABSOLUTE and friends is DoPortalRunFetch, and what it's doing
in this particular scenario is to rewind to start and fetch forwards,
thus bypassing PortalRunSelect's error check. And, since the query
is using FOR UPDATE, this table scan sees the row with ID=2 as already
dead. (Its replacement with ID=20 has been installed at the end of
the table, so while it would be visible to the cursor, it's not at
the same position as before.)
So basically, we *do* have this check and have done since 2007,
but it's not water-tight for all variants of FETCH. I think
tightening it up in HEAD and v14 is a no-brainer, but I'm a bit
more hesitant about whether to back-patch into stable branches.
regards, tom lane