]> granicus.if.org Git - postgresql/commitdiff
Don't call data type input functions in GUC check hooks
authorPeter Eisentraut <peter@eisentraut.org>
Sun, 30 Jun 2019 08:15:25 +0000 (10:15 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Sun, 30 Jun 2019 08:27:43 +0000 (10:27 +0200)
Instead of calling pg_lsn_in() in check_recovery_target_lsn and
timestamptz_in() in check_recovery_target_time, reorganize the
respective code so that we don't raise any errors in the check hooks.
The previous code tried to use PG_TRY/PG_CATCH to handle errors in a
way that is not safe, so now the code contains no ereport() calls and
can operate safely within the GUC error handling system.

Moreover, since the interpretation of the recovery_target_time string
may depend on the time zone, we cannot do the final processing of that
string until all the GUC processing is done.  Instead,
check_recovery_target_time() now does some parsing for syntax
checking, but the actual conversion to a timestamptz value is done
later in the recovery code that uses it.

Reported-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://www.postgresql.org/message-id/flat/20190611061115.njjwkagvxp4qujhp%40alap3.anarazel.de

src/backend/access/transam/xlog.c
src/backend/utils/adt/pg_lsn.c
src/backend/utils/misc/guc.c
src/include/access/xlog.h
src/include/utils/pg_lsn.h

index e08320e8290893e120563b22bc971483690c0987..13e0d2366f55480f25f805cf73dacaf5912dd457 100644 (file)
@@ -272,7 +272,8 @@ RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
 bool           recoveryTargetInclusive = true;
 int                    recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
 TransactionId recoveryTargetXid;
-TimestampTz recoveryTargetTime;
+char      *recovery_target_time_string;
+static TimestampTz recoveryTargetTime;
 const char *recoveryTargetName;
 XLogRecPtr     recoveryTargetLSN;
 int                    recovery_min_apply_delay = 0;
@@ -5409,6 +5410,18 @@ validateRecoveryParameters(void)
                !EnableHotStandby)
                recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
 
+       /*
+        * Final parsing of recovery_target_time string; see also
+        * check_recovery_target_time().
+        */
+       if (recoveryTarget == RECOVERY_TARGET_TIME)
+       {
+               recoveryTargetTime = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
+                                                                                                                                        CStringGetDatum(recovery_target_time_string),
+                                                                                                                                        ObjectIdGetDatum(InvalidOid),
+                                                                                                                                        Int32GetDatum(-1)));
+       }
+
        /*
         * If user specified recovery_target_timeline, validate it or compute the
         * "latest" value.  We can't do this until after we've gotten the restore
index 7242d3cfed485148edfcc73fdb3830d5e617688f..eb586851529e1a9a143ca51e8292f8efc50ec1eb 100644 (file)
  * Formatting and conversion routines.
  *---------------------------------------------------------*/
 
-Datum
-pg_lsn_in(PG_FUNCTION_ARGS)
+XLogRecPtr
+pg_lsn_in_internal(const char *str, bool *have_error)
 {
-       char       *str = PG_GETARG_CSTRING(0);
        int                     len1,
                                len2;
        uint32          id,
@@ -38,16 +37,16 @@ pg_lsn_in(PG_FUNCTION_ARGS)
        /* Sanity check input format. */
        len1 = strspn(str, "0123456789abcdefABCDEF");
        if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                errmsg("invalid input syntax for type %s: \"%s\"",
-                                               "pg_lsn", str)));
+       {
+               *have_error = true;
+               return InvalidXLogRecPtr;
+       }
        len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
        if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                errmsg("invalid input syntax for type %s: \"%s\"",
-                                               "pg_lsn", str)));
+       {
+               *have_error = true;
+               return InvalidXLogRecPtr;
+       }
 
        /* Decode result. */
        id = (uint32) strtoul(str, NULL, 16);
@@ -57,6 +56,23 @@ pg_lsn_in(PG_FUNCTION_ARGS)
        PG_RETURN_LSN(result);
 }
 
+Datum
+pg_lsn_in(PG_FUNCTION_ARGS)
+{
+       char       *str = PG_GETARG_CSTRING(0);
+       XLogRecPtr      result;
+       bool have_error = false;
+
+       result = pg_lsn_in_internal(str, &have_error);
+       if (have_error)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("invalid input syntax for type %s: \"%s\"",
+                                               "pg_lsn", str)));
+
+       PG_RETURN_LSN(result);
+}
+
 Datum
 pg_lsn_out(PG_FUNCTION_ARGS)
 {
index 1208eb9a6836033a8dd8ee991799d00a204c6f0f..92c4fee8f8b404d613220ae61fb6da5dcd4c7de6 100644 (file)
@@ -579,7 +579,6 @@ static bool assert_enabled;
 static char *recovery_target_timeline_string;
 static char *recovery_target_string;
 static char *recovery_target_xid_string;
-static char *recovery_target_time_string;
 static char *recovery_target_name_string;
 static char *recovery_target_lsn_string;
 
@@ -11572,20 +11571,20 @@ assign_recovery_target_xid(const char *newval, void *extra)
                recoveryTarget = RECOVERY_TARGET_UNSET;
 }
 
+/*
+ * The interpretation of the recovery_target_time string can depend on the
+ * time zone setting, so we need to wait until after all GUC processing is
+ * done before we can do the final parsing of the string.  This check function
+ * only does a parsing pass to catch syntax errors, but we store the string
+ * and parse it again when we need to use it.
+ */
 static bool
 check_recovery_target_time(char **newval, void **extra, GucSource source)
 {
        if (strcmp(*newval, "") != 0)
        {
-               TimestampTz time;
-               TimestampTz *myextra;
-               MemoryContext oldcontext = CurrentMemoryContext;
-
                /* reject some special values */
-               if (strcmp(*newval, "epoch") == 0 ||
-                       strcmp(*newval, "infinity") == 0 ||
-                       strcmp(*newval, "-infinity") == 0 ||
-                       strcmp(*newval, "now") == 0 ||
+               if (strcmp(*newval, "now") == 0 ||
                        strcmp(*newval, "today") == 0 ||
                        strcmp(*newval, "tomorrow") == 0 ||
                        strcmp(*newval, "yesterday") == 0)
@@ -11593,32 +11592,38 @@ check_recovery_target_time(char **newval, void **extra, GucSource source)
                        return false;
                }
 
-               PG_TRY();
-               {
-                       time = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
-                                                                                                                  CStringGetDatum(*newval),
-                                                                                                                  ObjectIdGetDatum(InvalidOid),
-                                                                                                                  Int32GetDatum(-1)));
-               }
-               PG_CATCH();
+               /*
+                * parse timestamp value (see also timestamptz_in())
+                */
                {
-                       ErrorData  *edata;
-
-                       /* Save error info */
-                       MemoryContextSwitchTo(oldcontext);
-                       edata = CopyErrorData();
-                       FlushErrorState();
-
-                       /* Pass the error message */
-                       GUC_check_errdetail("%s", edata->message);
-                       FreeErrorData(edata);
-                       return false;
+                       char       *str = *newval;
+                       fsec_t          fsec;
+                       struct pg_tm tt,
+                                          *tm = &tt;
+                       int                     tz;
+                       int                     dtype;
+                       int                     nf;
+                       int                     dterr;
+                       char       *field[MAXDATEFIELDS];
+                       int                     ftype[MAXDATEFIELDS];
+                       char            workbuf[MAXDATELEN + MAXDATEFIELDS];
+                       TimestampTz timestamp;
+
+                       dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+                                                                 field, ftype, MAXDATEFIELDS, &nf);
+                       if (dterr == 0)
+                               dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
+                       if (dterr != 0)
+                               return false;
+                       if (dtype !=  DTK_DATE)
+                               return false;
+
+                       if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
+                       {
+                               GUC_check_errdetail("timestamp out of range: \"%s\"", str);
+                               return false;
+                       }
                }
-               PG_END_TRY();
-
-               myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
-               *myextra = time;
-               *extra = (void *) myextra;
        }
        return true;
 }
@@ -11631,10 +11636,7 @@ assign_recovery_target_time(const char *newval, void *extra)
                error_multiple_recovery_targets();
 
        if (newval && strcmp(newval, "") != 0)
-       {
                recoveryTarget = RECOVERY_TARGET_TIME;
-               recoveryTargetTime = *((TimestampTz *) extra);
-       }
        else
                recoveryTarget = RECOVERY_TARGET_UNSET;
 }
@@ -11675,33 +11677,11 @@ check_recovery_target_lsn(char **newval, void **extra, GucSource source)
        {
                XLogRecPtr      lsn;
                XLogRecPtr *myextra;
-               MemoryContext oldcontext = CurrentMemoryContext;
-
-               /*
-                * Convert the LSN string given by the user to XLogRecPtr form.
-                */
-               PG_TRY();
-               {
-                       lsn = DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
-                                                                                                 CStringGetDatum(*newval),
-                                                                                                 ObjectIdGetDatum(InvalidOid),
-                                                                                                 Int32GetDatum(-1)));
-               }
-               PG_CATCH();
-               {
-                       ErrorData  *edata;
-
-                       /* Save error info */
-                       MemoryContextSwitchTo(oldcontext);
-                       edata = CopyErrorData();
-                       FlushErrorState();
+               bool            have_error = false;
 
-                       /* Pass the error message */
-                       GUC_check_errdetail("%s", edata->message);
-                       FreeErrorData(edata);
+               lsn = pg_lsn_in_internal(*newval, &have_error);
+               if (have_error)
                        return false;
-               }
-               PG_END_TRY();
 
                myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
                *myextra = lsn;
index 237f4e0350c89ec1ad0ea28fdb6cfc916e36e623..d519252aad77a37c1a2d7da98f4b93d7afdbb5a2 100644 (file)
@@ -132,7 +132,7 @@ extern char *PrimarySlotName;
 
 /* indirectly set via GUC system */
 extern TransactionId recoveryTargetXid;
-extern TimestampTz recoveryTargetTime;
+extern char *recovery_target_time_string;
 extern const char *recoveryTargetName;
 extern XLogRecPtr recoveryTargetLSN;
 extern RecoveryTargetType recoveryTarget;
index def5b25aa3710ac60e6dbdca14e883e212c21239..70d8640ef3eabb46bb84863ede58ebdfebd5501e 100644 (file)
@@ -24,4 +24,6 @@
 #define PG_GETARG_LSN(n)        DatumGetLSN(PG_GETARG_DATUM(n))
 #define PG_RETURN_LSN(x)        return LSNGetDatum(x)
 
+extern XLogRecPtr pg_lsn_in_internal(const char *str, bool *have_error);
+
 #endif                                                 /* PG_LSN_H */