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);