Skip to content

Commit 970a186

Browse files
committed
Use GUC lexer for recovery.conf parsing.
This eliminates some crufty, special-purpose code and, as a non-trivial side benefit, allows recovery.conf parameters to be unquoted. Dimitri Fontaine, with review and cleanup by Alvaro Herrera, Itagaki Takahiro, and me.
1 parent 9cea52a commit 970a186

File tree

4 files changed

+135
-209
lines changed

4 files changed

+135
-209
lines changed

src/backend/access/transam/xlog.c

Lines changed: 39 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -5023,116 +5023,21 @@ str_time(pg_time_t tnow)
50235023
return buf;
50245024
}
50255025

5026-
/*
5027-
* Parse one line from recovery.conf. 'cmdline' is the raw line from the
5028-
* file. If the line is parsed successfully, returns true, false indicates
5029-
* syntax error. On success, *key_p and *value_p are set to the parameter
5030-
* name and value on the line, respectively. If the line is an empty line,
5031-
* consisting entirely of whitespace and comments, function returns true
5032-
* and *keyp_p and *value_p are set to NULL.
5033-
*
5034-
* The pointers returned in *key_p and *value_p point to an internal buffer
5035-
* that is valid only until the next call of parseRecoveryCommandFile().
5036-
*/
5037-
static bool
5038-
parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
5039-
{
5040-
char *ptr;
5041-
char *bufp;
5042-
char *key;
5043-
char *value;
5044-
static char *buf = NULL;
5045-
5046-
*key_p = *value_p = NULL;
5047-
5048-
/*
5049-
* Allocate the buffer on first use. It's used to hold both the parameter
5050-
* name and value.
5051-
*/
5052-
if (buf == NULL)
5053-
buf = malloc(MAXPGPATH + 1);
5054-
bufp = buf;
5055-
5056-
/* Skip any whitespace at the beginning of line */
5057-
for (ptr = cmdline; *ptr; ptr++)
5058-
{
5059-
if (!isspace((unsigned char) *ptr))
5060-
break;
5061-
}
5062-
/* Ignore empty lines */
5063-
if (*ptr == '\0' || *ptr == '#')
5064-
return true;
5065-
5066-
/* Read the parameter name */
5067-
key = bufp;
5068-
while (*ptr && !isspace((unsigned char) *ptr) &&
5069-
*ptr != '=' && *ptr != '\'')
5070-
*(bufp++) = *(ptr++);
5071-
*(bufp++) = '\0';
5072-
5073-
/* Skip to the beginning quote of the parameter value */
5074-
ptr = strchr(ptr, '\'');
5075-
if (!ptr)
5076-
return false;
5077-
ptr++;
5078-
5079-
/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
5080-
value = bufp;
5081-
for (;;)
5082-
{
5083-
if (*ptr == '\'')
5084-
{
5085-
ptr++;
5086-
if (*ptr == '\'')
5087-
*(bufp++) = '\'';
5088-
else
5089-
{
5090-
/* end of parameter */
5091-
*bufp = '\0';
5092-
break;
5093-
}
5094-
}
5095-
else if (*ptr == '\0')
5096-
return false; /* unterminated quoted string */
5097-
else
5098-
*(bufp++) = *ptr;
5099-
5100-
ptr++;
5101-
}
5102-
*(bufp++) = '\0';
5103-
5104-
/* Check that there's no garbage after the value */
5105-
while (*ptr)
5106-
{
5107-
if (*ptr == '#')
5108-
break;
5109-
if (!isspace((unsigned char) *ptr))
5110-
return false;
5111-
ptr++;
5112-
}
5113-
5114-
/* Success! */
5115-
*key_p = key;
5116-
*value_p = value;
5117-
return true;
5118-
}
5119-
51205026
/*
51215027
* See if there is a recovery command file (recovery.conf), and if so
51225028
* read in parameters for archive recovery and XLOG streaming.
51235029
*
5124-
* XXX longer term intention is to expand this to
5125-
* cater for additional parameters and controls
5126-
* possibly use a flex lexer similar to the GUC one
5030+
* The file is parsed using the main configuration parser.
51275031
*/
51285032
static void
51295033
readRecoveryCommandFile(void)
51305034
{
51315035
FILE *fd;
5132-
char cmdline[MAXPGPATH];
51335036
TimeLineID rtli = 0;
51345037
bool rtliGiven = false;
5135-
bool syntaxError = false;
5038+
ConfigVariable *item,
5039+
*head = NULL,
5040+
*tail = NULL;
51365041

51375042
fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
51385043
if (fd == NULL)
@@ -5146,55 +5051,47 @@ readRecoveryCommandFile(void)
51465051
}
51475052

