Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * fileset.c
4 : * Management of named temporary files.
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/backend/storage/file/fileset.c
11 : *
12 : * FileSets provide a temporary namespace (think directory) so that files can
13 : * be discovered by name.
14 : *
15 : * FileSets can be used by backends when the temporary files need to be
16 : * opened/closed multiple times and the underlying files need to survive across
17 : * transactions.
18 : *
19 : *-------------------------------------------------------------------------
20 : */
21 :
22 : #include "postgres.h"
23 :
24 : #include <limits.h>
25 :
26 : #include "commands/tablespace.h"
27 : #include "common/file_utils.h"
28 : #include "common/hashfn.h"
29 : #include "miscadmin.h"
30 : #include "storage/fileset.h"
31 :
32 : static void FileSetPath(char *path, FileSet *fileset, Oid tablespace);
33 : static void FilePath(char *path, FileSet *fileset, const char *name);
34 : static Oid ChooseTablespace(const FileSet *fileset, const char *name);
35 :
36 : /*
37 : * Initialize a space for temporary files. This API can be used by shared
38 : * fileset as well as if the temporary files are used only by single backend
39 : * but the files need to be opened and closed multiple times and also the
40 : * underlying files need to survive across transactions.
41 : *
42 : * The callers are expected to explicitly remove such files by using
43 : * FileSetDelete/FileSetDeleteAll.
44 : *
45 : * Files will be distributed over the tablespaces configured in
46 : * temp_tablespaces.
47 : *
48 : * Under the covers the set is one or more directories which will eventually
49 : * be deleted.
50 : */
51 : void
52 370 : FileSetInit(FileSet *fileset)
53 : {
54 : static uint32 counter = 0;
55 :
56 370 : fileset->creator_pid = MyProcPid;
57 370 : fileset->number = counter;
58 370 : counter = (counter + 1) % INT_MAX;
59 :
60 : /* Capture the tablespace OIDs so that all backends agree on them. */
61 370 : PrepareTempTablespaces();
62 370 : fileset->ntablespaces =
63 370 : GetTempTablespaces(&fileset->tablespaces[0],
64 : lengthof(fileset->tablespaces));
65 370 : if (fileset->ntablespaces == 0)
66 : {
67 : /* If the GUC is empty, use current database's default tablespace */
68 370 : fileset->tablespaces[0] = MyDatabaseTableSpace;
69 370 : fileset->ntablespaces = 1;
70 : }
71 : else
72 : {
73 : int i;
74 :
75 : /*
76 : * An entry of InvalidOid means use the default tablespace for the
77 : * current database. Replace that now, to be sure that all users of
78 : * the FileSet agree on what to do.
79 : */
80 0 : for (i = 0; i < fileset->ntablespaces; i++)
81 : {
82 0 : if (fileset->tablespaces[i] == InvalidOid)
83 0 : fileset->tablespaces[i] = MyDatabaseTableSpace;
84 : }
85 : }
86 370 : }
87 :
88 : /*
89 : * Create a new file in the given set.
90 : */
91 : File
92 2270 : FileSetCreate(FileSet *fileset, const char *name)
93 : {
94 : char path[MAXPGPATH];
95 : File file;
96 :
97 2270 : FilePath(path, fileset, name);
98 2270 : file = PathNameCreateTemporaryFile(path, false);
99 :
100 : /* If we failed, see if we need to create the directory on demand. */
101 2270 : if (file <= 0)
102 : {
103 : char tempdirpath[MAXPGPATH];
104 : char filesetpath[MAXPGPATH];
105 350 : Oid tablespace = ChooseTablespace(fileset, name);
106 :
107 350 : TempTablespacePath(tempdirpath, tablespace);
108 350 : FileSetPath(filesetpath, fileset, tablespace);
109 350 : PathNameCreateTemporaryDir(tempdirpath, filesetpath);
110 350 : file = PathNameCreateTemporaryFile(path, true);
111 : }
112 :
113 2270 : return file;
114 : }
115 :
116 : /*
117 : * Open a file that was created with FileSetCreate() */
118 : File
119 6878 : FileSetOpen(FileSet *fileset, const char *name, int mode)
120 : {
121 : char path[MAXPGPATH];
122 : File file;
123 :
124 6878 : FilePath(path, fileset, name);
125 6878 : file = PathNameOpenTemporaryFile(path, mode);
126 :
127 6878 : return file;
128 : }
129 :
130 : /*
131 : * Delete a file that was created with FileSetCreate().
132 : *
133 : * Return true if the file existed, false if didn't.
134 : */
135 : bool
136 3052 : FileSetDelete(FileSet *fileset, const char *name,
137 : bool error_on_failure)
138 : {
139 : char path[MAXPGPATH];
140 :
141 3052 : FilePath(path, fileset, name);
142 :
143 3052 : return PathNameDeleteTemporaryFile(path, error_on_failure);
144 : }
145 :
146 : /*
147 : * Delete all files in the set.
148 : */
149 : void
150 418 : FileSetDeleteAll(FileSet *fileset)
151 : {
152 : char dirpath[MAXPGPATH];
153 : int i;
154 :
155 : /*
156 : * Delete the directory we created in each tablespace. Doesn't fail
157 : * because we use this in error cleanup paths, but can generate LOG
158 : * message on IO error.
159 : */
160 836 : for (i = 0; i < fileset->ntablespaces; ++i)
161 : {
162 418 : FileSetPath(dirpath, fileset, fileset->tablespaces[i]);
163 418 : PathNameDeleteTemporaryDir(dirpath);
164 : }
165 418 : }
166 :
167 : /*
168 : * Build the path for the directory holding the files backing a FileSet in a
169 : * given tablespace.
170 : */
171 : static void
172 12968 : FileSetPath(char *path, FileSet *fileset, Oid tablespace)
173 : {
174 : char tempdirpath[MAXPGPATH];
175 :
176 12968 : TempTablespacePath(tempdirpath, tablespace);
177 12968 : snprintf(path, MAXPGPATH, "%s/%s%lu.%u.fileset",
178 : tempdirpath, PG_TEMP_FILE_PREFIX,
179 12968 : (unsigned long) fileset->creator_pid, fileset->number);
180 12968 : }
181 :
182 : /*
183 : * Sorting has to determine which tablespace a given temporary file belongs in.
184 : */
185 : static Oid
186 12550 : ChooseTablespace(const FileSet *fileset, const char *name)
187 : {
188 12550 : uint32 hash = hash_any((const unsigned char *) name, strlen(name));
189 :
190 12550 : return fileset->tablespaces[hash % fileset->ntablespaces];
191 : }
192 :
193 : /*
194 : * Compute the full path of a file in a FileSet.
195 : */
196 : static void
197 12200 : FilePath(char *path, FileSet *fileset, const char *name)
198 : {
199 : char dirpath[MAXPGPATH];
200 :
201 12200 : FileSetPath(dirpath, fileset, ChooseTablespace(fileset, name));
202 12200 : snprintf(path, MAXPGPATH, "%s/%s", dirpath, name);
203 12200 : }
|