↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 12 / 11 / 10 / 9.6
历史版本。 PostgreSQL 12 已结束支持。 2024-11-21. 请参阅 当前版本手册.

53.4. 其他编码约定 #

C 标准

PostgreSQL 中的代码只应依赖 C99 标准提供的语言特性。这意味着,至少除少数平台相关部分外,符合 C99 的编译器必须能够编译 postgres。

不过,C99 标准中包含的少数特性目前不允许在 PostgreSQL 核心代码中使用。目前包括可变长度数组、声明与代码交错、// 注释以及通用字符名。原因包括可移植性和历史实践。

如果提供了回退方案,则可以使用来自 C 标准后续修订的特性或编译器特定特性。

例如,当前使用了 _Static_assert() 和 __builtin_constant_p,尽管它们分别来自 C 标准的较新修订和 GCC 扩展。如果前者不可用,我们会回退到一个执行相同检查的 C99 兼容替代方案,不过它发出的消息会相当晦涩;如果后者不可用,则不使用 __builtin_constant_p。

类函数宏和内联函数

带参数的宏和 static inline 函数都可以使用。如果把某段代码写成宏会有多次求值风险,那么后者更可取,例如下面这种情况:

#define Max(x, y)       ((x) > (y) ? (x) : (y))

或者当宏会变得非常长时也是如此。在其他情况下,只能使用宏,或者至少使用宏更容易。例如,需要向宏传递各种不同类型的表达式时就是如此。

当某个内联函数的定义引用了只在后端可用的符号(即变量、函数)时,该函数在前端代码中被包含时就不应可见。

#ifndef FRONTEND
static inline MemoryContext
MemoryContextSwitchTo(MemoryContext context)
{
    MemoryContext old = CurrentMemoryContext;

    CurrentMemoryContext = context;
    return old;
}
#endif   /* FRONTEND */

在这个示例中,只在后端中可用的 CurrentMemoryContext 被引用了,因此该函数用 #ifndef FRONTEND 隐藏起来。这条规则之所以存在,是因为有些编译器即使函数未被使用,也会为内联函数中包含的符号发出引用。

编写信号处理器

要让代码适合在信号处理器中运行,必须非常谨慎地编写。根本问题在于,只要没有被阻塞,信号处理器就能在任何时刻中断代码。如果信号处理器中的代码使用了与外部代码相同的状态,就可能引发混乱。举例来说,想想如果信号处理器试图获取一个已经被中断代码持有的锁,会发生什么。

除非有特殊安排,信号处理器中的代码只能调用异步信号安全函数(按 POSIX 的定义),并且只能访问类型为 volatile sig_atomic_t 的变量。postgres 中也有少数函数被认为是信号安全的,其中尤其重要的是 SetLatch()。

在大多数情况下,信号处理器只应记录信号已经到达,并使用锁存器唤醒在信号处理器之外运行的代码。下面就是这样的一个处理器示例:

static void
handle_sighup(SIGNAL_ARGS)
{
    int         save_errno = errno;

    got_SIGHUP = true;
    SetLatch(MyLatch);

    errno = save_errno;
}

errno 被保存并恢复,是因为 SetLatch() 可能会改变它。如果不这样做,当前正在检查 errno 的被中断代码可能会看到错误的值。

调用函数指针

为了清晰起见,当函数指针是一个简单变量时,调用其指向的函数时最好显式解引用,例如:

(*emit_log_hook) (edata);

(尽管 emit_log_hook(edata) 也能工作)。当函数指针是某个结构体的一部分时,这些额外符号可以,而且通常也应该省略,例如:

paramInfo->paramFetch(paramInfo, paramId);

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.