On Sun, 25 Jul 2021 at 16:34, Pavel Stehule <pavel.steh...@gmail.com> wrote:

>
>
> ne 25. 7. 2021 v 12:52 odesílatel Dinesh Chemuduru <
> dinesh.ku...@migops.com> napsal:
>
>> On Sat, 17 Jul 2021 at 01:29, Pavel Stehule <pavel.steh...@gmail.com>
>> wrote:
>>
>>> Hi
>>>
>>> pá 16. 7. 2021 v 21:47 odesílatel Dinesh Chemuduru <
>>> dinesh.ku...@migops.com> napsal:
>>>
>>>> Hi Everyone,
>>>>
>>>> We would like to propose the below 2 new plpgsql diagnostic items,
>>>> related to parsing. Because, the current diag items are not providing
>>>> the useful diagnostics about the dynamic SQL statements.
>>>>
>>>> 1. PG_PARSE_SQL_STATEMENT (returns parse failed sql statement)
>>>> 2. PG_PARSE_SQL_STATEMENT_POSITION (returns parse failed sql text
>>>> cursor position)
>>>>
>>>> Consider the below example, which is an invalid SQL statement.
>>>>
>>>> postgres=# SELECT 1 JOIN SELECT 2;
>>>> ERROR:  syntax error at or near "JOIN"
>>>> LINE 1: SELECT 1 JOIN SELECT 2;
>>>>                  ^
>>>> Here, there is a syntax error at JOIN clause,
>>>> and also we are getting the syntax error position(^ symbol, the
>>>> position of JOIN clause).
>>>> This will be helpful, while dealing with long queries.
>>>>
>>>> Now, if we run the same statement as a dynamic SQL(by using EXECUTE
>>>> <sql statement>),
>>>> then it seems we are not getting the text cursor position,
>>>> and the SQL statement which is failing at parse level.
>>>>
>>>> Please find the below example.
>>>>
>>>> postgres=# SELECT exec_me('SELECT 1 JOIN SELECT 2');
>>>> NOTICE:  RETURNED_SQLSTATE 42601
>>>> NOTICE:  COLUMN_NAME
>>>> NOTICE:  CONSTRAINT_NAME
>>>> NOTICE:  PG_DATATYPE_NAME
>>>> NOTICE:  MESSAGE_TEXT syntax error at or near "JOIN"
>>>> NOTICE:  TABLE_NAME
>>>> NOTICE:  SCHEMA_NAME
>>>> NOTICE:  PG_EXCEPTION_DETAIL
>>>> NOTICE:  PG_EXCEPTION_HINT
>>>> NOTICE:  PG_EXCEPTION_CONTEXT PL/pgSQL function exec_me(text) line 18
>>>> at EXECUTE
>>>> NOTICE:  PG_CONTEXT PL/pgSQL function exec_me(text) line 21 at GET
>>>> STACKED DIAGNOSTICS
>>>>  exec_me
>>>> ---------
>>>>
>>>> (1 row)
>>>>
>>>> From the above results, by using all the existing diag items, we are
>>>> unable to get the position of "JOIN" in the submitted SQL statement.
>>>> By using these proposed diag items, we will be getting the required
>>>> information,
>>>> which will be helpful while running long SQL statements as dynamic SQL
>>>> statements.
>>>>
>>>> Please find the below example.
>>>>
>>>> postgres=# SELECT exec_me('SELECT 1 JOIN SELECT 2');
>>>> NOTICE:  PG_PARSE_SQL_STATEMENT SELECT 1 JOIN SELECT 2
>>>> NOTICE:  PG_PARSE_SQL_STATEMENT_POSITION 10
>>>>  exec_me
>>>> ---------
>>>>
>>>> (1 row)
>>>>
>>>> From the above results, by using these diag items,
>>>> we are able to get what is failing and it's position as well.
>>>> This information will be much helpful to debug the issue,
>>>> while a long running SQL statement is running as a dynamic SQL
>>>> statement.
>>>>
>>>> We are attaching the patch for this proposal, and will be looking for
>>>> your inputs.
>>>>
>>>
>>> +1 It is good idea.  I am not sure if the used names are good. I propose
>>>
>>> PG_SQL_TEXT and PG_ERROR_LOCATION
>>>
>>> Regards
>>>
>>> Pavel
>>>
>>>
>> Thanks Pavel,
>>
>> Sorry for the late reply.
>>
>> The proposed diag items are `PG_SQL_TEXT`, `PG_ERROR_LOCATION` are much
>> better and generic.
>>
>> But, as we are only dealing with the parsing failure, I thought of adding
>> that to the diag name.
>>
>
> I understand. But parsing is only one case - and these variables can be
> used for any case. Sure, ***we don't want*** to have PG_PARSE_SQL_TEXT,
> PG_ANALYZE_SQL_TEXT, PG_EXECUTION_SQL_TEXT ...
>
> The idea is good, and you found the case, where it has benefits for users.
> Naming is hard.
>
>
Thanks for your time and suggestions Pavel.
I updated the patch as per the suggestions, and attached it here for
further inputs.

Regards,
Dinesh Kumar



> Regards
>
> Pavel
>
>
>> Regards,
>> Dinesh Kumar
>>
>>
>>>
>>>
>>>> Regards,
>>>> Dinesh Kumar
>>>>
>>>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 4cd4bcba80..3f732453e2 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -2985,6 +2985,16 @@ GET STACKED DIAGNOSTICS <replaceable>variable</replaceable> { = | := } <replacea
          <entry><type>text</type></entry>
          <entry>the name of the schema related to exception</entry>
         </row>
+        <row>
+         <entry><literal>PG_SQL_TEXT</literal></entry>
+         <entry><type>text</type></entry>
+         <entry>invalid dynamic sql statement, if any</entry>
+        </row>
+        <row>
+         <entry><literal>PG_ERROR_LOCATION</literal></entry>
+         <entry><type>text</type></entry>
+         <entry>invalid dynamic sql statement's text cursor position, if any</entry>
+        </row>
         <row>
          <entry><literal>PG_EXCEPTION_DETAIL</literal></entry>
          <entry><type>text</type></entry>
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index b5c208d83d..b6a977aa5c 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -2402,6 +2402,14 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
 									 estate->cur_error->hint);
 				break;
 
+			case PLPGSQL_GETDIAG_SQL_TEXT:
+				exec_assign_c_string(estate, var, estate->cur_error->internalquery);
+				break;
+
+			case PLPGSQL_GETDIAG_ERROR_LOCATION:
+				exec_assign_value(estate, var, UInt64GetDatum(estate->cur_error->internalpos), false, INT8OID, -1);
+				break;
+
 			case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
 				exec_assign_c_string(estate, var,
 									 unpack_sql_state(estate->cur_error->sqlerrcode));
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index e0863acb3d..f00a745e9a 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -311,6 +311,10 @@ plpgsql_getdiag_kindname(PLpgSQL_getdiag_kind kind)
 			return "PG_EXCEPTION_DETAIL";
 		case PLPGSQL_GETDIAG_ERROR_HINT:
 			return "PG_EXCEPTION_HINT";
+		case PLPGSQL_GETDIAG_SQL_TEXT:
+			return "PG_SQL_TEXT";
+		case PLPGSQL_GETDIAG_ERROR_LOCATION:
+			return "PG_ERROR_LOCATION";
 		case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
 			return "RETURNED_SQLSTATE";
 		case PLPGSQL_GETDIAG_COLUMN_NAME:
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 3fcca43b90..804faabb97 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -321,6 +321,8 @@ static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
 %token <keyword>	K_PG_CONTEXT
 %token <keyword>	K_PG_DATATYPE_NAME
 %token <keyword>	K_PG_EXCEPTION_CONTEXT
+%token <keyword>	K_PG_SQL_TEXT
+%token <keyword>	K_PG_ERROR_LOCATION
 %token <keyword>	K_PG_EXCEPTION_DETAIL
 %token <keyword>	K_PG_EXCEPTION_HINT
 %token <keyword>	K_PRINT_STRICT_PARAMS
@@ -1047,6 +1049,8 @@ stmt_getdiag	: K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
 								case PLPGSQL_GETDIAG_ERROR_CONTEXT:
 								case PLPGSQL_GETDIAG_ERROR_DETAIL:
 								case PLPGSQL_GETDIAG_ERROR_HINT:
+								case PLPGSQL_GETDIAG_SQL_TEXT:
+								case PLPGSQL_GETDIAG_ERROR_LOCATION:
 								case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
 								case PLPGSQL_GETDIAG_COLUMN_NAME:
 								case PLPGSQL_GETDIAG_CONSTRAINT_NAME:
@@ -1130,6 +1134,10 @@ getdiag_item :
 						else if (tok_is_keyword(tok, &yylval,
 												K_PG_EXCEPTION_CONTEXT, "pg_exception_context"))
 							$$ = PLPGSQL_GETDIAG_ERROR_CONTEXT;
+						else if (tok_is_keyword(tok, &yylval, K_PG_SQL_TEXT, "pg_sql_text"))
+							$$ = PLPGSQL_GETDIAG_SQL_TEXT;
+						else if (tok_is_keyword(tok, &yylval, K_PG_ERROR_LOCATION, "pg_error_location"))
+							$$ = PLPGSQL_GETDIAG_ERROR_LOCATION;
 						else if (tok_is_keyword(tok, &yylval,
 												K_COLUMN_NAME, "column_name"))
 							$$ = PLPGSQL_GETDIAG_COLUMN_NAME;
diff --git a/src/pl/plpgsql/src/pl_unreserved_kwlist.h b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
index fcb34f7c7f..233cadf59c 100644
--- a/src/pl/plpgsql/src/pl_unreserved_kwlist.h
+++ b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
@@ -81,9 +81,11 @@ PG_KEYWORD("option", K_OPTION)
 PG_KEYWORD("perform", K_PERFORM)
 PG_KEYWORD("pg_context", K_PG_CONTEXT)
 PG_KEYWORD("pg_datatype_name", K_PG_DATATYPE_NAME)
