cfparser.v3.patch
text/x-patch
Filename: cfparser.v3.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
Series: patch v3
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 46 | 0 |
| src/backend/utils/misc/guc-file.l | 3 | 0 |
| src/include/utils/guc.h | 21 | 1 |
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 5024,5200 **** str_time(pg_time_t tnow)
}
/*
- * Parse one line from recovery.conf. 'cmdline' is the raw line from the
- * file. If the line is parsed successfully, returns true, false indicates
- * syntax error. On success, *key_p and *value_p are set to the parameter
- * name and value on the line, respectively. If the line is an empty line,
- * consisting entirely of whitespace and comments, function returns true
- * and *keyp_p and *value_p are set to NULL.
- *
- * The pointers returned in *key_p and *value_p point to an internal buffer
- * that is valid only until the next call of parseRecoveryCommandFile().
- */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- char *ptr;
- char *bufp;
- char *key;
- char *value;
- static char *buf = NULL;
-
- *key_p = *value_p = NULL;
-
- /*
- * Allocate the buffer on first use. It's used to hold both the parameter
- * name and value.
- */
- if (buf == NULL)
- buf = malloc(MAXPGPATH + 1);
- bufp = buf;
-
- /* Skip any whitespace at the beginning of line */
- for (ptr = cmdline; *ptr; ptr++)
- {
- if (!isspace((unsigned char) *ptr))
- break;
- }
- /* Ignore empty lines */
- if (*ptr == '\0' || *ptr == '#')
- return true;
-
- /* Read the parameter name */
- key = bufp;
- while (*ptr && !isspace((unsigned char) *ptr) &&
- *ptr != '=' && *ptr != '\'')
- *(bufp++) = *(ptr++);
- *(bufp++) = '\0';
-
- /* Skip to the beginning quote of the parameter value */
- ptr = strchr(ptr, '\'');
- if (!ptr)
- return false;
- ptr++;
-
- /* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- value = bufp;
- for (;;)
- {
- if (*ptr == '\'')
- {
- ptr++;
- if (*ptr == '\'')
- *(bufp++) = '\'';
- else
- {
- /* end of parameter */
- *bufp = '\0';
- break;
- }
- }
- else if (*ptr == '\0')
- return false; /* unterminated quoted string */
- else
- *(bufp++) = *ptr;
-
- ptr++;
- }
- *(bufp++) = '\0';
-
- /* Check that there's no garbage after the value */
- while (*ptr)
- {
- if (*ptr == '#')
- break;
- if (!isspace((unsigned char) *ptr))
- return false;
- ptr++;
- }
-
- /* Success! */
- *key_p = key;
- *value_p = value;
- return true;
- }
-
- /*
* See if there is a recovery command file (recovery.conf), and if so
* read in parameters for archive recovery and XLOG streaming.
*
! * XXX longer term intention is to expand this to
! * cater for additional parameters and controls
! * possibly use a flex lexer similar to the GUC one
*/
static void
readRecoveryCommandFile(void)
{
FILE *fd;
- char cmdline[MAXPGPATH];
TimeLineID rtli = 0;
bool rtliGiven = false;
! bool syntaxError = false;
fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
! if (fd == NULL)
! {
! if (errno == ENOENT)
! return; /* not there, so no archive recovery */
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not open recovery command file \"%s\": %m",
! RECOVERY_COMMAND_FILE)));
! }
/*
! * Parse the file...
*/
! while (fgets(cmdline, sizeof(cmdline), fd) != NULL)
! {
! char *tok1;
! char *tok2;
!
! if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
! {
! syntaxError = true;
! break;
! }
! if (tok1 == NULL)
! continue;
! if (strcmp(tok1, "restore_command") == 0)
{
! recoveryRestoreCommand = pstrdup(tok2);
ereport(DEBUG2,
(errmsg("restore_command = '%s'",
recoveryRestoreCommand)));
}
! else if (strcmp(tok1, "recovery_end_command") == 0)
{
! recoveryEndCommand = pstrdup(tok2);
ereport(DEBUG2,
(errmsg("recovery_end_command = '%s'",
recoveryEndCommand)));
}
! else if (strcmp(tok1, "archive_cleanup_command") == 0)
{
! archiveCleanupCommand = pstrdup(tok2);
ereport(DEBUG2,
(errmsg("archive_cleanup_command = '%s'",
archiveCleanupCommand)));
}
! else if (strcmp(tok1, "recovery_target_timeline") == 0)
{
rtliGiven = true;
! if (strcmp(tok2, "latest") == 0)
rtli = 0;
else
{
errno = 0;
! rtli = (TimeLineID) strtoul(tok2, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
ereport(FATAL,
(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! tok2)));
}
if (rtli)
ereport(DEBUG2,
--- 5024,5096 ----
}
/*
* See if there is a recovery command file (recovery.conf), and if so
* read in parameters for archive recovery and XLOG streaming.
*
! * The file is parsed using the main configuration parser.
*/
static void
readRecoveryCommandFile(void)
{
FILE *fd;
TimeLineID rtli = 0;
bool rtliGiven = false;
! ConfigNameValuePair item, head, tail;
+ /*
+ * See if recovery.conf file is present
+ */
fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
! if (!fd)
! return; /* it's not there, all is fine */
!
! FreeFile(fd);
/*
! * Pretend we're reading recovery.conf from postgresql.conf in this
! * version of the patch, just so that we avoid either finding the
! * data_directory from xlog.c or changing the APIs.
*/
! if (!ParseConfigFile(RECOVERY_COMMAND_FILE, ConfigFileName, 1,
! PGC_INTERNAL, FATAL, &head, &tail))
! goto cleanup_list;
! for (item = head; item; item = item->next)
! {
! if (strcmp(item->name, "restore_command") == 0)
{
! recoveryRestoreCommand = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("restore_command = '%s'",
recoveryRestoreCommand)));
}
! else if (strcmp(item->name, "recovery_end_command") == 0)
{
! recoveryEndCommand = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("recovery_end_command = '%s'",
recoveryEndCommand)));
}
! else if (strcmp(item->name, "archive_cleanup_command") == 0)
{
! archiveCleanupCommand = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("archive_cleanup_command = '%s'",
archiveCleanupCommand)));
}
! else if (strcmp(item->name, "recovery_target_timeline") == 0)
{
rtliGiven = true;
! if (strcmp(item->value, "latest") == 0)
rtli = 0;
else
{
errno = 0;
! rtli = (TimeLineID) strtoul(item->value, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
ereport(FATAL,
(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! item->value)));
}
if (rtli)
ereport(DEBUG2,
***************
*** 5203,5222 **** readRecoveryCommandFile(void)
ereport(DEBUG2,
(errmsg("recovery_target_timeline = latest")));
}
! else if (strcmp(tok1, "recovery_target_xid") == 0)
{
errno = 0;
! recoveryTargetXid = (TransactionId) strtoul(tok2, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
ereport(FATAL,
(errmsg("recovery_target_xid is not a valid number: \"%s\"",
! tok2)));
ereport(DEBUG2,
(errmsg("recovery_target_xid = %u",
recoveryTargetXid)));
recoveryTarget = RECOVERY_TARGET_XID;
}
! else if (strcmp(tok1, "recovery_target_time") == 0)
{
/*
* if recovery_target_xid specified, then this overrides
--- 5099,5118 ----
ereport(DEBUG2,
(errmsg("recovery_target_timeline = latest")));
}
! else if (strcmp(item->name, "recovery_target_xid") == 0)
{
errno = 0;
! recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
ereport(FATAL,
(errmsg("recovery_target_xid is not a valid number: \"%s\"",
! item->value)));
ereport(DEBUG2,
(errmsg("recovery_target_xid = %u",
recoveryTargetXid)));
recoveryTarget = RECOVERY_TARGET_XID;
}
! else if (strcmp(item->name, "recovery_target_time") == 0)
{
/*
* if recovery_target_xid specified, then this overrides
***************
*** 5231,5274 **** readRecoveryCommandFile(void)
*/
recoveryTargetTime =
DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! CStringGetDatum(tok2),
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(-1)));
ereport(DEBUG2,
(errmsg("recovery_target_time = '%s'",
timestamptz_to_str(recoveryTargetTime))));
}
! else if (strcmp(tok1, "recovery_target_inclusive") == 0)
{
/*
* does nothing if a recovery_target is not also set
*/
! if (!parse_bool(tok2, &recoveryTargetInclusive))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
ereport(DEBUG2,
! (errmsg("recovery_target_inclusive = %s", tok2)));
}
! else if (strcmp(tok1, "standby_mode") == 0)
{
! if (!parse_bool(tok2, &StandbyMode))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
ereport(DEBUG2,
! (errmsg("standby_mode = '%s'", tok2)));
}
! else if (strcmp(tok1, "primary_conninfo") == 0)
{
! PrimaryConnInfo = pstrdup(tok2);
ereport(DEBUG2,
(errmsg("primary_conninfo = '%s'",
PrimaryConnInfo)));
}
! else if (strcmp(tok1, "trigger_file") == 0)
{
! TriggerFile = pstrdup(tok2);
ereport(DEBUG2,
(errmsg("trigger_file = '%s'",
TriggerFile)));
--- 5127,5170 ----
*/
recoveryTargetTime =
DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! CStringGetDatum(item->value),
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(-1)));
ereport(DEBUG2,
(errmsg("recovery_target_time = '%s'",
timestamptz_to_str(recoveryTargetTime))));
}
! else if (strcmp(item->name, "recovery_target_inclusive") == 0)
{
/*
* does nothing if a recovery_target is not also set
*/
! if (!parse_bool(item->value, &recoveryTargetInclusive))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
ereport(DEBUG2,
! (errmsg("recovery_target_inclusive = %s", item->value)));
}
! else if (strcmp(item->name, "standby_mode") == 0)
{
! if (!parse_bool(item->value, &StandbyMode))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
ereport(DEBUG2,
! (errmsg("standby_mode = '%s'", item->value)));
}
! else if (strcmp(item->name, "primary_conninfo") == 0)
{
! PrimaryConnInfo = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("primary_conninfo = '%s'",
PrimaryConnInfo)));
}
! else if (strcmp(item->name, "trigger_file") == 0)
{
! TriggerFile = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("trigger_file = '%s'",
TriggerFile)));
***************
*** 5276,5292 **** readRecoveryCommandFile(void)
else
ereport(FATAL,
(errmsg("unrecognized recovery parameter \"%s\"",
! tok1)));
}
- FreeFile(fd);
-
- if (syntaxError)
- ereport(FATAL,
- (errmsg("syntax error in recovery command file: %s",
- cmdline),
- errhint("Lines should have the format parameter = 'value'.")));
-
/*
* Check for compulsory parameters
*/
--- 5172,5180 ----
else
ereport(FATAL,
(errmsg("unrecognized recovery parameter \"%s\"",
! item->name)));
}
/*
* Check for compulsory parameters
*/
***************
*** 5332,5337 **** readRecoveryCommandFile(void)
--- 5220,5228 ----
recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
}
}
+
+ cleanup_list:
+ free_name_value_list(head);
}
/*
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 35,59 **** enum {
GUC_ERROR = 100
};
- struct name_value_pair
- {
- char *name;
- char *value;
- char *filename;
- int sourceline;
- struct name_value_pair *next;
- };
-
static unsigned int ConfigFileLineno;
/* flex fails to supply a prototype for yylex, so provide one */
int GUC_yylex(void);
- static bool ParseConfigFile(const char *config_file, const char *calling_file,
- int depth, GucContext context, int elevel,
- struct name_value_pair **head_p,
- struct name_value_pair **tail_p);
- static void free_name_value_list(struct name_value_pair * list);
static char *GUC_scanstr(const char *s);
%}
--- 35,45 ----
***************
*** 118,124 **** void
ProcessConfigFile(GucContext context)
{
int elevel;
! struct name_value_pair *item, *head, *tail;
char *cvc = NULL;
struct config_string *cvc_struct;
const char *envvar;
--- 104,110 ----
ProcessConfigFile(GucContext context)
{
int elevel;
! ConfigNameValuePair item, head, tail;
char *cvc = NULL;
struct config_string *cvc_struct;
const char *envvar;
***************
*** 387,393 **** ProcessConfigFile(GucContext context)
* where an error will lead to immediate process exit anyway; so there is
* no point in contorting the code so it can clean up nicely.
*/
! static bool
ParseConfigFile(const char *config_file, const char *calling_file,
int depth, GucContext context, int elevel,
struct name_value_pair **head_p,
--- 373,379 ----
* where an error will lead to immediate process exit anyway; so there is
* no point in contorting the code so it can clean up nicely.
*/
! bool
ParseConfigFile(const char *config_file, const char *calling_file,
int depth, GucContext context, int elevel,
struct name_value_pair **head_p,
***************
*** 590,596 **** cleanup_exit:
/*
* Free a list of name/value pairs, including the names and the values
*/
! static void
free_name_value_list(struct name_value_pair *list)
{
struct name_value_pair *item;
--- 576,582 ----
/*
* Free a list of name/value pairs, including the names and the values
*/
! void
free_name_value_list(struct name_value_pair *list)
{
struct name_value_pair *item;
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 17,23 ****
#include "tcop/dest.h"
#include "utils/array.h"
-
/*
* Certain options can only be set at certain times. The rules are
* like this:
--- 17,22 ----
***************
*** 97,102 **** typedef enum
--- 96,122 ----
} GucSource;
/*
+ * Parsing the configuation file will return a list of name-value pairs
+ */
+ struct name_value_pair
+ {
+ char *name;
+ char *value;
+ char *filename;
+ int sourceline;
+ struct name_value_pair *next;
+ } name_value_pair;
+
+ typedef struct name_value_pair *ConfigNameValuePair;
+
+ bool ParseConfigFile(const char *config_file, const char *calling_file,
+ int depth, GucContext context, int elevel,
+ ConfigNameValuePair *head_p,
+ ConfigNameValuePair *tail_p);
+
+ void free_name_value_list(struct name_value_pair * list);
+
+ /*
* Enum values are made up of an array of name-value pairs
*/
struct config_enum_entry