1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

Split up guc.c for better build speed and ease of maintenance.

guc.c has grown to be one of our largest .c files, making it
a bottleneck for compilation.  It's also acquired a bunch of
knowledge that'd be better kept elsewhere, because of our not
very good habit of putting variable-specific check hooks here.
Hence, split it up along these lines:

* guc.c itself retains just the core GUC housekeeping mechanisms.
* New file guc_funcs.c contains the SET/SHOW interfaces and some
  SQL-accessible functions for GUC manipulation.
* New file guc_tables.c contains the data arrays that define the
  built-in GUC variables, along with some already-exported constant
  tables.
* GUC check/assign/show hook functions are moved to the variable's
  home module, whenever that's clearly identifiable.  A few hard-
  to-classify hooks ended up in commands/variable.c, which was
  already a home for miscellaneous GUC hook functions.

To avoid cluttering a lot more header files with #include "guc.h",
I also invented a new header file utils/guc_hooks.h and put all
the GUC hook functions' declarations there, regardless of their
originating module.  That allowed removal of #include "guc.h"
from some existing headers.  The fallout from that (hopefully
all caught here) demonstrates clearly why such inclusions are
best minimized: there are a lot of files that, for example,
were getting array.h at two or more levels of remove, despite
not having any connection at all to GUCs in themselves.

There is some very minor code beautification here, such as
renaming a couple of inconsistently-named hook functions
and improving some comments.  But mostly this just moves
code from point A to point B and deals with the ensuing
needs for #include adjustments and exporting a few functions
that previously weren't exported.

Patch by me, per a suggestion from Andres Freund; thanks also
to Michael Paquier for the idea to invent guc_funcs.c.

Discussion: https://postgr.es/m/587607.1662836699@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2022-09-13 11:05:07 -04:00
parent 257eb57b50
commit 0a20ff54f5
59 changed files with 7466 additions and 7128 deletions

View File

@@ -36,6 +36,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/guc.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
#include "utils/rel.h"

View File

@@ -20,6 +20,7 @@
#include "commands/defrem.h"
#include "miscadmin.h"
#include "utils/fmgroids.h"
#include "utils/guc_hooks.h"
#include "utils/memutils.h"
#include "utils/syscache.h"

View File

@@ -97,7 +97,8 @@
#include "storage/smgr.h"
#include "storage/spin.h"
#include "storage/sync.h"
#include "utils/guc.h"
#include "utils/guc_hooks.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/relmapper.h"
@@ -105,6 +106,7 @@
#include "utils/snapmgr.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
extern uint32 bootstrap_data_checksum_version;
@@ -160,6 +162,12 @@ int CheckPointSegments;
static double CheckPointDistanceEstimate = 0;
static double PrevCheckPointDistance = 0;
/*
* Track whether there were any deferred checks for custom resource managers
* specified in wal_consistency_checking.
*/
static bool check_wal_consistency_checking_deferred = false;
/*
* GUC support
*/
@@ -4304,6 +4312,172 @@ check_wal_buffers(int *newval, void **extra, GucSource source)
return true;
}
/*
* GUC check_hook for wal_consistency_checking
*/
bool
check_wal_consistency_checking(char **newval, void **extra, GucSource source)
{
char *rawstring;
List *elemlist;
ListCell *l;
bool newwalconsistency[RM_MAX_ID + 1];
/* Initialize the array */
MemSet(newwalconsistency, 0, (RM_MAX_ID + 1) * sizeof(bool));
/* Need a modifiable copy of string */
rawstring = pstrdup(*newval);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawstring, ',', &elemlist))
{
/* syntax error in list */
GUC_check_errdetail("List syntax is invalid.");
pfree(rawstring);
list_free(elemlist);
return false;
}
foreach(l, elemlist)
{
char *tok = (char *) lfirst(l);
int rmid;
/* Check for 'all'. */
if (pg_strcasecmp(tok, "all") == 0)
{
for (rmid = 0; rmid <= RM_MAX_ID; rmid++)
if (RmgrIdExists(rmid) && GetRmgr(rmid).rm_mask != NULL)
newwalconsistency[rmid] = true;
}
else
{
/* Check if the token matches any known resource manager. */
bool found = false;
for (rmid = 0; rmid <= RM_MAX_ID; rmid++)
{
if (RmgrIdExists(rmid) && GetRmgr(rmid).rm_mask != NULL &&
pg_strcasecmp(tok, GetRmgr(rmid).rm_name) == 0)
{
newwalconsistency[rmid] = true;
found = true;
break;
}
}
if (!found)
{
/*
* During startup, it might be a not-yet-loaded custom
* resource manager. Defer checking until
* InitializeWalConsistencyChecking().
*/
if (!process_shared_preload_libraries_done)
{
check_wal_consistency_checking_deferred = true;
}
else
{
GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
pfree(rawstring);
list_free(elemlist);
return false;
}
}
}
}
pfree(rawstring);
list_free(elemlist);
/* assign new value */
*extra = guc_malloc(ERROR, (RM_MAX_ID + 1) * sizeof(bool));
memcpy(*extra, newwalconsistency, (RM_MAX_ID + 1) * sizeof(bool));
return true;
}
/*
* GUC assign_hook for wal_consistency_checking
*/
void
assign_wal_consistency_checking(const char *newval, void *extra)
{
/*
* If some checks were deferred, it's possible that the checks will fail
* later during InitializeWalConsistencyChecking(). But in that case, the
* postmaster will exit anyway, so it's safe to proceed with the
* assignment.
*
* Any built-in resource managers specified are assigned immediately,
* which affects WAL created before shared_preload_libraries are
* processed. Any custom resource managers specified won't be assigned
* until after shared_preload_libraries are processed, but that's OK
* because WAL for a custom resource manager can't be written before the
* module is loaded anyway.
*/
wal_consistency_checking = extra;
}
/*
* InitializeWalConsistencyChecking: run after loading custom resource managers
*
* If any unknown resource managers were specified in the
* wal_consistency_checking GUC, processing was deferred. Now that
* shared_preload_libraries have been loaded, process wal_consistency_checking
* again.
*/
void
InitializeWalConsistencyChecking(void)
{
Assert(process_shared_preload_libraries_done);
if (check_wal_consistency_checking_deferred)
{
struct config_generic *guc;
guc = find_option("wal_consistency_checking", false, false, ERROR);
check_wal_consistency_checking_deferred = false;
set_config_option_ext("wal_consistency_checking",
wal_consistency_checking_string,
guc->scontext, guc->source, guc->srole,
GUC_ACTION_SET, true, ERROR, false);
/* checking should not be deferred again */
Assert(!check_wal_consistency_checking_deferred);
}
}
/*
* GUC show_hook for archive_command
*/
const char *
show_archive_command(void)
{
if (XLogArchivingActive())
return XLogArchiveCommand;
else
return "(disabled)";
}
/*
* GUC show_hook for in_hot_standby
*/
const char *
show_in_hot_standby(void)
{
/*
* We display the actual state based on shared memory, so that this GUC
* reports up-to-date state if examined intra-query. The underlying
* variable (in_hot_standby_guc) changes only when we transmit a new value
* to the client.
*/
return RecoveryInProgress() ? "on" : "off";
}
/*
* Read the control file, set respective GUCs.
*

View File

@@ -44,7 +44,7 @@
#include "storage/bufmgr.h"
#include "storage/shmem.h"
#include "storage/smgr.h"
#include "utils/guc.h"
#include "utils/guc_hooks.h"
#include "utils/hsearch.h"
/*

View File

@@ -48,6 +48,7 @@
#include "pgstat.h"
#include "postmaster/bgwriter.h"
#include "postmaster/startup.h"
#include "replication/slot.h"
#include "replication/walreceiver.h"
#include "storage/fd.h"
#include "storage/ipc.h"
@@ -57,7 +58,9 @@
#include "storage/procarray.h"
#include "storage/spin.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/datetime.h"
#include "utils/guc_hooks.h"
#include "utils/pg_lsn.h"
#include "utils/ps_status.h"
#include "utils/pg_rusage.h"
@@ -4616,3 +4619,315 @@ RecoveryRequiresIntParameter(const char *param_name, int currValue, int minValue
errhint("You can restart the server after making the necessary configuration changes.")));
}
}
/*
* GUC check_hook for primary_slot_name
*/
bool
check_primary_slot_name(char **newval, void **extra, GucSource source)
{
if (*newval && strcmp(*newval, "") != 0 &&
!ReplicationSlotValidateName(*newval, WARNING))
return false;
return true;
}
/*
* Recovery target settings: Only one of the several recovery_target* settings
* may be set. Setting a second one results in an error. The global variable
* recoveryTarget tracks which kind of recovery target was chosen. Other
* variables store the actual target value (for example a string or a xid).
* The assign functions of the parameters check whether a competing parameter
* was already set. But we want to allow setting the same parameter multiple
* times. We also want to allow unsetting a parameter and setting a different
* one, so we unset recoveryTarget when the parameter is set to an empty
* string.
*
* XXX this code is broken by design. Throwing an error from a GUC assign
* hook breaks fundamental assumptions of guc.c. So long as all the variables
* for which this can happen are PGC_POSTMASTER, the consequences are limited,
* since we'd just abort postmaster startup anyway. Nonetheless it's likely
* that we have odd behaviors such as unexpected GUC ordering dependencies.
*/
static void
pg_attribute_noreturn()
error_multiple_recovery_targets(void)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple recovery targets specified"),
errdetail("At most one of recovery_target, recovery_target_lsn, recovery_target_name, recovery_target_time, recovery_target_xid may be set.")));
}
/*
* GUC check_hook for recovery_target
*/
bool
check_recovery_target(char **newval, void **extra, GucSource source)
{
if (strcmp(*newval, "immediate") != 0 && strcmp(*newval, "") != 0)
{
GUC_check_errdetail("The only allowed value is \"immediate\".");
return false;
}
return true;
}
/*
* GUC assign_hook for recovery_target
*/
void
assign_recovery_target(const char *newval, void *extra)
{
if (recoveryTarget != RECOVERY_TARGET_UNSET &&
recoveryTarget != RECOVERY_TARGET_IMMEDIATE)
error_multiple_recovery_targets();
if (newval && strcmp(newval, "") != 0)
recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
else
recoveryTarget = RECOVERY_TARGET_UNSET;
}
/*
* GUC check_hook for recovery_target_lsn
*/
bool
check_recovery_target_lsn(char **newval, void **extra, GucSource source)
{
if (strcmp(*newval, "") != 0)
{
XLogRecPtr lsn;
XLogRecPtr *myextra;
bool have_error = false;
lsn = pg_lsn_in_internal(*newval, &have_error);
if (have_error)
return false;
myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
*myextra = lsn;
*extra = (void *) myextra;
}
return true;
}
/*
* GUC assign_hook for recovery_target_lsn
*/
void
assign_recovery_target_lsn(const char *newval, void *extra)
{
if (recoveryTarget != RECOVERY_TARGET_UNSET &&
recoveryTarget != RECOVERY_TARGET_LSN)
error_multiple_recovery_targets();
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_LSN;
recoveryTargetLSN = *((XLogRecPtr *) extra);
}
else
recoveryTarget = RECOVERY_TARGET_UNSET;
}
/*
* GUC check_hook for recovery_target_name
*/
bool
check_recovery_target_name(char **newval, void **extra, GucSource source)
{
/* Use the value of newval directly */
if (strlen(*newval) >= MAXFNAMELEN)
{
GUC_check_errdetail("%s is too long (maximum %d characters).",
"recovery_target_name", MAXFNAMELEN - 1);
return false;
}
return true;
}
/*
* GUC assign_hook for recovery_target_name
*/
void
assign_recovery_target_name(const char *newval, void *extra)
{
if (recoveryTarget != RECOVERY_TARGET_UNSET &&
recoveryTarget != RECOVERY_TARGET_NAME)
error_multiple_recovery_targets();
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_NAME;
recoveryTargetName = newval;
}
else
recoveryTarget = RECOVERY_TARGET_UNSET;
}
/*
* GUC check_hook for recovery_target_time
*
* The interpretation of the recovery_target_time string can depend on the
* time zone setting, so we need to wait until after all GUC processing is
* done before we can do the final parsing of the string. This check function
* only does a parsing pass to catch syntax errors, but we store the string
* and parse it again when we need to use it.
*/
bool
check_recovery_target_time(char **newval, void **extra, GucSource source)
{
if (strcmp(*newval, "") != 0)
{
/* reject some special values */
if (strcmp(*newval, "now") == 0 ||
strcmp(*newval, "today") == 0 ||
strcmp(*newval, "tomorrow") == 0 ||
strcmp(*newval, "yesterday") == 0)
{
return false;
}
/*
* parse timestamp value (see also timestamptz_in())
*/
{
char *str = *newval;
fsec_t fsec;
struct pg_tm tt,
*tm = &tt;
int tz;
int dtype;
int nf;
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char workbuf[MAXDATELEN + MAXDATEFIELDS];
TimestampTz timestamp;
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
return false;
if (dtype != DTK_DATE)
return false;
if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
{
GUC_check_errdetail("timestamp out of range: \"%s\"", str);
return false;
}
}
}
return true;
}
/*
* GUC assign_hook for recovery_target_time
*/
void
assign_recovery_target_time(const char *newval, void *extra)
{
if (recoveryTarget != RECOVERY_TARGET_UNSET &&
recoveryTarget != RECOVERY_TARGET_TIME)
error_multiple_recovery_targets();
if (newval && strcmp(newval, "") != 0)
recoveryTarget = RECOVERY_TARGET_TIME;
else
recoveryTarget = RECOVERY_TARGET_UNSET;
}
/*
* GUC check_hook for recovery_target_timeline
*/
bool
check_recovery_target_timeline(char **newval, void **extra, GucSource source)
{
RecoveryTargetTimeLineGoal rttg;
RecoveryTargetTimeLineGoal *myextra;
if (strcmp(*newval, "current") == 0)
rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
else if (strcmp(*newval, "latest") == 0)
rttg = RECOVERY_TARGET_TIMELINE_LATEST;
else
{
rttg = RECOVERY_TARGET_TIMELINE_NUMERIC;
errno = 0;
strtoul(*newval, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
{
GUC_check_errdetail("recovery_target_timeline is not a valid number.");
return false;
}
}
myextra = (RecoveryTargetTimeLineGoal *) guc_malloc(ERROR, sizeof(RecoveryTargetTimeLineGoal));
*myextra = rttg;
*extra = (void *) myextra;
return true;
}
/*
* GUC assign_hook for recovery_target_timeline
*/
void
assign_recovery_target_timeline(const char *newval, void *extra)
{
recoveryTargetTimeLineGoal = *((RecoveryTargetTimeLineGoal *) extra);
if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
recoveryTargetTLIRequested = (TimeLineID) strtoul(newval, NULL, 0);
else
recoveryTargetTLIRequested = 0;
}
/*
* GUC check_hook for recovery_target_xid
*/
bool
check_recovery_target_xid(char **newval, void **extra, GucSource source)
{
if (strcmp(*newval, "") != 0)
{
TransactionId xid;
TransactionId *myextra;
errno = 0;
xid = (TransactionId) strtou64(*newval, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
return false;
myextra = (TransactionId *) guc_malloc(ERROR, sizeof(TransactionId));
*myextra = xid;
*extra = (void *) myextra;
}
return true;
}
/*
* GUC assign_hook for recovery_target_xid
*/
void
assign_recovery_target_xid(const char *newval, void *extra)
{
if (recoveryTarget != RECOVERY_TARGET_UNSET &&
recoveryTarget != RECOVERY_TARGET_XID)
error_multiple_recovery_targets();
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_XID;
recoveryTargetXid = *((TransactionId *) extra);
}
else
recoveryTarget = RECOVERY_TARGET_UNSET;
}