Hi folks The attached patch series adds support for detecting coding errors where a stack-allocated ErrorContextCallback is not popped from the error_context_stack before the variable leaves scope.
With the attached patches this code will emit a backtrace and abort() on cassert builds at the "return" statement where it leaks the stack pointer: ErrorContextCallback cb pg_errcontext_check(); ... cb.previous = error_context_stack; error_context_stack = &cb; ... if (something) return; /* whoops! */ ... error_context_stack = error_context_stack->previous; as I discuss on this old blog article here: https://www.2ndquadrant.com/en/blog/dev-corner-error-context-stack-corruption/ Note the pgl_errcontext_check() in the above, which enables the check. The infrastructure added by the patch could well be useful for other similar validation steps. In fact, I've added a similar validator that detects escapes from PG_TRY() blocks, so you get a neat assertion failure at the point of escape instead of a crash at a __longjmp() with a nonsensical stack. Example using a deliberately introduced mistake in postgres_fdw as a test: Without the PG_TRY() patch: #0 __longjmp () at ../sysdeps/x86_64/__longjmp.S:111 #1 0xc35a5d9e1a902cfc in ?? () Backtrace stopped: Cannot access memory at address 0xc0ea5d9e1a902cfc With the PG_TRY() patch (abbreviated): #3 0x0000000000abbd4b in pg_try_check_return_guard #4 0x00007f8e2e20e41a in fetch_more_data #5 0x00007f8e2e20a80a in postgresIterateForeignScan ... Note that valgrind's memcheck, gcc's -fstack-protector, etc are all quite bad at detecting this class of error, so being able to do so automatically should be nice. I've had an interesting time chasing down error context stack mistakes a few times in the past We unfortunately cannot use it for RAII-style coding because the underlying __attribute__((callback(..))) is not available on MSVC. As usual. But it remains useful for checking and assertions. The patch is supplied as a small series: (1) Add some documentation on error context callback usage (2) Add pg_errcontext_check() to detect ErrorContextCallback escapes (3) Enable pg_errcontext_check() for all in-tree users (4) Document that generally PG_CATCH() should PG_RE_THROW() (5) Assert() if returning from inside a PG_TRY() / PG_CATCH() block (6) [RFC] Add a more succinct API for error context stack callbacks (7) [RFC] Use __has_attribute for compiler attribute detection where possible I also added a patch (6) on top as a RFC of sorts, which adds a more succinct API for error context setup and teardown. It's not required for these changes, but it might make sense to adopt it at the same time if we're changing usage across the tree anyway. A further patch (7) switches over to using __has_attribute instead of explicit compiler version checks. Again, optional, more of a RFC. -- Craig Ringer http://www.2ndQuadrant.com/ 2ndQuadrant - PostgreSQL Solutions for the Enterprise
From 65127dced44b737179af53f2d3aca6b1b9993fa2 Mon Sep 17 00:00:00 2001 From: Craig Ringer <craig.rin...@2ndquadrant.com> Date: Thu, 3 Sep 2020 12:53:44 +0800 Subject: [PATCH 1/9] Add some documentation on error context callback usage --- src/include/utils/elog.h | 55 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 1e09ee0541..18276e1e93 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -221,7 +221,60 @@ extern void pre_format_elog_string(int errnumber, const char *domain); extern char *format_elog_string(const char *fmt,...) pg_attribute_printf(1, 2); -/* Support for attaching context information to error reports */ +/* + * Support for attaching context information to error reports. + * + * Functions may set append their own ErrorContextCallback to the + * error_context_stack chain to have callbacks invoked during ereport() or + * elog() processing, optionally with a pointer to local state. + * + * The callback typically calls errcontext() one or more times to append + * to the CONTEXT field of the error report. + * + * Error context callbacks may be omitted when Pg can show that it will not + * emit the error message, so it is not safe for an error context callback to + * have side-effects. + * + * Any memory allocated must be in the CurrentMemoryContext which is set to + * ErrorContext before the callback is invoked. + * + * The typical usage pattern is: + * + * struct MyFuncErrctxArg { + * // track your state here for error reporting + * int some_info_here; + * }; + * + * static void + * my_func_errctx_callback(void *arg) + * { + * struct MyFuncErrctxArg *ctxarg = arg; + * errcontext("in my_func() with some_info=%d", + * ctxarg->some_info_here; + * } + * + * void + * my_func(void) + * { + * ErrorContextCallback errctx; + * struct MyFuncErrctxArg ctxarg; + * + * errctx.callback = my_func_errctx_callback; + * errctx.arg = &ctxarg; + * errctx.previous = error_context_stack; + * error_context_stack = &errctx; + * + * // do your normal work here but (important) do NOT return without + * // popping the error context stack: + * + * if (error_context_stack == &errctx) + * error_context_stack = error_context_stack->previous; + * } + * + * It's a bit verbose, but extremely valuable for adding extra diagnostic + * information to errors at runtime. + *------------ + */ typedef struct ErrorContextCallback { -- 2.26.2
From 73a3bb31b789197eb182139fa0fb6e49ce183931 Mon Sep 17 00:00:00 2001 From: Craig Ringer <craig.rin...@2ndquadrant.com> Date: Thu, 3 Sep 2020 13:22:02 +0800 Subject: [PATCH 2/9] Add pg_errcontext_check() to detect ErrorContextCallback escapes The new macro pg_errcontext_check() may be applied to the declaration of any ErrorContextCallback auto variable on the stack to detect failure to pop the error context stack before returning from the function. It has no effect unless PostgreSQL was built with --enable-cassert. Change ErrorContextCallback errcb; to ErrorContextCallback pg_errcontext_check(); to use it. --- src/backend/utils/error/elog.c | 35 +++++++++++++++++++++++++++ src/include/c.h | 43 ++++++++++++++++++++++++++++++++++ src/include/utils/elog.h | 14 ++++++++++- 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index d0b368530e..f9cea36ba1 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -3548,3 +3548,38 @@ trace_recovery(int trace_level) return trace_level; } + +/* + * Check that pointers to auto variables in a function's stack are not retained + * in the error_context_stack when the function's scope ends. + * + * This is the implementation of pgl_errcontext_check() + */ +void +check_errcontext_stack_on_return(const ErrorContextCallback * const cb) +{ +#ifdef USE_ASSERT_CHECKING + /* + * No reference to the stack-allocated ErrorContextCallback that's leaving + * scope may remain on the global error_context_stack. + * + * Ideally we'd be able to report the function name, file and line of the + * ErrorContextCallback declaration, rather than this check function. But + * __attribute__((cleanup(cbfn))) does not offer any way to pass additional + * parameters. + * + * We do our best to emit a backtrace and (if enabled) a core file to help + * diagnose the problem. The pointer is still valid at this point, so we can + * safely use the error context stack to unwind. + * + * Don't attempt to walk the rest of the error_context_stack. We'll assume + * that anything below the top entry must be valid. + */ + if (error_context_stack == cb) + { + ereport(PANIC, + errmsg_internal("error_context_stack not unwound on function return"), + errbacktrace()); + } +#endif +} diff --git a/src/include/c.h b/src/include/c.h index 2c61ca8aa8..5ded7f3d16 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -178,6 +178,49 @@ #define pg_noinline #endif +/* + * pg_attribute_cleanup(cbfunc) can be used to annotate a stack (auto) variable + * with a cleanup call that's automatically executed by the compiler on any + * path where it leaves scope. + * + * This is useful for asserting that something stack-allocated got cleaned up + * even if a function has multiple returns, gotos, etc. + * + * WARNING: Do not use this for code that would execute incorrectly if the + * cleanup function is not called, because not all toolchains support it + * and it will be a silent no-op if unsupported. Instead use it to assert that + * an explicit cleanup step was not missed. + * + * The cleanup function is NOT executed before a longjmp() or siglongjmp(), so + * it will not execute on elog(ERROR) / ereport(ERROR), or as a result of a + * PG_RE_THROW(). It may be coupled with a PG_FINALLY() block if that is a + * concern. The cleanup function can then detect premature escape from the + * PG_TRY() block via an explicit return. + * + * The variant pg_attribute_cleanup_cassert(cb) is ignored unless PostgreSQL + * was built with assertions enabled. + * + * Supported at least on gcc and LLVM clang. + * + * See + * - the pg_errcontext_check() macro in elog.h + * - https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-cleanup-variable-attribute + * + * Added in gcc 3.4, but that's so old we'll just require gcc4. clang claims + * to be a much newer gcc, so it'll be fine. + */ +#if __GNUC__ >= 4 +#define pg_attribute_cleanup(cb) __attribute__((cleanup(cb))) +#ifdef USE_ASSERT_CHECKING +#define pg_attribute_cleanup_cassert(cb) __attribute__((cleanup(cb))) +#else +#define pg_attribute_cleanup_cassert(cb) +#endif +#else +#define pg_attribute_cleanup(cb) +#define pg_attribute_cleanup_cassert(cb) +#endif + /* * Mark a point as unreachable in a portable fashion. This should preferably * be something that the compiler understands, to aid code generation. diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 18276e1e93..95844de850 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -256,7 +256,7 @@ extern char *format_elog_string(const char *fmt,...) pg_attribute_printf(1, 2); * void * my_func(void) * { - * ErrorContextCallback errctx; + * ErrorContextCallback errctx pgl_errcontext_check(); * struct MyFuncErrctxArg ctxarg; * * errctx.callback = my_func_errctx_callback; @@ -286,6 +286,18 @@ typedef struct ErrorContextCallback extern PGDLLIMPORT ErrorContextCallback *error_context_stack; +/*----------- + * Check for error context stack escapes on cassert builds. + * + * Apply to the ErrorContextCallback declaration (not type), i.e.: + * + * ErrorContextCallback errctx check_errcontext_stack_on_return(); + *----------- + */ +extern void check_errcontext_stack_on_return(const ErrorContextCallback * const cb); +#define pg_errcontext_check() \ + pg_attribute_cleanup_cassert(check_errcontext_stack_on_return) + /*---------- * API for catching ereport(ERROR) exits. Use these macros like so: * -- 2.26.2
From aa5c2c30fd60a89db51e2ba3f64f9a962f6d40e5 Mon Sep 17 00:00:00 2001 From: Craig Ringer <craig.rin...@2ndquadrant.com> Date: Thu, 3 Sep 2020 12:53:55 +0800 Subject: [PATCH 4/9] Document that generally PG_CATCH() should PG_RE_THROW() --- src/include/utils/elog.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 95844de850..1552366477 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -307,7 +307,9 @@ extern void check_errcontext_stack_on_return(const ErrorContextCallback * const * } * PG_CATCH(); * { - * ... error recovery code ... + * ... local error handling code ... + * // In most cases you must re-propagate the error: + * PG_RE_THROW(); * } * PG_END_TRY(); * @@ -336,6 +338,12 @@ extern void check_errcontext_stack_on_return(const ErrorContextCallback * const * You cannot use both PG_CATCH() and PG_FINALLY() in the same * PG_TRY()/PG_END_TRY() block. * + * Important: PG_CATCH() does not do any cleanup of memory contexts, executor + * and transaction state, etc. You can NOT just PG_CATCH() arbitrary errors and + * continue normal execution without a PG_RE_THROW(). See the block following + * the sigsetjmp() call in PostgresMain() for an example of how cleanup of + * backend state after an error may be performed. + * * Note: while the system will correctly propagate any new ereport(ERROR) * occurring in the recovery section, there is a small limit on the number * of levels this will work for. It's best to keep the error recovery -- 2.26.2
From 8fe81e6a79f4e661fe0d0d82a781af98c6f45ffb Mon Sep 17 00:00:00 2001 From: Craig Ringer <craig.rin...@2ndquadrant.com> Date: Thu, 3 Sep 2020 18:33:24 +0800 Subject: [PATCH 5/9] Assert() if returning from inside a PG_TRY() / PG_CATCH() block --- src/backend/utils/error/elog.c | 9 +++++++++ src/include/utils/elog.h | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index f9cea36ba1..11de7ebd3c 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -3583,3 +3583,12 @@ check_errcontext_stack_on_return(const ErrorContextCallback * const cb) } #endif } + +/* + * Implementation of the return guard checking in PG_TRY() / PG_END_TRY() + */ +void +pg_try_check_return_guard(const bool * const did_return) +{ + Assert(!(*did_return)); /* "return" inside PG_TRY() or PG_CATCH() */ +} diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 1552366477..33fd1ca4a9 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -365,12 +365,24 @@ extern void check_errcontext_stack_on_return(const ErrorContextCallback * const * warnings are just about entirely useless for catching such oversights. *---------- */ + +extern void pg_try_check_return_guard(const bool * const returned_from_pg_try); +#ifdef USE_ASSERT_CHECKING +#define PG_TRY_SET_RETURN_GUARD \ + bool _pg_try_returned pg_attribute_cleanup_cassert(pg_try_check_return_guard) = true +#define PG_TRY_CLEAR_RETURN_GUARD _pg_try_returned = false +#else +#define PG_TRY_SET_RETURN_GUARD +#define PG_TRY_CLEAR_RETURN_GUARD +#endif + #define PG_TRY() \ do { \ sigjmp_buf *_save_exception_stack = PG_exception_stack; \ ErrorContextCallback *_save_context_stack = error_context_stack; \ sigjmp_buf _local_sigjmp_buf; \ bool _do_rethrow = false; \ + PG_TRY_SET_RETURN_GUARD; \ if (sigsetjmp(_local_sigjmp_buf, 0) == 0) \ { \ PG_exception_stack = &_local_sigjmp_buf @@ -396,6 +408,7 @@ extern void check_errcontext_stack_on_return(const ErrorContextCallback * const PG_RE_THROW(); \ PG_exception_stack = _save_exception_stack; \ error_context_stack = _save_context_stack; \ + PG_TRY_CLEAR_RETURN_GUARD; \ } while (0) /* -- 2.26.2
From 047edeedde19db720ec83b575dfa0b121246371f Mon Sep 17 00:00:00 2001 From: Craig Ringer <craig.rin...@2ndquadrant.com> Date: Thu, 3 Sep 2020 18:38:27 +0800 Subject: [PATCH 3/9] Enable pg_errcontext_check() for all in-tree users --- contrib/file_fdw/file_fdw.c | 4 +-- contrib/postgres_fdw/postgres_fdw.c | 2 +- src/backend/access/heap/vacuumlazy.c | 4 +-- src/backend/access/transam/xlog.c | 2 +- src/backend/catalog/pg_proc.c | 2 +- src/backend/commands/copy.c | 2 +- src/backend/commands/foreigncmds.c | 2 +- src/backend/executor/functions.c | 2 +- src/backend/executor/spi.c | 10 ++++---- src/backend/optimizer/util/clauses.c | 4 +-- src/backend/parser/parse_type.c | 2 +- src/backend/replication/logical/logical.c | 30 +++++++++++------------ src/backend/replication/logical/worker.c | 4 +-- src/backend/storage/buffer/bufmgr.c | 4 +-- src/backend/storage/lmgr/lmgr.c | 2 +- src/pl/plpgsql/src/pl_comp.c | 4 +-- src/pl/plpgsql/src/pl_exec.c | 6 ++--- src/pl/plpython/plpy_exec.c | 4 +-- src/pl/plpython/plpy_main.c | 4 +-- src/pl/tcl/pltcl.c | 2 +- 20 files changed, 48 insertions(+), 48 deletions(-) diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 072a6dc1c1..17187f1da2 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -707,7 +707,7 @@ fileIterateForeignScan(ForeignScanState *node) FileFdwExecutionState *festate = (FileFdwExecutionState *) node->fdw_state; TupleTableSlot *slot = node->ss.ss_ScanTupleSlot; bool found; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); /* Set up callback to identify error line number. */ errcallback.callback = CopyFromErrorCallback; @@ -1108,7 +1108,7 @@ file_acquire_sample_rows(Relation onerel, int elevel, bool is_program; List *options; CopyState cstate; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); MemoryContext oldcontext = CurrentMemoryContext; MemoryContext tupcontext; diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index a31abce7c9..1151829197 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -6323,7 +6323,7 @@ make_tuple_from_result_row(PGresult *res, bool *nulls; ItemPointer ctid = NULL; ConversionLocation errpos; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); MemoryContext oldcontext; ListCell *lc; int j; diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 92389e6666..1c63a25339 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -439,7 +439,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params, double new_live_tuples; TransactionId new_frozen_xid; MultiXactId new_min_multi; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(params != NULL); Assert(params->index_cleanup != VACOPT_TERNARY_DEFAULT); @@ -3517,7 +3517,7 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc) char *sharedquery; IndexBulkDeleteResult **stats; LVRelStats vacrelstats; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); lvshared = (LVShared *) shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_SHARED, false); diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 09c01ed4ae..81d04912fe 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7167,7 +7167,7 @@ StartupXLOG(void) if (record != NULL) { - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); TimestampTz xtime; InRedo = true; diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index a28ab74d60..b389f0d0a8 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -807,7 +807,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) Datum tmp; char *prosrc; parse_error_callback_arg callback_arg; - ErrorContextCallback sqlerrcontext; + ErrorContextCallback sqlerrcontext pg_errcontext_check(); bool haspolyarg; int i; diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index db7d24a511..202d81b845 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2711,7 +2711,7 @@ CopyFrom(CopyState cstate) MemoryContext oldcontext = CurrentMemoryContext; PartitionTupleRouting *proute = NULL; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); CommandId mycid = GetCurrentCommandId(true); int ti_options = 0; /* start with default options for insert */ BulkInsertState bistate = NULL; diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index de31ddd1f3..90236bdc32 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -1512,7 +1512,7 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt) { char *cmd = (char *) lfirst(lc); import_error_callback_arg callback_arg; - ErrorContextCallback sqlerrcontext; + ErrorContextCallback sqlerrcontext pg_errcontext_check(); List *raw_parsetree_list; ListCell *lc2; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index f940f48c6d..2ea1e2e5fc 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1007,7 +1007,7 @@ Datum fmgr_sql(PG_FUNCTION_ARGS) { SQLFunctionCachePtr fcache; - ErrorContextCallback sqlerrcontext; + ErrorContextCallback sqlerrcontext pg_errcontext_check(); MemoryContext oldcontext; bool randomAccess; bool lazyEvalOK; diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 055ebb77ae..7fcd172721 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -1436,7 +1436,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, Snapshot snapshot; MemoryContext oldcontext; Portal portal; - ErrorContextCallback spierrcontext; + ErrorContextCallback spierrcontext pg_errcontext_check(); /* * Check that the plan is something the Portal code will special-case as @@ -1909,7 +1909,7 @@ SPI_plan_get_cached_plan(SPIPlanPtr plan) { CachedPlanSource *plansource; CachedPlan *cplan; - ErrorContextCallback spierrcontext; + ErrorContextCallback spierrcontext pg_errcontext_check(); Assert(plan->magic == _SPI_PLAN_MAGIC); @@ -2050,7 +2050,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan) List *raw_parsetree_list; List *plancache_list; ListCell *list_item; - ErrorContextCallback spierrcontext; + ErrorContextCallback spierrcontext pg_errcontext_check(); /* * Setup error traceback support for ereport() @@ -2155,7 +2155,7 @@ _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan) List *raw_parsetree_list; List *plancache_list; ListCell *list_item; - ErrorContextCallback spierrcontext; + ErrorContextCallback spierrcontext pg_errcontext_check(); /* * Setup error traceback support for ereport() @@ -2219,7 +2219,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, SPITupleTable *my_tuptable = NULL; int res = 0; bool pushed_active_snap = false; - ErrorContextCallback spierrcontext; + ErrorContextCallback spierrcontext pg_errcontext_check(); CachedPlan *cplan = NULL; ListCell *lc1; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 750586fceb..3ebb581ed5 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -4369,7 +4369,7 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid, MemoryContext oldcxt; MemoryContext mycxt; inline_error_callback_arg callback_arg; - ErrorContextCallback sqlerrcontext; + ErrorContextCallback sqlerrcontext pg_errcontext_check(); FuncExpr *fexpr; SQLFunctionParseInfoPtr pinfo; TupleDesc rettupdesc; @@ -4858,7 +4858,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) MemoryContext oldcxt; MemoryContext mycxt; inline_error_callback_arg callback_arg; - ErrorContextCallback sqlerrcontext; + ErrorContextCallback sqlerrcontext pg_errcontext_check(); SQLFunctionParseInfoPtr pinfo; TypeFuncClass functypclass; TupleDesc rettupdesc; diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 2709f6f9c7..33176e5d59 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -743,7 +743,7 @@ typeStringToTypeName(const char *str) ResTarget *restarget; TypeCast *typecast; TypeName *typeName; - ErrorContextCallback ptserrcontext; + ErrorContextCallback ptserrcontext pg_errcontext_check(); /* make sure we give useful error for empty input */ if (strspn(str, " \t\n\r\f") == strlen(str)) diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c index 0f6af952f9..bcb9eacfe0 100644 --- a/src/backend/replication/logical/logical.c +++ b/src/backend/replication/logical/logical.c @@ -665,7 +665,7 @@ static void startup_cb_wrapper(LogicalDecodingContext *ctx, OutputPluginOptions *opt, bool is_init) { LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -692,7 +692,7 @@ static void shutdown_cb_wrapper(LogicalDecodingContext *ctx) { LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -725,7 +725,7 @@ begin_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn) { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -756,7 +756,7 @@ commit_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -787,7 +787,7 @@ change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -824,7 +824,7 @@ truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -862,7 +862,7 @@ bool filter_by_origin_cb_wrapper(LogicalDecodingContext *ctx, RepOriginId origin_id) { LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); bool ret; Assert(!ctx->fast_forward); @@ -895,7 +895,7 @@ message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -930,7 +930,7 @@ stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -976,7 +976,7 @@ stream_stop_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -1022,7 +1022,7 @@ stream_abort_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -1061,7 +1061,7 @@ stream_commit_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -1100,7 +1100,7 @@ stream_change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -1147,7 +1147,7 @@ stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); @@ -1187,7 +1187,7 @@ stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, { LogicalDecodingContext *ctx = cache->private_data; LogicalErrorCallbackState state; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Assert(!ctx->fast_forward); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 812aca8011..87d3cf8f7f 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -476,7 +476,7 @@ slot_store_data(TupleTableSlot *slot, LogicalRepRelMapEntry *rel, int natts = slot->tts_tupleDescriptor->natts; int i; SlotErrCallbackArg errarg; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); ExecClearTuple(slot); @@ -592,7 +592,7 @@ slot_modify_data(TupleTableSlot *slot, TupleTableSlot *srcslot, int natts = slot->tts_tupleDescriptor->natts; int i; SlotErrCallbackArg errarg; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); /* We'll fill "slot" with a virtual tuple, so we must start with ... */ ExecClearTuple(slot); diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index a2a963bd5b..b8c06259f8 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -2728,7 +2728,7 @@ static void FlushBuffer(BufferDesc *buf, SMgrRelation reln) { XLogRecPtr recptr; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); instr_time io_start, io_time; Block bufBlock; @@ -3275,7 +3275,7 @@ FlushRelationBuffers(Relation rel) ((buf_state = pg_atomic_read_u32(&bufHdr->state)) & (BM_VALID | BM_DIRTY)) == (BM_VALID | BM_DIRTY)) { - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); Page localpage; localpage = (char *) LocalBufHdrGetBlock(bufHdr); diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c index 7409de9405..14483f3238 100644 --- a/src/backend/storage/lmgr/lmgr.c +++ b/src/backend/storage/lmgr/lmgr.c @@ -641,7 +641,7 @@ XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, { LOCKTAG tag; XactLockTableWaitInfo info; - ErrorContextCallback callback; + ErrorContextCallback callback pg_errcontext_check(); bool first = true; /* diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index e7f4a5f291..7894e0437a 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -277,7 +277,7 @@ do_compile(FunctionCallInfo fcinfo, PLpgSQL_variable *var; PLpgSQL_rec *rec; int i; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); int parse_rc; Oid rettypeid; int numargs; @@ -844,7 +844,7 @@ plpgsql_compile_inline(char *proc_source) { char *func_name = "inline_code_block"; PLpgSQL_function *function; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); PLpgSQL_variable *var; int parse_rc; MemoryContext func_cxt; diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index d4a3d58daa..c36d21e811 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -470,7 +470,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo, bool atomic) { PLpgSQL_execstate estate; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); int i; int rc; @@ -902,7 +902,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func, TriggerData *trigdata) { PLpgSQL_execstate estate; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); int rc; TupleDesc tupdesc; PLpgSQL_rec *rec_new, @@ -1141,7 +1141,7 @@ void plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata) { PLpgSQL_execstate estate; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); int rc; /* diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c index c6f6a6fbcc..d2a41cb156 100644 --- a/src/pl/plpython/plpy_exec.c +++ b/src/pl/plpython/plpy_exec.c @@ -60,7 +60,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc) PyObject *volatile plrv = NULL; FuncCallContext *volatile funcctx = NULL; PLySRFState *volatile srfstate = NULL; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); /* * If the function is called recursively, we must push outer-level @@ -887,7 +887,7 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata, Datum *volatile modvalues; bool *volatile modnulls; bool *volatile modrepls; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); plerrcontext.callback = plpython_trigger_error_callback; plerrcontext.previous = error_context_stack; diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c index 3eedaa80da..25bde27ca1 100644 --- a/src/pl/plpython/plpy_main.c +++ b/src/pl/plpython/plpy_main.c @@ -218,7 +218,7 @@ plpython_call_handler(PG_FUNCTION_ARGS) bool nonatomic; Datum retval; PLyExecutionContext *exec_ctx; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); PLy_initialize(); @@ -300,7 +300,7 @@ plpython_inline_handler(PG_FUNCTION_ARGS) FmgrInfo flinfo; PLyProcedure proc; PLyExecutionContext *exec_ctx; - ErrorContextCallback plerrcontext; + ErrorContextCallback plerrcontext pg_errcontext_check(); PLy_initialize(); diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index f4eabc8f39..357713e228 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -591,7 +591,7 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted) LOCAL_FCINFO(fcinfo, 0); char *start_proc; const char *gucname; - ErrorContextCallback errcallback; + ErrorContextCallback errcallback pg_errcontext_check(); List *namelist; Oid procOid; HeapTuple procTup; -- 2.26.2
From a60b0dc84907c323f553815dcd14e36d90ad326d Mon Sep 17 00:00:00 2001 From: Craig Ringer <craig.rin...@2ndquadrant.com> Date: Thu, 3 Sep 2020 13:53:29 +0800 Subject: [PATCH 6/9] Add a more succinct API for error context stack callbacks Error context stack callbacks may now be set up for a function with: void my_function(void) { PG_ERRCONTEXT_VARS(errcb); struct MyFunctionCtxArgs ctxargs; PG_ERRCONTEXT_PUSH(errcb, the_callback_fn(), ctxargs); PG_ERRCONTEXT_POP(errcb); } The macros push and pop the stack, maintain the previous error context stack pointer, etc. --- src/include/utils/elog.h | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 33fd1ca4a9..db713907e2 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -256,23 +256,19 @@ extern char *format_elog_string(const char *fmt,...) pg_attribute_printf(1, 2); * void * my_func(void) * { - * ErrorContextCallback errctx pgl_errcontext_check(); + * PG_ERRCONTEXT_VARS(errctx); * struct MyFuncErrctxArg ctxarg; * - * errctx.callback = my_func_errctx_callback; - * errctx.arg = &ctxarg; - * errctx.previous = error_context_stack; - * error_context_stack = &errctx; + * PG_ERRCONTEXT_PUSH(errctx, my_func_errctx_callback, &ctxarg); * * // do your normal work here but (important) do NOT return without * // popping the error context stack: * - * if (error_context_stack == &errctx) - * error_context_stack = error_context_stack->previous; + * PG_ERRCONTEXT_POP(errctx); * } * - * It's a bit verbose, but extremely valuable for adding extra diagnostic - * information to errors at runtime. + * The error context argument is optional. Your error context callback can use + * a constant message, global data, etc, instead. *------------ */ @@ -285,6 +281,18 @@ typedef struct ErrorContextCallback extern PGDLLIMPORT ErrorContextCallback *error_context_stack; +#define PG_ERRCONTEXT_VARS(varname) \ + ErrorContextCallback varname pgl_errcontext_check() + +#define PG_ERRCONTEXT_PUSH(varname, cbfunc, cbarg) \ + (varname).callback = (cbfunc); \ + (varname).arg = (cbarg); \ + (varname).previous = error_context_stack; \ + error_context_stack = &(varname) + +#define PG_ERRCONTEXT_POP(varname) \ + if (error_context_stack == &varname) \ + error_context_stack = error_context_stack->previous /*----------- * Check for error context stack escapes on cassert builds. -- 2.26.2
From 4bb09d7bd1790b735f2957ae92fc76e967a122d5 Mon Sep 17 00:00:00 2001 From: Craig Ringer <craig.rin...@2ndquadrant.com> Date: Thu, 3 Sep 2020 12:05:04 +0800 Subject: [PATCH 7/9] Use __has_attribute for compiler attribute detection where possible GCC 5, LLVM clang, suncc 5.13, and increasingly other toolchains support the __has_attribute function-like macro as a preprocessor extension. This macro can be used to detect whether a given __attribute__((attname)) is supported by the compiler without needing external configure tests etc. Adopt this in c.h in place of explicit tests for __GNUC__ where possible. While clang and llvm both define __GNUC__ to version comparable to the equivalent gcc feature set, it's more explicit to test for the actual features we want rather than a compiler version. --- revertme | 17 +++++++++ src/include/c.h | 92 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 revertme diff --git a/revertme b/revertme new file mode 100644 index 0000000000..1b177c71eb --- /dev/null +++ b/revertme @@ -0,0 +1,17 @@ +diff --git a/src/include/c.h b/src/include/c.h +index f2cd6e12a0..1d1f489d34 100644 +--- a/src/include/c.h ++++ b/src/include/c.h +@@ -205,8 +205,11 @@ + * See + * - the pgl_errcontext_check() macro in elog.h + * - https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-cleanup-variable-attribute ++ * ++ * Added in gcc 3.4, but that's so old we'll just require gcc4. clang claims ++ * to be a much newer gcc, so it'll be fine. + */ +-#if __has_attribute(cleanup) ++#if __GNUC__ >= 4 + #define pgl_attribute_cleanup(cb) __attribute__((cleanup(cb))) + #ifdef USE_ASSERT_CHECKING + #define pgl_attribute_cleanup_cassert(cb) __attribute__((cleanup(cb))) diff --git a/src/include/c.h b/src/include/c.h index 5ded7f3d16..237dd6e679 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -85,26 +85,71 @@ /* * Disable "inline" if PG_FORCE_DISABLE_INLINE is defined. + * * This is used to work around compiler bugs and might also be useful for * investigatory purposes. + * + * In most cases it's better to use -fno-inline. */ #ifdef PG_FORCE_DISABLE_INLINE #undef inline #define inline #endif +/* + * Attribute macro support detection. + * + * GCC, LLVM's clang and sunpro C all support the __has_attribute preprocessor + * extension to allow for compile-time detection of individual attribute + * support without needing to use configure tests, compiler version guards, + * etc. + * + * __has_attribute was added in: + * + * - clang 2.9 (2010) + * - gcc 5 (2014) + * - suncc 5.13 (late 2014) + * + * See: + * + * - https://clang.llvm.org/docs/LanguageExtensions.html + * - https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005fattribute.html + * + * We'll treat GCC and clang versions older than these as not supporting the + * attribute tested for. + * + * Explicit tests are still required for newer compilers that support some + * attributes we want, but not the __has_attribute test, like XLC or older + * SUNPRO compilers. + */ +#ifdef __has_attribute +#define pg_has_attribute(attname) __has_attribute(attname) +#else +#define pg_has_attribute(attname) 0 +#endif + +/* Expose the similar __has_builtin for convenience here too */ +#ifdef __has_builtin +#define pg_has_builtin(builtinname) __has_builtin(builtinname) +#else +#define pg_has_builtin(builtinname) 0 +#endif + /* * Attribute macros * * GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html * GCC: https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html * Sunpro: https://docs.oracle.com/cd/E18659_01/html/821-1384/gjzke.html + * https://docs.oracle.com/cd/E77782_01/html/E77788/gjzke.html * XLC: https://www.ibm.com/support/knowledgecenter/SSGH2K_13.1.2/com.ibm.xlc131.aix.doc/language_ref/function_attributes.html * XLC: https://www.ibm.com/support/knowledgecenter/SSGH2K_13.1.2/com.ibm.xlc131.aix.doc/language_ref/type_attrib.html */ -/* only GCC supports the unused attribute */ -#ifdef __GNUC__ +/* + * Unused attribute: silence warnings for unused variables, functions, etc. + */ +#if pg_has_attribute(unused) #define pg_attribute_unused() __attribute__((unused)) #else #define pg_attribute_unused() @@ -121,8 +166,11 @@ #define PG_USED_FOR_ASSERTS_ONLY pg_attribute_unused() #endif -/* GCC and XLC support format attributes */ -#if defined(__GNUC__) || defined(__IBMC__) +/* + * GCC/clang and XLC support format attributes to check the arguments of + * printf()-style variadic argument lists against the format string. + */ +#if (pg_has_attribute(format) && pg_has_attribute(format_arg)) || defined(__IBMC__) #define pg_attribute_format_arg(a) __attribute__((format_arg(a))) #define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a))) #else @@ -130,19 +178,24 @@ #define pg_attribute_printf(f,a) #endif -/* GCC, Sunpro and XLC support aligned, packed and noreturn */ -#if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__) -#define pg_attribute_aligned(a) __attribute__((aligned(a))) +/* GCC/clang, Sunpro and XLC support noreturn */ +#if pg_has_attribute(noreturn) || defined(__SUNPRO_C) || defined(__IBMC__) #define pg_attribute_noreturn() __attribute__((noreturn)) -#define pg_attribute_packed() __attribute__((packed)) #define HAVE_PG_ATTRIBUTE_NORETURN 1 #else +#define pg_attribute_noreturn() +#endif + +/* GCC/clang, Sunpro and XLC support aligned and packed */ +#if (pg_has_attribute(aligned) && pg_has_attribute(packed)) || defined(__SUNPRO_C) || defined(__IBMC__) +#define pg_attribute_aligned(a) __attribute__((aligned(a))) +#define pg_attribute_packed() __attribute__((packed)) +#else /* * NB: aligned and packed are not given default definitions because they * affect code functionality; they *must* be implemented by the compiler * if they are to be used. */ -#define pg_attribute_noreturn() #endif /* @@ -151,8 +204,12 @@ * choose not to. But, if possible, don't force inlining in unoptimized * debug builds. */ -#if (defined(__GNUC__) && __GNUC__ > 3 && defined(__OPTIMIZE__)) || defined(__SUNPRO_C) || defined(__IBMC__) -/* GCC > 3, Sunpro and XLC support always_inline via __attribute__ */ +#if defined(__NO_INLINE__) || defined(PG_FORCE_DISABLE_INLINE) +/* Omit force inline completely if optimisation is off or inlining disabled */ +#define pg_attribute_always_inline +#else +#if pg_has_attribute(always_inline) || defined(__SUNPRO_C) || defined(__IBMC__) +/* GCC, Sunpro and XLC support always_inline via __attribute__ */ #define pg_attribute_always_inline __attribute__((always_inline)) inline #elif defined(_MSC_VER) /* MSVC has a special keyword for this */ @@ -161,6 +218,7 @@ /* Otherwise, the best we can do is to say "inline" */ #define pg_attribute_always_inline inline #endif +#endif /* * Forcing a function not to be inlined can be useful if it's the slow path of @@ -169,7 +227,7 @@ * above, this should be placed before the function's return type and name. */ /* GCC, Sunpro and XLC support noinline via __attribute__ */ -#if (defined(__GNUC__) && __GNUC__ > 2) || defined(__SUNPRO_C) || defined(__IBMC__) +#if __has_attribute(nolinline) || defined(__SUNPRO_C) || defined(__IBMC__) #define pg_noinline __attribute__((noinline)) /* msvc via declspec */ #elif defined(_MSC_VER) @@ -240,8 +298,12 @@ * * These should only be used sparingly, in very hot code paths. It's very easy * to mis-estimate likelihoods. + * + * Disable by defining PG_FORCE_DISABLE_BRANCH_HINTS in pg_config_manual.h when + * looking into branch prediction hinting issues, or per-file by defining it + * before including postgresql.h. */ -#if __GNUC__ >= 3 +#if pg_has_builtin(__builtin_expect) #define likely(x) __builtin_expect((x) != 0, 1) #define unlikely(x) __builtin_expect((x) != 0, 0) #else @@ -250,6 +312,8 @@ #endif /* + * PostgreSQL aliases for C preprocessor token pasting and stringification. + * * CppAsString * Convert the argument to a string, using the C preprocessor. * CppAsString2 @@ -260,6 +324,8 @@ * Note: There used to be support here for pre-ANSI C compilers that didn't * support # and ##. Nowadays, these macros are just for clarity and/or * backward compatibility with existing PostgreSQL code. + * + * A common use of CppAsString is to stringify the names of enums. */ #define CppAsString(identifier) #identifier #define CppAsString2(x) CppAsString(x) -- 2.26.2