Skip to content

Commit

Permalink
plpgsql: implement parsing for RAISE statements
Browse files Browse the repository at this point in the history
This patch adds parser support for PLpgSQL `RAISE` statements. This includes
all syntax forms apart from the empty `RAISE`, which is only valid in
combination with (currently unimplemented) `EXCEPTION` blocks. A future
commit will add support in the optbuilder as well.

Informs #105251

Release note: None
  • Loading branch information
DrewKimball committed Jul 20, 2023
1 parent 3f12d60 commit df37e00
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 48 deletions.
172 changes: 139 additions & 33 deletions pkg/sql/plpgsql/parser/plpgsql.y
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ func (u *plpgsqlSymUnion) plpgsqlExpr() plpgsqltree.PLpgSQLExpr {
return u.val.(plpgsqltree.PLpgSQLExpr)
}

func (u *plpgsqlSymUnion) plpgsqlExprs() []plpgsqltree.PLpgSQLExpr {
return u.val.([]plpgsqltree.PLpgSQLExpr)
}

func (u *plpgsqlSymUnion) plpgsqlDecl() *plpgsqltree.PLpgSQLDecl {
return u.val.(*plpgsqltree.PLpgSQLDecl)
}
Expand All @@ -137,6 +141,14 @@ func (u *plpgsqlSymUnion) plpgsqlDecls() []plpgsqltree.PLpgSQLDecl {
return u.val.([]plpgsqltree.PLpgSQLDecl)
}

func (u *plpgsqlSymUnion) plpgsqlOptionExpr() *plpgsqltree.PLpgSQLStmtRaiseOption {
return u.val.(*plpgsqltree.PLpgSQLStmtRaiseOption)
}

func (u *plpgsqlSymUnion) plpgsqlOptionExprs() []plpgsqltree.PLpgSQLStmtRaiseOption {
return u.val.([]plpgsqltree.PLpgSQLStmtRaiseOption)
}

%}
/*
* Basic non-keyword token types. These are hard-wired into the core lexer.
Expand Down Expand Up @@ -299,7 +311,8 @@ func (u *plpgsqlSymUnion) plpgsqlDecls() []plpgsqltree.PLpgSQLDecl {
%type <*tree.NumVal> foreach_slice
%type <plpgsqltree.PLpgSQLStatement> for_control

%type <str> any_identifier opt_block_label opt_loop_label opt_label query_options
%type <str> any_identifier opt_block_label opt_loop_label opt_label query_options
%type <str> opt_error_level option_type

%type <[]plpgsqltree.PLpgSQLStatement> proc_sect
%type <[]*plpgsqltree.PLpgSQLStmtIfElseIfArm> stmt_elsifs
Expand Down Expand Up @@ -329,6 +342,11 @@ func (u *plpgsqlSymUnion) plpgsqlDecls() []plpgsqltree.PLpgSQLDecl {
%type <*plpgsqltree.PLpgSQLStmtGetDiagItem> getdiag_list_item // TODO don't know what this is
%type <int32> getdiag_item

%type <*plpgsqltree.PLpgSQLStmtRaiseOption> option_expr
%type <[]plpgsqltree.PLpgSQLStmtRaiseOption> option_exprs opt_option_exprs
%type <plpgsqltree.PLpgSQLExpr> format_expr
%type <[]plpgsqltree.PLpgSQLExpr> opt_format_exprs format_exprs

%type <uint32> opt_scrollable

%type <*plpgsqltree.PLpgSQLStmtFetch> opt_fetch_direction
Expand Down Expand Up @@ -627,7 +645,9 @@ proc_stmt:pl_block ';'
$$.val = $1.plpgsqlStatement()
}
| stmt_raise
{ }
{
$$.val = $1.plpgsqlStatement()
}
| stmt_assert
{
$$.val = $1.plpgsqlStatement()
Expand Down Expand Up @@ -1017,58 +1037,143 @@ return_variable: expr_until_semi
}
;

stmt_raise: RAISE error_level ';'
stmt_raise:
RAISE ';'
{
return unimplemented(plpgsqllex, "empty RAISE statement")
}
| RAISE opt_error_level SCONST opt_format_exprs opt_option_exprs ';'
{
$$.val = &plpgsqltree.PLpgSQLStmtRaise{
LogLevel: $2,
Message: $3,
Params: $4.plpgsqlExprs(),
Options: $5.plpgsqlOptionExprs(),
}
}
| RAISE opt_error_level IDENT opt_option_exprs ';'
{
$$.val = &plpgsqltree.PLpgSQLStmtRaise{
LogLevel: $2,
CodeName: $3,
Options: $4.plpgsqlOptionExprs(),
}
}
| RAISE opt_error_level SQLSTATE SCONST opt_option_exprs ';'
{
$$.val = &plpgsqltree.PLpgSQLStmtRaise{
LogLevel: $2,
Code: $4,
Options: $5.plpgsqlOptionExprs(),
}
}
| RAISE opt_error_level USING option_exprs ';'
{
$$.val = &plpgsqltree.PLpgSQLStmtRaise{
LogLevel: $2,
Options: $4.plpgsqlOptionExprs(),
}
}
;

error_level:
opt_error_level:
DEBUG
| LOG
| INFO
| NOTICE
| WARNING
| EXCEPTION
| /* EMPTY */
{
return unimplemented(plpgsqllex, "raise")
$$ = ""
}
| EXCEPTION raise_cond option_expr
;

