Skip to content

Commit dd13ad9

Browse files
committed
Fix use of cursor sensitivity terminology
Documentation and comments in code and tests have been using the terms sensitive/insensitive cursor incorrectly relative to the SQL standard. (Cursor sensitivity is only relevant for changes made in the same transaction as the cursor, not for concurrent changes in other sessions.) Moreover, some of the behavior of PostgreSQL is incorrect according to the SQL standard, confusing the issue further. (WHERE CURRENT OF changes are not visible in insensitive cursors, but they should be.) This change corrects the terminology and removes the claim that sensitive cursors are supported. It also adds a test case that checks the insensitive behavior in a "correct" way, using a change command not using WHERE CURRENT OF. Finally, it adds the ASENSITIVE cursor option to select the default asensitive behavior, per SQL standard. There are no changes to cursor behavior in this patch. Discussion: https://www.postgresql.org/message-id/flat/96ee8b30-9889-9e1b-b053-90e10c050e85%40enterprisedb.com
1 parent 0b5e824 commit dd13ad9

File tree

10 files changed

+105
-34
lines changed

10 files changed

+105
-34
lines changed

doc/src/sgml/ecpg.sgml

+2-2
Original file line numberDiff line numberDiff line change
@@ -6792,8 +6792,8 @@ EXEC SQL DEALLOCATE DESCRIPTOR mydesc;
67926792

