Store GUC data in a memory context, instead of using malloc().

The only real argument for using malloc directly was that we needed
the ability to not throw error on OOM; but mcxt.c grew that feature
awhile ago.

Keeping the data in a memory context improves accountability and
debuggability --- for example, without this it's almost impossible
to detect memory leaks in the GUC code with anything less costly
than valgrind.  Moreover, the next patch in this series will add a
hash table for GUC lookup, and it'd be pretty silly to be using
palloc-dependent hash facilities alongside malloc'd storage of the
underlying data.

This is a bit invasive though, in particular causing an API break
for GUC check hooks that want to modify the GUC's value or use an
"extra" data structure.  They must now use guc_malloc() and
guc_free() instead of malloc() and free().  Failure to change
affected code will result in assertion failures or worse; but
thanks to recent effort in the mcxt infrastructure, it shouldn't
be too hard to diagnose such oversights (at least in assert-enabled
builds).

One note is that this changes ParseLongOption() to return short-lived
palloc'd not malloc'd data.  There wasn't any caller for which the
previous definition was better.

Discussion: https://postgr.es/m/2982579.1662416866@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2022-10-14 12:10:48 -04:00
parent 9c911ec065
commit 407b50f2d4
13 changed files with 185 additions and 127 deletions

View File

@ -287,8 +287,8 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
}
SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
free(name);
free(value);
pfree(name);
pfree(value);
break;
}
default:

View File

@ -1290,8 +1290,8 @@ check_temp_tablespaces(char **newval, void **extra, GucSource source)
}
/* Now prepare an "extra" struct for assign_temp_tablespaces */
myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) +
numSpcs * sizeof(Oid));
myextra = guc_malloc(LOG, offsetof(temp_tablespaces_extra, tblSpcs) +
numSpcs * sizeof(Oid));
if (!myextra)
return false;
myextra->numSpcs = numSpcs;

View File

@ -148,7 +148,7 @@ check_datestyle(char **newval, void **extra, GucSource source)
char *subval;
void *subextra = NULL;
subval = strdup(GetConfigOptionResetString("datestyle"));
subval = guc_strdup(LOG, GetConfigOptionResetString("datestyle"));
if (!subval)
{
ok = false;
@ -156,7 +156,7 @@ check_datestyle(char **newval, void **extra, GucSource source)
}
if (!check_datestyle(&subval, &subextra, source))
{
free(subval);
guc_free(subval);
ok = false;
break;
}
@ -165,8 +165,8 @@ check_datestyle(char **newval, void **extra, GucSource source)
newDateStyle = myextra[0];
if (!have_order)
newDateOrder = myextra[1];
free(subval);
free(subextra);
guc_free(subval);
guc_free(subextra);
}
else
{
@ -187,9 +187,9 @@ check_datestyle(char **newval, void **extra, GucSource source)
}
/*
* Prepare the canonical string to return. GUC wants it malloc'd.
* Prepare the canonical string to return. GUC wants it guc_malloc'd.
*/
result = (char *) malloc(32);
result = (char *) guc_malloc(LOG, 32);
if (!result)
return false;
@ -221,13 +221,13 @@ check_datestyle(char **newval, void **extra, GucSource source)
break;
}
free(*newval);
guc_free(*newval);
*newval = result;
/*
* Set up the "extra" struct actually used by assign_datestyle.
*/
myextra = (int *) malloc(2 * sizeof(int));
myextra = (int *) guc_malloc(LOG, 2 * sizeof(int));
if (!myextra)
return false;
myextra[0] = newDateStyle;
@ -366,7 +366,7 @@ check_timezone(char **newval, void **extra, GucSource source)
/*
* Pass back data for assign_timezone to use
*/
*extra = malloc(sizeof(pg_tz *));
*extra = guc_malloc(LOG, sizeof(pg_tz *));
if (!*extra)
return false;
*((pg_tz **) *extra) = new_tz;
@ -439,7 +439,7 @@ check_log_timezone(char **newval, void **extra, GucSource source)
/*
* Pass back data for assign_log_timezone to use
*/
*extra = malloc(sizeof(pg_tz *));
*extra = guc_malloc(LOG, sizeof(pg_tz *));
if (!*extra)
return false;
*((pg_tz **) *extra) = new_tz;
@ -500,7 +500,7 @@ check_timezone_abbreviations(char **newval, void **extra, GucSource source)
return true;
}
/* OK, load the file and produce a malloc'd TimeZoneAbbrevTable */
/* OK, load the file and produce a guc_malloc'd TimeZoneAbbrevTable */
*extra = load_tzoffsets(*newval);
/* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */
@ -647,7 +647,7 @@ check_transaction_deferrable(bool *newval, void **extra, GucSource source)
bool
check_random_seed(double *newval, void **extra, GucSource source)
{
*extra = malloc(sizeof(int));
*extra = guc_malloc(LOG, sizeof(int));
if (!*extra)
return false;
/* Arm the assign only if source of value is an interactive SET */
@ -735,8 +735,8 @@ check_client_encoding(char **newval, void **extra, GucSource source)
if (strcmp(*newval, canonical_name) != 0 &&
strcmp(*newval, "UNICODE") != 0)
{
free(*newval);
*newval = strdup(canonical_name);
guc_free(*newval);
*newval = guc_strdup(LOG, canonical_name);
if (!*newval)
return false;
}
@ -744,7 +744,7 @@ check_client_encoding(char **newval, void **extra, GucSource source)
/*
* Save the encoding's ID in *extra, for use by assign_client_encoding.
*/
*extra = malloc(sizeof(int));
*extra = guc_malloc(LOG, sizeof(int));
if (!*extra)
return false;
*((int *) *extra) = encoding;
@ -847,7 +847,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
ReleaseSysCache(roleTup);
/* Set up "extra" struct for assign_session_authorization to use */
myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
if (!myextra)
return false;
myextra->roleid = roleid;
@ -957,7 +957,7 @@ check_role(char **newval, void **extra, GucSource source)
}
/* Set up "extra" struct for assign_role to use */
myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
if (!myextra)
return false;
myextra->roleid = roleid;

