When restoring GUCs in parallel workers, show an error context.
authorThomas Munro <tmunro@postgresql.org>
Thu, 17 Oct 2019 00:24:50 +0000 (13:24 +1300)
committerThomas Munro <tmunro@postgresql.org>
Thu, 17 Oct 2019 00:47:01 +0000 (13:47 +1300)
Otherwise it can be hard to see where an error is coming from, when
the parallel worker sets all the GUCs that it received from the
leader.  Bug #15726.  Back-patch to 9.5, where RestoreGUCState()
appeared.

Reported-by: Tiago Anastacio
Reviewed-by: Daniel Gustafsson, Tom Lane
Discussion: https://postgr.es/m/15726-6d67e4fa14f027b3%40postgresql.org

src/backend/utils/misc/guc.c

index 2178e1cf5e2972e86779d07d1d9aeb9628deff39..31a5ef0474732590393afac972dc031494334cc3 100644 (file)
@@ -10209,6 +10209,21 @@ read_gucstate_binary(char **srcptr, char *srcend, void *dest, Size size)
    *srcptr += size;
 }
 
+/*
+ * Callback used to add a context message when reporting errors that occur
+ * while trying to restore GUCs in parallel workers.
+ */
+static void
+guc_restore_error_context_callback(void *arg)
+{
+   char      **error_context_name_and_value = (char **) arg;
+
+   if (error_context_name_and_value)
+       errcontext("while setting parameter \"%s\" to \"%s\"",
+                  error_context_name_and_value[0],
+                  error_context_name_and_value[1]);
+}
+
 /*
  * RestoreGUCState:
  * Reads the GUC state at the specified address and updates the GUCs with the
@@ -10227,6 +10242,7 @@ RestoreGUCState(void *gucstate)
    char       *srcend;
    Size        len;
    int         i;
+   ErrorContextCallback error_context_callback;
 
    /* See comment at can_skip_gucvar(). */
    for (i = 0; i < num_guc_variables; i++)
@@ -10239,9 +10255,16 @@ RestoreGUCState(void *gucstate)
    srcptr += sizeof(len);
    srcend = srcptr + len;
 
+   /* If the GUC value check fails, we want errors to show useful context. */
+   error_context_callback.callback = guc_restore_error_context_callback;
+   error_context_callback.previous = error_context_stack;
+   error_context_callback.arg = NULL;
+   error_context_stack = &error_context_callback;
+
    while (srcptr < srcend)
    {
        int         result;
+       char       *error_context_name_and_value[2];
 
        varname = read_gucstate(&srcptr, srcend);
        varvalue = read_gucstate(&srcptr, srcend);
@@ -10256,6 +10279,9 @@ RestoreGUCState(void *gucstate)
        read_gucstate_binary(&srcptr, srcend,
                             &varscontext, sizeof(varscontext));
 
+       error_context_name_and_value[0] = varname;
+       error_context_name_and_value[1] = varvalue;
+       error_context_callback.arg = &error_context_name_and_value[0];
        result = set_config_option(varname, varvalue, varscontext, varsource,
                                   GUC_ACTION_SET, true, ERROR, true);
        if (result <= 0)
@@ -10264,7 +10290,10 @@ RestoreGUCState(void *gucstate)
                     errmsg("parameter \"%s\" could not be set", varname)));
        if (varsourcefile[0])
            set_config_sourcefile(varname, varsourcefile, varsourceline);
+       error_context_callback.arg = NULL;
    }
+
+   error_context_stack = error_context_callback.previous;
 }
 
 /*