From 459304775ffcba76b91179dfd2c2c456cb009e16 Mon Sep 17 00:00:00 2001 From: nnuanyang Date: Wed, 29 Mar 2023 20:14:48 -0700 Subject: [PATCH] declare condition --- src/common/pl/plpgsql/src/gram.y | 119 ++++++++++++++++++--- src/common/pl/plpgsql/src/pl_comp.cpp | 36 +++---- src/common/pl/plpgsql/src/pl_exec.cpp | 5 +- src/include/knl/knl_session.h | 1 + src/include/utils/plpgsql.h | 3 - src/test/regress/expected/mysql_syntax.out | 46 ++++---- src/test/regress/sql/mysql_syntax.sql | 15 +++ 7 files changed, 170 insertions(+), 55 deletions(-) diff --git a/src/common/pl/plpgsql/src/gram.y b/src/common/pl/plpgsql/src/gram.y index e579eabd1..030fff196 100755 --- a/src/common/pl/plpgsql/src/gram.y +++ b/src/common/pl/plpgsql/src/gram.y @@ -222,6 +222,7 @@ static AttrNumber get_assign_attrno(PLpgSQL_datum* target, char* attrname); static void raw_parse_package_function(char* proc_str, int location, int leaderlen); static void checkFuncName(List* funcname); static void IsInPublicNamespace(char* varname); +static void CheckDuplicateCondition (char* name); static void SetErrorState(); static void AddNamespaceIfNeed(int dno, char* ident); static void AddNamespaceIfPkgVar(const char* ident, IdentifierLookup save_IdentifierLookup); @@ -346,7 +347,7 @@ static void processFunctionRecordOutParam(int varno, Oid funcoid, int* outparam) %type assign_el %type decl_sect -%type decl_varname +%type decl_varname declare_condname %type decl_varname_list %type decl_const decl_notnull exit_type %type decl_defval decl_rec_defval decl_cursor_query @@ -808,7 +809,7 @@ declare_stmt : T_DECLARE_CURSOR decl_varname K_CURSOR opt_scrollable pfree($2); $$ = NULL; } - | T_DECLARE_CONDITION decl_varname K_CONDITION K_FOR condition_value ';' + | T_DECLARE_CONDITION declare_condname K_CONDITION K_FOR condition_value ';' { int tok = -1; plpgsql_peek(&tok); @@ -827,17 +828,17 @@ declare_stmt : T_DECLARE_CURSOR decl_varname K_CURSOR opt_scrollable } } - IsInPublicNamespace($2->name); - PLpgSQL_var *var; + CheckDuplicateCondition($2->name); + PLpgSQL_condition* cond = $5; + cond->condname = pstrdup($2->name); + PLpgSQL_condition* old = u_sess->plsql_cxt.curr_compile_context->plpgsql_conditions; + if (old != NULL) { + cond->next = old; + u_sess->plsql_cxt.curr_compile_context->plpgsql_conditions = cond; + } else { + u_sess->plsql_cxt.curr_compile_context->plpgsql_conditions = cond; + } - var = (PLpgSQL_var *)plpgsql_build_variable($2->name, $2->lineno, - plpgsql_build_datatype(INT4OID, - -1, - InvalidOid), - true); - var->customCondition = $5->sqlerrstate; - var->sqlstateCondition = $5->sqlstate; - var->isSqlvalue = $5->isSqlvalue; pfree_ext($2->name); pfree($2); $$ = NULL; @@ -970,6 +971,83 @@ cond_element : any_identifier } ; +declare_condname: T_WORD + { + VarName* varname = NULL; + varname = (VarName *)palloc0(sizeof(VarName)); + varname->name = $1.ident; + varname->lineno = plpgsql_location_to_lineno(@1); + $$ = varname; + } + | unreserved_keyword + { + VarName* varname = NULL; + varname = (VarName *)palloc0(sizeof(VarName)); + varname->name = pstrdup($1); + varname->lineno = plpgsql_location_to_lineno(@1); + $$ = varname; + } + | T_VARRAY + { + VarName* varname = NULL; + varname = (VarName *)palloc0(sizeof(VarName)); + varname->name = pstrdup($1.ident); + varname->lineno = plpgsql_location_to_lineno(@1); + $$ = varname; + + } + | T_RECORD + { + VarName* varname = NULL; + varname = (VarName *)palloc0(sizeof(VarName)); + varname->name = pstrdup($1.ident); + varname->lineno = plpgsql_location_to_lineno(@1); + $$ = varname; + + } + | T_TABLE + { + VarName* varname = NULL; + varname = (VarName *)palloc0(sizeof(VarName)); + varname->name = pstrdup($1.ident); + varname->lineno = plpgsql_location_to_lineno(@1); + $$ = varname; + + } + | T_REFCURSOR + { + VarName* varname = NULL; + varname = (VarName *)palloc0(sizeof(VarName)); + varname->name = pstrdup($1.ident); + varname->lineno = plpgsql_location_to_lineno(@1); + $$ = varname; + + } + | T_TABLE_VAR + { + VarName* varname = NULL; + if ($1.idents != NIL) { + yyerror("syntax error"); + } + varname = (VarName *)palloc0(sizeof(VarName)); + varname->name = pstrdup($1.ident); + varname->lineno = plpgsql_location_to_lineno(@1); + $$ = varname; + + } + | T_VARRAY_VAR + { + VarName* varname = NULL; + if ($1.idents != NIL || strcmp($1.ident, "bulk_exceptions") == 0) { + yyerror("syntax error"); + } + varname = (VarName *)palloc0(sizeof(VarName)); + varname->name = pstrdup($1.ident); + varname->lineno = plpgsql_location_to_lineno(@1); + $$ = varname; + + } + ; condition_value : K_SQLSTATE { @@ -13330,6 +13408,23 @@ static void IsInPublicNamespace(char* varname) { } } +static void CheckDuplicateCondition (char* name) { + if (u_sess->plsql_cxt.curr_compile_context->plpgsql_conditions != NULL) { + PLpgSQL_condition* cond = u_sess->plsql_cxt.curr_compile_context->plpgsql_conditions; + while(cond) { + if (strcmp(cond->condname, name) == 0) { + const char* message = "duplicate declaration"; + InsertErrorMessage(message, plpgsql_yylloc); + ereport(errstate, + (errmodule(MOD_PLSQL), errcode(ERRCODE_SYNTAX_ERROR), + errmsg("duplicate declaration"), + errdetail("condition \"%s\" already defined", name))); + break; + } + cond = cond->next; + } + } +} static void AddNamespaceIfNeed(int dno, char* ident) { if (getCompileStatus() != COMPILIE_PKG_FUNC) { diff --git a/src/common/pl/plpgsql/src/pl_comp.cpp b/src/common/pl/plpgsql/src/pl_comp.cpp index f62e88617..8b76146e4 100644 --- a/src/common/pl/plpgsql/src/pl_comp.cpp +++ b/src/common/pl/plpgsql/src/pl_comp.cpp @@ -420,6 +420,7 @@ static void initCompileContext(PLpgSQL_compile_context* compile_cxt, MemoryConte compile_cxt->plpgsql_Datums = NULL; compile_cxt->datum_need_free = NULL; compile_cxt->plpgsql_curr_compile = NULL; + compile_cxt->plpgsql_conditions = NULL; compile_cxt->plpgsql_DumpExecTree = false; compile_cxt->plpgsql_pkg_DumpExecTree = false; compile_cxt->ns_top = NULL; @@ -3473,9 +3474,6 @@ PLpgSQL_variable* plpgsql_build_variable(const char* refname, int lineno, PLpgSQ var->datatype = dtype; var->notnull = (int)notNull; var->pkg = NULL; - var->customCondition = 0; - var->sqlstateCondition = NULL; - var->isSqlvalue = false; /* other fields might be filled by caller */ /* preset to NULL */ @@ -4306,17 +4304,17 @@ PLpgSQL_condition* plpgsql_parse_err_condition(char* condname) } if (prev == NULL) { - 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) { + PLpgSQL_condition* cond = u_sess->plsql_cxt.curr_compile_context->plpgsql_conditions; + while (cond) { + if (strcmp(cond->condname, condname) == 0) { newm = (PLpgSQL_condition*)palloc(sizeof(PLpgSQL_condition)); - newm->sqlerrstate = var->customCondition; + newm->sqlerrstate = cond->sqlerrstate; newm->condname = condname; newm->next = prev; prev = newm; + break; + } else { + cond = cond->next; } } if (prev == NULL) { @@ -4346,19 +4344,19 @@ PLpgSQL_condition* plpgsql_parse_err_condition_b_signal(const char* condname) PLpgSQL_condition* newm = NULL; PLpgSQL_condition* prev = NULL; - 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) { + PLpgSQL_condition* cond = u_sess->plsql_cxt.curr_compile_context->plpgsql_conditions; + while (cond) { + if (strcmp(cond->condname, condname) == 0) { newm = (PLpgSQL_condition*)palloc(sizeof(PLpgSQL_condition)); - newm->sqlerrstate = var->customCondition; + newm->sqlerrstate = cond->sqlerrstate; newm->condname = pstrdup(condname); - newm->sqlstate = var->sqlstateCondition; + newm->sqlstate = cond->sqlstate; newm->next = prev; - newm->isSqlvalue = var->isSqlvalue; + newm->isSqlvalue = cond->isSqlvalue; prev = newm; + break; + } else { + cond = cond->next; } } if (prev == NULL) { diff --git a/src/common/pl/plpgsql/src/pl_exec.cpp b/src/common/pl/plpgsql/src/pl_exec.cpp index e5e054e80..89cb23271 100644 --- a/src/common/pl/plpgsql/src/pl_exec.cpp +++ b/src/common/pl/plpgsql/src/pl_exec.cpp @@ -7805,7 +7805,10 @@ static int exec_stmt_fetch(PLpgSQL_execstate* estate, PLpgSQL_stmt_fetch* stmt) exec_set_notfound(estate, (n == 0) ? PLPGSQL_TRUE : PLPGSQL_FALSE, stmt->curvar + CURSOR_NOTFOUND); if (B_FETCH && n == 0) { - return PLPGSQL_RC_EXIT; + ereport(ERROR, + (errcode(ERRCODE_NO_DATA), + errmodule(MOD_PLSQL), + errmsg("No data - zero rows fetched, selected, or processed"))); } exec_set_rowcount(estate, n, false, stmt->curvar + CURSOR_ROWCOUNT); diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 3089c8a4c..398f06377 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -1490,6 +1490,7 @@ typedef struct PLpgSQL_compile_context { struct PLpgSQL_stmt_block* plpgsql_parse_error_result; struct PLpgSQL_datum** plpgsql_Datums; struct PLpgSQL_function* plpgsql_curr_compile; + struct PLpgSQL_condition* plpgsql_conditions; bool* datum_need_free; /* need free datum when free function/package memory? */ bool plpgsql_DumpExecTree; diff --git a/src/include/utils/plpgsql.h b/src/include/utils/plpgsql.h index e9ad4e26a..57b6d5b11 100644 --- a/src/include/utils/plpgsql.h +++ b/src/include/utils/plpgsql.h @@ -407,9 +407,6 @@ 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. */ - char *sqlstateCondition; /* only for declare condition variable. */ - bool isSqlvalue; Datum value; bool isnull; diff --git a/src/test/regress/expected/mysql_syntax.out b/src/test/regress/expected/mysql_syntax.out index db0c31641..2bcb54568 100644 --- a/src/test/regress/expected/mysql_syntax.out +++ b/src/test/regress/expected/mysql_syntax.out @@ -359,11 +359,8 @@ call test_cursor_1(); NOTICE: backberry : canada : 3 NOTICE: macrosoft : usa : 1 NOTICE: oracle : usa : 2 - test_cursor_1 ---------------- - -(1 row) - +ERROR: No data - zero rows fetched, selected, or processed +CONTEXT: PL/pgSQL function test_cursor_1() line 12 at FETCH set b_format_behavior_compat_options = ''; show b_format_behavior_compat_options; b_format_behavior_compat_options @@ -429,21 +426,9 @@ exception END; END; / -ERROR: duplicate declaration at or near "DIVISION_ZERO" -LINE 5: 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 +ERROR: duplicate declaration +DETAIL: condition "division_zero" already defined +CONTEXT: compilation of PL/pgSQL function "test_condition_3" near line 5 -- declare condition sqlcode create or replace procedure test_condition_4 as BEGIN @@ -516,6 +501,27 @@ NOTICE: SQLSTATE = 22012, SQLERRM = division by zero (1 row) +-- test other values compilte with condition name +create or replace procedure test_condition_7 as +declare + a int; +BEGIN + declare a condition for SQLSTATE '22012'; + a := 1/0; +exception + when a then + BEGIN + RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM; + END; +END; +/ +call test_condition_7(); +NOTICE: SQLSTATE = 22012, SQLERRM = division by zero + test_condition_7 +------------------ + +(1 row) + \c regression drop trigger animal_trigger1; ERROR: drop trigger without table name only support in B-format database diff --git a/src/test/regress/sql/mysql_syntax.sql b/src/test/regress/sql/mysql_syntax.sql index b6ea05e11..0bf175198 100644 --- a/src/test/regress/sql/mysql_syntax.sql +++ b/src/test/regress/sql/mysql_syntax.sql @@ -395,7 +395,22 @@ exception END; / call test_condition_1(); +-- test other values compilte with condition name +create or replace procedure test_condition_7 as +declare + a int; +BEGIN + declare a condition for SQLSTATE '22012'; + a := 1/0; +exception + when a then + BEGIN + RAISE NOTICE 'SQLSTATE = %, SQLERRM = %', SQLSTATE,SQLERRM; + END; +END; +/ +call test_condition_7(); \c regression drop trigger animal_trigger1; drop trigger if exists animal_trigger1;