View File

@ -849,8 +849,8 @@ PostmasterMain(int argc, char *argv[])
}
SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
free(name);
free(value);
pfree(name);
pfree(value);
break;
}

View File

@ -1054,9 +1054,9 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
return false;
}
/* GUC extra value must be malloc'd, not palloc'd */
/* GUC extra value must be guc_malloc'd, not palloc'd */
pconf = (SyncRepConfigData *)
malloc(syncrep_parse_result->config_size);
guc_malloc(LOG, syncrep_parse_result->config_size);
if (pconf == NULL)
return false;
memcpy(pconf, syncrep_parse_result, syncrep_parse_result->config_size);

View File

@ -3871,8 +3871,8 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx,
optarg)));
}
SetConfigOption(name, value, ctx, gucsource);
free(name);
free(value);
pfree(name);
pfree(value);
break;
}

View File

@ -29,6 +29,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/tzparser.h"
@ -4782,8 +4783,8 @@ TemporalSimplify(int32 max_precis, Node *node)
* to create the final array of timezone tokens. The argument array
* is already sorted in name order.
*
* The result is a TimeZoneAbbrevTable (which must be a single malloc'd chunk)
* or NULL on malloc failure. No other error conditions are defined.
* The result is a TimeZoneAbbrevTable (which must be a single guc_malloc'd
* chunk) or NULL on alloc failure. No other error conditions are defined.
*/
TimeZoneAbbrevTable *
ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
@ -4812,7 +4813,7 @@ ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
}
/* Alloc the result ... */
tbl = malloc(tbl_size);
tbl = guc_malloc(LOG, tbl_size);
if (!tbl)
return NULL;

View File

@ -633,9 +633,9 @@ check_default_text_search_config(char **newval, void **extra, GucSource source)
ReleaseSysCache(tuple);
/* GUC wants it malloc'd not palloc'd */
free(*newval);
*newval = strdup(buf);
/* GUC wants it guc_malloc'd not palloc'd */
guc_free(*newval);
*newval = guc_strdup(LOG, buf);
pfree(buf);
if (!*newval)
return false;

View File

@ -51,13 +51,13 @@ out-of-memory.
This might be used for example to canonicalize the spelling of a string
value, round off a buffer size to the nearest supported value, or replace
a special value such as "-1" with a computed default value. If the
function wishes to replace a string value, it must malloc (not palloc)
the replacement value, and be sure to free() the previous value.
function wishes to replace a string value, it must guc_malloc (not palloc)
the replacement value, and be sure to guc_free() the previous value.
* Derived information, such as the role OID represented by a user name,
can be stored for use by the assign hook. To do this, malloc (not palloc)
can be stored for use by the assign hook. To do this, guc_malloc (not palloc)
storage space for the information, and return its address at *extra.
guc.c will automatically free() this space when the associated GUC setting
guc.c will automatically guc_free() this space when the associated GUC setting
is no longer of interest. *extra is initialized to NULL before call, so
it can be ignored if not needed.
@ -255,10 +255,9 @@ maintained by GUC.
GUC Memory Handling
-------------------
String variable values are allocated with malloc/strdup, not with the
palloc/pstrdup mechanisms. We would need to keep them in a permanent
context anyway, and malloc gives us more control over handling
out-of-memory failures.
String variable values are allocated with guc_malloc or guc_strdup,
which ensure that the values are kept in a long-lived context, and provide
more control over handling out-of-memory failures than bare palloc.
We allow a string variable's actual value, reset_val, boot_val, and stacked
values to point at the same storage. This makes it slightly harder to free

View File

@ -188,6 +188,9 @@ static const char *const map_old_guc_names[] = {
};
/* Memory context holding all GUC-related data */
static MemoryContext GUCMemoryContext;
/*
* Actual lookup of variables is done through this single, sorted array.
*/
@ -595,19 +598,22 @@ bail_out:
return head;
}
/*
* Some infrastructure for checking malloc/strdup/realloc calls
* Some infrastructure for GUC-related memory allocation
*
* These functions are generally modeled on libc's malloc/realloc/etc,
* but any OOM issue is reported at the specified elevel.
* (Thus, control returns only if that's less than ERROR.)
*/
void *
guc_malloc(int elevel, size_t size)
{
void *data;
/* Avoid unportable behavior of malloc(0) */
if (size == 0)
size = 1;
data = malloc(size);
if (data == NULL)
data = MemoryContextAllocExtended(GUCMemoryContext, size,
MCXT_ALLOC_NO_OOM);
if (unlikely(data == NULL))
ereport(elevel,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
@ -619,11 +625,20 @@ guc_realloc(int elevel, void *old, size_t size)
{
void *data;
/* Avoid unportable behavior of realloc(NULL, 0) */
if (old == NULL && size == 0)
size = 1;
data = realloc(old, size);
if (data == NULL)
if (old != NULL)
{
/* This is to help catch old code that malloc's GUC data. */
Assert(GetMemoryChunkContext(old) == GUCMemoryContext);
data = repalloc_extended(old, size,
MCXT_ALLOC_NO_OOM);
}
else
{
/* Like realloc(3), but not like repalloc(), we allow old == NULL. */
data = MemoryContextAllocExtended(GUCMemoryContext, size,
MCXT_ALLOC_NO_OOM);
}
if (unlikely(data == NULL))
ereport(elevel,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
@ -634,15 +649,29 @@ char *
guc_strdup(int elevel, const char *src)
{
char *data;
size_t len = strlen(src) + 1;
data = strdup(src);
if (data == NULL)
ereport(elevel,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
data = guc_malloc(elevel, len);
if (likely(data != NULL))
memcpy(data, src, len);
return data;
}
void
guc_free(void *ptr)
{
/*
* Historically, GUC-related code has relied heavily on the ability to do
* free(NULL), so we allow that here even though pfree() doesn't.
*/
if (ptr != NULL)
{
/* This is to help catch old code that malloc's GUC data. */
Assert(GetMemoryChunkContext(ptr) == GUCMemoryContext);
pfree(ptr);
}
}
/*
* Detect whether strval is referenced anywhere in a GUC string item
@ -680,7 +709,7 @@ set_string_field(struct config_string *conf, char **field, char *newval)
/* Free old value if it's not NULL and isn't referenced anymore */
if (oldval && !string_field_used(conf, oldval))
free(oldval);
guc_free(oldval);
}
/*
@ -741,7 +770,7 @@ set_extra_field(struct config_generic *gconf, void **field, void *newval)
/* Free old value if it's not NULL and isn't referenced anymore */
if (oldval && !extra_field_used(gconf, oldval))
free(oldval);
guc_free(oldval);
}
/*
@ -749,7 +778,7 @@ set_extra_field(struct config_generic *gconf, void **field, void *newval)
* The "extra" field associated with the active value is copied, too.
*
* NB: be sure stringval and extra fields of a new stack entry are
* initialized to NULL before this is used, else we'll try to free() them.
* initialized to NULL before this is used, else we'll try to guc_free() them.
*/
static void
set_stack_value(struct config_generic *gconf, config_var_value *val)
@ -817,9 +846,9 @@ get_guc_variables(void)
/*
* Build the sorted array. This is split out so that it could be
* re-executed after startup (e.g., we could allow loadable modules to
* add vars, and then we'd need to re-sort).
* Build the sorted array. This is split out so that help_config.c can
* extract all the variables without running all of InitializeGUCOptions.
* It's not meant for use anyplace else.
*/
void
build_guc_variables(void)
@ -829,6 +858,17 @@ build_guc_variables(void)
struct config_generic **guc_vars;
int i;
/*
* Create the memory context that will hold all GUC-related data.
*/
Assert(GUCMemoryContext == NULL);
GUCMemoryContext = AllocSetContextCreate(TopMemoryContext,
"GUCMemoryContext",
ALLOCSET_DEFAULT_SIZES);
/*
* Count all the built-in variables, and set their vartypes correctly.
*/
for (i = 0; ConfigureNamesBool[i].gen.name; i++)
{
struct config_bool *conf = &ConfigureNamesBool[i];
@ -895,7 +935,7 @@ build_guc_variables(void)
for (i = 0; ConfigureNamesEnum[i].gen.name; i++)
guc_vars[num_vars++] = &ConfigureNamesEnum[i].gen;
free(guc_variables);
guc_free(guc_variables);
guc_variables = guc_vars;
num_guc_variables = num_vars;
size_guc_variables = size_vars;
@ -1001,7 +1041,7 @@ add_placeholder_variable(const char *name, int elevel)
gen->name = guc_strdup(elevel, name);
if (gen->name == NULL)
{
free(var);
guc_free(var);
return NULL;
}
@ -1020,8 +1060,8 @@ add_placeholder_variable(const char *name, int elevel)
if (!add_guc_variable((struct config_generic *) var, elevel))
{
free(unconstify(char *, gen->name));
free(var);
guc_free(unconstify(char *, gen->name));
guc_free(var);
return NULL;
}
@ -1255,7 +1295,7 @@ InitializeGUCOptions(void)
pg_timezone_initialize();
/*
* Build sorted array of all GUC variables.
* Create GUCMemoryContext and build sorted array of all GUC variables.
*/
build_guc_variables();
@ -1482,6 +1522,7 @@ SelectConfigFiles(const char *userDoption, const char *progname)
{
char *configdir;
char *fname;
bool fname_is_malloced;
struct stat stat_buf;
struct config_string *data_directory_rec;
@ -1509,12 +1550,16 @@ SelectConfigFiles(const char *userDoption, const char *progname)
* the same way by future backends.
*/
if (ConfigFileName)
{
fname = make_absolute_path(ConfigFileName);
fname_is_malloced = true;
}
else if (configdir)
{
fname = guc_malloc(FATAL,
strlen(configdir) + strlen(CONFIG_FILENAME) + 2);
sprintf(fname, "%s/%s", configdir, CONFIG_FILENAME);
fname_is_malloced = false;
}
else
{
@ -1530,7 +1575,11 @@ SelectConfigFiles(const char *userDoption, const char *progname)
* it can't be overridden later.
*/
SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
free(fname);
if (fname_is_malloced)
free(fname);
else
guc_free(fname);
/*
* Now read the config file for the first time.
@ -1604,12 +1653,16 @@ SelectConfigFiles(const char *userDoption, const char *progname)
* Figure out where pg_hba.conf is, and make sure the path is absolute.
*/
if (HbaFileName)
{
fname = make_absolute_path(HbaFileName);
fname_is_malloced = true;
}
else if (configdir)
{
fname = guc_malloc(FATAL,
strlen(configdir) + strlen(HBA_FILENAME) + 2);
sprintf(fname, "%s/%s", configdir, HBA_FILENAME);
fname_is_malloced = false;
}
else
{
@ -1621,18 +1674,26 @@ SelectConfigFiles(const char *userDoption, const char *progname)
return false;
}
SetConfigOption("hba_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
free(fname);
if (fname_is_malloced)
free(fname);
else
guc_free(fname);
/*
* Likewise for pg_ident.conf.
*/
if (IdentFileName)
{
fname = make_absolute_path(IdentFileName);
fname_is_malloced = true;
}
else if (configdir)
{
fname = guc_malloc(FATAL,
strlen(configdir) + strlen(IDENT_FILENAME) + 2);
sprintf(fname, "%s/%s", configdir, IDENT_FILENAME);
fname_is_malloced = false;
}
else
{
@ -1644,7 +1705,11 @@ SelectConfigFiles(const char *userDoption, const char *progname)
return false;
}
SetConfigOption("ident_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
free(fname);
if (fname_is_malloced)
free(fname);
else
guc_free(fname);
free(configdir);
@ -2289,12 +2354,12 @@ ReportGUCOption(struct config_generic *record)
pq_endmessage(&msgbuf);
/*
* We need a long-lifespan copy. If strdup() fails due to OOM, we'll
* set last_reported to NULL and thereby possibly make a duplicate
* report later.
* We need a long-lifespan copy. If guc_strdup() fails due to OOM,
* we'll set last_reported to NULL and thereby possibly make a
* duplicate report later.
*/
free(record->last_reported);
record->last_reported = strdup(val);
guc_free(record->last_reported);
record->last_reported = guc_strdup(LOG, val);
}
pfree(val);
@ -2893,7 +2958,7 @@ parse_and_validate_value(struct config_generic *record,
if (!call_string_check_hook(conf, &newval->stringval, newextra,
source, elevel))
{
free(newval->stringval);
guc_free(newval->stringval);
newval->stringval = NULL;
return false;
}
@ -3328,7 +3393,7 @@ set_config_option_ext(const char *name, const char *value,
{
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
if (*conf->variable != newval)
{
@ -3387,7 +3452,7 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
break;
#undef newval
@ -3426,7 +3491,7 @@ set_config_option_ext(const char *name, const char *value,
{
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
if (*conf->variable != newval)
{
@ -3485,7 +3550,7 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
break;
#undef newval
@ -3524,7 +3589,7 @@ set_config_option_ext(const char *name, const char *value,
{
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
if (*conf->variable != newval)
{
@ -3583,7 +3648,7 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
break;
#undef newval
@ -3617,7 +3682,7 @@ set_config_option_ext(const char *name, const char *value,
if (!call_string_check_hook(conf, &newval, &newextra,
source, elevel))
{
free(newval);
guc_free(newval);
return 0;
}
}
@ -3645,10 +3710,10 @@ set_config_option_ext(const char *name, const char *value,
/* Release newval, unless it's reset_val */
if (newval && !string_field_used(conf, newval))
free(newval);
guc_free(newval);
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
if (newval_different)
{
@ -3709,10 +3774,10 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newval anywhere */
if (newval && !string_field_used(conf, newval))
free(newval);
guc_free(newval);
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
break;
#undef newval
@ -3751,7 +3816,7 @@ set_config_option_ext(const char *name, const char *value,
{
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
if (*conf->variable != newval)
{
@ -3810,7 +3875,7 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra);
guc_free(newextra);
break;
#undef newval
@ -3848,7 +3913,7 @@ set_config_sourcefile(const char *name, char *sourcefile, int sourceline)
return;
sourcefile = guc_strdup(elevel, sourcefile);
free(record->sourcefile);
guc_free(record->sourcefile);
record->sourcefile = sourcefile;
record->sourceline = sourceline;
}
@ -4239,8 +4304,8 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
name, value)));
if (record->vartype == PGC_STRING && newval.stringval != NULL)
free(newval.stringval);
free(newextra);
guc_free(newval.stringval);
guc_free(newextra);
/*
* We must also reject values containing newlines, because the
@ -4535,7 +4600,7 @@ define_custom_variable(struct config_generic *variable)
set_string_field(pHolder, pHolder->variable, NULL);
set_string_field(pHolder, &pHolder->reset_val, NULL);
free(pHolder);
guc_free(pHolder);
}
/*
@ -4814,7 +4879,7 @@ MarkGUCPrefixReserved(const char *className)
}
/* And remember the name so we can prevent future mistakes. */
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
oldcontext = MemoryContextSwitchTo(GUCMemoryContext);
reserved_class_prefix = lappend(reserved_class_prefix, pstrdup(className));
MemoryContextSwitchTo(oldcontext);
}
@ -5287,9 +5352,9 @@ read_nondefault_variables(void)
if (varsourcefile[0])
set_config_sourcefile(varname, varsourcefile, varsourceline);
free(varname);
free(varvalue);
free(varsourcefile);
guc_free(varname);
guc_free(varvalue);
guc_free(varsourcefile);
}
FreeFile(fp);
@ -5731,9 +5796,9 @@ RestoreGUCState(void *gucstate)
* pointers.
*/
Assert(gconf->stack == NULL);
free(gconf->extra);
free(gconf->last_reported);
free(gconf->sourcefile);
guc_free(gconf->extra);
guc_free(gconf->last_reported);
guc_free(gconf->sourcefile);
switch (gconf->vartype)
{
case PGC_BOOL:
@ -5741,7 +5806,7 @@ RestoreGUCState(void *gucstate)
struct config_bool *conf = (struct config_bool *) gconf;
if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra);
guc_free(conf->reset_extra);
break;
}
case PGC_INT:
@ -5749,7 +5814,7 @@ RestoreGUCState(void *gucstate)
struct config_int *conf = (struct config_int *) gconf;
if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra);
guc_free(conf->reset_extra);
break;
}
case PGC_REAL:
@ -5757,18 +5822,18 @@ RestoreGUCState(void *gucstate)
struct config_real *conf = (struct config_real *) gconf;
if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra);
guc_free(conf->reset_extra);
break;
}
case PGC_STRING:
{
struct config_string *conf = (struct config_string *) gconf;
free(*conf->variable);
guc_free(*conf->variable);
if (conf->reset_val && conf->reset_val != *conf->variable)
free(conf->reset_val);
guc_free(conf->reset_val);
if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra);
guc_free(conf->reset_extra);
break;
}
case PGC_ENUM:
@ -5776,7 +5841,7 @@ RestoreGUCState(void *gucstate)
struct config_enum *conf = (struct config_enum *) gconf;
if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra);
guc_free(conf->reset_extra);
break;
}
}
@ -5838,7 +5903,7 @@ RestoreGUCState(void *gucstate)
/*
* A little "long argument" simulation, although not quite GNU
* compliant. Takes a string of the form "some-option=some value" and
* returns name = "some_option" and value = "some value" in malloc'ed
* returns name = "some_option" and value = "some value" in palloc'ed
* storage. Note that '-' is converted to '_' in the option name. If
* there is no '=' in the input string then value will be NULL.
*/
@ -5856,15 +5921,15 @@ ParseLongOption(const char *string, char **name, char **value)
if (string[equal_pos] == '=')
{
*name = guc_malloc(FATAL, equal_pos + 1);
*name = palloc(equal_pos + 1);
strlcpy(*name, string, equal_pos + 1);
*value = guc_strdup(FATAL, &string[equal_pos + 1]);
*value = pstrdup(&string[equal_pos + 1]);
}
else
{
/* no equal sign in string */
*name = guc_strdup(FATAL, string);
*name = pstrdup(string);
*value = NULL;
}
@ -5898,8 +5963,6 @@ ProcessGUCArray(ArrayType *array,
char *s;
char *name;
char *value;
char *namecopy;
char *valuecopy;
d = array_ref(array, 1, &i,
-1 /* varlenarray */ ,
@ -5920,22 +5983,16 @@ ProcessGUCArray(ArrayType *array,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("could not parse setting for parameter \"%s\"",
name)));
free(name);
pfree(name);
continue;
}
/* free malloc'd strings immediately to avoid leak upon error */
namecopy = pstrdup(name);
free(name);
valuecopy = pstrdup(value);
free(value);
(void) set_config_option(namecopy, valuecopy,
(void) set_config_option(name, value,
context, source,
action, true, 0, false);
pfree(namecopy);
pfree(valuecopy);
pfree(name);
pfree(value);
pfree(s);
}
}
@ -6399,7 +6456,7 @@ call_string_check_hook(struct config_string *conf, char **newval, void **extra,
}
PG_CATCH();
{
free(*newval);
guc_free(*newval);
PG_RE_THROW();
}
PG_END_TRY();

View File

@ -439,7 +439,7 @@ ParseTzFile(const char *filename, int depth,
* load_tzoffsets --- read and parse the specified timezone offset file
*
* On success, return a filled-in TimeZoneAbbrevTable, which must have been
* malloc'd not palloc'd. On failure, return NULL, using GUC_check_errmsg
* guc_malloc'd not palloc'd. On failure, return NULL, using GUC_check_errmsg
* and friends to give details of the problem.
*/
TimeZoneAbbrevTable *

View File

@ -401,6 +401,7 @@ extern ArrayType *GUCArrayReset(ArrayType *array);
extern void *guc_malloc(int elevel, size_t size);
extern pg_nodiscard void *guc_realloc(int elevel, void *old, size_t size);
extern char *guc_strdup(int elevel, const char *src);
extern void guc_free(void *ptr);
#ifdef EXEC_BACKEND
extern void write_nondefault_variables(GucContext context);

View File

@ -114,7 +114,7 @@ plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
list_free(elemlist);
}
myextra = (int *) malloc(sizeof(int));
myextra = (int *) guc_malloc(LOG, sizeof(int));
if (!myextra)
return false;
*myextra = extrachecks;