Fix deparsing FETCH FIRST <expr> ROWS WITH TIES REL_17_STABLE github/REL_17_STABLE
authorHeikki Linnakangas <[email protected]>
Mon, 19 May 2025 15:50:26 +0000 (18:50 +0300)
committerHeikki Linnakangas <[email protected]>
Mon, 19 May 2025 15:50:47 +0000 (18:50 +0300)
In the grammar, <expr> is a c_expr, which accepts only a limited set
of integer literals and simple expressions without parens. The
deparsing logic didn't quite match the grammar rule, and failed to use
parens e.g. for "5::bigint".

To fix, always surround the expression with parens. Would be nice to
omit the parens in simple cases, but unfortunately it's non-trivial to
detect such simple cases. Even if the expression is a simple literal
123 in the original query, after parse analysis it becomes a FuncExpr
with COERCE_IMPLICIT_CAST rather than a simple Const.

Reported-by: yonghao lee
Backpatch-through: 13
Discussion: https://www.postgresql.org/message-id/18929-077d6b7093b176e2@postgresql.org

src/backend/utils/adt/ruleutils.c
src/test/regress/expected/limit.out
src/test/regress/sql/limit.sql

index c6293b20cfeaf2158daf4c83da0169f9823d5ba0..d1139a268f3e98b72f3f32eeaf11fc978c4f0572 100644 (file)
@@ -5752,9 +5752,19 @@ get_select_query_def(Query *query, deparse_context *context)
    {
        if (query->limitOption == LIMIT_OPTION_WITH_TIES)
        {
+           /*
+            * The limitCount arg is a c_expr, so it needs parens. Simple
+            * literals and function expressions would not need parens, but
+            * unfortunately it's hard to tell if the expression will be
+            * printed as a simple literal like 123 or as a typecast
+            * expression, like '-123'::int4. The grammar accepts the former
+            * without quoting, but not the latter.
+            */
            appendContextKeyword(context, " FETCH FIRST ",
                                 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+           appendStringInfoChar(buf, '(');
            get_rule_expr(query->limitCount, context, false);
+           appendStringInfoChar(buf, ')');
            appendStringInfoString(buf, " ROWS WITH TIES");
        }
        else
index a2cd0f9f5b89ea1831af942ab303511f386ec756..d45427ac03952f56ff0251f7da422b601d003328 100644 (file)
@@ -643,7 +643,7 @@ View definition:
   WHERE thousand < 995
   ORDER BY thousand
  OFFSET 10
- FETCH FIRST 5 ROWS WITH TIES;
+ FETCH FIRST (5) ROWS WITH TIES;
 
 CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995
        ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS ONLY;
@@ -675,15 +675,29 @@ View definition:
    FROM onek
   WHERE thousand < 995
   ORDER BY thousand
- FETCH FIRST (NULL::integer + 1) ROWS WITH TIES;
+ FETCH FIRST ((NULL::integer + 1)) ROWS WITH TIES;
 
 CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995
-       ORDER BY thousand FETCH FIRST NULL ROWS ONLY;
+       ORDER BY thousand FETCH FIRST (5::bigint) ROWS WITH TIES;
 \d+ limit_thousand_v_4
                       View "public.limit_thousand_v_4"
   Column  |  Type   | Collation | Nullable | Default | Storage | Description 
 ----------+---------+-----------+----------+---------+---------+-------------
  thousand | integer |           |          |         | plain   | 
+View definition:
+ SELECT thousand
+   FROM onek
+  WHERE thousand < 995
+  ORDER BY thousand
+ FETCH FIRST (5::bigint) ROWS WITH TIES;
+
+CREATE VIEW limit_thousand_v_5 AS SELECT thousand FROM onek WHERE thousand < 995
+       ORDER BY thousand FETCH FIRST NULL ROWS ONLY;
+\d+ limit_thousand_v_5
+                      View "public.limit_thousand_v_5"
+  Column  |  Type   | Collation | Nullable | Default | Storage | Description 
+----------+---------+-----------+----------+---------+---------+-------------
+ thousand | integer |           |          |         | plain   | 
 View definition:
  SELECT thousand
    FROM onek
index 6f0cda98701559d13d620d68855f24a15914f0ba..603910fe6d11c10350a0f6e148a1f50b52504268 100644 (file)
@@ -196,6 +196,9 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995
        ORDER BY thousand FETCH FIRST (NULL+1) ROWS WITH TIES;
 \d+ limit_thousand_v_3
 CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995
-       ORDER BY thousand FETCH FIRST NULL ROWS ONLY;
+       ORDER BY thousand FETCH FIRST (5::bigint) ROWS WITH TIES;
 \d+ limit_thousand_v_4
+CREATE VIEW limit_thousand_v_5 AS SELECT thousand FROM onek WHERE thousand < 995
+       ORDER BY thousand FETCH FIRST NULL ROWS ONLY;
+\d+ limit_thousand_v_5
 -- leave these views