From dc8ad80fe33de37c1853dd2ca77dc2fd4654bead Mon Sep 17 00:00:00 2001 From: "jiancheng.ren" Date: Mon, 5 Feb 2024 06:39:42 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B8=B8=E6=A0=87=E6=94=AF=E6=8C=81select?= =?UTF-8?q?=20into=E8=AF=AD=E6=B3=95=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/parser/gram.y | 24 +- src/common/pl/plpgsql/src/gram.y | 62 ++++- src/common/pl/plpgsql/src/pl_comp.cpp | 27 +- .../sqladvisor/sqladvisor_distribution.cpp | 1 - .../expected/cursor_with_select_into.out | 235 ++++++++++++++++++ .../regress/expected/hw_package_variable.out | 4 +- src/test/regress/parallel_schedule0 | 2 +- .../regress/sql/cursor_with_select_into.sql | 159 ++++++++++++ 8 files changed, 486 insertions(+), 28 deletions(-) create mode 100644 src/test/regress/expected/cursor_with_select_into.out create mode 100644 src/test/regress/sql/cursor_with_select_into.sql diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 85f16266cc..0ffc3cc6ae 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -212,6 +212,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, bool *deferrable, bool *initdeferred, bool *not_valid, bool *no_inherit, core_yyscan_t yyscanner); static Node *processIntoClauseInSelectStmt(SelectStmt *stmt, IntoClause *itc); +static Node *processIntoClauseInSelectStmtInEnd(SelectStmt *stmt, IntoClause *itc); static char* GetPkgName(char* pkgName); static Expr *makeNodeDecodeCondtion(Expr* firstCond,Expr* secondCond); static List *make_action_func(List *arguments); @@ -23448,7 +23449,7 @@ select_no_parens: (Node*)list_nth($4, 0), (Node*)list_nth($4, 1), NULL, yyscanner); - $$ = processIntoClauseInSelectStmt((SelectStmt *) $1, (IntoClause *) $5); + $$ = processIntoClauseInSelectStmtInEnd((SelectStmt *) $1, (IntoClause *) $5); } | select_clause opt_sort_clause select_limit opt_for_locking_clause { @@ -23466,7 +23467,7 @@ select_no_parens: (Node*)list_nth($3, 0), (Node*)list_nth($3, 1), NULL, yyscanner); - $$ = processIntoClauseInSelectStmt((SelectStmt *) $1, (IntoClause *) $5); + $$ = processIntoClauseInSelectStmtInEnd((SelectStmt *) $1, (IntoClause *) $5); } | select_clause opt_sort_clause opt_select_limit into_clause opt_for_locking_clause { @@ -23475,7 +23476,7 @@ select_no_parens: (Node*)list_nth($3, 0), (Node*)list_nth($3, 1), NULL, yyscanner); - $$ = processIntoClauseInSelectStmt((SelectStmt *) $1, (IntoClause *) $4); + $$ = processIntoClauseInSelectStmtInEnd((SelectStmt *) $1, (IntoClause *) $4); } | with_clause select_clause { @@ -31939,6 +31940,23 @@ static Node *processIntoClauseInSelectStmt(SelectStmt *stmt, IntoClause *itc) return (Node *)stmt; } +static Node *processIntoClauseInSelectStmtInEnd(SelectStmt *stmt, IntoClause *itc) +{ + if (itc != NULL) { + if (stmt->intoClause != NULL) { + ereport(errstate, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("select statement can contain only one into_clause"))); + } + if (itc->rel != NULL && u_sess->attr.attr_sql.sql_compatibility == A_FORMAT) { + ereport(errstate, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("SQL command not properly ended"))); } + stmt->intoClause = itc; + } + return (Node *)stmt; +} + static Node* MakeConnectByRootNode(ColumnRef* cr, int location) { FuncCall *n = makeNode(FuncCall); diff --git a/src/common/pl/plpgsql/src/gram.y b/src/common/pl/plpgsql/src/gram.y index 5078acf767..dead6c24f0 100755 --- a/src/common/pl/plpgsql/src/gram.y +++ b/src/common/pl/plpgsql/src/gram.y @@ -8434,7 +8434,14 @@ read_sql_construct6(int until, int parenlevel = 0; PLpgSQL_expr *expr; MemoryContext oldCxt = NULL; - + /* here seven work for cursor with select into */ + bool have_select = false; + bool have_into = false; + bool isMultiInsert = false; + int into_start_loc_real = -1; + int into_start_loc_relative = -1; + int into_end_loc = -1; + int prev_prev_tok = 0; /* * buf stores the cursor attribute internal variables or * varray ident with operate function, so NAMEDATALEN + 128 is enough @@ -8502,6 +8509,22 @@ read_sql_construct6(int until, if (parenlevel < 0) yyerror("mismatched parentheses", true); } + if (have_into && into_end_loc < 0) { + into_end_loc = yylloc; + /* token after the INTO part */ + while (tok == COMMENTSTRING || tok == ',') { + prev_prev_tok = prev_tok; + prev_tok = tok; + if (tok == ',') { + tok = yylex(); + } + tok = yylex(); + into_end_loc = yylloc; + } + } + if (tok == COMMENTSTRING) { + prev_prev_tok = prev_tok; + } /* * End of function definition is an error, and we don't expect to * hit a semicolon either (unless it's the until symbol, in which @@ -9082,6 +9105,27 @@ read_sql_construct6(int until, ds_changed = construct_cword(&ds, &context, &tok, parenlevel, loc); break; default: + if (tok == K_SELECT) { + have_select = true; + } else if (tok == K_INTO && have_select) { + have_into = true; + if ((prev_tok == K_INSERT && (tok == K_ALL || tok == K_FIRST)) || + (prev_prev_tok == K_INSERT && prev_tok == COMMENTSTRING && (tok == K_ALL || tok == K_FIRST))) { + isMultiInsert = true; + } + if (prev_tok == K_INSERT || prev_tok == K_REPLACE || isMultiInsert || + (prev_tok == COMMENTSTRING && (prev_prev_tok == K_INSERT || prev_prev_tok == K_REPLACE))) { + have_into = false; /* INSERT INTO is not an INTO-target */ + } + if (prev_tok == K_MERGE) + have_into = false; /* MERGE INTO is not an INTO-target */ + if (have_into) { + have_select = false; + into_start_loc_real = ds.len; + into_start_loc_relative = yylloc; + tok = yylex(); + } + } tok = yylex(); if(tok > INT_MAX) @@ -9189,7 +9233,21 @@ read_sql_construct6(int until, expr->tableof_func_dno = tableof_func_dno; pfree_ext(ds.data); - + if (have_into && into_start_loc_real > 0 && into_end_loc > 0) { + /* + * In the syntax of CURSOR with SELECT INTO, everything after into is no use, + * the actual assignment is in the fetch step + */ + if (0 == strcasecmp ("LOOP", expected)) { + // Implicit cursors do not support select into in LOOPs + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("compile failed when parse the query: %s", expr->query), + errdetail("select into clause is not supported in for..in loop condition yet."), + errcause("The syntax of the query is wrong"), + erraction("modify the query"))); + } + memset(expr->query + into_start_loc_real + strlen(sqlstart), ' ', into_end_loc - into_start_loc_relative); + } if (valid_sql) check_sql_expr(expr->query, startlocation, strlen(sqlstart)); diff --git a/src/common/pl/plpgsql/src/pl_comp.cpp b/src/common/pl/plpgsql/src/pl_comp.cpp index ef35f53b9e..ce6120d366 100644 --- a/src/common/pl/plpgsql/src/pl_comp.cpp +++ b/src/common/pl/plpgsql/src/pl_comp.cpp @@ -5184,27 +5184,16 @@ TupleDesc getCursorTupleDesc(PLpgSQL_expr* expr, bool isOnlySelect, bool isOnlyP foreach(cell, parsetreeList) { Node *parsetree = (Node *)lfirst(cell); t_thrd.postgres_cxt.cur_command_tag = transform_node_tag(parsetree); - if (nodeTag(parsetree) == T_SelectStmt) { - if (checkSelectIntoParse((SelectStmt*)parsetree)) { - list_free_deep(parsetreeList); - ereport(ERROR, (errmodule(MOD_PLSQL), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("select into clause is not supported in cursor or for..in loop condition yet."), - errdetail("query \"%s\" is not supported in cursor or for..in loop condition yet.", expr->query), - errcause("feature not supported"), - erraction("modify the query"))); - } - } else { - if (isOnlySelect) { - expr->func->pre_parse_trig = temp_pre_parse_trig; - expr->func->datums = NULL; - expr->func->ndatums = 0; - if (u_sess->plsql_cxt.curr_compile_context->plpgsql_curr_compile == NULL) { - pfree(expr->func); - } + if (nodeTag(parsetree) != T_SelectStmt && isOnlySelect) { + expr->func->pre_parse_trig = temp_pre_parse_trig; + expr->func->datums = NULL; + expr->func->ndatums = 0; + if (u_sess->plsql_cxt.curr_compile_context->plpgsql_curr_compile == NULL) { + pfree(expr->func); expr->func = NULL; - list_free_deep(parsetreeList); - PG_TRY_RETURN(NULL); } + list_free_deep(parsetreeList); + return NULL; } queryList = pg_analyze_and_rewrite_params(parsetree, expr->query, (ParserSetupHook)plpgsql_parser_setup, (void*)expr); diff --git a/src/gausskernel/optimizer/sqladvisor/sqladvisor_distribution.cpp b/src/gausskernel/optimizer/sqladvisor/sqladvisor_distribution.cpp index 5ca8d74d06..aea594fdf5 100644 --- a/src/gausskernel/optimizer/sqladvisor/sqladvisor_distribution.cpp +++ b/src/gausskernel/optimizer/sqladvisor/sqladvisor_distribution.cpp @@ -2178,7 +2178,6 @@ static bool isCandidateAttrSatisfiedStadistinct(AdviseTable* adviseTable, Virtua bool checkSelectIntoParse(SelectStmt* stmt) { if (stmt != NULL) { - /* don't support select into... */ if (stmt->intoClause != NULL) { return true; } else { diff --git a/src/test/regress/expected/cursor_with_select_into.out b/src/test/regress/expected/cursor_with_select_into.out new file mode 100644 index 0000000000..fec6459c49 --- /dev/null +++ b/src/test/regress/expected/cursor_with_select_into.out @@ -0,0 +1,235 @@ +create SCHEMA schema_for_test_cursor_with_select_into; +set current_schema = schema_for_test_cursor_with_select_into; +-- Tests 1 for "cursor c for select 1 into a from sys_dummy;" actually no use +create or replace procedure ppp1 is +declare + a int := 2; + cursor c for select 1 into a from sys_dummy; +begin + open c; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + close c; +end; +/ +select ppp1(); +NOTICE: a: 2 +CONTEXT: referenced column: ppp1 +NOTICE: --------- +CONTEXT: referenced column: ppp1 +NOTICE: a: 1 +CONTEXT: referenced column: ppp1 + ppp1 +------ + +(1 row) + +-- Test 2 for fetch +create or replace procedure ppp2 is +declare + a int := 2; + cursor c for select 1 into a from sys_dummy; +begin + open c; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + close c; +end; +/ +select ppp2(); +NOTICE: a: 2 +CONTEXT: referenced column: ppp2 +NOTICE: --------- +CONTEXT: referenced column: ppp2 +NOTICE: a: 1 +CONTEXT: referenced column: ppp2 +NOTICE: --------- +CONTEXT: referenced column: ppp2 +NOTICE: a: 1 +CONTEXT: referenced column: ppp2 +NOTICE: --------- +CONTEXT: referenced column: ppp2 +NOTICE: a: 1 +CONTEXT: referenced column: ppp2 + ppp2 +------ + +(1 row) + +-- Test 3 a complex select +create or replace procedure ppp4 is +declare + a int := 2; + cursor c for select 1 into -- + + a from sys_dummy; +begin + open c; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + close c; +end; +/ +select ppp4(); +NOTICE: a: 2 +CONTEXT: referenced column: ppp4 +NOTICE: --------- +CONTEXT: referenced column: ppp4 + ppp4 +------ + +(1 row) + +-- Test 4 mutli value select into single values +create table schema_for_test_cursor_with_select_into.fordouble(id1 integer, id2 integer); +insert into schema_for_test_cursor_with_select_into.fordouble values(1,2); +create or replace procedure ppp_complex is +DECLARE + v_emp_id integer; + v_emp_name integer; + cursor c for SELECT id1, id2 INTO v_emp_id FROM schema_for_test_cursor_with_select_into.fordouble; +BEGIN + open c; + fetch c into v_emp_id,v_emp_name; + close c; + RAISE NOTICE 'a: %', v_emp_id; + RAISE NOTICE '---------'; + RAISE NOTICE 'a: %', v_emp_name; +END; +/ +select ppp_complex(); +NOTICE: a: 1 +CONTEXT: referenced column: ppp_complex +NOTICE: --------- +CONTEXT: referenced column: ppp_complex +NOTICE: a: 2 +CONTEXT: referenced column: ppp_complex + ppp_complex +------------- + +(1 row) + +-- Test 4 mutli values select into mutli values support now +create or replace procedure ppp_complex is +DECLARE + v_emp_id integer; + v_emp_name integer; + cursor c for SELECT id1, id2 INTO v_emp_id,v_emp_name FROM schema_for_test_cursor_with_select_into.fordouble; + +BEGIN + open c; + fetch c into v_emp_id,v_emp_name; + close c; + RAISE NOTICE 'a: %', v_emp_id; + RAISE NOTICE '---------'; + RAISE NOTICE 'a: %', v_emp_name; +END; +/ +select ppp_complex(); +NOTICE: a: 1 +CONTEXT: referenced column: ppp_complex +NOTICE: --------- +CONTEXT: referenced column: ppp_complex +NOTICE: a: 2 +CONTEXT: referenced column: ppp_complex + ppp_complex +------------- + +(1 row) + +-- Test 5 complex select +CREATE TABLE schema_for_test_cursor_with_select_into.products ( + product_id serial PRIMARY KEY, + name varchar(50) NOT NULL, + price numeric(10,2) NOT NULL +); +NOTICE: CREATE TABLE will create implicit sequence "products_product_id_seq" for serial column "products.product_id" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "products_pkey" for table "products" +INSERT INTO schema_for_test_cursor_with_select_into.products (name, price) VALUES +('Product 1', 9.99), +('Product 2', 19.99), +('Product 3', 29.99); +CREATE TABLE schema_for_test_cursor_with_select_into.orders ( + order_id serial PRIMARY KEY, + product_id integer NOT NULL REFERENCES schema_for_test_cursor_with_select_into.products(product_id), + quantity integer NOT NULL +); +NOTICE: CREATE TABLE will create implicit sequence "orders_order_id_seq" for serial column "orders.order_id" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "orders_pkey" for table "orders" +INSERT INTO schema_for_test_cursor_with_select_into.orders (product_id, quantity) VALUES +(1, 10), (2, 5), (3, 2); +create or replace procedure ppp_complex is +DECLARE + product_name varchar(50); + product_price numeric(10,2); + CURSOR product_cursor FOR + SELECT name, price INTO product_name FROM schema_for_test_cursor_with_select_into.products WHERE product_id IN ( + SELECT product_id FROM schema_for_test_cursor_with_select_into.orders WHERE quantity > 1 + ); +BEGIN + OPEN product_cursor; + LOOP + FETCH product_cursor INTO product_name, product_price; + EXIT WHEN NOT FOUND; + RAISE NOTICE 'Product: %, Price: %', product_name, product_price; + END LOOP; + CLOSE product_cursor; +END; +/ +select ppp_complex(); +NOTICE: Product: Product 1, Price: 9.99 +CONTEXT: referenced column: ppp_complex +NOTICE: Product: Product 2, Price: 19.99 +CONTEXT: referenced column: ppp_complex +NOTICE: Product: Product 3, Price: 29.99 +CONTEXT: referenced column: ppp_complex + ppp_complex +------------- + +(1 row) + +-- Test 6 : behavior_compat_options=allow_procedure_compile_check; +set behavior_compat_options=allow_procedure_compile_check; +create or replace procedure ppp1 is +declare + a int := 2; + cursor c for select 1 into a from sys_dummy; +begin + open c; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + close c; +end; +/ +select ppp1(); +NOTICE: a: 2 +CONTEXT: referenced column: ppp1 +NOTICE: --------- +CONTEXT: referenced column: ppp1 +NOTICE: a: 1 +CONTEXT: referenced column: ppp1 + ppp1 +------ + +(1 row) + +drop table schema_for_test_cursor_with_select_into.orders; +drop table schema_for_test_cursor_with_select_into.products; +drop table schema_for_test_cursor_with_select_into.fordouble; +drop function ppp2; +drop function ppp1; +drop function ppp4; +drop function ppp_complex; +drop schema schema_for_test_cursor_with_select_into; \ No newline at end of file diff --git a/src/test/regress/expected/hw_package_variable.out b/src/test/regress/expected/hw_package_variable.out index 4671a5d410..9990495bcf 100644 --- a/src/test/regress/expected/hw_package_variable.out +++ b/src/test/regress/expected/hw_package_variable.out @@ -1906,8 +1906,8 @@ end loop; end; end pck1; / -ERROR: compile failed when parse the query: (select a,b into v1 from tab1) -DETAIL: select into clause is not supported in cursor or for..in loop condition yet. +ERROR: compile failed when parse the query: SELECT (select a,b into v1 from tab1) +DETAIL: select into clause is not supported in for..in loop condition yet. CONTEXT: compilation of PL/pgSQL package near line 3 create or replace package pck1 as func int; diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index b0ea53349f..d613b6233e 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -72,7 +72,7 @@ test: transaction_with_snapshot # test select into statement test: select_into_user_defined_variables test: select_into_file - +test: cursor_with_select_into test: gs_dump_package trigger_dump gs_dumpall test: out_param_func #test: sqlcode_cursor diff --git a/src/test/regress/sql/cursor_with_select_into.sql b/src/test/regress/sql/cursor_with_select_into.sql new file mode 100644 index 0000000000..32315e6ad0 --- /dev/null +++ b/src/test/regress/sql/cursor_with_select_into.sql @@ -0,0 +1,159 @@ +create SCHEMA schema_for_test_cursor_with_select_into; +set current_schema = schema_for_test_cursor_with_select_into; +-- Tests 1 for "cursor c for select 1 into a from sys_dummy;" actually no use +create or replace procedure ppp1 is +declare + a int := 2; + cursor c for select 1 into a from sys_dummy; +begin + open c; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + close c; +end; +/ + +select ppp1(); + +-- Test 2 for fetch +create or replace procedure ppp2 is +declare + a int := 2; + cursor c for select 1 into a from sys_dummy; +begin + open c; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + close c; +end; +/ + +select ppp2(); + +-- Test 3 a complex select +create or replace procedure ppp4 is +declare + a int := 2; + cursor c for select 1 into -- + + a from sys_dummy; +begin + open c; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + close c; +end; +/ + +select ppp4(); + +-- Test 4 mutli value select into single values +create table schema_for_test_cursor_with_select_into.fordouble(id1 integer, id2 integer); +insert into schema_for_test_cursor_with_select_into.fordouble values(1,2); +create or replace procedure ppp_complex is +DECLARE + v_emp_id integer; + v_emp_name integer; + cursor c for SELECT id1, id2 INTO v_emp_id FROM schema_for_test_cursor_with_select_into.fordouble; +BEGIN + open c; + fetch c into v_emp_id,v_emp_name; + close c; + RAISE NOTICE 'a: %', v_emp_id; + RAISE NOTICE '---------'; + RAISE NOTICE 'a: %', v_emp_name; +END; +/ +select ppp_complex(); + +-- Test 4 mutli values select into mutli values support now +create or replace procedure ppp_complex is +DECLARE + v_emp_id integer; + v_emp_name integer; + cursor c for SELECT id1, id2 INTO v_emp_id,v_emp_name FROM schema_for_test_cursor_with_select_into.fordouble; + +BEGIN + open c; + fetch c into v_emp_id,v_emp_name; + close c; + RAISE NOTICE 'a: %', v_emp_id; + RAISE NOTICE '---------'; + RAISE NOTICE 'a: %', v_emp_name; +END; +/ +select ppp_complex(); + +-- Test 5 complex select +CREATE TABLE schema_for_test_cursor_with_select_into.products ( + product_id serial PRIMARY KEY, + name varchar(50) NOT NULL, + price numeric(10,2) NOT NULL +); +INSERT INTO schema_for_test_cursor_with_select_into.products (name, price) VALUES +('Product 1', 9.99), +('Product 2', 19.99), +('Product 3', 29.99); +CREATE TABLE schema_for_test_cursor_with_select_into.orders ( + order_id serial PRIMARY KEY, + product_id integer NOT NULL REFERENCES schema_for_test_cursor_with_select_into.products(product_id), + quantity integer NOT NULL +); +INSERT INTO schema_for_test_cursor_with_select_into.orders (product_id, quantity) VALUES +(1, 10), (2, 5), (3, 2); +create or replace procedure ppp_complex is +DECLARE + product_name varchar(50); + product_price numeric(10,2); + CURSOR product_cursor FOR + SELECT name, price INTO product_name FROM schema_for_test_cursor_with_select_into.products WHERE product_id IN ( + SELECT product_id FROM schema_for_test_cursor_with_select_into.orders WHERE quantity > 1 + ); +BEGIN + OPEN product_cursor; + LOOP + FETCH product_cursor INTO product_name, product_price; + EXIT WHEN NOT FOUND; + RAISE NOTICE 'Product: %, Price: %', product_name, product_price; + END LOOP; + CLOSE product_cursor; +END; +/ +select ppp_complex(); + +-- Test 6 : behavior_compat_options=allow_procedure_compile_check; +set behavior_compat_options=allow_procedure_compile_check; + +create or replace procedure ppp1 is +declare + a int := 2; + cursor c for select 1 into a from sys_dummy; +begin + open c; + RAISE NOTICE 'a: %', a; + RAISE NOTICE '---------'; + fetch c into a; + RAISE NOTICE 'a: %', a; + close c; +end; +/ +select ppp1(); + +drop table schema_for_test_cursor_with_select_into.orders; +drop table schema_for_test_cursor_with_select_into.products; +drop table schema_for_test_cursor_with_select_into.fordouble; +drop function ppp2; +drop function ppp1; +drop function ppp4; +drop function ppp_complex; +drop schema schema_for_test_cursor_with_select_into; \ No newline at end of file -- Gitee From adfedbfea3d978630cd10cf4ff17218af3e45295 Mon Sep 17 00:00:00 2001 From: "jiancheng.ren" Date: Mon, 5 Feb 2024 10:01:46 +0000 Subject: [PATCH 2/3] =?UTF-8?q?format=5Ferror=5Fbacktrace&raise=5Fapplicat?= =?UTF-8?q?ion=5Ferror=E9=9C=80=E6=B1=82=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/catalog/system_views.sql | 13 +- src/common/backend/utils/error/elog.cpp | 21 +++ src/common/backend/utils/fmgr/fmgr.cpp | 5 +- src/common/pl/plpgsql/src/pl_exec.cpp | 53 ++++++- src/common/pl/plpgsql/src/plsql_packages.cpp | 53 +++---- .../process/threadpool/knl_session.cpp | 3 + src/include/knl/knl_session.h | 3 + src/include/utils/plpgsql.h | 3 +- .../expected/format_error_backtrace.out | 143 ++++++++++++++++++ .../expected/raise_application_error.out | 132 ++++++++++++++++ src/test/regress/parallel_schedule0 | 3 + .../regress/sql/format_error_backtrace.sql | 93 ++++++++++++ .../regress/sql/raise_application_error.sql | 124 +++++++++++++++ 13 files changed, 610 insertions(+), 39 deletions(-) create mode 100644 src/test/regress/expected/format_error_backtrace.out create mode 100644 src/test/regress/expected/raise_application_error.out create mode 100644 src/test/regress/sql/format_error_backtrace.sql create mode 100644 src/test/regress/sql/raise_application_error.sql diff --git a/src/common/backend/catalog/system_views.sql b/src/common/backend/catalog/system_views.sql index d8952f06c8..d484fade70 100644 --- a/src/common/backend/catalog/system_views.sql +++ b/src/common/backend/catalog/system_views.sql @@ -2328,11 +2328,16 @@ returns text AS '$libdir/plpgsql','regexp_substr' LANGUAGE C STRICT IMMUTABLE NOT FENCED; -CREATE OR REPLACE FUNCTION pg_catalog.report_application_error( - IN log text, - IN code integer default null +CREATE OR REPLACE FUNCTION pg_catalog.raise_application_error( + IN code integer, + IN log text default null )RETURNS void -AS '$libdir/plpgsql','report_application_error' +AS '$libdir/plpgsql','raise_application_error' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE FUNCTION pg_catalog.format_error_backtrace() +RETURNS text +AS '$libdir/plpgsql','format_error_backtrace' LANGUAGE C VOLATILE NOT FENCED; create or replace function pg_catalog.bitand(bigint,bigint) diff --git a/src/common/backend/utils/error/elog.cpp b/src/common/backend/utils/error/elog.cpp index 6e87e590fc..e7a5563105 100644 --- a/src/common/backend/utils/error/elog.cpp +++ b/src/common/backend/utils/error/elog.cpp @@ -171,6 +171,7 @@ static void eraseSingleQuotes(char* query_string); static int output_backtrace_to_log(StringInfoData* pOutBuf); static void write_asp_chunks(char *data, int len, bool end); static void write_asplog(char *data, int len, bool end); +static void mallocErrorBacktrace(); const char* password_mask = "********"; #define MASK_OBS_PATH() \ @@ -552,6 +553,9 @@ void errfinish(int dummy, ...) */ oldcontext = MemoryContextSwitchTo(ErrorContext); + if (elevel >= ERROR) + mallocErrorBacktrace(); + /* * Call any context callback functions. Errors occurring in callback * functions will be treated as recursive errors --- this ensures we will @@ -6330,3 +6334,20 @@ void getDiagnosticsInfo(List* condInfo, bool hasCondNum, List* condNum) FreeStringInfo(&buf); copyErrorDataArea(u_sess->dolphin_errdata_ctx.lastErrorDataArea, u_sess->dolphin_errdata_ctx.errorDataArea); } + +static void mallocErrorBacktrace() +{ + if (unlikely(u_sess->plsql_cxt.error_backtrace_cxt == NULL)) { + u_sess->plsql_cxt.error_backtrace_cxt = AllocSetContextCreate(u_sess->top_mem_cxt, + "error backtrace context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + MemoryContext oldCxt = MemoryContextSwitchTo(u_sess->plsql_cxt.error_backtrace_cxt); + u_sess->plsql_cxt.error_backtrace = makeStringInfo(); + MemoryContextSwitchTo(oldCxt); + } else { + if (u_sess->plsql_cxt.error_backtrace != NULL && u_sess->plsql_cxt.first_time) + resetStringInfo(u_sess->plsql_cxt.error_backtrace); + } +} \ No newline at end of file diff --git a/src/common/backend/utils/fmgr/fmgr.cpp b/src/common/backend/utils/fmgr/fmgr.cpp index 860141bb8b..4e07fee5fa 100755 --- a/src/common/backend/utils/fmgr/fmgr.cpp +++ b/src/common/backend/utils/fmgr/fmgr.cpp @@ -150,13 +150,14 @@ typedef struct { /* notice: order by ascii code */ static RegExternFunc plpgsql_function_table[] = { + {"format_error_backtrace", format_error_backtrace}, {"intervaltonum", intervaltonum}, {"plpgsql_call_handler", plpgsql_call_handler}, {"plpgsql_inline_handler", plpgsql_inline_handler}, {"plpgsql_validator", plpgsql_validator}, + {"raise_application_error", raise_application_error}, {"rawtohex", rawtohex}, - {"regexp_substr", regexp_substr}, - {"report_application_error", report_application_error}, + {"regexp_substr", regexp_substr} }; /* diff --git a/src/common/pl/plpgsql/src/pl_exec.cpp b/src/common/pl/plpgsql/src/pl_exec.cpp index c275b96920..c82980a220 100644 --- a/src/common/pl/plpgsql/src/pl_exec.cpp +++ b/src/common/pl/plpgsql/src/pl_exec.cpp @@ -2239,23 +2239,68 @@ HeapTuple plpgsql_exec_trigger(PLpgSQL_function* func, TriggerData* trigdata) return rettup; } +static void plpgsql_add_error_backtrace(PLpgSQL_execstate* estate) +{ + char sqlstate[64] = {0}; + char funcname[128] = {0}; + char *nspname = NULL; + ErrorData* edata = &t_thrd.log_cxt.errordata[t_thrd.log_cxt.errordata_stack_depth]; + PLpgSQL_function *func = estate->func; + + u_sess->plsql_cxt.first_time = false; + if (func->fn_searchpath != NULL && + u_sess->plsql_cxt.error_backtrace != NULL && + OidIsValid(func->fn_oid)) { + errno_t rc = snprintf_s(sqlstate, sizeof(sqlstate), sizeof(sqlstate) - 1, "%s", plpgsql_get_sqlstate(edata->sqlerrcode)); + securec_check_ss(rc, "", ""); + + Oid nspid = get_func_namespace(func->fn_oid); + if (OidIsValid(nspid)) + nspname = get_namespace_name(nspid); + + if (nspname == NULL) + return; + + strncpy(funcname, func->fn_signature, sizeof(funcname)-1); + /* delete the () */ + if (strlen(funcname) >= 2) + funcname[strlen(funcname) - 2] = '\0'; + + if (estate->err_stmt != NULL) { + appendStringInfo(u_sess->plsql_cxt.error_backtrace, "%s: at \"%s.%s\", line %d\n", + sqlstate, + nspname, + funcname, + estate->err_stmt->lineno); + } else { + appendStringInfo(u_sess->plsql_cxt.error_backtrace, "%s: at \"%s.%s\"\n", + sqlstate, + nspname, + funcname); + } + } +} + /* * error context callback to let us supply a call-stack traceback */ static void plpgsql_exec_error_callback(void* arg) { + int elevel; + int sqlState; PLpgSQL_execstate* estate = (PLpgSQL_execstate*)arg; + getElevelAndSqlstate(&elevel, &sqlState); + + if (elevel >= ERROR) + plpgsql_add_error_backtrace(estate); + /* if we are doing RAISE, don't report its location */ if (estate->err_text == raise_skip_msg) { return; } if (AUDIT_EXEC_ENABLED) { - int elevel; - int sqlState; - - getElevelAndSqlstate(&elevel, &sqlState); if (elevel >= ERROR && estate->func->fn_oid >= FirstNormalObjectId) { errno_t ret = EOK; char details[PGAUDIT_MAXLENGTH] = {0}; diff --git a/src/common/pl/plpgsql/src/plsql_packages.cpp b/src/common/pl/plpgsql/src/plsql_packages.cpp index 54294bfd31..1bfbc33b5e 100644 --- a/src/common/pl/plpgsql/src/plsql_packages.cpp +++ b/src/common/pl/plpgsql/src/plsql_packages.cpp @@ -39,17 +39,20 @@ PG_MODULE_MAGIC; extern Datum textregexsubstr_enforce_a(PG_FUNCTION_ARGS); extern Datum regexp_substr(PG_FUNCTION_ARGS); extern Datum intervaltonum(PG_FUNCTION_ARGS); -extern Datum report_application_error(PG_FUNCTION_ARGS); +extern Datum raise_application_error(PG_FUNCTION_ARGS); +extern Datum format_error_backtrace(PG_FUNCTION_ARGS); extern "C" { Datum regexp_substr(PG_FUNCTION_ARGS); Datum intervaltonum(PG_FUNCTION_ARGS); -Datum report_application_error(PG_FUNCTION_ARGS); +Datum raise_application_error(PG_FUNCTION_ARGS); +Datum format_error_backtrace(PG_FUNCTION_ARGS); } PG_FUNCTION_INFO_V1(regexp_substr); PG_FUNCTION_INFO_V1(intervaltonum); -PG_FUNCTION_INFO_V1(report_application_error); +PG_FUNCTION_INFO_V1(raise_application_error); +PG_FUNCTION_INFO_V1(format_error_backtrace); // Convert interval(day) to numeric Datum intervaltonum(PG_FUNCTION_ARGS) @@ -90,35 +93,29 @@ Datum rawtohex(PG_FUNCTION_ARGS) } /* report self-defined error: -20999 <= errorcode <= -20000 */ -Datum report_application_error(PG_FUNCTION_ARGS) +Datum raise_application_error(PG_FUNCTION_ARGS) { -#if ((!defined(ENABLE_MULTIPLE_NODES)) && (!defined(ENABLE_PRIVATEGAUSS))) - DISTRIBUTED_FEATURE_NOT_SUPPORTED(); -#endif - char* log = NULL; - int errnum = 0; - - if (PG_GETARG_DATUM(0) == 0) { - log = ""; - } else { - log = text_to_cstring(PG_GETARG_TEXT_P(0)); - } + const char* log = PG_GETARG_DATUM(1) == 0 ? "" : text_to_cstring(PG_GETARG_TEXT_P(1)); + const int errnum = PG_ARGISNULL(0) ? 0 : PG_GETARG_INT32(0); + if ((errnum > -20000) || (errnum < -20999)) { + ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), + errmsg("custom error code must be between -20000 and -20999"))); + } else { + ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("ORA%d: %s", errnum, log))); + } + + PG_RETURN_TEXT_P(cstring_to_text(u_sess->plsql_cxt.error_backtrace->data)); +} - if (PG_ARGISNULL(1)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("%s", log))); - } else { - errnum = PG_GETARG_INT32(1); - if ((errnum > -20000) || (errnum < -20999)) { - ereport( - ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("custom error code must be between -20000 and -20999"))); - } else { - ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("ORA%d: %s", errnum, log))); - } - } - - PG_RETURN_VOID(); +Datum format_error_backtrace(PG_FUNCTION_ARGS) +{ + if (u_sess->plsql_cxt.error_backtrace != NULL) { + PG_RETURN_TEXT_P(cstring_to_text(u_sess->plsql_cxt.error_backtrace->data)); + } + PG_RETURN_TEXT_P(cstring_to_text("")); } + Datum regexp_substr(PG_FUNCTION_ARGS) { Oid collation = PG_GET_COLLATION(); diff --git a/src/gausskernel/process/threadpool/knl_session.cpp b/src/gausskernel/process/threadpool/knl_session.cpp index e8d5ea9f25..879c4019a8 100755 --- a/src/gausskernel/process/threadpool/knl_session.cpp +++ b/src/gausskernel/process/threadpool/knl_session.cpp @@ -878,6 +878,9 @@ static void knl_u_plpgsql_init(knl_u_plpgsql_context* plsql_cxt) plsql_cxt->isCreatePkg = false; plsql_cxt->isCreatePkgFunction = false; plsql_cxt->currCompilingObjStatus = true; + plsql_cxt->error_backtrace_cxt = NULL; + plsql_cxt->error_backtrace = NULL; + plsql_cxt->first_time = true; } static void knl_u_stat_init(knl_u_stat_context* stat_cxt) diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index a1dc6e8cfa..698b239f96 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -1726,6 +1726,9 @@ typedef struct knl_u_plpgsql_context { MemoryContext depend_mem_cxt; /* temp context for build_gs_depend*/ int compile_check_node_level; int real_func_num; + MemoryContext error_backtrace_cxt; + StringInfoData *error_backtrace; /* save error backtrace */ + bool first_time; } knl_u_plpgsql_context; //this is used to define functions in package diff --git a/src/include/utils/plpgsql.h b/src/include/utils/plpgsql.h index adc2e379e3..7289b98a7f 100644 --- a/src/include/utils/plpgsql.h +++ b/src/include/utils/plpgsql.h @@ -1834,7 +1834,8 @@ extern "C" { Datum regexp_substr(PG_FUNCTION_ARGS); Datum intervaltonum(PG_FUNCTION_ARGS); Datum rawtohex(PG_FUNCTION_ARGS); -Datum report_application_error(PG_FUNCTION_ARGS); +Datum raise_application_error(PG_FUNCTION_ARGS); +Datum format_error_backtrace(PG_FUNCTION_ARGS); } extern THR_LOCAL PLpgSQL_execstate* plpgsql_estate; diff --git a/src/test/regress/expected/format_error_backtrace.out b/src/test/regress/expected/format_error_backtrace.out new file mode 100644 index 0000000000..9af9ff77e1 --- /dev/null +++ b/src/test/regress/expected/format_error_backtrace.out @@ -0,0 +1,143 @@ +create SCHEMA schema_for_test_format_error_backtrace; +set current_schema = schema_for_test_format_error_backtrace; +--case1: format_error_backtrace save the error stack messages +Create or replace procedure proc1 is +Begin +raise NOTICE 'running proc1'; +Raise no_data_found; +End; +/ +create or replace procedure proc2 is +begin +raise NOTICE 'calling proc1'; +raise NOTICE '---------------'; +proc1; +end; +/ +create or replace procedure proc3 is +begin +raise NOTICE 'calling proc2'; +proc2; +exception +when no_data_found +then +raise NOTICE 'error stack at top level'; +raise NOTICE '%', format_error_backtrace(); +end; +/ +begin +raise NOTICE 'proc3->proc2->proc1 backtrace'; +proc3; +end; +/ +NOTICE: proc3->proc2->proc1 backtrace +NOTICE: calling proc2 +CONTEXT: SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: calling proc1 +CONTEXT: SQL statement "CALL proc2()" +PL/pgSQL function proc3() line 3 at PERFORM +SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: --------------- +CONTEXT: SQL statement "CALL proc2()" +PL/pgSQL function proc3() line 3 at PERFORM +SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: running proc1 +CONTEXT: SQL statement "CALL proc1()" +PL/pgSQL function proc2() line 4 at PERFORM +SQL statement "CALL proc2()" +PL/pgSQL function proc3() line 3 at PERFORM +SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: error stack at top level +CONTEXT: SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +NOTICE: P0002: at "schema_for_test_format_error_backtrace.proc1", line 3 +P0002: at "schema_for_test_format_error_backtrace.proc2", line 4 +P0002: at "schema_for_test_format_error_backtrace.proc3", line 3 + +CONTEXT: SQL statement "CALL proc3()" +PL/pgSQL function inline_code_block line 3 at PERFORM +--case2: format_error_backtrace can be as the parameter of function +create or replace procedure test_bt_a(i_err_bt varchar2 default format_error_backtrace(),i_sqlerm varchar2 default 'ddd') is +begin + raise NOTICE 'test_bt_a'; + raise NOTICE '%', i_err_bt; + raise NOTICE '%', i_sqlerm; +end; +/ +declare + a int; +begin + a := 'abc'; +exception + when others then + test_bt_a; +end; +/ +NOTICE: test_bt_a +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function inline_code_block line 6 at PERFORM +NOTICE: P0002: at "schema_for_test_format_error_backtrace.proc1", line 3 +P0002: at "schema_for_test_format_error_backtrace.proc2", line 4 +P0002: at "schema_for_test_format_error_backtrace.proc3", line 3 + +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function inline_code_block line 6 at PERFORM +NOTICE: ddd +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function inline_code_block line 6 at PERFORM +--case3: format_error_backtrace can be as the parameter of function, and save error stack messages +create or replace procedure test_bt_a(i_err_bt varchar2 default format_error_backtrace(),i_sqlerm varchar2 default 'aaa') is +begin + raise NOTICE '%', i_err_bt; + raise NOTICE '%', i_sqlerm; +end; +/ +create procedure test_bt_b is + a int; +begin + a := 'abc'; +exception + when others then + test_bt_a; +end; +/ +create procedure test_bt_c is + a int; +begin +test_bt_b; +end; +/ +begin + test_bt_c; +end; +/ +NOTICE: P0002: at "schema_for_test_format_error_backtrace.proc1", line 3 +P0002: at "schema_for_test_format_error_backtrace.proc2", line 4 +P0002: at "schema_for_test_format_error_backtrace.proc3", line 3 +22P02: at "schema_for_test_format_error_backtrace.test_bt_b", line 3 +22P02: at "schema_for_test_format_error_backtrace.test_bt_c", line 3 + +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function test_bt_b() line 6 at PERFORM +SQL statement "CALL test_bt_b()" +PL/pgSQL function test_bt_c() line 3 at PERFORM +SQL statement "CALL test_bt_c()" +PL/pgSQL function inline_code_block line 2 at PERFORM +NOTICE: aaa +CONTEXT: SQL statement "CALL test_bt_a()" +PL/pgSQL function test_bt_b() line 6 at PERFORM +SQL statement "CALL test_bt_b()" +PL/pgSQL function test_bt_c() line 3 at PERFORM +SQL statement "CALL test_bt_c()" +PL/pgSQL function inline_code_block line 2 at PERFORM +drop procedure test_bt_c; +drop procedure test_bt_b; +drop procedure test_bt_a; +drop procedure proc3; +drop procedure proc2; +drop procedure proc1; +drop schema schema_for_test_format_error_backtrace; diff --git a/src/test/regress/expected/raise_application_error.out b/src/test/regress/expected/raise_application_error.out new file mode 100644 index 0000000000..fe113ea1b0 --- /dev/null +++ b/src/test/regress/expected/raise_application_error.out @@ -0,0 +1,132 @@ +create SCHEMA schema_for_test_raise_application_error; +set current_schema = schema_for_test_raise_application_error; +--case1: Stored Procedures Called in Normal Flow +create or replace procedure a1 is + a int; +begin + null; + raise_application_error(-20001, 'dfff'); +end; +/ +begin + a1; +end; +/ +ERROR: ORA-20001: dfff +CONTEXT: SQL statement "CALL raise_application_error(-20001,'dfff')" +PL/pgSQL function a1() line 4 at PERFORM +SQL statement "CALL a1()" +PL/pgSQL function inline_code_block line 2 at PERFORM +--case2: Stored Procedures Called in Normal Flow, Printing the call stack and error messages +create or replace procedure a1 is + a int; + b int; +begin + raise_application_error(-20015, 'abcc'); +end; +/ +create or replace procedure a2(tt number, xx varchar2 default '0') is +begin + null; + null; + a1; +end; +/ +create or replace procedure a3 is + tt number; +begin + null; + null; + null; + a2(tt); +end; +/ +begin +a3; +end; +/ +ERROR: ORA-20015: abcc +CONTEXT: SQL statement "CALL raise_application_error(-20015,'abcc')" +PL/pgSQL function a1() line 4 at PERFORM +SQL statement "CALL a1()" +PL/pgSQL function a2(numeric,character varying) line 4 at PERFORM +SQL statement "CALL a2(tt)" +PL/pgSQL function a3() line 6 at PERFORM +SQL statement "CALL a3()" +PL/pgSQL function inline_code_block line 2 at PERFORM +--case3: Stored procedure exception process call +create or replace procedure a1 is + a int; + b int; +begin + b := 'dv'; +EXCEPTION when others then + raise_application_error(-20002, 'abcc'); +end; +/ +begin + a1; +end; +/ +ERROR: ORA-20002: abcc +CONTEXT: SQL statement "CALL raise_application_error(-20002,'abcc')" +PL/pgSQL function a1() line 6 at PERFORM +SQL statement "CALL a1()" +PL/pgSQL function inline_code_block line 2 at PERFORM +--case4: Calling on a procedure exception,Printing the Call Stack and Error Messages +create or replace procedure a1 is + a int; + b int; +begin + b := 'dv'; +EXCEPTION when others then + raise_application_error(-20002, 'abcc'); +end; +/ +create or replace procedure a2(tt number, xx varchar2 default '0') is +begin + null; + null; + a1; +end; +/ +create or replace procedure a3 is + tt number; +begin + null; + null; + null; + a2(tt); +end; +/ +begin + a3; +end; +/ +ERROR: ORA-20002: abcc +CONTEXT: SQL statement "CALL raise_application_error(-20002,'abcc')" +PL/pgSQL function a1() line 6 at PERFORM +SQL statement "CALL a1()" +PL/pgSQL function a2(numeric,character varying) line 4 at PERFORM +SQL statement "CALL a2(tt)" +PL/pgSQL function a3() line 6 at PERFORM +SQL statement "CALL a3()" +PL/pgSQL function inline_code_block line 2 at PERFORM +--case5: Allow select to be called directly +select raise_application_error(-20002, 'abcc'); +ERROR: ORA-20002: abcc +CONTEXT: referenced column: raise_application_error +--case6: NULL parameter test +select raise_application_error(NULL, 'abcc'); +ERROR: custom error code must be between -20000 and -20999 +CONTEXT: referenced column: raise_application_error +select raise_application_error(-20002, NULL); +ERROR: ORA-20002: +CONTEXT: referenced column: raise_application_error +select raise_application_error(NULL, NULL); +ERROR: custom error code must be between -20000 and -20999 +CONTEXT: referenced column: raise_application_error +drop procedure a1; +drop procedure a2; +drop procedure a3; +drop schema schema_for_test_raise_application_error; diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index d613b6233e..39189975c8 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -84,6 +84,9 @@ test: plpgsql_sql_with_proc_keyword test: plsql_show_all_error b_pg_plsql_show_all_error test: pldeveloper_gs_source test: index_advisor +# test errors trace +test: format_error_backtrace +test: raise_application_error #test: pl_debugger_server pl_debugger_client test: update_for_wait_s1 update_for_wait_s2 test: plan_hint plan_hint_set plan_hint_no_expand plan_hint_iud null_test_opt deserialize_func diff --git a/src/test/regress/sql/format_error_backtrace.sql b/src/test/regress/sql/format_error_backtrace.sql new file mode 100644 index 0000000000..ebef6424f1 --- /dev/null +++ b/src/test/regress/sql/format_error_backtrace.sql @@ -0,0 +1,93 @@ +create SCHEMA schema_for_test_format_error_backtrace; +set current_schema = schema_for_test_format_error_backtrace; + +--case1: format_error_backtrace save the error stack messages +Create or replace procedure proc1 is +Begin +raise NOTICE 'running proc1'; +Raise no_data_found; +End; +/ + +create or replace procedure proc2 is +begin +raise NOTICE 'calling proc1'; +raise NOTICE '---------------'; +proc1; +end; +/ + +create or replace procedure proc3 is +begin +raise NOTICE 'calling proc2'; +proc2; +exception +when no_data_found +then +raise NOTICE 'error stack at top level'; +raise NOTICE '%', format_error_backtrace(); +end; +/ + +begin +raise NOTICE 'proc3->proc2->proc1 backtrace'; +proc3; +end; +/ + +--case2: format_error_backtrace can be as the parameter of function +create or replace procedure test_bt_a(i_err_bt varchar2 default format_error_backtrace(),i_sqlerm varchar2 default 'ddd') is +begin + raise NOTICE 'test_bt_a'; + raise NOTICE '%', i_err_bt; + raise NOTICE '%', i_sqlerm; +end; +/ + +declare + a int; +begin + a := 'abc'; +exception + when others then + test_bt_a; +end; +/ + +--case3: format_error_backtrace can be as the parameter of function, and save error stack messages +create or replace procedure test_bt_a(i_err_bt varchar2 default format_error_backtrace(),i_sqlerm varchar2 default 'aaa') is +begin + raise NOTICE '%', i_err_bt; + raise NOTICE '%', i_sqlerm; +end; +/ + +create procedure test_bt_b is + a int; +begin + a := 'abc'; +exception + when others then + test_bt_a; +end; +/ + +create procedure test_bt_c is + a int; +begin +test_bt_b; +end; +/ + +begin + test_bt_c; +end; +/ + +drop procedure test_bt_c; +drop procedure test_bt_b; +drop procedure test_bt_a; +drop procedure proc3; +drop procedure proc2; +drop procedure proc1; +drop schema schema_for_test_format_error_backtrace; \ No newline at end of file diff --git a/src/test/regress/sql/raise_application_error.sql b/src/test/regress/sql/raise_application_error.sql new file mode 100644 index 0000000000..9e712d25ba --- /dev/null +++ b/src/test/regress/sql/raise_application_error.sql @@ -0,0 +1,124 @@ +create SCHEMA schema_for_test_raise_application_error; +set current_schema = schema_for_test_raise_application_error; + +--case1: Stored Procedures Called in Normal Flow +create or replace procedure a1 is + a int; +begin + null; + raise_application_error(-20001, 'dfff'); +end; +/ + + +begin + a1; +end; +/ + + +--case2: Stored Procedures Called in Normal Flow, Printing the call stack and error messages +create or replace procedure a1 is + a int; + b int; +begin + raise_application_error(-20015, 'abcc'); +end; +/ + + +create or replace procedure a2(tt number, xx varchar2 default '0') is +begin + null; + null; + a1; +end; +/ + + +create or replace procedure a3 is + tt number; +begin + null; + null; + null; + a2(tt); +end; +/ + + +begin +a3; +end; +/ + + +--case3: Stored procedure exception process call +create or replace procedure a1 is + a int; + b int; +begin + b := 'dv'; +EXCEPTION when others then + raise_application_error(-20002, 'abcc'); +end; +/ + + +begin + a1; +end; +/ + + +--case4: Calling on a procedure exception,Printing the Call Stack and Error Messages +create or replace procedure a1 is + a int; + b int; +begin + b := 'dv'; +EXCEPTION when others then + raise_application_error(-20002, 'abcc'); +end; +/ + + +create or replace procedure a2(tt number, xx varchar2 default '0') is +begin + null; + null; + a1; +end; +/ + + +create or replace procedure a3 is + tt number; +begin + null; + null; + null; + a2(tt); +end; +/ + + +begin + a3; +end; +/ + + +--case5: Allow select to be called directly +select raise_application_error(-20002, 'abcc'); + + +--case6: NULL parameter test +select raise_application_error(NULL, 'abcc'); +select raise_application_error(-20002, NULL); +select raise_application_error(NULL, NULL); + +drop procedure a1; +drop procedure a2; +drop procedure a3; +drop schema schema_for_test_raise_application_error; \ No newline at end of file -- Gitee From fc026ac9118be5b798f64b8a5985cc55a5f4ad9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=BB=E5=BB=BA=E6=88=90?= <2418478784@qq.com> Date: Thu, 18 Apr 2024 07:40:03 +0000 Subject: [PATCH 3/3] update src/common/pl/plpgsql/src/gram.y. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 任建成 <2418478784@qq.com> --- src/common/pl/plpgsql/src/gram.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/pl/plpgsql/src/gram.y b/src/common/pl/plpgsql/src/gram.y index 6e1d8a31a5..b6baf55cd0 100755 --- a/src/common/pl/plpgsql/src/gram.y +++ b/src/common/pl/plpgsql/src/gram.y @@ -9252,7 +9252,7 @@ read_sql_construct6(int until, errcause("The syntax of the query is wrong"), erraction("modify the query"))); } - memset(expr->query + into_start_loc_real + strlen(sqlstart), ' ', into_end_loc - into_start_loc_relative); + memset_s(expr->query + into_start_loc_real + strlen(sqlstart), into_end_loc - into_start_loc_relative + 1, ' ', into_end_loc - into_start_loc_relative); } if (valid_sql) check_sql_expr(expr->query, startlocation, strlen(sqlstart)); -- Gitee