PostgreSQL Source Code  git master
percentrepl.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * percentrepl.c
4  * Common routines to replace percent placeholders in strings
5  *
6  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/common/percentrepl.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 
16 #ifndef FRONTEND
17 #include "postgres.h"
18 #else
19 #include "postgres_fe.h"
20 #include "common/logging.h"
21 #endif
22 
23 #include "common/percentrepl.h"
24 #include "lib/stringinfo.h"
25 
26 /*
27  * replace_percent_placeholders
28  *
29  * Replace percent-letter placeholders in input string with the supplied
30  * values. For example, to replace %f with foo and %b with bar, call
31  *
32  * replace_percent_placeholders(instr, "param_name", "bf", bar, foo);
33  *
34  * The return value is palloc'd.
35  *
36  * "%%" is replaced by a single "%".
37  *
38  * This throws an error for an unsupported placeholder or a "%" at the end of
39  * the input string.
40  *
41  * A value may be NULL. If the corresponding placeholder is found in the
42  * input string, it will be treated as if an unsupported placeholder was used.
43  * This allows callers to share a "letters" specification but vary the
44  * actually supported placeholders at run time.
45  *
46  * This functions is meant for cases where all the values are readily
47  * available or cheap to compute and most invocations will use most values
48  * (for example for archive_command). Also, it requires that all values are
49  * strings. It won't be a good match for things like log prefixes or prompts
50  * that use a mix of data types and any invocation will only use a few of the
51  * possible values.
52  *
53  * param_name is the name of the underlying GUC parameter, for error
54  * reporting. At the moment, this function is only used for GUC parameters.
55  * If other kinds of uses were added, the error reporting would need to be
56  * revised.
57  */
58 char *
59 replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...)
60 {
61  StringInfoData result;
62 
63  initStringInfo(&result);
64 
65  for (const char *sp = instr; *sp; sp++)
66  {
67  if (*sp == '%')
68  {
69  if (sp[1] == '%')
70  {
71  /* Convert %% to a single % */
72  sp++;
73  appendStringInfoChar(&result, *sp);
74  }
75  else if (sp[1] == '\0')
76  {
77  /* Incomplete escape sequence, expected a character afterward */
78 #ifdef FRONTEND
79  pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
80  pg_log_error_detail("String ends unexpectedly after escape character \"%%\".");
81  exit(1);
82 #else
83  ereport(ERROR,
84  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
85  errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
86  errdetail("String ends unexpectedly after escape character \"%%\"."));
87 #endif
88  }
89  else
90  {
91  /* Look up placeholder character */
92  bool found = false;
93  va_list ap;
94 
95  sp++;
96 
97  va_start(ap, letters);
98  for (const char *lp = letters; *lp; lp++)
99  {
100  char *val = va_arg(ap, char *);
101 
102  if (*sp == *lp)
103  {
104  if (val)
105  {
106  appendStringInfoString(&result, val);
107  found = true;
108  }
109  /* If val is NULL, we will report an error. */
110  break;
111  }
112  }
113  va_end(ap);
114  if (!found)
115  {
116  /* Unknown placeholder */
117 #ifdef FRONTEND
118  pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
119  pg_log_error_detail("String contains unexpected placeholder \"%%%c\".", *sp);
120  exit(1);
121 #else
122  ereport(ERROR,
123  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
124  errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
125  errdetail("String contains unexpected placeholder \"%%%c\".", *sp));
126 #endif
127  }
128  }
129  }
130  else
131  {
132  appendStringInfoChar(&result, *sp);
133  }
134  }
135 
136  return result.data;
137 }
int errdetail(const char *fmt,...)
Definition: elog.c:1201
int errcode(int sqlerrcode)
Definition: elog.c:855
int errmsg(const char *fmt,...)
Definition: elog.c:1068
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
long val
Definition: informix.c:670
va_end(args)
exit(1)
va_start(args, fmt)
#define pg_log_error(...)
Definition: logging.h:106
#define pg_log_error_detail(...)
Definition: logging.h:109
char * replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...)
Definition: percentrepl.c:59
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:194
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59