+PG_KEYWORD("pg_error_location", K_PG_ERROR_LOCATION)
 PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT)
 PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL)
 PG_KEYWORD("pg_exception_hint", K_PG_EXCEPTION_HINT)
+PG_KEYWORD("pg_sql_text", K_PG_SQL_TEXT)
 PG_KEYWORD("print_strict_params", K_PRINT_STRICT_PARAMS)
 PG_KEYWORD("prior", K_PRIOR)
 PG_KEYWORD("query", K_QUERY)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index fcbfcd678b..6120837740 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -151,6 +151,8 @@ typedef enum PLpgSQL_getdiag_kind
 	PLPGSQL_GETDIAG_ERROR_CONTEXT,
 	PLPGSQL_GETDIAG_ERROR_DETAIL,
 	PLPGSQL_GETDIAG_ERROR_HINT,
+	PLPGSQL_GETDIAG_SQL_TEXT,
+	PLPGSQL_GETDIAG_ERROR_LOCATION,
 	PLPGSQL_GETDIAG_RETURNED_SQLSTATE,
 	PLPGSQL_GETDIAG_COLUMN_NAME,
 	PLPGSQL_GETDIAG_CONSTRAINT_NAME,
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 6ea169d9ad..4344d2f048 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5701,3 +5701,46 @@ END; $$ LANGUAGE plpgsql;
 ERROR:  "x" is not a scalar variable
 LINE 3:   GET DIAGNOSTICS x = ROW_COUNT;
                           ^
+--
+-- PG_SQL_TEXT, PG_ERROR_LOCATION
+-- should return sql and text cursor position, if the dynamic sql is syntactically incorrect
+--
+DO
+$$
+DECLARE
+v_sql TEXT:='SELECT 1 JOIN SELECT 2';
+v_err_sql_stmt TEXT;
+v_err_sql_pos INT;
+BEGIN
+EXECUTE v_sql;
+EXCEPTION
+WHEN OTHERS THEN
+GET STACKED DIAGNOSTICS v_err_sql_stmt = PG_SQL_TEXT,
+v_err_sql_pos = PG_ERROR_LOCATION;
+RAISE NOTICE 'exception sql %', v_err_sql_stmt;
+RAISE NOTICE 'exception sql position %', v_err_sql_pos;
+END;
+$$;
+NOTICE:  exception sql SELECT 1 JOIN SELECT 2
+NOTICE:  exception sql position 15
+--
+-- PG_SQL_TEXT, PG_ERROR_LOCATION
+-- should return empty results, if the dynamic sql is a valid
+--
+DO
+$$
+DECLARE
+v_err_sql_pos INT;
+v_err_sql_stmt TEXT;
+BEGIN
+PERFORM 1/0;
+EXCEPTION
+WHEN OTHERS THEN
+GET STACKED DIAGNOSTICS v_err_sql_stmt = PG_SQL_TEXT,
+v_err_sql_pos = PG_ERROR_LOCATION;
+RAISE NOTICE 'exception sql %', v_err_sql_stmt;
+RAISE NOTICE 'exception sql position %', v_err_sql_pos;
+END;
+$$;
+NOTICE:  exception sql 
+NOTICE:  exception sql position 0
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 781666a83a..3a53dc9e67 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4645,3 +4645,44 @@ BEGIN
   GET DIAGNOSTICS x = ROW_COUNT;
   RETURN;
 END; $$ LANGUAGE plpgsql;
+
+--
+-- PG_SQL_TEXT, PG_ERROR_LOCATION
+-- should return sql and text cursor position, if the dynamic sql is syntactically incorrect
+--
+DO
+$$
+DECLARE
+v_sql TEXT:='SELECT 1 JOIN SELECT 2';
+v_err_sql_stmt TEXT;
+v_err_sql_pos INT;
+BEGIN
+EXECUTE v_sql;
+EXCEPTION
+WHEN OTHERS THEN
+GET STACKED DIAGNOSTICS v_err_sql_stmt = PG_SQL_TEXT,
+v_err_sql_pos = PG_ERROR_LOCATION;
+RAISE NOTICE 'exception sql %', v_err_sql_stmt;
+RAISE NOTICE 'exception sql position %', v_err_sql_pos;
+END;
+$$;
+
+--
+-- PG_SQL_TEXT, PG_ERROR_LOCATION
+-- should return empty results, if the dynamic sql is a valid
+--
+DO
+$$
+DECLARE
+v_err_sql_pos INT;
+v_err_sql_stmt TEXT;
+BEGIN
+PERFORM 1/0;
+EXCEPTION
+WHEN OTHERS THEN
+GET STACKED DIAGNOSTICS v_err_sql_stmt = PG_SQL_TEXT,
+v_err_sql_pos = PG_ERROR_LOCATION;
+RAISE NOTICE 'exception sql %', v_err_sql_stmt;
+RAISE NOTICE 'exception sql position %', v_err_sql_pos;
+END;
+$$;

Reply via email to