char *subval;
void *subextra = NULL;
- subval = strdup(GetConfigOptionResetString("datestyle"));
+ subval = guc_strdup(LOG, GetConfigOptionResetString("datestyle"));
if (!subval)
{
ok = false;
}
if (!check_datestyle(&subval, &subextra, source))
{
- free(subval);
+ guc_free(subval);
ok = false;
break;
}
newDateStyle = myextra[0];
if (!have_order)
newDateOrder = myextra[1];
- free(subval);
- free(subextra);
+ guc_free(subval);
+ guc_free(subextra);
}
else
{
}
/*
- * 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;
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;
/*
* 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;
/*
* 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;
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 */
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 */
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;
}
/*
* 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;
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;
}
/* 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;
};
+/* Memory context holding all GUC-related data */
+static MemoryContext GUCMemoryContext;
+
/*
* Actual lookup of variables is done through this single, sorted array.
*/
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")));
{
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")));
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
/* 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);
}
/*
/* 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);
}
/*
* 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)
/*
- * 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)
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];
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;
gen->name = guc_strdup(elevel, name);
if (gen->name == NULL)
{
- free(var);
+ guc_free(var);
return NULL;
}
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;
}
pg_timezone_initialize();
/*
- * Build sorted array of all GUC variables.
+ * Create GUCMemoryContext and build sorted array of all GUC variables.
*/
build_guc_variables();
{
char *configdir;
char *fname;
+ bool fname_is_malloced;
struct stat stat_buf;
struct config_string *data_directory_rec;
* 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
{
* 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.
* 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
{
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
{
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);
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);
if (!call_string_check_hook(conf, &newval->stringval, newextra,
source, elevel))
{
- free(newval->stringval);
+ guc_free(newval->stringval);
newval->stringval = NULL;
return false;
}
{
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
- free(newextra);
+ guc_free(newextra);
if (*conf->variable != newval)
{
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
- free(newextra);
+ guc_free(newextra);
break;
#undef newval
{
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
- free(newextra);
+ guc_free(newextra);
if (*conf->variable != newval)
{
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
- free(newextra);
+ guc_free(newextra);
break;
#undef newval
{
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
- free(newextra);
+ guc_free(newextra);
if (*conf->variable != newval)
{
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
- free(newextra);
+ guc_free(newextra);
break;
#undef newval
if (!call_string_check_hook(conf, &newval, &newextra,
source, elevel))
{
- free(newval);
+ guc_free(newval);
return 0;
}
}
/* 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)
{
/* 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
{
/* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra))
- free(newextra);
+ guc_free(newextra);
if (*conf->variable != newval)
{
/* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra))
- free(newextra);
+ guc_free(newextra);
break;
#undef newval
return;
sourcefile = guc_strdup(elevel, sourcefile);
- free(record->sourcefile);
+ guc_free(record->sourcefile);
record->sourcefile = sourcefile;
record->sourceline = sourceline;
}
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
set_string_field(pHolder, pHolder->variable, NULL);
set_string_field(pHolder, &pHolder->reset_val, NULL);
- free(pHolder);
+ guc_free(pHolder);
}
/*
}
/* 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);
}
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);
* 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:
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:
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:
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:
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;
}
}
/*
* 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.
*/
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;
}
char *s;
char *name;
char *value;
- char *namecopy;
- char *valuecopy;
d = array_ref(array, 1, &i,
-1 /* varlenarray */ ,
(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);
}
}
}
PG_CATCH();
{
- free(*newval);
+ guc_free(*newval);
PG_RE_THROW();
}
PG_END_TRY();