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: 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
This commit is contained in:
parent
666cbae16d
commit
21f428ebde
@ -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)
|
|||||||
!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 @@
|
|||||||
* 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)
|
|||||||
/* 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)
|
|||||||
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;
|
|||||||
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)
|
|||||||
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)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PG_TRY();
|
/*
|
||||||
|
* parse timestamp value (see also timestamptz_in())
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
time = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
|
char *str = *newval;
|
||||||
CStringGetDatum(*newval),
|
fsec_t fsec;
|
||||||
ObjectIdGetDatum(InvalidOid),
|
struct pg_tm tt,
|
||||||
Int32GetDatum(-1)));
|
*tm = &tt;
|
||||||
}
|
int tz;
|
||||||
PG_CATCH();
|
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, ×tamp) != 0)
|
||||||
{
|
{
|
||||||
ErrorData *edata;
|
GUC_check_errdetail("timestamp out of range: \"%s\"", str);
|
||||||
|
|
||||||
/* Save error info */
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
edata = CopyErrorData();
|
|
||||||
FlushErrorState();
|
|
||||||
|
|
||||||
/* Pass the error message */
|
|
||||||
GUC_check_errdetail("%s", edata->message);
|
|
||||||
FreeErrorData(edata);
|
|
||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
XLogRecPtr lsn;
|
XLogRecPtr lsn;
|
||||||
XLogRecPtr *myextra;
|
XLogRecPtr *myextra;
|
||||||
MemoryContext oldcontext = CurrentMemoryContext;
|
bool have_error = false;
|
||||||
|
|
||||||
/*
|
lsn = pg_lsn_in_internal(*newval, &have_error);
|
||||||
* Convert the LSN string given by the user to XLogRecPtr form.
|
if (have_error)
|
||||||
*/
|
|
||||||
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();
|
|
||||||
|
|
||||||
/* 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;
|
|||||||
|
|
||||||
/* 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 @@
|
|||||||
#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 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user