Add error pattern checks for some TAP tests for non-existing objects
[pgsql.git] / src / bin / pg_basebackup / astreamer_inject.c
blob15334e458ad1e581be62d9a828659e8458f539a3
1 /*-------------------------------------------------------------------------
3 * astreamer_inject.c
5 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * IDENTIFICATION
8 * src/bin/pg_basebackup/astreamer_inject.c
9 *-------------------------------------------------------------------------
12 #include "postgres_fe.h"
14 #include "astreamer_inject.h"
15 #include "common/file_perm.h"
16 #include "common/logging.h"
18 typedef struct astreamer_recovery_injector
20 astreamer base;
21 bool skip_file;
22 bool is_recovery_guc_supported;
23 bool is_postgresql_auto_conf;
24 bool found_postgresql_auto_conf;
25 PQExpBuffer recoveryconfcontents;
26 astreamer_member member;
27 } astreamer_recovery_injector;
29 static void astreamer_recovery_injector_content(astreamer *streamer,
30 astreamer_member *member,
31 const char *data, int len,
32 astreamer_archive_context context);
33 static void astreamer_recovery_injector_finalize(astreamer *streamer);
34 static void astreamer_recovery_injector_free(astreamer *streamer);
36 static const astreamer_ops astreamer_recovery_injector_ops = {
37 .content = astreamer_recovery_injector_content,
38 .finalize = astreamer_recovery_injector_finalize,
39 .free = astreamer_recovery_injector_free
43 * Create a astreamer that can edit recoverydata into an archive stream.
45 * The input should be a series of typed chunks (not ASTREAMER_UNKNOWN) as
46 * per the conventions described in astreamer.h; the chunks forwarded to
47 * the next astreamer will be similarly typed, but the
48 * ASTREAMER_MEMBER_HEADER chunks may be zero-length in cases where we've
49 * edited the archive stream.
51 * Our goal is to do one of the following three things with the content passed
52 * via recoveryconfcontents: (1) if is_recovery_guc_supported is false, then
53 * put the content into recovery.conf, replacing any existing archive member
54 * by that name; (2) if is_recovery_guc_supported is true and
55 * postgresql.auto.conf exists in the archive, then append the content
56 * provided to the existing file; and (3) if is_recovery_guc_supported is
57 * true but postgresql.auto.conf does not exist in the archive, then create
58 * it with the specified content.
60 * In addition, if is_recovery_guc_supported is true, then we create a
61 * zero-length standby.signal file, dropping any file with that name from
62 * the archive.
64 astreamer *
65 astreamer_recovery_injector_new(astreamer *next,
66 bool is_recovery_guc_supported,
67 PQExpBuffer recoveryconfcontents)
69 astreamer_recovery_injector *streamer;
71 streamer = palloc0(sizeof(astreamer_recovery_injector));
72 *((const astreamer_ops **) &streamer->base.bbs_ops) =
73 &astreamer_recovery_injector_ops;
74 streamer->base.bbs_next = next;
75 streamer->is_recovery_guc_supported = is_recovery_guc_supported;
76 streamer->recoveryconfcontents = recoveryconfcontents;
78 return &streamer->base;
82 * Handle each chunk of tar content while injecting recovery configuration.
84 static void
85 astreamer_recovery_injector_content(astreamer *streamer,
86 astreamer_member *member,
87 const char *data, int len,
88 astreamer_archive_context context)
90 astreamer_recovery_injector *mystreamer;
92 mystreamer = (astreamer_recovery_injector *) streamer;
93 Assert(member != NULL || context == ASTREAMER_ARCHIVE_TRAILER);
95 switch (context)
97 case ASTREAMER_MEMBER_HEADER:
98 /* Must copy provided data so we have the option to modify it. */
99 memcpy(&mystreamer->member, member, sizeof(astreamer_member));
102 * On v12+, skip standby.signal and edit postgresql.auto.conf; on
103 * older versions, skip recovery.conf.
105 if (mystreamer->is_recovery_guc_supported)
107 mystreamer->skip_file =
108 (strcmp(member->pathname, "standby.signal") == 0);
109 mystreamer->is_postgresql_auto_conf =
110 (strcmp(member->pathname, "postgresql.auto.conf") == 0);
111 if (mystreamer->is_postgresql_auto_conf)
113 /* Remember we saw it so we don't add it again. */
114 mystreamer->found_postgresql_auto_conf = true;
116 /* Increment length by data to be injected. */
117 mystreamer->member.size +=
118 mystreamer->recoveryconfcontents->len;
121 * Zap data and len because the archive header is no
122 * longer valid; some subsequent astreamer must regenerate
123 * it if it's necessary.
125 data = NULL;
126 len = 0;
129 else
130 mystreamer->skip_file =
131 (strcmp(member->pathname, "recovery.conf") == 0);
133 /* Do not forward if the file is to be skipped. */
134 if (mystreamer->skip_file)
135 return;
136 break;
138 case ASTREAMER_MEMBER_CONTENTS:
139 /* Do not forward if the file is to be skipped. */
140 if (mystreamer->skip_file)
141 return;
142 break;
144 case ASTREAMER_MEMBER_TRAILER:
145 /* Do not forward it the file is to be skipped. */
146 if (mystreamer->skip_file)
147 return;
149 /* Append provided content to whatever we already sent. */
150 if (mystreamer->is_postgresql_auto_conf)
151 astreamer_content(mystreamer->base.bbs_next, member,
152 mystreamer->recoveryconfcontents->data,
153 mystreamer->recoveryconfcontents->len,
154 ASTREAMER_MEMBER_CONTENTS);
155 break;
157 case ASTREAMER_ARCHIVE_TRAILER:
158 if (mystreamer->is_recovery_guc_supported)
161 * If we didn't already find (and thus modify)
162 * postgresql.auto.conf, inject it as an additional archive
163 * member now.
165 if (!mystreamer->found_postgresql_auto_conf)
166 astreamer_inject_file(mystreamer->base.bbs_next,
167 "postgresql.auto.conf",
168 mystreamer->recoveryconfcontents->data,
169 mystreamer->recoveryconfcontents->len);
171 /* Inject empty standby.signal file. */
172 astreamer_inject_file(mystreamer->base.bbs_next,
173 "standby.signal", "", 0);
175 else
177 /* Inject recovery.conf file with specified contents. */
178 astreamer_inject_file(mystreamer->base.bbs_next,
179 "recovery.conf",
180 mystreamer->recoveryconfcontents->data,
181 mystreamer->recoveryconfcontents->len);
184 /* Nothing to do here. */
185 break;
187 default:
188 /* Shouldn't happen. */
189 pg_fatal("unexpected state while injecting recovery settings");
192 astreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
193 data, len, context);
197 * End-of-stream processing for this astreamer.
199 static void
200 astreamer_recovery_injector_finalize(astreamer *streamer)
202 astreamer_finalize(streamer->bbs_next);
206 * Free memory associated with this astreamer.
208 static void
209 astreamer_recovery_injector_free(astreamer *streamer)
211 astreamer_free(streamer->bbs_next);
212 pfree(streamer);
216 * Inject a member into the archive with specified contents.
218 void
219 astreamer_inject_file(astreamer *streamer, char *pathname, char *data,
220 int len)
222 astreamer_member member;
224 strlcpy(member.pathname, pathname, MAXPGPATH);
225 member.size = len;
226 member.mode = pg_file_create_mode;
227 member.is_directory = false;
228 member.is_link = false;
229 member.linktarget[0] = '\0';
232 * There seems to be no principled argument for these values, but they are
233 * what PostgreSQL has historically used.
235 member.uid = 04000;
236 member.gid = 02000;
239 * We don't know here how to generate valid member headers and trailers
240 * for the archiving format in use, so if those are needed, some successor
241 * astreamer will have to generate them using the data from 'member'.
243 astreamer_content(streamer, &member, NULL, 0,
244 ASTREAMER_MEMBER_HEADER);
245 astreamer_content(streamer, &member, data, len,
246 ASTREAMER_MEMBER_CONTENTS);
247 astreamer_content(streamer, &member, NULL, 0,
248 ASTREAMER_MEMBER_TRAILER);