From 6cd98d97f3fb923af2f041fbfc42334879a08a9c Mon Sep 17 00:00:00 2001 From: Chengxiong Ruan Date: Thu, 15 Dec 2022 16:12:55 -0500 Subject: [PATCH] sql: stmt_call, stmt_close, stmt_dynaexec, stmt_exit This commit adds the AST and grammar rules for `stmt_call`, `stmt_close`, `stmt_dynaexec`, and `stmt_exit` Release note: None --- pkg/sql/plpgsql/parser/lexer.go | 46 +++++++++++++++++ pkg/sql/plpgsql/parser/parser_test.go | 3 +- pkg/sql/plpgsql/parser/plpgsql.y | 33 ++++++------ pkg/sql/plpgsql/parser/testdata/stmt_call | 12 +++++ pkg/sql/plpgsql/parser/testdata/stmt_close | 10 ++++ pkg/sql/plpgsql/parser/testdata/stmt_dynexec | 54 ++++++++++++++++++++ pkg/sql/plpgsql/parser/testdata/stmt_exit | 12 +++++ pkg/sql/sem/plpgsqltree/statements.go | 16 +++++- 8 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 pkg/sql/plpgsql/parser/testdata/stmt_call create mode 100644 pkg/sql/plpgsql/parser/testdata/stmt_close create mode 100644 pkg/sql/plpgsql/parser/testdata/stmt_dynexec create mode 100644 pkg/sql/plpgsql/parser/testdata/stmt_exit diff --git a/pkg/sql/plpgsql/parser/lexer.go b/pkg/sql/plpgsql/parser/lexer.go index 1b73a27b181c..44e0bef0cf33 100644 --- a/pkg/sql/plpgsql/parser/lexer.go +++ b/pkg/sql/plpgsql/parser/lexer.go @@ -161,6 +161,52 @@ func (l *lexer) MakeExecSqlStmt(startTokenID int) *plpgsqltree.PLpgSQLStmtExecSq } } +func (l *lexer) MakeDynamicExecuteStmt() *plpgsqltree.PLpgSQLStmtDynamicExecute { + cmdStr, _ := l.ReadSqlConstruct(INTO, USING, ';') + ret := &plpgsqltree.PLpgSQLStmtDynamicExecute{ + Query: cmdStr, + } + + var lval plpgsqlSymType + l.Lex(&lval) + for { + if lval.id == INTO { + if ret.Into { + errors.AssertionFailedf("seen multiple INTO") + } + ret.Into = true + nextTok := l.Peek() + if nextTok.id == int32(STRICT) { + l.Lex(&lval) + ret.Strict = true + } + // TODO we need to read each "INTO" variable name instead of just a + // string. + l.ReadSqlExpressionStr2(USING, ';') + l.Lex(&lval) + } else if lval.id == USING { + if ret.Params != nil { + errors.AssertionFailedf("seen multiple USINGs") + } + ret.Params = make([]plpgsqltree.PLpgSQLExpr, 0) + for { + l.ReadSqlConstruct(',', ';', INTO) + ret.Params = append(ret.Params, nil) + l.Lex(&lval) + if lval.id == ';' { + break + } + } + } else if lval.id == ';' { + break + } else { + errors.AssertionFailedf("syntax error") + } + } + + return ret +} + // ReadSqlExpressionStr returns the string from the l.lastPos till it sees // the terminator for the first time. The returned string is made by tokens // between the starting index (included) to the terminator (not included). diff --git a/pkg/sql/plpgsql/parser/parser_test.go b/pkg/sql/plpgsql/parser/parser_test.go index bc2d33c1999d..a53e7c17e76b 100644 --- a/pkg/sql/plpgsql/parser/parser_test.go +++ b/pkg/sql/plpgsql/parser/parser_test.go @@ -22,9 +22,8 @@ import ( func TestParseDeclareSection(t *testing.T) { fn := ` DECLARE - ASSERT 1 > 2; - ASSERT 1 > 2 'error message' ; BEGIN + EXECUTE 'any command' INTO x1 USING x2; END` stmt, err := parser.Parse(fn) require.NoError(t, err) diff --git a/pkg/sql/plpgsql/parser/plpgsql.y b/pkg/sql/plpgsql/parser/plpgsql.y index f08916cfb09d..5f980e610e16 100644 --- a/pkg/sql/plpgsql/parser/plpgsql.y +++ b/pkg/sql/plpgsql/parser/plpgsql.y @@ -274,7 +274,7 @@ func (u *plpgsqlSymUnion) pLpgSQLStmtOpen() *plpgsqltree.PLpgSQLStmtOpen { %type <[]plpgsqltree.PLpgSQLStatement> proc_sect %type <[]plpgsqltree.PLpgSQLStatement> stmt_elsifs stmt_else // TODO is this a list of statement? %type loop_body -%type pl_block +%type pl_block %type proc_stmt %type stmt_assign stmt_if stmt_loop stmt_while stmt_exit %type stmt_return stmt_raise stmt_assert stmt_execsql @@ -595,15 +595,20 @@ stmt_perform : PERFORM } ; -stmt_call : CALL ';' +stmt_call : CALL call_cmd ';' { $$.val = &plpgsqltree.PLpgSQLStmtCall{IsCall: true} } - | DO ';' + | DO call_cmd ';' { $$.val = &plpgsqltree.PLpgSQLStmtCall{IsCall: false} } ; +call_cmd: +{ + plpgsqllex.(*lexer).ReadSqlExpressionStr(';') +} +; stmt_assign : T_DATUM { @@ -857,14 +862,10 @@ stmt_execsql : IMPORT } ; -// TODO(chengxiong): we should parse a valid expression, INTO and USING keywords -// instead of just match random symbols. -stmt_dynexecute : EXECUTE ';' - { - $$.val = &plpgsqltree.PLpgSQLStmtDynamicExecute{} - } - ; - +stmt_dynexecute : EXECUTE +{ + $$.val = plpgsqllex.(*lexer).MakeDynamicExecuteStmt()} +; stmt_open : OPEN cursor_variable { @@ -954,9 +955,11 @@ proc_condition : any_identifier } ; -expr_until_semi : ';' - { } - ; +expr_until_semi : +{ + plpgsqllex.(*lexer).ReadSqlExpressionStr(';') +} +; expr_until_then : { @@ -996,7 +999,7 @@ opt_label : opt_exitcond : ';' { } - | WHEN expr_until_semi + | WHEN expr_until_semi ';' { } ; diff --git a/pkg/sql/plpgsql/parser/testdata/stmt_call b/pkg/sql/plpgsql/parser/testdata/stmt_call new file mode 100644 index 000000000000..784921be8b99 --- /dev/null +++ b/pkg/sql/plpgsql/parser/testdata/stmt_call @@ -0,0 +1,12 @@ +parse +DECLARE +BEGIN + CALL fn(1); + DO $$ this is a code block $$; +END +---- +DECLARE +BEGIN +CALL a function/procedure +DO a code block +END diff --git a/pkg/sql/plpgsql/parser/testdata/stmt_close b/pkg/sql/plpgsql/parser/testdata/stmt_close new file mode 100644 index 000000000000..b61e63fc98bf --- /dev/null +++ b/pkg/sql/plpgsql/parser/testdata/stmt_close @@ -0,0 +1,10 @@ +parse +DECLARE +BEGIN + CLOSE some_cursor; +END +---- +DECLARE +BEGIN +CLOSE a cursor +END diff --git a/pkg/sql/plpgsql/parser/testdata/stmt_dynexec b/pkg/sql/plpgsql/parser/testdata/stmt_dynexec new file mode 100644 index 000000000000..dfc189d41ed8 --- /dev/null +++ b/pkg/sql/plpgsql/parser/testdata/stmt_dynexec @@ -0,0 +1,54 @@ +parse +DECLARE +BEGIN + EXECUTE 'any command'; +END +---- +DECLARE +BEGIN +EXECUTE a dynamic command +END + +parse +DECLARE +BEGIN + EXECUTE 'any command' INTO x1; +END +---- +DECLARE +BEGIN +EXECUTE a dynamic command WITH INTO +END + +parse +DECLARE +BEGIN + EXECUTE 'any command' INTO STRICT x1; +END +---- +DECLARE +BEGIN +EXECUTE a dynamic command WITH INTO STRICT +END + +parse +DECLARE +BEGIN + EXECUTE 'any command' INTO x1 USING x2; +END +---- +DECLARE +BEGIN +EXECUTE a dynamic command WITH INTO WITH USING +END + +parse +DECLARE +BEGIN + EXECUTE 'any command' INTO x1, x2 USING y1, y2; +END +---- +DECLARE +BEGIN +EXECUTE a dynamic command WITH INTO WITH USING +END diff --git a/pkg/sql/plpgsql/parser/testdata/stmt_exit b/pkg/sql/plpgsql/parser/testdata/stmt_exit new file mode 100644 index 000000000000..a6a0027593c5 --- /dev/null +++ b/pkg/sql/plpgsql/parser/testdata/stmt_exit @@ -0,0 +1,12 @@ +parse +DECLARE +BEGIN + EXIT some_label; + EXIT some_label WHEN some_condition; +END +---- +DECLARE +BEGIN +EXIT +EXIT +END diff --git a/pkg/sql/sem/plpgsqltree/statements.go b/pkg/sql/sem/plpgsqltree/statements.go index 48c8cd5ee7ec..d9e1c172a8f5 100644 --- a/pkg/sql/sem/plpgsqltree/statements.go +++ b/pkg/sql/sem/plpgsqltree/statements.go @@ -12,6 +12,7 @@ package plpgsqltree import ( "fmt" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" ) @@ -305,9 +306,10 @@ func (s *PLpgSQLStmtExecSql) Format(ctx *tree.FmtCtx) { } // stmt_dynexecute +// TODO(chengxiong): query should be a better expression type. type PLpgSQLStmtDynamicExecute struct { PLpgSQLStatementImpl - Query PLpgSQLExpr + Query string Into bool Strict bool Target PLpgSQLVariable @@ -315,7 +317,17 @@ type PLpgSQLStmtDynamicExecute struct { } func (s *PLpgSQLStmtDynamicExecute) Format(ctx *tree.FmtCtx) { - ctx.WriteString("EXECUTE a dynamic command\n") + ctx.WriteString("EXECUTE a dynamic command") + if s.Into { + ctx.WriteString(" WITH INTO") + if s.Strict { + ctx.WriteString(" STRICT") + } + } + if s.Params != nil { + ctx.WriteString(" WITH USING") + } + ctx.WriteString("\n") } // stmt_perform