From a14bbfbee9859fdd84adf0088c601121bb2a1a0c Mon Sep 17 00:00:00 2001 From: Boris Tyshkevich Date: Thu, 24 Sep 2026 10:53:36 +0200 Subject: [PATCH] parser: distinguish scalar format calls from output clauses --- parser/altinity_format_test.go | 27 +++++++++++++++++++++++++++ parser/parser_column.go | 5 +++++ 2 files changed, 32 insertions(+) create mode 100644 parser/altinity_format_test.go diff --git a/parser/altinity_format_test.go b/parser/altinity_format_test.go new file mode 100644 index 0000000..f9c1886 --- /dev/null +++ b/parser/altinity_format_test.go @@ -0,0 +1,27 @@ +package parser + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSelectFormatFunctionAndOutputClause(t *testing.T) { + for _, sql := range []string{ + "SELECT format('{}', 1)", + "SELECT 1, format('{}', 1)", + "SELECT 1, format('{}', 1) FORMAT JSON", + "SELECT 1 FORMAT JSON", + } { + t.Run(sql, func(t *testing.T) { + stmts, err := NewParser(sql).ParseStmts() + require.NoError(t, err) + require.Len(t, stmts, 1) + formatted := Format(stmts[0]) + again, err := NewParser(formatted).ParseStmts() + require.NoError(t, err) + require.Len(t, again, 1) + require.Equal(t, formatted, Format(again[0])) + }) + } +} diff --git a/parser/parser_column.go b/parser/parser_column.go index b630193..c850694 100644 --- a/parser/parser_column.go +++ b/parser/parser_column.go @@ -517,6 +517,11 @@ func (p *Parser) keywordIsSelectItemIdentifier() bool { // that begins a clause following the SELECT item list. When true, we should // not treat the keyword itself as a bare alias. func (p *Parser) isSelectItemTerminatorKeyword() bool { + // FORMAT followed by '(' is a scalar function in the next SELECT item, + // not the output FORMAT clause. The latter takes a format name. + if p.matchKeyword(KeywordFormat) && p.peekTokenKind(TokenKindLParen) { + return false + } if p.keywordIsSelectItemIdentifier() { return false }