Skip to content

Commit 7c6b9d8

Browse files
authored
support nested block statements and call statement in plpgsql (#2570)
1 parent f3a4abb commit 7c6b9d8

3 files changed

Lines changed: 89 additions & 4 deletions

File tree

server/plpgsql/json.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ type plpgSQL_stmt_block struct {
125125
LineNumber int32 `json:"lineno"`
126126
}
127127

128+
// plpgSQL_stmt_call exists to match the expected JSON format.
129+
type plpgSQL_stmt_call struct {
130+
LineNumber int32 `json:"lineno"`
131+
Expression expr `json:"expr"`
132+
IsCall bool `json:"is_call"`
133+
Target datum `json:"target"`
134+
}
135+
128136
// plpgSQL_stmt_case exists to match the expected JSON format.
129137
type plpgSQL_stmt_case struct {
130138
LineNumber int32 `json:"lineno"`
@@ -274,6 +282,8 @@ type sqlstmt struct {
274282
// having a singular expected implementation.
275283
type statement struct {
276284
Assignment *plpgSQL_stmt_assign `json:"PLpgSQL_stmt_assign"`
285+
Block *plpgSQL_stmt_block `json:"PLpgSQL_stmt_block"`
286+
Call *plpgSQL_stmt_call `json:"PLpgSQL_stmt_call"`
277287
Case *plpgSQL_stmt_case `json:"PLpgSQL_stmt_case"`
278288
DynExec *plpgSQL_stmt_dynexecute `json:"PLpgSQL_stmt_dynexecute"`
279289
ExecSQL *plpgSQL_stmt_execsql `json:"PLpgSQL_stmt_execsql"`
@@ -310,6 +320,29 @@ func (stmt *plpgSQL_stmt_assign) Convert() (Assignment, error) {
310320
}, nil
311321
}
312322

323+
// Convert converts the JSON statement into its output form.
324+
func (stmt *plpgSQL_stmt_call) Convert() (ExecuteSQL, error) {
325+
var target string
326+
if !stmt.IsCall {
327+
if stmt.Target.Row != nil {
328+
names := make([]string, len(stmt.Target.Row.Fields))
329+
for i, rowField := range stmt.Target.Row.Fields {
330+
names[i] = rowField.Name
331+
}
332+
target = strings.Join(names, ",")
333+
} else if stmt.Target.Variable != nil {
334+
target = stmt.Target.Variable.RefName
335+
} else {
336+
return ExecuteSQL{}, errors.Errorf("unhandled datum type: %T", stmt.Target)
337+
}
338+
}
339+
return ExecuteSQL{
340+
Statement: stmt.Expression.Expression.Query,
341+
Target: target,
342+
}, nil
343+
}
344+
345+
// Convert converts the JSON statement into its output form.
313346
func (stmt *plpgSQL_stmt_case) Convert() (block Block, err error) {
314347
// If the CASE statement has a main expression, start by assigning it to a variable so
315348
// we can evaluate it once and only once.
@@ -430,16 +463,15 @@ func (stmt *plpgSQL_stmt_dynexecute) Convert() (DynamicExecute, error) {
430463
func (stmt *plpgSQL_stmt_execsql) Convert() (ExecuteSQL, error) {
431464
var target string
432465
if stmt.Into {
433-
switch {
434-
case stmt.Target.Row != nil:
466+
if stmt.Target.Row != nil {
435467
names := make([]string, len(stmt.Target.Row.Fields))
436468
for i, rowField := range stmt.Target.Row.Fields {
437469
names[i] = rowField.Name
438470
}
439471
target = strings.Join(names, ",")
440-
case stmt.Target.Variable != nil:
472+
} else if stmt.Target.Variable != nil {
441473
target = stmt.Target.Variable.RefName
442-
default:
474+
} else {
443475
return ExecuteSQL{}, errors.Errorf("unhandled datum type: %T", stmt.Target)
444476
}
445477
}

server/plpgsql/json_convert.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ func jsonConvertStatement(stmt statement) (Statement, error) {
8585
switch {
8686
case stmt.Assignment != nil:
8787
return stmt.Assignment.Convert()
88+
case stmt.Block != nil:
89+
stmts, err := jsonConvertStatements(stmt.Block.Body)
90+
if err != nil {
91+
return Block{}, err
92+
}
93+
return Block{
94+
Body: stmts,
95+
}, nil
96+
case stmt.Call != nil:
97+
return stmt.Call.Convert()
8898
case stmt.Case != nil:
8999
return stmt.Case.Convert()
90100
case stmt.DynExec != nil:

testing/go/create_procedure_plpgsql_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,5 +441,48 @@ $$;`,
441441
},
442442
},
443443
},
444+
{
445+
Name: "use nested block statements and call statement in procedure body",
446+
SetUpScript: []string{
447+
`CREATE TABLE tbl (a int, b text);`,
448+
`CREATE PROCEDURE add_value(IN a int, IN b text)
449+
LANGUAGE plpgsql
450+
AS $$
451+
BEGIN
452+
INSERT INTO tbl VALUES (a, b);
453+
END;
454+
$$;`,
455+
},
456+
Assertions: []ScriptTestAssertion{
457+
{
458+
Query: `CREATE PROCEDURE check_and_add(IN i int, IN t text)
459+
LANGUAGE plpgsql
460+
AS $$
461+
DECLARE d text := t;
462+
BEGIN
463+
IF LENGTH(t) < 6 THEN
464+
d = t || ' is too short';
465+
END IF;
466+
467+
BEGIN
468+
CALL add_value(i, d);
469+
END;
470+
END;
471+
$$;`,
472+
},
473+
{
474+
Query: "CALL check_and_add(1, 'hi');",
475+
Expected: []sql.Row{},
476+
},
477+
{
478+
Query: "CALL check_and_add(3, 'hellooo');",
479+
Expected: []sql.Row{},
480+
},
481+
{
482+
Query: "SELECT * FROM tbl",
483+
Expected: []sql.Row{{1, "hi is too short"}, {3, "hellooo"}},
484+
},
485+
},
486+
},
444487
})
445488
}

0 commit comments

Comments
 (0)