postgres.git / summary / log / commit / refs

commit    72fe74ca533375fa04264f976d4c4971c6804dce
Author:   Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date:     Mon May 19 15:50:26 2025 +0000
First released in: 18.0, 13.22, 14.19, 15.14, 16.10, 17.6

    Fix deparsing FETCH FIRST <expr> ROWS WITH TIES
    
    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 | 10 ++++++++++ src/test/regress/expected/limit.out | 20 +++++++++++++++++--- src/test/regress/sql/limit.sql | 5 ++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 035faf418f0..59c9fba3827 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -5772,9 +5772,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 diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8eb..d10c912d12e 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -643,7 +643,7 @@ View definition: WHERE onek.thousand < 995 ORDER BY onek.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 onek.thousand < 995 ORDER BY onek.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 onek.thousand + FROM onek + WHERE onek.thousand < 995 + ORDER BY onek.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 onek.thousand FROM onek diff --git a/src/test/regress/sql/limit.sql b/src/test/regress/sql/limit.sql index 6f0cda98701..603910fe6d1 100644 --- a/src/test/regress/sql/limit.sql +++ b/src/test/regress/sql/limit.sql @@ -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 [parent: 9d1a62359606]