Error out if SKIP LOCKED and WITH TIES are both specified
authorAlvaro Herrera
Fri, 1 Oct 2021 21:29:18 +0000 (18:29 -0300)
committerAlvaro Herrera
Fri, 1 Oct 2021 21:29:18 +0000 (18:29 -0300)
Both bugs #16676[1] and #17141[2] illustrate that the combination of
SKIP LOCKED and FETCH FIRST WITH TIES break expectations when it comes
to rows returned to other sessions accessing the same row.  Since this
situation is detectable from the syntax and hard to fix otherwise,
forbid for now, with the potential to fix in the future.

[1] https://postgr.es/m/16676-fd62c3c835880da6@postgresql.org
[2] https://postgr.es/m/17141-913d78b9675aac8e@postgresql.org

Backpatch-through: 13, where WITH TIES was introduced
Author: David Christensen 
Discussion: https://postgr.es/m/CAOxo6XLPccCKru3xPMaYDpa+AXyPeWFs+SskrrL+HKwDjJnLhg@mail.gmail.com

doc/src/sgml/ref/select.sgml
src/backend/commands/matview.c
src/backend/parser/gram.y
src/test/regress/expected/limit.out
src/test/regress/sql/limit.sql

index 982835d53ef5648ee08e45872ba600fde86f6e6b..aa823d2ce77fdcbf86601c8bda7799c985c23eb5 100644 (file)
@@ -1454,7 +1454,8 @@ FETCH { FIRST | NEXT } [ count ] {
     The WITH TIES option is used to return any additional
     rows that tie for the last place in the result set according to
     the ORDER BY clause; ORDER BY
-    is mandatory in this case.
+    is mandatory in this case, and SKIP LOCKED is
+    not allowed.
     ROW and ROWS as well as
     FIRST and NEXT are noise
     words that don't influence the effects of these clauses.
index 4c9d1387ef6589b4b41e995e116e1f8857da2414..2ba5172f7c61e1cf822191414daa2d0ac5fcef7b 100644 (file)
@@ -185,7 +185,8 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
    if (concurrent && stmt->skipData)
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
-                errmsg("CONCURRENTLY and WITH NO DATA options cannot be used together")));
+                errmsg("%s and %s options cannot be used together",
+                       "CONCURRENTLY", "WITH NO DATA")));
 
    /*
     * Check that everything is correct for a refresh. Problems at this point
index 3adc087e3ffe59a5c4bf5443d7e49545f5f0ba51..74339aa4db871146e79e1043227ae464d21849d7 100644 (file)
@@ -16163,6 +16163,21 @@ insertSelectOptions(SelectStmt *stmt,
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("WITH TIES cannot be specified without ORDER BY clause")));
+       if (limitClause->limitOption == LIMIT_OPTION_WITH_TIES && stmt->lockingClause)
+       {
+           ListCell   *lc;
+
+           foreach(lc, stmt->lockingClause)
+           {
+               LockingClause *lock = lfirst_node(LockingClause, lc);
+
+               if (lock->waitPolicy == LockWaitSkip)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("%s and %s options cannot be used together",
+                                   "SKIP LOCKED", "WITH TIES")));
+           }
+       }
        stmt->limitOption = limitClause->limitOption;
    }
    if (withClause)
index b75afcc01a399db06e894ca089e307e9a3e183ac..8a98bbea8eb97ce6cc66526a106fda6210f1db24 100644 (file)
@@ -619,6 +619,11 @@ SELECT  thousand
         0
 (2 rows)
 
+-- SKIP LOCKED and WITH TIES are incompatible
+SELECT  thousand
+       FROM onek WHERE thousand < 5
+       ORDER BY thousand FETCH FIRST 1 ROW WITH TIES FOR UPDATE SKIP LOCKED;
+ERROR:  SKIP LOCKED and WITH TIES options cannot be used together
 -- should fail
 SELECT ''::text AS two, unique1, unique2, stringu1
        FROM onek WHERE unique1 > 50
index d2d4ef132df2f0a1e75dbdb8c3dd281b855dc852..6f0cda98701559d13d620d68855f24a15914f0ba 100644 (file)
@@ -173,6 +173,11 @@ SELECT  thousand
        FROM onek WHERE thousand < 5
        ORDER BY thousand FETCH FIRST 2 ROW ONLY;
 
+-- SKIP LOCKED and WITH TIES are incompatible
+SELECT  thousand
+       FROM onek WHERE thousand < 5
+       ORDER BY thousand FETCH FIRST 1 ROW WITH TIES FOR UPDATE SKIP LOCKED;
+
 -- should fail
 SELECT ''::text AS two, unique1, unique2, stringu1
        FROM onek WHERE unique1 > 50