Fix use of cursor sensitivity terminology
authorPeter Eisentraut <peter@eisentraut.org>
Wed, 7 Apr 2021 05:49:27 +0000 (07:49 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Wed, 7 Apr 2021 06:05:55 +0000 (08:05 +0200)
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

doc/src/sgml/ecpg.sgml
doc/src/sgml/ref/declare.sgml
src/backend/catalog/sql_features.txt
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/bin/psql/tab-complete.c
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/test/regress/expected/portals.out
src/test/regress/sql/portals.sql

index e2e757668a7c3a1b08f2e64f04b42da291fff5c6..56f5d9b5db1f91881977d9e869c2b006e9595e58 100644 (file)
@@ -6792,8 +6792,8 @@ EXEC SQL DEALLOCATE DESCRIPTOR mydesc;
 
    <refsynopsisdiv>
 <synopsis>
-DECLARE <replaceable class="parameter">cursor_name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">prepared_name</replaceable>
-DECLARE <replaceable class="parameter">cursor_name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">query</replaceable>
+DECLARE <replaceable class="parameter">cursor_name</replaceable> [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">prepared_name</replaceable>
+DECLARE <replaceable class="parameter">cursor_name</replaceable> [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">query</replaceable>
 </synopsis>
    </refsynopsisdiv>
 
index 2152134635e462ae762f5ffb6b230a2c1c52e7bc..8a2b8cc8929c593e33ca10c8e15f814762814157 100644 (file)
@@ -26,7 +26,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
+DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
     CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">query</replaceable>
 </synopsis>
  </refsynopsisdiv>
@@ -75,14 +75,25 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
    </varlistentry>
 
    <varlistentry>
+    <term><literal>ASENSITIVE</literal></term>
     <term><literal>INSENSITIVE</literal></term>
     <listitem>
      <para>
-      Indicates that data retrieved from the cursor should be
-      unaffected by updates to the table(s) underlying the cursor that occur
-      after the cursor is created.  In <productname>PostgreSQL</productname>,
-      this is the default behavior; so this key word has no
-      effect and is only accepted for compatibility with the SQL standard.
+      Cursor sensitivity determines whether changes to the data underlying the
+      cursor, done in the same transaction, after the cursor has been
+      declared, are visible in the cursor.  <literal>INSENSITIVE</literal>
+      means they are not visible, <literal>ASENSITIVE</literal> means the
+      behavior is implementation-dependent.  A third behavior,
+      <literal>SENSITIVE</literal>, meaning that such changes are visible in
+      the cursor, is not available in <productname>PostgreSQL</productname>.
+      In <productname>PostgreSQL</productname>, all cursors are insensitive;
+      so these key words have no effect and are only accepted for
+      compatibility with the SQL standard.
+     </para>
+
+     <para>
+      Specifying <literal>INSENSITIVE</literal> together with <literal>FOR
+      UPDATE</literal> or <literal>FOR SHARE</literal> is an error.
      </para>
     </listitem>
    </varlistentry>
@@ -133,7 +144,7 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
   </variablelist>
 
   <para>
-   The key words <literal>BINARY</literal>,
+   The key words <literal>ASENSITIVE</literal>, <literal>BINARY</literal>,
    <literal>INSENSITIVE</literal>, and <literal>SCROLL</literal> can
    appear in any order.
   </para>
@@ -246,10 +257,7 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
     fetched, in the same way as for a regular
     <link linkend="sql-select"><command>SELECT</command></link> command with
     these options.
-    In addition, the returned rows will be the most up-to-date versions;
-    therefore these options provide the equivalent of what the SQL standard
-    calls a <quote>sensitive cursor</quote>.  (Specifying <literal>INSENSITIVE</literal>
-    together with <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> is an error.)
+    In addition, the returned rows will be the most up-to-date versions.
    </para>
 
    <caution>
@@ -278,7 +286,7 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
     <para>
      The main reason not to use <literal>FOR UPDATE</literal> with <literal>WHERE
      CURRENT OF</literal> is if you need the cursor to be scrollable, or to be
-     insensitive to the subsequent updates (that is, continue to show the old
+     isolated from concurrent updates (that is, continue to show the old
      data).  If this is a requirement, pay close heed to the caveats shown
      above.
     </para>
@@ -318,20 +326,21 @@ DECLARE liahona CURSOR FOR SELECT * FROM films;
  <refsect1>
   <title>Compatibility</title>
 
-  <para>
-   The SQL standard says that it is implementation-dependent whether cursors
-   are sensitive to concurrent updates of the underlying data by default.  In
-   <productname>PostgreSQL</productname>, cursors are insensitive by default,
-   and can be made sensitive by specifying <literal>FOR UPDATE</literal>.  Other
-   products may work differently.
-  </para>
-
   <para>
    The SQL standard allows cursors only in embedded
    <acronym>SQL</acronym> and in modules. <productname>PostgreSQL</productname>
    permits cursors to be used interactively.
   </para>
 
+  <para>
+   According to the SQL standard, changes made to insensitive cursors by
+   <literal>UPDATE ... WHERE CURRENT OF</literal> and <literal>DELETE
+   ... WHERE CURRENT OF</literal> statements are visibible in that same
+   cursor.  <productname>PostgreSQL</productname> treats these statements like
+   all other data changing statements in that they are not visible in
+   insensitive cursors.
+  </para>
+
   <para>
    Binary cursors are a <productname>PostgreSQL</productname>
    extension.
index 657e6c734b381cfb90b6f5b3c400ed365d8d8d2b..9f424216e26cff0a8ee7912efd9fe49b7347421f 100644 (file)
@@ -452,7 +452,7 @@ T211    Basic trigger capability    07  TRIGGER privilege   YES
 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
 T212   Enhanced trigger capability         YES 
 T213   INSTEAD OF triggers         YES 
-T231   Sensitive cursors           YES 
+T231   Sensitive cursors           NO  
 T241   START TRANSACTION statement         YES 
 T251   SET TRANSACTION statement: LOCAL option         NO  
 T261   Chained transactions            YES 
index 5de1307570e593b7e752b669893d2584fa5aca88..bce7a27de00e5d9964d4ebff6279911fe0a47f36 100644 (file)
@@ -2681,14 +2681,21 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
    Query      *result;
    Query      *query;
 
-   /*
-    * Don't allow both SCROLL and NO SCROLL to be specified
-    */
    if ((stmt->options & CURSOR_OPT_SCROLL) &&
        (stmt->options & CURSOR_OPT_NO_SCROLL))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
-                errmsg("cannot specify both SCROLL and NO SCROLL")));
+                /* translator: %s is a SQL keyword */
+                errmsg("cannot specify both %s and %s",
+                       "SCROLL", "NO SCROLL")));
+
+   if ((stmt->options & CURSOR_OPT_ASENSITIVE) &&
+       (stmt->options & CURSOR_OPT_INSENSITIVE))
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+                /* translator: %s is a SQL keyword */
+                errmsg("cannot specify both %s and %s",
+                       "ASENSITIVE", "INSENSITIVE")));
 
    /* Transform contained query, not allowing SELECT INTO */
    query = transformStmt(pstate, stmt->query);
@@ -2734,10 +2741,10 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
    /* FOR UPDATE and INSENSITIVE are not compatible */
    if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE))
        ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+               (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
        /*------
          translator: %s is a SQL row locking clause such as FOR UPDATE */
-                errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported",
+                errmsg("DECLARE INSENSITIVE CURSOR ... %s is not valid",
                        LCS_asString(((RowMarkClause *)
                                      linitial(query->rowMarks))->strength)),
                 errdetail("Insensitive cursors must be READ ONLY.")));
index 38c36a49360ef52ecfeb23fc9aec25361c3421d8..517bf72378401aab7a3094a6d8267cd92ddbaf25 100644 (file)
@@ -637,7 +637,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 /* ordinary key words in alphabetical order */
 %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
    AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
-   ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
+   ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
 
    BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
    BOOLEAN_P BOTH BREADTH BY
@@ -11217,6 +11217,7 @@ cursor_options: /*EMPTY*/                   { $$ = 0; }
            | cursor_options NO SCROLL      { $$ = $1 | CURSOR_OPT_NO_SCROLL; }
            | cursor_options SCROLL         { $$ = $1 | CURSOR_OPT_SCROLL; }
            | cursor_options BINARY         { $$ = $1 | CURSOR_OPT_BINARY; }
+           | cursor_options ASENSITIVE     { $$ = $1 | CURSOR_OPT_ASENSITIVE; }
            | cursor_options INSENSITIVE    { $$ = $1 | CURSOR_OPT_INSENSITIVE; }
        ;
 
@@ -15424,6 +15425,7 @@ unreserved_keyword:
            | ALSO
            | ALTER
            | ALWAYS
+           | ASENSITIVE
            | ASSERTION
            | ASSIGNMENT
            | AT
@@ -15931,6 +15933,7 @@ bare_label_keyword:
            | AND
            | ANY
            | ASC
+           | ASENSITIVE
            | ASSERTION
            | ASSIGNMENT
            | ASYMMETRIC
index 891997ca36975cca5b6d9e7392ff8359dadff31c..d79d7b8145b7ed5bc11802266f56481b1ca973d7 100644 (file)
@@ -3052,7 +3052,7 @@ psql_completion(const char *text, int start, int end)
     * SCROLL, and CURSOR.
     */
    else if (Matches("DECLARE", MatchAny))