opt_option_exprs:
USING option_exprs
{
return unimplemented(plpgsqllex, "raise exception")
$$.val = $2.plpgsqlOptionExprs()
}
| DEBUG raise_cond option_expr
| /* EMPTY */
{
return unimplemented(plpgsqllex, "raise debug")
$$.val = []plpgsqltree.PLpgSQLStmtRaiseOption{}
}
| LOG raise_cond option_expr
;

option_exprs:
option_exprs ',' option_expr
{
return unimplemented(plpgsqllex, "raise log")
option := $3.plpgsqlOptionExpr()
$$.val = append($1.plpgsqlOptionExprs(), *option)
}
| INFO raise_cond option_expr
| option_expr
{
return unimplemented(plpgsqllex, "raise info")
option := $1.plpgsqlOptionExpr()
$$.val = []plpgsqltree.PLpgSQLStmtRaiseOption{*option}
}
| WARNING raise_cond option_expr
;

option_expr:
option_type assign_operator
{
return unimplemented(plpgsqllex, "raise warning")
// Read until reaching one of the tokens that can follow a raise option.
sqlStr, _ := plpgsqllex.(*lexer).ReadSqlConstruct(',', ';')
optionExpr, err := plpgsqllex.(*lexer).ParseExpr(sqlStr)
if err != nil {
return setErr(plpgsqllex, err)
}
$$.val = &plpgsqltree.PLpgSQLStmtRaiseOption{
OptType: $1,
Expr: optionExpr,
}
}
;

raise_cond:
{}
| SCONST
{}
| SQLSTATE
{}
| IDENT
{}
option_type:
MESSAGE
| DETAIL
| HINT
| ERRCODE
| COLUMN
| CONSTRAINT
| DATATYPE
| TABLE
| SCHEMA
;

opt_format_exprs:
format_exprs
{
$$.val = $1.plpgsqlExprs()
}
| /* EMPTY */
{
$$.val = []plpgsqltree.PLpgSQLExpr{}
}
;

option_expr:
{}
| USING MESSAGE assign_operator expr_until_semi
{}
| USING DETAIL assign_operator expr_until_semi
{}
| USING HINT assign_operator expr_until_semi
{}
| USING ERRCODE assign_operator expr_until_semi
{}
format_exprs:
format_expr
{
$$.val = []plpgsqltree.PLpgSQLExpr{$1.plpgsqlExpr()}
}
| format_exprs format_expr
{
$$.val = append($1.plpgsqlExprs(), $2.plpgsqlExpr())
}
;

format_expr: ','
{
// Read until reaching a token that can follow a raise format parameter.
sqlStr, _ := plpgsqllex.(*lexer).ReadSqlConstruct(',', ';', USING)
param, err := plpgsqllex.(*lexer).ParseExpr(sqlStr)
if err != nil {
return setErr(plpgsqllex, err)
}
$$.val = param
}
;

stmt_assert: ASSERT assert_cond ';'
{
Expand All @@ -1083,6 +1188,7 @@ assert_cond:
plpgsqllex.(*lexer).ReadSqlExpressionStr(';')
}
}
;

loop_body: proc_sect END LOOP
{
Expand Down
101 changes: 92 additions & 9 deletions pkg/sql/plpgsql/parser/testdata/stmt_raise
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,40 @@ END
----
expected parse error: at or near ";": syntax error: unimplemented: this syntax


parse
DECLARE
BEGIN
RAISE EXCEPTION USING MESSAGE = "why is this so involved?";
END
----
expected parse error: at or near "why is this so involved?": syntax error: unimplemented: this syntax

DECLARE
BEGIN
RAISE exception
USING MESSAGE = "why is this so involved?";
END

parse
DECLARE
BEGIN
RAISE LOG USING HINT = "Insert HINT";
END
----
expected parse error: at or near "Insert HINT": syntax error: unimplemented: this syntax
DECLARE
BEGIN
RAISE log
USING HINT = "Insert HINT";
END

parse
DECLARE
BEGIN
RAISE LOG 'Nonexistent ID --> %', user_id;
END
----
expected parse error: at or near ",": syntax error: unimplemented: this syntax
DECLARE
BEGIN
RAISE log 'Nonexistent ID --> %', user_id;
END

parse
DECLARE
Expand All @@ -39,22 +48,96 @@ BEGIN
USING HINT = "check...userid?" ;
END
----
expected parse error: at or near ",": syntax error: unimplemented: this syntax
DECLARE
BEGIN
RAISE log 'Nonexistent ID --> %', user_id
USING HINT = "check...userid?";
END

parse
DECLARE
BEGIN
RAISE 'foo %', 'bar';
END
----
DECLARE
BEGIN
RAISE 'foo %', 'bar';
END

parse
DECLARE
i INT := 0;
BEGIN
RAISE SQLSTATE '222222' USING HINT = "hm";
RAISE 'foo %', i;
END
----
expected parse error: at or near "sqlstate": syntax error: unimplemented: this syntax
DECLARE
i INT8 := 0;
BEGIN
RAISE 'foo %', i;
END

parse
DECLARE
i INT := 0;
BEGIN
RAISE 'foo %, %, %.', i, i*2, i*100;
END
----
DECLARE
i INT8 := 0;
BEGIN
RAISE 'foo %, %, %.', i, i * 2, i * 100;
END

parse
DECLARE
i INT := 0;
BEGIN
RAISE 'foo %', (SELECT count(*) FROM xy WHERE x = i);
END
----
DECLARE
i INT8 := 0;
BEGIN
RAISE 'foo %', (SELECT count(*) FROM xy WHERE x = i);
END

parse
DECLARE
BEGIN
RAISE SQLSTATE '222222' USING HINT = "hm";
END
----
DECLARE
BEGIN
RAISE SQLSTATE '222222'
USING HINT = hm;
END

parse
DECLARE
BEGIN
RAISE internal_screaming;
END
----
expected parse error: at or near "internal_screaming": syntax error: unimplemented: this syntax
DECLARE
BEGIN
RAISE internal_screaming;
END

parse
DECLARE
BEGIN
RAISE internal_screaming
USING MESSAGE = 'blah blah blah',
COLUMN = 'foo';
END
----
DECLARE
BEGIN
RAISE internal_screaming
USING MESSAGE = 'blah blah blah',
COLUMN = 'foo';
END
4 changes: 0 additions & 4 deletions pkg/sql/sem/plpgsqltree/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ package plpgsqltree

import "github.com/cockroachdb/errors"

// PLpgSQLRaiseOptionType represents the severity of the error in
// a raise statement.
type PLpgSQLRaiseOptionType int

// PLpgSQLGetDiagKind represents the type of error diagnostic
// item in stmt_getdiag.
type PLpgSQLGetDiagKind int
Expand Down
Loading

0 comments on commit df37e00

Please sign in to comment.