PostgreSQL Source Code  git master
reinit.c File Reference
#include "postgres.h"
#include <unistd.h>
#include "common/relpath.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 369 of file reinit.c.

References forkname_chars(), MAIN_FORKNUM, and OIDCHARS.

Referenced by ResetUnloggedRelationsInDbspaceDir(), and sendDir().

371 {
372  int pos;
373 
374  /* Look for a non-empty string of digits (that isn't too long). */
375  for (pos = 0; isdigit((unsigned char) name[pos]); ++pos)
376  ;
377  if (pos == 0 || pos > OIDCHARS)
378  return false;
379  *oidchars = pos;
380 
381  /* Check for a fork name. */
382  if (name[pos] != '_')
383  *fork = MAIN_FORKNUM;
384  else
385  {
386  int forkchar;
387 
388  forkchar = forkname_chars(&name[pos + 1], fork);
389  if (forkchar <= 0)
390  return false;
391  pos += forkchar + 1;
392  }
393 
394  /* Check for a segment number. */
395  if (name[pos] == '.')
396  {
397  int segchar;
398 
399  for (segchar = 1; isdigit((unsigned char) name[pos + segchar]); ++segchar)
400  ;
401  if (segchar <= 1)
402  return false;
403  pos += segchar;
404  }
405 
406  /* Now we should be at the end. */
407  if (name[pos] != '\0')
408  return false;
409  return true;
410 }
int forkname_chars(const char *str, ForkNumber *fork)
Definition: relpath.c:81
const char * name
Definition: encode.c:515
#define OIDCHARS
Definition: relpath.h:30

◆ ResetUnloggedRelations()

void ResetUnloggedRelations ( int  op)

Definition at line 46 of file reinit.c.

References AllocateDir(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, 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().

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

◆ ResetUnloggedRelationsInDbspaceDir()

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

Definition at line 149 of file reinit.c.

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().

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

◆ ResetUnloggedRelationsInTablespaceDir()

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

Definition at line 102 of file reinit.c.

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

Referenced by ResetUnloggedRelations().

103 {
104  DIR *ts_dir;
105  struct dirent *de;
106  char dbspace_path[MAXPGPATH * 2];
107 
108  ts_dir = AllocateDir(tsdirname);
109 
110  /*
111  * If we get ENOENT on a tablespace directory, log it and return. This
112  * can happen if a previous DROP TABLESPACE crashed between removing the
113  * tablespace directory and removing the symlink in pg_tblspc. We don't
114  * really want to prevent database startup in that scenario, so let it
115  * pass instead. Any other type of error will be reported by ReadDir
116  * (causing a startup failure).
117  */
118  if (ts_dir == NULL && errno == ENOENT)
119  {
120  ereport(LOG,
122  errmsg("could not open directory \"%s\": %m",
123  tsdirname)));
124  return;
125  }
126 
127  while ((de = ReadDir(ts_dir, tsdirname)) != NULL)
128  {
129  /*
130  * We're only interested in the per-database directories, which have
131  * numeric names. Note that this code will also (properly) ignore "."
132  * and "..".
133  */
134  if (strspn(de->d_name, "0123456789") != strlen(de->d_name))
135  continue;
136 
137  snprintf(dbspace_path, sizeof(dbspace_path), "%s/%s",
138  tsdirname, de->d_name);
139  ResetUnloggedRelationsInDbspaceDir(dbspace_path, op);
140  }
141 
142  FreeDir(ts_dir);
143 }
#define LOG
Definition: elog.h:26
Definition: dirent.h:9
static void ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
Definition: reinit.c:149
Definition: dirent.c:25
#define MAXPGPATH
int errcode_for_file_access(void)
Definition: elog.c:721
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2634
#define ereport(elevel,...)
Definition: elog.h:157
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2700
int errmsg(const char *fmt,...)
Definition: elog.c:909
char d_name[MAX_PATH]
Definition: dirent.h:15
#define snprintf
Definition: port.h:216
int FreeDir(DIR *dir)
Definition: fd.c:2752