-       COMPLETE_WITH("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
+       COMPLETE_WITH("BINARY", "ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL",
                      "CURSOR");
 
    /*
index 807fbaceaac557fcb46ef4de40234db7a5ac2b29..acc093d6e0f69bb0362174f644be1e7807c9d4fd 100644 (file)
@@ -2774,7 +2774,8 @@ typedef struct SecLabelStmt
 #define CURSOR_OPT_SCROLL      0x0002  /* SCROLL explicitly given */
 #define CURSOR_OPT_NO_SCROLL   0x0004  /* NO SCROLL explicitly given */
 #define CURSOR_OPT_INSENSITIVE 0x0008  /* INSENSITIVE */
-#define CURSOR_OPT_HOLD            0x0010  /* WITH HOLD */
+#define CURSOR_OPT_ASENSITIVE  0x0010  /* ASENSITIVE */
+#define CURSOR_OPT_HOLD            0x0020  /* WITH HOLD */
 /* these planner-control flags do not correspond to any SQL grammar: */
 #define CURSOR_OPT_FAST_PLAN   0x0100  /* prefer fast-start plan */
 #define CURSOR_OPT_GENERIC_PLAN 0x0200 /* force use of generic plan */
index 4bbe53e8522932bc9166aa0819f8a438ebe7e8d5..d3ecd72e72ed181e88242f15fcceb38810fb9cb5 100644 (file)
@@ -44,6 +44,7 @@ PG_KEYWORD("any", ANY, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD, AS_LABEL)
 PG_KEYWORD("as", AS, RESERVED_KEYWORD, AS_LABEL)
 PG_KEYWORD("asc", ASC, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("asensitive", ASENSITIVE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD, BARE_LABEL)
index dc0d2ef7dd81f9756f30205b21bd4a073fb9a5c7..42dc637fd4b7204169e9aa14c5ba5df6e30ed83d 100644 (file)
@@ -1094,7 +1094,7 @@ SELECT * FROM uctest;
   8 | one
 (1 row)
 
---- sensitive cursors can't currently scroll back, so this is an error:
+--- FOR UPDATE cursors can't currently scroll back, so this is an error:
 FETCH RELATIVE 0 FROM c1;
 ERROR:  cursor can only scan forward
 HINT:  Declare it with SCROLL option to enable backward scan.
@@ -1106,6 +1106,41 @@ SELECT * FROM uctest;
   8 | one
 (2 rows)
 
+-- Check insensitive cursor with INSERT
+-- (The above tests don't test the SQL notion of an insensitive cursor
+-- correctly, because per SQL standard, changes from WHERE CURRENT OF
+-- commands should be visible in the cursor.  So here we make the
+-- changes with a command that is independent of the cursor.)
+BEGIN;
+DECLARE c1 INSENSITIVE CURSOR FOR SELECT * FROM uctest;
+INSERT INTO uctest VALUES (10, 'ten');
+FETCH NEXT FROM c1;
+ f1 |  f2   
+----+-------
+  3 | three
+(1 row)
+
+FETCH NEXT FROM c1;
+ f1 | f2  
+----+-----
+  8 | one
+(1 row)
+
+FETCH NEXT FROM c1;  -- insert not visible
+ f1 | f2 
+----+----
+(0 rows)
+
+COMMIT;
+SELECT * FROM uctest;
+ f1 |  f2   
+----+-------
+  3 | three
+  8 | one
+ 10 | ten
+(3 rows)
+
+DELETE FROM uctest WHERE f1 = 10;  -- restore test table state
 -- Check inheritance cases
 CREATE TEMP TABLE ucchild () inherits (uctest);
 INSERT INTO ucchild values(100, 'hundred');
index 52560ac027516842c73ec524988ea0f0b4e79a13..bf1dff884d91c246047ac35e2709ee673e9e0459 100644 (file)
@@ -382,11 +382,26 @@ 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:
+--- FOR UPDATE cursors can't currently scroll back, so this is an error:
 FETCH RELATIVE 0 FROM c1;
 ROLLBACK;
 SELECT * FROM uctest;
 
+-- Check insensitive cursor with INSERT
+-- (The above tests don't test the SQL notion of an insensitive cursor
+-- correctly, because per SQL standard, changes from WHERE CURRENT OF
+-- commands should be visible in the cursor.  So here we make the
+-- changes with a command that is independent of the cursor.)
+BEGIN;
+DECLARE c1 INSENSITIVE CURSOR FOR SELECT * FROM uctest;
+INSERT INTO uctest VALUES (10, 'ten');
+FETCH NEXT FROM c1;
+FETCH NEXT FROM c1;
+FETCH NEXT FROM c1;  -- insert not visible
+COMMIT;
+SELECT * FROM uctest;
+DELETE FROM uctest WHERE f1 = 10;  -- restore test table state
+
 -- Check inheritance cases
 CREATE TEMP TABLE ucchild () inherits (uctest);
 INSERT INTO ucchild values(100, 'hundred');