67936793
<refsynopsisdiv>
67946794
<synopsis>
6795-
DECLARE <replaceable class="parameter">cursor_name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">prepared_name</replaceable>
6796-
DECLARE <replaceable class="parameter">cursor_name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">query</replaceable>
6795+
DECLARE <replaceable class="parameter">cursor_name</replaceable> [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">prepared_name</replaceable>
6796+
DECLARE <replaceable class="parameter">cursor_name</replaceable> [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">query</replaceable>
67976797
</synopsis>
67986798
</refsynopsisdiv>
67996799

doc/src/sgml/ref/declare.sgml

+29-20
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ PostgreSQL documentation
2626

2727
<refsynopsisdiv>
2828
<synopsis>
29-
DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
29+
DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
3030
CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">query</replaceable>
3131
</synopsis>
3232
</refsynopsisdiv>
@@ -75,14 +75,25 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
7575
</varlistentry>
7676

7777
<varlistentry>
78+
<term><literal>ASENSITIVE</literal></term>
7879
<term><literal>INSENSITIVE</literal></term>
7980
<listitem>
8081
<para>
81-
Indicates that data retrieved from the cursor should be
82-
unaffected by updates to the table(s) underlying the cursor that occur
83-
after the cursor is created. In <productname>PostgreSQL</productname>,
84-
this is the default behavior; so this key word has no
85-
effect and is only accepted for compatibility with the SQL standard.
82+
Cursor sensitivity determines whether changes to the data underlying the
83+
cursor, done in the same transaction, after the cursor has been
84+
declared, are visible in the cursor. <literal>INSENSITIVE</literal>
85+
means they are not visible, <literal>ASENSITIVE</literal> means the
86+
behavior is implementation-dependent. A third behavior,
87+
<literal>SENSITIVE</literal>, meaning that such changes are visible in
88+
the cursor, is not available in <productname>PostgreSQL</productname>.
89+
In <productname>PostgreSQL</productname>, all cursors are insensitive;
90+
so these key words have no effect and are only accepted for
91+
compatibility with the SQL standard.
92+
</para>
93+
94+
<para>
95+
Specifying <literal>INSENSITIVE</literal> together with <literal>FOR
96+
UPDATE</literal> or <literal>FOR SHARE</literal> is an error.
8697
</para>
8798
</listitem>
8899
</varlistentry>
@@ -133,7 +144,7 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
133144
</variablelist>
134145

135146
<para>
136-
The key words <literal>BINARY</literal>,
147+
The key words <literal>ASENSITIVE</literal>, <literal>BINARY</literal>,
137148
<literal>INSENSITIVE</literal>, and <literal>SCROLL</literal> can
138149
appear in any order.
139150
</para>
@@ -246,10 +257,7 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
246257
fetched, in the same way as for a regular
247258
<link linkend="sql-select"><command>SELECT</command></link> command with
248259
these options.
249-
In addition, the returned rows will be the most up-to-date versions;
250-
therefore these options provide the equivalent of what the SQL standard
251-
calls a <quote>sensitive cursor</quote>. (Specifying <literal>INSENSITIVE</literal>
252-
together with <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> is an error.)
260+
In addition, the returned rows will be the most up-to-date versions.
253261
</para>
254262

255263
<caution>
@@ -278,7 +286,7 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
278286
<para>
279287
The main reason not to use <literal>FOR UPDATE</literal> with <literal>WHERE
280288
CURRENT OF</literal> is if you need the cursor to be scrollable, or to be
281-
insensitive to the subsequent updates (that is, continue to show the old
289+
isolated from concurrent updates (that is, continue to show the old
282290
data). If this is a requirement, pay close heed to the caveats shown
283291
above.
284292
</para>
@@ -318,20 +326,21 @@ DECLARE liahona CURSOR FOR SELECT * FROM films;
318326
<refsect1>
319327
<title>Compatibility</title>
320328

321-
<para>
322-
The SQL standard says that it is implementation-dependent whether cursors
323-
are sensitive to concurrent updates of the underlying data by default. In
324-
<productname>PostgreSQL</productname>, cursors are insensitive by default,
325-
and can be made sensitive by specifying <literal>FOR UPDATE</literal>. Other
326-
products may work differently.
327-
</para>
328-
329329
<para>
330330
The SQL standard allows cursors only in embedded
331331
<acronym>SQL</acronym> and in modules. <productname>PostgreSQL</productname>
332332
permits cursors to be used interactively.
333333
</para>
334334

335+
<para>
336+
According to the SQL standard, changes made to insensitive cursors by
337+
<literal>UPDATE ... WHERE CURRENT OF</literal> and <literal>DELETE
338+
... WHERE CURRENT OF</literal> statements are visibible in that same
339+
cursor. <productname>PostgreSQL</productname> treats these statements like
340+
all other data changing statements in that they are not visible in
341+
insensitive cursors.
342+
</para>
343+
335344
<para>
336345
Binary cursors are a <productname>PostgreSQL</productname>
337346
extension.

src/backend/catalog/sql_features.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ T211 Basic trigger capability 07 TRIGGER privilege YES
452452
T211 Basic trigger capability 08 Multiple triggers for the same event are executed in the order in which they were created in the catalog NO intentionally omitted
453453
T212 Enhanced trigger capability YES
454454
T213 INSTEAD OF triggers YES
455-
T231 Sensitive cursors YES
455+
T231 Sensitive cursors NO
456456
T241 START TRANSACTION statement YES
457457
T251 SET TRANSACTION statement: LOCAL option NO
458458
T261 Chained transactions YES

src/backend/parser/analyze.c

+13-6
Original file line numberDiff line numberDiff line change
@@ -2681,14 +2681,21 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
26812681
Query *result;
26822682
Query *query;
26832683

2684-
/*
2685-
* Don't allow both SCROLL and NO SCROLL to be specified
2686-
*/
26872684
if ((stmt->options & CURSOR_OPT_SCROLL) &&
26882685
(stmt->options & CURSOR_OPT_NO_SCROLL))
26892686
ereport(ERROR,
26902687
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
2691-
errmsg("cannot specify both SCROLL and NO SCROLL")));
2688+
/* translator: %s is a SQL keyword */
2689+
errmsg("cannot specify both %s and %s",
2690+
"SCROLL", "NO SCROLL")));
2691+
2692+
if ((stmt->options & CURSOR_OPT_ASENSITIVE) &&
2693+
(stmt->options & CURSOR_OPT_INSENSITIVE))
2694+
ereport(ERROR,
2695+
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
2696+
/* translator: %s is a SQL keyword */
2697+
errmsg("cannot specify both %s and %s",
2698+
"ASENSITIVE", "INSENSITIVE")));
26922699

