Re: Row pattern recognition

From: Henson Choi <assam258(at)gmail(dot)com>
To: jian he <jian(dot)universality(at)gmail(dot)com>, Tatsuo Ishii <ishii(at)postgresql(dot)org>
Cc: zsolt(dot)parragi(at)percona(dot)com, sjjang112233(at)gmail(dot)com, vik(at)postgresfriends(dot)org, er(at)xs4all(dot)nl, jacob(dot)champion(at)enterprisedb(dot)com, david(dot)g(dot)johnston(at)gmail(dot)com, peter(at)eisentraut(dot)org, li(dot)evan(dot)chao(at)gmail(dot)com, pgsql-hackers(at)postgresql(dot)org
Subject: Re: Row pattern recognition
Date: 2026-06-19 00:43:10
Message-ID: CAAAe_zAe8CRN-ggNhcP7b-ALiRrHxKTXBWbNVMhdW_QoO=1c4Q@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Jian,

> ParseFuncOrColumn cleanly handles (f).prev by translating it to prev(f)
as a
> regular function call. However, if a dedicated window navigation function
> exists, this translation creates ambiguity -- it becomes unclear whether
> prev(f) is window navigation or a normal function call.

Agreed, and that is the reason I would rather not have a dedicated
navigation function at all. With navigation handled purely as syntax,
(f).prev never has to compete with it: attribute notation just falls
through to an ordinary function (prev(f)), the same inside and outside a
DEFINE clause. That is Tatsuo's and my preferred option (a), so I will
settle on it.

> Cases with additional dots (e.g., public.prev(arg)) should also be treated
> as normal function calls, IMHO.

Yes. A schema-qualified name is the explicit escape hatch to an ordinary
function; navigation is recognized only for an unqualified, single-element
name.

> As a result, only prev(arg) and prev(arg, offset) are recognized as
special
> window navigation syntax, despite being visually identical to a function
> call.

Right -- that is exactly the surface I want to land on: the dotless
prev(...)/next(...)/first(...)/last(...) forms are navigation, everything
else is an ordinary function.

> Summary:
> Dedicated window navigation functions should be removed entirely. Window
> navigation should be limited to a single syntactic form (no dots) -- one
> that *looks* like a function call but is parsed as syntax.

That is the direction I will take the tree. So the three of us have
converged.

> This is not unprecedented; there are many existing cases where something
> appears to be a function call but is actually a syntax form, for example:
>
> SELECT json_object('{}');
> json_object
> -------------
> {}
> (1 row)
>
> SELECT public.json_object('{}');
> ERROR: function public.json_object(unknown) does not exist
> LINE 1: SELECT public.json_object('{}');
> ^
>
> So I think in the DEFINE context, it makes sense for some form that looks
> like a function call to actually be syntax.

The json_object parallel is exact at the user-visible level, but I want to
flag that the implementation has to differ underneath -- and that is
actually why navigation is not done the json_object way.

json_object can live in the grammar because two constraints do not apply to
it:

- No context restriction. JSON_OBJECT means the same thing wherever a
value expression is allowed, so a single keyword production in the
shared a_expr grammar is enough.

- The name is safe to reserve. Making json_object a keyword costs almost
nobody an identifier.

Row pattern navigation has both constraints:

- It must mean navigation *only* inside DEFINE; everywhere else
prev/next/first/last are ordinary names. bison is LALR(1) and
context-free, so a production cannot be conditioned on "are we inside
DEFINE"; a_expr is shared by SELECT lists, WHERE, etc. And a row
pattern definition is "ColId AS a_expr", so navigation can appear
anywhere a value expression can. Special-casing it in the grammar
would mean duplicating the whole a_expr tree into a second DEFINE-only
expression grammar (or resorting to lexer feedback), which is not worth
it.

- The names are common. prev/next/first/last (especially next, first,
last) are everyday column and function names; reserving them globally
would break existing queries.

So the plan is the opposite of a grammar keyword: parse navigation as an
ordinary FuncCall and reinterpret it in parse analysis, gated on
p_expr_kind == EXPR_KIND_RPR_DEFINE and an unqualified, single-element
name. That keeps one expression grammar, leaves the names free everywhere
else, and confines the special meaning to exactly the DEFINE context -- the
same "looks like a function call, parsed as syntax" behavior you described,
reached by a lighter path that does not grow the keyword list.

Thanks,
Henson

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Richard Guo 2026-06-19 01:13:25 Prove a NOT IN's left-hand expressions non-nullable from quals
Previous Message jian he 2026-06-19 00:07:53 Re: Row pattern recognition