diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 693423e524..e68a6d7231 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -900,6 +900,15 @@ CREATE TYPE name
function is written in C.
+
+ In PostgreSQL version 16 and later, it is
+ desirable for base types' input functions to return safe
+ errors using the new ereturn() mechanism, rather
+ than throwing ereport() exceptions as in previous
+ versions. See src/backend/utils/fmgr/README for
+ more information.
+
+
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 2585e24845..6c8736f0c4 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -686,6 +686,153 @@ errfinish(const char *filename, int lineno, const char *funcname)
}
+/*
+ * ereturn_start --- begin a "safe" error-reporting cycle
+ *
+ * If "context" isn't an ErrorReturnContext node, this behaves as
+ * errstart(ERROR, domain).
+ *
+ * If it is an ErrorReturnContext node, but the node creator only wants
+ * notification of the fact of a safe error without any details, just set
+ * the error_occurred flag in the ErrorReturnContext node and return false,
+ * which will cause us to skip remaining error processing steps.
+ *
+ * Otherwise, create and initialize error stack entry and return true.
+ * Subsequently, errmsg() and perhaps other routines will be called to further
+ * populate the stack entry. Finally, ereturn_finish() will be called to
+ * tidy up.
+ */
+bool
+ereturn_start(void *context, const char *domain)
+{
+ ErrorReturnContext *ercontext;
+ ErrorData *edata;
+
+ /*
+ * Do we have a context for safe error reporting? If not, just punt to
+ * errstart().
+ */
+ if (context == NULL || !IsA(context, ErrorReturnContext))
+ return errstart(ERROR, domain);
+
+ /* Report that an error was detected */
+ ercontext = (ErrorReturnContext *) context;
+ ercontext->error_occurred = true;
+
+ /* Nothing else to do if caller wants no further details */
+ if (!ercontext->details_please)
+ return false;
+
+ /*
+ * Okay, crank up a stack entry to store the info in.
+ */
+
+ recursion_depth++;
+ if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
+ {
+ /*
+ * Wups, stack not big enough. We treat this as a PANIC condition
+ * because it suggests an infinite loop of errors during error
+ * recovery.
+ */
+ errordata_stack_depth = -1; /* make room on stack */
+ ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
+ }
+
+ /* Initialize data for this error frame */
+ edata = &errordata[errordata_stack_depth];
+ MemSet(edata, 0, sizeof(ErrorData));
+ edata->elevel = LOG; /* signal all is well to ereturn_finish */
+ /* the default text domain is the backend's */
+ edata->domain = domain ? domain : PG_TEXTDOMAIN("postgres");
+ /* initialize context_domain the same way (see set_errcontext_domain()) */
+ edata->context_domain = edata->domain;
+ /* Select default errcode based on the assumed elevel of ERROR */
+ edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
+ /* errno is saved here so that error parameter eval can't change it */
+ edata->saved_errno = errno;
+
+ /*
+ * Any allocations for this error state level should go into the caller's
+ * context. We don't need to pollute ErrorContext, or even require it to
+ * exist, in this code path.
+ */
+ edata->assoc_context = CurrentMemoryContext;
+
+ recursion_depth--;
+ return true;
+}
+
+/*
+ * ereturn_finish --- end a "safe" error-reporting cycle
+ *
+ * If ereturn_start() decided this was a regular error, behave as
+ * errfinish(). Otherwise, package up the error details and save
+ * them in the ErrorReturnContext node.
+ */
+void
+ereturn_finish(void *context, const char *filename, int lineno,
+ const char *funcname)
+{
+ ErrorReturnContext *ercontext = (ErrorReturnContext *) context;
+ ErrorData *edata = &errordata[errordata_stack_depth];
+
+ /* verify stack depth before accessing *edata */
+ CHECK_STACK_DEPTH();
+
+ /*
+ * If ereturn_start punted to errstart, then elevel will be ERROR or
+ * perhaps even PANIC. Punt likewise to errfinish.
+ */
+ if (edata->elevel >= ERROR)
+ errfinish(filename, lineno, funcname);
+
+ /*
+ * Else, we should package up the stack entry contents and deliver them to
+ * the caller.
+ */
+ recursion_depth++;
+
+ /* Save the last few bits of error state into the stack entry */
+ if (filename)
+ {
+ const char *slash;
+
+ /* keep only base name, useful especially for vpath builds */
+ slash = strrchr(filename, '/');
+ if (slash)
+ filename = slash + 1;
+ /* Some Windows compilers use backslashes in __FILE__ strings */
+ slash = strrchr(filename, '\\');
+ if (slash)
+ filename = slash + 1;
+ }
+
+ edata->filename = filename;
+ edata->lineno = lineno;
+ edata->funcname = funcname;
+ edata->elevel = ERROR; /* hide the LOG value used above */
+
+ /*
+ * We skip calling backtrace and context functions, which are more likely
+ * to cause trouble than provide useful context; they might act on the
+ * assumption that a transaction abort is about to occur.
+ */
+
+ /*
+ * Make a copy of the error info for the caller. All the subsidiary
+ * strings are already in the caller's context, so it's sufficient to
+ * flat-copy the stack entry.
+ */
+ ercontext->error_data = palloc_object(ErrorData);
+ memcpy(ercontext->error_data, edata, sizeof(ErrorData));
+
+ /* Exit error-handling context */
+ errordata_stack_depth--;
+ recursion_depth--;
+}
+
+
/*
* errcode --- add SQLSTATE error code to the current error
*
diff --git a/src/backend/utils/fmgr/README b/src/backend/utils/fmgr/README
index 49845f67ac..4aa1ce6d28 100644
--- a/src/backend/utils/fmgr/README
+++ b/src/backend/utils/fmgr/README
@@ -267,6 +267,66 @@ See windowapi.h for more information.
information about the context of the CALL statement, particularly
whether it is within an "atomic" execution context.
+* Some callers of datatype input functions (and in future perhaps
+other classes of functions) pass an instance of ErrorReturnContext.
+This indicates that the caller wishes to handle "safe" errors without
+a transaction-terminating exception being thrown: instead, the callee
+should store information about the error cause in the ErrorReturnContext
+struct and return a dummy result value. Further details appear in
+"Handling Non-Exception Errors" below.
+
+
+Handling Non-Exception Errors
+-----------------------------
+
+Postgres' standard mechanism for reporting errors (ereport() or elog())
+is used for all sorts of error conditions. This means that throwing
+an exception via ereport(ERROR) requires an expensive transaction or
+subtransaction abort and cleanup, since the exception catcher dare not
+make many assumptions about what has gone wrong. There are situations
+where we would rather have a lighter-weight mechanism for dealing
+with errors that are known to be safe to recover from without a full
+transaction cleanup. SQL-callable functions can support this need
+using the ErrorReturnContext context mechanism.
+
+To report a "safe" error, a SQL-callable function should call
+ ereturn(fcinfo->context, ...)
+where it would previously have done
+ ereport(ERROR, ...)
+If the passed "context" is NULL or is not an ErrorReturnContext node,
+then ereturn behaves precisely as ereport(ERROR): the exception is
+thrown via longjmp, so that control does not return. If "context"
+is an ErrorReturnContext node, then the error information included in
+ereturn's subsidiary reporting calls is stored into the context node
+and control returns normally. The function should then return a dummy
+value to its caller. (SQL NULL is recommendable as the dummy value;
+but anything will do, since the caller is expected to ignore the
+function's return value once it sees that an error has been reported
+in the ErrorReturnContext node.)
+
+Considering datatype input functions as examples, typical "safe" error
+conditions include input syntax errors and out-of-range values. An input
+function typically detects these cases with simple if-tests and can easily
+change the following ereport calls to ereturns. Error conditions that
+should NOT be handled this way include out-of-memory, internal errors, and
+anything where there is any question about our ability to continue normal
+processing of the transaction. Those should still be thrown with ereport.
+Because of this restriction, it's typically not necessary to pass the
+error context pointer down very far, as errors reported by palloc or
+other low-level functions are typically reasonable to consider internal.
+
+Because no transaction cleanup will occur, a function that is exiting
+after ereturn() returns normally still bears responsibility for resource
+cleanup. It is not necessary to be concerned about small leakages of
+palloc'd memory, since the caller should be running the function in a
+short-lived memory context. However, resources such as locks, open files,
+or buffer pins must be closed out cleanly, as they would be in the
+non-error code path.
+
+Conventions for callers that use the ErrorReturnContext mechanism
+to trap errors are discussed with the declaration of that struct,
+in nodes.h.
+
Functions Accepting or Returning Sets
-------------------------------------
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 3c210297aa..d200b9c296 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -1548,6 +1548,63 @@ InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
return result;
}
+/*
+ * Call a previously-looked-up datatype input function, with non-exception
+ * handling of "safe" errors.
+ *
+ * This is the same as InputFunctionCall, but the caller also passes a
+ * previously-initialized ErrorReturnContext node. (We declare that as
+ * "void *" to avoid including nodes.h in fmgr.h, but it had better be an
+ * ErrorReturnContext.) Any "safe" errors detected by the input function
+ * will be reported by filling the ercontext struct. The caller must
+ * check ercontext->error_occurred before assuming that the function result
+ * is meaningful.
+ */
+Datum
+InputFunctionCallSafe(FmgrInfo *flinfo, char *str,
+ Oid typioparam, int32 typmod,
+ void *ercontext)
+{
+ LOCAL_FCINFO(fcinfo, 3);
+ Datum result;
+
+ Assert(IsA(ercontext, ErrorReturnContext));
+
+ if (str == NULL && flinfo->fn_strict)
+ return (Datum) 0; /* just return null result */
+
+ InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, ercontext, NULL);
+
+ fcinfo->args[0].value = CStringGetDatum(str);
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].value = ObjectIdGetDatum(typioparam);
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].value = Int32GetDatum(typmod);
+ fcinfo->args[2].isnull = false;
+
+ result = FunctionCallInvoke(fcinfo);
+
+ /* Result value is garbage, and could be null, if an error was reported */
+ if (((ErrorReturnContext *) ercontext)->error_occurred)
+ return (Datum) 0;
+
+ /* Otherwise, 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",
+ flinfo->fn_oid);
+ }
+ else
+ {
+ if (fcinfo->isnull)
+ elog(ERROR, "input function %u returned NULL",
+ flinfo->fn_oid);
+ }
+
+ return result;
+}
+
/*
* Call a previously-looked-up datatype output function.
*
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 380a82b9de..a95bad117f 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -700,6 +700,9 @@ extern Datum OidFunctionCall9Coll(Oid functionId, Oid collation,
/* 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,
+ void *ercontext);
extern Datum OidInputFunctionCall(Oid functionId, char *str,
Oid typioparam, int32 typmod);
extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 1f33902947..7979aea16d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -430,4 +430,31 @@ typedef enum LimitOption
LIMIT_OPTION_DEFAULT, /* No limit present */
} LimitOption;
+/*
+ * ErrorReturnContext -
+ * function call context node for handling of "safe" errors
+ *
+ * A caller wishing to trap "safe" errors must initialize a struct like this
+ * with all fields zero/NULL except for the NodeTag. Optionally, set
+ * details_please = true if more than the bare knowledge that a "safe" error
+ * occurred is required. After calling code that might report an error this
+ * way, check error_occurred to see if an error happened. If so, and if
+ * details_please is true, error_data has been filled with error details
+ * (stored in the callee's memory context!). FreeErrorData() can be called
+ * to release error_data, although this step is typically not necessary
+ * if the called code was run in a short-lived context.
+ *
+ * nodes.h isn't a great place for this, but neither elog.h nor fmgr.h
+ * should depend on nodes.h, so we don't really have a better option.
+ */
+typedef struct ErrorReturnContext
+{
+ pg_node_attr(nodetag_only) /* this is not a member of parse trees */
+
+ NodeTag type;
+ bool error_occurred; /* set to true if we detect a "safe" error */
+ bool details_please; /* does caller want more info than that? */
+ struct ErrorData *error_data; /* details of error, if so */
+} ErrorReturnContext;
+
#endif /* NODES_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f107a818e8..2c8dd3d633 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -235,6 +235,46 @@ extern int getinternalerrposition(void);
ereport(elevel, errmsg_internal(__VA_ARGS__))
+/*----------
+ * Support for reporting "safe" errors that don't require a full transaction
+ * abort to clean up. This is to be used in this way:
+ * ereturn(context,
+ * errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ * errmsg("invalid input syntax for type %s: \"%s\"",
+ * "boolean", in_str),
+ * ... other errxxx() fields as needed ...);
+ *
+ * "context" is a node pointer or NULL, and the remaining auxiliary calls
+ * provide the same error details as in ereport(). If context is not a
+ * pointer to an ErrorReturnContext node, then ereturn(context, ...)
+ * behaves identically to ereport(ERROR, ...). If context is a pointer
+ * to an ErrorReturnContext node, then the information provided by the
+ * auxiliary calls is stored in the context node and control returns
+ * normally. The caller of ereturn() must then do any required cleanup
+ * and return control back to its caller. That caller must check the
+ * ErrorReturnContext node to see whether an error occurred before
+ * it can trust the function's result to be meaningful.
+ *
+ * ereturn_domain() allows a message domain to be specified; it is
+ * precisely analogous to ereport_domain().
+ *----------
+ */
+#define ereturn_domain(context, domain, ...) \
+ do { \
+ void *context_ = (context); \
+ pg_prevent_errno_in_scope(); \
+ if (ereturn_start(context_, domain)) \
+ __VA_ARGS__, ereturn_finish(context_, __FILE__, __LINE__, __func__); \
+ } while(0)
+
+#define ereturn(context, ...) \
+ ereturn_domain(context, TEXTDOMAIN, __VA_ARGS__)
+
+extern bool ereturn_start(void *context, const char *domain);
+extern void ereturn_finish(void *context, const char *filename, int lineno,
+ const char *funcname);
+
+
/* Support for constructing error strings separately from ereport() calls */
extern void pre_format_elog_string(int errnumber, const char *domain);