26932700
/* Transform contained query, not allowing SELECT INTO */
26942701
query = transformStmt(pstate, stmt->query);
@@ -2734,10 +2741,10 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
27342741
/* FOR UPDATE and INSENSITIVE are not compatible */
27352742
if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE))
27362743
ereport(ERROR,
2737-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2744+
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
27382745
/*------
27392746
translator: %s is a SQL row locking clause such as FOR UPDATE */
2740-
errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported",
2747+
errmsg("DECLARE INSENSITIVE CURSOR ... %s is not valid",
27412748
LCS_asString(((RowMarkClause *)
27422749
linitial(query->rowMarks))->strength)),
27432750
errdetail("Insensitive cursors must be READ ONLY.")));

src/backend/parser/gram.y

+4-1
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
637637
/* ordinary key words in alphabetical order */
638638
%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
639639
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
640-
ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
640+
ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
641641

642642
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
643643
BOOLEAN_P BOTH BREADTH BY
@@ -11217,6 +11217,7 @@ cursor_options: /*EMPTY*/ { $$ = 0; }
1121711217
| cursor_options NO SCROLL { $$ = $1 | CURSOR_OPT_NO_SCROLL; }
1121811218
| cursor_options SCROLL { $$ = $1 | CURSOR_OPT_SCROLL; }
1121911219
| cursor_options BINARY { $$ = $1 | CURSOR_OPT_BINARY; }
11220+
| cursor_options ASENSITIVE { $$ = $1 | CURSOR_OPT_ASENSITIVE; }
1122011221
| cursor_options INSENSITIVE { $$ = $1 | CURSOR_OPT_INSENSITIVE; }
1122111222
;
1122211223

@@ -15424,6 +15425,7 @@ unreserved_keyword:
1542415425
| ALSO
1542515426
| ALTER
1542615427
| ALWAYS
15428+
| ASENSITIVE
1542715429
| ASSERTION
1542815430
| ASSIGNMENT
1542915431
| AT
@@ -15931,6 +15933,7 @@ bare_label_keyword:
1593115933
| AND
1593215934
| ANY
1593315935
| ASC
15936+
| ASENSITIVE
1593415937
| ASSERTION
1593515938
| ASSIGNMENT
1593615939
| ASYMMETRIC

src/bin/psql/tab-complete.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -3052,7 +3052,7 @@ psql_completion(const char *text, int start, int end)
30523052
* SCROLL, and CURSOR.
30533053
*/
30543054
else if (Matches("DECLARE", MatchAny))
3055-
COMPLETE_WITH("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
3055+
COMPLETE_WITH("BINARY", "ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL",
30563056
"CURSOR");
30573057

30583058
/*

src/include/nodes/parsenodes.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -2774,7 +2774,8 @@ typedef struct SecLabelStmt
27742774
#define CURSOR_OPT_SCROLL 0x0002 /* SCROLL explicitly given */
27752775
#define CURSOR_OPT_NO_SCROLL 0x0004 /* NO SCROLL explicitly given */
27762776
#define CURSOR_OPT_INSENSITIVE 0x0008 /* INSENSITIVE */
2777-
#define CURSOR_OPT_HOLD 0x0010 /* WITH HOLD */
2777+
#define CURSOR_OPT_ASENSITIVE 0x0010 /* ASENSITIVE */
2778+
#define CURSOR_OPT_HOLD 0x0020 /* WITH HOLD */
27782779
/* these planner-control flags do not correspond to any SQL grammar: */
27792780
#define CURSOR_OPT_FAST_PLAN 0x0100 /* prefer fast-start plan */
27802781
#define CURSOR_OPT_GENERIC_PLAN 0x0200 /* force use of generic plan */

src/include/parser/kwlist.h

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ PG_KEYWORD("any", ANY, RESERVED_KEYWORD, BARE_LABEL)
4444
PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD, AS_LABEL)
4545
PG_KEYWORD("as", AS, RESERVED_KEYWORD, AS_LABEL)
4646
PG_KEYWORD("asc", ASC, RESERVED_KEYWORD, BARE_LABEL)
47+
PG_KEYWORD("asensitive", ASENSITIVE, UNRESERVED_KEYWORD, BARE_LABEL)
4748
PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD, BARE_LABEL)
4849
PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD, BARE_LABEL)
4950
PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD, BARE_LABEL)

