Skip to content

Commit cc4b900

Browse files
committed
plpgsql/parser: add parser support for RETURN NEXT
This commit adds support for `RETURN NEXT` statements to the PL/pgSQL parser. A following commit will add execution support. Informs #105240 Release note: None
1 parent d53e88e commit cc4b900

File tree

6 files changed

+186
-63
lines changed

6 files changed

+186
-63
lines changed

pkg/sql/plpgsql/parser/lexer.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,17 @@ func makeDoStmt(options tree.DoBlockOptions) (*plpgsqltree.DoBlock, error) {
373373
return &plpgsqltree.DoBlock{Block: parsedStmt.AST}, nil
374374
}
375375

376+
// ParseReturnExpr handles reading and parsing the expression for a RETURN or
377+
// RETURN NEXT statement, which can be nonexistent.
378+
func (l *lexer) ParseReturnExpr() (plpgsqltree.Expr, error) {
379+
startPos, endPos, _, err := l.readSQLConstruct(true /* isExpr */, true /* allowEmpty */, ';')
380+
if err != nil || startPos == endPos {
381+
return nil, err
382+
}
383+
exprStr := l.getStr(startPos, endPos)
384+
return l.ParseExpr(exprStr)
385+
}
386+
376387
func (l *lexer) ReadSqlExpr(
377388
terminator1 int, terminators ...int,
378389
) (sqlStr string, terminatorMet int, err error) {
@@ -383,17 +394,6 @@ func (l *lexer) ReadSqlExpr(
383394
return l.getStr(startPos, endPos), terminatorMet, err
384395
}
385396

386-
// ReadReturnExpr handles reading the expression for a RETURN statement, which
387-
// can be nonexistent.
388-
func (l *lexer) ReadReturnExpr() (sqlStr string, err error) {
389-
var startPos, endPos int
390-
startPos, endPos, _, err = l.readSQLConstruct(true /* isExpr */, true /* allowEmpty */, ';')
391-
if err != nil || startPos == endPos {
392-
return "", err
393-
}
394-
return l.getStr(startPos, endPos), err
395-
}
396-
397397
func (l *lexer) ReadSqlStatement(
398398
terminator1 int, terminators ...int,
399399
) (sqlStr string, terminatorMet int, err error) {

pkg/sql/plpgsql/parser/plpgsql.y

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -338,14 +338,14 @@ func (u *plpgsqlSymUnion) doBlockOption() tree.DoBlockOption {
338338
}
339339

340340
%type <str> decl_varname decl_defkey
341-
%type <bool> decl_const decl_notnull
341+
%type <bool> decl_const decl_notnull
342342
%type <plpgsqltree.Expr> decl_defval decl_cursor_query
343343
%type <tree.ResolvableTypeReference> decl_datatype
344-
%type <str> decl_collate
344+
%type <str> decl_collate
345345

346-
%type <str> expr_until_semi expr_until_paren stmt_until_semi return_expr
346+
%type <str> expr_until_semi expr_until_paren stmt_until_semi
347347
%type <str> expr_until_then expr_until_loop opt_expr_until_when
348-
%type <plpgsqltree.Expr> opt_exitcond
348+
%type <plpgsqltree.Expr> return_expr opt_exitcond
349349

350350
%type <[]plpgsqltree.Variable> for_target
351351
%type <*tree.NumVal> foreach_slice
@@ -1157,19 +1157,11 @@ stmt_continue: CONTINUE opt_label opt_exitcond
11571157

11581158
stmt_return: RETURN return_expr ';'
11591159
{
1160-
var expr plpgsqltree.Expr
1161-
if $2 != "" {
1162-
var err error
1163-
expr, err = plpgsqllex.(*lexer).ParseExpr($2)
1164-
if err != nil {
1165-
return setErr(plpgsqllex, err)
1166-
}
1167-
}
1168-
$$.val = &plpgsqltree.Return{Expr: expr}
1160+
$$.val = &plpgsqltree.Return{Expr: $2.expr()}
11691161
}
1170-
| RETURN_NEXT NEXT
1162+
| RETURN_NEXT NEXT return_expr ';'
11711163
{
1172-
return unimplemented(plpgsqllex, "return next")
1164+
$$.val = &plpgsqltree.ReturnNext{Expr: $3.expr()}
11731165
}
11741166
| RETURN_QUERY QUERY
11751167
{
@@ -1179,11 +1171,11 @@ stmt_return: RETURN return_expr ';'
11791171

11801172
return_expr:
11811173
{
1182-
sqlStr, err := plpgsqllex.(*lexer).ReadReturnExpr()
1174+
retExpr, err := plpgsqllex.(*lexer).ParseReturnExpr()
11831175
if err != nil {
11841176
return setErr(plpgsqllex, err)
11851177
}
1186-
$$ = sqlStr
1178+
$$.val = retExpr
11871179
}
11881180
;
11891181

pkg/sql/plpgsql/parser/testdata/stmt_return

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -190,32 +190,6 @@ We appreciate your feedback.
190190
----
191191
----
192192

193-
error
194-
DECLARE
195-
BEGIN
196-
RETURN NEXT 1 + 1;
197-
END
198-
----
199-
----
200-
at or near "next": syntax error: unimplemented: this syntax
201-
DETAIL: source SQL:
202-
DECLARE
203-
BEGIN
204-
RETURN NEXT 1 + 1;
205-
^
206-
HINT: You have attempted to use a feature that is not yet implemented.
207-
208-
Please check the public issue tracker to check whether this problem is
209-
already tracked. If you cannot find it there, please report the error
210-
with details by creating a new issue.
211-
212-
If you would rather not post publicly, please contact us directly
213-
using the support form.
214-
215-
We appreciate your feedback.
216-
----
217-
----
218-
219193
error
220194
DECLARE
221195
BEGIN
@@ -263,22 +237,22 @@ BEGIN
263237
RETURN 1, 'string';
264238
END
265239
----
266-
at or near ";": syntax error: query returned 2 columns
240+
at or near "string": syntax error: query returned 2 columns
267241
DETAIL: source SQL:
268242
DECLARE
269243
BEGIN
270244
RETURN 1, 'string';
271-
^
245+
^
272246

273247
error
274248
DECLARE
275249
BEGIN
276250
RETURN 1, (2, 3, 4, 5);
277251
END
278252
----
279-
at or near ";": syntax error: query returned 2 columns
253+
at or near ")": syntax error: query returned 2 columns
280254
DETAIL: source SQL:
281255
DECLARE
282256
BEGIN
283257
RETURN 1, (2, 3, 4, 5);
284-
^
258+
^
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
parse
2+
BEGIN
3+
RETURN NEXT foo;
4+
END
5+
----
6+
BEGIN
7+
RETURN NEXT foo;
8+
END;
9+
-- normalized!
10+
BEGIN
11+
RETURN NEXT (foo);
12+
END;
13+
-- fully parenthesized
14+
BEGIN
15+
RETURN NEXT foo;
16+
END;
17+
-- literals removed
18+
BEGIN
19+
RETURN NEXT _;
20+
END;
21+
-- identifiers removed
22+
23+
parse
24+
BEGIN
25+
RETURN NEXT 1 + foo + 3;
26+
END
27+
----
28+
BEGIN
29+
RETURN NEXT (1 + foo) + 3;
30+
END;
31+
-- normalized!
32+
BEGIN
33+
RETURN NEXT ((((1) + (foo))) + (3));
34+
END;
35+
-- fully parenthesized
36+
BEGIN
37+
RETURN NEXT (_ + foo) + _;
38+
END;
39+
-- literals removed
40+
BEGIN
41+
RETURN NEXT (1 + _) + 3;
42+
END;
43+
-- identifiers removed
44+
45+
parse
46+
BEGIN
47+
RETURN NEXT;
48+
END
49+
----
50+
BEGIN
51+
RETURN NEXT;
52+
END;
53+
-- normalized!
54+
BEGIN
55+
RETURN NEXT;
56+
END;
57+
-- fully parenthesized
58+
BEGIN
59+
RETURN NEXT;
60+
END;
61+
-- literals removed
62+
BEGIN
63+
RETURN NEXT;
64+
END;
65+
-- identifiers removed
66+
67+
parse
68+
BEGIN
69+
RETURN NEXT (1 + 2) + 3;
70+
END
71+
----
72+
BEGIN
73+
RETURN NEXT (1 + 2) + 3;
74+
END;
75+
-- normalized!
76+
BEGIN
77+
RETURN NEXT (((((1) + (2)))) + (3));
78+
END;
79+
-- fully parenthesized
80+
BEGIN
81+
RETURN NEXT (_ + _) + _;
82+
END;
83+
-- literals removed
84+
BEGIN
85+
RETURN NEXT (1 + 2) + 3;
86+
END;
87+
-- identifiers removed
88+
89+
parse
90+
BEGIN
91+
RETURN NEXT (SELECT * FROM xy);
92+
END
93+
----
94+
BEGIN
95+
RETURN NEXT (SELECT * FROM xy);
96+
END;
97+
-- normalized!
98+
BEGIN
99+
RETURN NEXT ((SELECT (*) FROM xy));
100+
END;
101+
-- fully parenthesized
102+
BEGIN
103+
RETURN NEXT (SELECT * FROM xy);
104+
END;
105+
-- literals removed
106+
BEGIN
107+
RETURN NEXT (SELECT * FROM _);
108+
END;
109+
-- identifiers removed
110+
111+
error
112+
BEGIN
113+
RETURN NEXT (foo;
114+
END
115+
----
116+
at or near "EOF": syntax error: mismatched parentheses
117+
DETAIL: source SQL:
118+
BEGIN
119+
RETURN NEXT (foo;
120+
END
121+
^
122+
123+
error
124+
BEGIN
125+
RETURN NEXT SELECT * FROM xy;
126+
END
127+
----
128+
at or near "xy": at or near "select": syntax error
129+
DETAIL: source SQL:
130+
SET ROW (SELECT * FROM xy)
131+
^
132+
--
133+
source SQL:
134+
BEGIN
135+
RETURN NEXT SELECT * FROM xy;
136+
^
137+
HINT: try \h SET SESSION

pkg/sql/sem/plpgsqltree/statements.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -824,19 +824,30 @@ func (s *Return) WalkStmt(visitor StatementVisitor) Statement {
824824

825825
type ReturnNext struct {
826826
StatementImpl
827-
Expr Expr
828-
RetVar Variable
827+
Expr Expr
828+
}
829+
830+
func (s *ReturnNext) CopyNode() *ReturnNext {
831+
copyNode := *s
832+
return &copyNode
829833
}
830834

831835
func (s *ReturnNext) Format(ctx *tree.FmtCtx) {
836+
ctx.WriteString("RETURN NEXT")
837+
if s.Expr != nil {
838+
ctx.WriteByte(' ')
839+
ctx.FormatNode(s.Expr)
840+
}
841+
ctx.WriteString(";\n")
832842
}
833843

834844
func (s *ReturnNext) PlpgSQLStatementTag() string {
835845
return "stmt_return_next"
836846
}
837847

838848
func (s *ReturnNext) WalkStmt(visitor StatementVisitor) Statement {
839-
panic(unimplemented.New("plpgsql visitor", "Unimplemented PLpgSQL visitor pattern"))
849+
newStmt, _ := visitor.Visit(s)
850+
return newStmt
840851
}
841852

842853
type ReturnQuery struct {

pkg/sql/sem/plpgsqltree/visitor.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,16 @@ func (v *SQLStmtVisitor) Visit(stmt Statement) (newStmt Statement, recurse bool)
190190
cpy.Expr = e
191191
newStmt = cpy
192192
}
193+
case *ReturnNext:
194+
e, v.Err = v.visitExpr(t.Expr)
195+
if v.Err != nil {
196+
return stmt, false
197+
}
198+
if t.Expr != e {
199+
cpy := t.CopyNode()
200+
cpy.Expr = e
201+
newStmt = cpy
202+
}
193203
case *Raise:
194204
for i, p := range t.Params {
195205
e, v.Err = v.visitExpr(p)
@@ -287,8 +297,7 @@ func (v *SQLStmtVisitor) Visit(stmt Statement) (newStmt Statement, recurse bool)
287297
}
288298
}
289299

290-
case *ForEachArray, *ReturnNext,
291-
*ReturnQuery, *Perform:
300+
case *ForEachArray, *ReturnQuery, *Perform:
292301
panic(unimp.New("plpgsql visitor", "Unimplemented PLpgSQL visitor"))
293302
}
294303
if v.Err != nil {

0 commit comments

Comments
 (0)