PostgreSQL Source Code  git master
reinit.c File Reference
#include "postgres.h"
#include <unistd.h>
#include "common/relpath.h"
#include "postmaster/startup.h"
#include "storage/copydir.h"
#include "storage/fd.h"
#include "storage/reinit.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
Include dependency graph for reinit.c:

Go to the source code of this file.

Data Structures

struct  unlogged_relation_entry
 

Functions

static void ResetUnloggedRelationsInTablespaceDir (const char *tsdirname, int op)
 
static void ResetUnloggedRelationsInDbspaceDir (const char *dbspacedirname, int op)
 
void ResetUnloggedRelations (int op)
 
bool parse_filename_for_nontemp_relation (const char *name, int *oidchars, ForkNumber *fork)
 

Function Documentation

◆ parse_filename_for_nontemp_relation()

bool parse_filename_for_nontemp_relation ( const char *  name,
int *  oidchars,
ForkNumber fork 
)

Definition at line 381 of file reinit.c.

383 {
384  int pos;
385 
386  /* Look for a non-empty string of digits (that isn't too long). */
387  for (pos = 0; isdigit((unsigned char) name[pos]); ++pos)
388  ;
389  if (pos == 0 || pos > OIDCHARS)
390  return false;
391  *oidchars = pos;
392 
393  /* Check for a fork name. */
394  if (name[pos] != '_')
395  *fork = MAIN_FORKNUM;
396  else
397  {
398  int forkchar;
399 
400  forkchar = forkname_chars(&name[pos + 1], fork);
401  if (forkchar <= 0)
402  return false;
403  pos += forkchar + 1;
404  }
405 
406  /* Check for a segment number. */
407  if (name[pos] == '.')
408  {
409  int segchar;
410 
411  for (segchar = 1; isdigit((unsigned char) name[pos + segchar]); ++segchar)
412  ;
413  if (segchar <= 1)
414  return false;
415  pos += segchar;
416  }
417 
418  /* Now we should be at the end. */
419  if (name[pos] != '\0')
420  return false;
421  return true;
422 }
const char * name
Definition: encode.c:561
int forkname_chars(const char *str, ForkNumber *fork)
Definition: relpath.c:81
@ MAIN_FORKNUM
Definition: relpath.h:43
#define OIDCHARS
Definition: relpath.h:30

References forkname_chars(), MAIN_FORKNUM, name, and OIDCHARS.

Referenced by ResetUnloggedRelationsInDbspaceDir(), and sendDir().

◆ ResetUnloggedRelations()

void ResetUnloggedRelations ( int  op)

Definition at line 47 of file reinit.c.

48 {
49  char temp_path[MAXPGPATH + 10 + sizeof(TABLESPACE_VERSION_DIRECTORY)];
50  DIR *spc_dir;
51  struct dirent *spc_de;
52  MemoryContext tmpctx,
53  oldctx;
54 
55  /* Log it. */
56  elog(DEBUG1, "resetting unlogged relations: cleanup %d init %d",
57  (op & UNLOGGED_RELATION_CLEANUP) != 0,
58  (op & UNLOGGED_RELATION_INIT) != 0);
59 
60  /*
61  * Just to be sure we don't leak any memory, let's create a temporary
62  * memory context for this operation.
63  */
65  "ResetUnloggedRelations",
67  oldctx = MemoryContextSwitchTo(tmpctx);
68 
69  /* Prepare to report progress resetting unlogged relations. */
71 
72  /*
73  * First process unlogged files in pg_default ($PGDATA/base)
74  */
76 
77  /*
78  * Cycle through directories for all non-default tablespaces.
79  */
80  spc_dir = AllocateDir("pg_tblspc");
81 
82  while ((spc_de = ReadDir(spc_dir, "pg_tblspc")) != NULL)
83  {
84  if (strcmp(spc_de->d_name, ".") == 0 ||
85  strcmp(spc_de->d_name, "..") == 0)
86  continue;
87 
88  snprintf(temp_path, sizeof(temp_path), "pg_tblspc/%s/%s",
91  }
92 
93  FreeDir(spc_dir);
94 
95  /*
96  * Restore memory context.
97  */
98  MemoryContextSwitchTo(oldctx);
99  MemoryContextDelete(tmpctx);
100 }
void begin_startup_progress_phase(void)
Definition: startup.c:315
#define DEBUG1
Definition: elog.h:24
#define elog(elevel,...)
Definition: elog.h:218
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2788
int FreeDir(DIR *dir)
Definition: fd.c:2840
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2722
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:195
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define MAXPGPATH
#define snprintf
Definition: port.h:222
static void ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, int op)
Definition: reinit.c:106
#define UNLOGGED_RELATION_INIT
Definition: reinit.h:26
#define UNLOGGED_RELATION_CLEANUP
Definition: reinit.h:25
#define TABLESPACE_VERSION_DIRECTORY
Definition: relpath.h:26
Definition: dirent.c:26
Definition: dirent.h:10
char d_name[MAX_PATH]
Definition: dirent.h:15

References AllocateDir(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, begin_startup_progress_phase(), CurrentMemoryContext, dirent::d_name, DEBUG1, elog, FreeDir(), MAXPGPATH, MemoryContextDelete(), MemoryContextSwitchTo(), ReadDir(), ResetUnloggedRelationsInTablespaceDir(), snprintf, TABLESPACE_VERSION_DIRECTORY, UNLOGGED_RELATION_CLEANUP, and UNLOGGED_RELATION_INIT.

Referenced by StartupXLOG().

◆ ResetUnloggedRelationsInDbspaceDir()

static void ResetUnloggedRelationsInDbspaceDir ( const char *  dbspacedirname,
int  op 
)
static

Definition at line 161 of file reinit.c.

162 {
163  DIR *dbspace_dir;
164  struct dirent *de;
165  char rm_path[MAXPGPATH * 2];
166 
167  /* Caller must specify at least one operation. */
169 
170  /*
171  * Cleanup is a two-pass operation. First, we go through and identify all
172  * the files with init forks. Then, we go through again and nuke
173  * everything with the same OID except the init fork.
174  */
175  if ((op & UNLOGGED_RELATION_CLEANUP) != 0)
176  {
177  HTAB *hash;
178  HASHCTL ctl;
179 
180  /*
181  * It's possible that someone could create a ton of unlogged relations
182  * in the same database & tablespace, so we'd better use a hash table
183  * rather than an array or linked list to keep track of which files
184  * need to be reset. Otherwise, this cleanup operation would be
185  * O(n^2).
186  */
187  ctl.keysize = sizeof(Oid);
188  ctl.entrysize = sizeof(unlogged_relation_entry);
190  hash = hash_create("unlogged relation OIDs", 32, &ctl,
192 
193  /* Scan the directory. */
194  dbspace_dir = AllocateDir(dbspacedirname);
195  while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
196  {
197  ForkNumber forkNum;
198  int oidchars;
200 
201  /* Skip anything that doesn't look like a relation data file. */
202  if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
203  &forkNum))
204  continue;
205 
206  /* Also skip it unless this is the init fork. */
207  if (forkNum != INIT_FORKNUM)
208  continue;
209 
210  /*
211  * Put the OID portion of the name into the hash table, if it
212  * isn't already.
213  */
214  ent.reloid = atooid(de->d_name);
215  (void) hash_search(hash, &ent, HASH_ENTER, NULL);
216  }
217 
218  /* Done with the first pass. */
219  FreeDir(dbspace_dir);
220 
221  /*
222  * If we didn't find any init forks, there's no point in continuing;
223  * we can bail out now.
224  */
225  if (hash_get_num_entries(hash) == 0)
226  {
228  return;
229  }
230 
231  /*
232  * Now, make a second pass and remove anything that matches.
233  */
234  dbspace_dir = AllocateDir(dbspacedirname);
235  while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
236  {
237  ForkNumber forkNum;
238  int oidchars;
240 
241  /* Skip anything that doesn't look like a relation data file. */
242  if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
243  &forkNum))
244  continue;
245 
246  /* We never remove the init fork. */
247  if (forkNum == INIT_FORKNUM)
248  continue;
249 
250  /*
251  * See whether the OID portion of the name shows up in the hash
252  * table. If so, nuke it!
253  */
254  ent.reloid = atooid(de->d_name);
255  if (hash_search(hash, &ent, HASH_FIND, NULL))
256  {
257  snprintf(rm_path, sizeof(rm_path), "%s/%s",
258  dbspacedirname, de->d_name);
259  if (unlink(rm_path) < 0)
260  ereport(ERROR,
262  errmsg("could not remove file \"%s\": %m",
263  rm_path)));
264  else
265  elog(DEBUG2, "unlinked file \"%s\"", rm_path);
266  }
267  }
268 
269  /* Cleanup is complete. */
270  FreeDir(dbspace_dir);
272  }
273 
274  /*
275  * Initialization happens after cleanup is complete: we copy each init
276  * fork file to the corresponding main fork file. Note that if we are
277  * asked to do both cleanup and init, we may never get here: if the
278  * cleanup code determines that there are no init forks in this dbspace,
279  * it will return before we get to this point.
280  */
281  if ((op & UNLOGGED_RELATION_INIT) != 0)
282  {
283  /* Scan the directory. */
284  dbspace_dir = AllocateDir(dbspacedirname);
285  while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
286  {
287  ForkNumber forkNum;
288  int oidchars;
289  char oidbuf[OIDCHARS + 1];
290  char srcpath[MAXPGPATH * 2];
291  char dstpath[MAXPGPATH];
292 
293  /* Skip anything that doesn't look like a relation data file. */
294  if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
295  &forkNum))
296  continue;
297 
298  /* Also skip it unless this is the init fork. */
299  if (forkNum != INIT_FORKNUM)
300  continue;
301 
302  /* Construct source pathname. */
303  snprintf(srcpath, sizeof(srcpath), "%s/%s",
304  dbspacedirname, de->d_name);
305 
306  /* Construct destination pathname. */
307  memcpy(oidbuf, de->d_name, oidchars);
308  oidbuf[oidchars] = '\0';
309  snprintf(dstpath, sizeof(dstpath), "%s/%s%s",
310  dbspacedirname, oidbuf, de->d_name + oidchars + 1 +
311  strlen(forkNames[INIT_FORKNUM]));
312 
313  /* OK, we're ready to perform the actual copy. */
314  elog(DEBUG2, "copying %s to %s", srcpath, dstpath);
315  copy_file(srcpath, dstpath);
316  }
317 
318  FreeDir(dbspace_dir);
319 
320  /*
321  * copy_file() above has already called pg_flush_data() on the files
322  * it created. Now we need to fsync those files, because a checkpoint
323  * won't do it for us while we're in recovery. We do this in a
324  * separate pass to allow the kernel to perform all the flushes
325  * (especially the metadata ones) at once.
326  */
327  dbspace_dir = AllocateDir(dbspacedirname);
328  while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
329  {
330  ForkNumber forkNum;
331  int oidchars;
332  char oidbuf[OIDCHARS + 1];
333  char mainpath[MAXPGPATH];
334 
335  /* Skip anything that doesn't look like a relation data file. */
336  if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
337  &forkNum))
338  continue;
339 
340  /* Also skip it unless this is the init fork. */
341  if (forkNum != INIT_FORKNUM)
342  continue;
343 
344  /* Construct main fork pathname. */
345  memcpy(oidbuf, de->d_name, oidchars);
346  oidbuf[oidchars] = '\0';
347  snprintf(mainpath, sizeof(mainpath), "%s/%s%s",
348  dbspacedirname, oidbuf, de->d_name + oidchars + 1 +
349  strlen(forkNames[INIT_FORKNUM]));
350 
351  fsync_fname(mainpath, false);
352  }
353 
354  FreeDir(dbspace_dir);
355 
356  /*
357  * Lastly, fsync the database directory itself, ensuring the
358  * filesystem remembers the file creations and deletions we've done.
359  * We don't bother with this during a call that does only
360  * UNLOGGED_RELATION_CLEANUP, because if recovery crashes before we
361  * get to doing UNLOGGED_RELATION_INIT, we'll redo the cleanup step
362  * too at the next startup attempt.
363  */
364  fsync_fname(dbspacedirname, true);
365  }
366 }
void copy_file(char *fromfile, char *tofile)
Definition: copydir.c:127
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:862
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:349
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
int errcode_for_file_access(void)
Definition: elog.c:721
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define DEBUG2
Definition: elog.h:23
#define ERROR
Definition: elog.h:33
#define ereport(elevel,...)
Definition: elog.h:143
void fsync_fname(const char *fname, bool isdir)
Definition: fd.c:673
static char dstpath[MAXPGPATH]
Definition: file_ops.c:32
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
Assert(fmt[strlen(fmt) - 1] !='\n')
unsigned int Oid
Definition: postgres_ext.h:31
#define atooid(x)
Definition: postgres_ext.h:42
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:715
bool parse_filename_for_nontemp_relation(const char *name, int *oidchars, ForkNumber *fork)
Definition: reinit.c:381
const char *const forkNames[]
Definition: relpath.c:33
ForkNumber
Definition: relpath.h:41
@ INIT_FORKNUM
Definition: relpath.h:46
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
Definition: dynahash.c:220
Definition: reinit.c:33
Oid reloid
Definition: reinit.c:34

References AllocateDir(), Assert(), atooid, copy_file(), CurrentMemoryContext, dirent::d_name, DEBUG2, dstpath, elog, HASHCTL::entrysize, ereport, errcode_for_file_access(), errmsg(), ERROR, forkNames, FreeDir(), fsync_fname(), hash(), HASH_BLOBS, HASH_CONTEXT, hash_create(), hash_destroy(), HASH_ELEM, HASH_ENTER, HASH_FIND, hash_get_num_entries(), hash_search(), HASHCTL::hcxt, INIT_FORKNUM, HASHCTL::keysize, MAXPGPATH, OIDCHARS, parse_filename_for_nontemp_relation(), ReadDir(), unlogged_relation_entry::reloid, snprintf, UNLOGGED_RELATION_CLEANUP, and UNLOGGED_RELATION_INIT.

Referenced by ResetUnloggedRelationsInTablespaceDir().

◆ ResetUnloggedRelationsInTablespaceDir()

static void ResetUnloggedRelationsInTablespaceDir ( const char *  tsdirname,
int  op 
)
static

Definition at line 106 of file reinit.c.

107 {
108  DIR *ts_dir;
109  struct dirent *de;
110  char dbspace_path[MAXPGPATH * 2];
111 
112  ts_dir = AllocateDir(tsdirname);
113 
114  /*
115  * If we get ENOENT on a tablespace directory, log it and return. This
116  * can happen if a previous DROP TABLESPACE crashed between removing the
117  * tablespace directory and removing the symlink in pg_tblspc. We don't
118  * really want to prevent database startup in that scenario, so let it
119  * pass instead. Any other type of error will be reported by ReadDir
120  * (causing a startup failure).
121  */
122  if (ts_dir == NULL && errno == ENOENT)
123  {
124  ereport(LOG,
126  errmsg("could not open directory \"%s\": %m",
127  tsdirname)));
128  return;
129  }
130 
131  while ((de = ReadDir(ts_dir, tsdirname)) != NULL)
132  {
133  /*
134  * We're only interested in the per-database directories, which have
135  * numeric names. Note that this code will also (properly) ignore "."
136  * and "..".
137  */
138  if (strspn(de->d_name, "0123456789") != strlen(de->d_name))
139  continue;
140 
141  snprintf(dbspace_path, sizeof(dbspace_path), "%s/%s",
142  tsdirname, de->d_name);
143 
144  if (op & UNLOGGED_RELATION_INIT)
145  ereport_startup_progress("resetting unlogged relations (init), elapsed time: %ld.%02d s, current path: %s",
146  dbspace_path);
147  else if (op & UNLOGGED_RELATION_CLEANUP)
148  ereport_startup_progress("resetting unlogged relations (cleanup), elapsed time: %ld.%02d s, current path: %s",
149  dbspace_path);
150 
151  ResetUnloggedRelationsInDbspaceDir(dbspace_path, op);
152  }
153 
154  FreeDir(ts_dir);
155 }
#define LOG
Definition: elog.h:25
static void ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
Definition: reinit.c:161
#define ereport_startup_progress(msg,...)
Definition: startup.h:18

References AllocateDir(), dirent::d_name, ereport, ereport_startup_progress, errcode_for_file_access(), errmsg(), FreeDir(), LOG, MAXPGPATH, ReadDir(), ResetUnloggedRelationsInDbspaceDir(), snprintf, UNLOGGED_RELATION_CLEANUP, and UNLOGGED_RELATION_INIT.

Referenced by ResetUnloggedRelations().