Commit 21f428eb authored by Peter Eisentraut's avatar Peter Eisentraut

Don't call data type input functions in GUC check hooks

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: default avatarAndres Freund <andres@anarazel.de>
Reviewed-by: default avatarMichael Paquier <michael@paquier.xyz>
Discussion: https://www.postgresql.org/message-id/flat/20190611061115.njjwkagvxp4qujhp%40alap3.anarazel.de
parent 666cbae1
...@@ -272,7 +272,8 @@ RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; ...@@ -272,7 +272,8 @@ RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
bool recoveryTargetInclusive = true; bool recoveryTargetInclusive = true;
int recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; int recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
TransactionId recoveryTargetXid; TransactionId recoveryTargetXid;
TimestampTz recoveryTargetTime; char *recovery_target_time_string;
static TimestampTz recoveryTargetTime;
const char *recoveryTargetName; const char *recoveryTargetName;
XLogRecPtr recoveryTargetLSN; XLogRecPtr recoveryTargetLSN;
int recovery_min_apply_delay = 0; int recovery_min_apply_delay = 0;
...@@ -5409,6 +5410,18 @@ validateRecoveryParameters(void) ...@@ -5409,6 +5410,18 @@ validateRecoveryParameters(void)
!EnableHotStandby) !EnableHotStandby)
recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN; 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 * 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 * "latest" value. We can't do this until after we've gotten the restore
......
...@@ -25,10 +25,9 @@ ...@@ -25,10 +25,9 @@
* Formatting and conversion routines. * Formatting and conversion routines.
*---------------------------------------------------------*/ *---------------------------------------------------------*/
Datum XLogRecPtr
pg_lsn_in(PG_FUNCTION_ARGS) pg_lsn_in_internal(const char *str, bool *have_error)
{ {
char *str = PG_GETARG_CSTRING(0);
int len1, int len1,
len2; len2;
uint32 id, uint32 id,
...@@ -38,16 +37,16 @@ pg_lsn_in(PG_FUNCTION_ARGS) ...@@ -38,16 +37,16 @@ pg_lsn_in(PG_FUNCTION_ARGS)
/* Sanity check input format. */ /* Sanity check input format. */
len1 = strspn(str, "0123456789abcdefABCDEF"); len1 = strspn(str, "0123456789abcdefABCDEF");
if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/') if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
ereport(ERROR, {
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), *have_error = true;
errmsg("invalid input syntax for type %s: \"%s\"", return InvalidXLogRecPtr;
"pg_lsn", str))); }
len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF"); len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0') if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
ereport(ERROR, {
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), *have_error = true;
errmsg("invalid input syntax for type %s: \"%s\"", return InvalidXLogRecPtr;
"pg_lsn", str))); }
/* Decode result. */ /* Decode result. */
id = (uint32) strtoul(str, NULL, 16); id = (uint32) strtoul(str, NULL, 16);
...@@ -57,6 +56,23 @@ pg_lsn_in(PG_FUNCTION_ARGS) ...@@ -57,6 +56,23 @@ pg_lsn_in(PG_FUNCTION_ARGS)
PG_RETURN_LSN(result); 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 Datum
pg_lsn_out(PG_FUNCTION_ARGS) pg_lsn_out(PG_FUNCTION_ARGS)
{ {
......
...@@ -579,7 +579,6 @@ static bool assert_enabled; ...@@ -579,7 +579,6 @@ static bool assert_enabled;
static char *recovery_target_timeline_string; static char *recovery_target_timeline_string;
static char *recovery_target_string; static char *recovery_target_string;
static char *recovery_target_xid_string; static char *recovery_target_xid_string;
static char *recovery_target_time_string;
static char *recovery_target_name_string; static char *recovery_target_name_string;
static char *recovery_target_lsn_string; static char *recovery_target_lsn_string;
...@@ -11572,20 +11571,20 @@ assign_recovery_target_xid(const char *newval, void *extra) ...@@ -11572,20 +11571,20 @@ assign_recovery_target_xid(const char *newval, void *extra)
recoveryTarget = RECOVERY_TARGET_UNSET; 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 static bool
check_recovery_target_time(char **newval, void **extra, GucSource source) check_recovery_target_time(char **newval, void **extra, GucSource source)
{ {
if (strcmp(*newval, "") != 0) if (strcmp(*newval, "") != 0)
{ {
TimestampTz time;
TimestampTz *myextra;
MemoryContext oldcontext = CurrentMemoryContext;
/* reject some special values */ /* reject some special values */
if (strcmp(*newval, "epoch") == 0 || if (strcmp(*newval, "now") == 0 ||
strcmp(*newval, "infinity") == 0 ||
strcmp(*newval, "-infinity") == 0 ||
strcmp(*newval, "now") == 0 ||
strcmp(*newval, "today") == 0 || strcmp(*newval, "today") == 0 ||
strcmp(*newval, "tomorrow") == 0 || strcmp(*newval, "tomorrow") == 0 ||
strcmp(*newval, "yesterday") == 0) strcmp(*newval, "yesterday") == 0)
...@@ -11593,32 +11592,38 @@ check_recovery_target_time(char **newval, void **extra, GucSource source) ...@@ -11593,32 +11592,38 @@ check_recovery_target_time(char **newval, void **extra, GucSource source)
return false; return false;
} }
PG_TRY(); /*
{ * parse timestamp value (see also timestamptz_in())
time = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in, */
CStringGetDatum(*newval), {
ObjectIdGetDatum(InvalidOid), char *str = *newval;
Int32GetDatum(-1))); fsec_t fsec;
} struct pg_tm tt,
PG_CATCH(); *tm = &tt;
{ int tz;
ErrorData *edata; int dtype;
int nf;
/* Save error info */ int dterr;
MemoryContextSwitchTo(oldcontext); char *field[MAXDATEFIELDS];
edata = CopyErrorData(); int ftype[MAXDATEFIELDS];
FlushErrorState(); 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;
/* Pass the error message */ if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
GUC_check_errdetail("%s", edata->message); {
FreeErrorData(edata); GUC_check_errdetail("timestamp out of range: \"%s\"", str);
return false; return false;
} }
PG_END_TRY(); }
myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
*myextra = time;
*extra = (void *) myextra;
} }
return true; return true;
} }
...@@ -11631,10 +11636,7 @@ assign_recovery_target_time(const char *newval, void *extra) ...@@ -11631,10 +11636,7 @@ assign_recovery_target_time(const char *newval, void *extra)
error_multiple_recovery_targets(); error_multiple_recovery_targets();
if (newval && strcmp(newval, "") != 0) if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_TIME; recoveryTarget = RECOVERY_TARGET_TIME;
recoveryTargetTime = *((TimestampTz *) extra);
}
else else
recoveryTarget = RECOVERY_TARGET_UNSET; recoveryTarget = RECOVERY_TARGET_UNSET;
} }
...@@ -11675,33 +11677,11 @@ check_recovery_target_lsn(char **newval, void **extra, GucSource source) ...@@ -11675,33 +11677,11 @@ check_recovery_target_lsn(char **newval, void **extra, GucSource source)
{ {
XLogRecPtr lsn; XLogRecPtr lsn;
XLogRecPtr *myextra; XLogRecPtr *myextra;
MemoryContext oldcontext = CurrentMemoryContext; bool have_error = false;
/*
* 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 */ lsn = pg_lsn_in_internal(*newval, &have_error);
MemoryContextSwitchTo(oldcontext); if (have_error)
edata = CopyErrorData();
FlushErrorState();
/* Pass the error message */
GUC_check_errdetail("%s", edata->message);
FreeErrorData(edata);
return false; return false;
}
PG_END_TRY();
myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr)); myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
*myextra = lsn; *myextra = lsn;
......
...@@ -132,7 +132,7 @@ extern char *PrimarySlotName; ...@@ -132,7 +132,7 @@ extern char *PrimarySlotName;
/* indirectly set via GUC system */ /* indirectly set via GUC system */
extern TransactionId recoveryTargetXid; extern TransactionId recoveryTargetXid;
extern TimestampTz recoveryTargetTime; extern char *recovery_target_time_string;
extern const char *recoveryTargetName; extern const char *recoveryTargetName;
extern XLogRecPtr recoveryTargetLSN; extern XLogRecPtr recoveryTargetLSN;
extern RecoveryTargetType recoveryTarget; extern RecoveryTargetType recoveryTarget;
......
...@@ -24,4 +24,6 @@ ...@@ -24,4 +24,6 @@
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n)) #define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
#define PG_RETURN_LSN(x) return LSNGetDatum(x) #define PG_RETURN_LSN(x) return LSNGetDatum(x)
extern XLogRecPtr pg_lsn_in_internal(const char *str, bool *have_error);
#endif /* PG_LSN_H */ #endif /* PG_LSN_H */
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