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