Skip to content

Commit ddef1a3

Browse files
committed
Allow a context to be passed in for error handling
As pointed out by Tom Lane, we can allow other users of the error handler callbacks to provide their own memory context by adding the context to use to ErrorData and using that instead of explicitly using ErrorContext. This then allows GetErrorContextStack() to be called from inside exception handlers, so modify plpgsql to take advantage of that and add an associated regression test for it.
1 parent a59516b commit ddef1a3

File tree

5 files changed

+221
-66
lines changed

5 files changed

+221
-66
lines changed

src/backend/utils/error/elog.c

+61-65
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ err_gettext(const char *str)
8787
/* This extension allows gcc to check the format string for consistency with
8888
the supplied arguments. */
8989
__attribute__((format_arg(1)));
90-
static void set_errdata_field(char **ptr, const char *str);
90+
static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
9191

9292
/* Global variables */
9393
ErrorContextCallback *error_context_stack = NULL;
@@ -373,6 +373,11 @@ errstart(int elevel, const char *filename, int lineno,
373373
/* errno is saved here so that error parameter eval can't change it */
374374
edata->saved_errno = errno;
375375

376+
/*
377+
* Any allocations for this error state level should go into ErrorContext
378+
*/
379+
edata->assoc_context = ErrorContext;
380+
376381
recursion_depth--;
377382
return true;
378383
}
@@ -786,7 +791,7 @@ errmsg(const char *fmt,...)
786791

787792
recursion_depth++;
788793
CHECK_STACK_DEPTH();
789-
oldcontext = MemoryContextSwitchTo(ErrorContext);
794+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
790795

791796
EVALUATE_MESSAGE(edata->domain, message, false, true);
792797

@@ -815,7 +820,7 @@ errmsg_internal(const char *fmt,...)
815820

816821
recursion_depth++;
817822
CHECK_STACK_DEPTH();
818-
oldcontext = MemoryContextSwitchTo(ErrorContext);
823+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
819824

820825
EVALUATE_MESSAGE(edata->domain, message, false, false);
821826

@@ -838,7 +843,7 @@ errmsg_plural(const char *fmt_singular, const char *fmt_plural,
838843

839844
recursion_depth++;
840845
CHECK_STACK_DEPTH();
841-
oldcontext = MemoryContextSwitchTo(ErrorContext);
846+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
842847

843848
EVALUATE_MESSAGE_PLURAL(edata->domain, message, false);
844849

@@ -859,7 +864,7 @@ errdetail(const char *fmt,...)
859864

860865
recursion_depth++;
861866
CHECK_STACK_DEPTH();
862-
oldcontext = MemoryContextSwitchTo(ErrorContext);
867+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
863868

864869
EVALUATE_MESSAGE(edata->domain, detail, false, true);
865870

@@ -886,7 +891,7 @@ errdetail_internal(const char *fmt,...)
886891

887892
recursion_depth++;
888893
CHECK_STACK_DEPTH();
889-
oldcontext = MemoryContextSwitchTo(ErrorContext);
894+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
890895

891896
EVALUATE_MESSAGE(edata->domain, detail, false, false);
892897

@@ -907,7 +912,7 @@ errdetail_log(const char *fmt,...)
907912

908913
recursion_depth++;
909914
CHECK_STACK_DEPTH();
910-
oldcontext = MemoryContextSwitchTo(ErrorContext);
915+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
911916

912917
EVALUATE_MESSAGE(edata->domain, detail_log, false, true);
913918

@@ -930,7 +935,7 @@ errdetail_plural(const char *fmt_singular, const char *fmt_plural,
930935

931936
recursion_depth++;
932937
CHECK_STACK_DEPTH();
933-
oldcontext = MemoryContextSwitchTo(ErrorContext);
938+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
934939

935940
EVALUATE_MESSAGE_PLURAL(edata->domain, detail, false);
936941

@@ -951,7 +956,7 @@ errhint(const char *fmt,...)
951956

952957
recursion_depth++;
953958
CHECK_STACK_DEPTH();
954-
oldcontext = MemoryContextSwitchTo(ErrorContext);
959+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
955960

956961
EVALUATE_MESSAGE(edata->domain, hint, false, true);
957962

@@ -976,7 +981,7 @@ errcontext_msg(const char *fmt,...)
976981

977982
recursion_depth++;
978983
CHECK_STACK_DEPTH();
979-
oldcontext = MemoryContextSwitchTo(ErrorContext);
984+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
980985

981986
EVALUATE_MESSAGE(edata->context_domain, context, true, true);
982987

@@ -1102,7 +1107,7 @@ internalerrquery(const char *query)
11021107
}
11031108

11041109
if (query)
1105-
edata->internalquery = MemoryContextStrdup(ErrorContext, query);
1110+
edata->internalquery = MemoryContextStrdup(edata->assoc_context, query);
11061111

11071112
return 0; /* return value does not matter */
11081113
}
@@ -1128,19 +1133,19 @@ err_generic_string(int field, const char *str)
11281133
switch (field)
11291134
{
11301135
case PG_DIAG_SCHEMA_NAME:
1131-
set_errdata_field(&edata->schema_name, str);
1136+
set_errdata_field(edata->assoc_context, &edata->schema_name, str);
11321137
break;
11331138
case PG_DIAG_TABLE_NAME:
1134-
set_errdata_field(&edata->table_name, str);
1139+
set_errdata_field(edata->assoc_context, &edata->table_name, str);
11351140
break;
11361141
case PG_DIAG_COLUMN_NAME:
1137-
set_errdata_field(&edata->column_name, str);
1142+
set_errdata_field(edata->assoc_context, &edata->column_name, str);
11381143
break;
11391144
case PG_DIAG_DATATYPE_NAME:
1140-
set_errdata_field(&edata->datatype_name, str);
1145+
set_errdata_field(edata->assoc_context, &edata->datatype_name, str);
11411146
break;
11421147
case PG_DIAG_CONSTRAINT_NAME:
1143-
set_errdata_field(&edata->constraint_name, str);
1148+
set_errdata_field(edata->assoc_context, &edata->constraint_name, str);
11441149
break;
11451150
default:
11461151
elog(ERROR, "unsupported ErrorData field id: %d", field);
@@ -1154,10 +1159,10 @@ err_generic_string(int field, const char *str)
11541159
* set_errdata_field --- set an ErrorData string field
11551160
*/
11561161
static void
1157-
set_errdata_field(char **ptr, const char *str)
1162+
set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str)
11581163
{
11591164
Assert(*ptr == NULL);
1160-
*ptr = MemoryContextStrdup(ErrorContext, str);
1165+
*ptr = MemoryContextStrdup(cxt, str);
11611166
}
11621167

11631168
/*
@@ -1257,6 +1262,9 @@ elog_start(const char *filename, int lineno, const char *funcname)
12571262
edata->funcname = funcname;
12581263
/* errno is saved now so that error parameter eval can't change it */
12591264
edata->saved_errno = errno;
1265+
1266+
/* Use ErrorContext for any allocations done at this level. */
1267+
edata->assoc_context = ErrorContext;
12601268
}
12611269

12621270
/*
@@ -1282,7 +1290,7 @@ elog_finish(int elevel, const char *fmt,...)
12821290
* Format error message just like errmsg_internal().
12831291
*/
12841292
recursion_depth++;
1285-
oldcontext = MemoryContextSwitchTo(ErrorContext);
1293+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
12861294

12871295
EVALUATE_MESSAGE(edata->domain, message, false, false);
12881296

@@ -1366,7 +1374,7 @@ EmitErrorReport(void)
13661374

13671375
recursion_depth++;
13681376
CHECK_STACK_DEPTH();
1369-
oldcontext = MemoryContextSwitchTo(ErrorContext);
1377+
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
13701378

13711379
/*
13721380
* Call hook before sending message to log. The hook function is allowed
@@ -1446,6 +1454,9 @@ CopyErrorData(void)
14461454
if (newedata->internalquery)
14471455
newedata->internalquery = pstrdup(newedata->internalquery);
14481456

1457+
/* Use the calling context for string allocation */
1458+
newedata->assoc_context = CurrentMemoryContext;
1459+
14491460
return newedata;
14501461
}
14511462

@@ -1563,6 +1574,9 @@ ReThrowError(ErrorData *edata)
15631574
if (newedata->internalquery)
15641575
newedata->internalquery = pstrdup(newedata->internalquery);
15651576

1577+
/* Reset the assoc_context to be ErrorContext */
1578+
newedata->assoc_context = ErrorContext;
1579+
15661580
recursion_depth--;
15671581
PG_RE_THROW();
15681582
}
@@ -1630,12 +1644,8 @@ pg_re_throw(void)
16301644
* GetErrorContextStack - Return the context stack, for display/diags
16311645
*
16321646
* Returns a pstrdup'd string in the caller's context which includes the PG
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-
* Note that this function may *not* be called from any existing error case
1637-
* and is not for error-reporting (use ereport() and friends instead, which
1638-
* will also produce a stack trace).
1647+
* error call stack. It is the caller's responsibility to ensure this string
1648+
* is pfree'd (or its context cleaned up) when done.
16391649
*
16401650
* This information is collected by traversing the error contexts and calling
16411651
* each context's callback function, each of which is expected to call
@@ -1644,78 +1654,64 @@ pg_re_throw(void)
16441654
char *
16451655
GetErrorContextStack(void)
16461656
{
1647-
char *result = NULL;
16481657
ErrorData *edata;
16491658
ErrorContextCallback *econtext;
1650-
MemoryContext oldcontext = CurrentMemoryContext;
1651-
1652-
/* this function should not be called from an exception handler */
1653-
Assert(recursion_depth == 0);
16541659

16551660
/*
1656-
* This function should never be called from an exception handler and
1657-
* therefore will only ever be the top item on the errordata stack
1658-
* (which is set up so that the calls to the callback functions are
1659-
* able to use it).
1660-
*
1661-
* Better safe than sorry, so double-check that we are not being called
1662-
* from an exception handler.
1661+
* Okay, crank up a stack entry to store the info in.
16631662
*/
1664-
if (errordata_stack_depth != -1)
1663+
recursion_depth++;
1664+
1665+
if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
16651666
{
1667+
/*
1668+
* Wups, stack not big enough. We treat this as a PANIC condition
1669+
* because it suggests an infinite loop of errors during error
1670+
* recovery.
1671+
*/
16661672
errordata_stack_depth = -1; /* make room on stack */
1667-
ereport(PANIC,
1668-
(errmsg_internal("GetErrorContextStack called from exception handler")));
1673+
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
16691674
}
16701675

16711676
/*
1672-
* Initialize data for the top, and only at this point, error frame as the
1673-
* callback functions we're about to call will turn around and call
1674-
* errcontext(), which expects to find a valid errordata stack.
1677+
* Things look good so far, so initialize our error frame
16751678
*/
1676-
errordata_stack_depth = 0;
16771679
edata = &errordata[errordata_stack_depth];
16781680
MemSet(edata, 0, sizeof(ErrorData));
16791681

16801682
/*
1681-
* Use ErrorContext as a short lived context for calling the callbacks;
1682-
* the callbacks will use it through errcontext() even if we don't call
1683-
* them with it, so we have to clean it up below either way.
1683+
* Set up assoc_context to be the caller's context, so any allocations
1684+
* done (which will include edata->context) will use their context.
16841685
*/
1685-
MemoryContextSwitchTo(ErrorContext);
1686+
edata->assoc_context = CurrentMemoryContext;
16861687

16871688
/*
16881689
* Call any context callback functions to collect the context information
16891690
* into edata->context.
16901691
*
16911692
* Errors occurring in callback functions should go through the regular
1692-
* error handling code which should handle any recursive errors and must
1693-
* never call back to us.
1693+
* error handling code which should handle any recursive errors, though
1694+
* we double-check above, just in case.
16941695
*/
16951696
for (econtext = error_context_stack;
16961697
econtext != NULL;
16971698
econtext = econtext->previous)
16981699
(*econtext->callback) (econtext->arg);
16991700

1700-
MemoryContextSwitchTo(oldcontext);
1701-
17021701
/*
1703-
* Copy out the string into the caller's context, so we can free our
1704-
* error context and reset the error stack. Caller is expected to
1705-
* pfree() the result or throw away the context.
1702+
* Clean ourselves off the stack, any allocations done should have been
1703+
* using edata->assoc_context, which we set up earlier to be the caller's
1704+
* context, so we're free to just remove our entry off the stack and
1705+
* decrement recursion depth and exit.
17061706
*/
1707-
if (edata->context)
1708-
result = pstrdup(edata->context);
1707+
errordata_stack_depth--;
1708+
recursion_depth--;
17091709

17101710
/*
1711-
* Reset error stack- note that we should be the only item on the error
1712-
* stack at this point and therefore it's safe to clean up the whole stack.
1713-
* This function is not intended nor able to be called from exception
1714-
* handlers.
1711+
* Return a pointer to the string the caller asked for, which should have
1712+
* been allocated in their context.
17151713
*/
1716-
FlushErrorState();
1717-
1718-
return result;
1714+
return edata->context;
17191715
}
17201716

17211717

src/include/utils/elog.h

+3
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ typedef struct ErrorData
397397
int internalpos; /* cursor index into internalquery */
398398
char *internalquery; /* text of internally-generated query */
399399
int saved_errno; /* errno at entry */
400+
401+
/* context containing associated non-constant strings */
402+
struct MemoryContextData *assoc_context;
400403
} ErrorData;
401404

402405
extern void EmitErrorReport(void);

src/pl/plpgsql/src/pl_gram.y

+3-1
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,6 @@ stmt_getdiag : K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
895895
/* these fields are disallowed in stacked case */
896896
case PLPGSQL_GETDIAG_ROW_COUNT:
897897
case PLPGSQL_GETDIAG_RESULT_OID:
898-
case PLPGSQL_GETDIAG_CONTEXT:
899898
if (new->is_stacked)
900899
ereport(ERROR,
901900
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -921,6 +920,9 @@ stmt_getdiag : K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
921920
plpgsql_getdiag_kindname(ditem->kind)),
922921
parser_errposition(@1)));
923922
break;
923+
/* these fields are allowed in either case */
924+
case PLPGSQL_GETDIAG_CONTEXT:
925+
break;
924926
default:
925927
elog(ERROR, "unrecognized diagnostic item kind: %d",
926928
ditem->kind);

0 commit comments

Comments
 (0)