declare cursor & condition

plpgsql_parse_declare change

condition keyword

strcasecmo change

error message

customCondition change
This commit is contained in:
nnuanyang
2022-11-07 17:34:42 -08:00
parent 93ec6efe7e
commit bb29f96079
6 changed files with 473 additions and 10 deletions

View File

@ -396,6 +396,7 @@ static void processFunctionRecordOutParam(int varno, Oid funcoid, int* outparam)
%type <fetch> opt_fetch_direction
%type <expr> fetch_limit_expr
%type <datum> fetch_into_target
%type <ival> condition_value
%type <keyword> unreserved_keyword
@ -441,6 +442,8 @@ static void processFunctionRecordOutParam(int varno, Oid funcoid, int* outparam)
%token T_CURSOR_FOUND
%token T_CURSOR_NOTFOUND
%token T_CURSOR_ROWCOUNT
%token T_DECLARE_CURSOR
%token T_DECLARE_CONDITION
/*
* Keyword tokens. Some of these are reserved and some are not;
@ -463,6 +466,7 @@ static void processFunctionRecordOutParam(int varno, Oid funcoid, int* outparam)
%token <keyword> K_COLLATE
%token <keyword> K_COLLECT
%token <keyword> K_COMMIT
%token <keyword> K_CONDITION
%token <keyword> K_CONSTANT
%token <keyword> K_CONTINUE
%token <keyword> K_CURRENT
@ -662,7 +666,7 @@ opt_semi :
| ';'
;
pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
pl_block : decl_sect begin_decl proc_sect exception_sect K_END opt_label
{
PLpgSQL_stmt_block *newp;
@ -688,6 +692,103 @@ pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
}
;
begin_decl : K_BEGIN
| K_BEGIN declare_stmts
;
declare_stmts : declare_stmts declare_stmt
| declare_stmt
;
declare_stmt : T_DECLARE_CURSOR decl_varname K_CURSOR opt_scrollable
{
IsInPublicNamespace($2->name);
plpgsql_ns_push($2->name);
}
decl_cursor_args decl_is_for decl_cursor_query
{
PLpgSQL_var *newp;
/* pop local namespace for cursor args */
plpgsql_ns_pop();
newp = (PLpgSQL_var *)
plpgsql_build_variable($2->name, $2->lineno,
plpgsql_build_datatype(REFCURSOROID,
-1,
InvalidOid),
true);
newp->cursor_explicit_expr = $8;
if ($6 == NULL)
newp->cursor_explicit_argrow = -1;
else
newp->cursor_explicit_argrow = $6->dno;
newp->cursor_options = CURSOR_OPT_FAST_PLAN | $4;
u_sess->plsql_cxt.plpgsql_yylloc = plpgsql_yylloc;
newp->datatype->cursorCompositeOid = IS_ANONYMOUS_BLOCK ?
InvalidOid : createCompositeTypeForCursor(newp, $8);
pfree_ext($2->name);
pfree($2);
}
| T_DECLARE_CONDITION decl_varname K_CONDITION K_FOR condition_value ';'
{
IsInPublicNamespace($2->name);
PLpgSQL_var *var;
var = (PLpgSQL_var *)plpgsql_build_variable($2->name, $2->lineno,
plpgsql_build_datatype(INT4OID,
-1,
InvalidOid),
true);
var->customCondition = $5;
pfree_ext($2->name);
pfree($2);
}
;
condition_value : any_identifier
{
if (strcmp($1, "sqlstate") == 0) {
/* next token should be a string literal */
char *sqlstatestr;
if (yylex() != SCONST)
yyerror("syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
yyerror("invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
yyerror("invalid SQLSTATE code");
if (strncmp(sqlstatestr, "00", 2) == 0) {
const char* message = "bad SQLSTATE";
InsertErrorMessage(message, plpgsql_yylloc);
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION),
errmsg("bad SQLSTATE '%s'",sqlstatestr)));
}
$$ = MAKE_SQLSTATE(sqlstatestr[0],
sqlstatestr[1],
sqlstatestr[2],
sqlstatestr[3],
sqlstatestr[4]);
} else {
yyerror("syntax error");
}
}
| ICONST
{
if ($1 == 0){
const char* message = "Incorrect CONDITION value: '0'";
InsertErrorMessage(message, plpgsql_yylloc);
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION),
errmsg("Incorrect CONDITION value: '0'")));
}
$$ = $1;
}
;
decl_sect : opt_block_label
{
@ -5582,6 +5683,7 @@ unreserved_keyword :
| K_BACKWARD
| K_CALL
| K_COMMIT
| K_CONDITION
| K_CONSTANT
| K_CONTINUE
| K_CURRENT

View File

@ -3429,6 +3429,7 @@ PLpgSQL_variable* plpgsql_build_variable(const char* refname, int lineno, PLpgSQ
var->lineno = lineno;
var->datatype = dtype;
var->pkg = NULL;
var->customCondition = 0;
/* other fields might be filled by caller */
/* preset to NULL */
@ -4258,15 +4259,30 @@ PLpgSQL_condition* plpgsql_parse_err_condition(char* condname)
}
if (prev == NULL) {
char message[MAXSTRLEN];
errno_t rc = 0;
rc = sprintf_s(message, MAXSTRLEN, "unrecognized exception condition \"%s\"", condname);
securec_check_ss(rc, "", "");
InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc);
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized exception condition \"%s\"", condname)));
PLpgSQL_nsitem* ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, condname, NULL, NULL, NULL);
if (ns != NULL) {
PLpgSQL_var* var = NULL;
var = (PLpgSQL_var*)(u_sess->plsql_cxt.curr_compile_context->plpgsql_Datums[ns->itemno]);
if (var->customCondition != 0) {
newm = (PLpgSQL_condition*)palloc(sizeof(PLpgSQL_condition));
newm->sqlerrstate = var->customCondition;
newm->condname = condname;
newm->next = prev;
prev = newm;
}
}
if (prev == NULL) {
char message[MAXSTRLEN];
errno_t rc = 0;
rc = sprintf_s(message, MAXSTRLEN, "unrecognized exception condition \"%s\"", condname);
securec_check_ss(rc, "", "");
InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc);
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized exception condition \"%s\"", condname)));
}
}
return prev;
}

