Skip to content

Commit 21f428e

Browse files
committed
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
1 parent 666cbae commit 21f428e

File tree

5 files changed

+85
-74
lines changed

5 files changed

+85
-74
lines changed

src/backend/access/transam/xlog.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
272272
bool recoveryTargetInclusive = true;
273273
int recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
274274
TransactionId recoveryTargetXid;
275-
TimestampTz recoveryTargetTime;
275+
char *recovery_target_time_string;
276+
static TimestampTz recoveryTargetTime;
276277
const char *recoveryTargetName;
277278
XLogRecPtr recoveryTargetLSN;
278279
int recovery_min_apply_delay = 0;
@@ -5409,6 +5410,18 @@ validateRecoveryParameters(void)
54095410
!EnableHotStandby)
54105411
recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
54115412

5413+
/*
5414+
* Final parsing of recovery_target_time string; see also
5415+
* check_recovery_target_time().
5416+
*/
5417+
if (recoveryTarget == RECOVERY_TARGET_TIME)
5418+
{
5419+
recoveryTargetTime = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
5420+
CStringGetDatum(recovery_target_time_string),
5421+
ObjectIdGetDatum(InvalidOid),
5422+
Int32GetDatum(-1)));
5423+
}
5424+
54125425
/*
54135426
* If user specified recovery_target_timeline, validate it or compute the
54145427
* "latest" value. We can't do this until after we've gotten the restore

src/backend/utils/adt/pg_lsn.c

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@
2525
* Formatting and conversion routines.
2626
*---------------------------------------------------------*/
2727

28-
Datum
29-
pg_lsn_in(PG_FUNCTION_ARGS)
28+
XLogRecPtr
29+
pg_lsn_in_internal(const char *str, bool *have_error)
3030
{
31-
char *str = PG_GETARG_CSTRING(0);
3231
int len1,
3332
len2;
3433
uint32 id,
@@ -38,16 +37,16 @@ pg_lsn_in(PG_FUNCTION_ARGS)
3837
/* Sanity check input format. */
3938
len1 = strspn(str, "0123456789abcdefABCDEF");
4039
if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
41-
ereport(ERROR,
42-
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
43-
errmsg("invalid input syntax for type %s: \"%s\"",
44-
"pg_lsn", str)));
40+
{
41+
*have_error = true;
42+
return InvalidXLogRecPtr;
43+
}
4544
len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
4645
if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
47-
ereport(ERROR,
48-
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
49-
errmsg("invalid input syntax for type %s: \"%s\"",
50-
"pg_lsn", str)));
46+
{
47+
*have_error = true;
48+
return InvalidXLogRecPtr;
49+
}
5150

5251
/* Decode result. */
5352
id = (uint32) strtoul(str, NULL, 16);
@@ -57,6 +56,23 @@ pg_lsn_in(PG_FUNCTION_ARGS)
5756
PG_RETURN_LSN(result);
5857
}
5958

59+
Datum
60+
pg_lsn_in(PG_FUNCTION_ARGS)
61+
{
62+
char *str = PG_GETARG_CSTRING(0);
63+
XLogRecPtr result;
64+
bool have_error = false;
65+
66+
result = pg_lsn_in_internal(str, &have_error);
67+
if (have_error)
68+
ereport(ERROR,
69+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
70+
errmsg("invalid input syntax for type %s: \"%s\"",
71+
"pg_lsn", str)));
72+
73+
PG_RETURN_LSN(result);
74+
}
75+
6076
Datum
6177
pg_lsn_out(PG_FUNCTION_ARGS)
6278
{

src/backend/utils/misc/guc.c

Lines changed: 41 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,6 @@ static bool assert_enabled;
579579
static char *recovery_target_timeline_string;
580580
static char *recovery_target_string;
581581
static char *recovery_target_xid_string;
582-
static char *recovery_target_time_string;
583582
static char *recovery_target_name_string;
584583
static char *recovery_target_lsn_string;
585584

@@ -11572,53 +11571,59 @@ assign_recovery_target_xid(const char *newval, void *extra)
1157211571
recoveryTarget = RECOVERY_TARGET_UNSET;
1157311572
}
1157411573

