/src/postgres/src/backend/storage/file/fileset.c
Line | Count | Source (jump to first uncovered line) |
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 | | FileSetInit(FileSet *fileset) |
53 | 0 | { |
54 | 0 | static uint32 counter = 0; |
55 | |
|
56 | 0 | fileset->creator_pid = MyProcPid; |
57 | 0 | fileset->number = counter; |
58 | 0 | counter = (counter + 1) % INT_MAX; |
59 | | |
60 | | /* Capture the tablespace OIDs so that all backends agree on them. */ |
61 | 0 | PrepareTempTablespaces(); |
62 | 0 | fileset->ntablespaces = |
63 | 0 | GetTempTablespaces(&fileset->tablespaces[0], |
64 | 0 | lengthof(fileset->tablespaces)); |
65 | 0 | if (fileset->ntablespaces == 0) |
66 | 0 | { |
67 | | /* If the GUC is empty, use current database's default tablespace */ |
68 | 0 | fileset->tablespaces[0] = MyDatabaseTableSpace; |
69 | 0 | fileset->ntablespaces = 1; |
70 | 0 | } |
71 | 0 | else |
72 | 0 | { |
73 | 0 | 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 | 0 | { |
82 | 0 | if (fileset->tablespaces[i] == InvalidOid) |
83 | 0 | fileset->tablespaces[i] = MyDatabaseTableSpace; |
84 | 0 | } |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | /* |
89 | | * Create a new file in the given set. |
90 | | */ |
91 | | File |
92 | | FileSetCreate(FileSet *fileset, const char *name) |
93 | 0 | { |
94 | 0 | char path[MAXPGPATH]; |
95 | 0 | File file; |
96 | |
|
97 | 0 | FilePath(path, fileset, name); |
98 | 0 | file = PathNameCreateTemporaryFile(path, false); |
99 | | |
100 | | /* If we failed, see if we need to create the directory on demand. */ |
101 | 0 | if (file <= 0) |
102 | 0 | { |
103 | 0 | char tempdirpath[MAXPGPATH]; |
104 | 0 | char filesetpath[MAXPGPATH]; |
105 | 0 | Oid tablespace = ChooseTablespace(fileset, name); |
106 | |
|
107 | 0 | TempTablespacePath(tempdirpath, tablespace); |
108 | 0 | FileSetPath(filesetpath, fileset, tablespace); |
109 | 0 | PathNameCreateTemporaryDir(tempdirpath, filesetpath); |
110 | 0 | file = PathNameCreateTemporaryFile(path, true); |
111 | 0 | } |
112 | |
|
113 | 0 | return file; |
114 | 0 | } |
115 | | |
116 | | /* |
117 | | * Open a file that was created with FileSetCreate() */ |
118 | | File |
119 | | FileSetOpen(FileSet *fileset, const char *name, int mode) |
120 | 0 | { |
121 | 0 | char path[MAXPGPATH]; |
122 | 0 | File file; |
123 | |
|
124 | 0 | FilePath(path, fileset, name); |
125 | 0 | file = PathNameOpenTemporaryFile(path, mode); |
126 | |
|
127 | 0 | return file; |
128 | 0 | } |
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 | | FileSetDelete(FileSet *fileset, const char *name, |
137 | | bool error_on_failure) |
138 | 0 | { |
139 | 0 | char path[MAXPGPATH]; |
140 | |
|
141 | 0 | FilePath(path, fileset, name); |
142 | |
|
143 | 0 | return PathNameDeleteTemporaryFile(path, error_on_failure); |
144 | 0 | } |
145 | | |
146 | | /* |
147 | | * Delete all files in the set. |
148 | | */ |
149 | | void |
150 | | FileSetDeleteAll(FileSet *fileset) |
151 | 0 | { |
152 | 0 | char dirpath[MAXPGPATH]; |
153 | 0 | 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 | 0 | for (i = 0; i < fileset->ntablespaces; ++i) |
161 | 0 | { |
162 | 0 | FileSetPath(dirpath, fileset, fileset->tablespaces[i]); |
163 | 0 | PathNameDeleteTemporaryDir(dirpath); |
164 | 0 | } |
165 | 0 | } |
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 | | FileSetPath(char *path, FileSet *fileset, Oid tablespace) |
173 | 0 | { |
174 | 0 | char tempdirpath[MAXPGPATH]; |
175 | |
|
176 | 0 | TempTablespacePath(tempdirpath, tablespace); |
177 | 0 | snprintf(path, MAXPGPATH, "%s/%s%lu.%u.fileset", |
178 | 0 | tempdirpath, PG_TEMP_FILE_PREFIX, |
179 | 0 | (unsigned long) fileset->creator_pid, fileset->number); |
180 | 0 | } |
181 | | |
182 | | /* |
183 | | * Sorting has to determine which tablespace a given temporary file belongs in. |
184 | | */ |
185 | | static Oid |
186 | | ChooseTablespace(const FileSet *fileset, const char *name) |
187 | 0 | { |
188 | 0 | uint32 hash = hash_any((const unsigned char *) name, strlen(name)); |
189 | |
|
190 | 0 | return fileset->tablespaces[hash % fileset->ntablespaces]; |
191 | 0 | } |
192 | | |
193 | | /* |
194 | | * Compute the full path of a file in a FileSet. |
195 | | */ |
196 | | static void |
197 | | FilePath(char *path, FileSet *fileset, const char *name) |
198 | 0 | { |
199 | 0 | char dirpath[MAXPGPATH]; |
200 | |
|
201 | 0 | FileSetPath(dirpath, fileset, ChooseTablespace(fileset, name)); |
202 | 0 | snprintf(path, MAXPGPATH, "%s/%s", dirpath, name); |
203 | 0 | } |