Различные соглашения по оформлению кода#
Стандарт C#
Код PostgreSQL должен опираться только на возможности языка, доступные в стандарте C99. Это означает, что компилятор, соответствующий стандарту C99, должен уметь компилировать Postgres, по крайней мере, за исключением нескольких частей, зависящих от платформы.
Некоторые возможности, включенные в стандарт C99, на данный момент не разрешены к использованию в основном коде Post- greSQL. В настоящее время это массивы переменной длины, перемежающиеся декларации и код, // комментарии, универсальные имена символов. Причины этого - переносимость и историческая практика.
Возможности из более поздних версий стандарта 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)
{
intsave_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);