51485053
/*
5149-
* Parse the file...
5150-
*/
5151-
while (fgets(cmdline, sizeof(cmdline), fd) != NULL)
5152-
{
5153-
char *tok1;
5154-
char *tok2;
5054+
* Since we're asking ParseConfigFp() to error out at FATAL, there's no
5055+
* need to check the return value.
5056+
*/
5057+
ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
51555058

5156-
if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
5157-
{
5158-
syntaxError = true;
5159-
break;
5160-
}
5161-
if (tok1 == NULL)
5162-
continue;
5163-
5164-
if (strcmp(tok1, "restore_command") == 0)
5059+
for (item = head; item; item = item->next)
5060+
{
5061+
if (strcmp(item->name, "restore_command") == 0)
51655062
{
5166-
recoveryRestoreCommand = pstrdup(tok2);
5063+
recoveryRestoreCommand = pstrdup(item->value);
51675064
ereport(DEBUG2,
51685065
(errmsg("restore_command = '%s'",
51695066
recoveryRestoreCommand)));
51705067
}
5171-
else if (strcmp(tok1, "recovery_end_command") == 0)
5068+
else if (strcmp(item->name, "recovery_end_command") == 0)
51725069
{
5173-
recoveryEndCommand = pstrdup(tok2);
5070+
recoveryEndCommand = pstrdup(item->value);
51745071
ereport(DEBUG2,
51755072
(errmsg("recovery_end_command = '%s'",
51765073
recoveryEndCommand)));
51775074
}
5178-
else if (strcmp(tok1, "archive_cleanup_command") == 0)
5075+
else if (strcmp(item->name, "archive_cleanup_command") == 0)
51795076
{
5180-
archiveCleanupCommand = pstrdup(tok2);
5077+
archiveCleanupCommand = pstrdup(item->value);
51815078
ereport(DEBUG2,
51825079
(errmsg("archive_cleanup_command = '%s'",
51835080
archiveCleanupCommand)));
51845081
}
5185-
else if (strcmp(tok1, "recovery_target_timeline") == 0)
5082+
else if (strcmp(item->name, "recovery_target_timeline") == 0)
51865083
{
51875084
rtliGiven = true;
5188-
if (strcmp(tok2, "latest") == 0)
5085+
if (strcmp(item->value, "latest") == 0)
51895086
rtli = 0;
51905087
else
51915088
{
51925089
errno = 0;
5193-
rtli = (TimeLineID) strtoul(tok2, NULL, 0);
5090+
rtli = (TimeLineID) strtoul(item->value, NULL, 0);
51945091
if (errno == EINVAL || errno == ERANGE)
51955092
ereport(FATAL,
51965093
(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
5197-
tok2)));
5094+
item->value)));
51985095
}
51995096
if (rtli)
52005097
ereport(DEBUG2,
@@ -5203,20 +5100,20 @@ readRecoveryCommandFile(void)
52035100
ereport(DEBUG2,
52045101
(errmsg("recovery_target_timeline = latest")));
52055102
}
5206-
else if (strcmp(tok1, "recovery_target_xid") == 0)
5103+
else if (strcmp(item->name, "recovery_target_xid") == 0)
52075104
{
52085105
errno = 0;
5209-
recoveryTargetXid = (TransactionId) strtoul(tok2, NULL, 0);
5106+
recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
52105107
if (errno == EINVAL || errno == ERANGE)
52115108
ereport(FATAL,
52125109
(errmsg("recovery_target_xid is not a valid number: \"%s\"",
5213-
tok2)));
5110+
item->value)));
52145111
ereport(DEBUG2,
52155112
(errmsg("recovery_target_xid = %u",
52165113
recoveryTargetXid)));
52175114
recoveryTarget = RECOVERY_TARGET_XID;
52185115
}
5219-
else if (strcmp(tok1, "recovery_target_time") == 0)
5116+
else if (strcmp(item->name, "recovery_target_time") == 0)
52205117
{
52215118
/*
52225119
* if recovery_target_xid specified, then this overrides
@@ -5231,62 +5128,54 @@ readRecoveryCommandFile(void)
52315128
*/
52325129
recoveryTargetTime =
52335130
DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
5234-
CStringGetDatum(tok2),
5131+
CStringGetDatum(item->value),
52355132
ObjectIdGetDatum(InvalidOid),
52365133
Int32GetDatum(-1)));
52375134
ereport(DEBUG2,
52385135
(errmsg("recovery_target_time = '%s'",
52395136
timestamptz_to_str(recoveryTargetTime))));
52405137
}
5241-
else if (strcmp(tok1, "recovery_target_inclusive") == 0)
5138+
else if (strcmp(item->name, "recovery_target_inclusive") == 0)
52425139
{
52435140
/*
52445141
* does nothing if a recovery_target is not also set
52455142
*/
5246-
if (!parse_bool(tok2, &recoveryTargetInclusive))
5143+
if (!parse_bool(item->value, &recoveryTargetInclusive))
52475144
ereport(ERROR,
52485145
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
52495146
errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
52505147
ereport(DEBUG2,
5251-
(errmsg("recovery_target_inclusive = %s", tok2)));
5148+
(errmsg("recovery_target_inclusive = %s", item->value)));
52525149
}
5253-
else if (strcmp(tok1, "standby_mode") == 0)
5150+
else if (strcmp(item->name, "standby_mode") == 0)
52545151
{
5255-
if (!parse_bool(tok2, &StandbyMode))
5152+
if (!parse_bool(item->value, &StandbyMode))
52565153
ereport(ERROR,
52575154
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
52585155
errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
52595156
ereport(DEBUG2,
5260-
(errmsg("standby_mode = '%s'", tok2)));
5157+
(errmsg("standby_mode = '%s'", item->value)));
52615158
}
5262-
else if (strcmp(tok1, "primary_conninfo") == 0)
5159+
else if (strcmp(item->name, "primary_conninfo") == 0)
52635160
{
5264-
PrimaryConnInfo = pstrdup(tok2);
5161+
PrimaryConnInfo = pstrdup(item->value);
52655162
ereport(DEBUG2,
52665163
(errmsg("primary_conninfo = '%s'",
52675164
PrimaryConnInfo)));
52685165
}
5269-
else if (strcmp(tok1, "trigger_file") == 0)
5166+
else if (strcmp(item->name, "trigger_file") == 0)
52705167
{
5271-
TriggerFile = pstrdup(tok2);
5168+
TriggerFile = pstrdup(item->value);
52725169
ereport(DEBUG2,
52735170
(errmsg("trigger_file = '%s'",
52745171
TriggerFile)));
52755172
}
52765173
else
52775174
ereport(FATAL,
52785175
(errmsg("unrecognized recovery parameter \"%s\"",
5279-
tok1)));
5176+
item->name)));
52805177
}
52815178

5282-
FreeFile(fd);
5283-
5284-
if (syntaxError)
5285-
ereport(FATAL,
5286-
(errmsg("syntax error in recovery command file: %s",
5287-
cmdline),
5288-
errhint("Lines should have the format parameter = 'value'.")));
5289-
52905179
/*
52915180
* Check for compulsory parameters
52925181
*/
@@ -5332,6 +5221,9 @@ readRecoveryCommandFile(void)
53325221
recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
53335222
}
53345223
}
5224+
5225+
FreeConfigVariables(head);
5226+
FreeFile(fd);
53355227
}
53365228

53375229
/*

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