11574+
/*
11575+
* The interpretation of the recovery_target_time string can depend on the
11576+
* time zone setting, so we need to wait until after all GUC processing is
11577+
* done before we can do the final parsing of the string. This check function
11578+
* only does a parsing pass to catch syntax errors, but we store the string
11579+
* and parse it again when we need to use it.
11580+
*/
1157511581
static bool
1157611582
check_recovery_target_time(char **newval, void **extra, GucSource source)
1157711583
{
1157811584
if (strcmp(*newval, "") != 0)
1157911585
{
11580-
TimestampTz time;
11581-
TimestampTz *myextra;
11582-
MemoryContext oldcontext = CurrentMemoryContext;
11583-
1158411586
/* reject some special values */
11585-
if (strcmp(*newval, "epoch") == 0 ||
11586-
strcmp(*newval, "infinity") == 0 ||
11587-
strcmp(*newval, "-infinity") == 0 ||
11588-
strcmp(*newval, "now") == 0 ||
11587+
if (strcmp(*newval, "now") == 0 ||
1158911588
strcmp(*newval, "today") == 0 ||
1159011589
strcmp(*newval, "tomorrow") == 0 ||
1159111590
strcmp(*newval, "yesterday") == 0)
1159211591
{
1159311592
return false;
1159411593
}
1159511594

11596-
PG_TRY();
11597-
{
11598-
time = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
11599-
CStringGetDatum(*newval),
11600-
ObjectIdGetDatum(InvalidOid),
11601-
Int32GetDatum(-1)));
11602-
}
11603-
PG_CATCH();
11595+
/*
11596+
* parse timestamp value (see also timestamptz_in())
11597+
*/
1160411598
{
11605-
ErrorData *edata;
11606-
11607-
/* Save error info */
11608-
MemoryContextSwitchTo(oldcontext);
11609-
edata = CopyErrorData();
11610-
FlushErrorState();
11611-
11612-
/* Pass the error message */
11613-
GUC_check_errdetail("%s", edata->message);
11614-
FreeErrorData(edata);
11615-
return false;
11599+
char *str = *newval;
11600+
fsec_t fsec;
11601+
struct pg_tm tt,
11602+
*tm = &tt;
11603+
int tz;
11604+
int dtype;
11605+
int nf;
11606+
int dterr;
11607+
char *field[MAXDATEFIELDS];
11608+
int ftype[MAXDATEFIELDS];
11609+
char workbuf[MAXDATELEN + MAXDATEFIELDS];
11610+
TimestampTz timestamp;
11611+
11612+
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
11613+
field, ftype, MAXDATEFIELDS, &nf);
11614+
if (dterr == 0)
11615+
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
11616+
if (dterr != 0)
11617+
return false;
11618+
if (dtype != DTK_DATE)
11619+
return false;
11620+
11621+
if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
11622+
{
11623+
GUC_check_errdetail("timestamp out of range: \"%s\"", str);
11624+
return false;
11625+
}
1161611626
}
11617-
PG_END_TRY();
11618-
11619-
myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
11620-
*myextra = time;
11621-
*extra = (void *) myextra;
1162211627
}
1162311628
return true;
1162411629
}
@@ -11631,10 +11636,7 @@ assign_recovery_target_time(const char *newval, void *extra)
1163111636
error_multiple_recovery_targets();
1163211637

1163311638
if (newval && strcmp(newval, "") != 0)
11634-
{
1163511639
recoveryTarget = RECOVERY_TARGET_TIME;
11636-
recoveryTargetTime = *((TimestampTz *) extra);
11637-
}
1163811640
else
1163911641
recoveryTarget = RECOVERY_TARGET_UNSET;
1164011642
}
@@ -11675,33 +11677,11 @@ check_recovery_target_lsn(char **newval, void **extra, GucSource source)
1167511677
{
1167611678
XLogRecPtr lsn;
1167711679
XLogRecPtr *myextra;
11678-
MemoryContext oldcontext = CurrentMemoryContext;
11679-
11680-
/*
11681-
* Convert the LSN string given by the user to XLogRecPtr form.
11682-
*/
11683-
PG_TRY();
11684-
{
11685-
lsn = DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
11686-
CStringGetDatum(*newval),
11687-
ObjectIdGetDatum(InvalidOid),
11688-
Int32GetDatum(-1)));
11689-
}
11690-
PG_CATCH();
11691-
{
11692-
ErrorData *edata;
11693-
11694-
/* Save error info */
11695-
MemoryContextSwitchTo(oldcontext);
11696-
edata = CopyErrorData();
11697-
FlushErrorState();
11680+
bool have_error = false;
1169811681

11699-
/* Pass the error message */
11700-
GUC_check_errdetail("%s", edata->message);
11701-
FreeErrorData(edata);
11682+
lsn = pg_lsn_in_internal(*newval, &have_error);
11683+
if (have_error)
1170211684
return false;
11703-
}
11704-
PG_END_TRY();
1170511685

1170611686
myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
1170711687
*myextra = lsn;

src/include/access/xlog.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ extern char *PrimarySlotName;
132132

133133
/* indirectly set via GUC system */
134134
extern TransactionId recoveryTargetXid;
135-
extern TimestampTz recoveryTargetTime;
135+
extern char *recovery_target_time_string;
136136
extern const char *recoveryTargetName;
137137
extern XLogRecPtr recoveryTargetLSN;
138138
extern RecoveryTargetType recoveryTarget;

src/include/utils/pg_lsn.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@
2424
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
2525
#define PG_RETURN_LSN(x) return LSNGetDatum(x)
2626

27+
extern XLogRecPtr pg_lsn_in_internal(const char *str, bool *have_error);
28+
2729
#endif /* PG_LSN_H */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy