gucs-config — custom GUC variables
This is the procedural cookbook for adding custom GUC variables to a
PostgreSQL backend patch or extension. For the conceptual model see
knowledge/idioms/guc-variables.md.
This skill is one of three siblings that share the
_PG_init / postmaster-lifecycle boundary:
- gucs-config (this skill) — custom GUC variables.
bgworker-and-extensions— RegisterBackgroundWorker, shared-library hooks.parallel-query— ParallelContext + parallel-safe markings.
1. Picking the right Define*Variable
Five typed entry points, all in utils/guc.h.
[verified-by-code source/src/include/utils/guc.h:358-416]
| Type | Function | Notes |
|---|---|---|
| bool | DefineCustomBoolVariable |
|
| int | DefineCustomIntVariable |
with minValue / maxValue |
| double | DefineCustomRealVariable |
with minValue / maxValue |
| string | DefineCustomStringVariable |
valueAddr is char **, see §5 |
| enum | DefineCustomEnumVariable |
takes a const struct config_enum_entry[] |
All five take the same trailing trio of hooks: check_hook, assign_hook,
show_hook (any can be NULL).
2. GucContext — when the value can change
[verified-by-code source/src/include/utils/guc.h:71-80]
GucContext |
When the user can change it |
|---|---|
PGC_INTERNAL |
Never — display-only (e.g. server_version). |
PGC_POSTMASTER |
Only at postmaster startup (postgresql.conf / cmd line). |
PGC_SIGHUP |
Postmaster start OR config-reload (SIGHUP / pg_reload_conf()). |
PGC_SU_BACKEND |
Start of backend, superusers can pass via startup packet. |
PGC_BACKEND |
Start of backend (libpq PGOPTIONS), then fixed. |
PGC_SUSET |
Any time by a superuser, including SET. |
PGC_USERSET |
Any time by anyone. |
Rule of thumb: pick the loosest context that's still safe. If
changing the value mid-session would require restarting a worker or
reallocating shared memory, use PGC_POSTMASTER. If it just changes
a runtime threshold, use PGC_SIGHUP or PGC_SUSET.
3. Skeleton — _PG_init for an extension with GUCs
void
_PG_init(void)
{
DefineCustomIntVariable("my_ext.naptime",
"Seconds between scans.",
NULL, /* long_desc */
&my_naptime, /* int *valueAddr */
10, /* boot_val */
1, INT_MAX, /* min, max */
PGC_SIGHUP,
GUC_UNIT_S, /* flags — UNIT_S = seconds */
NULL, NULL, NULL); /* check/assign/show */
DefineCustomStringVariable("my_ext.database",
"DB to connect to.",
NULL,
&my_database,
"postgres",
PGC_POSTMASTER,
0,
NULL, NULL, NULL);
/* MUST be called AFTER all DefineCustom* calls. */
MarkGUCPrefixReserved("my_ext");
}
[verified-by-code source/src/test/modules/worker_spi/worker_spi.c:303-360]
4. MarkGUCPrefixReserved
Call exactly once per extension, after every DefineCustom*Variable for
your prefix. It does two things:
- Removes any placeholder GUCs (
GUC_CUSTOM_PLACEHOLDER) under that prefix — these get created when a config file mentions a custom variable before the defining extension loads. Without removal, parallel-worker startup later trips over them. - Adds the prefix to a list so future placeholders under it are refused —
typos in
postgresql.conf(my_ext.napitme) now produce a clear error instead of silently being accepted.
[verified-by-code source/src/backend/utils/misc/guc.c:5185-5228]
The old name EmitWarningsOnPlaceholders is still a #define alias —
prefer the new name in new code. [verified-by-code
source/src/include/utils/guc.h:421]
5. String GUCs and guc_malloc
For string GUCs, valueAddr is a char **. The pointed-to storage is
owned by guc.c and must be allocated with guc_malloc / guc_strdup —
never palloc. If a check_hook wants to replace the proposed value
it must guc_malloc the new value and guc_free the old one. Use
guc_realloc to grow an existing guc_malloc allocation (typical for a
derived extra payload). All four functions live in
source/src/include/utils/guc.h:473-476.
[from-README source/src/backend/utils/misc/README:50-62]
6. The hook trio
check_hook(newval *, void **extra, GucSource source) → bool
Signature varies by GUC type — the first argument's exact pointer type matches the variable's storage:
| GUC type | check_hook first arg |
|---|---|
| bool | bool *newval |
| int | int *newval |
| real | double *newval |
| string | char **newval (so *newval is the proposed char *) |
| enum | int *newval (enum encoded as int) |
[from-README source/src/backend/utils/misc/README:25-30]
- Validate the proposed value; return false on reject.
- May modify
*newvalto canonicalize (round, lowercase, ...). - May allocate an
extrastruct withguc_mallocand return it through*extrato pass derived data to the assign hook. - For error detail:
GUC_check_errdetail(...),GUC_check_errhint(...),GUC_check_errcode(...),GUC_check_errmsg(...)— neverereport(ERROR)directly except on OOM.GUC_check_errcode(sqlerrcode)overrides the default rejection SQLSTATE (ERRCODE_INVALID_PARAMETER_VALUE, 22023); only override when you have a more specific code. - May run outside any transaction (bootstrap, postmaster startup,
config reload). Guard catalog lookups with
IsTransactionState(). - May also be called just to validate without assigning — must not have side effects.
- For
GUC_LIST_INPUTGUCs (see §7), parse*newvalinside the check_hook withSplitIdentifierString(rawstring, ',', &elemlist)(identifier-style lists) orSplitGUCList(rawstring, ',', &elemlist)(general case). Both live insource/src/include/utils/varlena.h:33-37; theypstrdupelements andpfree/list_freeis the caller's job. Don't roll your own splitter — these match how the parser handlessearch_path, including quoted elements.
[from-README source/src/backend/utils/misc/README:25-75]
assign_hook(newval, void *extra) → void
- Cannot fail (no return). Do all fallible work in the check hook.
- May be called during transaction rollback → no catalog lookups.
[from-README source/src/backend/utils/misc/README:78-109]
show_hook(void) → const char *
- Customise what SHOW displays. Static buffer is fine; not reentrant.
[from-README source/src/backend/utils/misc/README:112-117]
7. Useful flags bits
[verified-by-code source/src/include/utils/guc.h:214-242]
| Flag | Effect |
|---|---|
GUC_LIST_INPUT |
Value is a comma-separated list — needed to use lists at all. |
GUC_LIST_QUOTE |
Each list element is double-quoted on serialization. Required for lists of identifiers (e.g. search_path). |
GUC_UNIT_KB / _MB / _BYTE / _BLOCKS |
Int GUC understands '128MB'. |
GUC_UNIT_MS / _S / _MIN |
Time units. |
GUC_NOT_IN_SAMPLE |
Don't include in generated postgresql.conf.sample. |
GUC_SUPERUSER_ONLY |
Hide value from non-superusers in pg_settings. |
GUC_DISALLOW_IN_FILE |
Cannot appear in config file (set only at runtime). |
GUC_DISALLOW_IN_AUTO_FILE |
Cannot be set by ALTER SYSTEM. |
GUC_REPORT |
Auto-send ParameterStatus to client on change. |
GUC_EXPLAIN |
Include in EXPLAIN (SETTINGS) output. |
GUC_ALLOW_IN_PARALLEL |
OK to set inside a parallel-mode block. |
8. GUC state under workers (cross-cutting)
Parallel workers — GUC state is serialized by the leader and restored in each worker by
ParallelWorkerMainbefore your code runs. Workers see exactly the leader's GUC values at launch time. If aPGC_USERSETGUC changes mid-flight, in-flight workers do not see the change. Seeparallel-queryfor the launch mechanics.Background workers (non-parallel) — GUC state is whatever
postgresql.confsays at start. To honour SIGHUP, installSignalHandlerForConfigReloadand callProcessConfigFile(PGC_SIGHUP)whenConfigReloadPendingis set. Seebgworker-and-extensionsfor the signal-handling skeleton.
9. Checklist
-
_PG_initdefines every custom GUC. -
MarkGUCPrefixReserved(prefix)is called after all definitions. - String storage uses
guc_malloc/guc_strdup, neverpalloc. -
check_hookis side-effect-free and usesGUC_check_errdetail(notereport(ERROR)) for validation failures. -
check_hookguards catalog lookups withIsTransactionState(). - Right
GucContextchosen (don't usePGC_USERSETif the change would require restarting workers). - Unit flag set on int GUCs that represent size or time.
- List GUCs have
GUC_LIST_INPUT, andGUC_LIST_QUOTEif elements are identifiers.
10. Useful greps
- All custom GUC definitions:
grep -RIn 'DefineCustom\(Bool\|Int\|Real\|String\|Enum\)Variable' source/src - All check / assign hooks in tree:
grep -RIn 'check_hook\|assign_hook' source/src/include/utils/guc.h
Cross-references
.claude/skills/bgworker-and-extensions/SKILL.md—_PG_initflow for the surrounding extension; SIGHUP signal handler for picking up config reloads..claude/skills/parallel-query/SKILL.md— how GUCs get restored in parallel workers;GUC_ALLOW_IN_PARALLELflag..claude/skills/extension-development/SKILL.md—_PG_init,.controlfile,shared_preload_libraries..claude/skills/coding-style/SKILL.md— backend C style for the hook functions..claude/skills/error-handling/SKILL.md—GUC_check_errdetailvsereport(ERROR); OOM contract.knowledge/idioms/guc-variables.md— conceptual model.knowledge/files/src/include/utils/guc.h.md— per-file doc for the public API surface.knowledge/files/src/backend/utils/misc/guc.c.md— per-file doc for the implementation.