PostgreSQL Source Code  git master
sharedfileset.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * sharedfileset.c
4  * Shared temporary file management.
5  *
6  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * src/backend/storage/file/sharedfileset.c
11  *
12  * SharedFileSets provide a temporary namespace (think directory) so that
13  * files can be discovered by name, and a shared ownership semantics so that
14  * shared files survive until the last user detaches.
15  *
16  *-------------------------------------------------------------------------
17  */
18 
19 #include "postgres.h"
20 
21 #include <limits.h>
22 
23 #include "catalog/pg_tablespace.h"
24 #include "commands/tablespace.h"
25 #include "miscadmin.h"
26 #include "storage/dsm.h"
27 #include "storage/sharedfileset.h"
28 #include "utils/builtins.h"
29 #include "utils/hashutils.h"
30 
31 static void SharedFileSetOnDetach(dsm_segment *segment, Datum datum);
32 static void SharedFileSetPath(char *path, SharedFileSet *fileset, Oid tablespace);
33 static void SharedFilePath(char *path, SharedFileSet *fileset, const char *name);
34 static Oid ChooseTablespace(const SharedFileSet *fileset, const char *name);
35 
36 /*
37  * Initialize a space for temporary files that can be opened for read-only
38  * access by other backends. Other backends must attach to it before
39  * accessing it. Associate this SharedFileSet with 'seg'. Any contained
40  * files will be deleted when the last backend detaches.
41  *
42  * Files will be distributed over the tablespaces configured in
43  * temp_tablespaces.
44  *
45  * Under the covers the set is one or more directories which will eventually
46  * be deleted when there are no backends attached.
47  */
48 void
50 {
51  static uint32 counter = 0;
52 
53  SpinLockInit(&fileset->mutex);
54  fileset->refcnt = 1;
55  fileset->creator_pid = MyProcPid;
56  fileset->number = counter;
57  counter = (counter + 1) % INT_MAX;
58 
59  /* Capture the tablespace OIDs so that all backends agree on them. */
61  fileset->ntablespaces =
62  GetTempTablespaces(&fileset->tablespaces[0],
63  lengthof(fileset->tablespaces));
64  if (fileset->ntablespaces == 0)
65  {
66  fileset->tablespaces[0] = DEFAULTTABLESPACE_OID;
67  fileset->ntablespaces = 1;
68  }
69 
70  /* Register our cleanup callback. */
72 }
73 
74 /*
75  * Attach to a set of directories that was created with SharedFileSetInit.
76  */
77 void
79 {
80  bool success;
81 
82  SpinLockAcquire(&fileset->mutex);
83  if (fileset->refcnt == 0)
84  success = false;
85  else
86  {
87  ++fileset->refcnt;
88  success = true;
89  }
90  SpinLockRelease(&fileset->mutex);
91 
92  if (!success)
93  ereport(ERROR,
94  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
95  errmsg("could not attach to a SharedFileSet that is already destroyed")));
96 
97  /* Register our cleanup callback. */
99 }
100 
101 /*
102  * Create a new file in the given set.
103  */
104 File
105 SharedFileSetCreate(SharedFileSet *fileset, const char *name)
106 {
107  char path[MAXPGPATH];
108  File file;
109 
110  SharedFilePath(path, fileset, name);
111  file = PathNameCreateTemporaryFile(path, false);
112 
113  /* If we failed, see if we need to create the directory on demand. */
114  if (file <= 0)
115  {
116  char tempdirpath[MAXPGPATH];
117  char filesetpath[MAXPGPATH];
118  Oid tablespace = ChooseTablespace(fileset, name);
119 
120  TempTablespacePath(tempdirpath, tablespace);
121  SharedFileSetPath(filesetpath, fileset, tablespace);
122  PathNameCreateTemporaryDir(tempdirpath, filesetpath);
123  file = PathNameCreateTemporaryFile(path, true);
124  }
125 
126  return file;
127 }
128 
129 /*
130  * Open a file that was created with SharedFileSetCreate(), possibly in
131  * another backend.
132  */
133 File
134 SharedFileSetOpen(SharedFileSet *fileset, const char *name)
135 {
136  char path[MAXPGPATH];
137  File file;
138 
139  SharedFilePath(path, fileset, name);
140  file = PathNameOpenTemporaryFile(path);
141 
142  return file;
143 }
144 
145 /*
146  * Delete a file that was created with SharedFileSetCreate().
147  * Return true if the file existed, false if didn't.
148  */
149 bool
150 SharedFileSetDelete(SharedFileSet *fileset, const char *name,
151  bool error_on_failure)
152 {
153  char path[MAXPGPATH];
154 
155  SharedFilePath(path, fileset, name);
156 
157  return PathNameDeleteTemporaryFile(path, error_on_failure);
158 }
159 
160 /*
161  * Delete all files in the set.
162  */
163 void
165 {
166  char dirpath[MAXPGPATH];
167  int i;
168 
169  /*
170  * Delete the directory we created in each tablespace. Doesn't fail
171  * because we use this in error cleanup paths, but can generate LOG
172  * message on IO error.
173  */
174  for (i = 0; i < fileset->ntablespaces; ++i)
175  {
176  SharedFileSetPath(dirpath, fileset, fileset->tablespaces[i]);
178  }
179 }
180 
181 /*
182  * Callback function that will be invoked when this backend detaches from a
183  * DSM segment holding a SharedFileSet that it has created or attached to. If
184  * we are the last to detach, then try to remove the directories and
185  * everything in them. We can't raise an error on failures, because this runs
186  * in error cleanup paths.
187  */
188 static void
190 {
191  bool unlink_all = false;
192  SharedFileSet *fileset = (SharedFileSet *) DatumGetPointer(datum);
193 
194  SpinLockAcquire(&fileset->mutex);
195  Assert(fileset->refcnt > 0);
196  if (--fileset->refcnt == 0)
197  unlink_all = true;
198  SpinLockRelease(&fileset->mutex);
199 
200  /*
201  * If we are the last to detach, we delete the directory in all
202  * tablespaces. Note that we are still actually attached for the rest of
203  * this function so we can safely access its data.
204  */
205  if (unlink_all)
206  SharedFileSetDeleteAll(fileset);
207 }
208 
209 /*
210  * Build the path for the directory holding the files backing a SharedFileSet
211  * in a given tablespace.
212  */
213 static void
215 {
216  char tempdirpath[MAXPGPATH];
217 
218  TempTablespacePath(tempdirpath, tablespace);
219  snprintf(path, MAXPGPATH, "%s/%s%lu.%u.sharedfileset",
220  tempdirpath, PG_TEMP_FILE_PREFIX,
221  (unsigned long) fileset->creator_pid, fileset->number);
222 }
223 
224 /*
225  * Sorting hat to determine which tablespace a given shared temporary file
226  * belongs in.
227  */
228 static Oid
229 ChooseTablespace(const SharedFileSet *fileset, const char *name)
230 {
231  uint32 hash = hash_any((const unsigned char *) name, strlen(name));
232 
233  return fileset->tablespaces[hash % fileset->ntablespaces];
234 }
235 
236 /*
237  * Compute the full path of a file in a SharedFileSet.
238  */
239 static void
240 SharedFilePath(char *path, SharedFileSet *fileset, const char *name)
241 {
242  char dirpath[MAXPGPATH];
243 
244  SharedFileSetPath(dirpath, fileset, ChooseTablespace(fileset, name));
245  snprintf(path, MAXPGPATH, "%s/%s", dirpath, name);
246 }
static void SharedFileSetPath(char *path, SharedFileSet *fileset, Oid tablespace)
bool PathNameDeleteTemporaryFile(const char *path, bool error_on_failure)
Definition: fd.c:1702
int MyProcPid
Definition: globals.c:40
File PathNameCreateTemporaryFile(const char *path, bool error_on_failure)
Definition: fd.c:1634
Datum hash_any(const unsigned char *k, int keylen)
Definition: hashfn.c:148
void SharedFileSetInit(SharedFileSet *fileset, dsm_segment *seg)
Definition: sharedfileset.c:49
bool SharedFileSetDelete(SharedFileSet *fileset, const char *name, bool error_on_failure)
int GetTempTablespaces(Oid *tableSpaces, int numSpaces)
Definition: fd.c:2746
#define PointerGetDatum(X)
Definition: postgres.h:556
#define SpinLockInit(lock)
Definition: spin.h:60
int errcode(int sqlerrcode)
Definition: elog.c:608
void PathNameDeleteTemporaryDir(const char *dirname)
Definition: fd.c:1466
File PathNameOpenTemporaryFile(const char *path)
Definition: fd.c:1672
#define lengthof(array)
Definition: c.h:669
void TempTablespacePath(char *path, Oid tablespace)
Definition: fd.c:1552
unsigned int Oid
Definition: postgres_ext.h:31
void on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, Datum arg)
Definition: dsm.c:975
#define SpinLockAcquire(lock)
Definition: spin.h:62
#define ERROR
Definition: elog.h:43
#define PG_TEMP_FILE_PREFIX
Definition: pg_checksums.c:59
void PrepareTempTablespaces(void)
Definition: tablespace.c:1323
#define MAXPGPATH
char * tablespace
Definition: pgbench.c:189
unsigned int uint32
Definition: c.h:359
#define ereport(elevel, rest)
Definition: elog.h:141
#define SpinLockRelease(lock)
Definition: spin.h:64
uintptr_t Datum
Definition: postgres.h:367
void SharedFileSetDeleteAll(SharedFileSet *fileset)
#define Assert(condition)
Definition: c.h:739
Oid tablespaces[8]
Definition: sharedfileset.h:32
const char * name
Definition: encode.c:521
#define DatumGetPointer(X)
Definition: postgres.h:549
File SharedFileSetCreate(SharedFileSet *fileset, const char *name)
int errmsg(const char *fmt,...)
Definition: elog.c:822
void SharedFileSetAttach(SharedFileSet *fileset, dsm_segment *seg)
Definition: sharedfileset.c:78
static void SharedFilePath(char *path, SharedFileSet *fileset, const char *name)
static Oid ChooseTablespace(const SharedFileSet *fileset, const char *name)
int i
File SharedFileSetOpen(SharedFileSet *fileset, const char *name)
static bool success
Definition: initdb.c:163
void PathNameCreateTemporaryDir(const char *basedir, const char *directory)
Definition: fd.c:1435
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:541
#define snprintf
Definition: port.h:192
int File
Definition: fd.h:45
static void SharedFileSetOnDetach(dsm_segment *segment, Datum datum)