Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8312832

Browse files
committed
Add GET DIAGNOSTICS ... PG_CONTEXT in PL/PgSQL
This adds the ability to get the call stack as a string from within aPL/PgSQL function, which can be handy for logging to a table, or toinclude in a useful message to an end-user.Pavel Stehule, reviewed by Rushabh Lathia and rather heavily whackedaround by Stephen Frost.
1 parentfa2fad3 commit8312832

File tree

10 files changed

+229
-0
lines changed

10 files changed

+229
-0
lines changed

‎doc/src/sgml/plpgsql.sgml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2613,6 +2613,15 @@ SELECT merge_db(1, 'dennis');
26132613
expected.
26142614
</para>
26152615
</example>
2616+
</sect2>
2617+
2618+
<sect2 id="plpgsql-diagnostics">
2619+
<title>Getting Diagnostics Information</title>
2620+
2621+
<indexterm>
2622+
<primary>diagnostics</primary>
2623+
<secondary>in PL/pgSQL</secondary>
2624+
</indexterm>
26162625

26172626
<sect3 id="plpgsql-exception-diagnostics">
26182627
<title>Obtaining information about an error</title>
@@ -2736,6 +2745,54 @@ END;
27362745
</programlisting>
27372746
</para>
27382747
</sect3>
2748+
2749+
<sect3 id="plpgsql-get-diagnostics-context">
2750+
<title>Obtaining the call stack context information</title>
2751+
2752+
<para>
2753+
2754+
<synopsis>
2755+
GET <optional> CURRENT </optional> DIAGNOSTICS <replaceable>variable</replaceable> = <replaceable>PG_CONTEXT</replaceable> <optional> , ... </optional>;
2756+
</synopsis>
2757+
2758+
2759+
Calling <command>GET DIAGNOSTICS</command> with status
2760+
item <varname>PG_CONTEXT</> will return a text string with line(s) of
2761+
text describing the call stack. The first row refers to the
2762+
current function and currently executing <command>GET DIAGNOSTICS</command>
2763+
command. The second and any subsequent rows refer to the calling functions
2764+
up the call stack.
2765+
2766+
<programlisting>
2767+
CREATE OR REPLACE FUNCTION public.outer_func() RETURNS integer AS $$
2768+
BEGIN
2769+
RETURN inner_func();
2770+
END;
2771+
$$ LANGUAGE plpgsql;
2772+
2773+
CREATE OR REPLACE FUNCTION public.inner_func() RETURNS integer AS $$
2774+
DECLARE
2775+
stack text;
2776+
BEGIN
2777+
GET DIAGNOSTICS stack = PG_CONTEXT;
2778+
RAISE NOTICE e'--- Call Stack ---\n%', stack;
2779+
RETURN 1;
2780+
END;
2781+
$$ LANGUAGE plpgsql;
2782+
2783+
SELECT outer_func();
2784+
2785+
NOTICE: --- Call Stack ---
2786+
PL/pgSQL function inner_func() line 4 at GET DIAGNOSTICS
2787+
PL/pgSQL function outer_func() line 3 at RETURN
2788+
outer_func
2789+
------------
2790+
1
2791+
(1 row)
2792+
</programlisting>
2793+
2794+
</para>
2795+
</sect3>
27392796
</sect2>
27402797
</sect1>
27412798

‎src/backend/utils/error/elog.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,75 @@ pg_re_throw(void)
16261626
}
16271627

16281628

1629+
/*
1630+
* GetErrorContextStack - Return the error context stack
1631+
*
1632+
* Returns a pstrdup'd string in the caller's context which includes the full
1633+
* call stack. It is the caller's responsibility to ensure this string is
1634+
* pfree'd (or its context cleaned up) when done.
1635+
*
1636+
* This information is collected by traversing the error contexts and calling
1637+
* each context's callback function, each of which is expected to call
1638+
* errcontext() to return a string which can be presented to the user.
1639+
*/
1640+
char*
1641+
GetErrorContextStack(void)
1642+
{
1643+
char*result=NULL;
1644+
ErrorData*edata;
1645+
ErrorContextCallback*econtext;
1646+
MemoryContextoldcontext=CurrentMemoryContext;
1647+
1648+
/* this function should not be called from an exception handler */
1649+
Assert(recursion_depth==0);
1650+
1651+
/* Check that we have enough room on the stack for ourselves */
1652+
if (++errordata_stack_depth >=ERRORDATA_STACK_SIZE)
1653+
{
1654+
/*
1655+
* Stack not big enough..Something bad has happened, therefore
1656+
* PANIC as we may be in an infinite loop.
1657+
*/
1658+
errordata_stack_depth=-1;/* make room on stack */
1659+
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
1660+
}
1661+
1662+
/* Initialize data for this error frame */
1663+
edata=&errordata[errordata_stack_depth];
1664+
MemSet(edata,0,sizeof(ErrorData));
1665+
1666+
/* Use ErrorContext as a short lived context for the callbacks */
1667+
MemoryContextSwitchTo(ErrorContext);
1668+
1669+
/*
1670+
* Call any context callback functions to collect the context information
1671+
* into edata->context.
1672+
*
1673+
* Errors occurring in callback functions should go through the regular
1674+
* error handling code which should handle any recursive errors.
1675+
*/
1676+
for (econtext=error_context_stack;
1677+
econtext!=NULL;
1678+
econtext=econtext->previous)
1679+
(*econtext->callback) (econtext->arg);
1680+
1681+
MemoryContextSwitchTo(oldcontext);
1682+
1683+
/*
1684+
* Copy out the string into the caller's context, so we can free our
1685+
* error context and reset the error stack. Caller is expected to
1686+
* pfree() the result or throw away the context.
1687+
*/
1688+
if (edata->context)
1689+
result=pstrdup(edata->context);
1690+
1691+
/* Reset error stack */
1692+
FlushErrorState();
1693+
1694+
returnresult;
1695+
}
1696+
1697+
16291698
/*
16301699
* Initialization of error output file
16311700
*/

