2021-08-30 05:15:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* fileset.c
|
|
|
|
* Management of named temporary files.
|
|
|
|
*
|
2023-01-02 21:00:37 +01:00
|
|
|
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
|
2021-08-30 05:15:35 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* src/backend/storage/file/fileset.c
|
|
|
|
*
|
|
|
|
* FileSets provide a temporary namespace (think directory) so that files can
|
|
|
|
* be discovered by name.
|
|
|
|
*
|
|
|
|
* FileSets can be used by backends when the temporary files need to be
|
|
|
|
* opened/closed multiple times and the underlying files need to survive across
|
|
|
|
* transactions.
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
#include "catalog/pg_tablespace.h"
|
|
|
|
#include "commands/tablespace.h"
|
|
|
|
#include "common/hashfn.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "storage/ipc.h"
|
|
|
|
#include "storage/fileset.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
|
|
|
|
static void FileSetPath(char *path, FileSet *fileset, Oid tablespace);
|
|
|
|
static void FilePath(char *path, FileSet *fileset, const char *name);
|
|
|
|
static Oid ChooseTablespace(const FileSet *fileset, const char *name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize a space for temporary files. This API can be used by shared
|
|
|
|
* fileset as well as if the temporary files are used only by single backend
|
|
|
|
* but the files need to be opened and closed multiple times and also the
|
|
|
|
* underlying files need to survive across transactions.
|
|
|
|
*
|
|
|
|
* The callers are expected to explicitly remove such files by using
|
|
|
|
* FileSetDelete/FileSetDeleteAll.
|
|
|
|
*
|
|
|
|
* Files will be distributed over the tablespaces configured in
|
|
|
|
* temp_tablespaces.
|
|
|
|
*
|
|
|
|
* Under the covers the set is one or more directories which will eventually
|
|
|
|
* be deleted.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
FileSetInit(FileSet *fileset)
|
|
|
|
{
|
|
|
|
static uint32 counter = 0;
|
|
|
|
|
|
|
|
fileset->creator_pid = MyProcPid;
|
|
|
|
fileset->number = counter;
|
|
|
|
counter = (counter + 1) % INT_MAX;
|
|
|
|
|
|
|
|
/* Capture the tablespace OIDs so that all backends agree on them. */
|
|
|
|
PrepareTempTablespaces();
|
|
|
|
fileset->ntablespaces =
|
|
|
|
GetTempTablespaces(&fileset->tablespaces[0],
|
|
|
|
lengthof(fileset->tablespaces));
|
|
|
|
if (fileset->ntablespaces == 0)
|
|
|
|
{
|
|
|
|
/* If the GUC is empty, use current database's default tablespace */
|
|
|
|
fileset->tablespaces[0] = MyDatabaseTableSpace;
|
|
|
|
fileset->ntablespaces = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* An entry of InvalidOid means use the default tablespace for the
|
|
|
|
* current database. Replace that now, to be sure that all users of
|
|
|
|
* the FileSet agree on what to do.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < fileset->ntablespaces; i++)
|
|
|
|
{
|
|
|
|
if (fileset->tablespaces[i] == InvalidOid)
|
|
|
|
fileset->tablespaces[i] = MyDatabaseTableSpace;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new file in the given set.
|
|
|
|
*/
|
|
|
|
File
|
|
|
|
FileSetCreate(FileSet *fileset, const char *name)
|
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
File file;
|
|
|
|
|
|
|
|
FilePath(path, fileset, name);
|
|
|
|
file = PathNameCreateTemporaryFile(path, false);
|
|
|
|
|
|
|
|
/* If we failed, see if we need to create the directory on demand. */
|
|
|
|
if (file <= 0)
|
|
|
|
{
|
|
|
|
char tempdirpath[MAXPGPATH];
|
|
|
|
char filesetpath[MAXPGPATH];
|
|
|
|
Oid tablespace = ChooseTablespace(fileset, name);
|
|
|
|
|
|
|
|
TempTablespacePath(tempdirpath, tablespace);
|
|
|
|
FileSetPath(filesetpath, fileset, tablespace);
|
|
|
|
PathNameCreateTemporaryDir(tempdirpath, filesetpath);
|
|
|
|
file = PathNameCreateTemporaryFile(path, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open a file that was created with FileSetCreate() */
|
|
|
|
File
|
|
|
|
FileSetOpen(FileSet *fileset, const char *name, int mode)
|
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
File file;
|
|
|
|
|
|
|
|
FilePath(path, fileset, name);
|
|
|
|
file = PathNameOpenTemporaryFile(path, mode);
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a file that was created with FileSetCreate().
|
|
|
|
*
|
|
|
|
* Return true if the file existed, false if didn't.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
FileSetDelete(FileSet *fileset, const char *name,
|
|
|
|
bool error_on_failure)
|
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
|
|
|
|
FilePath(path, fileset, name);
|
|
|
|
|
|
|
|
return PathNameDeleteTemporaryFile(path, error_on_failure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete all files in the set.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
FileSetDeleteAll(FileSet *fileset)
|
|
|
|
{
|
|
|
|
char dirpath[MAXPGPATH];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete the directory we created in each tablespace. Doesn't fail
|
|
|
|
* because we use this in error cleanup paths, but can generate LOG
|
|
|
|
* message on IO error.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < fileset->ntablespaces; ++i)
|
|
|
|
{
|
|
|
|
FileSetPath(dirpath, fileset, fileset->tablespaces[i]);
|
|
|
|
PathNameDeleteTemporaryDir(dirpath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build the path for the directory holding the files backing a FileSet in a
|
|
|
|
* given tablespace.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
FileSetPath(char *path, FileSet *fileset, Oid tablespace)
|
|
|
|
{
|
|
|
|
char tempdirpath[MAXPGPATH];
|
|
|
|
|
|
|
|
TempTablespacePath(tempdirpath, tablespace);
|
|
|
|
snprintf(path, MAXPGPATH, "%s/%s%lu.%u.fileset",
|
|
|
|
tempdirpath, PG_TEMP_FILE_PREFIX,
|
|
|
|
(unsigned long) fileset->creator_pid, fileset->number);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sorting has to determine which tablespace a given temporary file belongs in.
|
|
|
|
*/
|
|
|
|
static Oid
|
|
|
|
ChooseTablespace(const FileSet *fileset, const char *name)
|
|
|
|
{
|
|
|
|
uint32 hash = hash_any((const unsigned char *) name, strlen(name));
|
|
|
|
|
|
|
|
return fileset->tablespaces[hash % fileset->ntablespaces];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute the full path of a file in a FileSet.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
FilePath(char *path, FileSet *fileset, const char *name)
|
|
|
|
{
|
|
|
|
char dirpath[MAXPGPATH];
|
|
|
|
|
|
|
|
FileSetPath(dirpath, fileset, ChooseTablespace(fileset, name));
|
|
|
|
snprintf(path, MAXPGPATH, "%s/%s", dirpath, name);
|
|
|
|
}
|