Skip to content

Commit b9ee42e

Browse files
committed
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 e45e990). 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>
1 parent a786403 commit b9ee42e

File tree

1 file changed

+31
-21
lines changed
  • src/backend/utils/misc

1 file changed

+31
-21
lines changed

src/backend/utils/misc/guc.c

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8675,7 +8675,9 @@ can_skip_gucvar(struct config_generic * gconf)
86758675

86768676
/*
86778677
* estimate_variable_size:
8678-
* Estimate max size for dumping the given GUC variable.
8678+
* Compute space needed for dumping the given GUC variable.
8679+
*
8680+
* It's OK to overestimate, but not to underestimate.
86798681
*/
86808682
static Size
86818683
estimate_variable_size(struct config_generic * gconf)
@@ -8686,9 +8688,8 @@ estimate_variable_size(struct config_generic * gconf)
86868688
if (can_skip_gucvar(gconf))
86878689
return 0;
86888690

8689-
size = 0;
8690-
8691-
size = add_size(size, strlen(gconf->name) + 1);
8691+
/* Name, plus trailing zero byte. */
8692+
size = strlen(gconf->name) + 1;
86928693

86938694
/* Get the maximum display length of the GUC value. */
86948695
switch (gconf->vartype)
@@ -8709,7 +8710,7 @@ estimate_variable_size(struct config_generic * gconf)
87098710
* small values. Maximum value is 2147483647, i.e. 10 chars.
87108711
* Include one byte for sign.
87118712
*/
8712-
if (abs(*conf->variable) < 1000)
8713+
if (Abs(*conf->variable) < 1000)
87138714
valsize = 3 + 1;
87148715
else
87158716
valsize = 10 + 1;
@@ -8719,19 +8720,28 @@ estimate_variable_size(struct config_generic * gconf)
87198720
case PGC_REAL:
87208721
{
87218722
/*
8722-
* We are going to print it with %.17g. Account for sign,
8723-
* decimal point, and e+nnn notation. E.g.
8724-
* -3.9932904234000002e+110
8723+
* We are going to print it with %e with REALTYPE_PRECISION
8724+
* fractional digits. Account for sign, leading digit,
8725+
* decimal point, and exponent with up to 3 digits. E.g.
8726+
* -3.99329042340000021e+110
87258727
*/
8726-
valsize = REALTYPE_PRECISION + 1 + 1 + 5;
8728+
valsize = 1 + 1 + 1 + REALTYPE_PRECISION + 5;
87278729
}
87288730
break;
87298731

87308732
case PGC_STRING:
87318733
{
87328734
struct config_string *conf = (struct config_string *) gconf;
87338735

8734-
valsize = strlen(*conf->variable);
8736+
/*
8737+
* If the value is NULL, we transmit it as an empty string.
8738+
* Although this is not physically the same value, GUC
8739+
* generally treats a NULL the same as empty string.
8740+
*/
8741+
if (*conf->variable)
8742+
valsize = strlen(*conf->variable);
8743+
else
8744+
valsize = 0;
87358745
}
87368746
break;
87378747

@@ -8744,17 +8754,17 @@ estimate_variable_size(struct config_generic * gconf)
87448754
break;
87458755
}
87468756

8747-
/* Allow space for terminating zero-byte */
8757+
/* Allow space for terminating zero-byte for value */
87488758
size = add_size(size, valsize + 1);
87498759

87508760
if (gconf->sourcefile)
87518761
size = add_size(size, strlen(gconf->sourcefile));
87528762

8753-
/* Allow space for terminating zero-byte */
8763+
/* Allow space for terminating zero-byte for sourcefile */
87548764
size = add_size(size, 1);
87558765

8756-
/* Include line whenever we include file. */
8757-
if (gconf->sourcefile)
8766+
/* Include line whenever file is nonempty. */
8767+
if (gconf->sourcefile && gconf->sourcefile[0])
87588768
size = add_size(size, sizeof(gconf->sourceline));
87598769

87608770
size = add_size(size, sizeof(gconf->source));
@@ -8872,7 +8882,7 @@ serialize_variable(char **destptr, Size *maxbytes,
88728882
{
88738883
struct config_real *conf = (struct config_real *) gconf;
88748884

8875-
do_serialize(destptr, maxbytes, "%.*g",
8885+
do_serialize(destptr, maxbytes, "%.*e",
88768886
REALTYPE_PRECISION, *conf->variable);
88778887
}
88788888
break;
@@ -8881,7 +8891,9 @@ serialize_variable(char **destptr, Size *maxbytes,
88818891
{
88828892
struct config_string *conf = (struct config_string *) gconf;
88838893

8884-
do_serialize(destptr, maxbytes, "%s", *conf->variable);
8894+
/* NULL becomes empty string, see estimate_variable_size() */
8895+
do_serialize(destptr, maxbytes, "%s",
8896+
*conf->variable ? *conf->variable : "");
88858897
}
88868898
break;
88878899

@@ -8898,7 +8910,7 @@ serialize_variable(char **destptr, Size *maxbytes,
88988910
do_serialize(destptr, maxbytes, "%s",
88998911
(gconf->sourcefile ? gconf->sourcefile : ""));
89008912

8901-
if (gconf->sourcefile)
8913+
if (gconf->sourcefile && gconf->sourcefile[0])
89028914
do_serialize_binary(destptr, maxbytes, &gconf->sourceline,
89038915
sizeof(gconf->sourceline));
89048916

@@ -8965,7 +8977,7 @@ read_gucstate(char **srcptr, char *srcend)
89658977
for (ptr = *srcptr; ptr < srcend && *ptr != '\0'; ptr++)
89668978
;
89678979

8968-
if (ptr > srcend)
8980+
if (ptr >= srcend)
89698981
elog(ERROR, "could not find null terminator in GUC state");
89708982

89718983
/* Set the new position to the byte following the terminating NUL */
@@ -9019,9 +9031,7 @@ RestoreGUCState(void *gucstate)
90199031
{
90209032
int result;
90219033

9022-
if ((varname = read_gucstate(&srcptr, srcend)) == NULL)
9023-
break;
9024-
9034+
varname = read_gucstate(&srcptr, srcend);
90259035
varvalue = read_gucstate(&srcptr, srcend);
90269036
varsourcefile = read_gucstate(&srcptr, srcend);
90279037
if (varsourcefile[0])

0 commit comments

Comments
 (0)