Coverage Report

Created: 2025-07-03 06:49

/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
}