View File

@ -124,6 +124,7 @@ static const ScanKeyword unreserved_keywords[] = {
PG_KEYWORD("call", K_CALL, UNRESERVED_KEYWORD)
PG_KEYWORD("collect", K_COLLECT, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", K_COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("condition", K_CONDITION, UNRESERVED_KEYWORD)
PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD)
PG_KEYWORD("continue", K_CONTINUE, UNRESERVED_KEYWORD)
PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD)
@ -226,6 +227,7 @@ static void push_back_token(int token, TokenAuxData* auxdata);
static void location_lineno_init(void);
static int get_self_defined_tok(int tok_flag);
static int plpgsql_parse_cursor_attribute(int* loc);
static int plpgsql_parse_declare(int* loc);
static void RecordDependencyOfPkg(PLpgSQL_package* pkg, Oid pkgOid, Oid currCompPkgOid);
static PLpgSQL_datum* SearchPackageDatum(PLpgSQL_package* pkg, Oid pkgOid, Oid currCompPkgOid,
const char* pkgname, const char* objname);
@ -258,6 +260,14 @@ int plpgsql_yylex(void)
plpgsql_yylloc = loc;
return tok1;
}
if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) {
/* parse declare condition */
tok1 = plpgsql_parse_declare(&loc);
if (tok1 != -1) {
plpgsql_yylloc = loc;
return tok1;
}
}
tok1 = internal_yylex(&aux1);
if (tok1 == IDENT || tok1 == PARAM || tok1 == T_SQL_BULK_EXCEPTIONS) {
@ -1120,6 +1130,55 @@ static int plpgsql_parse_cursor_attribute(int* loc)
return token;
}
static int plpgsql_parse_declare(int* loc)
{
TokenAuxData aux1;
int tok1 = -1;
int token = -1;
if (u_sess->parser_cxt.in_package_function_compile) {
return token;
}
tok1 = internal_yylex(&aux1);
if (tok1 == K_DECLARE) {
TokenAuxData aux2;
TokenAuxData aux3;
int tok2 = -1;
int tok3 = -1;
tok2 = internal_yylex(&aux2);
tok3 = internal_yylex(&aux3);
if (tok2 != IDENT || tok3 != IDENT) {
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
return token;
}
if (strcasecmp(aux3.lval.str, "cursor") == 0) {
token = T_DECLARE_CURSOR;
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
/* get the declare attribute location */
*loc = aux1.lloc;
plpgsql_yylval = aux1.lval;
} else if (strcasecmp(aux3.lval.str, "condition") == 0) {
token = T_DECLARE_CONDITION;
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
/* get the declare attribute location */
*loc = aux1.lloc;
plpgsql_yylval = aux1.lval;
} else {
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
}
} else {
push_back_token(tok1, &aux1);
}
return token;
}
/*
* a convenient method to see if the next two tokens are what we expected
*/

View File