‎src/include/utils/elog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ extern void FlushErrorState(void);
406406
externvoidReThrowError(ErrorData*edata) __attribute__((noreturn));
407407
externvoidpg_re_throw(void) __attribute__((noreturn));
408408

409+
externchar*GetErrorContextStack(void);
410+
409411
/* Hook for intercepting messages before they are sent to the server log */
410412
typedefvoid (*emit_log_hook_type) (ErrorData*edata);
411413
externPGDLLIMPORTemit_log_hook_typeemit_log_hook;

‎src/pl/plpgsql/src/pl_exec.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,16 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
15991599
estate->cur_error->schema_name);
16001600
break;
16011601

1602+
casePLPGSQL_GETDIAG_CONTEXT:
1603+
{
1604+
char*contextstackstr=GetErrorContextStack();
1605+
1606+
exec_assign_c_string(estate,var,contextstackstr);
1607+
1608+
pfree(contextstackstr);
1609+
}
1610+
break;
1611+
16021612
default:
16031613
elog(ERROR,"unrecognized diagnostic item kind: %d",
16041614
diag_item->kind);

‎src/pl/plpgsql/src/pl_funcs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ plpgsql_getdiag_kindname(int kind)
277277
return"ROW_COUNT";
278278
casePLPGSQL_GETDIAG_RESULT_OID:
279279
return"RESULT_OID";
280+
casePLPGSQL_GETDIAG_CONTEXT:
281+
return"PG_CONTEXT";
280282
casePLPGSQL_GETDIAG_ERROR_CONTEXT:
281283
return"PG_EXCEPTION_CONTEXT";
282284
casePLPGSQL_GETDIAG_ERROR_DETAIL:

‎src/pl/plpgsql/src/pl_gram.y

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ staticList*read_raise_options(void);
303303
%token<keyword>K_OPTION
304304
%token<keyword>K_OR
305305
%token<keyword>K_PERFORM
306+
%token<keyword>K_PG_CONTEXT
306307
%token<keyword>K_PG_DATATYPE_NAME
307308
%token<keyword>K_PG_EXCEPTION_CONTEXT
308309
%token<keyword>K_PG_EXCEPTION_DETAIL
@@ -894,6 +895,7 @@ stmt_getdiag: K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
894895
/* these fields are disallowed in stacked case*/
895896
case PLPGSQL_GETDIAG_ROW_COUNT:
896897
case PLPGSQL_GETDIAG_RESULT_OID:
898+
case PLPGSQL_GETDIAG_CONTEXT:
897899
if (new->is_stacked)
898900
ereport(ERROR,
899901
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -976,6 +978,9 @@ getdiag_item :
976978
elseif (tok_is_keyword(tok, &yylval,
977979
K_RESULT_OID,"result_oid"))
978980
$$ = PLPGSQL_GETDIAG_RESULT_OID;
981+
elseif (tok_is_keyword(tok, &yylval,
982+
K_PG_CONTEXT,"pg_context"))
983+
$$ = PLPGSQL_GETDIAG_CONTEXT;
979984
elseif (tok_is_keyword(tok, &yylval,
980985
K_PG_EXCEPTION_DETAIL,"pg_exception_detail"))
981986
$$ = PLPGSQL_GETDIAG_ERROR_DETAIL;
@@ -2287,6 +2292,7 @@ unreserved_keyword:
22872292
| K_NO
22882293
| K_NOTICE
22892294
| K_OPTION
2295+
| K_PG_CONTEXT
22902296
| K_PG_DATATYPE_NAME
22912297
| K_PG_EXCEPTION_CONTEXT
22922298
| K_PG_EXCEPTION_DETAIL

‎src/pl/plpgsql/src/pl_scanner.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ static const ScanKeyword unreserved_keywords[] = {
135135
PG_KEYWORD("no",K_NO,UNRESERVED_KEYWORD)
136136
PG_KEYWORD("notice",K_NOTICE,UNRESERVED_KEYWORD)
137137
PG_KEYWORD("option",K_OPTION,UNRESERVED_KEYWORD)
138+
PG_KEYWORD("pg_context",K_PG_CONTEXT,UNRESERVED_KEYWORD)
138139
PG_KEYWORD("pg_datatype_name",K_PG_DATATYPE_NAME,UNRESERVED_KEYWORD)
139140
PG_KEYWORD("pg_exception_context",K_PG_EXCEPTION_CONTEXT,UNRESERVED_KEYWORD)
140141
PG_KEYWORD("pg_exception_detail",K_PG_EXCEPTION_DETAIL,UNRESERVED_KEYWORD)

‎src/pl/plpgsql/src/plpgsql.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ enum
124124
{
125125
PLPGSQL_GETDIAG_ROW_COUNT,
126126
PLPGSQL_GETDIAG_RESULT_OID,
127+
PLPGSQL_GETDIAG_CONTEXT,
127128
PLPGSQL_GETDIAG_ERROR_CONTEXT,
128129
PLPGSQL_GETDIAG_ERROR_DETAIL,
129130
PLPGSQL_GETDIAG_ERROR_HINT,

‎src/test/regress/expected/plpgsql.out

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4897,3 +4897,51 @@ ERROR: value for domain orderedarray violates check constraint "sorted"
48974897
CONTEXT: PL/pgSQL function testoa(integer,integer,integer) line 5 at assignment
48984898
drop function arrayassign1();
48994899
drop function testoa(x1 int, x2 int, x3 int);
4900+
-- access to call stack
4901+
create function inner_func(int)
4902+
returns int as $$
4903+
declare _context text;
4904+
begin
4905+
get diagnostics _context = pg_context;
4906+
raise notice '***%***', _context;
4907+
return 2 * $1;
4908+
end;
4909+
$$ language plpgsql;
4910+
create or replace function outer_func(int)
4911+
returns int as $$
4912+
begin
4913+
return inner_func($1);
4914+
end;
4915+
$$ language plpgsql;
4916+
create or replace function outer_outer_func(int)
4917+
returns int as $$
4918+
begin
4919+
return outer_func($1);
4920+
end;
4921+
$$ language plpgsql;
4922+
select outer_outer_func(10);
4923+
NOTICE: ***PL/pgSQL function inner_func(integer) line 4 at GET DIAGNOSTICS
4924+
PL/pgSQL function outer_func(integer) line 3 at RETURN
4925+
PL/pgSQL function outer_outer_func(integer) line 3 at RETURN***
4926+
CONTEXT: PL/pgSQL function outer_func(integer) line 3 at RETURN
4927+
PL/pgSQL function outer_outer_func(integer) line 3 at RETURN
4928+
outer_outer_func
4929+
------------------
4930+
20
4931+
(1 row)
4932+
4933+
-- repeated call should to work
4934+
select outer_outer_func(20);
4935+
NOTICE: ***PL/pgSQL function inner_func(integer) line 4 at GET DIAGNOSTICS
4936+
PL/pgSQL function outer_func(integer) line 3 at RETURN
4937+
PL/pgSQL function outer_outer_func(integer) line 3 at RETURN***
4938+
CONTEXT: PL/pgSQL function outer_func(integer) line 3 at RETURN
4939+
PL/pgSQL function outer_outer_func(integer) line 3 at RETURN
4940+
outer_outer_func
4941+
------------------
4942+
40
4943+
(1 row)
4944+
4945+
drop function outer_outer_func(int);
4946+
drop function outer_func(int);
4947+
drop function inner_func(int);

‎src/test/regress/sql/plpgsql.sql

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3880,3 +3880,36 @@ select testoa(1,2,1); -- fail at update
38803880
38813881
drop function arrayassign1();
38823882
drop function testoa(x1 int, x2 int, x3 int);
3883+
3884+
-- access to call stack
3885+
create function inner_func(int)
3886+
returns int as $$
3887+
declare _context text;
3888+
begin
3889+
get diagnostics _context = pg_context;
3890+
raise notice'***%***', _context;
3891+
return 2 * $1;
3892+
end;
3893+
$$ language plpgsql;
3894+
3895+
create or replace function outer_func(int)
3896+
returns int as $$
3897+
begin
3898+
return inner_func($1);
3899+
end;
3900+
$$ language plpgsql;
3901+
3902+
create or replace function outer_outer_func(int)
3903+
returns int as $$
3904+
begin
3905+
return outer_func($1);
3906+
end;
3907+
$$ language plpgsql;
3908+
3909+
select outer_outer_func(10);
3910+
-- repeated call should to work
3911+
select outer_outer_func(20);
3912+
3913+
drop function outer_outer_func(int);
3914+
drop function outer_func(int);
3915+
drop function inner_func(int);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp