From af4e4bf23343ba26029adc3f8f90b8bb1f70c309 Mon Sep 17 00:00:00 2001 From: Vladimir Rachkin Date: Wed, 11 Dec 2024 17:14:45 +0300 Subject: [PATCH] Add infrastucture for "soft" error handle --- src/backend/utils/error/elog.c | 25 +++++++++++ src/backend/utils/fmgr/fmgr.c | 82 ++++++++++++++++++++++++++++++++++ src/include/fmgr.h | 11 +++++ src/include/nodes/miscnodes.h | 26 +++++++++++ src/include/nodes/nodes.h | 2 + src/include/utils/elog.h | 23 ++++++++++ 6 files changed, 169 insertions(+) create mode 100644 src/include/nodes/miscnodes.h diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index d4e6b39a12c..aef4a8c21dc 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -79,6 +79,7 @@ #include "libpq/pqformat.h" #include "libpq/pqsignal.h" #include "mb/pg_wchar.h" +#include "nodes/miscnodes.h" #include "miscadmin.h" #include "postmaster/postmaster.h" #include "postmaster/syslogger.h" @@ -830,6 +831,30 @@ errfinish_and_return(int dummy __attribute__((unused)),...) return edata_copy; } +/* + * errsave_start + */ +bool +errsave_start(struct Node *context, + const char *filename, int lineno, + const char *funcname, const char *domain) +{ + ErrorSaveContext *escontext; + + /* + * Do we have a context for soft error reporting? If not, just punt to + * errstart(). + */ + if (context == NULL || !IsA(context, ErrorSaveContext)) + return errstart(ERROR, filename, lineno, funcname, domain); + + /* Report that a soft error was detected */ + escontext = (ErrorSaveContext *) context; + escontext->error_occurred = true; + + return false; +} + /* * errcode --- add SQLSTATE error code to the current error diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 62bce447d17..b7a21779005 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -23,6 +23,7 @@ #include "lib/stringinfo.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "nodes/miscnodes.h" #include "pgstat.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -1887,6 +1888,41 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2, return result; } +/* + + */ +Datum +OidFunctionCall3CollSafe(Oid functionId, Oid collation, Datum arg1, Datum arg2, + Datum arg3, fmNodePtr escontext) +{ + FmgrInfo flinfo; + FunctionCallInfoData fcinfo; + Datum result; + + fmgr_info(functionId, &flinfo); + + InitFunctionCallInfoData(fcinfo, &flinfo, 3, collation, escontext, NULL); + + fcinfo.arg[0] = arg1; + fcinfo.arg[1] = arg2; + fcinfo.arg[2] = arg3; + fcinfo.argnull[0] = false; + fcinfo.argnull[1] = false; + fcinfo.argnull[2] = false; + + result = FunctionCallInvoke(&fcinfo); + + /* Result value is garbage, and could be null, if an error was reported */ + if (SOFT_ERROR_OCCURRED(escontext)) + return (Datum) 0; + + /* Check for null result, since caller is clearly not expecting one */ + if (fcinfo.isnull) + elog(ERROR, "function %u returned NULL", flinfo.fn_oid); + + return result; +} + /* * Special cases for convenient invocation of datatype I/O functions. @@ -1948,6 +1984,52 @@ InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod) return result; } +Datum +InputFunctionCallSafe(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod, fmNodePtr escontext) +{ + FunctionCallInfoData fcinfo; + Datum result; + bool pushed; + + if (str == NULL && flinfo->fn_strict) + return (Datum) 0; /* just return null result */ + + pushed = SPI_push_conditional(); + + InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, escontext, NULL); + + fcinfo.arg[0] = CStringGetDatum(str); + fcinfo.arg[1] = ObjectIdGetDatum(typioparam); + fcinfo.arg[2] = Int32GetDatum(typmod); + fcinfo.argnull[0] = (str == NULL); + fcinfo.argnull[1] = false; + fcinfo.argnull[2] = false; + + result = FunctionCallInvoke(&fcinfo); + + /* Result value is garbage, and could be null, if an error was reported */ + if (SOFT_ERROR_OCCURRED(escontext)) + return (Datum) 0; + + /* Should get null result if and only if str is NULL */ + if (str == NULL) + { + if (!fcinfo.isnull) + elog(ERROR, "input function %u returned non-NULL", + fcinfo.flinfo->fn_oid); + } + else + { + if (fcinfo.isnull) + elog(ERROR, "input function %u returned NULL", + fcinfo.flinfo->fn_oid); + } + + SPI_pop_conditional(pushed); + + return result; +} + /* * Call a previously-looked-up datatype output function. * diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 6a50df602bc..9ec04d6e311 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -579,6 +579,11 @@ extern Datum OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg6, Datum arg7, Datum arg8, Datum arg9); + +extern Datum OidFunctionCall3CollSafe(Oid functionId, Oid collation, + Datum arg1, Datum arg2, + Datum arg3, fmNodePtr escontext); + /* These macros allow the collation argument to be omitted (with a default of * InvalidOid, ie, no collation). They exist mostly for backwards * compatibility of source code. @@ -641,9 +646,15 @@ extern Datum OidFunctionCall9Coll(Oid functionId, Oid collation, OidFunctionCall9Coll(functionId, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) +#define OidFunctionCall3Safe(functionId, arg1, arg2, arg3, escontext) \ + OidFunctionCall3CollSafe(functionId, InvalidOid, arg1, arg2, arg3, escontext) + + /* Special cases for convenient invocation of datatype I/O functions. */ extern Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod); +extern Datum InputFunctionCallSafe(FmgrInfo *flinfo, char *str, + Oid typioparam, int32 typmod, fmNodePtr escontext); extern Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod); extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val); diff --git a/src/include/nodes/miscnodes.h b/src/include/nodes/miscnodes.h new file mode 100644 index 00000000000..f3968490c7e --- /dev/null +++ b/src/include/nodes/miscnodes.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * miscnodes.h + * Definitions for hard-to-classify node types. + * + * src/include/nodes/miscnodes.h + * + *------------------------------------------------------------------------- + */ + +#ifndef MISCNODES_H +#define MISCNODES_H +#include "nodes/nodes.h" + +typedef struct ErrorSaveContext +{ + NodeTag type; + bool error_occurred; /* set to true if we detect a soft error */ +} ErrorSaveContext; + +/* Often-useful macro for checking if a soft error was reported */ +#define SOFT_ERROR_OCCURRED(escontext) \ + ((escontext) != NULL && IsA(escontext, ErrorSaveContext) && \ + ((ErrorSaveContext *) (escontext))->error_occurred) + +#endif \ No newline at end of file diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index c4d4acf7574..84ee28b787f 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -571,6 +571,8 @@ typedef enum NodeTag T_GpPolicy, /* in catalog/gp_policy.h */ T_RetrieveStmt, + T_ErrorSaveContext, + } NodeTag; /* diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index de4c972ea9d..521811e9669 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -20,6 +20,9 @@ #include #include +/* We cannot include nodes.h yet, so forward-declare struct Node */ +struct Node; + /* Error level codes */ #define DEBUG5 10 /* Debugging messages, in categories of * decreasing detail. */ @@ -204,6 +207,26 @@ extern bool errstart(int elevel, const char *filename, int lineno, const char *funcname, const char *domain); extern void errfinish(int dummy,...); +#define errsave_domain(context, domain, rest) \ + do { \ + struct Node *context_ = (context); \ + if (errsave_start(context_, __FILE__, __LINE__, __func__, domain)) \ + errfinish rest; \ + } while(0) +#define errsave(context, rest) \ + errsave_domain(context, TEXTDOMAIN, rest) + +#define ereturn_domain(context, dummy_value, domain, rest) \ + do { \ + errsave_domain(context, domain, rest); \ + return dummy_value; \ + } while(0) +#define ereturn(context, dummy_value, rest) \ + ereturn_domain(context, dummy_value, TEXTDOMAIN, rest) + +extern bool errsave_start(struct Node* context, const char *filename, int lineno, + const char *funcname, const char *domain); + extern int errcode(int sqlerrcode); extern int errcode_for_file_access(void);