@ -370,6 +370,7 @@ typedef struct PLpgSQL_var { /* Scalar variable */
PLpgSQL_expr* cursor_explicit_expr;
int cursor_explicit_argrow;
int cursor_options;
int customCondition; /* only for declare condition variable. */
Datum value;
bool isnull;

View File

@ -188,5 +188,177 @@ insert into t_func_trigger(rep) values('after insert');END;
/
drop trigger trigger_insert on t_trigger;
drop user vbadmin;
-- test declare cursor
create table company(name varchar(100), loc varchar(100), no integer);
insert into company values ('macrosoft', 'usa', 001);
insert into company values ('oracle', 'usa', 002);
insert into company values ('backberry', 'canada', 003);
create or replace procedure test_cursor_1
as
company_name varchar(100);
company_loc varchar(100);
company_no integer;
begin
declare c1_all cursor is --cursor without args
select name, loc, no from company order by 1, 2, 3;
if not c1_all%isopen then
open c1_all;
end if;
loop
fetch c1_all into company_name, company_loc, company_no;
exit when c1_all%notfound;
raise notice '% : % : %',company_name,company_loc,company_no;
end loop;
if c1_all%isopen then
close c1_all;
end if;
end;
/
call test_cursor_1();
NOTICE: backberry : canada : 3
NOTICE: macrosoft : usa : 1
NOTICE: oracle : usa : 2
test_cursor_1
---------------
(1 row)
-- test declare condition
create or replace procedure test_condition_1 as
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END;
/
call test_condition_1();
NOTICE: SQLSTATE = 22012, SQLERRM = division by zero
test_condition_1
------------------
(1 row)
-- test rename condition
create or replace procedure test_condition_2 as
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
declare DIVISION_ZERO_two condition for SQLSTATE '22012';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END;
/
call test_condition_2();
NOTICE: SQLSTATE = 22012, SQLERRM = division by zero
test_condition_2
------------------
(1 row)
-- test reuse condition name
create or replace procedure test_condition_3 as
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
declare DIVISION_ZERO condition for SQLSTATE '22005';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END;
/
ERROR: syntax error at or near "DIVISION_ZERO"
LINE 6: declare DIVISION_ZERO condition for SQLSTATE '22005';
^
QUERY:
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
declare DIVISION_ZERO condition for SQLSTATE '22005';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END
-- declare condition sqlcode
create or replace procedure test_condition_4 as
BEGIN
declare DIVISION_ZERO condition for 1;
RAISE NOTICE 'declare condition successed';
END;
/
call test_condition_4();
NOTICE: declare condition successed
test_condition_4
------------------
(1 row)
-- declare condition sqlcode 0
create or replace procedure test_condition_5 as
BEGIN
declare DIVISION_ZERO condition for 0;
RAISE NOTICE 'declare condition successed';
END;
/
ERROR: Incorrect CONDITION value: '0'
CONTEXT: compilation of PL/pgSQL function "test_condition_5" near line 3
-- declare condition sqlstate begin with '00'
create or replace procedure test_condition_6 as
BEGIN
declare DIVISION_ZERO condition for sqlstate '00000';
RAISE NOTICE 'declare condition successed';
END;
/
ERROR: bad SQLSTATE '00000'
CONTEXT: compilation of PL/pgSQL function "test_condition_6" near line 3
\c regression
drop database db_mysql;
-- test declare condition in other compatibility
create or replace procedure test_condition_1 as
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END;
/
ERROR: syntax error at or near "SQLSTATE"
LINE 5: declare DIVISION_ZERO condition for SQLSTATE '22012';
^
QUERY:
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END
CONTEXT: invalid type name "condition for SQLSTATE '22012'"

View File

@ -164,5 +164,118 @@ insert into t_func_trigger(rep) values('after insert');END;
/
drop trigger trigger_insert on t_trigger;
drop user vbadmin;
-- test declare cursor
create table company(name varchar(100), loc varchar(100), no integer);
insert into company values ('macrosoft', 'usa', 001);
insert into company values ('oracle', 'usa', 002);
insert into company values ('backberry', 'canada', 003);
create or replace procedure test_cursor_1
as
company_name varchar(100);
company_loc varchar(100);
company_no integer;
begin
declare c1_all cursor is --cursor without args
select name, loc, no from company order by 1, 2, 3;
if not c1_all%isopen then
open c1_all;
end if;
loop
fetch c1_all into company_name, company_loc, company_no;
exit when c1_all%notfound;
raise notice '% : % : %',company_name,company_loc,company_no;
end loop;
if c1_all%isopen then
close c1_all;
end if;
end;
/
call test_cursor_1();
-- test declare condition
create or replace procedure test_condition_1 as
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END;
/
call test_condition_1();
-- test rename condition
create or replace procedure test_condition_2 as
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
declare DIVISION_ZERO_two condition for SQLSTATE '22012';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END;
/
call test_condition_2();
-- test reuse condition name
create or replace procedure test_condition_3 as
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
declare DIVISION_ZERO condition for SQLSTATE '22005';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END;
/
-- declare condition sqlcode
create or replace procedure test_condition_4 as
BEGIN
declare DIVISION_ZERO condition for 1;
RAISE NOTICE 'declare condition successed';
END;
/
call test_condition_4();
-- declare condition sqlcode 0
create or replace procedure test_condition_5 as
BEGIN
declare DIVISION_ZERO condition for 0;
RAISE NOTICE 'declare condition successed';
END;
/
-- declare condition sqlstate begin with '00'
create or replace procedure test_condition_6 as
BEGIN
declare DIVISION_ZERO condition for sqlstate '00000';
RAISE NOTICE 'declare condition successed';
END;
/
\c regression
drop database db_mysql;
-- test declare condition in other compatibility
create or replace procedure test_condition_1 as
declare
a int;
BEGIN
declare DIVISION_ZERO condition for SQLSTATE '22012';
a := 1/0;
exception
when DIVISION_ZERO then
BEGIN
RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM;
END;
END;
/