Coverage Report

Created: 2025-10-09 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/common/percentrepl.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * percentrepl.c
4
 *    Common routines to replace percent placeholders in strings
5
 *
6
 * Portions Copyright (c) 1996-2025, 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
0
{
61
0
  StringInfoData result;
62
63
0
  initStringInfo(&result);
64
65
0
  for (const char *sp = instr; *sp; sp++)
66
0
  {
67
0
    if (*sp == '%')
68
0
    {
69
0
      if (sp[1] == '%')
70
0
      {
71
        /* Convert %% to a single % */
72
0
        sp++;
73
0
        appendStringInfoChar(&result, *sp);
74
0
      }
75
0
      else if (sp[1] == '\0')
76
0
      {
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
0
        ereport(ERROR,
84
0
            errcode(ERRCODE_INVALID_PARAMETER_VALUE),
85
0
            errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
86
0
            errdetail("String ends unexpectedly after escape character \"%%\"."));
87
0
#endif
88
0
      }
89
0
      else
90
0
      {
91
        /* Look up placeholder character */
92
0
        bool    found = false;
93
0
        va_list   ap;
94
95
0
        sp++;
96
97
0
        va_start(ap, letters);
98
0
        for (const char *lp = letters; *lp; lp++)
99
0
        {
100
0
          char     *val = va_arg(ap, char *);
101
102
0
          if (*sp == *lp)
103
0
          {
104
0
            if (val)
105
0
            {
106
0
              appendStringInfoString(&result, val);
107
0
              found = true;
108
0
            }
109
            /* If val is NULL, we will report an error. */
110
0
            break;
111
0
          }
112
0
        }
113
0
        va_end(ap);
114
0
        if (!found)
115
0
        {
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
0
          ereport(ERROR,
123
0
              errcode(ERRCODE_INVALID_PARAMETER_VALUE),
124
0
              errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
125
0
              errdetail("String contains unexpected placeholder \"%%%c\".", *sp));
126
0
#endif
127
0
        }
128
0
      }
129
0
    }
130
0
    else
131
0
    {
132
0
      appendStringInfoChar(&result, *sp);
133
0
    }
134
0
  }
135
136
0
  return result.data;
137
0
}