src/test/regress/expected/portals.out

+36-1
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@ SELECT * FROM uctest;
10941094
8 | one
10951095
(1 row)
10961096

1097-
--- sensitive cursors can't currently scroll back, so this is an error:
1097+
--- FOR UPDATE cursors can't currently scroll back, so this is an error:
10981098
FETCH RELATIVE 0 FROM c1;
10991099
ERROR: cursor can only scan forward
11001100
HINT: Declare it with SCROLL option to enable backward scan.
@@ -1106,6 +1106,41 @@ SELECT * FROM uctest;
11061106
8 | one
11071107
(2 rows)
11081108

1109+
-- Check insensitive cursor with INSERT
1110+
-- (The above tests don't test the SQL notion of an insensitive cursor
1111+
-- correctly, because per SQL standard, changes from WHERE CURRENT OF
1112+
-- commands should be visible in the cursor. So here we make the
1113+
-- changes with a command that is independent of the cursor.)
1114+
BEGIN;
1115+
DECLARE c1 INSENSITIVE CURSOR FOR SELECT * FROM uctest;
1116+
INSERT INTO uctest VALUES (10, 'ten');
1117+
FETCH NEXT FROM c1;
1118+
f1 | f2
1119+
----+-------
1120+
3 | three
1121+
(1 row)
1122+
1123+
FETCH NEXT FROM c1;
1124+
f1 | f2
1125+
----+-----
1126+
8 | one
1127+
(1 row)
1128+
1129+
FETCH NEXT FROM c1; -- insert not visible
1130+
f1 | f2
1131+
----+----
1132+
(0 rows)
1133+
1134+
COMMIT;
1135+
SELECT * FROM uctest;
1136+
f1 | f2
1137+
----+-------
1138+
3 | three
1139+
8 | one
1140+
10 | ten
1141+
(3 rows)
1142+
1143+
DELETE FROM uctest WHERE f1 = 10; -- restore test table state
11091144
-- Check inheritance cases
11101145
CREATE TEMP TABLE ucchild () inherits (uctest);
11111146
INSERT INTO ucchild values(100, 'hundred');

src/test/regress/sql/portals.sql

+16-1
Original file line numberDiff line numberDiff line change
@@ -382,11 +382,26 @@ DELETE FROM uctest WHERE CURRENT OF c1; -- no-op
382382
SELECT * FROM uctest;
383383
UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op
384384
SELECT * FROM uctest;
385-
--- sensitive cursors can't currently scroll back, so this is an error:
385+
--- FOR UPDATE cursors can't currently scroll back, so this is an error:
386386
FETCH RELATIVE 0 FROM c1;
387387
ROLLBACK;
388388
SELECT * FROM uctest;
389389

390+
-- Check insensitive cursor with INSERT
391+
-- (The above tests don't test the SQL notion of an insensitive cursor
392+
-- correctly, because per SQL standard, changes from WHERE CURRENT OF
393+
-- commands should be visible in the cursor. So here we make the
394+
-- changes with a command that is independent of the cursor.)
395+
BEGIN;
396+
DECLARE c1 INSENSITIVE CURSOR FOR SELECT * FROM uctest;
397+
INSERT INTO uctest VALUES (10, 'ten');
398+
FETCH NEXT FROM c1;
399+
FETCH NEXT FROM c1;
400+
FETCH NEXT FROM c1; -- insert not visible
401+
COMMIT;
402+
SELECT * FROM uctest;
403+
DELETE FROM uctest WHERE f1 = 10; -- restore test table state
404+
390405
-- Check inheritance cases
391406
CREATE TEMP TABLE ucchild () inherits (uctest);
392407
INSERT INTO ucchild values(100, 'hundred');

0 commit comments

Comments
 (0)