Commit 13671b4b authored by Tom Lane's avatar Tom Lane

Code review for GUC serialization/deserialization code.

The serialization code dumped core for a string-valued GUC whose value
is NULL, which is a legal state.  The infrastructure isn't capable of
transmitting that state exactly, but fortunately, transmitting an empty
string instead should be close enough (compare, eg, commit e45e990e).

The code potentially underestimated the space required to format a
real-valued variable, both because it made an unwarranted assumption that
%g output would never be longer than %e output, and because it didn't count
right even for %e format.  In practice this would pretty much always be
masked by overestimates for other variables, but it's still wrong.

Also fix boundary-case error in read_gucstate, incorrect handling of the
case where guc_sourcefile is non-NULL but zero length (not clear that can
happen, but if it did, this code would get totally confused), and
confusingly useless check for a NULL result from read_gucstate.

Andreas Seltenreich discovered the core dump; other issues noted while
reading nearby code.  Back-patch to 9.5 where this code was introduced.

Michael Paquier and Tom Lane

Discussion: <871sy78wno.fsf@credativ.de>
parent 67dc4ccb
...@@ -8903,7 +8903,9 @@ can_skip_gucvar(struct config_generic * gconf) ...@@ -8903,7 +8903,9 @@ can_skip_gucvar(struct config_generic * gconf)
/* /*
* estimate_variable_size: * estimate_variable_size:
* Estimate max size for dumping the given GUC variable. * Compute space needed for dumping the given GUC variable.
*
* It's OK to overestimate, but not to underestimate.
*/ */
static Size static Size
estimate_variable_size(struct config_generic * gconf) estimate_variable_size(struct config_generic * gconf)
...@@ -8914,9 +8916,8 @@ estimate_variable_size(struct config_generic * gconf) ...@@ -8914,9 +8916,8 @@ estimate_variable_size(struct config_generic * gconf)
if (can_skip_gucvar(gconf)) if (can_skip_gucvar(gconf))
return 0; return 0;
size = 0; /* Name, plus trailing zero byte. */
size = strlen(gconf->name) + 1;
size = add_size(size, strlen(gconf->name) + 1);
/* Get the maximum display length of the GUC value. */ /* Get the maximum display length of the GUC value. */
switch (gconf->vartype) switch (gconf->vartype)
...@@ -8937,7 +8938,7 @@ estimate_variable_size(struct config_generic * gconf) ...@@ -8937,7 +8938,7 @@ estimate_variable_size(struct config_generic * gconf)
* small values. Maximum value is 2147483647, i.e. 10 chars. * small values. Maximum value is 2147483647, i.e. 10 chars.
* Include one byte for sign. * Include one byte for sign.
*/ */
if (abs(*conf->variable) < 1000) if (Abs(*conf->variable) < 1000)
valsize = 3 + 1; valsize = 3 + 1;
else else
valsize = 10 + 1; valsize = 10 + 1;
...@@ -8947,11 +8948,12 @@ estimate_variable_size(struct config_generic * gconf) ...@@ -8947,11 +8948,12 @@ estimate_variable_size(struct config_generic * gconf)
case PGC_REAL: case PGC_REAL:
{ {
/* /*
* We are going to print it with %.17g. Account for sign, * We are going to print it with %e with REALTYPE_PRECISION
* decimal point, and e+nnn notation. E.g. * fractional digits. Account for sign, leading digit,
* -3.9932904234000002e+110 * decimal point, and exponent with up to 3 digits. E.g.
* -3.99329042340000021e+110
*/ */
valsize = REALTYPE_PRECISION + 1 + 1 + 5; valsize = 1 + 1 + 1 + REALTYPE_PRECISION + 5;
} }
break; break;
...@@ -8959,7 +8961,15 @@ estimate_variable_size(struct config_generic * gconf) ...@@ -8959,7 +8961,15 @@ estimate_variable_size(struct config_generic * gconf)
{ {
struct config_string *conf = (struct config_string *) gconf; struct config_string *conf = (struct config_string *) gconf;
valsize = strlen(*conf->variable); /*
* If the value is NULL, we transmit it as an empty string.
* Although this is not physically the same value, GUC
* generally treats a NULL the same as empty string.
*/
if (*conf->variable)
valsize = strlen(*conf->variable);
else
valsize = 0;
} }
break; break;
...@@ -8972,17 +8982,17 @@ estimate_variable_size(struct config_generic * gconf) ...@@ -8972,17 +8982,17 @@ estimate_variable_size(struct config_generic * gconf)
break; break;
} }
/* Allow space for terminating zero-byte */ /* Allow space for terminating zero-byte for value */
size = add_size(size, valsize + 1); size = add_size(size, valsize + 1);
if (gconf->sourcefile) if (gconf->sourcefile)
size = add_size(size, strlen(gconf->sourcefile)); size = add_size(size, strlen(gconf->sourcefile));
/* Allow space for terminating zero-byte */ /* Allow space for terminating zero-byte for sourcefile */
size = add_size(size, 1); size = add_size(size, 1);
/* Include line whenever we include file. */ /* Include line whenever file is nonempty. */
if (gconf->sourcefile) if (gconf->sourcefile && gconf->sourcefile[0])
size = add_size(size, sizeof(gconf->sourceline)); size = add_size(size, sizeof(gconf->sourceline));
size = add_size(size, sizeof(gconf->source)); size = add_size(size, sizeof(gconf->source));
...@@ -9100,7 +9110,7 @@ serialize_variable(char **destptr, Size *maxbytes, ...@@ -9100,7 +9110,7 @@ serialize_variable(char **destptr, Size *maxbytes,
{ {
struct config_real *conf = (struct config_real *) gconf; struct config_real *conf = (struct config_real *) gconf;
do_serialize(destptr, maxbytes, "%.*g", do_serialize(destptr, maxbytes, "%.*e",
REALTYPE_PRECISION, *conf->variable); REALTYPE_PRECISION, *conf->variable);
} }
break; break;
...@@ -9109,7 +9119,9 @@ serialize_variable(char **destptr, Size *maxbytes, ...@@ -9109,7 +9119,9 @@ serialize_variable(char **destptr, Size *maxbytes,
{ {
struct config_string *conf = (struct config_string *) gconf; struct config_string *conf = (struct config_string *) gconf;
do_serialize(destptr, maxbytes, "%s", *conf->variable); /* NULL becomes empty string, see estimate_variable_size() */
do_serialize(destptr, maxbytes, "%s",
*conf->variable ? *conf->variable : "");
} }
break; break;
...@@ -9126,7 +9138,7 @@ serialize_variable(char **destptr, Size *maxbytes, ...@@ -9126,7 +9138,7 @@ serialize_variable(char **destptr, Size *maxbytes,
do_serialize(destptr, maxbytes, "%s", do_serialize(destptr, maxbytes, "%s",
(gconf->sourcefile ? gconf->sourcefile : "")); (gconf->sourcefile ? gconf->sourcefile : ""));
if (gconf->sourcefile) if (gconf->sourcefile && gconf->sourcefile[0])
do_serialize_binary(destptr, maxbytes, &gconf->sourceline, do_serialize_binary(destptr, maxbytes, &gconf->sourceline,
sizeof(gconf->sourceline)); sizeof(gconf->sourceline));
...@@ -9193,7 +9205,7 @@ read_gucstate(char **srcptr, char *srcend) ...@@ -9193,7 +9205,7 @@ read_gucstate(char **srcptr, char *srcend)
for (ptr = *srcptr; ptr < srcend && *ptr != '\0'; ptr++) for (ptr = *srcptr; ptr < srcend && *ptr != '\0'; ptr++)
; ;
if (ptr > srcend) if (ptr >= srcend)
elog(ERROR, "could not find null terminator in GUC state"); elog(ERROR, "could not find null terminator in GUC state");
/* Set the new position to the byte following the terminating NUL */ /* Set the new position to the byte following the terminating NUL */
...@@ -9247,9 +9259,7 @@ RestoreGUCState(void *gucstate) ...@@ -9247,9 +9259,7 @@ RestoreGUCState(void *gucstate)
{ {
int result; int result;
if ((varname = read_gucstate(&srcptr, srcend)) == NULL) varname = read_gucstate(&srcptr, srcend);
break;
varvalue = read_gucstate(&srcptr, srcend); varvalue = read_gucstate(&srcptr, srcend);
varsourcefile = read_gucstate(&srcptr, srcend); varsourcefile = read_gucstate(&srcptr, srcend);
if (varsourcefile[0]) if (varsourcefile[0])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment