Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * fileset.c
4 : * Management of named temporary files.
5 : *
6 : * Portions Copyright (c) 1996-2025, 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 388 : FileSetInit(FileSet *fileset)
53 : {
54 : static uint32 counter = 0;
55 :
56 388 : fileset->creator_pid = MyProcPid;
57 388 : fileset->number = counter;
58 388 : counter = (counter + 1) % INT_MAX;
59 :
60 : /* Capture the tablespace OIDs so that all backends agree on them. */
61 388 : PrepareTempTablespaces();
62 388 : fileset->ntablespaces =
63 388 : GetTempTablespaces(&fileset->tablespaces[0],
64 : lengthof(fileset->tablespaces));
65 388 : if (fileset->ntablespaces == 0)
66 : {
67 : /* If the GUC is empty, use current database's default tablespace */
68 388 : fileset->tablespaces[0] = MyDatabaseTableSpace;
69 388 : 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 388 : }
87 :
88 : /*
89 : * Create a new file in the given set.
90 : */
91 : File
92 2234 : FileSetCreate(FileSet *fileset, const char *name)
93 : {
94 : char path[MAXPGPATH];
95 : File file;
96 :
97 2234 : FilePath(path, fileset, name);
98 2234 : file = PathNameCreateTemporaryFile(path, false);
99 :
100 : /* If we failed, see if we need to create the directory on demand. */
101 2234 : if (file <= 0)
102 : {
103 : char tempdirpath[MAXPGPATH];
104 : char filesetpath[MAXPGPATH];
105 366 : Oid tablespace = ChooseTablespace(fileset, name);
106 :
107 366 : TempTablespacePath(tempdirpath, tablespace);
108 366 : FileSetPath(filesetpath, fileset, tablespace);
109 366 : PathNameCreateTemporaryDir(tempdirpath, filesetpath);
110 366 : file = PathNameCreateTemporaryFile(path, true);
111 : }
112 :
113 2234 : return file;
114 : }
115 :
116 : /*
117 : * Open a file that was created with FileSetCreate()
118 : */
119 : File
120 6830 : FileSetOpen(FileSet *fileset, const char *name, int mode)
121 : {
122 : char path[MAXPGPATH];
123 : File file;
124 :
125 6830 : FilePath(path, fileset, name);
126 6830 : file = PathNameOpenTemporaryFile(path, mode);
127 :
128 6830 : return file;
129 : }
130 :
131 : /*
132 : * Delete a file that was created with FileSetCreate().
133 : *
134 : * Return true if the file existed, false if didn't.
135 : */
136 : bool
137 3016 : FileSetDelete(FileSet *fileset, const char *name,
138 : bool error_on_failure)
139 : {
140 : char path[MAXPGPATH];
141 :
142 3016 : FilePath(path, fileset, name);
143 :
144 3016 : return PathNameDeleteTemporaryFile(path, error_on_failure);
145 : }
146 :
147 : /*
148 : * Delete all files in the set.
149 : */
150 : void
151 436 : FileSetDeleteAll(FileSet *fileset)
152 : {
153 : char dirpath[MAXPGPATH];
154 : int i;
155 :
156 : /*
157 : * Delete the directory we created in each tablespace. Doesn't fail
158 : * because we use this in error cleanup paths, but can generate LOG
159 : * message on IO error.
160 : */
161 872 : for (i = 0; i < fileset->ntablespaces; ++i)
162 : {
163 436 : FileSetPath(dirpath, fileset, fileset->tablespaces[i]);
164 436 : PathNameDeleteTemporaryDir(dirpath);
165 : }
166 436 : }
167 :
168 : /*
169 : * Build the path for the directory holding the files backing a FileSet in a
170 : * given tablespace.
171 : */
172 : static void
173 12882 : FileSetPath(char *path, FileSet *fileset, Oid tablespace)
174 : {
175 : char tempdirpath[MAXPGPATH];
176 :
177 12882 : TempTablespacePath(tempdirpath, tablespace);
178 12882 : snprintf(path, MAXPGPATH, "%s/%s%lu.%u.fileset",
179 : tempdirpath, PG_TEMP_FILE_PREFIX,
180 12882 : (unsigned long) fileset->creator_pid, fileset->number);
181 12882 : }
182 :
183 : /*
184 : * Sorting has to determine which tablespace a given temporary file belongs in.
185 : */
186 : static Oid
187 12446 : ChooseTablespace(const FileSet *fileset, const char *name)
188 : {
189 12446 : uint32 hash = hash_bytes((const unsigned char *) name, strlen(name));
190 :
191 12446 : return fileset->tablespaces[hash % fileset->ntablespaces];
192 : }
193 :
194 : /*
195 : * Compute the full path of a file in a FileSet.
196 : */
197 : static void
198 12080 : FilePath(char *path, FileSet *fileset, const char *name)
199 : {
200 : char dirpath[MAXPGPATH];
201 :
202 12080 : FileSetPath(dirpath, fileset, ChooseTablespace(fileset, name));
203 12080 : snprintf(path, MAXPGPATH, "%s/%s", dirpath, name);
204 12080 : }
|